Stdlib Macros


UCR StdLib2: Macros
2.1 - The MACROS.A File
2.1.1 - The IsConst Macro
2.1.2 - The IsNumConst Macro
2.1.3 - The IsStrConst Macro
2.1.4 - The IsReg Macro
2.1.5 - The IsVar Macro
2.1.6 - The IsByte, IsWord, and IsDWord Macros
2.1.7 - The IsWordPtr and IsDWordPtr Macros
2.1.8 - The IsReg8 Macro
2.1.9 - The pReg8 Macro
2.1.10 - The IsReg16 Macro
2.1.11 - The IsReg32 Macro
2.1.12 - The $Push Macro
2.1.13 - The $Pop Macro
2.1.14 - The $Peek Macro
2.1.15 - The Xtrn Macro
2.1.16 - The bCSStkTOS Macro
2.1.17 - The bCSStk Macro
2.1.18 - The wCSStkTOS Macro
2.1.19 - The wCSStk Macro
2.1.20 - The LCSStkTOS Macro
2.1.21 - The LCSSTK Macro
2.1.22 - The sbCSStkTOS Macro
2.1.23 - The swCSStkTOS Macro
2.1.24 - The sdCSStkTOS Macro


UCR StdLib2: Macros

As a general rule, one does not "call" standard library routines directly. Instead, the library includes a set of macros that simplify calling the various routines. Some of these macros do little more than directly call the routine (allowing you to simply specify the routine name rather than have to use the call instruction). Other macros are quite sophisticated and let you use a high level language syntax to invoke various stdlib routines.

MASM provides some terrific macro facilities that let you accomplish some amazing things. Unfortunately, accomplishing something and understanding how it was accomplished are two different things. Although MASM provides the capability, figuring out how to take advantage of it, and working around the bugs in MASM, can produce some incomprehensible code. The macros that support the UCR Standard Library certainly border on incomprehensible, if they do not cross over into that territory. The purpose of this chapter is to help make some sense of these macros.


2.1 The MACROS.A File

The macros.a file contains several useful macros used by the individual include files. The following subsections describe each of the individual macros in this file.


2.1.1 The IsConst Macro

There is often the need to determine if a macro parameter is a literal constant. The IsConst macro function handles this chore. MASM's OPATTR function seems to supply this capability, but it has one glaring problem - it only recognizes numeric constants, it does not handle string constants. IsConst handles this by first checking an operand to see if it is a string constant (i.e., it begins with either a quote or an apostrophe). If the operand is not a string constant, then IsConst uses OPATTR to determine if it is a numeric constant.

This macro function returns true (all bits set) if the specified operand is a literal constant. It returns false (0) if it is not a literal constant.

Checking for a string constant is rather difficult with MASM. I couldn't find an easy way to compare the first character of an operand against a quote or apostrophe. I did discover a truly disgusting way to do this; I would appreciate a better solution if somebody can discover something I've missed (that is reasonable compatible with all versions of MASM 6.x).

My solution was to build the text constant <' '> or <" "> by concatenating the first character of the operand with a space followed by the first character again. The following expression does this:

@catstr(@substr(<Operand>,1,1),< >,@substr(<Operand>,1,1))

I then use the IFIDN (if identical) statement to compare this result against <' '> and <" "> for equality. If the substring above matches either of these, I assume we've got a string literal constant. If the substring I've built doesn't match either of these, I use OPATTR to see if we've got a numeric constant.




;IsConst-       Checks an operand to see if it is a constant (numeric or string).
;               This function returns true (0FFh) if the operand is a constant,
;               it returns false (0) otherwise.  You would normally use it with
;               the "if" directive.

IsConst         macro   Operand

;; Assume if we see a quote or an apostrophe that this is a string constant
;; operand.  The funny syntax below is necessary because MASM doesn't handle
;; stuff like <'> or <"> too well.

%               ifidn   <@catstr(@substr(<Operand>,1,1),< >,@substr(<Operand>,1,1))>, <' '>
                exitm   <-1>
                endif

%               ifidn   <@catstr(@substr(<Operand>,1,1),< >,@substr(<Operand>,1,1))>, <" ">
                exitm   <-1>
                endif

;; If not quote or apostrophe, check it out with OPATTR.

                exitm   <(((OPATTR Operand) and 1100b) eq 0100b)>
                endm


2.1.2 The IsNumConst Macro

The IsNumConst macro is simply a wrapper around the OPATTR function. IsNumConst is much easier to read than the resulting call to OPATTR (see the implementation below). IsNumConst returns all one bits (-1) if the operand is a numeric literal constant. It returns zero if the operand is not a numeric literal constant. Note that IsNumConst returns false for character and string constants.




;IsNumConst-    Checks an operand to see if it is a numeric constant.
;               This function returns true (0FFh) if the operand is a constant,
;               it returns false (0) otherwise.  You would normally use it with
;               the "if" directive.

IsNumConst      macro   Operand

;; Check it out with OPATTR.

                exitm   <(((OPATTR Operand) and 1100b) eq 0100b)>
                endm

2.1.3 The IsStrConst Macro

