h27580
s 00249/00316/00718
d D 1.2 83/03/30 17:37:47 bog 2 1
c Near final:  still spins on Tx_Rdy interrupt.
e
s 01034/00000/00000
d D 1.1 83/03/23 15:29:18 bog 1 0
c date and time created 83/03/23 15:29:18 by bog
e
u
4
U
t
T
I 1
	Title	'HsIBM  IBM PC Communications Driver'

FStDim	Equ	40
EMPTY	Equ	-1
BREAK	Equ	-2
TRUE	Equ	-1
FALSE	Equ	0

D 2
Port	Equ	0	; 0 if port A, 1 if port B
E 2
I 2
Port	Equ	0			; 0 if port A, 1 if port B
E 2

; Uart I/O addresses
D 2
Base_Port	Equ	Word Ptr .00400h+Port; Port base i/o addr at 0000:0400
Data_Port	Equ	000h		; Data read/write
E 2
I 2
Base_Port	Equ	Word Ptr .00400h+Port+Port; Port i/o addr at 0000:0400
E 2
Int_Enable	Equ	001h		; Interrupt enable register
Int_Id		Equ	002h		; Interrupt identification register
Line_Control	Equ	003h		; Line control port
Modem_Control	Equ	004h		; Modem control port
Line_Status	Equ	005h		; Line status register
Modem_Status	Equ	006h		; Modem status register
D 2
Baud_Rate_Low	Equ	Data_Port	; Divisor latch lsb
E 2
I 2
Baud_Rate_Low	Equ	000h		; Divisor latch lsb
E 2
Baud_Rate_High	Equ	Int_Enable	; Divisor latch msb

; Interrupt Enable Register bits
En_Data_Avail	Equ	001h		; Enable Data Available interrupt
En_Tx_Empty	Equ	002h		; Enable Tx Empty interrupt
En_Line_Status	Equ	004h		; Enable Line Status interrupt
En_Modem_Status	Equ	008h		; Enable Modem Status interrupt
I 2
Enable_Initial	Equ	En_Data_Avail+En_Modem_Status
E 2

; Interrupt Identification Register bits
Int_Pending	Equ	001h		; On if interrupt is pending

; Line Control bits
Char_5_Bits	Equ	000h		; 5 bit word length
Char_6_Bits	Equ	000h		; 6 bit word length
Char_7_Bits	Equ	000h		; 7 bit word length
Char_8_Bits	Equ	000h		; 8 bit word length
Stop_Bit	Equ	004h		; On if 1.5 or 2 stop bits
Parity_Enable	Equ	008h		; Enable parity generation and check
Parity_Even	Equ	010h		; Parity even if enabled
Stick_Parity	Equ	020h		; Force parity
Set_Break	Equ	040h		; Force BREAK
Baud_Rate_Hook	Equ	080h		; Enable divisor access

; Modem Control bits
DTR_On		Equ	001h		; Data Set Ready
RTS_On		Equ	002h		; Request to Send
Int_Enable_1	Equ	004h		; OUT1 pin
Int_Enable_2	Equ	008h		; OUT2 pin
Loopback	Equ	010h		; Hook goesout to goesin
I 2
Modem_Initial	Equ	DTR_On+RTS_On+Int_Enable_1+Int_Enable_2
E 2

; Line Status bits
Rx_Rdy		Equ	001h		; Data available
Overrun_Error	Equ	002h		; Not read quickly enough
Parity_Error	Equ	004h		; Parity bit wrong
Framing_Error	Equ	008h		; Stop bit wrong state
Break_Cond	Equ	010h		; BREAK seen
Tx_Rdy		Equ	020h		; Transmit holding register empty
Tx_Shift_Empty	Equ	040h		; Transmit shift register empty


; Modem Status bits
Delta_CTS	Equ	001h		; CTS transition
Delta_DSR	Equ	002h		; DSR transition
Ring		Equ	004h		; Trailing edge of RI seen
Delta_DCD	Equ	008h		; DCD transition
CTS_State	Equ	010h		; CTS state
DSR_State	Equ	020h		; DSR state
RI_State	Equ	040h		; RI state
DCD_State	Equ	080h		; DCD state

D 2
Command_Initial	Equ	027h		; Command register initial value
Set_BREAK	Equ	0ff08h		; Turn on BREAK condition
Reset_BREAK	Equ	0f700h		; Turn off BREAK condition
Set_DTR		Equ	0ff02h		; Turn on DTR line
Reset_DTR	Equ	0fd00h		; Turn off DTR line
Tx_Enable	Equ	0ff01h		; Enable send when go string received
Tx_Disable	Equ	0fe00h		; Disable send when stop string rec'd
Reset_Errors	Equ	0ff10h		; Reset status register errors

DSR_On		Equ	080h		; Data Set Ready
DCD_On		Equ	040h		; Data Carrier Detect
Framing_Error	Equ	020h		; Stop bit wrong state
Overrun_Error	Equ	010h		; Not read quickly enough
Parity_Error	Equ	008h		; Parity bit wrong
Rx_Rdy		Equ	002h		; Receive holding register has data
Tx_Rdy		Equ	001h		; Transmit holding register empty

E 2
; 8259A Interrupt Controller addresses
Master_0	Equ	020h		; Master Even port
Master_1	Equ	021h		; Master Odd port

D 2
EOI		Equ	020h		; End of Interrupt command to Even
E 2
I 2
EOI	Equ	064h-Port	; Specific End of Interrupt command to Even
E 2

I 2
Int_Mask	Equ	010h-(Port*8)	; Port 0 is IRQ4; port 1 is IRQ3

E 2
Master_Int_Base	Equ	008h		; Base of interrupts

Isr_Int		Equ	Master_Int_Base+4-Port; Vector used for interrupts
D 2
IV		Equ	Int_Vector*4
E 2
I 2
IV		Equ	Isr_Int*4
E 2
Int_Vector	Equ	DWord Ptr .IV	; Rs232 vector

Send_Q_Len	Equ	FStDim+40	; 40 entries + recv stop string
Recv_Q_Len	Equ	512		; 512 entries of 2 bytes each
Recv_Q_Stop	Equ	(Recv_Q_Len*3)/4; Send stop string when 3/4 full
Recv_Q_Go	Equ	Recv_Q_Len/8	; Send go string when 1/8 full

	Eject

	Title	'HsRecv  Return a Character from the Input Queue'

HsIBM_Code	CSeg	Byte

MyDs	Dw	Seg	Recv_Queue	; Base of my DSeg

; Called by:
;	Call HsRecv(Ch,Error)
;		Output	Ch:	Integer*2 ADE char in [BREAK,EMPTY,NUL..255]
;			Error:	Integer*2 error code
;				  0:	No error
;				  1:	Parity error
;				  2:	Overrun error
;				  3:	Parity and Overrun
;				  4:	Framing error
;				  5:	Parity and Framing
;				  6:	Overrun and Framing
;				  7:	Parity and Overrun and Framing
;	    Return the next character from the host port or EMPTY if none.
;	    BREAK is returned if the port found a BREAK condition.

Old_Bp		Equ	Word Ptr 0[Bp]	; Caller's frame pointer
Old_Ds		Equ	Word Ptr 2[Bp]	; Caller's DSeg base
Ret_Addr	Equ	DWord Ptr Old_Ds+2
Error_Ptr 	Equ	DWord Ptr Ret_Addr+4
Ch_Ptr		Equ	Dword Ptr Error_Ptr+4

	Public	HsRecv

