home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Columbia Kermit
/
kermit.zip
/
archives
/
packetdrivers.tar.gz
/
pd.tar
/
src
/
pi.asm
< prev
next >
Wrap
Assembly Source File
|
1995-06-25
|
35KB
|
1,507 lines
; PC/FTP Packet Driver source, conforming to version 1.09 of the spec,
; for the Ottawa PI card.
; Dave Perry, VE3IFB, October 16, 1991
; Portions (C) Copyright 1991, 1992 David G Perry
;
; Copyright, 1988, 1989, 1990, 1991 Russell Nelson
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, version 1.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
; Change history:
; October 16, 1991 (dp) - Creation
; January 8, 1992 (dp) - Fixed bug in set_acc_delay (rewritten).
; - Improved high speed performance by decreasing the amount
; of time DMA is disabled in scc routines.
; - Added new options to command line to allow more clocking
; options (see documentation file).
; - Added transmit buffer queue.
; - Added command line option to specify number and size of
; DMA buffers.
; - Optional support for "pseudo" ethernet class
; - Fixed bug in internal clocking mode
include 8530inc.asm
include piinc.asm
ETHER equ 1 ; This driver will "pretend" the PI card is an ethernet
; card. I have used this method to run QVT/NET
; using back-to-back PI cards between
; two PCs. Do not use this feature on a radio link,
; as no call signs would be transmitted.
; To use it, reassemble with MYCLASS equ ETHER
; and etheraddr = desired ethernet address
AX25 equ 9 ; Driver class
MYCLASS equ AX25
PITYPE equ 1 ; Driver type
; transmitter states
IDLE equ 0 ; Transmitter off
TXDELAY equ 1 ; Sending leading flags
ACTIVE equ 2 ; Transmitter on, sending data
UNDERRUN equ 3 ; Transmitter on, flushing CRC
FLAGOUT equ 4 ; CRC sent - attempt to start next frame
DEFER equ 5 ; DCD Active - DEFER Transmit
CRCOUT equ 6 ; Waiting for CRC bytes to clear the fifo
DEFAULTBUFSIZE equ 2048
version equ 2
include defs.asm
code segment word public
assume cs:code, ds:code
etheraddr db 00h,00h,00h,00h,00h,00h
tstate db 0
; Short pointers to DMA buffers.
rxbufptr dw 0 ; Current rx buffer
altbufptr dw 0 ; Alternate receive buffer
bufptr1 dw 0 ; DMA approved buffer
bufptr2 dw 0 ; DMA approved buffer
txbufptr dw 0 ; Current tx buffer
freelist dw 0 ; Head of free list
txqueue dw 0 ; Head of transmit queue
txlength dw 0 ; Save area for length of incoming packet
public acc_delay
acc_delay dw 0,0
diff dw 0,0
tick1 dw 0,0
tick2 dw 0,0
tx_tc dw 0,0 ; Time constant for baud rate generator (transmit)
rx_tc dw 0,0 ; Time constant for baud rate generator (receive)
random db 0 ; Pseudo random number
; The following values may be overridden from the command line.
; If they are omitted from the command line, these defaults are used.
; All of them occupy 4 bytes to satisfy the call to get_number.
public int_no, io_addr, dma_channel
int_no db 7,0,0,0 ; HW Interrupt
io_addr dw 0380h,0 ; I/O address for card (jumpers)
dma_channel db 1,0,0,0 ; DMA channel for card (jumpers)
speed dw 0,0 ; Baud rate (0 for external clock)
txdelayparm dw 15,0 ; TX delay (length of flags before data)
persist dw 128,0 ; P - persistance probability out of 256
slottime dw 10,0 ; Slot time for backoff
tailtime dw 1,0 ; Depends on baud rate
clkmode dw 0,0 ; Clocking mode
bufsiz dw DEFAULTBUFSIZE,0; Buffer size
numbufs dw 5,0 ; Number of buffers
; The following 3 values are calculated at initialization and depend
; on which DMA channel has been selected
page_addr dw 83h ; To be calculated from dma channel
dma_dest dw 2 ; Defaults are for channel 1
dma_wcr dw 3
public driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class db MYCLASS,0 ; null terminated list of classes.(ax25)
driver_type db PITYPE ;Assigned by FTP Software Inc.
driver_name db 'pi',0 ;name of the driver.
driver_function db 1 ;Only basic functionality
parameter_list label byte
db 1 ;major rev of packet driver
db 9 ;minor rev of packet driver
db 14 ;length of parameter list
db 0 ;length of MAC-layer address
parm_mtu label word
dw DEFAULTBUFSIZE ; MTU, including MAC headers
dw MAX_MULTICAST * EADDR_LEN ;buffer size of multicast addrs
dw 0 ;(# of back-to-back MTU rcvs) - 1
dw 0 ;(# of successive xmits) - 1
int_num dw 0 ;Interrupt # to hook for post-EOI
;processing, 0 == none,
public rcv_modes
rcv_modes dw 4 ;number of receive modes in our table.
dw 0,0,0,rcv_mode_3
; Switch receive buffers so we can quickly set up DMA again
public switchbuffers
switchbuffers:
mov ax,rxbufptr
cmp ax,bufptr1
jnz sw_1
mov ax,bufptr2
mov rxbufptr,ax
mov ax,bufptr1
mov altbufptr,ax
ret
sw_1:
mov ax,bufptr1
mov rxbufptr,ax
mov ax,bufptr2
mov altbufptr,ax
ret
public bad_command_intercept
bad_command_intercept:
;called with ah=command, unknown to the skeleton.
;exit with nc if okay, cy, dh=error if not.
mov dh,BAD_COMMAND
stc
ret
public as_send_pkt
; The Asynchronous Transmit Packet routine.
; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
; interrupts possibly enabled.
; Exit with nc if ok, or else cy if error, dh set to error number.
; es:di and interrupt enable flag preserved on exit.
as_send_pkt:
ret
public drop_pkt
; Drop a packet from the queue.
; Enter with es:di -> iocb.
drop_pkt:
assume ds:nothing
ret
public xmit
; Process a transmit interrupt with the least possible latency to achieve
; back-to-back packet transmissions.
; May only use ax and dx.
xmit:
assume ds:nothing
ret
; Append buffer pointed to by ax to queue specified by bx
public enqueue
enqueue:
pushf
cli
push si
push bp
mov si,cs:bufsiz
mov bp,ax
mov word ptr cs:[bp+si],0 ; Make sure next pointer is null
cmp word ptr cs:[bx],0 ; Is list empty?
jne en1 ; No - find the end
mov cs:[bx],ax ; Yes - add it
pop bp
pop si
popf
ret
en1: mov bp,word ptr cs:[bx] ; get pointer to first element
en2:
cmp word ptr cs:[bp+si],0 ; Is next pointer null?
je en3 ; Yes, append our buffer
mov bp,cs:[bp+si] ; No - advance to next element
jmp en2
en3: mov cs:[bp+si],ax
pop bp
pop si
popf
ret
public dequeue
dequeue:
pushf
cli
push si
mov si,cs:bufsiz
cmp word ptr cs:[bx],0 ; Is list empty?
jne deq1
xor ax,ax ; Yes, return error
pop si
popf
stc
ret
deq1:
push bp
mov bp,cs:[bx] ; No, dequeue buffer
mov ax,cs:[bp+si]
mov cs:[bx],ax
mov ax,bp
pop bp
pop si
popf
clc
ret
; Check to see if we should transmit or DEFER
; Return: Carry set - DEFER
public check_dcd
check_dcd:
; Check DCD - see if we should DEFER transmission
cmp tstate,FLAGOUT
je go ; The transmitter is already on - don't defer
mov bx,R0+RES_EXT_INT
call wrtscc
mov bx,R0+RES_EXT_INT
call wrtscc
mov bx,R0
call rdscc
test al,DCD
jne check_dcd_1 ; Carrier detected - we have to DEFER
mov al,21 ; Generate pseudo random number
mul cs:random
add ax,53
mov cs:random,al ; Save it for next one in sequence
xor ah,ah
cmp ax,cs:persist ; Should we DEFER?
jle go ; No
mov cs:tstate,DEFER ; We have to wait
mov ax,cs:slottime ; Yes - DEFER 1 slot time
call tdelay
stc ; We are DEFERring
ret
go: clc ; We don't have to wait
ret
check_dcd_1:
mov cs:tstate,DEFER ; We have to wait
mov ax,1000 ; In case DCD int. missed (shouldn't happen)
call tdelay
; Defer until dcd transition or 1S timeout or abort
mov bx,R15+CTSIE+DCDIE+BRKIE
call wrtscc
stc ; We are DEFERring
ret
public send_pkt
send_pkt:
;enter with es:di->upcall routine, (0:0) if no upcall is desired.
; (only if the high-performance bit is set in driver_function)
;enter with ds:si -> packet, cx = packet length.
;if we're a high-performance driver, es:di -> upcall.
;exit with nc if ok, or else cy if error, dh set to error number.
assume ds:nothing
; Copy packet into local DMA approved buffer
; Enqueue it for transmission. If the queue is full, return error.
; If the transmitter is not active and the channel is free, start
; transmission, else defer. Return as soon as buffer can be released.
pushf
cli
mov ax,cs ; Point es at the code segment
mov es,ax
mov bx,offset freelist ; Get a buffer from the freelist
call dequeue
jc send_error ; None left
mov di,ax ; Use newly aquired DMA buffer
push bp
push si
mov bp,ax
mov si,cs:bufsiz
mov cs:[bp+si+2],cx ; Tack on length
pop si
pop bp
cld
rep movsb ; Copy packet
mov bx,offset txqueue ; Append it to the tx queue
call enqueue
mov al,cs:tstate ; Is transmitter idle?
cmp al,IDLE
je send_pkt_1 ; Yes - start transmitting
popf ; Else return without error
clc
ret
send_error:
popf
stc ; Return error
ret
send_pkt_1:
cli
mov bx,offset txqueue; Get a buffer from the txqueue
call dequeue
jc none_to_send ; None left
mov cs:txbufptr,ax ; Make it the active tx buffer
push bp
push si
mov bp,ax
mov si,cs:bufsiz
mov cx,cs:[bp+si+2] ; Get length
pop si
pop bp
mov cs:txlength,cx
; Check DCD - see if we should DEFER transmission
call check_dcd
jc send_pkt_exit ; DEFER
mov ax,cs ; Set up for TX DMA
mov cx,ax
mov bx,cs:txbufptr
mov ax,cs:txlength
call setup_tx_dma
mov cs:tstate,TXDELAY
call tx_on ; Start sending flags
mov ax,cs:txdelayparm ; generate an exint after TXDELAY
call tdelay
send_pkt_exit:
popf
clc ; No error
ret
none_to_send:
call tx_off
mov cs:tstate,IDLE
popf
clc ; No error
ret
public set_address
set_address:
;enter with ds:si -> Ethernet address, CX = length of address.
;exit with nc if okay, or cy, dh=error if any errors.
assume ds:nothing
ret
rcv_mode_3:
;receive mode 3 is the only one we support, so we don't have to do anything.
ret
public set_multicast_list
set_multicast_list:
;enter with ds:si ->list of multicast addresses, cx = number of addresses.
;return nc if we set all of them, or cy,dh=error if we didn't.
mov dh,NO_MULTICAST
stc
ret
public terminate
terminate:
ret
public reset_interface
reset_interface:
;reset the interface.
assume ds:code
ret
;called when we want to determine what to do with a received packet.
;enter with cx = packet length, es:di -> packet type, dl = packet class.
extrn recv_find: near
;called after we have copied the packet into the buffer.
;enter with ds:si ->the packet, cx = length of the packet.
extrn recv_copy: near
;call this routine to schedule a subroutine that gets run after the
;recv_isr. This is done by stuffing routine's address in place
;of the recv_isr iret's address. This routine should push the flags when it
;is entered, and should jump to recv_exiting_exit to leave.
;enter with ax = address of routine to run.
extrn schedule_exiting: near
;recv_exiting jumps here to exit, after pushing the flags.
extrn recv_exiting_exit: near
;enter with dx = amount of memory desired.
;exit with nc, dx -> that memory, or cy if there isn't enough memory.
extrn malloc: near
extrn count_in_err: near
extrn count_out_err: near
wcrsave dw 0
rsesave db 0
public rxint
rxint:
assume ds:code
mov bx,R1 ; Get special condition bits from R1
call rdscc
mov rsesave,al
; save length of frame from 8237 */
xor al,al
out DMA_RESETFF,al ; Reset byte pointer flipflop
mov dx,dma_wcr ; Get address of word count register
in al,dx ; Input low byte of word
mov bl,al ; Save it
in al,dx ; Input high byte
mov bh,al ; Save it - bx = bytes left
mov wcrsave,bx
call switchbuffers ; Switch buffers so we can get DMA happening
; Error reset lets the receive fifo empty out using dma. There can be a few
; garbage bytes left in there so it is important to 'drain' them by
; issuing the error reset *before* we set up DMA for the next receive.
mov bx,R0+ERR_RES ; Error reset
call wrtscc
mov ax,cs ; Setup for next receive
mov cx,ax
mov bx,rxbufptr
mov ax,cs:bufsiz
call setup_rx_dma
mov al,rsesave ; Recover status
test al,END_FR ; Is this an end of packet int?
jz rxint_exit ; no
test al,CRC_ERR ; CRC ok?
jnz rxint_exit ; No - toss frame
mov bx,wcrsave ; Recover frame length
mov ax,cs:bufsiz ; ax = buf size - 1
sub ax,1
sub ax,bx ; minus bytes left = byte count
cmp ax,10 ; Runt?
jl rxint_exit ; Yes, ignore it
; Valid frame
sub ax,2 ; Subtract 2 CRC bytes from byte count
mov cx,ax ; Load packet length
mov dl,MYCLASS ; Load packet class
mov ax,cs ; Get our segment
mov es,ax ; into es
mov di,rxbufptr ; Point to type field (not needed for AX25)
add di,12
call recv_find ; Try to get a buffer to send it up in.
mov ax, es ; Did recv_find give us a null pointer?
or ax, di ; ..
je rxint_exit ; If null, we must throw this one away
cld ; Copies which follow are forward
push ds ; Save our data seg
push cx ; We will want the count and pointer
push es ; to hand to client after copying,
push di ; so save them at this point
mov si,altbufptr
rep movsb ; Do the copy
pop si ; Recover pointer to destination
pop ds ; Tell client it's his source
pop cx ; And it's this long
call recv_copy ; Give it to him
pop ds ; Recover our data seg
rxint_exit:
ret
txint:
assume ds:code
ret
exint:
assume ds:code
pushf
cli
mov bx,R0 ; Get status
call rdscc
; reset external status latch
mov bx,R0+RES_EXT_INT
call wrtscc
mov al,cs:tstate ; Is transmit IDLE?
cmp al,IDLE
jnz exint_1 ; No
mov bx,R0+ERR_RES ; TX IDLE - unlock fifo to flush any garbage
call wrtscc
mov ax,cs ; Assume abort - Reset receive DMA
mov cx,ax
mov bx,rxbufptr
mov ax,cs:bufsiz
call setup_rx_dma
jmp exint_exit ; exit
exint_1:
mov al,cs:tstate
cmp al,ACTIVE
jne exint_2
; Transmitter is ACTIVE
mov cs:tstate,FLAGOUT
mov ax,tailtime
call tdelay
jmp exint_exit
exint_2:
cmp al,FLAGOUT
jne exint_3
; Transmitter in FLAGOUT state
mov ax,cs:txbufptr ; Free the tx buffer
mov bx, offset freelist
call enqueue
jmp send_pkt_1 ; Go see if there are more to send
exint_3:
cmp al,TXDELAY
jne exint_4
; Transmitter is in TXDELAY
mov bx,R0+RES_Tx_CRC+RES_Tx_P ; reset CRC, txint pending
call wrtscc
mov bx,R15+TxUIE ; Allow underrun int only
call wrtscc
; Enable TX DMA
mov bx,R1+WT_RDY_ENAB+WT_FN_RDYFN+EXT_INT_ENAB
call wrtscc
; Unmask channel n (enable DMA controller chip)
mov al,Byte Ptr dma_channel ; Enable DMA for this channel
add al,DMA_ENABLE
out DMA_MASK,al
mov bx,R0+RES_EOM_L ; Send CRC on underrun
call wrtscc
mov cs:tstate,ACTIVE ; Packet going out now
jmp exint_exit
exint_4:
; Transmitter must be in DEFER
call check_dcd ; See if we should DEFER again
jc exint_exit ; DEFER
mov ax,cs ; Set up for TX DMA
mov cx,ax
mov bx,txbufptr
mov ax,txlength
call setup_tx_dma
mov cs:tstate,TXDELAY
call tx_on ; Start sending flags
mov ax,txdelayparm ; generate an exint after TXDELAY
call tdelay
popf
ret
exint_exit:
popf
ret
public recv
recv:
;called from the recv isr. All registers have been saved, and ds=cs.
;Upon exit, the interrupt will be acknowledged.
assume ds:code
; Read interrupt status register from channel A
; Process all pending interrupts in a loop
recv_1:
mov bx,R3
call rdscc
cmp al,0
jz recv_exit
test al,CHARxIP ; Is channel A receive int pending?
jz recv_2 ; no
call rxint ; yes
jmp recv_eoi
recv_2: test al,CHATxIP ; Is channel A tx int pending?
jz recv_3 ; no
call txint ; yes
jmp recv_eoi
recv_3: test al,CHAEXT ; Is channel external/status int pending?
jz recv_eoi ; no
call exint ; yes
; Reset highest interrupt under service
recv_eoi:
mov bx,R0+RES_H_IUS
call wrtscc
jmp recv_1 ; Loop for more int processing
recv_exit:
ret
; This routine is used for all writes to the SCC. It satisfies the
; The SCCs access time restriction by using delay loops. These loops
; are processor speed dependent and are determined by acc_delay.
; Enter with bh = register, bl = value
public wrtscc
wrtscc:
pushf ; Save current interrupt state
cli ; Wouldn't do to be interrupted now
push ax
push cx
push dx
push di
push si
push bp
mov di,acc_delay; Load this into a register for speed
mov dx,io_addr ; Get card address
add dx,CTL_A ; Point to channel A control reg
mov bp,dx ; Save it
mov dx,io_addr ; get address of dma enable port
add dx,DMAEN
mov si,dx ; save it
mov cx,di ; For loop below
mov ax,100h ; Write a 0 to disable DMA while we touch the scc
out dx,al
; ******** DMA off
Loop1: loop Loop1 ;[5]
mov dx,bp ;[2] Get pointer to ch A control reg
mov al,bh ;[2] Select register
out dx,al ;[8]
mov cx,di ;[2]
Loop2: loop Loop2 ;[5]
mov dx,si ;[2] get address of dma enable port
mov al,ah ;[2] Enable DMA in between accesses
out dx,al ;[8]
; ******** DMA on - was off for 36 cycles
nop ; Leave extra time for XT ram refresh cycle to finish
nop
nop
mov cx,di ; For loop below
mov al,0 ; now disable DMA in between accesses
out dx,al ;
; ******** DMA off
Loop2a: loop Loop2a ;[5]
mov dx,bp ;[2] Get pointer to ch A control reg
mov al,bl ;[2] Output value
out dx,al ;[8]
mov cx,di ;[2]
Loop3: loop Loop3 ;[5]
mov dx,si ;[2] get address of dma enable port
mov al,ah ;[2] Enable DMA
out dx,al ;[8]
; ******** DMA on - was off for 36 cycles
pop bp
pop si
pop di
pop dx
pop cx
pop ax
popf
ret
; Enter with bh = reg
public rdscc
rdscc:
pushf
cli
push cx
push dx
push di
push si
push bp
mov di,acc_delay; Save it
mov dx,io_addr ; Get card address
add dx,CTL_A ; Point to channel A control reg
mov bp,dx ; Save it
mov dx,io_addr ; get address of dma enable port
add dx,DMAEN
mov si,dx ; save it
mov cx,di ; For loop below
mov ax,100h ; Disable DMA while we touch the scc
out dx,al
; ******** DMA off
Loop4: loop Loop4 ;[5]
mov dx,bp ;[2] Get address of SCC control reg
mov al,bh ;[2] Select register
out dx,al; ;[8]
mov cx,di ;[2]
Loop5: loop Loop5 ;[5]
mov dx,si ;[2] get address of dma enable port
mov al,ah ;[2] Enable DMA
out dx,al ;[8]
;********* DMA on - was off for 36 cycles
nop ; Leave extra time for XT ram refresh cycle to finish
nop
nop
mov cx,di ; For loop below
mov al,0
out dx,al
;********* DMA off
Loop5a: loop Loop5a ;[5]
mov dx,bp ;[2] Get address of SCC control reg
in al,dx ;[8] read register
mov bl,al ;[2] save return value
mov cx,di ;[2]
Loop6: loop Loop6 ;[5]
mov dx,si ;[2] get address of dma enable port
mov al,ah ;[2] Enable DMA
out dx,al ;[8]
; ******** DMA on - was off for 36 cycles
mov al,bl ; recover return value
pop bp
pop si
pop di
pop dx
pop cx
popf
ret
public delay8253
delay8253:
nop
nop
nop
nop
nop
ret
; Setup for DMA
; Initializes certain DMA chip registers - called by setup_rx_dma
; and setup_tx_dma
; Enter with cx:bx = buffer, ax = length
; Writes the dma chip word count reg, dest reg, and the dma page reg
;
public setup_dma
setup_dma:
pushf ; Save interrupt state
cli ; Disable interrupts
sub ax,1 ; adjust length for DMA chip
push ax ; Save it for later
mov al,Byte Ptr dma_channel ; Disable DMA for this channel
add al,DMA_DISABLE
out DMA_MASK,al
xor al,al
out DMA_RESETFF,al ; Reset byte pointer flipflop
; Calculate high order 4 bits of the buffer area and store
; them in the DMA page register
mov ax,cx ; Make a working copy of the seg reg
shl ax,1
shl ax,1
shl ax,1
shl ax,1
add ax,bx ; add offset - we have bottom word of abs. address
pushf ; Save carry bit
mov dx,dma_dest ; Output buf start (source) address
out dx,al
mov al,ah
out dx,al
mov ax,cx ; Get another copy of the seg reg
mov cl,12 ; shift it down
shr ax,cl ; to get page register
popf ; Recover carry bit
adc ax,0 ; add any carry from previous add
mov dx,page_addr ; Set up DMA page register
out dx,al
mov dx,dma_wcr ; Get address of word count register
pop ax ; Recover length and output it
out dx,al
mov al,ah
out dx,al
popf ; restore interrupt state
ret
; Setup receive dma
; Enter with cx:bx = buffer, ax = length
;
public setup_rx_dma
setup_rx_dma:
pushf ; Save interrupt state
cli ; Disable interrupts
call setup_dma
; Get ready for RX DMA
mov bx,R1+WT_FN_RDYFN+WT_RDY_RT+INT_ERR_Rx+EXT_INT_ENAB
call wrtscc
; Set DMA mode register to single transfers, incrementing address,
; auto init, writes
mov al,dma_channel ; Put dma chip in transmit for this channel
add al,DMA_RX_MODE
out DMA_MODE,al
; Unmask channel n (enable DMA controller chip)
mov al,Byte Ptr dma_channel ; Enable DMA for this channel
add al,DMA_ENABLE
out DMA_MASK,al
; If a packet is already coming in, this line is supposed
; to mess up the crc to avoid receiving a partial packet
mov bx,R0+RES_Rx_CRC
call wrtscc
; Enable RX dma in SCC chip
mov bx,R1+WT_RDY_ENAB+WT_FN_RDYFN+WT_RDY_RT+INT_ERR_Rx+EXT_INT_ENAB
call wrtscc
popf
ret
;
; Set up for transmit DMA
; Enter with cx:bx = buffer, ax = length
;
public setup_tx_dma
setup_tx_dma:
pushf ; Save interrupt state
cli ; Disable interrupts
call setup_dma
; Set DMA mode register to single transfers, incrementing address,
; no auto init, reads
mov al,dma_channel ; Put dma chip in transmit for this channel
add al,DMA_TX_MODE
out DMA_MODE,al
popf
ret
; Set up 8253 chip for time delay
; enter with time to delay (mS) in ax
;
public tdelay
tdelay:
push bx
push cx
push dx
push ax ; Save delay time
mov dx,io_addr
add dx,TMRCMD
mov al,SC1+LSB_MSB+MODE0 ; Setup timer sc
out dx,al
call delay8253 ; Satisy access time restriction
mov dx,io_addr
add dx,TMR1
pop ax ; Recover delay time
sal ax,1 ; Times 2 to make milliseconds
out dx,al ; Write low byte
call delay8253
mov al,ah
out dx,al ; then high byte
mov bx,R15+CTSIE ; Enable interrupt for timeout
call wrtscc
mov bx,R1+EXT_INT_ENAB
call wrtscc
mov bx,R0+RES_EXT_INT
call wrtscc
pop dx
pop cx
pop bx
ret
public tx_off
tx_off:
pushf
cli
push bx
mov bx,R5+Tx8+DTR ; TX off
call wrtscc
mov ax,speed ; Internally clocked?
cmp ax,0
jz tx_off_1 ; Externally clocked, don't change BRG
mov ax,clkmode ; Clocking mode 1?
cmp ax,1
jz tx_search ; Mode 1, don't change BRG
; But enter search mode
; Reprogram BRG for 32x clock for receive DPLL
mov bx,R14+BRSRC ; BRG off, keep Pclk source
call wrtscc
mov ax,rx_tc ; Get receive time constant for BRG
mov bx,R12 ; Write low byte of time constant to R12
mov bl,al
call wrtscc
mov ax,rx_tc ; Get receive time constant for BRG
mov bx,R13 ; Write high byte of time constant to R13
mov bl,ah
call wrtscc
tx_search:
mov bx,R14+BRSRC+SEARCH ; SEARCH mode, BRG source
call wrtscc
mov bx,R14+BRSRC+BRENABL ; Enable the baud rate generator
call wrtscc
tx_off_1:
mov bx,R3+RxENABLE+RxCRC_ENAB+Rx8 ; RX on
call wrtscc
push cs ; Set up RX DMA
pop cx
mov bx,rxbufptr
mov ax,cs:bufsiz
call setup_rx_dma
mov bx,R15+BRKIE
call wrtscc ; allow abort interrupt
pop bx
popf
ret
public tx_on
tx_on:
pushf
cli
push bx
mov bx,R15+0 ; Exints off first to avoid abort interrupt
call wrtscc
mov bx,R3+Rx8 ; Rx off
call wrtscc
mov bx,R1+WT_FN_RDYFN+EXT_INT_ENAB ; Set up for TX DMA
call wrtscc
mov ax,speed ; Internally clocked?
cmp ax,0
jz tx_on_1 ; Externally clocked, don't change BRG
mov ax,clkmode ; Clocking mode 1?
cmp ax,1
jz tx_on_1 ; Clocking mode 1, don't change BRG
mov ax,tx_tc ; Get transmit time constant for BRG
mov bx,R12 ; Write low byte of time constant to R12
mov bl,al
call wrtscc
mov ax,tx_tc ; Get transmit time constant for BRG
mov bx,R13 ; Write high byte of time constant to R13
mov bl,ah
call wrtscc
tx_on_1:
mov bx,R5+TxCRC_ENAB+RTS+TxENAB+Tx8+DTR
call wrtscc
pop bx
popf
ret
include popf.asm
public timer_isr
timer_isr:
;if the first instruction is an iret, then the timer is not hooked
iret
;any code after this will not be kept. Buffers used by the program, if any,
;are allocated from the memory between end_resident and end_free_mem.
public end_resident,end_free_mem
end_resident label byte
db 30000 dup(?)
end_free_mem label byte
; Initialize the SCC registers
public scc_init
scc_init:
pushf
cli
mov cs:tstate,IDLE
mov bx,R9+CHRA ; reset channel A
call wrtscc
mov bx,R2+0ffh ; Set interrupt vector
call wrtscc
mov bx,R1+0 ; Deselect all Rx and Tx interrupts
call wrtscc
mov bx,R15+0 ; Turn off external interrupts
call wrtscc
mov bx,R4+SDLC+X1CLK ; SDLC mode and times 1 clock
call wrtscc
mov ax,speed ; Get baud rate
cmp ax,0
jz scc_init_1 ; Jump if externally clocked
mov ax,clkmode ; Clocking mode 1?
cmp ax,1
jnz scc_init_5 ; No - must be internal clocking
mov bx,R10+CRCPS ; Clock mode 1, CRC preset, not NRZI mode
call wrtscc
; Receive clock = RTxC pin
; Transmit clock = baud rate generator
; Transmit clock is output on TRxC pin
mov bx,R11+TCBR+RCRTxCP+TRxCOI+TRxCTC
call wrtscc
jmp scc_init_2
scc_init_5:
mov bx,R10+CRCPS+NRZI ; Internal clock, CRC preset, NRZI mode
call wrtscc
; Receive clock = DPLL
; Transmit clock = baud rate generator
; Transmit clock is output on TRxC pin
mov bx,R11+TCBR+RCDPLL+TRxCOI+TRxCTC
call wrtscc
jmp scc_init_2
scc_init_1: ; Externally clocked
mov bx,R10+CRCPS ; CRC preset
call wrtscc
; Receive clock = RTxC pin
; TRxC pin is input for transmit clock
mov bx,R11+TCTRxCP ; Rcv clk is from Rtxcl, TRxC pin is input
call wrtscc
scc_init_2:
mov bx,R6+0 ; Null out SDLC start address
call wrtscc
mov bx,R7+FLAG ; SDLC flag
call wrtscc
mov bx,R5+Tx8+DTR ; Set up tx but don't enable it yet
call wrtscc
mov bx,R3+Rx8 ; Initial rx setup
call wrtscc
mov bx,R14+BRSRC
call wrtscc
mov ax,rx_tc ; Get receive time constant for BRG
mov bx,R12 ; Write low byte of time constant to R12
mov bl,al
call wrtscc
mov ax,rx_tc ; Get receive time constant for BRG
mov bx,R13 ; Write high byte of time constant to R13
mov bl,ah
call wrtscc
mov ax,speed ; Internal clocking?
cmp ax,0
jz scc_init_3 ; Jump if External
mov ax,clkmode
cmp ax,1
jnz t1
mov bx,R14+BRSRC+SSRTxC+BRENABL
call wrtscc
jmp scc_init_4
t1:
mov bx,R14+BRSRC+SSBR+BRENABL
call wrtscc
jmp scc_init_4
scc_init_3:
mov bx,R14+BRSRC+SSRTxC ; Set DPLL source = RTxC
call wrtscc
scc_init_4:
call tx_off ; Set up and enable RX
popf
ret
; This routine calculates the constant to be used in the delay loops
; which satisfy the SCC's access recovery time. This needs to be timed and
; calculated because a fixed value would not work in a 4.77mhz XT
; to a 40mhz 486 (and beyond).
public set_acc_delay
set_acc_delay:
pushf
cli
push ax
push bx
push cx
push dx
mov cx,1000
mov al,0 ;latch counter zero.
out 43h,al
in al,40h ;read counter zero.
mov ah,al
in al,40h
xchg ah,al
mov tick1,ax
h1: loop h1
mov al,0 ;latch counter zero.
out 43h,al
in al,40h ;read counter zero.
mov ah,al
in al,40h
xchg ah,al
mov tick2,ax
mov ax,tick1
sub ax,tick2
mov diff,ax ; Elapsed counts
mov bx,ax
mov dx,0
mov ax,5000 ; Divide by
div bx ; the number of counts elapsed
cmp al,0
jne set_acc_2
mov al,1 ; Delay constant must be at least 1
set_acc_2:
xor ah,ah
mov acc_delay,ax
mov di, offset acc_delay ; Processor speed constant
mov dx, offset acc_delay_name ; Message for it
call print_number
pop dx
pop cx
pop bx
pop ax
popf
ret
public usage_msg
usage_msg db "usage: pi <packet_int_no> [hardware_irq] [io_addr] [dma] [baud] [TXD] [P] [slot] [tail] [clock mode] [buf size] [number of buffers]",CR,LF,'$'
public copyright_msg
copyright_msg db "Packet driver for the Ottawa PI card, version ",'0'+majver,".",'0'+version,CR,LF
db "Portions Copyright 1991, 1992 Dave Perry",CR,LF,'$'
int_no_name db "Interrupt number ",'$'
io_addr_name db "I/O port ",'$'
dma_name db "DMA channel ",'$'
acc_delay_name db "Processor constant ",'$'
diff_name db "diff ",'$'
tick1_name db "tick1 ",'$'
tick2_name db "tick2 ",'$'
speed_name db "Baud rate ",'$'
txdelay_name db "TX delay ",'$'
persist_name db "P value ",'$'
slottime_name db "Slot time ",'$'
tailtime_name db "Tail time ",'$'
clkmode_name db "Clocking mode ",'$'
bufsiz_name db "Buffer size ",'$'
numbufs_name db "Number of buffers ",'$'
baud_err_msg db "Error: Selected baud rate is out of range",CR,LF,'$'
no_memory_msg db "Unable to allocate enough memory, look at end_resident in PI.ASM",CR,LF,'$'
extrn set_recv_isr: near
;enter with si -> argument string, di -> wword to store.
;if there is no number, don't change the number.
extrn get_number: near
;enter with dx -> argument string, di -> wword to print.
extrn print_number: near
;-> the assigned Ethernet address of the card.
extrn rom_address: byte
public parse_args
parse_args:
;exit with nc if all went well, cy otherwise.
mov di,offset int_no ; May override interrupt channel
call get_number
mov di,offset io_addr ; May override I/O address
call get_number
mov di,offset dma_channel ; May override channel
call get_number
mov di,offset speed ; May override speed
call get_number
mov di,offset txdelayparm ; May override TX delay time
call get_number
mov di,offset persist ; May override P value
call get_number
mov di,offset slottime ; May override Slot time
call get_number
; Calculate address of dma page register
mov al,Byte Ptr dma_channel
cmp al,2
jne parse_1
mov ax,81h
jmp parse_3
parse_1:
cmp al,3
jne parse_2
mov ax,82h
jmp parse_3
parse_2:
mov ax,83h
parse_3:
mov page_addr,ax
; Calculate address of dma destination and dma word count regs
mov ax,Word Ptr dma_channel
shl ax,1
mov dma_dest,ax
add ax,1
mov dma_wcr,ax
; Calculate time constants for baud rate generator
mov ax,word ptr speed
cmp ax,0
jz parse_4
mov dx,1ch ; Load SCC clock freq into dx:ax
mov ax,2000h
div speed ; Divide by baud rate
sub ax,2 ; sub 2 - this is the BRG time constant
mov tx_tc,ax ; for Transmit - save it
mov dx,0h ; Load SCC clock freq/32 into dx:ax
mov ax,0e100h
div speed ; Divide by baud rate
sub ax,2 ; sub 2 - this is the BRG time constant
mov rx_tc,ax ; for receive - save it
; Calculate tail time. This is the time required for the CRC and closing
; flag to be sent, during which the transmitter must be held on.
mov dx,0 ; Magic number
mov ax,8ca0h
div speed
add ax,1 ; Tail time is at least 1 ms
mov tailtime,ax
parse_4:
mov di,offset tailtime ; May override Tail time
call get_number
mov di,offset clkmode ; May override clocking mode
call get_number
mov di,offset bufsiz ; May override buffer size
call get_number
mov ax,bufsiz
mov parm_mtu,ax
mov di,offset numbufs ; May override number of buffers
call get_number
cmp numbufs,3 ; But we need at least 3
jge parse_4a
mov numbufs,3
parse_4a:
clc ; Return without error
ret
baud_error:
mov dx,offset baud_err_msg
mov ah,9
int 21h
stc
ret
; Return offset of a buffer which does not cross a DMA page boundary.
; Returns nc, ax=offset of buffer, or cy if no mem. Buffers are in code segment.
public getbuffer
getbuffer:
push dx
push cx
push bx
mov ax,cs ; get buffer segment
mov cx,ax
get1: mov ax,cs:bufsiz
add ax,4 ; Leave room for next pointer and length
mov dx,ax
call malloc
jc get_exit
mov bx,dx
call test_buffer
jnc get_exit ; found one
add numbufs,1 ; Account for it
pop bx
pop cx
pop dx
jmp getbuffer
get_exit:
mov ax,dx ; recover pointer
pop bx
pop cx
pop dx
ret ; return buffer found
public etopen
etopen:
; Not meaningful for AX25 class - this code assumes DIX ethernet
;get the address of the interface.
mov si,offset etheraddr
movseg es,ds
mov di,offset rom_address
mov cx,EADDR_LEN
rep movsb
; Set up DMA buffer pool
mov cx,numbufs ; We want this many
et1:
call getbuffer ; Get a DMA buffer
jc etopen_error
mov bx,offset freelist ; Append it to the free list
call enqueue
loop et1
mov bx,offset freelist ; Get a buffer from the freelist
call dequeue
jc etopen_error ; None left
mov bufptr1,ax ; Remember as primary
mov rxbufptr,ax ; Make it current
mov bx,offset freelist ; Get a buffer from the freelist
call dequeue
jc etopen_error ; None left
mov bufptr2,ax ; Use it for secondary rx buffer
; Adjust scc delay for speed of host cpu
call set_acc_delay
mov bx,R9+FHWRES ; Hardware reset SCC
call wrtscc
; Disable interrupts with Master interrupt ctrl reg */
mov bx,R9+0
call wrtscc
call scc_init
mov bx,R9+MIE+NV ; master interrupt enable
call wrtscc
; Set up counter chip
mov dx,io_addr
add dx,TMRCMD
mov al,SC0+LSB_MSB+MODE3 ; 500 uS square wave
out dx,al
call delay8253 ; Satisfy access time restriction
mov ax,922 ; time constant
mov dx,io_addr
add dx,TMR0
out dx,al ; LSB
call delay8253 ; Satisy access time restriction
mov al,ah
out dx,al ; MSB
call delay8253 ; Satisy access time restriction
call set_recv_isr ; Hook the board int
;if all is okay,
clc
ret
etopen_error:
mov dx,offset no_memory_msg
stc
ret
dma_page_save dw 0
dma_dest_save dw 0
dma_wcr_save dw 0
; Test a buffer to see if it crosses a DMA page boundary.
; Enter with cx:bx = buffer, ax = length
public test_buffer
test_buffer:
sub ax,1 ; adjust length for DMA chip
mov dma_wcr_save,ax ; Save it for the dma_wcr register
; Calculate high order 4 bits of the buffer area and store
; them in the DMA page register
mov ax,cx ; Make a working copy of the seg reg
shl ax,1
shl ax,1
shl ax,1
shl ax,1
add ax,bx ; add offset - we have bottom word of abs. address
pushf ; Save carry bit
mov dma_dest_save,ax ; save this for the dma_dest register
mov ax,cx ; Get another copy of the seg reg
mov cl,12 ; shift it down
shr ax,cl ; to get page register
popf ; Recover carry bit
adc ax,0 ; add any carry from previous add
mov dma_page_save,ax ; Save this for the dma_page register
; Now do the test: add the buffer size to the destination address
; and see if we're still in the same page
mov ax,dma_dest_save ; recover bottom 16 bits of absolute addr
add ax,cs:bufsiz ; add the buffer size
; if carry is set, we spilled over the page
ret ; return carry clear on success
public print_parameters
print_parameters:
;echo our command-line parameters
mov di, offset int_no ; Interrupt channel
mov dx, offset int_no_name ; Message for it
call print_number
mov di, offset io_addr ; I/O address
mov dx, offset io_addr_name ; Message for it
call print_number
mov di, offset dma_channel ; DMA channel
mov dx, offset dma_name ; Message for it
call print_number
mov di, offset speed ; Baud rate
mov dx, offset speed_name
call print_number
mov di, offset txdelayparm ; TX delay
mov dx, offset txdelay_name
call print_number
mov di, offset persist ; P persistance
mov dx, offset persist_name
call print_number
mov di, offset slottime ; Slot time
mov dx, offset slottime_name
call print_number
mov di, offset tailtime ; Tail time
mov dx, offset tailtime_name
call print_number
mov di, offset clkmode ; Clocking mode
mov dx, offset clkmode_name
call print_number
mov di, offset bufsiz ; Buffer size
mov dx, offset bufsiz_name
call print_number
mov di, offset numbufs ; Number of buffers
mov dx, offset numbufs_name
call print_number
ret
code ends
end