NOTES ON THE ASSEMBLER INTERFACE TO COMMS LINK DRIVER CODE


INTRODUCTION
------------

COMMS LINK driver code is interrupt driven and handles
XON/XOFF and hardware handshaking at the lowest level. It
also parity checks all incoming data if parity is enabled.

As all serial I/O is interrupt driven, interrupts CANNOT
be disabled (as with the keyboard).  All calls to the
operating system via the SWI instruction are intercepted by
the driver code once the port has been opened, to make data
pack access transparent (and possible!) while the port is
open.  It also ensures that the port is active and not
affected by various power saving techniques in the operating
system while it is open.



ALTERED SYSTEM SERVICES
-----------------------

The function of the following operating system services are
altered when the  port is opened; PK$SETP, PK$PKOF, BZ$TONE,
BZ$ALRM and BZ$BELL.  As all SWI's and interrupts are
intercepted directly, and the old handlers are not chained
to, any other application that re-vectors the same services
will not function correctly while the port is open.  The old
handlers are saved only when the comms pack is booted during
the install code but are restored every time the port is
closed.  Any application booted in after the comms pack,
which re-vectors any service used by the comms pack, will not
function correctly even when the port has been closed as the
original handler will be restored.

PK$SETP - This function will first wait for the correct time
to elapse from the last character being sent and then switch
off the port.  From this point the port will no longer be
able to receive characters.  The operating system function
will then select the pack in the normal way, and the next
call made to a COMMS LINK service will switch the port on
again.

PK$PKOF - This function will do nothing while the port is
open.

BZ$TONE, BZ$ALRM, BZ$BELL These three functions will all just
do a beep regardless of any parameters passed while the port
is open because interrupts cannot be disabled.



MACHINE CODE INTERFACE
----------------------

The interface to the COMMS LINK driver code is via a block of
fixed address memory of 40 bytes long starting at dvt_spar
(=$214f).  This memory is divided into two areas: a table of
variables which set various i/o parameters and an entry point
for calls to the driver code.  All these variables and a
macro for calling the driver code via this entry point are
defined below.



VARIABLES
---------

The following variables can be set set to control the
function of the COMMS LINK driver code.  However, these
variables are also set by the SETUP option in the COMMS menu
and by the LSET OPL function, so generally their values must
be preserved.

Address   Name                Size      Range

$2150     rsb_baud            1 byte    0-9
	  50,75,110,150,300,600,1200,2400,4800,9600

$2151     rsb_parity          1 byte    0-4
	  NONE,ODD,EVEN,MARK,SPACE

$2152     rsb_bits            1 byte    0-1
	  7 BIT DATA, 8 BIT DATA

$2153     rsb_stop            1 byte    0-1
	  1 STOP BIT, 2 STOP BITS

$2154     rsb_hand            1byte     0-7
	  NONE,XON,RTS,XON+RTS,DTR,DTR+XON,DTR+RTS,ALL

$2155     rsb_proto           1 byte    0-2
	  NONE, XMODEM, PSION (File transfer protocol)

$2156     rsb_echo            1 byte    0-1
	  LOCAL, HOST (Terminal emulation only)

$2157     rsb_width           1 byte    0-250
	  NONE,1-250 (Forced line width)

$2158     rsb_timout          1 byte    0-255
	  NONE,1-255 (LPRINT timeout in seconds)

$2159     rst_reol            3 bytes   0-2
	  NONE,1,2 (1st byte only, other two data)

$215C     rst_reof            3 bytes   0-2
	  NONE,1,2 (1st byte only, other two data)

$215F     rst_rtrn            3 bytes   0-2
   	  NONE,1,2 (1st byte only, other two data)

$2162     rst_teol            3 bytes   0-2
    	  NONE,1,2 (1st byte only, other two data)

$2165     rst_teof            3 bytes   0-2
	  NONE,1,2 (1st byte only, other two data)

$2168     rst_ttrn            3 bytes   0-2
 	  NONE,1,2 (1st byte only, other two data)

$216B     rsb_off_del         1 byte    1-255
 	  (Time to off delay in characters default=3)

$216C     rsb_xoff_del        1 byte    1-255
	  (Time to off delay with XON/XOFF default=7)

$216D     rsb_tcon_val        1 byte    0-255
 	  Time constant value for timer 2 baud rate gen

$216E     rsb_off_ticks       1 byte    0-255
	  No. of ticks for baud rate dependent Tx off delay

$216F     rsw_off_tcon        2 bytes   0-65535
	  Time constant for single tick Tx off delay

