;       PORTS.S is part of SIO2PC. It contains the routines which
;       set up and use the serial port.
;
; The following subroutines are found in this file:

;       CON_COM         GET_MSR         GET1            PUT1
;       PUT1A           REG_INIT        MAN_PORT        ENABLE
;       DISABLE         CK_LINES        COM_SET;        LOCK_IT
;       TOG_SPEED       RE_BAUD         INT_COM

COMMENT\

HERE ARE SOME FACTS STRAIGHT OFF THE CHIP DATA SHEET:

                LCR:
b7:     DLAB - 0 access receiver buffer, 1 access divisior latches
b0,1    11 means 8 data bits

                LSR:

b0      1 means data ready
b1      1 means over-run: data not read before new data entered
b2      1 parity error, when parity being used
b3      1 means framing error
b4      1 means BREAK occurred
b5      1 means transmitter holding register empty (THRE)
b6      1 means transmitter empty (TEMT)
b7      always 0 in char mode, in FIFO mode, 1 means an error exists in FIFO

                MCR:

b1      1 makes RTS output low
b3      1 enables the INT output *** SEE NOTE BELOW ***
b4      1 enables the loopback mode

; I found that setting bit 3 = 1 for COM3 locks up the MOUSE on COM1
; so don't do that.  It must have something to do with the interrupt.
; I assume setting b3 on COM4 would lock up the MOUSE on COM2, and
; vice versa for both COM1/3 & COM2/4.

                MSR:

b2      TERI trailing edge of RI detected
b6      -RI line status; 1 -> RI is low; 0 -> RI is high

ENDOFCOMMENT\

COMMENT\

REV 4.11 removes this routine

; This routine allows setting the lower 4 bits of the MCR by inputting a
; single hex digit 0 - F.  "Experiment with MCR".  Note - bit 1 won't
; stay clear because main loop calls DISABLE

EXP_MCR:
        MOV DX, MCR
        IN AL, DX
        CALL BYTE2HEX ; convert AL to ascii digits in BH (low) and BL (high)
        MOV W[MCR_HERE], BX ; insert current into GIVE_MCR string
        PSTATUS GIVE_MCR, ATTR9 ; print current and request new ...
        CALL GET_TORK
        CALL TO_UPPER
        CMP AL, 27 ; escape cancels
        JE > L1
        CALL HEXDIGIT ; converts ASCII digit to number in AL
        JC EXP_MCR ; if bad input
        MOV DX, MCR
        OUT DX, AL
        MOV LAST_MCR, AL
        CALL ACC_DLY
L1:     RET
ENDOFCOMMENT\

;               COM_SET

;       COM_SET gets the desired com # from tail or kybd
;       input, then sets registers and initializes PORT.
;
COM_SET:
        CALL GET_TORK
        CMP AL,' '
        JE COM_SET
        PUSH AX
        CALL PRINT1
        SUB AL,'1'; Convert ASCII to binary number, 0 - 3.
        CMP AL,04
        JB ABO; OK if <4
        PSTATUS VOR, ATTR9
        TICKS 36
        CALL FLUSH
        POP AX
        STC;            FLAG ERROR
        RET; Error exit
ABO:    MOV COM_NO,AL
        CALL REG_INIT
        CALL CON_COM
        MOV CONFIGD,0FFh; FLAG THAT PORT HAS BEEN CONFIGD.
        POP AX          ; Now, fill in the port # on the menu
        MOV [ACOMN], AL ; line.
        CURSET 0604h
        PRINTL TX7
        CLC;            GOOD STATUS
        RET



; GET1 is a sub that gets one byte from the defined PORT. It gets
; directly from the hardware, not using BIOS or DOS.

; USES AL. DATA IS RETURNED IN AL.

        
GET1:   PUSH ES, DX, AX
;         MOV AL, 'B'
;         CALL PUT_LOC
;
; 4.10 takes out the setting of DLAB for each byte.  DLAB is
; expected to already be in the right state.

;        MOV DX, LCR ; Set DLAB to 0 for access to receive buffer
;        IN AL, DX
;        CALL ACC_DLY
;        AND AL, 01111111xB
;        OUT DX, AL
;        CALL ACC_DLY ; 4.10 ADDED
        MOV VARBL, 2; Time out timer - 4.07 went 3 to 2
