3.0 The Interpreter Level: RSP/IO


p.10 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.

3.1 Calling Mechanisms

Here are the details of how each routine in RSP/IO is called from the Pascal level. The level of detail is intended to be such that an implementor of RSP will know how to get parameters off the stack when RSP is called and how the stack should look when RSP returns. The detailed semantics of each routine are discussed in section 3.2.

3.1.1 UNITREAD and UNITWRITE

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 ]]
                   );

3.1.1.1 Parameter Description

UNIT_NUMBER has been discussed in section 2.1.1. DATA_AREA is the user's buffer to or from which the data will be transferred. Describing it. as a VAR parameter signifies that UNITREAD and UNITWRITE are passed a pointer to the start of the data area. The Pascal programmer will call, say, UNITWRITE with an array element, for example A[2], as the actual parameter. Thus the procedure is provided with the starting address for the transfer. For byte-oriented units, the address of the start of the data area may or may not be on a word (16 bit) boundary. In the case of block-structured (disk) units, however, it is only defined in the case that it is on a word boundary; that is, a Pascal programmer must not allow actual parameters which reference non word-aligned bytes to occur when transferring to/from the disk. This is to avoid restricting block-structured units to byte-by-byte transfers.

p.11 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.

3.1.1.2 Parameter Stack Format

p.12 UNITREAD and UNITWRITE receive their parameters on the evaluation stack in the following order (each box represents a 16-bit quantity):
++++  |///////////////| <- - - - (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.

3.1.2 UNITBUSY

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.

p.13

++++ |///////////////|                |///////////////|
     +---------------+                +---------------+
     |  Unit number  | <---- SP ----> |   false (0)   |
     +---------------+                +---------------+
          before                            after

Figure 3.1 — Stack state before and after UNITBUSY

3.1.3 UNITWAIT

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                            after

Figure 3.2 — Stack state before and after UNITWAIT

3.1.4 UNITCLEAR

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.

p.14 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;
p.15
          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
26UBREAKVECTOR
24UALPHALOCK
22UBREAK UPAGELINES
20UFLUSH
18USTARTSTOP
16reserved for future use
14
12
10USPECIAL
8UPARITY
6UBAUDRATE
4USTOPBITS
2UDATABITS
0UNITKIND
uconsoleuprinter 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.

p.16

consoleUNITKIND = 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
printerUNITKIND = 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 *)
remoteUNITKIND = 2(* uremote *)
UDATABITS = 8
USTOPBITS = 1
UBAUDRATE = 6
UPARITY = 2
USPECIAL = 0(* not needed *)
diskUNITKIND = 3(* ublocked *)

Table 3.0 — Default UIR Values

3.2 Semantics

This section will detail the processing to be performed by RSP/IO. The primary function of RSP/IO is to manage calls to BIOS. In the case of disk I/O, for example, RSP does little except call BIOS to do all the work. Secondarily, RSP/IO is responsible for handling certain special functions which shall be described here. Appendix A contains a Pascal realization of RSP/IO which should be considered the most precise reference for the semantics.

3.2.1 Special Character Handling on Output

p.17 Output to the printer, console or remote units must be massaged to properly handle Blank Compression Codes and CR's.

3.2.1.1 Blank Compression Code (DLE's)

The UCSD Pascal System supports text files containing a two-byte blank compression code. It is the responsibility of RSP/IO to decode the blank compression code and send an appropriate number of blanks. The first byte is an ASCII DLE (decimal 16) which signals that the next byte should be interpreted as being (the number of blanks to be sent)+32. Thus the next byte following the OLE should be processed by subtracting 32 from its value and sending that number of blanks.

3.2.1.2 Carriage Return - Line Feed

Text files contain ASCII CR's (decimal 13) at the end of lines. We define this character as meaning "New Line", i.e. a carriage return followed by a line feed. Thus it is the responsibility of RSP/IO to send an ASCII LF (decimal 10) after sending each CR.

3.2.1.3 NOSPEC Bit in CONTROL Parameter

When this bit is set, the special handling accorded DLE's and CR's is shut off and they are sent out like other characters.

3.2.2 Special Character Handling on Input

There are several characters which will receive special treatment coming from the console in a complete implementation of this I/O system. All but one of them, however, are handled by the BIOS. The one which is handled in RSP/IO is the unit EOF character.

3.2.2.1 Unit EOF Character

p.18 The console EOF character, when received from the keyboard, printer or remote ports, signals that “end-of-file” has bean reached on that particular unit. Rather than being a fixed ASCII code, this is a “soft character”. That is, the exact character code which will be interpreted as “Console End-Of-File” may be changed during system execution by the Pascal user. Further discussion of the soft characters used by the I/O Subsystem may be found in section 4.4. The EOF character is in the SYSCOM data area and must be accessed by RSP/IO to determine what character to look for. When the EOF character is found in the input stream, the action to be taken depends somewhat upon which unit was referenced. If we are reading from unit 1 (CONSOLE), then a NUL (character code 0) is returned to the user's buffer instead of the EOF character. For all other units, the EOF character is put in the user's buffer. In either case, no further characters are transferred to the buffer; control immediately returns to the Pascal level. Further details are in Appendix A (procedure READBYTES).

3.2.2.2 BIOS Functions

Of the remaining special input characters, START/STOP, FLUSH, ALPHALOCK and BREAK, two (ALPHALOCK and BREAK) are used only for input from the console, not from the printer or remote ports. The other two (START/STOP and FLUSH) may be handled from both console and printer, but not from remote. They are handled by the BIOS and are described in section 4.5.1.4.

3.2.2.3 NOSPEC Bit in CONTROL Parameter

As in 3.2.1.3 above, when this bit is on, the special character handling performed by RSP/IO is turned off. This includes the EOF sensing function described above. It does not affect the BIOS functions.

3.3 Modelling RSP/IO Using Pascal

As the reader will notice in Appendix A, a Pascal program has been written which performs all special character handling required and calls BIOS with the specified parameters. While no version of the UCSD Pascal System has been implemented using this Pascal code for RSP/IO, it could be done.

p.19 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.


This page last regenerated Sun Jul 25 13:37:38 2010.