home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS - Coast to Coast
/
simteldosarchivecoasttocoast.iso
/
io_util
/
komm.asm
< prev
next >
Wrap
Assembly Source File
|
1994-03-04
|
29KB
|
1,095 lines
page 55,132
title COMM - Device drivers for COM1 and COM2.
subttl Parameter definitions:
;
; COMM.ASM - Installable device drivers for COM1 and COM2.
;
; All of this code originated at my dainty fingertips, and I won't be mad
; at anybody who wants to help himself to it. - Peter Pearson.
;
; Version of 20 June 1985.
;
; Revision history:
; 852005 - Send ^S when input buffer approaches fullness, ^Q when it
; is nearly empty again.
;
cseg segment para public 'code'
org 0
;
; Equates:
;
CTRLS equ 19
CTRLQ equ 17
primary equ 3f8h ;Base address for primary asynch.
secondary equ 2f8h ;Base address for secondary asynch.
prim_int equ 0ch ;Hardware interrupt vector number.
sec_int equ 0bh ;Hardware interrupt vector number.
;
;
; Description of the Serial Port:
;
; I/O I/O
; addr addr
; ---- ----
; 3F8 2F8 TX data, RX data, Div Latch LSB.
; 3F9 2F9 Interrupt Enable, Div Latch MSB.
; 7 6 5 4 3 2 1 0
; | | | | | | | \_ Data Available.
; | | | | | | \___ Xmit Holding Reg Empty.
; | | | | | \_____ Receiver Line Status.
; | | | | \_______ Modem Status.
; | | | \_________ = 0.
; | | \___________ = 0.
; | \_____________ = 0.
; \_______________ = 0.
;
; 3FA 2FA Interrupt Identification.
; 7 6 5 4 3 2 1 0
; | | | | | \_\_\___________ 001 = no interrupt.
; | | | | | 110 = rcvr status.
; | | | | | 100 = rcvd data.
; | | | | | 010 = THR empty.
; | | | | | 000 = Modem status.
; | | | | \_______ = 0.
; | | | \_________ = 0.
; | | \___________ = 0.
; | \_____________ = 0.
; \_______________ = 0.
;
; 3FB 2FB Line Control.
; 7 6 5 4 3 2 1 0
; | | | | | | | \_ Word Length Select Bit 0.
; | | | | | | \___ Word Length Select Bit 1.
; | | | | | \_____ Number Stop Bits.
; | | | | \_______ Parity Enable.
; | | | \_________ Even Parity Select.
; | | \___________ Stick Parity.
; | \_____________ Set Break.
; \_______________ Divisor Latch Access Bit.
;
; 3FC 2FC Modem Control.
; 7 6 5 4 3 2 1 0
; | | | | | | | \_ Data Terminal Ready.
; | | | | | | \___ Request to Send.
; | | | | | \_____ Out 1.
; | | | | \_______ Out 2. (= 1 to enable ints.)
; | | | \_________ Loop.
; | | \___________ = 0.
; | \_____________ = 0.
; \_______________ = 0.
;
; 3FD 2FD Line Status.
; 7 6 5 4 3 2 1 0
; | | | | | | | \_ Data Ready.
; | | | | | | \___ Overrun Error.
; | | | | | \_____ Parity Error.
; | | | | \_______ Framing Error.
; | | | \_________ Break interrupt.
; | | \___________ Transmitter Holding Reg Empty.
; | \_____________ Transmitter Shift Reg Empty.
; \_______________ = 0.
;
; 3FE 2FE Modem Status.
; 7 6 5 4 3 2 1 0
; | | | | | | | \_ Delta Clear to Send.
; | | | | | | \___ Delta Data Set Ready.
; | | | | | \_____ Trailing Edge Ring Indicator.
; | | | | \_______ Delta Rx Line Signal Detect.
; | | | \_________ Clear to Send.
; | | \___________ Data Set Ready.
; | \_____________ Ring Indicator.
; \_______________ Receive Line Signal Detect.
;
;
R_tx equ 0 ;UART data-to-transmit register.
R_rx equ 0 ;UART received-data register.
R_ie equ 1 ;UART interrupt-enable register.
R_ii equ 2 ;UART interrupt-identification register.
R_lc equ 3 ;UART Line-Control register.
R_mc equ 4 ;UART Modem-Control register.
R_ls equ 5 ;UART line-status register.
LC_dlab equ 80h ;UART LC reg. DLAB bit.
MC_dtr equ 1 ;UART MC reg. DTR bit.
MC_rts equ 2 ;UART MC reg. RTS bit.
MC_out2 equ 8 ;UART MC reg. "OUT2" bit.
LS_dr equ 1 ;UART LS reg. "data ready" bit.
LS_or equ 2 ;UART LS reg. "overrun error" bit.
LS_pe equ 4 ;UART LS reg. "parity error" bit.
LS_fe equ 8 ;UART LS reg. "framing error" bit.
LS_bi equ 10h ;UART LS reg. "Break Interrupt" bit.
LS_thre equ 20h ;UART LS reg. "Xmit Hold Reg empty" bit.
LS_tsre equ 40h ;UART LS reg. "Xmit Shift Reg Empty" bit.
IE_da equ 1 ;UART IE reg. "Data Available" bit.
IE_thre equ 2 ;UART IE reg. "Xmit Hold Reg Empty" bit.
IE_rls equ 4 ;UART IE reg. "Receive Line Status" bit.
IE_ms equ 8 ;UART IE reg. "Modem Status" bit.
II_never equ 0f8h ;UART II reg: these bits never on.
; Definitions for the Interrupt Controller Chip:
ICC_msk equ 21h ;I/O address of ICC's "mask" register.
ICC_ctl equ 20h ;I/O address of ICC's "control" reg.
ICC_eoi equ 20h ;Non-specific end-of-interrupt.
MSK_d1 equ 10h ;Bit in MSK: disable IRQ4 (COM1).
MSK_d2 equ 08h ;Bit in MSK: disable IRQ3 (COM2).
org 0 ;Define layout of Request Header:
RH_rhlen db ? ;Length of Request Header (bytes).
RH_unit db ? ;Unit code. (No meaning for char dev.)
RH_command db ? ;Command code.
RH_status dw ? ;Status.
RH_reserved db 8 dup(?) ;Reserved for DOS.
RH_char db ? ;Character of data.
RH_end_addr label word ;Ending address for INIT call.
RH_buf_addr dd ? ;Buffer address for INPUT or OUTPUT.
RH_count dw ? ;Byte count for INPUT or OUTPUT.
org 0
STAT_err equ 8000h ;Req Hdr, status reg., "error" bit.
STAT_busy equ 200h ;Req Hdr, status reg., "busy" bit.
STAT_done equ 100h ;Req Hdr, status reg., "done" bit.
S_E_unkunit equ 01h ;Req Hdr, status reg., "unknown unit" error code.
S_E_not_rdy equ 02h ;Req Hdr, status reg., "not ready" error code.
S_E_unkcom equ 03h ;Req Hdr, status reg., "unknown command" error code.
S_E_write equ 0ah ;Req Hdr, status reg., "write fault" error code.
S_E_read equ 0bh ;Req Hdr, status reg., "read fault" error code.
S_E_general equ 0ch ;Req Hdr, status reg., "general failure" error code.
page
;
; Equates relating to data structures:
;
; Buffers:
; --------
;
; When we talk about a "buffer" in this program, we're talking not
; just about the area of data storage, but about the pointers to that
; data area as well. Four pointers are expected to precede the first
; byte of data storage:
; BEGINNING - Offset of the first byte of data storage.
; END - Offset of the first byte AFTER data storage.
; IN - Offset at which next byte is to be put into buffer.
; OUT - Offset from which next byte is to be taken out
; of buffer.
; IN and OUT may range from BEGINNING to END-1. If IN = OUT, the
; buffer is empty.
; These definitions must be coordinated with the storage-allocation
; statements for the buffers near the end of this program (just before
; the INIT handler.)
;
B_beg equ 0
B_end equ 2
B_in equ 4
B_out equ 6
B_data equ 8
;
in_buf_len equ 2000 ;Length of input buffers.
out_buf_len equ 100 ;Length of output buffers.
near_full equ 1200 ;Input buffer is nearly full when it
; has more than this number of data
; bytes.
near_empty equ 200 ;Input buffer is nearly empty when it
; has fewer than this number of data
; bytes.
;
;
; Unit Control Blocks:
; --------------------
;
; (I just made up that name. Any conflict with any other usage is
; unfortunate.)
; A UCB is assigned to each asynchronous communications device
; (COM) we handle. (E.g., one for COM1, another for COM2.) The
; UCB contains
; BASE - The base I/O address for the hardware device.
; HOLD - XON/XOFF flag.
; HOLD & 1 nonzero means we have received a CTRLS.
; HOLD & 2 nonzero means we have sent a CTRLS.
; INBUF - An input buffer (as described above).
; OUTBUF - An output buffer (as described above).
;
; Take care to keep these definitions compatible with the actual
; allocation of these data areas near the end of this program (just
; before label "init:").
;
U_base equ 0
U_stat equ 2 ;Status bits, defined below.
U_inbuf equ 4
U_outbuf equ U_inbuf+B_data+in_buf_len
U_totlen equ U_outbuf+B_data+out_buf_len
;
; Bits in U_stat:
;
US_outhold equ 1 ;Host has sent us a "hold it".
US_inhold equ 2 ;We have sent the host a "hold it".
US_saywait equ 4 ;We need to send a "hold it".
US_saygo equ 8 ;We need to send an "OK to send" (un-holdit).
subttl Device Headers:
page
;
; Here are the Device Headers:
;
coms proc far
assume cs:cseg,ds:cseg
begin:
next_dev1 dw DevHdr2 ;Pointer to next device.
dw -1
attribute1 dw 0c000h ;Attributes.
strategy1 dw Com1Strat ;Pointer to "device strategy" entry.
interrupt1 dw Com1Int ;Pointer to software "interrupt" entry.
dev_name1 db 'COM1 '
DevHdr2:
next_dev2 dw -1 ;Pointer to next device.
dw -1
attribute2 dw 0c000h ;Attributes.
strategy2 dw Com2Strat ;Pointer to "device strategy" entry.
interrupt2 dw Com2Int ;Pointer to software "interrupt" entry.
dev_name2 db 'COM2 '
subttl Dispatch Table:
page
;
; Here is the dispatch table:
;
dispatch label byte
dw init ;Initialization.
dw MediaCheck ;Media check (block devices only).
dw BuildBPB ;Build BPB (block devices only).
dw IoctlIn ;IOCTL input.
dw input ;input (read).
dw NDInput ;Non-destructive input, no wait.
dw InStat ;Status for input.
dw InFlush ;Input flush.
dw output ;Output (write).
dw OutVerify ;Output with verify.
dw OutStat ;Status for output.
dw OutFlush ;Output flush.
dw IoctlOut ;IOCTL output.
subttl Entry points:
page
;
; Device "Strategy" entry points:
;
Com1Strat:
Com2Strat:
mov cs:ReqHdrSeg,es ;Segment of Request Header Pointer.
mov cs:ReqHdrOff,bx ;Offset of Request Header Pointer.
ret
;
; Device "Interrupt" entry points:
;
Com1Int: push ax
push bx
xor al,al ;Unit # for COM1 is 0.
mov bx,offset UCB1
jmp JointInt
Com2Int: push ax
push bx
mov al,1 ;Unit # for COM2 is 1.
mov bx,offset UCB2
JointInt:
push cx
push dx
push di
push si
push ds
push es
push cs
pop ds ;DS = CS.
mov unit,al ;Save unit number.
mov dx,[bx] ;DX = base address for appropriate card.
les bx,ReqHdr ;Load ES and BX pointers to Request Hdr.
;
; Dispatch through the Dispatch Table:
;
mov al,es:[bx]+RH_command ;Get function byte.
rol al,1
lea di,dispatch
xor ah,ah
add di,ax ;Jump through dispatch table +
jmp word ptr[di] ; 2 * function code.
subttl Return paths:
page
;
; Return "Unknown Unit" error code.
;
BadUnit: mov ax,STAT_err+STAT_done+S_E_unkunit
jmp SetStat
;
; Return "done" status.
;
NormalDone: mov ax,STAT_done
;
; Store AX in the STATUS word of the Request Header, then restore registers
; and return.
;
SetStat: lds bx,cs:ReqHdr
mov [bx]+RH_status,ax
;
; Restore the original registers and return.
;
RestRegsRet:
pop es
pop ds
pop si
pop di
pop dx
pop cx
pop bx
pop ax
ret
subttl Command processors:
page
;
; IOCTL input.
; Do the requested function.
; Set the actual number of bytes transferred.
; Set the status word in the Request header.
;
IoctlIn:
call CheckUnit
jc BadUnit
jmp NormalDone
page
;
; Input.
; Do the requested function.
; Set the actual number of bytes transferred.
; Set the status word in the Request header.
;
input:
mov cx,es:[bx]+RH_count ;CX = # bytes to transfer.
jcxz NormalDone
les di,es:[bx]+RH_buf_addr ;ES:DI points to destination.
call GetBase
jc BadUnit
call EnabInt
add bx,U_inbuf ;BX -> appropriate input buffer.
cld
input1: call TakeByte ;Get byte in AL.
jnc input6 ;Jump if no more chars avail.
stosb ;Store in es:[di++].
loop input1 ;Continue.
input6:
mov ax,[bx]+U_stat-U_inbuf
test ax,US_inhold
jz input8 ;If the "I've sent CTRLS" flag
call UsedBuff ; is set and there are fewer
cmp ax,near_empty ; than near_empty data bytes
jns input8 ; in the buffer, make a note
; to send a CTRLQ.
or word ptr [bx]+U_stat-U_inbuf,US_saygo
input8:
or cx,cx ;If we didn't transfer the
jz NormalD2 ; requested number of chars,
les bx,ReqHdr ; calculate the number of
mov ax,es:[bx]+RH_count ; characters moved and store
sub ax,cx ; it in the Request Header.
mov es:[bx]+RH_count,ax ;
mov ax,STAT_done+STAT_busy ;Return BUSY.
jmp SetStat
page
;
; Non-Destructive Input, No Wait.
; Return a byte from the device.
; Set the status word in the Request Header.
;
NDInput:
call GetBase
jc BadUnit
call EnabInt
add bx,U_inbuf ;BX -> appropriate input buff.
mov ax,[bx]+B_in ;Examine buffer's pointers.
cmp ax,[bx]+B_out
jz ndin1 ;Jump if buffer empty.
mov si,ax
mov al,[si] ;Fetch a character.
mov cx,STAT_done
jmp ndin2
ndin1: xor al,al ;No character available.
mov cx,STAT_done+STAT_busy
ndin2: les bx,ReqHdr
mov es:[bx]+RH_char,al
mov ax,cx
jmp SetStat
page
;
; Status for Input.
; Perform the operation.
; Set the status word in the Request Header.
;
InStat:
call GetBase
jc BadUnit2
add bx,U_inbuf ;BX -> appropriate input buff.
mov ax,STAT_done
mov cx,[bx]+B_in ;Examine buffer's pointers.
cmp cx,[bx]+B_out
jnz inst1 ;Jump if buffer not empty.
or ax,STAT_busy ;If empty, say "busy".
inst1: jmp SetStat
;
; Input Flush.
; Perform the operation.
; Set the status word in the Request Header.
;
InFlush:
call GetBase ;BX -> appropriate input buff.
jc BadUnit2
add bx,U_inbuf
mov ax,[bx]+B_in ;Set the "out" pointer equal to
mov [bx]+B_out,ax ; the "in" pointer.
NormalD2: jmp NormalDone
page
;
; Output (write) and Output with Verify.
; Do the requested function.
; Set the actual number of bytes transferred.
; Set the status word in the Request header.
;
output:
OutVerify:
mov cx,es:[bx]+RH_count ;CX = # bytes to transfer.
jcxz NormalD2
les si,es:[bx]+RH_buf_addr ;ES:SI points to source.
call GetBase
jc BadUnit2
add bx,U_outbuf ;BX -> appropriate output buf.
cld
output1: lods es:byte ptr [si] ;Get 1 char in AL.
call PutByte ;Put character in output buff.
jnc output8 ;Jump if no room in output buffer.
loop output1
output8: sub bx,U_outbuf ;BX -> appropriate UCB.
call EnabInt ;Enable interrupts, if approp.
or cx,cx ;If we didn't transfer the
jz NormalD2 ; requested number of chars,
les bx,ReqHdr ; calculate the number of
mov ax,es:[bx]+RH_count ; characters moved and store
sub ax,cx ; it in the Request Header.
mov es:[bx]+RH_count,ax ;
mov ax,STAT_done+STAT_busy ;Return BUSY.
jmp SetStat
BadUnit2: jmp BadUnit
page
;
; Status for Output.
; Perform the operation.
; Set the status word in the Request Header.
;
OutStat:
call GetBase
jc BadUnit2
add bx,U_outbuf ;BX -> appropriate output buf.
call IsRoom ;Is there room in the buffer?
mov ax,STAT_done
jc outst1 ;Jump if buffer not full.
or ax,STAT_busy ;If full, say "busy".
outst1: jmp SetStat
;
; Output Flush.
; Perform the operation.
; Set the status word in the Request Header.
;
OutFlush:
call CheckUnit
jc BadUnit2
jmp NormalDone
;
; IOCTL output.
; Do the requested function.
; Set the actual number of bytes transferred.
; Set the status word in the Request header.
;
IoctlOut:
call CheckUnit
jc BadUnit2
jmp NormalDone
page
;
; Entry points for functions not implemented:
;
NotImplemented:
MediaCheck:
BuildBPB:
mov ax,STAT_err+STAT_done+S_E_unkcom
jmp SetStat
subttl Interrupt handlers:
page
;
; Hardware interrupt handlers.
; We run with two interrupts enabled:
; Data Available
; Transmit Holding Register Empty.
;
; If Data Available is set
; If there's room in the IN buffer, put the byte there.
; If there are data in the OUT buffer,
; If the Transmit Holding Register is empty, output a byte.
;
Hdw1Int:
push ax
mov ax,offset UCB1
jmp HdwInt
Hdw2Int:
push ax
mov ax,offset UCB2
HdwInt:
push bx
push ds
push cx
push dx
push di
push si
push es
push cs
pop ds
mov bx,ax
mov di,[bx] ;DI = base I/O address.
lea dx,R_lc[di]
in al,dx ; the Line Control Register.
and al,255-LC_dlab
out dx,al
lea dx,R_ls[di]
in al,dx ;Read the Line Status Reg.
test al,LS_dr ;"Data Ready" set?
jz HdwI2
push ax
call ReadByte ;Process the incoming byte.
pop ax
HdwI2: test al,LS_thre ;"Xmit Holding Reg Empty" set?
jz HdwI3
;If we want to send a CTRLS or
; CTRLQ to the host, this is our
; chance to do it.
;Note that, if both are set, the
; CTRLS is not sent.
;
test word ptr [bx]+U_stat,US_saywait+US_saygo
jz HdwI5
test word ptr [bx]+U_stat,US_saygo
jz HdwI6
mov al,CTRLQ
and word ptr [bx]+U_stat,0ffffh-(US_saywait+US_saygo+US_inhold)
jmp HdwI7
HdwI6: ;else
mov al,CTRLS
and word ptr [bx]+U_stat,0ffffh-(US_saywait+US_saygo)
or word ptr [bx]+U_stat,US_inhold
HdwI7: mov dx,di
out dx,al
jmp HdwI3
; If we get here, we didn't want to
; send a CTRLS or CTLRQ.
HdwI5:
test word ptr [bx]+U_stat,US_outhold
jnz HdwI3 ;Jump if output held by CTRL-S.
add bx,U_outbuf
call TakeByte
jnc HdwI4 ;Jump if nothing to output.
mov dx,di
out dx,al ;Output another character.
HdwI4: sub bx,U_outbuf
HdwI3: ;;;
pop es
pop si
pop di
pop dx
pop cx
cli
mov al,ICC_eoi ;Issue "end-of-interrupt" command
out ICC_ctl,al ; to the interrupt-controller chip.
call EnabInt ;Re-enable interrupts at the UART.
pop ds
pop bx
pop ax
iret
page
;
; Process an incoming character.
; Pick up the character in the device.
; If it's a CTRLS or CTRLQ,
; set or clear the "he said hold" flag for this device;
; otherwise
; put it into the buffer.
;
; Enter with:
; BX pointing to the UCB.
; DI containing the base address for the device.
;
; Clobbers: AX, DX.
;
; Side effects:
; May set or clear the low-order bit of the "hold" word
; for this device.
;
ReadByte proc near
lea dx,R_rx[di]
in al,dx ;Read the data byte.
cmp al,CTRLS ;Watch for XOFF and XON (CTRLS and
jnz ReadB1 ; CTRLQ).
mov ax,US_outhold
or word ptr [bx]+U_stat,ax
ret
ReadB1: cmp al,CTRLQ
jnz ReadB3
and word ptr [bx]+U_stat,0ffffh-US_outhold
ret
ReadB3: add bx,U_inbuf
call PutByte ;Put it into the buffer.
call UsedBuff
cmp ax,near_full ;If the buffer is getting full,
js ReadB4 ; and we haven't already asked the
; host to wait, do that.
test word ptr [bx]+U_stat-U_inbuf,US_inhold
jnz ReadB4
or word ptr [bx]+U_stat-U_inbuf,US_saywait
ReadB4:
sub bx,U_inbuf
ret
ReadByte endp
subttl General-Purpose Subroutines:
page
;
; General-purpose subroutines.
;
; ----------------------------------------------------------------------
;
; Return a pointer to the "base" of the data block for this unit.
;
; Enter with: unit number (0 or 1) in the variable "unit".
; DS = CS.
;
; Returns: BX containing the offset of either UCB1 or UCB2.
;
GetBase proc near
call CheckUnit
jnc gb0
ret
gb0: test al,0ffh
jz gb1
mov bx,offset UCB2
ret
gb1: mov bx,offset UCB1
ret
GetBase endp
;
; ----------------------------------------------------------------------
;
; Check the unit number to make sure it's valid.
; Enter with:
; the variable "unit" set to the unit number to be checked;
; the variable "maxunit" set to the maximum unit number allowed.
;
; Returns with:
; "carry" condition set if "unit" < 0 or "unit" > "maxunit".
; AL = contents of the variable "unit".
;
CheckUnit proc near
mov al,unit
or al,al
js RetCarry
test maxunit,080h
jnz RetCarry
cmp al,maxunit
jg RetCarry
clc
ret
RetCarry: stc
ret
CheckUnit endp
page
;
; ----------------------------------------------------------------------
;
; Take a byte out of a buffer and return it in AL.
;
; Enter with:
; BX pointing to the buffer from which a byte is to be taken.
;
; Returns:
; Carry condition and a byte in AL if the buffer was not empty,
; NoCarry condition if the buffer was empty.
;
; Side effect: The "output" pointer for the buffer is advanced to reflect
; the used-upness of the character taken.
;
; Note:
; Since this subroutine may be interrupted, it is important that
; the character be taken out of the buffer before the OUT pointer
; is advanced to reflect its absence.
;
TakeByte proc near
mov ax,[bx]+B_out ;Output pointer.
cmp ax,[bx]+B_in ; = input pointer?
jz tb9 ;If equal, buffer is empty.
push si
push cx
mov si,ax
mov cl,[si] ;Fetch the character.
call advance ;Advance the "out" pointer.
mov [bx]+B_out,ax ;Store updated ptr.
mov al,cl
pop cx
pop si
stc ;Set carry flag.
tb9: ret
TakeByte endp
page
;
; ----------------------------------------------------------------------
;
; Put a character into a buffer.
;
; Enter with:
; BX pointing to the beginning of a buffer,
; AL holding the character to be put into the buffer.
;
; Returns:
; NoCarry condition if the buffer is full (character couldn't be put).
; Carry condition if the character is successfully put into the buffer.
;
; Note:
; Since this subroutine may be interrupted, it is important that
; the character be put into the buffer before the IN pointer is
; adjusted to reflect its availablility.
;
PutByte proc near
push cx
push di
mov cl,al ;CL = the character.
mov ax,[bx]+B_in
mov di,ax ;DI = original IN pointer.
call advance ;AX = updated IN pointer.
cmp ax,[bx]+B_out
jz pb9 ;Jump if buffer is full.
mov [di],cl ;Store the character.
mov [bx]+B_in,ax ;Update the pointer.
stc
pb9: pop di
pop cx
ret
PutByte endp
page
;
; ----------------------------------------------------------------------
;
; Is there room in this buffer?
;
; Enter with BX pointing to the beginning of a buffer.
;
; Returns:
; NoCarry condition if the buffer is full,
; Carry condition if there is room in the buffer.
;
IsRoom proc near
mov ax,[bx]+B_in
call advance
cmp ax,[bx]+B_out
jz ir9
stc
ir9: ret
IsRoom endp
;
; ----------------------------------------------------------------------
;
; How many data bytes are in this buffer?
;
; Enter with BX pointing to the beginning of a buffer.
;
; Returns:
; AX containing the number of bytes used in the buffer.
; Z flag set if AX contains 0.
;
UsedBuff proc near
mov ax,[bx]+B_in
sub ax,[bx]+B_out
jns ub9
add ax,[bx]+B_end
sub ax,[bx]+B_beg
ub9: ret
UsedBuff endp
page
;
; ----------------------------------------------------------------------
;
; Advance a buffer pointer.
;
; Enter with
; BX pointing to the beginning of the buffer.
; AX containing a pointer into the buffer.
;
; Returns with
; AX incremented by one, wrapped to the beginning of the buffer
; if necessary.
;
advance proc near
inc ax
cmp ax,[bx]+B_end
jnz adv9
mov ax,[bx]
adv9: ret
advance endp
;
; ----------------------------------------------------------------------
;
; Reset the input-buffer pointers for one unit.
;
; Enter with BX pointing to the Unit Control Block for the unit whose input-
; buffer pointers are to be cleared.
;
ResInbuf proc near
mov ax,[bx]+U_inbuf+B_beg
mov [bx]+U_inbuf+B_in,ax
mov [bx]+U_inbuf+B_out,ax
ret
ResInbuf endp
;
; ----------------------------------------------------------------------
;
; Reset the output-buffer pointers for one unit.
;
; Enter with BX pointing to the Unit Control Block for the unit whose output-
; buffer pointers are to be cleared.
;
ResOutbuf proc near
mov ax,[bx]+U_outbuf+B_beg
mov [bx]+U_outbuf+B_in,ax
mov [bx]+U_outbuf+B_out,ax
ret
ResOutbuf endp
page
;
; ----------------------------------------------------------------------
;
; Enable appropriate interrupts.
;
; Enter with:
; BX pointing to the Unit Control Block controlling the device
; whose interrupts are to be enabled.
;
EnabInt proc near
push cx
push dx
push di
xor cx,cx
add bx,U_inbuf
call IsRoom
jnc ei1 ;If there's room in the input buffer,
or cl,IE_da ; enable "data available" interrupt.
ei1: sub bx,U_inbuf
;Enable the "transmit holding register
; empty" interrupt if:
; - there's something in the output
; buffer, and output is not being
; "held"; or
; - we want to send a CTRLS or CTRLQ.
mov ax,[bx]+U_outbuf+B_in
cmp ax,[bx]+U_outbuf+B_out
mov ax,[bx]+U_stat
jz ei2
test ax,US_outhold
jz ei4
ei2: ;;;
test ax,US_saygo+US_saywait
jz ei3
ei4: or cl,IE_thre
ei3:
mov di,[bx]
lea dx,R_lc[di]
in al,dx ;Turn off DLAB (just in case).
and al,255-LC_dlab
out dx,al
lea dx,R_ie[di]
mov al,0
out dx,al ;Interrupt Enable = 0 (to ensure a
mov al,cl ; transition).
out dx,al ;Set final Interrupt Enable.
lea dx,R_mc[di] ;Turn the enabling "out2" bit
mov al,MC_out2+MC_rts+MC_dtr ; on.
out dx,al
pop di
pop dx
pop cx
ret
EnabInt endp
subttl Storage:
page
;
; Storage for both COM1 and COM2.
;
ReqHdr dd ? ;The Request Header:
ReqHdrOff equ word ptr ReqHdr
ReqHdrSeg equ ReqHdrOff+2
;
;
unit db ? ;0 for COM1, 1 for COM2.
;(Set according to which "interrupt"
; entry point is used.)
maxunit db -1 ;Maximum unit number allowed.
;(Set during INIT.)
end0: ;Ending address if no asynch cards found.
;
; Unit Control Block for COM1:
; Keep this space allocation consistent with the definitions of U_inbuf,
; B_end, etc.
;
UCB1:
base_1: dw ? ;Base address for COM1.
;(Set during INIT.)
hold_1: dw ?
inb1_beg: dw offset inb1
inb1_end: dw offset inb1+in_buf_len
inb1_in: dw ?
inb1_out: dw ?
inb1: db in_buf_len dup(?)
outb1_beg: dw offset outb1
outb1_end dw offset outb1+out_buf_len
outb1_in: dw ?
outb1_out: dw ?
outb1: db out_buf_len dup(?)
end1: ;Ending address if only 1 asynch card found.
;
; Unit Control Block for COM2.
; Keep this space allocation consistent with the definitions of U_inbuf,
; B_end, etc.
;
UCB2:
base_2: dw ? ;Base address for COM2.
;(Set during INIT.)
hold_2: dw ?
inb2_beg: dw offset inb2
inb2_end: dw offset inb2+in_buf_len
inb2_in: dw ?
inb2_out: dw ?
inb2: db in_buf_len dup(?)
outb2_beg: dw offset outb2
outb2_end dw offset outb2+out_buf_len
outb2_in: dw ?
outb2_out: dw ?
outb2: db out_buf_len dup(?)
;
end2: ;Ending address if 2 asynch cards found.
subttl Initialization:
page
;
; Initialization:
; Perform any initialization code.
; If COM2, skip this part.
; Fill base_1 and base_2, as appropriate.
; Initialize pointers to input & output buffers.
; Install our hardware interrupt vectors.
; Set up the ending address for resident code.
; Set the status word in the Request Header.
;
init:
mov ax,endaddr ;Have we been called before?
or ax,ax
jnz initend ;Skip this part if so.
mov dx,primary
mov bx,offset base_1
call InitUCB
call HaveUnit ;Is "primary" card there?
jnz init2
push dx ;Assign our interrupt handler
mov dx,NextHandler ; to its interrupt.
mov al,prim_int
mov ah,25h
int 21h
mov ax,offset Hdw2Int ;Use other interrupt handler
mov NextHandler,ax ; next time.
pop dx
mov [bx],dx ;Use the first UCB
call EnabInt ; for it.
mov bx,offset base_2
call InitUCB
inc maxunit
init2: mov dx,secondary
call HaveUnit ;Is "secondary" card there?
jnz init3
push dx ;Assign our interrupt handler
mov dx,NextHandler ; to its interrupt.
mov al,sec_int
mov ah,25h
int 21h
pop dx
mov [bx],dx ;Use for it whichever
call EnabInt ; UCB is indicated by
inc maxunit ; BX.
init3: mov bl,maxunit
inc bl
xor bh,bh
rol bl,1
mov ax,[bx]+endtable
mov endaddr,ax
initend: mov ax,endaddr ;Store the ending address.
lds bx,cs:ReqHdr
mov [bx]+RH_end_addr,ax
mov [bx]+RH_end_addr+2,cs
in al,ICC_msk ;Tell the interrupt controller chip
and al,255-(MSK_d1+MSK_d2) ; to allow these interrupts.
out ICC_msk,al
jmp NormalDone
;
; Is there an asynch card answering at this address?
;
; Enter with:
; DX holding the base I/O address to be interrogated.
;
; Returns:
; Z condition if there is a card there,
; NZ condition if there is NO card there.
;
HaveUnit proc near
add dx,R_ii
in al,dx ;Read the Interrupt Identification
sub dx,R_ii ; register.
and al,II_never ;If there's a device there, these bits
ret ; will be zero.
HaveUnit endp
;
; Initialize a Unit Control Block (UCB).
;
; Enter with
; BX pointing to the data block for the channel whose input
; and output buffer pointers are to be reset.
;
InitUCB proc near
mov word ptr [bx]+U_stat,0
call ResInbuf
call ResOutbuf
ret
InitUCB endp
;
endaddr: dw 0 ;END address returned on INIT call.
endtable: dw offset end0 ;END address if 0 asynch cards found.
dw offset end1 ;END address if 1 asynch cards found.
dw offset end2 ;END address if 2 asynch cards found.
NextHandler: dw offset Hdw1Int ;Successive interrupt-handler addresses.
coms endp
cseg ends
end begin