; > TestSrc.ExtIO

        TTL RISC OS 2+ POST external commands
;
; External interface for RISC OS ROM.
; provides entry points to send byte- and word- and string-sized objects
; and to receive byte- and word-sized objects   
;
; A minimal set of opcodes should be used (ideally, only B, LDR and ADDS)
; so that a processor test may be validly included in the internal test
; sequence.
;
;------------------------------------------------------------------------
; History
;
; Date          Name            Comment
; ----          ----            -------
; 06-Dec-89     ArtG            Initial version - split from `Begin`
;                               Release 0.2 for integration
; 31-Mar-90     ArtG            Added ts_MoreText, cursor position, hex.
; 19-Apr-90     ArtG            Added bus exercise commands
; 09-May-90     ArtG            Changed LCD strobe to 12 pulses
; 15-May-90     ArtG            Added ReadyByte : improves synchronization
;                               when ExtCmd execution toggles A21/A22.
; 18-Jun-90	ArtG		Added CPR15 read/write functions
; 04-Oct-96	JRH		Updated comments to refer to test box on A23.
;				Added support for speaking to the test box when
;				running from the 2nd ROM bank, conditioned on
;				CanLiveOnROMCard. Needed because 2nd ROM bank
;				isn't ghosted on the A23 line like the 1st bank.
;------------------------------------------------------------------------


        SUBT    Test adapter interface

;
; The test adapter senses an access to the ROM with address line A23 high.
; Current (8M addressing space) ROMs only use address lines A2 to A22,
; so if A23 is asserted it will be ignored (the ROMS are aliased
; into 16M of space). With no test adapter, the aliased ROM location will
; be read and may be recognised. The test adapter may selectively disable
; ROMs when A23 is high, causing arbitrary data to be read. This data
; should be dependent on the previous ROM read operation, and will
; therefore be predictably not equal to the data read when the ROMs are
; aliased.
; The assumption that A23 is unused may be invalidated by a later issue
; of the PCB. Machines using larger ROMs than 8Mbytes will require explicit
; decoding or a new communication scheme.
;


;
; This section determines whether the test interface adapter exists, and 
; what variety is fitted (dumb, display or external)      
; 3 read operations are performed (a WS operation): if all of these 
; find a ROM alias then no adapter is fitted.
;
; If an adapter responds, then a RD operation is performed - 4 strobes then
; clocking 8 bits into r4. These bits may be all zeros (a dumb adapter)
; or all ones (a display adapter) or some other value (an external
; adapter)
;

ts_GetCommand  ROUT

        LDR     r0,%01
        ADD     pc,pc,r0
01
        &       0

        ; delay to make a gap before reading

        LDR     r3,%02
        ADD     pc,pc,r0
02
        &       ts_recover_time
03
        ADDS    r3,r3,r3                ; 16-loop delay
        BCC     %03

        ROUT

;
; Load up the registers for the test interface communication -
;
;�Point r2 at a word which contains 0 in 0-8MB physical space.
; Note that this code doesn't cope with the case where it can't find a zero
; word anywhere in the whole ROM. I don't think that this is a problem.

	MOV	r0, #0			; expected below
	MOV	r2, #0			; start of physical space
