Category : Assembly Language Source Code
Archive   : IBMRS232.ZIP
Filename : IBMRS232.ASM

 
Output of file : IBMRS232.ASM contained in archive : IBMRS232.ZIP
page 60,132
;
;***********************************************************
;** **
;** Device Driver for RS232 communications **
;** IBM PC Version **
;** Copyright (C) Texas Instruments 1986 **
;** Author: Greg Haley **
;** **
;** THIS SOURCE CODE MAY BE DISTRIBUTED AND MODIFIED **
;** ONLY IF THE ORIGINAL COPYRIGHT AND AUTHOR CREDITS **
;** REMAIN INTACT. **
;** **
;** Project Start Date: 11/20/86 **
;** **
;** Re: 12/29/86 by Greg Haley **
;** Added send_xon routine. **
;** Added 19200 baud. **
;** **
;** Re: 02/11/87 by Bill Hinkle **
;** Fixed DLAB baud rate setting bug **
;** **
;** Re: 03/18/87 by Bill Hinkle **
;** Added support for ports 3 and 4 (3E8h & 2E8H) **
;** **
;** Re: 8/10/87 by Joe McDaniel **
;** Fixed numerous bugs in xon/xoff protocol **
;** Made transmit interrupt driven **
;** Really installed 19,200 baud support **
;***********************************************************

name ibmrs232
title Device Driver for IBM PC Communications

code segment byte
assume cs:code,ds:nothing,es:nothing

include rs232.inc

page

;***********************************************************
;** Communications Routines **
;***********************************************************
trans_size equ 200h ; Transmit buffer size
recv_size equ 200h ; Receive buffer size
; Both must be one of these values:
; 0004h = 4 bytes
; 0008h = 8 bytes
; 0010h = 16 bytes
; 0020h = 32 bytes
; 0040h = 64 bytes
; 0080h = 128 bytes
; 0100h = 256 bytes
; 0200h = 512 bytes
; 0400h = 1024 bytes
; 0800h = 2048 bytes
; 1000h = 4096 bytes
; 2000h = 8192 bytes
; 4000h = 16384 bytes
; 8000h = 32768 bytes

recv_limit equ recv_size-1 ; Receive buffer limit mask
tran_limit equ trans_size-1 ; Receive buffer limit mask
busy_len equ recv_size *3 /4 ; Go busy at 3/4 buf length
not_busy_len equ recv_size /2 ; Not busy at 1/2 buf length


int_controller equ 21h ; 8259A Interrupt Controller port
int_ack equ 20h ; 8259A Interrupt Acknowledge port

enable_ints equ 00001111b ; Enable interrupts
disable_ints equ 00000000b ; Disable interrupts
dcd_bit equ 128 ; DCD bit in modem status register
cts_bit equ 16 ; CTS bit in modem status register
dsr_bit equ 32 ; DSR bit in modem status register
ri_bit equ 64 ; RI bit in modem status register

dtr_rts_out2 equ 00001011b ; DTR, RTS, & OUT2
dlab equ 10000000b ; DLAB (Divisor Latch Access Bit)
char_waiting equ 00000001b ; Receive buffer full bit
int_pending equ 00000001b ; Int pending bit in 8250

xon equ 11h ; xmit on busy char
xoff equ 13h ; xmit off busy char

page
;***********************************************************
;** Variables **
;***********************************************************

old_ss dw 0 ; Old SS reg
old_sp dw 0 ; Old SP reg
db 80 dup (?) ; Stack
i_stack label byte

rq_head dw 0 ; Receive Queue start
rq_tail dw 0 ; Receive Queue stop
rq_len dw 0 ; Current receive Queue length
rqueue db recv_size dup (?) ; Receive queue

tq_head dw 0 ; Transmit Queue start
tq_tail dw 0 ; Transmit Queue stop
tq_len dw 0 ; Current transmit Queue length
tqueue db trans_size dup(?) ; Transmit queue

oldseg dw 0 ; Old segment for int vector
oldoff dw 0 ; Old offset for int vector
busy_hand db 0 ; busy handling type
r_busy db 0 ; recv busy flag
t_busy db 0 ; xmit busy flag (protocol control)
xmit_busy db 0 ; xmit busy flag (interrupts active)
m_stat db 0 ; Current modem status
l_stat db 0 ; Current line status
dcw dw 1110000001000000b ; default raw mode, 1200, 8, N, 1
parity_on db 0 ; Parity flag

