home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Serving the Web
/
ServingTheWeb1995.disc1of1.iso
/
connect
/
tcpip
/
crynwr
/
exp16116
/
exp16.asm
< prev
next >
Wrap
Assembly Source File
|
1994-05-20
|
64KB
|
2,958 lines
version equ 6
DEBUG equ 0
;History:1985,1
; Copyright 1991, 1992, 1993 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.
include defs.asm
code segment word public
assume cs:code, ds:code
;
; Waits for SCB command unit to become idle
;
; MUST NOT TRASH CX OR THE ISSUE_COMMAND PROCEDURE WILL FAIL
;cmd_clear exits with cy if the command didn't become idle in one millisecond.
cmd_clear macro
local exit
lea dx, [bp].@SCB_CMD ;BP -> I/O Base
in ax, dx ;Read SCB command
or ax, ax ;Wait for command accepted
jz exit
call cmd_wait
exit:
endm
;
; Drives channel attention to the 586. We don't care whats in AX, the write
; will cause the ASIC to drive CA to the 586.
;
issue_ca MACRO
lea dx, [bp].@CA_Ctrl
out dx, al
endm
;
;
; Enable and disable exp16s interrupts.
;
enable_board_ints macro
lea dx, [bp].@Sel_IRQ
mov al, encoded_int_no
or al, 08h
out dx, al
endm
disable_board_ints macro
lea dx, [bp].@Sel_IRQ
mov al, encoded_int_no
out dx, al
endm
RxBufferSize equ 1518+14+18 ;Max Rx packet+MAC header+
; 18 (alignment)
TxBufferSize equ 1518+14+20 ;Max Tx packet+MAC header+
; 20 (alignment)
include exp16.inc
_64K_not_32K db 0 ;<>0 if we have 64K of memory.
_16_not_8_bit_slot db 0 ;<>0 if we're in a 16-bit slot.
;possible values for connection_type:
CONN_BNC equ 0
CONN_AUI equ 1
CONN_TPE equ 2
CONN_AUTO equ 3
connection_type db ?
;Memory Sizes
mem_size_address equ 00H
_32K equ 00000h
_64K equ 00001h
CONNECTION_ADDRESS equ 00H
CONNECTION_FIELD equ 0001000000000000B
AUTO_CON_ADDRESS EQU 01H
AUTO_CON_MASK EQU 10000000B
TPE_address equ 05H
TPE_type_field equ 0000000000000001B
int_num_address equ 00H
int_num_field equ 1110000000000000B
int_field_shift equ 13
EE_ETHERNET_ADD_LOW equ 2
EE_ETHERNET_ADD_MID equ 3
EE_ETHERNET_ADD_HIGH equ 4
EE_INT equ 0
EE_SHIFT equ 13
; Slot Width
slot_width_mask equ 04h
_16_bit_slot equ 0000h
_8_bit_slot equ 0001h
_16_bit_override_bit equ 08h
;
; EXP16 base port structure
;
@EXP16BasePorts struc
@Data_Reg dw ? ;Data Transfer Register.
@Write_Ptr dw ? ;Write Address Pointer.
@Read_Ptr dw ? ;Read Address Pointer.
@CA_Ctrl db ? ;Channel Attention Control.
@Sel_IRQ db ? ;IRQ Select.
@SMB_Ptr dw ? ;Shadow Memory Bank Pointer.
db ?
@MEM_Ctrl db ?
@MEM_Page_Ctrl db ?
@Config db ?
@EEPROM_Ctrl db ?
@ID_Port db ?
@EXP16BasePorts ends
ECR1 equ 300eh
@ISR_Ports struc
db 0C008h dup(?)
@SCB_STAT dw ?
@SCB_CMD dw ?
@SCB_CBL dw ?
@SCB_RFA dw ?
; @SCB_CRCERRS dw ?
; @SCB_ALNERRS dw ?
; @SCB_RSCERRS dw ?
; @SCB_OVRNERRS dw ?
@ISR_Ports ends
@memory_page_struc STRUC
DB 4000H DUP (?)
@mem_loc_0 DW ?
@mem_loc_2 DW ?
@mem_loc_4 DW ?
@mem_loc_6 DW ?
@mem_loc_8 DW ?
@mem_loc_10 DW ?
@mem_loc_12 DW ?
@mem_loc_14 DW ?
DB 4000H - 16 DUP (?)
@mem_loc_16 DW ?
@mem_loc_18 DW ?
@mem_loc_20 DW ?
@mem_loc_22 DW ?
@mem_loc_24 DW ?
@mem_loc_26 DW ?
@mem_loc_28 DW ?
@mem_loc_30 DW ?
@memory_page_struc ENDS
;
;
; TData Segment
;
; This segment template represents the 64-KB segment that the 82586
; can address. Shared memory can be either 32K or 64K for the EXP16.
; All structures must be quad-word aligned.
;
; Offset Type of Block Block Size Cnt
; ------ ------------- ---------- ---
; 0000h ISCP 8 1
; 0008h SCB 16 1
; 0020h CommandBlock 104 1
; 00E0h SendBlock(s) 1568 variable
; ????h ReceiveBlock(s) 1568 variable
; FFF6h SCP 10 1
;
TData_64 segment at 0
ISCP ISysConfigPtr <>
SCB SystemControlBlock <>
ORG 20H
CB CommandBlock <>
Send_Blocks DB SIZE SendBlock * Number_of_Tx_Buffers dup (?)
Receive_Blocks LABEL WORD
ORG (0FFFFh - SIZE SCPS + 1) AND 0FFE0H
SCP LABEL BYTE
ORG 0FFFFh - SIZE SCPS + 1
THeapTop LABEL BYTE ;Top of available memory
end_of_send_blocks = 20h + SIZE CommandBlock + (Number_of_Tx_Buffers * SIZE SendBlock)
num_rx_buf_32k = (07FFFh - SIZE SCPS + 1 - end_of_send_blocks) / (SIZE ReceiveBlock)
num_rx_buf_64k = (0FFFFh - SIZE SCPS + 1 - end_of_send_blocks) / (SIZE ReceiveBlock)
TData_64 ENDS
public int_no
int_no db 3,0,0,0 ;must be four bytes long for get_number.
io_addr dw -1,-1 ; I/O address for card
encoded_int_no db ? ;encoded for exp16.
public driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class db BLUEBOOK, IEEE8023, 0 ;from the packet spec
driver_type db 91 ;from the packet spec
driver_name db 'EtherExpress16',0 ;name of the driver.
driver_function db 2 + 16 + 8 ;8 = error in DH, 16 = has TDR function.
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 EADDR_LEN ;length of MAC-layer address
dw GIANT ;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,
save_err db 0 ;error bits on promiscuous recieve
pro0 db 0
pro1 db 0
BART_Board_ID EQU 0BABAH
BUD_board_ID EQU 0BABBH
mca_hp_board_ID EQU 0BABCH
board_id dw ?
public rcv_modes
rcv_modes dw 7 ;number of receive modes in our table.
dw 0 ;There is no mode zero
dw 0
dw rcv_mode_2
dw rcv_mode_3
dw rcv_mode_4 ;haven't set up perfect filtering yet.
dw 0
dw rcv_mode_6
;
; 82586 Configuration Parameters
;
config_params LABEL WORD
num_config_params LABEL BYTE ;12 BYTEs
DB 0CH
fifo_limit LABEL BYTE
DB 14
srdy LABEL BYTE ;srdy = 1
save_bad_frame LABEL BYTE ;do not save bad frame
DB 40H
address_length LABEL BYTE ;6 BYTEs
auto_insert_address LABEL BYTE ;auto insert off (1)
preamble_length LABEL BYTE ;2 BYTEs
internal_loopback LABEL BYTE ;0 = OFF 1 = ON
external_loopback LABEL BYTE ;0 = OFF 1 = ON
DB 2EH ;26h in IPXWS
linear_priority LABEL BYTE ;default is 0
accel_contention_resolution LABEL BYTE ;default is 0
exp_backoff_method LABEL BYTE ;0 = 802.3 1 = Alternate
DB 00H
interframe_spacing LABEL BYTE ;default is 60H (96 bits)
DB 60H
slot_time_low LABEL BYTE ;0
DB 00H
slot_time_high LABEL BYTE ;2
retry_num LABEL BYTE ;15
DB 0F2H
promiscuous_mode LABEL BYTE ;0 = OFF 1 = ON
broadcast_disable LABEL BYTE ;0 = OFF 1 = ON
encode_decode LABEL BYTE ;0 = NRZ 1 = MANCHESTER
transmit_on_no_carrier LABEL BYTE ;0 = STOP 1 = CONTINUE
no_crc_insertion LABEL BYTE ;0 = OFF 1 = ON
crc_type LABEL BYTE ;0 = 32 bit autodin II
;1 = 16 bit CCITT
bit_stuffing LABEL BYTE ;0 = 802.3 1 = HDLC
padding LABEL BYTE ;0 = OFF 1 = ON
DB 00H
carrier_sense_filter LABEL BYTE ;0 = OFF 1 = 0N
carrier_sense_source LABEL BYTE ;0 = external 1 = internal
collision_detect_filter LABEL BYTE ;default = 0
collision_detect_source LABEL BYTE ;0 = external 1 = internal
DB 00H
min_frame_length LABEL BYTE ;60 BYTEs
DB 60
DB 00H ;undefined
RxBufferCount dw ?
TxBufferCount dw ?
Receive_Head DW ?
Receive_Tail DW ?
HEADER_LEN equ EADDR_LEN + EADDR_LEN + MAX_P_LEN
even
our_type db HEADER_LEN dup(?)
extrn is_186: byte ;=0 if 808[68], =1 if 80[123]86.
extrn sys_features: byte
include io16.asm
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.
cmp ah,26
jne bad_command_intercept_1
jmp do_tdr
bad_command_intercept_1:
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
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
cmp pro1,1
je no_size_check1
cmp cx,GIANT ; Is this packet too large?
jna no_size_check1
jmp send_pkt_toobig
no_size_check1:
push bp
mov bp,io_addr
disable_board_ints
cmd_clear ; wait for command processor to clear
cmp pro1,1 ; on pro drive do not ajust size
je oklen
cmp cx,RUNT ; minimum length for Ether
jae oklen
mov cx,RUNT ; make sure size at least RUNT
oklen:
;
; Set SMB pointer to the transmit buffer.
;
lea dx,[BP].@SMB_Ptr
mov ax,offset send_blocks
out dx,ax
;
; Write evenized, padded packet length (>= RUNT bytes) to 586 TBD byte
; count field. OR on the End of Frame bit.
;
lea dx,[BP].@TBD_ByteCount
mov ax,cx
or ah,byte_BIT_EOF
out dx,ax
;
; Set the TxCB command field for transmit. Also set the EL bit (End of List).
;
lea dx, [BP].@TxCB_Command
mov ax, BIT_EL+GA_Transmit
out dx, ax
;
; Set write pointer to the data buffer for current send block. Also
; set DX to the data register.
;
lea dx, [BP].@Write_Ptr
mov ax, offset send_blocks
add ax, offset TB_Data
out dx, ax
mov dx, bp ;@Data_Reg
inc cx ;round size up to next even number.
and cx,not 1
call repoutsw
send_continue:
;
; Set the SCBs command block pointer to this send packet.
;
lea dx, [BP].@SCB_CBL
mov ax, offset send_blocks
out dx, ax
;
; Set the SCB command to start the command unit.
;
lea dx, [BP].@SCB_CMD
mov ax, CUC_START
out dx, ax
issue_ca
cmd_clear ; wait for frame transmit to start
lea dx,[BP].@SMB_Ptr
mov ax,offset send_blocks
out dx,ax
lea dx,[BP].@TxCB_status
transmiting:
in ax,dx
test ax,BIT_C ; check status of transmit
jz transmiting
test ax,BIT_OK ; how was the x-mit
jnz tx_ok
enable_board_ints
pop bp
mov dh,CANT_SEND
jmp tx_bad
tx_ok:
enable_board_ints
pop bp
clc
ret
send_pkt_toobig:
mov dh,NO_SPACE
tx_bad:
stc
ret
; ReadTickCounter
;
; Read the 16 bit timer tick count register (system board timer 0).
; The count register decrements by 2 (even numbers) every 838ns.
;
; Assumes: Interrupts disabled
;
; Returns: AX with the current count
; Interrupts disabled
ReadTickCounter:
push dx
mov dx, 43h ;Command 8254 timer to latch
xor al, al ; T0's current count
out dx, al
mov dx, 40h ;read the latched count
in al, dx ; LSB first
mov ah, al
in al, dx ; MSB next
xchg al, ah ;put count in proper order
pop dx
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:code
push bp
push si
;
; Set up individual address command block.
;
mov bp,ds:io_addr
cmd_clear
mov bx, BIT_EL+GA_IA_Setup
call setup_command_block
pop si
;
; Copy individual address command parameters to individual address
; command block.
;
mov cx, EADDR_LEN/2
move_node_address:
lodsw
out dx, ax
loop move_node_address
;
; Signal 586 to execute individual address command.
;
mov ax, CUC_Start
mov bx, BIT_CNA
call issue_command
lea dx, [bp].@SMB_Ptr ;Move IO frame to the command
mov ax, offset cb ; command block.
out dx, ax
lea dx,[BP].@CB_Status
working1:
in ax,dx
test ax,BIT_C ; check status of set_ia
jz working1
test ax,BIT_OK
jnz set_ok
stc
jmp set_address_done
set_ok:
mov cx,5000h ; on 486's if we do not delay this command does not work
set1:
mov bx,cx
mov ax,[bx]
loop set1 ; just for delay
mov cx,EADDR_LEN ;return their address length.
clc
set_address_done:
pop bp
ret
rcv_mode_2:
and promiscuous_mode,not 3
or promiscuous_mode,2 ;disable broadcasts.
mov min_frame_length,60
and save_bad_frame,not 80h
mov pro0,0
;;; mov pro1,0 ;not present in NSA version
jmp short configure_command
rcv_mode_4:
rcv_mode_3:
and promiscuous_mode,not 3 ;clear promiscuous mode.
mov min_frame_length,60
and save_bad_frame,not 80h
mov pro0,0
;;; mov pro1,0 ;not present in NSA version
jmp short configure_command
rcv_mode_6:
and promiscuous_mode,not 3
or promiscuous_mode,1 ;set promiscuous mode.
mov min_frame_length,60 ;allow runt. but let it come in as a bad frame
or save_bad_frame,80h ; yes we want bad frames here
mov pro0,1
mov pro1,1
;
configure_command:
mov bp,io_addr
;
; Set up configure command block.
;
mov bx, bit_el + GA_Configure ;Set command block for a
call setup_command_block ; configure command. BIT_EL
; means last command.
;
; Copy configure command parameters to configure command block.
;
mov si,offset config_params ;Set DS:SI to parameters and
mov cx, 6 ; copy them to the command
move_config_params:
lodsw
out dx, ax
loop move_config_params
;
; Signal 586 to execute configure command.
;
mov ax, CUC_Start
mov bx, BIT_CNA
call issue_command
ret
;*****************************************************************
; TDR cable tester
; enter with es:di pointing to time int
;*****************************************************************
public do_tdr
do_tdr:
push bp
push es
push di
mov bp,io_addr
;
; Set up configure command block.
;
mov bx, bit_el + GA_TDR ;Set command block for a
call setup_command_block ; TDR command. BIT_EL
; means last command.
; Signal 586 to execute configure command.
;
mov ax, CUC_Start
mov bx, BIT_CNA
call issue_command
lea dx, [bp].@SMB_Ptr ;Move IO frame to the command
mov ax, offset cb ; command block.
out dx, ax
lea dx,[BP].@CB_Status
working:
in ax,dx
test ax,BIT_C ; check status of transmit
jz working
lea dx,[bp].@CB_Parm0
in ax,dx
pop di
pop es
pop bp
test ax,LNK_OK
jz tdr_bad_cable
clc
ret
tdr_bad_cable:
push ax
and ax,TDR_TIME
stosw ; store time
pop ax
mov cl,12
shr ax,cl
and ax,7
mov dh,al
stc
ret
;
; BX = 586 command word
;
setup_command_block:
;
; Setup command block's command, status, and link fields.
;
lea dx, [bp].@SMB_Ptr ;Move IO frame to the command
mov ax, offset cb ; command block.
out dx, ax
lea dx, [bp].@CB_link ;Set command block's link to
out dx, ax ; itself.
lea dx, [bp].@SCB_CBL ;Point SCB command list
out dx, ax ; pointer to command block.
lea dx, [bp].@CB_command ;Set command block's command
mov ax, bx ; word to value passed in
out dx, ax ; BX.
;
; Return with the write pointer set to the command block parameter
; offset.
;
lea dx, [bp].@Write_Ptr ;Set write pointer to command
lea ax, CB.CB_Param0 ; parameter field.
out dx, ax
mov dx, bp ;@Data_Reg
ret
;
; Wait for 586 command register to be clear ( 1 millisecond max)
; Issue command
; Wait for command to complete (500 milliseconds max)
; Acknowledge command complete
;
;
issue_command:
;
; Make sure 586 command in SCB is clear.
;
mov cx, ax
cmd_clear
jc issue_command_error_exit
mov ax, cx
;
; Put command code in AX into SCB command register and signal the
; 586 to look at it. The cmd_clear macro leaves DX pointing to
; the SCB command register.
;
out dx, ax
issue_ca
wait_for_commad_status_outer_loop:
;
; Wait here for 586 to complete the command. 586 should never take
; more than 500 Milliseconds to complete a command.
;
call readtickcounter
mov cx, ax
lea dx, [bp].@SCB_STAT
wait_for_commad_status:
in ax, dx
cmp ax, bx
je acknowledge_command_complete
call readtickcounter
neg ax
add ax, cx
cmp ax, ten_mils
jb wait_for_commad_status
_586_command_error:
;
; Signal error by pointing AX to error message, and setting
; condition codes.
;
mov dx,offset _586_not_responding_msg
mov ah,9
int 21h
stc
ret
issue_command_error_exit:
stc
ret
acknowledge_command_complete:
;
; Tell the 586 that we know its done by copying the status back to
; the command register's acknowledge fields. BX has status.
;
lea dx, [bp].@SCB_CMD
out dx, ax
issue_ca
;
; Signal no error.
;
xor ax, ax
ret
public set_multicast_list
set_multicast_list:
;enter with ds:si ->list of multicast addresses, ax = number of addresses,
; cx = number of bytes.
;return nc if we set all of them, or cy,dh=error hf we didn't.
mov dh,NO_MULTICAST
stc
ret
public terminate
terminate:
push bp
mov bp,io_addr
cmd_clear ; wait until last command is done processing
disable_board_ints ; shut off board ints
lea dx,[bp].@SCB_CMD
mov ax, RUC_SUSPEND ; suspend recieve unit
out dx, ax
issue_ca
pop bp ; now ok to un-hook interrupts and release
ret ; driver
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
extrn count_in_err: near
extrn count_out_err: near
; extrn resource_err_in:word
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
;
; Set BP to the base IO address of exp16. Also disable exp16s ability
; to generate interrupts.
;
mov bp,io_addr
disable_board_ints
;
; Make sure that command unit is clear before reading the status.
;
cmd_clear
;
; Get the SCB status to see why exp16 interrupted us. Also need to
; copy the status back to the command word to acknowledge the 586
; interrupt. Issue channel attention to get the 586 to realize the
; interrupt aknowledge.
;
; lea dx, [bp].@SCB_RSCERRS
; in ax,dx
; mov word ptr resource_err_in,ax
; lea dx, [bp].@SCB_OVRNERRS
; in ax,dx
; add word ptr resource_err_in,ax
lea dx, [bp].@SCB_STAT
in ax, dx
and ax, ACK_INT_MASK
lea dx, [bp].@SCB_CMD
out dx, ax
issue_ca ; AX is not changed.
;
; Check for frame receive status. Jump if no frames received,
; otherwise processes the receive.
;
test AX, BIT_FR OR BIT_RNR
jz ExitDriverISR
call ProcessRx
ExitDriverISR:
;
; Here if there was a bad receive status. Make sure the receive unit
; of the 586 is running. Read in the SCB status and jump if we need
; to restart the receive unit. We choose to have the jump to restart
; the RU since most of the time we will not need to do this. This
; saves cycle times when exiting without a restart.
;
lea dx, [bp].@SCB_STAT
in ax, dx
test ax, READY
jz restart_RU
drvisr_continue:
enable_board_ints
ret
restart_RU:
;
; Need to restart the receive unit of the 586. Set the SCB pointer to
; the receive frame area.
;
lea dx, [bp].@SCB_RFA
mov ax, Receive_Head
out dx, ax
;
; Make sure 586 is in a state to accept a command.
;
cmd_clear
;
; Put the receive unit start command into the SCB command word and
; issue the channel attention.
;
; NOTE: cmd_clear macro leaves DX pointing to the SCB command word.
;
; lea dx, [bp].@SCB_CMD
mov ax, RUC_START
out dx, ax
issue_ca
jmp drvisr_continue
ProcessRx:
;
; Prime the pump that services receives by setting AX to the receive
; head. There could be over 30 packets received.
;
mov ax,Receive_Head
CheckNextReceiveStatus:
;
; Save receive head pointer in BX. BX should not be altered during
; this loop.
;
mov bx, ax
;
; Set SMB pointer to the top of the receive frame.
;
lea dx, [bp].@SMB_Ptr
out dx, ax
;
; Get the status for this receive frame. Check to make sure that
; it completed with no errors. If not, then jump to check the
; 586 receive unit.
;
lea dx, [bp].@FD_Status
in ax, dx
mov save_err,1 ; be sure we done accdently report an error
test ax, BIT_C ; command compleat
jz PRx_no
test ax,BIT_OK ; good frame
jnz ProcessRx_1
call count_in_err
mov save_err,0
cmp pro0,1
je PRx_bad ; if not in pro-mode lose bad frames and inc error
; call count_in_err
jmp recv_isr_9
PRx_no:
ret
PRx_bad:
mov dh,0 ;build save_err.
test ax,BIT_S7
jz PR_no_frag
or dh,2
PR_no_frag:
test ax,BIT_S11
jz PR_no_crc
or dh,4
PR_no_crc:
test ax,BIT_S10
jz PR_no_align
or dh,8
PR_no_align:
test ax,BIT_S8
jz PR_no_dma
or dh,16
PR_no_dma:
test ax,BIT_S9
jz PR_no_ru
or dh,32
PR_no_ru:
mov save_err,dh
ProcessRx_1:
;
; Set the read pointer to access the LengthTypeField.
;
; NOTE: BX was set to receive head at the beginning of this
; procedure.
;
lea dx, [bp].@Read_Ptr
lea ax, [BX].IPX_DestAddr_HIGH
out dx, ax
mov dx, bp ;@Data_Reg
mov ax,cs
mov es,ax
mov di,offset our_type
mov cx,HEADER_LEN
call repinsw
;
; Get the packet size from the 586 data structure.
;
lea dx, [bp].@RBD_ByteCount
in ax, dx
and ah, 3fh
mov cx, ax
mov di,offset our_type+EADDR_LEN+EADDR_LEN
mov dl, BLUEBOOK ;assume bluebook Ethernet.
mov ax, es:[di]
xchg ah, al
cmp ax, 1518
ja BlueBookPacket
inc di ;set di to 802.2 header
inc di
mov dl, IEEE8023
BlueBookPacket:
push bx
push cx ; for some reison recv_find trashes cx
mov dh,save_err
call recv_find
mov bp,cs:io_addr ; just in case
pop cx
pop bx
mov ax,es ;is this pointer null?
or ax,di
je recv_isr_9 ;yes - just free the frame.
push es ;remember where the buffer pointer is.
push di
push cx
if 0
;
; Copy the packet's header into their buffer.
;
push cx
mov si,offset our_type
mov cx,HEADER_LEN/2
rep movsw
pop cx
sub cx,HEADER_LEN
endif
;
; Set the read pointer to access the packet.
;
; NOTE: BX was set to receive head at the beginning of this
; procedure.
;
lea dx, [bp].@Read_Ptr
if 0
lea ax, [BX].IPX_DestAddr_HIGH + HEADER_LEN
else
lea ax, [BX].IPX_DestAddr_HIGH
endif
out dx, ax
mov dx, bp ;@Data_Reg
;
; Now read the packet into es:di
;
call repinsw
pop cx
pop si
pop ds
assume ds:nothing
call recv_copy ;tell them that we copied it.
mov ax,cs ;restore our ds.
mov ds,ax
assume ds:code
recv_isr_9:
;
; Restore SMB pointer to the top of the receive frame.
;
mov bp,io_addr ; just in case
mov ax,bx
lea dx, [bp].@SMB_Ptr
out dx, ax
;
; Clear the current receive frames status.
;
lea dx, [bp].@FD_Status
xor ax, ax
out dx, ax
;
; Set the end of list bit for the current receive frame.
;
lea dx, [bp].@FD_command
mov ax, BIT_EL
out dx, ax
;
; Read in the pointer to the next receive frame.
;
lea dx, [bp].@FD_Link
in ax, dx
;
; Update our local pointer to the receive resources:
;
; Receive_tail <-- Receive head
; Receive_head <-- next receive frame.
;
mov BX, Receive_Head
mov Receive_Tail, BX
mov Receive_Head, AX
mov cx, ax
;
; Clear previous receive frame descriptors end of list bit to free up
; a receive resource. We do this by moving the write pointer to the
; the command word in the previous receive frame and then writing a
; zero to it.
;
lea dx, [bp].@Write_Ptr
lea ax, [bx].FD_command
out dx, ax
mov dx, bp ;@Data_Reg
xor ax, ax
out dx, ax
;
; Check for another receive frame.
;
mov ax, cx
jmp CheckNextReceiveStatus
; This procedure is called from the cmd_clear macro after the macro determines
; that we must wait for a command to clear.
;
; MUST NOT TRASH BX or CX OR THE ISSUE_COMMAND PROCEDURE WILL FAIL.
;
cmd_wait:
;enter with dx -> SCB_CMD
;exit with nc if it cleared in one millisecond, cy if not.
push bx
call readtickcounter ;Reference clock. die after
mov bx, ax ; 1 ms.
wait_cmd_clear:
in ax, dx ;Read SCB command
or ax, ax ;Wait for command accepted
jz exit
call readtickcounter
neg ax
add ax, bx
cmp ax, one_mil
jb wait_cmd_clear
stc
exit:
pop bx
ret
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 after initialization. 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
end_free_mem label byte
public usage_msg
usage_msg db "usage: exp16 [options] <packet_int_no> <io_addr>",CR,LF,'$'
public copyright_msg
copyright_msg db "Packet driver for the Intel EtherExpress 16, version ",'0'+(majver / 10),'0'+(majver mod 10),".",'0'+version,CR,LF
db "Copyright 1991 Intel Corp",CR,LF
db "Portions Copyright 1993 Crynwr Software",CR,LF,'$'
int_no_name db "Interrupt number ",'$'
io_addr_name db "I/O port ",'$'
extrn set_recv_isr: near
;enter with si -> argument string, di -> dword to store.
;if there is no number, don't change the number.
extrn get_number: near
;enter with dx -> argument string, di -> dword to print.
extrn print_number: near
;-> the assigned Ethernet address of the card.
extrn rom_address: byte
chan_sel db 0 ;Channel select for SYS_MCA machines
public etopen
etopen:
;initialize the driver.
assume ds:code
;
; Get board information (Node address, etc.). Returns with BP
; set to the board IO base address (if not errors).
;
call get_system_info
jc driver_init_error_exit
disable_board_ints
;
; If they're in an 8-bit slot, make sure that they aren't using the slave PIC.
;
cmp _16_not_8_bit_slot,0 ;are they using a 16-bit slot?
jne check_config_exit ;yes -- cool.
mov dx,offset irq_config_error
cmp int_no, 9 ;no - don't let them use the upper IRQs.
jb check_config_exit
driver_init_error_exit:
stc
ret
check_config_exit:
;
; Initialize the 586 and the 586 data structures.
;
call init_586
jc driver_init_error_exit
; test iochrdy
test sys_features,SYS_MCA
jnz skip_iochrdy_test
call iochrdy_test
skip_iochrdy_test:
cmp connection_type,CONN_AUTO
jne no_auto_con
call auto_connector
jnc did_auto_con
mov dx,offset no_wire_msg
mov ah,9
int 21h
jmp short did_auto_con
no_auto_con:
call write_connector_setting_to_hardware
did_auto_con:
;
; Set up Interrupt line, start the receive unit, and Enable exp16s interrupt.
;
call set_recv_isr
call ru_start
enable_board_ints ;;; only one left in broken version.
mov al, int_no ; Get board's interrupt vector
add al, 8
cmp al, 8+8 ; Is it a slave 8259 interrupt?
jb set_int_num ; No.
add al, 70h - 8 - 8 ; Map it to the real interrupt.
set_int_num:
xor ah, ah ; Clear high byte
mov int_num, ax ; Set parameter_list int num.
clc
ret
write_connector_setting_to_hardware:
assume ds:code
test sys_features,SYS_MCA
je isa_connector_bart
mov bl,80h
cmp connection_type,CONN_TPE ;did they ask for TPE?
je mca_tpe_connector
mov bl,0
mca_tpe_connector:
call enable_slot
mov dx,102h ;stuff the AUI/Other bit in.
in al,dx
and al,not 04h
cmp connection_type,CONN_AUI
je mca_tpe_connector_1
or al,04h
mca_tpe_connector_1:
out dx,al
mov dx,103h ;stuff the BNC/TPE bit in.
in al,dx
and al,not 80h
cmp connection_type,CONN_BNC
je mca_tpe_connector_2
or al,80h ;<>0 means TPE
mca_tpe_connector_2:
out dx,al
call disable_slot
jmp no_connector_bart
isa_connector_bart:
cmp board_id,BART_Board_ID ;a real BART?
je no_connector_bart ;yes, it doesn't *have* an ECR1.
mov bp,io_addr
lea dx,ECR1[bp]
in al,dx
and al,not 82h ;turn off Other/not_AUI (read as
; MCA/ISA), and TPE/not_BNC
cmp connection_type,CONN_AUI
je set_connector_bart
or al,80h ;set Other/not_AUI
cmp connection_type,CONN_BNC
je set_connector_bart
or al,02h ;set TPE/not_BNC
set_connector_bart:
out dx,al
no_connector_bart:
mov cx,10 ;10*10 milliseconds = 100 millis
cmp connection_type,CONN_AUI
jne set_connector_delay
mov cx,50 ;50*10 milliseconds = 500 millis
set_connector_delay:
; Our delay procedure can not accept a value over 27 milliseconds,
; so we use a loop to delay the 100 milliseconds.
set_connector_delay_1:
push cx
mov di, ten_mils
call eeprom_delay
pop cx
loop set_connector_delay_1
ret
; int number to IRQ translate table.
IRQ_xlat_table LABEL BYTE
db 12, 2, 3, 4, 5, 10, 11, 15
io_addresses label word
dw 300h,310h,320h,330h,340h,350h,360h,370h
dw 200h,210h,220h,230h,240h,250h,260h,270h
dw 0
enable_slot:
mov al,chan_sel
out 96h, al ; select channel
mov al, 0ffh
out 94h, al ; protect system board
ret
disable_slot:
xor al,al ;disable pos
out 96h,al
ret
public parse_args
parse_args:
;exit with nc if all went well, cy otherwise.
test sys_features,SYS_MCA
jne parse_args_mc
jmp parse_args_1
parse_args_mc:
;The following code to read the POS registers is courtesy of Racal-Interlan.
; channel selector resides at io 96h
; POS register base is at io 100h
; EXP16/MC ID is 0628bh
; search thro' the slots for a EXP16/MC card
mov cx, 8 ; for all channels(slots)
; channel select value for slots 0,1,2.. is 8,9,A etc
; start with slot 0, and then 7,6,5,4,3,2,1
get_05:
mov ax, cx ; channel number
or ax, 08h ; reg. select value
mov chan_sel,al ; save each one, the LAST will right
; read adapter id
call enable_slot
mov dx, 101h
in al, dx ; adapter id - ms byte
mov ah, al
dec dx
in al, dx ; adapter id - ls byte
; Check if EXP16/MC
cmp ax, 0628bh
je get_10
loop get_05
call disable_slot
mov dx,offset Board_Not_Installed
stc
ret
get_10:
; found our Adapter
call write_pos_to_eeprom
clc
ret
parse_args_1:
mov di,offset io_addr
call get_number
clc
ret
include exp16mca.asm
;-----------------------------------------------------------------------------
; It is safe to assume that the command unit is up and running ready
; for the first send. And that there are at least two send buffers
; available.
;
; This procedure works by sending a 64 byte packet to itself. We do not turn
; on loopback, so we do not expect to see a receive. Instead, we
; expect to see that the send completed OK. If the send does not complete OK,
; then this routine switches to other connectors and the test is repeated.
;-----------------------------------------------------------------------------
auto_connector:
; Let's start doing the test send with AUI option first. If we tried BNC
; first, and they were using AUI, it would seem like the BNC worked. So
; we switch to AUI, which turns off the BNC power supply.
MOV connection_type, CONN_AUI
CALL write_connector_setting_to_hardware
; Test connection to see if it works. Jump if it does.
call test_send
clc
je auto_connector_exit
; Toggle AUI/Other setting and try to send a packet. Jump if send
; works. Now try sending on BNC.
MOV connection_type, CONN_BNC
CALL write_connector_setting_to_hardware
call test_send
clc
je auto_connector_exit
; Here if we just tried a send the BNC. If this is
; a three connector card, then we toggle the BNC/TPE setting.
; Jump if connection is found.
MOV connection_type, CONN_TPE
CALL write_connector_setting_to_hardware
call test_send
clc
je auto_connector_exit
stc
auto_connector_exit:
ret
test_packet label byte
db EADDR_LEN dup(?)
db EADDR_LEN dup(?)
db 00h,2eh ;A 46 in network order
db 0,0 ;DSAP=0 & SSAP=0 fields
db 0f3h,0 ;Control (Test Req + P bit set)
; This procedure sends a packet on the wire and wait for the send to
; complete. It the checks to see if the send completed OK and returns
; the results.
test_send:
mov si,offset rom_address ;set the destination address.
movseg es,cs
mov di,offset test_packet
repmov EADDR_LEN
mov si,offset rom_address ;set the source address.
repmov EADDR_LEN
mov cx,60
mov si,offset test_packet
call send_pkt
call ReadTickCounter
mov cx, ax
mov bp,io_addr
lea dx, [bp].@TxCB_Status
send_test_wait:
in ax,dx
test ax,8000h ;did it finish?
jne send_test_done
; See if we have been here for a millisecond. Jump if not.
call ReadTickCounter
neg ax
add ax, cx
cmp ax, one_mil
jb send_test_wait
in ax,dx ;get the transmit status back again.
send_test_done:
mov cx, ax ; Save status in CX.
xor ax, ax ; Clear the transmit command block status.
out dx, ax
; Send is complete. Get the status and compare to OK send and return.
; Save flags for return.
and cx, 0f000h
cmp cx, 0a000h
ret
no_wire_msg db "***Unable to detect a cable on any connector***",CR,LF,'$'
bnc_connector_msg db "Using the BNC connector",CR,LF,'$'
aui_connector_msg db "Using the AUI connector",CR,LF,'$'
tpe_connector_msg db "Using the twisted-pair connector",CR,LF,'$'
_8_bit_slot_msg db "The board is in an 8-bit slot",CR,LF,'$'
_16_bit_slot_msg db "The board is in a 16-bit slot",CR,LF,'$'
badset_msg db "Set Address Command Failed",CR,LF,'$'
public print_parameters
print_parameters:
mov di,offset int_no
mov dx,offset int_no_name
call print_number
mov io_addr.segm,0
mov di,offset io_addr
mov dx,offset io_addr_name
call print_number
mov dx,offset bnc_connector_msg ;print the connection type.
cmp connection_type,CONN_BNC
je print_parameters_1
mov dx,offset aui_connector_msg
cmp connection_type,CONN_AUI
je print_parameters_1
mov dx,offset tpe_connector_msg
print_parameters_1:
mov ah,9
int 21h
mov dx,offset _16_bit_slot_msg ;print the slot size.
cmp _16_not_8_bit_slot,0
jne print_parameters_2
mov dx,offset _8_bit_slot_msg
print_parameters_2:
mov ah,9
int 21h
ret
init_586:
;enter with bp=io_addr, 586 reset.
;
; Set the number of transmit and receive buffers according to the
; amount of RAM on the adapter.
;
MOV AX, Number_of_Tx_Buffers
MOV BX, num_rx_buf_32k
cmp _64K_not_32K,0
je save_number_of_buffers
MOV BX, num_rx_buf_64k
save_number_of_buffers:
MOV TxBufferCount, AX
MOV RxBufferCount, BX
;
; initialize SCP. Move IO frame to top of SCP.
;
LEA DX, [bp].@SMB_Ptr
MOV AX, OFFSET scp
OUT DX, AX
;
; Write initial SCP values:
; bus width 0 = 16 bit 1 = 8 bit.
; ISCP address always found at address 000000.
;
LEA DX, [BP].@SCP_SystemBus
MOV AL, bus_width
OUT DX, AX
LEA DX, [BP].@SCP_ISCP_Ptr_low
XOR AX, AX
OUT DX, AX
LEA DX, [BP].@SCP_ISCP_Ptr_high
OUT DX, AX
;
; Initialize ISCP. Move IO frame to top of ISCP. NOTE: AX falls
; through as zero.
;
LEA DX, [bp].@SMB_Ptr
OUT DX, AX
;
; Write initial ISCP values:
; ISCP BUSY 1 = Indicates 586 is in initialization
; process. 586 sets this byte to 0 when
; initialization is complete.
;
; SCB address Always 000008
;
LEA DX, [BP].@iscp_busy
MOV AX, initialize_586
OUT DX, AX
LEA DX, [BP].@ISCP_SCB_Ptr_low
XOR AX, AX
OUT DX, AX
LEA DX, [BP].@ISCP_SCB_Ptr_high
OUT DX, AX
LEA DX, [BP].@iscp_scb_offset
MOV AX, OFFSET scb
OUT DX, AX
;
; Initialize SCB. Move Write Ptr to top of SCB. NOTE: AX falls
; through as offset to SCB.
;
XOR AX, AX
LEA DX, [BP].@SCB_Status
OUT DX, AX
LEA DX, [BP].@SCB_Command
OUT DX, AX
LEA DX, [BP].@SCB_CommandList
OUT DX, AX
LEA DX, [BP].@SCB_RecBlockList
OUT DX, AX
LEA DX, [BP].@SCB_CRC_Errors
OUT DX, AX
LEA DX, [BP].@SCB_ALN_Errors
OUT DX, AX
LEA DX, [BP].@SCB_RSC_Errors
OUT DX, AX
LEA DX, [BP].@SCB_OVR_Errors
OUT DX, AX
;
; Initialize Receive Block(s). First set head pointer and link from
; SCB to first frame descriptor. NOTE: IO frame points to ISCB.
; The SCB link field can be addresses becasue the SCB is physically
; contiguous to the ISCB.
;
mov ax,offset Receive_Blocks
lea dx, [bp].@SCB_RecBlockList ;IO address of SCB RF link
out dx, ax
;
; Initialize the frame descriptor structures. Move the Write ptr
; the top of the current frame descriptor. AX falls through with the
; offset to the first frame descriptor. DI is used to keep track of
; the address of the current frame descriptor. The frame descriptor,
; receive buffer descriptor, and receive buffer are all contiguous in
; memory, and make up the frame descriptor structure.
;
MOV DI, AX ;DI is current frame descriptor
MOV RECEIVE_HEAD, DI
MOV CX, RxBufferCount ;CX is number of frame
; descriptors to initialize.
init_receive_frames:
LEA DX, [bp].@SMB_Ptr
MOV AX, DI ; descriptor.
OUT DX, AX
;
; Init Frame Descriptor (FD).
;
XOR AX, AX ;Init frame descriptor status
LEA DX, [BP+4000H]
OUT DX, AX ; and command to zero.
LEA DX, [BP+4002H]
OUT DX, AX
MOV AX, RECEIVE_HEAD ;Init frame descriptors link
CMP CX, 1 ; to next frame descriptor.
JE next_receive_link ; If this is the last receive
; block, then set its link to
LEA AX, [DI+SIZE ReceiveBlock] ; the first receive block.
next_receive_link:
LEA DX, [BP+4004H]
OUT DX, AX
LEA AX, [DI+OFFSET RBD_ByteCount] ;Init frame descriptors
LEA DX, [BP+4006H]
OUT DX, AX ; link to its receive buffer descriptor.
;
; Init Receive Buffer Descriptor (RBD).
;
LEA DX, [BP+4008H]
OUT DX, AX ;Init the RBD Actual count to zero.
MOV AX, -1 ;Init the RBD link to next RBD
LEA DX, [BP+400AH]
OUT DX, AX ; to minus 1 (unused link).
LEA AX, [DI+RB_Data] ;Init the RDB link to the data
LEA DX, [BP+400CH]
OUT DX, AX ; buffer.
XOR AX, AX
LEA DX, [BP+400EH]
OUT DX, AX
MOV AX, RxBufferSize OR BIT_EL ;Init the RBD count and status
LEA DX, [BP+8000H]
OUT DX, AX
MOV RECEIVE_TAIL, DI ;Set receive tail pointer.
ADD DI, SIZE ReceiveBlock ;Point DI to next frame
LOOP init_receive_frames ; descriptor.
;
; Initialize the send block structures. Move the Write pointer the
; top of the current send block. AX falls through with address of
; first send block. DI is used to keep track of the address of the
; current send block. The Transmit command block, transmit buffer
; descriptor, and transmit buffer are all contiguous in memory, and
; make up the send block structure.
;
MOV DI, OFFSET send_blocks
MOV CX, TxBufferCount
LEA DX, [BP].@SMB_Ptr
XOR AX, AX
OUT DX, AX
MOV AX, DI
LEA DX, [BP].@SCB_CommandList
OUT DX, AX
init_send_blocks:
LEA DX, [BP].@SMB_Ptr ;Set write pointer to send
MOV AX, DI ; block.
OUT DX, AX
;
; Init Transmit Control Block (TCB).
;
XOR AX, AX ;Init transmit control block
LEA DX, [BP+4000H]
OUT DX, AX ; status to zero.
LEA DX, [BP+4002H]
OUT DX, AX ;Init transmit control block
; command to anything. The
MOV AX, offset send_blocks ; Init transmit control block
CMP CX, 1 ; link to next send block.
JE next_send_link ; If this is the last send
; block, then point it to
LEA AX, [DI+SIZE SendBlock] ; the first block.
next_send_link:
LEA DX, [BP+4004H]
OUT DX, AX
LEA AX, [DI+OFFSET TBD_ByteCount] ;Init transmit control block
LEA DX, [BP+4006H]
OUT DX, AX ; link to Transmit Buffer
; descriptor.
;
; Init Transmit Buffer Descriptor (TBD).
;
LEA DX, [BP+4008H]
OUT DX, AX ;Init Transmit Buffer Descriptor
; byte count to anything
MOV AX, -1 ;Init Transmit Buffer Descriptor
LEA DX, [BP+400AH]
OUT DX, AX ; link to next TBD.
LEA AX, [DI+OFFSET TB_Data] ;Init Transmit Buffer Descriptor
LEA DX, [BP+400CH]
OUT DX, AX ; link to transmit
XOR AX, AX ; buffer. Low part of pointer
LEA DX, [BP+400EH]
OUT DX, AX ; is done first.
ADD DI, SIZE SendBlock ;Point DI to next send block
LOOP init_send_blocks ; and loop to initialize it.
;
; Enable loopback to insure nothing acidently hits the cable while
; the 586 gets initialized.
;
LEA DX, [BP].@Config
IN AX, DX
OR AL, loopback_enable
OUT DX, AL
;
; Free the 586 from reset.
;
CALL free_586_reset
;
; Initialize and configure the 586. Carry flag set means error and
; dx will point to error message.
;
CALL init_cmd
mov dx,offset bad_init_msg
jc init_exp16_ram_exit
CALL configure_command
mov dx,offset bad_config_msg
jc init_exp16_ram_exit
CALL diagnose_command
jc init_exp16_ram_exit
movseg es,cs
mov si,offset rom_address
mov cx,EADDR_LEN
call set_address
jnc sa_ok
mov dx,offset badset_msg
mov ah,9
int 21h
sa_ok:
;
; Disable loopback.
;
LEA DX, [BP].@Config
IN AX, DX
AND AL, NOT loopback_enable
OUT DX, AL
clc
ret
init_exp16_ram_exit:
stc
RET
; This procedure must wait until the ASIC finishes reset. This will take
; around 240 uSec. This loop will time out after 500 uSec.
;
five_hundred_micros EQU 1194
;
reset_board:
;enter with bp=io_addr
LEA DX, [BP].@EEPROM_Ctrl
MOV AL, ASIC_Reset
OUT DX, AL
XOR AL, AL
OUT DX, AL
;
; Get current tick count. This loop will wait for 500 uSec to pass
; before failing.
;
CALL ReadTickCounter
MOV BX, AX
reset_delay:
PUSH BX
LEA DX, [BP].@ID_Port ; Set DX to the auto-id port.
CALL check_for_exp16_hardware
POP BX
CMP AX, BART_Board_ID
JE reset_board_exit
CALL ReadTickCounter
NEG AX
ADD AX, BX
CMP AX, five_hundred_micros
JB reset_delay
mov dx, offset reset_error
stc
RET
reset_board_exit:
MOV BX, AX
clc
RET
reset_586:
;enter with bp = io_addr
lea dx, [bp].@EEPROM_Ctrl
mov al, _586_Reset
out dx, al
ret
free_586_reset:
;enter with bp = io_addr
lea dx, [bp].@EEPROM_Ctrl
xor al, al
out dx, al
ret
check_for_exp16_hardware:
;enter with dx = ID port (or maybe shadow ID port).
;
; BX will have board ID when the loop is done. CX is the loop count.
;
XOR BX, BX
MOV CX, 4
get_board_id_loop:
;
; Init registers for loop. CX needs to be used in the loop, and AH
; could be set from the last loop.
;
PUSH CX
XOR AH, AH
;
; Read ID port. See description above.
;
IN AL, DX
;
; Make nibble ID a shift count in CL.
;
MOV CL, AL
AND CL, 00000011B
SHL CL, 1
SHL CL, 1
;
; Move ID nibble to low order bits of AX, then shift then into place. Put
; the board ID nibble into BX. After four passes, BX will have board ID.
;
SHR AL, 1
SHR AL, 1
SHR AL, 1
SHR AL, 1
SHL AX, CL
OR BX, AX
;
; Recover loop count, and loop.
;
POP CX
LOOP get_board_id_loop
;
; Return board ID in AX.
;
MOV AX, BX
RET
;
; Command #1: Initialize
;
init_cmd:
XOR AX, AX ; Init command
MOV BX, BIT_CX + BIT_CNA ; status
CALL issue_command
RET
diagnose_command:
;return nc if no problem, else cy,dx->error message.
;
; Set up individual address command block.
;
MOV BX, BIT_EL+GA_diagnose
CALL setup_command_block
;
; Execute the diagnose command.
;
MOV AX, CUC_Start
MOV BX, BIT_CNA
CALL issue_command
;
; Check diagnostics results.
;
LEA DX, [BP].@SMB_Ptr ;Move IO frame to the command
MOV AX, OFFSET cb ; command block.
OUT DX, AX
LEA DX, [BP].@mem_loc_0 ;Read in the diagnose status
IN AX, DX ; word.
;
; Assume 586 failed the test. Set AX to an error message and exit
; with the zero bit cleared.
;
test ax, 0800H ;Test failure bit. If set,
jnz diagnose_error ; then error exit.
clc
ret
diagnose_error:
mov dx,offset _586_diagnostic_failure
stc
ret
;
; Command #4: RU_START
;
ru_start:
;
; Set SCBs pointer to receive blocks at head of list.
;
lea dx, [bp].@SCB_RFA ;Set pointer to receive blocks
mov ax, receive_head ; to the head of the receive
out dx, ax ; block list.
;
; Signal 586 to start the receive unit. cmd_clear leaves DX pointing
; to SCB command register.
;
cmd_clear
mov ax, ruc_start
out dx, ax
issue_CA
ret
find_a_board:
mov bx,offset io_addresses
no_exp16_here:
mov bp,[bx]
cmp bp,0
je no_exp16s
inc bx
inc bx
push bx
call reset_board
pop bx
jc no_exp16_here
clc
ret
no_exp16s:
stc
ret
get_system_info:
;exit with zr if okay, or nz, dx -> error message if not okay.
cmp io_addr,-1
jne addr_override
call find_a_board
jnc board_id_ok
jmp bad_or_no_board
addr_override:
MOV bp, io_addr
;
; Next, get the exp16 board ID. If the ID is not what we expect, then
; exit with error code in AX.
;
call reset_board
jnc board_id_ok
;
; Could search for exp16 board, but we error exit instead.
;
bad_or_no_board:
mov dx, offset board_not_installed
stc
JMP SHORT get_system_info_exit
board_id_ok:
mov io_addr,bp
LEA DX, [BP].@ID_Port ; Set DX to the auto-id port.
or DX,3000h ;use the shadow port
CALL check_for_exp16_hardware
mov board_id,ax
;
; Since the software will be reading the EEPROM during the
; initialization, the 586 needs to be inactive. This is because the
; control lines to the EEPROM are shared between the 586 and this
; software. To keep from getting fouled up, the 586 reset line is
; asserted here (now that we have a base IO address, and we know that
; the hardware is present). The 586 is not released from reset until
; after its data structures are intialized.
;
CALL reset_586
;
; Validate the EEPROM by doing a checksum.
;
mov cx, 40h
CALL check_eeprom
JC get_system_info_exit
;
; Get the amount of memory on the adapter.
;
CALL test_buffer_memory
JC get_system_info_exit
;
; Get the connection type. If carry clear, then AL has connection
; type. Otherwise AX has error code.
;
CALL get_connection_type
JC get_system_info_exit
MOV connection_type, AL
;
; Read the Ethernet address out of the EEPROM.
;
MOV AX, EE_ETHERNET_ADD_HIGH
CALL read_eeprom
xchg ah,al
MOV word ptr rom_address[0], AX
MOV AX, EE_ETHERNET_ADD_MID
CALL read_eeprom
xchg ah,al
MOV word ptr rom_address[2], AX
MOV AX, EE_ETHERNET_ADD_LOW
CALL read_eeprom
xchg ah,al
MOV word ptr rom_address[4], AX
mov ax,EE_INT
call read_eeprom
mov cl,EE_SHIFT
shr ax,cl
mov encoded_int_no,al
mov bx,offset irq_xlat_table
xlat
mov int_no,al
;
; Get the Slot width. If carry clear, then AL has the slot width.
; Otherwise AX has error code.
;
LEA DX, [BP].@Config
IN AL, DX
;
; Slot width is found in the adapters configuration register.
;
and al, slot_width_mask
mov _16_not_8_bit_slot, al
clc
ret
get_system_info_exit:
;we only ever get here with cy set.
RET
;
;
; Return connection type in AL with carry clear.
;
;
get_connection_type:
cmp board_id,BART_Board_ID ;a real BART?
je get_auto_cnt_exit ;yes, it doesn't *have* an ECR1.
MOV AX, AUTO_CON_ADDRESS ;get the auto-connection bit.
CALL read_eeprom
TEST AX, AUTO_CON_MASK ;set?
Je get_auto_cnt_exit ;no, okay.
mov bl,CONN_AUTO ;yes, we should autosense the connector.
jmp short get_connection_type_ret
get_auto_cnt_exit:
;
; Read the connection address from the EEPROM.
;
MOV AX, connection_address
CALL read_eeprom ;Read_eeprom does not trash DI
;
; Assume AUI type connection. Check connection fields to see if this
; is a BNC connection. Jump if it is BNC.
;
MOV BL, CONN_AUI
TEST AX, CONNECTION_FIELD
JZ get_connection_type_ret
;
; Here if NOT BNC. Must read another EEPROM word to tell if AUI or
; TPE. Assume AUI and then check for TPE.
;
MOV AX, tpe_address
CALL read_eeprom ;Read_eeprom does not trash DI
;
; Check for AUI connection.
;
mov bl,CONN_BNC
TEST AX, TPE_type_field
JZ get_connection_type_ret
;
; Here if TPE type connection.
;
MOV BL, CONN_TPE
get_connection_type_ret:
MOV AL, BL
CLC
RET
;
;
; Do a checksum on the EEPROM.
;
; The checksum is the same as the board ID.
;
check_eeprom:
;enter with bp=io_addr, cx = count of bytes to sum starting at 0.
;exit with nc if okay, cy, dx -> error message if not.
xor bx, bx
checksum_loop:
mov ax, cx
dec ax
call read_eeprom
add bx, ax
loop checksum_loop
cmp bx, BART_Board_ID
jne checksum_error
clc
ret
checksum_error:
mov dx,offset eeprom_checksum_error
stc
ret
test_buffer_memory:
;enter with bp=io_addr
;exit with nc and al=_64k_not_32k if no error, or cy and di -> address in error.
; Set up SI with the maximum number of words to test. If there is an
; error testing the high 32K of memory, then we will restart the
; test at 32K. When the tests pass, the memory size is passed back
; in AX.
;
mov si, 64 * (1024/2)
;
; Warm up the buffer memory with 16 word writes.
;
mov cx, 16
call write_zeros
cmp cs:is_186,0
jne start_memory_tests
restart_memory_tests:
;
; Here if error testing memory and SI is set to 64K buffer size.
; We restart the tests for 32K buffer only. If an error occurs
; with SI set to 32K, then a memory error is reported.
;
mov si, 32 * (1024/2)
start_memory_tests:
;
; Zero RAM. Set write pointer to the base address and then fill the
; bufffer memory with zeros.
;
mov cx, si
call write_zeros
call word_memory_test_pattern
jc buffer_mem_error_exit
cmp cs:is_186,0
je _8088_quick_exit
mov cx, si
call write_zeros
call byte_memory_test_pattern
jc buffer_mem_error_exit
_8088_quick_exit:
;
; Zero RAM. Set write pointer to the base address and then fill the
; bufffer memory with zeros.
;
mov cx, si
call write_zeros
;
; Set _64K_not_32K to the size of the memory that passed diagnostics.
;
cmp si, 32 * (1024/2) ;did we quit at 32K
je buffer_mem_exit ;yes, we only have 32K.
mov _64K_not_32K,1 ;no, we must have 64K.
buffer_mem_exit:
clc
ret
buffer_mem_error_exit:
;
; If error occured, and SI is not set for 32K, then retry tests with
; SI set for 32K. If the error occurs and the size is 32K, then the
; Exp16 board memory is bad.
;
CMP SI, 32 * (1024/2)
JNE restart_memory_tests
MOV DX, OFFSET buffer_memory_error
STC ;Set carry to indicate error.
RET
write_zeros:
;enter with bp=io_addr, cx=number of zero words to write.
;
; Move write pointer to the beginning of the buffer memory.
;
LEA DX, [BP].@Write_Ptr
XOR AX, AX
OUT DX, AX
;
; Set DX to the data register and write out zeros CX times.
;
mov dx, bp ;@Data_Reg
warm_up:
out dx, ax
loop warm_up
ret
word_memory_test_pattern:
;enter with si = number of words to test.
;
; Set CX to number of words to test. Set BX to beginning og pattern.
;
MOV CX, SI
MOV BX, 1
;
; Set DI to beginning of the buffer.
;
XOR DI, DI
word_inc_pattern:
CALL loop_set_up
IN AX, DX
OR AX, AX
JNE word_memory_test_pattern_error
;
; Write Test Pattern.
;
MOV AX, BX
OUT DX, AX
;
; Increment BX to next test pattern value.
;
ADD BX, 3
;
; Increment DI to next test memory location.
;
ADD DI, 2
LOOP word_inc_pattern
;
; Set Read pointer to beginning of buffer.
;
LEA DX, [BP].@read_ptr
XOR AX, AX
OUT DX, AX
MOV DX, BP ;@Data_Reg
;
; Set CX to number of words to test. Set BX to beginning og pattern.
;
MOV CX, SI
MOV BX, 1
word_check_pattern:
IN AX, DX
CMP AX, BX
JNE word_memory_test_pattern_error
;
; Increment BX to next test pattern value.
;
ADD BX, 3
LOOP word_check_pattern
CLC
RET
word_memory_test_pattern_error:
STC
RET
byte_memory_test_pattern:
;enter with si = number of bytes to test.
;
; Set CX to number of bytes to test. Set BX to beginning of pattern.
;
MOV CX, SI
SHL CX, 1
MOV BL, 1
;
; Set DI to beginning of the buffer.
;
XOR DI, DI
byte_inc_pattern:
CALL loop_set_up
IN AL, DX
OR AL, AL
JNE byte_memory_test_pattern_error
;
; Write Test Pattern.
;
MOV AL, BL
OUT DX, AL
;
; Increment BX to next test pattern value.
;
ADD BL, 3
;
; Increment DI to next test memory location.
;
INC DI
LOOP byte_inc_pattern
;
; Set Read pointer to beginning of buffer.
;
LEA DX, [BP].@read_ptr
XOR AX, AX
OUT DX, AX
MOV DX, BP ;@Data_Reg
;
; Set CX to number of bytes to test. Set BX to beginning of pattern.
;
MOV CX, SI
SHL CX, 1
MOV BL, 1
byte_check_pattern:
IN AL, DX
CMP AL, BL
JNE byte_memory_test_pattern_error
;
; Increment BX to next test pattern value.
;
add bl, 3
loop byte_check_pattern
clc
ret
byte_memory_test_pattern_error:
stc
ret
loop_set_up:
;enter with bp=io_addr, di=buffer address.
;exit with ax=value at di, dx=I/O address to access buffer memory at DI
PUSH DI
MOV AX, DI ;SMB_Ptr must be on 16 byte
AND AX, 0FFE0H
AND DI, 0001FH
LEA DX, [BP].@SMB_Ptr ;Set IO page frame to the
OUT DX, AX
TEST DI, 0010H
JZ loop_set_up_1
ADD DI, @MEM_LOC_0
loop_set_up_1:
AND DI, 0FFEFH
LEA DX, [BP+DI].@MEM_LOC_0 ;Set DX for IO from buffer and
POP DI
RET
read_eeprom:
;enter with bp=io_addr, ax = EEPROM location.
;exit with ax = EEPROM contents.
;preserves di.
push di
push bx
push cx
; Point to EEPROM control port.
lea dx, [bp].@eeprom_ctrl
mov bx, ax
; Select the EEPROM. Mask off the ASIC and 586 reset bits and set
; the ee_cs bit in the EEPROM control register.
in al, dx
and al, 10110010b
or al, ee_cs
out dx, al
; Write out read opcode and EEPROM location.
mov ax, eeprom_read_opcode ;Set AX to READ opcode and
mov cx, 3 ;Send it to the EEPROM circuit
call shift_bits_out
mov ax, bx ;Tell EEPROM which register is
mov cx, 6 ; to be read. 6 bit address.
call shift_bits_out
call shift_bits_in ;AX gets EEPROM register
call eeprom_clean_up ;Leave EEPROM in known state.
pop cx
pop bx
pop di
ret
;-----------------------------------------------------------------------------
; Writes the specified value to the specified EEPROM register at the
; specified base I/O address.
;
; Entry - BP Base IO.
; AX EEPROM location to write.
; BX Value to write
;
; Return - AX = 0 if no error
; AX = Pointer to Error message
;
;-----------------------------------------------------------------------------
write_eeprom:
;enter with bp=io_addr, ax = EEPROM location, bx = new contents.
;exit with ax = 0 if no error, ax -> error if error.
;preserves di.
push bx
mov bx, ax
lea dx, [bp].@EEPROM_Ctrl ;Point to EEPROM port.
in al, dx ;Select EEPROM
and al, 10110000b
or al, ee_cs
out dx, al
; Send Erase/write enable opcode to the EEPROM.
mov ax, EEPROM_EWEN_opcode ;Send erase/write enable
mov cx, 5 ; command to the EEPROM.
call shift_bits_out
mov cx, 4 ;Send 4 don't cares as
call shift_bits_out ; required by the eeprom
call stand_by
; Send the erase opcode to the EEPROM and wait for the command to
; complete.
mov ax, EEPROM_erase_opcode ;Send Erase command to the
mov cx, 3 ; EEPROM.
call shift_bits_out
mov ax, bx ;Send EEPROM location the the
mov cx, 6 ; EEPROM. 6 bit address.
call shift_bits_out
call wait_eeprom_cmd_done ;wait for end-of-operation
jc write_fault_pop ; go if error
call stand_by
; Send the write opcode, location to write, and data to write to the
; EEPROM. Wait for the write to complete.
mov ax, EEPROM_write_opcode ;Send write command to the
mov cx, 3 ; EEPROM.
call shift_bits_out
mov ax, bx ;Send the EEPROM location to
mov cx, 6 ; the EEPROM. 5 bit address.
call shift_bits_out
pop ax ;Send data to write to the
mov cx, 16 ; EEPROM. 16 bits.
call shift_bits_out
call wait_eeprom_cmd_done ;Await end-of-command
jc write_fault ;go if error
call stand_by
; Send the erase write disable command to the EEPROM.
mov ax, EEPROM_EWDS_opcode ;Disable the Erase/write
mov cx, 5 ; command previously sent to
call shift_bits_out ; EEPROM.
mov cx, 4 ;Send 4 don't cares as
call shift_bits_out ; required by the eeprom
call eeprom_clean_up
clc
ret
write_fault_pop:
add sp, 2 ;Get rid of data on stack
write_fault:
;;; lea ax, eeprom_write_error
stc
ret
shift_bits_out:
;enter with ax=data to be shifted, cx=# of bits to be shifted.
push bx
; Data bits are right justified in the AX register. Move the data
; into BX and left justify it. This will cause addresses to to
; be sent to the EEPROM high order bit first.
mov bx, ax
mov ch, 16
sub ch, cl
xchg cl, ch
shl bx, cl
xchg cl, ch
xor ch, ch
; Get the EEPROM control register into AL. Mask of the ASIC asn 586
; reset bits.
in al, dx
and al, 10111111b
; Set or clear DI bit in EEPROM control register based on value of
; data in BX.
out_shift_loop:
and al, not ee_di ;Assume data bit will be zero
rcl bx, 1 ;Is the data bit a one?
jnc out_with_it ;No
or al, ee_di ;Yes
out_with_it:
out dx, al ;Output a 0 or 1 on data pin
; Set up time for data is .4 Microseconds. So to be safe (incase of
; this software is run on a cray), call delay.
mov di, 1
call eeprom_delay
call raise_eeprom_clock ; Clock the data into the EEPROM.
call lower_eeprom_clock
loop out_shift_loop ;Send next bit
and al, not ee_di ;Force data bit to zero
out dx, al ;Output a 0 on data pin
pop bx
ret
shift_bits_in:
;exit with ax = register contents.
push bx
push cx
; BX will receive the 16 bits read from the EEPROM. Data is valid in
; data out bit (DO) when clock is high. There for, this procedure
; raises clock and waits a minimum amount of time. DO is read, and
; clock is lowered.
in al, dx ;Init AL to eeprom control
and al, 10111111b ; register.
xor bx, bx ;Init holding register
mov cx, 16 ;We'll shift in 16 bits
in_shift_loop:
shl bx, 1 ;Adjust holding register for
; next bit
call raise_eeprom_clock
in al, dx
and al, 10111111b
test al, ee_do ;Was the data bit a one?
jz in_eeprom_delay ;No
or bx, 1 ;Yes, reflect data bit state
; in holding register.
in_eeprom_delay:
call lower_eeprom_clock
loop in_shift_loop ;CONTINUE
mov ax, bx ;AX = data
pop cx
pop bx
ret
raise_eeprom_clock:
or al, ee_sk ;Clock the bit out by raising
jmp short eeprom_clock_common
lower_eeprom_clock:
and al, not ee_sk ;Lower ee_sk
eeprom_clock_common:
out dx, al
mov di, ee_tick
call eeprom_delay ;Waste time
ret
wait_eeprom_cmd_done:
;Wait for ee_do to go high, indicating end-of-write or end-of-erase
;operation. Wait at most 20 ms (EEPROM needs at most 10).
;exit with nc if no error.
push bx
push cx
call stand_by
call readtickcounter
mov di, ax
ee_do_wait_loop:
call readtickcounter
neg ax
add ax, di
cmp ax, 28640 ;12 Millisecond wait
jb ee_do_wait_loop
in al, dx ;Get EEPROM control register
test al, EE_DO ;ee_do high?
jnz ee_do_found
stc
jmp short wait_eeprom_cmd_done_exit
ee_do_found:
clc ;"clean" status (no timeout)
wait_eeprom_cmd_done_exit:
pop cx
pop bx
ret
stand_by:
; lower chip select for 1 microsecond.
in al, dx ;de-select EEPROM
and al, (10111110b) and (not ee_cs)
out dx, al
mov di, 2
call eeprom_delay
or al, ee_cs
out dx, al
ret
; Lower EEPROM chip select and DI.
; Clock EEPROM twice and leave clock low.
eeprom_clean_up:
push ax
in al, dx
and al, 10111111b
and al, not (ee_cs or ee_di)
out dx, al
call raise_eeprom_clock
call lower_eeprom_clock
pop ax
ret
; DI has number of 838 Nanoseconds clock counts
eeprom_delay:
push ax
push bx
push dx
call readtickcounter
mov bx, ax
eeprom_delay_loop:
call readtickcounter
neg ax
add ax, bx
cmp ax, di
jb eeprom_delay_loop
pop dx
pop bx
pop ax
ret
iochrdy_test:
; First test to see if the driver is supposed to run this test.
MOV AX, lock_bit_address
CALL read_eeprom
TEST AX, lock_bit_mask
JNZ iochrdy_test_exit
; Get the configuration register.
LEA DX, [BP].@config
IN AL, DX
; Set the iochrdy test bit with and set iochrdy to late.
OR AL, iochrdy_test_mask+iochrdy_late
CLI
OUT DX, AL
; Test IOCHRDY with IO.
LEA DX, [BP].@mem_loc_0
IN AX, DX
; Read in the results of the test. Save results in BL.
LEA DX, [BP].@config
IN AL, DX
MOV BL, AL
; Turn iochrdy test off.
AND AL, NOT iochrdy_test_mask
OUT DX, AL
STI
; Test results. Exit if IOCHRDY_LATE bit is set correctly.
TEST BL, iochrdy_test_result
JZ iochrdy_test_exit
; Here if test failed. Clear the 16 bit override bit (force 8 bit
; transfers) and print a warning message.
AND AL, NOT _16_bit_override_bit
OUT DX, AL
mov dx, offset iochrdy_problem
mov ah,9
int 21h
iochrdy_test_exit:
RET
iochrdy_problem db "IOCHRDY Problem. EtherExpress forced into 8 Bit Mode.",CR,LF,'$'
buffer_memory_error db "Memory error on the EtherExpress board",CR,LF,'$'
reset_error db "ASIC reset failure on EtherExpress board",CR,LF,'$'
_586_diagnostic_failure db "82586 diagnostic failure on the "
db "EtherExpress board",CR,LF,'$'
_586_not_responding_msg db "82586 did not respond to command "
db "on the EtherExpress board",CR,LF,'$'
command_unit_not_idle db "82586 command unit is not "
db "responding on the EtherExpress board",CR,LF,'$'
invalid_int_number db "EtherExpress board IRQ/Interrupt number "
db "not specified correctly",CR,LF,'$'
eeprom_checksum_error db "EEPROM failed checksum",CR,LF,'$'
Board_Not_Installed db "EtherExpress Board not found",CR,LF,'$'
irq_config_error db "IRQ selection is for 16 bit slot only.",CR,LF,'$'
bad_config_msg db "Could not configure the 82586",CR,LF,'$'
bad_init_msg db "Could not initialize the 82586",CR,LF,'$'
code ends
end