01
	LDR	r1, [r2, #4]!
	TEQ	r1, r0			; is it zero?
	BNE	%01

	ADD	r2, r2, #ts_Alias_bits	; point to zero word in ghost
	MOV	r1, #-1			; expected below
04
        ; do an RD operation (four strobes) to ensure interface cleared

        LDR     r3,[r2]
        ADDS    r3,r3,r1
        LDR     r3,[r2]
        ADDS    r3,r3,r1
        LDR     r3,[r2]
        ADDS    r3,r3,r1
        LDR     r3,[r2]
        ADDS    r3,r3,r1

        ; write a byte (initially, &90) to indicate readiness

        LDR     r4,%20
        ADD     pc,pc,r0
20
        &       ts_ReadyByte_00
        ADDS    r14,r0,pc
        B       ts_SendByte

        ; delay to make a gap between WRS and RD operations

        LDR     r3,%05
        ADD     pc,pc,r0
05
        &       ts_recover_time
06
        ADDS    r3,r3,r3                ; 16-loop delay
        BCC     %06

        LDR     r5,%07                  ; counter for first 5 bits
        ADD     pc,pc,r0
07
        &       1 :SHL: (32 - 5)
        LDR     r6,%08                  ; counter for last 3 bits
        ADD     pc,pc,r0
08
        &       1 :SHL: (32 - 3)
        ADDS    r4,r0,r0                ; input accumulator initialisation

; put the test interface into input mode

        LDR     r3,[r2]                 ; 3 bit lead-in
        ADDS    r3,r3,r1                ; (adapter detects WS operation)
        BCC     ts_User_startup         ; abort if no adapter present

        LDR     r3,[r2]                 ; two more strobes, then waitloop
        ADDS    r3,r3,r1
        LDR     r3,[r2]
        ADDS    r3,r3,r1

; started input operation : wait for interface to be ready

09
        LDR     r3,[r2]                 ; read start bit repeatedly
        ADDS    r3,r3,r1                ; (adapter detects RD operation)
        BCC     %09                     ; loop until interface is ready

; read the first 5 bits into r5 and the second 3 bits into r4

10      LDR     r3,[r2]                 ; read a bit of the byte
        ADDS    r3,r3,r1                ; .. if the test adapter is present, carry bit set
        ADCS    r4,r4,r4                ; .. shift left and add in carry

        ADDS    r5,r5,r5                ; loop until 5 bits are read
        BCC     %10

        ADDS    r5,r4,r4                ; copy bits 7..3 to r5, bits 5..1 

        ADDS    r4,r0,r0                ; and read the last 3 bits to r4
11      LDR     r3,[r2]                 ; read a bit of the byte
        ADDS    r3,r3,r1
        ADCS    r4,r4,r4

        ADDS    r6,r6,r6                ; loop until last 3 bits are read
        BCC     %11

;
; Command byte read in (split between r4 and r5)
; Pass the option bits (r4) to the function identified by r5.
;

        ADDS    r5,r5,r5                ; index * 2 -> index * 4 
        LDR     r3,%12                  ; pc-relative ptr to command_table
        ADD     pc,pc,r0
12
        &       ts_command_table - %13
        ADDS    r3,pc,r3                ; absolute pointer to command table
        ADDS    r3,r3,r5

13      LDR     r3,[r3]                 ; get table entry 
14      ADD     pc,pc,r3                ; (offset from command_table)

        &       0                       ; necessary padding : pc must point
                                        ; to command table when r3 is added.

;
; This is the table of offsets to all the built-in functions. 
; The top 5 bits of the command are used to index, so there are
; 32 possible entries, mostly illegal.
; Decoding of the function modifier bits is performed by multiple
; entries in this table.
;

; pc must point here when ADDS pc,r3,pc is executed

        ASSERT  ((ts_command_table - %14)  = 8)

ts_command_table

        DCD     (ts_Dealer_startup - ts_command_table)   ; display interface
ts_Windex
        DCD     (ts_write_memory   - ts_command_table)   ; external tests
ts_Rindex
        DCD     (ts_read_memory    - ts_command_table)
ts_Eindex
        DCD     (ts_execute        - ts_command_table)
ts_Bindex
        DCD     (ts_bus_exercise   - ts_command_table)

        DCD     (ts_GetCommand     - ts_command_table)	; dummy entry aligns CPR instructions
							; to allow 4-bit option field
ts_CWindex
	DCD	(ts_write_cpr15l   - ts_command_table)
	DCD	(ts_write_cpr15h   - ts_command_table)
ts_CRindex
	DCD	(ts_read_cpr15l    - ts_command_table)
	DCD	(ts_read_cpr15h    - ts_command_table)

        ; pad the table out to 31 entries 
        ; (leave space for display vector)

OldOpt  SETA    {OPT}
        OPT     OptNoList
doffset SETA    .
        WHILE   doffset < (ts_command_table + (31 * 4)) ; illegal entries
        DCD     (ts_GetCommand     - ts_command_table)
doffset SETA    doffset + 4
        WEND
        OPT     OldOpt

        DCD     (ts_Forced_startup - ts_command_table)  ; dumb interface

;
; The indexes into the above table are needed in ExtCmd ...
;
ts_WriteCmdByte         *       ((ts_Windex - ts_command_table) :SHL: 1)
ts_ReadCmdByte          *       ((ts_Rindex - ts_command_table) :SHL: 1)
ts_ExecuteCmdByte       *       ((ts_Eindex - ts_command_table) :SHL: 1)
ts_BusExCmdByte         *       ((ts_Bindex - ts_command_table) :SHL: 1)
ts_CPWCmdByte		*	((ts_CWindex  - ts_command_table) :SHL: 1)
ts_CPRCmdByte		*	((ts_CRindex  - ts_command_table) :SHL: 1)


;
; Primitives for reading data from the external interface
;
;               - Get a byte from the interface (into r4)
;               - Get a (4 byte) word from the interface (into r4)
;
; Required register setup is presumed done by a recent ts_GetCommand.
; r0, r1 and r2 have critical values
; r14 is the link address
;

ts_GetWord      ROUT

        LDR     r6,%01                  ; counter for 4 bytes per word
        ADD     pc,pc,r0                ; (bit set 4 left shifts from Carry)
01
        &       1 :SHL: (32 - 4)
        B       ts_Getdata

ts_GetByte      ROUT
        LDR     r6,%01                  ; counter for single byte
        ADD     pc,pc,r0
01
        &       1 :SHL: (32 - 1)

ts_Getdata      ROUT
        ADDS    r4,r0,r0                ; input accumulator initialisation

        LDR     r3,[r2]                 ; 3 bit lead-in
        ADDS    r3,r3,r1                ; (adapter detects RD operation)
        LDR     r3,[r2]
        ADDS    r3,r3,r1
        LDR     r3,[r2]
        ADDS    r3,r3,r1

; started input operation : now loop until r6 shifts into Carry

02
        LDR     r5,%03                  ; counter for 8 bits per byte
        ADD     pc,pc,r0
03
        &       2_00000001000000010000000100000001
04
        LDR     r3,[r2]                 ; read start bit repeatedly
        ADDS    r3,r3,r1
        BCC     %04                     ; loop until interface is ready
05
        LDR     r3,[r2]                 ; read a bit of the byte
        ADDS    r3,r3,r1
        ADCS    r4,r4,r4                ; SHL r4, add carry bit.

        ADDS    r5,r5,r5                ; loop until byte is read
        BCC     %05

        ADDS    r6,r6,r6                ; loop until word is read
        BCC     %04

        ADD     pc,r0,r14               ; back to the caller


;
; Primitives for sending data to the interface
;
;               - Send a byte to the interface (from r4 lsb)
;               - Send a (4 byte) word to the interface (from r4)
;
; Required register setup is presumed done by a recent ts_GetCommand.
; r0, r1 and r2 have critical values
; r14 is the link address
;

ts_SendWord     ROUT
        LDR     r6,%01                  ; counter for 4 bytes per word
        ADD     pc,pc,r0                ; (bit set 4 left shifts from Carry)
01
        &       1 :SHL: (32 - 4)
        B       ts_Putdata

ts_SendByte      ROUT
        LDR     r6,%01                  ; counter for single byte
        ADD     pc,pc,r0
01
        &       (3 :SHL: 7)
02      ADDS    r4,r4,r4                ;shift byte into highest 8 bits
        ADDS    r6,r6,r6
        BCC     %02                     ;stop when byte shifted,
                                        ;leaving bit 31 set in r6

ts_Putdata      ROUT

; Wait - gap between successive WS attempts or successive bytes

01      LDR     r3,%02
        ADD     pc,pc,r0
02
        &       ts_recover_time
03      ADDS    r3,r3,r3                ; 16-loop delay
        BCC     %03

        LDR     r3,[r2]                 ; Test for adapter ready for data
        ADDS    r3,r3,r1                ; (adapter detects WS operation)
        LDR     r3,[r2]
        ADDS    r3,r3,r1
        BCC     %10                     ; skip out if adapter not present
        LDR     r3,[r2]
        ADDS    r3,r3,r1
        BCC     %01                     ; loop back until adapter is ready

; Adapter ready - loop around all the bits in the byte

        LDR     r5,%04                  ; load bits-per-byte counter
        ADD     pc,pc,r0
04
        &       (1 :SHL: (32-8))

05      LDR     r3,%06                  ; delay before sending bit
        ADD     pc,pc,r0
06
        &       ts_recover_time
07      ADDS    r3,r3,r3                ; 16-loop delay
        BCC     %07

        ; Send a single bit : 1 pulse for 1, 2 pulses for 0

        LDR     r3,[r2]
        ADDS    r4,r4,r4                ; shift current bit into Carry
        LDRCC   r3,[r2]                 ; second pulse if bit is 0

        ; repeat until 8 bits are sent

        ADDS    r5,r5,r5
        BCC     %05

; Repeat for all the bytes to be sent (1 or 4)

        ADDS    r6,r6,r6
        BCC     %01

; Go to TXRDY to ensure the host sees the transmit request

        LDR     r3,%08                  ; delay before sending pattern
        ADD     pc,pc,r0
08
        &       ts_recover_time
09      ADDS    r3,r3,r3                ; 16-loop delay
        BCC     %09

        LDR     r3,[r2]
        ADDS    r3,r3,r1                ; dummy - space between pulses
        LDR     r3,[r2]
        ADDS    r3,r3,r1
        LDR     r3,[r2]

; All sent - r14 holds the caller's return address
10
        ADD     pc,r0,r14



;
; Reporting primitive
;
;               - Send the text (nul-terminated, at r4) to the display
;
; Interface registers need to be set up : this function is called from test 
; code rather than external interface code.
;
; The display is assumed to be a standard 16 character LCD module using 
; the Hitachi HD44780 display controller.
; The 16-digit module uses a single 44780. This is an abnormal use of the
; controller, and requires it to be set to two-line mode, with the first
; 8 displayed characters on the first 'line', and the second 8 on the
; second 'line'. Characters sent to the second line must be written at
; character position 40 +. In order to permit different modules to be
; fitted to later adapters, it is suggested that the first 7 characters 
; be treated as a 'title' line, and the second 8 as a 'comment' line.
; A space should always be placed at the end of the title line to
; split the display fields, unless there is no 'comment' line.
; Do not display characters across the two areas as though they adjoined
; (even though they do :-) ).
;
; The controller is operated in its 4-bit mode, which allows the interface
; to drive 4 bits of alpha information and 4 bits of control information.
; The bits in a transmitted byte are assigned as :
;
;       bit 0   -       D4  }   4-bit mode data bus
;           1   -       D5  }  
;           2   -       D6  }
;           3   -       D7  }
;
;           4   -       RS      Register Select : 0 for control, 1 for data
;
;           5   -           }   Unassigned
;           6   -           }
;
;           7   -       CPEN    Interface control :     0 for enable, 
;                                                       1 for disable
;
; For each message sent, the display is first initialised, using the 
; following sequence (each byte is sent as 2 bytes, high nibble first, 
; with RS clear in bit 4 of each byte)
; After each byte, an RD operation is performed : this is used by the
; interface hardware to strobe the data into the display. 
;
;
; The message addressed by r4 is then sent (data mode : RS set in each byte)
; until a 0 byte is encountered.
;

