home *** CD-ROM | disk | FTP | other *** search
- 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