This Week With My Coleco ADAM 9709.26

by Richard F. Drushel (drushel@apk.net)

I. Administrivia.

My apologies that this issue of TWWMCA is some days late; work-related tasks have taken more time than expected this week. However, as promised, here is the first published version of the ADAMserve protocol. Comments are welcome.

II. The ADAMserve Protocol.

I list the version number at 4.0 because I have some comment references to a version 3.0 of the standards document. Unfortunately, I cannot find a copy of the 3.0 document, let alone 1.0 or 2.0. I may have uploaded 3.0 to Michael Hurst's BBS a few years ago; Michael, could you check for me?

So, I had to rewrite the standard from scratch, combining memory, intentions, and reference to the source code for the ADAM client and PC server software. It is usually bad news to let the implementation define the standard; but the implementations *were* written with that 3.0 standards document in front of me, so I'm pretty confident that it is a complete representation of the standard as it exists.


     *************************************************
     **                                             **
     **  ADAMserve Serially-Linked Device Protocol  **
     **                                             **
     **         for the Coleco ADAM computer        **
     **                                             **
     **   version 4.0  Friday, 26 September 1997    **
     **                                             **
     **    (c) 1994-1997 by Richard F. Drushel      **
     **                                             **
     **               drushel@apk.net               **
     **                                             **
     *************************************************

Disclaimer.

This is a working standards document. The ADAMserve protocol is subject to change as dictated by expediency and ease of implementation. At some point in the near future, however, the standard will be frozen, which will make it "safe" for others to write servers on non-PC hardware, based only upon this standards document.

Malediction.

The internal workings of ADAMserve concern only client and server software, not user applications. Anyone who writes a user application which depends upon the exact internals of the handshaking, etc., or on anything other than the public symbols which define the Standard Devices and Error Codes, is writing non-portable code which is liable to break in later versions of ADAMserve. Don't complain to me if this happens to you--you've been warned!

Overview.

ADAMserve is a protocol to allow an ADAM client to utilize the hardware resources of a second server computer, the two computers being connected by their serial ports. The client maps ADAMnet devices to server devices through a modified version of the EOS operating system. ADAMserve EOS implements ADAMnet emulation and logical-to-physical remapping so that user applications doing device I/O through EOS function calls can have transparent access to server devices. The server software waits for ADAMserve requests and processes them as received. ADAMserve is a handshaking protocol with modest error detection and recovery capabilities. These capabilities are in practice limited by the stringent need to keep the size of the client EOS operating system code less than 8192 bytes (8K).

Minimum Hardware and Software Requirements.

Standard ADAMserve Devices.

ADAMserve defines several standard (logical) devices. New devices may be added to the *end* of this list. Both client and server are free to map these logical names to any desired physical hardware. These are public global symbols.

FD0       EQU  0    ;floppy disk drive 0   A:               block
FD1       EQU  1    ;floppy disk drive 1   B:               block
HD0       EQU  2    ;hard drive 0          EOSHD0.DSK       block
HD1       EQU  3    ;hard drive 1          EOSHD1.DSK       block
SP0       EQU  4    ;serial port 0         COMx:            character
SP1       EQU  5    ;serial port 1         COMx:            character
PP0       EQU  6    ;parallel port 0       LPTx:            character
PP1       EQU  7    ;parallel port 1       LPTx:            character
CLOCK     EQU  8    ;real-time clock       BIOS clock       block
STATUS    EQU  9    ;status device         device summary   block
VIDEO     EQU  10   ;video graphics        video display    block
VT100     EQU  11   ;emulated VT100        video display    character
KYBD      EQU  12   ;alternate keyboard    server keyboard  character