;
; This is the command sequence sent to initialise the display
;

ts_initialise
        =       &30,&30,&30,&20 ; power-up initialisation
        =       &20,&80         ; 4 bit mode, two line, Font 0
        =       &00,&C0         ; Display on, no cursor visible
        =       &00,&60         ; Incrementing display position, no shift
        =       &80,&00         ; Set DD RAM address 0
        =       &00,&20         ; Cursor home
        =       &00,&10         ; Display clear
ts_initialise_end

        ASSERT  ((ts_initialise_end - ts_initialise) / 2) < 32


;
; This is the command sequence sent when continuation text is sent
;

ts_extend
        =       &00,&C0         ; Display on, cursor invisible
        =       &00,&60         ; Incrementing display position, no shift
ts_extend_end

        ASSERT  ((ts_extend_end - ts_extend) / 2) < 32

;
; One of these commands are sent when offset text is required
;

ts_offset_table
        =       &80,&00         ; Set DD RAM address 0
ts_offset_table_1
        =       &80,&10         ; Set DD RAM address 1
        =       &80,&20         ; Set DD RAM address 2
        =       &80,&30         ; Set DD RAM address 3
        =       &80,&40         ; Set DD RAM address 4
        =       &80,&50         ; Set DD RAM address 5
        =       &80,&60         ; Set DD RAM address 6
        =       &80,&70         ; Set DD RAM address 7
        =       &C0,&00         ; Set DD RAM address 40
        =       &C0,&10         ; Set DD RAM address 41
        =       &C0,&20         ; Set DD RAM address 42
        =       &C0,&30         ; Set DD RAM address 43
        =       &C0,&40         ; Set DD RAM address 44
        =       &C0,&50         ; Set DD RAM address 45
        =       &C0,&60         ; Set DD RAM address 46
        =       &C0,&70         ; Set DD RAM address 47


