345 lines
15 KiB
NASM
345 lines
15 KiB
NASM
|
; TRAPOUT2.ASM v2.0 by ARK (ark@lhq.com, root@ark.dyn.ml.org) 11-28-97
|
||
|
; Traps IN and OUT instructions in INT 10h and displays DX and AX/AL values.
|
||
|
;
|
||
|
; In the header "T DX/I AX/L", T is the Type of instruction (I=IN, O=OUT),
|
||
|
; DX/I is the value of DX or the Immediate value if port<256, and AX/L
|
||
|
; is the value of AX or AL depending on if an 8 or 16 bit value is listed.
|
||
|
; AX/L is meaningless for IN's since it is the value if AX/L *before* the
|
||
|
; call to IN.
|
||
|
;
|
||
|
; This is very useful to find information about how your video card works.
|
||
|
; I wrote this to get register dumps for my Trident TVGA9440AGi card so
|
||
|
; that I could use it under Linux.
|
||
|
;
|
||
|
; NOTE: Pipe the output or you won't see anything!
|
||
|
; (ex: TRAP-OUT 4F02 0101 > 640x480.256)
|
||
|
;
|
||
|
; New in v2.0:
|
||
|
; * Traces into INT 10 calls that are called from inside INT 10!
|
||
|
; * Allows AX and BX values to be specified!
|
||
|
; * Command line accepts trailing spaces now.
|
||
|
; x Code to trap INT's also! (T column='N', DX/I=INT ##, AX/L=AX value)
|
||
|
; (Its commented out - but you can recompile with it if you want)
|
||
|
;
|
||
|
; How to assemble with Borland:
|
||
|
; tasm /ml /zd ncr.asm (case sensitive, line number debug info only)
|
||
|
; tlink /x /t ncr.obj (no map, make com file)
|
||
|
;
|
||
|
|
||
|
.model tiny ; Tiny memory model, all segments point to the same 64K
|
||
|
.286 ; This code will run on a 286... actually, it
|
||
|
.code ; Everything is in the code segment(cs) will probably
|
||
|
.startup ; Startup run on anything
|
||
|
|
||
|
jmp Start ; Go to beginning of progam
|
||
|
realINT1 dd 52411A3Eh ; Address of original INT 01h routine offset
|
||
|
realINT10 dd 3C1B214Bh ; Memory for [es:bx] of the real INT 10h
|
||
|
; (defaults are '>-ARK!-<' just for looks in the .COM)
|
||
|
|
||
|
; strings
|
||
|
no_command_line db 'Use: TRAPOUT2 [AX] [BX]',13,10
|
||
|
db ' Traces all IN/OUT calls inside INT 10h',13,10,36
|
||
|
tracing db 'Tracing INT 10h with AX:',36
|
||
|
bx_msg db ' BX:',36
|
||
|
header db 13,10,'T DX/I AX/L',13,10,36
|
||
|
|
||
|
INT1 proc ; Interrupt Service Routine for Single Step Debugging
|
||
|
push ax ; save registers
|
||
|
push dx
|
||
|
push es
|
||
|
push di
|
||
|
push bp
|
||
|
|
||
|
mov bp,sp ; set bp to the stack
|
||
|
push word ptr cs:[bp+12] ; put the real cs
|
||
|
pop es ; into es
|
||
|
push word ptr cs:[bp+10] ; put the real ip
|
||
|
pop di ; into di
|
||
|
mov al,byte ptr es:[di] ; set al to the next instruction that will
|
||
|
; be executed after this INT 01 is done.
|
||
|
|
||
|
; This code will trap INT's also...
|
||
|
; cmp al,0CDh ; If al is not CD (INT) keep going
|
||
|
; jne not_int ; If it is, display some stuff...
|
||
|
;; This will skip doing the INT's...
|
||
|
;; add word ptr cs:[bp+10],2 ; Add 2 to the real ip, to skip the INT
|
||
|
; mov dl,4Eh ; Display an N
|
||
|
; mov ah,02h ; The immediate value/DX is the INT ##
|
||
|
; int 21h ; that is called. AX is the value before
|
||
|
; mov dl,20h ; Display a space
|
||
|
; mov ah,02h ;
|
||
|
; int 21h ; Display the immediate value which is
|
||
|
; jmp is_imm ; reallly the interrupt number called.
|
||
|
|
||
|
not_int:
|
||
|
and al,0F4h ; If al is E4-E7 or EC-EF (all IN/OUT's)
|
||
|
cmp al,0E4h ; Then we display our stuff
|
||
|
jne not_io ; Otherwise, do nothing
|
||
|
; note: 1 more byte of code after this
|
||
|
; jmp will make it out of range...
|
||
|
|
||
|
mov al,byte ptr es:[di] ; Set al to next instruction
|
||
|
test al,02h ; If bit 1 is set then we have an OUT
|
||
|
jz is_in ; If bit 1 is 0, we have an IN
|
||
|
|
||
|
mov dl,4Fh ; Display an O
|
||
|
mov ah,02h
|
||
|
int 21h
|
||
|
jmp dx_or_imd
|
||
|
|
||
|
is_in: ; Display an I
|
||
|
mov dl,49h
|
||
|
mov ah,02h
|
||
|
int 21h
|
||
|
|
||
|
dx_or_imd: ; Display a space
|
||
|
mov dl,20h
|
||
|
mov ah,02h
|
||
|
int 21h
|
||
|
|
||
|
mov al,byte ptr es:[di] ; Set al to next instruction
|
||
|
test al,08h ; If bit 3 is set then we are using DX
|
||
|
jz is_imm ; If bit 3 is 0, we are using an immediate
|
||
|
|
||
|
mov ax,[bp+6] ; restore dx to ax
|
||
|
call ShowHex ; Display dx
|
||
|
call ShowHex
|
||
|
call ShowHex
|
||
|
call ShowHex
|
||
|
jmp ax_or_al
|
||
|
|
||
|
is_imm:
|
||
|
mov dl,20h ; Display 2 spaces
|
||
|
mov ah,02h
|
||
|
int 21h
|
||
|
mov dl,20h
|
||
|
mov ah,02h
|
||
|
int 21h
|
||
|
|
||
|
mov ah,byte ptr es:[di+1] ; Set ah to byte after the next instruction
|
||
|
call ShowHex ; Display the immediate value
|
||
|
call ShowHex
|
||
|
|
||
|
ax_or_al:
|
||
|
mov dl,2Ch ; Display a comma
|
||
|
mov ah,02h
|
||
|
int 21h
|
||
|
|
||
|
mov al,byte ptr es:[di] ; Set al to next instruction
|
||
|
test al,01h ; If bit 0 is set then we are using AX
|
||
|
jz is_al ; If bit 0 is 0, we are using AL
|
||
|
|
||
|
mov ax,[bp+8] ; Restore ax
|
||
|
call ShowHex ; Display ax
|
||
|
call ShowHex
|
||
|
call ShowHex
|
||
|
call ShowHex
|
||
|
jmp print_next_line
|
||
|
|
||
|
is_al:
|
||
|
mov ah,[bp+8] ; Restore al to ah
|
||
|
call ShowHex ; Display al
|
||
|
call ShowHex
|
||
|
|
||
|
print_next_line:
|
||
|
mov dl,0Dh ; print a newline
|
||
|
mov ah,02h
|
||
|
int 21h
|
||
|
mov dl,0Ah
|
||
|
mov ah,02h
|
||
|
int 21h
|
||
|
|
||
|
not_io:
|
||
|
pop bp ; restore registers
|
||
|
pop di
|
||
|
pop es
|
||
|
pop dx
|
||
|
pop ax
|
||
|
iret ; end interrupt
|
||
|
INT1 endp
|
||
|
|
||
|
; INT 10h that fakes the real INT 10 and sets the trap flag.
|
||
|
INT10 proc ; Interrupt Service Routine for Tracing INT 10h
|
||
|
push ax ; Save AX
|
||
|
pushf ; Put flags on the stack
|
||
|
pop ax ; Then into AX
|
||
|
or ax,0100h ; Set the trap flag
|
||
|
push ax ; Trap Flag calls INT 01h between every instruction
|
||
|
popf ; Stuff new flags back into the flags register
|
||
|
pop ax ; Restore AX
|
||
|
|
||
|
cli ; Fake INT call: clear interrupt flag, skip clearing
|
||
|
pushf ; trap flag, push flags, call to location.
|
||
|
call cs:[realINT10] ; This call to INT 10h is be trapped for
|
||
|
; IN/OUT/INT Normal INT calls would clear
|
||
|
; the trap flag and then INT 01h would never
|
||
|
; be called.
|
||
|
iret ; end interrupt
|
||
|
INT10 endp
|
||
|
|
||
|
; function that prints the highest 4 bits of ax as text {0-9,A-F} to stdout
|
||
|
; ax will be shifted left 4 bits on return.
|
||
|
ShowHex proc
|
||
|
push ax ; save registers
|
||
|
push dx
|
||
|
|
||
|
shr ax,0Ch ; move the highest 4 bits to the lowest 4
|
||
|
and al,0Fh ; limit to lowest 4 bits
|
||
|
or al,30h ; change range to 30h-3Fh {0-9:;<=>?}
|
||
|
cmp al,39h ; if it is 30h-39h
|
||
|
jbe is_0_thru_9 ; then its already set
|
||
|
add al,07h ; otherwise change :;<=>? to A-F
|
||
|
is_0_thru_9:
|
||
|
mov dl,al
|
||
|
mov ah,02h
|
||
|
int 21h
|
||
|
|
||
|
pop dx ; restore dx
|
||
|
pop ax ; restore ax
|
||
|
shl ax,4 ; set up ax for next call
|
||
|
ret ; return
|
||
|
ShowHex endp
|
||
|
|
||
|
Start: ; Program begins here
|
||
|
mov si,0080h ; CS:0080h is the command line
|
||
|
cmp byte ptr [si],10 ; I want it to be at least 10 bytes long
|
||
|
jae process_command_line ; if not, abort
|
||
|
|
||
|
mov dx,offset no_command_line ; ds is preset
|
||
|
mov ah,09h ; Dos function 09h
|
||
|
int 21h ; Display no command line string
|
||
|
ret ; Exit program
|
||
|
|
||
|
process_command_line:
|
||
|
inc si ; move si to start of actual string
|
||
|
mov ax,[si+1] ; copy first 2 chrs to ax, skipping the space
|
||
|
mov bx,[si+3] ; copy 2nd two characters to bx
|
||
|
sub al,30h ; subtract 30h so chrs 0-9 have value 0-9
|
||
|
cmp al,09h ; if its 0-9, its ok.
|
||
|
jbe al_is_ok ; if its not, its probably A-F or a-f
|
||
|
sub al,07h ; so subtract 7 more
|
||
|
and al,0Fh ; and limit to 0-F
|
||
|
al_is_ok:
|
||
|
sub ah,30h ; do the same to ah
|
||
|
cmp ah,09h
|
||
|
jbe ah_is_ok
|
||
|
sub ah,07h
|
||
|
and ah,0Fh
|
||
|
ah_is_ok:
|
||
|
sub bl,30h ; do the same to bl
|
||
|
cmp bl,09h
|
||
|
jbe bl_is_ok
|
||
|
sub bl,07h
|
||
|
and bl,0Fh
|
||
|
bl_is_ok:
|
||
|
sub bh,30h ; do the same to bh
|
||
|
cmp bh,09h
|
||
|
jbe bh_is_ok
|
||
|
sub bh,07h
|
||
|
and bh,0Fh
|
||
|
bh_is_ok:
|
||
|
shl al,04h ; Combine the values so that AL-AH-BL-BH
|
||
|
or ah,al ; Goes into --AH- --AL-
|
||
|
mov al,bl ; <----AX--->
|
||
|
shl al,04h
|
||
|
or al,bh
|
||
|
mov word ptr [si],ax ; store the value over the string
|
||
|
|
||
|
mov ax,[si+6] ; copy 3rd 2 chrs to ax, skip the 2nd space
|
||
|
mov bx,[si+8] ; copy 4th two characters to bx
|
||
|
sub al,30h ; subtract 30h so chrs 0-9 have value 0-9
|
||
|
cmp al,09h ; if its 0-9, its ok.
|
||
|
jbe al_is_ok2 ; if its not, its probably A-F or a-f
|
||
|
sub al,07h ; so subtract 7 more
|
||
|
and al,0Fh ; and limit to 0-F
|
||
|
al_is_ok2:
|
||
|
sub ah,30h ; do the same to ah
|
||
|
cmp ah,09h
|
||
|
jbe ah_is_ok2
|
||
|
sub ah,07h
|
||
|
and ah,0Fh
|
||
|
ah_is_ok2:
|
||
|
sub bl,30h ; do the same to bl
|
||
|
cmp bl,09h
|
||
|
jbe bl_is_ok2
|
||
|
sub bl,07h
|
||
|
and bl,0Fh
|
||
|
bl_is_ok2:
|
||
|
sub bh,30h ; do the same to bh
|
||
|
cmp bh,09h
|
||
|
jbe bh_is_ok2
|
||
|
sub bh,07h
|
||
|
and bh,0Fh
|
||
|
bh_is_ok2:
|
||
|
shl al,04h ; Combine the values so that AL-AH-BL-BH
|
||
|
or ah,al ; Goes into --AH- --AL-
|
||
|
mov al,bl ; <----AX--->
|
||
|
shl al,04h
|
||
|
or al,bh
|
||
|
mov word ptr [si+2],ax ; store the value over the string
|
||
|
; Now [si] contains the real values of AX and BX
|
||
|
|
||
|
mov dx,offset tracing ; ds is preset
|
||
|
mov ah,09h ; Dos function 09h
|
||
|
int 21h ; Display tracing string
|
||
|
mov ax,word ptr [si] ; Restore ax
|
||
|
call ShowHex ; Display command line
|
||
|
call ShowHex ; ax value back to user
|
||
|
call ShowHex ; by placing it in ax
|
||
|
call ShowHex ; and calling ShowHex
|
||
|
mov dx,offset bx_msg ; ds is preset
|
||
|
mov ah,09h ; Dos function 09h
|
||
|
int 21h ; Display bx message
|
||
|
mov ax,word ptr [si+2] ; Restore bx into ax
|
||
|
call ShowHex ; Display command line
|
||
|
call ShowHex ; bx value back to user
|
||
|
call ShowHex ; by placing it in ax
|
||
|
call ShowHex ; and calling ShowHex
|
||
|
mov dx,offset header ; ds is preset
|
||
|
mov ah,09h ; Dos function 09h
|
||
|
int 21h ; Display header to output
|
||
|
|
||
|
mov ax,3501h ; Dos function 35h, Get vector of INT 01h
|
||
|
int 21h ; Store it in es:bx
|
||
|
mov word ptr [realINT1],bx ; Store address of original INT 01h
|
||
|
mov word ptr [realINT1+2],es ; into realINT1
|
||
|
|
||
|
mov ax,3510h ; Dos function 35h, Get vector of INT 10h
|
||
|
int 21h ; Store it in es:bx
|
||
|
mov word ptr [realINT10],bx ; Store address of original INT 10h
|
||
|
mov word ptr [realINT10+2],es ; into realINT10 so we can fake an INT
|
||
|
|
||
|
mov ax,2501h ; Dos function 25h, Store DS:DX to INT 01h
|
||
|
mov dx,offset INT1 ; ds is preset, dx is the handler's offset
|
||
|
int 21h ; Set new Single Step handler
|
||
|
|
||
|
mov ax,2510h ; Dos function 25h, Store DS:DX to INT 10h
|
||
|
mov dx,offset INT10 ; ds is preset, dx is the handler's offset
|
||
|
int 21h ; Set new Video Interrupt
|
||
|
|
||
|
mov ax,word ptr [si] ; We will use the command line ax/bx
|
||
|
mov bx,word ptr [si+2] ; values for the fake int call
|
||
|
int 10h ; Call my int 10 which fakes the
|
||
|
; real int 10 and traps it.
|
||
|
|
||
|
mov ax,2501h ; Dos function 25h, Store DS:DX to INT 01h
|
||
|
mov dx,word ptr [realINT1] ; ds/dx are in realINT1
|
||
|
push ds ; Save old ds
|
||
|
push word ptr [realINT1+2] ; Put segment on stack
|
||
|
pop ds ; Set ds to the segment
|
||
|
int 21h ; Reset old Single Step handler
|
||
|
pop ds ; Restore old ds
|
||
|
|
||
|
mov ax,2510h ; Dos function 25h, Store DS:DX to INT 10h
|
||
|
mov dx,word ptr [realINT10] ; ds/dx are in realINT10
|
||
|
push ds ; Save old ds
|
||
|
push word ptr [realINT10+2] ; Put segment on stack
|
||
|
pop ds ; Set ds to the segment
|
||
|
int 21h ; Reset old Video Interrupt
|
||
|
pop ds ; Restore old ds
|
||
|
|
||
|
mov ax,0003h ; Set ax to 3
|
||
|
int 10h ; Set 80x25 Text mode
|
||
|
ret ; End of program
|
||
|
end ; End of file
|