        title   AS5PDD  Epoc Serial physical device driver for the 16550
        subttl  Copyright Psion PLC 1993
        name    SYS$AS5

;       VERSION   DATE	   DESCRIPTION
;       ------- -------- ---------------
;         1.0   08/12/94 Initial version

;       Written by Jason December 1994
				  
;       Serial Driver for Epoc based around ASIC5

Sr5S3   =       0
Sr5S3a  =       1

ifdef   BUILDS3
Sr5S3   =       1
Sr5S3a  =       0
BUILDHH	equ     1
endif

if      Sr5S3a
BUILDSB equ     1
endif

        include ..\inc\epoc.inc
        include ..\inc\epocser.inc
        include ..\inc\epoclib.inc
        include ..\inc\epocsibo.inc
        include ..\srcs\ossibo.inc
        include ..\srcs\ospack.inc

Sr5ChannelStruct struc
        Sr5Open			db      ?               ; Is the channel open
        Sr5Ctrl			db      ?               ; State of control lines
        Sr5IntVector		db      ?               ; The Vector number
        Sr5Channel		db      ?               ; Which channel are we
        Sr5Mask			db      ?               ; InterruptMask
        Sr5Running		db      ?               ; Are we running
        Sr5IntRoutineVec	dw      ?               ; Vector to Interrupt
        Sr5Baud			dw      ?               ; The baud rate
        Sr5LddData		dw      ?               ; Info from Ldd above
        Sr5StatusInt		dd      ?               ; Vectors in serial
        Sr5RecvInt		dd      ?               ; Above to be called
        Sr5XmitInt		dd      ?               ; On input/output
        Sr5ClockEnable		db      ?               ; Reason to stop
        Sr5TheLines		db      ?               ; State of the modem lines
Sr5ChannelStruct ends

A5Ent struc                                             ; ASIC5 Read/Write
        A5PortA                 db      ?               ; Port A R/W
        A5PortB                 db      ?               ; Port B R/W
        A5PortBMode             db      ?               ; Inc/Mode
        A5PortD                 db      ?               ; Port CD Write only
        A5Swipe1                db      ?               
        A5Swipe2                db      ?               
        A5IntMask               db      ?               ; Interrupt mask R/W
        A5CtrlReg               db      ?               ; IntType/Ctrl register
        A5USR		        db      ?               ; UART Status/Ctrl
        A5RHR		        db      ?               ; Receive/Transmit
        A5BDLSB                 db      ?               ; Baud Rate write only
        A5BDMSB                 db      ?               ; Baud Rate write only
        A5MCR1Eoi               db      ?               ; MCR shift register
        A5MCRPresentEoi         db      ?               ; Barcode data&ints
        A5MCR2Eoi               db      ?               
        A5DUMMYF                db      ?
A5Ent ends

if      Consumer
        NumberOfChannels        equ     1               ; S3 Single Channel
else
        NumberOfChannels        equ     3               ; HC,S3C Three Channel
endif
	OsActivityMeter         equ     158ch           ; Activity Channel
        StopTimeOut             equ     1000            ; ~1s (1000ms) wait

        S_RS232ON       equ     00000001b               ; RS232 on
        S_RSTTLON       equ     00000100b               ; Line drivers on
        S_CENTON        equ     00010000b               ; Line drivers enable
        S_RXENB	        equ     00000001b               ; Receive interrupt on
        S_TXENB	        equ     00000010b               ; Transmit interrupt on
        S_TXEMPTY       equ     00010000b               ; transmit buffer empty
        S_RXINT	        equ     00000001b               ; Receive interrupt?
        S_TXINT         equ     00000010b               ; transmit interrupt?
        S_MDINT         equ     00000100b               ; Modem status interrupt
        S_CTS           equ     00000001b               ; CTS
        S_RTS           equ     00000010b               ; RTS
        S_DCD		equ	00000100b               ; DCD
        S_DSR           equ     00000010b               ; DCR
        S_DTR           equ     00000100b               ; DTR
        OVERRUN_ERROR   equ     01000000b               ; Character overrun
        PARITY_ERROR    equ     10000000b               ; Parity error
        S_PERIPHERALMODE equ    00000011b               ; ASIC5 RS232 mode
        S_UART_OFF      equ     00000010b               ; ASIC5 peripheral mode

dgroup  group   stack
        assume  ds:dgroup,es:dgroup,ss:dgroup

        CodeSeg

        ProcBegin@ OsAS5PDD
