Xinu Version 7.0 on the Sun

Originally written by:
Peter Phillips, Graeme Clark, Terry Coatta, and Barry Brachman

Updated for the Sun 3 and converted to HTML, January 1995 by Barry Brachman

Table of Contents

Introduction

Over the duration of Computer Science 415 you will be working with computers and operating systems at the lowest level. In many cases you will be accessing the hardware of the computer directly to carry out the tasks given in the assignments. This requires detailed information on the architecture of the computer the devices attached to it. The text that you will be using for the course is, unfortunately, based on a version of the software that runs on PDP-11 computers. The hardware that you will be using is composed of Sun 2s or Sun 3s and HP workstations. To compensate for this difference, this document has been produced to provide all the information that you will require to complete the assignments given in the course.

The Xinu Environment

At UBC there are several Sun workstations dedicated to running the Xinu operating system. Because Xinu is an educational system, the general way it is used is quite different from an operating system in actual use, such as the Unix and Unix-like systems that run on most of the department's machines. One does not just walk to a Xinu machine and ``log on'' to an already-running operating system; rather, one logs on to one of the HP workstations, and uses a special version of the Unix C compiler to prepare a version of Xinu, possibly containing one's modifications or test program. This code is then downloaded to one of the Xinu machines and executed. If all goes well, Xinu begins operation on the machine, and runs the user's program. Usually the program completes (whether successfully or unsuccessfully) quite quickly. This causes Xinu to halt. Based on the results, the user may wish to repeat this, editing the program on the HP, recompiling, and trying again. Because the editing and compiling of Xinu programs takes place on HPs, and the Xinu machines are used only for the execution of these programs, a small number of Xinu machines can be successfully shared among a relatively large number of people, without undue delays.

The Xinu Machines

Currently there are several Sun 2 or Sun 3 workstations dedicated to running Xinu. These machines, known as xinu1, xinu2, etc., are located in the Rick Lab. The machines do not have directly attached screens or keyboards, but connect to the outside world in two ways. First, each machine has a ``console'' serial port which, if the machine were being operated normally (say for running Unix) would be connected directly to an ordinary terminal. From the console port one can communicate with the Sun's machine-language-level PROM monitor, which takes control whenever the machine is powered-up or reset.

The second connection each of the Xinu machines has is an ethernet port. The ethernet is a high-speed network that the Xinu machines can use for communicating with each other, and with a boot server that runs Unix. The boot server, in turn, is connected to the network of HP systems that form the cross-development environment. The transfer of a compiled Xinu system from an HP to a Xinu machine, mentioned above, takes place over this network.

To allow the Xinu machines to be used from locations other than the lab, the Xinu machines' console ports are not connected directly to terminals. Software is provided that allows a user on any HP machine to communicate with these console ports, effectively making his terminal a temporary console for the Xinu machine.

Using Xinu

The procedure for compiling, loading, and running a complete example Xinu program is described here. The simplest Xinu program is one that simply prints a message and exits:
  #include <conf.h>

  main()
  {
    printf("Hello world!\n");
  }

This program may be compiled and linked with the Xinu operating system, with the command:

  xcc hello.c
Note: The programs xcc and getxinu (described below) reside in the directory ~xinu/bin. It is probably desirable to add this directory to one's command search path by editing the .cshrc file in one's home directory, adding ~xinu/bin to the list of directories in the set path command.
If no errors occur, this creates an executable image in the file a.out. To run this on a Xinu machine, use the getxinu command:
  getxinu a.out
This command has the following effects. First, it attempts to obtain a free Xinu machine. If all machines are busy, then the user is queued, and getxinu will pause until a machine is free. During this waiting period, the user may type s to obtain a status report indicating how many users are waiting to use Xinu machines, or q to quit waiting.

Once a machine is available, a message indicating which one the user has been assigned to is printed, and the user's terminal is effectively connected to the indicated Xinu machine, becoming its console.

