;
; Low level ASYNC serial I/O driver for
; use with DDS MICRO-C compiler on IBM/PC.
;
; Copyright 1990-2000 Dave Dunfield
; All rights reserved.
;
; Misc constants.
?BUFMSK EQU     $00FF           ;Buffer size mask
?WINDOW EQU     256-30          ;Flow control assertion window
?RXRDY  EQU     %00000001       ;Uart Receiver ready flag
?TXRDY  EQU     %00100000       ;Uart Transmitter ready flag
?8259   EQU     $0020           ;8259 interrupt controller
; Bits in driver control flags
?RFLOW  EQU     %10000000       ;Received flow control
?TFLOW  EQU     %01000000       ;Transmit flow control
?TXOFF  EQU     %00100000       ;Transmit XOFF pending
?TXON   EQU     %00010000       ;Transmit XON  pending
?XPAREN EQU     %00001000       ;Transparency enabled
;
$SE:1
;
; Initialized variables & tables
;
Cflags  DB      0               ;Control flags
?comvec DW      0               ;Comm interrupt vector
;
$DD:?rdptr  2                   ;Buffer read pointer
$DD:?wrptr  2                   ;Buffer write pointer
$DD:?comadr 2                   ;Com port address
$DD:?oldoff 2                   ;Saved old interrupt offset
$DD:?oldseg 2                   ;Saved old interrupt segment
$DD:?buffer 256                 ;Receive buffer
;
$SE:0
;
; Open the com port: Copen(int port, int speed, char mode, char modem)
;
Copen   PUSH    BP              ;Save callers stack
        MOV     BP,SP           ;Address parameters
        CALL    Cclose          ;Insure its closed
        MOV     AX,10[BP]       ;Get com port
        MOV     DX,#$03FD       ;Comm 1 address
        MOV     BX,#$0030       ;Comm 1 interrupt vector
        MOV     CL,#$0EF        ;Comm 1 interrupt mask
        DEC     AX              ;Is it com1?
        JZ      ?2              ;Yes, it is
        MOV     DX,#$02FD       ;Comm 2 address
        MOV     BX,#$002C       ;Comm 2 interrupt vector
        MOV     CL,#$0F7        ;Comm 2 interrupt mask
        DEC     AX              ;Is it com2?
        JZ      ?2              ;Yes it is
; Report failure to open port
?1      STI                     ;Insure interrupts enabled
        MOV     AX,#-1          ;Indicate failure
        POP     BP              ;Restore caller
        RET
; Proceed with opening the comm port
?2      MOV     ?comadr,DX      ;Save address
        CLI                     ;Inhibit interrupts
; Setup the uart
        DEC     DX              ;Backup ...
        DEC     DX              ;to line control register (FB)
        IN      AL,DX           ;Read current value
        OR      AL,#$80         ;Enable baud rate register
        OUT     DX,AL           ;Write it
        MOV     AX,8[BP]        ;Get baud rate
        SUB     DX,#3           ;Point to baud rate LSB (F8)
        OUT     DX,AL           ;Write it
        INC     DX              ;Advance to MSB (F9)
        MOV     AL,AH           ;Get MSB
        OUT     DX,AL           ;Write it
        DEC     DX              ;Backup to LSB (F8)
        IN      AL,DX           ;Re-read LSB
        MOV     AH,AL           ;Copy for later
        INC     DX              ;Back to MSB (F9)
        IN      AL,DX           ;Re-read MSB
        XCHG    AH,AL           ;Swap for multi
        CMP     AX,8[BP]        ;Does it match
        JNZ     ?1              ;No, its dosn't
        MOV     AL,6[BP]        ;Get mode
        AND     AL,#$7F         ;Insure no baud rate
        INC     DX              ;Advance...
        INC     DX              ;to line control register (FB)
        OUT     DX,AL           ;Write it
        MOV     AL,#$01         ;Receive interrupt only
        DEC     DX              ;Backup ...
        DEC     DX              ;to Interrupt enable register (F9)
        OUT     DX,AL           ;Write it
        MOV     AL,4[BP]        ;Get modem control
        ADD     DX,#3           ;Point to modem control register (FC)
        OUT     DX,AL           ;Write it
; Setup the interrupt vector
        MOV     ?comvec,BX      ;Save interrupt vector
        MOV     CS:?dsval,DS    ;Save data segment for int handler
        XOR     AX,AX           ;Get a zero
        MOV     ?rdptr,AX       ;Zero read pointer
        MOV     ?wrptr,AX       ;Zero write pointer
        MOV     AX,ES:[BX]      ;Get old offset
        MOV     ?oldoff,AX      ;Save old offset
        MOV     AX,ES:2[BX]     ;Get old segmemt
        MOV     ?oldseg,AX      ;Save segment
        MOV     AX,#?COMINT     ;Point to routine
        MOV     ES:[BX],AX      ;Write new offset
        MOV     ES:2[BX],CS     ;Write new segment