;       ====================

        dw      PDDSignature
        db      'TTY.SR5',0
        dw      (VectorEnd-Vector)/2
Vector: 
        dw	OsAS5Install
        dw	OsAS5Remove
        dw      OsAS5Open
        dw      OsAS5Strategy
VectorEnd:

        BaudRateTable   dw      -077fh,-04ffh,-0368h,-02cch,-027fh,-013fh
                        dw      -009fh,-004fh,-0035h,-0030h,-0027h,-0019h
                        dw      -0013h,-000ch,-0009h,-0004h
        DataBitsTable   db      0,2,4,6                 ; 5,6,7,8 bits per char frame
        ParityTable     db      08h,18h,0h,0h           ; Even,Odd,Mark,Space parity

        Chan0           Sr5ChannelStruct        <>
        Chan1           Sr5ChannelStruct        <>
        Chan2           Sr5ChannelStruct        <>

if Consumer
  if Asic9
        SetupTable      dw      offset AS5Int1
                        db      HwIrq2Revector,mask A9MSlave
                        db      SelectChannel5,(mask A9MClockEnable5 shr 8)
  else
        SetupTable      dw      offset AS5Int1
                        db      HwIrq4Revector,mask Asic2Int
                        db      SelectChannel7,(mask ClockEnable7 shr 8)
  endif
else
  if Asic9
        SetupTable      dw      offset AS5Int1
                        db      HwIrq4Revector,mask A9MExpIntA
                        db      SelectChannel3,(mask A9MClockEnable3 shr 8)
                        dw      offset AS5Int2
                        db      HwIrq5Revector,mask A9MExpIntB
                        db      SelectChannel4,(mask A9MClockEnable4 shr 8)
                        dw      offset AS5Int3
                        db      HwIrq2Revector,mask A9MSlave
                        db      SelectChannel5,(mask A9MClockEnable5 shr 8)
  else
        SetupTable      dw      offset AS5Int1
                        db      HwIrq3Revector,mask ExpIntLeftA
                        db      ExpChannelLeftA,(mask ClockEnable6 shr 8)
                        dw      offset AS5Int2
                        db      HwIrq2Revector,mask ExpIntRightB
                        db      ExpChannelRightB,(mask ClockEnable5 shr 8)
                        dw      offset AS5Int3
                        db      HwIrq4Revector,mask Asic2Int
                        db      SelectChannel7,(mask ClockEnable7 shr 8)
  endif
endif
        ProcEnd noret

        ProcBegin@ OsAS5Install,far
;       ============================	

;       Install the device driver
;       Out: Carry clear -happy to install 

        cld                                             ; Clear the channels
        mov     cx, NumberOfChannels                    ; and set up fixed
        mov     di, offset Chan0                        ; parameters such
        mov     si, offset SetupTable                   ; as the interrupt
        pushf                                           ; vectors and masks
        cli                                             ; for each channel
        push    ds
        mov     ax, cs
        mov     ds, ax     
ResetAllChannelsLoop:
        mov     [di].Sr5Open, 0                         ; Channel not open
        lodsw
        mov     [di].Sr5IntRoutineVec, ax               ; Set the interrupt
        lodsb                                           ; handler to call
        mov     [di].Sr5IntVector, al                   ; Which Interrupt
        lodsb
        mov     [di].Sr5Mask, al                        ; Mask for that
        lodsb                                           ; interrupt
        mov     [di].Sr5Channel, al                     ; Which Psion serial
        lodsb                                           ; channel
        mov     [di].Sr5ClockEnable, al                 ; Baud rate clocking
        add     di, size Sr5ChannelStruct               ; enable
        loop    ResetAllChannelsLoop
        pop     ds
        popf
AllChannelsOkay:						
        clc						; Returns with
        ret						; Carry clear
        ProcEnd noret

        ProcBegin@ OsAS5Remove,far
;       ==========================
	
;       Remove the device driver
;       Out: Carry clear -happy to remove
;            Carry set -we have an open channel and cant be removed.

        xor     ax, ax                                  ; If we have a    
        or      al, Chan0.Sr5Open                       ; channel Still open
        or      al, Chan1.Sr5Open                       ; then return a can't
        or      al, Chan2.Sr5Open                       ; do error else
        jz      AllChannelsOkay                         ; complete okay     
        mov     ax, InUseErr				
        stc					
        ret						
        ProcEnd noret					

        ProcBegin@ OsAS5Open,far