Loading and running the Xinu program (a.out) is now a two-step procedure. First, the Xinu machine must be put in a state in which its PROM monitor has control. Pressing RETURN a couple of times can be done to check this; when the monitor is running it will respond with its prompt ``>''. If something else, or nothing, occurs, then the machine is either running someone else's leftover Xinu program, or crashed. Typing \b (a backslash followed by a ``b'') sends a BREAK to the Xinu machine; this usually will (after a few seconds) cause the machine to stop whatever it is doing, and return to the monitor. If this repeatedly fails to get the monitor's attention, the machine must be power-cycled. Turn the appropriate Xinu machine off, wait 10 seconds, then turn it on again. The machine, once turned on, will sit for a while (checking memory etc.) and then print some messages on the console while it attempts to boot Unix. This boot attempt should be aborted by sending a break (\b) as before, which now is fairly certain to get the monitor's attention.

The Xinu program may now be loaded and run by issuing an appropriate boot command to the monitor. This command has the form:

  ble() your-login/progname
where your-login is the user's login name, and progname is the program name (a.out in this example) provided with the getxinu command.

The Xinu program should now run, printing in this case:

    Xinu Version 7.0 SUN3 (02/29/88)
    720895 bytes real mem
    33204 bytes Xinu code
    clock enabled
    Hello world!

    All user processes have completed.

For the convenience of later users of the Xinu machine, who may be signed on remotely and not be able to power-cycle the machine, it is a good idea to return the machine to the monitor state before typing \q to quit getxinu.

Getxinu may be instructed to obtain a specific Xinu machine with the -m option; e.g. getxinu -m xinu1 a.out to run a.out on machine xinu1. This is usually unnecessary but may be important when running programs involving the Xinu networking system.

Please release your Xinu machine as soon as you are finished with it.
Others may be waiting to use it.

Modifying and Compiling Xinu

The xcc command is similar to the standard gcc command, except that the only options supported are:
  -V -v -c -o -D -I -O -S -P -s
All of the options behave as they do for gcc, except for -V and -v. The -v flag is for verbose output; it causes xcc to spew out a lot of information. The -V flag is the version flag, and if used, the following argument is taken as a version-identifier, which indicates which version of Xinu is to be linked with the user's programs. The versions currently available are:

Xinu consists of a large collection of routines, most written in C, with some in 680X0 assembler language. The source for these routines is in the directory ~xinu/xinu.sun3/src/sys/sys. The header files are in ~xinu/xinu.sun3/src/sys/h. Usually only one or a few routines live in a single source file, whose name is that of the most important function in the file. For example, the kill() (process destruction) routine resides in ~xinu/xinu.sun3/src/sys/sys/kill.c. All Xinu routines have been compiled and linked to form a library that is automatically searched by xcc. To modify one or more of the Xinu routines, the procedure is to make a copy of the appropriate source files in a local directory, change this copy, and provide this file on the command line to xcc, along with the test program. This will cause the changed version of the file to be used, and the original one in the library to be ignored.

As an example, suppose we wish to make a change to the kill() routine. First, we make a copy in our home directory (or some subdirectory):

  cp ~xinu/xinu.sun3/src/sys/sys/kill.c .
Now we modify this file, and prepare a test program:
  vi kill.c
    ...
  vi mytest.c
    ...
Now we compile our test program and changed kill() routine, and link them with the remainder of Xinu:
  xcc -o mytest mytest.c kill.c
(The option -o mytest causes the result to be placed in the file mytest rather than a.out.)

Now the program can be run as before:

  getxinu mytest

Xinu Executables

The command
  getxinu progname
causes the file progname to be copied to the directory
  ~xinu/tftpboot/your-login/progname
on the boot server. The monitor command
  ble() your-login/progname
instructs the machine to load and run the named executable file from the directory ~xinu/tftpboot/your-login. You should remove old copies of the executables placed in that directory to conserve disk space. But in the mean time, it is possible to maintain a set of Xinu executables in one's subdirectory of ~xinu/tftpboot, running any of them when desired with the appropriate ble() command.

Getxinu may be run with no program argument (i.e. simply getxinu or getxinu -m machine-name), which will connect the user to a Xinu machine without copying an executable.

Getxinu may also be run with more that one program argument (i.e. getxinu progone progtwo progthree), which will copy all the named files into one's subdirectory and connect the user to a Xinu machine.

Architecture Details of the 68010/68020