int_tbl dw mod_stat ; Interrupt branch table
dw xmit_mt
dw rec_full
dw lin_stat

; table for baud rate constants
baud_tbl dw 417h ; 110
dw 300h ; 150
dw 180h ; 300
dw 0c0h ; 600
dw 60h ; 1200
dw 30h ; 2400
dw 18h ; 4800
dw 0ch ; 9600
dw 06h ;19200

speed dw 0060h ; Default baud rate to use (1200)
comm_parms db 00000011b ; No parity, 8 data, 1 stop

; table for port number init tables
p_table dw port1_tbl
dw port2_tbl
dw port3_tbl
dw port4_tbl

port_n dw port1_tbl ; default port is 1

; port 1 init table
port1_tbl dw 0ch ; Interrupt vector for irq
db 11101111b ; Mask to enable irq
db 00010000b ; Mask to unable irq
dw 3f8h ; Receive buffer port
dw 3f8h ; Transmit buffer port
dw 3f8h ; Divisor least significant byte
dw 3f9h ; Divisor most significant byte
dw 3fbh ; 8250 UART Control port
dw 3fdh ; 8250 UART Status port
dw 3fch ; 8250 Modem Control port
dw 3feh ; 8250 Modem Status port
dw 3f9h ; 8250 interrupt enable register
dw 3fah ; 8250 interrupt ack register
db 64h ; 8259 Specific EOI
p_tbl_size equ $-port1_tbl

; port 2 init table
port2_tbl dw 0bh ; Interrupt vector for irq
db 11110111b ; Mask to enable irq
db 00001000b ; Mask to unable irq
dw 2f8h ; Receive buffer port
dw 2f8h ; Transmit buffer port
dw 2f8h ; Divisor least significant byte
dw 2f9h ; Divisor most significant byte
dw 2fbh ; 8250 UART Control port
dw 2fdh ; 8250 UART Status port
dw 2fch ; 8250 Modem Control port
dw 2feh ; 8250 Modem Status port
dw 2f9h ; 8250 interrupt enable register
dw 2fah ; 8250 interrupt ack register
db 63h ; 8259 Specific EOI

; port 3 init table
port3_tbl dw 0ch ; Interrupt vector for irq
db 11101111b ; Mask to enable irq
db 00010000b ; Mask to unable irq
dw 3e8h ; Receive buffer port
dw 3e8h ; Transmit buffer port
dw 3e8h ; Divisor least significant byte
dw 3e9h ; Divisor most significant byte
dw 3ebh ; 8250 UART Control port
dw 3edh ; 8250 UART Status port
dw 3ech ; 8250 Modem Control port
dw 3eeh ; 8250 Modem Status port
dw 3e9h ; 8250 interrupt enable register
dw 3eah ; 8250 interrupt ack register
db 64h ; 8259 Specific EOI

; port 4 init table
port4_tbl dw 0bh ; Interrupt vector for irq
db 11110111b ; Mask to enable irq
db 00001000b ; Mask to unable irq
dw 2e8h ; Receive buffer port
dw 2e8h ; Transmit buffer port
dw 2e8h ; Divisor least significant byte
dw 2e9h ; Divisor most significant byte
dw 2ebh ; 8250 UART Control port
dw 2edh ; 8250 UART Status port
dw 2ech ; 8250 Modem Control port
dw 2eeh ; 8250 Modem Status port
dw 2e9h ; 8250 interrupt enable register
dw 2eah ; 8250 interrupt ack register
db 63h ; 8259 Specific EOI

; defaults for port 1
port_tbl label word
comm1_vector dw 0ch ; Interrupt vector for irq
irq_enab_mask db 11101111b ; Mask to enable irq
irq_unab_mask db 00010000b ; Mask to unable irq
recv_buffer dw 3f8h ; Receive buffer port
send_buffer dw 3f8h ; Transmit buffer port
lsb_divisor dw 3f8h ; Divisor least significant byte
msb_divisor dw 3f9h ; Divisor most significant byte
line_control dw 3fbh ; 8250 UART Control port
line_status dw 3fdh ; 8250 UART Status port
modem_control dw 3fch ; 8250 Modem Control port
modem_status dw 3feh ; 8250 Modem Status port
int_enable dw 3f9h ; 8250 interrupt enable register
int_id dw 3fah ; 8250 interrupt ID register
SEOI db 64h ; 8259 Specific EOI

page
;***********************************************************
;** Subroutine to set up comm chip per the DCW **
;** DCW is in AX **
;***********************************************************
set_dcw:

; clear comm parms
xor dl,dl

; set parity type
mov bl,al ; get low byte in BL
and bl,00000011b ; mask unused bits
or dl,bl ; change parity
mov cl,3
shl dl,cl
and bl,00000001b ; change parity flag
mov parity_on,bl ;

; set num stop bits
mov bl,al ; get low byte in BL
and bl,00001000b ; mask unused bits
shr bl,1
or dl,bl

; set data bits
mov bl,ah ; get high byte in BL
and bl,01000000b ; mask unused bits
or bl,10000000b
mov cl,6
shr bl,cl
or dl,bl

; Save comm parms
mov comm_parms,dl

; set baud rate
mov bl,al ; get low byte in BL
and bl,11110000b ; mask unused bits
mov cl,3 ; shift to make word ptr
shr bl,cl
mov si,offset baud_tbl ; point to baud table
xor bh,bh ; make BX a byte ptr
add si,bx ; SI now points to baud rate const
mov dx,word ptr [si] ; get baud rate in DX
mov speed,dx ; save baud rate

; set busy type
mov bl,ah
and bl,00000011b ; mask unwanted bits
mov byte ptr busy_hand,bl ; store it

; set port number
mov bl,ah ; get high byte in BL
and bl,00001000b ; mask unused bits
shr bl,1 ; shift to make word ptr
shr bl,1
mov si,offset p_table ; point to port table
xor bh,bh ; make BX a byte ptr
add si,bx ; SI now points to baud rate const
mov dx,word ptr [si] ; get port adrs in DX
mov word ptr port_n,dx ; save port number adrs
ret

page
;***********************************************************
;** Subroutine to set up interrupt vector **
;** and initialize the 8250 comm chip **
;***********************************************************
init_comm:
push ds
push cs
pop ds

; get correct port parameters
push es
push cs
pop es

mov si,word ptr port_n ; get port table adrs
mov di,offset port_tbl ; DI points to table to use
mov cx,p_tbl_size ; CX has table length
repz movsb ; move it

pop es

; Save old int vector for irq
mov di,comm1_vector
call get_vector
mov word ptr oldseg,bx
mov word ptr oldoff,dx

; Set up int vector for irq
push cs ; Make BX = CS
pop bx
mov dx,offset isr
mov di,comm1_vector
call set_vector

; Enable irq from 8259A
cli
in al,int_controller
jmp $+2 ; delay
and al,irq_enab_mask
out int_controller,al

; Set baud rate, parity, etc.
mov dx,line_control
mov al,comm_parms
or al,dlab ; Bring up DLAB
out dx,al
mov dx,lsb_divisor ; Set LSB of Divisor Latch
mov ax,speed
out dx,al
mov dx,msb_divisor ; Set MSB of Divisor Latch
mov al,ah
out dx,al
mov dx,line_control
in al,dx
and al,not dlab
out dx,al

; Read receive buffer register
call lin_stat
test al,char_waiting
jz init_1
mov dx,recv_buffer
in al,dx
init_1:

; Read modem control register
mov dx,modem_control
in al,dx

; Read modem status register
call mod_stat

; Read UART status register
call lin_stat

; Enable 8250 interrupts
mov dx,line_control
in al,dx
and al,not dlab
out dx,al
mov dx,int_enable
mov al,enable_ints
out dx,al

; Raise DTR, RTS, & OUT2
mov dx,modem_control
mov al,dtr_rts_out2
out dx,al

pop ds
sti
ret

page
;***********************************************************
;** Subroutine to restore interrupt vector **
;** and reset the 8250 comm chip **
;***********************************************************
de_init:
cli

; Disable irq
in al,int_controller
or al,cs:byte ptr irq_unab_mask
jmp $+2 ; delay
jmp $+2 ; delay
out int_controller,al