;       ========================

;       Open a serial channel
;       In:  SS:SI is a pointer to the open Ent Structure
;       Out: Carry clear, control block in BX
;            Carry set, error in AX

        cld
        mov     si, [si].OpenNamePtr                    ; Open the channel
        mov     al, [si+1]                              ; Get the channel
        CharToFoldedChar                                ; Make Upper Case
        cmp     al, 'A'                                 ; Indicator which is
        jb      ErrorInOpen                             ; Part of the name
        sub     al, 'A'                                 ; Should be A,B,C
        cmp     al, NumberOfChannels
        jae     ErrorInOpen
        xor     ah, ah
        mov     bx, offset Chan0                        ; What Channel are
        cmp     al, 1                                   ; We Openning
        jb      GotChan                                 ; Pointer to Control
        mov     bx, offset Chan2                        ; Block in bx
        ja      GotChan
        mov     bx, offset Chan1
GotChan:
        mov     al, 1
        xchg    al, cs:[bx].Sr5Open
        cmp     al, 0                                   ; Now try to Open
        je      OkayToOpen                              ; That channel
CantOpen:
        mov     ax, InUseErr
CantOpenDiffErr:
        stc
        ret
ErrorInOpen:						
        mov     ax, NameErr
        stc
        ret
OkayToOpen:
        xor     al, al
        mov     cs:[bx].Sr5Running, al
        mov     cs:[bx].Sr5TheLines, al
        mov     cs:[bx].Sr5Ctrl, al
        mov     al, cs:[bx].Sr5Mask
	HwGetChannel
        jc      CantOpenSoClose				; Check that we have
	call    CheckHardwarePresent			; The right Hardware
	jnc     OpenedOkay
	mov     al, cs:[bx].Sr5Mask
	HwFreeChannel
CantOpenSoClose:
        mov     cs:[bx].Sr5Open, 0
        mov     ax, DeviceErr
        jmp     short CantOpenDiffErr			; Return with the
OpenedOkay:						; Offset of our
        xor     ax, ax					; Control Block
        ret						; In bx
        ProcEnd noret

AS5StrategyJumpTable label word
        dw      offset AS5Open				; Load Handler offsets
        dw      offset AS5Close			        ; Close the channel
        dw      offset AS5Start		        	; Start the channel
        dw      offset AS5Stop				; Stop the channel
        dw      offset AS5Set				; Does nothing
        dw      offset AS5Sense			        ; Returns chan status
        dw      offset AS5Control			; Drive the lines
        dw      offset AS5Enquire			; Returns baud rate
        dw      offset AS5Enable			; Begin Output
        dw      offset AS5SetHandlerCS			; Get Handler segments

	ProcBegin@ OsAS5Strategy,far
;	=============================

;       Strategy functions entry point
;	Warning! This can be called from within Interrupt	
;	In:  vector number in AX + various data in other registers
;            DS is OsDataGroup (and MUST be preserved)
;	Out: DI is pointer to control block
;            Old channel and flags on stack             
                                                       
	cld                                             
	mov     bx, sp                                  
	mov     bx, ss:[bx+4]                           
	mov     bl, cs:[bx].Sr5Channel          ; Select the correct        
        xchg    bx, ax                          ; Output channel
	pushf                                   ; Then Call the right        
	cli                                     ; Function to deal
        HwSelectChannel                         ; With the strategy
        mov     ah, bh                          ; request            
        xor     bh, bh                           
        push    ax                              ; Interrupts off
        mov     ax, di                          ; and flags are on
	mov     di, sp                          ; the stack        
	mov     di, ss:[di+8]                           
	jmp     AS5StrategyJumpTable[bx]		
	ProcEnd noret				
							
	ProcBegin@ AS5Open,far
;	======================

;       Old channel and flags on the stack, interrupts off
							
        mov     cs:[di].Sr5LddData, cx                  ; Load the offsets of
        mov     word ptr cs:[di].Sr5StatusInt, ax       ; the data send and 
        mov     word ptr cs:[di].Sr5RecvInt, si	        ; recieve routines in
        mov     word ptr cs:[di].Sr5XmitInt, dx         ; the Ldd above us
        xor     dx, dx                                  ; DX=0 -We don't support
        pop     ax                                      ; power management
        HwSelectChannel                                 ; Return the old
        popf                                            ; channel
        ret
        ProcEnd noret

        ProcBegin@ AS5Close,far
