home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 June
/
SIMTEL_0692.cdr
/
msdos
/
c
/
break.arc
/
BREAK.ASM
next >
Wrap
Assembly Source File
|
1985-08-21
|
14KB
|
597 lines
title BREAK - handle ctrl-break
include m:dos.mac
debug=0
subttl Documentation
;*****************************************************************************
; This code uses the standard Lattice interface. The logical device 'm:'
; represents the memory model being used. Supply your own path to dos.mac
;
; This code is intended to allow me to break out of various perverse
; loops in my application code.
;
; * * * W A R N I N G * * *
;
; This code is an extreme *HACK*. It solves my problem. It may not solve
; yours. I make no guarantees. Use at your own risk!
;
; It was tested using a Hercules monocrhome card. I make no guarantees that
; I have done anything reasonable with respect to color cards.
;
;
; Restrictions
;
; The current code works only for the Lattice C compiler using the S or D
; models (64K program segment). It depends upon the fact that the CS register
; is global to the entire code body. It will probably not have the desired
; effect if you are executing out of the P or L models.
;
;
; Failure to clear the handler before exiting the program will lead to
; various bizarre behavior. Calling the initialization more than once
; will confuse the world beyond practical recovery.
;
; Abstract
;
; A procedure is called to establish the existence of the control-break
; handler (interrupt 1BH). If a control-break is taken during program
; execution, and the CS:IP was in the user's code, execution is terminated.
;
; This code is to be linked into the user's application.
;
; Hacks
;
; What the initialization procedure does is record the CS: of the caller,
; which in the small memory model must be the CS: of all the code. It then
; sets up the interrupt 1BH to point to its intercept routine.
;
; The finalization procedure restores the interrupt vector 1BH to its previous
; value.
;
; The intercept routine uses the BIOS calls to write an informative message to
; the user. It then reaches back into the stack a fixed number of bytes and
; examines the CS: at the time the interrupt from the keyboard was taken.
;
; BEWARE! If you are not using the BIOS intercepts but are using any sort
; of keyboard extender (BUF160, ProKey, SuperKey) you will have to determine
; where this interrupt has stored the old CS:IP. Have fun.
;
; If the CS: of the executing code (at the time the 1BH was taken) was in the
; user space, the IP: is modified to point to some termination code. Normal
; return is then taken, except that when the BIOS exits the keyboard interrupt
; it returns to the exit control code and the process terminates.
;
; Possible extensions
;
; SETCTL(x) where x is the address of a procedure could call an arbitrary
; procedure. Note, however, that the old CS:IP is currently destroyed, so
; anyone extending this would have to make appropriate changes.
;
; * * * B E W A R E * * *
;
; This code is delicate and high-risk. It works well enough for my one
; application. I may have missed all sorts of bizarre DOS or BIOS hacks.
;
; It is not guaranteed to be bug-free. The user assumes all risk in using
; it. This is code that should be used only if you understand enough of
; DOS/BIOS hacking to be sure it will work for you.
;
;
subttl Interface specifications
;-----------------------------------------------------------------------------
; extern void SETCTL();
;
; Effects:
; Establishes a Ctrl-brk abort handler
;
; Limitations:
; May only be called once; subsequent calls may damage universe
;
;-----------------------------------------------------------------------------
; extern void CLRCTL();
;
; Effects:
; Resets the Ctrl-brk abort handler
;
; Limitations:
; Should be called only if SETCTL() has been called. However, if
; SETCTL has not already been called, nothing will happen.
;
;*****************************************************************************
subttl Working storage
; This storage is in the data segment
DSEG
UserCS DW ? ; CS: of where we were interrupted
UserIP DW ? ; IP: of where we were interrupted
UserIPL DW ? ; Address of user IP: (relative to seg reg)
MyCS DW ? ; CS: of where we were set up from
apage DB ? ; Current display page
ENDDS
subttl Vector locations
VECTORS SEGMENT AT 0H
ORG 1BH*4 ; ctrl-brk interrupt interrupt
CTLBRK LABEL DWORD
CTLBRK_IP DW ; IP of control-break vector
CTLBRK_CS DW ; CS of control-break vector
VECTORS ENDS
subttl Code segment storage
;
; This is in the code segment because I don't know how to make it addressible
; in the data segment
;
PSEG
OLD_REQ label DWORD ; we store former 1BH vector here
OLD_IP DW 0 ; 0, not ?
OLD_CS DW 0 ; 0, not ?
OldAX DW ?
OldSS DW ? ; old stack segment
OldSP DW ? ; old stack pointer
DW 512 DUP(?) ; local stack space for intercept routine
STACK label WORD
subttl wdigit - Write a hex digit
;-----------------------------------------------------------------------------
; wdigit
;
; Inputs:
; AX: Single hex digit to convert (0..15)
; BX: Place to put it
;
; Effects:
; AX is converted from binary to hex and placed at the location
; defined by ES:BX
; Modified:
; AX
;-----------------------------------------------------------------------------
wdigit PROC NEAR
push DI ; ... working registers
push SI
mov DI,BX ; get destination
and AX,0FH ; mask off l/o bits
add AX,OFFSET TBL ; get address of decoded byte
mov SI,AX ; make available for indexing
mov AL,CS:[SI] ; get the byte "0".."F"
mov ES:[DI],AL ; store it at the destination
pop SI ; ...restore working regs
pop DI
ret ; return
wdigit ENDP
TBL: DB '0123456789ABCDEF' ; hex digit decode table
subttl cvt1
;-----------------------------------------------------------------------------
; cvt1
;
; Inputs:
; AX: Word to convert
; ES:BX Place to put character
;
; Effects:
; Converts l/o bits of AX to character, places character at ES:BX
; decrements BX, shifts AX right 4 bits
;
; Output conditions:
; BX--
; AX >>= 4
;-----------------------------------------------------------------------------
cvt1 PROC NEAR
push AX ; save AX
call wdigit ; write digit to buffer ES:BX
pop AX ; get old AX
SHR AX,1 ; shift right 4
SHR AX,1 ; ...
SHR AX,1 ; ...
SHR AX,1 ; ...
dec BX ; select next higher character position
ret
cvt1 ENDP
subttl cvt - convert binary to hex
;-----------------------------------------------------------------------------
; Inputs:
; AX - value to convert
; ES:BX - place to put it
;
; Effects:
; Converts binary value in AX to 4-digit hex value and places it in
; location indexed by BX (BX points to low-order position, and is
; decremented for each position
;
; Modified:
; AX, BX
;-----------------------------------------------------------------------------
cvt PROC NEAR
call cvt1 ; write first digit
call cvt1 ; write second digit
call cvt1 ; write third digit
call cvt1 ; write fourth digit
ret
cvt ENDP
subttl CTLSEEN - CTRL-BREAK handler
;-----------------------------------------------------------------------------
; ctlseen
; Inputs:
; None; called as interrupt routine via interrupt 1BH
;
; Result:
; AL=0FFH
;
; Effects:
; Issue message: 'Ctrl-brk seen at use PC xxxx:xxxx'
;
; If called from user program, sets up user CS:IP return value to
; return to "exit code" which is call on C '_exit' routine.
;
;-----------------------------------------------------------------------------
CTLSEEN PROC FAR
if debug
int 3
endif
; Set up user stack
mov OldSS,SS ; Save old stack
mov OldSP,SP ; ...
mov OldAX,AX ; save AX
cli ; interrupts off
mov AX,CS ; Get CS: so we can make our local stack
; addressible
mov SS,AX ; New stack segment
mov SP,offset STACK ; ...
sti ; interrupts back on
push BX ; Save us some working regs
push CX ; ...
push DX ; ...
push ES ; ...
push BP ; ...
push DS ; ...
mov AX,OldSP ; We want to look at the user stack
mov ES,OldSS ; which will be in ES:AX
; AX is the top of the stack upon entry (before stack switching)
;
; +---------------+
; AX ---> | IP | +0 ; return point to Interrupt handler
; +---------------+
; | CS | +2 ; return point to interrupt handler
; +---------------+
; | flags | +4
; +---------------+
; | BP | +6 ; saved regs
; +---------------+
; | AX | +8
; +---------------+
; | BX | +10
; +---------------+
; | CX | +12
; +---------------+
; | DX | +14
; +---------------+
; | SI | +16
; +---------------+
; | DI | +18
; +---------------+
; | DS | +20
; +---------------+
; | ES | +22
; +---------------+
; | User IP | +24 ; return to user code
; +---------------+
; | User CS | +26 ; return to user code
; +---------------+
; Establish addressibility of DS
; DS := Seg(&UserIP);
mov BX,SEG UserIP ; make our data area available by
; using SEG of some variable in it
mov DS,BX ; to DS:
mov BP,AX ; AX is now in BP, so ES:BP holds
; old SS:SP
; UserIP := * (OldSS:(OldSP+24))
mov AX,ES:[BP+24] ; Magic offset! Get user IP
mov UserIP,AX ; Store it for later use
lea AX,ES:[BP+24] ; Magic offset! Get address of where
; it was
; UserIPL := (OldSS:(OldSP+24))
mov UserIPL,AX ; Store this as the UserIP Location
; UserCS := * (OldSS:(OldSP+26))
mov AX,ES:[BP+26] ; Magic offset! Get user CS:
mov UserCS,AX ; Save it for later use
; Video_state(&apage)
mov AH,15 ; return video state
int 10H
; AH = current video mode
; AL = #of columns on screen
mov apage,BH ; active page in BH
; /* Convert CS: to hex */
; cvt(UserCS,&MSGCS)
; /* call cvt(AX,ES:BX) */
;
mov AX,CS ; Get CS: to make msg addressible
mov ES,AX ; Put in ES
mov BX,OFFSET MSGCS ; ES:BX = Place to put digits
mov AX,UserCS ; AX gets value to put into message
call cvt ; Go convert it, modifying message
; /* Convert IP: to hex */
; cvt(UserIP,&MSGIP)
; /* call cvt(AX,ES:BX) */
;
mov AX,CS ; Get CS: to make msg addressible
mov ES,AX ; put in ES
mov BX,OFFSET MSGIP ; ES:BX = place to put digits
mov AX,UserIP ; AX gets value to put into message
call cvt ; Go convert it, modifying message
; Write_string(mode=0,Attr=rev,string=MSG1,length=MSG1L,CursorX=0,CursorY=24,page=apage)
; ES:BP = &MSG1; /* string */
mov AX,CS
mov ES,AX
mov BP,OFFSET MSG1
; CX = strlen(MSG1); /* length */
mov CX,MSG1L
; DH = 24; /* CursorY */
mov DH,24
; DL = 0; /* CursorX */
mov DL,0
; BH = apage; /* page */
mov BH,apage
; AL = 1; /* mode: 1 -> BL has attr, string has chars,
; move cursor */
mov AL,1 ;
; BL = 70H; /* attribute: 70H = Reverse video block */
mov BL,70H ;
mov AH,19 ; write string
int 10H ; ...
; /* If we came from the user's program, abort */
; if(UserCS == MyCS) _exit()
; /* Store the IP of the abort code in the return pointer, then return */
mov AX,UserCS
cmp AX,MyCS
jne NotMe ; not in my program, go on
mov BP,UserIPL ; get address of user IP
mov AX,OFFSET ByeBye ; plan to die upon return
mov ES,OldSS ; make segment addressible
mov ES:[BP],AX ; store the updated pointer
NotMe:
; /* Otherwise, continue as if we weren't here */
pop DS
pop BP
pop ES
pop DX
pop CX
pop BX
; /* Reset to old user stack */
cli
mov SS,OldSS
mov SP,OldSP
sti
mov AX,OldAX
; /* Now go to the real handler */
jmp OLD_REQ
CTLSEEN ENDP
; The message and its various subcomponents
MSG1: DB 'Ctrl-break at PC '
DB 'xxxx'
MSGCS equ $-1
DB ':'
DB 'xxxx'
MSGIP equ $-1
DB 0DH,0AH ; CR,LF
MSG1L equ $-MSG1
subttl SETCTL - Set CTL-BRK vector
;-----------------------------------------------------------------------------
; extern void SETCTL()
;-----------------------------------------------------------------------------
PUBLIC SETCTL
IF LPROG
SETCTL PROC FAR
ELSE
SETCTL PROC NEAR ; set CTL-BRK interrupt
ENDIF
push BP ; C prolog
mov BP,SP ; ...
; MyCS := CS;
mov MyCS,CS ; save CS
ASSUME ES:VECTORS
; OldES := ES;
push ES ; save old ES
push AX ; save old AX
; ES := Segment(&Vectors);
mov AX,VECTORS
mov ES,AX
; Disable_Interrupts();
CLI ; turn off interrupts
; OLD_REQ.IP := CTLBRK.IP
mov AX,CTLBRK
mov OLD_REQ,AX
; OLD_REQ.CS := CTLBRK.CS
mov AX,CTLBRK[2]
mov OLD_REQ[2],AX
; CTLBRK := &CTLSEEN
mov CTLBRK_IP,OFFSET CTLSEEN
mov CTLBRK_CS,CS
; Enable_interrupts();
STI ; allow interrupts
; ES := OldES;
pop AX
pop ES
; return;
pop BP ; C epilog
ret ; return to caller
; This is the User Exit routine
IF LPROG
extrn _Exit:Far
ELSE
extrn _Exit:Near
ENDIF
ByeBye:
call CLRCTL
call _Exit
SETCTL ENDP
subttl CLRCTL - Clear CTL-BRK vector
;-----------------------------------------------------------------------------
; extern void CLRCTL();
;
; Resets the ctrl-break interrupt vector
;-----------------------------------------------------------------------------
PUBLIC CLRCTL
IF LPROG
CLRCTL PROC FAR
ELSE
CLRCTL PROC NEAR
ENDIF
ASSUME ES:VECTORS
; OldES := ES;
push ES ; save old ES
push AX ; save old AX
push BX
if debug
int 3
mov AH,62H
int 21H ; get program segment prefix
mov PSP,BX ; save it
endif
; ES := Segment(&Vectors);
mov AX,VECTORS
mov ES,AX
; Disable_Interrupts();
CLI ; turn off interrupts
; if(OLD_REQ == 0) goto NoVectorStored
mov AX,OLD_REQ
cmp AX,0 ; is it zero?
je NoVectorStored
; CTLBRK.IP := OLD_REQ.IP
mov CTLBRK,AX
; CTLBRK.CS := OLD_REQ.CS
mov AX,OLD_REQ[2]
mov CTLBRK[2],AX
NoVectorStored:
; Enable_interrupts();
STI ; allow interrupts
; ES := OldES;
pop BX
pop AX
pop ES
ret ; return to caller
PSP: DW ?
CLRCTL ENDP
ENDPS
END