CHECK:
        CMP VARBL, 0
        JNE >L1
        MOV AL, 'a'
        MOV ERR_CHAR, AL
        CALL PUT_ERCH; Put report of the timeout to ERROR field
        STC     ; error exit - carry means timed out
        POP AX, DX, ES
	RET
L1:     MOV DX,LSR      ; bit 0 of LSR set means data ready
        IN AL,DX
        CALL ACC_DLY
        AND AL, 00000001xB
        JE CHECK        ; Loop if bit is 0...

;       Else, read a byte from receive buffer...
        
        POP AX  ; preserve AH

        MOV DX,BAUD
        IN AL, DX
        CALL ACC_DLY ; 3.01 TOOK OUT - 4.10 put back
        POP DX, ES
        CLC     ; 'good' status
        RET

; GETSLO (3.20) is like GET1, except it allows several seconds before
; timing out ...

GETSLO: PUSH ES,DX,AX

; 4.10 takes out PUT_LOC call and DLAB setting ...

;        MOV AL,'B'
;        CALL PUT_LOC
;        MOV DX, LCR ; Set DLAB to 0 for access to receive buffer
;        IN AL, DX
;        CALL ACC_DLY
;        AND AL, 01111111xB
;        OUT DX, AL
;        CALL ACC_DLY ; added by 4.10

        MOV VARBL, 72; Time out timer

CHKSLO:
        CMP VARBL, 0
        JNE >L1
        MOV AL, 'a'
        MOV ERR_CHAR, AL
        CALL PUT_ERCH; Put report of the timeout to ERROR field
        STC     ; error exit - carry means timed out
        POP AX, DX, ES
	RET
L1:     MOV DX, LSR      ; bit 0 of LSR set means data ready
        IN AL, DX
        CALL ACC_DLY
        AND AL,00000001xB
	JE CHKSLO	 ; Loop if bit is 0...

;       Else, read a byte from receive buffer...
        
        POP AX  ; preserve AH
        MOV DX, BAUD
        IN AL, DX
        CALL ACC_DLY ; 3.01 TOOK OUT - 4.10 PUT BACK
        POP DX, ES
        CLC     ; 'good' status
        RET

;               PUT1:

; This subroutine puts one byte from AL out to the defined PORT. It 
; waits for the transmitter to be empty before returning. 
; AL IS USED. AL IS CHANGED BY THIS SUBROUTINE.

PUT1:   PUSH DX
        PUSH AX
        MOV ERR_CHAR,'b'; Error reporting code for timeout
;        MOV W[COUNT_TIME],0; set-up time-out timer
        MOV VARBL, 2; - 4.06; 4.07 changed 5 to 2
;        MOV AL,'C'; loop reporting code
;        CALL PUT_LOC
        MOV DX,LSR
L5:     CMP VARBL, 0 ;CALL DO_TIME; time-out timer 4.06
        JZ >L1; break out of this part of loop
        IN AL,DX        ; Check if holding buffer is empty
        CALL ACC_DLY
        AND AL,00100000xB ; bit 5 is hold reg. status
        JE L5           ;  bit set means empty
L1:     MOV DX,BAUD     ; *** rev 2.4 L1: was on next line ***
        POP AX
        OUT DX,AL
        CALL ACC_DLY
        MOV DX,LSR      ; Get status of transmitter
L6:     CMP VARBL, 0 ;CALL DO_TIME 4.06
        JZ >L1
        IN AL,DX
        CALL ACC_DLY
        AND AL,01000000xB ; bit 6 = 1 means empty
        JE L6
L1:     POP DX
        MOV ERR_CHAR, ' '; don't blame me!
        RET     

;       PUT1A
;
; This subroutine also puts one byte out. However, it loads when the 
; holding buffer is empty. Also, it returns immediately, unless CX = 1
; then it waits till the transmitter is empty. It is used to send a 
; number of bytes using CX as a counter in external loop.
; USES & CHANGES AL

; If T6 <> 0, this routine will wait until transmitter is empty,
; plus the delay of T6 * 850 nSec

PUT1A:  PUSH DX
        PUSH AX
;        PUSH AX         ; DEBUG INFO
;        MOV AL,'D'      ;  "
;        CALL PUT_LOC    ;  "
;        POP AX          ;  "
;        MOV W[COUNT_TIME], 0 ; "
        MOV VARBL, 2; 4.07 changed 5 to 2
        MOV ERR_CHAR,'c'
        MOV DX,LSR