;       =======================
	
;       Old channel and flags on the stack, interrupts off

        and     cs:[di].Sr5Open, 0                      ; Close the Channel 
        mov     al, cs:[di].Sr5Mask                     ; Free up our channel
        HwFreeChannel                                   ; And the Hardware
        pop     ax                                      ; Channel      
        HwSelectChannel                                 ; Return the old
        popf                                            ; channel
        ret						
        ProcEnd noret				

        ProcBegin@ AS5Start,far
;       =======================

;       Old channel and flags on the stack, interrupts off

        call    CheckHardwareFromStart
        mov     dx, 0
        jc      CantStartSomethingWhichIsntThere

if      Asic9
	in	ax, A9WControlExtraRW                   ; Start the S3s clock
	or	ah, cs:[di].Sr5ClockEnable              ; Generator
	out	A9WControlExtraRW, ax
else
	mov	al, cs:[di].Sr5ClockEnable
        HwSetA2Control2Bits
endif

        mov     al, SerialWriteSingle or A5USR          ; Set the baud rate
        SBUSY                                           ; and other
        SCONTOUT                                        ; characteristics
        mov     al, cs:[di].Sr5Ctrl                        
        SBUSY
        SDATAOUT
        mov     al, SerialWriteSingle or A5BDLSB
        SBUSY
        SCONTOUT
        mov     al, byte ptr cs:[di].Sr5Baud
        SBUSY
        SDATAOUT
        mov     al, SerialWriteSingle or A5BDMSB
        SBUSY
        SCONTOUT
        mov     al, byte ptr cs:[di+1].Sr5Baud
        SBUSY
        SDATAOUT

	call	GetTheInterrupt                         ; Get the interrupt
	xor	cx, cx                                  ; and clear down
        call    DriveRts                                ; the RTS line (DTR
                                                        ; stays at the state
        mov     al, SerialWriteSingle or A5CtrlReg      ; it was set)
        SBUSY
        SCONTOUT
        mov     al, (S_CENTON or S_RS232ON or  S_RSTTLON)
        SBUSY                                           
        SDATAOUT                                        ; Switch on the line
        mov     cx, 8 ; 8 ms                            ; drivers and wait
        pop     dx                                      ; for them to power up
        call    WaitTimer
        push    dx
        mov     ah, (S_RXENB or S_TXENB or S_MDINT)     ; Start all interrupts
        call    EnableTheInterrupt
        mov     cs:[di].Sr5Running, 1
        mov     bx, Asic5SerialCurrent
        HwSetPCurrent
        call	Status
CantStartSomethingWhichIsntThere:
        pop     ax      	     			; Return the old
	HwSelectChannel             			; channel
	popf
	ret
        ProcEnd noret

	ProcBegin@ AS5Stop,far
;	=======================
	
;       Old channel and flags on the stack, interrupts off


        xor     cx, cx                                  ; Clear the state of
        call	DriveRts                                ; the modem lines
        mov     al, SerialReadSingle or A5IntMask       ; Stop all of the
        SBUSY                                           ; interrupts
        SCONTOUT
        XNOP
        SBUSY
        SDATAIN
        and     al, not (S_TXENB or S_RXENB or S_MDINT)
	mov     ah, al
        mov     al, SerialWriteSingle or A5IntMask
        SBUSY
        SCONTOUT
        mov     al, ah
        SBUSY
        SDATAOUT
        pop     dx
        cmp     dh, DevHoldPowerFail
        je      DontStopHardware
        cmp	cs:[di].Sr5Running, 1
        jne     DontWait
        mov     cx, StopTimeOut
        jmp     short CompareNow
WaitForEmpty:
        call    TickTimer
CompareNow:
        mov     al, SerialReadSingle or A5USR
        SBUSY
        SCONTOUT
        XNOP
        SBUSY
        SDATAIN
        test    al, S_TXEMPTY
        loopne	WaitForEmpty
DontWait:
        cmp	bh,DevHoldNormal
        je	DontStopHardware
        
if      Asic9
        mov     cl, cs:[di].Sr5ClockEnable              ; Turn off baud rate
        not     cl                                      ; clocking from the
        in      ax, A9WControlExtraRW                   ; S3/3a
        and     ah, cl
        out     A9WControlExtraRW, ax