This section is intended to provide information on how the Xinu system makes use of the Sun 2 and Sun 3 architectures, concentrating on the Motorola 68010/68020 processors used by these computers. Familiarity with assembly languages is assumed and the 68010/68020 instruction sets will not be discussed here. Since a complete understanding of Xinu requires at least a passing acquaintance with assembly language programming of the 68010/68020, however, further references may be required.

Basic Structure of the 68010/68020

The 68010/68020 are fairly standard 32-bit processors. Although there are some differences between the 68010 and 68020, they aren't significant to Xinu and so this document will treat the processors as being identical. At present, we will discuss the Sun 2 (68010) rather than the Sun 3 (68020), since they are quite similar.

The 68010 number of registers, is capable of moving or operating on data which resides either in these registers or in main memory, can receive interrupts from external sources, etc. A notable feature of the 68000 series processors is that they differentiate between registers that hold data and those that hold addresses. There are 8 data registers conventionally referred to as d0 through d7. There are 7 standard address registers (a0 through a6) and the special stack address register a7. In addition, there is a status register (sr) and the program counter (pc). Register a7 is set apart from the others because it is actually associated with 2 registers. The register to which it refers depends on whether the processor is in supervisor or user mode. In user mode certain processor operations are treated as privileged and an attempt to execute them results in an exception being generated. The dual stack registers provide further security by ensuring that when the processor is in user mode it cannot access the supervisor stack.

The distinction between address registers and data registers can be seen clearly in the instruction set of the processor. The operations that can be performed on address registers are a subset of those available for data registers, while any form of indirect addressing must use an address register. Since the contents of one type of register can be moved to the a register of the other type, however, there are no real restrictions on the manipulations that can be performed.

When running Xinu, you don't have to be concerned with running the processor in user mode. When the Sun 2 boots your Xinu code it places the machine in supervisor mode and the Xinu system never places the machine in user mode while it is operating. The effect of this is that any of your code will always run in supervisor mode. One of the effects of being in supervisor mode is that you can access the system byte of the status register. The status register is 16 bits wide. The lower 8 bits are the user byte, and their interpretations are given in the following table:

     Bit   Meaning
     =============-=====
     0     Carry Flag
     1     Overflow Flag
     2     Zero Flag
     3     Negative Flag
     4     Extend Flag
     5-7   Not Used

     Status Register User Byte
As can be seen from the table, the user byte simply contains the arithmetic flags (the extend bit is essentially identical to the carry bit). The upper bits are the system byte and their interpretations are given in the following table:
     Bit   Meaning
     ==========================
     0-2   Interrupt Level Mask
     3-4   Not Used
     5     Supervisor Mode Flag
     6     Not Used
     7     Trace Mode Flag

     Status Register System Byte
The supervisor mode bit is used to control whether the processor is in supervisor mode or not (bit = 1 --> supervisor mode), while the trace mode bit acts similarly. When in trace mode, a trace exception is generated after each instruction is executed. The first three bits are used to form an interrupt priority level. They are interpreted as a number from 0 through to 7. When set to a given level N, all interrupts not greater than N are ignored. More information will be given on interrupt levels in the next section.

Exception Processing

Interrupts, processor errors, and user traps are all basically handled by the same mechanism on 68000 series processors. The processors use a 1K region of memory to hold 256, 4-byte exception vectors. Each exception that the processor recognizes is associated with one of these exception vectors. Each exception vector holds the address of a routine that will be invoked when the exception occurs. On the 68000 processor this 1K region of memory starts at location 0 and extends upwards in memory. On the 68010 and later processors there is a special register, called the vector base register (vbr), that contains the address of the first element of the exception vector table.

The invocation of an exception handler is similar to a C function call. In fact, C routines can be used as exception handlers as long as the programmer is aware of the differences between invocation as an exception handler and invocation as a C function. The first difference is that all exception handling code is executed in supervisor mode. The second difference is that the stack format established for an exception handler is not like that established for a function. When an exception occurs, the processor switches to supervisor mode and pushes the status register and then the program counter onto the stack. This differs from a function call where only a return address is pushed onto the stack. Because of this difference, the return from an exception handler cannot be achieved via the normal ``return from subroutine'' instruction (rts). Instead, the ``return from exception'' instruction (rte) must be used.

