You've got

 
 
 Ŀ 
                                                               
                    \ | /  
  -- * -- 
                              / | \  
                                                               
                           VERSION 1.0                         
                      Earl Hammon, July 1996                   
  
 
 
 

ͻ
  Ŀ  
   !! SOME IMPORTANT STUFF YOU MUST KNOW TO REALLY USE THE LIBRARY !!   
    
ͼ
o  The library is case sensitive (unless you compile without sensitivity).
o  You will frequently encounter "relative jump out of range by xxxx bytes"
   errors when you assemble.  To correct this problem precede the offending
   command by a JUMPS directive, and follow it with a NOJUMPS directive.

ͻ
 CONDITIONAL MACROS 
ͻ
 JUMPIF <condition> location                   
 IF_ <condition>                               
 _ELSE_                                        
 _ELSEIF_ <condition>                          
 _ENDIF                                        
 SWITCH_ <argument>                            
 CASE_ <argument>                              
 MULTICASE_ <argument>                         
 LASTCASE_ <argument>                          
 JUMPCASE <argument> location                  
 DEFAULT_                                      
 _BREAK                                        
 _ENDCASE                                      
 _ENDS                                         
ͼ
  
  Ŀ
   JUMPIF <expression> location 

DESCRIPTION
     Jumps to "location" if <expression> evaluates to true.

GENERAL NOTES
     <expression> may be any valid conditional.  See appendix.

OPTIMIZATION NOTES
     Standard IF_ optimazition.  See appendix.


  
  Ŀ
   IF_ <expression> 

DESCRIPTION
     If <expression> evaluates to true executes the code up until the next 
     _ELSEIF_, _ELSE_ or _ENDIF

GENERAL NOTES
     *  <expression> may be any valid conditional.  See appendix.
     *  IF_ statements may be nested.

TECHNICAL NOTES
     IF_ jumps to a nonsense label whenever the opposite of <expression> is
     met.  This label is declared by the next _ELSEIF_, _ELSE_ or _ENDIF.

OPTIMIZATION NOTES
     Standard IF_ optimazition.  See appendix.


  
  Ŀ
   _ELSE_ 

DESCRIPTION
     If the previous conditional evaluated false, executes the code up to the
     next _ELSEIF_, _ELSE_, or _ENDIF.
     
GENERAL NOTES
     _ELSE_ statements may be chained, though it usually would be considered
     bad programming practice.  See Technical Notes for explanation.

TECHNICAL NOTES
     _ELSE_ inserts a jump command to a nonsense label, followed by the label
     from the previous IF_.  The nonsense label _ELSE_ jumps to is of the
     same form as the nonsense label IF_ jumps to  otherwise IF_..._ELSE_
     constructs would require a unique terminator, _ENDIFELSE.  What all this
     means is that the following will assemble without complaint:
                IF_ <expression>
                  ...
                _ELSE_
                  ...
                _ELSE_
                  ...
                _ENDIF
     If expression evaluates to true, then the code in the first and third
     blocks will be executed.  If expression evaluates false, then the code
     in the middle section will be executed.

OPTIMIZATION NOTES
     If the _ENDIF terminating an _ELSE_ block is followed by another _ELSE_,
     the macros will produce a jump to a jump.  Use SENDSKIPTO to bypass any
     subsequent _ELSE_ blocks which will never be executed. For example, the
     following code
                  _ELSE_
                    ...
                  _ENDIF
                _ELSE_
                  ...
                _ENDIF
                ...
     is optimized with
                  SENDSKIPTO here
                  _ELSE_
                    ...
                  _ENDIF
                _ELSE_
                  ...
                _ENDIF
        here:   ...



  Ŀ
   _ELSEIF_ <expression> 

DESCRIPTION
     If the previous conditional evaluated false and <expression> evaluates
     true, executes the code up to the next _ELSEIF_, _ELSE_, or _ENDIF.  If
     the previous conditional evaluated to true, or any previous _ELSEIF_ in
     its particular chain evaluated to true, the code is skipped.

GENERAL NOTES
     *  <expression> may be any valid conditional.  See appendix.
     *  _ELSEIF_ statements may be chained and/or nested.

TECHNICAL NOTES
     The construct
                _ELSEIF_ <expression> 
                  ...
                _ELSEIF_ <expression> 
                  ...
                _ENDIF
     is literally converted into 
                _ELSE_ 
                  IF_ <expression>
                    ...
                  _ELSE_ 
                    IF_ <expression>
                      ...
                    _ENDIF
                  _ENDIF
                _ENDIF