else
	mov     al, cs:[di].Sr5ClockEnable                 
        HwClearA2Control2Bits
endif
        mov     al, SerialWriteSingle or A5CtrlReg      ; Stop the drivers
        SBUSY                                           ; and stuff
        SCONTOUT
        sub     al, al                                  ; clear S_CENTON,
        SBUSY                                           ; S_RS232ON, S_RSTTLON
        SDATAOUT
DontStopHardware:
        mov     al, dl                                  ; Return the old
        HwSelectChannel                                 ; channel
	popf
        xor     bx, bx
        HwSetPCurrent
        ret
        ProcEnd noret

        ProcBegin@ AS5Set,far
;       =====================

;       Old channel and flags on the stack, interrupts off
;       In:  Information required is on the stack

        pop     ax                                      ; Return the old
        HwSelectChannel                                 ; channel
        popf
        mov	ah, ss:[si].SerialCharTbaud             ; Set the Recieve,
        cmp	ah, ss:[si].SerialCharRbaud             ; Transmit characteristics
        jnz     ErrorInSet                              ; First set the Baud
        cmp     ah, P_BAUD_50                           ; Rate
        jb      ErrorInSet
        cmp     ah, P_BAUD_19200
        ja      ErrorInSet
GotTheSpecialBaud:
        dec     ah
	mov     cl, ss:[si].SerialCharFrame             ; Then the number of
        mov     al, ss:[si].SerialCharParity            ; Stop bits
        dec     al
        xor     ch, ch
        push    bx
        test    cl, P_TWOSTOP
        jz      OnlyOneStopBit
        or      ch, 020h
OnlyOneStopBit:
        test    cl, P_PARITY                            ; The Parity
	jz      NoParity	
        mov     bx, offset ParityTable
        xlat    cs:[ParityTable]
        or      ch, al
NoParity:	
        mov     bx, offset DataBitsTable                ; And finally the
        and     cl, P_DATA_FRM                          ; Number of data bits
        mov     al, cl                                  ; Per frame
        xlat    cs:[DataBitsTable]
        or      ch, al
        mov     bx, offset BaudRateTable
        mov     al, ah
        xor     ah, ah
        shl     ax, 1
        add     bx, ax
        mov     ax, cs:[bx]
        pop     bx
        mov     cs:[di].Sr5Ctrl, ch
        mov     cs:[di].Sr5Baud, ax
        xor     al, al
        ret
ErrorInSet:
        mov     al, NotSupportedErr
        stc
        ret
        ProcEnd noret

        ProcBegin@ AS5Sense,far
;       =======================
	
;       Old channel and flags on the stack, interrupts off
;       Out: The state of the DCD,CTS,DSR lines returned in DX

        call    Status
	pop     ax                                      ; Return the old
        HwSelectChannel                                 ; channel
        popf
        ret
        ProcEnd noret
	
        ProcBegin@ Status
;       =================

;       Out: State of modem lines in DX

        mov     al, SerialReadSingle or A5USR           ; Get the current
        SBUSY                                           ; Modem status
        SCONTOUT                                        ; Lines and return
        XNOP                                            ; With the result
        SBUSY                                           ; In dx
        SDATAIN
        and	al, (S_CTS or S_DSR or S_DCD)           ; Get the lines we want
        xor	al, (S_CTS or S_DSR or S_DCD)           ; Invert signals
        xor     ah, ah
        mov     dx, ax
        ret
        ProcEnd noret

        ProcBegin@ AS5Control,far
;       =========================
	
;       Old channel and flags on the stack, interrupts off
;       In:  Lines to drive and state to drive them in DX

        mov     cl, dl                                  ; Set the state of one
        test    dh, P_SRCTRL_DTR                        ; of the modem Lines
        jz      DriveRtsNow                             ; DH is the line to
        call    DriveDtr                                ; Drive and DL is the
        pop     ax      	     			; State	to drive it to
        HwSelectChannel                                 ; Return the old
        popf                                            ; channel
        ret						
DriveRtsNow:
        call    DriveRts
        pop     ax                                      ; Return the old
        HwSelectChannel                                 ; channel
        popf
        ret
        ProcEnd noret

        ProcBegin@ DriveDtr
;       ===================

        mov     ah, S_DTR                               ; Set/Reset the DTR
        jmp     short DriveTheLine                      ; Line
        ProcEnd noret

        ProcBegin@ DriveRts