The IsStrConst macro checks its operand to see if it is a string constant. See the description of the IsConst macro earlier for a blow-by-blow description of how this macro works. IsStrConst returns true (all one bits, or -1) if the operand is a string constant, it returns false (0) if it is not a string constant.




;IsStrConst-    Checks an operand to see if it is a strong constant.
;               This function returns true (0FFh) if the operand is a constant,
;               it returns false (0) otherwise.  You would normally use it with
;               the "if" directive.

IsStrConst      macro   Operand

;; Assume if we see a quote or an apostrophe that this is a string constant
;; operand.  The funny syntax below is necessary because MASM doesn't handle
;; stuff like <'> or <"> too well.

%               ifidn   <@catstr(@substr(<Operand>,1,1), \
                                 < >, \
                                 @substr(<Operand>,1,1))>, \
                        <' '>
                exitm   <-1>
                endif

%               ifidn   <@catstr(@substr(<Operand>,1,1), \
                                 < >, \
                                 @substr(<Operand>,1,1))>, \
                        <" ">
                exitm   <-1>
                endif

;; If not quote or apostrophe, it's not a string constant.

                exitm   <0>
                endm


2.1.4 The IsReg Macro

The IsReg macro checks its operand to see if it is an 80x86 register. Unlike the previous macros, this particular function does not return true or false. Instead, it returns the operand if it is an 80x86 register, it return a blank text field if the operand is not an 80x86 register. You can use the IFB (if blank) directive to test an operand to see if it is a register. By returning the register name in this fashion, the IsReg macro is useful in a few situations (e.g., the operand field of various instructions) where it wouldn't be if it only returned true or false.

This macro is a wrapper around the MASM OPATTR function to make the test for a register more readable.




;IsReg-         Checks to see if an operand is a register.
;               This function returns the operand as a textual value if
;               it is a register.  It returns the empty string (blank)
;               if the operand is not a register.  You would normally
;               use this function in an "ifb" directive.

IsReg           macro   Operand
                if      (((OPATTR Operand) and 10000b) eq 10000b)
                exitm   <Operand>
                else
                exitm   <>
                endif
                endm


2.1.5 The IsVar Macro

The IsVar macro is very similar to the IsReg macro except, of course, it tests for a variable name rather than a register. Otherwise, its usage is the same. Like IsReg, IsVar is simply a wrapper around the OPATTR function to make programs easier to read.





;IsVar-         Checks an operand to see if it is a variable.
;               This function returns the operand as a textual value if
;               it is a variable.  It returns the empty string (blank)
;               if the operand is not a variable.  You would normally
;               use this function in an "ifb" directive.

IsVar           macro   Operand
                if      (((OPATTR Operand) and 1010b) eq 1010b)
                exitm   <Operand>
                else
                exitm   <>
                endif
                endm


2.1.6 The IsByte, IsWord, and IsDWord Macros

These three macros test their operand and return true or false depending on the size of the operand (byte, word, or dword, respectively). Typically, one would apply these functions to a variable to determine its size. Like many of the macros in this package, these macros are really wrappers around OPATTR in order to produce a more readable program.

These functions return true (all bits set, or -1) if the specified operand is a variable of the specified type; they return false (0) otherwise. One would normally use them as the operand of an IF directive.




; IsByte,
; IsWord,
; IsDword-      Tests its operand to see if it is a variable of the specific 
;               object size.

IsByte          macro   Operand
                exitm   <((type Operand) eq 1) and \
                         (((OPATTR Operand) and 1010b) eq 1010b)>
                endm

IsWord          macro   Operand
                exitm   <((type Operand) eq 2) and \
                         (((OPATTR Operand) and 1010b) eq 1010b)>
                endm

IsDword         macro   Operand
                exitm   <((type Operand) eq 4) and \
                         (((OPATTR Operand) and 1010b) eq 1010b)>
                endm


2.1.7 The IsWordPtr and IsDWordPtr Macros

The IsWordPtr and IsDWordPtr macros check for a word and dword variable (respectively) surrounded by square brackets. These two functions return a blank text object if the specified operand is not a word pointer or a dword pointer. They return the object inside the square brackets if it is a word or dword variable.

These two macros begin by checking to see if the "[" and "]" characters surround the operand. After verifying this, these macros call the IsWord or IsDWord macros, passing in the characters between the "[" and "]" to determine if we've got word pointer or dword pointer syntax. If this is the case, these macros return the substring between the "[" and "]" characters (otherwise these functions return an empty string).




; IsWordPtr-    Checks the operand to see if it is a word variable
;               surrounded by square brackets.
;
;               This function returns the operand minus the square brackets
;               if it is indeed a word ptr operand.  It returns blank
;               otherwise.  You would normally use this function in an
;               "ifb" directive or in the operand field of an instruction.

IsWordPtr       macro   Operand
%               ifidn   <@substr(<Operand>,1,1)>, <[>
%               ifidn   <@substr(<Operand>,@SizeStr(<Operand>),1)>, <]>
%               if      IsWord(<@substr(<Operand>,2,@SizeStr(<Operand>)-2)>)
%               exitm   <@substr(<Operand>,2,@SizeStr(<Operand>)-2)>
                endif
                endif
                endif
                exitm   <>
                endm