HsRecv:	Push	Ds		; Save caller's Ds
	Push	Bp		;  & frame
	Mov	Bp,Sp		; Make my frame
	Mov	Ds,MyDs		;  & Ds

	Mov	Dx,0		; Assume no errors

	Mov	Ax,Recv_Count	; Get number of characters in queue
	Or	Ax,Ax		; Are there any characters there?
	Jnz	May_XOn		; Jump if there is a character to return

	Mov	Ax,EMPTY	; Nothing to return

; Ax holds Ch value, Dx holds Error value

Recv_Return:
	Lds	Bx,Ch_Ptr	; Get ptr to caller's Ch
	Mov	Word Ptr 0[Bx],Ax;  & stuff value

	Jmp	Hs_Return	; Common Error stuff and return

May_XOn:
	Test	Flags,InStopped	; Have we sent an XOff?
	Jz	Recv_One	; Jump if no need to see if XOn needed

	Cmp	Ax,Recv_Q_Go	; Count below turnon point?
	Jae	Recv_One	; Jump if not yet ready to send XOn

	Mov	Es,State_Base	; Need base of Current State Common
	Mov	Ax,CIFMod	; Get Input Flagging Mode
	Or	Ax,Ax		; Is input flagging enabled?
	Jz	Recv_One	; Jump if input flagging not enabled

	Call	Host_Go		; Send XOn or turn on DTR to let host go again

	And	Flags,not InStopped	; Input is no longer stopped

	Mov	Dx,0		; Assume no errors

Recv_One:
	Mov	Bx,Recv_Tail	; Get index to next character in queue

	Mov	Ax,Recv_Queue[Bx]; Al <- data byte, Ah <- error flags

	Inc	Bx		; Bump to next char/flag word
	Inc	Bx
	Cmp	Bx,Recv_Wrap	; Set carry if new index did not wrap
	Sbb	Cx,Cx		; Cx <- wrap ? 00000h : 0ffffh
	And	Bx,Cx		; Bx <- wrap ? 00000h : bumped old index
	Mov	Recv_Tail,Bx	;  & save for next call

	Dec	Recv_Count	; Show one character taken

	Xchg	Dl,Ah		; Dx <- error flag, Ax <- char

	Or	Dl,Dl		; BREAK code?
	Jns	Recv_Return	; Return if not

	Mov	Ax,BREAK	; Set BREAK code
	And	Dl,07fh		; Reset BREAK bit

	Jmps	Recv_Return	; Return with character and error flags

	Eject

	Title	'HsSend  Send a Character to the Host'

;	Call HsSend(Ch,Error)		{Far FORTRAN callable}
;		Input	Ch:	Integer*2 ADE character
;				 in [BREAK,NUL..255]
;		Output	Error:	Integer*2 busy flag
;				  -1:	busy; Ch not sent
;				   0:   Ch sent successfully

	Public	HsSend

HsSend:	Push	Ds		; Save caller's DSeg base register
	Push	Bp		; Save caller's frame
	Mov	Bp,Sp		; Make local frame

	Mov	Ds,MyDs		; Point to my DSeg

	Mov	Dx,-1		; Assume queue is full

	Mov	Ax,Send_Count	; Get number of bytes in queue

	Mov	Es,State_Base	; Base of Current State Common

	Cmp	CIFMod,1	; String flagging on input?
	Jne	Tx_Not_String_Flagging; Jump if no

	Add	Ax,CInStL	; Leave room for Recv to send stop string

Tx_Not_String_Flagging:
	Cmp	Ax,Send_Q_Len	; Room in queue for this byte?
	Jae	Hs_Return	; Return "Busy" status in Error if not

	Xor	Dx,Dx		; Return "not BUSY"

	Mov	Bx,Send_Head	; Get offset of next place to enqueue
	Les	Si,Ch_Ptr	; Get ptr to character to send

	Mov	Ax,Es:[Si]	; Get character to send
	Mov	Send_Queue[Bx],Ax; Enqueue character to send

	Inc	Bx		; Bump Head to next slot
	Inc	Bx
	Cmp	Bx,Send_Wrap	; Carry set if NOT wrap past end of queue
	Sbb	Ax,Ax		; Ax = wrap ? 00000h : 0ffffh
	And	Ax,Bx		; Ax = wrap ? 00000h : new Head
	Mov	Send_Head,Ax	; Save new Head index

; New byte now enqueued.  Count can be bumped since the rest of the structure
; is now correct and the byte just enqueued could be dequeued.

	Inc	Send_Count	; Bump number of bytes in queue

D 2
	Cmp	Send_Count,1	; Enqueued to empty queue?
	Ja	Hs_Return	; No need to wake up Tx_Isr if not
E 2
I 2
	Call	Awaken_Tx_Isr	; In case enqueue to empty queue
E 2

D 2
	Call	Wake_Up_Tx_Isr	; Awaken Tx_Isr if needed

E 2
; Dx contains Error

Hs_Return:
	Lds	Bx,Error_Ptr	; Get ptr to Error
	Mov	Ds:(Word Ptr .0)[Bx],Dx	; Return Error to caller

	Mov	Sp,Bp		; Trash local frame
	Pop	Bp		; Restore caller's frame
	Pop	Ds		;  & DSeg base
	Retf	8		; Return, tossing parameters

	Eject

	Title	'HsInit	Initialize Communications Channel'

; Called by:
;	Call HsInit
;		Input	Parameters in Common /GHStat/
;	    Initialize communications channel with parameters in GHStat.

	Public	HsInit

HsInit:	Push	Ds		; Save caller's DSeg base
	Push	Bp		; Save caller's frame
	Mov	Bp,Sp		; Set up local frame

	Mov	Ds,MyDs		; Set up local DSeg base

	Sub	Ax,Ax		; Need a zero

	Mov	Cx,Zero_Clear_Length/2	; Number of words to clear
	Mov	Di,Offset Zero_Clear	; Starting spot
	Push	Ds			; Point to segment base
	Pop	Es			;  of area to clear
	Cld				; Work up in address
    Rep	Stos	Es:Zero_Clear[Di]	; Clear various state variables

	Mov	Es,Ax		; Need access to vectors in bottom of memory

	Mov	Ax,Es:Word Ptr Int_Vector; Save old vector offset
	Mov	Word Ptr Save_Vector,Ax
	Mov	Ax,Es:Word Ptr Int_Vector+2; Save old vector segbase
	Mov	Word Ptr Save_Vector+2,Ax

	Mov	Es:Word Ptr Int_Vector+2,Cs; Point vector to my Isr
D 2
	Mov	Es:Word Ptr Int_Vector,Offset Timer_Isr
E 2
I 2
	Mov	Es:Word Ptr Int_Vector,Offset Isr
E 2

; Fix up Uart registers
	Mov	Dx,Es:Base_Port	; Base I/O address
I 2
	Mov	Data_Port,Dx	;  & save it
E 2

	Mov	Es,State_Base	; Get base of state common

I 2
	Add	Dl,Line_Control	; Point to Line Control port
	Mov	Al,Baud_Rate_Hook; Enable Baud Rate Divisor ports
	Out	Dx,Al		;  via Baud Rate Hook

	Mov	Bx,RBaudI	; Get baud rate index
	Shl	Bx,1		; Double for word index
	Mov	Ax,Baud_Rate-2[Bx]; Get divisor

	Mov	Si,Ax		; Save baud rate divisor for later in Si

	Add	Dl,Baud_Rate_Low-Line_Control; Point to Baud Rate port
	Out	Dx,Al		; Output low byte of divisor
	Inc	Dx		; Point to Baud Rate high port
	Mov	Al,Ah		; High byte to low
	Out	Dx,Al		; Output high byte of divisor