L7:     CMP VARBL, 0 ; CALL DO_TIME 4.06
        JZ >L1
        IN AL,DX
        CALL ACC_DLY
        AND AL,00100000xB ; bit 5 means xmtr holding reg empty
        JE L7
L1:     MOV DX,BAUD
        POP AX
        OUT DX,AL       ; send the byte
        CALL ACC_DLY    ; rev 4.12 added
        CMP T6,0        ; If T6 <> 0 wait for TX empty
        JNE >L1
        CMP CX,1        ; If CX = 1, then wait for transmitter empty...
        JNE AAF
L1:     MOV DX,LSR
L1:     CMP VARBL, 0 ;CALL DO_TIME 4.06
        JZ AAF
        IN AL,DX
        CALL ACC_DLY
        AND AL,01000000xB        ; Tx buf empty when b6 = 1.
        JE L1
        CMP T6,0        ; Do between bytes time delay if T6 <> 0
        JE AAF
        PUSH BX
        MOV BX,T6
        CALL TIMER_0
        POP BX
AAF:    POP DX
        RET

; SET NEW BAUD RATE, AS IN SPARTA'S ULTRASPEED I/O:
; Note: Desired baud divisor must have been previously
; stored @ T9

RE_BAUD:
	MOV SPEED, 'N'  ; Assumption is for Normal
	MOV DX,LCR      ; First change DLAB to 1 so can address divisor
        MOV AL,10000011XB;
	OUT DX,AL
        CALL ACC_DLY ; 4.10
;
	MOV AX,T9       ; 6: 19.2; 4: 28.8; 3: 38.4
	MOV DX,BAUD
;        OUT DX,AX       ; 16 bit transfer 4.10 changes to two 8's
        OUT DX, AL
        XCHG AH, AL
        CALL ACC_DLY
        INC DX          ; high bit to next register @ BAUD + 1
        OUT DX, AL
        CALL ACC_DLY
        XCHG AH, AL ; for compare below
;
	CMP AX, 6       ; If T9 <> 6, then High speed
        JE >L1
	MOV SPEED, 'H'
L1:     CALL PUT_SPEED
        MOV AL,00000011Xb
	MOV DX,LCR
	OUT DX,AL
	CALL ACC_DLY

	RET

; Following toggles speed between ultra and 19.2:

TOG_SPEED:
	CMP SPEED, 'H'
        JNE >L1
	MOV SPEED, 'N'
	MOV T9, 6
        JE > L2 ; 4.10 -  L2 was ADY
L1:     MOV SPEED, 'H'
	MOV AX, PCDIV
	MOV T9, AX
L2:     CALL RE_BAUD
        RET


; Subroutine CON_COM configures the chosen COM port and changes its
; BAUD to 19,200. Set those registers up first!

        
CON_COM:
; Now, change the baud to 19,200:

        MOV DX,LCR      ; First change DLAB to 1 so can address divisor
        MOV AL,10000011xB; ***PROGRAM MODE INDEPENDENT OF BIOS: 1.05 ***
        OUT DX,AL
        CALL ACC_DLY ; REV 4.12
;
        MOV AX,06       ; 6 is divisor for 19.2 KB

        MOV T9, AX      ; Keep a record
	MOV SPEED, 'N'  ; Normal
        CALL PUT_SPEED
        MOV DX,BAUD
;       OUT DX,AX       ; 16 bit transfer
;
; 4.10 changes speed programing from a 16 bit transfer to two 8 bit
; transfers ...

        OUT DX, AL      ; low byte of rate word
        XCHG AH, AL
        CALL ACC_DLY
        INC DX
        OUT DX, AL      ; high byte

        CALL ACC_DLY    ; 4.10 added
;
        MOV AL,00000011xB
        MOV DX,LCR
        OUT DX,AL
        CALL ACC_DLY
;        CALL TIME_LAG 4.10 took out
        IN AL,DX;        See if port reads back as written.
        CALL ACC_DLY ; 4.10 ADDED
        CMP AL,00000011xB
        JE >L1   ;        This is just a warning, doesn't stop program
        PSTATUS BADLCR, ATTR10
        TICKS 54

; check - why is below only done if there's an error? 4.10 fixed ...
; moved L1 up here ...

L1:     MOV DX,MCR      ; Initialize MCR

