**************************************************************************
**                                                                      **
**                ADAMserve PROTOCOL STANDARDS DOCUMENT                 **
**                                                                      **
**                           20 August 2000                             **
**                                                                      **
**               by Richard F. Drushel (drushel@apk.net)                **
**                                                                      **
**            extracted from "This Week With My Coleco ADAM"            **
**                    articles 9709.26 and 9709.30                      **
**                                                                      **
**************************************************************************


I.  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.

    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.
===========================================

ADAM:  Base ADAM (1 tape drive), with one serial port (Orphanware or MI).
While the MI serial port can be configured to 38.4 kbps, the Orphanware
serial port maximum bitrate is 19.2 kbps.  Thus, the client-server link
serial port is currently set at the greatest common bitrate, 19.2 kbps,
though this is subject to change in future.

SERVER:  Currently the server software exists only for x86-based IBM-PC
compatibles (though any machine with a serial port which implements the
ADAMserve protocol can in principle work successfully).  Base PC:  PC-XT
(8088, 4 MHz), 256K RAM, one serial port, one 360K disk drive, one 20 MB hard
drive, CGA monitor, MS-DOS 2.0 or greater, ANSI.SYS screen driver.

CABLE:  Standard RS-232C serial cable in null-modem configuration (either
hardwired or with adapter).


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 telecommuni-
cations 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.


II.  Expanding ADAMserve.

    One feature that I'd like to add (before I freeze the protocol) is a
Set ADAMserve Version function:


*****************************
**                         **
**  SET ADAMSERVE VERSION  **
**                         **
*****************************


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

V [version] [1's complement of version] ---------->


<------------------------------------------------- ACK (or NAK
                                                           VER_NOT_AVAIL_ERR)


VER_NOT_AVAIL_ERR   EQU       8Fh       ;server doesn't support the requested
                                        ;level of ADAMserve functionality


This would allow server software to evolve to support greater levels of
functionality (perhaps even on non-ADAM computers; I have in my moments of
delusion of godhood imagined that ADAMserve could be a general-purpose way
for 8-bit computers to access other hardware resources), while remaining
backward-compatible with earlier versions of the client software.  In this
case, the client would tell the server what version of ADAMserve it was
expecting.  If the server supports the requested version, it will acknowledge
the request, and all further ADAMserve transactions will follow this level
of functionality.  If the server does not support the requested version of
ADAMserve, it returns an error code, so that the client can either attempt
to "downshift" and negotiate a compatible level of ADAMserve functionality,
or else abort gracefully.  By default, the server should start up in the
original (i.e., current) ADAMserve mode, and not change unless specifically
requested by the client.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                                         ;;
;;   NOTE:  as of the date of this protocol standards document (20 August  ;;
;;   2000), there are *no* existing implementations of ADAMserve which     ;;
;;   support the Set ADAMserve Version function.                           ;;
;;                                                                         ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

     I will hereby define the default version to be the current ADAMserve 4.0
specification. Version 0 will be ADAMserve 5.0, when that specification is
finally written and frozen.  Note that any clients with 5.0+ functionality
attempting to run on a 4.0 server will get an INV_COMMAND_ERR in response to
the Set ADAMserve Version command, and hence should either downgrade to 4.0
level or abort.


***************************** end PROTOCOL.TXT ******************************