E 2
	Mov	Bx,StoBtI	; Stop bits index
	Mov	Al,Stop_Bits-1[Bx]; Get initial and proper mode bits

	Mov	Bx,CurPar	; Current parity setting
	Or	Al,Parity[Bx]	; Or in proper mode bits

	Mov	Bx,DatBtI	; Data bits index
	Or	Al,Data_Bits-1[Bx]; Or in proper mode bits

D 2
	Add	Dx,Line_Control	; Point to Line Control register
E 2
I 2
	Add	Dl,Line_Control-Baud_Rate_High; Point to Line Control register
	Out	Dx,Al		; Set initial Line Control register
E 2

D 2
	Out	Dx,Al		; Set Line Control register
E 2
I 2
	And	Ax,00fh		; Mask to Data Bits, Stop Bits, Parity
	Mov	Di,Ax		;  into index register
E 2

D 2
	And	Al,not Baud_Rate_Hook; Turn off DLAB
	Push	Ax		; Save Line Control without DLAB
E 2
I 2
	Mov	Al,Modem_Initial; Initial Modem Control register values
	Inc	Dx		; Point to Modem Control register
	Out	Dx,Al		; Set initial Modem Control register
E 2

D 2
	Mov	Bx,RBaudI	; Get baud rate index
	Shl	Bx,1		; Double for word index
	Mov	Ax,Baud_Rate[Bx]; Get divisor
E 2
I 2
	Add	Dl,-Modem_Control; Point at Data Port register
	In	Al,Dx		; Get garbage data
E 2

D 2
	Add	Dx,Baud_Rate_Low-Line_Control; Point to Baud Rate port
	Out	Dx,Al		; Output low byte of divisor
	Inc	Dx		; Point to Baud Rate high port
	Out	Dx,Al		; Output high byte of divisor
E 2
I 2
	Cli			; Disable interrupts while enabling chips
E 2

D 2
	Pop	Ax		; Get Line Control back
	Add	Dx,Line_Control-Baud_Rate_High; Point to Line Control register
	Out	Dx,Al		; Set Line Control without DLAB
E 2
I 2
	Add	Dl,Int_Enable	; Point to Line Control register
	Mov	Al,0		; Disable ALL
	Out	Dx,Al		;  so 8259A will see a transition
	Mov	Al,Enable_Initial; Initial Interrupt Enable register value
	Out	Dx,Al		; Enable interrupts on Uart
E 2

D 2
	Mov	Current_Command,Command_Initial; Initial value for Command
	Mov	Ax,0ff00h	; Nop command change
	Call	New_Command	; Set command register
E 2
I 2
	In	Al,Master_1	; Get 8259A Interrupt Mask register
	And	Al,not Int_Mask	; Turn off mask bit for this port
	Out	Master_1,Al	; Enable this port's interrupt in 8259A
E 2

D 2
	Call	Command_Timer	; Give command to timer
E 2
I 2
	Sti			; Make sure interrupts are enabled
E 2

D 2
	Mov	Si,Offset Init_8259A_Table	; Point to Icws
	Mov	Cx,Init_8259A_Length		; Number of entries
	Call	Control_8259A			; Initialize the 8259As
E 2
I 2
; Compute number of characters required to minimally exceed the BREAK time.
E 2

D 2
	Sti			; Make sure interrupts are enabled
E 2
I 2
;  Compute the number of baud clock tics in 2 characters:
;   tics/bit * bits/char * 2
	Mov	Al,Twice_Bits[Di]; Get twice number of bits per char in Al
	Cbw			;  then in Ax
	Mul	Si		; Now number of tics in 2 chars
	Mov	Si,Ax		; Max is 00600h*018h or 09000h
E 2

I 2

;  Here we first compute the approximate number of tics in a BREAK time
;  in Dx|Ax.
	Mov	Ax,230		; 115.2 baud clock tics per millisecond times
	Mul	CBkTim		;  ms is approximate tics per 2 BREAKs in Dx|Ax

	Dec	Si		; Want lowest number of chars .ge. BREAK time
	Add	Ax,Si		;  so add in before divide
	Adc	Dx,0		;  to get enough BREAK time
	Inc	Si		; Put Si back

	Div	Si		; Divided by (tics/bit)*(bits/char)*2

;  Ax now holds the number of characters in a BREAK time.

	Mov	BREAK_Time,Ax	; Save for use by BREAK


E 2
	Mov	Sp,Bp		; Restore caller's stack frame
	Pop	Bp
	Pop	Ds		; Restore caller's DSeg base
	Retf			;  & return

	Eject

	Title	'HsStop  Close Communications Channel'

; Called by:
;	Call HsStop
;	    Close the open communications channel

	Public	HsStop

HsStop:	Push	Ds		; Save caller's DSeg base
	Push	Bp		; Save caller's frame
	Mov	Bp,Sp		; Set up local frame

	Mov	Ds,MyDs		; Set up local DSeg base
I 2
	Sub	Ax,Ax		; Need access to base segment
	Mov	Es,Ax		; Point Es: to 0000:xxxx
E 2

D 2
	Mov	Ax,Reset_BREAK	; Reset any BREAK condition
	Call	New_Command	;  in the Uart
E 2
I 2
	Mov	Dx,Data_Port	; Get I/O address of port
	Add	Dl,Line_Control	; Point to Line Control register
E 2

D 2
	Call	Command_Timer	; Give command to timer
E 2
I 2
	In	Al,Dx		; Get current Line Control contents
	And	Al,not Set_Break; Turn off BREAK bit
	Out	Dx,Al		;  in Line Control register
E 2

D 2
	Mov	Si,Offset Reset_8259A_Table	; Point to Icws
	Mov	Cx,Reset_8259A_Length		; Number of entries
	Call	Control_8259A			; Reset the 8259As
E 2
I 2
	In	Al,Master_1	; Get 8259A Interrupt Mask register
	Or	Al,Int_Mask	; Turn on mask bit for this port
	Out	Master_1,Al	; Disable this port's interrupt in 8259A
E 2

D 2
	Sub	Ax,Ax		; Need access to vectors
	Mov	Es,Ax		;  in bottom of memory
E 2
I 2
	Mov	Ax,Word Ptr Save_Vector; Restore vector offset
	Mov	Es:Word Ptr Int_Vector,Ax
	Mov	Ax,Word Ptr Save_Vector+2; Restore vector segbase
	Mov	Es:Word Ptr Int_Vector+2,Ax
E 2

D 2
	Mov	Ax,Es:Word Ptr Time_Save_Vector; Restore timer vector offset
	Mov	Word Ptr Time_Int_Vector,Ax
	Mov	Ax,Es:Word Ptr Time_Save_Vector+2; Restore timer vect segbase
	Mov	Word Ptr Time_Int_Vector+2,Ax
	Mov	Ax,Es:Word Ptr Send_Save_Vector; Restore send vector offset
	Mov	Word Ptr Send_Int_Vector,Ax
	Mov	Ax,Es:Word Ptr Send_Save_Vector+2; Restore send vector segbase
	Mov	Word Ptr Send_Int_Vector+2,Ax
	Mov	Ax,Es:Word Ptr Recv_Save_Vector; Restore receive vector offset
	Mov	Word Ptr Recv_Int_Vector,Ax
	Mov	Ax,Es:Word Ptr Recv_Save_Vector+2; Restore recv vector segbase
	Mov	Word Ptr Recv_Int_Vector+2,Ax