To accommodate these differences, Xinu uses assembler stubs (called interrupt dispatchers ) for its exception handlers. These stubs usually do no more than set up the stack appropriately and call a C language routine. When control returns from the C routine, they execute an rte instruction to complete exception processing.

It was noted above that the status register contains three bits that are used to form an interrupt level number. When an external device signals an interrupt to the processor, it provides both an exception number (an offset into the exception vector table) and an exception level (from 0 through to 7). The level of the interrupt is compared with the interrupt mask from the status register, and if the interrupt level is not greater, it is not acknowledged until the interrupt mask is lowered. Additionally, when an interrupt of a given level occurs, the interrupt level mask is set to the level of the interrupt so that lower priority interrupts cannot occur. Of course, the interrupt mask can be manipulated directly by altering the value of the interrupt mask bits in the status register. This, in fact, is what Xinu's disable() and enable() routines do. The disable() routine stores the value of the status register in a location provided by the caller, and then sets the interrupt level mask to 7. The enable() routine restores the status register from the location provided by the caller (presumably the same one as used in the disable() call).

Note: The disable() and restore() routines for the version of Xinu that you are using are different from those used in Comer's book. Specifically, the parameter to each routine is simply a short in the book, while you must declare the parameter to the routines to be a STATWORD.

As a final note, both the 68010 and the 68020 support memory management hardware, and have mechanisms for dealing with page faults. The handling of these exceptions is more complicated than outlined above because the processor may be in the middle of an instruction when the fault occurs and it needs to be capable of restarting that instruction. This requirement means that a great deal of state information needs to be pushed on the stack when a page fault occurs. The stack format for exceptions is thus not as simple as in the explanation given above. Refer to a text on the 68010 or 68020 for more information about exception handling on these processors.

Calling Sequences

To comprehend assembler routines that are called from C language routines, or that call C language routines themselves, you need to understand the calling sequence conventions used by the C compiler. The C compiler which you use (xcc) is nothing more than a specialized version of the standard GCC C compiler. You can gain some understanding of the type of code the compiler generates by using the -S option of the compiler. For example:
     xcc -S test.c
causes the compiler to generate assembly code for your program (putting it in test.s) rather than generating an executable.

The compiler passes arguments to a subroutine on the stack. These arguments are pushed onto the stack in reverse order before the function call is made. For example, consider the following C fragment:

  main()
  {
    int a, b, c;

    foo(a, b, c);
  }

  int foo(x, y, z)
  int x, y, z;
  {
    int d, e;

    d = x + z + e;
    return(e);
  }
The call to the function foo() generates the code shown below:
        movl    a6@(-0xc),sp@-
        movl    a6@(-0x8),sp@-
        movl    a6@(-0x4),sp@-
        jbsr    _foo
        lea     sp@(0xc),sp
        rts
The instruction movl a6@(-0xc),sp@- pushes the value of the variable c onto the stack. Similarly, movl a6@(-0x8),sp@- pushes the value of the variable b onto the stack. The function is invoked with the instruction jbsr _foo (note that all C names are prepended with an underscore in assembler). The instruction lea sp@(0xc),sp is an obscure (though efficient) mechanism for adding the (hexadecimal) value 0xc to the stack pointer, effectively popping off the arguments that were pushed onto the stack when foo() was called.

For the above code to make much sense, you'll have to be able to make sense of the various addressing modes of the 68000. An instruction like movl a6@(-0xc),sp@- means take the value in address register 6, add the constant -0xc (-12), fetch the contents of the memory address which results from this computation, place them in the memory location referred to by the stack pointer, and then decrement the value of the stack pointer by 4. In general, the symbol @ means to take a value and refer to the memory location indicated by that value. Values in parentheses are offsets that are to be added to registers before dereferencing occurs. Trailing minus or plus signs indicate that the value of a register is to be decremented or incremented by a certain amount as a side effect of the instruction.