OPTIMIZATION NOTES
     Standard IF_ optimazition.  See appendix.



  Ŀ
   _ENDIF 

DESCRIPTION
     Signifies end of a block begun by IF_, _ELSEIF_ or _ELSE_.

GENERAL NOTES
     None.

TECHNICAL NOTES
     _ENDIF creates the nonsense label jumped to by the closest preceding
     unmatched IF_, _ELSEIF_ or _ELSE_.

OPTIMIZATION NOTES
     No code is ever generated from an _ENDIF, so optimization is impossible.



  Ŀ
   SWITCH_ <argument> 

DESCRIPTION
     Sets up the argument compared with the arguments to subsequent CASE_
     statements.

GENERAL NOTES
     *  <argument> may be a constant, register, or memory location.  However,
        its size may not exceed the current word size.
     *  The angle brackets are only required if the argument contains 
        multiple words (e.g., <byte ptr 2>).
     *  SWITCH_ statements may not be nested.
     *  There may be code between a SWITCH_ and the first CASE_ statement.
        This code will always be executed, so be careful what you do if you
        choose to use it!  See Technical Notes to avoid possible frustration.

TECHNICAL NOTES
     If no register is declared safe to use, AX or EAX is pushed onto the
     stack and used as temporary storage for the argument variable.  This is
     completely transparent to the program as it is popped before any user
     code is executed.

OPTIMIZATION NOTES
     If you specify a register with the USE directive, that register will be
     used for the argument and so long as the register is big enough to hold
     the argument nothing will be pushed or popped from the stack.  Further,
     you can assume within your CASE_ statements that the specified register
     will still equal the argument to the SWITCH_.



  Ŀ
   CASE_ <argument> 

DESCRIPTION
     Compares "argument" to the argument specified in the current SWITCH_.
     If the two are equivalent, program code is executed until the next 
     _BREAK statement.

GENERAL NOTES
     *  <argument> may be a constant, register, or memory location.  However,
        its size may not exceed the current word size.
     *  The angle brackets are only required if the argument contains 
        multiple words (e.g., <byte ptr 2>).
     *  As in C, CASE_ statements "fall through" into the next CASE_ unless
        explicitly forbidden.

TECHNICAL NOTES
     *  If any code was generated between the last CASE_ variation and this
        CASE_, a jump past the comparison is emplaced to implement the "fall
        through" feature.
     *  CASE_ compares the argument to the register containing the argument
        specified in the last SWITCH_ statement.  If it fails it jumps to a
        nonsense label specifying the start of the next CASE_ or DEFAULT_.
        This jump skips over any "fall through" jump.

OPTIMIZATION NOTES
     *  Use an _ENDCASE to terminate a CASE_ block if the last instruction of
        the block is a jump.  This will prevent the "fall through" jump from
        being assembled.
     *  Use MULTICASE_ and LASTCASE_ whenever you have more than one CASE_ 
        statement for a block of code, as in the following:
                CASE_ arg1
                CASE_ arg2
                CASE_ arg3
                  ...
        becomes
                MULTICASE_ arg1
                MULTICASE_ arg2
                LASTCASE_ arg3
                  ...
        This replaces a conditional skip of a "fall through" jump with a jump
        to the start of the block after the LASTCASE_.  This is especially
        useful whenever you have more than two CASE_ statements, and the "fall
        through" jumps cascade.  In this situation the MULTICASE_ format will
        eliminate one jump (and one pop if no register was specified with a
        USE directive) per CASE_, making your code smaller and faster.



  Ŀ
   MULTICASE_ <argument> 

DESCRIPTION
     Compares "argument" to the argument specified in the current SWITCH_.
     If the two are equivalent, program code is executed from the point after
     the next LASTCASE_ up until the next _BREAK statement.

GENERAL NOTES
     *  <argument> may be a constant, register, or memory location.  However,
        its size may not exceed the current word size.
     *  The angle brackets are only required if the argument contains 
        multiple words (e.g., <byte ptr 2>).
     *  There may be normal CASE_ statements between a MULTICASE_ instruction
        and its LASTCASE_; the MULTICASE_ will skip these rather than "fall
        through".  Furthermore, any CASE_ statement which "falls through" a
        MULTICASE_ will resume execution at the code following its LASTCASE_.
     *  No code following a MULTICASE_ will be executed.

TECHNICAL NOTES
     *  If any code was generated between the last CASE_ variation and this
        MULTICASE_, a jump past the comparison is emplaced to implement the 
        "fall through" feature.
     *  MULTICASE_ compares the argument to the register containing the 
        argument specified in the last SWITCH_ statement.  If it fails it 
        jumps to a nonsense label specifying the start of the next CASE_,
        MULTICASE_, LASTCASE_ or DEFAULT_.  This jump skips over any "fall
        through" jump.