E 2
	Mov	Sp,Bp		; Restore caller's stack frame
	Pop	Bp
	Pop	Ds		; Restore caller's DSeg base
	Retf			;  & return

	Eject

D 2
; A byte was enqueued to and empty Tx queue.  If Tx_Rdy is not true, the
; interrupt when it goes true will send the character; if Tx_Rdy is already
; true, awaken Isr so it will send character.
Wake_Up_Tx_Isr:
	In	Al,Status_Port	; Get port status
	Test	Al,Tx_Rdy	; Ready for transmit?
	Jz	Wake_Up_Quit	; If not ready, ready interrupt will find char

	Cli			; Disable interrupts so Tx_Isr cannot sneak in
	Mov	Al,Current_Command; Get the command in use
	And	Al,not Tx_Enable; Disable Tx to turn of Tx_Rdy bit
	Out	Command_Port,Al	; Turn off Tx_Rdy bit
	Or	Al,Tx_Enable	; Re-enable Tx to turn Tx_Rdy back on to
	Out	Command_Port,Al	;  force Tx_Isr interrupt
	Sti			; Re-enable interrupts

Wake_Up_Quit:
	Ret			; Return to caller with Tx_Isr awakened

	Eject

E 2
	Title	'Host_Go/Stop  Set/Reset DTR or Send Go/Stop String to Host'

Host_Go:
	Dec	Ax		; Ax <- 0 if string flagging, 1 if h/w flag
	Jnz	Host_Set_DTR	; Go set DTR if hardware flagging

	Mov	Cx,CInGoL	; Get length of Go string
	Jcxz	Host_Go_Quit	; Quit if Go string is zero length

	Mov	Bx,Offset CInGoS; Start with first character

Host_Go_Loop:
	Push	Cx		; Save loop counter

Host_Go_Busy:
	Push	Es		; Save Es
	Push	Bx		;  & Bx
	Push	Es		; Call HsSend(Char,Error)
	Push	Bx
	Push	Ds
	Mov	Ax,Offset Error
	Push	Ax
	Push	Cs		; Make a Callf from a Call
	Call	HsSend		; Enqueue one go string character
	Pop	Bx		; Restore base and offset of byte to send
	Pop	Es

	Mov	Ax,Error	; Was character enqueued?
	Or	Ax,Ax		; Error returns busy
	Jnz	Host_Go_Busy	; Jump if busy

	Inc	Bx		; Bump to next char to send
	Inc	Bx
	Pop	Cx		; Restore loop counter
	Loop	Host_Go_Loop	;  & loop until all string sent
	Ret			; Return when go string sent

Host_Set_DTR:
D 2
	Mov	Ax,Set_DTR	; Code to set Data Terminal Ready
E 2
I 2
	Mov	Al,Modem_Initial; Code to set Data Terminal Ready
E 2

Host_Set_Reset_DTR:
D 2
	Call	New_Command	; Set/reset DTR
E 2
I 2
	Push	Dx		; Save caller's Dx
	Mov	Dx,Data_Port	; Point to data port
	Add	Dl,Modem_Control; Point to Modem Control register
	Out	Dx,Al		; Set/Reset DTR
	Pop	Dx		; Restore caller's Dx
E 2

Host_Go_Quit:
	Ret			;  & return

Host_Reset_DTR:
D 2
	Mov	Ax,Reset_DTR	; Code to reset Data Terminal Ready
E 2
I 2
	Mov	Al,Modem_Initial and not DTR_On; Data Terminal Ready reset
E 2
	Jmps	Host_Set_Reset_DTR; Go reset DTR

; This is called only by the Rx Isr.  Since it is of higher priority than
; the Tx Isr, it need not run with interrupts disabled.  If that assumption
; should change, though, then the enqueuing of the stop string backwards on
; the tail of the send queue should be done such that the Tx Isr cannot
; interrupt it.
; Dx is not crashed.
Host_Stop:
	Dec	Ax		; Ax <- 0 if string flagging, 1 if h/w flag
	Jnz	Host_Reset_DTR	; Go reset DTR if hardware flagging

; Es already set
	Mov	Cx,CInStL	; Get length of stop string
	Jcxz	Host_Go_Quit	; Quit if empty

	Mov	Si,Cx		; Start with last character
	Add	Si,Cx		;  in string

Host_Stop_Loop:
	Mov	Ax,CInStS-2[Si]	; Get a byte of stop string

	Mov	Bx,Send_Tail	; Get index of tail of send queue
	Dec	Bx		; Point to next tail spot
	Dec	Bx
	Jnc	Host_Stop_Stuff	; Jump if now queue wrap
	Mov	Bx,Send_Wrap-2	; Point to last slot if wrap

Host_Stop_Stuff:
	Mov	Send_Queue[Bx],Ax; Enqueue the stop string character
	Mov	Send_Tail,Bx	; Save updated tail pointer
	Inc	Send_Count	;  & bump number of characters to send
	Loop	Host_Stop_Loop	; Loop until entire stop string enqueued

D 2
	Or	Flags,InStopped	; Tell HsRecv that host is stopped
E 2
I 2
	Call	Awaken_Tx_Isr	; In case enqueued to empty queue
E 2

D 2
	Call	Wake_Up_Tx_Isr	; Awaken transmit if not already awake

E 2
	Ret			; Then return

	Eject

D 2
	Title	'RxIsr   Receive Interrupt Service Routine'
E 2
I 2
	Title	'Awaken Tx Isr'
E 2

D 2
Rx_Isr:	Push	Ax		; Need a register
	Call	Isr_Entry	; Save interrupted routine's registers
E 2
I 2
; Enable Tx_Rdy interrupt in case not already enabled.
; Only Ax crashed.
Awaken_Tx_Isr:
	Push	Dx		; Save caller's Dx
E 2

D 2
	In	Al,Status_Port	; Get Uart status
	Test	Al,Rx_Rdy	; Character ready to be read?
	Jz	Isr_Exit	; Ignore if no character to read
E 2
I 2
	Mov	Dx,Data_Port	; Get I/O address of data port
	Add	Dl,Int_Enable	; Point to Interrupt Enable register
	Mov	Al,En_Data_Avail+En_Tx_Empty+En_Modem_Status
	Out	Dx,Al		; Enable Tx_Rdy interrupt if not already on
E 2

D 2
	Mov	Ah,Al		; Save status in Ah
	In	Al,Data_Port	; Get data byte
	Mov	Dx,Ax		; Dh <- status, Dl <- data
E 2
I 2
	Pop	Dx		; Restore caller's Dx
	Ret			; Return with only Ax changed
E 2

D 2
	Cli			; Disable interrupts
	Mov	Al,Reset_Errors	; Bit to reset errors in Uart status
	Or	Al,Current_Command; Include current command bits
	Out	Command_Port,Al	; Reset error bits in status register
	Sti			; Turn interrupts on again
E 2
I 2
	Eject
E 2

D 2
	And	Dh,038h		; Isolate Framing, Overrun, Parity errors
E 2
I 2
	Title	'Rx_Isr   Receive Interrupt Service Routine'
E 2

D 2
	Test	Dh,Framing_Error; BREAK is seen as FE and Nul
	Jz	Rx_Not_BREAK	; Jump if not possible BREAK
	Or	Dl,Dl		; Nul?
	Jnz	Rx_Not_BREAK	; Jump if not a BREAK character