An interesting point about the above example is that even though parameters are passed on the stack (recall that the stack pointer is a7), the local variables are referred to via offsets from register a6. The C compiler that you use for your Xinu programs uses register a6 as a ``frame pointer''. This frame pointer provides a convenient pointer into the stack from which a routine's local variables and parameters may be accessed. The format of this stack frame is as shown below:

          +-----------------------+
    SP    |                       |
          +-----------------------+
  (-n*4)  |   local variable n    |
          +-----------------------+
          .                       .
          .                       .
          .                       .
          +-----------------------+
   (-8)   |   local variable 2    |
          +-----------------------+
   (-4)   |   local variable 1    |
          +-----------------------+
    A6    |       old A6          |
          +-----------------------+
   (+4)   |   return address      |
          +-----------------------+
   (+8)   |     parameter 1       |
          +-----------------------+
   (+12)  |     parameter 2       |
          +-----------------------+
          .                       .
          .                       .
          .                       .
          +-----------------------+
 (+n*4+4) |     parameter n       |
          +-----------------------+
At any given time during the execution of a process, its stack consists of a series of these stack frames piled end to end, with the sp pointing just under the local variables of the ``active'' stack frame. The active stack frame belongs to the procedure which the process is currently executing. Thus, the active stack frame contains the parameters to, and local variables of, the procedure currently being executed by a given process. The stack frame above the active stack frame belongs to the caller of the active procedure, and so on.

Maintaining register a6 so that these relationships hold is achieved via a special ``prologue'' of assembler instructions prepended to each routine. Consider the code that is generated for the routine foo() of the C code given above:

        .globl  _foo
_foo:
        link    a6,#0
        addl    #-LF24,sp
        moveml  #LS24,sp@
        movl    a6@(0x8),d0
        addl    a6@(0x10),d0
        addl    a6@(-0x8),d0
        movl    d0,a6@(-0x4)
        movl    a6@(-0x8),d0
        jra     LE24
LE24:
        unlk    a6
        rts
        LF24 = 8
        LS24 = 0x0
The instruction link a6,#0 takes the value of a6 and stores it in the memory location referred to by the stack pointer. The value of the stack pointer is then copied into a6, and then the stack pointer is decremented by 4. The instruction addl #-LF24,sp decrements the stack pointer by 8 (since that is the value of the symbol LF24), which allocates space on the stack for 8 bytes worth of local variables. At the end of the routine, the instruction unlk a6 reverses the above procedure, restoring the old value of a6 and setting the stack pointer to the value it had at entry into the routine.

This information should be sufficient to allow you to interface assembler and C routines to each other, and will help shed light on what the various assembler routines that constitute a portion of Xinu, are doing.

I/O Structure of Xinu on the Suns

This section describes the structure of the Xinu system's device management routines as they are implemented on the Sun 2 and Sun 3. The emphasis will be on those elements of the system that differ from the Xinu implementation that runs on PDP-11s and is described in Comer's book. Very little information will be given about specific device drivers; only the underlying device management system will be dealt with.

Device Management

Xinu attempts to provide a uniform device management system, that is, all devices that the system utilizes are handled via the same mechanisms. Devices are divided into two classes: real devices and pseudo-devices. Real devices are those associated with actual hardware devices; consequently they require interrupt handlers. Pseudo-devices are software implemented devices. For example, in Xinu the remote file system is implemented by a pseudo-device (the implementation makes use of the ethernet device, which is a real device, to communicate with the remote file server).

Almost all the information relating to the structure of the device management system is contained in the file Configuration which resides in the same directory as all the Xinu source files. This file lists all the devices that constitute the Xinu device subsystem, and indicates which routines correspond to the basic device operations (open, close, read, write, etc.), where the device's control register is, and which interrupt vector it uses. To add or remove devices from the Xinu system, all that is required is to write the various routines to handle the basic device operations, write an interrupt handler if it is a real device, and add a description of the device to the configuration file. In the same directory as the configuration file, there is a program called config that reads the configuration file and produces two other files: conf.c and conf.h. Once these new files have been generated, Xinu can be recompiled and will recognize the new device.

This structure is intended to make it very simple to alter the configuration of the Xinu system, and to isolate hardware dependencies. Unfortunately, the version of Xinu we are actually running has been hacked to a certain degree, and in various places the values defined in the configuration file are not used. For example, in the file ttyinit.c which contains the code to initialize the tty device, you can see that the constant SVECTOR is used in a call to the routine set_evec() which is used to give a value to an entry in the processor's exception vector table. What should actually be used here is the vector number from the device structure, as SVECTOR is a constant defined in the file slu.h. If an individual attempted to reconfigure the tty port Xinu used via a change to the configuration file, he would would probably be somewhat discouraged to find that his attempts were in vain because of the incorrect manner in which the interrupt vector is set up in ttyinit.c. As we have time we will attempt to remove some of these bugs, and we would appreciate your help in spotting any more.