$2171     rsb_sec_timer       1 byte    0-255
	  General purpose decrement to zero second timer

$2172     RESERVED WORD

$2174     rst_entry_point     3 bytes   -
	  Entry point for assembler interface

All the 3 byte fields are of the following format:
byte 0 = Length of data, 0 for none
byte 1 = First data byte (optional)
byte 2 = Second data byte (optional)

After any of these variables have been altered a call to
rs$setvars must be made to set up various derived variables.
None of these variables should be altered while the port is
open.

In addition a general purpose timer is available,
rsb_sec_timer.  This location is decremented once every
second if it is non-zero.  This timer is used by the
higher level functions such as LPRINT to implement time outs
so be warned!



CALLS TO THE DRIVER
-------------------

All calls to the COMMS LINK driver code are made via the
macro "rs" which is defined below:-

;
; RS - Macro for assembler interface to COMMS LINK drivers
;
.macro    rs function
          jsr rst_entry_point
.byte     function
.endm

Before using this macro a check must be made that the COMMS
LINK code has been booted as follows:

ldx       #opl_name           ; choose an OPL function unique to COMMS-LINK
os        dv$lkup             ; ask O/S if it's booted
bcs       not_booted          ;ok to call rs$ routines now

opl_name: .ascic"XFEOF"




DRIVER FUNCTIONS
----------------

The following functions are available:

Function No. Function     Description

0            rs$open      Open the COMMS LINK channel
1            rs$close     Close the COMMS LINK channel
2            rs$putchar   Put a character to the RS232 port
3            rs$getchar   Get a character from the RS232 port
4            rs$flush     Flush the receive buffer
5            rs$setvars   Set the COMMS LINK variables up after a change
6            rs$lprint    Print a string
7            rs$linput    Input a string
8            rs$licon     Link layer connect call
9            rs$lidis     Link layer disconnect call
10           rs$liput     Link layer put a frame call
11           rs$liget     Link layer get a frame call

Example to open the RS232 port for reading and writing

	clr       b               ; Open for reading and writing
	rs        rs$open
	bcs       error           ; Deal with error
    	...                   ; Rest of code


In general all functions indicate an error condition by
returning with the carry flag set and the appropriate error
code in the B register.

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

Function name:      rs$open
Function number:    0
Input parameters:   B reg - Mode to open.
Output values:      Carry set if error, error number in B

Description:-
Opens the RS232 channel and initialises the hardware, also
sets various modes of operation for the port depending on the
value passed in B.
Bits in B : Bit 0 - Set if port in Tx only mode.
            Bit 1 - Set to enable break key error inhibiting.
            Bit 2 - Set if th Tx paused state is to be cleared.

If a pack access is made while RS232 is open this access
will be delayed until any stray characters have been delt
with.  Any subsequent calls to rs_putchar or rs_getchar will
switch the port on again. If a call to rs$open is made while
the port is allready open the call is ignored.

Registers corrupted:      All
Errors:                   Carry set, error number in b
ER_DV_NP  Device not present
ER_GN_BL  Battery too low

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

Function name:      rs$close
Function number:    1
Input parameters:   B reg mode to close see below
Output parameters:  Carry set if can not close port because
                    host is busy
Description:-
Closes the RS232 port, bits in B:

Bit 0 CLEAR port closed and is turned off later by pack
access or key scan
Bit 0 SET port closed and turned off
Bit 1 CLEAR host paused state is ignored
Bit 1 SET port fails to close if the host is busy (paused by an XOFF)

Registers corrupted:      All
Errors:                   Carry set if failed to close due to
                          host busy

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

Function name:      rs$putchar
Function number:    2
Input parameters:   A register - Character to be put to buffer
Output parameters:  Carry set if error or busy, B clear or error number.

Description:-
Transmits the passed character.  Returns with the carry set
if an error or the port was busy, else the carry is clear. B
is clear if no error.

Registers corrupted:      B,CCR
Errors:                   Carry set error number in B
ER_DV_CA      Invalid device call - Port not open
ER_RT_BK      Break Key
ER_GN_BL      Battery too low

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

Function name:      rs$getchar
Function number:    3
Input parameters:   None
Output parameters:  A register - Next character from receive buffer
Carry set if error or busy, B clear or error number.

Description:-
Gets the next character from the receive buffer.  Returns
with carry set if error or no characters in the buffer, else
the next character from the buffer in the A register B is
clear.

Registers corrupted:      A,B,CCR
Errors:                   Carry set error number in B
ER_DV_CA      Invalid device call - Port not open
ER_GN_RF      Device read fail - Parity or overrun
ER_RT_BK      Break Key
ER_GN_BL      Battery too low

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

