;	SCCSID = @(#)debbcmd.asm	6.2 91/06/01
PAGE	,132
NAME	DEBBCMD
TITLE	DEBBCMD.ASM - Debug Breakpoint, Vector, Default command handler
.286p

;***	DEBBCMD.ASM
;
;	Contains the routines to handle the breakpoints, etc.
;
;	SCCSID = @(#)debbcmd.asm	6.2 91/06/01
;

.xlist
        include dosseg.inc			; 148688
        include basemaca.inc			; 148688
        include osmaca.inc			; 148688
	include debseg.inc
	include debmac.inc
	include debequ.inc
	include debinit.inc
	include debdesc.inc
	include debsym.inc
.list

DataSegmentStart

Extern DebData, 	WORD
Extern DefType, 	WORD
Extern sbrtab,		BYTE
Extern pTXT,		WORD
Extern pTXTstart,	WORD
Extern RegSA,		WORD
Extern DefaultCmd,	BYTE

Global BPcount, 	WORD,	0		; Number of breakpoints
Static BPtable, 	BPLEN,	0		; Breakpoint table
Static TempAddr,	STRUC,	AddrS, <>

BreakCommands	label	byte
	CmdTable_s <"C", codeOFFSET BreakClr>
	CmdTable_s <"D", codeOFFSET BreakDis>
	CmdTable_s <"E", codeOFFSET BreakEna>
	CmdTable_s <"L", codeOFFSET BreakList>
	CmdTable_s <"P", codeOFFSET BreakSet>
	CmdTable_s <"R", codeOFFSET BreakReg>
; Start of change DGE
	CmdTable_s <"S", codeOFFSET BreakShow>
	CmdTable_s <"T", codeOFFSET BreakTimeStamp>
; End of change DGE
	CmdTable_s <0,	 codeOFFSET perr>

VectorCommands	label	byte
	CmdTable_s <"C", codeOFFSET VectorClear>
	CmdTable_s <"L", codeOFFSET VectorList>
	CmdTable_s <"S", codeOFFSET VectorSet>
	CmdTable_s <"T", codeOFFSET VectorTrap>
	CmdTable_s <0,	 codeOFFSET perr>

ChgDefCommands	label	byte
	CmdTable_s <"L", codeOFFSET ChgDefList>
	CmdTable_s <"S", codeOFFSET ChgDefSet>
	CmdTable_s <0,	 codeOFFSET perr>

;*  BR command option table

BROption	struc
	BROpChar	db	?
	BROpMask	db	?
	BROpValue	db	?
BROption	ends

BROptionTable	label	byte
	BROption <'E', mask DR_TYPE, DR_TYPE_EXECUTE shl DR_TYPE>
	BROption <'W', mask DR_TYPE, DR_TYPE_WRITE shl DR_TYPE>
	BROption <'R', mask DR_TYPE, DR_TYPE_RW shl DR_TYPE>
	BROption <'I', mask DR_TYPE, DR_TYPE_IO shl DR_TYPE>		; 148688
	BROption <'1', mask DR_LEN, DR_LEN_1 shl DR_LEN>
	BROption <'2', mask DR_LEN, DR_LEN_2 shl DR_LEN>
	BROption <'4', mask DR_LEN, DR_LEN_4 shl DR_LEN>
	db	0	; end of table

;*  386 debug register allocation array.  0 = free, !0 = allocated.

DebugRegAlloc	db	NUM_DEBUG_REG dup (0)

DataSegmentEnd

CodeSegmentStart

Extern perr,		NEAR
Extern perror,		NEAR
Extern CmdBreak,	NEAR
Extern command, 	NEAR

;***	BreakPoint
;
;	Set, Enable, Disable, Clear, and list 'Sticky Breakpoints'
;	10 Sticky Breakpoint addresses may be specified.
;
;	ENTRY:	NONE
;
;	EXIT:	NONE
;
;	USES:	ALL
;

DefProc BreakPoint, NEAR, PUBLIC
	Pcall	GetChrI 		; Get char & advance

;	AL = subcommand letter

	mov	si,dataOFFSET BreakCommands
	Pcall	ExecSubr
EndProc

; Start of change DGE
;***	BreakShow
;
;	Show the time stamp entries
;

DefProc BreakShow

	Pcall	ShowTimeStamps

EndProc
; End of change DGE

;***	BreakList
;
;	List the current sticky break points
;

DefProc BreakList, NEAR, LOCAL
	Pcall	GetEOL
	mov	cx,STICKY_BP_MAX
	mov	si,dataOFFSET sbrtab

bkl0:	push	cx
	mov	al,[si].BrkFlag
	or	al,al
	jz	bkl5			; jump not set

	mov	bl,al			; (BL) = BrkFlag
	mov	dx,STICKY_BP_MAX
	sub	dx,cx
	xor	ax,ax			; (AX:DX) = BP number to print
	Pcall	OutHex
	Pcall	blank

	mov	al,"e"                  ; assume the breakpoint is enabled
	test	bl,BP_ENABLED		; is it?
	jnz	bkl1			; if so, display

	mov	al,"d"                  ; assume the breakpoint is disabled
bkl1:	Pcall	putchar 		; "d"= disabled,"e"= enabled,"i" = bad

	mov	al," "                  ; assume breakpoint is valid
	test	bl,BP_BADADDR		; is it?
; Start of change DGE
	jz	bkl1a			; if so, go display blank

	mov	al,"I"                  ; mark breakpoint as invalid
	jmp	short bkl2

bkl1a:	test	bl,BP_TIMESTAMP 	; Time stamping break point?
	jz	bkl2			; is not, leave blank
	mov	al,"T"                  ; "T" = time stamp bp
	jmp	short bkl2

bkl1b:	jmp	short bkl0
; End of change DGE

bkl2:	Pcall	putchar

	Pcall	blank

	Pcall	DisplayDRFlag		; display debug register flags
	Pcall	PrintBPaddr		; display the breakpoint address

	cmp	[si].BrkReps,0
	je	bkl3

;	Print passcount

	Pcall	blank
	mov	dx,[si].BrkCntr
	xor	ax,ax			; (AX:DX) = passcount to print
	Pcall	OutHex
	Pcall	blank
	mov	al,"("
	Pcall	putchar
	mov	dx,[si].BrkReps
	xor	ax,ax			; (AX:DX) = count to print
	Pcall	OutHex
	mov	al,")"
	Pcall	putchar

;	Print breakpoint command line (if any)

bkl3:	cmp	[si].BrkCmd,0		; no breakpoint command
	je	bkl4

	Pcall	blank			; display starting quote
	mov	al,'"'
	Pcall	putchar
	lea	dx,[si].BrkCmd		; (DS:DX) -> BP command string
	Pcall	rprbuf
	mov	al,'"'
	Pcall	putchar
bkl4:	Pcall	crlf

bkl5:	add	si,size StickyBP_struc
	pop	cx
	loop	bkl1b
EndProc

;***	DisplayDRFlag
;
;	Display the BrkDRFlag field for a 386 debug register
;	breakpoint.
;
;	ENTRY:	(SI) = BP table entry
;
;	EXIT:	NONE
;
;	USES:	AX, DI, Flags
;

DefProc DisplayDRFlag, NEAR, LOCAL
	test	[si].BrkDRFlag,mask DR_SET
	jz	ddfx

	mov	di,dataOFFSET BROptionTable
ddf1:	cmp	[di].BROpChar,0
	je	ddf3			; jump if end of table

	mov	al,[si].BrkDRFlag	; (AX) = break register flags
	and	al,[di].BROpMask	; (AX) = masked off register flags
	cmp	al,[di].BROpValue
	jne	ddf2			; jump if option value not equal

	mov	al,[di].BROpChar	; (AL) = option character
	Pcall	putchar

ddf2:	add	di,size BROption
	jmp	short ddf1

ddf3:	Pcall	blank
ddfx:
EndProc

;***	PrintBPaddr - Display breakpoint address and symbol
;
;	Checks to see it this address has a symbol associated with it.
;	If it does, it is printed on the debugging terminal
;
;	ENTRY	(SI) = break point table entry.
;
;	EXIT	NONE
;
;	USES	AX, BX, CX, DX, Flags
;

DefProc PrintBPaddr, NEAR, LOCAL
	SaveReg <si, di>
	lea	bx,[si].BrkAddr
	Pcall	OutAddrS, <bx>
	lea	si,[si].BrkAddr 		; (SI) = Break Point Address
	mov	cx,MAP_BRACKET			; (CX) = Sym flags
	Pcall	blank
	Pcall	PrintNearSym			; Print Symbol near address
	Pcall	blank
	RestReg <di, si>
EndProc

;***	BreakReg
;
;	Set a 386 debug register breakpoint
;
;	BR[<bp>] E|W|R|I|1|2|4 [<addr>] [<passcnt>] ["<bp cmds>"] 	; 148688
;
;	  1 - one-byte length (default)
;	  2 - word length on word boundary
;	  4 - dword length on dword boundary
;
;	  E - break on instruction execution only (one-byte length only)
;	  W - break on writes only
;	  R - break on reads and writes
;	  I - break on i/o reads and writes			  	; 148688
;
;	ENTRY:	NONE
;
;	EXIT:	NONE
;
;	USES:	AX, BX, CX, DX, SI, DI, Flags
;

DefProc BreakReg, NEAR, LOCAL
	test	DebData.InitFlag,INIT_CPU386
	jz	brg1
	mov	bx,-1
	jmp	short BreakSetSub

brg1:	Jump	perr
EndProc

; Start of change DGE
;***	BreakTimeStamp
;
;	Set time stamp breakpoint
;
;	BT[<bp>] [<addr>]
;
;	<bp>	  - a breakpoint number
;	<addr>	  - breakpoint address
;
;	ENTRY:	NONE
;
;	EXIT:	NONE
;
;	USES:	AX, BX, CX, DX, SI, DI, Flags
;

DefProc BreakTimeStamp, NEAR, LOCAL
	mov	bx,1
	jmp	short BreakSetSub
EndProc
; End of change DGE

;***	BreakSet
;
;	Set sticky breakpoints.
;
;	BP[<bp>] [<addr>] [<passcnt>] ["<bp cmds>"]
;
;	<bp>	  - a breakpoint number
;	<addr>	  - breakpoint address
;	<passcnt> - number of times to execute BP before breaking.
;	<bp cmds> - command line execute when breaking on the BP.
;
;	ENTRY:	NONE
;
;	EXIT:	NONE
;
;	USES:	AX, BX, CX, DX, SI, DI, Flags
;

DefProc BreakSet, NEAR, LOCAL
	xor	bx,bx

;***	BreakSetSub
;
;	ENTRY:	(BX) = break/debug register flag
;
;	EXIT:	NONE
;
;	USES:	AX, BX, CX, DX, SI, DI, Flags

Entry	BreakSetSub, NEAR, LOCAL
	Pcall	IsSeparator
	jne	bks2			; jump if not a command separator

	mov	di,dataOFFSET sbrtab
	mov	cx,STICKY_BP_MAX
bks1:	cmp	[di].BrkFlag,0
	je	bks3			; jump if got free BR
	add	di,size StickyBP_struc
	loop	bks1

Entry	TooManyBPs, NEAR, LOCAL
	putstr	"Too many breakpoints"
	jmp	CmdBreak		; table is full

Entry	BreakBad, NEAR, LOCAL
	putstr	"Bad breakpoint command"
	jmp	CmdBreak

bks2:	Pcall	GetBPR			; (DI) = BRn's table index
	cmp	[di].BrkFlag,0		; check if clear
	je	bks3

	Pcall	ScanP			; see if adding a BP command to
	jz	BreakBad		; an existing BP

	xor	dx,dx			; (DX) = default passcount
	Pcall	CheckString
	je	short bks7		; jump if command string

bks3:	Pcall	ScanP
	jz	BreakBad		; error if eos

;	(BX) = break/debug register flag
;	(DI) = BP table entry

	Pcall	FreeDebugReg		; free the debug register
	mov	[di].BrkDRFlag,0	; clear debug register flag
	or	bx,bx
	jz	bks4			; jump if BP (normal) command
; Start of change DGE
	jns	bksts			; jump if BT (time stamp) command
; End of change DGE
	Pcall	SetDRFlag		; parse the BR command parameters
	mov	bx,dataOFFSET TempAddr	; (BX) = temporary address holder
	Pcall	GetCSIPaddr
	Pcall	EvalAddr, <bx, bx>
	jmp	short bks5

bks4:	mov	bx,dataOFFSET TempAddr	; (BX) = temporary address holder
	Pcall	GetCSIPaddr
	Pcall	EvalAddr, <bx, bx>
	Pcall	CheckAddr, <bx>
	Pcall	ChkBPaddr		; has the Sticky BP been set
	jnz	bks5			; no, go ahead and set it
	jnc	bks5			; jump if GO BP

	mov	al,[di].BrkDRFlag
	mov	di,si			; (DI) = breakpoint table entry
	push	ax
	Pcall	FreeDebugReg		; free the out going debug register
	pop	ax
	mov	[di].BrkDRFlag,al	; move debug register flag

bks5:	lea	bx,[di].BrkAddr 	; (BX) = address struct pointer
	Pcall	CopyAddr, <dataOFFSET TempAddr, bx>
	Pcall	CheckDRAddr		; check if address is align correctly
	mov	[di].BrkCmd,0		; no commands yet

;	Check for passcount or command

	xor	dx,dx
	Pcall	ScanP
	jz	short bks8		; Count is 0, no command

	Pcall	CheckString
	je	short bks7		; jump if command string

;	Get/set passcount

	Pcall	EvalWord
	Pcall	ScanP
	jz	short bks8

	Pcall	CheckString
	je	short bks7		; jump if command string
	jmp	perror

;	Set breakpoint command

bks7:	push	di
	lea	di,[di].BrkCmd		; (DI) = breakpoint command string
	Pcall	ParseCommandLine
	pop	di
	jnc	short bks8
	mov	[di].BrkCmd,0
	jmp	BreakBad

bks8:	mov	[di].BrkReps,dx 	; Set repeat count
	mov	[di].BrkCntr,dx 	; Set down counter
	Pcall	AllocDebugReg
	mov	[di].BrkFlag,BP_SET+BP_ENABLED
	jmp	short bksx
;Start of change DGE

; Set breakpoint time stamp

bksts:	mov	bx,dataOFFSET TempAddr	; (BX) = temporary address holder
	Pcall	GetCSIPaddr
	Pcall	EvalAddr, <bx, bx>
	Pcall	CheckAddr, <bx>
	Pcall	ChkBPaddr		; has the Sticky BP been set
	jnz	bksts1			; no, go ahead and set it
	jnc	bksts1			; jump if GO BP

	mov	al,[di].BrkDRFlag
	mov	di,si			; (DI) = breakpoint table entry
	push	ax
	Pcall	FreeDebugReg		; free the out going debug register
	pop	ax
	mov	[di].BrkDRFlag,al	; move debug register flag

bksts1: lea	bx,[di].BrkAddr 	; (BX) = address struct pointer
	Pcall	CopyAddr, <dataOFFSET TempAddr, bx>
	Pcall	CheckDRAddr		; check if address is align correctly
	mov	[di].BrkCmd,0		; no commands allowed
	mov	[di].BrkFlag,BP_SET+BP_ENABLED+BP_TIMESTAMP

bksx:
;End of change DGE

EndProc

;***	SetDRFlag
;
;	Set BrkDRFlag field for a 386 debug register breakpoint.
;
;	ENTRY:	(DI) = BP table entry
;
;	EXIT:	NONE
;
;	USES:	AX, CX, SI, Flags
;

DefProc SetDRFlag, NEAR, LOCAL
	xor	cx,cx			; (CX) = field has been set mask
	mov	[di].BrkDRFlag,(DR_LEN_1 shl DR_LEN) or (mask DR_SET)
sdf1:	Pcall	IsSeparator
	je	sdf4			; jump to exit
	Pcall	GetChrI 		; (AL) = character from cmd line
	jz	sdfe

	mov	si,dataOFFSET BROptionTable
sdf2:	cmp	[si].BROpChar,0
	je	sdfe			; jump if end of table (to error)
	cmp	al,[si].BROpChar
	je	sdf3			; jump if found match
	add	si,size BROption
	jmp	short sdf2

sdfe:	Jump	BreakBad

sdf3:	mov	al,[si].BROpMask
	test	cl,al			; has this field already been set
	jnz	sdfe			; jump if already set (to error)

	or	cl,al			; indicate the field has been set
	not	al
	and	[di].BrkDRFlag,al	; clear the field

	mov	al,[si].BROpValue
	or	[di].BrkDRFlag,al	; put into value into cleared field
	jmp	short sdf1

sdf4:	mov	al,[di].BrkDRFlag	; (AL) = debug register flags
	mov	ah,al

	and	al,mask DR_TYPE 	; (AL) = type
	cmp	al,DR_TYPE_EXECUTE shl DR_TYPE
	jne	sdf5

	and	ah,mask DR_LEN		; (AH) = length
	cmp	ah,DR_LEN_1 shl DR_LEN	; is length a byte?
	jne	sdfe			; jump to error if length isn't a byte
sdf5:
EndProc

;***	CheckDRAddr
;
;	Free the 386 debug register.
;
;	ENTRY:	(DI) = breakpoint entry
;
;	EXIT:	NONE
;
;	USES:	AX, BX, CX, DX, Flags
;

DefProc CheckDRAddr, NEAR, LOCAL
	test	[di].BrkDRFlag,mask DR_SET
	jz	cdax

	lea	bx,[di].BrkAddr 	; (BX) = BP AddrS pointer
	mov	cx,1			; (CX) = region size
	Pcall	AddrToLin		; (AX:DX) = linear address
	jc	cdae

	mov	bl,[di].BrkDRFlag
	and	bl,mask DR_LEN
	cmp	bl,DR_LEN_2 shl DR_LEN	; word length?
	jne	cda1
	test	dx,01h			; check if word aligned
	jz	cdax
	putstr	"BR address must be word aligned"
	Jump	CmdBreak

cda1:	cmp	bl,DR_LEN_4 shl DR_LEN	; dword length?
	jne	cdax
	test	dx,03h			; check if dword aligned
	jz	cdax
	putstr	"BR address must be dword aligned"
	Jump	CmdBreak

cdae:	Jump	ErrBreak
cdax:
EndProc

;***	BreakClr
;
;	Clear sticky break pointes.
;

DefProc BreakClr, NEAR, LOCAL
	Pcall	ScanP
	jz	short bkc2		;error if eos
	cmp	al,"*"
	jne	short bkc3

	Pcall	SkipB
	mov	di,dataOFFSET sbrtab
	mov	cx,STICKY_BP_MAX

bkc1:	Pcall	FreeDebugReg
	mov	[di].BrkFlag,0
	mov	[di].BrkDRFlag,0
	add	di,size StickyBP_struc
	loop	bkc1
	jmp	short bkc4

bkc2:	jmp	BreakBad

bkc3:	Pcall	GetBPR			; (DI) = BP table index
	Pcall	FreeDebugReg
	mov	[di].BrkFlag,0
	mov	[di].BrkDRFlag,0
	Pcall	ScanP
	jnz	short bkc3
bkc4:
EndProc

;***	BreakDis
;
;	Disables/Enables sticky break points.
;

DefProc BreakDis, NEAR, LOCAL
	mov	dl,"D"
	jmp	short bkd1

Entry	BreakEna
	mov	dl,"E"
bkd1:	Pcall	ScanP
	jz	short bkd3		; error if eos
	cmp	al,"*"
	jne	short bkd4

	Pcall	SkipB
	mov	di,dataOFFSET sbrtab
	mov	cx,STICKY_BP_MAX

;	DL = break disable/enable flag

bkd2:	Pcall	BreakDEflag
	add	di,size StickyBP_struc
	loop	bkd2
	jmp	short bkd5

bkd3:	jmp	BreakBad

bkd4:	Pcall	GetBPR			; (DI) = breakpoint table index

;	DL = break disable/enable flag

	Pcall	BreakDEflag
	Pcall	ScanP
	jnz	short bkd4
bkd5:
EndProc

;***	BreakDEflag
;
;	Set the break point disable/enable flag.
;

DefProc BreakDEflag, NEAR, LOCAL
	cmp	dl,"D"
	jne	bkf1		; brif was enable
	Pcall	FreeDebugReg	; free the debug register while disabled
	and	[di].BrkFlag,not (BP_ENABLED + BP_INMEM)
	jmp	short bkf2

bkf1:	test	[di].BrkFlag,BP_SET
	jz	bkf2		; jump if not set
	Pcall	AllocDebugReg	; allocate debug register when enabled
	or	[di].BrkFlag,BP_ENABLED
bkf2:
EndProc

;***	AllocDebugReg
;
;	Allocate one of the free 386 debug registers.  This function
;	will not return if there are no debug registers left.  Prints
;	an error message and jumps to the command loop.  This routine
;	should only be called if the breakpoint is or is going to be
;	set and enabled.
;
;	ENTRY:	(DI) = breakpoint entry
;
;	EXIT:	NONE
;
;	USES:	AX, SI, Flags
;

DefProc AllocDebugReg, NEAR, LOCAL
	test	[di].BrkDRFlag,mask DR_SET
	jz	short adrx		; jump if not a debug register bp

	test	[di].BrkDRFlag,mask DR_ALLOC
	jnz	short adrx		; jump if already allocated

	Pcall	IsDebProtMode
	jnc	short adr0		; jump if real mode (use old scheme)

	Pcall	DebCallOut,<DEB_CALLOUT_DRALLOC>
	cmp	si,-1			; (SI) = debug register index
	je	short adr2		; jump if no debug registers left

	jmp	short adr3

adr0:	mov	si,DR_NUM-1		; (SI) = index into allocation array
adr1:	cmp	DebugRegAlloc[si],0	; use old allocation scheme
	je	short adr3		; jump if debug register is free
	dec	si
	jnb	short adr1

adr2:	putstr	"Out of debug registers"
	Jump	CmdBreak

adr3:	mov	DebugRegAlloc[si],-1	; mark debug register as allocated
	mov	ax,si
	shl	al,DR_NUM		; (AX) = shifted over debug reg number
	and	[di].BrkDRFlag,not (mask DR_NUM)
	or	al,mask DR_ALLOC
	or	[di].BrkDRFlag,al
adrx:
EndProc

;***	FreeDebugReg
;
;	Free the 386 debug register if breakpoint is set and enabled.
;	The kernel debugger must free debug registers in LIFO order -
;	debug registers are allocated contiguously from DR3 to DR0, so
;	they must be freed in the order 0,1,2,3.
;
;	ENTRY:	(DI) = breakpoint entry
;
;	EXIT:	NONE
;
;	USES:	AX, SI, Flags
;

DefProc FreeDebugReg, NEAR, LOCAL
	test	[di].BrkDRFlag,mask DR_SET
	jz	short fdrx		; jump if not a debug register bp

	test	[di].BrkDRFlag,mask DR_ALLOC
	jz	short fdrx		; jump if already free

; Start of code change DGE
	test	[di].BrkFlag,BP_TIMESTAMP ; freeing a time stamp bp?
	jz	short fdr05		; jump if not
	Pcall	ClearTimeStamps
fdr05:
; End of code change DGE

	and	[di].BrkDRFlag,not (mask DR_ALLOC)
	xor	ax,ax
	mov	al,[di].BrkDRFlag	; (AL) = debug register flags
	and	al,mask DR_NUM		; (AL) = shifted debug register number
	shr	al,DR_NUM		; (AL) = debug register number
	Pcall	IsDebProtMode
	jnc	short fdr60		; jump if real mode

	push	di
	mov	di,-1
fdr10:	inc	di			; find lowest DR index in use
	cmp	DebugRegAlloc[di],0
	je	short fdr10		; jump if not in use
					; (DI) = lowest DR in use
	cmp	ax,di			; if this DR is also the lowest DR,
	je	short fdr50		;  no shuffle needed

	push	bx

;	Scan breakpoint table, find SI such that sbrtab[(SI)].DRnum == (DI)

	mov	si,dataOFFSET sbrtab
fdr20:	test	[si].BrkDRFlag,mask DR_SET
	jz	short fdr30		; jump if not a debug register bp

	test	[si].BrkDRFlag,mask DR_ALLOC
	jz	short fdr30		; jump if no debug register allocated

	xor	bx,bx
	mov	bl,[si].BrkDRFlag	; (BL) = debug register flags
	and	bl,mask DR_NUM		; (BL) = shifted debug register number
	shr	bl,DR_NUM		; (BL) = debug register number
	cmp	bx,di
	je	short fdr40		; jump if sbrtab[si].DRnum == (DI)

fdr30:	add	si,size StickyBP_struc	; (SI) = next sbrtab index
	jmp	short fdr20

;	Set sbrtab[(SI)].DRnum == (AX)

fdr40:	shl	al,DR_NUM		; (AX) = shifted over debug reg number
	and	[si].BrkDRFlag,not (mask DR_NUM)
	or	al,mask DR_ALLOC
	or	[si].BrkDRFlag,al

	pop	bx

	mov	ax,di			; (AX) = DR index to free
fdr50:	pop	di

	Pcall	DebCallOut,<DEB_CALLOUT_DRFREE>
	or	si,si
	jz	short fdr60		; jump if no error

	putstr	"FreeDebugReg: Free not LIFO"
	jmp	CmdBreak

fdr60:	mov	si,ax
	mov	DebugRegAlloc[si],0	; mark debug register as free
fdrx:
EndProc

;***	GetBPR
;
;	Get the BPn type of sticky break point.
;
;	ENTRY:	NONE
;
;	EXIT:	(AX) = sticky breakpoint number
;		(DI) = breakpoint table index
;
;	USES:	AX, FLAGS
;

DefProc GetBPR, NEAR, LOCAL
	Pcall	GetChrI
	cmp	al,'0'
	jb	gbp1			; bad breakpoint
	cmp	al,'9'
	jbe	gbp2			; bad breakpoint
gbp1:	jmp	BreakBad

gbp2:	push	ax
	Pcall	IsSeparator
	pop	ax
	jnz	gbp1
	and	ax,000fh
	imul	di,ax,size StickyBP_struc
	add	di,dataOFFSET sbrtab
EndProc

;***	ChangeBPTaskNum
;
;	Changes all BPs with the old task number to use the new task
;	number...if the new task number is -1; then the BPs are
;	disabled.
;
;	When a not-1 thread dies, the kernel calls this routine and
;	has it change the task number of all BPs set for this task to
;	that of thread 1 - this way, the BPs are still valid.  When
;	thread 1 dies, this routine marks the BP addresses as disabled.
;
;	Pcall	ChangeBPTaskNum 	; from inside debugger
;
;	ENTRY:	di = old task number
;		dx = new task number
;
;	EXIT:	NONE
;
;	USES:	ALL
;
	assumeDATA <ds, es, ss>

DefProc ChangeBPTaskNum, NEAR, PUBLIC

;	Check the GO breakpoints

	mov	cx,BPcount		; (CX) = number of GO BPs
	jcxz	ctn5			; if none, check sticky BPs

	mov	si,dataOFFSET BPtable	; (SI) -> BP under consideration

ctn2:	test	[si].BpFlag,BP_SET	; see if this BP is set
	jz	ctn4			; if not, skip to next BP

	lea	bx,[si].BpAddr		; (BX) -> this BP's addr struc

	Pcall	GetAddrType		; BUGBUG: for 386, linear may use task #
	cmp	al,EXPR_TYPE_SEL	; is it SEL:OFF type?
	jne	ctn4			; if not, skip to next BP

	mov	ax,[bx].AddrSeg
	test	al,TABLE_MASK		; is it an LDT selector?
	jz	ctn4			; if not, skip to next BP

	cmp	[bx].AddrTask,di	; is this in the old task #?
	jne	ctn4			; if not, skip to next BP

	cmp	dx,TASK_DISABLE_BPS	; does DX mean "disable"?
	jne	ctn3			; if not, go set new task number

	and	[si].BpFlag,not BP_ENABLED
	or	[si].BrkFlag,BP_BADADDR
	jmp	short ctn4		; now go to next BP

ctn3:	mov	[bx].AddrTask,dx	; set new task #

ctn4:	add	si,size BPtbl_struc	; (SI) -> next BP
	loop	ctn2			; continue until no more GO BPs

;	Check all the sticky breakpoints

ctn5:	mov	si,dataOFFSET sbrtab	; (SI) -> BP under consideration
	mov	cx,STICKY_BP_MAX	; (CX) = max number of sticky BPs

ctn6:	test	[si].BrkFlag,BP_SET	; see if this BP is set
	jz	ctn8			; if not, skip to next BP

	lea	bx,[si].BrkAddr 	; (BX) -> this BP's addr struc

	Pcall	GetAddrType		; BUGBUG: for 386, linear may use task #
	cmp	al,EXPR_TYPE_SEL	; is it SEL:OFF type?
	jne	ctn8			; if not, skip to next BP

	mov	ax,[bx].AddrSeg
	test	al,TABLE_MASK		; is it an LDT selector?
	jz	ctn8			; if not, skip to next BP

	cmp	[bx].AddrTask,di	; is this in the old task #?
	jne	ctn8			; if not, skip to next BP

	cmp	dx,TASK_DISABLE_BPS	; does DX mean "disable"?
	jne	ctn7			; if not, go change task number

	and	[si].BrkFlag,not BP_ENABLED
	or	[si].BrkFlag,BP_BADADDR
	jmp	short ctn8		; disabled; go to next BP

ctn7:	mov	[bx].AddrTask,dx	; set the new task #

ctn8:	add	si,size StickyBP_struc	; (SI) -> next BP
	loop	ctn6			; continue until no more sticky BPs
EndProc

;***	Vector
;
;	Set, clear and list the real mode and protected mode interrupt
;	vectors intercepted by the debugger.
;
;	ENTRY:	NONE
;
;	EXIT:	NONE
;
;	USES:	ALL
;

DefProc Vector, NEAR, PUBLIC

LocVar	vtr_chCmd, BYTE

BeginProc
	Pcall	GetChrI 			; Get char & advance
	mov	[vtr_chCmd],al			; save character

	Pcall	GetChrI 			; (AL) = vector type character
	jz	vtr20

	mov	cx,TRAP_RM_USED 		; (CX) = real mode vector flag
	cmp	al,"R"                          ; jump if real mode only
	je	vtr30

	mov	cx,TRAP_PM_USED 		; (CX) = prot mode vector flag
	cmp	al,"P"                          ; jump if protected mode cmd
	je	vtr30

	mov	cx,TRAP_V86_USED		; (CX) = V86 mode vector flag
	cmp	al,"V"                          ; jump if v86 mode cmd
	je	vtr30

	mov	cx,TRAP_FATAL			; (CX) = process fatal trap
	cmp	al,"F"                          ; jump if fatal trap cmd
	je	vtr30

	mov	cx,TRAP_BEEP
	cmp	al,"N"                          ; clear/set beeping (noise)
	je	vtr30

;	Do both real, v86, process fatal faults and protected mode

vtr10:	dec	[pTXT]				; go back to the character
vtr20:	mov	cx,TRAP_RM_USED 		; (CX) = real mode vector flag
	mov	al,[vtr_chCmd]			; (AL) = subcommand letter
	mov	si,dataOFFSET VectorCommands
	push	[pTXT]				; save operand pointer
	Pcall	ExecSubr
	pop	[pTXT]				; restore operand pointer

	mov	cx,TRAP_V86_USED		; CX = V86 mode vector flag
	mov	al,[vtr_chCmd]			; (AL) = subcommand letter
	mov	si,dataOFFSET VectorCommands
	push	[pTXT]				; save operand pointer
	Pcall	ExecSubr
	pop	[pTXT]				; restore operand pointer

	mov	cx,TRAP_PM_USED 		; CX = prot mode vector flag
	mov	al,[vtr_chCmd]			; (AL) = subcommand letter
	mov	si,dataOFFSET VectorCommands
	push	[pTXT]				; save operand pointer
	Pcall	ExecSubr
	pop	[pTXT]				; restore operand pointer

	mov	cx,TRAP_FATAL			; CX = prot mode vector flag
vtr30:	mov	al,[vtr_chCmd]			; (AL) = subcommand letter
	mov	si,dataOFFSET VectorCommands
	Pcall	ExecSubr
EndProc

;***	VectorList
;
;	Lists the current set interrupt vectors.
;
;	ENTRY:	(CX) = real/protected mode flag
;
;	EXIT:	NONE
;
;	USES:	ALL
;

DefProc VectorList, NEAR, LOCAL
	mov	al,'P'
	test	cx,TRAP_PM_USED
	jnz	vtl10
	mov	al,'R'
	test	cx,TRAP_RM_USED
	jnz	vtl10
	mov	al,'V'
	test	cx,TRAP_V86_USED
	jnz	vtl10
	mov	al,'F'
	test	cx,TRAP_FATAL
	jnz	vtl10
	mov	al,'N'                          ; otherwise it is beep (Noise)
vtl10:	Pcall	putchar
	Pcall	blank

;	Print the vectors that are set

	mov	dx,cx				; (DX) = flags
	xor	ax,ax				; (AX) = initial int number
	mov	cx,NUMBER_TRAPS
vtl20:	Pcall	GetTrapTblEntry, <ax>
	test	[bx].TrapTblFlag,dx		; check if set
	jz	vtl40
	Pcall	OutRadix, <ax, 0, 16, 0, 0>	; print interrupt number
	test	dl,TRAP_PM_USED
	jz	vtl30				; skip if RM display
	test	[bx].TrapTblFlag,TRAP_PM_HARD
	jz	vtl30				; if not hard skip "*" display
	push	ax
	mov	al,'*'
	Pcall	putchar
	pop	ax
vtl30:	Pcall	blank
vtl40:	inc	ax
	loop	vtl20
	Pcall	crlf				; new line
EndProc

;***	VectorTrap
;***	VectorSet
;
;	Sets a real mode or protected mode interrupt vector. The
;	'*' operand sets all the vectors.
;
;	ENTRY:	(CX) = real/protected mode flag.
;
;	EXIT:	NONE
;
;	USES:	ALL
;

DefProc VectorTrap, NEAR, LOCAL

LocVar	Flag, WORD

	test	cx,TRAP_PM_USED
	jz	VectorSet
	or	cx,TRAP_PM_HARD

Entry	VectorSet, NEAR

BeginProc
	mov	Flag,cx
vts10:	Pcall	ScanP			; skip spaces, commas
	jz	vtsx			; jump if end of line
	cmp	al,'*'                  ; set all
	je	vts30
	Pcall	EvalByte		; (DX) = interrupt number
	cmp	dl,NUMBER_TRAPS
	jae	vts20
	mov	di,dx			; (DI) = interrupt number
	test	[Flag],TRAP_PM_USED
	jz	short vts15
	Pcall	RestIdtEntry, <TRAP_PM_HARD, di>
vts15:	Pcall	SetIdtEntry, <[Flag], di>
	jmp	vts10

vts20:	jmp	perr

vts30:	Pcall	SkipB
	test	[Flag],TRAP_RM_USED
	mov	ax,NUMBER_RM_TRAPS
	jnz	vts40			; jump if RM traps
	mov	ax,NUMBER_TRAPS
vts40:	xor	di,di			; start at int 0

vts50:	push	ax
	test	[Flag],TRAP_PM_USED
	jz	short vts55
	Pcall	RestIdtEntry, <TRAP_PM_HARD, di>
vts55:	Pcall	SetIdtEntry, <[Flag], di>
	pop	ax
	inc	di
	cmp	di,ax
	jne	vts50
vtsx:
EndProc

;***	VectorClear
;
;	Clears a real mode or protected mode interrupt vector.
;
;	ENTRY:	(CX) = real/protected mode flag.
;
;	EXIT:	NONE
;
;	USES:	ALL
;

DefProc VectorClear, NEAR, LOCAL

LocVar	Flag, WORD

BeginProc
	test	cx,TRAP_PM_USED
	jz	short vtc10
	or	cx,TRAP_PM_HARD
vtc10:	mov	Flag,cx

vtc20:	Pcall	ScanP			; skip spaces, commas
	jz	vtcx			; jump if end of line
	cmp	al,'*'                  ; set all
	je	vtc40
	Pcall	EvalByte		; DX = interrupt number
	cmp	dl,NUMBER_TRAPS
	jae	vtc30
	Pcall	RestIdtEntry, <Flag, dx>
	jmp	vtc20

vtc30:	jmp	perr			; syntax error

vtc40:	Pcall	SkipB
	xor	di,di			; start at int 0
vtc50:	Pcall	RestIdtEntry, <Flag, di>
	inc	di
	cmp	di,NUMBER_TRAPS
	jne	vtc50
vtcx:
EndProc

;***	ChangeDefCmd
;
;	Change the default command executed when the debugger is
;	entered thru a trace, ptrace or breakpoint which there is
;	no breakpoint command.
;
;	ENTRY:	NONE
;
;	EXIT:	NONE
;
;	USES:	ALL
;

DefProc ChangeDefCmd, NEAR, PUBLIC
	Pcall	GetChrI 			; Get char & advance
	jz	short cdc1

;	Do the list or set of the default command

	mov	si,dataOFFSET ChgDefCommands
	Pcall	ExecSubr
	jmp	short cdc2

;	The Z command by itself executes the current default command

cdc1:	mov	si,dataOFFSET DefaultCmd
	Pcall	DoCommand
cdc2:
EndProc

;***	ChgDefList (ZL)
;
;	Print out the current default command.
;
;	ENTRY:	NONE
;
;	EXIT:	NONE
;
;	USES:	ALL
;

DefProc ChgDefList, NEAR, LOCAL
	Pcall	GetEOL
	mov	al,'"'                          ; display starting quote
	Pcall	putchar 			;

	cmp	DefaultCmd,0			; if nothing in buffer
	je	cdl1

	mov	dx,dataOFFSET DefaultCmd	; (DX) = default command ptr
	Pcall	rprbuf

cdl1:	mov	al,'"'
	Pcall	putchar
	Pcall	crlf
EndProc

;***	ChgDefSet (ZS)
;
;	Set the default command line.
;
;	ENTRY:	NONE
;
;	EXIT:	NONE
;
;	USES:	ALL
;

DefProc ChgDefSet, NEAR, LOCAL
	Pcall	ScanP
	jz	cds1
	mov	di,dataOFFSET DefaultCmd
	Pcall	ParseCommandLine
	jnc	cds2
	mov	di,dataOFFSET DefaultCmd	; if error, reset to "R" cmd
	mov	ax,"R"
	stosw
cds1:	jmp	BadCommandLine

cds2:
EndProc

;***	ExecuteIfTrue
;
;	This command will execute the command following the expression
;	if the expression is true (not zero). J <expr> 'cmd' ;
;

DefProc ExecuteIfTrue, NEAR, PUBLIC

LocVar	TempCmdBuf, <CMDLINELEN>
LocVar	TmpExpr, <size ExprS>

BeginProc
	Pcall	ScanP			; skip leading commas and spaces
	lea	si,TmpExpr
	Pcall	EvalExpr

	lea	di,TempCmdBuf
	Pcall	ParseCommandLine	; parse off command
	jc	BadCommandLine		; if bad command line

	lea	si,TmpExpr
	Pcall	GetExprS		; (BX:AX,DX) = expression value
	Pcall	IsTrue
	jnc	eit2			; if false, don't execute command

	lea	si,TempCmdBuf		; execute command
	Pcall	DoCommand

eit1:	Pcall	GetChrI
	jnz	eit1			; go back to command prompt
	or	al,al
	jz	eit2
	inc	pTXT
	jmp	short eit1

Entry	BadCommandLine

	putstr	"Bad command line"
	jmp	CmdBreak
eit2:
EndProc

;***	ParseCommandLine
;
;	Parses off a command line or string from input buffer.
;
;	Pcall	ParseCommandLine
;
;	ENTRY:	(DI) = address of buffer to put command
;
;	EXIT:	Carry clear if no error
;		Carry set if error
;
;	USES:	AX, CL, SI, DI, Flags
;

DefProc ParseCommandLine, NEAR, LOCAL
	push	di				; save buffer pointer for later
	call	ParseString			; call ParseString
	pop	si
	jc	short pcl1
	Pcall	StrUpr				; force string upper case
	clc
pcl1:
EndProc

;***	ParseString
;
;	Parses off a string from input buffer.
;
;	Pcall	ParseString
;
;	ENTRY:	(DI) = address of buffer to put command
;
;	EXIT:	(DI) = pointer end of buffer, to EOS byte
;		Carry clear if no error
;		Carry set if error
;
;	USES:	AX, CL, SI, DI, Flags
;

DefProc ParseString, NEAR, PUBLIC
	xor	cl,cl
	Pcall	ScanP				; (AL) = next character
	mov	si,pTXT 			; (SI) = ptr to command line
	jz	pst6

	Pcall	CheckString			; check for begining quote
	je	short pst1

	mov	al,";"                          ; EOS becomes the end of cmd
	dec	si

pst1:	inc	si				; skip quote
	mov	ah,al				; save quote or EOS

pst2:	lodsb					; fetch character
	or	al,al
	jz	pst5				; exit on EOL
	cmp	al,ah
	jne	pst3				; exit on EOS
	cmp	al,";"
	je	pst5				; exit if EOS is ";"
	cmp	al,[si]
	jne	pst6				; check if two quotes in a row
	inc	si

pst3:	cmp	cl,CMDLINELEN
	jae	pst4				; error if command to long
	stosb
	inc	cl
	jmp	pst2

pst4:	stc
	jmp	short pst7

pst5:	dec	si				; don't eat ";"

pst6:	mov	pTXT,si 			; fix up text pointer
	xor	al,al
	stosb					; put NULL at end of cmd line
	clc
pst7:	dec	di
EndProc

;***	CheckString
;
;	Check if a starting string character " or '.
;
;	Pcall	CheckString
;
;	ENTRY:	AL = character
;
;	EXIT:	If zero clear, not a string character
;		If zero set, the character is a " or '.
;
;	USES:	FLAGS
;

DefProc CheckString, NEAR, PUBLIC
	cmp	al,"'"
	je	short cst1
	cmp	al,'"'
cst1:
EndProc

;***	SetTmpBP - set a temporary break point
;
;	Set a temporary breakpoint.  Uses the GO bp table setting
;	the BP_TMP flag.  A GO bp doesn't have the BP_TMP set and
;	a temporary bp is a GO with the BP_TMP flag set.  A sticky
;	BP may have the BP_TMP flag set if a temporary bp is placed
;	at the same location.
;
;	ENTRY:	(BX) = pointer to AddrS structure
;		(DX) = offset from address to put breakpoint
;
;	EXIT:	NONE
;
;	USES:	FLAGS
;

DefProc SetTmpBP, NEAR, PUBLIC
	SaveReg <bx, cx, si, di>
	mov	si,dataOFFSET TempAddr
	Pcall	CopyAddr, <bx, si>
	mov	bx,si			; (BX) = temporary addr structure
	Pcall	AddAddr, <dx, 0>	; add offset into addr structure
	Pcall	ChkBPaddr
	jz	stb1			; jump if the BP already set
	Pcall	SetGoBP
	or	[si].BpFlag,BP_TMP	; mark a temporary bp
	jmp	short stbx

stb1:	jnc	stbx			; jump if go bp
	or	[si].BrkFlag,BP_TMP	; mark that a temp bp is on the sticky
stbx:	RestReg <di, si, cx, bx>
EndProc

;***	SetGoBP
;
;	Add a breakpoint to the GO breakpoint table.
;
;	ENTRY:	(BX) = pointer to AddrS structure
;
;	EXIT:	(SI) = GO BP table entry
;
;	USES:	CX, SI, DI, FLAGS
;

DefProc SetGoBP, NEAR, PUBLIC
	Pcall	ChkBPaddr
	jnz	sgb1			; jump if no BP already set
	jc	sgbx			; jump if sticky BP
	and	[si].BpFlag,not BP_TMP	; clear the temp BP bit
	jmp	short sgbx

sgb1:	cmp	BPcount,BPMAX
	jb	sgb2
	jmp	TooManyBPs

sgb2:	mov	si,BPcount
	imul	si,size BPtbl_struc
	add	si,dataOFFSET BPtable
	mov	[si].BpFlag,BP_SET	; initialize flag
	lea	di,[si].BpAddr
	Pcall	CopyAddr, <bx, di>
	inc	BPcount
sgbx:
EndProc

;***	CheckDRBP - check for DR register BP
;
;	ENTRY:	NONE
;
;	EXIT:	if carry set,
;			(SI) = address of matching entry (if one).
;			(CX) = sticky breakpoint number (if match).
;		if carry clear,
;			no DR register BP
;
;	USES:	EAX, EBX, ECX, DX, SI, DI, Flags
;

.386p
DefProc CheckDRBP, NEAR, PUBLIC

LocVar	TmpAddr, <size AddrS>

BeginProc
	test	DebData.InitFlag,INIT_CPU386
	jz	short cdbx		; jump if not 386, CARRY CLEAR

	xor	ebx,ebx
cdb1:	bt	RegSA.DR6sav,ebx	; check B0-B3 bits
	jnc	short cdb2		; jump if B bit clear
	.errnz	DR6_B0 - 0
	.errnz	DR6_B1 - 1
	.errnz	DR6_B2 - 2
	.errnz	DR6_B3 - 3

	lea	ecx,[ebx*2+1]		; (ECX) = (EBX*2)+1
	bt	RegSA.DR7sav2,ecx	; check G0-G3 bits
	jnc	short cdb2		; jump if G bit clear
	.errnz	DR7_G0 - 1
	.errnz	DR7_G1 - 3
	.errnz	DR7_G2 - 5
	.errnz	DR7_G3 - 7

	mov	eax,RegSA.DR0sav[ebx*4] ; (EAX) = linear address of DR hit
	.errnz	(DR1sav - DR0sav) - 4
	.errnz	(DR2sav - DR0sav) - 8
	.errnz	(DR3sav - DR0sav) - 12

	lea	bx,TmpAddr
	Pcall	FillLinAddr, <bx, eax>
	Pcall	ChkBPaddr
	jnz	short cdb2
	jnc	short cdb2
	test	[si].BrkDRFlag,mask DR_SET
	jnz	short cdb3		; jump to exit

cdb2:	inc	ebx
	cmp	ebx,4
	jb	short cdb1
	clc
	jmp	short cdbx

cdb3:	stc
cdbx:
EndProc
.286p

;***	CheckDRhit - check for 386 debug register hit
;
;	Returns indications when a 386 debug register is hit.
;
;	ENTRY:	NONE
;
;	EXIT:	if carry set,
;			DR register hit
;		if carry clear,
;			no DR register hit
;
	assumeDATA <ds, es, ss>

DefProc CheckDRhit, NEAR, PUBLIC
	test	DebData.InitFlag,INIT_CPU386
	jz	cdr1		; jump if not 386, CARRY CLEAR
	test	RegSA.DR7sav2.LowWord,mask DR7_G0 + mask DR7_G1 + mask DR7_G2 + mask DR7_G3
	jz	cdr1		; jump if no hit, CARRY CLEAR
	test	RegSA.DR6sav.LowWord,mask DR6_B0 + mask DR6_B1 + mask DR6_B2 + mask DR6_B3
	jz	cdr1		; jump if no hit, CARRY CLEAR
	stc			; set carry
cdr1:
EndProc

;***	ChkBPaddr - check if break point is already set
;
;	ENTRY:	(BX) = pointer to address AddrS struc
;
;	EXIT:	Zero flag set if match
;		(SI) = address of matching entry (if one).
;		(CX) = sticky breakpoint number (if match).
;
;		Carry reset if BP is in GO BP table
;		 else set if BP is in Sticky BP table
;
;	USES:	CX, SI, FLAGS
;

DefProc ChkBPaddr, NEAR, PUBLIC
	SaveReg <ax, dx, di>

;	(BX) = pointer to address being checked

	mov	cx,BPcount
	jcxz	cbp3
	mov	si,dataOFFSET BPtable

cbp1:	lea	di,[si].BpAddr
	push	bx
	Pcall	CompareAddr, <di, bx>
	pop	bx
	jc	cbp2			; jump if bad address
	jnz	cbp2			; jump if not equal (AX = 0)
	clc				; reset for GO BP table
	jmp	short cpb6

cbp2:	add	si,size BPtbl_struc
	loop	cbp1

;	(BX) = pointer to address being checked

cbp3:	mov	si,dataOFFSET sbrtab
	mov	cx,STICKY_BP_MAX

cbp4:	test	[si].BrkFlag,BP_SET
	jz	cbp5			; not set

	lea	di,[si].BrkAddr
	push	bx
	Pcall	CompareAddr, <di, bx>
	pop	bx
	jc	cbp5			; jump if bad address
	jnz	cbp5			; jump if not equal (AX = 0)

	neg	cx
	add	cx,STICKY_BP_MAX	; cx = breakpoint number
	cmp	ax,ax			; set Z flag
	stc				; set for Sticky BP table
	jmp	short cpb6

cbp5:	add	si,size StickyBP_struc
	loop	cbp4			; Z flag reset for no match

cpb6:	RestReg <di, dx, ax>
EndProc

;***	PutBPoints
;
;	Puts all the break points in the sticky and go break
;	point tables into the code.
;
;	Pcall	PutBPoints, <AllFlag>
;
;	ENTRY:	AllFlag = if TRUE, then all the BP's will be put into the
;		code even if at CS:IP. If FALSE, then any BP's at CS:IP
;		will not be put into the code.
;
;	EXIT:	NONE
;
;	USES:	AX, BX, CX, DX, SI, DI
;

DefProc PutBPoints, NEAR, PUBLIC

ArgVar	AllFlag, <BYTE>

BeginProc
	mov	cx,BPcount
	jcxz	pbp5

;	Set the GO breakpoints

	mov	si,dataOFFSET BPtable
pbp2:	cmp	AllFlag,TRUE			; check if we set all BP's
	je	pbp3

	lea	bx,[si].BpAddr
	Pcall	CompareAddr, <bx, dataOFFSET RegSA.CSIPsav>
	jc	pbp3				; jump if bad address
	jz	pbp4				; jump if BP at CS:IP

pbp3:	test	[si].BpFlag,BP_SET
	jz	pbp4				; jump if not set
	test	[si].BpFlag,BP_INMEM
	jnz	pbp4				; jump if already "in mem"

	lea	bx,[si].BpAddr			; (BX) = AddrS struct ptr
	Pcall	GetByte
	jc	pbp4				; jump if bad address

;	DS:SI = pointer to BP table
;	BX = still equals AddrS struct ptr
;	AL = intstruction byte

	mov	[si].BpInst,al			; save instruction byte
	mov	al,0cch 			; put int 3 into code
	Pcall	PutByte
	jc	pbp4				; jump if bad address
	or	[si].BpFlag,BP_INMEM		; set the "in mem" flag

pbp4:	add	si,size BPtbl_struc
	loop	pbp2

;	Set all the sticky breakpoints

pbp5:	mov	si,dataOFFSET sbrtab
	mov	cx,STICKY_BP_MAX

pbp6:	mov	al,[si].BrkFlag
	and	al,BP_SET + BP_ENABLED
	cmp	al,BP_SET + BP_ENABLED
	jne	pbp11				; Brif not set & enabled

	cmp	AllFlag,TRUE			; check if we set all BP's
	je	pbp7

	lea	bx,[si].BrkAddr 		; (BX) = offset of AddrS
	Pcall	CompareAddr, <bx, dataOFFSET RegSA.CSIPsav>
	jc	pbp7				; jump if bad address
	jz	pbp11				; jump if BP at CS:IP

pbp7:	test	[si].BrkDRFlag,mask DR_SET
	jz	pbp8				; jump if not debug register

	push	cx
	Pcall	SetDebugReg			; set debug register
	pop	cx
	jc	pbp10				; jump if error
	jmp	short pbp9

pbp8:	test	[si].BrkFlag,BP_INMEM		; if already "in mem"
	jnz	pbp11

	lea	bx,[si].BrkAddr 		; (BX) = AddrS struct ptr
	Pcall	GetByte
	jc	pbp10				; jump if bad address

;	(DS:SI) = pointer to BP table
;	(BX) = still equals AddrS struct ptr
;	(AL) = intstruction byte

	mov	[si].BrkInst,al 		; save instruction byte
	mov	al,0cch 			; put int 3 into code
	Pcall	PutByte
	jc	pbp10				; jump if bad address

	or	[si].BrkFlag,BP_INMEM		; set the "in mem" flag
pbp9:	and	[si].BrkFlag,not BP_BADADDR	; clear the bad address bit
	jmp	short pbp11

pbp10:	or	[si].BrkFlag,BP_BADADDR 	; mark the address as bad

pbp11:	add	si,size StickyBP_struc
	loop	pbp6
EndProc

;***	SetDebugReg - sets the debug registers
;
;	Sets the debug register for the specific breakpoint.
;
;	ENTRY:	(SI) = pointer to breakpoint table entry
;
;	EXIT:	if carry clear, debug register set
;		if carry set, bad address error
;
;	USES:	AX, BX, CX, DX, Flags
;

DefProc SetDebugReg, NEAR, LOCAL
	xor	ch,ch
	mov	cl,[si].BrkDRFlag	; (CL) = debug register flags
	and	cl,mask DR_NUM
	shr	cl,DR_NUM		; (CX) = debug register number

;	Convert the virtual address to a linear address

	push	cx
	lea	bx,[si].BrkAddr
	mov	cx,1
	Pcall	AddrToLin		; (AX:DX) = linear address
	pop	cx
	jc	short sdrx		; jump if error

;	Put linear address in appropriate debug register

	mov	bx,cx			; (BX) = debug reg num
	shl	bx,2			; (BX) = debug reg num * 4

	mov	RegSA.DR0sav[bx].HighWord,ax
	mov	RegSA.DR0sav[bx].LowWord,dx
	.errnz	(DR1sav - DR0sav) - 4
	.errnz	(DR2sav - DR0sav) - 8
	.errnz	(DR3sav - DR0sav) - 12

;	Clear the status bit for this debug register

	mov	al,not (mask DR6_B0)
	.errnz	DR6_B0 - 0
	.errnz	DR6_B1 - 1
	.errnz	DR6_B2 - 2
	.errnz	DR6_B3 - 3
	rol	al,cl			; (AL) = DR6_B0 << debug reg num
	and	RegSA.DR6sav.LowWord.LowByte,al

;	Set appropriate G0-G3 flag and the DR7_GE flag.

	shl	cx,1			; (CX) = debug reg num * 2

	or	RegSA.DR7sav.LowWord,(mask DR7_GE)
	mov	al,mask DR7_G0
	.errnz	mask DR7_G0 - 00000010b
	.errnz	mask DR7_G1 - 00001000b
	.errnz	mask DR7_G2 - 00100000b
	.errnz	mask DR7_G3 - 10000000b
	shl	al,cl			; (AL) = DR7_G0 << (debug reg num * 2)
	or	RegSA.DR7sav.LowWord.LowByte,al

;	Clear the type field

	mov	cx,bx			; (CX) = debug register number * 4

	mov	ax,not (mask DR0_TYPE)
	rol	ax,cl			; (AX) = DR0_TYPE << (debug reg num * 4)
	and	RegSA.DR7sav.HighWord,ax

;	Set the type field

	xor	ah,ah
	mov	al,[si].BrkDRFlag	; (AX) = debug register flags
	mov	bx,ax			; (BX) = debug register flags
	and	ax,mask DR_TYPE 	; (AX) = type field
	.errnz	DR_TYPE - DR0_TYPE
	shl	ax,cl			; (AX) = DR0_TYPE << (debug reg num * 4)
	.errnz	DR0_TYPE - 0
	.errnz	DR1_TYPE - 4
	.errnz	DR2_TYPE - 8
	.errnz	DR3_TYPE - 12
	or	RegSA.DR7sav.HighWord,ax
	.errnz	DR_TYPE_EXECUTE - DR7_TYPE_EXECUTE
	.errnz	DR_TYPE_WRITE - DR7_TYPE_WRITE
	.errnz	DR_TYPE_IO - DR7_TYPE_IO		; 148688
	.errnz	DR_TYPE_RW - DR7_TYPE_RW

;	Clear the length field

	mov	ax,not (mask DR0_LEN)
	rol	ax,cl			; (AX) = DR0_LEN << (debug reg num * 4)
	and	RegSA.DR7sav.HighWord,ax

;	Set the length field

	mov	ax,bx			; (AX) = debug register flags
	and	ax,mask DR_LEN		; (AL) = type field
	.errnz	DR_LEN - DR0_LEN
	shl	ax,cl			; (AX) = DR0_LEN << (debug reg num * 4)
	.errnz	DR0_LEN - 2
	.errnz	DR1_LEN - 6
	.errnz	DR2_LEN - 10
	.errnz	DR3_LEN - 14
	or	RegSA.DR7sav.HighWord,ax ; Carry CLEARED by "or" instruction
	.errnz	DR_LEN_1 - DR7_LEN_1
	.errnz	DR_LEN_2 - DR7_LEN_2
	.errnz	DR_LEN_4 - DR7_LEN_4

sdrx:
EndProc

;***	RestoreBPoints
;
;	Puts the instructions back in the breakpoints.
;
;	ENTRY:	NONE
;
;	EXIT:	NONE
;
;	USES:	AX, BX, CX, DX, SI, DI
;
;	WARNING: Does not clear the GO breakpoint table any
;		 more. The "BPcount" variable needs to be
;		 cleared when the table is to be cleared.
;

DefProc RestoreBPoints, NEAR, PUBLIC
	mov	si,dataOFFSET BPtable
	mov	cx,BPcount
	jcxz	rbp4

;	Remove all the GO breakpoints

rbp2:	test	[si].BpFlag,BP_INMEM		; see if BP is in memory
	jz	rbp3

;	(SI) = pointer to BP table

	mov	al,[si].BpInst			; get Instruction byte
	lea	bx,[si].BpAddr			; (BX) = AddrS struct ptr
	Pcall	PutByte
	jc	rbp3				; jump if bad address
	and	[si].BpFlag,not BP_INMEM	; clear flag

rbp3:	add	si,size BPtbl_struc
	loop	rbp2

;	Remove all the sticky breakpoints

rbp4:	mov	si,dataOFFSET sbrtab
	mov	cx,STICKY_BP_MAX

rbp5:	test	[si].BrkDRFlag,mask DR_SET
	jz	rbp6				; jump if not debug register

;	Get the debug register number

	xor	ch,ch
	mov	cl,[si].BrkDRFlag		; (CL) = debug register flags
	and	cl,mask DR_NUM
	shr	cl,DR_NUM			; (CL) = debug reg number
	shl	cx,1				; (CX) = debug reg num * 2

;	Clear the G0-G3 bit for the debug register

	mov	al,not (mask DR7_G0)
	.errnz	mask DR7_G0 - 00000010b
	.errnz	mask DR7_G1 - 00001000b
	.errnz	mask DR7_G2 - 00100000b
	.errnz	mask DR7_G3 - 10000000b
	rol	al,cl				; (AL) = DR7_G0 << (reg num * 2)
	and	RegSA.DR7sav.LowWord.LowByte,al

	push	cx
	mov	cx,1
	lea	bx,[si].BrkAddr 		; (BX) = AddrS struct ptr
	Pcall	AddrToLin			; validate the address
	pop	cx
	jc	rbp7				; jump if error
	jmp	short rbp8

rbp6:	test	[si].BrkFlag,BP_INMEM		; check if BP is set in the code
	jz	rbp9

;	(SI) = pointer to Sticky BP table

	mov	al,[si].BrkInst 		; get Instruction byte
	lea	bx,[si].BrkAddr 		; (BX) = AddrS struct ptr
	Pcall	PutByte
	jnc	rbp8				; jump if address ok

rbp7:	or	[si].BrkFlag,BP_BADADDR 	; mark the address as bad
	jmp	short rbp10			; aways clear the tmp bit

rbp8:	and	[si].BrkFlag,not (BP_INMEM + BP_BADADDR)

rbp9:	test	[si].BrkFlag,BP_TMP
	jz	rbp11

;	Clear BP_TMP if not the a sticky BP at the current CS:IP

	lea	bx,[si].BrkAddr
	Pcall	CompareAddr, <bx, dataOFFSET RegSA.CSIPsav>
	jc	rbp11				; jump if bad address
	jz	rbp11				; jump if address's are equal

rbp10:	and	[si].BrkFlag,not BP_TMP 	; clear tmp flag if not current

rbp11:	add	si,size StickyBP_struc
	loop	rbp5
EndProc

CodeSegmentEnd

	end