OPTIMIZATION NOTES
     MULTICASE_ is itself an optimization.



  Ŀ
   LASTCASE_ <argument> 

DESCRIPTION
     Compares "argument" to the argument specified in the current SWITCH_.
     If the two are equivalent, program code is executed up until the next
     _BREAK statement.  Signifies last part of a MULTICASE_ chain.

GENERAL NOTES
     *  <argument> may be a constant, register, or memory location.  However,
        its size may not exceed the current word size.
     *  The angle brackets are only required if the argument contains 
        multiple words (e.g., <byte ptr 2>).
     *  LASTCASE_ statements need not be preceeded by MULTICASE_ statements.
        MULTICASE_ statements, however, must be followed by a LASTCASE_.

TECHNICAL NOTES
     *  If any code was generated between the last CASE_ variation and this
        LASTCASE_, a jump past the comparison is emplaced to implement the 
        "fall through" feature.
     *  LASTCASE_ compares the argument to the register containing the 
        argument specified in the last SWITCH_ statement.  If it fails it 
        jumps to a nonsense label specifying the start of the next CASE_,
        MULTICASE_, LASTCASE_ or DEFAULT_.  This jump skips over any "fall
        through" jump.

OPTIMIZATION NOTES
     LASTCASE_ is itself an optimization.



  Ŀ
   JUMPCASE <argument> location 

DESCRIPTION
     Compares "argument" to the argument specified in the current SWITCH_.
     If the two are equivalent, program execution is sent to "location".

GENERAL NOTES
     *  <argument> may be a constant, register, or memory location.  However,
        its size may not exceed the current word size.
     *  The angle brackets are only required if the argument contains 
        multiple words (e.g., <byte ptr 2>).
     *  No prior CASE_ statements "fall through" into a JUMPCASE.  If you
        want to fall through to a jump, use
                CASE_ <argument>
                  jmp     location
                _ENDCASE
     *  JUMPCASE produces more optimal code if there was a free register for
        the SWITCH_ statement to use.  Use a "FLAG" directive to determine
        which register is being used, if any.

TECHNICAL NOTES
     JUMPCASE_ compares the argument to the register containing the argument
     specified in the last SWITCH_ statement.  If there is no need to pop
     anything from the stack, it jumps to "location" on success or "falls
     through" on failure.  If something must be popped from the stack it
     produces code almost identical to the sample code immediately above,
     with the exception that "fall through" execution skips the jump.

OPTIMIZATION NOTES
     JUMPCASE is itself an optimization.


  Ŀ
   DEFAULT_ 

DESCRIPTION
     Following block of code up to the next _BREAK is executed.

GENERAL NOTES
     *  <argument> may be a constant, register, or memory location.  However,
        its size may not exceed the current word size.
     *  The angle brackets are only required if the argument contains 
        multiple words (e.g., <byte ptr 2>).
     *  Previous CASE_ variations do "fall through" the DEFAULT_.
     *  DEFAULT_ may be used in the middle of a SWITCH_ command to insert
        code that will always be executed if execution makes it that far in
        the SWITCH_.  If you do this, you must end the DEFAULT_ with _ENDCASE
        to resume checking against CASE_ arguments rather than "falling
        through".  Also, be careful not to destroy the comparison register,
        and remember to push AX (for byte or word SWITCH_ arguments) or EAX
        (for dword arguments) before the _ENDCASE if you did not specify a
        register with USE.

TECHNICAL NOTES
     DEFAULT_ is essentially a CASE_ which always evaluates true.

OPTIMIZATION NOTES
     None.



  Ŀ
   _BREAK 

DESCRIPTION
     Sends program execution past the _ENDS statement.

GENERAL NOTES
     None.

TECHNICAL NOTES
     Creates a jump past the end of the current SWITCH_ block.  Also prevents
     "fall through" jump from being created if no code is generated before
     the next CASE_ variation.

OPTIMIZATION NOTES
     None.



  Ŀ
   _ENDCASE 

DESCRIPTION
     Prevents generation of "fall through" jump if no code is generated
     next CASE_ variation.

GENERAL NOTES
     _ENDCASE does not alter execution flow in any way; if any code is
     inserted between an _ENDCASE and the next CASE_ variation, the _ENDCASE
     does absolutely nothing!

TECHNICAL NOTES
     None.

OPTIMIZATION NOTES
     _ENDCASE is itself an optimization.



  Ŀ
   _ENDS 

