COMMENT @----------------------------------------------------------------------

	Copyright (c) GeoWorks 1989 -- All Rights Reserved

PROJECT:	PC GEOS
FILE:		ec.def

DECLARER:	Kernel

MACROS:
	Name		Description
	----		-----------
	ERROR		call FatalError with an error number
	ERROR_C		call FatalError if carry flag set
	ERROR_NC	call FatalError if carry flag not set
	ERROR_Z		call FatalError if zero flag set
		(etc.; one ERROR_ for every conditional jump instruction)
	WARNING		Have Swat display a warning
	WARNING_C	Display warning if carry set
	WARNING_NC	Display warning if carry not set
		(etc.; one WARNING_ for every conditional jump instruction)
	FALL_THRU	Fall through to the given routine
	REAL_FALL_THRU	Real FALL_THRU macro that produces no code.
	GOTO		Jump to another routine
	FALL_THRU_POP	Pop registers for gross routines that expect
			registers on the stack.
	EC		Include the current line in error-check version only
	EEC		Include temporary error checking code
	NEC		Include code when NOT error checking
	LONG		Long conditional jump
	EC_LONG		Long conditional jump, in error checking version only

DESCRIPTION:
	This file defines error checking structures and routines.

	$Id: ec.def,v 1.1 97/04/04 14:23:14 newdeal Exp $

------------------------------------------------------------------------------@

SetDef	__EC

;
; FatalErrors is an enumerated type into which named error codes are placed.
; The members placed in the type should be accessible to all modules of a
; patient or be assigned ranges by the programmer. That makes no difference
; to Swat. NOTE: definition must lie outside the StartKernel/EndKernel bounds
; if Swat is to receive the type unmolested
;
FatalErrors etype word, 0
					; Errors global to entire system
					; here (errors used inside of macros)
CAN_NOT_USE_CHUNKSIZEPTR_MACRO_ON_EMPTY_CHUNKS			enum FatalErrors
CHUNK_ARRAY_BAD_ELEMENT						enum FatalErrors
MACRO_REQUIRES_FIXED_SIZE_ELEMENTS				enum FatalErrors
CANNOT_USE_DBCS_IN_THIS_VERSION					enum FatalErrors
; Double Byte characters (DBCS) are not supported in this version of PC/GEOS

;
; Warnings is similar in declaration and function to FatalErrors, but is for
; places where the error by the programmer isn't immediately fatal. It will
; only be caught and printed if the programmer is attached with Swat.
; 
Warnings etype word, 0
ContextValues	etype	word, 0

StartKernel

;
; FatalError is a near routine for the kernel. The far routine AppFatalError
; is actually exported by the kernel as FatalError, to avoid giving the linker
; fits.
;
global FatalError:appfar
global WarningNotice:far
global CWARNINGNOTICE:far

global ECCheckFileHandle:far
global ECCheckMemHandle:appfar
global ECCheckMemHandleNS:appfar
global ECCheckThreadHandle:appfar
global ECCheckProcessHandle:far
global ECCheckResourceHandle:far
global ECCheckGeodeHandle:far
global ECCheckDriverHandle:far
global ECCheckLibraryHandle:far
global ECCheckGStateHandle:far
global ECCheckWindowHandle:far
global ECCheckQueueHandle:far
global ECCheckEventHandle:far
global ECCheckLMemHandle:far
global ECCheckLMemHandleNS:far
global ECLMemValidateHeap:appfar
global ECLMemValidateHandle:far
global ECCheckLMemChunk:far
global ECMEMVERIFYHEAP:far
global ECLMemExists:far
global ECCheckChunkArray:far

global ECCheckClass:far
global ECCheckObject:far
global ECCheckLMemObject:far
global ECCheckOD:far
global ECCheckLMemOD:far
global ECCheckSegment:far
global ECCheckSegments:far
global ECCHECKSTACK:far
global ECVMCheckVMFile:far
global ECVMCheckVMBlockHandle:far
global ECVMCheckMemHandle:far
global ECCheckBounds:far
global ECAssertValidFarPointerXIP:far

