home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The C Users' Group Library 1994 August
/
wc-cdrom-cusersgrouplibrary-1994-08.iso
/
listings
/
v_02_10
/
2n10062a
< prev
next >
Wrap
Text File
|
1991-07-17
|
17KB
|
617 lines
COMMENT ~
---------------------------------------------------------
LISTING 1
File: breakout.asm
This module contains routines necessary to enable
Ctrl-Break to "break out" of infinite loops. A user
supplied clean up routine may be specified if necessary.
MASM 5.1/TASM 2.0 - C callable installation &
de-installation functions.
Language & memory model independant. Change the .model
directive as needed.
Author: David Burki
---------------------------------------------------------
END OF COMMENT ~
title BREAKOUT.ASM
page 57,132
% .model m_model, lang
; ---- MACROS and EQUATES ----
;macro to simulate an interrupt so the interrupt returns
;to the instruction immediately following the call
sim_int macro num
pushf
call cs:orig_&num
endm
;macro to push selected registers
apush macro a,b,c,d,e,f,g,h
irp x,<a,b,c,d,e,f,g,h>
ifnb <x>
push x
endif
endm
endm
;macro to pop selected registers
apop macro a,b,c,d,e,f,g,h
irp x,<h,g,f,e,d,c,b,a>
ifnb <x>
pop x
endif
endm
endm
;macro to install a replacement vector
; -- assumes that ds = code seg of replacing function
; -- all var names used to save original vector must be
; 2 characters long plus a training "h" (i.e. for
; int 8h "orig_08h")
install_vector macro vector_num,function_name
mov ax,35&vector_num
int 21h
mov word ptr orig_&vector_num,bx
mov word ptr orig_&vector_num+2,es
lea dx,function_name
mov ax,25&vector_num
int 21h
endm
;macro to un-install a replaced vector
; -- all var names used to save original vector must be
; 2 characters long plus a training "h" (i.e. for
; int 8h "orig_08h")
restore_vector macro replaced_vector
lds dx,cs:orig_&replaced_vector
mov ax,25&replaced_vector
int 21h
endm
; segment and offset of the BIOS break flag
BIOS_SEG equ 40h
BREAK_FLAG_OFF equ 71h
; ---- CODE ----
.code
; declare externally visible functions
public insure, cancel
; NOTE: all variables used here are part of the code
; segment so the interrupt routines can have easy
; access to them
; --- storage for the address of the InDOS flag
indos_addr dd 0
; --- storage for the address of the critical error flag
critter_addr dd 0
; --- storage for the version of dos
dos_major_version db 0
dos_minor_version db 0
; --- flag indicating disk i/o in progress
busy_flag db 0
; --- far pointer to clean up routine
exit_routine dd 0
; --- storage for original addresses of intercepted
; interrupt vectors.
orig_08h dd 0
orig_1bh dd 0
orig_13h dd 0
orig_25h dd 0
orig_26h dd 0
; --- storage for the segment addr of the psp of process
; that asked for insurance - if zero, insurance isn't
; in effect
insured_psp dw 0
; --- flag if still processing in int 8h
in_already db 0
; --- interrupted program's SS & SP
save_ss dw 0
save_sp dw 0
; --- original C stack segment
installer_ss dw 0
installer_sp dw 0
; --- local stack while in int_8h_handler
my_stack dw 256 dup(0)
stack_top label word
assume ds:@curseg, es:nothing
;---------------------------------------------------------
; GET_DOS_VERSION()
; Obtain the version & revision of DOS. Store the version
; in the variable "dos_major_version" and the revision in
; the variable "dos_minor_version". Both variables are code
; segment variables.
;
; Returns:
; AL - dos minor version number
; AH - dos major version number
;---------------------------------------------------------
public get_dos_version
get_dos_version proc
mov ah,30h
int 21h
mov cs:dos_major_version,al
mov cs:dos_minor_version,ah
ret
get_dos_version endp
;---------------------------------------------------------
; GET_CRITICAL_ERROR_ADDR()
; This function obtains & saves the address of the
; critical error flag. For DOS versions 3+, the critical
; error flag is the byte just before the InDOS flag. For
; DOS 2.x, it's the byte just after.
;
; NOTE: get_dos_version() must be called before this
; function
;
; NOTE: For COMPAQ DOS version 3.x, the critical error
; flag is located 01aah bytes BEFORE the InDOS flag.
;
; Trashes: AX
;---------------------------------------------------------
public get_critical_error_addr
get_critical_error_addr proc USES ES BX
; --- get the address of the InDOS flag
mov ah,34h
int 21h
; --- dos 3.x or better use byte before InDOS
cmp byte ptr cs:dos_major_version,3
jge byte_before ;good_version
; --- dos 2.x, use byte after InDOS flag
inc bx
jmp store_critter_addr
byte_before:
dec bx
store_critter_addr:
; --- save the address & return success
mov word ptr cs:critter_addr,bx
mov word ptr cs:critter_addr+2,es
ret
get_critical_error_addr endp
;---------------------------------------------------------
; GET_INDOS_ADDR()
; Uses the undocumented (but well known) function 34h of
; INT 21h to obtain a far pointer to the "InDOS" flag.
; Stores the address of the flag in the code segment
; variable "indos_addr".
;
; Trashes AX
;---------------------------------------------------------
public get_indos_addr
get_indos_addr proc USES ES BX
; --- get the address
mov ah,34h
int 21h
; --- save address in cs referenced variable
mov word ptr cs:indos_addr,bx
mov word ptr cs:indos_addr+2,es
ret
get_indos_addr endp
;---------------------------------------------------------
; CHECK_CRITTER_FLAG()
; Examine the undocumented "critical error" flag to
; determine if a critical error is in progress.
;
; Returns:
; carry clear - A critical error is NOT in progress
; carry set - A critical error is in progress
;---------------------------------------------------------
public check_critter_flag
check_critter_flag proc USES AX DS SI
; --- ds:si <- address of critter flag
lds si,dword ptr cs:critter_addr
; --- check critter flag = zero
lodsb
or al,al
jz no_critter
; --- critter flag not zero, return carry set
stc
jmp critter_exit
; --- critter flag zero, return carry clear
no_critter:
clc
critter_exit:
ret
check_critter_flag endp
;---------------------------------------------------------
; CHECK_INDOS_FLAG()
; Examine the undocumented "InDOS" flag to determine if
; non-rentrant DOS functions are currently executing.
;
; Returns:
; carry clear - InDOS flag is zero.
; carry set - InDOS flag is non-zero.
;---------------------------------------------------------
public check_indos_flag
check_indos_flag proc USES AX DS SI
; --- ds:si <-- addr of InDOS flag, then load al with
; the byte at that address
lds si,dword ptr cs:indos_addr
lodsb
; --- indos flag equal zero, DOS is stable
or al,al
jz dos_stable
; --- no, set carry & return
stc
jmp indos_exit
; --- yes, clear carry and return
dos_stable:
clc
indos_exit:
ret
check_indos_flag endp
;---------------------------------------------------------
; CHECK_BREAK_BIT()
; This function examines the BIOS Break Flag (bit 7 of
; the byte at 40:71).
;
; Returns:
; carry clear - Break Flag is clear.
; carry set - Break Flag is set.
;---------------------------------------------------------
check_break_bit proc USES DS ES BX
; --- establish addressing to the flag
mov bx,BIOS_SEG
mov es,bx
mov bx,BREAK_FLAG_OFF
; --- is bit 7 set
test es:byte ptr [bx],10000000b
jnz break_bit_set
; --- no, return with carry clear
clc
jmp check_break_exit
; --- yes, clear it & return carry set
break_bit_set:
and es:byte ptr [bx],01111111b
stc
check_break_exit:
ret
check_break_bit endp
;---------------------------------------------------------
; INSTALL_INTERCEPTS()
; Retrieve & save the current vectors, replacing the
; vector table entries with the addresses of the
; intercept routines.
; Entry:
; DS = CS
; Returns:
; No value
; Trashes:
; AX, BX, DX, ES
;---------------------------------------------------------
install_intercepts proc
assume ds:@curseg
; --- capture int 8h vector
install_vector 08h,int_8h_handler
; --- capture int 13h vector
install_vector 13h,thirteen_handler
; --- capture int 25h vector
install_vector 25h,twenty_five_handler
; --- capture int 26h vector
install_vector 26h,twenty_six_handler
; --- capture int 1bh vector
install_vector 1bh,break_handler
ret
install_intercepts endp
;---------------------------------------------------------
; INSURE( cleanup_function )
; void (*PFV)(); /* far pointer to cleanup function */
;
; Installs the breakout tool.
;
; Returns:
; AX = zero - installed successfully
; AX not zero - did not install
;---------------------------------------------------------
insure proc USES DS ES SI DI, CLEAN_UP:FAR PTR
; --- force ds = cs
push cs
pop ds
assume ds:@curseg
; --- currently in palce?
cmp word ptr insured_psp,0
; --- yes, return to caller with no action
jnz not_installed
; --- get dos version
call get_dos_version
; --- if not dos 3.x don't install
cmp ah,3
jl not_installed
; --- retrieve & save the psp of the current process
; --- function 62h (get PSP) is DOS 3+ only
mov ah,62h
int 21h
mov insured_psp,bx
; --- get the addr of clean up function & save it
les bx,CLEAN_UP
mov word ptr exit_routine+2,es
mov word ptr exit_routine,bx
; --- get the addr of "InDOS" flag
call get_indos_addr
; --- get the gritical error flag address
call get_critical_error_addr
; --- install the interrupt intercepts
call install_intercepts
; --- insure BIOS break flag is clear
mov bx,BIOS_SEG
mov es,bx
mov bx,BREAK_FLAG_OFF
and es:byte ptr [bx],01111111b
; --- save installer's SS and SP
mov installer_ss,ss
mov installer_sp,sp
; --- set good return code & clear "in already" flag
; for int 8h handler
xor ax,ax
mov byte ptr in_already,al
jmp insure_exit
not_installed:
mov ax,1
insure_exit:
ret
insure endp
assume ds:nothing
;---------------------------------------------------------
; CANCEL()
; Restore the original vectors and clear the
; "insured_psp" address.
; Returns:
; No value
; Trashes:
; AX
;---------------------------------------------------------
cancel proc USES DS DX
; --- if not installed, get out with no action
mov dx,cs:insured_psp
or dx,dx
jz no_cancellation
; --- get the original int 8h & restore it
restore_vector 08h
; --- get the original int 1bh & restore it
restore_vector 1bh
; --- get the original int 13h & restore it
restore_vector 13h
; --- get the original int 25h & restore it
restore_vector 25h
; --- get the original int 26h & restore it
restore_vector 26h
; --- clear the insured_psp address
mov cs:word ptr insured_psp,0
no_cancellation:
ret
cancel endp
assume cs:@curseg
;---------------------------------------------------------
; INT_8H_HANDLER()
; Intercept for timer tick interrupt.
; Checks the BIOS break flag (bit 7 at 40:71) on each
; invocation.
; Flagged to prevent rentry.
;---------------------------------------------------------
int_8h_handler proc far
; --- let int 8 do it's stuff
sim_int 08h
cli
; --- if int 8 post processing is still going on, don't
; re-enter - return to the interrupted code
cmp cs:byte ptr in_already,1
jnz do_break_ck
iret
do_break_ck:
; --- flag that we're processing an int 8h & swap stacks
mov cs:byte ptr in_already,1
mov cs:save_ss,ss
mov cs:save_sp,sp
push cs
pop ss
mov sp,offset cs:stack_top
sti
apush ax,bx,cx,dx,ds,es,si,di
; --- if the BIOS break flag is NOT set, iret to caller
call check_break_bit
jnc no_break
; --- if disk i/o in progress, iret back to caller
cmp cs:busy_flag,0
jnz no_break
; --- if not in dos or critical error
call check_indos_flag
jc no_break
call check_critter_flag
jc no_break
; --- if the psp of the caller isn't the same as
; the psp when installed, skip it
mov ah,62h
int 21h
cmp bx,cs:insured_psp
jnz no_break
; --- BIOS break flag was set, everything is stable and the
; active PSP is the same as the one that installed the
; insurance. clear the BIOS break bit and uninstall
; the intercepted vectors
call cancel
; --- if a user supplied exit routine is installed, do it
mov bx,word ptr exit_routine+2
or bx,bx
jnz do_user_supplied_exit
; --- otherwise, DOS terminate function - error level = 1
mov ax,4c01h
int 21h
do_user_supplied_exit:
; --- get back BX & ES plus clean off CS, IP & FLAGS pushed
; on original interrupt, then jump to user exit routine
mov ss,cs:installer_ss
mov sp,cs:installer_sp
jmp cs:exit_routine
no_break:
; --- clean up the stack
apop ax,bx,cx,dx,ds,es,si,di
cli
mov ss,cs:save_ss
mov sp,cs:save_sp
sti
mov cs:byte ptr in_already,0
skip_it:
; --- iret to the int 8 caller
iret
int_8h_handler endp
;---------------------------------------------------------
; BREAK_HANDLER()
; Simple replacement for int 1bh - no more than an iret
; to keep the "^C" off the display screen.
;---------------------------------------------------------
break_handler proc far
iret
break_handler endp
;---------------------------------------------------------
; THIRTEEN_HANDLER()
; intercept for int 13h
; Increments the busy flag on entry, calls the original
; interrupt service routine, and decrements the busy flag
; when the disk routine is complete. The busy flag must
; be zero to indicate no disk i/o in progress.
;---------------------------------------------------------
thirteen_handler proc far
pushf
inc cs:busy_flag
popf
sim_int 13h
pushf
dec cs:busy_flag
popf
iret
thirteen_handler endp
;---------------------------------------------------------
; TWENTY_FIVE_HANDLER()
; intercept for int 25h
; Increments the busy flag on entry, calls the original
; interrupt service routine, and decrements the busy flag
; when the disk routine is complete. The busy flag must
; be zero to indicate no disk i/o in progress.
;---------------------------------------------------------
twenty_five_handler proc far
pushf
inc cs:busy_flag
popf
sim_int 25h
pushf
dec cs:busy_flag
popf
iret
twenty_five_handler endp
;---------------------------------------------------------
; TWENTY_SIX_HANDLER()
; intercept for int 26h
; Increments the busy flag on entry, calls the original
; interrupt service routine, and decrements the busy flag
; when the disk routine is complete. The busy flag must
; be zero to indicate no disk i/o in progress.
;---------------------------------------------------------
twenty_six_handler proc far
pushf
inc cs:busy_flag
popf
sim_int 26h
pushf
dec cs:busy_flag
popf
iret
twenty_six_handler endp
end