DESCRIPTION
     Specifies end of a SWITCH_ block.  Program execution resumes after this
     point upon a _BREAK command.

GENERAL NOTES
     None.

TECHNICAL NOTES
     Creates the nonsense label that _BREAK jumps to.

OPTIMIZATION NOTES
     No code is ever generated from a _ENDS, so optimization is not possible.



ͻ
 LOOPING MACROS                                                   
ͻ
 DO_                                             
 _WHILE                                          
 WHILE_                                          
 _WEND                                           
ͼ

  Ŀ
   DO_ 

DESCRIPTION
     Signifies start of a block of code to be repeated until the condition
     specified in the corresponding _WHILE evaluates false.  The end of the
     block is signified with a _WHILE command.  Condition testing takes place
     after the block of code, so it is always executed at least once.

GENERAL NOTES
     DO_ statements can be nested.

TECHNICAL NOTES
     DO_ simply creates a nonsense label which _WHILE jumps to.

OPTIMIZATION NOTES
     No code is generated, so no optimization is possible.



  Ŀ
   _WHILE <expression> 

DESCRIPTION
     Provides the condition under which a DO_ block of code is repeated.  If
     expression evaluates true, the block is repeated.

GENERAL NOTES
     Be careful not to confuse _WHILE with WHILE_.  To keep track of the
     difference, note that the underscore "points" toward the block of code
     that the instruction is associated with.

TECHNICAL NOTES
     _WHILE gets directly translated to a JUMPIF to a nonsense label.  This
     label is created by the DO_ statement.

OPTIMIZATION NOTES
     Standard IF_ optimazition.  See appendix.



  Ŀ
   WHILE_ <expression> 

DESCRIPTION
     Executes the following block of code up to the corresponding _WEND
     statement until expression evaluates false.  Evaluation takes place
     before code execution, so it is possible that the code in the WHILE_
     block will never be executed.

GENERAL NOTES
     *  WHILE_ statements can be nested.
     *  Be careful not to confuse WHILE_ with _WHILE.  To keep track of the
        difference, note that the underscore "points" toward the block of
        code that the instruction is associated with.
     *  You can make FOR loops out of a WHILE_ in the following manner:
                mov     counter, start
                WHILE_ <counter NE stop>
                  ...
                  add     counter, step
                _WEND
        C programmers will recognize that this is exactly what C does when it
        encounters a for loop.

TECHNICAL NOTES
     WHILE_ gets directly translated into a nonsense label followed by an IF_
     statement of the same condition.  The _WEND directive jumps to this
     label.

OPTIMIZATION NOTES
     Standard IF_ optimazition.  See appendix.



  Ŀ
   _WEND 

DESCRIPTION
     Demarcates the end of a WHILE_ block.  Execution is always returned to
     the start of the corresponding WHILE_ condition testing.

GENERAL NOTES
     None.

TECHNICAL NOTES
     _WEND gets directly translated to a jump to a nonsense label.  This
     label is created by the WHILE_ statement.

OPTIMIZATION NOTES
     No optimization is possible.



ͻ
 FUNCTION / SUBROUTINE MACROS 
ͻ
 DECLARE type fname <args> [scope] [language]    
 FUNCTION_ type fname <args> [scope] [language]  
 _ENDFUNC [fname]                                
 RETURN value                                    
 fname <args>              C function call       
 @fname <args>             Pascal function call  
ͼ
  
  Ŀ
   DECLARE type fname <args> [scope] [language] 

DESCRIPTION
     Creates a macro to handle calls to C and Pascal functions, including
     parameter passing and stack frame setup/cleanup.

