home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 September
/
Simtel20_Sept92.cdr
/
msdos
/
ddjmag
/
ddj8706.arc
/
EXCOM.ASM
< prev
next >
Wrap
Assembly Source File
|
1987-08-05
|
21KB
|
891 lines
title extended int 14 handler version 2
name excom
;
; by Tom Zimniewicz
;
; this file is one of three, the other two are exmode.c and excom.txt
;
;
; (C) 1987, Crystal Computer Consulting Inc.
; This software may be used freely, at your own risk,
; as long as this notice is not removed.
;
_text segment word public 'code'
assume cs:_text, ss:_text, ds:_text, es:_text
org 0100h ; com-able
start: ; must be first
jmp excom
even
; misc constants
INSZ equ 0100h ; input buffer size, must be > 20h
XOFFSZ equ INSZ - 010h ; input flow control turn off point
XONSZ equ INSZ - 020h ; input flow control turn on point
OUTSZ equ 0100h ; output buffer size, must be > 0
NOCTS equ 010h ; CTS not required to transmit
NODSR equ 020h ; DSR not required to transmit
DTR equ 001h ; select DTR input flow control
RTS equ 002h ; select RTS input flow control
XNXFIN equ 001h ; select XON/XOFF input flow control
XNXFOUT equ 002h ; select XON/XOFF output flow control
ANYXOUT equ 004h ; any char restarts output after XOFF
B19200 equ 010h ; set baud rate to 19200
B38400 equ 020h ; set baud rate to 38400
OUT2 equ 008h ; bit to set OUT2
C1MSK equ 010h ; com1 mask for 8259
C2MSK equ 008h ; com2 mask for 8259
UARTMSK equ 00Bh ; 8250 mask for xmit, recv and status
XMTINT equ 002h ; transmit ready interrupt
RCVINT equ 004h ; receive data interrupt
; structure for port control blocks
pcbs struc
; in & out buffers parameters
putin dw ? ; points to last in char put
getin dw ? ; points to next in char to get
incnt dw ? ; number of in chars in buffer
inend dw ? ; address past in buffer end
putout dw ? ; points to last out char put
getout dw ? ; points to next out char to get
outcnt dw ? ; number of out chars in buffer
outend dw ? ; address past out buffer end
inbuf dw INSZ dup (?) ; the input buffer
outbuf db OUTSZ dup (?) ; the output buffer
; other stuff
pbase dw ? ; port address
timeout db ? ; timeout outer loop
mask db ? ; mask to enable port interrupt
vector db ? ; uart hardware interrupt vector
mscopts db ? ; misc in/out options
inopts db ? ; holds receive protocol options
outopts db ? ; holds transmit protocol options
oldier db ? ; old interrupt enable register
oldlcr db ? ; old line control register
oldmcr db ? ; old modem control register
olddll db ? ; old baud low divisor latch
olddlm db ? ; old baud high divisor latch
inoff db ? ; set if recv has been stopped
outxoff db ? ; set if xmit stopped by ^S
outbsy db ? ; set if xmit is busy
xmtxnxf db ? ; protocol character to xmit
modstt db ? ; modem status
lchar db ? ; holds character attributes
baudndx db ? ; baud rate table index
pcbs ends
; allocate storage for port control blocks
pcb pcbs 2 dup (<>) ; array of port control blocks
old0B dd ? ; old vector for int0B
old0C dd ? ; old vector for int0C
old14 dd ? ; old vector for int14
oldmsk db ? ; old mask for 8259
; baud rate table for UART initialization
baudtbl dw 0417h ; divisor for 110 baud
dw 0300h ; divisor for 150 baud
dw 0180h ; divisor for 300 baud
dw 00C0h ; divisor for 600 baud
dw 0060h ; divisor for 1200 baud
dw 0030h ; divisor for 2400 baud
dw 0018h ; divisor for 4800 baud
dw 000Ch ; divisor for 9600 baud
baud19 dw 0006h ; divisor for 19200 baud
dw 0003h ; divisor for 38400 baud
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ;
; 0Bh & 0Ch (com2 & com1) interrupt handler ;
; put characters into the appropriate buffer ;
; input flow control - stop input when buffer nears full ;
; ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
int0B:
; point to com2 port control block with ds:si
push ds
push si
mov si, cs
mov ds, si
lea si, pcb + size pcbs
jmp short a00
int0C:
; point to com1 port control block with ds:si
push ds
push si
mov si, cs
mov ds, si
lea si, pcb
a00:
push di
push dx
push bx
push ax
; determine interrupt type, jump to the handler
mov dx, [si].pbase
inc dx
inc dx
in al, dx
dec dx
dec dx
cmp al, XMTINT
je a100 ; transmit interrupt
cmp al, RCVINT
jne a000 ; assume status interrupt
jmp a200 ; receive interrupt
a10:
; send interrupt complete command to interrupt controller
mov al, 020h
out 020h, al
pop ax
pop bx
pop dx
pop di
pop si
pop ds
iret
;
; modem status change interrupt
a000:
add dx, 6
in al, dx ; modem status
mov [si].modstt, al
; restart output if required and ok
or al, [si].outopts
cmp al, 0FFh
jne a10 ; status inhibits output
cmp [si].outxoff, 0
jne a10 ; xoff inhibits output
sub dx, 6
call a010 ; restart output
jmp short a10
;
; subroutine to start up output if required, called several
; places below uses al, assumes dx is port base
a010:
cmp [si].outbsy, 0
jne a030
a020: ; call here if outbsy already checked or to be ignored
inc dx
mov al, UARTMSK
out dx, al
mov [si].outbsy, 1
dec dx
a030:
ret
;
; transmit interrupt
a100:
cmp [si].xmtxnxf, 0
je a110
mov al, [si].xmtxnxf ; must send ^S or ^Q
out dx, al
mov [si].xmtxnxf, 0
jmp short a10
a110:
cmp [si].outxoff, 0
jne a130 ; xoff inhibits output
mov al, [si].modstt
or al, [si].outopts
cmp al, 0FFh
jne a130 ; status inhibits output
mov ax, [si].outcnt
dec ax
jl a130 ; output buffer empty
mov [si].outcnt, ax
mov di, [si].getout
mov al, [di]
out dx, al ; send the character
; inc buffer pointer, wrap if req'd
inc di
cmp di, [si].outend
jne a120
lea di, [si].outbuf
a120:
mov [si].getout, di
jmp short a10
a130:
mov [si].outbsy, 0
jmp a10
;
; receive interrupt
a200:
; read character
mov di, [si].putin ; buffer put pointer
in al, dx ; read character and clear interrupt
mov ah, al
; do output XON-XOFF processing, if it's ^S, stop output
; if ^Q or ANYXOUT is set, restart output
test [si].mscopts, XNXFOUT
jz a230
cmp al, 'Q' - '@'
je a210 ; it's a ^Q, restart the output
cmp [si].outxoff, 0
je a220
test [si].mscopts, ANYXOUT ; can any char restart it
jz a220
a210:
mov [si].outxoff, 0
call a010 ; restart output
jmp a10
a220:
cmp al, 'S' - '@'
jne a230
mov [si].outxoff, 1 ; it's a ^S, stop the output
jmp a10
a230:
; if this fills buffer to XOFFSZ, then send XOFF or
; drop DTR and/or RTS as indicated by extended options
cmp [si].incnt, XOFFSZ
jl a260 ; jump if buffer below turnoff point
cmp [si].inoff, 0
jne a250 ; jump if receive already disabled
mov [si].inoff, 1 ; indicate receiver now disabled
test [si].mscopts, XNXFIN
jz a240
mov [si].xmtxnxf, 'S' - '@' ; get ^S sent
call a020 ; restart output
a240:
mov bl, [si].inopts
cmp bl, 0
je a250
add dx, 4
in al, dx
not bl
and al, bl
out dx, al ; drop DTR and/or RTS
sub dx, 4
a250:
; if the buffer is full, set overflow status and exit
cmp [si].incnt, INSZ
jl a260
or word ptr [di], 0200h
jmp a10
a260:
; read the port status
add dx, 5
in al, dx
; get new buffer pointer, adjust for wrap around if required
inc di
inc di
cmp di, [si].inend
jne a270
lea di, [si].inbuf
a270:
; store new buffer pointer and put status:char into buffer
mov [si].putin, di
xchg ah, al
mov [di], ax
inc [si].incnt
jmp a10
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ;
; 14h interrupt handler ;
; emulate pc bios functions plus extensions ;
; ;
; dx is used to select the port for all calls ;
; dx = 0 for com1, dx = 1 for com2 ;
; ;
; ah = 0 initialize the port, parameters in al ;
; al bits 7-5 set the baud rate ;
; 000 = 110, 001 = 150, 010 = 300, 011 = 600 ;
; 100 = 1200, 101 = 2400, 110 = 4800, 111 = 9600 ;
; al bits 4-3 set the parity ;
; 00 = none, 01 = odd, 11 = even ;
; al bit 2 is number of stop bits ;
; 0 = 1 stop bit, 1 = 2 stop bits ;
; al bits 1-0 set the word length ;
; 00 = 5, 01 = 6, 10 = 7, 11 = 8 ;
; returns ah & al as per comm status (ah=3) below ;
; example: ah = 10101110 is 2400 baud, ;
; odd parity, 2 stop bits, 7 bit characters ;
; ;
; ah = 1 transmit the character in al, al preserved ;
; returns ah as per comm status (ah=3) below ;
; ;
; ah = 2 return receive character in al ;
; returns ah as per comm status (ah=3) below ;
; only bits 1-2-3-4-7 can be set ;
; ah having any bits set is a receive error or timeout ;
; ;
; ah = 3 return comm port status in ah & al ;
; ah bits are: ;
; b0 = data ready b1 = overrun ;
; b2 = parity error b3 = framing error ;
; b4 = break detect b5 = xmit holding reg empty ;
; b6 = xmit shift reg empty b7 = timeout ;
; al bits are: ;
; b0 = delta CTS b1 = delta DSR ;
; b2 = trail edge ring b3 = delta recv detect ;
; b4 = CTS b5 = DSR ;
; b6 = ring indicator b7 = recv signal detect ;
; ;
; ah = 4 extended options ;
; returns 05A5Ah in ax, used to identify excom ;
; al contains options as follows: ;
; bit 0 enables XON/XOFF input flow control ;
; bit 1 enables XON/XOFF output flow control ;
; bit 2 set to restart upon any character after XOFF ;
; bit 4 sets baud rate to 19200 ;
; bit 5 sets baud rate to 38400 ;
; ch contains options as follows: ;
; bit 0 enables DTR input flow control ;
; bit 1 enables RTS input flow control ;
; cl contains options as follows: ;
; bit 4 don't require CTS to transmit ;
; bit 5 don't require DSR to transmit ;
; ;
; ah = 5 remove excom, restore old vectors, release memory ;
; ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
int14:
sti
push ds
push si
push dx
push cx
push bx
; point to selected com port control block with ds:si
mov bx, cs
mov ds, bx
lea si, pcb
or dx, dx
jz b00
add si, size pcbs
b00:
; get port address in dx, return if port not installed
mov dx, [si].pbase
or dx, dx
jz b20
; ah has request type, jump to selected routine
cmp ah, 5
ja b20
mov bl, ah
add bl, bl
sub bh, bh
jmp word ptr [bx+b10]
b10:
dw b000 ; initialize
dw b100 ; transmit a char
dw b200 ; receive a char
dw b300 ; get status
dw b400 ; extended initialize
dw b500 ; remove excom
b20:
pop bx
pop cx
pop dx
pop si
pop ds
iret
;
; ah = 0
; initialize the port
b000:
push ax
; initialize the pcb, this 'empties' the buffers
lea ax, [si].inbuf
mov [si].putin, ax
inc ax
inc ax
mov [si].getin, ax
add ax, (INSZ * 2) - 2
mov [si].inend, ax
lea ax, [si].outbuf
mov [si].putout, ax
mov [si].getout, ax
add ax, OUTSZ
mov [si].outend, ax
sub ax, ax
mov [si].incnt, ax
mov [si].outcnt, ax
mov [si].inoff, al
mov [si].outxoff, al
mov [si].outbsy, al
mov [si].xmtxnxf, al
; set up baud rates and character attributes
pop ax
mov bl, al
and al, 01Fh
mov [si].lchar, al
mov al, [si].mscopts
and al, B19200 or B38400
jz short b010
mov bl, baud19 - baudtbl ; this is an extended baud rate
test al, B19200
jnz b020
add bl, 2
jmp short b020
b010:
mov cl, 4 ; normal baud rate
rol bl, cl
and bl, 0Eh
b020:
mov [si].baudndx, bl
call b030 ; init the hardware
jmp b300 ; return status
;
; subroutine to do hardware initialization
; assumes dx is port base, changes ax and bx
b030:
; set up interrupt vectors for 0Bh & 0Ch
push dx
lea dx, int0B
mov ah, 025h
mov al, 0Bh
int 021h
lea dx, int0C
mov ah, 025h
mov al, 0Ch
int 021h
pop dx
; init the baud rate and character attributes
add dx, 3
mov al, 080h
out dx, al
mov bl, [si].baudndx
sub bh, bh
mov ax, [bx+baudtbl]
sub dx, 3
out dx, al ; set low order divisor on uart
inc dx
mov al, ah
out dx, al ; set high order divisor on uart
inc dx
inc dx
mov al, [si].lchar
out dx, al ; set character attributes
add dx, 3
in al, dx ; get modem status
mov [si].modstt, al
; init interrupts
dec dx
dec dx
mov al, DTR or RTS or OUT2
out dx, al
cli
in al, 021h
and al, [si].mask
out 021h, al ; unmask com port on int controller
sti
sub dx, 3
mov al, UARTMSK
out dx, al ; enable interrupts on uart
dec dx
ret
;
; ah = 1
; transmit the character in al
b100:
push ax
cmp [si].outcnt, OUTSZ
jl b130 ; room in buffer
; wait for room to appear, or return timeout
mov bl, [si].timeout
add bl, bl ; shorter timeout loop, so loop more
b110:
sub cx, cx
b120:
cmp [si].outcnt, OUTSZ
jl b130
loop b120
dec bl
jnz b110
call b310 ; get line status
or ah, 080h ; indicate a timeout
jmp short b170
b130:
; put the character in the buffer, inc pointer and fix for wrap
mov bx, [si].putout
mov [bx], al
inc bx
cmp bx, [si].outend
jne b140
lea bx, [si].outbuf
b140:
mov [si].putout, bx
cli
inc [si].outcnt
cmp [si].outbsy, 0
jne b160
; spot check for damage, has the UART vector been changed
mov bl, [si].vector
mov cx, cs
sub ax, ax
push ds
mov ds, ax
mov bh, al
cmp cx, [bx]
pop ds
je b150
sti
call b030 ; re-init the hardware
cli
b150:
call a020 ; restart output
b160:
sti
call b310 ; get line status
b170:
pop cx
mov al, cl ; restore al
jmp b20
;
; ah = 2
; receive a character, put it in al
b200:
cmp [si].incnt, 0
jne b230 ; chars in buffer, return one
call b030 ; re-init the hardware
; wait for a character to appear in the buffer, or return timeout
mov al, [si].timeout
add al, al ; shorter timeout loop, so loop more
b210:
sub cx, cx
b220:
cmp [si].incnt, 0
jne b230
loop b220
dec al
jnz b210
mov ax, 08000h ; timeout return value
jmp b20
b230:
; if receiver is disabled and room now exists, enable receiver
cmp [si].inoff, 0
je b250 ; receive is already enabled
cmp [si].incnt, XONSZ
jg b250 ; buffer fuller than turn on point
mov [si].inoff, 0 ; indicate receiver enabled
test [si].mscopts, XNXFIN
jz b240
cli
mov [si].xmtxnxf, 'Q' - '@' ; get ^Q sent
call a020 ; restart output
sti
b240:
; it's easier to just assert DTR & RTS rather than see if needed
add dx, 4
in al, dx
or al, DTR or RTS
out dx, al ; assert DTR and RTS
b250:
; read the buffer, update get pointer, and wrap if needed
mov bx, [si].getin
mov ax, [bx] ; get status:char
inc bx
inc bx
cmp bx, [si].inend
jne b260
lea bx, [si].inbuf
b260:
mov [si].getin, bx ; updated pointer
dec [si].incnt
jmp b20
;
; ah = 3
; get port status
b300:
call b310
mov al, [si].modstt ; modem status
jmp b20
;
; subroutine to get line status from hardware and/or input buffer
; returns in ah, assumes dx is port base, changes al, bx, cl, & dx
b310:
add dx, 5
in al, dx ; line status
cmp [si].incnt, 0
je b320
; when chars in buffer, fix status as per status in buffer
mov bx, [si].getin
mov ah, [bx + 1] ; status
mov cl, 01Eh ; these status bits from buffer
and ah, cl
not cl
and al, cl
or al, ah ; phys & buffer status combined
or al, 1 ; char in buffer, show data ready
b320:
; show xmit holding register empty if room in xmit buffer
cmp [si].outcnt, OUTSZ
jl b330
and al, 0DFh ; no room
jmp short b340
b330:
or al, 020h ; room in xmit buffer
b340:
mov ah, al
ret
;
; ah = 4
; extended initialization
b400:
; save the options in the pcb
mov [si].mscopts, al
mov [si].inopts, ch
or cl, not (NOCTS or NODSR); simplify later use
mov [si].outopts, cl
mov ax, 05A5Ah ; magic value to identify excom
jmp b20
;
; ah = 5
; remove excom
b500:
push ds
push ds
; restore status of interrupt controller and uarts
cli
mov ah, oldmsk
and ah, C1MSK or C2MSK
in al, 021h
and al, not (C1MSK or C2MSK)
or al, ah
out 021h, al
sti
lea si, pcb ; com1 pcb
call b510 ; restore com1 uart status
lea si, pcb + size pcbs ; com2 pcb
call b510 ; restore com1 uart status
jmp short b530
; subroutine to restore uart status
; only called from just above
b510:
mov dx, [si].pbase
or dx, dx
jz b520 ; no port
add dx, 3
mov al, 080h
out dx, al ; set access to baud divisors
sub dx, 3
mov al, [si].olddll
out dx, al ; baud divisor low
inc dx
mov al, [si].olddlm
out dx, al ; baud divisor high
add dx, 2
mov al, [si].oldlcr
out dx, al ; line control register
inc dx
mov al, [si].oldmcr
out dx, al ; modem control register
sub dx, 3
mov al, [si].oldier
out dx, al ; interrupt enable register
b520:
ret
b530:
; restore old int 0Bh vector
lds dx, old0B
mov ah, 025h
mov al, 0Bh
int 021h
; restore old int 0Ch vector
pop ds
lds dx, old0C
mov ah, 025h
mov al, 0Ch
int 021h
; restore old int 14h vector
pop ds
lds dx, old14
mov ah, 025h
mov al, 14h
int 021h
; free excom's memory, this memory
push es
mov ax, cs
mov es, ax
mov ah, 049h
int 021h
pop es
jmp b20
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ;
; installation entry point, must be at file end ;
; initialize and stay resident ;
; ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
excom:
; initialize constant parts of each pcb
mov ax, 040h
mov es, ax ; bios data segment
sub di, di ; offset to com1 base address
mov bx, 07Ch ; offset to com1 timeout
mov ch, 032h ; offset to com2 interrupt vector
mov cl, not C1MSK ; com1 interrupt mask
lea si, pcb ; com1 pcb address
call c00 ; init com1
inc di
inc di ; offset to com2 base address
inc bx ; offset to com2 timeout
mov ch, 02Eh ; offset to com2 interrupt vector
mov cl, not C2MSK ; com2 interrupt mask
add si, size pcbs ; com2 pcb address
call c00 ; init com2
jmp short c10
; subroutine to initialize parts of a pcb
; only called from just above
c00:
mov dx, es:[di] ; port base from bios
mov [si].pbase, dx
mov al, es:[bx]
mov [si].timeout, al ; copy timeout from bios
mov [si].vector, ch ; interrupt vector
mov [si].mask, cl ; save interrupt mask
sub al, al
mov [si].mscopts, al
mov [si].inopts, al
mov [si].outopts, not (NOCTS or NODSR)
ret
c10:
; save old status of interrupt controller and uarts
in al, 021h
mov oldmsk, al
lea si, pcb ; com1 pcb
call c20 ; save old com1 uart status
lea si, pcb + size pcbs ; com2 pcb
call c20 ; save old com1 uart status
jmp short c40
; subroutine to save uart status
; only called from just above
c20:
mov dx, [si].pbase
or dx, dx
jz c30 ; no port
add dx, 3
in al, dx
and al, 07Fh
mov [si].oldlcr, al ; line control register
mov al, 080h
out dx, al ; set access to baud divisors
sub dx, 3
in al, dx
mov [si].olddll, al ; baud divisor low
inc dx
in al, dx
mov [si].olddlm, al ; baud divisor high
add dx, 2
mov al, [si].oldlcr
out dx, al ; restore register access
inc dx
in al, dx
mov [si].oldmcr, al ; modem control register
sub dx, 3
in al, dx
mov [si].oldier, al ; interrupt enable register
c30:
ret
c40:
; release environment memory, not used
mov bx, 02Ch
mov ax, [bx]
mov es, ax ; address of env
mov ah, 049h
int 021h ; free memory
; save old int 0Bh vector, install new handler
mov ah, 035h
mov al, 0Bh
int 021h
mov word ptr old0B, bx
mov ax, es
mov word ptr old0B + 2, ax
; save old int 0Ch vector, install new handler
mov ah, 035h
mov al, 0Ch
int 021h
mov word ptr old0C, bx
mov ax, es
mov word ptr old0C + 2, ax
; save old int 14h vector, install new handler
mov ah, 035h
mov al, 014h
int 021h
mov word ptr old14, bx
mov ax, es
mov word ptr old14 + 2, ax
lea dx, int14
mov ah, 025h
mov al, 014h
int 021h
; exit and keep everything above the entry point 'excom'
lea dx, excom
mov cl, 4
shr dx, cl
inc dx
mov ah, 031h
mov al, 0
int 021h ; terminate-and-stay-resident
_text ends
end start
endm