; Clear out any pending characters
        SUB     DX,#4           ;Point to data register (F8)
        IN      AL,DX           ;Read to clear interrupt
        IN      AL,DX           ;Read to clear interrupt
; Setup the interrupt controller
        IN      AL,?8259+1      ;Read interrupt mask
        AND     AL,CL           ;Enable serial port
        OUT     ?8259+1,AL      ;Write interrupt controller
        STI                     ;Re-enable interrupts
        XOR     AX,AX           ;Indicate success
        POP     BP              ;Restore caller
        RET
;
; Close the comm port: Cclose()
;
Cclose  XOR     AX,AX           ;Get zero
        MOV     ES,AX           ;Point to interrupt vectors
        MOV     BX,?comvec      ;Get old vector
        AND     BX,BX           ;Is it set?
        JZ      ?3              ;No, its not
        MOV     ?comvec,AX      ;Indicate not set
        CLI                     ;Disable interrupts
; Restore the old comm interrupt vector
        MOV     DX,?oldoff      ;Get old offset
        MOV     ES:[BX],DX      ;Restore old offset
        MOV     DX,?oldseg      ;Get old segment
        MOV     ES:2[BX],DX     ;Restore old segment
; Disable interrupts on the uart
        MOV     DX,?comadr      ;Get uart address
        SUB     DX,#4           ;Point to interrupt enable register
        OUT     DX,AL           ;Write zero
        IN      AL,?8259+1      ;Read interrupt mask
        OR      AL,#$18         ;Disable comm interrupts
        OUT     ?8259+1,AL      ;Write interrupt mask
        STI                     ;Re-enable interrupts
?3      RET
;
; Test for char from com port: int Ctestc()
;
Ctestc  MOV     BX,?rdptr       ;Get read pointer
        CMP     BX,?wrptr       ;Test for data in buffer
        JNZ     ?4              ;Yes, we have some
        MOV     AX,#-1          ;Report no data available
        RET
;
; Read a character from the comport: int Cgetc()
;
Cgetc   MOV     BX,?rdptr       ;Get read pointer
        CMP     BX,?wrptr       ;Test for data in buffer
        JZ      Cgetc           ;No characters, wait for them
; Read character from com port
?4      MOV     DI,#?buffer     ;Get I/O buffer address
        MOV     AL,[BX+DI]      ;Get character from buffer
        XOR     AH,AH           ;Zero high
        INC     BX              ;Advance read pointer
        AND     BX,#?BUFMSK     ;Mask for buffer wrap
        MOV     ?rdptr,BX       ;Resave read pointer
        CMP     BX,?wrptr       ;Did we just empty buffer?
        JNZ     ?3              ;No, its ok
        PUSH    AX              ;Save for later
; Buffer is empty, send XON if necessary
        MOV     DX,?comadr      ;Point to com port
        CLI                     ;No interrupts
        MOV     AH,Cflags       ;Get control flags
        TEST    AH,#?TFLOW      ;Flow controlled?
        JZ      ?7              ;No, its not
        AND     AH,#~(?TFLOW|?TXOFF|?TXON) ;Clear flags
        IN      AL,DX           ;Get status
        TEST    AL,#?TXRDY      ;Ok to send?
        JZ      ?5              ;No, set pending
        SUB     DX,#5           ;Backup to data port
        MOV     AL,#'Q'-$40     ;Get XON character
        OUT     DX,AL           ;Send the XON
        JMP     <?6             ;And continue
; Cannot send not, set pending flag
?5      OR      AH,#?TXON       ;Set XON pending flag
?6      MOV     Cflags,AH       ;Resave the flags
?7      STI                     ;Re-enable interrupts
        POP     AX              ;Restore character
        RET
;
; Write a character to the com port: Cputc(char c)
;
Cputc   PUSH    BP              ;Save callers stack frame
        MOV     BP,SP           ;Address parameters
?8      MOV     DX,?comadr      ;Get address of uart
        IN      AL,DX           ;Read uart status
        TEST    AL,#?TXRDY      ;Ok to transmit
        JZ      ?8              ;No, wait for it
        SUB     DX,#5           ;Position to data address
        CLI                     ;Disallow interrupts
        MOV     AH,Cflags       ;Get control flags
; Test for pending XOFF to send
        TEST    AH,#?TXOFF      ;Transmit XOFF?
        JZ      ?9              ;No, try next
        MOV     AL,#'S'-$40     ;Get XOFF
        AND     AH,#~?TXOFF     ;Clear the bit
        JMP     <?10            ;Write to comm port