ECMemVerifyHeap	equ	ECMEMVERIFYHEAP
ECCheckStack	equ	ECCHECKSTACK

;-

global CFATALERROR:far
global ECCHECKFILEHANDLE:far
global ECCHECKMEMHANDLE:far
global ECCHECKMEMHANDLENS:far
global ECCHECKTHREADHANDLE:far
global ECCHECKPROCESSHANDLE:far
global ECCHECKRESOURCEHANDLE:far
global ECCHECKGEODEHANDLE:far
global ECCHECKDRIVERHANDLE:far
global ECCHECKLIBRARYHANDLE:far
global ECCHECKGSTATEHANDLE:far
global ECCHECKWINDOWHANDLE:far
global ECCHECKQUEUEHANDLE:far
global ECCHECKEVENTHANDLE:far
global ECCHECKLMEMHANDLE:far
global ECCHECKLMEMHANDLENS:far
global ECLMEMVALIDATEHEAP:far
global ECLMEMVALIDATEHANDLE:far
global ECCHECKLMEMCHUNK:far
global ECLMEMEXISTS:far
global ECCHECKCHUNKARRAY:far

global ECCHECKCLASS:far
global ECCHECKOBJECT:far
global ECCHECKLMEMOBJECT:far
global ECCHECKOD:far
global ECCHECKLMEMOD:far
global ECVMCHECKVMFILE:far
global ECVMCHECKVMBLOCKHANDLE:far
global ECVMCHECKMEMHANDLE:far
global ECCHECKBOUNDS:far

;-----------------------------------------------------------------------------
;			 Error Checking Stuff
;-----------------------------------------------------------------------------
global SysGetECLevel:far
global SysSetECLevel:far


; we keep these around for purposed of the upgrade/wizrd and zoomer
; that went out with the old set of EC flags
OldErrorCheckingFlags	record
    OECF_REGION:1		;Region checking
    OECF_HEAP_FREE_BLOCKS:1	;Ensure that all free blocks are 0xcccc
    OECF_LMEM_INTERNAL:1		;Internal lmem checking
    OECF_LMEM_FREE_AREAS:1	;Ensure that all free areas are 0xcccc
    OECF_LMEM_OBJECT:1		;Consistency checks on objects in lmem chunks
    OECF_BLOCK_CHECKSUM:1	;Checksum on a particular block
    OECF_GRAPHICS:1		;Misc graphics stuff
    OECF_SEGMENT:1		;Extensive segment checking
    OECF_NORMAL:1		;Misc kernel error checking
    OECF_VMEM:1			;VM file consistency
    OECF_APP:1			;Application error checking (if implemented
				;by applications)
    OECF_LMEM_MOVE:1		;Force lmem blocks to move whenever possible
    OECF_UNLOCK_MOVE:1		;Force unlocked blocks to move
    OECF_VMEM_DISCARD:1		;Force clean VM blocks to be discarded
    OECF_ANAL_VMEM:1		;Extensive VM error checking
    OECF_TEXT:1
OldErrorCheckingFlags	end

ErrorCheckingFlags	record
    ECF_UNUSED_1:1	; these 3 bits are resrved for future use, they are
    ECF_UNUSED_2:1	; declared individulally so fmtrecord prints them
    ECF_ANAL_VMEM:1	; extensize VMEM checking ;         out nicely...
    ECF_FREE:1		;Ensure that all free blocks are 0xcccc
    ECF_HIGH:1			; lot of random extra checking (old NORMAL)
    ECF_LMEM:1			;Internal lmem checking
    ECF_BLOCK_CHECKSUM:1	;Checksum on a particular block
    ECF_GRAPHICS:1		;Misc graphics stuff
    ECF_SEGMENT:1		;Extensive segment checking
    ECF_NORMAL:1		;Misc kernel error checking
    ECF_VMEM:1			;VM file consistency
    ECF_APP:1			;Application error checking (if implemented
				;by applications)
    ECF_LMEM_MOVE:1		;Force lmem blocks to move whenever possible
    ECF_UNLOCK_MOVE:1		;Force unlocked blocks to move
    ECF_VMEM_DISCARD:1		;Force clean VM blocks to be discarded
    ECF_TEXT:1