; warnings for MCR: make sure loopback is explicitly turned off (b4 = 0)
; and make sure b3 isn't set.  b3 will mess up the mouse on the
; corresponding odd or even port.

        MOV AL,00000111xB; RTS,DTR OUT1,OUT2 all low, no loopback mode
        OUT DX,AL
        CALL ACC_DLY
        MOV LAST_MCR, AL
        CALL DISABLE; PUT RTS into disable data out state.

        CALL ACC_DLY    ; 4.10 - was TIME_LAG - location was L1
        MOV DX,INT_EN   ; *** DISABLE ALL INTERRUPTS FROM UART***
        MOV AL,0        ; *** A REVISION 1.03 CHANGE ***
        OUT DX,AL

;       Do loopback test on UART:

;       Give the UART time to settle...

        TICKS 2
        MOV DX,BAUD; First clear the input register to reset
        IN AL,DX
        CALL ACC_DLY ; 4.10 changed from TIME_LAG
        MOV DX,MCR
        IN AL,DX
        PUSH AX;        Save old MCR mode
        OR AL,10h;     Enable loopback mode
        CALL ACC_DLY	; 4.10 - was TIME_LAG
        OUT DX,AL
;        MOV LAST_MCR,AL ; 4.10 took out - MCR already saved
        TICKS 2
        MOV CX,0FFh;       So PUT1A will return immediately
        MOV AL,11001101xB; a random char
        CALL PUT1A
        CALL GET1
        JC > L1
        CMP AL,11001101xB
        JE > L2 ; 4.10 - L2 was ABJ
L1:
        PSTATUS NOLOOP, ATTR10
        TICKS 54

L2:     POP AX ; was ABJ
        MOV DX,MCR
        OUT DX,AL
        CALL ACC_DLY ; 4.10
        MOV LAST_MCR,AL
        MOV AL, 0 ; turn off 16550 FIFO mode 4.07
        MOV DX, INT_IDENT ; this is FIFO control as write register
        OUT DX, AL
        CALL ACC_DLY ; 4.10
        CALL MT_550 ; now clear any data from 16550 buffer - 4.07
        RET

; a routine to clear the 16550 buffer; 40.7

MT_550:
        PUSH AX, CX, DX
        MOV CX, 16
L2:     MOV DX, LSR
        IN AL, DX
        CALL ACC_DLY ; 4.10 ADDED
        TEST AL, 1 ; b0 set means data ready
        JZ > L1 ; 0 means no byte waiting
        MOV DX, BAUD ; else read and throw away data
        IN AL, DX
        CALL ACC_DLY ; 4.10 ADDED
        LOOP L2 ; up to 16 times
L1:     POP DX, CX, AX
        RET


;               GET_MSR

; This routine reads the Modem Status Register and returns
; it in the AL register. Uses AL only.

GET_MSR:
        PUSH DX
        MOV DX, MSR; MSR must be defined previously.
        IN AL, DX
        CALL ACC_DLY
        POP DX
        RET

;       REGISTER INITIALIZE ...

; This routine loads the register names with the values
; appropriate based on the base value found in the variable
; COM_NO

REG_INIT:
        PUSH AX,DS
        MOV DI,WORD PTR COM_NO; contains 0 to 3; ****QUICKA CHANGE****
        AND DI,0Fh; screen off high byte
        SHL DI,1; times 2 to address word
        MOV DX,0040h; point to BIOS data area
        MOV DS,DX
        MOV DX,[DI]; get UART base address for chosen port

	#IF CARLM
	PUSH DX, DI
	MOV DI, 08 ; LPT1 address
	MOV DX, [DI]
	CMP DX, 0
	JZ >L1; if BIOS AREA DOESN'T LIST ONE ...
	PUSH CS
	POP ES

	MOV ES:[PP_OUT1], DX
	INC DX
	MOV ES:[PP_IN], DX
	INC DX
	MOV ES:[PP_OUT2], DX
L1:	POP DI, DX
	#ENDIF

	POP DS

; Now, if bios data area said port address = 0,

        CMP DX,0
        JNE ENTRY2
        MOV DX, PORTS[DI]; Get default port address

; ENTRY2 is the entry point for MAN_PORT, which already has DX = ADDR

ENTRY2:
        MOV BAUD,DX; and store it in my data area
        INC DX
        MOV INT_EN, DX; +1
        INC DX
        MOV INT_IDENT,DX; +2
        INC DX
        MOV LCR,DX; +3
        INC DX
        MOV MCR,DX; +4
        INC DX
        MOV LSR,DX; +5
        INC DX
        MOV MSR,DX; +6

