	title BREAK - handle ctrl-break 
;***************************************************************************** 
;	    Change Log 
;  Date	    | Change 
;-----------+----------------------------------------------------------------- 
; 11-Aug-85 | Created 
;  1-Sep-85 | Rearranged so _clrctl precedes its call (needed in P model) 
; 31-Dec-85 | Made special copy for Adagio support.  Stripped out most stuff 
;	    | and handle DOS ctrl-break exit 
;  1-Jan-86 | Use 1 for control-break and 2 for control-C 
;  1-Jan-86 | Cause return to skip over sub instruction (forcing ignore code 
;	    | acceptance) on PC/AT 
;  2-Jan-86 | take out m: from include m:dos.mac 
;****************************************************************************/ 
	include 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! 
; 
;			     Restrictions 
; 
; 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 
; the flag _CBREAK is set to true 
; 
; This code is to be linked into the user's application. 
; 
; This code also sets up a handler for the DOS ctrl-brk exit; this is called 
; if DOS sees a C-C character come by. 
;  
;		       * * * 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 
extrn	_CBREAK:word 
; This storage is in the data segment 
;	public	_CBREAK 
	DSEG 
	even 
;_CBREAK	dw	0 
OLD_BIOS label	DWORD		; we store former 1BH vector here 
OLD_BIP DW	0		; 0, not ? 
OLD_BCS DW	0		; 0, not ? 
 
OLD_DOS label	DWORD		; we store former 23H vector here 
OLD_DIP DW	0 
OLD_DCS dw	0		 
 
	ENDDS 
 
  
	subttl Vector locations 
VECTORS SEGMENT AT 0H 
	ORG	1BH*4		; BIOS ctrl-brk interrupt vector 
CTLBRK	LABEL	DWORD 
CTLBRK_IP DW	?		; IP of BIOS control-break vector 
CTLBRK_CS DW	?		; CS of BIOS control-break vector 
 
	ORG	23H*4		; DOS ctrl-brk interrupt vector 
DOSCBRK LABEL	DWORD 
DOSC_IP	 DW	? 
DOSC_CS	 DW	? 
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 
 
  
	subttl	CTLSEEN - CTRL-BREAK handler 
;----------------------------------------------------------------------------- 
;				    ctlseen 
; Inputs: 
;	None; called as interrupt routine via interrupt 1BH 
; 
; Result: 
;	AL=0FFH 
; 
; Effects: 
;	sets _CBREAK to true.   
;----------------------------------------------------------------------------- 
 
CTLSEEN PROC FAR 
 
 
if debug 
	int  3 
endif 
	push	ax 
	push	ds 
	mov	ax,seg _CBREAK 
	mov	ds,ax 
	mov	ds:_CBREAK,1 
	pop	ds 
	pop	ax 
	push	es 
	mov	AX,0F000H	; from the ROM,  
	mov	ES,AX		; we want pc ID 
	mov	AH,ES:0FFFEH	; The Peter Norton Programmer's Guide to the 
				; IBM PC, page 60 
	cmp	AH,0FCH		;  PC/AT? 
	jnz	no 
if debug 
	int	3		; WARNING!  If you have this debug trap 
				; enabled, and continue from it, the 
				; ctrl-key-up transition is lost (because 
				; the key went up to talk to the debugger) 
				; this confuses the world; you will have to 
				; hit the control-key once to get the 
				; world resynchronized. 
 
endif 
; We increment the return address by 2 
; 
;	+-------------------+ 
;  SS	|      (ES)	    |  ES:0[BP] 
;	+-------------------+ 
;	|      (BP)	    |  ES:2[BP] 
;	+-------------------+ 
;	|	IP	    |  ES:4[BP] 
;	+-------------------+ 
;	|	CS	    |  ES:6[BP] 
;	+-------------------+ 
	mov	AX,SS	 
	mov	ES,AX 
	push	BP 
	mov	BP,SP 
	inc	WORD PTR ES:4[BP] 
	inc	WORD PTR ES:4[BP] 
	pop	BP 
no:	mov	AX,-1 
			; "ignore" character 
			; PC/XT ref page A-31 lines 2090-2091, page A-33 
			; lines 2217-2219 
			; PC/AT ref page 5-118 loc 2AE..285 indicates this 
			; has no effect for the /AT, which is why we did 
			; the above hack 
	pop	ES 
	iret				;return  
 
CTLSEEN ENDP 
 
CTLC	PROC	FAR		; Control-C handler 
	push	ax 
	push	ds 
	mov	ax,seg _CBREAK 
	mov	ds,ax 
	mov	ds:_CBREAK,2 
	pop	ds 
	pop	ax 
	iret				;return  
CTLC	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 
 
; 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		 
	ASSUME ES:VECTORS 
 
; Disable_Interrupts(); 
 
	CLI			; turn off interrupts 
 
; if(OLD_BIOS == 0) goto NoVectorStored 
 
	mov	AX,WORD PTR OLD_BIOS 
	cmp	AX,0		; is it zero? 
	je	NoBIOSStored 
 
; CTLBRK.IP := OLD_BIOS.IP 
 
	mov	WORD PTR CTLBRK,AX 
 
; CTLBRK.CS := OLD_BIOS.CS 
 
	mov	AX,WORD PTR OLD_BIOS[2] 
	mov	WORD PTR CTLBRK[2],AX 
 
NoBIOSStored: 
 
	mov	AX,WORD PTR OLD_DOS 
	cmp	AX,0		; initialized? 
	je	NoDOSStored	; no, no dos vector 
 
; DOSCBRK.IP = OLD_DOS.IP 
 
	mov	WORD PTR DOSCBRK,AX 
 
; DOSCBRK.CS := OLD_DOS.CS 
 
	mov	AX,WORD PTR OLD_DOS[2] 
	mov	WORD PTR DOSCBRK[2],AX 
 
NoDOSStored: 
 
; Enable_interrupts(); 
 
	STI		; allow interrupts 
 
; ES := OldES; 
 
	pop	BX 
	pop	AX 
	pop	ES	 
 
	ret		; return to caller 
 
PSP:	DW	? 
_clrctl	ENDP 
  
	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			; ... 
 
if debug 
	int	3 
endif 
 
	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_BIOS.IP := CTLBRK.IP 
 
	mov	AX,WORD PTR CTLBRK 
	mov	WORD PTR OLD_BIOS,AX 
 
; OLD_BIOS.CS := CTLBRK.CS 
 
	mov	AX,WORD PTR CTLBRK[2] 
	mov	WORD PTR OLD_BIOS[2],AX 
 
; CTLBRK := &CTLSEEN 
 
	mov	CTLBRK_IP,OFFSET CTLSEEN 
	mov	CTLBRK_CS,CS 
 
; OLD_DOS.IP := DOSCBRK.IP 
 
	mov	AX,WORD PTR DOSCBRK 
	mov	WORD PTR OLD_DOS,AX 
 
; OLD_DOS.CS := DOSCBRK.CS 
 
	mov	AX,WORD PTR DOSCBRK[2] 
	mov	WORD PTR OLD_DOS[2],AX 
 
; DOSCBRK := &CTLC 
 
	mov	DOSC_IP,OFFSET CTLC 
	mov	DOSC_CS,CS 
 
; Enable_interrupts(); 
 
	STI		; allow interrupts 
 
; ES := OldES; 
 
	pop	AX 
	pop	ES	 
 
; return; 
	pop	BP	; C epilog 
	ret		; return to caller 
 
_setctl	ENDP 
 
	ENDPS 
	END 