The current version of the server software implements FD0, FD1, HD0, PP0, and PP1. PC disk drives can read/write ADAM floppy disks, so original ADAM disks may be used directly in A: (FD0) and B: (FD1). A 10 MB EOS hard drive partitioned into 10 logical drives is implemented as a virtual disk file, EOSHD0.DSK. PP0 emulates the bidirectional ADAM printer, while PP1 is just a standard parallel printer port. It is intended that the block structure of the CLOCK device be compatible with Chris Braymen's ADAMnet clock. The STATUS device is intended to return the current status of all server hardware in a single status block. The VIDEO device was reserved for the development of some high-level graphics display protocol (such as used by Prodigy) to allow client programs to display hi-res color graphics on the server monitor. The VT100 and KYBD devices are reserved for use by client telecommunications programs which need an 80x24 VT100 terminal for video display, and a keyboard with more function keys than are provided with the ADAM keyboard. It is intended that SP0 and SP1 will be fully interrupt- driven, ring-buffered serial ports which can be used with modems, terminals, and printers. Note that these are *not* the same as the server serial port used for the actual client-server link.

Standard ADAMserve Status and Error Codes.

ADAMserve defines several status and error codes which are used in handshaking. The error codes begin at 81 hex (for compatibility with ADAMnet DCB status codes). New error messages may be added to the *end* of this list. These are public global symbols.

ACK             EQU     5       ;positive acknowledgement token, ASCII 05H
NAK             EQU     21      ;negative acknowledgement token, ASCII 15H

;these are ADAMnet error codes

CHECKSUM_ERR    EQU     81H     ;bad checksum/CRC
INV_BLOCK_ERR   EQU     82H     ;bad block number
MISS_MEDIA_ERR  EQU     83H     ;missing media in disk drive
INV_DEV_ERR     EQU     84H     ;invalid/unimplemented device
WRITE_PROT_ERR  EQU     85H     ;disk is write-protected
DEVICE_ERR      EQU     86H     ;general device fault

;these are additional error codes

INV_COMMAND_ERR EQU     87H     ;invalid ADAMserve command
INV_BAUD_ERR    EQU     88H     ;invalid baudrate set attempt for SP0/SP1
INV_PDS_ERR     EQU     89H     ;invalid parity/data/stop bit set attempt
                                ;for SP0/SP1
INV_CLOCK_ERR   EQU     8AH     ;invalid date/time set attempt
PRN_OFFLINE_ERR EQU     8BH     ;printer is offline
PRN_NOPAPER_ERR EQU     8CH     ;printer is out of paper
PRN_NOPOWER_ERR EQU     8DH     ;printer is turned off
NO_CHAR_RDY_ERR EQU     8EH     ;server timed out expecting a character from
                                ;the client

ADAMserve Protocol.

All ADAMserve transactions are initiated by the client, through the use of a two-byte Request Header which is transmitted over the client-server serial link:

ADAM                                           SERVER
====                                           ======