; This assertion is forced by the code : each sequence assumed 2 bytes.

        ASSERT  ((ts_offset_table_1 - ts_offset_table) = 2)



        ALIGN

;
; Here starts the code ...
;

ts_SendQuit     ROUT                    ; put this code BEFORE %16
        ADD     pc,r0,r14               ;



;
; Entry point for initialising the display and sending r4 text.
;


ts_SendText     ROUT

;
; Point to the command sequence to setup and clear the display
;

        LDR     r0,%10                  ; set zero in r0
        ADD     pc,pc,r0
10
        &       0
        LDR     r7,%11                  ; pointer to init sequence
	ADDS	r7,pc,r7
        ADD     pc,pc,r0
11
        &       (ts_initialise - .)
        LDR     r6,%12                  ; length of init sequence
        ADD     pc,pc,r0
12
        &       (1 :SHL: (32 - (ts_initialise_end - ts_initialise)))
        B       ts_SendLCDCmd


;
; Entry point for adding text to current cursor position
;

ts_MoreText     ROUT

        LDR     r0,%10                  ; set zero in r0
        ADD     pc,pc,r0
10
        &       0
        LDR     r7,%11                  ; pointer to command sequence
	ADDS	r7,pc,r7
        ADD     pc,pc,r0
11
        &       (ts_extend - .)
        LDR     r6,%12                  ; length of command sequence
        ADD     pc,pc,r0
