home *** CD-ROM | disk | FTP | other *** search
- ;;*****************************************************************************
- ;; i8250.inc i8250.inc
- ;;*****************************************************************************
- ;;
- ;; Copyright (C) 1990 Vance Morrison
- ;;
- ;;
- ;; Permission to view, compile, and modify for LOCAL (intra-organization)
- ;; USE ONLY is hereby granted, provided that this copyright and permission
- ;; notice appear on all copies. Any other use by permission only.
- ;;
- ;; Vance Morrison makes no representations about the suitability
- ;; of this software for any purpose. It is provided "as is" without expressed
- ;; or implied warranty. See the copywrite notice file for complete details.
- ;;
- ;;*****************************************************************************
- ;; this software essentially is responsible for setup of the 8250 and
- ;; getting individual chacters off the serial line. The routines here
- ;; where designed so that they will work with the 16550AF and take advantage
- ;; of that chip's read and write FIFOs.
- ;;
- ;; The functions provided by this file are
- ;;
- ;; I8250_DECLARE name, port_prefix, port, io_addr, irq, flow
- ;; I8250_DEFINE_in_BX name, fail
- ;; I8250_WRITE_in_AL_const_BX_BP_ES MACRO name
- ;; I8250_CAN_WRITE_const_BX_CX_BP_SI_DI_ES MACRO name not_ready
- ;;
- ;; the following 'upcall' routines will be called from interupt level
- ;; <port_prefix>_CONSUME_in_AL_const_BX_BP_ES 'port'
- ;; <port_prefix>_WRITE_EMPTY_const_BX_BP_ES 'port'
- ;;
- ;; i8250_&name&_declared ;; one if this interface exists
- ;; i8250_declared ;; one if ANY interface exists
- ;;
- ;;******************************************************************************
-
-
- ;; 'standard' port numbers
- IBM_COM1_PORT = 3F8H
- IBM_COM2_PORT = 2F8H
- IBM_COM3_PORT = 3E8H
- IBM_COM4_PORT = 2E8H
-
- ;; and interupts
- IBM_COM1_IRQ = 4
- IBM_COM2_IRQ = 3
-
- ;; the 10 registers of the 8250
- I8250_REG_THR = 0 ;; trans hold reg (LCR_DIVISOR = 0) (write only)
- I8250_REG_RDR = 0 ;; rec data reg (LCR_DIVISOR = 0) (read only)
- I8250_REG_DIVL = 0 ;; baud rate divisor (LCR_DIVISOR = 1)
- I8250_REG_DIVH = 1 ;; baud rate divisor (LCR_DIVISOR = 1)
- I8250_REG_IER = 1 ;; interupt enable (LCR_DIVISOR = 0)
- I8250_REG_IIR = 2 ;; interupt ident (read only)
- NS16550_REG_FIFO = 2 ;; FIFO control register for the NS16550AF chip
- I8250_REG_LCR = 3 ;; line control
- I8250_REG_MCR = 4 ;; modem control
- I8250_REG_LSR = 5 ;; line status
- I8250_REG_MSR = 6 ;; modem status
- NS16550_REG_SCR = 7 ;; Scratch register.
-
- ;; masks for the IER
- I8250_IER_DIN = 1H ;; interupt on data in
- I8250_IER_DOUT = 2H ;; interupt on data ready for out
- I8250_IER_ERR = 4H ;; interupt on data error
- I8250_IER_MODEM = 8H ;; interupt on modem status change
-
- ;; masks for the IIR
- I8250_IIR_NOINT = 1H ;; the rest of IIR valid only if this bit is 0
- I8250_IIR_TYPE = 6H ;; type of highest priority interupt
- NS16550_IIR_CHECK = 0C0H ;; Both of these bits must be 1 if its a 16550AF
-
- ;; values for type field of the IIR (read only)
- I8250_TYPE_MODEM = 0H ;; notice they are shifted to the TYPE field
- I8250_TYPE_DOUT = 2H
- I8250_TYPE_DIN = 4H
- I8250_TYPE_ERR = 6H
-
- ;; masks for the FIFO reg (write only)
- NS16550_FIFO_ON = 1 ;; Enable the FIFOs
- NS16550_FIFO_FIN = 2 ;; Flush input FIFO
- NS16550_FIFO_FOUT = 4 ;; Flush input FIFO
- NS16550_FIFO_TRIG = 0C0H ;; bytes in input FIFO before interupt
-
- ;; values for the TRIG field of the FIFO reg
- NS16550_TRIG_1 = 000H ;; trigger on 1 byte
- NS16550_TRIG_4 = 040H ;; trigger on 4 bytes
- NS16550_TRIG_8 = 080H ;; trigger on 8 bytes
- NS16550_TRIG_14 = 0C0H ;; trigger on 14 bytes
-
- ;; masks for the LCR
- I8250_LCR_BITS = 3H ;; number of bits - 5
- I8250_LCR_STOP = 4H
- I8250_LCR_PARITY = 38H
- I8250_LCR_BREAK = 40H ;; send a break
- I8250_LCR_DIVISOR = 80H ;; make reg 0,1 be the divisor register
-
- ;; values for the parity field of the LCR
- I8250_PARITY_IGN = 0H ;; notice they are shifted
- I8250_PARITY_ODD = 20H
- I8250_PARITY_EVEN = 30H
- I8250_PARITY_MARK = 28H
- I8250_PARITY_SPACE = 38H
-
- ;; masks for the MCR
- I8250_MCR_DTR = 1H ;; activate DTR line
- I8250_MCR_RTS = 2H ;; activate RTS line
- I8250_MCR_OUT1 = 4H ;; out1 Line
- I8250_MCR_OUT2 = 8H ;; out2 line
- I8250_MCR_LOOP = 10H ;; loopback
-
- ;; masks for the LSR
- I8250_LSR_DIN = 1H ;; data in read register
- I8250_LSR_OVR = 2H ;; data overun
- I8250_LSR_PERR = 4H ;; data parity error
- I8250_LSR_FERR = 8H ;; data framing error
- I8250_LSR_BREAK = 10H ;; break detected
- I8250_LSR_DOUT = 20H ;; Transmit buffer empty
- I8250_LSR_TSR = 40H ;; Transmit shift reg empty (no data on line)
-
- ;; masks for the MSR
- I8250_MSR_DELTA_CTS = 1H ;; CTS changed
- I8250_MSR_DELTA_DSR = 2H ;; DSR changed
- I8250_MSR_DELTA_RI = 4H ;; RI changed
- I8250_MSR_DELTA_DCD = 8H ;; DCD changed
- I8250_MSR_CTS = 10H ;; CTS active
- I8250_MSR_DSR = 20H ;; DSR active
- I8250_MSR_RI = 40H ;; RI active
- I8250_MSR_DCD = 80H ;; DCD active
-
- ;;*****************************************************************************
- ;; defs for the I8259 programable interupt controller
-
- IBM_I8259 = 20H ;; standard place on the IBM PC
-
- I8259_REG_CNTRL = 0 ;; the 8259 control register
- I8259_REG_MASKS = 1 ;; the interupt mask register
-
- ;; control register functions
- I8259_CNTRL_EOI = 20H ;; the End of Interupt command
-
-
- ;;*****************************************************************************
- ;; I8250_DECLARE declares your intent to use a 8250 serial controler at
- ;; address 'io_addr', using interupt 'irq'. When it gets characters it calls
- ;; <port_prefix>_CONSUME_in_AL_const_BX_BP_ES 'port' and
- ;; <port_prefix>_WRITE_EMPTY_const_BX_CX_DX_BP_SI_DI_ES 'port' when it
- ;; can write characters. If 'flow' is non-blank and = 1
- ;; then hardware flow control using RTS-CTS is followed.
- ;;
- I8250_DECLARE MACRO name, port_prefix, port, io_addr, irq, flow
- .errb <name>
- .errb <irq>
-
- .DATA
- i8250_declared = name
- i8250_&name&_port = port
- i8250_&name&_prefix equ <port_prefix>
- i8250_&name&_declared = 1
- i8250_&name&_io = io_addr
- i8250_&name&_irq = irq
-
- i8250_&name&_flow = 0
- ifnb <flow>
- i8250_&name&_flow = 0&flow
- endif
-
- .CODE
- global i8250_data_seg:word
- global i8250_&name&_real_define_in_BX_out_AX:near
- ENDM
-
-
- ;;******************************************************************************
- ;; I8250_DEFINE name, fail
- ;; sets asside memory an name object and initializes it. This
- ;; routine is a no-op if 'name' was not declared. It jumps to 'fail'
- ;; if there was an error in setup. BX contains the word that
- ;; is the baud rate divisor. (12 = 9600Baud, 6 = 19200Baud, etc)
- ;;
- I8250_DEFINE_in_BX MACRO name, fail
- .errb <name>
- .errb <fail>
-
- ifdef i8250_&name&_declared
- call i8250_&name&_real_define_in_BX_out_AX
- or AX, AX
- jnz fail
- endif
- ENDM
-
- ;;*************************************************************************
- ;; I8250_REAL_DEFINE is simply a the code that I8250_DEFINE jumps to.
- ;; this indirection is necessary to avoid macros getting too large.
- ;;
- I8250_REAL_DEFINE MACRO name
- local done
- .errb <name>
-
- ifdef i8250_&name&_declared
- i8250_&name&_real_define_in_BX_out_AX:
-
- mov word ptr CS:i8250_data_seg, DS ;; make sure interupt routine OK
-
- ;; turn off my interupt
- READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8259_REG_MASKS, IBM_I8259
- and AL, (1 shl i8250_&name&_irq)
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8259_REG_MASKS, IBM_I8259
-
- I8250_SETUP_in_BX_out_AX name, done
-
- ;; turn on my interupt
- READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8259_REG_MASKS, IBM_I8259
- and AL, (not (1 shl i8250_&name&_irq))
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8259_REG_MASKS, IBM_I8259
- sti ;; turn on interupts, if not already on
-
- xor AX, AX ;; return success
- done:
- ret
-
- if i8250_declared eq name ; define once for all
-
- ;;*****************************************************************
- ;; define the interupt handler
- .CODE
- i8250_data_seg: DW 0 ;; this location hold the data segment
-
- i8250_interupt: ;; the actual interupt handler
- cli
- push DS
- push AX
- push CX
- push DX
- push SI
- push DI
- mov DS, word ptr CS:i8250_data_seg
-
- IRP idx, <1,2,3,4,5,6,7,8> ;; check every chip
- I8250_IF_CHECK_const_BX_BP_ES idx
- endm
-
- mov AL, I8259_CNTRL_EOI ; tell the 8259 we are done
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8259_REG_CNTRL, IBM_I8259
- pop DI
- pop SI
- pop DX
- pop CX
- pop AX
- pop DS
- iret ; interupts flag restored on return
- endif
- endif
-
- ENDM
-
-
- ;;******************************************************************************
- ;; I8250_SETUP does all the 'per chip' setup for a 8250 serial chip associated
- ;; with 'name'. if there is a failure 'fail' is jumped to, AX will have a
- ;; non-zero value if the 'fail' branch is taken. BX contains the word that
- ;; is the baud rate divisor. (12 = 9600Baud, 6 = 19200Baud)
- ;;
- I8250_SETUP_in_BX_out_AX MACRO name, fail
- local done, is_a_16550AF, is_a_8250
-
- ifdef i8250_&name&_declared
- ; check to see that a serial port is there by reading 8250 register
- READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_LSR, i8250_&name&_io
- cmp AL, 0FFH ;; non-existant registers turn up as all 1s
- jz fail
-
- ;; set the baud rate divisor
- mov AL, I8250_LCR_DIVISOR
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8250_REG_LCR,i8250_&name&_io
-
- mov AX, BX ;; get the baud rate
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8250_REG_DIVL, i8250_&name&_io
- mov AL, AH
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8250_REG_DIVH,i8250_&name&_io
-
- mov AL, (8-5) + I8250_PARITY_IGN ;; 8 bits no parity 1 stop bit
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8250_REG_LCR, i8250_&name&_io
-
- if i8250_&name&_flow eq 1
- mov AL, I8250_IER_DIN + I8250_IER_DOUT + I8250_IER_MODEM
- else
- mov AL, I8250_IER_DIN + I8250_IER_DOUT
- endif
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8250_REG_IER, i8250_&name&_io
-
- ;; Turn on FIFO mode if this is a 16550AF
- mov AL, NS16550_FIFO_ON+NS16550_FIFO_FIN+NS16550_FIFO_FOUT+NS16550_TRIG_8
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES NS16550_REG_FIFO,i8250_&name&_io
-
- ;; make sure that this is a 16550AF not just a 16550 (which has a bug)
- READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_IIR, i8250_&name&_io
- and AL, NS16550_IIR_CHECK
- test AL, 80H ;; is it a 16550?
- jz is_a_8250
- test AL, 40H ;; is it a 16550AF?
- jnz is_a_16550AF
- mov AL, 0 ;; turn off FIFO, the 16550 is buggy
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES NS16550_REG_FIFO,i8250_&name&_io
- is_a_16550AF:
- is_a_8250:
-
- ;; set DTR and RTS for those modems that care
- ;; ON AN IBM PC OUT2 MUST BE SET FOR PROPER OPERATION
- mov AL, I8250_MCR_DTR + I8250_MCR_RTS + I8250_MCR_OUT2
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8250_REG_MCR, i8250_&name&_io
-
- cli
- xor AX, AX ;; load the interupt vector
- mov ES, AX
- mov DI, (i8250_&name&_irq+8)*4
- mov BX, offset i8250_interupt
- mov AX, CS
- mov ES:[DI], BX
- mov ES:[DI+2], AX
- sti
-
- ; acknowledge any interupts that may have occured in the past
- READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_RDR, i8250_&name&_io
- READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_MSR, i8250_&name&_io
- READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_IIR, i8250_&name&_io
- endif
- ENDM
-
-
- ;;******************************************************************************
- ;; I8250_IF_CHECK checks to see of interface 'name' has any characters to write
- ;; or to read and does the appropriate upcall if there is anthing to do.
- ;;
- I8250_IF_CHECK_const_BX_BP_ES MACRO name
- local done, try_write, try_read, done_write
-
- ifdef i8250_&name&_declared
- try_read:
- READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_LSR, i8250_&name&_io
- and AL, I8250_LSR_DIN ;; is there a character ready?
- jz try_write
-
- READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_RDR, i8250_&name&_io
- UPCALL_CONSUME_in_AL_const_BX_BP_ES %i8250_&name&_prefix, %i8250_&name&_port
- jmp try_read ;; keep trying until failure (for 16650)
-
- try_write:
- ; acknowledge the Tran Empty interupt
- READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_IIR, i8250_&name&_io
-
- I8250_CAN_WRITE_const_BX_CX_BP_SI_DI_ES name, done_write
- UPCALL_WRITE_EMPTY_const_BX_BP_ES %i8250_&name&_prefix,%i8250_&name&_port
- done_write:
-
- ;; have all the interupts been processed?
- READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_IIR, i8250_&name&_io
- test AL, I8250_IIR_NOINT
- jz try_read ;; no, then try again until they are
-
- ;; Note that this final check for interupts is absolutely
- ;; necessary because the 8259 responds to EDGES, not LEVELs
- ;; only if we are absolutely sure the interupt line has gone
- ;; low (which would be the case if IIR has NOINT bit = 1) can
- ;; we be sure that the next interupt will generate an EDGE
- ;; the the 8259 will respond to. Without this check it is
- ;; possible for interupts from the 8250 to be lost causing
- ;; the code to hang
- done:
- endif
- ENDM
-
- UPCALL_CONSUME_in_AL_const_BX_BP_ES MACRO prefix, port
- prefix&_CONSUME_in_AL_const_BX_BP_ES port
- ENDM
-
- UPCALL_WRITE_EMPTY_const_BX_BP_ES MACRO prefix, port
- prefix&_WRITE_EMPTY_const_BX_BP_ES port
- ENDM
-
-
- ;;******************************************************************************
- ;; W_CAN_WRITE jumps to 'not_ready' if the 8250 chip associated with
- ;; 'name' is not ready to recieve another byte. Otherwise it just returns.
- ;;
- I8250_CAN_WRITE_const_BX_CX_BP_SI_DI_ES MACRO name, not_ready
- .errb <name>
- .errb <success>
-
- ;; if we are doing flow control, we can only send if CTS is active
- if i8250_&name&_flow eq 1
- READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_MSR, i8250_&name&_io
- and AL, I8250_MSR_CTS
- jz not_ready
- endif
-
- READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_LSR, i8250_&name&_io
- and AL, I8250_LSR_DOUT ;; is there a character ready?
- jz not_ready
- ENDM
-
-
- ;;******************************************************************************
- ;; WRITE_in_AL writes the character in AL to the chip 'name'. It
- ;; will busy wait if necessary, however this routine is guarenteed not
- ;; to busy wait if it is called only when the WRITE_EMPTY upcall is issued.
- ;;
- I8250_WRITE_in_AL_const_BX_BP_ES MACRO name
- local wait_loop, ready
- .errb <name>
-
- mov SI, AX ;; save AL
- xor CX, CX
- wait_loop:
- READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_LSR, i8250_&name&_io
- and AL, I8250_LSR_DOUT ;; is there a character ready?
- jnz ready
- dec CX ;; so we don't wait forever
- jnz wait_loop
-
- ready:
- mov AX, SI ;; restore AL
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8250_REG_THR, i8250_&name&_io
-
- ENDM
-
-
- ;;******************************************************************************
- ;; utility functions needed only within this module
-
- READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES MACRO port, if_io
- mov DX, if_io+port
- in AL, DX ;; AL contains data read from port
- ENDM
-
- ;;******************************************************************************
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES MACRO port, if_io
- mov DX, if_io+port
- out DX, AL ;; AL contains data read from port
- ENDM
-
-