Category : Network Files
Archive   : TCP_SRC.ZIP
Filename : TRACE.ASM
include defs.asm
; Copyright, 1988, 1989, 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.
code segment word public
assume cs:code, ds:code
org 2ch
phd_env label word
org 5ch
phd_fcb1 label byte
org 6ch
phd_fcb2 label byte
org 80h
phd_dioa label byte
org 100h
start:
jmp start_1
stack label byte
comment /
Plan:
Keep a circular queue of events. The size of the queue is a settable
parameter. Discard events that fall off the end. Remember how many events
were discarded. Remember when the events occurred.
Type of events to remember:
calls to the packet driver
upcalls to the receiver handler (both kinds)
/
; In addition to the function numbers specified in the FTP Software
; packet driver spec, the following pseudo-functions are defined:
EVENT_RECEIVE equ 255 ;receiver upcall.
event_struc struc
event_length dw ? ;length of this event.
event_function db ? ;the event function number.
event_time dw ?,? ;timer tick at the time of the call.
event_error db ? ;set to value of dh after the SWI.
event_struc ends
di_struc struc
db (size event_struc) dup (?)
di_version dw ?
di_class db ?
di_type dw ?
di_number db ?
di_basic db ?
di_struc ends
at_struc struc
db (size event_struc) dup (?)
at_if_class db ?
at_if_type dw ?
at_if_number db ?
at_typelen dw ?
at_handle dw ?
at_struc ends
handle_struc struc
db (size event_struc) dup (?)
event_handle dw ?
handle_struc ends
ga_struc struc
db (size event_struc) dup (?)
ga_handle dw ?
ga_length dw ?
ga_struc ends
srm_struc struc
db (size event_struc) dup (?)
srm_mode dw ?
srm_handle dw ?
srm_struc ends
queue_length dw 10000,? ;length of the queue.
queue_tail dw ? ;points after the last item in the queue.
queue_ptr dw ? ;points to the new item in the queue.
queue_head dw ? ;points to the first item in the queue.
queue_end dw ? ;points to the end of the queue, but
;there is room for one more event after
;this one.
packet_int_no db ?,0,0,0
parm dw 0
parm2 dw their_dioa,?
parm3 dw phd_fcb1,?
parm4 dw phd_fcb2,?
comspec_env_str db "COMSPEC="
comspec_env_len equ $-comspec_env_str
program db 64 dup(?)
their_dioa db 0,0dh,128-2 dup(?)
saved_ax label word
saved_al db ?
saved_ah db ?
saved_bx dw ?
saved_ds dw ?
saved_f dw ?
functions label word
dw f_driver_info ;function 1
dw f_access_type
dw f_release_type
dw f_send_pkt
dw f_terminate
dw f_get_address
dw f_reset_interface ;function 7
dw f_set_rcv_mode ;function 20
dw f_get_rcv_mode
dw f_set_multicast_list
dw f_get_multicast_list
dw f_get_statistics
dw f_set_address ;function 25
their_isr dd ?
our_isr:
jmp our_isr_0 ;the required signature.
signature db 'PKT DRVR',0
signature_len equ $-signature
our_isr_0:
assume ds:nothing
mov saved_ds,ds
mov saved_bx,bx
mov saved_ax,ax
cld
mov bx,sp
mov bx,ss:[bx+4] ;get the original flags.
mov saved_f,bx
mov bx,cs ;set up ds.
mov ds,bx
assume ds:code
;the following code runs with ax, bx, ds, and flags saved in save_*.
;otherwise, all the registers are the same as those we were called with.
mov bl,ah ;jump to the correct function.
mov bh,0
cmp bx,7 ;highest function is 7.
jbe our_isr_3
cmp bx,20
jb our_isr_bad
cmp bx,25
ja our_isr_bad
sub bx,20-7-1 ;map 20 right after 7.
our_isr_3:
add bx,bx ;*2
jmp functions-2[bx] ;table starts at 1.
our_isr_bad:
call do_their_isr
jmp our_isr_done
f_driver_info:
mov bx,(size di_struc)
call queue_advance
call do_their_isr
jc f_driver_info_1
mov ax,saved_bx
mov [bx].di_version,ax
mov [bx].di_class,ch
mov [bx].di_type,dx
mov [bx].di_number,cl
mov al,saved_al
mov [bx].di_basic,al
;we ignore the name -- too much work.
f_driver_info_1:
jmp our_isr_done
f_get_statistics:
;strictly speaking, we should remember the statistics, but I'm not going to now.
f_terminate:
f_reset_interface:
f_release_type:
f_get_rcv_mode:
mov bx,(size handle_struc)
call queue_advance
mov ax,saved_bx
mov [bx].event_handle,ax
call do_their_isr
jmp our_isr_done
f_access_type:
mov bx,(size at_struc)
call queue_advance
mov al,saved_al
mov [bx].at_if_class,al
mov ax,saved_bx
mov [bx].at_if_type,ax
mov [bx].at_if_number,dl
mov [bx].at_typelen,cx
mov their_recv.segm,es
mov their_recv.offs,di
mov ax,cs ;and stick our receiver in.
mov es,ax
mov di,offset our_recv
call do_their_isr
jc f_access_type_1
mov ax,saved_ax
mov [bx].at_handle,ax
f_access_type_1:
les di,their_recv ;now restore ds and si.
jmp our_isr_done
f_send_pkt:
mov bx,(size event_struc)
add bx,cx
call queue_advance
;make a copy of their packet.
push cx
push si
push di
push ds
push es
lea di,[bx] + (size event_struc)
mov ax,cs
mov es,ax
mov ds,saved_ds
rep movsb
pop es
pop ds
pop di
pop si
pop cx
call do_their_isr
jmp our_isr_done
f_get_address:
mov bx,(size ga_struc)
add bx,cx
call queue_advance
mov ax,saved_bx ;save their handle
mov [bx].ga_handle,ax
call do_their_isr
jc f_get_address_1
;make a copy of their address.
mov [bx].ga_length,cx ;we need to save this because it
;might be less than the total allocated.
push cx
push si
push di
push ds
push es
mov si,di ;get es:di into ds:si
mov ax,es
mov ds,ax
lea di,[bx] + (size ga_struc) ;get our pointer into es:di.
mov ax,cs
mov es,ax
rep movsb
pop es
pop ds
pop di
pop si
pop cx
f_get_address_1:
jmp our_isr_done
f_set_rcv_mode:
mov bx,(size srm_struc)
add bx,2
call queue_advance
mov ax,saved_bx ;save their handle.
mov [bx].srm_handle,ax
mov [bx].srm_mode,cx ;save their mode.
call do_their_isr
jmp our_isr_done
f_set_multicast_list:
f_get_multicast_list:
f_set_address:
mov bx,(size event_struc)
call queue_advance
call do_their_isr
our_isr_done:
push saved_f ;restore their flags, see [2]
popf
mov ax,saved_ax
mov bx,saved_bx
mov ds,saved_ds ;restore the two registers we destroyed.
assume ds:nothing
foobar proc far ;masm 4.0 is really stupid.
ret 2 ;return, popping their old flags, [2].
foobar endp
assume ds:code
;do_their_isr executes their isr with the original registers.
;called with all their registers except f, ds, and bx.
;exits with all their registers except ds, ax, and bx. bx is queue_ptr
do_their_isr:
;setup their context.
mov ax,saved_f ;restore their flags, see [1]
and ax,not 200h ;clear the interrupt flag, as required
push ax ; when faking an interrupt.
mov ax,saved_ax
mov bx,saved_bx
mov ds,saved_ds ;restore the two registers we destroyed.
assume ds:nothing
; [1] we pushed the flags earlier.
call their_isr ;now fake their interrupt.
;save their context.
mov saved_ax,ax ;save the new registers.
mov saved_bx,bx
mov saved_ds,ds
mov ax,cs ;set up a pointer to the next event.
mov ds,ax
assume ds:code
pushf ;save the new flags.
pop ax
and saved_f,200h ;merge the interrupt flag in saved_f
or saved_f,ax ; with the new flags.
;remember whether it succeeded or not.
mov bx,queue_ptr
mov [bx].event_error,NO_ERROR ;assume that all was okay.
jnc our_isr_1
mov [bx].event_error,dh ;it wasn't.
our_isr_1:
ret
their_recv dd ?
our_recv:
assume ds:nothing
mov saved_ds,ds
mov saved_bx,bx
mov saved_ax,ax
cld
mov bx,cs ;set up ds.
mov ds,bx
assume ds:code
or ax,ax ;first call or second?
je our_recv_first
mov bx,(size handle_struc)
add bx,cx
call queue_advance
mov [bx].event_function,EVENT_RECEIVE ;not a real function.
mov [bx].event_error,0 ;no errors possible.
mov ax,saved_bx
mov [bx].event_handle,ax ;remember which handle it was.
push cx
push si
push di
push ds
push es
lea di,[bx] + (size handle_struc)
mov ax,cs
mov es,ax
mov ds,saved_ds
rep movsb
pop es
pop ds
pop di
pop si
pop cx
jmp short our_recv_done
our_recv_first:
;ignore the first upcall.
our_recv_done:
mov ax,saved_ax
mov bx,saved_bx
mov ds,saved_ds ;restore the two registers we destroyed.
assume ds:nothing
jmp their_recv
assume ds:code
queue_advance:
;enter with bx = number of bytes that we require in the queue.
;exit with bx,queue_ptr set to a pointer to our entry.
;preserve everything but ds, bx, and the flags.
queue_advance_3:
mov ax,queue_tail
add ax,bx
cmp ax,queue_head ;if we don't overlap the head, we're
jbe queue_advance_1 ; okay.
xchg bx,queue_head ;get queue_head and save bx.
add bx,[bx].event_length
cmp bx,queue_end ;see if we hit the end.
xchg queue_head,bx ;store queue_head and restore bx.
jb queue_advance_3 ;if we're less than the end, continue.
;we have to wrap here.
push bx
mov bx,queue_tail
mov ax,queue_end ;make an event length that's too large.
sub ax,offset queue_begin
mov [bx].event_length,ax
mov bx,offset queue_begin ;and restart from the beginning.
mov queue_tail,bx ;ensure that we nuke some more.
mov queue_head,bx
pop bx
jmp queue_advance_3
queue_advance_1:
xchg ax,queue_tail ;update the tail and get this ptr.
mov queue_ptr,ax ;save this pointer.
xchg ax,bx
mov [bx].event_length,ax ;store the length of this entry here.
mov ah,saved_ah ;store their function value.
mov [bx].event_function,ah
;remember when it happened.
push dx
push ds
mov ax,40h
mov ds,ax
mov ax,ds:6ch ;get the timer tick count.
mov dx,ds:6eh
pop ds
mov [bx].event_time+0,ax
mov [bx].event_time+2,dx
pop dx
ret
copyleft_msg label byte
db "Packet driver tracer version ",majver+'0','.',version+'0'," copyright 1988-89, Russell Nelson.",CR,LF
db "This program is free software; see the file COPYING for details.",CR,LF
db "NO WARRANTY; see the file COPYING for details.",CR,LF
crlf_msg db CR,LF,'$'
packet_int_no_name db "Packet interrupt number ",'$'
buffer_size_name db "Buffer size ",'$'
before_exec_msg db "Now run your network software and type 'exit' when finished",CR,LF,'$'
run_dump_msg db "Now run 'dump' to interpret 'trace.out'",CR,LF,'$'
disk_full_msg db "Disk Full!",'$'
already_msg db CR,LF,"There is no packet driver at ",'$'
packet_int_msg db CR,LF
db "Error:
db '$'
usage_msg db "usage: trace packet_int_no
queue_error_msg db "Error:
trace_out db "TRACE.OUT",0 ;filename that we write the dump to.
HT equ 09h
CR equ 0dh
LF equ 0ah
usage_error:
mov dx,offset usage_msg
error:
mov ah,9
int 21h
int 20h
already_error:
mov dx,offset already_msg
mov di,offset packet_int_no
call print_number
int 20h
start_1:
mov sp,offset stack
mov dx,offset copyleft_msg
mov ah,9
int 21h
mov si,offset phd_dioa+1
cmp byte ptr [si],CR ;end of line?
je usage_error
mov di,offset packet_int_no ;parse the packet interrupt number
mov bx,offset packet_int_no_name
call get_number ; for them.
mov di,offset queue_length ;parse the packet interrupt number
mov bx,offset buffer_size_name
call get_number ; for them.
cmp byte ptr [si],CR ;end of line?
jne usage_error
cmp queue_length+2,0
jne start_3
cmp queue_length,2000
ja start_2
start_3:
mov dx,offset queue_error_msg
jmp error
start_2:
;initialize the queue
mov bx,offset queue_begin
mov queue_tail,bx
add bx,queue_length
mov queue_end,bx ;initialize the head of the queue.
mov [bx].event_length,1 ;anything >0 will ensure that we're >end.
mov queue_head,bx
;do some error checking.
mov dx,offset packet_int_msg;make sure that the packet interrupt
cmp packet_int_no,60h ; number is in range.
jb error
cmp packet_int_no,80h
ja error
mov ah,35h ;get their packet interrupt.
mov al,packet_int_no
int 21h
lea di,3[bx] ;see if there is already a signature
mov si,offset signature ; there.
mov cx,signature_len
repe cmpsb
je start_4 ;yes, so we can trace it.
jmp already_error ;no, give them an error.
start_4:
mov ah,35h ;remember their packet interrupt.
mov al,packet_int_no
int 21h
mov their_isr.offs,bx
mov their_isr.segm,es
mov ah,25h ;install our packet interrupt
mov dx,offset our_isr
int 21h
mov dx,offset before_exec_msg
mov ah,9
int 21h
;
; Now free the memory we don't need.
;
mov bx,queue_end
add bx,size event_struc ;leave room for one more.
add bx,0fh ;round up to next highest paragraph.
mov cl,4
shr bx,cl
push cs
pop es
mov ah,4ah
int 21h
; Now we execute command.com
mov ax,cs
mov word ptr parm2+2,ax
mov word ptr parm3+2,ax
mov word ptr parm4+2,ax
mov si,offset their_dioa+1 ;re-parse the two fcbs.
mov di,offset phd_fcb1
push ds
pop es
mov ax,2901h
int 21h
mov di,offset phd_fcb2
mov ax,2901h
int 21h
mov si,offset comspec_env_str ;see if this is the one.
mov cx,comspec_env_len
mov di,offset program
call getenv
mov ah,4bh
mov bx,offset parm
mov dx,offset program
mov al,0
int 21h
mov bx,cs ;restore our segment registers.
mov ds,bx
mov es,bx
mov ss,bx
mov sp,offset stack
; Give up our packet interception.
mov al,packet_int_no ;release our_isr.
mov ah,25h
push ds
lds dx,their_isr
int 21h
pop ds
; Now we write our captured information out to disk.
mov dx,offset trace_out ;create "trace.out".
mov ah,3ch
mov cx,0
int 21h
mov bx,ax
mov si,queue_head
write_out:
mov dx,si
mov cx,[si].event_length ;write this event out.
add si,cx ;is this the end of the queue?
cmp si,queue_end ;
ja write_out_1
mov ah,40h
int 21h
cmp ax,cx
jne write_out_full
jmp write_out
write_out_1:
mov si,offset queue_begin ;yes.
write_out_2:
cmp si,queue_tail ;quit when we hit the tail.
jae write_out_3
mov dx,si ;set dx for the file write below.
mov cx,[si].event_length ;write this event out.
add si,cx
mov ah,40h
int 21h
cmp ax,cx
jne write_out_full
jmp write_out_2
write_out_3:
mov ah,3eh ;close the file.
int 21h
mov dx,offset run_dump_msg
mov ah,9
int 21h
int 20h
write_out_full:
mov ah,9
mov dx,offset disk_full_msg
int 21h
int 20h
get_number:
mov bp,10 ;we default to 10.
jmp short get_number_0
get_hex:
mov bp,16
;get a hex number, skipping leading blanks.
;enter with si->string of digits,
; bx -> dollar terminated name of number,
; di -> dword to store the number in. [di] is not modified if no
; digits are given, so it acts as the default.
;return cy if there are no digits at all.
;return nc, bx:cx = number, and store bx:cx at [di].
get_number_0:
push bx ;remember the name of this number.
call skip_blanks
call get_digit ;is there really a number here?
jc get_number_3
or al,al ;Does the number begin with zero?
jne get_number_4 ;no.
mov bp,8 ;yes - they want octal.
get_number_4:
xor cx,cx ;get a hex number.
xor bx,bx
get_number_1:
lodsb
cmp al,'x' ;did they really want hex?
je get_number_5 ;yes.
cmp al,'X' ;did they really want hex?
je get_number_5 ;yes.
call get_digit ;convert a character into an int.
jc get_number_2 ;not a digit (neither hex nor dec).
xor ah,ah
cmp ax,bp ;larger than our base?
jae get_number_2 ;yes.
push ax ;save the new digit.
mov ax,bp ;multiply the low word by ten.
mul cx
mov cx,ax ;keep the low word.
push dx ;save the high word for later.
mov ax,bp
mul bx
mov bx,ax ;we keep only the low word (which is our high word)
pop dx
add bx,ax ;add the high result from earlier.
pop ax ;get the new digit back.
add cx,ax ;add the new digit in.
adc bx,0
jmp get_number_1
get_number_5:
mov bp,16 ;change the base to hex.
jmp get_number_1
get_number_2:
dec si
mov [di],cx ;store the parsed number.
mov [di+2],bx
clc
jmp short get_number_6
get_number_3:
stc
get_number_6:
pop dx ;get the name of the number back.
pushf ;save some stuff.
push bx
push cx
push si
push di
call print_number
pop di
pop si
pop cx
pop bx
popf
ret
print_number:
;enter with dx -> dollar terminated name of number, di ->dword.
;exit with the number printed and the cursor advanced to the next line.
mov ah,9 ;print the name of the number.
int 21h
mov al,'0'
call chrout
mov al,'x'
call chrout
mov ax,[di] ;print the number in hex.
mov dx,[di+2]
call hexout
mov al,' '
call chrout
mov al,'('
call chrout
mov ax,[di] ;print the number in decimal.
mov dx,[di+2]
call decout
mov al,')'
call chrout
mov al,CR
call chrout
mov al,LF
call chrout
ret
skip_blanks:
lodsb ;skip blanks.
cmp al,' '
je skip_blanks
cmp al,HT
je skip_blanks
dec si
ret
get_digit:
;enter with al = character
;return nc, al=digit, or cy if not a digit.
cmp al,'0' ;decimal digit?
jb get_digit_1 ;no.
cmp al,'9' ;. .?
ja get_digit_2 ;no.
sub al,'0'
clc
ret
get_digit_2:
or al,20h
cmp al,'a' ;hex digit?
jb get_digit_1
cmp al,'f' ;hex digit?
ja get_digit_1
sub al,'a'-10
clc
ret
get_digit_1:
stc
ret
hexout:
mov cl,'0' ;prepare to eliminate leading zeroes.
xchg ax,dx ;just output 32 bits in hex.
call wordout ;output dx.
xchg ax,dx
jmp wordout ;output ax.
decout:
mov si,ax ;get the number where we want it.
mov di,dx
xor ax,ax ;start with all zeroes in al,bx,bp
mov bx,ax
mov bp,ax
mov cx,32 ;32 bits in two 16 bit registers.
decout_1:
shl si,1
rcl di,1
xchg bp,ax
call addbit
xchg bp,ax
xchg bx,ax
call addbit
xchg bx,ax
adc al,al
daa
loop decout_1
mov cl,'0' ;prepare to eliminate leading zeroes.
call byteout ;output the first two.
mov ax,bx ;output the next four
call wordout ;output the next four
mov ax,bp
wordout:
push ax
mov al,ah
call byteout
pop ax
byteout:
mov ah,al
shr al,1
shr al,1
shr al,1
shr al,1
call digout
mov al,ah
digout:
and al,0fh
add al,90h ;binary digit to ascii hex digit.
daa
adc al,40h
daa
cmp al,cl ;leading zero?
je return
mov cl,-1 ;no more leading zeros.
chrout:
push ax ;print the char in al.
xchg al,dl
mov ah,2
int 21h
xchg al,dl
pop ax
return:
ret
addbit: adc al,al
daa
xchg al,ah
adc al,al
daa
xchg al,ah
ret
getenv:
;enter with ds:si -> environment string to look for, cx = length of string,
; ds:di -> place to put the string's value.
push es
push di ;remember where we're supposed to put it.
mov es,phd_env ;search the environment.
xor di,di
getenv_2:
cmp byte ptr es:[di],0 ;see if we're at the end.
je getenv_0
push cx
push si
push di
repe cmpsb
pop di
pop si
je getenv_3
mov cx,-1 ;skip to the next null.
xor al,al
repne scasb
pop cx
jmp getenv_2
getenv_3:
;copy the environment string to current_dir.
pop cx
add di,cx ;go to the end of the string.
pop si ;pushed as di, -> place to put the string.
getenv_4:
mov al,es:[di]
mov [si],al
inc di
inc si
or al,al
jne getenv_4
dec si ;point si to the null again.
pop es
clc
ret
getenv_0:
add sp,2
pop es
stc
ret
end_code label byte
queue_begin label byte
code ends
end start
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/