;       SCCSID = @(#)debmac.inc	6.1 90/11/17
;***	DEBMAC.INC
;
;	This file contains the macros used by the
;	kernal debugger.
;
;	SCCSID = @(#)debmac.inc	13.9 90/08/24
;
	.xall

;***	BREAK
;
;	This macro breaks the listing and changes the sub title
;

BREAK	macro	subtitle
	subttl	subtitle
	page
	endm
.xcref	BREAK

;***	POPFF - special version of pop flags for i286
;
;	This avoids the pitfalls of the 286 POPF instruction
;	which will sometimes allow an interrupt to come in
;	even if IF is off in both the old and new flags.
;

POPFF	macro
	push	cs
ifdef CRSLATER
	Pcall	PopFlags
else
        popf
endif
	endm

;***	Jump
;
;	Jump to an external entry point.  It is a
;	near jump.
;

Jump	macro	name
.xcref
	if2
		ifndef	name
			extrn	_&name:NEAR
			name	equ	_&name
		endif
	endif
	jmp	name
	endm
	.xcref	Jump

;***	Pcall
;
;	Call a procedure that may be external.  The call
;	will be near.	Adds an underscore to name.  The argument
;	order is "C" convention, but the callie must remove
;	the arguments (with RET n via the EndProc macro).
;
;	This macro can correctly accept 32 bit registers
;	as arguments.
;

Pcall	macro	name, arglst
.xcref
	if2
		ifndef	name
			extrn	_&name:NEAR
			name	equ	_&name
		endif
	endif
	.cref
	?arg	<arglst>
	call	name
	endm
	.xcref	Pcall

;***	Ccall
;
;	Call a "C" procedure that may be external. The call
;	will be near. Adds an underscore to name. Uses "C"
;	argument pushing convention and removes the arguments
;	after the call.
;
;	This macro can correctly accept 32 bit registers
;	as arguments.
;
;	USES: Flags.

Ccall	macro	name, arglst
.xcref
	if2
		ifndef	name
			extrn	_&name:NEAR
			name	equ	_&name
		endif
	endif
	.cref
	?arg	<arglst>
	call	name
	if	?argc ne 0
		add	sp,?argc * 2
	endif
	endm
	.xcref	Ccall

; Processes argument list - used by Pcall/Ccall only

?arg	macro	arglst
	?argc = 0			;; number of words on stack (global)
	?numpush = 0			;; number of objects to push
	irp	x,<arglst>
		?dwordarg = FALSE
		irp	reg,<eax,ebx,ecx,edx,ebp,esp,esi,edi>
			ifidni	<x>,<reg>
				?dwordarg = TRUE
				exitm
			endif
		endm
		?argc = ?argc + 1
		if ?dwordarg
			?argc = ?argc + 1
		endif
		?numpush = ?numpush + 1
		?marg	<x>,%?numpush
	endm
	?count = ?numpush
	rept	?numpush
		?invprg <?AM>,%?count
		?count = ?count - 1
	endm
	endm
	.xcref	?arg

; Makes a macro that will push argment when invoke - used by Pcall/Ccall only

?marg	macro	name, num
	.xcref
	.xcref	?AM&num
	.cref
	?AM&num &macro
		push	name
		&endm
	endm
	.xcref	?marg

; Concatenates, invokes and purges a macro name

?invprg macro	name1, name2
	name1&name2
	purge	name1&name2
	endm
	.xcref	?invprg

;***	SaveReg
;
;	Save a list of registers
;

SaveReg macro	reglist		;; push those registers
	irp reg,<reglist>
		?stackdepth = ?stackdepth + 1
		push	reg
	endm
	endm
	.xcref	SaveReg

;***	RestReg
;
;	Restores a list of registers
;

RestReg	 macro	 reglist	;; pop those registers
	irp reg,<reglist>
		?stackdepth = ?stackdepth - 1
		pop reg
	endm
	endm
	.xcref	RestReg

RestoreReg macro reglist	;; because we replace basemaca's Savereg,
	RestReg <reglist>	;; we must also replace basemaca's RestoreReg
	endm
	.xcref	RestoreReg


;***	Entry
;
;	Define an public entry in a procedure.	Adds an
;	underscore to name.
;

Entry	macro name, distance, pub, far16
_&name	label	near
name	equ	_&name
	GenPub	name, pub
	ifidni	<far16>,<FAR16>
f__&name:
	public	f__&name
	endif
	endm
	.xcref	Entry

;***	DefProc
;
;	Defines a procedure name, if it's near or far, public
;	or private. It will add an underscore to the begin of the
;	name.
;

.xcref	?frame
.xcref	?aframe
.xcref	?initaframe
.xcref	?stackdepth
.xcref	?initstack
.xcref	?needframe
?frame	    =  0	; argument local frame size
?aframe	    =  0	; argument frame size
?initaframe =  0	; initial aframe
?stackdepth =  0	; stack size
?initstack  =  0	; stack size
?needframe  =  0	; if not zero then need to do enter/leave

DefProc macro	name, distance, pub, frame
	local	pnam
	?frame		= 0
	?needframe	= 0		;; no stack frame yet
	?cframe		= 0		;; no "C" frame
	?far16		= FALSE

	ifidni	<distance>,<FAR>
		?initaframe = 6
	else
	ifidni	<distance>,<FAR16>
		?initaframe = 6
	else
		?initaframe = 4
	endif
	endif
	?aframe = ?initaframe

	ifidni	<frame>,<C>
		?cframe	= 1
	endif
	?initstack = ?stackdepth	;; beginning of procedure

	ifidni	<distance>,<FAR16>

f__&name proc	far
	?procnam &macro
f__&name endp
	&endm
name	equ	f__&name
	public	f__&name
	?far16	= TRUE

	else

_&name	proc	distance
	?procnam &macro
_&name	endp
	&endm
name	equ	_&name
	GenPub	name, pub

	endif

	ifidni	<frame>,<FAR16>
f__&name:
	public	f__&name
	endif
	endm
	.xcref	DefProc

;***	BeginProc
;
;	Setups up the stack frame for stack based local
;	variables and parameters. It will only set up a
;	stack frame prologue if there is either some local
;	variables or arguments. This macro is need unless
;	there some stack arguments or locals.
;

BeginProc   macro
	if ?needframe NE 0
		if ?frame EQ 0
			push	bp	;; use this if no local variables
			mov	bp,sp
		else
			enter	?frame, 0
		endif
	endif
	endm
	.xcref	BeginProc

;***	EndProc
;
;	End and exit a procedure and check that stack
;	depth is preserved. It will only put the stack
;	frame epilogue if there were any local or argument
;	variables.
;

EndProc macro	chk
	ifdifi	<chk>,<NoCheck>				;; check the stack size
		if2
			if ?initstack NE ?stackdepth	;; is it different?
				%out *** Possible stack size error  ***
				.err
				exitm
			endif
		endif
	endif
	ifdifi	<chk>,<NoGen>
		if	?needframe NE 0
			leave				;; use 286 instruction
			if	?cframe NE 0
				ret
			else
				if	?aframe GT ?initaframe
					ret	?aframe - ?initaframe
				else
					ret
				endif
			endif
		else
			if	?far16
				db	OPER_OVERRIDE
				retf
			else
				ret
			endif
		endif
	endif
	?procnam
	purge ?procnam
	endm
	.xcref	EndProc

;***	LocVar
;
;	Declares local stack variables
;

LocVar	macro	name, length, struc
	local	a
	?needframe = 1				;; flag that we will need frame
	ifidni	<length>,<BYTE>
		?frame = ?frame + 2
		a = ?frame
		name equ (byte ptr [bp-a])
	else
	ifidni	<length>,<WORD>
		?frame = ?frame + 2
		a = ?frame
		name equ (word ptr [bp-a])
	else
	ifidni	<length>,<DWORD>
		?frame = ?frame + 4
		a = ?frame
		name equ (dword ptr [bp-a])
	else
	ifidni	<length>,<FWORD>
		?frame = ?frame + 8
		a = ?frame
		name equ (FWORDPTR [bp-a])
	else
	ifidni	<length>,<QWORD>
		?frame = ?frame + 8
		a = ?frame
		name equ (qword ptr [bp-a])
	else
	ifidni	<length>,<STRUC>
		?frame = ?frame + (size struc)
		a = ?frame
		name equ (byte ptr [bp-a])
		if ?frame and 1
			?frame = ?frame + 1
		endif
	else
		?frame =  ?frame + length
		a = ?frame
		name equ (byte ptr [bp-a])
		if ?frame and 1
			?frame = ?frame + 1
		endif
	endif	; STRUC
	endif	; QWORD
	endif	; FWORD
	endif	; DWORD
	endif	; WORD
	endif	; BYTE
	endm
	.xcref	LocVar

;***	ArgVar
;
;	Defines a stack based parameters.
;

ArgVar	macro	name, length, struc
	local	a
	?needframe = 1				;; flag that we will need frame
	ifidni	<length>,<BYTE>
		a = ?aframe
		?aframe = ?aframe + 2
		name equ (byte ptr [bp+a])
	else
	ifidni	<length>,<WORD>
		a = ?aframe
		?aframe = ?aframe + 2
		name equ (word ptr [bp+a])
	else
	ifidni	<length>,<DWORD>
		a = ?aframe
		?aframe = ?aframe + 4
		name equ (dword ptr [bp+a])
	else
	ifidni	<length>,<FWORD>
		a = ?aframe
		?aframe = ?aframe + 8		;; always exspect 8 bytes
		name equ (FWORDPTR [bp+a])
	else
	ifidni	<length>,<QWORD>
		a = ?aframe
		?aframe = ?aframe + 8
		name equ (qword ptr [bp+a])
	else
	ifidni	<length>,<STRUC>
		a = ?aframe
		?aframe = ?aframe + (size struc)
		name equ (byte ptr [bp+a])
		if ?aframe and 1
			?aframe = ?aframe + 1
		endif
	else
		a = ?aframe
		?aframe = ?aframe + length
		name equ (byte ptr [bp+a])
		if ?aframe and 1
			?aframe = ?aframe + 1
		endif
	endif	; STRUC
	endif	; QWORD
	endif	; FWORD
	endif	; DWORD
	endif	; WORD
	endif	; BYTE
	endm
	.xcref	ArgVar

;***	Extern
;
;	This macro defines a external data or procedure
;	reference. It will add an underscore to the name
;	and equate the name without the underscore to the
;	underscored name.
;

Extern	macro	name, length
	extrn	_&name:length
name	equ	_&name
	endm
	.xcref	Extern

;***	Global
;
;	Defines and initializes public data.
;

Global	macro	name, length, init, sinit
	Static	<_&name>, <length>, <init>, <sinit>, NOGEN
	GenPub	name, PUBLIC
name	equ	_&name
	endm
	.xcref	Global

;***	Static
;
;	Defines and initializes local data.
;

Static	macro	name, length, init, sinit, scope
	ifb	<scope>
		GenPub	name, NOGEN
	else
		GenPub	name, scope
	endif
	ifidni	<length>,<BYTE>
		name	db	init
	else
	ifidni	<length>,<WORD>
		name	dw	init
	else
	ifidni	<length>,<DWORD>
		name	dd	init
	else
	ifidni	<length>,<FWORD>
		name	df	init
	else
	ifidni	<length>,<QWORD>
		name	dq	init
	else
	ifidni	<length>,<STRING>
		name	db	init, 0
	else
	ifidni	<length>,<STRUC>
		name	init	<sinit>
	else
	ifidni	<length>,<LABEL>
		name	label	init
	else
		name	db	length dup(init)
	endif	; LABEL
	endif	; STRUC
	endif	; STRING
	endif	; QWORD
	endif	; FWORD
	endif	; DWORD
	endif	; WORD
	endif	; BYTE
	endm
	.xcref	Static

;**	GenPub - generate a public symbol
;
;	Generate a public symbol in all cases, but first pervert the
;	name if it really is a local symbol.  For symbolic debugger
;	support only.
;

?deblcnt = 0
GenPub	macro name,scope		;; generate a public or local symbol
	local	pub
	pub = TRUE
	ifidni	<scope>,<LOCAL>
		ifndef	NOLOCAL
			pub = FALSE
		endif
	else
	ifidni	<scope>,<NOGEN>
		pub = FALSE
	endif ; NOGEN
	endif ; LOCAL
	if pub
		public _&name
	else
	        ifdef DEBUGLOCAL
			ifnb	<name>
				?GenLoc %?deblcnt,name
				?deblcnt = ?deblcnt + 1
			endif
		endif
	endif
	endm

?GenLoc macro cnt,name			;; generate a public local symbol
_l&cnt&_&name	label	byte
	public	_l&cnt&_&name
	endm

;***	putstr
;
;	Puts a string on console
;

putstr	macro	str
	local	strptr
DataSegmentStart ,noassume
strptr	db	str, 0
DataSegmentEnd
	Pcall	prtstr, <dataOFFSET strptr>
	endm

;***	printf
;
;	Prints a formated string on console.
;
;	This macro can correctly accept 32 bit registers
;	as arguments.
;

printf	macro	str, args
	local	strptr
DataSegmentStart ,noassume
strptr	db	str, 0
DataSegmentEnd
	ifb	<args>
	Ccall	prntf, <dataOFFSET strptr>
	else
	Ccall	prntf, <dataOFFSET strptr, args>
	endif
	endm

;***	debprt
;
;	Prints a formated string on console for debugger debugging.
;
;	This macro can correctly accept 32 bit registers
;	as arguments.
;

debprt	macro	str, args
	local	strptr
	ifdef	DEBUG
DataSegmentStart ,noassume
strptr	db	str, 0
DataSegmentEnd
	pushf
	ifb	<args>
	Ccall	dprntf, <dataOFFSET strptr, 0>
	else
	Ccall	dprntf, <dataOFFSET strptr, (?argc - 2) * 2, args>
	endif
	POPFF
	endif	; DEBUG
	endm

;***	loadall
;
;	The macro defines the loadall instruction
;

loadall macro
	db	0fh, 05h
	endm