; Now use macro to print hex value of base address on COM status line

        MOV AX,BAUD
        HEXTEX CMTXT
        CURSET 092Ch
        PRINTL OPT2
        POP AX
        RET

; 3.20 subroutines to raise and lower RTS (command line)
; for the 1050-2-PC hardware ...

RTS_DN:
       PUSH AX, DX
       MOV AL, LAST_MCR
       OR AL, 2; setting bit makes RTS low
       MOV LAST_MCR, AL
       MOV DX, MCR
       OUT DX, AL
       CALL ACC_DLY; 4.15 added
       POP DX, AX
       RET

RTS_UP:
       PUSH AX, DX
       MOV AL, LAST_MCR
       AND AL, 11111101xb; clearing bit makes RTS high
       MOV LAST_MCR, AL
       MOV DX, MCR
       OUT DX, AL
       CALL ACC_DLY ; 4.15
       POP DX, AX
       RET


; A Subroutine to set the RTS line to level required to enable
; the data out line:


ENABLE:
        PUSH AX
        PUSH DX
        CMP LOCK_RTS,0
        JNE ABL; Skip if locked on
        MOV AL, LAST_MCR; See if already enabled **** 1.07 ****
        AND AL, 2
        JE ABL; skip if already enabled
        MOV DX, MCR
        IN AL, DX
        CALL ACC_DLY
        AND AL,0FFh-2   ; Set bit 1 (RTS) off
        OUT DX, AL
        CALL ACC_DLY
        MOV LAST_MCR, AL
ABL:    POP DX
        POP AX
        RET


; A subroutine to disable the data out line to the Atari bus:

DISABLE:
        PUSH AX
        PUSH DX
        CMP LOCK_RTS,0; <>0 means RTS locked in enabled state
        JNE ABM
        MOV AL,LAST_MCR
        AND AL,2
        JNE ABM
        MOV DX,MCR
        IN AL, DX
        CALL ACC_DLY
        OR AL, 2 ; Set bit 1 (RTS) on
        JMP > L1
L1:     OUT DX, AL
        CALL ACC_DLY
        MOV LAST_MCR, AL
ABM:    POP DX
        POP AX
        RET

; MAN_PORT is called when menu choice Option - "E" is made. It gets
; a 4 digit port address from the user and re-configures the 8250
; based on the new port address.

MAN_PORT:
        PSTATUS QADRS, ATTR9
        CALL GET4H;     Get 16 bit # in BX
        JC >L1 ;        Abort because ESC pressed
        MOV DX,BX;      REG_INIT expects port base in DX
        CALL ENTRY3;    New entry point into REG_INIT is ENTRY2
        CALL CON_COM;   Configure port
        CURSET 0602h
        PRINTL TX7
L1:     RET

ENTRY3: PUSH AX; This is just to get something on the stack because
        JMP ENTRY2; REG_INIT POP's AX before returning.



; This subroutine checks the MSR and puts status info into the
; debug info line as to whether the command line is high or low
; EXPECTS MSR INFO IN AX. REGISTERS AREN'T DISTURBED.
; The result will be that, regardless of one chip or two chip,
; ZF true means command line is low

CK_LINES:
        PUSH AX
        TEST AL,01000000xB; RI is command line
        LAHF    ; 3.06 - complement ZF if ONECHIP. AH <- FLAGS
        CMP B[REV_FLAG], 'i'
        JNE >L1
        XOR AH, 01000000Xb; complement ZF
L1:     SAHF; FLAGS <- AH
        JE >L2; branch if command is 0 (low) ; 4.10 L2 was L1
        MOV AL,'H'

        JMP > L3 ; 4.10 L3 was XAAD
L2:     MOV AL,'L'; Command is low
L3:     CALL PUT_CLINE
        POP AX
        RET

; LOCK_IT LOCKS the RTS line in the ENABLED position

LOCK_IT:
        CMP LOCK_RTS,0
        JNE >L1
        CALL ENABLE; First, enable it
        MOV LOCK_RTS,0FFh; Then, lock it on
        SMOVE STLOCKD, LOCKSTAT, 6
        JMP ABQ

L1:     MOV LOCK_RTS,0
        CALL DISABLE
        SMOVE STNORM, LOCKSTAT, 6
ABQ:    NOP
        CURSET 0804h
        PRINTL TX14
        RET