12
        &       (1 :SHL: (32 - (ts_extend_end - ts_extend)))
        B       ts_SendLCDCmd


ts_PosText      ROUT

;
; Entry point for adding text at a specific cursor position
; Used iteratively by SendText, etc if cursor position command found.
; Offset into display is given in r6.
;

        LDR     r0,%10                  ; set zero in r0
        ADD     pc,pc,r0
10
        &       0
        LDR     r7,%11                  ; pointer to command sequence
	ADDS	r7,pc,r7
        ADD     pc,pc,r0
11
        &       (ts_offset_table - .)   ; offset * 2 into table of
        ADDS    r6,r6,r6                ; offset command sequences
        ADDS    r7,r7,r6

        LDR     r6,%12                  ; length of command sequence
        ADD     pc,pc,r0
12
        &       (1 :SHL: (32 - 2))


;
; Entry point for writing arbitrary command strings.
; Set r7 to point to command string, r6 length (as tables above),
; Set r4 to point to following Data string (null-terminated).
;

ts_SendLCDCmd
;�Point r2 at a word which contains 0 in 0-8MB physical space.
; If this word still reads as 0 when its ghost/alias is read from 8-16MB space
; (A23 set) then we don't have a test box, otherwise we do.
; Note that this code doesn't cope with the case where it can't find a zero
; word anywhere in the whole ROM. I don't think that this is a problem.

	MOV	r0, #0			; ts_send_command_byte expects r0=0
	MOV	r2, #0			; start of physical space
