This section provides details of the design and operation of the Input/Output division of the Runtime Support Package (RSP/IO). While the design itself is processor and hardware independent, it is intended to be realized in native code. Thus the final product will be processor-specific but still independent of the exact peripherals used.
PROCEDURE UNITREAD(UNIT_NUMBER: INTEGER; VAR DATA_AREA: PACKED ARRAY [0..BYTESTOTRANSFER-1] OF 0..255; BYTES_TO_TRANSFER: INTEGER; [ LOGICAL_BLOCK: INTEGERS; [ CONTROL: INTEGER ]] ); PROCEDURE UNITWRITE(UNIT_NUMBER: INTEGER; VAR DATA_AREA: PACKED ARRAY [0..BYTESTOTRANSFER-1] OF 0..255; BYTES_TO_TRANSFER: INTEGER; [ LOGICAL_BLOCK: INTEGERS; [ CONTROL: INTEGER ]] );
Starting with the 2.0 release level, byte addresses such as that described above are represented as an address couple, consisting of a word base address and a byte offset. On processors which use byte addressing, the effective address is computed by simply adding the base and offset, since both quantities are in bytes. For processors using word addressing, the effective address is computed by indexing byte-wise from the base address (always toward higher locations).
Note: For release Level I systems, the data area address is represented by a single word, i.e. by a simple byte address rather than an address couple.
The third item in the READ or WRITE parameter list, BYTES_TO_TRANSFER, contains the number of bytes to move between the user's data area and the physical unit.
Two optional parameters follow for UNITREAD and UNITWRITE: LOGICAL_BLOCK and CONTROL. If not specified by the Pascal programmer, the compiler will assign them both the default value zero. LOGICAL_BLOCK is only relevant for block structured units; as discussed in section 2.3, it specifies the Pascal logical block to be accessed. The CONTROL word has been discussed in section 2.1.2.
++++ |///////////////| <- - - - (when finished, SP +---------------+ points here) | Unit Number | +---------------+ | Word Base | +---------------+ | Byte Offset | +---------------+ (The stack shown here | Byte Count | grows down) +---------------+ | Block Number | +---------------+ | Control | <- - - - SP ---- +---------------+Figure 3.0 — Stack state on entering UNITREAD or UNITWRITE
Like ordinary Pascal procedures, these RSP routines pop their parameters from the stack when they are finished.
FUNCTION UNITBUSY(UNIT_NUMBER: INTEGER): BOOLEAN;
On implementations supporting asynchronous I/O, this function tests whether the specified unit is busy or not and returns the boolean result as the function value. On the totally synchronous system that we are describing, UNITBUSY should always return false. Figure 3.1 illustrates the stack states before and after calling UNITBUSY; notice that the stack pointer does not change.
++++ |///////////////| |///////////////| +---------------+ +---------------+ | Unit number | <---- SP ----> | false (0) | +---------------+ +---------------+ before afterFigure 3.1 — Stack state before and after UNITBUSY
PROCEDURE UNITWAIT(UNIT_NUMBER: INTEGER);
Like UNITBUSY, UNITWAIT is only useful in an asynchronous environment. It is intended to kill time until the designated unit becomes not busy. In a synchronous system, UNITWAIT is essentially a no-op since no unit should be busy unless a read or write request is pending. The single parameter is on top of stack when the procedure is called and is popped off before the procedure returns. The use of the stack is illustrated in Figure 3.2.
++++ |///////////////| SP ----> |///////////////| +---------------+ +---------------+ | Unit number | <---- SP +---------------+ before afterFigure 3.2 — Stack state before and after UNITWAIT
PROCEDURE UNITCLEAR(UNIT_NUMBER: INTEGER; UINITPTR: ^UIR);
The purpose of UNITCLEAR is implied by its name: it restores the specified unit to its “initial” state. In an asynchronous system, this implies cancelling any pending I/O operations. In the synchronous environment with which we are concerned here, it is useful for initializing the RSP and BIOS routines concerned with that unit. The two parameters, UNIT_NUMBER and UINITPTR are, respectively, the number of the unit to be initialized and a pointer to a Unit Initialization Record (UIR) containing unit-specific initialization values. The structure of the UIR may vary depending on the type of unit.
If the value of UINITPTR is nil then RSP/IO must provide a pointer to a default UIR. The pointer is then passed to the BIOS. A Pascal representation of the UIR structure is shown in Figure 3.3. This corresponds to a 28-byte physical structure. The structures of the various cases are diagrammed In Figure 3.4. An area of six bytes has been reserved for future use. Note that RSP/IO must set UBREAKVECTOR (in the case UNITKIND = UCONSOLE) regardless of the contents of that field assigned by the Pascal system. In practice, the Pascal programmer will leave UBREAKVECTOR uninitialized, knowing that only the interpreter knows the address of the BREAK-handling subroutine.
Correct interpretation of this Pascal representation requires the knowledge that, in UCSD's implementation, the values used to represent the values of variables of scalar types such as unit_types are zero-based starting from the left. Thus a variable of type unit_types having the value uconsole actually has the value zero, one means uprinter, two means uremote and three means ublocked. The implementation is similar for all other scalar types.
type unit_types = (uconsole, uprinter, uremote, ublocked); baud_types = (b_11O, b_300, b_600, b_1200, b_2400, b_4800, b_9600, b_19200, b_autosense, b_other); parity_types = (p_even, p_odd, p_none); stp_bit_types = (s_one, s_oneandhalf, s_two); uir = record case UNITKIND: unit_types of uconsole, uprmnter, uremote: (UDATABITS: integer; USTOPBITS: stp_bit_types UBAUDRATE: baud_types; UPARITY: parity_types; USPECIAL: integer; (* Used with b_other *) (* Future use area *) UFUTURE: array [0..2] of integer;
case UNITKIND of uconsole: (USTARTSTOP: char; UFLUSH: char; UBREAK: char; UALPHALOCK: char; UBREAKVECTOR: ^integer); uprinter: (USTARTSTOP: char; UFLUSH: char; UPAGELINES: integer) ); (* ublocked needs none *) end;Figure 3.3 — Sample UIR Declaration
28 | |||
26 | UBREAKVECTOR | ||
24 | UALPHALOCK | ||
22 | UBREAK | UPAGELINES | |
20 | UFLUSH | ||
18 | USTARTSTOP | ||
16 | reserved for future use | ||
14 | |||
12 | |||
10 | USPECIAL | ||
8 | UPARITY | ||
6 | UBAUDRATE | ||
4 | USTOPBITS | ||
2 | UDATABITS | ||
0 | UNITKIND | ||
uconsole | uprinter | ublocked | |
Figure 3.4 — UIR Physical Structure |
When RSP/IO is passed a nil UINITPTR, it should provide the BIOS with default UIR's having the values shown in Table 3.0.
console | UNITKIND = 0 | (* uconsole *) |
UDATABITS = 8 | (* eight *) | |
USTOPBITS = 1 | (* one and a half *) | |
UBAUDRATE = 6 | (* b_9600 *) | |
UPARITY = 2 | (* p_none *) | |
USPECIAL = 0 | (* not needed *) | |
USTARTSTOP = 19 | (* DC3 *) | |
UFLUSH 6 (* ACK *) | ||
UBREAK = 0 (* NUL *) | ||
UALPHALOCK 18 (* 0C2 *) | ||
UBREAKVECTOR = Address of Break Subroutine | ||
printer | UNITKIND = 1 | (* uprinter *) |
UDATABITS = 8 | ||
USTOPBITS = 1 | ||
UBAUDRATE = 1 | (* b_300 *) | |
UPARITY = 2 | ||
USPECIAL = 0 | (* not needed *) | |
USTARTSTOP 19 | (* DC3 *) | |
UFLUSH = 6 | (* ACK *) | |
UPAGELINES = 58 | (* 11 in., 6 lpi, 4-line margins *) | |
remote | UNITKIND = 2 | (* uremote *) |
UDATABITS = 8 | ||
USTOPBITS = 1 | ||
UBAUDRATE = 6 | ||
UPARITY = 2 | ||
USPECIAL = 0 | (* not needed *) | |
disk | UNITKIND = 3 | (* ublocked *) |
Table 3.0 — Default UIR Values |
Our real intention in providing this program is to provide a precise specification of the RSP/IO requirements to those who must implement it in native code. It is possible to translate the Pascal into assembly language and produce an implementation that is quite efficient if the implementor is not too literal-minded.