; ************ DIAGNOSTICS SUB-MENU *************************************
;
DIAGS:
        CALL CLR_SCRN
        PRINTL M_DIAG
L4:     CALL GET_TORK
        CALL TO_UPPER
        CMP AL, '1'
        JNE > L1
        CALL INT_COM
        JMP DIAGS
L1:	CMP AL, '2' ; line status troubleshooting
	JNE > L3
	CALL LINES
        JMP DIAGS
L3:     CMP AL, '3' ; put sectors to Atari
        JNE > L5
        CALL DGETSEX
        JMP SHORT DIAGS
L5:     CMP AL, '5'
        JNE L4
L2:     RET


; ************* An Intercom Program to test the serial ports ************

; This routine monitors for keyboard input, sends out all bytes recieved
; over the serial port. (Escape terminates this mode, and 0 not allowed.)
; bytes are also echoed to the screen
; The incoming port is also monitored, and all bytes received are put to
; the screen (Escape cancels this mode)

INT_COM:
        CALL CLR_SCRN
L1:     CALL IS_A_KEY ; carry clear means no key
        JNC > L2
        CMP AL, 0 ; 0 not allowed, it's a flag on the atari end
        JE > L2
        PUSH AX
        MOV CX, 0FFFFh ; 0 makes PUT1A hold till empty
        CMP AL, 13      ; if CR
        JNE > L3        ; translate to EOL
        MOV AL, 09Bh
L3:     CALL PUT1A ; send byte and return immediately
        POP AX
        CMP AL, 27 ; escape?
        JE > O1 ; exit function
        CMP AL, 13 ; CR ??
        JNE > A1
        CALL LINEFEED
        JMP > L2
A1:     CALL PRINT1 ; put char in AL to screen
;        CMP AL, 09Bh  ; if carriage return
;        JNE > L2
;        CALL LINEFEED

; Now, see if any incoming byte in serial port

L2:     MOV DX, LSR
        IN AL, DX       ; get line status - NOTE - CHECK FRAMING AND OVERRUN
        CALL ACC_DLY    ; 4.10 ADDED
        AND AL, 1       ; bit 0 set means input ready
        JZ L1           ; no input, restart loop
        CALL GET1       ; input the byte
        CMP AL, 09Bh    ; translate EOL to CR/LF
        JNE > L4
        CALL LINEFEED
        JMP L1
L4:     CALL PRINT1
        CMP AL, 27
        JNE L1
O1:     RET              ; O means OUT of here

; ************ LINE STATUS DIAGNOSTICS ****************************

LINES:
        CALL CLR_SCRN
        CURSET 0
        PRINTL M_LINES
        CALL OUT_STAT
        CALL HI
L2:     CALL CMD_STAT
        CALL CK_BRK
        CALL IS_A_KEY
        JNC L2 ; carry clear means no key
        CALL TO_UPPER
        CMP AL, 'O'; toggle data out status?
        JNE > L1
        CALL OUT_TOG
        CALL OUT_STAT
        JMP SHORT L2
L1:     CMP AL, 'I'
        JNE > L3
        CALL HI
L3:     CMP AL, 27 ; escape
        JNE L2
        MOV CMD_LAS, ' ' ; force H or L next time
        MOV OUTNOW, ' '
        CALL OUT_HIGH ; clear BREAK bit in LCR
        RET

; A routine to toggle the status of the Data Out line ...

OUT_TOG:
        MOV DX, LCR
        IN AL, DX
        XOR AL, 040h ; bit 6 = 1 to set break mode ( data out low )
        CALL ACC_DLY
        OUT DX, AL
        CALL ACC_DLY ; 4.10 ADDED
        RET

OUT_HIGH:
        MOV DX, LCR
        IN AL, DX
        AND AL, 10111111xB
        CALL ACC_DLY
        OUT DX, AL
        RET

; Update the status of the Data Out Line ...

OUT_STAT:
        MOV DX, LCR
        IN AL, DX
        AND AL, 01000000xB
        JZ > L1 ; 0 means bit 6 not set which means line is HIGH
        MOV AL, 'L'
        JMP > L2
L1:     MOV AL, 'H'
L2:     CMP AL, OUTNOW
        JZ > O1 ; Out of here if no change
        MOV OUTNOW, AL
        CURSET 050Ah ; row 5, col 10
        CALL PRINT1
O1:     RET

; Toggle the status of Data In line to H