GENERAL NOTES
     *  "type" may be one of "void", "byte", "char", "word", "dword", "near",
        or "far".  It is case sensitive.  "void" signifies that the function
        returns nothing.  "near" and "far" are pointer types.  In 16-bit
        segments near pointers take 2 bytes and far pointers take 4; in a
        32-bit segment near pointers take 4 bytes and far pointers take 6.
        o  Byte and char values are returned in AL
        o  Word and 16-bit near values are returned in AX
        o  16-bit dword and 16-bit far values are returned in DX:AX, DX is
           the segment or high word;
        o  32-bit dword and 32-bit near values are returned in EAX
        o  32-bit far values are returned in DX:EAX.
     *  Argument format is
              <[static] [type] name [ = default], ...>
        o  "static" declares that the variable is to occupy a memory
           location; whenever absent the variable is placed on the stack.
        o  "type" is the same as the return type, but may not be "void".  A
           type must be included with all variables except registers.
        o  "name" may be a register or a label.  Local labels are allowed -
           i.e, "name" can appear in more than one function without any
           confusion on the assembler's part.  The only restriction is that
           if you declare a static variable to have name "name", that label
           will only reference that particular static memory area.  Any later
           declaration of "name" will share that same address space.  This is
           different from stack variables, which are unique to each function.
        o  "default" must be a register, constant, or memory location if it
           is declared.  This feature is independent of the actual function!
           This means external C and Pascal functions can be given defaults,
           and C++ functions can be given unique defaults.  It also means
           that if those languages support defaults, those defaults are not
           ported to the assembly function.
     *  "scope" can be either "near" or "far".  The default is near for both
        16-bit and 32-bit segments.
     *  "language" can be "c" or "pascal".  The default is C.
        o  C functions push the first parameter last and prefix the function
           name with an underscore (_fname).  Do not put the underscore there
           yourself, unless you want double underscores (__fname)!  The stack
           is cleaned up by adding stack argument size to the stack pointer
           after the function call returns.  C functions can be called with
           "fname <parms>".
        o  Pascal functions push the first parameter first and don't alter the
           function name.  The stack is cleaned up by the returning function.
           Pascal functions can be called with "@fname <parms>".
     *  Functions are inherently recursive.  Just be cautious with register
        and static parameters.
     *  Can be used to declare an external C or Pascal function, as in a C
        header file.  It thus makes calls to C and Pascal code simple.  Note
        that I used the Borland parameter return convention, as I was able to
        find it.  I never have seen Pascal, so the Pascal functions may not
        work exactly right, though they should if I read the specs properly -
        and they were the right specs :).  If this is not the case, please
        let me know the proper format so I can fix it.  Thanks.

TECHNICAL NOTES
     DECLARE makes a macro which can be used to perform a function call.  It
     generates no code except when you call it.

OPTIMIZATION NOTES
     Use of purely static and register variables prevents any stack frame
     setup/cleanup, as such work would be pointless.  This saves a single
     instruction in C function calls, none in Pascal calls.

  
  
  Ŀ
   FUNCTION_ type fname <args> [scope] [language] 

DESCRIPTION
     If "fname" has not been declared previously, calls DECLARE with the
     given argument list.  If "fname" was declared previously it need not use
     identical argument names, but it must use the same types to avoid any
     unpredictable forms of assembly trauma.  The function implementation
     will follow up until the "_ENDFUNC fname" directive is encountered.

GENERAL NOTES
     *  The program may access function parameters by the names given in the
        parameter list, even if that parameter is a parameter to another
        function as well.
     *  "static" parameters not declared earlier in the code are created in
        the code segment.  However, addressing defaults to the data segment!
        BE CAREFUL!  I.e., whenever possible explicitly declare any static
        storage before the FUNCTION_ directive.  You need not declare storage
        before a DECLARE directive, however.
     *  You need not set up the stack frame in any way.  FUNCTION_ saves the
        BP or EBP register and points it to the top of the stack whenever any
        stack variables are used.  First stack parameter is at BP+4 for near
        16-bit functions, BP+6 for far 16-bit, EBP+8 for near 32-bit, and
        EBP+10 for far 32-bit if there is such a thing, I'm not sure... :).
        This corresponds to the first C parameter, or to the last Pascal
        parameter.
     *  Function declarations may not be nested.  Nesting will result in
        strange errors, primarily with data type mismatches.
     *  If you use this command to start a function, declare all parameters
        to be on the stack (i.e. no "static" or registers), and then access
        these parameters by their names rather than offsets from [BP], you
        can convert a C function to Pascal or vice versa simply by adding or
        removing "pascal" from the declaration!

TECHNICAL NOTES
     It declares stack parameters by making a temporary alias to that offset
     from BP or EBP with a size qualifier.  This is redefinable later, so you
     can reuse parameter names between functions.  Static labels, on the
     other hand, are program labels which cannot be redefined (for obvious
     reasons - schizophreniac assembly is BAD), hence you may not reuse a
     static parameter's name unless you want to access the same actual memory
     location.  The directive also sets up return type information.  These
     are the reasons that nesting is not allowed.
     
OPTIMIZATION NOTES
     Two instructions are saved at the top of the program if you make all
     parameters either static or registers.

  
  
  Ŀ
   _ENDFUNC [fname] 

DESCRIPTION
     Declares the end of a function.  If any code was assembled between the
     last RETURN directive and the _ENDFUNC, it generates a RETURN NOTHING.

GENERAL NOTES
     "fname" is optional.

TECHNICAL NOTES
     None.

