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

	Copyright (c) GeoWorks 1989 -- All Rights Reserved

PROJECT:	PC GEOS
FILE:		geos.def

DECLARER:	Kernel

MACROS:
	SetDef		Control access and prevent double-inclusion
	StartKernel	Begin definitions related to the kernel.
	EndKernel	Finish definitions related to the kernel.
	PrintMessage	Print a message tagged with the current file and line
	ErrMessage	Print a message and generate an error
	ForceRef	Force a reference to a symbol to keep Esp and Glue
			from complaining about it.
	clr		clear a register or memory location
	BitSet		given a location and a record field, set the
			corresponding bit in the location
	BitClr		given a location and a record field, clear the
			corresponding bit in the location.
	tst		test a register
	segmov		move one segment register to another using push/pop
	segxchg		exchange one segment register with another using
			push/pop
	push		Does several pushes
	DoPop		Does several pops
	pop		Does several pops in reverse of order given.
	PrintE		Print the value of an expression during assembly
	CmpStrings	Compare two strings for equality.
	CheckHack	Macro to verify that a data-structure-based hack
			will continue to work.
	XchgTopStack	Exchange a register with the top word on the stack

	cmpdw		Compare dwords (unsigned only)
	jgedw		Signed compare and branch >= for dwords
	jgdw		Signed compare and branch > for dwords
	jledw		Signed compare and branch <= for dwords
	jldw		Signed compare and branch < for dwords
	tstdw		Test a dword
	pushdw		Push a dword
	popdw		Pop a dword
	notdw		Not a dword
	negdw		Negate a dword
	incdw		Increment a dword
	decdw		Decrement a dword
	movdw		Move a dword
	adddw		Add a dword
	adcdw		Add with carry a dword
	subdw		Subtract a dword
	sbbdw		Subtract with borrow a dword
	clrdw		Clear a dword
	shrdw		Logical shift right for dword
	sardw		Arithmetic shift right for dword
	shldw		Logical shift left for dword (=saldw)
	saldw		Arithmetic shift left for dword (=shldw)
	xchgdw		Swap two dwords

	cmpwwf		Compare WWFixed's (unsigned only)
	jgewwf		Signed compare and branch >= for WWFixed's
	jgwwf		Signed compare and branch > for WWFixed's
	jlewwf		Signed compare and branch <= for WWFixed's
	jlwwf		Signed compare and branch < for WWFixed's
	tstwwf		Test a WWFixed
	pushwwf		Push a WWFixed
	popwwf		Pop a WWFixed
	notwwf		Not a WWFixed
	negwwf		Negate a WWFixed
	incwwf		Increment a WWFixed
	decwwf		Decrement a WWFixed
	movwwf		Move a WWFixed
	addwwf		Add a WWFixed
	adcwwf		Add with carry a WWFixed
	subwwf		Subtract a WWFixed
	sbbwwf		Subtract with borrow a WWFixed
	clrwwf		Clear a WWFixed
	shrwwf		Logical shift right for WWFixed
	sarwwf		Arithmetic shift right for WWFixed
	shlwwf		Logical shift left for WWFixed (=salwwf)
	salwwf		Arithmetic shift left for WWFixed (=shlwwf)
	rndwwf		Round a signed WWFixed down to a word
	rnduwwf		Round an unsigned WWFixed down to a word

	cmpdwf		Compare DWFixed's (unsigned only)
	jgedwf		Signed compare and branch >= for DWFixed's
	jgdwf		Signed compare and branch > for DWFixed's
	jledwf		Signed compare and branch <= for DWFixed's
	jldwf		Signed compare and branch < for DWFixed's
	tstdwf		Test a DWFixed
	pushdwf		Push a DWFixed
	popdwf		Pop a DWFixed
	notdwf		Not a DWFixed
	negdwf		Negate a DWFixed
	incdwf		Increment a DWFixed
	decdwf		Decrement a DWFixed
	movdwf		Move a DWFixed
	adddwf		Add a DWFixed
	adcdwf		Add with carry a DWFixed
	subdwf		Subtract a DWFixed
	sbbdwf		Subtract with borrow a DWFixed
	clrdwf		Clear a DWFixed
	shrdwf		Logical shift right for DWFixed
	sardwf		Arithmetic shift right for DWFixed
	shldwf		Logical shift left for DWFixed (=saldwf)
	saldwf		Arithmetic shift left for DWFixed (=shldwf)
	rnddwf		Round a signed DWFixed down to a dword
	rndudwf		Round an unsigned DWFixed down to a dword
	copybuf		Copies a fixed size buffer

	GetVSEG		Turns a handle in a register into a vseg
	PushSP		Pushes the SP register safely on all platforms

TYPES:
	Name		Description
	----		-----------
	BBFixed		Define 8+8 bit fixed-point number
	WBFixed		Define 16+8 bit fixed-point number
	WWFixed		Define 16+16 bit fixed-point number

DESCRIPTION:
	This file defines common structures and constants

	$Id: geos.def,v 1.1 97/04/04 14:22:34 newdeal Exp $

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

;-----------------------------------------------------------------------------
;		Enable .186 Instructions
;-----------------------------------------------------------------------------

.186

;-----------------------------------------------------------------------------
;		Access Control Support
;-----------------------------------------------------------------------------

SetDef	macro	sym
ifdef sym
	exitf
endif
sym = 1
endm

;-----------------------------------------------------------------------------

SetDef	__GEOS

;-----------------------------------------------------------------------------


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
		StartKernel
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SYNOPSIS:	Begin a group of definitions from the kernel.

PASS:		_Kernel defined if assembing the kernel

REVISION HISTORY:
	Name	Date		Description
	----	----		-----------
	tony	2/89		Initial version

DESCRIPTION:

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

StartKernel	macro
ifndef _Kernel
geos		segment library
endif
		endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
		EndKernel
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SYNOPSIS:	Finish out a group of definitions from the kernel

PASS:		_Kernel defined if assembling the kernel

REVISION HISTORY:
	Name	Date		Description
	----	----		-----------
	tony	4/89		Initial version

DESCRIPTION:

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

EndKernel	macro
ifndef _Kernel
geos		ends
endif
		endm

;-----------------------------------------------------------------------------

StartKernel

;-----------------------------------------------------------------------------
;		Error checking stuff
;-----------------------------------------------------------------------------

ifdef	DO_ERROR_CHECKING
ERROR_CHECK	=	-1	;True to assemble a version with error checking
else
ERROR_CHECK	=	0
endif

;-----------------------------------------------------------------------------
;		DBCS stuff
;-----------------------------------------------------------------------------
ifdef	DO_DBCS
DBCS_PCGEOS	=	-1
TCHAR		equ	<wchar>
else
DBCS_PCGEOS	=	0
TCHAR		equ	<char>
endif

ifdef	DO_PIZZA
PZ_PCGEOS	=	-1
else
PZ_PCGEOS	=	0
endif

RUSSIAN		=	0

;
; Some routines exported by the kernel are near for the kernel, but far for
; applications. These are designated as type "appfar" in global declarations.
;

ifdef _Kernel
appfar	equ	<near>
else
appfar	equ	<far>
endif

;-----------------------------------------------------------------------------
;		Common constants and structures
;-----------------------------------------------------------------------------

FALSE		=	0
TRUE		=	-1
NULL		=	0

BooleanByte 	etype byte
BB_FALSE 	enum BooleanByte, 0
BB_TRUE 	enum BooleanByte, 255

BooleanWord 	etype word
BW_FALSE 	enum BooleanWord, 0
BW_TRUE 	enum BooleanWord, 0ffffh

WordAndAHalf	struct
    WAAH_low	word
    WAAH_high	byte
WordAndAHalf	ends

BBFixed	struct
    BBF_frac	byte			;8 bits fraction
    BBF_int	byte			;8 bits integer
BBFixed	ends

WBFixed	struct
    WBF_frac	byte			;8 bits fraction
    WBF_int	word			;16 bits integer
WBFixed	ends

WWFixed	struct
    WWF_frac	word			;16 bits fraction
    WWF_int	word			;16 bits integer
WWFixed	ends

DWFixed	struct
    DWF_frac	word			;16 bits fraction
    DWF_int	sdword			;32 bits integer
DWFixed	ends

DDFixed struct
    DDF_frac	dword			; 32 bits fraction
    DDF_int	sdword			; 32 bits integer
DDFixed	ends
;
;	CPU flags
;
CPUFlags	record
    :4
    CPU_OVERFLOW:1
    CPU_DIRECTION:1
    CPU_INTERRUPT:1
    CPU_TRAP:1
    CPU_SIGN:1
    CPU_ZERO:1
    :1
    CPU_AUX_CARRY:1
    :1
    CPU_PARITY:1
    :1
    CPU_CARRY:1
CPUFlags	end

;-------------------------------------------------------

global ThreadSetError:far
global THREADSETERROR:far
global THREADGETERROR:far

ThreadGetError	equ	THREADGETERROR

NO_ERROR_RETURNED	= 0

;----------------------------------------------------------------------------
;
; WWFix 	- define a fixed point number
; WBFix 	- define a fixed point number
; BBFix 	- define a fixed point number
;
; 	args:	number	- floating point number.  The allowed format for the
;			  number is:
;				[+-][integer][.[fraction]]
;				where:
;					integer: -32K <= integer <= 32K
;					fraction: 0 <= fraction <= .9999
;
;			  as indicated, everything is optional.
;
;	samples:	
;		WWFix	5.55		; define the number 5.55
;		WWFix	-5.55		; define the number -5.55
;		WWFix	-0.55		; define the number -0.55
;		WWFix	2		; define the number 2.0
;
;	notes:
;		This will handle most cases, but will probably choke on
;		nonsense degenerate cases.  So don't be a degenerate.
;
;		If you specify a fraction > 4 digits, it will be truncated
;		to 4.
;----------------------------------------------------------------------------
DWFix	macro	number
GenFix	number, dword, word
endm

WWFix	macro	number
GenFix	number, word, word
endm

WBFix	macro	number
GenFix	number, word, byte
endm

BBFix	macro	number
GenFix	number, byte, byte
endm

;---------

GenFix	macro	number, intSize, fracSize

local	sign, startpos, slen, fracPart, intPart

sign	substr <&number>, 1, 1		;; get sign of number
startpos	=	1		;; assume no sign prefix
ifidn	sign, <->			;; use sign to determine starting pos
    factor	=	-1
    startpos	=	2
else 
    factor	=	1
    ifidn sign, <+>
	startpos =	2
    endif
endif

slen	sizestr	<&number>		;; get length of entire string
dotpos	instr	<&number>, <.>		;; get position of decimal point

		 			;; handle special cases...
if	(dotpos eq 0) 			;; No decimal point...
    fracPart equ	<0>
    intPart	substr	<&number>, startpos, slen-startpos+1
else 
    if (slen eq dotpos)			;; decimal point at end of string
        fracPart equ	<0>
        intPart	substr	<&number>, startpos, slen-startpos
    else 
	if (dotpos eq startpos)		;; decimal point at start of string
            fracPart substr	<&number>, startpos+1, slen-startpos
            intPart equ	<0>
	else				;; normal case
            fracPart substr	<&number>, dotpos+1, slen-dotpos
            intPart	substr	<&number>, startpos, dotpos-startpos
	endif
    endif
endif

flen	sizestr	fracPart		;; determine accuracy of fraction

if (flen gt 6)				;; 1/65536 = .00001525878906...
    fracPart substr fracPart, 1, 6	;; so keep 6 digits for rounding
    flen = 6				;; accuracy
endif

denom	= 1
rept	flen				;; calc divisor for below
denom	= denom * 10
endm


	;; FINALLY, allocate the bugger !

    ifidn	<fracSize>, <word>
	fracBits	= 16
	maxNum		= 0x10000
    else
	fracBits	= 8
	maxNum		= 0x100
    endif

    if (factor lt 0)
	if (fracPart eq 0)
	    fracSize	0
	    intSize	0-intPart
	else
	    fracSize	maxNum-(((fracPart shl fracBits)+(denom/2))/denom)
	    intSize	0-intPart-1
	endif
    else
	    fracSize	((fracPart shl fracBits)+(denom/2))/denom
	    intSize	intPart
    endif
endm


COMMENT }%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
		PrintMessage, ErrMessage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SYNOPSIS:	Print a message to the output in the proper Esp format
		ErrMessage also generates an error

PASS:		the message to print

PSEUDO CODE/STRATEGY:
       Need to use the nested _PrintMessage macro to actually get
       the file and line number in the message, as %out and string handling
       in general don't handle string equates or the @File and @Line constants

KNOWN BUGS/SIDE EFFECTS/IDEAS:

REVISION HISTORY:
	Name	Date		Description
	----	----		-----------
	ardeb	10/27/89	Initial version

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%}
_PrintMessage	macro 	fname, lnum, msg
%out file "fname", line lnum: msg
		endm
PrintMessage	macro	msg
		_PrintMessage %@File, %@Line, <msg>
		endm
ErrMessage	macro	msg
		_PrintMessage %@File, %@Line, <msg>
		.err
		endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
		ForceRef
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SYNOPSIS:	Force the reference bit for a symbol to be set by Esp so as
		to make sure it doesn't generate a warning if -Wunref given

PASS:		The name of an ALREADY DEFINED symbol to reference.

PSEUDO CODE/STRATEGY:
		Just subtract the offset of the symbol from itself as the
		argument of a conditional. This works both with directly
		addressed variables and with local variables too, as the
		offset operator returns their offset from bp.

KNOWN BUGS/SIDE EFFECTS/IDEAS:

REVISION HISTORY:
	Name	Date		Description
	----	----		-----------
	ardeb	11/10/89	Initial version

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
ForceRef	macro	sym
ifdef	sym
	if offset sym - offset sym
	endif
endif
		endm

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

	clr1	location

	FUNCTION:
		Clears a register or memory location
	ARGUMENTS:
		location -- the byte or word to clear
	DESTROYS:
		flags
	EXPANDS TO:
		2-6 bytes 