HI:
        CURSET 060Ah ; row 6, col 10
        MOV AL, 'H'
        CALL PRINT1
        RET

; check for BREAK ( data in line low)
; NOTE: you can only read this bit once per BREAK, then it
; clears, and the data in line must go high again and another
; BREAK occur before the bit is set again.

CK_BRK:
        MOV DX, LSR
        IN AL, DX
        CALL ACC_DLY ; 4.10 ADDED
        AND AL, 00010000xB ; bit 4 set means break occurred
        JZ > O1
        CURSET 060Ah
        MOV AL, 'L'
        CALL PRINT1
        RET

; Update the status of the COMMAND line

CMD_STAT:
        MOV DX, MSR
        IN AL, DX
        CALL ACC_DLY ; 4.12
        TEST AL,01000000xB; RI is command line
        LAHF    ; 3.06 - complement ZF if ONECHIP. AH <- FLAGS
        CMP B[REV_FLAG], 'i'
        JNE >L1
        XOR AH, 01000000Xb; complement ZF
L1:     SAHF; FLAGS <- AH
        JE >L1; branch if command is 0 (low)
        MOV AL,'H'
        JMP >L2
L1:     MOV AL,'L'; Command is low
L2:     CMP AL, CMD_LAS
        JZ > O1
        MOV CMD_LAS, AL
        CURSET 040Ah
        CALL PRINT1
O1:     RET


; Put/Get sectors to/from the Atari:

DGETSEX:
        PRINTL NUMSECS
        TICKS 40
L2:     CALL CLR_SCRN
        MOV DS_SCRN, 0 ; initialize screen pointer

L4:	CALL CL_ORKEY ; command line low or keypress
        JNZ > O1 ; 0 flag means cmnd line went low; else - keypressed

; here, command line went low and no keypress ...

L3:	CALL GET_CF ; read in a command frame
        JNC > L5 ; if no error, don't print status
        CALL DO_SS; put status on screen
        JMP L4 ; and try again for command frame
L5:     CALL DS_SECNO ; good CF, then put sector number to field

; now, see if command was write or read

        MOV AL, CMND
        CMP AL, 'W'
        JNE > L6
L7:     CALL DPUTSEC
        CALL DO_SS ; put status to screen
        JMP L4 ; keep looping till user hits a key
L6:     CMP AL, 'P' ; works with Get or Put
        JE L7
        CMP AL, 'R' ; read sector?
        JNE L4 ; not W, P, or R shouldn't be possible
        CALL DGETSEC ; Doesn't return an error status
        CALL DO_SS
        JMP L4
O1:     RET

; Wait for CMND LINE to go low or key to be pressed ..
; Note: when bit 6 of MSR = 0, this means that the RI input is
; logic 1; or command line is HIGH (TTL 1) or, RS-232 line is at
; -10 volts: all mean the same thing. With the ONECHIP design
; the line status is same at PC as at Atari. With the old design
; the RI line is inverse on PC due to inversion on board.

; Returns with 0 flag set if CMND LOW, else 0 flag clear and char
; in AL if key was pressed.

CL_ORKEY:
	CALL IS_A_KEY ; carry clear, no key; else char in AL
	JNC > L2
	LAHF ; put flags in AH
	AND AH, 10111111xB ; clear zero flag i.e. - force not zero condition
        SAHF ; restore flags
        JC > L3 ; forced branch - carry will still be set here

L2:	MOV DX, MSR
	IN AL,DX
        CALL ACC_DLY ; 4.12
	AND AL,01000000xB ; checking RI bit...
        LAHF    ; 3.06 - complement ZF if ONECHIP. AH <- FLAGS
	CMP [REV_FLAG], 'i'
        JNE >L1
        XOR AH, 01000000Xb; complement ZF
L1:	SAHF; FLAGS <- AH - For cmnd line LOW, Zero (equal) results
	JNE CL_ORKEY
L3:     RET

; GET_CF reads in a command frame.  Cmnd line already verified low

GET_CF:
        MOV DX, BAUD ; Read the register
        IN AL,DX;       Throw away this byte...
        CALL ACC_DLY

; First, clear any error bits from LSR: (REV 3.01)

	MOV DX, LSR
	IN AL, DX
        CALL ACC_DLY
;
; Now read characters from the port. Command frame is 5 bytes long.
; First, get 4 bytes, computing checksum...

; First, a routine to time out if no data forthcoming in 1/6 sec

        MOV VARBL,10; 3.06
