📄 head.asm
字号:
count_in_err:
assume ds:nothing
add2 errors_in,1
ret
public count_out_err
count_out_err:
assume ds:nothing
add2 errors_out,1
ret
recv_isr_frame struc
recv_isr_ds dw ?
recv_isr_dx dw ?
recv_isr_ax dw ?
recv_isr_ip dw ?
recv_isr_cs dw ?
recv_isr_f dw ?
recv_isr_frame ends
;
; I have had a problem with some hardware which under extreme LAN loading
; conditions will re-enter the recv_isr. Since the 8259 interrupts for
; the card are masked off, and the card's interrupt mask register is
; cleared (in 8390.asm at least) disabling the card from interrupting, this
; is clearly a hardware problem. Due to the low frequencey of occurance, and
; extreme conditions under which this happens, it is not lilely to be fixed
; in hardware any time soon, plus retrofitting of hardware in the field will
; not happen. To protect the driver from the adverse effects of this I am
; adding a simple re-entrancy trap here. - gft - 910617
;
in_recv_isr db 0 ; flag to trap re-entrancy
recv_isr:
cmp in_recv_isr, 0
jne recv_iret
mov in_recv_isr, 1
; I realize this re-entrancy trap is not perfect, you could be re-entered
; anytime before the above instruction. However since the stacks have not
; been swapped re-entrancy here will only appear to be a spurious interrupt.
; - gft - 910617
; In order to achieve back-to-back packet transmissions, we handle the
; latency-critical portion of transmit interrupts first. The xmit
; interrupt routine should only start the next transmission, but do
; no other work. It may only touch ax and dx (the only register necessary
; for doing "out" instructions) unless it first pushes any other registers
; itself.
PUSH_16_32 ax
PUSH_16_32 dx
call xmit
jc not_our_interrupt
; Now switch stacks, push remaining registers, and do remaining interrupt work.
push ds
mov ax,cs ;ds = cs.
mov ds,ax
assume ds:cgroup
mov savesp,sp
mov sp,offset cgroup:our_stack
mov savess,ss
mov ss,ax
cld
PUSH_16_32 bx
PUSH_16_32 cx
PUSH_16_32 si
PUSH_16_32 di
PUSH_16_32 bp
push es
; The following comment is wrong in that we now do a specific EOI command,
; and because we don't enable interrupts (even though we should).
; Chips & Technologies 8259 clone chip seems to be very broken. If you
; send it a Non Specific EOI command, it clears all In Service Register
; bits instead of just the one with the highest priority (as the Intel
; chip does and clones should do). This bug causes our interrupt
; routine to be reentered if: 1. we reenable processor interrupts;
; 2. we reenable device interrupts; 3. a timer or other higher priority
; device interrupt now comes in; 4. the new interrupting device uses
; a Non Specific EOI; 5. our device interrupts again. Because of
; this bug, we now completely mask our interrupts around the call
; to "recv", the real device interrupt handler. This allows us
; to send an EOI instruction to the 8259 early, before we actually
; reenable device interrupts. Since the interrupt is masked, we
; are still guaranteed not to get another interrupt from our device
; until the interrupt handler returns. This has another benefit:
; we now no longer prevent other devices from interrupting while our
; interrupt handler is running. This is especially useful if we have
; other (multiple) packet drivers trying to do low-latency transmits.
mov al,int_no ; Disable further device interrupts
call maskint
; The following is from Bill Rust, <wjr@ftp.com>
; this code dismisses the interrupt at the 8259. if the interrupt number
; is > 8 then it requires fondling two PICs instead of just one.
mov al, int_no ; get hardware int #
cmp al, 8 ; see if its on secondary PIC
jg recv_isr_4
add al, 60h ; make specific EOI dismissal
out 20h, al
jmp short recv_isr_3 ; all done
recv_isr_4:
add al,60h - 8 ; make specific EOI (# between 9 & 15).
out 0a0h,al ; Secondary 8259 (PC/AT only)
mov al,62h ; Acknowledge on primary 8259.
out 20h,al
recv_isr_3:
; sti ; Interrupts are now completely safe
call recv
cli ;interrupts *must* be off between
;here and the stack restore, because
;if we have one of our interrupts
;pending, we would trash our stack.
mov al,int_no ; Now reenable device interrupts
call unmaskint
pop es
POP_16_32 bp
POP_16_32 di
POP_16_32 si
POP_16_32 cx
POP_16_32 bx
mov ss,savess
mov sp,savesp
pop ds
assume ds:nothing
;IBM has a situation where:
; 1) Serial interrupt IRQ4 or IRQ3 occurs
; 2) Network interrupt occurs in the middle.
; 3) Network interrupt completes and issues EOI
; 4) Network interrupt falls through to default handler.
; 5) Default handler assumes there is no interrupt handler
; and masks off the current (IRQ4 or IRQ3) interrupt. Bad!
;To fix this problem, we are simply going to iret if we serviced our interrupt.
POP_16_32 dx
POP_16_32 ax
mov in_recv_isr, 0 ; clear the re-entrancy flag - gft - 901617
iret
not_our_interrupt:
POP_16_32 dx
POP_16_32 ax
mov in_recv_isr, 0 ; clear the re-entrancy flag - gft - 901617
recv_iret:
jmp their_recv_isr
recv_exiting_flag db 0 ;nonzero if recv_exiting will be run.
recv_exiting_addr dd ?
public schedule_exiting
schedule_exiting:
;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.
cmp recv_exiting_flag,0 ;is it already scheduled?
jne schedule_exiting_1 ;yes, don't do it again!
inc recv_exiting_flag ;set the flag.
les di,savespss ;make es:di -> their stack.
xchg ax,es:[di].recv_isr_ip ;stuff our routine's address in,
mov recv_exiting_addr.offs,ax ;and save the original address.
mov ax,cs
xchg ax,es:[di].recv_isr_cs
mov recv_exiting_addr.segm,ax
schedule_exiting_1:
ret
public recv_exiting_exit
recv_exiting_exit:
;recv_exiting jumps here to exit, after pushing the flags.
cli ;protect the following semaphore.
mov recv_exiting_flag,0
push recv_exiting_addr.segm
push recv_exiting_addr.offs
iret
public maskint
maskint:
or al,al ;are they using a hardware interrupt?
je maskint_1 ;no, don't mask off the timer!
assume ds:cgroup
mov dx,21h ;assume the master 8259.
cmp al,8 ;using the slave 8259 on an AT?
jb mask_not_irq2
mov dx,0a1h ;go disable it on slave 8259
sub al,8
mask_not_irq2:
mov cl,al
in al,dx ;disable them on the correct 8259.
mov ah,1 ;set the bit.
shl ah,cl
or al,ah
;
; 500ns Stall required here, per INTEL documentation for eisa machines
; - gft - 910617
;
push ax
in al,61h ; 1.5 - 3 uS should be plenty
in al,61h
in al,61h
pop ax
out dx,al
maskint_1:
ret
public unmaskint
unmaskint:
;exit with cl = 0 if the interrupt had been enabled.
assume ds:cgroup
mov dx,21h ;assume the master 8259.
mov cl,al
cmp cl,8 ;using the slave 8259 on an AT?
jb unmask_not_irq2 ;no
in al,dx ;get master mask
push ax
in al,61h ;wait lots of time.
in al,61h
in al,61h
pop ax
and al,not (1 shl 2) ; and clear slave cascade bit in mask
out dx,al ;set new master mask (enable slave int)
;
; 500ns Stall required here, per INTEL documentation for eisa machines
; - gft - 910617
;
push ax
in al,61h ; 1.5 - 3 uS should be plenty
in al,61h
in al,61h
pop ax
mov dx,0a1h ;go enable int on slave 8259
sub cl,8
unmask_not_irq2:
in al,dx ;enable interrupts on the correct 8259.
mov ah,1 ;clear the bit.
shl ah,cl
mov cl,al ;remember the original mask.
and cl,ah
not ah
and al,ah
;
; 500ns Stall required here, per INTEL documentation for eisa machines
; - gft - 910617
;
push ax
in al,61h ; 1.5 - 3 uS should be plenty
in al,61h
in al,61h
pop ax
out dx,al
ret
public recv_locate
recv_locate:
;called when we want to determine what to do with a received packet.
;enter with es:di -> packet type, dl = packet class.
;exit with cy if the packet is not desired, or nc if we know its type.
assume ds:cgroup, es:nothing
; If -n option take IEEE 802.3 encapsulated packets that could be Novell IPX
; and make them Ethernet encapsulated Novell IPX packets (for PDSHELL).
test flagbyte,N_OPTION
jz not_n_op
; Make IEEE 802.3-like packets that could be Novell IPX into BlueBook class
; Novell type 8137 packets.
cmp dl,IEEE8023 ;Is this an IEEE 802.3 packet?
jne recv_not_802_3 ;no
cmp word ptr es:[di],0ffffh ;if this word not ffff
jne recv_not_8137 ; then not Novell
sub di,2 ; back it up to the 8137 word.
mov es:[di],3781h ; fake as Novell protocol (8137)
mov dl,BLUEBOOK
jmp short recv_not_8137
recv_not_802_3:
; Convert incoming Ethernet type 8137 IPX packets to type 8138, as with -n in
; effect we can't send type 8137, and it will only confuse Netware.
cmp dl,BLUEBOOK ;Is this a BLUEBOOK packet?
jne recv_not_8137 ;no, don't change it.
cmp word ptr es:[di],3781h ;Is it an 8137 packet?
jne recv_not_8137 ;no, don't change it.
mov es:[di],word ptr 3881h ;yes, mung it slightly.
recv_not_8137:
not_n_op:
mov bx,offset cgroup:handles
recv_find_1:
cmp [bx].in_use,0 ;is this handle in use?
je recv_find_2 ;no - don't check the type.
mov ax,[bx].receiver.offs ;do they have a receiver?
or ax,[bx].receiver.segm
je recv_find_2 ;no - they're not serious about it.
;per request by the UK people, we match on IEEE 802.3 classes, then types.
;for all others, we match on type, then class. This lets their software work
;without breaking BLUEBOOK type length=0 clients.
cmp [bx].class,IEEE8023 ;is this an IEEE 802.3 handle
jne recv_find_7 ;no.
cmp dl,IEEE8023 ;is the packet also IEEE 802.3?
jne recv_find_2 ;no, give up on it now.
recv_find_7:
mov cx,[bx].packet_type_len ;compare the packets.
lea si,[bx].packet_type
jcxz recv_find_3 ;if cx is zero, they want them all.
cmp [bx].class, dl ;is this the right class?
jne recv_find_2 ;no- don't bother
push di
repe cmpsb
pop di
je recv_find_3 ;we've got it!
recv_find_2:
add bx,(size per_handle) ;go to the next handle.
cmp bx,offset cgroup:end_handles
jb recv_find_1
add2 packets_dropped,1 ;count it as dropped.
stc
ret
recv_find_3:
mov found_handle,bx ;remember what our handle was.
clc
ret
public recv_find, recv_found
recv_find:
;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.
;exit with es:di = 0 if the packet is not desired, or es:di -> packet buffer
; to be filled by the driver.
assume ds:cgroup, es:nothing
push cx ;preserve packet length.
call recv_locate ;search for the packet type.
pop cx
jc recv_find_5 ;we didn't find it -- discard it.
;
recv_found:
;called to do the first upcall.
;exit with es:di = 0 if the packet is not desired, or es:di -> packet buffer
; to be filled by the driver.
add2 packets_in,1
add2 bytes_in,cx ;add up the received bytes.
mov bx,found_handle
les di,[bx].receiver ;remember the receiver upcall.
mov receive_ptr.offs,di
mov receive_ptr.segm,es
test flagbyte,W_OPTION ;did they select the Windows option?
je recv_find_6 ;no, don't check for the upcall.
; does the receiver signature match whats currently in memory? if not,
; jump to fake return
push si
push cx
lea si,[bx].receiver_sig
mov cx,8/2
repe cmpsw
pop cx
pop si
je recv_find_6
recv_find_5:
xor di,di ;"return" a null pointer.
mov es,di
ret
recv_find_6:
mov ax,0 ;allocate request.
stc ;with stc, flags must be an odd number
push ax ; save a number that cant be flags
pushf ;save flags in case iret used.
call receive_ptr ;ask the client for a buffer.
; on return, flags should be at top of stack. if an IRET has been used,
; then 0 will be at the top of the stack
pop bx
cmp bx,0
je recv_find_4 ;0 is at top of stack
add sp,2
recv_find_4:
ret
public recv_copy
recv_copy:
;called after we have copied the packet into the buffer.
;enter with ds:si ->the packet, cx = length of the packet.
;preserve bx.
assume ds:nothing, es:nothing
push bx
mov bx,found_handle
mov ax,1 ;store request.
clc ;with clc, flags must be an even number
push ax ; save a number that can't be flags
pushf ;save flags incase iret used.
call receive_ptr ;ask the client for a buffer.
pop bx
cmp bx,1 ;if this is a 1, IRET was used.
je recv_copy_1
pop bx
recv_copy_1:
pop bx
ret
public send_queue
send_queue:
; Queue an iocb.
; Enter with es:di -> iocb, interrupts disabled.
; Destroys ds:si.
assume ds:nothing, es:nothing
mov es:[di].next.offs,0 ; Zero next offset
mov es:[di].next.segm,0 ; Zero next segment
mov si,send_head.offs ; Queue empty?
or si,send_head.segm
jnz sq_notempty ; No
mov send_head.offs,di ; Set head offset
mov send_head.segm,es ; Set head segment
jmp short sq_settail
sq_notempty: ; Queue is not empty
lds si,send_tail ; Get tail segment:offset
mov ds:[si].next.offs,di ; Set next offset
mov ds:[si].next.segm,es ; Set next segment
sq_settail:
mov send_tail.offs,di ; Set tail offset
mov send_tail.segm,es ; Set tail segment
ret
public send_dequeue
send_dequeue:
; Dequeue an iocb and possibly call its upcall.
; Enter with device or processor interrupts disabled, ah = return code.
; Exits with es:di -> iocb; destroys ds:si, ax, bx, cx, dx, bp.
assume ds:nothing, es:nothing
les di,send_head ; Get head segment:offset
lds si,es:[di].next ; Get next segment:offset
mov send_head.offs, si ; Set head offset
mov send_head.segm, ds ; Set head segment
or es:flags[di], DONE ; Mark done
mov es:ret_code[di], ah ; Set retcode
test es:[di].flags,CALLME ; Does he want an upcall?
je send_dequeue_1 ; No.
push es ; Push iocb segment
push di ; and offset
clc ; Clear carry.
mov ax,1 ; Push a number that cant be flags.
push ax
pushf ; Save flags in case iret used.
call es:[di].upcall ; Call the client.
pop ax ; Pop first word.
cmp ax,1 ; If this is a 1, IRET was used.
je send_dequeue_2 ; Far return used.
add sp,2 ; Pop flags.
send_dequeue_2:
pop di ; Pop iocb segment
pop es ; and offset
send_dequeue_1:
ret
code ends
_text segment para public 'code'
_text ends
init segment para public 'code'
init ends
end start
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -