1; TRAPOUT2.ASM v2.0 by ARK (ark@lhq.com, root@ark.dyn.ml.org) 11-28-97
2; Traps IN and OUT instructions in INT 10h and displays DX and AX/AL values.
3;
4; In the header "T DX/I AX/L", T is the Type of instruction (I=IN, O=OUT),
5;  DX/I is the value of DX or the Immediate value if port<256, and AX/L
6;  is the value of AX or AL depending on if an 8 or 16 bit value is listed.
7;  AX/L is meaningless for IN's since it is the value if AX/L *before* the
8;  call to IN.
9;
10; This is very useful to find information about how your video card works.
11; I wrote this to get register dumps for my Trident TVGA9440AGi card so
12;  that I could use it under Linux.
13;
14; NOTE: Pipe the output or you won't see anything!
15;  (ex: TRAP-OUT 4F02 0101 > 640x480.256)
16;
17; New in v2.0:
18;  * Traces into INT 10 calls that are called from inside INT 10!
19;  * Allows AX and BX values to be specified!
20;  * Command line accepts trailing spaces now.
21;  x Code to trap INT's also! (T column='N', DX/I=INT ##, AX/L=AX value)
22;    (Its commented out - but you can recompile with it if you want)
23;
24; How to assemble with Borland:
25; tasm /ml /zd ncr.asm (case sensitive, line number debug info only)
26; tlink /x /t ncr.obj (no map, make com file)
27;
28
29.model tiny           ; Tiny memory model, all segments point to the same 64K
30.286                  ; This code will run on a 286...           actually, it
31.code                 ; Everything is in the code segment(cs)   will probably
32.startup              ; Startup                               run on anything
33
34jmp Start              ; Go to beginning of progam
35realINT1 dd 52411A3Eh  ; Address of original INT 01h routine offset
36realINT10 dd 3C1B214Bh ; Memory for [es:bx] of the real INT 10h
37                       ; (defaults are '>-ARK!-<' just for looks in the .COM)
38
39; strings
40no_command_line db 'Use: TRAPOUT2 [AX] [BX]',13,10
41db '     Traces all IN/OUT calls inside INT 10h',13,10,36
42tracing db 'Tracing INT 10h with AX:',36
43bx_msg db ' BX:',36
44header db 13,10,'T DX/I AX/L',13,10,36
45
46INT1 proc               ; Interrupt Service Routine for Single Step Debugging
47        push ax                                              ; save registers
48        push dx
49        push es
50        push di
51        push bp
52
53        mov bp,sp                  ; set bp to the stack
54        push word ptr cs:[bp+12]   ; put the real cs
55        pop es                     ;  into es
56        push word ptr cs:[bp+10]   ; put the real ip
57        pop di                     ;  into di
58        mov al,byte ptr es:[di]    ; set al to the next instruction that will
59                                   ;  be executed after this INT 01 is done.
60
61; This code will trap INT's also...
62;        cmp al,0CDh                ; If al is not CD (INT) keep going
63;        jne not_int                ; If it is, display some stuff...
64;; This will skip doing the INT's...
65;;        add word ptr cs:[bp+10],2  ; Add 2 to the real ip, to skip the INT
66;        mov dl,4Eh                 ; Display an N
67;        mov ah,02h                 ;  The immediate value/DX is the INT ##
68;        int 21h                    ;  that is called. AX is the value before
69;        mov dl,20h                 ; Display a space
70;        mov ah,02h                 ;
71;        int 21h                    ; Display the immediate value which is
72;        jmp is_imm                 ;  reallly the interrupt number called.
73
74    not_int:
75        and al,0F4h                ; If al is E4-E7 or EC-EF (all IN/OUT's)
76        cmp al,0E4h                ; Then we display our stuff
77        jne not_io                 ; Otherwise, do nothing
78                                   ; note: 1 more byte of code after this
79                                   ; jmp will make it out of range...
80
81        mov al,byte ptr es:[di]         ; Set al to next instruction
82        test al,02h                     ; If bit 1 is set then we have an OUT
83        jz is_in                        ; If bit 1 is 0, we have an IN
84
85        mov dl,4Fh                                             ; Display an O
86        mov ah,02h
87        int 21h
88        jmp dx_or_imd
89
90    is_in:                                                     ; Display an I
91        mov dl,49h
92        mov ah,02h
93        int 21h
94
95    dx_or_imd:                                              ; Display a space
96        mov dl,20h
97        mov ah,02h
98        int 21h
99
100        mov al,byte ptr es:[di]    ; Set al to next instruction
101        test al,08h                ; If bit 3 is set then we are using DX
102        jz is_imm                  ; If bit 3 is 0, we are using an immediate
103
104        mov ax,[bp+6]                                      ; restore dx to ax
105        call ShowHex                                       ; Display dx
106        call ShowHex
107        call ShowHex
108        call ShowHex
109        jmp ax_or_al
110
111    is_imm:
112        mov dl,20h                                         ; Display 2 spaces
113        mov ah,02h
114        int 21h
115        mov dl,20h
116        mov ah,02h
117        int 21h
118
119        mov ah,byte ptr es:[di+1] ; Set ah to byte after the next instruction
120        call ShowHex              ; Display the immediate value
121        call ShowHex
122
123    ax_or_al:
124        mov dl,2Ch                                          ; Display a comma
125        mov ah,02h
126        int 21h
127
128        mov al,byte ptr es:[di]        ; Set al to next instruction
129        test al,01h                    ; If bit 0 is set then we are using AX
130        jz is_al                       ; If bit 0 is 0, we are using AL
131
132        mov ax,[bp+8]                                            ; Restore ax
133        call ShowHex                                             ; Display ax
134        call ShowHex
135        call ShowHex
136        call ShowHex
137        jmp print_next_line
138
139    is_al:
140        mov ah,[bp+8]                                      ; Restore al to ah
141        call ShowHex                                       ; Display al
142        call ShowHex
143
144    print_next_line:
145        mov dl,0Dh                                          ; print a newline
146        mov ah,02h
147        int 21h
148        mov dl,0Ah
149        mov ah,02h
150        int 21h
151
152    not_io:
153        pop bp                                            ; restore registers
154        pop di
155        pop es
156        pop dx
157        pop ax
158        iret                                                  ; end interrupt
159INT1 endp
160
161; INT 10h that fakes the real INT 10 and sets the trap flag.
162INT10 proc                    ; Interrupt Service Routine for Tracing INT 10h
163        push ax           ; Save AX
164        pushf             ; Put flags on the stack
165        pop ax            ; Then into AX
166        or ax,0100h       ; Set the trap flag
167        push ax           ; Trap Flag calls INT 01h between every instruction
168        popf              ; Stuff new flags back into the flags register
169        pop ax            ; Restore AX
170
171        cli              ; Fake INT call: clear interrupt flag, skip clearing
172        pushf            ;  trap flag, push flags, call to location.
173        call cs:[realINT10]      ; This call to INT 10h is be trapped for
174                                 ; IN/OUT/INT Normal INT calls would clear
175                                 ; the trap flag and then INT 01h would never
176                                 ; be called.
177        iret                                                  ; end interrupt
178INT10 endp
179
180; function that prints the highest 4 bits of ax as text {0-9,A-F} to stdout
181; ax will be shifted left 4 bits on return.
182ShowHex proc
183        push ax                                              ; save registers
184        push dx
185
186        shr ax,0Ch                  ; move the highest 4 bits to the lowest 4
187        and al,0Fh                  ; limit to lowest 4 bits
188        or  al,30h                  ; change range to 30h-3Fh {0-9:;<=>?}
189        cmp al,39h                  ; if it is 30h-39h
190        jbe is_0_thru_9             ; then its already set
191        add al,07h                  ; otherwise change :;<=>? to A-F
192    is_0_thru_9:
193        mov dl,al
194        mov ah,02h
195        int 21h
196
197        pop dx                                      ; restore dx
198        pop ax                                      ; restore ax
199        shl ax,4                                    ; set up ax for next call
200        ret                                         ; return
201ShowHex endp
202
203Start:                               ; Program begins here
204        mov si,0080h                 ; CS:0080h is the command line
205        cmp byte ptr [si],10         ; I want it to be at least 10 bytes long
206        jae  process_command_line    ; if not, abort
207
208        mov dx,offset no_command_line        ; ds is preset
209        mov ah,09h                           ; Dos function 09h
210        int 21h                              ; Display no command line string
211        ret                                  ; Exit program
212
213    process_command_line:
214        inc si                  ; move si to start of actual string
215        mov ax,[si+1]           ; copy first 2 chrs to ax, skipping the space
216        mov bx,[si+3]           ; copy 2nd two characters to bx
217        sub al,30h              ; subtract 30h so chrs 0-9 have value 0-9
218        cmp al,09h              ; if its 0-9, its ok.
219        jbe al_is_ok            ; if its not, its probably A-F or a-f
220        sub al,07h              ; so subtract 7 more
221        and al,0Fh              ; and limit to 0-F
222    al_is_ok:
223        sub ah,30h              ; do the same to ah
224        cmp ah,09h
225        jbe ah_is_ok
226        sub ah,07h
227        and ah,0Fh
228    ah_is_ok:
229        sub bl,30h              ; do the same to bl
230        cmp bl,09h
231        jbe bl_is_ok
232        sub bl,07h
233        and bl,0Fh
234    bl_is_ok:
235        sub bh,30h              ; do the same to bh
236        cmp bh,09h
237        jbe bh_is_ok
238        sub bh,07h
239        and bh,0Fh
240    bh_is_ok:
241        shl al,04h              ; Combine the values so that AL-AH-BL-BH
242        or  ah,al               ; Goes into                  --AH- --AL-
243        mov al,bl               ;                            <----AX--->
244        shl al,04h
245        or  al,bh
246        mov word ptr [si],ax    ; store the value over the string
247
248        mov ax,[si+6]           ; copy 3rd 2 chrs to ax, skip the 2nd space
249        mov bx,[si+8]           ; copy 4th two characters to bx
250        sub al,30h              ; subtract 30h so chrs 0-9 have value 0-9
251        cmp al,09h              ; if its 0-9, its ok.
252        jbe al_is_ok2           ; if its not, its probably A-F or a-f
253        sub al,07h              ; so subtract 7 more
254        and al,0Fh              ; and limit to 0-F
255    al_is_ok2:
256        sub ah,30h              ; do the same to ah
257        cmp ah,09h
258        jbe ah_is_ok2
259        sub ah,07h
260        and ah,0Fh
261    ah_is_ok2:
262        sub bl,30h              ; do the same to bl
263        cmp bl,09h
264        jbe bl_is_ok2
265        sub bl,07h
266        and bl,0Fh
267    bl_is_ok2:
268        sub bh,30h              ; do the same to bh
269        cmp bh,09h
270        jbe bh_is_ok2
271        sub bh,07h
272        and bh,0Fh
273    bh_is_ok2:
274        shl al,04h              ; Combine the values so that AL-AH-BL-BH
275        or  ah,al               ; Goes into                  --AH- --AL-
276        mov al,bl               ;                            <----AX--->
277        shl al,04h
278        or  al,bh
279        mov word ptr [si+2],ax  ; store the value over the string
280                             ; Now [si] contains the real values of AX and BX
281
282        mov dx,offset tracing                      ; ds is preset
283        mov ah,09h                                 ; Dos function 09h
284        int 21h                                    ; Display tracing string
285        mov ax,word ptr [si]                       ; Restore ax
286        call ShowHex                               ; Display command line
287        call ShowHex                               ;  ax value back to user
288        call ShowHex                               ;  by placing it in ax
289        call ShowHex                               ;  and calling ShowHex
290        mov dx,offset bx_msg                       ; ds is preset
291        mov ah,09h                                 ; Dos function 09h
292        int 21h                                    ; Display bx message
293        mov ax,word ptr [si+2]                     ; Restore bx into ax
294        call ShowHex                               ; Display command line
295        call ShowHex                               ;  bx value back to user
296        call ShowHex                               ;  by placing it in ax
297        call ShowHex                               ;  and calling ShowHex
298        mov dx,offset header                       ; ds is preset
299        mov ah,09h                                 ; Dos function 09h
300        int 21h                                    ; Display header to output
301
302        mov ax,3501h                ; Dos function 35h, Get vector of INT 01h
303        int 21h                     ; Store it in es:bx
304        mov word ptr [realINT1],bx   ; Store address of original INT 01h
305        mov word ptr [realINT1+2],es ; into realINT1
306
307        mov ax,3510h                ; Dos function 35h, Get vector of INT 10h
308        int 21h                     ; Store it in es:bx
309        mov word ptr [realINT10],bx  ; Store address of original INT 10h
310        mov word ptr [realINT10+2],es ; into realINT10 so we can fake an INT
311
312        mov ax,2501h               ; Dos function 25h, Store DS:DX to INT 01h
313        mov dx,offset INT1         ; ds is preset, dx is the handler's offset
314        int 21h                    ; Set new Single Step handler
315
316        mov ax,2510h               ; Dos function 25h, Store DS:DX to INT 10h
317        mov dx,offset INT10        ; ds is preset, dx is the handler's offset
318        int 21h                    ; Set new Video Interrupt
319
320        mov ax,word ptr [si]             ; We will use the command line ax/bx
321        mov bx,word ptr [si+2]           ; values for the fake int call
322        int 10h                          ; Call my int 10 which fakes the
323                                         ;  real int 10 and traps it.
324
325        mov ax,2501h               ; Dos function 25h, Store DS:DX to INT 01h
326        mov dx,word ptr [realINT1] ; ds/dx are in realINT1
327        push ds                    ; Save old ds
328        push word ptr [realINT1+2] ; Put segment on stack
329        pop ds                     ; Set ds to the segment
330        int 21h                    ; Reset old Single Step handler
331        pop ds                     ; Restore old ds
332
333        mov ax,2510h               ; Dos function 25h, Store DS:DX to INT 10h
334        mov dx,word ptr [realINT10] ; ds/dx are in realINT10
335        push ds                     ; Save old ds
336        push word ptr [realINT10+2] ; Put segment on stack
337        pop ds                      ; Set ds to the segment
338        int 21h                     ; Reset old Video Interrupt
339        pop ds                      ; Restore old ds
340
341        mov ax,0003h               ; Set ax to 3
342        int 10h                    ; Set 80x25 Text mode
343        ret                        ; End of program
344end                                                             ; End of file
345