L2:     MOV DX, LSR
        IN AL,DX        ; First see if a byte is ready
        CALL ACC_DLY
N1:     AND AL,1
        JNZ > L1         ; branch if byte waiting...
        CMP VARBL,0
        JNE L2         ; loop until timed out... REV 3.06
U1:     MOV AL, 'T'
U3:     MOV [DS_FLD+4], AL ; flag that timeout did occur
        JMP EO1 ; go home

        MOV DX, MSR     ; 3.05
        IN AL, DX
        CALL ACC_DLY ; 4.12
;       CALL CK_LINES
        AND AL, 01000000XB
        LAHF
        CMP [REV_FLAG], 'i'
        JNE >L3
        XOR AH, 01000000xB
L3:     SAHF
        JE L2  ; JE MEANS CMND LINE IS LOW
        MOV AL, 'H' ; flag cmnd line didn't stay low
        MOV [DS_FLD+4], AL
        JMP EO1        ; else abort attempt to get cmnd frame

L1:     CALL GET1; Get DEVID
        JC U1 ; means timed out

        MOV DEVID,AL

; get 3 more bytes

        MOV CX,3
        MOV BL,DEVID

L1:     CALL GET1
        JC U1    ; carry set means timed out in GET1
        MOV SI,CX
        MOV [CFCKSM+SI],AL ; Store the command frame bytes
        CLC     ; do checksum math
        ADC BL,AL
        ADC BL,0

        LOOP L1

; Now get checksum and store it...

        CALL GET1
        JC U1
        MOV CFCKSM, AL
        
        CMP BL, CFCKSM
        JE U2           ; Abort the whole thing if bad CKSUM
        MOV AL, 'K'     ; flag bad checksum
        JNE U3          ; forced branch

; 3.05 moved block below. was after checking cmnd line raised:


; At this point, we have successfully read a command frame. Now, wait
; for the computer to raise the command line:

U2:     MOV VARBL, 2; 4.08 - did use BX in a crude counter for timeout
B1:     CMP VARBL, 0
        JNE >L1 ; timed out, cmnd line didn't go high
        MOV AL, 'L'; flag cmnd line didn't go back high
        JE U3   ; forced branch
L1:     MOV DX, MSR
        IN AL,DX
        CALL ACC_DLY ; 4.12
        AND AL,01000000xB
        LAHF    ; 3.06 - complement ZF if ONECHIP. AH <- FLAGS
	CMP [REV_FLAG], 'i'
        JNE >L4
        XOR AH, 01000000xb; complement ZF
L4:     SAHF; FLAGS <- AH
        JE B1
        CLC ; flag good result
        JNC > O3
EO1:     STC ; flag error
O3:     RET

; This routine puts the SECTOR I/O status chars to the screen and
; advances the screen location pointer.  Direct screen writes are used.
; it also fills the source field back with the default chars after each
; write.

DO_SS:
        PUSH ES,SI,DI,AX,CX
        MOV ES, SCR_SEG
        MOV SI, DS_FLD
        MOV DI, DS_SCRN ; current screen ram pointer to field
        MOV CX, 16 ; # of chars to put
S0:     MOV AL, [SI]
        MOV ES:[DI],AL
        INC SI
        INC DI
        INC DI ; skip attribute byte
        LOOP S0
        CMP DI, (25*160 -1)
        JB > S1
        XOR DI, DI ; overflow to top of page
S1:     MOV DS_SCRN, DI ; store pointer to next field
        MOV AX, 'n'*0100H + 'n' ; get nn in AX
        MOV [DS_FLD], AX
        MOV AH, ' '
        MOV [DS_FLD+2], AX
        MOV CX, 5
        MOV DI, DS_FLD+4
        MOV AX, '*'*0100h + '*' ; load ** into AX
S3:     MOV [DI], AX
        INC DI
        INC DI
        LOOP S3
        MOV [DI],AL ; last odd one!
        POP CX, AX, DI, SI, ES
        RET

; Put the sector number in DFAUX1/2 to the DS_FLD field

DS_SECNO:
        MOV DI, OFFSET DS_FLD + 2 ; Point to 1's digit ...
        MOV AL, CFAUX1
        MOV AH, CFAUX2
        CALL INT2DEC
        RET

; ****************** Interrupt driven serial I/O *****************

; A subroutine to set up and enable the interrupt:

S_U_INT:

