	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