Given the above description of the configuration file, you might have a guess at how Xinu handles the basic device operations. When for instance, you do an open() call on a given device you pass a device number to Xinu's open() routine. The open routine uses this number to index into a table of device descriptors, from which it obtains the address of the open routine for that specific device (the table of device descriptors is declared and initialized in the file conf.c, which you will recall was generated by the config program). It then calls that routine, passing it whatever additional parameters that you provided. Initialization of devices works by a similar mechanism. In the configuration file, each device has an initialization routine associated with it. When the system is started up, one of the first things that it does is to scan through the device descriptor table and call all the initialization routines found there. When the initialization routine is called, it receives as a parameter its entry from the device descriptor table. From this entry it can obtain information such as the location of the control register for the device, and which interrupt vector it is to use.

This brings us to the last piece of information regarding the device management system in Xinu: interrupt processing. As was just noted, each device initialization routine is provided (indirectly) with the interrupt vector that is to be used for that device. All the initialization routine has to do is call the routine set_evec() which will place a pointer to a given routine into the indicated entry of the exception vector table. Once this has taken place, any interrupt that occurs will result in that routine being called. Typically, these interrupt service routines are just short assembler language stubs which call C language routines which do the real work. The reason that the assembler stubs are required is that while a C language routine terminates with a ``return from subroutine'' instruction, an interrupt handler must terminate with the ``return from exception'' instruction. In addition, the assembler routine may want to preprocess the interrupt to a certain degree.

A short note should be made at this point of the difference between the version of Xinu you are using (Version 7.0) and the version discussed in Comer's book (Version 6.0). The older Xinu version used a statically initialized vector table. The static definitions for the table were found in the file lowcore.s. Since the vector table in the newer version of Xinu is dynamically initialized, the file lowcore.s no longer exists. It would not, therefore, be worthwhile to spend much time looking for it.

To gain a more complete understanding of the Xinu device management system, look at the configuration file, some device initialization routines, and some of the code that handles a particular device call, such as write(). Once you have done this you should have a good feel for the manner in which the various components of the Xinu system interact with one another.

Serial Communication Hardware

The serial communication hardware on the Sun 2 or Sun 3 machines that you will be using is not radically different from the serial communication hardware described in Comer's book. The differences between what is described in the text and what you will be using in the course can be divided into two general categories:
  1. Those related to interrupt handling, and
  2. Those related to the particular serial communication chip used.

The Zilog 8530 SCC

The Sun 2 uses a Zilog 8530 SCC (Serial Communications Controller) to perform serial I/O. This chip is considerably more advanced than the one described in Comer; in addition to functioning as a typical UART (Universal Asynchronous Receiver/Transmitter), it can also act as an SDLC (Synchronous Data Link Control) controller, or a timer. Since Xinu uses the 8530 simply as a UART, no discussion of features related to SDLC or timers is required. As a result of the chip's increased complexity, it has a fairly large number of registers which are used to control its various operational parameters. To complicate matters further, the chip is actually a dual controller; that is, it contains two more-or-less independent serial controllers (the degree to which the two sides are not independent will be detailed later in the document).

Each half of the 8530 logically contains 16 registers, but these registers are not mapped onto 16 memory locations in the Sun 2. Instead, each half has only 2 memory-mapped registers. The first of these is referred to as the control register, while the second is referred to as the data register. Accessing one of the chip's logical registers is a two step process:

  1. Writing the number of the desired register into the control register informs the SCC of a pending access to the given register, and
  2. A subsequent access to the control register has the effect of accessing the indicated register.
Not all registers may be both read and written. When the various registers are described in the following section, their read/write capability will be noted. The second memory-mapped register for each half is the data register, which is where received characters will appear, and where characters to be transmitted are placed. A template for the memory mapped registers can be found in the file zsreg.h in the standard Xinu include directory. The template is shown in the following figure:
     struct zscc_device
       {
       unsigned char zscc_control;
       unsigned char :8;                 /* pad byte */
       unsigned char zscc_data;
       unsigned char :8;                 /* pad byte */
       };
