home *** CD-ROM | disk | FTP | other *** search
- page ,132
- ;*****************************************************************************
- ;
- ; Asynchronous Support Routines
- ; Beispielprogramm zum Linken mit C Version 1.0
- ;*****************************************************************************
- _TEXT SEGMENT BYTE PUBLIC 'CODE' ; Default code segment
- _TEXT ENDS
- _DATA SEGMENT WORD PUBLIC 'DATA' ; Default data segment
- _DATA ENDS
- CONST SEGMENT WORD PUBLIC 'CONST' ; Read only constants
- CONST ENDS
- _BSS SEGMENT WORD PUBLIC 'BSS' ; Uninitialized static data
- _BSS ENDS
- STACK SEGMENT PARA STACK 'STACK' ; Stack segment
- STACK ENDS
-
- DGROUP GROUP _DATA,CONST,_BSS,STACK
- _TEXT SEGMENT BYTE PUBLIC 'CODE'
- ASSUME CS:_TEXT,DS:DGROUP
- @AB EQU 4
-
- XMITI EQU 00000h ;set to 0FFFFh for xmit via interrupt
-
- public _asnget ;get char from asynch buffer
- public _asnput ;put char to asynch line
- public _asnstat ;get status of asynch port
- public _asninit ;initialize asynch port & routines
- public _asnclr ;clear asynch receive buffer
- public _asnfls ;flush asynch transmit buffer
- public _asnexit ;remove asynch interrupt routine
- public _asnbaud ;set baud rate for asynch port
- ;*****************************************************************************
- ;
- ; POPF Macro for PC/AT 2/86 hardware bugÜ
- ;
- ;*****************************************************************************
- POPFF MACRO ;this kludge is due to INTEL's idiocyÜ
- local f1,b1
- jmp short f1 ;jump over iret
- b1: iret ;simulate the POPF in a way that works
- f1: push cs ;save cs so that the IRET works correctly
- call b1 ;save ip and execute the IRET, and continue
- ENDM
- page
- ;*****************************************************************************
- ;
- ; Miscellaneous Definitions
- ;
- ;*****************************************************************************
- BSIZE equ 4096 ;circular receive buffer size
- LCRBYTE equ 00010011b ;LCR default, 8 bits+no parity
-
- ;*****************************************************************************
- ;
- ; Device address definitions
- ;
- ;*****************************************************************************
- PICCMD equ 20h ;8259 Priority Interrupt Controller
- PICMSK equ 21h ;8259 Priority Interrupt Controller
-
- ASRBR equ 008h ;8250 Receiver Buffer Register
- ASTHR equ 008h ;8250 Transmitter Holding Register
- ASDLL equ 008h ;8250 Divisor Latch Low
- ASDLH equ 009h ;8250 Divisor Latch High
- ASIER equ 009h ;8250 Interrupt Enable Register
- ASIIR equ 00Ah ;8250 Interrupt Identification Register
- ASLCR equ 00Bh ;8250 Line Control Register
- ASMCR equ 00Ch ;8250 Modem Control Register
- ASLSR equ 00Dh ;8250 Line Status Register
- ASMSR equ 00Eh ;8250 Modem Status Register
-
- ;*****************************************************************************
- ;
- ; data
- ;
- ;*****************************************************************************
- _DATA segment
- clock dd 115200 ;dividend value for asnbaud
- rbhead dw offset dgroup:rbfbeg ;recv buffer empty pointer
- rbtail dw offset dgroup:rbfbeg ;recv buffer fill pointer
- rbfbeg db BSIZE dup (?) ;recv buffer for receive data
- rbfend equ this byte ;recv buffer end address
-
- IF XMITI
- xbhead dw offset dgroup:xbfbeg ;xmit buffer empty pointer
- xbtail dw offset dgroup:xbfbeg ;xmit buffer fill pointer
- xbcnt dw BSIZE ;count of room left in buffer
- xbfbeg db BSIZE dup (?) ;xmit buffer for receive data
- xbfend equ this byte ;xmit buffer end address
- ENDIF
-
- iolist dw 03f0h,02f0h ;COM1, COM2 I/O base addresses
- ioaddr dw ? ;selected I/O adapter base address
-
- ivlist dw 0030h,002Ch ;COM1, COM2 interrupt vector addr list
- ivaddr dw ? ;selected interrupt vector
-
- imlist db 11101111b,11110111b ;COM1, COM2 interrupt mask list
- imbyte db ? ;selected interrupt mask byte
- _DATA ends
- page
- ;*****************************************************************************
- ;
- ; asninit is called to initialize the async support routines
- ;
- ; it MUST be called before any of the other asynch routines
- ; argument should be 0 for COM1 or 1 for COM2
- ;
- ; syntax - asninit(comport)
- ;
- ;*****************************************************************************
- _asninit proc near
- cli ;disable interrupts
- push bp
- mov bp,sp
- mov ax,@AB[bp] ;get argument
- mov bx,ax ;get adapter selection value into bx
- cmp bx,1 ;test for invalid value
- jna asi2 ;ok, continue
- mov bx,0 ;invalid, assume 0
- asi2: mov al,imlist[bx] ;get selected interrupt mask byte
- mov imbyte,al ;save it in imbyte
- add bx,bx ;convert to index into iolist
- mov ax,iolist[bx] ;get i/o adapter base address
- mov ioaddr,ax ;store into ioaddr
- mov ax,ivlist[bx] ;get interrupt vector address
- mov ivaddr,ax ;store into ivaddr
- mov cx,cs ;get current cs into cx
- mov dx,offset asnint ;get offset of interrupt routine
- mov bx,ivaddr ;get interrupt vector address
- mov ax,0
- push ds
- mov ds,ax ;set ds to 0
- mov [bx+2],cx ;set interrupt vector in low mem
- mov [bx],dx
- pop ds
- mov ax,offset dgroup:rbfbeg ;get offset of buffer
- mov rbhead,ax ;initialize buffer empty pointer
- mov rbtail,ax ;initialize buffer fill pointer
- IF XMITI
- mov ax,offset dgroup:xbfbeg ;get offset of buffer
- mov xbhead,ax ;initialize buffer empty pointer
- mov xbtail,ax ;initialize buffer fill pointer
- mov ax,BSIZE ;get buffer size
- mov xbcnt,ax ;initialize buffer room count
- ENDIF
- in al,PICMSK ;read 8259 interrupt mask
- and al,imbyte ;enable the async interrupt
- out PICMSK,al ;output the new mask
- IF XMITI
- mov al,00000011b ;interrupt mask for 8250 (rx & tx)
- ELSE
- mov al,00000001b ;interrupt mask for 8250 (rx only)
- ENDIF
- mov dx,ASIER ;interrupt enable register address
- add dx,ioaddr ;get adapter address
- out dx,al ;enable interrupts on 8250
- mov al,00001011b ;rts,dtr,out2 bits in 8250 mcr
- mov dx,ASMCR ;8250 modem control register address
- add dx,ioaddr ;get adapter base address
- out dx,al ;set modem control register bits
- mov al,LCRBYTE ;set line control register
- mov dx,ASLCR ;lcr address
- add dx,ioaddr ;get adapter address
- out dx,al ;set lcr
- sti ;re-enable interrupts
- pop bp
- ret
- _asninit endp
-
- ;*****************************************************************************
- ;
- ; asnbaud is called to set the 8250 baud latch
- ;
- ; syntax - asynbaud(baudrate)
- ;
- ;*****************************************************************************
- _asnbaud proc near
- push bp
- mov bp,sp
- mov ax,@AB[bp] ;get argument to AX
- mov cx,ax ;baud rate (divisor) into cx
- mov ax,word ptr clock ;low order of dividend into ax
- mov dx,word ptr clock+2 ;hi order of dividend into dx
- div cx ;divide to get 8250 divisor word
- mov cx,ax ;get result into cx temporarily
- mov dx,ASLCR ;8250 Line control register address
- add dx,ioaddr ;add adapter base address
- in al,dx ;get current LCR
- mov bl,al ;...save in BL
- or al,10000000b ;turn Divisor Latch Enable bit on
- out dx,al ;...in LCR
- mov dx,ASDLL ;8250 Divisor Latch Low order address
- add dx,ioaddr ;add adapter base address
- mov al,cl ;get low order byte of divisor word
- out dx,al ;output to the Divisor Latch register
- mov dx,ASDLH ;8250 Divisor Latch Hi order address
- add dx,ioaddr ;add adapter base address
- mov al,ch ;get hi order byte of divisor word
- out dx,al ;output to the Divisor Latch register
- mov dx,ASLCR ;LCR address
- add dx,ioaddr ;add adapter base address
- mov al,bl ;old LCR contents
- out dx,al ;restore old LCR contents
- pop bp
- ret
- _asnbaud endp
-
- ;*****************************************************************************
- ;
- ; asnget is called to get a character from asynch receive buffer
- ;
- ; it returns the character or
- ; -1 if no character is available in the buffer
- ;
- ;*****************************************************************************
- _asnget proc near
- pushf ;save interrupt state
- cli ;disable interrupts
- mov bx,rbhead ;buffer empty pointer into bx
- mov ax,rbtail ;buffer fill pointer into ax
- cmp ax,bx ;see if data in circular buffer
- jz asngx ;no, return -1 to caller
- mov al,[bx] ;get character from buffer into al
- inc bx ;bump buffer empty pointer
- cmp bx,offset dgroup:rbfend ;test for end of buffer
- jnz asng1 ;no, continue
- mov bx,offset dgroup:rbfbeg ;yes, reset buffer empty pointer
- asng1: mov rbhead,bx ;update buffer empty pointer
- mov ah,0 ;make sure ax is a character
- POPFF ;restore interrupt state
- ret
-
- asngx: mov ax,0ffffh ;set ax to -1 to indicate no char
- POPFF ;restore interrupt state
- ret
- _asnget endp
-
- IF XMITI
- ;*****************************************************************************
- ;
- ; asnput is called to output a character to the asynch device
- ;
- ; syntax - asnput(char)
- ;
- ;*****************************************************************************
- _asnput proc near
- push bp
- mov bp,sp
- pushf ;save interrupt state
- asnp00: cli ;temporarily disable interrupts
- cmp xbcnt,BSIZE ;buffer empty?
- jne asnp0a ;no, continue
- mov dx,ASLSR ;8250 line status register address
- add dx,ioaddr ;add adapter base address
- in al,dx ;get line status byte
- test al,01000000b ;test for Transmit empty
- jz asnp0a ;no, store byte in buffer
- mov ax,@AB[bp] ;get argument in ax
- mov dx,ASTHR ;address of 8250 xmit hold register
- add dx,ioaddr ;add adapter base address
- out dx,al ;xmit the character
- POPFF ;restore interrupt state
- pop bp
- ret
- asnp0a: cmp xbcnt,0 ;room left in buffer?
- jne asnp01 ;yes, continue
- sti ;enable interrupts
- nop ;waste a little time
- jmp asnp00 ;and check again
- asnp01: mov bx,xbtail ;buffer fill pointer
- mov ax,@AB[bp] ;get argument in ax
- mov [bx],al ;store character in buffer
- inc bx ;bump pointer
- dec xbcnt ;and decrement room in buffer
- cmp bx,offset dgroup:xbfend ;test for end of buffer
- jb aspn1 ;no, continue
- mov bx,offset dgroup:xbfbeg ;offset of buffer start
- aspn1: mov xbtail,bx ;update buffer fill pointer
- POPFF ;restore interrupt state
- pop bp
- ret
- _asnput endp
- ELSE
- ;*****************************************************************************
- ;
- ; asnput is called to output a character to the asynch device
- ;
- ; syntax - asnput(char)
- ;
- ;*****************************************************************************
- _asnput proc near
- push bp
- mov bp,sp
- mov dx,ASLSR ;8250 line status register address
- add dx,ioaddr ;add adapter base address
- asnp0: in al,dx ;get line status byte
- test al,00100000b ;test for THRE
- jz asnp0 ;no, loop
- mov ax,@AB[bp] ;get argument in ax
- mov dx,ASTHR ;address of 8250 xmit hold register
- add dx,ioaddr ;add adapter base address
- out dx,al ;xmit the character
- pop bp
- ret
- _asnput endp
- ENDIF
-
- ;*****************************************************************************
- ;
- ; asnfls is called to flush the transmit data buffer
- ; (i.e. wait until all characters have been transmitted)
- ;
- ;*****************************************************************************
- _asnfls proc near
- IF XMITI
- asnf0: cmp xbcnt,BSIZE ;test for xmit buffer empty
- jnz asnf0 ;no, keep waiting
- ENDIF
- mov dx,ASLSR ;8250 line status register address
- add dx,ioaddr ;add adapter base address
- asnf1: in al,dx ;get line status byte
- test al,01000000b ;test for TSRE
- jz asnf1 ;no, loop
- ret
- _asnfls endp
-
- ;*****************************************************************************
- ;
- ; asnstat is called to obtain the asynch status bits
- ;
- ; the modem status bits are returned in high order byte
- ; the line status bits (error bits only) are returned in low order byte
- ;
- ;*****************************************************************************
- _asnstat proc near
- mov dx,ASMSR ;8250 modem status register address
- add dx,ioaddr ;add adapter base address
- in al,dx ;get modem status byte
- mov ah,al ;into ah
- dec dx ;address of 8250 line status register
- in al,dx ;get line status byte
- and al,00011110b ;strip everything but error bits
- ret
- _asnstat endp
-
-
- ;*****************************************************************************
- ;
- ; asnclr is called to flush the receive data buffer
- ;
- ;*****************************************************************************
- _asnclr proc near
- cli ;disable interrupts
- mov ax,offset dgroup:rbfbeg ;offset of buffer beginning
- mov rbhead,ax ;reset buffer empty pointer
- mov rbtail,ax ;reset buffer fill pointer
- sti ;re-enable interrupts
- ret
- _asnclr endp
-
- ;*****************************************************************************
- ;
- ; asnexit must be called to terminate the asynch support routines
- ; it disables the asynch interrupts which MUST be done
- ; before the calling program exits to DOS
- ;
- ; Failure to call this routine prior to returning back to DOS
- ; will leave the asynch interrupt vectors pointing into the
- ; DOS user program area, with probable disasterous results
- ; if a character comes in from the asynch line.
- ;
- ;*****************************************************************************
- _asnexit proc near
- cli
- in al,PICMSK ;get 8259 interrupt mask
- mov bl,imbyte ;get interrupt mask byte
- not bl ;invert it
- or al,bl ;turn off async interrupt
- out PICMSK,al ;output updated mask to 8259
- mov al,0 ;turn off all 8250 interrupts
- mov dx,ASIER ;8250 interrupt mask register address
- add dx,ioaddr ;add adapter base address
- out dx,al ;output new mask
- mov dx,ASMCR ;8250 modem control register address
- add dx,ioaddr ;add adapter base address
- out dx,al ;turn off dtr,rts,out2 bits
- sti
- ret
- _asnexit endp
- page
- ;*****************************************************************************
- ;
- ; asnint is the asynch interrupt handler. it is NOT user callable
- ;
- ;*****************************************************************************
- assume cs:_TEXT,ds:DGROUP
- asnint proc far
- push ds ;asynchronous interrupt handler
- push dx ;save all registers
- push bx
- push ax
- mov ax,DGROUP
- mov ds,ax
- mov dx,ASIIR ;address of interrupt id register
- add dx,ioaddr ;add adapter base address
- in al,dx ;get interrupt status
- asi0: cmp al,0000100b ;test for receiver interrupt
- je asir ;yes, process receiver interrupt
- IF XMITI
- cmp al,0000010b ;test for transmitter empty
- je asit ;yes, process xmitter empty
- ENDIF
- jmp asix ;neither, ignore the interrupt
- ;*****************************************************************************
- ;
- ; received data available interrupt
- ;
- ;*****************************************************************************
- asir: mov dx,ASRBR ;address of receiver buffer register
- add dx,ioaddr ;add adapter base address
- in al,dx ;get data byte from 8250 rbr
- mov bx,rbtail ;buffer fill pointer
- mov [bx],al ;store character in buffer
- inc bx ;bump pointer
- cmp bx,offset dgroup:rbfend ;test for end of buffer
- jb asin1 ;no, continue
- mov bx,offset dgroup:rbfbeg ;offset of buffer start
- asin1: mov rbtail,bx ;update buffer fill pointer
- jmp asix ;exit from interrupt
- IF XMITI
- ;*****************************************************************************
- ;
- ; transmit buffer empty interrupt
- ;
- ;*****************************************************************************
- asit: mov bx,xbcnt ;get buffer room count
- cmp bx,BSIZE ;buffer empty?
- je asix ;yes, exit
- mov bx,xbhead ;get buffer head
- mov al,[bx] ;get character from buffer into al
- inc bx ;bump buffer empty pointer
- cmp bx,offset dgroup:xbfend ;test for end of buffer
- jnz asitg1 ;no, continue
- mov bx,offset dgroup:xbfbeg ;yes, reset buffer empty pointer
- asitg1: mov xbhead,bx ;update buffer empty pointer
- mov dx,ASTHR ;address of 8250 xmit hold register
- add dx,ioaddr ;add adapter base address
- out dx,al ;xmit the character
- inc xbcnt ;increment room count
- jmp asix ;exit from interrupt routine
- ENDIF
- ;*****************************************************************************
- ;
- ; exit interrupt routine
- ;
- ;*****************************************************************************
- asix: mov dx,ASIIR ;address of interrupt id register
- add dx,ioaddr ;add adapter base address
- in al,dx ;get interrupt status
- test al,00000001b ;test for another interrupt pending
- jz asi0 ;yes, process another one
- mov al,00100000b ;EOI for 8259
- out PICCMD,al ;reset 8259
- pop ax ;restore saved registers
- pop bx
- pop dx
- pop ds
- iret ;return from interrupt
- asnint endp
- _TEXT ENDS
- end