E 2
I 2
Rx_Isr:	And	Al,Overrun_Error+Parity_Error+Framing_Error+Break_Cond
	Cbw			; High byte of status zero
	Mov	Bx,Ax		;  into Bx
E 2

D 2
	Or	Dh,004h		; BREAK bit will rotate to Dh[7]
	And	Dh,not Framing_Error; Turn of FE bit
E 2
I 2
	Add	Dl,-Line_Status	; Point back to data port
	In	Al,Dx		; Get data byte
	Mov	Dx,Ax		; Dh <- status, Dl <- data
E 2

D 2
Rx_Not_Break:
	Ror	Dh,1		; Shift Framing, Overrun and Parity
	Ror	Dh,1		;  errors to Dh[2..0];
	Ror	Dh,1		;  BREAK to Dh[7];
E 2
I 2
	Ror	Bx,1		; Status bits to right end of Bx
	Mov	Dh,Queued_Status[Bx]; Get encoded status bits
E 2

	Mov	Al,Flags	; Get saved Flags byte
	And	Al,Overrun	; Isolate Overrun bit from recognizer
	Or	Dh,Al		; Include in returned Overrun

; Dh[7] is BREAK, Dh[2] is Framing, Dh[1] is Overrun, Dh[0] is Parity
; Dl is data byte if not BREAK

	Cmp	COFMod,1	; String output flagging?
	Jne	No_Look_for_Match; Skip looking for match if not

	Mov	Di,Matched	; Get local number of bytes matched so far
	Sub	Si,Si		; Start rescan stack empty

	Mov	Bx,Offset COtStL; Assume looking for stop string
	Test	Flags,OutStopped; Looking for go string?
	Jz	Look_for_Match	; Jump if not
	Mov	Bx,Offset COtGoL; Looking for go string

; Bx holds the offset to either COtStL or COtGoL, depending on OutStopped.
; Di holds the contents of Matched (see below) inside this context.
; Si holds Match_Tos inside this context.
Look_for_Match:
	Call	Check_for_Match	; Check Dx for match
	Call	Pop_ReScan	; Pop rescan stack if any
	Jnz	Look_for_Match	; Check again if rescan stack not empty

	Mov	Matched,Di	; Set global number of bytes matched
	Jmps	Isr_Exit	;  & return until next char comes in

No_Look_for_Match:
	Call	Rx_Data_Stuff	; Stuff the character if there's room
I 2
	Jmps	Isr_Exit	;  then go check on Tx_Rdy
E 2

D 2
	Eject
E 2
I 2
	Title	'Isr     Interrupt Service Routine'
E 2

D 2
	Title	'Isr Exit'
E 2
I 2
; There are 3 possible sources for an interrupt:
;  a.  Character received from line, possibly with errors
;  b.  Transmit holding register empty
;  c.  Transition in CTS, DSR, RI or DCD
E 2

D 2
Isr_Exit:
	Cli			; Disable interrupts while futzing with 8259A
	Mov	Al,EOI		; End of Interrupt command
	Out	Slave_0,Al	; Tell slave End of Interrupt
E 2
I 2
; Source (a) is serviced by Rx_Isr.
; Sources (b) and (c) are serviced by Tx_Isr.
E 2

D 2
	Mov	Al,RIS		; Read IS register command
	Out	Slave_0,Al	; Select IS register
E 2
I 2
; Note that none of them can interrupt each other.
E 2

D 2
	In	Al,Slave_0	; Read IS register
	Or	Al,Al		; Anything in service?
	Jnz	No_EOI_Master	; If so, do not EOI master
E 2
I 2
Isr:	Sti			; Enable higher-priority interrupts
E 2

D 2
	Mov	Al,EOI		; End of Interrrupt command
	Out	Master_0,Al	; Tell master End of Interrupt
E 2
I 2
	Push	Ax		; Save interrupted routine's registers
	Push	Bx
	Push	Cx
	Push	Dx
	Push	Bp
	Push	Si
	Push	Di
	Push	Ds
	Push	Es
E 2

D 2
No_EOI_Master:
E 2
I 2
	Cld			; Work up in address

	Mov	Ds,MyDs		; Point to my DSeg
	Mov	Es,State_Base	; Point to /GHStat/ common

Isr_Exit:			; Look again
	Mov	Dx,Data_Port	; Point to data port
	Add	Dl,Int_Id	; Point to Interrupt Identification register
	In	Al,Dx		; Unload Interrupt Identification
	Add	Dl,Modem_Status-Int_Id; Point to Modem Status register
	In	Al,Dx		; Get Modem Status
	Mov	Ah,Al		;  into Ah
	Add	Dl,Int_Enable-Modem_Status; Point to Interrupt Enable register
	In	Al,Dx		; Get Interrupt Enable register
	Mov	Bh,Al		;  into Bh
	Add	Dl,Line_Status-Int_Enable; Point to Line Status register
	In	Al,Dx		; Get Line Status

	Test	Al,Rx_Rdy	; Character ready to read?
	Jnz	Rx_Isr		; Read the character if ready

	Test	Bh,En_Tx_Empty	; Tx_Rdy enabled?
	Jz	Isr_Quit	; Don't look at Tx_Rdy if disabled

	Test	Al,Tx_Rdy	; Ready to send a character?
	Jnz	Tx_Isr		; Try to send if so

Isr_Quit:
	Cli			; Disable while enabling chips
	Mov	Dx,Data_Port	; Point to Data Port
	Add	Dl,Int_Enable	;  then to Interrupt Enable register
	In	Al,Dx		; Get current enables
	Mov	Ah,Al		; Save in Ah
	Mov	Al,0		; Disable EVERYTHING
	Out	Dx,Al		;  so pendings will be serviced
	Mov	Al,Ah		; Get correct enables again
	Out	Dx,Al		;  back to Interrupt Enable register

	Mov	Al,EOI		; Specific End Of Interrupt command
	Out	Master_0,Al	; End interrupt

E 2
	Pop	Es		; Restore interrupted routine's registers
	Pop	Ds
	Pop	Di
	Pop	Si
	Pop	Bp
	Pop	Dx
	Pop	Cx
	Pop	Bx
	Pop	Ax
	IRet			; Return from Interrupt

	Eject

	Title	'TxIsr   Transmit Interrupt Service Routine'

D 2
Tx_Isr:	Push	Ax		; Need a register
	Call	Isr_Entry	; Save interrupted routine's registers
E 2
I 2
Tx_Isr:	Mov	Cx,BREAK_Down	; BREAK running?
	Jcxz	Tx_Not_BREAK	; Jump if not sending BREAK
E 2

D 2
	In	Al,Status_Port	; Look at Uart status
	Test	Al,Tx_Rdy	; Ready for transmit?
	Jz	Isr_Exit	; Quit if not ready
E 2
I 2
	Add	Dl,Line_Control-Line_Status; Point to Line Control register
	In	Al,Dx		; Get current Line Control value
	Or	Al,Set_BREAK	; Turn on BREAK condition
E 2

D 2
	Mov	Cx,Send_Count	; Get number of bytes in queue
	Jcxz	Isr_Exit	; Jump if nothing to send
E 2
I 2
	Loop	Tx_More_BREAK	; Jump if BREAK continuing (Jmp if Cx.ne.1)
E 2

D 2
	Test	Current_Command,(Set_BREAK and 255); BREAK running?
	Jnz	Isr_Exit	; Wait until BREAK is done before sending
E 2
I 2
	And	Al,not Set_BREAK; Turn off BREAK bit