The memory locations to which the 8530 registers are mapped, are found in the configuration file. Hardware dependent values can also be found in the file slu.h. In the latter file, the address of only one half of the 8530 is provided (it is the preprocessor constant SERIAL0_BASE). This half of the 8530 is externally connected to serial port A on the Sun 2. Both files also contain values for the interrupt vector offset used by the 8530.

Zilog 8530 Register Descriptions

This description of the 8530's registers is incomplete. Only those registers relevant to using the 8530 as a simple UART are detailed. Furthermore, this discussion assumes that the 8530 has been initialized (Xinu performs this initialization for you so this should not cause any problems -- see routine ttyinit() in file ttyinit.c).
NOTE: The symbolic names given for each bit are located in the file zsreg.h.

The layout for register 0 (for a read operation) is shown in the following table. This register provides the status information that is required for using the 8530 in a polled (as opposed to interrupt driven) environment.

     Bit      Meaning
      0       ZSRR0_RX_READY, if 1 indicates that a received
              character is available
      1       Not Used
      2       ZSRR0_TX_READY, if 1 indicates that the device
              is ready to transmit a character
      3-6     Not Used
      7       ZSRR0_BREAK, if 1 indicates that a break has
              been detected

  Register 0 (read)
On writes, register 0 behaves a little differently than most of the SCC control registers, which can, like register 0, be regarded simply as an array of bits. Instead, a number of commands can be written into the register. A subset of these commands are given in the following table:
    ZSWR0_RESET_ERRORS (0x30)  -- reset the error flags
    ZSWR0_RESET_STATUS (0x10)  -- reset the status bits
    ZSWR0_CLR_INTR     (0x38)  -- acknowledge interrupt

  Register 0 (write)
A further interesting point is that the control register of the SCC is, in fact, write register 0. That is, to issue one of the above commands all you need do is write the given command into the SCC control register. Commands 0x00 through 0x0f simply tell the SCC to prepare to read/write one of its internal registers on the next access to the control register.

The layout for register 1 (for read operations) is given in the next table. This register provides information on device errors. Note that these error bits are latched and must be cleared by issuing the ZSWR0_RESET_ERRORS command.

     Bit      Meaning
      0-3     Not Used
      4       ZSRR1_PE, if 1 indicates a parity error
      5       ZSRR1_DO, if 1 indicates a data overrun
      6       ZSRR1_FE, if 1 indicates a framing error

  Register 1 (read)
On write operations, register 1 allows control over the enabling and disabling of interrupts. The layout of this register is shown in the following table:
     Bit      Meaning
      0       ZSWR1_SIE, if 1 enable interrupts when the
              status of the device changes
      1       ZSWR1_TIE, if 1 enable transmitter interrupts
      2-3     Not Used
      4       ZSWR1_RIE, if 1 enable receiver interrupts
      5-7     Not Used

  Register 1 (write)
Register 2 (for both read and write operations) contains the interrupt vector offset for the device. Note that the device generates only one interrupt even though there are numerous conditions that trigger the generation of an interrupt. Read register 3 contains status bits that allow the cause of an SCC interrupt to be determined.

Register 3 (for read operations) contains status information pertaining to interrupts. Its layout is given in the next table.

     Bit      Meaning
      0       ZSRR3_IP_B_STAT, if 1 indicates that a status
              interrupt is pending on channel B
      1       ZSRR3_IP_B_TX, if 1 indicates that a transmitter
              interrupt is pending on channel B
      2       ZSRR3_IP_B_RX, if 1 indicates that a receiver
              interrupt is pending on channel B
      3       ZSRR3_IP_A_STAT, this and the following 2
              bits serve the same function as bits 0-2
              but for channel A rather than B
      4       ZSRR3_IP_A_TX
      5       ZSRR3_IP_A_RX
      6-7     Not Used

  Register 3 (read)
This register is a bit odd in that it contains information about both devices in the SCC. Furthermore, it can only be accessed from the A device's control register. This means using the B device from within Xinu would require something of a hack because the B device's control block would have to contain a pointer to the A device's control block in order for it to be able to access this register.