Category : Files from Magazines
Archive   : DDJ-9003.ZIP
Filename : WILLIAMS.LST
by Al Williams
[LISTING ONE]
;******************************************************************************
;* File: BREAK386.ASM *
;* BREAK386 "main programs". Contains setup386, clear386, break386 and *
;* int1_386. *
;* Williams - June, 1989 *
;* Compile with: MASM /Ml BREAK386; *
;******************************************************************************
.MODEL small
.386P
public _break386,_clear386,_setup386,_int1_386
; Set up stack offsets for word size arguments based on the code size
; Be careful, regardless of what Microsoft's documentation says,
; you must use @CodeSize (not @codesize, etc.) when compiling with /Ml
IF @CodeSize ; True for models with far code
arg1 EQU <[BP+6]>
arg2 EQU <[BP+8]>
arg3 EQU <[BP+10]>
arg4 EQU <[BP+12]>
ELSE
arg1 EQU <[BP+4]>
arg2 EQU <[BP+6]>
arg3 EQU <[BP+8]>
arg4 EQU <[BP+10]>
ENDIF
.DATA
; Things you may want to change:
DIRECT EQU 0 ; IF 0 use BIOS; IF 1 use direct video access
STKWRD EQU 32 ; # of words to dump off the stack
INTSTACK EQU 1 ; When 0 don't display interrupt stack words
USE_INT1 EQU 1 ; Set to 0 to disable int1_386()
oldoffset dw 0 ; old interrupt 1 vector offset
oldsegment dw 0 ; old interrupt 1 vector segment
IF USE_INT1
video dw 0b000H ; segment of video adapter (changed by vinit)
csip db 'CODE=',0
done db 'Program terminated normally.',0
notdone db 'Program breakpoint:',0
stkmess db 'Stack dump:',0
vpage db 0
vcols db 80
IFE DIRECT
prompt db '
savcursor dw 0 ; inactive video cursor
ALIGN 4
vbuff dd 1000 dup (07200720H)
ELSE
cursor dw 0
color db 7
ENDIF
ENDIF
.CODE
; This is the start up code. The old interrupt one vector is saved in
; oldsegment, oldoffset. int1_386 does not chain to the old vector, it
; simply replaces it.
_setup386 proc
push bp
mov bp,sp
push es
mov ax,3501H ; get old int1 vector
int 21h
mov ax,es
mov oldsegment,ax
mov oldoffset,bx
pop es
mov ax,arg2 ; get new interrupt handler address
push ds
mov dx,arg1
; If int1_386 is being assembled, setup386 will check to see if you are
; installing int1386. If so, it will call vinit to set up the video parameters
; that int1_386 requires.
IF USE_INT1
cmp ax,seg _int1_386
jnz notus
cmp dx,offset _int1_386
jnz notus
push dx
push ax
call vinit ; Int'l video if it is our handler
pop ds
pop dx
ENDIF
notus: mov ax,2501H ; Store interrupt address in vector table
int 21H
pop ds
xor eax,eax ; Clear DR7/DR6 (just in case)
mov dr7,eax
mov dr6,eax
pop bp
ret
_setup386 endp
; This routine sets/clears breakpoints
; Inputs:
; breakpoint # (1-4)
; breakpoint type (see BREAK386.INC)
; segment/offset of break address (or null to clear breakpoint)
; Outputs:
; AX=0 If successful
; AX=-1 If not successful
_break386 proc
push bp
mov bp,sp
mov bx,arg1 ; breakpoint # (1-4)
cmp bx,1
jb outrange
cmp bx,4
jna nothigh
outrange:
mov ax,0ffffH ; error: breakpoint # out of range
pop bp
ret
nothigh:
movzx eax,word ptr arg4 ; get breakpoint address
shl eax,4
movzx edx,word ptr arg3 ; calculate linear address
add eax,edx ; if address = 0 then
jz resetbp ; turn breakpoint off!
dec bx ; set correct address register
jz bp0
dec bx
jz bp1
dec bx
jz bp2
mov dr3,eax
jmp short brcont
bp0: mov dr0,eax
jmp short brcont
bp1: mov dr1,eax
jmp short brcont
bp2: mov dr2,eax
brcont:
movzx eax,word ptr arg2 ; get type
mov cx,arg1 ; calculate proper position
push cx
dec cx
shl cx,2
add cx,16
shl eax,cl ; rotate type
mov edx,0fh
shl edx,cl ; calculate type mask
not edx
pop cx
shl cx,1 ; calculate position of enable bit
dec cx
mov ebx,1
shl ebx,cl
or eax,ebx ; enable bp
mov ebx,dr7 ; get old DR7
and ebx,edx ; mask out old type
or ebx,eax ; set new type/enable bits
; Adjust enable bit (set on for data bp's, off if no data bp's)
adjge:
mov eax,200H
and ebx,0fffffdffH ; reset GE bit
test ebx,033330000H ; test for data bp's
jz nodatabp
or ebx,512
nodatabp:
mov dr7,ebx
pop bp
xor ax,ax
ret
; Here we reset a breakpoint by turning off it's enable bit & setting type to 0
; Clearing the type is required so that disabling all data breakpoints will
; clear the GE bit also.
resetbp:
mov cx,bx ; calculate type/len bit positions
mov edx,0fh
dec cx
shl cx,2
add cx,16
shl edx,cl
not edx
mov cx,bx ; calculate enable bit position
shl cx,1
dec cx
mov eax,1
shl eax,cl
not ax ; flip bits
mov ebx,dr7
and ebx,eax ; clear enable
and ebx,edx ; clear type
jmp adjge
_break386 endp
; Reset the debug register, disabling all breakpoint. Also restore the old
; interrupt 1 vector
_clear386 proc
pushf
pop ax
and ax,0FEFFH ; turn off trace flag
push ax
popf
xor eax,eax ; turn off all other breakpoints
mov dr7,eax
mov dr0,eax
mov dr1,eax
mov dr2,eax
mov dr3,eax
mov dr6,eax
mov ax,2501H ; restore old int 1 vector
push ds
mov bx,oldsegment
mov dx,oldoffset
mov ds,bx
int 21H
pop ds
ret
_clear386 endp
IF USE_INT1
; This is all code relating to the optional INT 1 handler
; This macro is used to get a register value off the stack and display it
; R is the register name and n is the position of the register on the stack
; i.e.: outreg 'AX',10
outreg macro r,n
mov ax,&r
mov dx,[ebp+&n SHL 1]
call regout
endm
; This is the interrupt 1 handler
_int1_386 proc far
sti ; Enable interrupts (see text)
pusha ; Save all Registers
push ds
push es
push ss
push @data
pop ds ; Reload DS
mov bp,sp ; point ebp to top of stack
IFE DIRECT
call savevideo
ENDIF
mov ax,video ; get video addressabilty
mov es,ax
assume cs:@code,ds:@data
mov bx,offset notdone ; Display breakpoint message
call outstr
mov edx,dr6
call hexout
xor edx,edx
mov dr6,edx
call crlf
;do register dump
outreg 'AX',10
outreg 'FL',13
outreg 'BX',7
outreg 'CX',9
outreg 'DX',8
call crlf
outreg 'SI',4
outreg 'DI',3
outreg 'SP',6
outreg 'BP',5
call crlf
outreg 'CS',12
outreg 'IP',11
outreg 'DS',2
outreg 'ES',1
outreg 'SS',0
call crlf
; do stack dump
IF STKWRD
mov bx,offset stkmess
call outstr ; Print stack dump title
push fs
mov dx,[ebp] ; get program's ss
mov fs,dx
mov al,'('
call ouch
mov al,' '
call ouch
call hexout
mov al,':'
call ouch
mov al,' '
call ouch
mov bx,[ebp+12] ; get stack pointer (before pusha)
IFE INTSTACK
add bx,6 ; skip interrupt info if desired
ENDIF
mov dx,bx
push bx
call hexout
mov al,')'
call ouch
call crlf
pop bx
mov cx,STKWRD
sloop:
mov dx,fs:[bx] ; get word at stack
push bx
push cx
call hexout ; display it
pop cx
pop bx
inc bx
inc bx
loop sloop
pop fs
ENDIF
nostack:
; Here we will dump 16 bytes starting 8 bytes prior to the instruction
; that caused the break
push fs
call crlf
mov bx, offset csip
call outstr
mov cx,8
mov ax,[ebp+24] ; get cs
mov fs,ax
mov bx,[ebp+22] ; get ip
cmp bx,8 ; make sure we have 8 bytes before
jnb ipbegin ; the begining of the segment
mov cx,bx ; If not, only dump from the start
ipbegin: sub bx,cx ; of the segment
push bx
push cx
mov dx,ax ; display address
call hexout
mov al,':'
call ouch
mov al,' '
call ouch
mov dx,bx
call hexout
mov al,'='
call ouch
pop cx
pop bx
or bx,bx ; if starting at 0, don't display any
jz ipskip ; before IP
iploop:
mov dl,fs:[bx] ; get byte
push bx
push cx
call hex1out ; output it
pop cx
pop bx
inc bx
loop iploop
ipskip:
push bx
mov al,'*' ; put '*' before IP location
call ouch
mov al,' '
call ouch
pop bx
; This is basically a repeat of the above loop except it dumps the 8 bytes
; starting at IP
mov cx,8
xiploop:
mov dl,fs:[bx]
push bx
push cx
call hex1out
pop cx
pop bx
inc bx
loop xiploop
call crlf
call crlf
pop fs
IFE DIRECT
; Here we will ask if we should continue or abort
mov bx,offset prompt
call outstr
keyloop:
xor ah,ah ; Get keyboard input
int 16H
and al,0dfh ; make upper case
cmp al,'T'
jz ttoggle
cmp al,'A'
jz q1
cmp al,'C'
jz c1
cmp al,'V'
jnz keyloop
; Display program's screen until any key is pressed
call savevideo
xor ah,ah
int 16H
call savevideo
jmp keyloop
; Execution comes here to toggle trace flag and continue
ttoggle:
xor word ptr [bp+26],256 ; toggle trace flag on stack
; Execution comes here to continue running the target program
c1:
call crlf
IFE DIRECT
call savevideo
ELSE
xor ax,ax
mov cursor,ax
ENDIF
pop ss
pop es
pop ds
popa
; This seems complicated at first.
; You MUST insure that RF is set before continuing. If RF is not set
; you will just cause a breakpoint immediately!
; In protected mode, this is handled automatically. In real mode it
; isn't since RF is in the high 16 bits of the flags register.
; Essentially we have to convert the stack from:
;
; 16 bit Flags 32 bit flags (top word = 1 to set RF)
; 16 bit CS to -----> 32 bit CS (garbage in top 16 bits)
; 16 bit IP 32 bit IP (top word = 0)
;
; All this so we can execute an IRETD which will change RF.
sub esp,6 ; make a double stack frame
xchg ax,[esp+6] ; get ip in ax
mov [esp],ax ; store it
xor ax,ax
mov [esp+2],ax ; eip = 0000:ip
mov ax,[esp+6]
xchg ax,[esp+8] ; get cs
mov [esp+4],ax
xor ax,ax
mov [esp+6],ax
mov ax,[esp+8] ; zero that stack word & restore ax
xchg ax,[esp+10] ; get flags
mov [esp+8],ax
mov ax,1 ; set RF
xchg ax,[esp+10]
iretd ; DOUBLE IRET (32 bits!)
ENDIF
; Execution resumes here to abort the target program
q1:
IFE DIRECT
call savevideo
ENDIF
call quit
_int1_386 endp
IFE DIRECT
; save video screen & restore ours (only with BIOS please!)
; (assumes 25 lines/page)
savevideo proc near
pusha
push es
mov ah,0fh
int 10h ; reread video page/size in case
mov vpage,bh ; program changed it
mov vcols,ah
push savcursor
mov ah,3 ; get old cursor
mov bh,vpage
int 10H
mov savcursor,dx
pop dx
mov ah,2 ; set new cursor
int 10H
movzx ax,vpage
mov cl,vcols ; compute # bytes/page
xor ch,ch
mov dx,cx ; vcols * 25 * 2
shl cx,3
shl dx,1
add cx,dx
mov dx,cx
shl cx,2
add cx,dx
push cx
mul cx
mov di,ax ; start at beginning of page
pop cx
shr cx,2 ; # of double words to transfer
mov ax,video
mov es,ax
mov si,offset vbuff ; store inactive screen in vbuff
xloop: mov eax,es:[di] ; swap screens
xchg eax,[si]
mov es:[di],eax
add si,4
add di,4
loop xloop
pop es
popa
ret
savevideo endp
ENDIF
; This routine prints a register value complete with label
; The register name is in AX and the value is in dx (see the outreg macro)
regout proc near
push dx
push ax
mov al,ah
call ouch
pop ax
call ouch
mov al,'='
call ouch
pop dx
call hexout
ret
regout endp
; Plain vanilla hexadecimal digit output routine
hexdout proc near
and dl,0fh
add dl,'0'
cmp dl,3ah
jb ddigit
add dl,'A'-3ah
ddigit:
mov al,dl
call ouch
ret
hexdout endp
; Plain vanilla hexadecimal word output routine
hexout proc near
push dx
shr dx,12
call hexdout
pop dx
push dx
shr dx,8
call hexdout
pop dx
; Call with this entry point to output just a byte
hex1out:
push dx
shr dx,4
call hexdout
pop dx
call hexdout
mov al,' '
call ouch
ret
hexout endp
; These routines are for direct video output. Using them allows you to
; debug video bios calls, but prevents you from single stepping
IF DIRECT
;output a character in al assumes ds=dat es=video destroys bx,ah
ouch proc near
mov bx,cursor
mov ah,color
mov es:[bx],ax
inc bx
inc bx
mov cursor,bx
ret
ouch endp
;
crlf proc near
mov ax,cursor
mov cx,160
xor dx,dx
div cx
inc ax
mul cx
mov cursor,ax
mov cx,80
mov ah,color
mov al,' '
mov di,cursor
cld
rep stosw
ret
crlf endp
ELSE
; These are the BIOS output routines
; Output a character
ouch proc near
mov ah,0eh
mov bh,vpage
int 10h
ret
ouch endp
;
crlf proc near
mov al,0dh
call ouch
mov al,0ah
call ouch
ret
crlf endp
ENDIF
; Intialize the video routines
vinit proc near
mov ah,0fh
int 10h
mov vcols,ah
mov vpage,bh
cmp al,7 ; monochrome
mov ax,0b000H
jz vexit
mov ax,0b800H
vexit: mov video,ax
ret
vinit endp
; outputs string pointed to by ds:bx (ds must be dat) es= video when DIRECT=1
outstr proc near
outagn:
mov al,[bx]
or al,al
jz outout
push bx
call ouch
pop bx
inc bx
jmp outagn
outout: ret
outstr endp
; This routine is called to return to DOS
quit proc near
call _clear386
mov ax,4c00h ; Return to DOS
int 21h
quit endp
ENDIF
end
[LISTING TWO
;******************************************************************************
;* File: BREAK386.INC *
;* Header file to include with assembly language programs using BREAK386 *
;* Williams - June, 1989 *
;******************************************************************************
IF @CodeSize ; If large style models
extrn _break386:far,_clear386:far,_setup386:far,_int1_386:far
ELSE
extrn _break386:near,_clear386:near,_setup386:near,_int1_386:far
ENDIF
; Breakpoint equates
BP_CODE EQU 0 ; CODE BREAKPOINT
BP_DATAW1 EQU 1 ; ONE BYTE DATA WRITE BREAKPOINT
BP_DATARW1 EQU 3 ; ONE BYTE DATA R/W BREAKPOINT
BP_DATAW2 EQU 5 ; TWO BYTE DATA WRITE BREAKPOINT
BP_DATARW2 EQU 7 ; TWO BYTE DATA R/W BREAKPOINT
BP_DATAW4 EQU 13 ; FOUR BYTE DATA WRITE BREAKPOINT
BP_DATARW4 EQU 15 ; FOUR BYTE DATA R/W BREAKPOINT
; Macros to turn tracing on and off
; Note: When tracing, you will actually "see" traceoff before it turns
; tracing off
traceon macro
push bp
pushf
mov bp,sp
xchg ax,[bp]
or ax,100H
xchg ax,[bp]
popf
pop bp
endm
traceoff macro
push bp
pushf
mov bp,sp
xchg ax,[bp]
and ax,0FEFFH
xchg ax,[bp]
popf
pop bp
endm
[LISTING THREE]
/******************************************************************************
* File: BREAK386.H *
* C Header for C programs using BREAK386 or CBRK386 *
* Williams - June, 1989 *
*****************************************************************************/
#ifndef NO_EXT_KEYS
#define _CDECL cdecl
#else
#define _CDECL
#endif
#ifndef BR386_HEADER
#define BR386_HEADER
/* declare functions */
void _CDECL setup386(void (_CDECL interrupt far *)());
void _CDECL csetup386(void (_CDECL far *)());
void _CDECL clear386(void);
int _CDECL break386(int,int, void far *);
void _CDECL far interrupt int1_386();
/* breakpoint types */
#define BP_CODE 0 /* CODE BREAKPOINT*/
#define BP_DATAW1 1 /* ONE BYTE DATA WRITE BREAKPOINT*/
#define BP_DATARW1 3 /* ONE BYTE DATA R/W BREAKPOINT*/
#define BP_DATAW2 5 /* TWO BYTE DATA WRITE BREAKPOINT*/
#define BP_DATARW2 7 /* TWO BYTE DATA R/W BREAKPOINT*/
#define BP_DATAW4 13 /* FOUR BYTE DATA WRITE BREAKPOINT*/
#define BP_DATARW4 15 /* FOUR BYTE DATA R/W BREAKPOINT*/
#endif
[LISTING FOUR]
;******************************************************************************
;* File: DEBUG386.ASM *
;* Example assembly language program for use with BREAK386 *
;* Williams - June, 1989 *
;* Compile with: MASM /Ml DEBUG386.ASM; *
;******************************************************************************
.model large
.386
INCLUDE break386.inc
.stack 0a00H
.data
align 2 ; make sure this is word aligned
memcell dw 0 ; cell to write to
.code
main proc
;setup data segment
mov ax,@data
mov ds,ax
assume cs:@code,ds:@data
; start debugging
push seg _int1_386 ; segment of interrupt handler
push offset _int1_386 ; offset of interrupt handler
call _setup386
add sp,4 ; balance stack (like a call to C)
; set up a starting breakpoint
push seg bp1 ; segment of breakpoint
push offset bp1 ; offset of breakpoint
push BP_CODE ; breakpoint type
push 1 ; breakpoint # (1-4)
call _break386
add sp,8 ; balance the stack
push seg bp2 ; set up breakpoint #2
push offset bp2
push BP_CODE
push 2
call _break386
add sp,8
push seg bp3 ; set up breakpoint #3
push offset bp3
push BP_CODE
push 3
call _break386
add sp,8
push @data ; set up breakpoint #4 (data)
push offset memcell
push BP_DATAW2
push 4
call _break386
add sp,8
bp1:
mov cx,20 ; loop 20 times
loop1:
mov dl,cl ; print some letters
add dl,'@'
mov ah,2
bp2:
int 21h
bp3:
loop loop1 ; repeat
mov bx,offset memcell ; point bx at memory cell
mov ax,[bx] ; read cell (no breakpoint)
mov [bx],ah ; this should cause breakpoint 4
call _clear386 ; shut off debugging
mov ah,4ch
int 21h ; back to DOS
main endp
end main
[LISTING FIVE]
/****************************************************************************
* File: DBG386.C *
* Example C program using BREAK386 with the built in interrupt handler *
* Al Williams -- 15 July 1989 *
* Compile with: CL DBG386.C BREAK386 *
****************************************************************************/
#include
#include
#include "break386.h"
int here[10];
void far *bp;
int i;
main()
{
int j;
setup386(int1_386); /* set up debugging */
bp=(void far *)&here[2]; /* make long pointer to data word */
break386(1,BP_DATAW2,bp); /* set breakpoint */
for (j=0;j<2;j++) { /* loop twice */
for (i=0;i<10;i++) /* for each element in here[] */
{
char x;
putchar(i+'0'); /* print index digit */
here[i]=i; /* assign # to array element */
}
break386(1,0,NULL); /* turn off breakpoint on 2nd pass */
}
clear386(); /* turn off debugging */
}
[LISTING SIX]
;******************************************************************************
;* File: DBGOFF.ASM *
;* Try this program if you leave a program abnormally (say, with a stack *
;* overflow). It will reset the debug register. *
;* Williams - June, 1989 *
;* Compile with: MASM DBGOFF; *
;******************************************************************************
.model small
.386P
.stack 32
.code
main proc
xor eax,eax ; clear dr7
mov dr7,eax
mov ah,4ch ; exit to DOS
int 21H
main endp
end main
[LISTING SEVEN]
;******************************************************************************
;* File: CBRK386.ASM *
;* Functions to allow breakpoint handlers to be written in C. *
;* Williams - June, 1989 *
;* Compile with: MASM /Ml CBRK386.ASM; *
;******************************************************************************
.MODEL small
.386P
public _csetup386
; Set up stack offsets for word size arguments based on the code size
; Be careful, regardless of what Microsoft's documentation says,
; you must use @CodeSize (not @codesize, etc.)
IF @CodeSize ; True for models with far code
arg1 EQU <[BP+6]>
arg2 EQU <[BP+8]>
arg3 EQU <[BP+10]>
arg4 EQU <[BP+12]>
ELSE
arg1 EQU <[BP+4]>
arg2 EQU <[BP+6]>
arg3 EQU <[BP+8]>
arg4 EQU <[BP+10]>
ENDIF
.DATA
; You may need to change the next line to expand the stack your breakpoint
; handler runs with
STACKSIZE EQU 2048
oldoffset dw 0 ; old interrupt 1 vector offset
oldsegment dw 0 ; old interrupt 1 vector segment
oldstack equ this dword
sp_save dw 0
ss_save dw 0
ds_save dw 0
es_save dw 0
ccall equ this dword ; C routine's adress is saved here
c_off dw 0
c_seg dw 0
oldstkhqq dw 0 ; Old start of stack
newsp equ this dword ; New stack address for C routine
dw offset stacktop
dw seg newstack
; Here is the new stack. DO NOT MOVE IT OUT OF DGROUP
; That is, leave it in the DATA or DATA? segment.
newstack db STACKSIZE DUP (0)
stacktop EQU $
extrn STKHQQ:word ; Microsoft heap/stack bound
.CODE
; This routine is called in place of setup386(). You pass it the address of
; a void far function that you want invoked on a breakpoint.
; It's operation is identical to setup386() except for:
;
; 1) The interrupt 1 vector is set to cint1_386() (see below)
; 2) The address passed is stored in location CCALL
; 3) DS and ES are stored in ds_save and es_save
_csetup386 proc
push bp
mov bp,sp
push es
mov ax,es
mov es_save,ax
mov ax,ds
mov ds_save,ax
mov ax,3501H
int 21h
mov ax,es
mov oldsegment,ax
mov oldoffset,bx
pop es
mov ax,arg2
push ds
mov dx,arg1
mov c_seg,ax
mov c_off,dx
mov ax,seg _cint1_386
mov ds,ax
mov dx,offset _cint1_386
mov ax,2501H
int 21H
pop ds
xor eax,eax
mov dr6,eax
pop bp
ret
_csetup386 endp
;*****************************************************************************
;* *
;* Here is the interrupt handler!!! *
;* Two arguments are passed to C, a far pointer to the base of the stack *
;* frame and the complete contents of dr6 as a long unsigned int. *
;* *
;* The stack frame is as follows: *
;* *
;* . *
;* . *
;* (Interrupted code's stack) *
;* FLAGS *
;* CS *
;* IP
;* CX 3 *
;* DX 3 *
;* BX 3 *
;* SP DDDDDY (Stack pointer points to IP above) *
;* BP *
;* SI *
;* DI *
;* ES *
;* DS *
;* SS
;* The pointer is two way. That is, you can read the values or set any of *
;* them except SS. You should, however, refrain from changing CS,IP,or SP. *
;* *
;*****************************************************************************/
_cint1_386 proc
pusha ; save registers
push es
push ds
push ss
mov ax,@data ; point at our data segment
mov ds,ax
mov ax,ss
mov ss_save,ax ; remember old stack location
mov sp_save,sp
cld
lss sp,newsp ; switch stacks
mov ax,STKHQQ ; save old end of stack
mov oldstkhqq,ax
mov ax,offset newstack ; load new end of stack
mov STKHQQ,ax
sti
mov eax,dr6 ; put DR6 on stack for C
push eax
push ss_save ; put far pointer to stack frame
push sp_save ; on new stack for C
mov ax,es_save ; restore es/ds from csetup386()
mov es,ax
mov ax,ds_save
mov ds,ax
call ccall ; call the C program
xor eax,eax ; clear DR6
mov dr6,eax
mov ax,@data
mov ds,ax ; regain access to data
lss sp,oldstack ; restore old stack
add sp,2 ; don't pop off SS
; (in case user changed it)
mov ax,oldstkhqq ; restore end of stack
mov STKHQQ,ax
pop ds
pop es
popa
; This seems complicated at first.
; You MUST insure that RF is set before continuing. If RF is not set
; you will just cause a breakpoint immediately!
; In protected mode, this is handled automatically. In real mode it
; isn't since RF is in the high 16 bits of the flags register.
; Essentially we have to convert the stack from:
;
; 16 bit Flags 32 bit flags (top word = 1 to set RF)
; 16 bit CS to -----> 32 bit CS (garbage in top 16 bits)
; 16 bit IP 32 bit IP (top word = 0)
;
; All this so we can execute an IRETD which will change RF.
sub esp,6 ; make a double stack frame
xchg ax,[esp+6] ; get ip in ax
mov [esp],ax ; store it
xor ax,ax
mov [esp+2],ax ; eip = 0000:ip
mov ax,[esp+6]
xchg ax,[esp+8] ; get cs
mov [esp+4],ax
xor ax,ax
mov [esp+6],ax
mov ax,[esp+8] ; zero that stack word & restore ax
xchg ax,[esp+10] ; get flags
mov [esp+8],ax
mov ax,1 ; set RF
xchg ax,[esp+10]
iretd ; DOUBLE IRET (32 bits!)
_cint1_386 endp
end
[LISTING EIGHT]
/******************************************************************************
* File: CBRKDEMO.C *
* Example C interrupt handler for use with CBRK386 *
* Williams - June, 1989 *
* Compile with: CL CBRKDEMO.C BREAK386 CBRK386 *
******************************************************************************/
#include
#include
#include
#include
#include "break386.h"
/* functions we will reference */
int loop();
void far broke();
main()
{
int i;
/* declare function broke as our interrupt handler */
csetup386(broke);
break386(1,BP_CODE,(void far *)loop); /* set break at function loop */
for (i=0;i<10;i++) loop(i);
printf("Returned to main.\n");
clear386(); /* turn off debugging */
}
/* This function has a breakpoint on its entry */
loop(int j)
{
printf("Now in loop (%d)\n",j);
}
/*****************************************************************************
* *
* Here is the interrupt handler!!! *
* Note it must be a far function (normal int the LARGE, HUGE & MEDIUM *
* models). Two arguments are passed: a far pointer to the base of the stack *
* frame and the complete contents of dr6 as a long unsigned int. *
* *
* The stack frame is as follows: *
* *
* . *
* . *
* (Interrupted code's stack) *
* FLAGS *
* CS *
* IP
* CX 3 *
* DX 3 *
* BX 3 *
* SP DDDDDY (Stack pointer points to IP above) *
* BP *
* SI *
* DI *
* ES *
* DS *
* SS
* The pointer is two way. That is, you can read the values or set any of *
* them except SS. You should, however, refrain from changing CS,IP,or SP. *
* *
*****************************************************************************/
void far broke(void far *p,long dr6)
{
static int breaking=1; /* don't do anything if breaking=0 */
int c;
if (breaking)
{
int n;
int far *ip;
/*****************************************************************************
* *
* Here we will read the local variable off the interrupted program's stack! *
* Assuming small model, the stack above our stack frame looks like this: *
* i - variable sent to loop *
* add - address to return to main with *
*
* *
* This makes i the 15th word on the stack (16th on models with far code) *
* *
*****************************************************************************/
#define IOFFSET 15 /* use 16 for large, medium or huge models */
n=*((unsigned int far *)p+IOFFSET);
printf("\nBreakpoint reached! (DR6=%lX i=%d)\n",dr6,n);
/* Ask user what to do. */
do {
printf("
c=getche();
putch('\r');
putch('\n'); /* start a new line */
if (!c) /* function key pressed */
{
getch();
continue;
}
c=toupper(c);
/* Modify loop's copy of i (doesn't change main's i) */
if (c=='M')
{
int newi;
printf("Enter new value for i: ");
scanf("%d",&newi);
*((unsigned int far *)p+IOFFSET)=newi;
continue;
}
if (c=='A') /* Exiting */
{
clear386(); /* ALWAYS turn off debugging!!! */
exit(0);
}
if (c=='N')
breaking=0; /* We could have turned off breakpoints instead */
} while (c!='A'&&c!='N'&&c!='C');
}
}
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/