E 2

D 2
	Test	Flags,OutStopped; Host requested that we stop sending?
	Jz	Tx_Send		; Nope; send a character
E 2
I 2
Tx_More_BREAK:
	Out	Dx,Al		; Turn off BREAK condition
	Jmps	Tx_Send_BREAK	; One last BREAK character
E 2

D 2
	Mov	Ax,Tx_Disable	; Disable transmitter until COtGoS received
	Call	New_Command	; Rx_Isr will re-enable, which will interrupt
E 2
I 2
Tx_Not_BREAK:
	Test	Ah,CTS_State	; CTS?
	Jz	Sleep_Tx_Rdy	; Stop Tx_Rdy from interrupting
E 2

D 2
; Possible deadlock:
;  Tx_Isr tests OutStopped, finds it set
;  Rx_Isr resets OutStopped, sets Tx_Enable
;  Tx_Isr sets Tx_Disable...  H A N G
E 2
I 2
	Test	Flags,OutStopped; Host stopped us?
	Jnz	Sleep_Tx_Rdy	; Stop Tx_Rdy from interrupting
E 2

D 2
	Test	Flags,OutStopped; Deadlock avoidance check after disable
	Jnz	Isr_Exit	; Leave disabled if Rx_Isr did not see COtGoS
E 2
I 2
	Mov	Ax,Send_Count	; Get number of bytes in queue
	Dec	Ax		; Decrement queue byte count
	Js	Sleep_Tx_Rdy	; Was empty -- stop Tx_Rdy from interrupting
E 2

D 2
	Mov	Ax,Tx_Enable	; Enable in case receive enable got cancelled
	Call	New_Command	;  by above Tx_Enable
E 2
I 2
	Mov	Send_Count,Ax	; Save decremented byte count
E 2

D 2
Tx_Send:
	Dec	Send_Count	; Decrement queue byte count

	Cli			; Disable interrupts while mucking with Tail *
	Mov	Bx,Send_Tail	; Get offset of next item to send	     *
	Mov	Ax,Send_Queue[Bx]; Get next item to send		     *
	Inc	Bx		; Bump tail to next slot		     *
E 2
I 2
	Mov	Bx,Send_Tail	; Get offset of next item to send
	Mov	Ax,Send_Queue[Bx]; Get next item to send
	Inc	Bx		; Bump tail to next slot
E 2
	Inc	Bx
D 2
	Cmp	Bx,Send_Wrap	; Carry set if NOT wrap past end of queue    *
	Sbb	Cx,Cx		; Cx <- wrap ? 00000h : 0ffffh		     *
	And	Cx,Bx		; Cx <- wrap ? 00000h : new Tail	     *
	Mov	Send_Tail,Cx	; Save new tail				     *
	Sti			; Re-enable interrupts			     *
E 2
I 2
	Cmp	Bx,Send_Wrap	; Carry set if NOT wrap past end of queue
	Sbb	Cx,Cx		; Cx <- wrap ? 00000h : 0ffffh
	And	Cx,Bx		; Cx <- wrap ? 00000h : new Tail
	Mov	Send_Tail,Cx	; Save new tail
E 2

	Cmp	Ax,BREAK	; BREAK code?
D 2
	Je	Tx_BREAK	; Yes -- send a BREAK
E 2
I 2
	Jne	Tx_Send_Al	; No -- send the character
E 2

D 2
	Out	Data_Port,Al	; Send new byte
E 2
I 2
	Mov	Cx,BREAK_Time	; Get char count
	Jcxz	Tx_Isr_Exit	; Quit if no BREAK
E 2

D 2
	Jmps	Isr_Exit	; Return from interrupt
E 2
I 2
Tx_Send_BREAK:
	Mov	BREAK_Down,Cx	; Set BREAK counter
	Mov	Al,0		; Send all zeros
E 2

D 2
	Eject
E 2
I 2
Tx_Send_Al:
	Mov	Dx,Data_Port	; Point to Data Port
	Out	Dx,Al		; Send a byte
E 2

D 2
; Send a BREAK
Tx_BREAK:
	Mov	Cx,CBkTim	; Get BREAK time requested
	Jcxz	Isr_Exit	; Quit if no BREAK
E 2
I 2
Tx_Isr_Exit:
	Mov	Ax,Send_Count	; Anything left to send?
	Or	Ax,BREAK_Down	; Sending BREAK?
	Jnz	Isr_Exit_0	; More to do -- leave interrupt enabled
E 2

D 2
	Mov	BREAK_Time,Cx	; Set Isr count down
E 2
I 2
Sleep_Tx_Rdy:
	Mov	Dx,Data_Port	; Point to Data Port
	Add	Dl,Int_Enable	;  then to Interrupt Enable register
	Mov	Al,En_Data_Avail+En_Modem_Status; Disable
	Out	Dx,Al		;  Tx_Rdy interrupt
E 2

D 2
	Mov	Ax,Set_BREAK	; Mask for setting BREAK
	Call	New_Command	; Send new Command to port

	Call	Load_BREAK_Timer; Start the BREAK timer

E 2
Isr_Exit_0:
D 2
	Jmp	Isr_Exit	; Return, waiting for end of BREAK
E 2
I 2
	Jmp	Isr_Exit	; Quit until next interrupt
E 2

	Eject

D 2
	Title	'Isr Entry'

Isr_Entry:
	Sti			; Enable higher-priority interrupts

	Pop	Ax		; Return address in Ax

	Push	Bx		; Save interrupted routine's registers
	Push	Cx
	Push	Dx
	Push	Bp
	Push	Si
	Push	Di
	Push	Ds
	Push	Es

	Mov	Ds,MyDs		; Point to my DSeg
	Mov	Es,State_Base	; Point to /GHStat/ common

	Jmp	Ax		; Return with registers saved

	Eject

	Title	'Timer_Isr  BREAK Timer Interrupt Service Routine'

Timer_Isr:
	Push	Ax		; Need a register
	Call	Isr_Entry	; Save registers, enable inteerrupts

	Sub	Break_Time,10	; Decrement time another 10 milliseconds
	Jnc	Isr_Exit_0	; Jump if more time to count

	Call	Command_Timer	; Give command to timer

	Mov	Ax,Reset_BREAK	; Turn off
	Call	New_Command	;  BREAK state in Uart

	Call	Wake_Up_Tx_Isr	; Awaken transmit if necessary

	Jmps	Isr_Exit_0	; Return from Timer interrupt

	Eject

E 2
	Title	'Match Check Received Data for Stop/Go String'

; Matched is the number of bytes matching so far in the stop or go string,
;  i.e. how deep into the match we are.  HsInit clears it and it is kept up to
;  date here.  Its local copy is in Di.
; Upon entry Dh contains the new error code and Dl contains the new data byte.

Check_for_Match:
	Shl	Di,1		; Matched -- word index
	Mov	Al,Es:Byte Ptr .2[Bx+Di]; Get byte to match
	Shr	Di,1		; Matched -- byte index
	Cmp	Dl,Al		; Match against COtStS or COtGoS?
	Jne	Is_No_Match	; Jump if not a match

	Mov	Error_Save[Di],Dh; Save error code in case later unmatch

	Inc	Di		; Matched another byte

	Cmp	Di,Es:Word Ptr .0[Bx]; Full match against stop or go string?
	Jb	Check_for_Match_Quit; Look some more if not yet full match

	Xor	Flags,OutStopped; Toggle output stopped/going switch
	Sub	Di,Di		; Start with no bytes matching