ErrorCheckingFlags	end

DEFAULT_EC	equ	<>

COMMENT @----------------------------------------------------------------------

	ERROR	errorNumber

FUNCTION:
	Calls FatalError with an error code
ARGUMENTS:
	errorNumber -- error code
DESTROYS:
	The System
EXPANDS TO:
	5 bytes (kernel::kcode)
	7 bytes (kernel::kinit)
	8 bytes (application)

------------------------------------------------------------------------------@
ERROR	macro	errorNumber
ifdef _Kernel
 if @CurSeg ne kcode
 	call	AppFatalError
 else
 	call	FatalError
 endif
else
	call	FatalError
endif
	mov	ax,errorNumber		; NOT "word errorNumber" so listing
					;  continues to work...

	.UNREACHED
endm


COMMENT @----------------------------------------------------------------------

	ERROR_C		errorNumber
	ERROR_NC	errorNumber
		etc.; for every condidional jump instruction jfoo, there
		is a corresponding macro ERROR_FOO


FUNCTION:
	Calls FatalError if the specified condition holds
ARGUMENTS:
	errorNumber -- error code
DESTROYS:
	If error condition occurs: 
		The System
	Otherwise:
		Nothing (flags preserved)
EXPANDS TO:
	7 bytes (kernel)
	8 bytes (application)

PSEUDO-CODE_STRATEGY:
	Each of these macros expands in the same way. As an example,
	ERROR_C has the following declaration:

	ERROR_C	macro errorNumber
		local	notError
		jnc	notError
		ERROR	errorNumber
	notError:
	endm

	The other macros expand in precisely analogous ways.

------------------------------------------------------------------------------@
__deferr	macro	suffix, branch
    ERROR_&suffix macro errorNumber
	local 	notError
	branch 	notError
	ERROR	errorNumber
notError:
		endm
	endm

irp	er, <C jnc, NC jc, Z jnz, NZ jz, E jne, NE je, A jna, NA ja, AE jnae, NAE jae, B jnb, NB jb, BE jnbe, NBE jbe, G jng, NG jg, GE jnge, NGE jge, L jnl, NL jl, LE jnle, NLE jle, S jns, NS js, NP jp, P jnp, PE jpo, PO jpe, O jno, NO jo>
	__deferr er
	endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
		WARNING
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SYNOPSIS:	Similar to the ERROR macro, but this one doesn't cause
		death, it just makes Swat print out a message when it's
		hit, as an indication that the programmer has done something
		wrong, but not immediately fatal

PASS:		errorNumber	= error number from the Warnings enumerated
				  type.


KNOWN BUGS/SIDE EFFECTS/IDEAS:

REVISION HISTORY:
	Name	Date		Description
	----	----		-----------
	ardeb	10/25/92	Initial version

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
WARNING	macro	warningNumber
	call	WarningNotice
	mov	ax,warningNumber	; Warning will skip over this
					;  instruction, but it needs to be
					;  an instruction so listing
					;  this code doesn't cause confusion
					;  on Swat's part
	endm



COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
		WARNING_C, etc.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SYNOPSIS:	Conditional warning macro, analagous to the ERROR_C
		(etc.) macros. Each WARNING_ macro causes Swat to
		display a warning if the appropriate flags are set;
		e.g. WARNING_C displays a warning if CF is set.

		There is one WARNING_ instruction to correspond to
		every conditional jump instruction (except jcxz).

PASS:		errorNumber	= error number from the Warnings enumerated
				  type.

PSEUDO-CODE_STRATEGY:
	Each of these macros expands in the same way. As an example,
	WARNING_C has the following declaration:

	WARNING_C	macro errorNumber
		local	noWarning
		jnc	noWarning
		WARNING	errorNumber
	noWarning:
	endm

	The other macros expand in precisely analogous ways.