; IsDwordPtr-   Checks the operand to see if it is a dword variable
;               surrounded by square brackets.
;
;               This function returns the operand minus the square brackets
;               if it is indeed a dword ptr operand.  It returns blank
;               otherwise.  You would normally use this function in an
;               "ifb" directive or in the operand field of an instruction.

IsDwordPtr      macro   Operand
%               ifidn   <@substr(<Operand>,1,1)>, <[>
%               ifidn   <@substr(<Operand>,@SizeStr(Operand),1)>, <]>
%               if      IsDword(@substr(<Operand>,2,@SizeStr(<Operand>)-2))
%               exitm   <@substr(<Operand>,2,@SizeStr(<Operand>)-2)>
                endif
                endif
                endif
                exitm   <>
                endm


2.1.8 The IsReg8 Macro

This function checks the operand to see if it is a valid eight-bit 80x86 register. If so, this macro returns that register as the result, otherwise it returns an empty string as the result. One would normally use this macro as the operand of an IFB directive, or in the operand field of some other instruction requiring an eight-bit register.

The implementation of this macro is fairly straight-forward. It simply compares its operand against the eight eight-bit register names (ignoring case) and returns the corresponding register as the result if a match occurs.




; IsReg8-       Checks the parameter to see if it corresponds to a
;               valid eight-bit 80x86 register.  It returns that
;               register as an operand if it is a valid 8-bit reg.
;               It returns blank otherwise.

IsReg8          macro   reg8
                ifidni  <reg8>, <al>
                exitm   <al>
                endif

                ifidni  <reg8>, <bl>
                exitm   <bl>
                endif

                ifidni  <reg8>, <cl>
                exitm   <cl>
                endif

                ifidni  <reg8>, <dl>
                exitm   <dl>
                endif

                ifidni  <reg8>, <ah>
                exitm   <ah>
                endif

                ifidni  <reg8>, <bh>
                exitm   <bh>
                endif

                ifidni  <reg8>, <ch>
                exitm   <ch>
                endif

                ifidni  <reg8>, <dh>
                exitm   <dh>
                endif

                exitm   <>      ;; Not a valid eight-bit register.
                endm


2.1.9 The pReg8 Macro

The pReg8 macro checks its operand to see if it is one of the eight-bit registers. If so, this function pushes that register onto the stack. Since the 80x86 doesn't allow eight-bit push operations, this macro actually pushes the 16-bit register that contains the specified eight-bit register. pReg8 always pushes the specified value as the L.O. byte. Therefore, it will execute a pair of XCHG instructions around the push instruction if you specify one of AH, BH,CH, or DH. Note that pReg8 will always correct this change after pushing the register onto the stack so the original eight-bit register pairs will be unchanged. If the specified register is not an eight-bit register, this macro does nothing.

Note that this macro is a true macro, not a macro function. It does not return any value(s) to test with a conditional directive.




; pReg8-        If the specified operand is an eight-bit register,
;               this macro will push it onto the stack (it actually
;               pushes 16 bits, the specified value occupies the
;               L.O. byte of the value pushed).

pReg8           macro   Operand1

                ifidni  <Operand1>, <al>
                push    ax
                endif

                ifidni  <Operand1>, <bl>
                push    bx
                endif

                ifidni  <Operand1>, <cl>
                push    cx
                endif

                ifidni  <Operand1>, <dl>
                push    dx
                endif

                ifidni  <Operand1>, <ah>
                xchg    al, ah
                push    ax
                xchg    al, ah
                endif

                ifidni  <Operand1>, <bh>
                xchg    bl, bh
                push    bx
                xchg    bl, bh
                endif

                ifidni  <Operand1>, <ch>
                xchg    cl, ch
                push    cx
                xchg    cl, ch
                endif

                ifidni  <Operand1>, <dh>
                xchg    dl, dh
                push    dx
                xchg    dl, dh
                endif

                endm


2.1.10 The IsReg16 Macro

Like it's eight-bit counterpart, this macro checks its operand to determine if we have a 16-bit register. If the operand is the name of a 16-bit register, it returns that register as the function result, otherwise it returns an empty string as the function result.




;IsReg16-       Checks the parameter to see if it corresponds to a
;               valid 16-bit register.

IsReg16         macro   reg16
                ifidni  <reg16>, <ax>
                exitm   <ax>
                endif

                ifidni  <reg16>, <bx>
                exitm   <bx>
                endif

                ifidni  <reg16>, <cx>
                exitm   <cx>
                endif

                ifidni  <reg16>, <dx>
                exitm   <dx>
                endif

                ifidni  <reg16>, <si>
                exitm   <si>
                endif

                ifidni  <reg16>, <di>
                exitm   <di>
                endif

                ifidni  <reg16>, <bp>
                exitm   <bp>
                endif

                ifidni  <reg16>, <sp>
                exitm   <sp>
                endif

                exitm   <>      ;; Not a valid 16-bit register.
                endm


2.1.11 The IsReg32 Macro

Like the IsReg8 and IsReg16 macros, the IsReg32 macro checks its operand to see if it is a 32-bit register. This function returns the register name if it is a valid 32-bit register, it returns an empty string otherwise. Typically you would call this macro in the operand field of an instruction that expects a 32-bit register or use this macro with the IFB directive.




