
There are two types of I/O objects in Scheme 48, channels and ports.
Channels are the raw, unbuffered ports of the operating system.  The
only I/O operations the VM supports for channels are block reads and
writes.  Ports are the actual Scheme ports and are implemented in Scheme,
with some support from the VM for READ-CHAR, PEEK-CHAR, and WRITE-CHAR
for efficiency.  The run-time system provides ports that are buffered
versions of channels.  Other sorts of ports are in big/more-port.scm.

Source files:

  rts/port.scm            port operations and port handlers
  rts/current-port.scm    current-input-port, etc.
  rts/channel.scm         blocking on channels and handling i/o interrupts
  rts/channel-port.scm    ports that read and write to channels
  rts/low.scm             CHANNEL-READ and CHANNEL-WRITE
  big/more-port.scm       additional kinds of ports
  vm/arch.scm             fields of ports and channels
  vm/prim-io.scm          VM i/o opcodes
  vm/vmio.scm             implementation of channels
  
----------------------------------------------------------------

CHANNELS

The VM instructions that deal with channels are:

 (OPEN-CHANNEL <spec> <mode>) -> channel
   <mode> is a from the enumeration OPEN-CHANNEL-OPTION in arch.scm.
   <spec> is either a filename (as a string) or an OS port (as a one-word
   code-vector), depending on the mode.

 (CLOSE-CHANNEL <channel>)    -> unspecific

 (CHANNEL-MAYBE-READ <string-or-code-vector> <start-index> <count> <wait?>
                     <channel>)
                              -> number of bytes read or the eof-object
 (CHANNEL-MAYBE-WRITE <string-or-code-vector> <start-index> <count> <channel>)
                              -> number of bytes written
   These read or write up to the specified number of characters or bytes
   from or to the string or code-vector, with the first character or byte
   going at <start-index>.

 (CHANNEL-ABORT <channel>)    -> number of bytes read or written or
                                 the eof-object
   This aborts any pending read or write operation on the channel.  The return
   value reflects any partial completion.

CHANNEL-MAYBE-READ and CHANNEL-MAYBE-WRITE do not block.  If the read or
write cannot be completed immediately a PENDING-CHANNEL-I/O exception is
raised.  It is then up to the run-time system to either wait or run some
other thread.  The VM raises an I/O-COMPLETION interrupt whenever an i/o
operation completes.

Because CHANNEL-MAYBE-READ and CHANNEL-MAYBE-WRITE are awkward to use,
the RTS defines somewhat simpler versions:

 (CHANNEL-READ <buffer> <start> <needed> <channel>)
                              -> number of bytes read or the eof-object
 (CHANNEL-WRITE <buffer> <start> <count> <channel>)
                              -> unspecified
   <Buffer> is either a string or code vector and <start> is the index of the
   first character read or written. <Needed> is one of:
     N > 0 : the call returns when this many characters has been read or
        an EOF is reached.
     'IMMEDIATE : the call reads as many characters as are available and
        returns immediately.
     'ANY : the call returns as soon as at least one character has been read
        or an EOF is reached.
   <Count> is the number of characters to be written.  CHANNEL-READ will read
   the requested number of characters unless an EOF is reached.  CHANNEL-WRITE
   will write the requested number of characters.

----------------------------------------------------------------

PORTS

Ports are actual Scheme port and are (usually) buffered.  They are fully
exposed to the run-time system.  The VM instructions on ports could be
implemented in Scheme; they are in the VM for efficiency.  Buffers are
code-vectors (this is a micro-hack; strings have a slightly higher overhead
because of the null terminating byte for C compatibility) (code-vectors are
just vectors of bytes).