REVISION HISTORY:
	Name	Date		Description
	----	----		-----------
	ardeb	10/25/92	Initial version
	AMS	3/21/93   	Added missing macros

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@

__defwarn	macro	suffix, branch
    WARNING_&suffix macro errorNumber
	local 	noWarning
	branch 	noWarning
	WARNING	errorNumber
    noWarning:
		endm
	endm

irp	w, <C jnc, NC jc, Z jnz, NZ jz, E jne, NE je, A jna, NA ja, AE jnae, NAE jae, B jnb, NB jb, BE jnbe, NBE jbe, G jng, NG jg, GE jnge, NGE jge, L jnl, NL jl, LE jnle, NLE jle, S jns, NS js, NP jp, P jnp, PE jpo, PO jpe, O jno, NO jo>
	__defwarn w
	endm



; non-error checking with backtracing macros
ifndef NONEC_WITH_BACKTRACE
	NONEC_WITH_BACKTRACE equ	0
endif


COMMENT @----------------------------------------------------------------------

	FALL_THRU	routineName, r1, r2, r3, r4, r5, r6
	REAL_FALL_THRU	routineName
	GOTO		routineName, r1, r2, r3, r4, r5, r6

	FUNCTION:
		Fall through to the given routine. If ERROR_CHECK is defined
		and non-zero, this generates a call and return to aid Swat
		in decoding the stack. Otherwise, it generates no code but
		does a check to ensure that the routine is actually immediately
		following. Use REAL_FALL_THRU if you can't afford the call/ret,
		but Swat won't be able to decode the stack through that 
		function.
		
		The r1..r6 args are used if there are registers on the stack
		that the fallen-into routine is expected to pop in a non-error-
		checking version. The FALL_THRU_POP macro should be used before
		the return from the fallen-into routine. The registers are
		popped in the order given.
		
		GOTO is used in place of a jmp into another procedure.
		routineName and r1..r6 are as for the FALL_THRU.
		
		In both the FALL_THRU and the GOTO, the assertion is made that
		the destination routine has the same return type as the
		current, as it must if the destination is to return for
		the current procedure.

------------------------------------------------------------------------------@
FALL_THRU	macro	routineName, r1, r2, r3, r4, r5, r6
	if ERROR_CHECK
		call	routineName
		DoPop	<r1>, <r2>, <r3>, <r4>, <r5>, <r6>
		ret
	endif
		REAL_FALL_THRU routineName
	.assert	TYPE @CurProc eq TYPE routineName, <FALL_THRU to routine with different return type>
	.assert	not (((.type routine) and 0x4001) eq 0x4001), <FALL_THRU to routineName but routineName is a dynamic method>
endm

REAL_FALL_THRU	macro	routineName, dontTellEsp?
ifb <dontTellEsp?>
	.fall_thru	routineName
else
	.assert	$ EQ routineName, <Cannot fall through to routineName>
endif
endm


GOTO_LOW macro routineName
	;
	; Complex expression required to handle GOTO through a vector...
	; 
  .assert       ((TYPE (routineName) eq word) AND (TYPE @CurProc eq near)) OR \
		((TYPE (routineName) eq dword) AND (TYPE @CurProc eq far)) OR \
		((TYPE (routineName) eq TYPE @CurProc)), \
		<GOTO to routine with different return type>

  if (.TYPE @CurProc) AND 0x8000
	;
	; Only optimize in a message handler
	;
    ifidn <routineName>, <ObjCallSuperNoLock>
		jmp	ObjGotoSuperTailRecurse
    elifidn <routineName>, <ObjCallInstanceNoLock>
		jmp	ObjGotoInstanceTailRecurse
    elifidn <routineName>, <GenCallParent>
		jmp	GenGotoParentTailRecurse
    elifidn <routineName>, <VisCallParent>
		jmp	VisGotoParentTailRecurse
    else
		jmp	routineName
    endif
  else
		jmp	routineName
  endif
endm

