home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HAM Radio 1
/
HamRadio.cdr
/
misc
/
monax2
/
serialip.asm
< prev
next >
Wrap
Assembly Source File
|
1987-06-03
|
14KB
|
520 lines
page 60,132
title SERIAL C interrupt driven serial I/O routines for IBM PC COM ports
;These routines allow c programs to use serial port 0 in an interrupt
;driven environment. This environment is locally generated and controlled
;
;This program is released to the public domain for non-commercial
;uses. Please do not charge "copying fees" when redistrubiting
;this program. Feedback is desired.
;
;Skip Hansen, WB6YMH 6/22/86
;RCP/M (213) 541-2503
;or via RLI linked systems at WB6KAJ or KD6SQ
;
; 1/84 Greg Lindberg Original routines written for Pronto Computer
;
; 2/86 Skip,WB6YMH Modified for use on IBM PC COM1
;
; 4/26/86 Lee, WB6KAJ Modified transmit byte routine to check
; CTS before sending a byte for use with
; hardware flow control.
;
; 01/30/87 Mike, WA6FXT Modified to conform to Microsoft 'C' 4.0
; Also added DTR control to _serinit, _serldone
;
; 01/31/87 Mike, WA6FXT Modified to allow COM port selection to be
; done dynamic (command option).
;
; 6/1/87 Skip, WB6YMH Corrected bug in serldone. Fixed bug in
; serlbaud caused by fallthru logic after
; port option was added to serlinit.
;
;
;
;macro for output from al to 16 bit port adr
;
outbyte macro name
mov dx,name ;point dx to port adr
out dx,al ;output 8 bit value
endm
;
;macro for byte input to al from 16 bit port
;
inbyte macro name
mov dx,name ;point dx to port adr
in al,dx ;read byte
endm
;
;macro for output from al to 16 bit port base adr with offset 'name'
;
outport macro name
mov dx,name ;get the offset value
call out_byte ;output the byte
endm
;
;macro for byte input to al from 16 bit port base adr with offset 'name'
;
inport macro name
mov ax,port_base ;get the base address
add ax,name ;add the OFFSET
mov dx,ax ;set DX to point at port address
in al,dx ;read byte
endm
false equ 0
true equ not false
i8259_mask equ 21h ;interrupt mask register
i8259_ctrl equ 20h ;interrupt controller control port
rdaien equ 1 ;enable bit for RDA interrupts
linmod equ 03h ;line mode, 8 bits, no parity
mdmmod equ 0bh ;modem mode= DTR and RTS high &
; COM port definitions
COM1 equ 3f8h ;base address of COM1 8250
com_vec1 equ 0ch ;interrupt vector for COM1
int_level1 equ 4 ;interrupt level on 8259
int_mask1 equ 00010000b ;interrupt mask bit in 8259
COM2 equ 2f8h ;base address of COM2 8250
com_vec2 equ 0bh ;interrupt vector for COM2
int_level2 equ 3 ;interrupt level on 8259
int_mask2 equ 00001000b ;interrupt mask bit in 8259
; OFFSETS from base address of COM port
mdmdat equ 0 ;offset to data port
mdmint equ 1 ;interrupt enable register
mdmlin equ 3 ;line control register
mdmmdm equ 4 ;modem control register
mdmsta equ 5 ;line status register
mdmmsr equ 6 ;modem status register
DTR_BIT equ 1 ;DTR control bit in modem control reg
RTS_BIT equ 2 ;RTS control bit
check_cts equ true
_TEXT SEGMENT BYTE PUBLIC 'CODE'
_TEXT ENDS
_DATA SEGMENT WORD PUBLIC 'DATA'
_DATA ENDS
CONST SEGMENT WORD PUBLIC 'CONST'
CONST ENDS
_BBS SEGMENT WORD PUBLIC 'BBS'
_BBS ENDS
DGROUP GROUP CONST, _BBS, _DATA
ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP
_DATA SEGMENT
public trnbuffin,trnbuffout,trn_buff,trn_buff_end
public rcvbuffin,rcvbuffout,rcv_buff,rcv_buff_end
public com_port,port_base
; DATA AREA
trnbuffin dw offset DGROUP:trn_buff ;pointer to input point in transmit buffer
trnbuffout dw offset DGROUP:trn_buff ;pointer to output point in transmit buffer
trn_buff db 160 dup (?) ;transmit buffer
trn_buff_end equ $
rcvbuffin dw offset DGROUP:rcv_buff ;pointer to input point in receive buffer
rcvbuffout dw offset DGROUP:rcv_buff ;pointer to output point in receive buffer
rcv_buff dw 2048 dup (?) ;receive buffer
rcv_buff_end equ $
dw 100 dup (?) ;local interrupt stack
intstk equ $
ssseg dw ? ;storage for interrupted stack seg
spptr dw ? ;storage for interrupted stack pointer
com_port dw ? ;storage for COM port selection
port_base dw ? ;storage for COM port base address
_DATA ENDS
_TEXT SEGMENT
PUBLIC _SERLINIT,_TRNBYTE,_RCVBYTE,_SERLDONE
PUBLIC _TIMERCV,_PURGERCV,_SERLBAUD,_rcvint
PUBLIC _dtr_on,_dtr_off,rts_on,rts_off
dsseg dw $ ;storage for C data segment
;
; name serlbaud -- baud rate initialazation for serial port 0
;
; synopsis serlbaud(,port_num,baud);
; int port_num; port number (IE: 0=COM1)
; int baud; baud rate for serial port
;
; description This routine initializes serial port 0 at the specified
; baud rate.
;
_serlbaud proc near
push bp ;save BP
mov bp,sp ;point to passed parms
mov dx,[bp+4] ;get port number
mov al,[bp+6] ;get requested baud rate
ror al,1 ;put in right place
ror al,1
ror al,1
and al,0e0h ;save only baud rate
or al,3 ;get rest of int 14h command
xor ah,ah ;make it init command
int 14h ;init port 0 for 8 data bits, 1 stop, no parity
mov ax,[bp+4] ;get port number
push ax ;
call _serlinit ;
pop ax ;fix stack
pop bp ;restore callers BP
ret ;all done so fall thru to serlinit()
;since ibm clears interrupt setup with
;int 14 baudrate calls
_serlbaud endp
;
; name serlinit -- interrupt driven serial initialization
;
; synopsis serlinit(port_num);
; int port_num; port number (IE: 0=COM1)
;
; description This routine initializes interrupts for serial port 0.
;
_serlinit proc near
push bp ;save BP
mov bp,sp ;point to passed parms
push ds ;save data segment
mov dsseg,ds ;save C data segment for interrupt routine
mov ax,[bp+4] ;get the port number
mov com_port,ax ;set the number for later use
mov dx,COM1 ;assume COM1
cmp ax,0 ;see if COM1
jz ser_setcom
mov dx,COM2 ;set for COM2
ser_setcom: mov port_base,dx ;set the port base address
mov ax,com_port ;get the port number
mov bx,2500h+com_vec1 ;assume COM1
cmp ax,0 ;COM1?
jz ser_setvec ;yes
mov bx,2500h+com_vec2 ;must be COM2
ser_setvec: mov ax,cs ;set up for init of interrupt vector
mov ds,ax
mov dx,offset cs:_rcvint ;point to isr
mov ax,bx ;move the vector to AX
cli ;no interrupts now
int 21h ;initialize receive interrupt
pop ds ;restore data pointer
mov ax,offset ds:trn_buff ;clear transmit buffer
mov trnbuffin,ax
mov trnbuffout,ax
mov ax,offset ds:rcv_buff ;clear receive buffer
mov rcvbuffin,ax
mov rcvbuffout,ax
inport mdmdat ;clear any garbarge characters
in al,dx ;from COM port
in al,dx ;
mov al,linmod ;clear DLAB bit and set line mode
outport mdmlin ;
mov al,mdmmod ;get mode to use
outport mdmmdm ;to modem control register
mov al,rdaien ;enable interrupts from RDA
outport mdmint ;
mov dx,i8259_mask ;unmask interrupts from serial port 0
in al,dx ;get present mask
push ax ;save it
mov ax,com_port ; check the port
cmp ax,0 ;see if COM1
jnz ser_mask2 ;must be COM2
pop ax ;restore AX
and al,not int_mask1 ;enable interrupts for COM1
jmp short ser_setmask
ser_mask2: pop ax ;restore AX
and al,not int_mask2 ;enable interrupts for COM2
ser_setmask: out dx,al ;restore new mask
call _dtr_on ;allow inputs
sti ;interrupts back on
pop bp ;restore callers BP
ret ;all done so exit
_serlinit endp
;
; name trnbyte -- transmit byte of data over serial port 0
;
; synopsis trnbyte(trndata);
; int trnbyte; ;data byte to send
;
; description This routine sends a byte of data out serial port 0.
;
_trnbyte proc near
push bp ;save BP
mov bp,sp ;point to passed parms
trn_byte_loop: inport mdmsta ;get status byte
and al,20h ;transmit buffer empty ?
jz trn_byte_loop ;loop if not
if check_cts
inport mdmmsr ;get modem status reg.
and al,10h ;look at cts.
jz trn_byte_loop ;loop until ready.
endif
mov al,[bp+4] ;get character to output
outport mdmdat ;send character
pop bp ;restore callers BP
ret ;all done so exit
_trnbyte endp
;
; name rcvbyte -- receive byte from serial port 0
;
; synopsis rcvbyte();
;
; description This routine attempts to return a byte of data from
; serial channel 0. If no data is available it returns
; 0ffffh else it returns the data with error status in
; the high byte
;
_rcvbyte proc near
mov bx,rcvbuffout ;is buffer empty
cmp bx,rcvbuffin
mov ax,0ffffh ;load false return
je short rcvend ;if so go try leave
mov ax,[bx] ;get return byte
inc bx ;update pointer
inc bx
cmp bx,offset ds:rcv_buff_end
jl rcvoutsav
mov bx,offset ds:rcv_buff
rcvoutsav: mov rcvbuffout,bx
rcvend: ret ;all done so exit
_rcvbyte endp
_rcvint proc far
push ds ;save present data seg
mov ds,dsseg ;set to C data segment
mov ssseg,ss ;save interrupted stack
mov spptr,sp
mov ss,dsseg ;point to local stack
mov sp,offset ds:intstk
push ax ;save everything
push bx ;
push dx ;
inport mdmdat ;get received char
mov bl,al ;save char
inport mdmsta ;get status
and al,0eh ;save only framing, and overrun error
mov ah,al ;put status into high byte
mov al,bl ;get char back
mov bx,rcvbuffin ;is buffer full
inc bx
inc bx
cmp bx,offset ds:rcv_buff_end
jl rcvint1
mov bx,offset ds:rcv_buff
rcvint1: cmp bx,rcvbuffout
je short rcvintdn ;if so leave
mov bx,rcvbuffin
mov [bx],ax ;else save char and error status
inc bx ;update pointer
inc bx
cmp bx,offset ds:rcv_buff_end
jl rcvint2
mov bx,offset ds:rcv_buff
rcvint2: mov rcvbuffin,bx
rcvintdn:
mov ax,com_port ;check which COM port
cmp ax,0 ;COM1?
jnz rcv_com2 ;no
MOV AL,60h+int_level1 ;specific EOI, level 1
jmp short rcv_int_clr
rcv_com2: MOV AL,60h+int_level2 ;specific EOI, level 1
rcv_int_clr: outbyte i8259_ctrl ;send specific EOI to 8259
rcv_int_dn: pop dx ;restore everything
pop bx ;
pop ax ;
mov ss,ssseg ;restore interrupted stack
mov sp,spptr
pop ds ;restore data seg
iret ;and leave
_rcvint endp
;
; name timercv -- receive with time out
;
; synopsis timercv(timeout);
; int timeout; ;time out in seconds
;
; description This routine tries to receive a char within a given
; time. If a char becomes available it is returned.
; Else if passed time elapses it returns -1 as an error.
;
_timercv proc near
push bp ;save BP
mov bp,sp ;point to passed parms
sub sp,4 ;make local storage
mov ax,[bp+4] ;get time to wait
mov cx,182 ;find time in ticks
mul cx
mov cx,10
div cx
mov [bp-4],ax ;save it
xor ax,ax ;find out current time in ticks
int 1ah
add [bp-4],dx ;make it ending time
adc cx,0
mov [bp-2],cx ;and save it
timrlp: call _rcvbyte ;is there a character ready
cmp ax,0ffffh
jne timrend ;leave if so
xor ax,ax ;else get new time
int 1ah
sub dx,[bp-4] ;timed out yet
sbb cx,[bp-2]
jl timrlp ;if not go back and check again
mov ax,0ffffh ;else error out
timrend: add sp,4 ;deallocate local storage
pop bp ;restore callers BP
ret ;all done so exit
_timercv endp
;
; name purgercv -- purge receiver
;
; synopsis purgercv();
;
; description This routine keeps requesting receive characters
; until the timercv times out for one second.
;
_purgercv proc near
mov ax,1 ;time out of one second
push ax ;
purgelp: call _timercv ;see if there are any chars
cmp ax,0ffffh ;did it time out
jne purgelp ;try again if not
pop bx ;clean stack
ret ;done leave
_purgercv endp
;
; name serldone -- disables interrupts from serial port 0
;
; synopsis serldone();
;
; description This routine turns off interrupts from serial port 0
; and restores the interrupt vectors to there default valuse
;
_serldone proc near
push bp ;save BP
mov bp,sp ;point to passed parms
call _dtr_off ;disable the port
inbyte i8259_mask ;mask out interrupts from serial port
push ax ;save it
mov ax,com_port ; check the port
cmp ax,0 ;see if COM1
jnz serd_mask2 ;must be COM2
pop ax ;restore AX
or al,int_mask1 ;disable interrupts for COM1
jmp short serd_clrmask
serd_mask2: pop ax ;restore AX
or al,int_mask2 ;disable interrupts for COM2
serd_clrmask: out dx,al ;restore new mask
pop bp ;restore callers BP
ret ;all done so exit
_serldone endp
;
; name dtr_on -- turn on DTR handshaking line on RS-233 port
;
; synopsis dtr_on();
;
_dtr_on proc near
mov al,linmod ;clear DLAB bit and set line mode
outport mdmlin ;
inport mdmmdm ;get current setup
or al,DTR_BIT ;turn DTR on
outport mdmmdm ;to modem control register
ret
_dtr_on endp
;
; name dtr_off -- turn on DTR handshaking line off RS-233 port
;
; synopsis dtr_off();
;
_dtr_off proc near
mov al,linmod ;clear DLAB bit and set line mode
outport mdmlin ;
inport mdmmdm ;get current setup
and al,not DTR_BIT ;DTR off
outport mdmmdm ;to modem control register
ret
_dtr_off endp
;
; name rts_on -- turn on RTS handshaking line on RS-233 port
;
; synopsis rts_on();
;
rts_on: mov al,linmod ;clear DLAB bit and set line mode
outport mdmlin ;
inport mdmmdm ;get current setup
or al,RTS_BIT ;turn RTS on
outport mdmmdm ;to modem control register
ret
;
; name rts_off -- turn on RTS handshaking line off RS-233 port
;
; synopsis rts_off();
;
rts_off: mov al,linmod ;clear DLAB bit and set line mode
outport mdmlin ;
inport mdmmdm ;get current setup
and al,not RTS_BIT ;RTS off
outport mdmmdm ;to modem control register
ret
;
; A routine to output a byte in al, to the port @ port_base, offset
; by the value in dx
;
out_byte: push ax ;save the byte to send
mov ax,port_base ;get the base address
add ax,dx ;add the OFFSET
mov dx,ax ;set DX to point at port address
pop ax ;restore AX
out dx,al ;output 8 bit value
ret
_TEXT ENDS
end