01
	LDR	r1, [r2, #4]!
	TEQ	r1, r0			; is it zero?
	BNE	%01

	ADD	r2, r2, #ts_Alias_bits	; point to zero word in ghost
04

; Wait - gap between successive WS attempts or successive bytes

ts_send_command_byte ROUT

        LDR     r3,%14
        ADD     pc,pc,r0
14
        &       ts_recover_time
15      ADDS    r3,r3,r3                ; 16-loop delay
        BCC     %15
        LDR     r1,%16                  ; reload test register
        ADD     pc,pc,r0
16
        &       (-1)

        LDR     r3,[r2]                 ; Test for adapter ready for data
        ADDS    r3,r3,r1                ; (adapter detects WS operation)
        BCC     ts_SendQuit             ; skip output : adapter not present
                                        ; (backward jump helps ensure LDR r3,[r2]
                                        ; only reads zero when adapter absent
        LDR     r3,[r2]			; since previous bus data is nonzero)
        ADDS    r3,r3,r1
        LDR     r3,[r2]
        ADDS    r3,r3,r1
        BCC     ts_send_command_byte    ; loop back until adapter is ready

; Adapter ready - loop around all the bits in the byte


        LDR     r5,%21                  ; load byte-shift counter ...
        ADD     pc,pc,r0                ; ... and bits-per-byte counter
21
        &       (1 :SHL: 8) + 1         ; 24 shifts + 8 shifts
        LDRB    r1,[r7]
22      ADDS    r1,r1,r1                ; shift byte up into m.s.d.
        ADDS    r5,r5,r5
        BCC     %22

        ; Send a single bit : 1 pulse for 1, 2 pulses for 0

23      LDR     r3,[r2]
        ADDS    r1,r1,r1                ; shift current bit into Carry
        LDRCC   r3,[r2]                 ; second pulse if bit is 0

        ; and wait for the inter-bit time

        LDR     r3,%24
        ADD     pc,pc,r0
24
        &       ts_recover_time
25      ADDS    r3,r3,r3                ; 16-loop delay
        BCC     %25

        ; repeat until 8 bits are sent

        ADDS    r5,r5,r5
        BCC     %23

        ; do a RD operation to strobe the data out

        LDR     r5,%26
        ADD     pc,pc,r0
26
        &       (1 :SHL: (32 - 12))
27
        LDR     r3,[r2]
        ADDS    r5,r5,r5
        BCC     %27

; Repeat for all the bytes to be sent (ts_initialise_end - ts_initialise)

        LDR     r3,%33
        ADD     pc,pc,r0
33
        &       1
        ADDS    r7,r7,r3                ; bump the pointer
        ADDS    r6,r6,r6                ; bump the counter (shift left)
        BCC     ts_send_command_byte


;
; Then send all the display bytes (in 4-bit mode) until a nul-terminator
; is reached.
;

;
; Send a single character (as two separate 4-bit fields)
; First, look to see if it's one of :
;
;       NUL                     - end of text string
;       0x80 - 0xfe             - cursor positioning
;       0xff                    - introduce a hex digit
;

ts_send_text_byte       ROUT

        LDR     r1,%40                  ; reload test register
        ADD     pc,pc,r0
40
        &       (-1)

        LDRB    r7,[r4]
        ADDS    r3,r7,r1                ; test for nul terminator
        BCC     ts_SendEnd

;
; Byte isn't null. Check for >= 0x80.
;

        LDR     r6,%42                  ; test for cursor control
        ADD     pc,pc,r0
42
        &       (-&80)                  ; &8x means column x.
        ADDS    r6,r7,r6
        BCC     ts_printable_char       ; < &80 : write a character

;
; Carry set : r6 now holds (value - 0x80). Check for numeric escape (&ff).
;
        LDR     r3,%43
        ADD     pc,pc,r0
43
        &       (-&7f)
        ADDS    r3,r6,r3
        BCC     %47

;
; Carry set : fetch a nybble from the top of r8 and display that.
;

        ADDS    r8,r8,r8
        ADCS    r6,r0,r0
        ADDS    r8,r8,r8
        ADCS    r6,r6,r6
        ADDS    r8,r8,r8
        ADCS    r6,r6,r6
        ADDS    r8,r8,r8
        ADCS    r6,r6,r6

        LDRB    r7,[pc,r6]
        B       ts_printable_char
45
        =       "0123456789ABCDEF"

;
; Not &ff : r6 holds cursor positioning offset (< &80). Skip over
; the cursor control byte and iterate thro' PosText to move
; typing position.
;

47
        LDR     r3, %48
        ADD     pc,pc,r0
48
        &       1
        ADDS    r4,r3,r4
        B       ts_PosText

;
; Character is normal text : write it to the LCD.
; The shift loop is used to generate the inter-byte delay normally 
; provided by  ts_recover_time. Always make sure this is long enough.
;

ts_printable_char

        ADDS    r6,r0,r7                ; take a copy of character
        LDR     r5,%51                  ; load byte-shift counter ...
        ADD     pc,pc,r0                ; ... and bits-per-byte counter
51                                      ; as a bitmask of the shift pattern
        &       (1:SHL:8)+(1:SHL:4)+1   ; 24 shifts + 4 shifts + 4 shifts
52      ADDS    r6,r6,r6                ; shift byte up into m.s.d.
        ADDS    r0,r0,r0                ; slow this loop down - ensure it's
        ADDS    r0,r0,r0                ; always slower than ts_recover_time
        ADDS    r5,r5,r5
        BCC     %52

        LDR     r3,[r2]                 ; Test for adapter ready for data
        ADDS    r3,r3,r1                ; (adapter detects WS operation)
        LDR     r3,[r2]
        ADDS    r3,r3,r1
        LDR     r3,[r2]
        ADDS    r3,r3,r1
        BCC     ts_printable_char       ; loop back until adapter is ready

; Adapter ready - loop around all the bits in the byte

ts_send_tbit_upper

        ; wait for the inter-bit time

        LDR     r3,%55
        ADD     pc,pc,r0
55
        &       ts_recover_time
56      ADDS    r3,r3,r3                ; 16-loop delay
        BCC     %56

        ; Send a single bit : 1 pulse for 1, 2 pulses for 0

        LDR     r3,[r2]
        ADDS    r6,r6,r6                ; shift current bit into Carry
        LDRCC   r3,[r2]                 ; second pulse if bit is 0

        ; repeat until upper 4 bits are sent

        ADDS    r5,r5,r5
        BCC     ts_send_tbit_upper

        ; then send the interface control bits

        LDR     r1,%57
        ADD     pc,pc,r0
57
        &       (8 :SHL: 28)            ; assert RS control pin

ts_send_cbit_upper

        ; wait for the inter-bit time

        LDR     r3,%58
        ADD     pc,pc,r0
58
        &       ts_recover_time
59      ADDS    r3,r3,r3                ; 16-loop delay
        BCC     %59

        ; Send a single bit : 1 pulse for 1, 2 pulses for 0

        LDR     r3,[r2]
        ADDS    r1,r1,r1                ; shift current bit into Carry
        LDRCC   r3,[r2]                 ; second pulse if bit is 0
        ADDS    r5,r5,r5
        BCC     ts_send_cbit_upper

;
; do a RD operation to strobe the data out
;

        LDR     r3,%61
        ADD     pc,pc,r0
61
        &       ts_recover_time
62      ADDS    r3,r3,r3                ; 16-loop delay
        BCC     %62

        LDR     r5,%63
        ADD     pc,pc,r0
63
        &       (1 :SHL: (32 - 12))
64
        LDR     r3,[r2]
        ADDS    r5,r5,r5
        BCC     %64

        ; prepare to send the lower 4 bits out

        LDR     r5,%70                  ; bitcount mask for 4 data bits
        ADD     pc,pc,r0                ; and 4 interface control bits
70
        &       (((1 :SHL: 4) + 1) :SHL: 24)   

ts_send_text_lower
        LDR     r3,%71
        ADD     pc,pc,r0
71
        &       ts_recover_time
72      ADDS    r3,r3,r3                ; 16-loop delay
        BCC     %72

        LDR     r1,%73
        ADD     pc,pc,r0
73
        &       (-1)

        LDR     r3,[r2]                 ; Test for adapter ready for data
        ADDS    r3,r3,r1                ; (adapter detects WS operation)
        LDR     r3,[r2]
        ADDS    r3,r3,r1
        LDR     r3,[r2]
        ADDS    r3,r3,r1
        BCC     ts_send_text_lower      ; loop back until adapter is ready

ts_send_tbit_lower

        ; wait for the inter-bit time

        LDR     r3,%76
        ADD     pc,pc,r0
76
        &       ts_recover_time
77      ADDS    r3,r3,r3                ; 16-loop delay
        BCC     %77

        ; Send a single bit : 1 pulse for 1, 2 pulses for 0

        LDR     r3,[r2]
        ADDS    r6,r6,r6                ; shift current bit into Carry
        LDRCC   r3,[r2]                 ; second pulse if bit is 0

        ; repeat until lower 4 bits are sent

        ADDS    r5,r5,r5
        BCC     ts_send_tbit_lower


        ; then send the interface control bits

        LDR     r1,%78
        ADD     pc,pc,r0
78
        &       (8 :SHL: 28)            ; assert RS control pin

ts_send_cbit_lower

        ; wait for the inter-bit time

        LDR     r3,%80
        ADD     pc,pc,r0
80
        &       ts_recover_time
81      ADDS    r3,r3,r3                ; 16-loop delay
        BCC     %81

        ; Send a single bit : 1 pulse for 1, 2 pulses for 0

        LDR     r3,[r2]
        ADDS    r1,r1,r1                ; shift current bit into Carry
        LDRCC   r3,[r2]                 ; second pulse if bit is 0

        ADDS    r5,r5,r5
        BCC     ts_send_cbit_lower

;
; do a RD operation to strobe the data out
;

        ; wait for the inter-bit time

        LDR     r3,%82
        ADD     pc,pc,r0
82
        &       ts_recover_time
83      ADDS    r3,r3,r3                ; 16-loop delay
        BCC     %83

        LDR     r5,%84
        ADD     pc,pc,r0
84
        &       1 :SHL: (32 - 12)
85
        LDR     r3,[r2]
        ADDS    r5,r5,r5
        BCC     %85

; Repeat for all the bytes to be sent (until nul terminator is found)

        LDR     r3,%86
        ADD     pc,pc,r0
86
        &       1
        ADDS    r4,r3,r4                ; bump text pointer
        B       ts_send_text_byte

;
; Wait for about 1 seconds worth of LCD operation delays to
; permit the operator to read the text. 
; Use of the interface's monitor allows this delay to be increased 
; or decreased externally.
;

ts_SendEnd      ROUT

        LDR     r7, %01
        ADD     pc,pc,r0
01
        &       (ts_pause_time + 1)     ; must be an odd number
					; to ensure pairs of zeros
        ASSERT ((ts_pause_time :AND: 1) = 0)

02
        LDR     r3,%03
        ADD     pc,pc,r0
03
        &       ts_recover_time
04      ADDS    r3,r3,r3                ; 16-loop delay
        BCC     %04
        LDR     r1,%05                  ; reload test register
        ADD     pc,pc,r0
05
        &       (-1)

        LDR     r3,[r2]                 ; Test for adapter ready for data
        ADDS    r3,r3,r1                ; (adapter detects WS operation)
        BCC     ts_SendQuit             ; skip output : adapter not present
        LDR     r3,[r2]
        ADDS    r3,r3,r1
        LDR     r3,[r2]
        ADDS    r3,r3,r1
        BCC     %02                     ; loop back until adapter is ready

; Adapter ready - loop around all the bits in the byte
; Note that each byte is actually 4 bits to the LCD module,
; so a even number must be sent or the display will get out
; of sync until the next display reset sequence.

        LDR     r5,%10                  ; bits-per-byte counter
        ADD     pc,pc,r0
10
        &       (1 :SHL: 24)
        LDR     r3,%11
        ADD     pc,pc,r0 
11
        &       ts_recover_time         ; wait before sending data bits
12      ADDS    r3,r3,r3                ; for byte timing.
        BCC     %12

        ; Send a single bit : always 2 pulses for 0

13      LDR     r3,[r2]
        LDR     r3,[r2]

        ; and wait for the inter-bit time

        LDR     r3,%14
        ADD     pc,pc,r0
14
        &       ts_recover_time
15      ADDS    r3,r3,r3                ; 16-loop delay
        BCC     %15

        ; repeat until 8 bits are sent

        ADDS    r5,r5,r5
        BCC     %13

        ; do a RD operation to strobe the data out

        LDR     r5,%16
        ADD     pc,pc,r0
16
        &       1 :SHL: (32 - 12)
17
        LDR     r3,[r2]
        ADDS    r5,r5,r5
        BCC     %17

        ; repeat until a sufficient number of nuls are done

        ADDS    r7,r7,r1                ; count down loop counter
        BCS     %02

        ADD    pc,r0,r14                ; back to caller


        END