Function name:      rs$flush
Function number:    4
Input parameters:   None
Output parameters:  None

Description:-
Flushes the receive buffer.

Registers corrupted:      CCR
Errors:                   None

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

Function name:      rs$setvars
Function number:    5
Input parameters:   None
Output parameters:  None

Description:-
Sets the derived COMMS LINK variables after a change to the
setable COMMS LINK variables.

Registers corrupted:      All
Errors:                   None

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

Function name:      rs$lprint
Function number:    6
Input parameters:   X register - Pointer to text to print
                    B register - Length of string to print
Output parameters:  B register - Error code if any

Description:-
Opens the RS232 port for output only, then writes the passed
string to the port applying all the translates, timeouts etc
specified.

Registers corrupted:      All
Errors:
ER_GN_BL,                 Battery too low
ER_GN_WF,                 Device write fail (Timeout)
ER_RT_BK,                 Break Key
ER_DV_NP                  Device not present

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

Function name:      rs$linput
Function number:    7
Input parameters:   A register - Timeout in seconds (0=no
                    timeout)
                    B register - Number of characters to
                    recieve
                    X register - Address of buffer to place
                    characters
Output parameters:  B register - Error code if any
                    A register - Number of characters
                    received

Description:-
Opens the RS232 port for output and input and then reads the
passed number of bytes into the passed buffer applying all
the translates, timeouts etc specified.

Registers corrupted:      All
Errors:
ER_GN_BL,                 Battery too low
ER_GN_RF,                 Device read fail (Timeout)
ER_RT_BK,                 Break Key
ER_DV_NP                  Device not present

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

Function name:      rs$licon
Function number:    8
Input parameters:   None
Output parameters:  B register - Error code if any

Description:-
Attempts to establish a logical link with the correspondent
link entity.  Wait for a suitable acknowledgment.

Registers corrupted:      All
Errors:
ER_RT_F0 -                a link already exists (local error)
ER_GN_RF -                timeout trying to get a connection
Any errors that rs$open can return

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

Function name:      rs$lidis
Function number:    9
Input parameters:   None
Output parameters:  B register - Error code if any

Description:-
Disconnect the logical link with the correspondent link
layer.  Harmless if the link is already disconnected.

Registers corrupted:      All
Errors:
ER_RT_FC -                no link in existance
ER_GN_BL,                 Battery too low

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

Function name:      rs$liput
Function number:    10
Input parameters:   D register - Length of buffer to send
                    X register - Address of buffer to send
Output parameters:  B register - Error code if any

Description:-
Send data in buf to the correspondent. The data length must
be >=0 and <=MAXILEN.  Waits for a suitable acknowledgment.
(MAXILEN currently 260)

Registers corrupted:      All
Errors:
ER_RT_FC -                a link does not exist (local error)
ER_GN_RF -                a re-transmission threshold expired
ER_LX_ST -                len exceeds MAXILEN (no data
                          transferred)
A server disconnection reason code.
Eg "no disk space"
ER_GN_BL,                 Battery too low

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

Function name:      rs$liget
Function number:    11
Input parameters:   D register - Number of bytes to get
                    X register - Address of buffer to put
                    bytes
Output parameters:  B register - Error code if any
                    D register - Number of bytes placed in
                    buffer if OK

Description:-
Wait for a frame to arrive from the physical layer. Returns
number of bytes placed in buf if all ok.

Registers corrupted:      All
Errors:
ER_RT_FC -                a link does not exist (local error)
ER_GN_RF -                a timer expired
ER_LX_ST -                data length exceeds len -
1st len bytes in buf
A file server disconnection reason code.
Eg drive door open
ER_GN_BL,                 Battery too low




RS232 CONTROL SIGNALS
---------------------

The COMMS LINK hardware provides the following RS232 control
signals on the port POB_PORT2 (address $0003) :-

Name  Top slot pin    Direction  PORT2 bit no   Set if:
----  ------------    ---------  ------------   -------
RTS	5		input		2	correspondent busy
CTS	6		output		0	ORGANISER II busy
DSR	4		input		1	correspondent busy
DTR	-	   linked to DSR	-	DSR set

Note that DTR (normally an output) is merely linked to DSR
and can not be driven by a program.

All bits are readable, writing to input bits is to be avoided.



PROGRAMMING CONSIDERATIONS
--------------------------

In between a call to RS$OPEN and a call to RS$CLOSE,
programmers should look out for the following operating
system routines :

