;
; 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>