[command] [device #] ----------------------------->

If the command is invalid (such as might occur if the client and server are out of synchronization due to a transmission error), the server responds by sending the INV_COMMAND_ERR code and then flushing its own link receive buffer (hopefully to clear it of garbage so that client and server can resynchronize).

The following commands are defined. These are public global symbols.

READ_CODE       EQU     "R"     ;read
WRITE_CODE      EQU     "W"     ;write
FORMAT_CODE     EQU     "F"     ;format
SETSERIAL_CODE  EQU     "S"     ;set SP0/SP1 serial port parameters

The device number is one of those defined in the list of Standard ADAMserve Devices. If the device number is invalid, the server responds by sending the INV_DEVICE_ERR code. Otherwise, based upon whether the device is a block device (such as a disk drive or hard drive) or a character device (such as a printer or serial port), the handshaking continues as illustrated below:

*****************************
**                         **
**  READ CHARACTER DEVICE  **
**                         **
*****************************


ADAM                                           SERVER
====                                           ======

R [char dev #]  ---------------------------------->


<------------------------------------------------- ACK (or INV_DEVICE_ERR
                                                           DEVICE_ERR)


ACK ---------------------------------------------->


<------------------------------------------------- char

<------------------------------------------------- 1's complement (char)


ACK ---------------------------------------------->
(or NAK if CPL(sent char)
not equal to sent 1's complement (char))



******************************
**                          **
**  WRITE CHARACTER DEVICE  **
**                          **
******************************


ADAM                                           SERVER
====                                           ======

W [char dev #]  ---------------------------------->


<------------------------------------------------- ACK (or INV_DEVICE_ERR)


char --------------------------------------------->

1's complement (char) ---------------------------->


<------------------------------------------------- ACK (or CHECKSUM_ERR
                                                           PRN_NOPAPER_ERR
                                                           PRN_OFFLINE_ERR
                                                           PRN_NOPOWER_ERR
                                                           DEVICE_ERR)



*************************
**                     **
**  READ BLOCK DEVICE  **
**                     **
*************************


ADAM                                           SERVER
====                                           ======

R [block dev #]  --------------------------------->


<------------------------------------------------- ACK (or INV_DEVICE_ERR
                                                           DEVICE_ERR)


[lo] [hi] [lo] [hi] block # ---------------------->
 loword     hiword


<------------------------------------------------- ACK (or INV_BLOCK_ERR)


ACK ---------------------------------------------->


                                                   [now server reads
                                                    physical media]


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                                          ;;
;;  NOTE:  the following handshake was in the original specifications, but  ;;
;;  is not currently implemented due to space limitations in EOS RAM.       ;;
;;  Without it, it is not possible to specifically trap hard read errors    ;;
;;  on the server side.  The current behavior in case of hard read errors   ;;
;;  is to abort on the server side and wait for the ADAM side to timeout.   ;;
;;                                                                          ;;
;;                                                                          ;;
;;  <--------------------------------------------- ACK (or CHECKSUM_ERR     ;;
;;                                                         MISS_MEDIA_ERR   ;;
;;                                                         DEVICE_ERR)      ;;
;;                                                                          ;;
;;                                                                          ;;
;;  ACK ------------------------------------------>                         ;;
;;                                                                          ;;
;;                                                                          ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


<------------------------------------------------- [block of data]

<------------------------------------------------- [lo] [hi] checksum


ACK----------------------------------------------->
(or NAK if checksum error)



**************************
**                      **
**  WRITE BLOCK DEVICE  **
**                      **
**************************


ADAM                                           SERVER
====                                           ======

W [block dev #]  --------------------------------->


<------------------------------------------------- ACK (or INV_DEVICE_ERR
                                                           DEVICE_ERR)


[lo] [hi] [lo] [hi] block # ---------------------->
 loword     hiword


<------------------------------------------------- ACK (or INV_BLOCK_ERR)


[block of data] ---------------------------------->

[lo] [hi] checksum ------------------------------->


<------------------------------------------------- ACK (or CHECKSUM_ERR
                                                           WRITE_PROT_ERR
                                                           DEVICE_ERR
                                                           NO_CHAR_RDY_ERR)



***************************
**                       **
**  FORMAT BLOCK DEVICE  **
**                       **
***************************


ADAM                                           SERVER
====                                           ======

F [block dev #]  --------------------------------->


<------------------------------------------------- ACK (or INV_DEVICE_ERR
                                                           DEVICE_ERR)


                                                   [now server does the
                                                    physical format]



<------------------------------------------------- ACK (or WRITE_PROT_ERR
                                                           DEVICE_ERR)



**********************************
**                              **
**  SET SERIAL PORT PARAMETERS  **
**                              **
**********************************


ADAM                                           SERVER
====                                           ======

S [char dev #]  ---------------------------------->


<------------------------------------------------- ACK (or INV_DEVICE_ERR
                                                           DEVICE_ERR)


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                                          ;;
;;  NOTE:  the remainder of the Set Serial Port Parameters handshaking is   ;;
;;  currently undefined, because SP0 and SP1 have not yet been implemented. ;;
;;                                                                          ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Implementation Notes.

  1. Currently, due to space limitations in EOS RAM on the client side, there are no automatic retries implemented in case of errors. As long as this fact is made known, user programs can easily implement error retries if desired. Clearly, however, the more ideal situation would be for EOS to handle retries.

  2. NMIs (non-maskable interrupts) are disabled during transfers for systems using Orphanware serial ports. Serial port overruns result if the user program has an active NMI routine and it remains active during client-server data transfers. NMIs are *not* disabled for systems using MI serial ports, because the 2681 UART has a 4-byte on-chip buffer which can hold several characters which may arrive during an active NMI routine. The client software is "smart" in that it will leave NMIs off if they were off already, and will restart them safely if they were on and turned off.

  3. The ADAMnet floppy disk drive firmware responds to a _WRITE_BLOCK command to block 0FACEH by invoking a disk formatting routine. Current client EOS does *not* trap writes to block 0FACEH by issuing an ADAMserve format command; nor does the current server software trap writes to this block and invoke disk formatting of its own accord. Probably the latter should be implemented.

  4. The current server software has several server-keyboard-invokable "fudges" to permit certain ill-behaved applications to function correctly in an ADAMserve environment. One is for PowerPaint, which sends garbage as the hiword of block number when reading/writing blocks in the file dialogue box; the fudge tells the server to force the hiword of block number to zero. Another fudge is for ADAMcalc, to translate "magic" control characters sent by the ADAMnet serial printer driver into appropriate "standard" carriage returns and linefeeds.

  5. The ADAMserve HARDDISK shell loads a special version of ADAMserve EOS if SmartLOGO is executed. SmartLOGO writes to VDP control registers directly instead of using EOS function calls (which save a copy of what was written to these write-only registers), so when enabling/disabling NMIs during ADAMserve transactions (which also requires a VDP register write), there's no way to tell what the current state is, so that it can be put back how it was when NMIs are restarted. The SmartLOGO-only version of ADAMserve EOS resorts to the evil expedient of poking 0EDH 45H (the machine code for RETN, RETurn from Nonmaskable interrupt) at addresses 102-103, waiting for 1/60th of a second to be sure that the NMI occurs and returns (but without a read of VDP register 8 to restart the NMIs), doing the serial data transfer, then restoring the original contents of 102-103 and reading VDP register 8.

  6. Note the large handshaking overhead in character I/O transfers. For Read Character Device, 7 characters must be transferred to successfully read 1 character. For Write Character Device, 6 characters are transferred for each character written. At 19.2 kbps, 8 data bits, no parity, 1 stop bit (9 bits per byte) the maximum data transfer rate is 19200/9 = 2133 bytes per second. The effective rate for ADAMserve character reads is 2133/7 = 305 characters per second (about 2.7 kbps), and for writes is 2133/6 = 363 characters per second (about 3.3 kbps). The implication for ADAMserve-based telecommunications programs is that, even if the physical SP0 or SP1 is a 28.8 kbps modem, the effective rate seen on the ADAM screen will be only slightly better than 2400 bps. Of course, using an MI client serial port at 38.4 kbps would double all these rates. The users of Orphanware serial ports, however, are somewhat out of luck.

  7. The handshaking overhead in block I/O transfers is not noticable for disks with 1024-byte blocks. The effective transfer rate, however, is still not much faster than a genuine ADAMnet digital data drive (i.e., tape). The calculation is left as an exercise for the reader :-)

  8. The reason that Set Serial Port Parameters is currently undefined is that I'm still trying to decide whether or not I want to support the bizarre way that the prototype ADAMnet serial device sets these parameters (in brief, by sort of switching from a character device to a block device to send a parameter block, then switching back to single-character mode). Chris Braymen explained it to me once, but it's all gone hazy now.

  9. The checksum computed in block I/O transfers is a simple sum. A CRC (cyclic redundancy check) would be far more robust, but there is no space available in EOS RAM to implement a CRC calculator. For local implementations (i.e., client and server physically close and connected by a short serial cable) this should not be much of a problem; but an arrangement in which client and server are connected by dialin telephone lines would be much more subject to errors due to line noise, and CRC is safer than simple checksum in this case.

III. For Next Time.

I should have another TWWMCA article written for 29 September 1997, because B.A.S.I.C. is having its monthly meeting this weekend, and I ought to be able to write about the meeting. It will be a short article, but I'll be back on schedule.

See you next week!

*Rich*


Next Article
Previous Article
TWWMCA Archive Main Page