Routines which access packs :

all FL$ routines
all PK$ routines except for PK$PKOF
DV$BOOT, DV$LOAD
any other routines which indirectly access packs, such as
calls to OPL, DV$VECT, TL$XXMD etc.

These turn off the RS232 port, until the next call to COMMS
LINK. This is normally no problem, unless a program tries to
access POB_PORT2 after a call to one of the above routines
but before another COMMS LINK call. The RS232 port should
then be turned on by calling RS$OPEN again, as follows :

Example : no problem here

	os FL$OPEN
	...			; RS232 now off
	jsr rst_entry_point
	.byte rs$getchar	; or similar COMMS LINK function
	...
	tim #cts,POB_PORT2	; test the cts line

Example : this is a BUG

	os PK$SETP
	...			; RS232 now off
	tim #cts,POB_PORT2	; test the cts line

Example : corrected version of above

	os PK$SETP

				;Re-open the RS232 port, to switch it on
	ldab #opened_with	; use value originally supplied to RS$OPEN
	andb #^XFE		; but with bit 0 clear to indicate that
	jsr rst_entry_point	; the handshaking state is not to be reset
	.byte rs$open
	...
	tim #cts,POB_PORT2	; now ok to access port

Other routines whose behaviour is altered while the RS232
port open :

PK$PKOF - has no effect
BZ$ routines - these all give a strange beep
KB$ routines do not switch the packs off to save power

DO NOT CALL :

BT$SWOF - without first closing the RS232 port with RS$CLOSE

The keyboard interrupt service routine is altered so that
interrupts remain enabled while the keyboard is being
scanned. This gives RS232 interrupts a higher priority than
keyboard interrupts.



PROGRAMMING EXAMPLES


Checking that COMMS LINK is installed
-------------------------------------
First check that the COMMS LINK software has been loaded - ie
COMMS is  in the top-level menu. If COMMS LINK is not
installed, any program which calls the machine code interface
routines provided by COMMS LINK will crash.

This check can be performed in OPL as follows :

	commsin%:
	rem return true if COMMS LINK installed
	onerr iserr::
	xfeof:          :rem try a harmless COMMS LINK function
	iserr::
	return err<>203 : rem not installed if "missing proc" error

or in machine code :

		bsr check_present
		bcs not_present
		bra is_present

check_present:  bsr 1$		; PC relative LDX #XFEOF
		.ascic "XFEOF"	; leading count byte string
	   1$:	pulx		; PC = address of "XFEOF"
		os dv$lkup	; if XFEOF not there, no COMMS LINK
		rts


Opening and closing the RS232 port
----------------------------------
Before accessing the RS232 port, the program must first call
the COMMS LINK machine code interface routine RS$OPEN. This
indicates that the RS232 port is now in use.

Example :

		clrb		; Open for reading and writing
		jsr rst_entry_point
		.byte rs$open
		bcs error	; Deal with error
		...		; Now free to access the port

RS$CLOSE should be called when the RS232 port is no longer
needed.

	; bit masks for rs$close
	turn_off_immed= 1	; switch off immediately after close
				; else leave port on
	fail_busy= 2		; fail with carry set if paused by XOFF
				; else ignore XOFF state, and close immediately

	; Example where port not closed while XOFF handshake

	close_type = fail_busy	; dont close if busy handshaking,
				; leave port switched on

	wait_busy:	ldab #close_type
			jsr  rst_entry_point
			.byte rs$close
			bcs wait_busy	; wait until XON

	; Straight close

	close_type = 0	; ignore XOFF state,
			; leave port switched on

			ldab #close_type
			jsr rst_entry_point
			.byte rs$close



Examples using handshaking lines
--------------------------------
; Bit masks
		cts= 1
		dsr= 2
		rts= 4

;Set CTS to indicate ORGANISER II busy

		oim #cts,POB_PORT2

Example :

	;Clear CTS to indicate ORGANISER II ready for input

		aim #cts,POB_PORT2	; not (1) = $FE

Example :

	;Wait on both RTS and DSR

	wait:	tim #rts!dsr,POB_PORT2
		bne wait

Example : OPL/Machine code for controlling the RTS line directly:

	global rtshigh%(2),rtslow%(2),a%
	rtshigh%(1)=$71fe
	rtshigh%(2)=$0339
	rtslow%(1)=$7201
	rtslow%(2)=$0339

	rem to assert (raise) the rts line
	a%=usr%(addr(rtshigh%()),0)

	rem to de-assert (lower) the rts line
	a%=usr%(addr(rtslow%()),0)