OPTIMIZATION NOTES
     None.

  
  
  Ŀ
   RETURN [value] 

DESCRIPTION
     Cleans up the stack and returns a value from a C/Pascal function.

GENERAL NOTES
     *  The return value is stored as follows:
        o  For byte returns, "value" is stored in AL.
        o  For 16/32-bit word and 16-bit near returns, "value" is stored in
           AX.
        o  For 16-bit word and far returns, "value" is stored in DX:AX, with
           DX being the high word or segment.
        o  For 32-bit word and near returns, "value" is stored in EAX.
        o  For 32-bit far returns, "value" is stored in DX:EAX, with DX being
           the 32-bit equivalent of a segment.
     *  "value" can be one of the following:
        o  "NOTHING" (to avoid warnings when you don't specify a return value
           for a non-void function)
        o  A register, memory address, or number.  Segment overrides are
           allowed.
        o  If the return size is greater than the word size (i.e. a 16-bit
           dword or a 16/32-bit far pointer) "value" may also be of the form
           "high wordlow word/dword".  Each part is independent, and may be
           a register, memory address, or number.  Segment overrides are
           allowed and may be unique for each part.
     *  The method of stack cleanup is different for C and Pascal functions.
        By using this macro instead of "ret" instructions, you can convert
        your assembly code from a C routine to a Pascal routine or vice
        versa simply by adding or removing "pascal" from the declaration.
     *  It is safe to use any register combination in returns (e.g. AXDX as
        a 16-bit dword return).  RETURN insures that the right register gets
        the right value, period.

TECHNICAL NOTES
     None.

OPTIMIZATION NOTES
     RETURN always generates optimal code for the value specified.



  Ŀ
   C function call 

DESCRIPTION
     Calls a C function with a given parameter list.

GENERAL NOTES
     *  Function must be declared with DECLARE or FUNCTION_ before the call.
        It need not be defined first.
     *  Syntax is fname <arg1, arg2...>
     *  Can be used to call C functions in an external C program.

TECHNICAL NOTES
     "fname" is in fact a macro created by the DECLARE statement.

OPTIMIZATION NOTES
     The function call is self-optimizing based on parameters and cpu.



  Ŀ
   Pascal function call 

DESCRIPTION
     Calls a Pascal function with a given parameter list.

GENERAL NOTES
     *  Function must be declared with DECLARE or FUNCTION_ before the call.
        It need not be defined first.
     *  Syntax is @fname <arg1, arg2...>
     *  Can be used to call Pascal functions in an external Pascal program.

TECHNICAL NOTES
     "@fname" is in fact a macro created by the DECLARE statement.

OPTIMIZATION NOTES
     The function call is self-optimizing based on parameters and cpu.



ͻ
 EXPANSION CONTROL and OPTIMIZATION OPTIONS 
ͻ
 USE register                                    
 CANUSE argument                                 
 PROTECT argument                                
 FLAG <text string>                              
 SENDSKIPTO location                             
ͼ

  Ŀ
   USE register 

DESCRIPTION
     Forces use of "register" next time a command requires use of a register.

GENERAL NOTES
     *  "argument" may be "NONE", "ALL", or a register.  The only registers
        that can be used are EAX, EBX, ECX, EDX, ESI, EDI, and EBP, or parts
        of those registers (e.g. AH, BP).
     *  USE forces use of "register" by preventing use of all registers but
        the one specified.  Therefore, as soon as the register is used by a
        command there are no registers left available.  The one exception is
        an 8086/88 function call, which leaves the register free for use.

TECHNICAL NOTES
     "USE reg" is literally a shorthand form of "PROTECT ALL / USE reg".

OPTIMIZATION NOTES
     "USE" is itself an optimization feature.



  Ŀ
   CANUSE argument 

DESCRIPTION
     Signifies that registers indicated by "argument" are safe for use by the
     extension commands.

GENERAL NOTES
     *  "argument" may be "NONE", "ALL", or "<register, register,...>".
        The only registers that can be freed for use are EAX, EBX, ECX, EDX,
        ESI, EDI, and EBP, or portions of those registers (e.g. AH, BP).
     *  If only one register is being freed for use, the angle brackets are
        optional.
     *  CANUSE only adds arguments to the usable list.  If you want to force
        use of a particular register, you must use "USE register".
     *  Whenever an extension command needs a register, it uses the first one
        flagged as available from the following search order:
        o  8-bit:   AH, AL, CH, CL, BH, BL, DH, DL
        o  16-bit:  AX, CX, BX, DX, SI, DI, BP
        o  32-bit:  EAX, ECX, EBX, EDX, ESI, EDI, EBP
     *  As soon as a register is used by a command it is removed from the
        usable pool.