; Disable interrupts on 8250 and drop DTR, RTS
mov dx,cs:line_control
in al,dx
jmp $+2 ; delay
and al,not dlab
out dx,al
mov dx,cs:int_enable
xor al,al
out dx,al

; It's probably not a good idea to restore the vector at close in
; this case, but here's the code to do it:
;
; Restore int vector for irq
; mov bx,cs:word ptr oldseg
; mov dx,cs:word ptr oldoff
; mov di,comm1_vector
; call set_vector
sti
ret

page
;***********************************************************
;** Interrupt Service Routine **
;***********************************************************
isr:
cli
cld
; Set up new stack
mov cs:word ptr old_sp,sp
mov cs:word ptr old_ss,ss
mov sp,cs
mov ss,sp
mov sp,offset i_stack

push ax
push bx
push cx
push dx
push ds

push cs
pop ds

; Verify int came from 8250
mov dx,int_id
in al,dx
test al,int_pending
jnz isr_exit

; Branch to correct routine
cbw ; make int type a word
mov bx,offset int_tbl ; point to int table
add bx,ax ; add int type
sti
call cs:word ptr [bx] ; go do subroutine
cli

isr_exit:
; Tell 8259A we're done
mov al,SEOI
out int_ack,al

pop ds
pop dx
pop cx
pop bx
pop ax

; restore stack
mov ss,cs:word ptr old_ss
mov sp,cs:word ptr old_sp

sti
iret

page
;***********************************************************
;** Subroutine to read the modem status **
;***********************************************************
mod_stat:
mov dx,modem_status ; Read modem status (CHB)
in al,dx ; into reg AL

xor ah,ah
test al,ri_bit
jz test_dsr
or ah,4
test_dsr:
test al,dsr_bit
jz test_cts
or ah,1
test_cts:
shl ah,1
test al,cts_bit
jz test_dcd
or ah,1
test_dcd:
shl ah,1
test al,dcd_bit
jz test_done
or ah,1
test_done:

; AH now contains the line signals
; ---------------------------------
; | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
; ---------------------------------
; | | | | RI| |DSR|CTS|DCD|
; ---------------------------------
; 0=down, 1=up

mov cs:byte ptr m_stat,ah ; Update current modem status

; Check for busy handling
shr ah,1 ; DSR in bit 0
shr ah,1
cmp cs:byte ptr busy_hand,2 ; DSR busy handling?
jne xit_mod_stat ; No, skip
mov al,ah ; save AH
set_tbusy:
and al,1 ; up = busy on
; not al ; down = busy on
mov cs:byte ptr t_busy,al ; set busy


xit_mod_stat:
ret

page
;***********************************************************
;** Subroutine to send the next char in queue **
;***********************************************************
xmit_mt:
cmp cs:byte ptr t_busy,0 ; see if protocol busy in effect
je xmit_mt_0 ; no protocol busy for now

mov cs:byte ptr xmit_busy,0 ; Tell world we're not busy any more
ret

xmit_mt_0:
cmp cs:word ptr tq_len,0 ; anything at all to send?
jne xmit_mt_1 ; there is something to send
mov cs:byte ptr xmit_busy,0 ; Tell world we're not busy any more
ret

xmit_mt_1:
mov bx,offset tqueue ; Dequeue char
mov dx,cs:word ptr tq_tail
add bx,dx
mov al,cs:byte ptr [bx] ; get the character
inc dx
and dx,tran_limit ; wrap if >= receive size
mov cs:word ptr tq_tail,dx
dec cs:word ptr tq_len ; Adjust queue length

mov dx,cs:word ptr send_buffer ; get port address
out dx,al ; send character
mov cs:byte ptr xmit_busy,1 ; Tell world we are busy now

ret

page
;***********************************************************
;** Subroutine to receive a char and queue it **
;***********************************************************
rec_full:
mov dx,recv_buffer ; Get char
in al,dx
cmp cs:byte ptr parity_on,0 ; parity?
jz no_par ; No, don't mask off parity bit
and al,7fh ; Yes, mask off parity bit
no_par:
; check for XON-XOFF busy char
cmp cs:byte ptr busy_hand,3 ; XON-XOFF busy handling?
jne queue_char ; No, skip busy handling
cmp al,xoff ; Need to set busy?
jne chk_r_xon ; No, skip
mov cs:byte ptr t_busy,1 ; set busy
jmp xit_rec_full ; We're done

