home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Stars of Shareware: Programmierung
/
SOURCE.mdf
/
programm
/
msdos
/
asm
/
drivers1
/
davidsys.asm
< prev
next >
Wrap
Assembly Source File
|
1992-01-23
|
49KB
|
1,698 lines
page 58, 132
;DBGINT3 equ 0
;DEBUG equ 1 ; FOR NOW
version equ 5 ; version of this file
s8005_version equ 1 ; SEEQ 8005 stuff (not separate (yet))
;version
; 1 = initially from Mark Dye
; 2 = Fix spin loop bug (cx=0 ->65536), broke on slower systems (6MHz)
; 3 = Fix '-n' bug, fix warnings
; 4 = Fix Compaq DOS bug (we must do STI after set_recv_isr)
; Also, compile with DBGINT3 commmented out.
; I thought that 0=false, 1=true, but they were IFDEFfed!
; Also, Fix section to ignore broadcasts that we initiated
; 5 = Release version (nothing changed)
;s8005_version
; 0 = Initially from MD
; 1 = Fix 8-bit insb bug (broke on 8088 PCs)
; Copyright 1991 DAVID Systems, Inc (Sunnyvale, CA) and Marc S Dye
; (d.b.a. ayuda Company - Camarillo, CA). Distribution of this code
; including any derivation thereof, shall be made so as to afford the
; recipient of such distribution a copy of this source code, including
; any alterations, at no charge (except for copying, media, and shipping
; costs). Any distribution of this source or derivations must contain
; this copyright notice. Documentation describing this work or its
; derivations must acknowledge the copyright holders herein. The names
; of the copyright holders may not be used to advertise, signify, or
; otherwise characterize any derived work, except for the aforementioned
; copyright acknowledgement. This work is provided AS IS, with NO
; WARRANTIES expressed or implied, NOR any representations made as to
; its suitability or fitness for any purpose whatsoever. The recipient
; assumes full responsibility for use of this work.
; Some patches made by:
; Jeff Douglass
ifdef DEBUG
private macro SYM
public SYM
endm
else
private macro SYM
endm
endif
include defs.asm
; Ethernet-2 header length defined
;#
ENET_HDR equ 2*EADDR_LEN + 4 ;was +2
; assemble a select few instructions (16-bit i/o) from the extended set
; otherwise, must do only 8086-compatible stuff!
.286p
; Definitions of interface to the SEEQ 8005 EDLC. See the SEEQ data book
; for complete details of this device.
; 8005 directly-addressable register offsets (from i/o base address)
COMSTAT_R equ 0 ; COMMAND / STATUS
CONFIG1_R equ 2 ; CONFIGURATION 1
CONFIG2_R equ 4 ; CONFIGURATION 2
REA_PTR_R equ 6 ; Receive End Area Pointer
BUFWIN_R equ 8 ; Buffer Window
RX_PTR_R equ 10 ; Receive Pointer
TX_PTR_R equ 12 ; Transmit Pointer
DMA_ADDR_R equ 14 ; DMA Address
; 8005 COMMAND register
DMA_INT_EN equ 0001h
RX_INT_EN equ 0002h
TX_INT_EN equ 0004h
WINDOW_INT_EN equ 0008h
DMA_INT_ACK equ 0010h
RX_INT_ACK equ 0020h
TX_INT_ACK equ 0040h
WINDOW_INT_ACK equ 0080h
SET_DMA_ON equ 0100h
SET_RX_ON equ 0200h
SET_TX_ON equ 0400h
SET_DMA_OFF equ 0800h
SET_RX_OFF equ 1000h
SET_TX_OFF equ 2000h
FIFO_READ equ 4000h
FIFO_WRITE equ 8000h
; 8005 STATUS register
DMA_INT equ 0010h
RX_INT equ 0020h
TX_INT equ 0040h
WINDOW_INT equ 0080h
DMA_ON equ 0100h
RX_ON equ 0200h
TX_ON equ 0400h
FIFO_FULL equ 2000h
FIFO_EMPTY equ 4000h
FIFO_DIR equ 8000h
; 8005 CONFIGURATION 1 register
MATCH_BITS_M equ 0C000h
MATCH_ONLY equ 0000h
MATCH_BROAD equ 4000h
MATCH_MULTI equ 8000h
MATCH_ALL equ 0C000h
ALL_STATIONS equ 3F00h
STATION_0_EN equ 0100h
STATION_1_EN equ 0200h
STATION_2_EN equ 0400h
STATION_3_EN equ 0800h
STATION_4_EN equ 1000h
STATION_5_EN equ 2000h
DMA_LENGTH_M equ 00C0h
NBYTES_1 equ 0000h
NBYTES_2 equ 0040h
NBYTES_4 equ 0080h
NBYTES_8 equ 00C0h
DMA_INTERVAL_M equ 0030h
CONTINUOUS equ 0000h
DELAY_800 equ 0010h
DELAY_1600 equ 0020h
DELAY_3200 equ 0030h
BUFFER_CODE_M equ 000Fh
STATION_0_SEL equ 0000h
STATION_1_SEL equ 0001h
STATION_2_SEL equ 0002h
STATION_3_SEL equ 0003h
STATION_4_SEL equ 0004h
STATION_5_SEL equ 0005h
PROM_SEL equ 0006h
TEA_SEL equ 0007h
BUFFER_MEM_SEL equ 0008h
INT_VECTOR_SEL equ 0009h
; Ether-T PC/AT device configuration register -- access is overloaded onto
; the set of registers accessible via the reserved range in CONFIGURATION 1
CARD_CONFIG_SEL equ 000Fh
SET_16BITMODE_M equ 0080h ; OR mask for 16 bit mode
; 8005 CONFIGURATION 2 register
BYTE_SWAP equ 0001h
AUTO_UPDATE_REA equ 0002h
CRC_ERR_EN equ 0008h
DRIBBLE_EN equ 0010h
SHORT_FRAME_EN equ 0020h
SLOT_TIME_SEL equ 0040h
XMIT_NO_PREAM equ 0080h
ADDR_LENGTH equ 0100h
RECEIVE_CRC equ 0200h
XMIT_NO_CRC equ 0400h
LOOPBACK_EN equ 0800h
KILL_WATCHDOG equ 1000h
RESET equ 8000h
; 8005 TRANSMIT HEADER COMMAND BYTE (byte 3 of transmit packet header)
BABBLE_INT_EN equ 01h
COLL_INT_EN equ 02h
COLL_16_INT_EN equ 04h
XMIT_OK_INT_EN equ 08h
DATA_FOLLOWS equ 20h ; also in RECEIVE HEADER COMMAND BYTE
CHAIN_CONTINUE equ 40h ; also in RECEIVE HEADER COMMAND BYTE
PACKET_PRESENT equ 80h
TX_HEADER_Z equ 4 ; # of bytes in a TX header
; 8005 TRANSMIT HEADER STATUS BYTE (byte 4 of transmit packet header)
BABBLE_ERR equ 01h
COLL equ 02h
COLL_16_ERR equ 04h
HDR_DONE equ 80h
; 8005 RECEIVE HEADER COMMAND BYTE (byte 3 of receive packet header)
;DATA_FOLLOWS 20h from TRANSMIT HEADER COMMAND BYTE
;CHAIN_CONTINUE 40h from TRANSMIT HEADER COMMAND BYTE
; 8005 RECEIVE HEADER STATUS BYTE (byte 4 of receive packet header)
OVERSIZE equ 01h
CRC_ERR equ 02h
DRIBBLE_ERR equ 04h
SHORT_FRAME equ 08h
; 8005 Receive Area Base Pointer
RCV_PTR equ ((4 + GIANT + 4 + 255) AND NOT 0FFh)
; 8005 Transmit End Area Pointer (upper 8-bits)
TEA_INIT equ (((RCV_PTR - 1) / 256) AND 0FFh)
; macro to provide a delay after dinking w/ 8005 registers
io_delay macro CNTVAR
local io_delay_loop, skip_delay
push cx
mov cx, CNTVAR
jcxz skip_delay
io_delay_loop: nop
loop io_delay_loop
skip_delay: pop cx
endm
; macros to get and set the various device registers
; these assume that a proper 'loadport' context exists, that input/output
; register values are in AX (for word i/o) or AL (for byte i/o)
get_r macro R
setport R
call inb_8005
endm
getw_r macro R
setport R
call [inw_fn]
endm
set_r macro R
setport R
call outb_8005
endm
setw_r macro R
setport R
call [outw_fn]
endm
; wait for the 8005 FIFO to be free
fifo_wait macro
local fifo_wait_loop
setport COMSTAT_R ; fix access within the loop
fifo_wait_loop: getw_r COMSTAT_R ; wait for the FIFO to be free to use
and ax, FIFO_EMPTY+FIFO_DIR
cmp ax, FIFO_EMPTY
jne fifo_wait_loop ; loop
endm
CODE segment byte public
assume cs:CODE, ds:CODE
public INT_NO, IO_ADDR
INT_NO db 3, 0, 0, 0 ; interrupt number
IO_ADDR dw 300h, 0 ; (factory) i/o port address
public DRIVER_CLASS, DRIVER_TYPE, DRIVER_NAME, DRIVER_FUNCTION
public PARAMETER_LIST, INT_NUM
DRIVER_CLASS db BLUEBOOK, 0, 0
DRIVER_TYPE db 70 ; Ether-T PC/AT, specifically
DRIVER_NAME db "Ether-T PC/AT", 0 ; name of the driver
DRIVER_FUNCTION db 2 ; standard plus extensions (except
; specific multicast)
PARAMETER_LIST label byte
db 1 ; major rev of packet driver spec
db 9 ; minor rev of packet driver spec
db 14 ; length of parameter list
db EADDR_LEN ; length of MAC-layer address
dw GIANT ; MTU, including headers
dw MAX_MULTICAST * EADDR_LEN
; buffer size of multicast addresses
dw 0 ; (# of back-to-back MTU recvs) - 1
dw 0 ; (# of successive xmits) - 1
INT_NUM dw 0 ; interrupt # to hook for post-EOI
; processing, 0 == none
public RCV_MODES
; Receive mode set-up dispatch table. Refers to actual code frags below that
; do the job.
RCV_MODES dw 7 ; number of receive modes in our table
dw 0 ; none exists
dw rcv_mode_1 ; receiver off
dw rcv_mode_2 ; only packets for this station
dw rcv_mode_3 ; mode 2 plus broadcast
dw 0 ; mode 3 plus limited multicast
dw rcv_mode_5 ; mode 3 plus all multicast
dw rcv_mode_6 ; all packets
; per-device instance structures -- keep state of the device -- ones marked
; as (copy) are soft copies of things actually on the device
private command_r
command_r dw 0 ; COMMAND (copy, sort of)
private config1_r_copy
config1_r_copy dw 0 ; CONFIGURATION 1 (copy)
private config2_r_copy
config2_r_copy dw 0 ; CONFIGURATION 2 (copy)
private cardconfig_r
cardconfig_r db 0 ; CARD CONFIGURATION (copy)
private rcv_errors
rcv_errors db 0 ; want to receive even flawed packets
private rcv_ptr_copy
rcv_ptr_copy dw 0 ; current (next) received packet ptr
private transmit
transmit db 0 ; currently transmitting
db ?
station_0 db 6 dup(0) ;current address (for disallowing
; broadcast packets)(JLD)(9-25-91)
; i/o instruction processing variables
private delay_mult
delay_mult dw 10, 0 ; delay multiplier / divisor - is
; a tens fraction: delay becomes
; (calibrated_delay * delay_mult) / 10
private io_16
io_16 db 0 ; do 16-bit i/o?
db ?
private io_delay_cnt
io_delay_cnt dw 1, 0 ; # of NOP spins to do to effect the
; required delay (2000 ns) for the
; worst-case i/o access
private iowm_delay_cnt
iowm_delay_cnt dw 1, 0 ; # of NOP spins to effect the required
; delay (800 ns) for a write to the
; buffer memory in the window register
private inw_fn
inw_fn dw OFFSET inwb_8005 ; function to input a word (w/ delay)
private outw_fn
outw_fn dw OFFSET outwb_8005 ; function to output a word (w/ delay)
; interrupt processing variables
private int_test
int_test db 0 ; in the initial interrupt testing?
private int_tested
int_tested db 0 ; was initial interrupt test successful?
ifdef DEBUG
public ghost_int_c, send_wait_c
ghost_int_c dw 0 ; ghost (empty) interrupt counter
rx_restart dw 0 ; stalled receiver restart counter
send_wait_c dw 0 ; previous send completion wait counter
endif
extrn THEIR_ISR : dword
extrn COUNT_IN_ERR : near
extrn COUNT_OUT_ERR : near
extrn GET_NUMBER : near
extrn MASKINT : near
extrn PRINT_NUMBER : near
extrn RECV_COPY : near
extrn RECV_FIND : near
extrn SET_RECV_ISR : near
extrn UNMASKINT : near
public AS_SEND_PKT
; Asynchronous packet transmit routine (high-performance drivers only).
; Entry with:
; ES:DI control block pointer
; DS:SI packet to send
; CX packet length
; Interrupts *may* be enabled. Returns:
; ES:DI preserved from call (control block pointer)
; and
; NC if OK
; or
; CY if transmission error
; DH error code
; Interrupt disablement on entry is preserved on exit.
AS_SEND_PKT:
assume ds:nothing
ret
public DROP_PKT
; Drop a packet from the queue (high-performance drivers only.)
DROP_PKT:
assume ds:nothing
ret
public GET_ADDRESS
; Get the MAC-layer address of the device. Entry with:
; ES:DI spot to plop the gotten address
; CX size of the address buffer at ES:DI
; Returns:
; NC if OK
; CX actual size of address gotten
; or
; CY if buffer not big enough
GET_ADDRESS:
assume ds:CODE
cmp cx, EADDR_LEN ; make sure there's enough room
jb get_address_err
mov cx, EADDR_LEN ; prepare to copy 6 bytes
; mask receiver interrupts (note the receiver is still
; running however) -- this also arbitrates use of the FIFO
push cx ; push stuff so MASKINT won't bite us
mov al, INT_NO
call MASKINT
pop cx ; revive stuff pushed for MASKINT
; get the station address
loadport
mov ax, config1_r_copy ; select STATION address reg 0
or ax, STATION_0_SEL
setw_r CONFIG1_R
xor ax, ax
setw_r DMA_ADDR_R
setport BUFWIN_R ; fix access within the loop
GET_ADDRESS_1: get_r BUFWIN_R ; input bytes of the address
mov es:[di], al
inc di
loop GET_ADDRESS_1
; unmask receiver interrupts, releasing FIFO
mov al, INT_NO
call UNMASKINT
mov cx, EADDR_LEN ; return the length copied
clc ; OK
ret
get_address_err:
stc ; error
ret
private inb_8005
; input a byte from an arbitrary 8005 port
inb_8005:
assume ds:nothing
in al, dx
io_delay io_delay_cnt ; generic delay
ret
private inwb_8005
; input a word from an arbitrary 8005 port -- 8 bit version
inwb_8005:
assume ds:nothing
in al, dx
io_delay io_delay_cnt ; generic delay
inc dx
xchg al, ah
in al, dx
xchg al, ah
io_delay io_delay_cnt ; generic delay
dec dx ; to keep 'setport' in synch
ret
private inw_8005
; input a word from an arbitrary 8005 port -- 16 bit version
inw_8005:
assume ds:nothing
in ax, dx
io_delay io_delay_cnt ; generic delay
ret
private outb_8005
; output a byte to an arbitrary 8005 port
outb_8005:
assume ds:nothing
out dx, al
io_delay io_delay_cnt ; generic delay
ret
private outwb_8005
; output a word to an arbitrary 8005 port -- 8 bit version
outwb_8005:
assume ds:nothing
out dx, al
io_delay io_delay_cnt ; generic delay
inc dx
xchg al, ah
out dx, al
xchg al, ah
io_delay io_delay_cnt ; generic delay
dec dx ; to keep 'setport' in synch
ret
private outw_8005
; output a word to an arbitrary 8005 port -- 16 bit version
outw_8005:
assume ds:nothing
out dx, ax
io_delay io_delay_cnt ; generic delay
ret
private rcv_mode_1
private rcv_mode_2
private rcv_mode_3
private rcv_mode_5
private rcv_mode_6
; Receive mode set-up routines. Dispatched to out of the RCV_MODES
; switch (above) or called directly by ETOPEN (once).
assume ds:CODE
rcv_mode_1: ; turn off receiver
call turn_rx_off
ret
rcv_mode_2: ; only packets to this station address
mov ax, MATCH_ONLY ; config 1
mov dx, 0 ; config 2
jmp SHORT rcv_mode_common
rcv_mode_3: ; mode 2 plus broadcast packets
mov ax, MATCH_BROAD ; config 1
mov dx, CRC_ERR_EN+DRIBBLE_EN+SHORT_FRAME_EN ; config 2
jmp SHORT rcv_mode_common
rcv_mode_5: ; mode 3 plus all multicast packets
mov ax, MATCH_MULTI ; config 1
mov dx, CRC_ERR_EN+DRIBBLE_EN+SHORT_FRAME_EN ; config 2
jmp SHORT rcv_mode_common
rcv_mode_6: ; all packets (promiscuous, including errors)
mov ax, MATCH_ALL ; config 1
mov dx, CRC_ERR_EN+DRIBBLE_EN+SHORT_FRAME_EN ; config 2
rcv_mode_common:
push dx ; save the changes while we
push ax ; wang on the receiver
call turn_rx_off ; shut down whilst we play
loadport
pop bx ; alter the CONFIGURATION 1 register
mov ax, config1_r_copy
and ax, NOT MATCH_BITS_M
or ax, bx
mov rcv_errors, 0
cmp bx, MATCH_ALL ; figure if we want to see errors
jne rcv_mode_common_1
mov rcv_errors, 1
rcv_mode_common_1:
mov config1_r_copy, ax
setw_r CONFIG1_R
pop bx ; alter the CONFIGURATION 2 register
mov ax, config2_r_copy
and ax, NOT (CRC_ERR_EN+DRIBBLE_EN+SHORT_FRAME_EN)
or ax, bx
mov config2_r_copy, ax
setw_r CONFIG2_R
call turn_rx_on ; drop trou now, brown cow
ret
private turn_rcv_on
; turn the receiver back on, as per the COMMAND register atop the stack
; removes the COMMAND register upon return
rcv_on:
assume ds:CODE
mov bx, sp ; get former COMMAND register
mov ax, ss:[bx+2]
test ax, SET_RX_OFF ; see if it's to go back on
jnz rcv_on_1
mov command_r, ax ; restore it
call turn_rx_on
rcv_on_1: ret 2 ; clear stack of COMMAND register too
private rcvxmt_off
; forcibly turn off the receiver and/or transmitter -- if the transmitter
; is running, this will abort the transmission
; leaves the prior COMMAND register on top of the stack (for use by a
; matching 'rcv_on' call)
rcvxmt_off:
assume ds:CODE
pop bx ; return address
push command_r ; left for 'rcv_on'
or command_r, SET_TX_OFF
mov transmit, 0 ; not doing it now
call turn_rx_off
jmp bx ; return
public RECV
; Part one of the ISR receive packet processing. Called before interrupt
; acknowledgement. All registers have been saved, DS == CS, and we're
; running on a private stack with interrupts disabled.
RECV:
assume ds:CODE
ifdef DBGINT3
int 03h ; Danger, Will Robinson!
endif
loadport
; check for and acknowledge the 8005 receive Interrupt
ifdef DEBUG
getw_r COMSTAT_R
test ax, RX_INT
jnz recv_1
inc ghost_int_c ; ghost interrupt counter
recv_1:
endif
mov ax, command_r ; acknowledge the Rx Interrupt
or ax, RX_INT_ACK
setw_r COMSTAT_R
cmp int_test, 0 ; is this interrupt a test one?
je recv_setup
inc int_tested ; indicated tested and be done
ret
recv_setup: ; set-up DMA access of the buffer read memory
mov ax, config1_r_copy ; select BUFFER MEMORY in BUFWIN
or ax, BUFFER_MEM_SEL
setw_r CONFIG1_R
setport DMA_ADDR_R ; code block port state synch
; COME HERE w/ port @ DMA_ADDR_R
recv_rcvptr: mov ax, rcv_ptr_copy ; address the next RECEIVE HEADER
setw_r DMA_ADDR_R
mov ax, command_r ; set the FIFO to READ now
or ax, FIFO_READ
setw_r COMSTAT_R
setport BUFWIN_R ; code block port state synch
; COME HERE w/ port @ BUFWIN_R
recv_loop: ; check for an end-of-list header (0 next pointer)
getw_r BUFWIN_R ; get the HEADER pointer
cmp al, 0 ; check for end of receive list
je recv_loop_1 ; looking only at first byte
xchg al, ah ; swap to proper host order
mov cx, ax ; save pointer away
getw_r BUFWIN_R ; get the HEADER & PACKET STATI
test al, CHAIN_CONTINUE ; test for completion
xchg al, ah ; AL now holds PACKET STATUS byte
jnz recv_continue
recv_loop_1: jmp recv_done
recv_continue: ; process a new frame - compute length and check it
push cx ; squirrel away HEADER POINTER
sub cx, rcv_ptr_copy ; compute length, checking for wrappage
jae recv_continue_1
sub cx, RCV_PTR ; adjust for wrap, sans xmit area
recv_continue_1:
sub cx, 4 ; less size of 8005 garf
cmp cx, ENET_HDR ; < a header is tallied and pitched
jb recv_continue_2
cmp cx, GIANT ; > GIANT likewise
ja recv_continue_2
test al, SHORT_FRAME+DRIBBLE_ERR+CRC_ERR
jz recv_alloc ; no error is a-OK
cmp rcv_errors, 0 ; see if we have to allow bad ones
jne recv_alloc ; yes, keep this dreck too
recv_continue_2:
call COUNT_IN_ERR ; tally this error
jmp recv_sail_it
recv_alloc: ; this one we want - allocate enough space on the stack
; to hold the Ethernet-2 packet header (so we can upcall
; pointing at the type field)
mov bx, cx ; save the real length
mov cx, ENET_HDR
sub sp, cx ; allocate a header on the stack
mov di, sp ; and point at it w/ DI
mov ax, ss ; set-up ES too
mov es, ax
cmp io_16, 0 ; do we do it fast or slow?
je recv_8bit_hdr
sar cx, 1 ; turn into words
; rep insw
recv_16bit_hdr:
insw ; 16-bit input
io_delay iowm_delay_cnt ; reduced delay w/i loop
loop recv_16bit_hdr
jmp SHORT recv_upcall1
recv_8bit_hdr: ; 8-bit input
in al, dx
mov es:[di], al
inc di
loop recv_8bit_hdr
recv_upcall1:
;# ; check to make sure this isn't our own broadcast(JLD)(9-25-91)
push di
mov di, sp ; point at the destination
add di, 2 ; which is 2 bytes away(remember push di?)
mov ax, 0ffffh
mov cx, 6/2
repe scasw
jne NotBroadcast
push si
mov si, offset station_0
mov cx, 6/2
repe cmps word ptr cs:[si], word ptr es:[di]
pop si
jne NotBroadcast
pop di
add sp, ENET_HDR ; pop the Ethernet-2 header from stack
; I forgot this until it bit me (11-6)
jmp recv_sail_it ; if it's us, bail out!
NotBroadcast:
pop di
; prepare an upcall with CX == length, ES:DI pointing at type,
; DL == packet class number (V8)
mov di, sp ; point at the type field
add di, EADDR_LEN*2
mov cx, bx ; get the real length for RECV_FIND
push bx ; save the real length
push dx ; and save our precious port setup too
;#
mov dl, BLUEBOOK ;assume bluebook Ethernet.
mov ax, es:[di]
xchg ah, al
cmp ax, 1500
ja BlueBookPacket
inc di ;set di to 802.2 header
inc di
mov dl, IEEE8023
BlueBookPacket:
;;;; mov dl, BLUEBOOK ; Ethernet-2 'Bluebook' (V8)
call RECV_FIND
pop dx ; get back port setup
pop bx ; and true packet length
mov ax, es ; see if the client passed on this one
or ax, di
jz recv_sail_pop
mov si, sp ; prepare to MOVS the Ethernet-2 header
mov ax, ss
mov ds, ax
assume ds:nothing
mov cx, ENET_HDR
push di ; save client packet base pointer
rep movsb
pop si ; restore client packet base pointer
add sp, ENET_HDR ; pop the Ethernet-2 header from stack
mov cx, bx ; compute residual length to bring in
sub cx, ENET_HDR
cmp io_16, 0 ; do we do it fast or slow?
je recv_8bit_data
sar cx, 1 ; /2: compute # of words to transfer
; rep insw ; loosely termed "full-bandwidth i/o"
recv_16bit_data:
insw
io_delay iowm_delay_cnt ; reduced delay w/i loop
loop recv_16bit_data
mov cx, bx ; get length back one more time
test bl, 1 ; see if there's a dangling byte
jz recv_upcall2
mov cx, 1 ; set-up to fall through to 8-bit
recv_8bit_data: in al, dx
mov es:[di], al
inc di
loop recv_8bit_data
mov cx, bx ; get length back one more time
xor bl, bl ; for sail testing after upcall2
recv_upcall2: ; prepare an upcall with CX == length, DS:SI pointing at
; client-allocated and now filled packet
mov ax, es
mov ds, ax
push dx ; save our precious port setup
call RECV_COPY
pop dx ; get back port setup
mov ax, cs ; and set DS back to CODE
mov ds, ax
assume ds:CODE
; have to check if 16-bit i/o processed an odd-length packet
; - if so, we MAY have blown the harmony of the 8005 DMA
; (may now be off by one) so just sail it
test bl, 1
jnz recv_sail_it
pop cx ; pop squirreled next HEADER POINTER
mov rcv_ptr_copy, cx ; and make that the new pointer
mov al, ch ; update the REA PTR register, making
set_r REA_PTR_R ; room for more inbound packets
setport BUFWIN_R
jmp recv_loop
recv_sail_pop: add sp, ENET_HDR ; pop the Ethernet-2 header from stack
recv_sail_it: ; rid ourselves of this frame the hard way: by resetting the
; DMA, in addition to our own bookkeeping stuff
loadport
mov ax, command_r ; release the DMA FIFO
or ax, FIFO_WRITE+DMA_INT_ACK
setw_r COMSTAT_R
pop cx ; pop squirreled next HEADER POINTER
mov rcv_ptr_copy, cx ; and make that the new pointer
mov al, ch ; update the REA PTR register, making
set_r REA_PTR_R ; room for more inbound packets
setport DMA_ADDR_R
jmp recv_rcvptr
recv_done: ; all inbound frames processed
loadport
mov ax, command_r ; release the DMA FIFO
or ax, FIFO_WRITE+DMA_INT_ACK
setw_r COMSTAT_R
; restart a stalled receiver
getw_r COMSTAT_R ; grab the STATUS register
test ax, RX_ON ; receiver still on?
jnz recv_done_1
ifdef DEBUG
inc rx_restart
endif
call turn_rx_on
recv_done_1:
ret
public RECV_EXITING
; Part two of the ISR receive packet processing. Called after interrupts
; have been acknowledged. Only DS and AX have been saved, DS == CS and
; we're running on the original stack (i.e. not the private one). On
; entry, interrupts are still disabled but it is possible to intelligently
; turn them back on.
RECV_EXITING:
assume ds:CODE
ret
public RESET_INTERFACE
; Reset the interface.
RESET_INTERFACE:
assume ds:CODE
loadport
mov ax, RESET
setw_r CONFIG2_R
; wait a good, long while
mov ax, 1
call set_timeout
RESET_INTERFACE_1:
call do_timeout
jnz RESET_INTERFACE_1
;;;; ; delay another 4 microseconds
;;;; io_delay io_delay_cnt ; generic delay
;;;; io_delay io_delay_cnt ; generic delay
ret
private turn_rx_off
; Turn off the receiver portion of the device. Undoes 'turn_rx_on'.
turn_rx_off:
assume ds:CODE
loadport
mov ax, command_r
and ax, NOT RX_INT_EN
or ax, SET_RX_OFF
mov command_r, ax
setw_r COMSTAT_R
ret
private turn_rx_on
; Turn on the receiver portion of the device. Assumes the station address
; has already been programmed, receive ISR registered, etc. Just brings it
; all to life on the wire. Undone by 'turn_rx_off'.
turn_rx_on:
assume ds:CODE
loadport
; load up Receive End Area Pointer
mov al, TEA_INIT + 1
set_r REA_PTR_R
; load up the Receive Pointer Register
mov ax, RCV_PTR
setw_r RX_PTR_R
mov rcv_ptr_copy, ax
; now turn it on
mov ax, command_r
and ax, NOT SET_RX_OFF
or ax, RX_INT_EN
mov command_r, ax
or ax, SET_RX_ON
setw_r COMSTAT_R
ret
public SEND_PKT
; Send one packet. Entry with:
; DS:SI packet buffer to xmit
; CX packet length
; ES:DI upcall routine (0:0 if no upcall is desired) -- only supported
; if the high-performance bit is set in 'driver_function' byte
; Returns:
; NC if OK
; or
; CY if transmission error
; DH error code
SEND_PKT:
assume ds:nothing
ifdef DBGINT3
int 03h ; Danger, Will Robinson!
endif
mov ax, ds ; restore DS usage right here, saving
mov es, ax ; it in ES (as we don't do upcalls)
mov ax, cs
mov ds, ax
assume ds:CODE
cmp cx, ENET_HDR ; firewall against trivia
jae SEND_PKT_1
jmp send_pkt_ret
SEND_PKT_1:
cmp cx, GIANT ; see if too big
jbe SEND_PKT_2
jmp send_pkt_NOSPACE
SEND_PKT_2:
cmp cx, RUNT ; see if too small
jae SEND_PKT_3 ; ... sounds like the Three Bears
mov cx, RUNT ; transmit at least this much
SEND_PKT_3:
; mask receiver interrupts (note the receiver is still
; running however) -- this also arbitrates use of the FIFO
push cx ; push stuff so MASKINT won't bite us
mov al, INT_NO
call MASKINT
loadport
mov ax, config1_r_copy ; select BUFFER MEMORY in BUFWIN
or ax, BUFFER_MEM_SEL
setw_r CONFIG1_R
; check status of the previous transmission
cmp transmit, 0 ; see if a transmit is incomplete
je send_pkt_load
send_pkt_wait:
loadport ; synch for the loop
mov ax, 3 ; address the TRANSMIT STATUS byte
setw_r DMA_ADDR_R
mov ax, command_r ; set the FIFO to READ now, also
or ax, FIFO_READ+TX_INT_ACK ; ACKs any TX int bit
setw_r COMSTAT_R
get_r BUFWIN_R ; get the STATUS byte
mov bl, al ; save the gotten STATUS byte
mov ax, command_r ; set the FIFO back to WRITE (aborts,
or ax, FIFO_WRITE+DMA_INT_ACK ; drains, ACKs FIFO int bit)
setw_r COMSTAT_R
test bl, HDR_DONE ; check for transmit done
ifdef DEBUG
jnz send_pkt_wait_1
inc send_wait_c ; count send completion waits
jmp send_pkt_wait ; loop
send_pkt_wait_1:
else
jz send_pkt_wait ; loop
endif
; done -- check for transmit error & count if so
test bl, BABBLE_ERR+COLL_16_ERR
jz send_pkt_load
call COUNT_OUT_ERR ; count the error
send_pkt_load: ; load the new packet into the transmit area
pop cx ; revive stuff pushed for MASKINT
; load up the transmit buffer -- allows only a single
; transmit (doesn't chain 'em); however, this transmission
; is overlapped with the preparation of the next
loadport ; synch the state
xor ax, ax ; TRANSMIT NEXT HEADER word
setw_r DMA_ADDR_R
mov ax, cx ; calculate xmit request length
add ax, 4 ; including 8005 garf
xchg ah, al ; SWAP
setw_r BUFWIN_R
mov al, PACKET_PRESENT+DATA_FOLLOWS ; TRANSMIT COMMAND byte
xor ah, ah ; TRANSMIT STATUS byte
setw_r BUFWIN_R ; both in one SWAPPED swoop
; semi-quick programmed output here -- direct use of macros
mov ax, es ; go back to DS-relative packet data
mov ds, ax
assume ds:nothing
cmp io_16, 0 ; can do 16-bit i/o?
je send_pkt_8bit
test cx, 1 ; odd length?
jz send_pkt_load_1
inc cx ; make even for 16-bit mode output
send_pkt_load_1:
sar cx, 1 ; /2: compute # of words to transfer
cmp iowm_delay_cnt, 0 ; check for full-tilt boogie
jne send_pkt_load_2 ; nope, need delays
rep outsw ; blow-out clearance i/o
jmp SHORT send_pkt_xmt
send_pkt_load_2:
mov ax, [si] ; Note: DS-relative packet here
out dx, ax
io_delay iowm_delay_cnt ; reduced delay w/i loop
inc si
inc si
loop send_pkt_load_2
jmp SHORT send_pkt_xmt ; done w/ buffer load
send_pkt_8bit: ; 8 bit packet load
cmp iowm_delay_cnt, 0 ; check for full-tilt boogie
jne UpHere_1 ; nope, need delays
;the following replaces rep outsb, which doesn't work too well on 8088-based PCs
;; rep outsb ; blow-out clearance i/o
UpHere: lodsb ; Note: DS-relative packet here
out dx, al
loop UpHere
jmp SHORT send_pkt_xmt
UpHere_1:
mov al, [si] ; Note: DS-relative packet here
out dx, al
io_delay iowm_delay_cnt ; reduced delay w/i loop
inc si
loop UpHere_1
send_pkt_xmt: ; packet now loaded, finish up and transmit it
mov ax, cs ; restore our DS
mov ds, ax
assume ds:CODE
; set TRANSMIT POINTER (overlap as FIFO drains)
xor ax, ax ; start chain at address 0
setw_r TX_PTR_R
; wait for DMA FIFO to drain
fifo_wait
; now transmit it
mov ax, command_r ; remove default transmitter off cmd
and ax, NOT SET_TX_OFF
mov command_r, ax
or ax, SET_TX_ON
setw_r COMSTAT_R ; Send!
mov transmit, 1 ; note to check completion next time
; unmask receiver interrupts, releasing FIFO
mov al, INT_NO
call UNMASKINT
send_pkt_ret:
clc ; OK
ret
send_pkt_NOSPACE:
mov dh, NO_SPACE
stc ; error
ret
public SET_ADDRESS
; Set the MAC station receiver address. Entry with:
; DS:SI the new MAC address to adopt
; CX length of address at DS:SI
; Returns:
; DS restored to point at CODE segment
; plus
; NC if OK
; or
; CY if error
; DH error code
SET_ADDRESS:
assume ds:nothing
mov ax, ds ; undo the twisted DS usage
mov es, ax ; back to addressing our memory
mov ax, cs
mov ds, ax
assume ds:CODE
cmp cx, EADDR_LEN ; check for proper address length
jne set_addr_BADADDRESS
test BYTE PTR es:[si], 1 ; deny broadcast or multicast
jnz set_addr_BADADDRESS
;# ; set the new station address (JLD)(9-25-91)
mov ax, ds:[si][0]
mov word ptr ds:station_0[0], ax
mov ax, ds:[si][2]
mov word ptr ds:station_0[2], ax
mov ax, ds:[si][4]
mov word ptr ds:station_0[4], ax
; disable receiver and/or transmitter
call rcvxmt_off ; leaves COMMAND register on stack
; set the new station address
loadport
mov ax, config1_r_copy ; select STATION address reg 0
or ax, STATION_0_SEL
setw_r CONFIG1_R
xor ax, ax
setw_r DMA_ADDR_R
setport BUFWIN_R ; fix access within the loop
SET_ADDRESS_1:
mov al, es:[si] ; output bytes of the new address
inc si
set_r BUFWIN_R
loop SET_ADDRESS_1
; reenable the receiver if it was on before
call rcv_on ; removes COMMAND register from stack
clc ; OK
ret
set_addr_BADADDRESS:
mov dh, BAD_ADDRESS ; improper address
stc ; error
ret
public SET_MULTICAST_LIST
; Set the multicast list. Entry with:
; DS:SI the list of multicast MAC addresses
; CX number of addresses at DS:SI
; Returns:
; NC if OK
; or
; CY if error
; DH error code
SET_MULTICAST_LIST:
assume ds:CODE
jmp SHORT set_multicast_err ; FOR NOW: no multicast support
clc ; OK
ret
set_multicast_err:
mov dh, NO_MULTICAST
stc ; error
ret
public TERMINATE
; Terminate the packet driver. Nothing special to do.
TERMINATE:
assume ds:CODE
ret
public XMIT
; Routine to process a queued transmission with the least possible latency.
; Called immediately within the receive ISR. The attempt here is to effect
; back-to-back transmissions. This routine may only use AX and DX freely
; and is running whatever stack exists at interrupt time (not the private
; one). This routine isn't necessary on the 8005, because it can chain
; transmissions (if anyone really cared).
XMIT:
assume ds:nothing
ret
include timeout.asm
; Everything above this line is resident upon successful load.
private END_RESIDENT
END_RESIDENT label byte
; Everything below this line is discarded upon installation.
public USAGE_MSG
USAGE_MSG db "usage: davidsys [-n] [-d] [-w] <packet_int_no> <int_no> <io_addr> <delay_mult>",CR,LF,'$'
public COPYRIGHT_MSG
COPYRIGHT_MSG db "Packet driver for the Ether-T PC/AT ",'0'+(majver / 10),'0'+(majver mod 10),".",'0'+version,".",'0'+s8005_version,CR,LF
db "Portions Copyright 1991 DAVID Systems, Inc and Marc S Dye",CR,LF,'$'
badint_msg db "Specified device interrupt not supported for"
db " your installation",CR,LF,'$'
int8_msg db " Try: 2, 3, 4, 5, 6, or 7",CR,LF,'$'
int16_msg db " Try: 3, 4, 5, 6, 7, 9, 10, 11, 12, 14, or 15"
db CR,LF,'$'
inttimedout_msg db "Timed out awaiting device test interrupt",CR,LF,'$'
nodev_msg db "No device found at specified i/o address",CR,LF,'$'
setaddr_msg db "Unable to set initial station address",CR,LF,'$'
delay_mult_name db "Delay multipler (x10) ",'$'
int_no_name db "Interrupt number ",'$'
io_addr_name db "I/O port ",'$'
ifdef DEBUG
count_del_name db "Incremental i/o delay spin (nsec x10) ",'$'
delay_del_name db "Basic i/o delay (nsec x100) ",'$'
io_delay_name db "I/O delay (spins) ",'$'
iowm_delay_name db "I/O write memory delay (spins) ",'$'
null_bias_name db "Null (overhead) cost (nsec x100) ",'$'
null_spin_name db "Null spin count ",'$'
ref_spin_name db "Reference spin count ",'$'
unit_spin_name db "Single spin count ",'$'
endif
private int_map
; Interrupt number to Ether-T PC/AT configuration register code mapping table
; 0 1 2 3 4 5 6 7
int_map db 00h, 00h, 02h, 01h, 03h, 04h, 05h, 06h
; 8 9 10 11 12 13 14 15
db 00h, 02h, 07h, 08h, 09h, 00h, 0Ah, 0Bh
REF_DELAY equ 64
private count_delay
count_delay dw ?, 0 ; # of 10 nsecs in each io_delay spin
private delay_delay
delay_delay dw ?, 0 ; # of 100 nsecs in the basic io_delay
private null_bias
null_bias dw ?, 0 ; # of 100 nsecs in a null spin
private null_spins
null_spins dw ?, 0 ; # of spins for without io_delay macro
private ref_spins
ref_spins dw ?, 0 ; # of spins for io_delay of 1+REF_DELAY
private test_delay
test_delay dw ? ; holding tank for current sample count
private unit_spins
unit_spins dw ?, 0 ; # of spins for io_delay of 1
; macro to sample timer zero's counter register
timer0_sample macro
xor al, al ; latch timer zero counter
out 43h, al
in al, 40h ; sample timer zero counter
mov ah, al
in al, 40h
xchg ah, al
endm
private calibrate_one
calibrate_one:
assume ds:CODE
mov test_delay, ax
; find the timer near zero
mov cx, 100h
calibrate_one_1:
timer0_sample
cmp ax, cx
ja calibrate_one_1
; now find the timer above that value (after wrapping to max)
calibrate_one_2:
timer0_sample
cmp ax, cx
jbe calibrate_one_2
; wait till it gets to the one quarter point
mov cx, 0C000h
calibrate_one_3:
timer0_sample
cmp ax, cx
ja calibrate_one_3
; calibration time - disable interrupts, watch for half a
; clock period to elapse
pushf
cli
xor bx, bx
mov cx, 4000h
cmp test_delay, 0 ; see if measuring overhead?
jne calibrate_io
; calibrating the overhead of this timing stuff only
calibrate_one_4:
inc bx
jc calibrate_exit ; overflow - exit w/ 0
timer0_sample
cmp ax, cx
ja calibrate_one_4
jmp SHORT calibrate_exit
calibrate_io: io_delay test_delay
inc bx
jc calibrate_exit ; overflow - exit w/ 0
timer0_sample
cmp ax, cx
ja calibrate_io
calibrate_exit: popf
mov ax, bx
ret
private calibrate_delay
calibrate_delay:
assume ds:CODE
; if user wanted no delays, skip all this stuff
cmp delay_mult, 0
jne calibrate_delay_1
xor ax, ax ; normalize to zero
jmp calibrate_store
calibrate_delay_1:
; calibrate the overhead of the time sampling loop
xor ax, ax
call calibrate_one
or ax, ax
jz calibrate_dj ; can't time -- default it all
mov null_spins, ax
ifdef DEBUG
mov di, OFFSET null_spins
mov dx, OFFSET null_spin_name
call PRINT_NUMBER
endif
; calibrate a unit (single spin) loop
mov ax, 1
call calibrate_one
or ax, ax
jz calibrate_dj ; can't time -- default it
mov unit_spins, ax
ifdef DEBUG
mov di, OFFSET unit_spins
mov dx, OFFSET unit_spin_name
call PRINT_NUMBER
mov ax, unit_spins ; reload AX
endif
cmp null_spins, ax ; ensure null >= unit
jb calibrate_dj ; bogonic -- default it
; now one with some teeth in it
mov ax, 1+REF_DELAY
call calibrate_one
or ax, ax
jz calibrate_dj ; can't time -- default it all
mov ref_spins, ax
ifdef DEBUG
mov di, OFFSET ref_spins
mov dx, OFFSET ref_spin_name
call PRINT_NUMBER
mov ax, ref_spins ; reload AX
endif
cmp unit_spins, ax ; ensure unit > ref
ja calibrate_dj_1
calibrate_dj: jmp SHORT calibrate_def ; bogonic -- default it
calibrate_dj_1:
; now do the math - compute the overhead of the timing code
; (in units of 100 nsec)
mov cx, null_spins
call compute_100nsecs
jc calibrate_def
mov null_bias, ax
ifdef DEBUG
mov di, OFFSET null_bias
mov dx, OFFSET null_bias_name
call PRINT_NUMBER
endif
; compute cost of the basic i/o delay macro (in units of
; 100 nsec)
mov cx, unit_spins
call compute_100nsecs
jc calibrate_def
sub ax, null_bias
mov delay_delay, ax
ifdef DEBUG
mov di, OFFSET delay_delay
mov dx, OFFSET delay_del_name
call PRINT_NUMBER
endif
; compute cost of the reference spin in the i/o delay
; macro (this time, in units of 10 nsec)
mov cx, ref_spins
call compute_100nsecs
jc calibrate_def
sub ax, null_bias
sub ax, delay_delay ; now delay for REF_COUNT spins
mov cx, 10
mul cx
jnc calibrate_dj_2
; this machine is *sooo* slow, a minimum delay is fine
mov ax, 1
jmp SHORT calibrate_store
calibrate_dj_2:
mov cx, REF_DELAY
div cx
mov count_delay, ax
ifdef DEBUG
mov di, OFFSET count_delay
mov dx, OFFSET count_del_name
call PRINT_NUMBER
endif
; now, see if basic i/o delay (w/o extra spins) is enough
mov bx, 1 ; basic macro includes one spin
mov ax, delay_delay
sub ax, 22 ; >= 2.2 usec ?
jae calibrate_dj_3
neg ax ; correct sign of residual
mov cx, 10 ; scale to 10 nsec units
mul cx
jc calibrate_def
div count_delay
inc ax
add bx, ax ; back into spin count result
calibrate_dj_3:
mov ax, bx ; the calibrated basic result
jmp SHORT calibrate_store
calibrate_def: ; this processor is *sooo* fast that we can't calibrate it!?
; just use a conservative default (should be good to about a
; 400MHz 80486)
mov ax, 120
; scale the delay by the user-request multiplier (if any)
calibrate_store:
mul delay_mult ; a tens multiple
mov cx, 10 ; so reduce / 10
div cx ; allowed to be zero
mov io_delay_cnt, ax
; compute the .9 usec 'iowm_delay_cnt'
mov cx, 9
mul cx
mov cx, 20
div cx
mov iowm_delay_cnt, ax
ret
private compute_100nsecs
; Given a value in CX (representing spin counter during a 1/36.04 timing
; run), compute the number of hundreds of nanoseconds per unit spin. Return
; result in AX, or set CY and return if algorithm won't scale.
compute_100nsecs:
assume ds:CODE
mov ax, 9 ; compute spins * 9 (36.04/4)
mul cx
jc compute_100n_err ; won't fit -- return error
mov cx, ax ; prepare for division
mov dx, 26h ; put 2,500,000 into DX,AX
mov ax, 25a0h
div cx ; compute 10M / (spins*36.04)
clc
ret
compute_100n_err:
ret
public ETOPEN
; Perform initial open of the device. Called only once: immediately prior to
; hooking the packet driver service interrupt and TSRing. Device interrupts
; are to be hooked (if this routine deems reasonable to do so). No registers
; are given on entry. On return:
; NC if OK
; DX offset of the end of the resident portion of the driver
; or
; CY if error
ETOPEN:
assume ds:CODE
ifdef DBGINT3
int 03h ; Danger, Will Robinson!
endif
; calibrate the i/o delays needed
call calibrate_delay
ifdef DEBUG
mov di, OFFSET io_delay_cnt
mov dx, OFFSET io_delay_name
call PRINT_NUMBER
mov di, OFFSET iowm_delay_cnt
mov dx, OFFSET iowm_delay_name
call PRINT_NUMBER
endif
; test for access to the device; check for 16-bit i/o
call try_io
jnc ETOPEN_1
jmp etopen_err
ETOPEN_1:
; try out the interrupt channel & leave hooked if successful
call try_interrupt
jnc ETOPEN_2
jmp etopen_err
ETOPEN_2:
; get the PROM Ethernet address
call RESET_INTERFACE ; reset the device (again!)
loadport
mov ax, command_r ; everything still off
or ax, FIFO_WRITE
setw_r COMSTAT_R
xor ax, ax ; reset the DMA address to 0
setw_r DMA_ADDR_R
mov ax, config1_r_copy ; select the PROM
or ax, PROM_SEL
setw_r CONFIG1_R
mov ax, config2_r_copy ; blast the watchdog
or ax, KILL_WATCHDOG
setw_r CONFIG2_R
mov cx, EADDR_LEN ; allocate some space for the address
sub sp, cx
mov bp, sp
setport BUFWIN_R ; fix access within the loop
ETOPEN_3:
get_r BUFWIN_R ; next byte of PROM Ethernet address
mov [bp], al
inc bp
loop ETOPEN_3
; set the PROM address as the default STATION 0 address
call RESET_INTERFACE ; reset the device (yet again!)
mov cx, EADDR_LEN ; size of Ethernet address
mov si, sp ; PROM Ethernet address atop stack
call SET_ADDRESS
add sp, EADDR_LEN ; pop Ethernet address from the stack
; load up Transmit End Area Pointer
loadport
mov ax, config1_r_copy
or ax, TEA_SEL
setw_r CONFIG1_R
mov al, TEA_INIT
set_r BUFWIN_R
; set up a few other default goodies once only here:
; config 1 : STATION 0 address (Matchmode by 'rcv_mode_3')
; config 2 : defaults are OK
or config1_r_copy, STATION_0_EN
mov ax, config1_r_copy
setw_r CONFIG1_R
; we're go now -- wake up and smell the roses!
and command_r, NOT SET_RX_OFF
; reload the CARD CONFIGURATION register
mov ax, config1_r_copy ; select CARD CONFIGURATION register
or ax, CARD_CONFIG_SEL
setw_r CONFIG1_R
mov al, cardconfig_r
set_r BUFWIN_R ; writes CARD CONFIGURATION register
; turn on the receiver now, in (default) mode 3
call rcv_mode_3
; tell the caller where resident memory ends
mov dx, OFFSET END_RESIDENT
clc ; OK
ret
etopen_err:
stc ; error
ret
public PARSE_ARGS
; Parse the device-specific portion of the invokation command line (after
; the packet driver vector number). Entry with:
; DS:SI residual command line
; Can emit device-specific bitches as necessary (using DOS i/o) and exit
; without returning. If it returns:
; NC if OK
; or
; CY if usage error
; Returning w/ carry set will cause a usage error, then exit without further
; processing.
PARSE_ARGS:
assume ds:CODE
mov di, OFFSET INT_NO
call GET_NUMBER
mov di, OFFSET IO_ADDR
call GET_NUMBER
mov di, OFFSET delay_mult
call GET_NUMBER
clc ; OK
ret
public PRINT_PARAMETERS
; Spew out all of the configuration parameters to the console device.
; Called within the main program, just prior to deciding to TSR. Should
; dump out each device-specific parameter, parsed by PARSE_ARGS (just to
; make the user warm-and-fuzzy I suppose...) Entry with:
; no special register contents
; Returns:
; nothing
PRINT_PARAMETERS:
assume ds:CODE
mov di, OFFSET INT_NO
mov dx, OFFSET int_no_name
call PRINT_NUMBER
mov di, OFFSET IO_ADDR
mov dx, OFFSET io_addr_name
call PRINT_NUMBER
mov di, OFFSET delay_mult
mov dx, OFFSET delay_mult_name
call PRINT_NUMBER
ret
private try_interrupt
; try out the receiver interrupt (once at the outset); in the process,
; sets up the CARD CONFIGURATION register
try_interrupt:
assume ds:CODE
call RESET_INTERFACE
loadport
mov ax, config1_r_copy ; program CARD CONFIGURATION register
or ax, CARD_CONFIG_SEL
setw_r CONFIG1_R
; map interrupt number to card config enumeration value
mov bx, WORD PTR INT_NO
mov al, [bx + OFFSET int_map]
or al, al
jz try_int_badint_err
; check for 16-bit i/o usage; include in card config value
cmp io_16, 0
je try_interrupt_1
or al, SET_16BITMODE_M
try_interrupt_1:
set_r BUFWIN_R ; writes CARD CONFIGURATION register
mov cardconfig_r, al ; save soft-copy of this
; install the 'recv_isr' handler
mov int_test, 1 ; flag that we're testing only
call SET_RECV_ISR
; cause a receiver interrupt
loadport
mov ax, command_r ; SET_RX_OFF in here now
or ax, RX_INT_EN+SET_RX_ON
setw_r COMSTAT_R ; Interrupt!
;# Compaq DOS leaves CPU ints disabled!
sti
mov ax, 1 ; wait one tick
call set_timeout
try_interrupt_2:
call do_timeout
jz try_int_timedout
cmp int_tested, 0 ; did it happen?
je try_interrupt_2
; the interrupt worked -- leave w/ the receiver off, the
; ISR hooked but masked
mov ax, command_r ; SET_RX_OFF in here now
setw_r COMSTAT_R ; shut up!
mov al, INT_NO
call MASKINT
mov int_test, 0 ; from now on, it's the real thing
clc ; OK
ret
try_int_timedout:
; timed-out (bummer dude!) -- turn receiver back off
mov ax, command_r ; SET_RX_OFF in here now
or ax, RX_INT_ACK ; just in case ...
setw_r COMSTAT_R ; shut up!
; deregister handler and boogie
; This should be a standard call -- I've asked Russ ...
mov al, INT_NO
add al, 8
cmp al, 8+8 ; is it a slave 8259 interrupt?
jb try_int_timedout_1; no.
add al, 70h - (8+8) ; map it to the real interrupt
try_int_timedout_1:
push ds
assume ds:nothing
lds dx, THEIR_ISR
mov ah, 25h
int 21h
pop ds
assume ds:CODE
mov dx, OFFSET inttimedout_msg
jmp SHORT try_int_err
try_int_badint_err:
mov dx, OFFSET badint_msg
mov ah, 9 ; blab to the user
int 21h
cmp io_16, 0 ; tell him/her/it what's legit
jne try_int_badint_err_1
mov dx, OFFSET int8_msg
jmp SHORT try_int_err
try_int_badint_err_1:
mov dx, OFFSET int16_msg
try_int_err:
mov ah, 9 ; blab to the user
int 21h
stc ; error
ret
private try_io
; try out i/o to the device; in the process, determines if 16-bit i/o is OK
; NOTE: This code was transliterated from the C source for DAVID's NDIS driver.
; In the absence of any written documentation on the CARD CONFIGURATION
; register, the many magic numbers herein remain a mystery.
try_io:
assume ds:CODE
call RESET_INTERFACE
mov command_r, SET_TX_OFF+SET_RX_OFF+SET_DMA_OFF
loadport
mov ax, config1_r_copy ; select CARD CONFIGURATION register
or ax, CARD_CONFIG_SEL
set_r CONFIG1_R ; BYTE-size only here!
; first taunting of the device ...
mov al, 05h ; C sez: "4 bit is LSBS value"
set_r BUFWIN_R ; " ... and ... "
get_r BUFWIN_R ; C sez: "comes back in the 4 MSBs"
and al, 0F0h
cmp al, 50h ; this has to compare
jne try_io_err
; second taunting of the device ...
mov al, 0Ah ; "Your mother was a hamster ..."
set_r BUFWIN_R
get_r BUFWIN_R ; "... and your father smelt of"
and al, 0F0h ; "ELDERBERRIES!"
cmp al, 0A0h
jne try_io_err
; now for something completely different -- ask the CARD
; CONFIGURATION register if it's plugged into an 8- or
; 16-bit slot; mark it well
get_r BUFWIN_R
and al, 1 ; bit on means 8-bit slot
xor al, 1 ; complement sense for 'io_16'
mov io_16, al
jz try_io_1
; set-up 16-bit versions of word i/o functions
; N.B. From this point forward, we MUST to 16 bit i/o
; on 16 bit objects if the card told us to!
mov inw_fn, OFFSET inw_8005 ; input function
mov outw_fn, OFFSET outw_8005 ; output function
try_io_1:
clc ; OK
ret
try_io_err:
mov dx, OFFSET nodev_msg
mov ah, 9 ; blab to the user
int 21h
stc ; error
ret ; "Now go away or I shall taunt you a second time-uh!"
CODE ends
end
; eof