;IsReg32-       Checks the parameter to see if it corresponds to a
;               valid 32-bit register.

IsReg32         macro   reg32
                ifidni  <reg32>, <eax>
                exitm   <eax>
                endif

                ifidni  <reg32>, <ebx>
                exitm   <ebx>
                endif

                ifidni  <reg32>, <ecx>
                exitm   <ecx>
                endif

                ifidni  <reg32>, <edx>
                exitm   <edx>
                endif

                ifidni  <reg32>, <esi>
                exitm   <esi>
                endif

                ifidni  <reg32>, <edi>
                exitm   <edi>
                endif

                ifidni  <reg32>, <ebp>
                exitm   <ebp>
                endif

                ifidni  <reg32>, <esp>
                exitm   <esp>
                endif

                exitm   <>      ;; Not a valid 32-bit register.
                endm


2.1.12 The $Push Macro

Some of the control constructs in the Standard Library require a context free grammar (CFG) for proper implementation. In particular, different macros need to communicate with one another. A CFG requires the use of a stack. The Standard Library uses text equates for stack variables. An empty stack consists of an empty string. Values pushed onto the stack are simply concatenated to the stack. To separate items on the stack, the Standard Library uses the ":" character. For example, after pushing the values 2, 3, and 6 onto a "stack", the corresponding stack variable would contain the following text:

< 6 : 3 : 2 : >

The $Push macro pushes a value onto a stack. This macro requires two operands: the name of the stack on which to push the value (a MASM symbol) and a numeric value to push. This function pushes the value by appending the current stack value to the text < value : >.

This is a true macro, not a macro function. Therefore, you would never call it from the operand field of some other instruction or diretive.




; $Push Symstk, Value
;
; Symstk is a symbol name (it need not already exist).
; Value is a numeric value.
; $Push "pushes" this value onto the stack named Symstk by
; concatenating the string equivalent of the value to the
; begining of the Symstk symbol as a text value.  Note that
; $push separates stack entries (which must be numbers) using
; the ":" symbol.

$push           macro   SymStk, value
                ifnb    <value>
SymStk          catstr  <value>, <:>, SymStk
                endif
                endm


2.1.13 The $Pop Macro