chk_r_xon:
cmp al,xon ; Need to reset busy?
jne queue_char ; No, skip busy handling
mov cs:byte ptr t_busy,0 ; reset busy
cmp cs:byte ptr busy_hand,3 ; XON-XOFF busy handling?
jne xit_rec_full ; no, just restart transmitter
cmp cs:byte ptr xmit_busy,0 ; see if transmitter is already
; (still) busy
jne xit_rec_full ; it was. we're done

call xmit_mt ; restart transmitter
jmp xit_rec_full ; We're done

queue_char:
; check for buffer overflow
cmp cs:word ptr rq_len,recv_limit ; buffer full?
jb buf_full1 ; No, skip
inc cs:word ptr rq_tail ; Yes, lose 1 char
and cs:word ptr rq_tail,recv_limit
dec cs:word ptr rq_len ; Adjust queue length
buf_full1:

mov bx,offset rqueue ; Queue char
mov dx,cs:word ptr rq_head
add bx,dx
mov cs:byte ptr [bx],al
inc dx
and dx,recv_limit ; wrap if >= receive size
mov cs:word ptr rq_head,dx
inc cs:word ptr rq_len ; Adjust queue length

; set busy if needed
cmp cs:byte ptr busy_hand,3 ; XON-XOFF busy handling?
jne set_rb_done ; No, skip
cmp cs:word ptr rq_len,busy_len ; Need to set busy?
jb set_rb_done ; No, skip
mov cs:byte ptr r_busy,1 ; set busy flag
mov al,xoff ; send the xoff character
call send_xon
set_rb_done:

xit_rec_full:
ret

page
;***********************************************************
;** Subroutine to send an xon char if needed **
;***********************************************************
send_xon:
cmp cs:byte ptr busy_hand,3 ; XON-XOFF busy handling?
jne xit_send_xon ; No, skip

mov bx,offset tqueue ; Queue char
mov dx,cs:word ptr tq_tail
dec dx
and dx,tran_limit ; wrap if >= receive size
add bx,dx
mov cs:byte ptr [bx],al ; put the character in the queue
mov cs:word ptr tq_tail,dx
inc cs:word ptr tq_len ; Adjust queue length

cmp cs:byte ptr xmit_busy,0 ; is transmitter busy?
jne xit_send_xon

send_xon_1:
call xmit_mt ; force first character out

xit_send_xon:
ret

page
;***********************************************************
;** Subroutine to read the line status **
;***********************************************************
lin_stat:
mov dx,line_status
in al,dx ; read status
mov cs:byte ptr l_stat,al ; Update current line status

ret

page
;***********************************************************
;** Subroutine to get an interrupt vector **
;** **
;** di = vector number **
;** **
;** Return: **
;** bx = segment **
;** dx = offset **
;***********************************************************
get_vector:
push es
xor ax,ax
mov es,ax
shl di,1
shl di,1
mov dx,es:word ptr[di]
mov bx,es:word ptr[di+2]
pop es
ret

page
;***********************************************************
;** Subroutine to set an interrupt vector **
;** **
;** di = vector number **
;** bx = segment **
;** dx = offset **
;***********************************************************
set_vector:
push es
xor ax,ax
mov es,ax
shl di,1
shl di,1
mov es:word ptr[di],dx
mov es:word ptr[di+2],bx
pop es
ret

page
;***********************************************************
;** Everything past here is truncated after install **
;***********************************************************

init proc near
lds bx,cs:[ptrsav]
mov word ptr [bx].trans,offset init ;set break address
mov [bx].trans+2,cs

push cs
pop ds
mov dx,offset init_msg
mov ah,9
int 21h


jmp exit
init endp

init_msg:
db cr,lf,'IBM PC Communications Driver v3.00'
db ' Copyright (C) Texas Instruments 1986, 1987',cr,lf
db ' Copyright (C) Fein-Marquart Associates, 1987'
db cr,lf,cr,lf,'$'
code ends
end


  3 Responses to “Category : Assembly Language Source Code
Archive   : IBMRS232.ZIP
Filename : IBMRS232.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/