; Test for pending XON to send
?9      TEST    AH,#?TXON       ;Transmit XON?
        JZ      ?11             ;No, output character
        MOV     AL,#'Q'-$40     ;Get XON
        AND     AH,#~?TXON      ;Clear the bit
; Resave the control flags & proceed
?10     MOV     Cflags,AH       ;Re-save control flags
        STI                     ;Re-enable interrupts
        OUT     DX,AL           ;Write character
        JMP     <?8             ;And proceed
; No pending flow control, output data
?11     STI                     ;Re-enable interrupts
        TEST    AH,#?RFLOW      ;Output inhibited?
        JNZ     ?8              ;Yes, wait for it
        MOV     AL,4[BP]        ;Get character
        OUT     DX,AL           ;Write to comm port
        POP     BP              ;Restore caller
        RET
;
; Read the com port signals: int Csignals()
;
Csignals MOV    DX,?comadr      ;Get the com port address
        INC     DX              ;Advance to modem status register
        IN      AL,DX           ;Read modem status
        XOR     AH,AH           ;Zero high bits
        RET
;
; Comms Interrupt handler
;
?COMINT PUSH    AX              ;Save AX
        PUSH    BX              ;Save BX
        PUSH    DX              ;Save DX
        PUSH    DI              ;Save DI
        PUSH    DS              ;Save DS
        MOV     DS,CS:?dsval    ;Get data segment
        MOV     DX,?comadr      ;Get com port I/O address
        IN      AL,DX           ;Read uart status register
        TEST    AL,#?RXRDY      ;Receiver ready?
        JZ      ?15             ;No, Spurious interrupt
        SUB     DX,#5           ;Backup to data port
        MOV     AH,Cflags       ;Get comm flags
        IN      AL,DX           ;Read data character
        TEST    AH,#?XPAREN     ;Are we transparent?
        JNZ     ?13             ;Yes, do not interpret flow control
; Test for XOFF, inhibit output
        CMP     AL,#'S'-$40     ;Is it XOFF?
        JNZ     ?12             ;No, try next
        OR      AH,#?RFLOW      ;Set flow control bit
        JMP     <?14            ;and continue
; Test for XON, enable output
?12     CMP     AL,#'Q'-$40     ;Is it XON
        JNZ     ?13             ;No, its not
        AND     AH,#~?RFLOW     ;Reset flow control bit
        JMP     <?14            ;and continue
; Normal character, stuff in buffer
?13     MOV     DI,#?buffer     ;Get I/O buffer address
        MOV     BX,?wrptr       ;Get write pointer
        MOV     [BX+DI],AL      ;Write into buffer
        INC     BX              ;Advance
        AND     BX,#?BUFMSK     ;Mask for buffer wrap
        MOV     ?wrptr,BX       ;Resave pointer
; Test for nearing end of buffer
        SUB     BX,?rdptr       ;Calculate size of buffer
        AND     BX,#?BUFMSK     ;Mask for buffer wrap
        CMP     BX,#?WINDOW     ;Are we nearing end
        JB      ?15             ;No, its ok
        TEST    AH,#?XPAREN     ;Are we transparent?
        JNZ     ?15             ;Don't send flow ctrl
; Send XOFF, flow control dest
        OR      AH,(?TFLOW|?TXOFF) ;Indicate flow control asserted
        AND     AH,#~?TXON      ;Insure no XON pending
        ADD     DX,#5           ;Offset to status register
        IN      AL,DX           ;Read status
        TEST    AL,#?TXRDY      ;Test for transmitter ready
        JZ      ?14             ;Not ready
        SUB     DX,#5           ;Backup to data port
        MOV     AL,#'S'-$40     ;Get XOFF character
        OUT     DX,AL           ;Write to port
        AND     AH,#~?TXOFF     ;No pending XOFF needed
; Resave status flags to record ;changes
?14     MOV     Cflags,AH       ;Resave flags
; Reset 8259, Restore registers ;& return from interrupt
?15     MOV     AL,#$20         ;End of Interrupt command
        OUT     ?8259,AL        ;Write to interrupt controller
        POP     DS              ;Restore DS
        POP     DI              ;Restore DI
        POP     DX              ;Restore DX
        POP     BX              ;Restore BX
        POP     AX              ;Restore AX
        IRET
; Saved data segment incase we are running in small model
?dsval  DW      0               ;Stored code segment value
D</HEAD>
<BODY BGCOLOR=#FFFFFF TEXT=#000000><pre>
<FONT COLOR=#006000><B>IRET</B>