The $Pop macro undoes the effect of the $Push macro. It copies the prefix of a stack variable (all the characters up to the first ":" character) to its destination operand and then it removes the prefix characters (including the ":") from the beginning of the stack variable. Note that this macro will report an error if you attempt to pop a value from an empty stack. Like $Push, $Pop is a macro procedure, not a function. Therefore it does not return a value (instead, it stores the popped value into the destination variable.




; $Pop- extracts the top of stack value (a numeric value) and equates
; the dest symbol to this value.  $Pop removes the item on the top
; of the stack from the Symstk text value (everything up to and including
; the ":" stack separator).

$pop            macro   SymStk, dest
                local   posn

posn            instr   1,SymStk,<:>

                ifb     SymStk
                echo    Error- Empty stack (POP)
                err
                exitm
                endif

                if      posn ne 0
dest            substr  SymStk, 1, posn-1
                else
dest            textequ <>
                endif
                if      posn ne @sizestr(%&SymStk&)
SymStk          substr  SymStk, posn+1
                else
SymStk          textequ <>
                endif
                endm


2.1.14 The $Peek Macro

The $Peek macro is similar to $Pop except it only returns the value currently on the top of the symbol stack; it does not remove that item from the stack.




; $Peek is like $Pop except it does not remove the item from
; the top of the stack.

$peek           macro   SymStk, dest
                local   posn

posn            instr   1,SymStk,<:>

                ifb     SymStk
                echo    Error- Empty stack (PEEK)
                err
                exitm
                endif

                if      posn ne 0
dest            substr  SymStk, 1, posn-1
                else
dest            textequ <>
                endif
                endm


2.1.15 The Xtrn Macro

The job of the xtrn macro is to write other macros. Many of the Standard Library functions provide a wide variety of different calls that vary in the way one passes the parameters to the function. For example, consider the IsAlpha function. There are actually four different IsAlpha functions: $IsAlpha, $IsAlphaTOS, $IsAlphaStk, and $IsAlphaCS. Each of these functions requires an externdef statement and a macro that lets you invoke the routine by simply specifying the names IsAlpha, IsAlphaTOS, IsAlphaStk, or IsAlphaCS (e.g., without having to specify the call instruction). The xtrn macro provides a convenient way to create all these externdef and macro definitions. To use xtrn, one simply specifies the base function name (e.g., IsAlpha) as the first operand and the allowable suffixes (e.g., TOS, STK, and CS) as the remaining operands. The xtrn macro generates all the necessary macros and externdef statements from this operand list. A typical call to xtrn would look like the following:




xtrn IsAlpha, TOS, STK, CS

Although the xtrn macro is intended primarily for the Standard Library routines, you can use it if you want to create your own library routines that use the same calling sequence and linkages as the Standard Library functions.




; xtrn- Generates a set of externdef statements for a given
;       label.  The first parameter specifies the name of the
;       symbol, the remaining parameters provide the suffixes.

xtrn            macro     name, suffixes:vararg
                externdef $&name&:far

                ifnb      <suffixes>
                for       suffix,<suffixes>
                externdef $&name&&suffix&:far
&name&&suffix&  textequ   <call $&name&&suffix&>
                endm
                else
&name&          textequ   <call $&name&>
                endif
                endm


2.1.16 The bCSStkTOS Macro

The bCSStkTOS macro is one of several macros designed to implement other macros for Standard Library routines. In particular, those Standard Library functions that operate on byte operands and support plain, CS, STK, and TOS modes typically use this macro to actually invoke a particular routine.

The primary purpose of the bCSStkTOS macro is to call an appropriate Standard Library function and automatically handle the plain, register, constant, and pointer addressing modes. For example, consider the IsAlpha Standard Library function. The Standard Library supports the following variations of IsAlpha:




	IsAlpha
	IsAlphaTOS
	IsAlphaSTK
	IsAlphaCS

A corresponding invocation of the xtrn macro generates the macros for these calls, you could call each of these functions using the following syntax:





	mov al, CharToTest
	IsAlpha

	push word ptr CharToTest
	IsAlphaTOS

	pshadrs CharToTest
	IsAlphaStk

	IsAlphaCS
	dword CharToTest

Note, however, that the Standard Library provides additional syntax allowing a more convenient use of the IsAlpha set of routines. Specifically, you can supply an operand to IsAlpha as follows:





	IsAlpha
	IsAlpha   bl             ;Or any 8-bit register
	IsAlpha   'a'            ;Or any 8-bit constant
	IsAlpha   CharVar
	IsAlpha   [wordvar]
	IsAlpha   [dwordvar]

The first example above is the standard call to IsAlpha. The second and third examples above actually push their operand onto the stack and call IsAlphaTOS. The third example above (charvar) winds up calling the IsAlphaCS routine. The last two examples above push the 32-bit address specified (DS assumed for word variables) onto the stack and call IsAlphaSTK.

The bCSStkTOS macro handles processing the IsAlpha (and other routines') operand field to determine which routine it should actually call and how it should pass the parameters. Basically, it uses the following algorithm:





; bCSStkTOS:
;
;       Routines that have a byte operand and support the plain, CS,
;       Stk, and TOS addressing modes.


bCSStkTOS       macro   funcName, Operand

                ifb     <Operand>
                call    $&funcName&
                exitm
                endif

;; See if it's an eight-bit register

                ifnb    IsReg8( <Operand> )

                pReg8   <Operand>
                call    $&funcName&TOS
                exitm

                endif

;; See if it's a numeric constant

                if      IsNumConst( <Operand> )

                push    Operand
                call    $&funcName&TOS
                exitm

                endif

;; See if it's a byte variable:

                if      IsByte( <Operand> )

                call    $&funcName&CS
                dword   Operand
                exitm

                endif

;; See if it's a near or far pointer variable.

                ifnb    IsWordPtr(<Operand>)
                push    ds
                push    IsWordPtr(<Operand>)
                call    $&funcName&Stk
                exitm
                endif

                ifnb    IsDwordPtr(<Operand>)
                pushd   IsDwordPtr(<Operand>)
                call    $&funcName&Stk
                exitm
                endif

;; If it's not any of the above, we have an error.

%               echo    Illegal &funcName& operand.
                err
                endm


2.1.17 The bCSStk Macro

Provides support like bCSStkTOS except this macro is intended for use by routines that do not supply the xxxxTOS call. This macro is just a hacked-down version of bCSStk, so there is no real need to spend a lot of time discussing it. Note that because of the TOS omission, this macro does not support the constant or register addressing modes.





; bCSStk:
;
;       Input routines that have a byte operand and support the plain, CS,
;       and Stk addressing modes.


bCSStk          macro   funcName, Operand

                ifb     <Operand>
                call    $&funcName&
                exitm
                endif


;; See if it's a byte variable:

                if      IsByte( <Operand> )

                call    $&funcName&CS
                dword   Operand
                exitm

                endif

;; See if it's a near or far pointer variable.

                ifnb    IsWordPtr(<Operand>)
                push    ds
                push    IsWordPtr(<Operand>)
                call    $&funcName&Stk
                exitm
                endif

                ifnb    IsDwordPtr(<Operand>)
                pushd   IsDwordPtr(<Operand>)
                call    $&funcName&Stk
                exitm
                endif

;; If it's not any of the above, we have an error.

%               echo    Illegal &funcName& operand.
                err
                endm


2.1.18 The wCSStkTOS Macro

The wCSStkTOS macro is one of several macros designed to implement other macros for Standard Library routines. In particular, those Standard Library functions that operate on word operands and support plain, CS, STK, and TOS modes typically use this macro to actually invoke a particular routine.

The primary purpose of the wCSStkTOS macro is to call an appropriate Standard Library function and automatically handle the plain, register, constant, and pointer addressing modes. For example, consider the puti Standard Library function. The Standard Library supports the following variations of puti:




	puti
	putiTOS
	putiSTK
	putiCS

A corresponding invocation of the xtrn macro generates the macros for these calls, you could call each of these functions using the following syntax:





	mov ax, IntToPrint
	puti

	push IntToPrint
	putiTOS

	pshadrs IntToPrint
	putiStk

	puti
	dword IntToPrint

Note, however, that the Standard Library provides additional syntax allowing a more convenient use of the puti set of routines. Specifically, you can supply an operand to puti as follows:





	puti
	puti      bx             ;Or any 16-bit register
	puti      125            ;Or any 16-bit constant
	puti      IntToPrint
	puti      [wordvar]
	puti      [dwordvar]

The first example above is the standard call to puti. The second and third examples above actually push their operand onto the stack and call putiTOS. The third example above (IntToPrint) winds up calling the putiCS routine. The last two examples above push the 32-bit address specified (DS assumed for word variables) onto the stack and call putiSTK.

The wCSStkTOS macro handles processing the puti (and other routines') operand field to determine which routine it should actually call and how it should pass the parameters. Basically, it uses the following algorithm:





; wCSStkTOS:
;
;       Routines that have a word operand and support the plain, CS,
;       Stk, and TOS addressing modes.


wCSStkTOS       macro   funcName, Operand

                ifb     <Operand>
                call    $&funcName&
                exitm
                endif

;; See if it's a 16-bit register

                ifnb    IsReg16( <Operand> )

                push    IsReg16( <Operand> )
                call    $&funcName&TOS
                exitm

                endif

;; See if it's a numeric constant

                if      IsNumConst( <Operand> )

                push    Operand
                call    $&funcName&TOS
                exitm

                endif

;; See if it's a near or far pointer variable.

                ifnb    IsWordPtr(<Operand>)
                push    ds
                push    IsWordPtr(<Operand>)
                call    $&funcName&Stk
                exitm
                endif

                ifnb    IsDwordPtr(<Operand>)
                pushd   IsDwordPtr(<Operand>)
                call    $&funcName&Stk
                exitm
                endif

;; See if it's a word variable:

                if      IsWord( <Operand> )

                call    $&funcName&CS
                dword   Operand
                exitm

                endif

;; If it's not any of the above, we have an error.

%               echo    Illegal &funcName& operand.
                err
                endm


2.1.19 The wCSStk Macro

Provides support like wCSStkTOS except this macro is intended for use by routines that do not supply the xxxxTOS call. This macro is just a hacked-down version of wCSStk, so there is no real need to spend a lot of time discussing it. Note that because of the TOS omission, this macro does not support the constant or register addressing modes.






; wCSStk:
;
;       Input routines that have a word operand and support the plain, CS,
;       and Stk addressing modes, returning a 16-bit value.


wCSStk          macro   funcName, Operand

                ifb     <Operand>
                call    $&funcName&
                exitm
                endif

;; See if it's a 16-bit register (need to implement TOS mode
;; for getc, etc.)
;;
;;              ifnb    IsReg16( <Operand> )
;;
;;              call    $&funcName&TOS
;;              pop     IsReg16( <Operand> )
;;              exitm
;;
;;              endif


;; See if it's a near or far pointer variable.

                ifnb    IsWordPtr(<Operand>)
                push    ds
                push    IsWordPtr(<Operand>)
                call    $&funcName&Stk
                exitm
                endif

                ifnb    IsDwordPtr(<Operand>)
                pushd   IsDwordPtr(<Operand>)
                call    $&funcName&Stk
                exitm
                endif

;; See if it's a word variable:

                if      IsWord( <Operand> )

                call    $&funcName&CS
                dword   Operand
                exitm

                endif

;; If it's not any of the above, we have an error.

%               echo    Illegal &funcName& operand.
                err
                endm





; LCSStkTOS:
;
;       Routines that have a dword operand and support the plain, CS,
;       Stk, and TOS addressing modes.


2.1.20 The LCSStkTOS Macro

The LCSStkTOS macro is one of several macros designed to implement other macros for Standard Library routines. In particular, those Standard Library functions that operate on dword (long) operands and support plain, CS, STK, and TOS modes typically use this macro to actually invoke a particular routine.

The primary purpose of the LCSStkTOS macro is to call an appropriate Standard Library function and automatically handle the plain, register, constant, and pointer addressing modes. For example, consider the putl Standard Library function. The Standard Library supports the following variations of putl:




	putl
	putlTOS
	putlSTK
	putlCS

A corresponding invocation of the xtrn macro generates the macros for these calls, you could call each of these functions using the following syntax:





	mov ax, LongToPrint
	putl

	push LongToPrint
	putlTOS

	pshadrs LongToPrint
	putlStk

	putl
	dword LongToPrint

Note, however, that the Standard Library provides additional syntax allowing a more convenient use of the putl set of routines. Specifically, you can supply an operand to putl as follows:





	putl
	putl      ebx            ;Or any 32-bit register
	putl      125            ;Or any 32-bit constant
	putl      LongToPrint
	putl      [wordvar]
	putl      [dwordvar]

The first example above is the standard call to putl. The second and third examples above actually push their operand onto the stack and call putlTOS. The third example above (LongToPrint) winds up calling the putlCS routine. The last two examples above push the 32-bit address specified (DS assumed for word variables) onto the stack and call putlSTK.

The LCSStkTOS macro handles processing the putl (and other routines') operand field to determine which routine it should actually call and how it should pass the parameters. Basically, it uses the following algorithm:





LCSStkTOS       macro   funcName, Operand

                ifb     <Operand>
                call    $&funcName&
                exitm
                endif

;; See if it's a 16-bit register

                ifnb    IsReg32( <Operand> )

                push    IsReg32( <Operand> )
                call    $&funcName&TOS
                exitm

                endif

;; See if it's a numeric constant

                if      IsNumConst( <Operand> )

                pushd   Operand
                call    $&funcName&TOS
                exitm

                endif

;; See if it's a near or far pointer variable.

                ifnb    IsWordPtr(<Operand>)
                push    ds
                push    IsWordPtr(<Operand>)
                call    $&funcName&Stk
                exitm
                endif

                ifnb    IsDwordPtr(<Operand>)
                pushd   IsDwordPtr(<Operand>)
                call    $&funcName&Stk
                exitm
                endif

;; See if it's a dword variable:

                if      IsDWord( <Operand> )

                call    $&funcName&CS
                dword   Operand
                exitm

                endif

;; If it's not any of the above, we have an error.

%               echo    Illegal &funcName& operand.
                err
                endm


2.1.21 The LCSSTK Macro

Provides support like LCSStkTOS except this macro is intended for use by routines that do not supply the xxxxTOS call. This macro is just a hacked-down version of LCSStk, so there is no real need to spend a lot of time discussing it. Note that because of the TOS omission, this macro does not support the constant or register addressing modes.




; lCSStk:
;
;       Input routines that have a word operand and support the plain, CS,
;       and Stk addressing modes, returning a 32-bit value.


lCSStk          macro   funcName, Operand

                ifb     <Operand>
                call    $&funcName&
                exitm
                endif

;; See if it's a 32-bit register (need to implement TOS mode
;; for getul, etc.)
;;
;;              ifnb    IsReg32( <Operand> )
;;
;;              call    $&funcName&TOS
;;              pop     IsReg32( <Operand> )
;;              exitm
;;
;;              endif


;; See if it's a near or far pointer variable.

                ifnb    IsWordPtr(<Operand>)
                push    ds
                push    IsWordPtr(<Operand>)
                call    $&funcName&Stk
                exitm
                endif

                ifnb    IsDwordPtr(<Operand>)
                pushd   IsDwordPtr(<Operand>)
                call    $&funcName&Stk
                exitm
                endif

;; See if it's a dword variable:

                if      IsDWord( <Operand> )

                call    $&funcName&CS
                dword   Operand
                exitm

                endif

;; If it's not any of the above, we have an error.

%               echo    Illegal &funcName& operand.
                err
                endm


2.1.22 The sbCSStkTOS Macro

The sbCSStkTOS macro is one of several macros designed to implement other macros for Standard Library routines. Standard Library functions that operate on a byte operand producing a string result, or operate on a string operand producing a byte result typically use this macro. This macro supports the plain, CS, STK, and TOS modes for use with the byte operand.

The primary purpose of the sbCSStkTOS macro is to call an appropriate Standard Library function and automatically handle the plain, register, constant, and pointer addressing modes. For example, consider the htoa Standard Library function. The Standard Library supports the following variations of htoa:




	htoa
	htoaTOS
	htoaSTK
	htoaCS

A corresponding invocation of the xtrn macro generates the macros for these calls, you could call each of these functions using the following syntax:





	lesi    StringVar
	htoa                ;Leaves value in AL.

	pshadrs StringVar
	htoaTOS             ;Leaves value on TOS.

	pshadrs StringVar   ;Address of eight-bit value
	pshadrs HexValue
	htoaStk

	htoaCS              ;Leaves value in AL.
	dword StringVar

Note, however, that the Standard Library provides additional syntax allowing a more convenient use of the atoh set of routines. Specifically, you can supply an operand to htoa as follows:





	htoa
	htoa      HexValue, StringVar     ;Uses TOS form
	htoa      HexVar, StringVar       ;Uses Stk form.

The first example above is the standard call to htoa. The second example above actually pushes the value of hexValue and the address of StringVar onto the stack and calls htoaTOS. The third example above winds up calling the htoaStk routine.

The sbSStkTOS macro handles processing the htoa (and other routines') operand field to determine which routine it should actually call and how it should pass the parameters. Basically, it uses the following algorithm:





; sbCSStkTOS:
;
;       Routines that have a string operand and support the plain, CS,
;       Stk, and TOS addressing modes.
;
;       They must produce a 8-bit result and leave the result in
;       AL, TOS, or a destination byte.
;
;       Note:   The "ScndOpb" macro handles the second operand if it
;               is present.

ScndOpb         macro   Operand2, funcName
                if      IsByte( <Operand2> )
                push    seg &Operand2&
                push    offset &Operand2&
                call    $&funcName&Stk
                exitm
                endif

                ifnb    IsWordPtr( <Operand2> )
                push    ds
                push    IsWordPtr( <Operand2> )
                call    $&funcName&Stk
                exitm
                endif

                ifnb    IsDWordPtr( <Operand2> )
                push    IsDWordPtr( <Operand2> )
                call    $&funcName&Stk
                exitm
                endif

%               echo    If &funcName has two operands,  then second
                echo    operand must be a byte, [word], or [dword] parameter.
                err

                endm



sbCSStkTOS      macro   funcName, Operand1, Operand2

                ifb     <Operand2>

                ifb     <Operand1>
                call    $&funcName&
                exitm
                endif


;; See if it's a byte variable:

                if      IsByte( <Operand1> )

                call    $&funcName&CS
                dword   Operand1
                exitm

                endif




                else    ;Operand2 is not blank




                if      IsByte( <Operand1> )

                push    seg &Operand1&
                push    offset &Operand1&

                ScndOpb Operand2, funcName
                exitm

                endif   ;IsByte




;; See if it's a near or far pointer variable.

                ifnb    IsWordPtr( <Operand1> )
                push    ds
                push    IsWordPtr( <Operand1> )

                ScndOpb Operand2, funcName
                exitm

                endif


; Check far ptr here.

                ifnb    IsDwordPtr(<Operand1>)
                pushd   IsDwordPtr(<Operand1>)
                
                ScndOpb Operand2, funcName
                exitm
                endif


                endif   ;Check of operand2.

;; If it's not any of the above, we have an error.

%               echo    Illegal &funcName& operand.
                err
                endm


2.1.23 The swCSStkTOS Macro

The swCSStkTOS macro is virtually identical to the sbCSStkTOS macro except it handles a word and string operand pair rather than a byte and string operand pair. Standard Library routines like AtoI and ItoA use this macro.





swCSStkTOS      macro   funcName, Operand1, Operand2

                ifb     <Operand2>

                ifb     <Operand1>
                call    $&funcName&
                exitm
                endif


;; See if it's a byte variable:

                if      IsByte( <Operand1> )

                call    $&funcName&CS
                dword   Operand1
                exitm

                endif




                else    ;Operand2 is not blank




                if      IsByte( <Operand1> )

                push    seg &Operand1&
                push    offset &Operand1&

                ScndOpw Operand2, funcName
                exitm

                endif   ;IsByte




;; See if it's a near or far pointer variable.

                ifnb    IsWordPtr( <Operand1> )
                push    ds
                push    IsWordPtr( <Operand1> )

                ScndOpw Operand2, funcName
                exitm

                endif


; Check far ptr here.

                ifnb    IsDwordPtr(<Operand1>)
                pushd   IsDwordPtr(<Operand1>)
                
                ScndOpw Operand2, funcName
                exitm
                endif


                endif   ;Check of operand2.

;; If it's not any of the above, we have an error.

%               echo    Illegal &funcName& operand.
                err
                endm


2.1.24 The sdCSStkTOS Macro

The sdCSStkTOS macro is virtually identical to the sbCSStkTOS macro except it handles a dword and string operand pair rather than a byte and string operand pair. Standard Library routines like AtoL and LtoA use this macro.





; sdCSStkTOS:
;
;       Routines that have a string operand and support the plain, CS,
;       Stk, and TOS addressing modes.
;
;       They must produce a 32-bit result and leave the result in
;       EAX, TOS, or a destination word.
;
;       Note:   The "ScndOpd" macro handles the second operand if it
;               is present.

ScndOpd         macro   Operand2, funcName
                if      IsDWord( <Operand2> )
                push    seg &Operand2&
                push    offset &Operand2&
                call    $&funcName&Stk
                exitm
                endif

                ifnb    IsWordPtr( <Operand2> )
                push    ds
                push    IsWordPtr( <Operand2> )
                call    $&funcName&Stk
                exitm
                endif

                ifnb    IsDWordPtr( <Operand2> )
                push    IsDWordPtr( <Operand2> )
                call    $&funcName&Stk
                exitm
                endif

%               echo    If &funcName has two operands,  then second
                echo    operand must be a dword, [word], or [dword] parameter.
                err

                endm



sdCSStkTOS      macro   funcName, Operand1, Operand2

                ifb     <Operand2>

                ifb     <Operand1>
                call    $&funcName&
                exitm
                endif


;; See if it's a byte variable:

                if      IsByte( <Operand1> )

                call    $&funcName&CS
                dword   Operand1
                exitm

                endif




                else    ;Operand2 is not blank




                if      IsByte( <Operand1> )

                push    seg &Operand1&
                push    offset &Operand1&

                ScndOpd Operand2, funcName
                exitm

                endif   ;IsByte




;; See if it's a near or far pointer variable.

                ifnb    IsWordPtr( <Operand1> )
                push    ds
                push    IsWordPtr( <Operand1> )

                ScndOpd Operand2, funcName
                exitm

                endif


; Check far ptr here.

                ifnb    IsDwordPtr(<Operand1>)
                pushd   IsDwordPtr(<Operand1>)
                
                ScndOpd Operand2, funcName
                exitm
                endif


                endif   ;Check of operand2.

;; If it's not any of the above, we have an error.

%               echo    Illegal &funcName& operand.
                err
                endm