The fields of a port are:
 
 PORT-STATUS: a bit set represented as a fixnum.
  Indices into this bit set are from the PORT-STATUS-OPTIONS
  enumeration in arch.scm.  The current bits are: input, output,
  open-for-input, open-for-output (the last two are for things like
  sockets, on which you need to block but which do not support
  normal reading or writing).

 PORT-HANDLER: a record containing three procedures.  These handle
  printing the port, closing the port, and filling (for input ports)
  or emptying (for output ports) buffers.

 PORT-DATA: ?
  Whatever stuff the handler needs.

 PORT-LOCKED?, PORT-LOCK: used by the system to guarentee the atomicity
  of i/o operations.

 PORT-BUFFER: a code-vector.  The input or output buffer of the port.

 PORT-INDEX: a fixnum.  The index of the next byte to read or written.

 PORT-LIMIT: a fixnum.  One past the end of the valid/available buffer space.

 PORT-PENDING-EOF?: true if the next read to this port should return EOF.

Additional operations on ports:

 (READ-BLOCK string-or-code-vector start count input-port)
    Read COUNT bytes into STRING-OR-CODE-VECTOR starting at index START.
    Returns the number of bytes read.  Only an end-of-file will prevent
    the requested number of bytes from being read.
 
 (WRITE-STRING string output-port)
    Write the characters in the string to the port.

 (WRITE-BLOCK string-or-code-vector start count output-port)
    The output counterpart to READ-BLOCK.  This always writes out the
    requested number of bytes.  Its return value is unspecified.

 (FORCE_OUTPUT output-port)
    Causes any buffered characters to be written out.
    
 (CURRENT-ERROR-PORT)
    The current error port, analogous to Scheme's CURRENT-INPUT-PORT
    and CURRENT-OUTPUT-PORT.

The system maintains a list of output ports whose buffers should be
periodically flushed.  The default output port and ports made by
OPEN-OUTPUT-FILE are on this list.  (PERIODICALLY-FORCE-OUTPUT! <output-port>)
may be used to add others.

----------------------------------------------------------------

PORT HANDLERS

Every port has a handler with three procedures.  The first two are used
for printing and closing ports and have the same type for all ports:
 
 (DISCLOSE port-data) -> disclose list
 (CLOSE port-data) -> unspecific

For CLOSE, The system takes care of modifying the port's status.

The third procedure is used to fill and empty buffers.  Its arguments
and return values depend on the kind of port:

 Buffered output ports:
  (BUFFER-PROC port-data buffer start-index byte-count) -> unspecific
    BYTE-COUNT bytes should be copied from the buffer beginning at
    START-INDEX.  The buffer may be either a string or a code-vector.

 Unbuffered output ports:
  (BUFFER-PROC port-data char) -> unspecific
    Write out the given character.  The system uses this for the default
    error port.

 Input ports:
  (BUFFER-PROC data buffer start-index needed-bytes)
     -> EOF or number of bytes read (before an EOF)
    Bytes should be copied into the buffer starting at START-INDEX.  The
    buffer may be either a string or a code-vector.  NEEDED-BYTES is one of:

      'IMMEDIATE
        The call should return immediately after transfering whatever number
        of bytes are currently available, possibly none (this is used for
	CHAR-READY?). The maximum number of characters is determined by the
	length of BUFFER.

      'ANY
        The call should wait until at least one byte is available or an EOF
        occurs (used for READ-CHAR and PEEK-CHAR).  The maximum number of
	characters is determined by the length of BUFFER.

      N > 0
        The call should wait until N bytes have been copied into the buffer
        or an EOF occurs.  If the return value is less than NEEDED-BYTES the
        port code inserts an EOF after the last byte.

----------------------------------------------------------------

Ports and the Virtual Machine

Ports could be implemented entirely in Scheme, with no support from
the VM.  For efficiency reasons VM instructions are supplied for
three port operations:

  (READ-CHAR <port>)
  (PEEK-CHAR <port>)
  (WRITE-CHAR <char> <port>)

For each of these, if there is sufficient data or space in the
appropriate buffer the VM performs the operation.  Otherwise a
buffer-full/empty exception is raised and the exception handler
uses the buffer procedure from the port's handler to fill or
empty the buffer.