-------------------------------------------------------------------------------@
clr1	macro 	location
    if	(.TYPE location) AND 10h  ; should be non-zero if register
	xor	location,location ;turn off all bits that are on
    else
	mov	location, 0	;else move a zero to clear it
    endif
endm

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

	clr	loc1 [,loc2[,loc3..........

	FUNCTION:
		Clears one or more register or memory location(s) of like size
	ARGUMENTS:
		loc* -- the bytes or words to clear
	DESTROYS:
		flags
	EXPANDS TO:
		depends on args passed	

-------------------------------------------------------------------------------@
clr	macro 	loc1, loc2, loc3, loc4, loc5, loc6, loc7, loc8, loc9
	clr1	loc1
ifnb	<loc2>
	if	(.TYPE loc1) AND 10h  ; if loc1 = reg
		czr	loc1, loc2, loc3, loc4, loc5, loc6, loc7, loc8, loc9
	else
		clr	loc2, loc3, loc4, loc5, loc6, loc7, loc8, loc9
	endif
endif
endm

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

	czr	zeroReg, dest1[,dest2[,dest3.....

	FUNCTION:
		Copies a register with value 0 into the passed locations
	ARGUMENTS:
		zeroReg -- a register with value zero
		dest* -- the bytes or words to clear
	DESTROYS:
		nothing (flags preserved)
	EXPANDS TO:
		depends on args passed	

-------------------------------------------------------------------------------@
czr	macro 	zeroReg,dest1,dest2,dest3,dest4,dest5,dest6,dest7,dest8,dest9
ifb <dest1>
	ErrMessage	"czr expects multiple arguments."
else
    if	(.TYPE zeroReg) AND 10h  ; should be non-zero if register
	ifidn	<zeroReg>, <ax>
		ifidn	<dest1>, <dx>
			cwd			;if zeroReg = ax, loc
		else
			mov	dest1, zeroReg
		endif
	else
		mov	dest1, zeroReg
	endif
	ifnb <dest2>
		czr	zeroReg,dest2,dest3,dest4,dest5,dest6,dest7,dest8,dest9
	endif
    else
	ErrMessage	"Zero register not a register"
    endif
endif
endm

COMMENT @----------------------------------------------------------------------

	BitSet	location, bit

	FUNCTION:
		Sets a bit in a location
	ARGUMENTS:
		location -- the byte or word to set the bit in
		bit -- bit field to set (this is a field in a record)
	DESTROYS:
		nothing
	EXPANDS TO:
		2-6 bytes 

-------------------------------------------------------------------------------@
BitSet	macro 	location, bit
	ornf	location,mask bit
endm

COMMENT @----------------------------------------------------------------------

	BitClr	location, bit

	FUNCTION:
		Sets a bit in a location
	ARGUMENTS:
		location -- the byte or word to clear the bit in
		bit -- bit field to clear (a field in a record)
	DESTROYS:
		nothing
	EXPANDS TO:
		2-6 bytes 

-------------------------------------------------------------------------------@
BitClr	macro 	location, bit
	andnf	location,not (mask bit)
endm

COMMENT @----------------------------------------------------------------------

	tst	val
	tst_clc	val

	FUNCTION:
		Tests a register/memory location for non-zeroness. 
	ARGUMENTS:
		val -- register or memory location
	DESTROYS:
		carry cleared

------------------------------------------------------------------------------@
tst	macro 	val,arg2
ifnb <arg2>
	ErrMessage	"Multiple arguments passed to tst macro."
else
	if (.TYPE (val)) AND 10h	;see if val is register
		or	val, val	;3 cycles, 2 bytes
	else				;val is mem
	 	cmp	val, 0		;14+EA cycles, 3-5 bytes for word sized
					;10+EA cycles, 3-5 bytes for byte sized
	endif
endif
endm

tst_clc	macro	val, arg2
	tst	val, arg2
endm

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

	segmov	destSeg, sourceSeg [,useReg]

	FUNCTION:
		Move one segment register to another using push/pop (or
		an intermediate register)
	ARGUMENTS:
		destSeg   -- segment register to move TO
		sourceSeg -- segment register to move FROM
		useReg    -- register to use for fast version
	DESTROYS:
		nothing (useReg destroyed if that option chosen)
	EXPANDS TO:
		2 bytes/26 cycles	(push/pop version)
		4 bytes/ 4 cycles	(useReg version)

-------------------------------------------------------------------------------@
segmov	macro 	destSeg, sourceSeg, useReg
if <useReg> ne <> and \
   	((((.type (destSeg)) and 0x4010) ne 0x4010) or \
	 ((.type (sourceSeg)) and 0x0004) or \
	 (((.type (sourceSeg)) and 0x4010) eq 0x4010) or \
	 ((.type (sourceSeg)) eq 0))
	; have trash register and dest isn't a segment register, or source is
	; constant or another segment register or it's currently undefined (in
	; the latter case, we assume the programmer knows what s/he is doing)
	mov	useReg, sourceSeg
	mov	destSeg, useReg
else
	push	sourceSeg
	pop	destSeg
endif
endm

COMMENT @----------------------------------------------------------------------

	segxchg	destSeg, sourceSeg [,useReg]

	FUNCTION:
		Exchange one segment register with another using push/pop
	ARGUMENTS:
		seg1, seg2 -- Segment registers to exchange
	DESTROYS:
		nothing
	EXPANDS TO:
		4 bytes/ 52 cycles

-------------------------------------------------------------------------------@
segxchg	macro 	seg1, seg2
	push	seg1
	push	seg2
	pop	seg1
	pop	seg2
endm

COMMENT @----------------------------------------------------------------------

	DoPop

	FUNCTION:
		pops the arguments in left-to-right order.

-------------------------------------------------------------------------------@
DoPop	macro	ar1, ar2, ar3, ar4, ar5, ar6, ar7, ar8, ar9, ar10, ar11, ar12
    ifnb <ar1>
	pop	ar1
	DoPop	ar2, ar3, ar4, ar5, ar6, ar7, ar8, ar9, ar10, ar11, ar12
    endif	
endm


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

	Destroy

	FUNCTION:
		Destroys a variable number of items, by "not"ing them. Used
		to enforce "Allowed to destroy" documentation.
		Handles up to 12 arguments, the max that the assembler can take.

-------------------------------------------------------------------------------@
Destroy	macro	ar1, ar2, ar3, ar4, ar5, ar6, ar7, ar8, ar9, ar10, ar11, ar12
if	ERROR_CHECK
    ifnb <ar1>
	not	ar1
	Destroy	ar2, ar3, ar4, ar5, ar6, ar7, ar8, ar9, ar10, ar11, ar12
    endif	
endif
endm

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

	PrintE		prompt, expr

	FUNCTION:
		Helps debugging in this wonderful assembler by printing values
		of symbols on the screen.  Use as follows:
		
			PrintE	<The answer is = >, %(expr)
	ARGUMENTS:
		prompt -- some text to say what you're printing
		expr   -- some expression to evaluate. All elements of it must
			  be defined when this macro is invoked, as there is
			  no second pass in Esp
		
------------------------------------------------------------------------------@
PrintE	macro	prompt, expr
	  PrintMessage	<* &prompt&expr *>
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
		CheckHack
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SYNOPSIS:	There are certain places in this code where assumptions
		have been made about data structures in order to generate
		more efficient code. This macro will verify these assumptions
		and produce an error message if they are invalid.

PASS:		expression that must evaluate true.

PSEUDO CODE/STRATEGY:

KNOWN BUGS/SIDE EFFECTS/IDEAS:

REVISION HISTORY:
	Name	Date		Description
	----	----		-----------
	ardeb	1/ 9/90		Initial version

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
CheckHack	macro	expr
		local	msg
ifb @CurProc
msg	equ	<"expr must be true for this program to work">
else
msg	catstr <"expr must be true for >, @CurProc, < to work">
endif

; use .assert to deal with forward references, now that .assert will try to
; check the assertion on the first pass...
.assert expr, msg

		endm

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

	XchgTopStack	reg

	FUNCTION:
		Exchange a register with the top word on the stack
	ARGUMENTS:
		reg -- register to exchange

	65 cycles total (if not bp)
-------------------------------------------------------------------------------@
XchgTopStack	macro	reg

if <reg> ne <bp>
	; Use bp to avoid the cost of a segment override
	push	bp			;15
	mov	bp, sp			;2
	xchg	ss:[bp+2], reg		;36
	pop	bp			;12
else
	; Pick on SI instead of BP
	push	si
	mov	si, sp
	xchg	ss:[si+2], reg
	pop	si
endif

;;;; This used to be done and defined in interrupt.def, but it's unsafe when
;;;; NMI's are taken into account. The above code is 21 cycles slower on
;;;; an 8088, but two bytes smaller (no INT_OFF/INT_ON needed), and is not
;;;; susceptible to NMI's (which are used rather heavily on the GRiDPad)
;;;; 					-- ardeb 7/30/90
;;;;ifdif	<flags>,<OFF>
;;;;	INT_OFF
;;;;endif
;;;;	xchg	reg,sp		;reg = stack pointer, sp = value to save
;;;;	xchg	sp,ss:[reg]	;top of stack = value to save, sp = value to get
;;;;	xchg	reg,sp		;sp = stack pointer, reg = value to get
;;;;ifdif	<flags>,<OFF>
;;;;	INT_ON
;;;;endif
;;;;
endm


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

	mov_trash	reg1, reg2
	mov_tr is a synonym for mov_trash		

	FUNCTION:
	The mov_trash (mov_tr) macro can be used whenever moving data
	between AX and another (non-segment) register, where, after
	the move, the source register is no longer needed.  If either
	the source or destination register is AX, this produces a
	single-byte xchg of the two registers, rather than a 2-byte
	"mov".

	Although the "xchg" instruction (3 cycles) initially appears
	to be slower than a "mov" (2 cycles), the fact that it's a
	single-byte instruction means that it actually ends up
	executing faster than a "mov" (also, it produces smaller code,
	which is always desirable).  If neither register is AX, it's
	the same as a "mov", but declares your intention of no longer
	needing the source register.
		
	ARGUMENTS:
		reg1 -- destination register
		reg2 -- source register whose value will no longer be needed

	For example:

		mov_tr	bp, ax
		mov	ax, SOME_VALUE

	In this case, we know that AX will no longer be needed after
	its value is copied into BP, so the mov_tr macro is used to
	save space (and therefore, execution time).

	mov_trash can also be used when moving data INTO ax:

		mov_tr	ax, cx
		mov	cx, SOME_VALUE



	Be careful to avoid things like:

		mov_tr	bp, ax

		...
		cmp	ax, SOME_VALUE

	In this case, the mov_tr is erroneous, since the value in AX
	is still needed later in the procedure.  Note:  you should
	NEVER rely on the fact that mov_tr gets assembled as "xchg" --
	you must simply assume that the source gets destroyed.


-------------------------------------------------------------------------------@
mov_trash	macro	reg1, reg2
ifidn	<reg1>, <reg2>
ErrMessage <mov_trash registers are the same>
endif
if (((.type &reg1) and 0x4010) eq 0x4010) or \
   (((.type &reg2) and 0x4010) eq 0x4010)
	; one of them is a segment register, so we can't use xchg
	mov	reg1, reg2	
elif	<reg1> eq <ax> or <reg2> eq <ax> or <reg1> eq <AX> or <reg2> eq <AX>
	xchg	reg1, reg2
else
	mov	reg1, reg2
endif
endm

mov_tr	macro	reg1, reg2
	mov_trash	<reg1>, <reg2>
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
		StartLibrary
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SYNOPSIS:	Begin definitions in a library. Note that any nested
		UseLibrary invocations in a library definition file *must* 
		occur after the StartLibrary for the file.

PASS:		Library name.

REVISION HISTORY:
	Name	Date		Description
	----	----		-----------
	tony	9/88		Initial version

DESCRIPTION:
	This is placed at the start of definitions for things in a given
	library (normally in its .def file).
	
	_DECLARER_ is TRUE if the library is including the definitions
	segment. It is FALSE if only a user of the library is including them.
	The UseLib and DefLib macros should be used to set this symbol.

	Each StartLibrary must be matched by a corresponding EndLibrary.
	
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@

StartLibrary	macro	libName
ifndef _DECLARER_
  PrintMessage <_DECLARER_ is not defined for libName>
	   %out  >>>
  	   %out  >>> This file can not just be included, it is part of a library
  	   %out  >>> and therefore must be used with "UseLib libName.def".
	   %out  >>> If you are including this file within the library itself
	   %out  >>> you should use "DefLib libName.def"
	   %out  >>>
	.err
endif

_DECLARER_&libName = _DECLARER_
libName		segment library
endm



COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
		ContinueLibrary
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SYNOPSIS:	Prepare to include additional files that belong to the same
		library to which the current include file belongs. Use it
		after having included any other file not related to this
		library since the most recent StartLibrary or ContinueLibrary

PASS:		libName		= the permanent name of the library, as
				  passed to StartLibrary and EndLibrary

PSEUDO CODE/STRATEGY:

KNOWN BUGS/SIDE EFFECTS/IDEAS:

REVISION HISTORY:
	Name	Date		Description
	----	----		-----------
	ardeb	7/31/92		Initial version

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
ContinueLibrary	macro	libName
_DECLARER_ = _DECLARER_&libName
		endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
		EndLibrary
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SYNOPSIS:	Exit the current library

PASS:		The library to exit

REVISION HISTORY:
	Name	Date		Description
	----	----		-----------
	ardeb	7/12/88		Initial version

DESCRIPTION:
	Issues the ENDS directive to exit a library's segment

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
EndLibrary	macro	libName
_DECLARER_ = _DECLARER_&libName
libName		ends
		endm



COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
		UseLib
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SYNOPSIS:	Include a library definitions file as a user of the library

PASS:		File to include

REVISION HISTORY:
	Name	Date		Description
	----	----		-----------
	ardeb	12/14/88	Initial version

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
UseLib		macro	file
_DECLARER_	= FALSE
include		file
		endm



COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
		StartDriver
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SYNOPSIS:	Begin definitions in a library.

PASS:		Driver name.

REVISION HISTORY:
	Name	Date		Description
	----	----		-----------
	tony	9/88		Initial version

DESCRIPTION:
	This is placed at the start of definitions for things in a given
	driver (normally in its .def file).
	
	_DECLARER_ is TRUE if the driver is including the definitions
	segment. It is FALSE if only a user of the driver is including them.
	The UseLib and DefLib macros should be used to set this symbol.

	Each StartDriver must be matched by a corresponding EndDriver.
	
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@

StartDriver	macro	libName
_DECLARER_&libName = _DECLARER_
libName		segment library
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
		EndDriver
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SYNOPSIS:	Exit the current library

PASS:		The library to exit

REVISION HISTORY:
	Name	Date		Description
	----	----		-----------
	ardeb	7/12/88		Initial version

DESCRIPTION:
	Issues the ENDS directive to exit a driver's segment. 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
EndDriver	macro	libName
_DECLARER_ = _DECLARER_&libName
libName		ends
		endm



COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
		UseDriver
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SYNOPSIS:	Include a driver definitions file as a user of the driver

PASS:		File to include

REVISION HISTORY:
	Name	Date		Description
	----	----		-----------
	ardeb	12/14/88	Initial version

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
UseDriver	macro	file
_DECLARER_	= FALSE
include		file
		endm

;--------------------------------------------------------------------------
;	Macros for C Interface Stubs
;--------------------------------------------------------------------------

SetGeosConvention	macro
.model	medium, pascal
endm

SetDefaultConvention	macro
.model	medium, C
endm

;---

PopReturnAddr	macro	retSeg, retOff
	pop	retOff
	pop	retSeg
endm

PushReturnAddr	macro	retSeg, retOff
	push	retSeg
	push	retOff
endm

;---
_WarnNonDestroyableRegs macro	a1, a2, a3, a4
	local	i
ifnb <a1>
	i instr <diDIsiSIbpBPdsDS>, <a1>
	if i
		PrintMessage <Register a1 may not be destroyed by a C stub. Make sure you've saved it>
	endif
	_WarnNonDestroyableRegs <a2>, <a3>, <a4>
endif
	endm

C_GetOneWordArg	macro	reg, retSeg, retOff
_WarnNonDestroyableRegs	reg, retSeg, retOff
if <reg> eq <retSeg> or <reg> eq <retOff>
	.err <dest register can't be same as return address segment or offset>
endif
	PopReturnAddr	retSeg, retOff
	pop	reg
	PushReturnAddr	retSeg, retOff
endm

;---

C_GetTwoWordArgs	macro	reg1, reg2, retSeg, retOff
_WarnNonDestroyableRegs	reg1, reg2, retSeg, retOff
if <reg1> eq <retSeg> or <reg1> eq <retOff> or \
   <reg2> eq <retSeg> or <reg2> eq <retOff>
	.err <dest registers can't be same as return address segment or offset>
endif
	PopReturnAddr	retSeg, retOff
	pop	reg2
	pop	reg1
	PushReturnAddr	retSeg, retOff
endm

;---

C_GetOneDWordArg	macro	seg, off, retSeg, retOff
_WarnNonDestroyableRegs	seg, off, retSeg, retOff
if <seg> eq <retSeg> or <seg> eq <retOff> or \
   <off> eq <retSeg> or <off> eq <retOff>
	.err <dest registers can't be same as return address segment or offset>
endif
	PopReturnAddr	retSeg, retOff
	pop	off
	pop	seg
	PushReturnAddr	retSeg, retOff
endm

;---


C_GetThreeWordArgs	macro	firstArg, secondArg, thirdArg, retOff
_WarnNonDestroyableRegs	firstArg, secondArg, thirdArg, retOff
if <secondArg> eq <retOff> or <thirdArg> eq <retOff> or <firstArg> eq <retOff>
	.err <dest registers can't be same as return offset>
endif
	pop	retOff		;retOff = retAddr.offset
	pop	firstArg	;firstArg = retAddr.segment

	pop	thirdArg	;thirdArg = third arg
	pop	secondArg	;secondArg = second arg

	XchgTopStack	firstArg ;firstArg = first arg, push retAddr.segment
	push	retOff		;push retAddr.offset
endm



COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		dword math macros

ARGUMENTS:
	There are three classes of dword math macros: one-argument,
	two-argument, and signed compare macros.  If stored in registers,
	dwords are traditionally referred to by either regHregL or regH:regL
	(eg. dxcx or dx:cx), where the high word comes first.  If stored in
	memory, remember the 80x86 machines store data LSB first, so it will
	be stored regL then regH.

	For one-argument functions, the destination can either a register
	pair or memory:

	dest
	----
	regpair
	memory

	For two-argument functions, the arguments can either be a register
	pair and memory, two register pairs, or a constant and a register
	pair:

	dest		source
	------		----
	regpair		memory
	regpair		constant
	regpair		regpair
	memory		regpair
	memory		constant
	memory		memory		NOTE: requires trash register

	Signed compare functions are necessary because for a signed dword,
	the high word is signed, but the low word is not.  This means that
	the 'cmpdw' macro will not work -- it will return flags for greater
	than for both the high and low words, which will give incorrect
	results.  As with the two-argument functions, the arguments can either
	be a register pair and memory, two register pairs, or a constant and
	a register pair.  They also take a label to branch to if the specified
	condition is true.

EXAMPLES:
	negdw	dxax
	adddw	bxax, dxcx
	cmpdw	dxax, ss:[bp].MRVP_bounds.R_left
	movde	dxcx, 0x12345678
	jgedw	ds:[si], dxax, aLabel

NOTE:

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

barf	macro	arg
%out arg
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		_CheckDWordOneArg

DESCRIPTION:	Check argument to one-arg dword macro

ARGUMENTS:
	dest - argument side
	op - string to emit for error-identification

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
_CheckDWordOneArg	macro	dest, op, arg2
ifnb <arg2>
	ErrMessage	<Multiple arguments passed to op macro>
else
	ifnb <dest>
	    dlen	sizestr	<&dest>
	    if ((.TYPE &dest) AND 4)
		ErrMessage <op : single argument cannot be constant>
	    endif
	else
	    ErrMessage <op : missing argument>
	endif
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		_CheckDWordTwoArg

DESCRIPTION:	Check arguments to two-arg dword macro

ARGUMENTS:
	dest - left-hand side
	src - right-hand side
	op - string to emit for error-identification

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
_CheckDWordTwoArg	macro	dest, src, op

ifnb	<src>
    ifidn <dest>, <src>
	ErrMessage <op : destination = source>
    else
	if ((.TYPE &dest) AND 4)
	    ErrMessage <op : destination argument cannot be constant>
	endif
    endif
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		_ParseDWordArg

DESCRIPTION:	Parse an argument to a dword math macro

PASS:
	arg - argument passed to macro
	regcount - # of register-pair arguments
	op - string to emit for error-identification
RETURN:
	rl - low part of arg
	rh - high part of arg
	regcount - # of register-pair or constant arguments

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

_ParseDWordArg	macro	arg, rl, rh, regcount, op

alen	sizestr	<&arg>

ifnb <arg>

if (4 AND .TYPE (arg))
    ; => is constant
    rh catstr <((arg) SHR 16)>
    rl catstr <((arg) AND 0xffff)>
    regcount=regcount+1
else
    if (alen eq 4)
	rh substr <arg>, 1, 2
	rl substr <arg>, 3, 2

;	ifidn rl, rh
;	    ErrMessage <op : register used twice in dword>
;	endif
	if (16 and .type (rl)) and (16 and .type (rh))
	    regcount=regcount+1
	else
	    ; one of the pair isn't actually a register, so assume arg is var.
	    rh catstr <arg>, <.high>
	    rl catstr <arg>, <.low>
	endif
    else
        rh catstr <arg>, <.high>
        rl catstr <arg>, <.low>
    endif
endif

else
    ErrMessage <op : missing argument>
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		cmpdw

DESCRIPTION:	Compare dwords

ARGUMENTS:
	dest, source - dest, source to compare
	[trashreg] - re'qd if comparing memory to memory
CAVEATS:
	This is only valid for *unsigned* comparisons.

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

cmpdw	macro	dest, source, trashreg
	local	foo, drl, drh, srl, srh, regargs

regargs = 0
_CheckDWordTwoArg	<dest>, <source>, <cmpdw>
_ParseDWordArg	<dest>, drl, drh, regargs, <cmpdw>
_ParseDWordArg	<source>, srl, srh, regargs, <cmpdw>

if (regargs gt 0)
	cmp	drh, srh
	jne	foo
	cmp	drl, srl
elseifnb <trashreg>
	mov	trashreg, drh
	cmp	trashreg, srh
	jne	foo
	mov	trashreg, drl
	cmp	trashreg, srl
else
	ErrMessage <cmpdw : need trash register to compare mem, mem>
endif

foo:
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		jgedw

DESCRIPTION:	Signed compare and branch >= for dwords

ARGUMENTS:
	dest, source - dest, source to compare
	blabel - label to branch to branch to if >=
	[trashreg] - re'qd if comparing memory to memory

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

jgedw	macro	dest, source, blabel, trashreg
	local	foo, drl, drh, srl, srh, regargs

regargs = 0
_CheckDWordTwoArg	<dest>, <source>, <jgedw>
_ParseDWordArg	<dest>, drl, drh, regargs, <jgedw>
_ParseDWordArg	<source>, srl, srh, regargs, <jgedw>

ifb <blabel>
	ErrMessage <jgedw : need label for branch>
endif

if (regargs gt 0)
	cmp	drh, srh
	jg	blabel
	jl	foo
	cmp	drl, srl
	jae	blabel
foo:
elseifnb <trashreg>
	mov	trashreg, drh
	cmp	trashreg, srh
	jg	blabel
	jl	foo
	mov	trashreg, drl
	cmp	trashreg, srl
	jae	blabel
foo:
else
	ErrMessage <jgedw : need trash register to compare mem, mem>
endif
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		jgdw

DESCRIPTION:	Signed compare and branch > for dwords

ARGUMENTS:
	dest, source - dest, source to compare
	blabel - label to branch to branch to if >
	[trashreg] - re'qd if comparing memory to memory

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

jgdw	macro	dest, source, blabel, trashreg
	local	foo, drl, drh, srl, srh, regargs

regargs = 0
_CheckDWordTwoArg	<dest>, <source>, <jgdw>
_ParseDWordArg	<dest>, drl, drh, regargs, <jgdw>
_ParseDWordArg	<source>, srl, srh, regargs, <jgdw>

ifb <blabel>
	ErrMessage <jgdw : need label for branch>
endif

if (regargs gt 0)
	cmp	drh, srh
	jg	blabel
	jl	foo
	cmp	drl, srl
	ja	blabel
foo:
elseifnb <trashreg>
	mov	trashreg, drh
	cmp	trashreg, srh
	jg	blabel
	jl	foo
	mov	trashreg, drl
	cmp	trashreg, srl
	ja	blabel
foo:
else
	ErrMessage <jgdw : need trash register to compare mem, mem>
endif
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		jledw

DESCRIPTION:	Signed compare and branch <= for dwords

ARGUMENTS:
	dest, source - dest, source to compare
	blabel - label to branch to branch to if <=
	[trashreg] - re'qd if comparing memory to memory

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

jledw	macro	dest, source, blabel, trashreg
	local	foo, drl, drh, srl, srh, regargs

regargs = 0
_CheckDWordTwoArg	<dest>, <source>, <jledw>
_ParseDWordArg	<dest>, drl, drh, regargs, <jledw>
_ParseDWordArg	<source>, srl, srh, regargs, <jledw>

ifb <blabel>
	ErrMessage <jledw : need label for branch>
endif

if (regargs gt 0)
	cmp	drh, srh
	jl	blabel
	jg	foo
	cmp	drl, srl
	jbe	blabel
foo:
elseifnb <trashreg>
	mov	trashreg, drh
	cmp	trashreg, srh
	jl	blabel
	jg	foo
	mov	trashreg, drl
	cmp	trashreg, srl
	jbe	blabel
foo:
else
	ErrMessage <jledw : need trash register to compare mem, mem>
endif
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		jldw

DESCRIPTION:	Signed compare and branch < for dwords

ARGUMENTS:
	dest, source - dest, source to compare
	blabel - label to branch to branch to if <
	[trashreg] - re'qd if comparing memory to memory

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

jldw	macro	dest, source, blabel, trashreg
	local	foo, drl, drh, srl, srh, regargs

regargs = 0
_CheckDWordTwoArg	<dest>, <source>, <jldw>
_ParseDWordArg	<dest>, drl, drh, regargs, <jldw>
_ParseDWordArg	<source>, srl, srh, regargs, <jldw>

ifb <blabel>
	ErrMessage <jldw : need label for branch>
endif

if (regargs gt 0)
	cmp	drh, srh
	jl	blabel
	jg	foo
	cmp	drl, srl
	jb	blabel
foo:
elseifnb <trashreg>
	mov	trashreg, drh
	cmp	trashreg, srh
	jl	blabel
	jg	foo
	mov	trashreg, drl
	cmp	trashreg, srl
	jb	blabel
foo:
else
	ErrMessage <jldw : need trash register to compare mem, mem>
endif
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		tstdw

DESCRIPTION:	Sets the Z flag if the dword is zero.
		Does NOT set the "s" flag correctly (unlike "tst").

ARGUMENTS:
	dest - memory or register pair to test


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
tstdw	macro	dest, arg2

	local	foo, drl, drh, regargs

regargs = 0


_CheckDWordOneArg	<dest>, <tstdw>, <arg2>
_ParseDWordArg	<dest>, drl, drh, regargs, <tstdw>

	tst	drh
	jnz	foo
	tst	drl
foo:

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		pushdw

DESCRIPTION:	Push a dword

ARGUMENTS:
	dest - memory or register pair to push

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
pushdw	macro	dest, arg2
	local	drl, drh, regargs

regargs = 0
_CheckDWordOneArg	<dest>, <pushdw>, <arg2>
_ParseDWordArg	<dest>, drl, drh, regargs, <pushdw>

	push	drh
	push	drl
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		popdw

DESCRIPTION:	Pop a dword

ARGUMENTS:
	dest - memory or register pair to pop into

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
popdw	macro	dest, arg2
	local	drl, drh, regargs

regargs = 0
_CheckDWordOneArg	<dest>, <popdw>, <arg2>
_ParseDWordArg	<dest>, drl, drh, regargs, <popdw>

	pop	drl
	pop	drh
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		notdw

DESCRIPTION:	Not a dword

ARGUMENTS:
	dest - memory or register pair to not

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
notdw	macro	dest, arg2
	local	drl, drh, regargs

regargs = 0
_CheckDWordOneArg	<dest>, <notdw>, <arg2>
_ParseDWordArg	<dest>, drl, drh, regargs, <notdw>

	not	drl
	not	drh
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		negdw

DESCRIPTION:	Negate a dword

ARGUMENTS:
	dest - memory or register pair to not
	[zeroreg] - if you have a register = 0, this is quicker
CAVEATS:
	Does not set flags the same as the 'neg' instruction

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
negdw	macro	dest, zeroreg
	local	drl, drh, regargs

regargs = 0
_CheckDWordOneArg	<dest>, <negdw>
_ParseDWordArg	<dest>, drl, drh, regargs, <negdw>

					;cycles/bytes (registers)
	neg	drh			;3/2
	neg	drl			;3/2
ifnb	<zeroreg>
	sbb	drh, zeroreg		;3/2
else
	sbb	drh, 0			;4/3
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		incdw

DESCRIPTION:	Increment a dword

ARGUMENTS:
	dest - memory or register pair to inc
CAVEATS:
	Does not set flags same as 'inc' instruction,

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
incdw	macro	dest, arg2
	local	drl, drh, regargs

regargs = 0
_CheckDWordOneArg	<dest>, <incdw>, <arg2>
_ParseDWordArg	<dest>, drl, drh, regargs, <incdw>

	add	drl, 1			;3 bytes / 3 cycles
	adc	drh, 0			;3 bytes / 3 cycles

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		decdw

DESCRIPTION:	Decrement a dword

ARGUMENTS:
	dest - memory or register pair to dec
CAVEATS:
	Does not set flags same as 'dec' instruction,

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
decdw	macro	dest, arg2
	local	drl, drh, regargs

regargs = 0
_CheckDWordOneArg	<dest>, <decdw>, <arg2>
_ParseDWordArg	<dest>, drl, drh, regargs, <decdw>

	sub	drl, 1
	sbb	drh, 0
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		movdw

DESCRIPTION:	Move a dword

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if moving memory to memory

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
movdw	macro	dest, source, trashreg
	local	drl, drh, srl, srh, regargs

regargs = 0
_CheckDWordTwoArg	<dest>, <source>, <movdw>
_ParseDWordArg	<dest>, drl, drh, regargs, <movdw>
_ParseDWordArg	<source>, srl, srh, regargs, <movdw>

if (regargs gt 0)
    if (((.type (drh)) and 0x4010) eq 0x4010) and \
       (((.type (srh)) and 0x4010) eq 0x4010)
	; both are segment registers, so must use segmov. use trashreg if given
	segmov	drh, srh, trashreg
	mov	drl, srl
    elif (((.type (drh)) and 0x4010) eq 0x4010) and \
	 (((.type (drh)) and 0x0700) eq 0x0000) and \
	 (((.type (srh)) and 0x0010) eq 0)
	; dest is es + reg, while source is mem, so use les
	les	drl, source
    elif (((.type (drh)) and 0x4010) eq 0x4010) and \
	 (((.type (drh)) and 0x0700) eq 0x0300) and \
	 (((.type (srh)) and 0x0010) eq 0)
	; dest is ds + reg, while source is mem, so use lds
	lds	drl, source
    elifidn drh,srl
	mov	drl, srl
	mov	drh, srh
    else
	mov	drh, srh
	mov	drl, srl
    endif
elseifnb <trashreg>
	mov	trashreg, srh
	mov	drh, trashreg
	mov	trashreg, srl
	mov	drl, trashreg
else
	ErrMessage <movdw : need trash register to move mem, mem>
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		xchgdw

DESCRIPTION:	Exchange two dwords

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if moving memory to memory

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
xchgdw	macro	dest, source, trashreg
	local	drl, drh, srl, srh, regargs

regargs = 0
_CheckDWordTwoArg	<dest>, <source>, <xchgdw>
_ParseDWordArg	<dest>, drl, drh, regargs, <xchgdw>
_ParseDWordArg	<source>, srl, srh, regargs, <xchgdw>

if (regargs gt 0)
	xchg	drh, srh
	xchg	drl, srl
elseifnb <trashreg>
	mov	trashreg, srh
	xchg	drh, trashreg
	mov	srh, trashreg
	mov	trashreg, srl
	xchg	drl, trashreg
	mov	srl, trashreg
else
	ErrMessage <xchgdw : need trash register to xchg mem, mem>
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		adddw

DESCRIPTION:	Add a dword

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if adding memory to memory

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
adddw	macro	dest, source, trashreg
	local	drl, drh, srl, srh, regargs

regargs = 0
_CheckDWordTwoArg	<dest>, <source>, <adddw>
_ParseDWordArg	<dest>, drl, drh, regargs, <adddw>
_ParseDWordArg	<source>, srl, srh, regargs, <adddw>

if (regargs gt 0)
	add	drl, srl
	adc	drh, srh
elseifnb <trashreg>
	mov	trashreg, srl
	add	drl, trashreg
	mov	trashreg, srh
	adc	drh, trashreg
else
	ErrMessage <adddw : need trash register to add mem, mem>
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		adcdw

DESCRIPTION:	Add with carry a dword

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if adding memory to memory

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
adcdw	macro	dest, source, trashreg
	local	drl, drh, srl, srh, regargs

regargs = 0
_CheckDWordTwoArg	<dest>, <source>, <adcdw>
_ParseDWordArg	<dest>, drl, drh, regargs, <adcdw>
_ParseDWordArg	<source>, srl, srh, regargs, <adcdw>

if (regargs gt 0)
	adc	drl, srl
	adc	drh, srh
elseifnb <trashreg>
	mov	trashreg, srl
	adc	drl, trashreg
	mov	trashreg, srh
	adc	drh, trashreg
else
	ErrMessage <adcdw : need trash register to adc mem, mem>
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		subdw

DESCRIPTION:	Subtract a dword

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if adding memory to memory

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
subdw	macro	dest, source, trashreg
	local	drl, drh, srl, srh, regargs

regargs = 0
_CheckDWordTwoArg	<dest>, <source>, <subdw>
_ParseDWordArg	<dest>, drl, drh, regargs, <subdw>
_ParseDWordArg	<source>, srl, srh, regargs, <subdw>

if (regargs gt 0)
	sub	drl, srl
	sbb	drh, srh
elseifnb <trashreg>
	mov	trashreg, srl
	sub	drl, trashreg
	mov	trashreg, srh
	sbb	drh, trashreg
else
	ErrMessage <subdw : need trash register to sub mem, mem>
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		sbbdw

DESCRIPTION:	Subtract with borrow a dword

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if adding memory to memory

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
sbbdw	macro	dest, source, trashreg
	local	drl, drh, srl, srh, regargs

regargs = 0
_CheckDWordTwoArg	<dest>, <source>, <sbbdw>
_ParseDWordArg	<dest>, drl, drh, regargs, <sbbdw>
_ParseDWordArg	<source>, srl, srh, regargs, <sbbdw>

if (regargs gt 0)
	sbb	drl, srl
	sbb	drh, srh
elseifnb <trashreg>
	mov	trashreg, srl
	sbb	drl, trashreg
	mov	trashreg, srh
	sbb	drh, trashreg
else
	ErrMessage <sbbdw : need trash register to sbb mem, mem>
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		clrdw

DESCRIPTION:	Clear a dword

ARGUMENTS:
	dest - memory or register pair to clear
	[zeroreg] - if you have a register = 0, this is quicker

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
clrdw	macro	dest, zeroreg
	local	drl, drh, regargs

regargs = 0
_CheckDWordOneArg	<dest>, <clrdw>
_ParseDWordArg	<dest>, drl, drh, regargs, <clrdw>

ifnb	<zeroreg>
	mov	drh, zeroreg
	mov	drl, zeroreg
elseif (regargs gt 0)
	clr	drh
	mov	drl, drh
else
	clr	drh
	clr	drl
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		sardw

DESCRIPTION:	Arithmetic shift right for dword

ARGUMENTS:
	dest - memory or register pair to shift right
CAVEATS:
	Does not set flags the same as the 'sar' instruction

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

sardw	macro	dest, arg2
	local	drl, drh, regargs

regargs = 0
_CheckDWordOneArg	<dest>, <sardw>, <arg2>
_ParseDWordArg	<dest>, drl, drh, regargs, <sardw>

	sar	drh, 1
	rcr	drl, 1

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		shrdw

DESCRIPTION:	Logical shift right for dword

ARGUMENTS:
	dest - memory or register pair to shift right
CAVEATS:
	Does not set flags the same as the 'shr' instruction

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

shrdw	macro	dest, arg2
	local	drl, drh, regargs

regargs = 0
_CheckDWordOneArg	<dest>, <shrdw>, <arg2>
_ParseDWordArg	<dest>, drl, drh, regargs, <shrdw>

	shr	drh, 1
	rcr	drl, 1

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		shldw, saldw

DESCRIPTION:	Logical/Arithmetic shift left for dword

ARGUMENTS:
	dest - memory or register pair to shift left
CAVEATS:
	Does not set flags the same as the 'shl/sal' instruction

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

shldw	macro	dest, arg2
	local	drl, drh, regargs

regargs = 0
_CheckDWordOneArg	<dest>, <shldw>, <arg2>
_ParseDWordArg	<dest>, drl, drh, regargs, <shldw>

	shl	drl, 1
	rcl	drh, 1

endm

saldw	macro	dest
	shldw	dest
endm



COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:	WWFixed math macros

ARGUMENTS:
	See dword math macros

EXAMPLES:
	negwwf	dxax
	addwwf	bxax, dxcx
	cmpwwf	dxax, ss:[bp].MRVP_bounds.R_left
	movwwf	dxcx, 0x12345678
	jgewwf	ds:[si], dxax, aLabel
	rndwwf	dxax
	rnduwwf	dxax
NOTE:
	ASSUMES: non-constant argument of 4 letters is a register pair
	ASSUMES: non-constant argument of less than 4 letters is bogus

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

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		cmpwwf

DESCRIPTION:	Compare WWFixed's (unsigned)

ARGUMENTS:
	dest, source - dest, source to compare
	[trashreg] - re'qd if comparing memory to memory
CAVEATS:
	This is only valid for *unsigned* comparisons.

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

cmpwwf	macro	dest, source, trashreg
	cmpdw	<dest>, <source>, <trashreg>
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		jgewwf, jgwwf, jlewwf, jlwwf

DESCRIPTION:	Signed compare and branch instructions for WWFixed's

ARGUMENTS:
	dest, source - dest, source to compare
	blabel - label to branch to branch to if >=
	[trashreg] - re'qd if comparing memory to memory

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

jgewwf	macro	dest, source, blabel, trashreg
	jgedw	<dest>, <source>, blabel, <trashreg>
endm

jgwwf	macro	dest, source, blabel, trashreg
	jgdw	<dest>, <source>, blabel, <trashreg>
endm

jlewwf	macro	dest, source, blabel, trashreg
	jledw	<dest>, <source>, blabel, <trashreg>
endm

jlwwf	macro	dest, source, blabel, trashreg
	jldw	<dest>, <source>, blabel, <trashreg>
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		tstwwf

DESCRIPTION:	Test a WWFixed

ARGUMENTS:
	dest - memory or register pair to test

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

tstwwf	macro	dest
	tstdw	<dest>
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		pushwwf, popwwf

DESCRIPTION:	Push or pop a WWFixed

ARGUMENTS:
	dest - memory or register pair to push or pop into

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

pushwwf	macro	dest
	pushdw	<dest>
endm

popwwf	macro	dest
	popdw	<dest>
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		notwwf

DESCRIPTION:	Not a WWFixed

ARGUMENTS:
	dest - memory or register pair to not

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

notwwf	macro	dest
	notdw	<dest>
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		negwwf

DESCRIPTION:	Negate a WWFixed

ARGUMENTS:
	dest - memory or register pair to not
	[zeroreg] - if you have a register = 0, this is quicker
CAVEATS:
	Does not set flags the same as the 'neg' instruction

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

negwwf	macro	dest, zeroreg
	negdw	<dest>, <zeroreg>
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		incwwf, decwwf

DESCRIPTION:	Increment or decrement a WWFixed

ARGUMENTS:
	dest - memory or register pair to inc
CAVEATS:
	Does not set flags same as 'inc' instruction,

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

incwwf	macro	dest
	incdw	<dest>
endm

decwwf	macro	dest
	decdw	<dest>
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		movwwf, xchgwwf

DESCRIPTION:	Move or exchange a WWFixed

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if moving memory to memory

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

movwwf	macro	dest, source, trashreg
	movdw	<dest>, <source>, <trashreg>
endm

xchgwwf	macro	dest, source, trashreg
	xchgdw	<dest>, <source>, <trashreg>
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		addwwf, subwwf

DESCRIPTION:	Add or subtract a WWFixed

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if adding memory to memory

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

addwwf	macro	dest, source, trashreg
	adddw	<dest>, <source>, <trashreg>
endm

subwwf	macro	dest, source, trashreg
	subdw	<dest>, <source>, <trashreg>
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		adcwwf, sbbwwf

DESCRIPTION:	Add or subtract wiht carry or borrow a WWFixed

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if adding memory to memory

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

adcwwf	macro	dest, source, trashreg
	adcdw	<dest>, <source>, <trashreg>
endm

sbbwwf	macro	dest, source, trashreg
	sbbdw	<dest>, <source>, <trashreg>
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		clrwwf

DESCRIPTION:	Clear a WWFixed

ARGUMENTS:
	dest - memory or register pair to clear
	[zeroreg] - if you have a register = 0, this is quicker

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

clrwwf	macro	dest, zeroreg
	clrdw	<dest>, <zeroreg>
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		sarwwf

DESCRIPTION:	Arithmetic shift right for WWFixed

ARGUMENTS:
	dest - memory or register pair to shift right
CAVEATS:
	Does not set flags the same as the 'sar' instruction

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

sarwwf	macro	dest
	sardw	<dest>
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		shrwwf

DESCRIPTION:	Logical shift right for WWFixed

ARGUMENTS:
	dest - memory or register pair to shift right
CAVEATS:
	Does not set flags the same as the 'shr' instruction

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

shrwwf	macro	dest
	shrdw	<dest>
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		shlwwf, salwwf

DESCRIPTION:	Logical/Arithmetic shift left for WWFixed

ARGUMENTS:
	dest - memory or register pair to shift left
CAVEATS:
	Does not set flags the same as the 'shl/sal' instruction

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

shlwwf	macro	dest
	shldw	<dest>
endm

salwwf	macro	dest
	shlwwf	<dest>
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		rndwwf

DESCRIPTION:	Round a WWFixed value to a single word

ARGUMENTS:
	source - WWFixed value to round to a single word
	dest   - Destination word (optional)

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

rndwwf	macro	source, dest
	local	round1, round2, done, drf, dri, regargs

regargs	= 0
_CheckDWordOneArg	<source>, <rndwwf>
_ParseDWordArg		<source>, drf, dri, regargs, <rndwwf>

ifnb <dest>
	;;
	;; Make sure that the destination is not the same as the fractional
	;; part of the source. If we don't do this, we may end up trashing
	;; something we try to test later.
	;;
	ifdif	dri, <dest>
	    ifidn <dest>, drf
		ErrMessage <rndwwf: Will destroy fraction before testing it>
	    else
		mov	dest, dri
	    endif
	endif

	cmp	drf, 8000h
	jb	done
	ja	round1
	tst	dest
	js	done
round1:
	inc	dest
else
	cmp	drf, 8000h
	jb	done
	ja	round2
	tst	dri
	js	done
round2:
	inc	dri
endif
done:
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		rnduwwf

DESCRIPTION:	Round an unsigned WWFixed value to a single word

ARGUMENTS:
	source - WWFixed value (unsigned) to round to a single word
	dest   - Destination word (optional)

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

rnduwwf	macro	source, dest
	local	round1, round2, done, drf, dri, regargs

regargs	= 0
_CheckDWordOneArg	<source>, <rnduwwf>
_ParseDWordArg		<source>, drf, dri, regargs, <rnduwwf>

ifnb <dest>
	;;
	;; Make sure that the destination is not the same as the fractional
	;; part of the source. If we don't do this, we may end up trashing
	;; something we try to test later.
	;;
	ifdif	dri, <dest>
	    ifidn <dest>, drf
		ErrMessage <rnduwwf: Will destroy fraction before testing it>
	    else
		mov	dest, dri
	    endif
	endif

	cmp	drf, 8000h
	jb	done
	inc	dest
else
	cmp	drf, 8000h
	jb	done
	inc	dri
endif
done:
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		rndwwbf

DESCRIPTION:	Round a WWFixed value to a WBFixed

ARGUMENTS:
	source - A WWFixed value to round

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

rndwwbf	macro	source
	local	round, done, drf, dri, regargs

regargs	= 0
_CheckDWordOneArg	<source>, <rndwwnf>
_ParseDWordArg		<source>, drf, dri, regargs, <rndwwbf>

	cmp	{byte}drf, 80h
	jb	done
	ja	round
	tst	dri
	js	done
round:
	add	drf, 0100h
	adc	dri, 0
done:
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		DWFixed math macros

ARGUMENTS:
	There are three classes of DWFixed math macros: one-argument,
	two-argument, and signed compare macros.  If stored in registers,
	dwords are traditionally referred to by either regHregLregF or
	regH:regL.regF (eg. dxcxbx or dx:cx.bx), where the high word comes
	first.  If stored in memory, remember the 80x86 machines store data
	LSB first, so it will be stored regF, regL, regH.

	For one-argument functions, the destination can either a register
	triplet or memory:

	dest
	----
	regtriplet
	memory

	For two-argument functions, the arguments can either be a register
	pair and memory, two register pairs, or a constant and a register
	pair:

	dest		source
	------		----
	regtriplet	memory
	regtriplet	constant
	regtriplet	regtriplet
	memory		regtriplet
	memory		constant
	memory		memory		NOTE: requires trash register

	Signed compare functions are necessary because for a signed DWFixed,
	the high word is signed, but the low word is not.  This means that
	the 'cmpdw' macro will not work -- it will return flags for greater
	than for both the high and low words, which will give incorrect
	results.  As with the two-argument functions, the arguments can either
	be a register triplet and memory, two register triplets, or a constant
	and a register triplet.  They also take a label to branch to if the
	specified condition is true.

EXAMPLES:
	negdwf	dxbxax
	adddwf	cxbxax, sididx
	cmpdwf	dxcxax, ss:[bp].FOO_bar
	movdwf	dxcxax, 0x123456789ABC
	jgedwf	ds:[si], dxcxax, aLabel
	rnddwf	dxcxax
	rndudwf	dxcxax

NOTE:
	ASSUMES: non-constant argument of 6 letters is a register triplet
	ASSUMES: non-constant argument of less than 6 letters is bogus

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


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		_CheckDWFixedOneArg

DESCRIPTION:	Check argument to one-arg DWFixed macro

ARGUMENTS:
	dest - argument side
	op - string to emit for error-identification

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
_CheckDWFixedOneArg	macro	dest, op

ifnb <dest>
    if ((.TYPE &dest) AND 4)
	ErrMessage <op : single argument cannot be constant>
    endif
else
    ErrMessage <op : missing argument>
endif
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		_CheckDWFixedTwoArg

DESCRIPTION:	Check arguments to two-arg DWFixed macro

ARGUMENTS:
	dest - left-hand side
	src - right-hand side
	op - string to emit for error-identification

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
_CheckDWFixedTwoArg	macro	dest, src, op

ifnb	<src>
    ifidn <dest>, <src>
	ErrMessage <op : destination = source>
    else
	if ((.TYPE &dest) AND 4)
	    ErrMessage <op : destination argument cannot be constant>
	endif
    endif
endif
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		_ParseDWFixedArg

DESCRIPTION:	Parse an argument to a DWFixed math macro

PASS:
	arg - argument passed to macro
	regcount - # of register-pair arguments
	op - string to emit for error-identification
RETURN:
	rf - fractional part of arg
	rl - low part of arg
	rh - high part of arg
	regcount - # of register-pair or constant arguments

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

_ParseDWFixedArg	macro	arg, rf, rl, rh, regcount, op

alen	sizestr	<&arg>

ifnb <arg>

if (4 AND .TYPE (arg))
    ErrMessage <op : ParseDWFixedArg doesn't deal with constants>
    regcount=regcount+1
else
    if (alen eq 6)
	rh substr <arg>, 1, 2
	rl substr <arg>, 3, 2
	rf substr <arg>, 5, 2
;	ifidn rl, rh
;	    ErrMessage <op : register used twice in DWFixed>
;	endif
;	ifidn rl, rf
;	    ErrMessage <op : register used twice in DWFixed>
;	endif
;	ifidn rf, rh
;	    ErrMessage <op : register used twice in DWFixed>
;	endif
	regcount=regcount+1
    else
        rh catstr <arg>, <.DWF_int.high>
        rl catstr <arg>, <.DWF_int.low>
	rf catstr <arg>, <.DWF_frac>
    endif
endif

else
    ErrMessage <op : missing argument>
endif
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		cmpdwf

DESCRIPTION:	Compare DWFixed's (unsigned only)

ARGUMENTS:
	dest, source - dest, source to compare
	[trashreg] - re'qd if comparing memory to memory
CAVEATS:
	This is only valid for *unsigned* comparisons.

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

cmpdwf	macro	dest, source, trashreg
	local	foo, drf, drl, drh, srf, srl, srh, regargs

regargs = 0
_CheckDWFixedTwoArg	<dest>, <source>, <cmpdw>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <cmpdw>
_ParseDWFixedArg	<source>, srf, srl, srh, regargs, <cmpdw>

if (regargs gt 0)
	cmp	drh, srh
	jne	foo
	cmp	drl, srl
	jne	foo
	cmp	drf, srf
elseifnb <trashreg>
	mov	trashreg, drh
	cmp	trashreg, srh
	jne	foo
	mov	trashreg, drl
	cmp	trashreg, srl
	jne	foo
	mov	trashreg, drf
	cmp	trashreg, srf
else
	ErrMessage <cmpdwf : need trash register to compare mem, mem>
endif

foo:
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		jgedwf

DESCRIPTION:	Signed compare and branch >= for DWFixed's

ARGUMENTS:
	dest, source - dest, source to compare
	blabel - label to branch to branch to if >=
	[trashreg] - re'qd if comparing memory to memory

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

jgedwf	macro	dest, source, blabel, trashreg
	local	foo, drf, drl, drh, srf, srl, srh, regargs

regargs = 0
_CheckDWFixedTwoArg	<dest>, <source>, <jgedwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <jgedwf>
_ParseDWFixedArg	<source>, srf, srl, srh, regargs, <jgedwf>

ifb <blabel>
	ErrMessage <jgedwf : need label for branch>
endif

if (regargs gt 0)
	cmp	drh, srh
	jg	blabel
	jl	foo
	cmp	drl, srl
	ja	blabel
	jb	foo
	cmp	drf, srf
	jae	blabel
foo:
elseifnb <trashreg>
	mov	trashreg, drh
	cmp	trashreg, srh
	jg	blabel
	jl	foo
	mov	trashreg, drl
	cmp	trashreg, srl
	ja	blabel
	jb	foo
	mov	trashreg, drf
	cmp	trashreg, srf
	jae	blabel
foo:
else
	ErrMessage <jgedwf : need trash register to compare mem, mem>
endif
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		jgdwf

DESCRIPTION:	Signed compare and branch > for DWFixed's

ARGUMENTS:
	dest, source - dest, source to compare
	blabel - label to branch to branch to if >
	[trashreg] - re'qd if comparing memory to memory

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

jgdwf	macro	dest, source, blabel, trashreg
	local	foo, drf, drl, drh, srf, srl, srh, regargs

regargs = 0
_CheckDWFixedTwoArg	<dest>, <source>, <jgdwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <jgdwf>
_ParseDWFixedArg	<source>, srf, srl, srh, regargs, <jgdwf>

ifb <blabel>
	ErrMessage <jgdwf : need label for branch>
endif

if (regargs gt 0)
	cmp	drh, srh
	jg	blabel
	jl	foo
	cmp	drl, srl
	ja	blabel
	jb	foo
	cmp	drf, srf
	ja	blabel
foo:
elseifnb <trashreg>
	mov	trashreg, drh
	cmp	trashreg, srh
	jg	blabel
	jl	foo
	mov	trashreg, drl
	cmp	trashreg, srl
	ja	blabel
	jb	foo
	mov	trashreg, drf
	cmp	trashreg, srf
	ja	blabel
foo:
else
	ErrMessage <jgdwf : need trash register to compare mem, mem>
endif
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		jledwf

DESCRIPTION:	Signed compare and branch <= for DWFixed's

ARGUMENTS:
	dest, source - dest, source to compare
	blabel - label to branch to branch to if <=
	[trashreg] - re'qd if comparing memory to memory

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

jledwf	macro	dest, source, blabel, trashreg
	local	foo, drf, drl, drh, srf, srl, srh, regargs

regargs = 0
_CheckDWFixedTwoArg	<dest>, <source>, <jledwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <jledwf>
_ParseDWFixedArg	<source>, srf, srl, srh, regargs, <jledwf>

ifb <blabel>
	ErrMessage <jledwf : need label for branch>
endif

if (regargs gt 0)
	cmp	drh, srh
	jl	blabel
	jg	foo
	cmp	drl, srl
	jb	blabel
	ja	foo
	cmp	drf, srf
	jbe	blabel
foo:
elseifnb <trashreg>
	mov	trashreg, drh
	cmp	trashreg, srh
	jl	blabel
	jg	foo
	mov	trashreg, drl
	cmp	trashreg, srl
	jb	blabel
	ja	foo
	mov	trashreg, drf
	cmp	trashreg, srf
	jbe	blabel
foo:
else
	ErrMessage <jledwf : need trash register to compare mem, mem>
endif
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		jldwf

DESCRIPTION:	Signed compare and branch < for dwfords

ARGUMENTS:
	dest, source - dest, source to compare
	blabel - label to branch to branch to if <
	[trashreg] - re'qd if comparing memory to memory

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

jldwf	macro	dest, source, blabel, trashreg
	local	foo, drfm drl, drh, srfm srl, srh, regargs

regargs = 0
_CheckDWFixedTwoArg	<dest>, <source>, <jldwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <jldwf>
_ParseDWFixedArg	<source>, srf, srl, srh, regargs, <jldwf>

ifb <blabel>
	ErrMessage <jldwf : need label for branch>
endif

if (regargs gt 0)
	cmp	drh, srh
	jl	blabel
	jg	foo
	cmp	drl, srl
	jb	blabel
	ja	foo
	cmp	drf, srf
	jb	blabel
foo:
elseifnb <trashreg>
	mov	trashreg, drh
	cmp	trashreg, srh
	jl	blabel
	jg	foo
	mov	trashreg, drl
	cmp	trashreg, srl
	jb	blabel
	ja	foo
	mov	trashreg, drf
	cmp	trashreg, srf
	jb	blabel
foo:
else
	ErrMessage <jldwf : need trash register to compare mem, mem>
endif
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		tstdwf

DESCRIPTION:	Sets the Z flag if the DWFixed is zero.
		Does NOT set the "s" flag correctly (unlike "tst").

ARGUMENTS:
	dest - memory or register pair to test


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
tstdwf	macro	dest
	local	foo, drf, drl, drh, regargs

regargs = 0
_CheckDWFixedOneArg	<dest>, <tstdwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <tstdwf>

	tst	drh
	jnz	foo
	tst	drl
	jnz	foo
	tst	drf
foo:
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		pushdwf

DESCRIPTION:	Push a DWFixed

ARGUMENTS:
	dest - memory or register pair to push

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
pushdwf	macro	dest
	local	drf, drl, drh, regargs

regargs = 0
_CheckDWFixedOneArg	<dest>, <pushdwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <pushdwf>

	push	drh, drl, drf
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		popdwf

DESCRIPTION:	Pop a DWFixed

ARGUMENTS:
	dest - memory or register pair to pop into

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
popdwf	macro	dest
	local	drf, drl, drh, regargs

regargs = 0
_CheckDWFixedOneArg	<dest>, <popdwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <popdwf>

	pop	drh, drl, drf
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		notdwf

DESCRIPTION:	Not a DWFixed

ARGUMENTS:
	dest - memory or register pair to not

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
notdwf	macro	dest
	local	drf, drl, drh, regargs

regargs = 0
_CheckDWFixedOneArg	<dest>, <notdwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <notdwf>

	not	drf
	not	drl
	not	drh
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		negdwf

DESCRIPTION:	Negate a DWFixed

ARGUMENTS:
	dest - memory or register pair to not
	[zeroreg] - if you have a register = 0, this is quicker
CAVEATS:
	Does not set flags the same as the 'neg' instruction

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
negdwf	macro	dest, zeroreg
	local	drf, drl, drh, regargs

regargs = 0
_CheckDWFixedOneArg	<dest>, <negdwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <negdwf>

					;cycles/bytes (registers)
	not	drh			;3/2
	not	drl			;3/2
	neg	drf			;3/2 (set/clear carry flag)
	cmc
ifnb	<zeroreg>
	adc	drl, zeroreg		;3/2
	adc	drh, zeroreg		;3/2
else
	adc	drl, 0			;4/3
	adc	drh, 0
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		incdwf

DESCRIPTION:	Increment a DWFixed

ARGUMENTS:
	dest - memory or register pair to inc
CAVEATS:
	Does not set flags same as 'inc' instruction,

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
incdwf	macro	dest
	local	drf, drl, drh, regargs

regargs = 0
_CheckDWFixedOneArg	<dest>, <incdwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <incdwf>

	add	drf, 1
	adc	drl, 0			;3 bytes / 3 cycles
	adc	drh, 0			;3 bytes / 3 cycles

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		decdwf

DESCRIPTION:	Decrement a DWFixed

ARGUMENTS:
	dest - memory or register pair to dec
CAVEATS:
	Does not set flags same as 'dec' instruction,

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
decdwf	macro	dest
	local	drf, drl, drh, regargs

regargs = 0
_CheckDWFixedOneArg	<dest>, <decdwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <decdwf>

	sub	drf, 1
	sbb	drl, 0
	sbb	drh, 0
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		movdwf

DESCRIPTION:	Move a DWFixed

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if moving memory to memory

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
movdwf	macro	dest, source, trashreg
	local	drf, drl, drh, srf, srl, srh, regargs

regargs = 0
_CheckDWFixedTwoArg	<dest>, <source>, <movdwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <movdwf>
_ParseDWFixedArg	<source>, srf, srl, srh, regargs, <movdwf>

if (regargs gt 0)
	mov	drh, srh
	mov	drl, srl
	mov	drf, srf
elseifnb <trashreg>
	mov	trashreg, srh
	mov	drh, trashreg
	mov	trashreg, srl
	mov	drl, trashreg
	mov	trashreg, srf
	mov	drf, trashreg
else
	ErrMessage <movdwf : need trash register to move mem, mem>
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		xchgdwf

DESCRIPTION:	Exchange two DWFixed's

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if moving memory to memory

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
xchgdwf	macro	dest, source, trashreg
	local	drf, drl, drh, srf, srl, srh, regargs

regargs = 0
_CheckDWFixedTwoArg	<dest>, <source>, <xchgdwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <xchgdwf>
_ParseDWFixedArg	<source>, srf, srl, srh, regargs, <xchgdwf>

if (regargs gt 0)
	xchg	drh, srh
	xchg	drl, srl
	xchg	drf, srf
elseifnb <trashreg>
	mov	trashreg, srh
	xchg	drh, trashreg
	mov	srh, trashreg
	mov	trashreg, srl
	xchg	drl, trashreg
	mov	srl, trashreg
	mov	trashreg, srf
	xchg	drf, trashreg
	mov	srf, trashreg
else
	ErrMessage <xchgdwf : need trash register to xchg mem, mem>
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		adddwf

DESCRIPTION:	Add a DWFixed

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if adding memory to memory

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
adddwf	macro	dest, source, trashreg
	local	drf, drl, drh, srf, srl, srh, regargs

regargs = 0
_CheckDWFixedTwoArg	<dest>, <source>, <adddwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <adddwf>
_ParseDWFixedArg	<source>, srf, srl, srh, regargs, <adddwf>

if (regargs gt 0)
	add	drf, srf
	adc	drl, srl
	adc	drh, srh
elseifnb <trashreg>
	mov	trashreg, srf
	add	drf, trashreg
	mov	trashreg, srl
	adc	drl, trashreg
	mov	trashreg, srh
	adc	drh, trashreg
else
	ErrMessage <adddwf : need trash register to add mem, mem>
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		adcdwf

DESCRIPTION:	Add with carry a DWFixed

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if adding memory to memory

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
adcdwf	macro	dest, source, trashreg
	local	drf, drl, drh, srf, srl, srh, regargs

regargs = 0
_CheckDWFixedTwoArg	<dest>, <source>, <adddwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <adddwf>
_ParseDWFixedArg	<source>, srf, srl, srh, regargs, <adddwf>

if (regargs gt 0)
	adc	drf, srf
	adc	drl, srl
	adc	drh, srh
elseifnb <trashreg>
	mov	trashreg, srf
	adc	drf, trashreg
	mov	trashreg, srl
	adc	drl, trashreg
	mov	trashreg, srh
	adc	drh, trashreg
else
	ErrMessage <adcdwf : need trash register to adc mem, mem>
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		subdwf

DESCRIPTION:	Subtract a DWFixed

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if adding memory to memory

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
subdwf	macro	dest, source, trashreg
	local	drf, drl, drh, srf, srl, srh, regargs

regargs = 0
_CheckDWFixedTwoArg	<dest>, <source>, <subdwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <subdwf>
_ParseDWFixedArg	<source>, srf, srl, srh, regargs, <subdwf>

if (regargs gt 0)
	sub	drf, srf
	sbb	drl, srl
	sbb	drh, srh
elseifnb <trashreg>
	mov	trashreg, srf
	sub	drf, trashreg
	mov	trashreg, srl
	sbb	drl, trashreg
	mov	trashreg, srh
	sbb	drh, trashreg
else
	ErrMessage <subdwf : need trash register to sub mem, mem>
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		sbbdwf

DESCRIPTION:	Subtract with borrow a DWFixed

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if adding memory to memory

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
sbbdwf	macro	dest, source, trashreg
	local	drf, drl, drh, srf, srl, srh, regargs

regargs = 0
_CheckDWFixedTwoArg	<dest>, <source>, <subdwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <subdwf>
_ParseDWFixedArg	<source>, srf, srl, srh, regargs, <subdwf>

if (regargs gt 0)
	sbb	drf, srf
	sbb	drl, srl
	sbb	drh, srh
elseifnb <trashreg>
	mov	trashreg, srf
	sbb	drf, trashreg
	mov	trashreg, srl
	sbb	drl, trashreg
	mov	trashreg, srh
	sbb	drh, trashreg
else
	ErrMessage <sbbdwf : need trash register to sbb mem, mem>
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		clrdwf

DESCRIPTION:	Clear a DWFixed

ARGUMENTS:
	dest - memory or register pair to clear
	[zeroreg] - if you have a register = 0, this is quicker

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
clrdwf	macro	dest, zeroreg
	local	drf, drl, drh, regargs

regargs = 0
_CheckDWFixedOneArg	<dest>, <clrdwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <clrdwf>

ifnb	<zeroreg>
	mov	drh, zeroreg
	mov	drl, zeroreg
	mov	drf, zeroreg
elseif (regargs gt 0)
	clr	drh
	mov	drl, drh
	mov	drf, drh
else
	clr	drh
	clr	drl
	clr	drf
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		sardwf

DESCRIPTION:	Arithmetic shift right for DWFixed

ARGUMENTS:
	dest - memory or register pair to shift right
CAVEATS:
	Does not set flags the same as the 'sar' instruction

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

sardwf	macro	dest
	local	drf, drl, drh, regargs

regargs = 0
_CheckDWFixedOneArg	<dest>, <sardwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <sardwf>

	sar	drh, 1
	rcr	drl, 1
	rcr	drf, 1

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		shrdwf

DESCRIPTION:	Logical shift right for DWFixed

ARGUMENTS:
	dest - memory or register pair to shift right
CAVEATS:
	Does not set flags the same as the 'shr' instruction

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

shrdwf	macro	dest
	local	drf, drl, drh, regargs

regargs = 0
_CheckDWFixedOneArg	<dest>, <shrdwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <shrdwf>

	shr	drh, 1
	rcr	drl, 1
	rcr	drf, 1

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		shldwf, saldwf

DESCRIPTION:	Logical/Arithmetic shift left for DWFixed

ARGUMENTS:
	dest - memory or register pair to shift left
CAVEATS:
	Does not set flags the same as the 'shl/sal' instruction

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

shldwf	macro	dest
	local	drf, drl, drh, regargs

regargs = 0
_CheckDWFixedOneArg	<dest>, <shldwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <shldwf>

	shl	drf, 1
	rcl	drl, 1
	rcl	drh, 1

endm

saldwf	macro	dest
	shldwf	dest
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		rnddwf

DESCRIPTION:	Round a signed DWFixed value to a dword

ARGUMENTS:
	dest - memory or register DWFixed value to round to a double-word

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

rnddwf	macro	dest
	local	round, done, drf, drl, drh, regargs

regargs = 0
_CheckDWFixedOneArg	<dest>, <rnddwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <rnddwf>

	cmp	drf, 8000h
	jb	done
	ja	round
	tst	drh
	js	done
round:
	add	drl, 1
	adc	drh, 0
done:
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		rndudwf

DESCRIPTION:	Round an unsigned DWFixed value to a dword

ARGUMENTS:
	dest - memory or register DWFixed value to round to a double-word

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

rndudwf	macro	dest
	local	round, done, drf, drl, drh, regargs

regargs = 0
_CheckDWFixedOneArg	<dest>, <rndudwf>
_ParseDWFixedArg	<dest>, drf, drl, drh, regargs, <rndudwf>

	cmp	drf, 8000h
	jb	done
	add	drl, 1
	adc	drh, 0
done:
endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		WBFixed math macros

ARGUMENTS:
	There are three classes of WBFixed math macros: one-argument,
	two-argument, and signed compare macros.  If stored in registers,
	WBFixeds are traditionally referred to by regH.regL (eg. dxal or
	dx.al), where the high word comes first.  If stored in memory, 
	remember the 80x86 machines store data LSB first, so it will be
	stored regL, regH.

	For one-argument functions, the destination can either a register
	pair or memory:

	dest
	----
	regpair
	memory

	For two-argument functions, the arguments can either be a register
	pair and memory, two register pairs, or a constant and a register
	pair:

	dest		source
	------		----
	regpair		memory
	regpair		constant
	regpair		regpair
	memory		regpair
	memory		constant
	memory		memory		NOTE: requires trash register

	Signed compare functions are necessary because for a signed WBFixed,
	the high word is signed, but the low byte is not.  This means that
	the 'cmpwbf' macro will not work -- it will return flags for greater
	than for both the high and low words, which will give incorrect
	results.  As with the two-argument functions, the arguments can either
	be a register pair and memory, two register pairs, or a constant
	and a register pair.  They also take a label to branch to if the
	specified condition is true.

EXAMPLES:
	negwbf	dxal
	addwbf	cxal, sidl
	cmpwbf	dxal, ss:[bp].FOO_bar
	movwbf	dxal, 0x123456
	jgewbf	ds:[si], dxal, aLabel

NOTE:
	ASSUMES: non-constant argument of 4 letters is a register pair
	ASSUMES: non-constant argument of less than 4 letters is bogus

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


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		_CheckWBFixedOneArg

DESCRIPTION:	Check argument to one-arg WBFixed macro

ARGUMENTS:
	dest - argument side
	op - string to emit for error-identification

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
_CheckWBFixedOneArg	macro	dest, op

ifnb <dest>
    if ((.TYPE &dest) AND 4)
	ErrMessage <op : single argument cannot be constant>
    endif
else
    ErrMessage <op : missing argument>
endif
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		_CheckWBFixedTwoArg

DESCRIPTION:	Check arguments to two-arg WBFixed macro

ARGUMENTS:
	dest - left-hand side
	src - right-hand side
	op - string to emit for error-identification

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
_CheckWBFixedTwoArg	macro	dest, src, op

ifnb	<src>
    ifidn <dest>, <src>
	ErrMessage <op : destination = source>
    else
	if ((.TYPE &dest) AND 4)
	    ErrMessage <op : destination argument cannot be constant>
	endif
    endif
endif
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		_ParseWBFixedArg

DESCRIPTION:	Parse an argument to a WBFixed math macro

PASS:
	arg - argument passed to macro
	regcount - # of register-pair arguments
	op - string to emit for error-identification
RETURN:
	rl - low byte of arg
	rh - high word of arg
	regcount - # of register-pair or constant arguments

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

_ParseWBFixedArg	macro	arg, rl, rh, regcount, op
	local	lc
alen	sizestr	<&arg>

ifnb <arg>

if (4 AND .TYPE (arg))
    ErrMessage <op : ParseWBFixedArg doesn't deal with constants>
    regcount=regcount+1
else
    if (alen eq 4)
	rh substr <arg>, 1, 2
	rl substr <arg>, 3, 2
	lc substr <arg>, 4, 1		;; lc <- last character
	
	ifdif lc, <l>			;; must end in "l"
	  ifdif lc, <h>
	    ErrMessage <op : last register in WBFixed must be of form [a-d][lh]>
	  endif
	endif

	ifidn rl, rh
	    ErrMessage <op : register used twice in WBFixed>
	endif
	regcount=regcount+1
    else
        rh catstr <arg>, <.WBF_int>
        rl catstr <arg>, <.WBF_frac>
    endif
endif

else
    ErrMessage <op : missing argument>
endif
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		_ParseTrashReg

DESCRIPTION:	Parse a trash register into a word and byte register.

PASS:
	reg - trash register

RETURN:
	rl - low byte of arg
	rx - high word of arg

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
_ParseTrashReg	macro	reg, rx, rl, op
	local	t, q, lc

if (4 AND .TYPE (arg))
    ErrMessage <op : ParseWBFixedArg doesn't deal with constants>
else
    alen sizestr <&reg>
    if (alen eq 2)
	t  substr <reg>, 1, 1
	lc substr <reg>, 2, 1
	
	rx catstr t, <x>
	rl catstr t, <l>

	ifdif lc, <x>			;; must end in "x"
	    ErrMessage <op : Trash register must be one of ax, bx, cx, or dx>
	endif
	
	q instr <abcd>, t
	if (q eq 0)
	    ErrMessage <op : trash register must be one of ax, bx, cx, or dx>
	endif
    else
	ErrMessage <op : Trash register can't be used>
    endif
endif

endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		_ParseTrashReg2

DESCRIPTION:	Parse a trash register into a word and both byte registers

PASS:
	reg - trash register

RETURN:
	rx - high word of arg
	rl - low byte of arg
	rh - high byte of arg

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
_ParseTrashReg2	macro	reg, rx, rl, rh, op
	local	t, q, lc

if (4 AND .TYPE (arg))
    ErrMessage <op : ParseTrashReg2 doesn't deal with constants>
else
    alen sizestr <&reg>
    if (alen eq 2)
	t  substr <reg>, 1, 1
	lc substr <reg>, 2, 1
	
	rx catstr t, <x>
	rl catstr t, <l>
	rh catstr t, <h>

	ifdif lc, <x>			;; must end in "x"
	    ErrMessage <op : Trash register must be one of ax, bx, cx, or dx>
	endif
	
	q instr <abcd>, t
	if (q eq 0)
	    ErrMessage <op : Trash register must be one of ax, bx, cx, or dx>
	endif
    else
	ErrMessage <op : Trash register non-existent or otherwise unusable!>
    endif
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		cmpwbf

DESCRIPTION:	Compare WBFixed's (unsigned only)

ARGUMENTS:
	dest, source - dest, source to compare
	[trashreg] - re'qd if comparing memory to memory
CAVEATS:
	This is only valid for *unsigned* comparisons.

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

cmpwbf	macro	dest, source, trashreg
	local	foo, drl, drh, srl, srh, regargs, rx, rl

regargs = 0
_CheckWBFixedTwoArg	<dest>, <source>, <cmpdw>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <cmpwbf>
_ParseWBFixedArg	<source>, srl, srh, regargs, <cmpwbf>

if (regargs gt 0)
	cmp	drh, srh
	jne	foo
	cmp	drl, srl
elseifnb <trashreg>
_ParseTrashReg	<trashreg>, rx, rl, <cmpwbf>
	
	mov	rx, drh
	cmp	rx, srh
	jne	foo
	mov	rl, drl
	cmp	rl, srl
else
	ErrMessage <cmpwbf : need trash register to compare mem, mem>
endif

foo:
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		jgewbf

DESCRIPTION:	Signed compare and branch >= for WBFixed's

ARGUMENTS:
	dest, source - dest, source to compare
	blabel - label to branch to branch to if >=
	[trashreg] - re'qd if comparing memory to memory

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

jgewbf	macro	dest, source, blabel, trashreg
	local	foo, drl, drh, srl, srh, regargs, rx, rl

regargs = 0
_CheckWBFixedTwoArg	<dest>, <source>, <jgewbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <jgewbf>
_ParseWBFixedArg	<source>, srl, srh, regargs, <jgewbf>

ifb <blabel>
	ErrMessage <jgewbf : need label for branch>
endif

if (regargs gt 0)
	cmp	drh, srh
	jg	blabel
	jl	foo
	cmp	drl, srl
	jae	blabel
foo:
elseifnb <trashreg>
_ParseTrashReg	<trashreg>, rx, rl, <jgewbf>

	mov	rx, drh
	cmp	rx, srh
	jg	blabel
	jl	foo
	mov	rl, drl
	cmp	rl, srl
	jae	blabel
foo:
else
	ErrMessage <jgewbf : need trash register to compare mem, mem>
endif
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		jgwbf

DESCRIPTION:	Signed compare and branch > for WBFixed's

ARGUMENTS:
	dest, source - dest, source to compare
	blabel - label to branch to branch to if >
	[trashreg] - re'qd if comparing memory to memory

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

jgwbf	macro	dest, source, blabel, trashreg
	local	foo, drl, drh, srl, srh, regargs, rx, rl

regargs = 0
_CheckWBFixedTwoArg	<dest>, <source>, <jgwbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <jgwbf>
_ParseWBFixedArg	<source>, srl, srh, regargs, <jgwbf>

ifb <blabel>
	ErrMessage <jgwbf : need label for branch>
endif

if (regargs gt 0)
	cmp	drh, srh
	jg	blabel
	jl	foo
	cmp	drl, srl
	ja	blabel
foo:
elseifnb <trashreg>
_ParseTrashReg	<trashreg>, rx, rl, <jgewbf>

	mov	rx, drh
	cmp	rx, srh
	jg	blabel
	jl	foo
	mov	rl, drl
	cmp	rl, srl
	ja	blabel
foo:
else
	ErrMessage <jgwbf : need trash register to compare mem, mem>
endif
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		jlewbf

DESCRIPTION:	Signed compare and branch <= for WBFixed's

ARGUMENTS:
	dest, source - dest, source to compare
	blabel - label to branch to branch to if <=
	[trashreg] - re'qd if comparing memory to memory

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

jlewbf	macro	dest, source, blabel, trashreg
	local	foo, drl, drh, srl, srh, regargs, rx, rl

regargs = 0
_CheckWBFixedTwoArg	<dest>, <source>, <jlewbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <jlewbf>
_ParseWBFixedArg	<source>, srl, srh, regargs, <jlewbf>

ifb <blabel>
	ErrMessage <jlewbf : need label for branch>
endif

if (regargs gt 0)
	cmp	drh, srh
	jl	blabel
	jg	foo
	cmp	drl, srl
	jbe	blabel
foo:
elseifnb <trashreg>
_ParseTrashReg	<trashreg>, rx, rl, <jgewbf>

	mov	rx, drh
	cmp	rx, srh
	jl	blabel
	jg	foo
	mov	rl, drl
	cmp	rl, srl
	jbe	blabel
foo:
else
	ErrMessage <jlewbf : need trash register to compare mem, mem>
endif
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		jlwbf

DESCRIPTION:	Signed compare and branch < for WBFixed's

ARGUMENTS:
	dest, source - dest, source to compare
	blabel - label to branch to branch to if <
	[trashreg] - re'qd if comparing memory to memory

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

jlwbf	macro	dest, source, blabel, trashreg
	local	foo, drl, drh, srl, srh, regargs, rx, rl

regargs = 0
_CheckWBFixedTwoArg	<dest>, <source>, <jlwbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <jlwbf>
_ParseWBFixedArg	<source>, srl, srh, regargs, <jlwbf>

ifb <blabel>
	ErrMessage <jlwbf : need label for branch>
endif

if (regargs gt 0)
	cmp	drh, srh
	jl	blabel
	jg	foo
	cmp	drl, srl
	jb	blabel
foo:
elseifnb <trashreg>
_ParseTrashReg	<trashreg>, rx, rl, <jgewbf>

	mov	rx, drh
	cmp	rx, srh
	jl	blabel
	jg	foo
	mov	rl, drl
	cmp	rl, srl
	jb	blabel
foo:
else
	ErrMessage <jlwbf : need trash register to compare mem, mem>
endif
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		tstwbf

DESCRIPTION:	Sets the Z flag if the WBFixed is zero.
		Does NOT set the "s" flag correctly (unlike "tst").

ARGUMENTS:
	dest - memory or register pair to test


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
tstwbf	macro	dest
	local	foo, drl, drh, regargs

regargs = 0
_CheckWBFixedOneArg	<dest>, <tstwbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <tstwbf>

	tst	drh
	jnz	foo
	tst	drl
foo:
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		pushwbf

DESCRIPTION:	Push a WBFixed

SYNOPSIS:
	Build the WBFixed structure on the stack in a packed manner (i.e.
	the byte-sized fractional part and the low part of the integer in
	the lowest word and then the high part of the integer and garbage
	in the high word (i.e., Fractional part, Low part of integer, high
	part of integer, Garbabge byte).

ARGUMENTS:
	dest - memory or register pair to push

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
pushwbf	macro	dest, trashreg
	local	drl, drh, regargs, trx, trl, trh, frx, frl, frh

regargs = 0
_CheckWBFixedOneArg	<dest>, <pushwbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <pushwbf>
_ParseTrashReg2		<trashreg>, trx, trl, trh, <pushwbf>

ifb <trashreg>
	ErrMessage <pushwbf : need trash register>
endif

if (regargs gt 0)	;; Register operand
	_ParseTrashReg2		%drh, frx, frl, frh, <pushwbf>
	;;
	;; frl = drh.low
	;; frh = drh.high
	;;
	mov	trl, frh		;; trl = WBF_int.high
	push	trx			;; Push WBF_int.high & garbage byte.
	mov	trl, drl		;; trl = WBF_frac
	mov	trh, frl		;; trh = WBF_int.low
	push	trx			;; Push WBF_frac, WBF_int.low
else			;; Memory operand
	mov	trl, drh.high		;; trh = WBF_int.high.
	push	trx			;; Push WBF_int.high & garbage byte.
	push	{word} drl		;; Push WBF_frac, WBF_int.low next
endif
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		popwbf

DESCRIPTION:	Pop a WBFixed

SYNOPSIS:
	Extract the WBFixed structure from the stack (assumed to have been
	placed there in a manner equivalent to pushwbf).

ARGUMENTS:
	dest - memory or register pair to pop into

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
popwbf	macro	dest, trashreg
	local	drl, drh, regargs, trx, trl, trh, frx, frl, frh

regargs = 0
_CheckWBFixedOneArg	<dest>, <popwbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <popwbf>
_ParseTrashReg2		<trashreg>, trx, trl, trh, <popwbf>

ifb <trashreg>
	ErrMessage <popwbf : need trash register>
endif

if (regargs gt 0)	;; Register operand
	_ParseTrashReg2		%drh, frx, frl, frh, <popwbf>
	;;
	;; frl = dest.low
	;; frh = dest.high
	;;
	pop	trx			;; trh <- frac + int.low
	mov	drl, trl		;; drl <- frac
	pop	drh			;; drh <- int.high + garbage
	mov	frh, frl		;; frh <- int.high
	mov	frl, trh		;; frl <- int.low
else			;; Memory operand
	pop	trx			;; trx <- WBF_frac + int.low
	mov	drl, trl		;; Save over WBF_frac
	mov	drh.low, trh		;; Save over WBF_int.low
	pop	trx			;; Pop WBF_int.high + byte of garbage
	mov	drh.high, trl		;; Save over WBF_int.high
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		notwbf

DESCRIPTION:	Not a WBFixed

ARGUMENTS:
	dest - memory or register pair to not

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
notwbf	macro	dest
	local	drl, drh, regargs

regargs = 0
_CheckWBFixedOneArg	<dest>, <notwbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <notwbf>

	not	drl
	not	drh
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		negwbf

DESCRIPTION:	Negate a WBFixed

ARGUMENTS:
	dest - memory or register pair to not
	[zeroreg] - if you have a register = 0, this is quicker
CAVEATS:
	Does not set flags the same as the 'neg' instruction

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
negwbf	macro	dest, zeroreg
	local	drl, drh, regargs

regargs = 0
_CheckWBFixedOneArg	<dest>, <negwbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <negwbf>

					;cycles/bytes (registers)
	not	drh			;3/2
	neg	drl			;3/2
	cmc
ifnb	<zeroreg>
	adc	drh, zeroreg		;3/2
else
	adc	drh, 0
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		incwbf

DESCRIPTION:	Increment a WBFixed

ARGUMENTS:
	dest - memory or register pair to inc
CAVEATS:
	Does not set flags same as 'inc' instruction,

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
incwbf	macro	dest
	local	drl, drh, regargs

regargs = 0
_CheckWBFixedOneArg	<dest>, <incwbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <incwbf>

	add	drl, 1			;3 bytes / 3 cycles
	adc	drh, 0			;3 bytes / 3 cycles

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		decwbf

DESCRIPTION:	Decrement a WBFixed

ARGUMENTS:
	dest - memory or register pair to dec
CAVEATS:
	Does not set flags same as 'dec' instruction,

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
decwbf	macro	dest
	local	drl, drh, regargs

regargs = 0
_CheckWBFixedOneArg	<dest>, <decwbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <decwbf>

	sub	drl, 1
	sbb	drh, 0
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		movwbf

DESCRIPTION:	Move a WBFixed

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if moving memory to memory

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
movwbf	macro	dest, source, trashreg
	local	drl, drh, srl, srh, regargs, rx, rl

regargs = 0
_CheckWBFixedTwoArg	<dest>, <source>, <movwbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <movwbf>
_ParseWBFixedArg	<source>, srl, srh, regargs, <movwbf>

if (regargs gt 0)
	mov	drh, srh
	mov	drl, srl
elseifnb <trashreg>
_ParseTrashReg	<trashreg>, rx, rl, <movwbf>

	mov	rx, srh
	mov	drh, rx
	mov	rl, srl
	mov	drl, rl
else
	ErrMessage <movwbf : need trash register to move mem, mem>
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		xchgwbf

DESCRIPTION:	Exchange two WBFixed's

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if moving memory to memory

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
xchgwbf	macro	dest, source, trashreg
	local	drl, drh, srl, srh, regargs, rx, rl

regargs = 0
_CheckWBFixedTwoArg	<dest>, <source>, <xchgwbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <xchgwbf>
_ParseWBFixedArg	<source>, srl, srh, regargs, <xchgwbf>

if (regargs gt 0)
	xchg	drh, srh
	xchg	drl, srl
elseifnb <trashreg>
_ParseTrashReg	<trashreg>, rx, rl, <jgewbf>

	mov	rx, srh
	xchg	drh, rx
	mov	srh, rx
	mov	rl, srl
	xchg	drl, rl
	mov	srl, rl
else
	ErrMessage <xchgwbf : need trash register to xchg mem, mem>
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		addwbf

DESCRIPTION:	Add a WBFixed

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if adding memory to memory

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
addwbf	macro	dest, source, trashreg
	local	drl, drh, srl, srh, regargs, rx, rl

regargs = 0
_CheckWBFixedTwoArg	<dest>, <source>, <addwbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <addwbf>
_ParseWBFixedArg	<source>, srl, srh, regargs, <addwbf>

if (regargs gt 0)
	add	drl, srl
	adc	drh, srh
elseifnb <trashreg>
_ParseTrashReg	<trashreg>, rx, rl, <jgewbf>

	mov	rl, srl
	add	drl, rl
	mov	rx, srh
	adc	drh, rx
else
	ErrMessage <addwbf : need trash register to add mem, mem>
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		adcwbf

DESCRIPTION:	Add with carry a WBFixed

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if adding memory to memory

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
adcwbf	macro	dest, source, trashreg
	local	drl, drh, srl, srh, regargs, rx, rl

regargs = 0
_CheckWBFixedTwoArg	<dest>, <source>, <adcwbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <adcwbf>
_ParseWBFixedArg	<source>, srl, srh, regargs, <adcwbf>

if (regargs gt 0)
	adc	drl, srl
	adc	drh, srh
elseifnb <trashreg>
_ParseTrashReg	<trashreg>, rx, rl, <jgewbf>
	
	mov	rl, srl
	adc	drl, rl
	mov	rx, srh
	adc	drh, rx
else
	ErrMessage <adcwbf : need trash register to adc mem, mem>
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		subwbf

DESCRIPTION:	Subtract a WBFixed

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if adding memory to memory

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
subwbf	macro	dest, source, trashreg
	local	drl, drh, srl, srh, regargs, rx, rl

regargs = 0
_CheckWBFixedTwoArg	<dest>, <source>, <subwbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <subwbf>
_ParseWBFixedArg	<source>, srl, srh, regargs, <subwbf>

if (regargs gt 0)
	sub	drl, srl
	sbb	drh, srh
elseifnb <trashreg>
_ParseTrashReg	<trashreg>, rx, rl, <jgewbf>

	mov	rl, srl
	sub	drl, rl
	mov	rx, srh
	sbb	drh, rx
else
	ErrMessage <subwbf : need trash register to sub mem, mem>
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		sbbwbf

DESCRIPTION:	Subtract with borrow a WBFixed

ARGUMENTS:
	dest - memory or register pair for destination
	source - memory or register pair for source
	[trashreg] - re'qd if adding memory to memory

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
sbbwbf	macro	dest, source, trashreg
	local	drl, drh, srl, srh, regargs, rx, rl

regargs = 0
_CheckWBFixedTwoArg	<dest>, <source>, <subwbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <subwbf>
_ParseWBFixedArg	<source>, srl, srh, regargs, <subwbf>

if (regargs gt 0)
	sbb	drl, srl
	sbb	drh, srh
elseifnb <trashreg>
_ParseTrashReg	<trashreg>, rx, rl, <jgewbf>

	mov	rl, srl
	sbb	drl, rl
	mov	rx, srh
	sbb	drh, rx
else
	ErrMessage <sbbwbf : need trash register to sbb mem, mem>
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		clrwbf

DESCRIPTION:	Clear a WBFixed

ARGUMENTS:
	dest - memory or register pair to clear
	[zeroreg] - if you have a register = 0, this is quicker

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
clrwbf	macro	dest, zeroreg
	local	drl, drh, regargs, rx, rl

regargs = 0
_CheckWBFixedOneArg	<dest>, <clrwbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <clrwbf>

if (regargs gt 0)
	clr	drh
	clr	drl
elseifnb <zeroreg>
_ParseTrashReg	<zeroreg>, rx, rl, <clrwbf>
	mov	drh, rx
	mov	drl, rl
else
	clr	drh
	clr	drl
endif

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		sarwbf

DESCRIPTION:	Arithmetic shift right for WBFixed

ARGUMENTS:
	dest - memory or register pair to shift right
CAVEATS:
	Does not set flags the same as the 'sar' instruction

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

sarwbf	macro	dest
	local	drl, drh, regargs

regargs = 0
_CheckWBFixedOneArg	<dest>, <sarwbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <sarwbf>

	sar	drh, 1
	rcr	drl, 1

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		shrwbf

DESCRIPTION:	Logical shift right for WBFixed

ARGUMENTS:
	dest - memory or register pair to shift right
CAVEATS:
	Does not set flags the same as the 'shr' instruction

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

shrwbf	macro	dest
	local	drl, drh, regargs

regargs = 0
_CheckWBFixedOneArg	<dest>, <shrwbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <shrwbf>

	shr	drh, 1
	rcr	drl, 1

endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		shlwbf, salwbf

DESCRIPTION:	Logical/Arithmetic shift left for WBFixed

ARGUMENTS:
	dest - memory or register pair to shift left
CAVEATS:
	Does not set flags the same as the 'shl/sal' instruction

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

shlwbf	macro	dest
	local	drl, drh, regargs

regargs = 0
_CheckWBFixedOneArg	<dest>, <shlwbf>
_ParseWBFixedArg	<dest>, drl, drh, regargs, <shlwbf>

	shl	drl, 1
	rcl	drh, 1

endm

salwbf	macro	dest
	shlwbf	dest
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		rndwbf

DESCRIPTION:	Round a (signed) WBFixed value to a word

ARGUMENTS:
	source - A WBFixed value to round
	dest   - The destination word (optional)
CAVEATS:

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

rndwbf	macro	source, dest
	local	round1, round2, done, drl, drh, regargs

regargs = 0
_CheckWBFixedOneArg	<source>, <rndwbf>
_ParseWBFixedArg	<source>, drl, drh, regargs, <rndwbf>

ifnb <dest>
	cmp	drl, 80h
    ifdif	drh, <dest>
	;;
	;; We do this here so that if the fraction is actually part of the
	;; dest register (eg: cl and cx) we won't nuke the fraction before
	;; we have a chance to test it.
	;;
	mov	dest, drh
    endif
	jb	done
	ja	round1
	tst	dest
	js	done
round1:
	inc	dest
else
	cmp	drl, 80h
	jb	done
	ja	round2
	tst	drh
	js	done
round2:
	inc	drh
endif
done:
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		rnduwbf

DESCRIPTION:	Round an unsigned WBFixed value to a word

ARGUMENTS:
	source - A WBFixed value to round
	dest   - The destination word register
CAVEATS:

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

rnduwbf	macro	source, dest
	local	done, drl, drh, regargs

regargs = 0
_CheckWBFixedOneArg	<source>, <rnduwbf>
_ParseWBFixedArg	<source>, drl, drh, regargs, <rnduwbf>

ifnb	<dest>
	cmp	drl, 80h
    ifdif	drh, <dest>
	;;
	;; We do this here so that if the fraction is actually part of the
	;; dest register (eg: cl and cx) we won't nuke the fraction before
	;; we have a chance to test it.
	;;
	mov	dest, drh
    endif
	jb	done
	add	dest, 1
else
	cmp	drl, 80h
	jb	done
	add	drh, 1
endif
done:
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		ceilwbf

DESCRIPTION:	Get the ceiling of a WBFixed value into a word

ARGUMENTS:
	source - WBFixed to get the ceiling of
	dest   - Word register to put the ceiling into

CAVEATS:

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

ceilwbf	macro	source, dest
	local	round, done, drl, drh, regargs

regargs = 0
_CheckWBFixedOneArg	<source>, <rndwbf>
_ParseWBFixedArg	<source>, drl, drh, regargs, <ceilwbf>

	tst	drl
ifdif	drh, <dest>
	;;
	;; We do this here so that if the fraction is actually part of the
	;; dest register (eg: cl and cx) we won't nuke the fraction before
	;; we have a chance to test it.
	;;
	mov	dest, drh	;; Does not nuke flags
endif
	jz	done
	inc	dest
done:
endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		HiLoValue

DESCRIPTION:	This macro allows you to set up a word-length constant in
		Hi-Lo order, for an arbitrary word-length input value.

ARGUMENTS:
	result - Constant to define
	value  - Value to use

EXAMPLE:
	HiLoValue HILO_SIZE_OF_BUFFER, "size MyInputBuffer"

CAVEATS:

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

HiLoValue	macro	result, value

&result		equ	(((value) AND 0xff) shl 8) or \
			(((value) shr 8) AND 0xff)

		endm

COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

MACRO:		HiLoEnum

DESCRIPTION:	
		This macro allows us to define word-length enumerated types
		with specific values, and to have the values automatically
		arranged so that they are in Big-Endian (hi-lo) order.
		Novell expects lots of word-length fields to be set up this way.

		Thanks, Adam :)

ARGUMENTS:
	name	- name of enumerated constant to define
	type	- name of enumerated type to define it from
	value	- value to use

EXAMPLE:
	HiLoEnum	WNC_UNO, WeirdNovellConstants, 0x0201

CAVEATS:

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

HiLoEnum	macro	name, type, value

name		enum	type, (((value) AND 0xff) shl 8) or \
			      (((value) shr 8) AND 0xff)
		endm

;Use the following types when defined fields which will hold HiLo words
;or dwords. Swat won't display them properly, but at least we'll know
;when coding that these fields are weird.

HiLoWord		type	word
HiLoDWord		type	dword



COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
		copybuf
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SYNOPSIS:	Copies a fixed length buffer.

PASS:		ds:si - ptr to source
		es:di - ptr to dest
		size  - size of buffer in bytes
		<optional> SAVE_REGS to preserve registers

DESTROYED:	cx, si, di (unless SAVE_REGS passed)

PSEUDO CODE/STRATEGY:
KNOWN BUGS/SIDE EFFECTS/IDEAS:

EXPANDS TO:
	if SAVE_REGS:
		15 bytes if length is even
		16 bytes if length is odd
	else:
		5 bytes if length is even
		6 bytes if length is odd
	
REVISION HISTORY:
	Name	Date		Description
	----	----		-----------
	martin	11/4/92		Initial version

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
copybuf		macro	size, saveFlag
ifnb <saveFlag>
    ifidn <saveFlag>, <SAVE_REGS>
	push	cx, si, di			; 3 / 45
    endif
endif
	mov	cx, (size) / 2			; 3 / 4
	rep	movsw				; 2 / 9+12.5n
if (((size) and 1) eq 1)
	movsb					; 1 / 18
endif
ifnb <saveFlag>
    ifidn <saveFlag>, <SAVE_REGS>
	pop	cx, si, di			; 3 / 36
    endif
endif
endm



COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
		GetVSEG
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SYNOPSIS:	Turn the handle value in a register to a vseg value

PASS:		reg	- register holding handle value

PSEUDO CODE/STRATEGY:
		Saves 2 whole bytes and 2 whole cycles over 4 SHR
		and an OR.  May I have a Wow?  *wow*!  Thanks.  :)

KNOWN BUGS/SIDE EFFECTS/IDEAS:
		Biffs flags, but so would normal code.

REVISION HISTORY:
	Name	Date		Description
	----	----		-----------
	TS	4/ 7/94   	Initial version

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
GetVSEG		macro	reg

if (.TYPE reg) AND 10h
		stc			; 1 bytes / 2 cycles
		rcr	reg		; 2 bytes / 2 cycles
		sar	reg		; 2 bytes / 2 cycles
		sar	reg		; 2 bytes / 2 cycles
		sar	reg		; 2 bytes / 2 cycles
else
	ErrMessage <GetVSeg requires register>
endif

endm


COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
		PushSP
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SYNOPSIS:	Push the SP onto the stack consistantly

PASS:		sp	-> value to push

PSEUDO CODE/STRATEGY:
		We need a trash register (sigh) so we also push
		ax

KNOWN BUGS/SIDE EFFECTS/IDEAS:

REVISION HISTORY:
	Name	Date		Description
	----	----		-----------
	TS	4/19/94   	Initial version

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
PushAXSP	macro
		mov	ax, sp
		inc	ax
		inc	ax
		push	ax
		endm



EndKernel