D 2
	Test	Flags,OutStopped; Now stopped or going?
	Jnz	Check_for_Match_Quit; Quit if now stopped
E 2
I 2
	Call	Awaken_Tx_Isr	; Tell Tx Isr about OutStopped transition
E 2

D 2
	Mov	Ax,Tx_Enable	; If now going, must re-enable transmit
	Call	New_Command	;  by turning on Tx_Enable bit in command

E 2
Check_for_Match_Quit:
	Ret			; Return to try next character

; Dh and Dl contain a character which did not match.  Di (local Matched)
; contains a count of characters in COtStS or COtGoS and Error_Save which
; must be rescanned for possible matches.
; Push Dx, nth thru 2nd chars in COtStS/COtGoS and Error_Save on Match_Stack.
; Deliver 1st char from COtStS/COtGoS and Error_Save to Recv_Queue.
; Then characters in Match_Stack will be rescanned for potential matches.
Is_No_Match:
	Dec	Si		; Decrement local rescan Tos
	Dec	Si

	Mov	Match_Stack[Si],Dx; Push character to be rescanned

	Dec	Di		; See if more to push
	Js	Is_No_Match_Quit; Jump if just pushed 1st match char

	Shl	Di,1		; Matched -- word index
	Mov	Dl,Es:Byte Ptr .2[Bx+Di]; Get the previous match character
	Shr	Di,1		; Matched -- byte index
	Mov	Dh,Error_Save[Di];  and its error code
	Jmps	Is_No_Match	;  & go push it

Is_No_Match_Quit:
	Inc	Di		; No characters now match
	Call	Pop_ReScan	; Pop 1st character from rescan stack
	Push	Bx		; Preserve local stop/go pointer from stuffer
	Call	Rx_Data_Stuff	; Stuff it in the queue
	Pop	Bx		; Restore local stop/go pointer
	Ret			;  & return for rescan if any

; Pop Dx from Match_Stack. Z set and no pop if stack was empty.
Pop_ReScan:
	Or	Si,Si		; Rescan stack already empty?
	Jz	Pop_ReScan_Quit	; Return if so

	Mov	Dx,Match_Stack[Si]; Get item to pop
	Inc	Si		; Increment for pop
	Inc	Si
	Or	Bx,Bx		; Clear Z bit

Pop_ReScan_Quit:
	Ret			; Return with Z or popped item in Dx

	Eject

	Title	'Rx_Data_Stuff  Stuff a Received Byte in the Recv_Queue'

Rx_Data_Stuff:
	Cmp	Recv_Count,Recv_Q_Stop; At or above XOff level?
	Jb	No_XOff		; Jump if not

	Test	Flags,InStopped	; Already set XOff?
	Jnz	No_XOff		; Jump if so

	Mov	Ax,CIFMod	; Get input flagging mode switch
	Or	Ax,Ax		; Input flagging enabled?
	Jz	No_XOff		; Jump if no

	Call	Host_Stop	; Send stop string or reset DTR
I 2
	Or	Flags,InStopped	; Tell HsRecv that host is stopped
E 2

No_XOff:
	Mov	Ax,Recv_Count	; Get number of items in receive queue
	Cmp	Ax,Recv_Q_Len	; Is queue already full?
	Jb	Rx_Stuff_It	; Go stuff in queue if not

	Or	Flags,Overrun	; Turn on Overrun bit for next stuff
	Jmps	Rx_Stuff_Quit	;  & return, trashing data

Rx_Stuff_It:
	And	Flags,not Overrun; Turn off overrun bit for next time

	Mov	Bx,Recv_Head	; Get offset of next spot to enqueue

	Mov	Recv_Queue[Bx],Dx; Enqueue the character

	Inc	Bx		; Bump Head to next slot
	Inc	Bx
	Cmp	Bx,Recv_Wrap	; Does queue wrap around? Carry if NOT
	Sbb	Ax,Ax		; Ax <- wrap ? 00000h : 0ffffh
	And	Ax,Bx		; Ax <- wrap ? 00000h : new slot #
	Mov	Recv_Head,Ax	; Stuff new slot offset

	Inc	Recv_Count	; Bump number of items in receive queue

Rx_Stuff_Quit:
	Ret			; Return after stuffing or tossing a byte

	Eject

D 2
	Title	'I/O Control Subroutines'

Control_8259A:
	Pushf			; Preserve caller's interrupt bit
	Cli			; Make sure interrupts are off
	Cld			; Work up in address
	Sub	Dh,Dh		; High half of address is 00000h 

C_Loop:	Lods	Ds:(Word Ptr .0)[Si]; Ah <- address, Al <- value
	Mov	Dl,Ah		; Dl <- port address
	Out	Dx,Al		; Send control byte
	Loop	C_Loop		;  & loop until all sent

	Popf			; Restore caller's interrupt bit
	Ret			;  & return

; Send new command to Uart
New_Command:
	Cli			; Disable ints while mucking with command
	Or	Al,Current_Command; Get current command with new bit
	And	Al,Ah		; Turn off bits if needed
	Mov	Current_Command,Al; Save back new command
	Out	Command_Port,Al	; Set new command condition
	Sti			; Re-enable interrupts
	Ret			;  & return

; Start the BREAK time counting
Load_BREAK_Timer:
	Call	Command_Timer	; Give command to timer
	Mov	Ax,20000	; 10 ms. count
	Out	Timer_Data,Al	; Low byte goes out
	Mov	Al,Ah		;  then
	Out	Timer_Data,Al	;  high byte
	Ret			; Return to caller

Command_Timer:
	Mov	Al,Counter_Mode	; Select counter 2, Mode 2
	Out	Timer_Control,Al; Tell timer chip
	Ret

	Eject

E 2
Host_Data	DSeg	Word

State_Base	Dw	Seg COpen	; Base of /GHStat/ Common

D 2
BREAK_Time	Rw	1		; Down counter for BREAK timing
E 2
I 2
BREAK_Time	Rw	1		; Stuffed by HsInit
E 2

Error		Rw	1		; Local recursion Error spot

D 2
Time_Save_Vector	Rd	1	; Save area for old Timer vector
Send_Save_Vector	Rd	1	; Save area for old Tx vector
Recv_Save_Vector	Rd	1	; Save area for old Rx vector
E 2
I 2
Save_Vector	Rd	1	; Save area for old Timer vector
E 2

		Rw	FStDim		; Rescan stack for stop/go string
Match_Stack	Rw	0

Error_Save	Rb	FStDim		; Error code save area for match buff

Zero_Clear	Rw	0		; HsInit clears this area

I 2
BREAK_Down	Rw	1		; Character down counter for BREAK

Data_Port	Rw	1		; I/O address of data port

E 2
Matched		Rw	1		; Number of bytes matching COtSt/COtGo

Flags		Rb	1		; Various state flags
 InStopped	Equ	001h		; XOff has been sent to host
 Overrun	Equ	002h		; Receive queue overflow
 OutStopped	Equ	004h		; Sending currently stopped

; Head is offset at which next byte would be enqueued by HsSend or Recv_Isr
; Tail is offset at which next byte is available for HsRecv or Send_Isr
; Note that when Recv_Isr needs to send the Stop_String, it enqueues it
;  (backwards) on the Tail, thus being sent out before anything else.

; The queue is empty if Count is 0 and full if Count = Max

Recv_Head	Rw	1		; Offset from start of queue
Recv_Tail	Rw	1
Recv_Count	Rw	1		; Number of items in queue