;       ===================

        mov     ah, S_RTS                               ; Set/Reset RTS
DriveTheLine:
        mov     al, SerialWriteSingle or A5PortD        ; Common code to set
        SBUSY                                           ; and reset either
        SCONTOUT                                        ; line while
        mov     al, cs:[di].Sr5TheLines                 ; preserving the
        test    cl, cl                                  ; states of the other
        jz      ClearLine                               ; lines
	or      al, ah					
        jmp     DoTheOutput
ClearLine:
        not     ah
        and     al, ah
DoTheOutput:
        mov     cs:[di].Sr5TheLines, al
        SBUSY
        SDATAOUT
        ret
        ProcEnd noret

        ProcBegin@ AS5Enquire,far
;       =========================

;       Old channel and flags on the stack, interrupts off
;       Out: DX,AX are the supported baud rates AX for 50 to 19200 DX for above
;            CX says what data bits, parity, etc we support

	pop     ax                                      ; Return the old
	HwSelectChannel                                 ; channel
	popf
if      Asic9
	mov     ax, -1
else
        mov     ax, 07fffh
endif
        xor     dx, dx
        mov     cx, (0ffffh AND (NOT (P_SRINQ_SPLIT OR P_SRINQ_PARSPACE OR P_SRINQ_PARMARK)))
        ret
        ProcEnd noret

        ProcBegin@ AS5Enable,far
;       ========================

;       Old channel and flags on the stack, interrupts off

        mov     ah, S_TXENB                             ; Begin Output by
        call    EnableTheInterrupt                      ; enabling transmit
        pop     ax                                      ; interrupts
        HwSelectChannel                                 ; Return the old
        popf                                            ; channel
        ret
        ProcEnd noret

        ProcBegin@ AS5SetHandlerCS,far
;       ==============================
	
;       Old channel and flags on the stack, interrupts off
;       CX is CS of above LDD

        mov     word ptr cs:[di].(Sr5StatusInt+2), cx   ; Load the Segment
        mov     word ptr cs:[di].(Sr5RecvInt+2), cx     ; Within which the
	mov     word ptr cs:[di].(Sr5XmitInt+2), cx     ; The Ldd above us
	pop     ax                                      ; Return the old
	HwSelectChannel                                 ; channel
        popf                                            ; Resides
        ret						
        ProcEnd noret

        ProcBegin@ AS5Int3,far				
;       ======================
							
        mov     di, offset Chan2                        ; Channel 3 interrupt
        mov     ax, PortCActive                         ; vector
        jmp     ComInt                                  ; Jumps to Comint
        ProcEnd noret

        ProcBegin@ AS5Int2,far
;       ======================
			       				
        mov     di, offset Chan1                        ; Channel 2 interrupt
        mov     ax, PortBActive                         ; vector
        jmp     ComInt                                  ; Jumps to Comint
        ProcEnd noret

        ProcBegin@ AS5Int1,far
;       ======================
							
        mov	di,offset Chan0                         ; Channel 1 interrupt
        mov     ax,PortAActive                          ; vector
;       FALL THROUGH                                    ; Falls through
        ProcEnd noret                                   ; to Comint

        ProcBegin@ ComInt,far
;       =====================

;       The common interrupt handler
;       DS points to OS data space
;       DI is our control block
;       AX is the Active channel

if      S3b or S3c
        or      ds:[OsActivityMeter], ax                ; Set active state
endif
        mov     al, cs:[di].Sr5Channel                  ; Select our
        HwSelectChannel                                 ; channel
        push    ax
        mov     bx, cs:[di].Sr5LddData                  ; Data for LDD above
TheInterruptLoop:                                       ; in bx
        mov     al, SerialReadSingle or A5CtrlReg       ; Find out what
        SBUSY                                           ; caused the
        SCONTOUT                                        ; interrupt
        XNOP
        SBUSY
        SDATAIN
        test	al, al
        je	NothingToDo
HaveWeGotAModemStatusLineInterrupt:
        test    al, S_MDINT
        jz      HaveWeGotARecieveInterrupt
        mov     al, SerialReadSingle or A5USR
        SBUSY
        SCONTOUT
        XNOP
        SBUSY
        SDATAIN
        test    al, (OVERRUN_ERROR or PARITY_ERROR)
        jz      ModemStatusInterruptOnly
        push    ax                                      ; Something has been
        mov     ah, SERPARITY_ERR                       ; recieved in error
        test    al, PARITY_ERROR
        jnz     IsAParityError                          ; Establish the error
        mov     ah, SEROVERRUN_ERR
