stdlib segment para public 'slcode' assume cs:stdlib ; ; ; Routines to handle COM1 data transmission. ; ; COM1 refers to HARDWARE COM1 port which is located beginning at port ; address 3f8h and causes INT 0Ch. ; ; ; Released to the public domain. ; Created by Randall Hyde. ; Date: 8/11/90 ; ; ; Useful equates: ; BIOSvars = 40h Com1Adrs = 0 Com2Adrs = 2 ; BufSize = 256 ;# of bytes in buffer. ; ; ; Serial port equates: ; Com1Port = 3F8h Com1IER = 3F9h Com1IIR = 3FAh Com1LCR = 3FBh Com1MCR = 3FCh Com1LSR = 3FDh Com1MSR = 3FEh ; ; ; Register assignments: ; ; Interupt enable register (IER): ; ; If one: ; bit 0- Enables received data available interrupt. ; bit 1- Enables transmitter holding register empty interrupt. ; bit 2- Enables receiver line status interrupt. ; bit 3- Enables the modem status interrupt. ; bits 4-7- Always set to zero. ; ; Interrupt ID Register (IIR): ; ; bit 0- No interrupt is pending (interrupt pending if zero). ; bits 1,2- Binary value denoting source of interrupt: ; 00-Modem status ; 01-Transmitter Hold Register Empty ; 10-Received Data Available ; 11-Receiver line status ; bits 3-7 Always zero. ; ; ; Line Control Register (LCR): ; ; bits 0,1- Word length (00=5, 01=6, 10=7, 11=8 bits). ; bit 2- Stop bits (0=1, 1=2 stop bits [1-1/2 if 5 data bits]). ; bit 3- Parity enabled if one. ; bit 4- 0 for odd parity, 1 for even parity (assuming bit 3 = 1). ; bit 5- 1 for stuck parity. ; bit 6- 1=force break. ; bit 7- 1=Divisor latch access bit. 0=rcv/xmit access bit. ; ; Modem Control Register (MCR): ; ; bit 0- Data Terminal Ready (DTR) ; bit 1- Request to send (RTS) ; bit 2- OUT 1 ; bit 3- OUT 2 ; bit 4- Loop back control. ; bits 5-7- Always zero. ; ; Line Status Register (LSR): ; ; bit 0- Data Ready ; bit 1- Overrun error ; bit 2- Parity error ; bit 3- Framing error ; bit 4- Break Interrupt ; bit 5- Transmitter holding register is empty. ; bit 6- Transmit shift register is empty. ; bit 7- Always zero. ; ; Modem Status Register (MSR): ; ; bit 0- Delta CTS ; bit 1- Delta DSR ; bit 2- Trailing edge ring indicator ; bit 3- Delta carrier detect ; bit 4- Clear to send ; bit 5- Data Set Ready ; bit 6- Ring indicator ; bit 7- Data carrier detect ; ; ; sl_Com1Baud: Set the COM1 port baud rate ; AX = baud rate (110, 150, 300, 600, 1200, 2400, 4800, 9600, 19200) ; public sl_Com1Baud sl_Com1Baud proc far push ax push dx cmp ax, 9600 ja Set19200 je Set9600 cmp ax, 2400 ja Set4800 je Set2400 cmp ax, 600 ja Set1200 je Set600 cmp ax, 150 ja Set300 je Set150 mov ax, 1047 ;Default to 110 baud jmp short SetPort ; Set150: mov ax, 768 jmp short SetPort ; Set300: mov ax, 384 jmp short SetPort ; Set600: mov ax, 192 jmp short SetPort ; Set1200: mov ax, 96 jmp short SetPort ; Set2400: mov ax, 48 jmp short SetPort ; Set4800: mov ax, 24 jmp short SetPort ; Set9600: mov ax, 12 jmp short SetPort ; Set19200: mov ax, 6 SetPort: mov dx, ax ;Save baud value. call far ptr sl_GetLCRCom1 push ax ;Save old divisor bit value. or al, 80h ;Set divisor select bit. call far ptr sl_SetLCRCom1 mov ax, dx ;Get baud rate divisor value. mov dx, Com1Port out dx, al inc dx mov al, ah out dx, al mov dx, Com1LCR pop ax call far ptr sl_SetLCRCom1 ;Restore divisor bit value. pop dx pop ax ret sl_Com1Baud endp ; ; ; sl_Com1Stop: ; Set the number of stop bits. ; ; AL=1 for one stop bit, 2 for two stop bits. ; public sl_com1Stop sl_com1Stop proc far push ax push dx dec ax shl ax, 1 ;position into bit #2 shl ax, 1 mov ah, al mov dx, Com1LCR in al, dx and al, 11111011b ;Mask out Stop Bits bit or al, ah ;Mask in new # of stop bits. out dx, al pop dx pop ax ret sl_com1Stop endp ; ; ; sl_com1size: Sets word size on the com1 port. ; AX = 5, 6, 7, or 8 which is the number of bits to set. ; public sl_com1size sl_com1size proc far push ax push dx sub al, 5 cmp al, 3 jbe Okay mov al, 3 ;Default to eight bits. Okay: mov ah, al mov dx, com1LCR in al, dx and al, 11111100b ;Mask out old word size or al, ah ;Mask in new word size out dx, al pop dx pop ax ret sl_com1size endp ; ; ; sl_com1parity: Turns parity on/off, selects even/odd parity, or stuck parity. ; ax contains the following: ; ; bit 0- 1 to enable parity, 0 to disable. ; bit 1- 0 for odd parity, 1 for even (only valid if bit 0 is 1). ; bit 2- Stuck parity bit. If 1 and bit 0 is 1, then the parity bit ; is always set to the inverse of bit 1. ; public sl_com1parity sl_com1parity proc far push ax push dx ; shl ax, 1 shl ax, 1 shl ax, 1 and ax, 00111000b ;Mask out other data. mov ah, al mov dx, com1LCR in al, dx and al, 11000111b or al, ah out dx, al pop dx pop ax ret sl_com1parity endp ; ; ;**************************************************************************** ; ; Polled I/O: ; ; ; sl_ReadCom1- Reads a character from COM1 and returns that character in ; the AL register. Synchronous call, meaning it will not ; return until a character is available. ; public sl_ReadCom1 sl_ReadCom1 proc far push dx call far ptr sl_GetLCRCom1 push ax ;Save divisor latch access bit. and al, 7fh ;Select normal port. call far ptr sl_SetLCRCom1 mov dx, com1LSR WaitForChar: call far ptr sl_GetLSRCom1 test al, 1 ;Data Available? jz WaitForChar mov dx, com1Port in al, dx mov dl, al ;Save character pop ax ;Restore divisor access bit. call far ptr sl_SetLCRCom1 mov al, dl ;Restore output character. pop dx ret sl_ReadCom1 endp ; ; ; ; sl_WriteCom1- Writes the character in AL to the com1 port. ; public sl_WriteCom1 sl_WriteCom1 proc far push dx push ax mov dl, al ;Save character to output call far ptr sl_GetLCRCom1 push ax ;Save divisor latch access bit. and al, 7fh ;Select normal port. call far ptr sl_SetLCRCom1 WaitForXmtr: call far ptr sl_GetLSRCom1 test al, 00100000b ;Xmtr buffer empty? jz WaitForXmtr mov al, dl ;Get output character. mov dx, Com1Port out dx, al pop ax ;Restore divisor access bit. call far ptr sl_SetLCRCom1 pop ax pop dx ret sl_WriteCom1 endp ; ; ; ; sl_TstInpCom1-Returns AL=0 if a character is not available at the com1 port. ; Returns AL=1 if a character is available. ; public sl_TstInpCom1 sl_TstInpCom1 proc far push dx mov dx, com1LSR in al, dx and al, 1 pop dx ret sl_TstInpCom1 endp ; ; ; sl_TstOutCom1-Returns AL=1 when it's okay to send another character to ; the transmitter. Returns zero if the transmitter is full. ; public sl_TstOutCom1 sl_TstOutCom1 proc far push dx mov dx, com1LSR in al, dx test al, 00100000b mov al, 0 jz toc1 inc ax toc1: pop dx ret sl_TstOutCom1 endp ; ; ; sl_GetLSRCom1-Returns the LSR in al: ; ; AL: ; bit 0- Data Ready ; bit 1- Overrun error ; bit 2- Parity error ; bit 3- Framing error ; bit 4- Break interrupt ; bit 5- Xmtr holding register is empty. ; bit 6- Xmtr shift register is empty. ; bit 7- Always zero. ; public sl_GetLSRCom1 sl_GetLSRCom1 proc far push dx mov dx, com1LSR in al, dx pop dx ret sl_GetLSRCom1 endp ; ; ; sl_GetMSRCom1-Returns the modem status register in AL ; ; AL: ; bit 0- Delta clear to send ; bit 1- Delta data set ready ; bit 2- Trailing edge ring indicator ; bit 3- Delta data carrier detect ; bit 4- Clear to send (CTS) ; bit 5- Data set ready (DSR) ; bit 6- Ring indicator (RI) ; bit 7- Data carrier detect (DCD) ; public sl_GetMSRCom1 sl_GetMSRCom1 proc far push dx mov dx, com1MSR in al, dx pop dx ret sl_GetMSRCom1 endp ; ; ; sl_SetMCRCom1-Writes the data in AL to the modem control register. ; sl_GetMCRCom1-Reads the data from the modem control register into AL. ; ; AL: ; bit 0- Data terminal ready (DTR) ; bit 1- Request to send (RTS) ; bit 2- Out 1 ; bit 3- Out 2 ; bit 4- Loop ; bits 5-7 (must be zero) ; public sl_SetMCRCom1 sl_SetMCRCom1 proc far push dx mov dx, com1MCR out dx, al pop dx ret sl_SetMCRCom1 endp ; public sl_GetMCRCom1 sl_GetMCRCom1 proc far push dx mov dx, com1MCR in al, dx pop dx ret sl_GetMCRCom1 endp ; ; ; ; sl_GetLCRCom1- Reads the value from the line control register into AL. ; sl_SetLCRCom1- Writes the value in AL to the line control register. ; ; AL: ; bits 0,1- Word length selection ; bit 2- Number of stop bits ; bit 3- Parity Enable ; bit 4- Even parity select ; bit 5- Stuck parity ; bit 6- Set Break ; bit 7- Divisor latch access bit ; public sl_GetLCRCom1 sl_GetLCRCom1 proc far push dx mov dx, com1LCR in al, dx pop dx ret sl_GetLCRCom1 endp ; public sl_SetLCRCom1 sl_SetLCRCom1 proc far push dx mov dx, com1LCR out dx, al pop dx ret sl_SetLCRCom1 endp ; ; ; sl_GetIIRCom1-Reads the interrupt indentification register and returns its ; value in AL. ; ; AL: ; bit 0- 0 if interrupt pending, 1 if no interrupt. ; bits 1,2- Interrupt ID (highest priority). ; bits 3-7- Always zero. ; ; Interrupt ID ; bit 2 1 Source Reset by ; ---- ----------------------------- ------------------------------ ; 0 0 CTS, DSR, RI Reading the MSR ; 0 1 Xmtr holding register empty Reading IIR or writing to xmtr ; 1 0 Receiver data available Reading rcvr buffer ; 1 1 Overrun, parity, framing, or Reading the LSR. ; break ; ; public sl_GetIIRCom1 sl_GetIIRCom1 proc far push dx mov dx, com1IIR in al, dx pop dx ret sl_GetIIRCom1 endp ; ; ; sl_GetIERCom1-Reads the IER and returns it in AL. ; sl_SetIERCom1-Stores the value in AL into the IER. ; ; AL: ; bit 0- Enable data available interrupt. ; bit 1- Enable xmtr holding register empty interrupt. ; bit 2- Enable receive line status interrupt. ; bit 3- Enable modem status interrupt ; bits 4-7 Always zero. ; public sl_GetIERCom1 sl_GetIERCom1 proc far push dx call sl_GetLCRCom1 push ax ;Save divisor access bit. and al, 7fh ;Address the IER. call sl_SetLCRCom1 mov dx, com1IER in al, dx mov dl, al ;Save for now pop ax call sl_SetLCRCom1 ;Restore divisor latch mov al, dl ;Restore IER value pop dx ret sl_GetIERCom1 endp ; ; public sl_SetIERCom1 sl_SetIERCom1 proc far push dx push ax mov ah, al ;Save value to output call sl_GetLCRCom1 ;Get and save divsor access push ax ;bit. and al, 7fh ;Address the IER. call sl_SetLCRCom1 mov al, ah mov dx, com1IER out dx, al pop ax ;Restore divisor latch bit. call sl_SetLCRCom1 pop ax pop dx ret sl_SetIERCom1 endp ; ; ;**************************************************************************** ; ; Interrupt-driven Serial I/O ; int0Cofs equ es:[30h] int0Cseg equ es:[32h] int0cVec dd ? ;Holds old int 0ch vector. InHead dw InpBuf InTail dw InpBuf InpBuf db Bufsize dup (?) InpBufEnd equ this byte ; OutHead dw OutBuf OutTail dw OutBuf OutBuf db BufSize dup (?) OutBufEnd equ this byte ; i8259a db 0 ;8259a interrupt enable register. TestBuffer db 0 ;1 means we are transmitting out of ; ; the transmit buffer. 0 means the ; ; transmitter register is empty and ; ; we can store data directly to it. ; ; ; sl_InitCom1Int- Initializes the hardware to use interrupt-driven I/O ; for COM1: ; public sl_InitCom1Int sl_InitCom1Int proc far pushf ;Save interrupt disable flag. push es push ax push dx ; ; Turn off the interrupts while we're screwing around here. ; cli ; ; Save old interrupt vector. ; xor ax, ax ;Point at interrupt vectors mov es, ax mov ax, Int0Cofs ;Get ofs int 0ch vector. mov word ptr cs:int0cVec, ax mov ax, Int0Cseg ;Get seg int 0ch vector. mov word ptr cs:int0cVec+2, ax ; ; Point int 0ch vector at our interrupt service routine: ; mov ax, cs mov Int0Cseg, ax mov ax, offset Com1IntISR mov Int0Cofs, ax ; ; Clear any pending interrupts: ; call far ptr sl_GetLSRCom1 ;Clear Receiver line status call far ptr sl_GetMSRCom1 ;Clear CTS/DSR/RI Interrupts call far ptr sl_GetIIRCom1 ;Clear xmtr empty interrupt mov dx, Com1Port in al, dx ;Clear data available intr. ; ; Clear divisor latch access bit. WHILE OPERATING IN INTERRUPT MODE, THE ; DIVISOR ACCESS LATCH BIT MUST ALWAYS BE ZERO. If for some horrible reason ; you need to change the baud rate in the middle of a transmission (or while ; the interrupts are enabled) clear the interrupt flag, do your dirty work, ; clear the divisor latch bit, and finally restore interrupts. ; call far ptr sl_getLCRCom1 and al, 7fh call far ptr sl_SetLCRCom1 ; ; ; Enable the receiver and transmitter interrupts ; mov al, 3 ;Enable rcv/xmit interrupts call far ptr sl_SetIERCom1 ; ; Must set the OUT2 line for interrupts to work. ; Also sets DTR and RTS active. ; mov al, 00001011b call far ptr sl_SetMCRCom1 ; ; Activate the COM1 (int 0ch) bit in the 8259A interrupt controller chip. ; in al, 21h mov cs:i8259a, al ;Save interrupt enable bit. and al, 0efh ;Bit 4=IRQ 4 = INT 0Ch out 21h, al ; pop dx pop ax pop es popf ;Restore interrupt disable flag. ret sl_InitCom1Int endp ; ; ; sl_IntsOffCom1- Disconnects the interrupt system and shuts off interrupt ; activity at the COM1: port. ; ; Warning! This routine assumes that interrupts are currently active ; due to a call to sl_InitCom1Int. If you call this guy ; w/o first calling sl_InitCom1Int you will probably crash ; the system. Furthermore, this routine makes the (rather ; presumptuous) assumption that no one else has patched into ; the INT 0Ch vector since SL_InitCom1Int was called. ; public sl_IntsOffCom1 sl_IntsOffCom1 proc far pushf push es push dx push ax ; cli ;Don't allow interrupts while messing xor ax, ax ; with the interrupt vectors. mov es, ax ;Point at interrupt vectors. ; ; First, turn off the interrupt source: ; call far ptr sl_GetMCRCom1 and al, 3 ;Mask out OUT 2 bit (masks ints) call far ptr sl_SetMCRCom1 ; in al, 21h ;Get 8259a ier and al, 0efh ;Clear IRQ 4 bit. mov ah, cs:i8259a ;Get our saved value and ah, 1000b ;Mask out com1: bit (IRQ 4). or al, ah ;Put bit back in. out 21h, al ; ; Restore the interrupt vector: ; mov ax, word ptr cs:Int0cVec mov Int0Cofs, ax mov ax, word ptr cs:Int0cVec+2 mov Int0Cseg, ax ; pop ax pop dx pop es popf ret sl_IntsOffCom1 endp ; ;---------------------------------------------------------------------------- ; ; Com1IntISR- Interrupt service routine for COM1: ; Com1IntISR proc far push ax push bx push dx TryAnother: mov dx, Com1IIR in al, dx ;Get id test al, 1 ;Any interrupts left? jnz IntRtn test al, 100b jnz ReadCom1 test al, 10b jnz WriteCom1 ; ; Bogus interrupt? ; call sl_GetLSRCom1 ;Clear receiver line status call sl_GetMSRCom1 ;Clear modem status. jmp TryAnother ; IntRtn: mov al, 20h ;Acknowledge interrupt to the out 20h, al ; 8259A interrupt controller. pop dx pop bx pop ax iret ; ; Handle incoming data here: ; (Warning: This is a critical region. Interrupts MUST BE OFF while executing ; this code. By default, interrupts are off in an ISR. DO NOT TURN THEM ON ; if you modify this code). ; ReadCom1: mov dx, Com1Port in al, dx ;Get the input char mov bx, cs:InHead mov cs:[bx], al inc bx cmp bx, offset InpBufEnd jb NoInpWrap mov bx, offset InpBuf NoInpWrap: cmp bx, cs:InTail je TryAnother mov cs:InHead, bx jmp TryAnother ; ; ; Handle outgoing data here (This is also a critical region): ; WriteCom1: mov bx, cs:OutTail cmp bx, cs:OutHead jne OutputChar ; ; If head and tail are equal, simply set the TestBuffer variable to zero ; and quit. If they are not equal, then there is data in the buffer and ; we should output the next character. ; mov cs:TestBuffer, 0 jmp TryAnother ; ; The buffer pointers are not equal, output the next character down here. ; OutputChar: mov al, cs:[bx] mov dx, Com1Port out dx, al inc bx cmp bx, offset OutBufEnd jb NoOutWrap mov bx, offset OutBuf NoOutWrap: mov cs:OutTail, bx jmp TryAnother Com1IntISR endp ; ; ;---------------------------------------------------------------------------- ; ; Routines to read/write characters in serial buffers. ; public sl_InCom1 sl_InCom1 proc far pushf ;Save interrupt flag push bx sti ;Make sure interrupts are on. TstInLoop: mov bx, cs:InTail cmp bx, cs:InHead je TstInLoop mov al, cs:[bx] ;Get next char. cli ;Turn off ints while adjusting inc bx ; buffer pointers. cmp bx, offset InpBufEnd jne NoWrap2 mov bx, offset InpBuf NoWrap2: mov cs:InTail, bx pop bx popf ;Restore interrupt flag. ret sl_InCom1 endp ; ; public sl_OutCom1 sl_OutCom1 proc far pushf cli ;No interrupts now! cmp cs:TestBuffer, 0 ;Write directly to serial chip? jnz BufferItUp call far ptr sl_WriteCom1 ;Output to port mov cs:TestBuffer, 1 ;Must buffer up next char. popf ret ; BufferItUp: push bx mov bx, cs:OutHead mov cs:[bx], al inc bx cmp bx, offset OutBufEnd jne NoWrap3 mov bx, offset OutBuf NoWrap3: cmp bx, cs:OutTail je NoSetTail mov cs:OutTail, bx NoSetTail: pop bx popf ret sl_OutCom1 endp ; stdlib ends end