TECHNICAL NOTES
     "CANUSE" updates an internal flag word via a logical OR, hence the
     addition of items to the list with each "CANUSE" rather than composing
     the entire list.

OPTIMIZATION NOTES
     "CANUSE" is itself an optimization feature.



  Ŀ
   PROTECT argument 

DESCRIPTION
     Signifies that registers indicated by "argument" must not be touched by
     any extension commands.

GENERAL NOTES
     *  "argument" may be "NONE", "ALL", or "<register, register,...>".
        The only registers that are used are EAX, EBX, ECX, EDX, ESI, EDI,
        and EBP, or portions of those registers (e.g. AH, BP).  Therefore,
        these are the only valid registers to protect.
     *  If only one register is being protected, the angle brackets are
        optional.
     *  As soon as a register is used by a command it is removed from the
        usable pool.

TECHNICAL NOTES
     "PROTECT" updates an internal flag word via a logical AND, hence the
     removal of items from the list with each "PROTECT" rather than composing
     the entire list.

OPTIMIZATION NOTES
     "PROTECT" is itself an optimization feature.



  Ŀ
   SENDSKIPTO location 

DESCRIPTION
     Redirects the skip jump of many instructions to "location"

GENERAL NOTES
     SENDSKIPTO works with IF_, _ELSEIF_, _ELSE_, WHILE_ and CASE_
     instructions.
     o  In IF_ instructions, it controls where execution goes when the test
        condition is not met.
     o  In _ELSE_ and _ELSEIF_ commands, it controls where execution goes
        once the previous block has finished executing.
     o  In WHILE_ statements, it controls where execution goes once the test
        condition fails.
     o  In CASE_ statements, it controls where successful jumps go.  Always
        use JUMPCASE instead, as this will keep you out of possible stack
        trouble, is easier to read and requires less typing.

TECHNICAL NOTES
     SENDSKIPTO merely overrides the default jump location.  Any nonsense
     labels are still created, though possibly unreferenced.

OPTIMIZATION NOTES
     SENDSKIPTO is itself an optimization feature.



  Ŀ
   FLAG <text string> 

DESCRIPTION
     Displays register usage information at a particular point in program
     code.  "text string" is used to identify which part of the program the
     message goes with, and precedes the register info.

GENERAL NOTES
     *  Use FLAG to help optimize and debug your code.
     *  If FLAG says that something is on the stack you may not assume that
        you can reference the used item via the register given.  Instead, the
        register will be restored before your code is reached.
     *  Use FLAG only immediately after an IF_, _ELSEIF_, JUMPIF, WHILE_,
        _WHILE, or SWITCH_ statement, or after an 8086 function call.  Any
        other place will give misleading information.

TECHNICAL NOTES
     None.

OPTIMIZATION NOTES
     "FLAG" is purely an assembly-time optimizing / debugging tool, and never
     generates any code.


ͻ
͹
                         APPENDIX - IF_ STUFF                            
͹
ͼ
1.  Valid conditional test.
2.  Multiple-test conditionals.
3.  Optimization notes.


Ŀ
 VALID CONDITIONAL EXPRESSIONS                                             

There are three basic forms of conditionals:
o  value comparison
o  value testing
o  flag testing

Value comparison is of the form <arg1 test arg2>.  "test" must be one of:
                EQ, equal               NE, not equal
                LT, less than           LE, less or equal
                GT, greater than        GE, greater or equal
"arg1" and "arg2" may be registers, memory locations, or number.  If they are 
numbers and you get error messages, include data size (e.g. "word ptr 7").

Value testing is of the form <[NOT] arg>.  Again, "arg" may be a register,
memory location, or number.  <arg> evaluates true whenever "arg" is non-zero;
<NOT arg> evaluates true whenever "arg" is zero.

Flag testing is of the form <condition>.  Condition must be one of:
                ZERO                    NOT ZERO
                CARRY                   NO CARRY
                EQUAL                   NOT EQUAL
                POSITIVE                NEGATIVE
                LESS                    LESS OR EQUAL
                GREATER                 GREATER OR EQUAL
                EVEN PARITY             ODD PARITY
                OVERFLOW                NO OVERFLOW
The flags must have been set previous to the instruction.


Ŀ
 MULTIPLE-TEST CONDITIONALS                                                