IsAParityError:                                         
        mov     al, SerialReadSingle or A5RHR           ; Get the character
        SBUSY                                           ; in question
        SCONTOUT
        XNOP
        SBUSY
        SDATAIN
        push    di
        call    dword ptr cs:[di].Sr5RecvInt
        pop     di
        pop     ax
ModemStatusInterruptOnly:
        and     ax, (S_CTS or S_DSR or S_DCD)
        xor     al, (S_CTS or S_DSR or S_DCD)   ; invert signals
        mov     dx, ax
        push    di
        call    dword ptr cs:[di].Sr5StatusInt
        pop     di
        jmp     short TheInterruptLoop
HaveWeGotARecieveInterrupt:
        test    al, S_RXINT
        jz      HaveWeGotATransmitInterrupt
        mov     al, SerialReadSingle or A5RHR
        SBUSY
        SCONTOUT
        XNOP
        SBUSY
        SDATAIN
        xor     ah, ah                          ; AX has character received
        push    di
        call    dword ptr cs:[di].Sr5RecvInt
        pop     di
        jmp     short TheInterruptLoop
HaveWeGotATransmitInterrupt:
        test    al, S_TXINT
        jz      NothingToDo
        push    di
        call    dword ptr cs:[di].Sr5XmitInt
        pop     di
        test    ax, ax          ; -1 if disable
        js      DisableTransmitInts
        mov     ah, al
        mov     al, SerialWriteSingle or A5RHR
        SBUSY
        SCONTOUT
        mov     al, ah
        SBUSY
        SDATAOUT
        jmp     TheInterruptLoop
NothingToDo:
if      Asic9
        out     A9BNonSpecificEoiW,al			
else
        out     A1NonSpecificEoi, al
endif
        pop     ax
        HwSelectChannel           
        clc 					; Resced if neccessary
        ret
DisableTransmitInts:                            ; disable TX interrupts
        mov     al, SerialReadSingle or A5IntMask
        SBUSY
        SCONTOUT
        XNOP
        SBUSY
        SDATAIN
        and     al, not S_TXENB 
        mov     ah, al
        mov     al, SerialWriteSingle or A5IntMask
        SBUSY
        SCONTOUT
        mov     al, ah
        SBUSY
        SDATAOUT
        jmp     TheInterruptLoop
        ProcEnd noret

        ProcBegin@ GetTheInterrupt
;       ==========================

        mov     al, cs:[di].Sr5IntVector                ; Load the address of
        mov     cx, cs                                  ; the appropriate
        mov     bx, cs:[di].Sr5IntRoutineVec            ; interrupt routine
        GenSetRevector                                  ; into the correct
        ret                                             ; vector
        ProcEnd noret

        ProcBegin@ EnableTheInterrupt
;       =============================

        mov      al, SerialReadSingle or A5IntMask
        SBUSY
        SCONTOUT
        XNOP
        SBUSY
        SDATAIN
        or      ah, al
        mov     al, SerialWriteSingle or A5IntMask
        SBUSY
        SCONTOUT
        mov     al, ah
        SBUSY
        SDATAOUT
if      Asic9
        in      al,A9BInterruptMaskRW			; Set the mask
        or      al, cs:[di].Sr5Mask			; to enable Interrupts
        out     A9BInterruptMaskRW,al
else
        in      al, A1InterruptMask
        or 	al, cs:[di].Sr5Mask
        out     A1InterruptMask, al
endif
        ret
        ProcEnd noret


        ProcBegin@ StopInterrupts
;       =========================

        mov     al, SerialReadSingle or A5IntMask       ; Stop interrupts
        SBUSY                                           ; from Asic5
        SCONTOUT
        XNOP
        SBUSY
        SDATAIN
        and     al, not (S_RXENB or S_TXENB or S_MDINT)
        mov     ah, al
        mov     al, SerialWriteSingle or A5IntMask
        SBUSY
        SCONTOUT
        mov     al, ah
        SBUSY
        SDATAOUT

        mov     ah, cs:[di].Sr5Mask			
        not     ah					
if      Asic9
        in      al,A9BInterruptMaskRW			; Stop Interrupts
        and     al,ah					; By clearing the  
        out     A9BInterruptMaskRW,al                   ; Mask and resetting
