home *** CD-ROM | disk | FTP | other *** search
- 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