GOTO		macro	routineName, r1, r2, r3, r4, r5, r6
if ERROR_CHECK
		call	routineName
		DoPop	<r1>, <r2>, <r3>, <r4>, <r5>, <r6>
		ret
elif NONEC_WITH_BACKTRACE
ifb <r1>
		call	routineName
		DoPop	<r1>, <r2>, <r3>, <r4>, <r5>, <r6>
		ret
else
	GOTO_LOW routineName
endif
else
	GOTO_LOW routineName
endif
		endm


GOTO_NOOPT	macro	routineName, r1, r2, r3, r4, r5, r6
    if ERROR_CHECK 
		call	routineName
		DoPop	<r1>, <r2>, <r3>, <r4>, <r5>, <r6>
		ret
    elif NONEC_WITH_BACKTRACE
	ifb <r1>)
		call	routineName
		DoPop	<r1>, <r2>, <r3>, <r4>, <r5>, <r6>
		ret
	else
		jmp	routineName
	endif
    else
		jmp	routineName
    endif

	;
	; Complex expression required to handle GOTO through a vector...
	; 
	.assert	((TYPE (routineName) eq word) AND (TYPE @CurProc eq near)) OR \
		((TYPE (routineName) eq dword) AND (TYPE @CurProc eq far)) OR \
		((TYPE (routineName) eq TYPE @CurProc)), \
		<GOTO to routine with different return type>
	.assert	not (((.type routine) and 0x4001) eq 0x4001), <GOTO to routineName but routineName is a dynamic method>

		endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
		FALL_THRU_POP
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SYNOPSIS:	Do a fall-through passing things on the stack.

PASS:		Registers expected on the stack.

PSEUDO CODE/STRATEGY:
	This is the second half of FALL_THRU/GOTO's to a routine that expects
	to pop registers from the stack before returning. If ERROR_CHECK
	is non-zero, the registers are left alone and a simple return
	performed -- the calling function will pop the registers.

	Otherwise, the registers are popped in the order given
	
	The "ret" for the routine should follow this macro.

REVISION HISTORY:
	Name	Date		Description
	----	----		-----------
	ardeb	11/28/88	Initial version

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@

FALL_THRU_POP	macro	r1, r2, r3, r4, r5, r6
	ife ERROR_CHECK
		DoPop	<r1>, <r2>, <r3>, <r4>, <r5>, <r6>
	endif
		endm

COMMENT @----------------------------------------------------------------------

	EC	code-line
	EEC	code-line
	NEC	code-line

	FUNCTION:
		Include a line of code based on ERROR_CHECK.
		EC includes the code if ERROR_CHECK is true.
		EEC is like EC, but reminds you that the code is temporary
		NEC includes the code if ERROR_CHECK is false.
	ARGUMENTS:
		code line -- line of code

------------------------------------------------------------------------------@
EC	macro	line
	if	ERROR_CHECK
line
	endif
endm

BEC	macro	line
	if	BETA_ERROR_CHECKING
line
	endif
endm

EEC	macro	line
	if	ERROR_CHECK
line
	PrintMessage <Temporary error checking here>
	endif
endm

NEC	macro	line
	ife	ERROR_CHECK
line
	endif
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
		LONG, LONG_EC
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SYNOPSIS:	For conditional jumps to far-away locations.  For code like
			jnz	LB_exit
		where LB_exit is more than 256 bytes away.  First of all,
		you should feel really guilty for needing this (and you'll
		be punished by endless asap if-else warnings), but now
		you must get on with business.  Typing:
			LONG	jnz	LB_exit
		will expand to:
		
			jz	tempLabel
			jmp	LB_exit
		tempLabel:

		which we all know and love.  This is less frivolous when used
		with the LONG_EC macro, which only does the expansion in the
		error checking versions,  so that if your EC code
		forces a long branch you will not be penalized in the non-
		error-checking version.  People should use LONG_EC first
		in these situations, and only change to LONG when the non-error-
		checking version gets assembled and complains.
		
PASS:		opcode	-	branch opcode we're replacing
		label   - 	label to branch to 

