home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Hacker Chronicles 2
/
HACKER2.BIN
/
884.PRMBIO.98T
/
COMBIOS.ASM
< prev
next >
Wrap
Assembly Source File
|
1987-01-04
|
30KB
|
889 lines
PAGE 60,132
TITLE COMBIOS - Serial interface for COM1 & COM2
;--------------------------------------------------------------------------;
; ;
;Interupt driven RS232 serial port routines. ;
; these routines replace the BIOS rs232 calls with a version that has ;
; interupt driven character receive, and can thus operate at considerably ;
; higher speeds than the standard bios calls (int 14h). ;
; ;
;--------------------------------------------------------------------------;
;--------------------------------------------------------------------------;
; Set up the works ;
;--------------------------------------------------------------------------;
everything SEGMENT PUBLIC
ASSUME CS: everything ; These assumptions are for the
ASSUME DS: everything ; assembler's benefit. The code
ASSUME ES: nothing ; jerks things around as it pleases
ASSUME SS: nothing ;
;--------------------------------------------------------------------------;
; Program start and buffer declares ;
;--------------------------------------------------------------------------;
ORG 100H
foo: JMP start ; Entry point
ORG 0 ; Back up so we can use this all as
; buffer space
;--------------------------------------------------------------------------;
; Area where things are declared ;
;--------------------------------------------------------------------------;
buffer_size EQU 1024 ; Bytes in each buffer
buffer_full EQU 950 ; Max count before handshaking
dummy: ; Dummy labels to provide a structure
buffer DB buffer_size dup(?) ; Buffer (1 per com card)
buffer_e: ; End of buffer
comnumber DB ? ; Comm number - 1
flags DB ? ; Flag byte
last_rs DB ? ; Last receive status
hiv DB ? ; Hardware interrupt vector
int_mask DB ? ; Mask for 8259
baseaddr DW ? ; Base port address
count DW ? ; # of chars in buffer
buffer_in DW ? ; Buffer in pointer
buffer_out DW ? ; Buffer out pointer
dummy_end:
ORG dummy ; Back up over this
;--------------------------------------------------------------------------;
; These are the things to work with ;
;--------------------------------------------------------------------------;
DB buffer_size dup(?) ; Buffer (1 per com card)
DB 0 ; Com number
DB 0 ; Flags
DB 0 ; Last receive status
DB 0CH ; Hardware interrupt vector
DB 0EFH ; Mask
DW 03F8H ; Base port address
DW 0 ; # of chars in buffer
DW ? ; Buffer in pointer
DW ? ; Buffer out pointer
DB buffer_size dup(?) ; Buffer (1 per com card)
DB 1 ; Com number
DB 0 ; Flags
DB 0 ; Last receive status
DB 0BH ; Hardware interrupt vector
DB 0F7H ; Mask
DW 02F8H ; Base port address
DW 0 ; # of chars in buffer
DW ? ; Buffer in pointer
DW ? ; Buffer out pointer
comend: ; Marker for last com port
;--------------------------------------------------------------------------;
; Hardware constants ;
;--------------------------------------------------------------------------;
pic_cmd_port EQU 020H ; 8259 interrupt controller command address
pic_mask_port EQU 021H ; 8259 interrupt controller mask address
;--------------------------------------------------------------------------;
; 8250 hardware constants ;
;--------------------------------------------------------------------------;
rbr_8250 EQU 00H ; xF8 Receive Buffer Register
thr_8250 EQU 00H ; xF8 Transmitter Holding Register
ier_8250 EQU 01H ; xF9 Interrupt Enable Register
iir_8250 EQU 02H ; xFA Interrupt Identification Register
lcr_8250 EQU 03H ; xFB Line Control Register
mcr_8250 EQU 04H ; xFC Modem Control Register
lsr_8250 EQU 05H ; xFD Line Status Register
msr_8250 EQU 06H ; xFE Modem Status Register
dll_8250 EQU 00H ; xF8 Divisor Latch Least Significant
dlm_8250 EQU 01H ; xF9 Divisor Latch Most Significant
;--------------------------------------------------------------------------;
; Communications constants ;
;--------------------------------------------------------------------------;
CR EQU 0DH ; Carriage Return
LF EQU 0AH ; Line Feed
;--------------------------------------------------------------------------;
; Static variables ;
;--------------------------------------------------------------------------;
old_bios_vector DW ? ; Save previous interrupt vector
DW ? ;
int_8250 LABEL WORD
DW OFFSET serint_1_8250
DW OFFSET serint_2_8250
divisor_table LABEL WORD ;
DW 1047 ; 110
DW 768 ; 150
DW 384 ; 300
DW 192 ; 600
DW 96 ; 1200
DW 48 ; 2400
DW 24 ; 4800
DW 12 ; 9600
;--------------------------------------------------------------------------;
; Our BIOS handler ;
;--------------------------------------------------------------------------;
rsint:
ASSUME DS: NOTHING ; Don't use DS
ASSUME ES: NOTHING ; Don't use ES
ASSUME SS: NOTHING ; Don't use SS
;--------------------------------------------------------------------------;
; Not disabled please ;
;--------------------------------------------------------------------------;
STI ;
;--------------------------------------------------------------------------;
; See if this is our vector? ;
;--------------------------------------------------------------------------;
PUSH BP ; Save BP over our loop
XOR BP,BP ;
com_srch_loop: ;
CMP DL,CS:comnumber[BP] ; Is this our port?
JE rsint_ours ; Yes...
ADD BP,OFFSET dummy_end ; Increment to next com port block
CMP BP,OFFSET comend ; Anything left to be checked?
JL com_srch_loop ; Yep....
;--------------------------------------------------------------------------;
; Not us.. Pop things out and call regular handler ;
;--------------------------------------------------------------------------;
not_us:
POP BP ; Pop BP
JMP CS:DWORD PTR old_bios_vector
;--------------------------------------------------------------------------;
; BP now contains the pointer to the com block... See what ;
; the user has requested and we may or may not do it....... ;
;--------------------------------------------------------------------------;
rsint_ours:
PUSH DX ; We need the DX register
PUSH CX ; We need the CX register
PUSH BX ; We need the BX register
MOV CX,baseaddr[BP] ; Get base address for chip
OR AH,AH ; Initialize
JE rsint_init ; Yes...
DEC AH ; 1 = Send character
JZ rsint_send
DEC AH ; 2 = Receive character
JZ rsint_recv_jmp
DEC AH ; 3 = Status request
JZ rsint_status_jmp
DEC AH ; 4 = Inquiry
JZ rsint_inquiry_jmp
DEC AH ; 5 = Drop RTS
JZ rsint_RTS_off_jmp
DEC AH ; 6 = Raise RTS
JZ rsint_RTS_on_jmp
DEC AH ; 7 = Send break
JZ rsint_break_jmp
DEC AH ; 8 = Non-destructive read
JNZ rsint_exit ; Nope..
JMP rsint_nd_recv ; Yep..
rsint_recv_jmp:
JMP rsint_recv
rsint_status_jmp:
JMP rsint_status
rsint_inquiry_jmp:
JMP rsint_inquiry
rsint_RTS_off_jmp:
JMP rsint_RTS_off
rsint_RTS_on_jmp:
JMP rsint_RTS_on
rsint_break_jmp:
JMP rsint_break
;--------------------------------------------------------------------------;
; Interrupt exit ;
;--------------------------------------------------------------------------;
rsint_exit:
POP BX ; Restore registers
POP CX ;
POP DX ;
POP BP ;
IRET ; and leave
;--------------------------------------------------------------------------;
; Init.. ;
;--------------------------------------------------------------------------;
rsint_init:
MOV AH,AL ; Save the parms for later
MOV BL,AH ; Look up the baud rate
MOV CL,4 ; parameter
ROL BL,CL ;
AND BX,0EH ;
MOV BX,divisor_table[BX] ;
MOV CX,baseaddr[BP] ; Get base address for chip
MOV DX,CX ; Address of LCR
ADD DX,lcr_8250 ;
MOV AL,10000000B ; Enable access to divisor
OUT DX,AL ;
MOV DX,CX ; Address of lower divisior half
ADD DX,dll_8250 ;
MOV AL,BL ; Put lower half
OUT DX,AL ;
MOV DX,CX ; Address of upper divisior half
ADD DX,dlm_8250 ;
MOV AL,BH ; Put upper half
OUT DX,AL ;
MOV AL,AH ; Get parms back
AND AL,01FH ; Throw away baud rate
MOV DX,CX ; Address of LCR
ADD DX,lcr_8250 ;
OUT DX,AL ; Output the parms
JMP rsint_status ; Now just status please
;--------------------------------------------------------------------------;
; Send a character ;
;--------------------------------------------------------------------------;
rsint_send:
MOV AH,AL ; Save the character to send
rsint_send_loop: ; Loop here until we can send
MOV DX,CX ; Compute port address for the MSR
ADD DX,msr_8250 ; and then
IN AL,DX ; get it into AX
AND AL,00110000B ; CTS & DSR?
CMP AL,00110000B ;
JNE rsint_send_loop
MOV DX,CX ; Compute port address for the LSR
ADD DX,lsr_8250 ; and then
IN AL,DX ; get itinto AX
TEST AL,00100000B ; THR empty?
JZ rsint_send_loop ; No.. Loop back
MOV AL,AH ; Get ready to out character
MOV DX,CX ; Compute port address for the THR
; ADD DX,thr_8250 ; and then
OUT DX,AL ; out the character
JMP rsint_status ; Do a status
;--------------------------------------------------------------------------;
; Receive a character ;
;--------------------------------------------------------------------------;
rsint_recv:
MOV BX,buffer_out[BP] ; Get buffer output pointer
rsint_recv_loop:
CMP count[BP],0 ; See in anything in buffer
JE rsint_recv_loop ; Wait for it
rsint_get_char:
MOV AL,CS:[BX] ; Get character from buffer
PUSH AX ; Save char
INC BX ; Bump pointer
DEC count[BP] ; Dec char count
MOV DX,OFFSET buffer_e ; Compute end of buffer
ADD DX,BP ;
CMP BX,DX ; Have we wrapped the buffer?
JL test_handshake ; No.. All done
MOV BX,OFFSET buffer ; Yes.. Reset pointer
ADD BX,BP ;
test_handshake:
cmp count[BP],80H
jnb test_full
mov ah,flags[BP]
test ah,1
jz test_full
roll_hands:
mov DX,CX
add DX,mcr_8250
in AL,DX
or AL,00001011B ; Raise DTR, RTS & OUT2
out DX,AL
and AH,0FEH
mov flags[BP],AH
test_full:
POP AX ; Restore Char
MOV buffer_out[BP],BX ; Save pointer
MOV AH,last_rs[BP] ; Get last LSR from receive
AND AH,11111110B ; Remove data ready bit
CMP count[BP],0 ; Anything left in buffer?
JE short_exit ; Nope so leave
OR AH,00000001B ; Turn on data ready
short_exit:
JMP rsint_exit ; and go leave
;--------------------------------------------------------------------------;
; Non-destructive Receive character ;
; (get next character, no pointer updates, no check for character ready) ;
;--------------------------------------------------------------------------;
rsint_nd_recv:
MOV BX,buffer_out[BP] ; Get buffer output pointer
MOV AL,CS:[BX] ; Get character from buffer
JMP rsint_exit ; and go leave
;--------------------------------------------------------------------------;
; Status.. ;
;--------------------------------------------------------------------------;
rsint_status:
MOV DX,CX ; Compute port address for the LSR
ADD DX,lsr_8250 ; and then
IN AL,DX ; get it
AND AL,11111110B ; Remove data ready bit
CMP count[BP],0 ; Anything left in buffer?
JE rsint_status_nodr ; Nope so leave
OR AL,00000001B ; Turn on data ready
rsint_status_nodr:
MOV AH,AL ; Save LSR
MOV DX,CX ; Compute port address for the MSR
ADD DX,msr_8250 ; and then
IN AL,DX ; get it
JMP rsint_exit ; All done
;--------------------------------------------------------------------------;
; Inquiry ;
; (Return AA55H in AX - Just an identification scheme to tell ;
; if this silly driver has been loaded) ;
;--------------------------------------------------------------------------;
rsint_inquiry:
MOV AX,0AA55H
JMP rsint_exit ; and go leave
;--------------------------------------------------------------------------;
; RTS off ;
; (Drop RTS) ;
;--------------------------------------------------------------------------;
rsint_RTS_off:
MOV DX,CX
add DX,mcr_8250
in AL,DX
and AL,0FCH
out DX,AL
JMP rsint_exit ; and go leave
;--------------------------------------------------------------------------;
; RTS on ;
; (Raise RTS) ;
;--------------------------------------------------------------------------;
rsint_RTS_on:
MOV DX,CX
add DX,mcr_8250
in AL,DX
or AL,00001011B ; Raise DTR, RTS & OUT2
out DX,AL
;make sure the interrupt is still turned on
CLI ; Disable interrupts
MOV DX,CX ; Compute port address for the IER
ADD DX,ier_8250 ;
MOV AL,00000001B ; Enable data interrupt only
OUT DX,AL ;
;while we are at it, lets make sure the 8259 didnt get turned off!
IN AL,pic_mask_port ; Set up 8259 interupt controller
AND AL,int_mask[BP] ; Enable the interrupts for this device
OUT pic_mask_port,AL ;
STI
JMP rsint_exit ; and go leave
;--------------------------------------------------------------------------;
; Send break ;
; ;
;--------------------------------------------------------------------------;
rsint_break:
MOV DX,CX
add DX,lcr_8250
in AL,DX
mov bl,al ;save old LCR
or al,40H ; set break condition
out dx,al
mov cx,0 ; wait a while
bkwait: loop bkwait
mov al,bl ; restore LCR
out dx,al
JMP rsint_exit ; and exit
;--------------------------------------------------------------------------;
; 8250 interrupt handler ;
;--------------------------------------------------------------------------;
serint_1_8250:
PUSH BP
MOV BP,int_8250+0
JMP SHORT serint_8250
serint_2_8250:
PUSH BP
MOV BP,int_8250+2
serint_8250:
PUSH DX
PUSH AX
PUSH CX ; Save some registers
PUSH DI ;
MOV CX,baseaddr[BP] ; Get base address for chip
MOV DX,CX ; Get the IIR
ADD DX,iir_8250 ;
IN AL,DX ;
TEST AL,00000001B ; Interrupt pending?
JZ service
JMP serint_8250_exit ; No leave...
service:
MOV DX,CX ; Get the LSR
ADD DX,lsr_8250 ;
IN AL,DX ;
MOV last_rs[BP],AL ; And tuck it away
MOV DX,CX ; Get the RBR
; ADD DX,rbr_8250 ;
IN AL,DX ;
MOV DI,buffer_in[BP] ; Get the buffer pointer
MOV CS:[DI],AL ; Save the character
INC DI ; Bump pointer and handle wrap
inc count[BP] ; inc char count
MOV AX,OFFSET buffer_e ;
ADD AX,BP ;
CMP DI,AX ;
JL serint_8250_nowrap ;
MOV DI,OFFSET buffer ;
ADD DI,BP ;
serint_8250_nowrap:
cmp count[BP],buffer_full
jb hand_done
mov DX,CX
add DX,mcr_8250
in al,DX
and AL,11111100B ; Drop DTR & RTS
out DX,AL
mov ah,flags[BP]
or AH,1
mov flags[BP],AH
hand_done:
CMP DI,buffer_out[BP] ; Overflow of buffer?
JNE serint_8250_noover ;
OR last_rs[BP],02H ; Overrun indicate
JMP SHORT serint_8250_exit ; Don't save the updated pointer
serint_8250_noover:
MOV buffer_in[BP],DI ; Save the updated pointer
serint_8250_exit:
MOV AL,020H ; Tell 8259 we are done
OUT pic_cmd_port,AL ;
POP DI ; Restore registers
POP CX ;
POP AX ;
POP DX ;
POP BP ;
IRET ; Exit
program_end:
;--------------------------------------------------------------------------;
; Main line to initialize.
;--------------------------------------------------------------------------;
ASSUME DS: everything
;--------------------------------------------------------------------------;
; Constants only needed by initialization ;
;--------------------------------------------------------------------------;
inter_count DW 0
P3F8 DB 0 ;1 IF CARD AT 3F8
P2F8 DB 0 ;1 IF CARD AT 2F8
PCNT DB 0 ;Total # ports found
HELLO DB CR,LF
DB 'COMBIOS Driver v1.6',CR,LF
DB 'Dec 31, 1986 '
DB CR,LF,'$'
BYE DB CR,LF
DB 'Driver Installed OK!'
DB CR,LF,'$'
LOADED DB CR,LF
DB 'COMBIOS already loaded!'
DB CR,LF,'$'
COM1F DB CR,LF
DB 'COM1: board found at 03F8',CR,LF
DB '$'
COM2F DB CR,LF
DB 'COM2: board found at 02F8',CR,LF
DB '$'
NOCOM DB CR,LF
DB 'Only found 1 serial board.'
DB CR,LF
DB 'Driver NOT installed!',CR,LF
DB '$'
; INITIALIZE ROUTINE
start: MOV AX,CS ; Point DS in the right place
MOV DS,AX
CALL SINON ;PRINT SIGN ON
MOV DX,00 ;Check to see if support already loaded
MOV AH,04
INT 14H
CMP AX,0AA55H
JZ EXIT ;Must be loaded
MOV DX,01 ;Check to see if support already loaded
MOV AH,04
INT 14H
CMP AX,0AA55H
JZ EXIT ;Must be loaded
CALL CNFIG ;CHECK CONFIGURATION
OR AX,AX
JNZ EREXIT ;JMP IF ERRORS
CALL init_vectors ;SET INTERRUPT VECTORS
CALL SINOFF ;PRINT SIGN OFF
MOV AL,0 ;set exit code
MOV DX,OFFSET program_end
MOV CL,4
SHR DX,CL
INC DX
MOV AH,31H
INT 21H ; Terminate but stay resident
EREXIT: MOV AL,1 ;set exit code = Error
MOV AH,4CH
INT 21H ; Terminate and return
EXIT: LEA DX,LOADED
MOV AH,9
INT 21H
MOV AL,0 ;set exit code = Ok
MOV AH,4CH
INT 21H ; Terminate and return
;---------
CNFIG: CALL FNDPT ;Find Ports
CMP P3F8,1
JNE CNF1
INC PCNT
LEA DX,COM1F ;Say COM1 Found
MOV AH,9
INT 21H
CNF1: CMP P2F8,1
JNE CNF2
LEA DX,COM2F ;Say COM2 Found
INC PCNT
MOV AH,9
INT 21H
CNF2: CMP PCNT,2
JE CNF3
LEA DX,NOCOM ;Say OOPS
MOV AH,9
INT 21H
CNFERR: MOV AX,0FFFFH ;Set Error
RET
CNF3: SUB AX,AX
RET
;------------
FNDPT: MOV DX,03F8H
CALL CKPORT
CMP BP,0
JZ PT2
INC P3F8
PT2: MOV DX,02F8H
CALL CKPORT
CMP BP,0
JZ PT3
INC P2F8
PT3: RET
;----------------------
;Check for valid Port
;Enter with DX containing Port address
;Exit with BP=Port address if port found BP=0 if not found
CKPORT: MOV BP,DX ;For return
MOV CL,DL ;Keep DL Handy
ADD DX,3
IN AL,DX ;Save old LCR in CH
MOV CH,AL
MOV AL,80H ;Address Divisor Latch
OUT DX,AL
CALL WAIT
MOV DL,CL
IN AL,DX
MOV AH,AL ;Save old Divisor
CALL WAIT
INC DX
IN AL,DX ;Save old Divisor
MOV BX,AX
CALL WAIT
MOV DL,CL
MOV AL,55H ;OUTPUT 55
OUT DX,AL
CALL WAIT
INC DX
MOV AL,0AAH
OUT DX,AL ;OUTPUT AA
CALL WAIT
MOV DL,CL
IN AL,DX ;Read New Divisor
MOV AH,AL
CALL WAIT
INC DX
IN AL,DX
CMP AX,55AAH ;DO THEY MATCH?
JZ Match
XOR BP,BP ;Zero - no match
Match: MOV DL,CL
MOV AL,BH ;Restore old Divisor
OUT DX,AL
CALL WAIT
INC DX
MOV AL,BL
OUT DX,AL
CALL WAIT
INC DX
INC DX ;and old LCR
MOV AL,CH
OUT DX,AL
CALL WAIT
WAIT: RET
;---------
;PRINT SIGNON
SINON: LEA DX,HELLO
MOV AH,9
INT 21H
RET
;PRINT SIGNOFF
SINOFF: LEA DX,BYE
MOV AH,9
INT 21H
RET
;--------------------------------------------------------------------------;
; Now snatch the BIOS comm vector (Int 14) ;
;--------------------------------------------------------------------------;
init_vectors:
MOV AL,14H
MOV AH,35H
INT 21H
MOV old_bios_vector,BX ; save old vector
MOV old_bios_vector+2,ES ;
MOV DX,OFFSET rsint ; Replace with our vector
MOV AL,14H
MOV AH,25H
INT 21H
;--------------------------------------------------------------------------;
; Loop initializing each COM port ;
;--------------------------------------------------------------------------;
XOR AX,AX ; Get a zero
MOV BP,AX ; Place to start the comm loop is 0
init_loop:
;--------------------------------------------------------------------------;
; Set the hardware interrupt vector ;
;--------------------------------------------------------------------------;
MOV DI,inter_count ;Get next ptr
MOV DX,int_8250[DI] ; Get our address
MOV AL,hiv[BP]
MOV AH,25H
INT 21H
MOV int_8250[DI],BP ; save com block address
INC DI
INC DI
MOV inter_count,DI ;Save next ptr
;--------------------------------------------------------------------------;
; Initialize the buffer ring pointers ;
;--------------------------------------------------------------------------;
MOV AX,OFFSET buffer ; Get start of buffer address
ADD AX,BP ; include our offset
MOV buffer_in[BP],AX ; Put in the buffer pointers
MOV buffer_out[BP],AX ; Put in the buffer pointers
;--------------------------------------------------------------------------;
; Set the interrupt registers in the UART and clean things up ;
;--------------------------------------------------------------------------;
CLI ; Disable interrupts
IN AL,pic_mask_port ; Set up 8259 interupt controller
AND AL,int_mask[BP] ; Enable the interrupts for this device
OUT pic_mask_port,AL ;
MOV CX,baseaddr[BP] ; Get base address for chip
MOV DX,CX ; Compute port address for the IER
ADD DX,ier_8250 ;
MOV AL,00000001B ; Enable data interrupt only
OUT DX,AL ;
MOV DX,CX ; Compute port address for the MCR
ADD DX,mcr_8250 ;
MOV AL,00001000B ; Raise OUT2. OUT2 turns
; on interrupts
OUT DX,AL ;
MOV DX,CX ; Compute port address for the MCR
ADD DX,rbr_8250 ;
IN AL,DX ; Read the input buffer and throw it away
;--------------------------------------------------------------------------;
; Loop thru the next comm port ;
;--------------------------------------------------------------------------;
ADD BP,OFFSET dummy_end
CMP BP,OFFSET comend ;done yet?
JGE com_done
JMP init_loop
com_done:
;--------------------------------------------------------------------------;
; Enable interrupts ;
;--------------------------------------------------------------------------;
STI ; Enable CPU to receive interupts
RET
everything ENDS
END foo