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