title	ldebtrap Debug Breakpoint handler
;	SCCSID = @(#)ldebtrap.asm	11.1 87/10/29

.xlist
.xcref
include ldebequ.inc
.list
.cref

include ldebseg.inc

BPMAX	  =	10			; Maximum number of breakpoints
BPLEN	  =	5*BPMAX 		; Length of breakpoint table


DBDATA	segment
	extrn	_DISADD:byte, _ASMADD:byte, _DEFDUMP:byte
	extrn	_SBRTAB:byte, _pTXT:word
	extrn	_RegSA:word

	extrn	_StackTop:word

BRKCNT		dw	?		; Number of breakpoints
BPTAB		db	BPLEN dup (?)	; Breakpoint table
TCOUNT		dw	?		; Number of steps to trace
intflags	dw	?		; int flags for testing trace bit
OldIPSav	dw	0		; IP saved during trace
OldCSSav	dw	0		; CS saved during trace
NextTimer	dd	0		; next timer interrupt handler
OldInt1		dd	0		; old single step interrupt handler
OldInt3		dd	0		; old breakpoint interrupt handler
OldInt6		dd	0		; old illegal instr. interrupt handler

BPointTrace	db	0		; nonzero if tracing over BP address
TraceEntry	db	0		; nonzero if trace int entered
force_entry	db	0		; nonzero if programm called debugger
BPs_Set 	db	0		; nonzero if Breakpoints have been set
DebugInited	db	0		; true => int vectors inited

HEADER		db	"System Debug 070185",cr,lf,"$"

DBDATA	ends

DBCODE	segment

	public	_BreakPoint, _GO, _TRACE, _Ptrace
	public	Debug_Entry, Debug_Terminate

	extrn	_ScanP:near, _GetChr:near, _GetChrI:near
	extrn	_chkctlc:near
	extrn	_CRLF:near, _BLANK:near, _OUT:near, _OUT16:near
	extrn	_Dec16Out:near
	extrn	_RPRBUF:near
	extrn	_ADDRESS:near, _GetWord:near, _GetByte:near, _GETEOL:near
	extrn	_DISPREG:near
	extrn	_PERR:near, _ERR:near, _COMMAND:near

; Set, Enable, Disable, Clear, and list 'Sticky Breakpoints'
; 10 Sticky Breakpoint addresses may be specified.

SBRTAB_frame struc
	BrkFlg		db  ?		;Break flags, 1= Set, 2= Enable
	BrkCntr 	dw  ?		;Repetition down  counter
	BrkReps 	dw  ?		;Repetition count before Break
	BrkOff		dw  ?		;Break addr offset
	BrkSeg		dw  ?		;Break addr Segment
	BrkInst 	db  ?		;Save instruction
	BrkCmdL 	db  ?		;Breakpoint commands length
	BrkCmnd 	db  32 Dup (?)	;Breakpoint commands
SBRTAB_frame ends

_BreakPoint:
	CALL	_GetChrI		;Get char & advance
	JZ	BrkErr			;Error if eol
	CMP	AL,"L"
	JE	BreakList		;List Breakpoints
	CMP	AL,"P"
	JNE	BreakPC 		;try Clear
	JMP	BreakSet		;Set Breakpoints
BreakPC:
	CMP	AL,"C"
	JNE	BreakPD 		;try Disable
	JMP	BreakClr		;Clear Breakpoints
BreakPD:
	CMP	AL,"D"
	JE	BreakPDE		;Disable Breakpoints
	CMP	AL,"E"
	JNE	BrkErr			;Else Error
BreakPDE:
	JMP	BreakDisEna		;Disable/Enable Breakpoints
BrkErr:
	JMP	_PERR

BreakList:
	MOV	CX,10
	MOV	SI,OFFSET ds:_SBRTAB
BreakL1:
	PUSH	CX
	MOV	AL,[SI].BrkFlg
	OR	AL,AL
	JZ	BreakLx 		;Brif not set
	PUSH	AX
	MOV	DX,10
	SUB	DX,CX
	CALL	_Dec16Out
	CALL	_BLANK
	POP	AX
	SHR	AL,1
	AND	AL,1
	ADD	AL,"d"
	CALL	_OUT			;"d"= disabled, "e"= enabled
	CALL	_BLANK
	MOV	DX,[SI].BrkSeg
	CALL	_OUT16
	MOV	AL,":"
	CALL	_OUT
	MOV	DX,[SI].BrkOff
	CALL	_OUT16
	CALL	_BLANK
	MOV	DX,[SI].BrkCntr
	CALL	_Dec16Out
	CALL	_BLANK
	MOV	AL,"("
	CALL	_OUT
	MOV	DX,[SI].BrkReps
	CALL	_Dec16Out
	MOV	AL,")"
	CALL	_OUT
	CALL	_BLANK
	MOV	AL,"'"
	CALL	_OUT
	MOV	AL,"'"
	CALL	_OUT
	CALL	_CRLF
BreakLx:
	ADD	SI,SIZE SBRTAB_frame
	POP	CX
	LOOP	BreakL1
	RET

BreakSet:
	MOV	BX,OFFSET ds:_SBRTAB
	MOV	SI,[_pTXT]
	CMP	byte ptr [SI]," "	;assume next available
	JE	BreakS1 		;if not BR0 thru BR9
	CMP	byte ptr [SI],9 	;assume next available
	JE	BreakS1 		;if not BR0 thru BR9
	CALL	GetBPR			;[CX]= BRn or error
	MOV	AX,SIZE SBRTAB_frame
	MUL	CX
	ADD	BX,AX
	CMP	[BX].BrkFlg,0
	JZ	BreakS2 		;Brif got free BR
	JMP	BPerror 		;BR ERROR
BreakS1:
	MOV	CX,10
BreakS1l:
	CMP	[BX].BrkFlg,0
	JZ	BreakS2 		;Brif got free BR
	ADD	BX,SIZE SBRTAB_frame
	LOOP	BreakS1l
BreakSer:
	JMP	BPerror 		;Table is full
BreakS2:
	CALL	_GetChr
	JZ	BreakSer		;error if eos
	MOV	BP,[_RegSA.CSSAV]
	CALL	_ADDRESS
	call	Chk_BPaddr		;has the Sticky BP been set
	jne	BreakS5 		; no, go ahead and set it
	jnc	BreakS5
	mov	bx,di			; yes, reset the same BP
BreakS5:
	MOV	[BX].BrkSeg,AX		;Set segment
	MOV	[BX].BrkOff,DX		;Set offset
	XOR	DX,DX
	CALL	_GetChr
	JZ	BreakS3 		;Count is 0
	CALL	_GetWord
BreakS3:
	OR	DX,DX
	JNZ	BreakS4
	INC	DX
BreakS4:
	MOV	[BX].BrkReps,DX 	;Set repeat count
	MOV	[BX].BrkCntr,DX 	;Set down counter
	MOV	[BX].BrkFlg,3		;Set + Enabled
	RET

GetBPR:
	CALL	_GetByte		;[DL]= BRn or error
	CMP	DL,10			;Error
	JNB	BreakSer		;if not BR0 thru BR9
	MOV	CX,DX
	RET

BreakClr:
	MOV	BX,OFFSET ds:_SBRTAB
	CALL	_GetChr
	JZ	BreakCerr		;error if eos
	CMP	AL,"*"
	JNE	BreakC2
	MOV	CX,10
BreakC1l:
	MOV	[BX].BrkFlg,0
	ADD	BX,SIZE SBRTAB_frame
	LOOP	BreakC1l
BreakCx:
	RET
BreakCerr:
	JMP	BPerror
BreakC2:
	CALL	GetBPR			;[CX]= BRn or error
	MOV	AX,SIZE SBRTAB_frame
	MUL	CX
	MOV	BX,OFFSET ds:_SBRTAB
	ADD	BX,AX
	MOV	[BX].BrkFlg,0
	CALL	_ScanP
	jnz	BreakC2
	ret

BreakDisEna:
	MOV	AH,AL			;Save 'D' or 'E' in [AH]
	MOV	BX,OFFSET ds:_SBRTAB
	CALL	_GetChr
	JZ	BreakDEr		;error if eos
	CMP	AL,"*"
	JNE	BreakDE2
	MOV	CX,10
BreakDEl:
	CALL	BreakDEflag
	ADD	BX,SIZE SBRTAB_frame
	LOOP	BreakDEl
BreakDEx:
	RET
BreakDEr:
	JMP	BPerror
BreakDE2:
	PUSH	AX
	CALL	GetBPR			;[CX]= BRn or error
	MOV	AX,SIZE SBRTAB_frame
	MUL	CX
	MOV	BX,OFFSET ds:_SBRTAB
	ADD	BX,AX
	POP	AX
	CALL	BreakDEflag
	CALL	_ScanP
	jnz	BreakDE2
	ret

BreakDEflag:
	CMP	AH,"D"
	JNE	BreakEflag		;brif was enable
	AND	[BX].BrkFlg,1
	RET
BreakEflag:
	TEST	[BX].BrkFlg,1
	JZ	BreakEx
	OR	[BX].BrkFlg,2
BreakEx:
	RET

; Jump to program, setting up registers according to the
; save area. up to 10 breakpoint addresses may be specified.

BPTAB_frame struc
BpOff	dw  ?		    ;Break addr offset
BpSeg	dw  ?		    ;Break addr Segment
BpInst	db  ?		    ;Save instruction
BPTAB_frame ends

_GO:
	CALL	SETADD
	mov	BRKCNT,0
	MOV	DI,OFFSET ds:BPTAB
Go_1:
	CALL	_ScanP
	JZ	Go_2
	MOV	BP,[_RegSA.CSSAV]
	CALL	_ADDRESS
	push	di
	call	Chk_BPaddr		; has the BP been set?
	pop	di
	je	Go_1			;  yes, dont need to reset it
	MOV	[DI].BpOff,DX		; Save offset
	MOV	[DI].BpSeg,AX		; Save segment
	ADD	DI,SIZE BPTAB_frame
	INC	BRKCNT
	CMP	BRKCNT,1+BPMAX
	jne	Go_1
BPerror:
	mov	BRKCNT,0
	MOV	AX,"RB" 		; BR ERROR
	JMP	_ERR
Go_2:
	mov	[TCOUNT],0
	mov	ax,_RegSA.CSSAV
	mov	dx,_RegSA.IPSAV
	call	Chk_BPaddr
	je	TraceFirst
Go_3:
	call	Set_BPoints
	JMP	DEXIT
TraceFirst:
	mov	BPointTrace,1
	jmp	STEP2


;**	Chk_BPaddr - check if break point is already set
;
;	Entry:	AX:DX = address to check for
;	Exit:	Zero set if match
;		Carry reset if BP is in _GO BP table
;		 else set if BP is in Sticky BP table

Chk_BPaddr:				; AX:DX = address to check for
	MOV	CX,[BRKCNT]
	JCXZ	NoGoBPs
	MOV	DI,OFFSET ds:BPTAB
ChkBP:
	cmp	[di].BpOff,dx		;does offset match
	jne	NextGoBP
	cmp	[di].BpSeg,ax		;does segment match
	clc				;reset for _GO BP table
	je	ChkBPret
NextGoBP:
	ADD	DI,SIZE BPTAB_frame
	LOOP	ChkBP
NoGoBPs:
	MOV	DI,OFFSET ds:_SBRTAB
	MOV	CX,10
ChkSBP:
	test	[di].BrkFlg,1
	jz	NextSBP 		;not set
	cmp	[di].BrkOff,dx		;does offset match
	jne	NextSBP
	cmp	[di].BrkSeg,ax		;does segment match
	stc				;set for Sticky BP table
	je	ChkBPret
NextSBP:
	ADD	DI,SIZE SBRTAB_frame
	LOOP	ChkSBP			;Z flag reset for no match
ChkBPret:
	ret

Set_BPoints:
	cld
	MOV	CX,[BRKCNT]
	PUSH	DS
	JCXZ	NOBP
	MOV	DI,OFFSET ds:BPTAB
SETBP:
	LDS	SI,ES:dword ptr [DI].BpOff  ;[DS:SI]= Brkpoint addr
	LODSB
	MOV	ES:[DI].BpInst,AL	    ;Save Instruction byte
	MOV	byte ptr [SI-1],0CCH
	ADD	DI,SIZE BPTAB_frame
	LOOP	SETBP
NOBP:
	MOV	DI,OFFSET ds:_SBRTAB
	MOV	CX,10
SETSBP:
	CMP	ES:[DI].BrkFlg,3
	JNE	NOTSBP			    ;brif not set & enabled
	LDS	SI,ES:dword ptr [DI].BrkOff ;[DS:SI]= Brkpoint addr
	LODSB
	MOV	ES:[DI].BrkInst,AL	    ;Save Instruction byte
	MOV	byte ptr [SI-1],0CCH
NOTSBP:
	ADD	DI,SIZE SBRTAB_frame
	LOOP	SETSBP
	POP	DS
	mov	BPs_Set,1
	ret

RestoreBPoints:
	MOV	SI,OFFSET ds:BPTAB
	xor	cx,cx
	xchg	CX,[BRKCNT]
	PUSH	ES
	JCXZ	SHOREG
CLEARBP:
	LES	DI,dword ptr [SI].BpOff     ;[ES:DI]= Brkpoint addr
	MOV	AL,[SI].BpInst		    ;Get Instruction byte
	STOSB
	ADD	SI,SIZE BPTAB_frame
	LOOP	CLEARBP
SHOREG:
	MOV	SI,OFFSET ds:_SBRTAB
	MOV	CX,10
CLRSBP:
	CMP	[SI].BrkFlg,3
	JNE	NCLSBP			    ;brif not set & enabled
	LES	DI,dword ptr [SI].BrkOff    ;[ES:DI]= Brkpoint addr
	MOV	AL,[SI].BrkInst 	    ;Get Instruction byte
	STOSB
NCLSBP:
	ADD	SI,SIZE SBRTAB_frame
	LOOP	CLRSBP
	POP	ES
	ret

ChkTraceFrame:
	xor	al,al
	xchg	TraceEntry,al
	or	al,al			; did we enter thru the trace int
	jz	Return_Ok		; no, trace was ok
ChkTraceOk:
	test	intflags,0100h		; did we trace into an interrupt
	jnz	Return_Ok		; no, trace was ok
	push	es
	mov	di,[_RegSA.SPSAV]
	mov	es,[_RegSA.SSSAV]
	mov	dx,es:[di]
	mov	ax,es:2[di]
	and	es:word ptr 4[di],0FEFFh ; reset trace bit
	pop	es
	ret
Return_Ok:
	xor	al,al
	ret


; just like trace except skips OVER next INT or CALL.

_Ptrace:

if REALSMALL
	jmp	_PERR
else
	CALL	SETADD			; get potential starting point
	CALL	_GETEOL 		; check for end of line
	push	es
	MOV	ES,[_RegSA.CSSAV]	; point to instruction to execute
	MOV	DI,[_RegSA.IPSAV]	; include offset in segment
	XOR	DX,DX			; where to place breakpoint
	MOV	AL,ES:[DI]		; get the opcode

	CMP	AL,00101110B		; CS: override
	JE	PtraceOV		; Brif so
	CMP	AL,00100110B		; ES: override
	JE	PtraceOV		; Brif so
	CMP	AL,00110110B		; SS: override
	JNE	PtraceNOV		; Brif not
PtraceOV:
	INC	DI
	MOV	AL,ES:[DI]		; get the opcode
PtraceNOV:
	CMP	AL,11101000B		; direct intra call
	JZ	Ptrace3 		; yes, 3 bytes
	CMP	AL,10011010B		; direct inter call
	JZ	Ptrace5 		; yes, 5 bytes
	CMP	AL,11111111B		; indirect?
	JZ	PtraceModRM		; yes, go figure length
	CMP	AL,11001100B		; short interrupt?
	JZ	Ptrace1 		; yes, 1 byte
	CMP	AL,11001101B		; long interrupt?
	JZ	Ptrace2 		; yes, 2 bytes
	CMP	AL,11100010B		; loop
	JZ	Ptrace2 		; 2 byter
	CMP	AL,11100001B		; loopz/loope
	JZ	Ptrace2 		; 2 byter
	CMP	AL,11100000B		; loopnz/loopne
	JZ	Ptrace2 		; 2 byter
	AND	AL,11111110B		; check for rep
	CMP	AL,11110010B		; perhaps?
	JNZ	Stepv			; can't do anything special, step
	MOV	AL,ES:[DI+1]		; next instruction
	AND	AL,11111110B		; ignore w bit
	CMP	AL,10100100B		; MOVS
	JZ	Ptrace2 		; two byte
	CMP	AL,10100110B		; CMPS
	JZ	Ptrace2 		; two byte
	CMP	AL,10101110B		; SCAS
	JZ	Ptrace2 		; two byte
	CMP	AL,10101100B		; LODS
	JZ	Ptrace2 		; two byte
	CMP	AL,10101010B		; STOS
	JZ	Ptrace2 		; two byte
Stepv:
	pop	es			; restore ES
	mov	dx,1
	JMP	Step			; bogus, do single step

PtraceModRM:
	MOV	AL,ES:[DI+1]		; get next byte
	MOV	AH,AL
	AND	AL,11000000B		; get mod
	AND	AH,00111000B		; get type
	CMP	AH,00010000B
	JE	PtraceMod01x		; Brif Intra indirect
	CMP	AH,00011000B		; Inter indirect?
	JNE	Stepv			; Not a CALL instr
PtraceMod01x:
	CMP	AL,11000000B		; indirect through register?
	JZ	Ptrace2 		; two byte
	CMP	AL,01000000B		; indirect 8 bit offset?
	JZ	Ptrace3 		; yes, three byte whammy
	CMP	AL,10000000B		; indirect 16 bit offset?
	JZ	Ptrace4 		; four byte offset
	MOV	AL,ES:[DI+1]		; must be 00, reget modrm
	AND	AL,00000111B		; get r/m
	CMP	AL,00000110B		; Indirect immediate?
	JZ	Ptrace4 		; Brif so, 4 byte
	JMP	Ptrace2 		; else 2 reg indirect, 2 byte
Ptrace5:INC	DX
Ptrace4:INC	DX
Ptrace3:INC	DX
Ptrace2:INC	DX
Ptrace1:INC	DX
	add	dx,di			; offset to breakpoint instruction
	mov	ax,es			; segment to breakpoint instruction
	pop	es			; restore ES
endif

Set_Tmp_BP:				; start the operation!
	call	Chk_BPaddr
	jz	SetBPs_Go
	mov	[BPTAB.BpOff],dx
	MOV	[BPTAB.BpSeg],ax
	MOV	[BRKCNT],1		; only this breakpoint
SetBPs_Go:
	jmp	Go_3			; set break points and go

; Trace 1 instruction or the number of instruction specified
; by the parameter using 8086 trace mode. Registers are all
; set according to values in save area

_TRACE:
	CALL	SETADD
	CALL	_ScanP
	MOV	DX,1
	JZ	STOCNT
	CALL	_GetWord
STOCNT:
	CALL	_GETEOL
Step:
	MOV	[TCOUNT],DX
STEP1:
	mov	ax,[_RegSA.IPSAV]
	mov	OldIPSav,ax
	mov	ax,[_RegSA.CSSAV]
	mov	OldCSSav,ax
STEP2:
	OR	byte ptr [_RegSA.FSAV+1],1
DEXIT:
	CLI
	MOV	SP,OFFSET ds:_RegSA.AXSAV
	POP	AX
	POP	BX
	POP	CX
	POP	DX
	POP	BP
	POP	BP
	POP	SI
	POP	DI
	POP	ES
	POP	ES
	POP	SS
	MOV	SP,[_RegSA.SPSAV]
	PUSH	[_RegSA.FSAV]
	PUSH	[_RegSA.CSSAV]
	PUSH	[_RegSA.IPSAV]
	MOV	DS,[_RegSA.DSSAV]
	IRET


Debug_Terminate:
	pop	cs:[_RegSA.IPSAV]	; set up iret frame
	pushf
	push	cs
	push	cs:[_RegSA.IPSAV]
	cli
	push	ax
	push	ds

	xor	al,al
	xchg	cs:DebugInited,al
	or	al,al			; Have the int vectors been initialized?
	jz	d_notinited		;  no, don't un-initialize

	xor	ax,ax
	mov	ds,ax

	mov	ax,word ptr cs:[OldInt1]	; Reset vector 1--Single step
	mov	word ptr ds:[1*4],ax
	mov	ax,word ptr cs:[OldInt1+2]
	mov	word ptr ds:[1*4+2],ax

	mov	ax,word ptr cs:[OldInt3]	; Reset vector 3--Breakpoint
	mov	word ptr ds:[3*4],ax
	mov	ax,word ptr cs:[OldInt3+2]
	mov	word ptr ds:[3*4+2],ax

	mov	ax,word ptr cs:[OldInt6]	; Reset vector 6--Illegal Instr
	mov	word ptr ds:[6*4],ax
	mov	ax,word ptr cs:[OldInt6+2]
	mov	word ptr ds:[6*4+2],ax

	mov	ax,word ptr cs:[NextTimer]	; Reset vector 8--Timer Int
	mov	word ptr ds:[8*4],ax
	mov	ax,word ptr cs:[NextTimer+2]
	mov	word ptr ds:[8*4+2],ax

d_notinited:
	push	cs
	pop	ds
	push	si
	push	di
	push	cx

	call	RestoreBPoints		; go restore breakpoints

	pop	cx
	pop	di
	pop	si
	pop	ds
	pop	ax
	iret


; Debugger entry point called by program to force debugger control

Debug_Entry:
	cmp	cs:_RegSA.IPSAV,offset ds:Debug_Entry
	jne	not_T_mode		; prevent tracing into debugger
	ret
not_T_mode:
	push	ax
	mov	ax,00FFh
	xchg	cs:DebugInited,al
	cli
	or	ax,ax			; Have the int vectors been initialized?
	jnz	d_inited		;  yes, don't reinitialize

;;	  push	  ds
;;	  push	  dx
;;	  push	  cs
;;	  pop	  ds
;;	  mov	  dx,offset cs:BREAKFIX
;;	  mov	  ax,2503h
;;	  int	  21h
;;	  mov	  dx,offset cs:REENTER
;;	  mov	  ax,2501h
;;	  int	  21h
;;	  pop	  dx
;;	  pop	  ds

	push	ds
	mov	ds,ax

	mov	ax,offset cs:REENTER	; Set vector 1--single step
	xchg	ax,word ptr ds:[1*4]
	mov	word ptr cs:[OldInt1],ax
	mov	ax,cs
	xchg	ax,word ptr ds:[1*4+2]
	mov	word ptr cs:[OldInt1+2],ax

	mov	ax,offset cs:BREAKFIX	; Set vector 3--breakpoint
	xchg	ax,word ptr ds:[3*4]
	mov	word ptr cs:[OldInt3],ax
	mov	ax,cs
	xchg	ax,word ptr ds:[3*4+2]
	mov	word ptr cs:[OldInt3+2],ax

	mov	ax,offset cs:IllegInst	; Set vector 6--illegal instruction
	xchg	ax,word ptr ds:[6*4]
	mov	word ptr cs:[OldInt6],ax
	mov	ax,cs
	xchg	ax,word ptr ds:[6*4+2]
	mov	word ptr cs:[OldInt6+2],ax

	mov	ax,offset cs:TrapEntry	; Set vector 8--timer interrupt
	xchg	ax,word ptr ds:[8*4]
	mov	word ptr cs:[NextTimer],ax
	mov	ax,cs
	xchg	ax,word ptr ds:[8*4+2]
	mov	word ptr cs:[NextTimer+2],ax

	pop	ds

d_inited:
	pop	ax
	pop	cs:_RegSA.IPSAV
	pushf
	push	cs
	push	cs:_RegSA.IPSAV
	mov	cs:force_entry,1
	jmp	SHORT ReenterReal

; Re-entry point from breakpoint. Need to decrement instruction
; pointer so it points to location where breakpoint actually
; occured.

BREAKFIX:
	PUSH	BP
	MOV	BP,SP
	DEC	word ptr [BP].OldIP
	POP	BP
	JMP	SHORT ReenterReal

; Re-entry point from an illegal instruction.  We don't need to decrement
; the instruction pointer, it already points to the correct value.
IllegInst:
	JMP	SHORT ReenterReal

; Re-entry point from trace mode or interrupt during
; execution. All registers are saved so they can be
; displayed or modified.

Interrupt_Frame struc
	OldBP	dw  ?
	OldIP	dw  ?
	OldCS	dw  ?
	OldF	dw  ?
Interrupt_Frame ends

REENTER:
	mov	cs:TraceEntry,1
ReenterReal:
	MOV	CS:[_RegSA.SPSAV],SP
	MOV	CS:[_RegSA.SSSAV],SS
	MOV	CS:[_RegSA.FSAV],CS
	MOV	SS,CS:[_RegSA.FSAV]
	MOV	SP,OFFSET cs:_RegSA.ESSAV+2
	PUSH	ES
	PUSH	DS
	PUSH	DI
	PUSH	SI
	PUSH	BP
	DEC	SP
	DEC	SP
	PUSH	DX
	PUSH	CX
	PUSH	BX
	PUSH	AX
	PUSH	SS
	POP	DS
	MOV	SS,[_RegSA.SSSAV]
	MOV	SP,[_RegSA.SPSAV]
	POP	[_RegSA.IPSAV]
	POP	[_RegSA.CSSAV]
	POP	ax
	mov	intflags,ax
	and	ah,0FEh 		; turn off trace bit
	mov	[_RegSA.FSAV],ax
	MOV	[_RegSA.SPSAV],SP
	PUSH	DS
	POP	ES
	PUSH	DS
	POP	SS
	MOV	SP,OFFSET ds:_StackTop

	CLD
	xor	al,al
	xchg	BPs_Set,al
	or	al,al			; have any breakpoints been set
	jz	ChkBPTrace
	call	RestoreBPoints		; yes, go restore breakpoints
ChkBPTrace:
	xor	al,al
	xchg	BPointTrace,al
	or	al,al			; were we tracing over a break point
	jz	CheckOld
	call	ChkTraceFrame
	jmp	Go_3			; yes, set break points and go

CheckOld:
	mov	ax,OldIPSav
	or	ax,OldCSSav		; did we start a trace operation
	jz	CheckDisp		; no

	mov	ax,OldIPSav
	cmp	ax,[_RegSA.IPSAV]
	jne	ChkTraceEnt
	mov	ax,OldCSSav
	cmp	ax,[_RegSA.CSSAV]
	jne	ChkTraceEnt		; are we at the same address
	call	ChkTraceFrame
	JMP	STEP1			; yes, trace again

ChkTraceEnt:
	call	ChkTraceFrame
	jz	Trace_Ok
	jmp	Set_Tmp_BP

Trace_Ok:
	xor	ax,ax
	mov	OldIPSav,ax		; trace operation completed ok
	mov	OldCSSav,ax
CheckDisp:
	call	ChkTraceFrame
	CALL	_CRLF
	xor	al,al
	xchg	force_entry,al
	or	al,al
	jz	NoHeader
	MOV	DX,OFFSET ds:HEADER
	CALL	_RPRBUF
	mov	ax,_RegSA.CSSAV
	MOV	word ptr [_DISADD+2],AX
	MOV	word ptr [_ASMADD+2],AX
	mov	ax,_RegSA.DSSAV
	MOV	word ptr [_DEFDUMP+2],AX
	mov	ax,_RegSA.IPSAV
	MOV	word ptr[_DISADD],AX
	MOV	word ptr[_ASMADD],AX
	MOV	word ptr [_DEFDUMP],0
NoHeader:
	CALL	_DISPREG
	cmp	[TCOUNT],0
	je	NoTraceCnt
	DEC	[TCOUNT]		; is this the last trace count
	JZ	NoTraceCnt		; yes, go display and get command
	JMP	STEP1			; trace again

NoTraceCnt:
	JMP	_COMMAND

SETADD:
	MOV	BP,[_RegSA.CSSAV]
	CALL	_ScanP
	CMP	AL,"="
	JNZ	RET$5
	CALL	_GetChrI
	CALL	_ADDRESS
	MOV	[_RegSA.CSSAV],AX
	MOV	[_RegSA.IPSAV],DX
RET$5:	RET


assume	ds:nothing,es:nothing,ss:nothing

TrapEntry:
	call	_chkctlc
	jnz	trape1
	call	Debug_Entry
trape1:
	jmp	NextTimer

DBCODE	ends

	end