Send_Head	Rw	1		; Offset from start of queue
Send_Tail	Rw	1
Send_Count	Rw	1		; Number of bytes in queue

Zero_Clear_Length	Equ	((Offset $)-(Offset Zero_Clear))

Recv_Queue	Rw	Recv_Q_Len	; Byte of data and byte of error flags
Recv_Wrap	Equ	Recv_Q_Len*2	; 1st slot beyond queue

Send_Queue	Rw	Send_Q_Len	; Word of data or BREAK
Send_Wrap	Equ	Send_Q_Len*2	; First slot beyond queue

	Eject

D 2
; Uart Mode and Command bits
Stop_Bits	Db	Mode1_Initial+040h	; 1 stop bit
		Db	Mode1_Initial+080h	; 1 1/2 stop bits
		Db	Mode1_Initial+0c0h	; 2 stop bits
Parity		Db	000h,010h,030h	; None, odd, even parity in Mode 1
Data_Bits	Db	000h,004h,008h,00ch; 5, 6, 7, 8 data bits in Mode 1
E 2
I 2
; Uart control bytes
Baud_Rate	Dw	00600h,00417h,00359h,00300h,00180h
		Dw	000c0h,00060h,00040h,0003ah,00030h
		Dw	00020h,00018h,00010h,0000ch,00006h
E 2

D 2
Current_Command	Rb	1	; Current Uart Command
E 2
I 2
Stop_Bits	Db	000h		; 1 stop bit
		Db	004h		; 2 stop bits (1.5 if 5 data bits)
E 2

D 2
; Interrupt Controller Commands
Init_8259A_Table	Rw	0
	Db	011h,Slave_0		; Icw1:  Edge, cascade, Icw4
	Db	Slave_Int_Base,Slave_1	; Icw2:  Rx, Tx interrupt numbers
	Db	007h,Slave_1		; Icw3:  Slave is on IR7 of master
	Db	001h,Slave_1		; Icw4:  Non-buff, not auto EOI, 86
	Db	037h,Slave_1		; Ocw1:  Enable Timer2, Tx_Rdy, Rx_Rdy
	Db	0c6h,Slave_0		; Ocw2:  Set Tx_Rdy lowest priority
	Db	019h,Master_0		; Icw1:  Edge, cascade, Icw4
	Db	Master_Int_Base,Master_1; Icw2:  Master interrupt numbers
	Db	080h,Master_1		; Icw3:  Only IR7 has a slave
	Db	011h,Master_1		; Icw4:  Sp full nest, ~buff, ~aEOI
	Db	07fh,Master_1		; Ocw1:  Mask all masters except slave
Init_8259A_Length	Equ	((Offset $)-(Offset Init_8259A_Table))/2
E 2
I 2
Parity		Db	000h		; No parity
		Db	008h,018h	; Odd parity, even parity
		Db	038h,028h	; Forced low, forced high
E 2

D 2
Reset_8259A_Table	Rw	0
	Db	019h,Slave_0		; Icw1:  Level, cascade, Icw4
	Db	Slave_Int_Base,Slave_1	; Icw2:  Rx, Tx interrupt numbers
	Db	007h,Slave_1		; Icw3:  Slave is on IR7 of master
	Db	001h,Slave_1		; Icw4:  Non-buff, not auto EOI, 86
	Db	0ffh,Slave_1		; Ocw1:  Mask all interrupts
	Db	0c6h,Slave_0		; Ocw2:  Set Tx_Rdy lowest priority
	Db	019h,Master_0		; Icw1:  Level, cascade, Icw4
	Db	Master_Int_Base,Master_1; Icw2:  Master interrupt numbers
	Db	080h,Master_1		; Icw3:  Only IR7 has a slave
	Db	011h,Master_1		; Icw4:  Sp full nest, ~buff, ~aEOI
	Db	0ffh,Master_1		; Ocw1:  Mask all interrupts
Reset_8259A_Length	Equ	((Offset $)-(Offset Reset_8259A_Table))/2
E 2
I 2
Data_Bits	Db	000h,001h,002h,003h; 5, 6, 7, 8 data bits
E 2

I 2
Queued_Status	Db	000h,002h,001h,003h,004h,006h,005h,007h
		Db	080h,082h,080h,082h,080h,082h,080h,082h

Twice_Bits	Db	5+5+4,6+6+4,7+7+4,8+8+4; Data, 1 stop, no parity
		Db	5+5+5,6+6+6,7+7+6,8+8+6; Data, 1.5 or 2 stop, no parity
		Db	5+5+6,6+6+6,7+7+6,8+8+6; Data, 1 stop, parity
		Db	5+5+7,6+6+8,7+7+8,8+8+8; Data, 1.5 or 2 stop, parity

E 2
	Eject

@GHStat	ESeg	Para	Common

; Current state of host interface

COpen	Dw	0		; NOT currently open
RBaudI	Rw	1		; Current receive baud rate index
CRBaud	Rw	1		; Current receive baud rate
TBaudI	Rw	1		; Current transmit baud rate index
CTBaud	Rw	1		; Current transmit baud rate
CurPar	Rw	1		; Current parity setting
DatBtI	Rw	1		; Current data bits per char index
CDatBt	Rw	1		; Current data bits per character
StoBtI	Rw	1		; Current stop bits per character index
CStoBt	Rw	1		; Current stop bits per character
CBkTim	Rw	1		; Current BREAK milliseconds
CIFMod	Rw	1		; Current receive flagging mode
COFMod	Rw	1		; Current transmit flagging mode
CInStL	Rw	1		; Current receive stop string length
CInStS	Rw	FStDim		; Current receive stop string
CInGoL	Rw	1		; Current receive go string length
CInGoS	Rw	FStDim		; Current receive go string
COtStL	Rw	1		; Current transmit stop string length
COtStS	Rw	FStDim		; Current transmit stop string
COtGoL	Rw	1		; Current transmit go string length
COtGoS	Rw	FStDim		; Current transmit go string

@GHCmCp	ESeg	Para	Common

RBdCpL	Dw	15		; Number of baud rates
RxBdCp	Dw	75,110,134,150,300,600,1200,1800,2000,2400,3600
	Dw	4800,7200,9600,19200	; Receive baud rate table (15)
TBdCpL	Dw	0		; Transmit matches receive
TxBdCp	Dw	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
ParCap	Dw	-1		; Can change parity
DtBCpL	Dw	4		; Number of data bits per character formats
DtBtCp	Dw	5,6,7,8		; Data bit formats
D 2
StBCpL	Dw	3		; Number of stop bit formats
StBtCp	Dw	1,2,3		; 1, 1.5, 2 stop bits per character
E 2
I 2
StBCpL	Dw	2		; Number of stop bit formats
StBtCp	Dw	1,3		; 1 or 2 stop bits per character
E 2
TxBkCp	Dw	-1		; Can send BREAK
TBkTCp	Dw	-1		; Can change break timing
RHwFCp	Dw	-1		; Can hardware flag receive
RStFCp	Dw	2		; Arbitrary string flag receive
THwFCp	Dw	-1		; Can hardware flag transmit
TStFCp	Dw	2		; Arbitrary string flag transmit
SpFMCp	Dw	-1		; Can have Rx & Tx flagging modes

D 2
Stack	SSeg	Para	Public
E 2
I 2
Stack	SSeg	Word	Public
E 2
	Rw	27		; HsRecv calls Host_Go
				;  which calls HsSend
				;  queue empty forces Wake_Up_Isr
				;  sending BREAK has two levels of call
	End
E 1