else                                                    ; The Vector
        in      al, A1InterruptMask
        and     al,ah
        out     A1InterruptMask, al
endif

        mov     al, cs:[di].Sr5IntVector
        GenResetRevector
        ret
        ProcEnd noret

	ProcBegin@ CheckHardwarePresent
;	===============================

	pushf
	cli						
	mov	al, cs:[bx].Sr5Channel                  ; Check that the
	HwSelectChannel					; Harware is there
	HwNullFrame					; And that it is what
	mov	al,(SerialSelect or Asic5NormalId)	; It should be
	SBUSY						; First look for An
	SCONTOUT					; ASIC4 at the other
	XNOP						; End of the link
	SBUSY
	SDATAIN
	test	al,al
	je	ConnectionFailed
GotConnection:
	popf
	clc
	ret
ConnectionFailed:
	mov	al, SerialSelect or Asic4Id             ; Asic4 Id
	SBUSY
	SCONTOUT
	XNOP
	SBUSY
	SDATAIN
        test    al, al
	jne	ConnectionFailedExit
	mov	al, SerialSelect or Asic8Id             ; Modem chip Id
	SBUSY
	SCONTOUT
	XNOP
	SBUSY
	SDATAIN
        test    al, al
	jne	ConnectionFailedExit
	mov	al, SerialSelect or Asic5PackId         ; Asic5pack Id
	SBUSY
	SCONTOUT
	XNOP
	SBUSY
	SDATAIN
ConnectionFailedExit:
	popf
	stc
	ret
	ProcEnd noret

	ProcBegin@ CheckHardwareFromStart
;	=================================

	pushf
	HwNullFrame					; Check that the
	mov	al,(SerialSelect or Asic5NormalId)	; Harware is there
	SBUSY						; First look for An
	SCONTOUT					; ASIC5 at the other
	XNOP						; End of the link  
	SBUSY
	SDATAIN
	test	al,al
	je	ConnectionFailed                        ; If not a 3link or 
	test	al, mask A5MultiDrop                    ; if in multidrop
	jne	ConnectionFailedExit                    ; mode then we are
	popf                                            ; in trouble
	mov     al, SerialWriteSingle or A5PortBMode
        SBUSY
        SCONTOUT                                        ; Put the 3Link in
        mov     al, S_PERIPHERALMODE                    ; peripheral mode
        SBUSY
        SDATAOUT
	clc
	ret
	ProcEnd noret

	ProcBegin@ WaitTimer
;	====================

; 	In:  CX number of ms to wait for
;            Channel store in DL
;       Out: Channel store in DL
	
	inc	cx					; Wait for  a given
WaitTickLoop:						; Number of ms
	call	TickTimer				; Plus one to guarantee
	loop	WaitTickLoop				; That at least cx ms
	ret						; Go by
	ProcEnd
;
	ProcBegin@ TickTimer
;       ====================

;	Uses writes down our channel to simulate tick timer waits
;	Must allow interrupts so other things can run -we will be here
;	for 1/1000 of a second and may be called many times
;       ChannelStore in/out in DL

        mov     al, dl                                  ; Return the old
        HwSelectChannel                                 ; channel
        mov     ah, al
	pushf
	sti
	push	cx
	mov	cx, 12					; 1ms
WaiterLoop:						; 128 frames =1ms
        pushf						; about 12 times
        cli                                             ; round the loop
        out	ResetWatchDog,al			
        mov     al, ah
        HwSelectChannel
        mov     dl, al                                  ; Get Correct channel
        mov 	al, (SerialWriteSingle or 0)		; Do a write to
        SBUSY						; nowhere and 
        SCONTOUT					; waste some time
        SBUSY
        SCONTOUT     					; Do it eight times
        SBUSY
        SCONTOUT
        SBUSY
        SCONTOUT
        SBUSY
        SCONTOUT
        SBUSY
        SCONTOUT
        SBUSY
        SCONTOUT
        SBUSY
        SCONTOUT
        SBUSY
        mov     al, dl   	     			; Return the old
        HwSelectChannel         			; channel
        mov     ah, al
        popf
        loop	WaiterLoop
        pop	cx
        popf
        mov     al, ah
        HwSelectChannel
        mov     dl, al
        ret
        ProcEnd

        EndCodeSeg

stack	segment stack para 'data'
stack	ends

	end OsAS5PDD


