home *** CD-ROM | disk | FTP | other *** search
- title 'Interrupt-Driven Serial Port Handler'
-
- ; THIS PROGRAM IS HEREBY PLACED IN THE PUBLIC DOMAIN,
- ; AND MAY BE FREELY COPIED AND DISTRIBUTED.
-
- ; Context Sensitive Inc.
- ; 4200 Aurora Ave. N.
- ; Seattle, WA 98103
- ; (206) NEC-0301
- ;
- ; Program: SERIALIO.A86
- ;
- ; Version: 1.2 23 February 1984
- ; Author: Ron Blanford
-
-
- ; This is only a fragment of a program. The routines given
- ; here can be included in any (assembly language) program,
- ; and called as required to perform input and output to the
- ; primary serial (RS232) port on the NEC Advanced Personal
- ; Computer. These routines duplicate functions that are
- ; provided by CP/M-86, with the difference that input from
- ; the serial port is handled on an interrupt-driven basis
- ; and characters are stored until requested.
- ;
- ; To add these routines to your program, insert just prior
- ; to the END statement of your program the statement
- ; INCLUDE SERIALIO.A86
- ;
- ; These routines preserve all registers, except possibly AL
- ; and the flags.
- ;
- ; The routines available to your program are:
- ;
- ; 1. sio_rxstatus: Returns -1 in AL if a character is
- ; available in the serial input buffer,
- ; and 0 if no character is waiting.
- ;
- ; 2. sio_receive: Returns a character from the serial
- ; input buffer in AL. If no character
- ; is available, it waits until one
- ; arrives.
- ;
- ; 3. sio_txstatus: Returns -1 in AL if the transmit
- ; buffer is empty and a character may be
- ; transmitted, and 0 if transmission is
- ; not possible.
- ;
- ; 4. sio_transmit: Sends the character in AL out the
- ; serial port. If the port is not ready,
- ; it waits until it is.
- ;
- ; 5. sio_start: Called once to initialize the port
- ; before using it. Sets initial values:
- ; baud = 300, parity = none
- ; data = 8, stop = 1.
- ;
- ; 6. sio_finish: Called once to de-initialize the port
- ; just before ending your program.
- ;
- ; 7. sio_setmode: Called to set the baud rate and mode
- ; of the port. DX contains the parameters
- ; that are described in the CP/M-86 System
- ; Reference Guide, page 5-40, as follows:
- ;
- ; DH = baud rate DL = asynchronous mode byte
- ; 0 = 150 bps 7 6 5 4 3 2 1 0
- ; 1 = 200 bps _________________________________
- ; 2 = 300 bps | | | | 1 0 |
- ; 3 = 600 bps ----^-------^-------^-------^----
- ; 4 = 1200 bps | | | +- must be 10
- ; 5 = 2400 bps | | +- data bits: 00=5, 01=6
- ; 6 = 4800 bps | | 10=7, 11=8
- ; 7 = 9600 bps | +--- parity: 00 or 10=none
- ; 8 = 19200 bps | 01=odd, 11=even
- ; +--- stop bits: 01=1, 10=1.5
- ; 11=2, 00 illegal
- ;
- ; If DH is greater than 8, the baud rate will
- ; not be changed. If DL is 0, the mode will
- ; not be changed.
- ;
- ;
- ; An alternative entry point to these routines which is
- ; more suitable for high-level languages is to use the
- ; initial jump vector table. The vectors are allocated
- ; in the same sequence as listed above, and are each the
- ; standard 3-byte relative jump instruction. The address
- ; of the vector table depends on the load address assigned
- ; by the high-level language.
-
-
- cseg $
-
- sio_jumptable:
- jmp sio_rxstatus ; receive status
- jmp sio_receive ; receive character
- jmp sio_txstatus ; transmit status
- jmp sio_transmit ; transmit character
- jmp sio_start ; initialize serial port
- jmp sio_finish ; de-initialize serial port
- jmp sio_setmode ; set port characteristics
-
-
- ; Interrupt vector locations, in data segment 0
-
- sio_offset equ 84h ; Sio interrupt offset
- sio_segment equ 86h ; Sio interrupt segment
-
-
- ; 8259 Interrupt controller (master) port addresses
-
- ic_command equ 20h ; Interrupt command register
- ic_mask equ 22h ; Interrupt mask register
-
- ; 8259 commands and masks
-
- icmd_endInt equ 20h ; End of interrupt
- imask_timerOff equ 08h ; Disable timer interrupt
- imask_sioOff equ 02h ; Disable RS232 interrupt
-
-
- ; 8253-5 Interval timer port addresses
-
- timer_data equ 2Bh ; Baud set (for channel 1)
- timer_command equ 2Fh ; Baud timer command port
-
- ; 8253 Timer commands
-
- tcmd_ch1 equ 76h ; Select & init channel 1
-
-
- ; 8251A USART controller port addresses
-
- sio_data equ 30h ; Data port
- sio_sts1 equ 32h ; in Status port 1
- sio_sts2 equ 34h ; in Status port 2
- sio_command equ 32h ; out Command port
- sio_mask equ 34h ; out Interrupt mask port
- sio_disable equ 36h ; out Disable trans. port
-
- ; 8251 status port 1 bits
-
- sio_RxRDY equ 02h ; Receive ready value
- sio_TxRDY equ 01h ; Send ready value
- sio_DSR equ 80h ; Data set ready
-
- ; 8251 status port 2 bits
-
- sio_CS equ 04h ; Clear to send
-
- ; 8251 initialization instructions
-
- ; command instructions
-
- scmd_TxE equ 01h ; Transmit enable
- scmd_DTR equ 02h ; DTR signal high
- scmd_RxE equ 04h ; Receive enable
- scmd_BRK equ 08h ; Send break
- scmd_ERR equ 10h ; Error reset
- scmd_RTS equ 20h ; RTS signal high
- scmd_MODE equ 40h ; Reset - accept mode inst.
- scmd_HUNT equ 80h ; Hunt for sync characters
-
- ; mode instructions
-
- smode_1x equ 01h ; Baud rate factor: 1x
- smode_16x equ 02h ; 16x
- smode_64x equ 03h ; 64x
- smode_5data equ 00h ; Data bits: 5
- smode_6data equ 04h ; 6
- smode_7data equ 08h ; 7
- smode_8data equ 0Ch ; 8
- smode_pnone equ 00h ; Parity: none
- smode_podd equ 10h ; odd
- smode_peven equ 30h ; even
- smode_1stop equ 40h ; Stop bits: 1
- smode_15stop equ 80h ; 1.5
- smode_2stop equ 0C0h ; 2
-
- ; 8251 interrupt mask port bits
-
- sint_txOff equ 01h ; Transmit complete int.
- sint_rxOff equ 02h ; Receive complete int.
- sint_tbeOff equ 04h ; Trans. buffer empty int.
-
-
-
- ; sio_rxstatus: returns -1 in AL if a character has been received
- ; returns 0 if the input interrupt buffer is empty.
-
- sio_rxstatus:
- cmp sio_chars,0 ; Any chars in the buffer?
- je siorx1 ; If not, report failure
- or al,0FFh ; Otherwise give positive
- jmps siorx2 ; response
- siorx1: and al,0
- siorx2: ret
-
-
- ; sio_receive: returns the next input character in AL
- ; waits for a character if none have arrived.
-
- sio_receive:
- call sio_rxstatus ; Wait for a character
- jz sio_receive
- push bx
- cli ; Prevent interrupts.
- dec sio_chars ; Uncount the character.
- mov bx,sio_charPtr ; Get the buffer pointer.
- inc bx ; Point to the next char.
- cmp bx,offset sio_buffer+sio_size ; Past the end?
- jb sio_r2
- lea bx,sio_buffer ; If so wrap to the start.
- sio_r2: mov sio_charPtr,bx ; Save the updated pointer.
- mov al,[bx] ; Get the character.
- sti ; Re-enable interrupts.
- pop bx
- ret
-
-
- ; sio_int: Handles the serial port input interrupts
-
- sio_int:
- cli ; Prevent interrupts.
- push ax ; Save registers we'll use.
- push bx
- push ds
- mov ax,cs:sio_dataSeg ; Get our own data segment.
- mov ds,ax
-
- call sio_process ; Receive and store char.
-
- mov al,icmd_endInt ; Signal End of Interrupt.
- out ic_command,al
- pop ds ; Restore registers we used.
- pop bx
- pop ax
- iret ; Return from the interrupt.
-
-
- ; sio_process: Reads the input character from the serial port and
- ; stores it in the ring buffer.
-
- sio_process:
- in al,sio_sts1 ; Get the port status.
- and al,sio_RxRDY ; Is a character waiting?
- jz sio_p3 ; No, just a false alarm.
- in al,sio_data ; Otherwise read character.
- cmp sio_chars,sio_size ; Is the buffer full?
- je sio_p3 ; If so, discard character.
- inc sio_chars ; Otherwise count character.
- mov bx,sio_spacePtr ; Point to the next space.
- inc bx ; Increment pointer.
- cmp bx,offset sio_buffer+sio_size ; Past the end?
- jb sio_p2
- lea bx,sio_buffer ; Yes, wrap to the start.
- sio_p2: mov sio_spacePtr,bx ; Save the pointer.
- mov [bx],al ; Save the character.
- sio_p3: ret
-
-
- ; sio_txstatus: returns -1 in AL if the serial port can accept a
- ; character for transmission, returns 0 otherwise.
-
- sio_txstatus:
- in al,sio_sts1 ; Read status port 1.
- and al,sio_DSR+sio_TxRDY ; Mask DSR and TxRDY bits.
- xor al,sio_DSR+sio_TxRDY ; Check that both are set.
- jnz siotx1 ; If not, port is not ready.
- in al,sio_sts2 ; Read status port 2.
- and al,sio_CS ; Check for clear to send.
- jz siotx1 ; If not, return failure.
- or al,0FFh ; Otherwise ready to send.
- jmps siotx2
- siotx1: and al,0
- siotx2: ret
-
-
- ; sio_transmit: sends the character in AL to the serial port.
- ; doesn't return until the character has been sent.
-
- sio_transmit:
- push ax ; Save character to be sent.
- sio_t1: call sio_txstatus ; Wait for port to be ready.
- jz sio_t1
- pop ax
- out sio_data,al ; Send the character.
- ret
-
-
- ; sio_start: Initializes the interrupt vectors and controller
- ; and the serial port to default values
-
- sio_start:
- cmp sio_init,0 ; Skip initialization
- jne sio_s1 ; if already performed.
- mov sio_init,0FFh ; Set "initialized" flag.
-
- push ax ; Save registers we'll use.
- push dx
- push es
-
- mov ax,ds ; Save data segment in CSEG
- mov cs:sio_dataSeg,ax ; for the int. handler.
-
- in al,ic_mask ; Get current interrupt mask
- mov sio_oldInt,al ; Save for later restoration
-
- mov ax,0 ; Point interrupt vector
- mov es,ax ; table in page zero.
- mov ax,es:.sio_segment ; Save the current vector
- mov sio_oldSeg,ax ; segment and offset.
- mov ax,es:.sio_offset
- mov sio_oldOff,ax
-
- cli
- mov ax,cs ; Replace with address of
- mov es:.sio_segment,ax ; interrupt handler.
- mov ax,offset sio_int
- mov es:.sio_offset,ax
-
- mov dh,8 ; Set default baud 19200
- mov dl,smode_1stop+smode_pnone+smode_8data+smode_16x
- call sio_setmode ; and default mode.
-
- mov al,00h ; Reset disable register.
- out sio_disable,al
-
- in al,sio_data ; Clear input buffer.
-
- mov al,sint_txOff+sint_tbeOff ; Set serial int. mask.
- out sio_mask,al
-
- in al,ic_mask ; Set master controller to
- or al,imask_timerOff ; disable timer
- and al,not imask_sioOff ; and enable sio.
- out ic_mask,al
- sti
-
- pop es ; Restore our registers.
- pop dx
- pop ax
-
- sio_s1: ret
-
-
- ; sio_finish: This routine restores the interrupt controller
- ; mask and vector table to the original values.
-
- sio_finish:
- cmp sio_init,0 ; Was initialization done?
- je sio_f1 ; If not, don't undo it.
- mov sio_init,0
-
- push ax ; Save our registers.
- push es
- cli ; Prevent interrupts.
-
- mov al,sio_oldInt ; Restore the old mask.
- out ic_mask,al
-
- mov ax,0 ; restore the old vector.
- mov es,ax
- mov ax,sio_oldSeg
- mov es:.sio_segment,ax
- mov ax,sio_oldOff
- mov es:.sio_offset,ax
-
- sti
- pop es ; Restore our registers.
- pop ax
-
- sio_f1: ret
-
-
- ; sio_setmode: sets the baud rate, data bits, parity, and stop
- ; bits for the serial port. DH contains an index to
- ; the baud rate table, and DL contains the mode byte.
-
- sio_setmode:
- push ax ; Save our registers.
- push bx
-
- or dl,dl ; See if mode is to be set:
- jz sio_m1 ; not if DL = 0.
-
- mov al,0 ; Suggested reset sequence:
- out sio_command,al ; 3 zeros and scmd_MODE.
- mov al,0
- out sio_command,al
- mov al,0
- out sio_command,al
- mov al,scmd_MODE
- out sio_command,al
-
- push ax ; waste some time to allow
- pop ax ; the 8251 time to reset
-
- mov al,dl ; Set mode with supplied
- out sio_command,al ; parameters.
-
- mov al,scmd_RTS+scmd_ERR+scmd_RxE+scmd_DTR+scmd_TxE
- out sio_command,al ; RTS & DTR high, Tx & Rx on
-
- sio_m1: mov al,dh ; Get the baud rate index.
- cmp al,8 ; Validate range (0-8).
- ja sio_m2
-
- lea bx,cs:sio_baud ; Point to baud table.
- add al,al ; Make index a word offset.
- mov ah,0
- add bx,ax ; Point to desired entry.
- mov al,tcmd_ch1 ; Set timer channel 1 mode.
- out timer_command,al
- mov ax,cs:[bx] ; Get the baud table entry.
- out timer_data,al ; Output low byte.
- mov al,ah
- out timer_data,al ; Output high byte.
-
- sio_m2: pop bx ; Restore our registers.
- pop ax
- ret
-
-
-
- sio_dataSeg dw 0 ; Storage in CSEG to point
- ; to DSEG.
-
-
- ; Interval Timer values (assumes 16x baud rate mode)
-
- sio_baud dw 0400h ; 150 baud 0
- dw 0300h ; 200 baud 1
- dw 0200h ; 300 baud 2
- dw 0100h ; 600 baud 3
- dw 0080h ; 1200 baud 4
- dw 0040h ; 2400 baud 5
- dw 0020h ; 4800 baud 6
- dw 0010h ; 9600 baud 7
- dw 0008h ; 19200 baud 8
-
-
- dseg $
-
- ; Impure storage with contents that are changed dynamically
-
- sio_init db 0 ; "Initialized" flag.
- sio_oldInt db 0 ; 8259 interrupt mask
- sio_oldSeg dw 0 ; Sio interrupt vector
- sio_oldOff dw 0
-
- sio_size equ 128 ; Size of circular buffer.
- sio_buffer rb sio_size ; Circular character buffer.
- sio_spacePtr dw sio_buffer ; Char. insertion pointer.
- sio_charPtr dw sio_buffer ; Char. retrieval pointer.
- sio_cha