Individual conditional units may be combined with AND and OR to form complex
conditionals.  Two things are important to know:
o  These operators are NOT bitwise; they are zero/nonzero!!!
o  These operators employ an "early-out" scheme.  Let's say you have two
   items, 'a' and 'b'.  These might be flag, variables, or even entire
   conditional subtrees.  Anyway, if you have <a OR b> and 'a' is nonzero,
   'b' is not tested.  Similarly, if you have <a AND b> and 'a' is zero, 'b'
   is not tested.

Consider an example.
                
                IF_ <<ax EQ 3> OR bx>

Note the way angle brackets are used.  The way conditional expansion works,
you must never have more than three "items" at any "level".  The example has
two levels, "ax EQ 3" and "<...> OR bx".  Each level has three items.  That
is, <ax OR bx OR cx> would not assemble properly; the "OR cx" would be
completely ignored!

Here is a tree diagram to more clearly show what I mean by levels and items:

                        < <...>  OR  bx >
                           /
                         /
                 < ax  EQ  3 >

Consider a more complicated example:

        _WHILE <<<NOT bx> OR <<ax EQ var1> OR <cx EQ 2>>>> AND <dx NE es>>

It's tree diagram would be
                        
                        < <...>  AND  <...> >
                           /             \
                         /                 \
              < <...>  OR  <...> >    < dx  NE  es >
                 /            \
               /                \
         < NOT bx >     < <...>  OR  <...> >
                           /            \
                         /                \
                 < ax  EQ  var1 >    < cx  EQ  2 >

WHEW!!!  So how do you make sure you get the right form?  Let's do an example
to get the above tree.  What you really want to implement is
                
        _WHILE <NOT bx OR ax EQ var1 OR cx EQ 2 AND dx NE es>

The first step is to put angle brackets around individual comparison units,
as in the following:

        _WHILE <<NOT bx> OR <ax EQ var1> OR <cx EQ 2> AND <dx NE es>>

Particularly note the brackets around "NOT bx".  Next, we must remember
whether the AND goes with all of the ORs, or just with the last OR.  In our
example it goes with the whole set, so we now have

        _WHILE <<<NOT bx> OR <ax EQ var1> OR <cx EQ 2>> AND <dx NE es>>

This still is not good, because the first sub-expression has five items.  To
fix this we arbitrarily group two of the items, like so:

        _WHILE <<<NOT bx> OR <<ax EQ var1> OR <cx EQ 2>>>> AND <dx NE es>>

And we're done.  If instead we had chosen the AND to go with only the last OR
we would have had

        _WHILE <<<NOT bx> OR <ax EQ var1>> OR <<cx EQ 2> AND <dx NE es>>>

If you lack confidence you can prove this works to yourself, or you can just
take my word for it.

As an interesting point, here is an easy way to trace testing order from the
diagram.  I'll use the second example here as well.  Simply start at the
left-most node (hitherto an item), in this case "NOT bx", and trace the tree
from there.  We always start at a node, so back up one level.  We haven't
gone down the other branch of this node yet.  The "OR" tells us we need to
trace this other branch only if bx is zero.  Let's say for argument's sake
that it is, in which case we trace down the left branch until we find a new
node.  That gives us "ax EQ var1".  Let's pretend that ax does in fact equal
var1, so that we don't have to trace down the other branch of the OR node to
"cx EQ 2".  Instead we back up all the way to the first level, which is an
AND node.  Since we said that ax equals var1 the left side has evaluated to
nonzero or true, so we must trace the other branch.  If dx equals es then the
tree will evaluate false, and if dx does not equal es the tree will evaluate
true.


Ŀ
 OPTIMIZATION                                                              

This leads directly into the next section, optimization.

What is the current minimum number of conditionals evaluated in the tree?
Two - if "NOT bx" is true, only "dx NE es" must be evaluated.  If "NOT bx" is
false then at least three conditionals must be evaluated, four if ax does not
equal var1.  But what if we switched the left and right branches of the top
node?  Then as little as one conditional might be evaluated, instead of at
least two.

Here then is the optimization technique - put shorter branches on the left of
the tree.  The exceptions would be AND branches which are usually true or OR
branches which are usually false, in which case the other branch will usually
have to be evaluated and another organization might be quicker.  Also, try to
make your trees UNBALANCED, with a bias to the right.  This will tend to cut
the most testing.  Our earlier example might be optimized by
                        
                        < <...>  AND  <...> >
                           /             \
                         /                 \
                 < dx  NE  es >    < <...>  NE  <...> >
                                      /            \
                                    /                \
                              < NOT bx >     < <...>  OR  <...> >
                                                /            \
                                              /                \
                                      < ax  EQ  var1 >    < cx  EQ  2 >

Notice the characteristic right-heaviness.

Anyway, that's about it.  Happy programming :)