PSEUDO CODE/STRATEGY:
		I've made an attempt to put the most-used opcodes at the 
		front, in the hopes that this won't be a CPU drain.  Feel
		free to move them around.

REVISION HISTORY:
	Name	Date		Description
	----	----		-----------
	chris	6/14/89		Initial version

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@

LONG_EC	macro	opcode, label
	if	ERROR_CHECK
		LONG	opcode, label
	else
		opcode	label
	endif
endm

;
; Instead of us generating the correct code, we'll just let esp do it with
; warnings turned off temporarily

LONG	macro	opcode, label
	.warn -jmp
	opcode label
	.warn @jmp
endm

;
; ecnear can be used as the class of a procedure that needs to be far only in
; the non-ec version of something, where a routine will actually fall into
; or jump to the routine and expect that routine to return for it. In the ec
; version, of course, a call is made. If call GOTO's for the routine are
; from the same segment, the routine can safely be made near and an extra
; PUSH CS eliminated.
;
; FALL_THRU_ECN and GOTO_ECN are similar to their counterparts but will only
; type-check the destination in the non-ec version.
;
if ERROR_CHECK
ecnear	equ	<near call>
else
ecnear	equ	<far>
endif

;
; necjmp should be used in any routine that contains a FALL_THRU_POP statement.
; This is to ensure that nobody just does a "call" to the routine. It also
; catches people doing a REAL_FALL_THRU to a routine containing a FALL_THRU_POP
; statement, as this can cause problems.
;
if ERROR_CHECK
necjmp equ	<near call>
else
necjmp	equ	<far jmp>
endif

FALL_THRU_ECN	macro	routineName, r1, r2, r3, r4, r5, r6
	if ERROR_CHECK
		call	routineName
		DoPop	<r1>, <r2>, <r3>, <r4>, <r5>, <r6>
		ret
	else
		.assert	TYPE @CurProc eq TYPE routineName, <FALL_THRU to routine with different return type>
	endif
		REAL_FALL_THRU routineName
endm

GOTO_ECN	macro	routineName, r1, r2, r3, r4, r5, r6
    if ERROR_CHECK
		call	routineName
		DoPop	<r1>, <r2>, <r3>, <r4>, <r5>, <r6>
		ret
    elif NONEC_WITH_BACKTRACE
	ifb <r1>
		call	routineName
		DoPop	<r1>, <r2>, <r3>, <r4>, <r5>, <r6>
		ret
	else
	;
	; Complex expression required to handle GOTO through a vector...
	; 
	.assert	((TYPE (routineName) eq word) AND (TYPE @CurProc eq near)) OR \
		((TYPE (routineName) eq dword) AND (TYPE @CurProc eq far)) OR \
		((TYPE (routineName) eq TYPE @CurProc)), \
		<GOTO to routine with different return type>

		jmp	routineName
	endif
    else
	;
	; Complex expression required to handle GOTO through a vector...
	; 
	.assert	((TYPE (routineName) eq word) AND (TYPE @CurProc eq near)) OR \
		((TYPE (routineName) eq dword) AND (TYPE @CurProc eq far)) OR \
		((TYPE (routineName) eq TYPE @CurProc)), \
		<GOTO to routine with different return type>

		jmp	routineName
    endif

		endm

COMMENT @----------------------------------------------------------------------

	EC_BOUNDS	seg, reg

	FUNCTION:
		Assert that a given address is in bounds
	ARGUMENTS:
		seg - segment register
		reg - offset register

------------------------------------------------------------------------------@
EC_BOUNDS	macro	seg, off
if	ERROR_CHECK

ifdif	<seg>, <ds>
	push	ds
	segmov	ds, seg
endif
ifdif	<off>, <si>
	xchg	si, off
endif

	call	ECCheckBounds

ifdif	<off>, <si>
	xchg	si, off
endif
ifdif	<seg>, <ds>
	pop	ds
endif

endif
endm


COMMENT @----------------------------------------------------------------------

	EC_CHUNK_HANDLE	seg, chk

	FUNCTION:
		Assert that a given address is in bounds
	ARGUMENTS:
		seg - segment register
		reg - offset register

------------------------------------------------------------------------------@
EC_CHUNK_HANDLE	macro	seg, off
if	ERROR_CHECK

	push	si
ifdif	<seg>, <ds>
	push	ds
	segmov	ds, seg
endif
ifdif	<off>, <si>
	mov	si, off
endif

	mov	si, ds:[si]
	call	ECCheckLMemChunk

ifdif	<seg>, <ds>
	pop	ds
endif
	pop	si

endif
endm


COMMENT @----------------------------------------------------------------------

	EC_CHUNK_PTR	seg, chk

	FUNCTION:
		Assert that a given address is in bounds
	ARGUMENTS:
		seg - segment register
		reg - offset register

------------------------------------------------------------------------------@
EC_CHUNK_PTR	macro	seg, off
if	ERROR_CHECK

ifdif	<seg>, <ds>
	push	ds
	segmov	ds, seg
endif
ifdif	<off>, <si>
	xchg	si, off
endif

	call	ECCheckLMemChunk

ifdif	<off>, <si>
	xchg	si, off
endif
ifdif	<seg>, <ds>
	pop	ds
endif

endif
endm


CONTEXT	macro	con
.warn -unreach
	jmp	$+5
	mov	ax, con
.warn @unreach
endm

EC_CONTEXT macro con
if	ERROR_CHECK
.warn -unreach
	jmp	$+5
	mov	ax, con
.warn @unreach
endif
endm



COMMENT @----------------------------------------------------------------------

	PUSH_EC/POP_EC	tag, <args>

	FUNCTION:
		performs push/pop's, verifying that they correspond to each
		other.
	ARGUMENTS:
		tag - label to assign to a push/pop pair
		args - things to push/pop

	STRATEGY:
		after pushing args, PUSH_EC will push a word containing
		a unique value associated with tag inside the current
		procedure.  POP_EC, before popping the arguments, will
		make sure that the value on the top of the stack matches
		the value of tag before popping the arguments.

	NOTES:

		One immediate improvement that can be made is to
		optimize POP_EC to pop the tag into a1 if it is a
		register, and compare, rather than XchgTopStack

------------------------------------------------------------------------------@

POP_DOES_NOT_MATCH_CORRESPONDING_PUSH	enum	FatalErrors
; the POP_EC macro detected a pop which does not match a PUSH_EC
; having the same tag.

_SetTagValue	macro	tagName
	ifndef	tagName
		tagName equ @Line
		; PrintMessage <setting &tagName equ &%@Line>
	endif
endm

_EnsurePushTagDefined	macro	scope, label, tagName
	; This makes sure that the first time a push/pop label is used
	; inside a procedure, it is assigned a unique value.  The label
	; is combined with the name of the current procedure to create
	; a uniquely-named symbol, relieving the programmer from having
	; to come up with their own.  This symbol is then assigned the
	; current line number
	tagName equ <&scope&label>
	_SetTagValue	&scope&label
	; PrintMessage <&scope&label equ &%@Line>
endm

_push_lr	macro	a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12
	ifnb <a1>
		push	a1
		_push_lr	a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12
	endif
endm

_pop_rl	macro	a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12
	ifnb <a1>
		_pop_rl	a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12
		pop	a1
	endif
endm

PUSH_EC	macro	label, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12
local	tag
	_push_lr	r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12
if	ERROR_CHECK
	_EnsurePushTagDefined %@CurProc, label, tag
	push	ax
	mov	ax, tag
	XchgTopStack	ax
endif
endm

POP_EC	macro	label, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12
local	tag
if	ERROR_CHECK
	_EnsurePushTagDefined %@CurProc, label, tag
	XchgTopStack	ax
	pushf
	cmp	ax, tag
	ERROR_NE  POP_DOES_NOT_MATCH_CORRESPONDING_PUSH
	popf
	pop	ax
endif
	_pop_rl	r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12
endm


EndKernel
