; GENERAL DEF'S ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  GotGeneralDefs = -1

; General-purpose data definitions & macros ;;;;;;;;;;;;;;;;;;;;

  FALSE = 0
  TRUE	= -1

  NewLine equ $0A,$0D

; Auxiliary message display macros =============================

  macro ReportErrorToUser szMessage, szWindowTitle, hWnd
  {
  if ~ hWnd eq
    invoke  MessageBox, hWnd, szMessage, szWindowTitle, \
	      MB_OK or MB_ICONERROR or MB_DEFBUTTON1
  else if ~ szWindowTitle eq
    invoke  MessageBox, HWND_DESKTOP, szMessage, \
	      szWindowTitle, \
	      MB_OK or MB_ICONERROR or MB_DEFBUTTON1
  else
    invoke  MessageBox, HWND_DESKTOP, szMessage, "Error", \
	      MB_OK or MB_ICONERROR or MB_DEFBUTTON1
  end if
  }

; Time stuff ===================================================

; Symbol def's -------------------------------------------------

  Monday    = 1
  Tuesday   = 2
  Wednesday = 3
  Thursday  = 4
  Friday    = 5
  Saturday  = 6
  Sunday    = 7

  January   = 1
  February  = 2
  March     = 3
  April     = 4
  May	    = 5
  June	    = 6
  July	    = 7
  August    = 8
  September = 9
  October   = 10
  November  = 11
  December  = 12

  Y2004     = 4

  struc DATECODE Day, Month, Year {
    label .base
    label .DWCode as dword ; Note this is arranged so that
    .Day    db Day	     ; comparing a DateCode1.TimeCode
    .Month  db Month	     ; with a DateCode2.TimeCode will
    .Year   dw Year	     ; do a correct time comparison.
    .sizeof = $ - .base
  }

  struc TIMECODE Hour, Minute, Second {
    label .base
    label .DWCode as dword
    .Sec256ths	db ?
    .Second	db Second
    .Minute	db Minute
    .Hour	db Hour
    .sizeof = $ - .base
  }

; Coordinates & such ===========================================

  struc BOXDIMS {
    label .base
    .XPos   dd ?
    .YPos   dd ?
    .Width  dd ?
    .Height dd ?
    .sizeof = $ - .base
  }
  virtual at ebx
    EBX_BoxDims BOXDIMS
  end virtual
  virtual at esi
    ESI_BoxDims BOXDIMS
  end virtual
  virtual at edi
    EDI_BoxDims BOXDIMS
  end virtual

  struc COORD2D {
    label .base
    label .XPos dword
    label .XDelta dword
    label .XChg dword
    .x	dd ?
    label .YPos dword
    label .YDelta dword
    label .YChg dword
    .y	dd ?
    .sizeof = $ - .base
  }
  virtual at ebx
    EBX_Crd2D COORD2D
  end virtual

  struc COORD3D {
    label .base
    label .XPos dword
    label .XDelta dword
    label .XChg dword
    .x	dd ?
    label .YPos dword
    label .YDelta dword
    label .YChg dword
    .y	dd ?
    label .ZPos dword
    label .ZDelta dword
    label .ZChg dword
    .z	dd ?
    .sizeof = $ - .base
  }
  virtual at ebx
    EBX_Crd3D COORD3D
  end virtual

  struc VECTOR2D {
    label .base
    .x	dd ?
    .y	dd ?
    .sizeof = $ - .base
  }
  virtual at ebx
    EBX_Vector2D VECTOR2D
  end virtual

  struc VECTOR3D {
    label .base
    .x	dd ?
    .y	dd ?
    .z	dd ?
    .sizeof = $ - .base
  }
  virtual at ebx
    EBX_Vector3D VECTOR3D
  end virtual

  struc CURSORCTRL {
    label .base
    .time   dd ?
    .x	    dd ?
    .y	    dd ?
    .z	    dd ?
    .wheel  dd ?
    label .throttle dword at .wheel
    .yaw    dd ?
    .hatx   dd ?
    .haty   dd ?
    .sizeof = $ - .base
  }

  struc CURSOR {
    label .base
    .FlagsLoDW	dd ?
    .FlagsHiDW	dd ?
    .Pos	CURSORCTRL
    .Delta	CURSORCTRL
    .sizeof = $ - .base
  }

  ; CURSOR LoDW flags
  CURSOR_Button1Down	equ $00000001
  CURSOR_LButtonDown	equ $00000001
  CURSOR_LButtonDblClk	equ $00000002
  CURSOR_Button2Down	equ $00000004
  CURSOR_MButtonDown	equ $00000004
  CURSOR_MButtonDblClk	equ $00000008
  CURSOR_Button3Down	equ $00000010
  CURSOR_RButtonDown	equ $00000010
  CURSOR_RButtonDblClk	equ $00000020

; Windows-relevant data definitions ============================

  struc WINDOWINFO {
    label .base
    .XPos   dd 0
    label .Left dword at .XPos
    .YPos   dd 0
    label .Top dword at .YPos
    .Right  dd 0
    .Bottom dd 0
    .Width  dd 0
    .Height dd 0
    .State  dd 0
    .Style  dd 0
    .sizeof = $ - .base
  }

  struc EXTWINDOWINFO {
    label .base
    .XPos   dd 0
    label .Left dword at .XPos
    .YPos   dd 0
    label .Top dword at .YPos
    .Right  dd 0
    .Bottom dd 0
    .Width  dd 0
    .Height dd 0
    .State  dd 0
    .Style  dd 0
    .Title  rb $80
    .sizeof = $ - .base
  }
  virtual at ebx
    EBX_ExtWindowInfo EXTWINDOWINFO
  end virtual
  virtual at esi
    ESI_ExtWindowInfo EXTWINDOWINFO
  end virtual
  virtual at edi
    EDI_ExtWindowInfo EXTWINDOWINFO
  end virtual

; Basic program guts data ======================================

  struc GENREGBACKUP
  { ; Note pushad pushes the gen. registers in this same order.
    label .base
    .EAX dd ?
    .ECX dd ?
    .EDX dd ?
    .EBX dd ?
    .ESP dd ?
    .EBP dd ?
    .ESI dd ?
    .EDI dd ?
    .sizeof = $ - .base
  }
  virtual at 0
    Type_GenRegBackup GENREGBACKUP
  end virtual
  virtual at ebx
    EBX_GenRegBackup GENREGBACKUP
  end virtual
  virtual at esi
    ESI_GenRegBackup GENREGBACKUP
  end virtual
  virtual at edi
    EDI_GenRegBackup GENREGBACKUP
  end virtual

; General-purpose macros ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; CPU stuff macros =============================================

  macro SAVEGENREGS location
  {
    mov [location#.EAX], eax
    mov [location#.ECX], ecx
    mov [location#.EDX], edx
    mov [location#.EBX], ebx
    mov [location#.ESP], esp
    mov [location#.EBP], ebp
    mov [location#.ESI], esi
    mov [location#.EDI], edi
  }

  macro RSTRGENREGS location
  {
    mov eax, [location#.EAX]
    mov ecx, [location#.ECX]
    mov edx, [location#.EDX]
    mov ebx, [location#.EBX]
    mov esp, [location#.ESP]
    mov ebp, [location#.EBP]
    mov esi, [location#.ESI]
    mov edi, [location#.EDI]
  }

  macro RSTRGENREGS_KEEP_EAX location
  {
    mov ecx, [location#.ECX]
    mov edx, [location#.EDX]
    mov ebx, [location#.EBX]
    mov esp, [location#.ESP]
    mov ebp, [location#.EBP]
    mov esi, [location#.ESI]
    mov edi, [location#.EDI]
  }

  macro RSTRGENREGS_KEEP_ECX location
  {
    mov eax, [location#.EAX]
    mov edx, [location#.EDX]
    mov ebx, [location#.EBX]
    mov esp, [location#.ESP]
    mov ebp, [location#.EBP]
    mov esi, [location#.ESI]
    mov edi, [location#.EDI]
  }

  macro RSTRGENREGS_KEEP_EAX_ECX location
  {
    mov edx, [location#.EDX]
    mov ebx, [location#.EBX]
    mov esp, [location#.ESP]
    mov ebp, [location#.EBP]
    mov esi, [location#.ESI]
    mov edi, [location#.EDI]
  }

; Struct property def. macros ==================================

; The following macros define these constants for arrays
; declared at assembly time with the d<type> and r<type>
; directives:
; .Base = the base address of the declared array
; .Size = the size of the declared array
; .Dim = the number of elements in the array

  struc db [data]
  {
  common
    label .base byte
    db data
    .sizeof = $ - .base
    .dim = .sizeof
  }

  struc dw [data]
  {
  common
    label .base word
    dw data
    .sizeof = $ - .base
    .dim = .sizeof / 2
  }

  struc dd [data]
  {
  common
    label .base dword
    dd data
    .sizeof = $ - .base
    .dim = .sizeof / 4
  }

  struc dq [data]
  {
  common
    label .base qword
    dq data
    .sizeof = $ - .base
    .dim = .sizeof / 8
  }

  struc rb MemberCount
  {
    label .base byte
    rb MemberCount
    .sizeof = $ - .base
    .dim = MemberCount
  }

  struc rw MemberCount
  {
    label .base word
    rw MemberCount
    .sizeof = $ - .base
    .dim = MemberCount
  }

  struc rd MemberCount
  {
    label .base dword
    rd MemberCount
    .sizeof = $ - .base
    .dim = MemberCount
  }

  struc rq MemberCount
  {
    label .base qword
    rq MemberCount
    .sizeof = $ - .base
    .dim = MemberCount
  }

; Data movement macros =========================================

  macro CopyBytes Number, Dest, Source
  {
    push  ecx
    cld
    mov   ecx, [Number]
    lea   esi, [Source]
    lea   edi, [Dest]
    rep movsb
    pop   ecx
  }

  macro CopyWords Number, Dest, Source
  {
    push  ecx
    cld
    mov   ecx, [Number]
    lea   esi, [Source]
    lea   edi, [Dest]
    rep movsw
    pop   ecx
  }

  macro CopyDwords Number, Dest, Source
  {
    push  ecx
    cld
    mov   ecx, [Number]
    lea   esi, [Source]
    lea   edi, [Dest]
    rep movsd
    pop   ecx
  }

  ; ANSI null-terminated string copying
  macro CopyAzString MaxNumber, Dest, Source
  {
    local LoopTop, LoopEnd

    push  ecx
    push  eax
    mov   ecx, [MaxNumber]
    lea   esi, [Source]
    lea   edi, [Dest]
  LoopTop:
      dec   ecx        ; In case that the program picks up a
      js    LoopEnd    ; MaxNumber of 0, this will solve that.
      mov   al, [esi]
      mov   [edi], al
      or    al, al
      jz    LoopEnd
      inc   esi
      inc   edi
      jmp   LoopTop
  LoopEnd:
    pop   eax
    pop   ecx
  }

  ; Wide-char. (unicode) null-terminated string copying
  macro CopyWzString MaxNumber, Dest, Source
  {
    local LoopTop, LoopEnd

    push  ecx
    push  eax
    mov   ecx, [MaxNumber]
    lea   esi, [Source]
    lea   edi, [Dest]
  LoopTop:
      dec   ecx
      js    LoopEnd
      mov   ax, [esi]
      mov   [edi], ax
      or    ax, ax
      jz    LoopEnd
      add   esi, 2
      add   edi, 2
      jmp   LoopTop
  LoopEnd:
    pop   eax
    pop   ecx
  }

; Data extraction macros =======================================

; Windows-related data extraction macros -----------------------
  macro LOWORD Value { and [Value], $0000FFFF }
  macro HIWORD Value { shr [Value], $10 }

; Bit field data reading & writing macros ----------------------
; Notes:
; - These macros handle bit fields up to 32 bits in size.
; - These macros also require that EAX, ECX, and EDX be
;   available for modification.

  macro BitFld_InsWithMask BitField, Mask, Input
  {
    mov   eax, [BitField] ; Get the bit field into the accum..
    mov   edx, [Mask] ; Load the bit mask into ECX.
    not   edx	      ; Invert the mask and use it to mask
    and   eax, edx    ; out the bits that'll be replaced.
    push  eax	      ; Save the modified bit field.
    not   edx	      ; Reinvert the mask.
    bsf   ecx, edx    ; Find the offset of its first ON bit.
    mov   eax, [Input] ; Load the input bit sequence.
    shl   edx, cl     ; Shift it to its correct bit offset.
    and   eax, edx    ; Make sure there's no superfluous bits.
    pop   ecx	      ; Restore the modified bit mask.
    or	  eax, ecx    ; Combine the modified bit mask and the
  }		      ; shifted bits.

  macro BitFld_ExtWithMask BitField, Mask, Output
  {
    mov eax, [BitField]
    and eax, [Mask]
    bsf ecx, [Mask]
    shr eax, cl
    mov [Output], eax
  }

  macro BitFld_InsWithOffset BitField, Offset, Size, Input
  {
    mov eax, $FFFFFFFF	; Create a mask for truncating the
    shl eax, [Size]	; input bit sequence in case there's
    not eax		; superfluous bits there.
    mov edx, [Input]	; Load the input bit sequence.
    and edx, eax	; Mask out possible unwanted bits.
    mov ecx, [Offset]	; Load the bit offset desired.
    shl eax, ecx	; Shift the mask to its bit field
    shl edx, ecx	; position, along with the input bits.
    mov ecx, [BitField] ; Get the bit field sequence.
    and ecx, eax	; Mask out the bits being replaced.
    or	eax, ecx	; Combine the two bit sequences.
    mov [BitField], eax ; Send back the new bit sequence.
  }

  macro BitFld_ExtWithOffset BitField, Offset, Size, Output
  {
    mov eax, [BitField]
    shr eax, [Offset]
    mov ecx, $FFFFFFFF
    shl ecx, [Size]
    not ecx
    and eax, ecx
    mov [Output], eax
  }

; Address processing macros ====================================

  macro RNDUPTO4XVALUE reg
  {
  local Done

    push  reg
    and   reg, $00000003
    pop   reg
    jz	  Done
    and   reg, $FFFFFFFC
    add   reg, $4
  Done:
  }

  macro RNDUPTO16XVALUE Value
  {
  local Done

    push  reg
    and   reg, $0000000F
    pop   reg
    jz	  Done
    and   reg, $FFFFFFF0
    add   reg, $10
  Done:
  }

; Binary <-> ASCII conversion macros ===========================

  ; Note these macros require that eax, ecx, and edx be
  ; available for modification.  (Other general registers used
  ; may be saved in the stack.)  For decimal and FP value
  ; translations,

; Binary <-> ASCII hexadecimal conversions ---------------------

; DWORD Binary to ASCII hexadecimal string conversion

  ; Hexadecimal dump of DWORD (i.e. any value up to 4B in size)
  ; !!!> Outp here should be at least an 8B buffer.

  macro DWORD_to_HDStr Inp, OutpAddr
  {
  local LoopStart, KeepNumeral, LoopEnd

    push  eax ebx ecx edx
    mov   edx, [Inp]
    mov   ecx, $8
    mov   ebx, [OutpAddr]
    add   ebx, $7
  LoopStart:
      mov   eax, edx
      and   eax, $0000000F
      add   eax, '0'	       ; (30h)
      cmp   eax, '9'	       ; (39h)
      jbe   KeepNumeral
      add   eax, 'A' - '9' - 1 ; (07h)
    KeepNumeral:
      mov   [ebx], al  ; Record the HD character.
      dec   ecx
      jz    LoopEnd
      dec   ebx
      shr   edx, $4
      jmp   LoopStart
  LoopEnd:
    pop   edx ecx ebx eax
  }

; ASCII hexadecimal string to DWORD binary conversion

  ; Note:  If the input string is invalid, the macro will set
  ; ECX to number of the character that caused an error.
  ; Otherwise, it will set ECX to 0.

  macro HDStr_to_DWORD InpAddr, Outp
  {
  local Loop_Top, ProcNumeral, ProcUpperCap, ProcLowerCap, \
    ContLoop, Finish, Abort

    push  ebx
    mov   ebx, [InpAddr]
    xor   ecx, ecx   ;
    xor   edx, edx   ; Store the final value in here.
  Loop_Top:
      mov   eax, [ebx]
      cmp   eax, '0'
      jb    Abort
      cmp   eax, '9'
      jbe   ProcNumeral
      cmp   eax, 'A'
      jb    Abort
      cmp   eax, 'F'
      jbe   ProcUpperCap
      cmp   eax, 'a'
      jb    Abort
      cmp   eax, 'f'
      jbe   ProcLowerCap
      jmp   Abort
    ProcNumeral:
      sub   eax, '0'
      jmp   ContLoop
    ProcUpperCap:
      sub   eax, 'A' - $A
      jmp   ContLoop
    ProcLowerCap:
      sub   eax, 'a' - $A
    ContLoop:
      add   edx, eax
      inc   ebx
      inc   ecx
      cmp   ecx, 8
      je    Done
      shl   edx, 4
      jmp   Loop_Top
  Finish:
    xor   ecx, ecx
    mov   [Outp], edx
  Abort:
    pop   ebx
  }

; Binary <-> ASCII decimal conversions -------------------------

; DWORD binary to ASCII decimal string conversion

  ; Note the output string buffer for this should be at least
  ; 10 bytes in length.
  macro UInt4B_to_DecStr Inp, OutpAddr
  {
  local Loop_Top

    push  ebx
    mov   eax, [Inp] ; Use EAX as the source register.
    mov   ebx, [OutpAddr]
  Loop_Top:
      xor   edx, edx
      div   eax:edx, 10
      add   dl, '0'
      mov   [ebx], dl
      inc   ebx
      or    eax, eax	; If there's still more to be
      jnz   Loop_Top	; processed, continue the loop.
    pop   ebx
  }

  ; Note the output string buffer for this should be at least
  ; 11 bytes in length.
  macro SInt4B_to_DecStr Inp, OutpAddr
  {
  local Loop_Top

    push  ebx
    mov   eax, [Inp]
    mov   ebx, [OutpAddr]
    or	  eax, eax
    jns   Loop_Top
    mov   [ebx], '-'
    inc   ebx
    neg   eax
  Loop_Top:
      xor   edx, edx
      div   eax:edx, 10
      add   dl, '0'
      mov   [ebx], dl
      inc   ebx
      or    eax, eax	; If there's still more to be
      jnz   Loop_Top	; processed, continue the loop.
    pop   ebx
  }

  ; Note:  If the string supplied is invalid, ECX will be set
  ; to the number of the first character that is invalid;
  ; otherwise, ECX will be 0.  If there is an overflow, ECX
  ; will be -1.  This instruction also requires that the FPU
  ; stack not be full.  The input string should be NULL
  ; terminated.
  macro DecStr_to_Int4B InpAddr, Outp
  {
  local Loop_Top, Finish, Abort_InvalidChar, Abort_Overflow, \
    Done

    push  ebx
    mov   ebx, [InpAddr]
    xor   ecx, ecx
    xor   eax, eax
    mov   dl, byte [ebx]
    cmp   dl, '-'
    jne   Loop_Top
    inc   ebx
  Loop_Top:
      cmp   dl, '0'
      jb    Abort_InvalidChar
      cmp   dl, '9'
      ja    Abort_InvalidChar
      sub   dl, '0'
      add   eax, edx
      xor   edx, edx
      mul   $0000000A
      or    edx, edx
      jnz   Abort_Overflow
      inc   ecx
      inc   ebx
      mov   dl, byte [ebx]
      or    dl, dl
      jnz   Loop_Top
  Finish:
    xor   ecx, ecx
    mov   ebx, [InpAddr]
    cmp   byte [ebx], '-'
    jne   Done
    or	  eax, eax
    js	  Abort_Overflow
    neg   eax
    jmp   Done
  Abort_Overflow:
    mov   ecx, -1
  Abort_InvalidChar:
    xor   eax, eax
  Done:
    mov   [Outp], eax
    pop   ebx
  }

; Integer range tests, conditional jumps =======================

; Unsigned integer tests ---------------------------------------

; IS ON RANGE
  macro UIntIsOnRange Value, LBound, UBound, TargetLabel
  {
  local TestFailed

    cmp [Value], [LBound]
    jb	TestFailed
    cmp [Value], [UBound]
    jbe TargetLabel
  TestFailed:
  }

; IS IN RANGE
  macro UIntIsInRange Value, LBound, UBound, TargetLabel
  {
  local TestFailed

    cmp [Value], [LBound]
    jbe TestFailed
    cmp [Value], [UBound]
    jb	TargetLabel
  TestFailed:
  }

; IS NOT ON RANGE
  macro UIntIsNotOnRange Value, LBound, UBound, TargetLabel
  {
  local TestFailed

    cmp [Value], [LBound]
    jb	TargetLabel
    cmp [Value], [UBound]
    ja	TargetLabel
  TestFailed:
  }

; IS NOT IN RANGE
  macro UIntIsNotInRange Value, LBound, UBound, TargetLabel
  {
  local TestFailed

    cmp [Value], [LBound]
    jbe TargetLabel
    cmp [Value], [UBound]
    jae TargetLabel
  TestFailed:
  }

; Signed integer tests -----------------------------------------

; IS ON RANGE
  macro SIntIsOnRange Value, LBound, UBound, TargetLabel
  {
  local TestFailed

    cmp [Value], [LBound]
    jl	TestFailed
    cmp [Value], [UBound]
    jle TargetLabel
  TestFailed:
  }

; IS IN RANGE
  macro SIntIsInRange Value, LBound, UBound, TargetLabel
  {
  local TestFailed

    cmp [Value], [LBound]
    jle TestFailed
    cmp [Value], [UBound]
    jl	TargetLabel
  TestFailed:
  }

; IS NOT ON RANGE
  macro SIntIsNotOnRange Value, LBound, UBound, TargetLabel
  {
  local TestFailed

    cmp [Value], [LBound]
    jl	TargetLabel
    cmp [Value], [UBound]
    jg	TargetLabel
  TestFailed:
  }

; IS NOT IN RANGE
  macro SIntIsNotInRange Value, LBound, UBound, TargetLabel
  {
  local TestFailed

    cmp [Value], [LBound]
    jle TargetLabel
    cmp [Value], [UBound]
    jge TargetLabel
  TestFailed:
  }

; Integer range tests, conditional proc. calls =================

; Unsigned integer tests ---------------------------------------

; IS ON RANGE
  macro UIntDoIfOnRange Value, LBound, UBound, ProcName, \
    [DWParam]
  {
  local TestFailed

  common
    cmp   [Value], [LBound]
    jb	  TestFailed
    cmp   [Value], [UBound]
    ja	  TestFailed
  reverse
    pushd DWParam
  common
    call  ProcName
  TestFailed:
  }

; IS IN RANGE
  macro UIntDoIfInRange Value, LBound, UBound, ProcName, \
    [DWParam]
  {
  local TestFailed

  common
    cmp   [Value], [LBound]
    jbe   TestFailed
    cmp   [Value], [UBound]
    jae   TestFailed
  reverse
    pushd DWParam
  common
    call  ProcName
  TestFailed:
  }

; IS NOT ON RANGE
  macro UIntDoIfNotOnRange Value, LBound, UBound, ProcName, \
    [DWParam]
  {
  local TestFailed, TestPassed

  common
    cmp   [Value], [LBound]
    jb	  TestPassed
    cmp   [Value], [UBound]
    ja	  TestPassed
    jmp   TestFailed
  TestPassed:
  reverse
    pushd DWParam
  common
    call  ProcName
  TestFailed:
  }

; IS NOT IN RANGE
  macro UIntDoIfNotInRange Value, LBound, UBound, ProcName, \
    [DWParam]
  {
  local TestFailed, TestPassed

  common
    cmp   [Value], [LBound]
    jbe   TestPassed
    cmp   [Value], [UBound]
    jae   TestPassed
    jmp   TestFailed
  TestPassed:
  reverse
    pushd DWParam
  common
    call  ProcName
  TestFailed:
  }

; Signed integer tests -----------------------------------------

; IS ON RANGE
  macro SIntDoIfOnRange Value, LBound, UBound, ProcName, \
    [DWParam]
  {
  local TestFailed

  common
    cmp   [Value], [LBound]
    jl	  TestFailed
    cmp   [Value], [UBound]
    jg	  TestFailed
  reverse
    pushd DWParam
  common
    call  ProcName
  TestFailed:
  }

; IS IN RANGE
  macro SIntDoIfInRange Value, LBound, UBound, ProcName, \
    [DWParam]
  {
  local TestFailed

  common
    cmp   [Value], [LBound]
    jle   TestFailed
    cmp   [Value], [UBound]
    jge   TestFailed
  reverse
    pushd DWParam
  common
    call  ProcName
  TestFailed:
  }

; IS NOT ON RANGE
  macro SIntDoIfNotOnRange Value, LBound, UBound, ProcName, \
    [DWParam]
  {
  local TestFailed, TestPassed

  common
    cmp   [Value], [LBound]
    jl	  TestPassed
    cmp   [Value], [UBound]
    jg	  TestPassed
    jmp   TestFailed
  TestPassed:
  reverse
    pushd DWParam
  common
    call  ProcName
  TestFailed:
  }

; IS NOT IN RANGE
  macro SIntDoIfNotInRange Value, LBound, UBound, ProcName, \
    [DWParam]
  {
  local TestFailed, TestPassed

  common
    cmp   [Value], [LBound]
    jle   TestPassed
    cmp   [Value], [UBound]
    jge   TestPassed
    jmp   TestFailed
  TestPassed:
  reverse
    pushd DWParam
  common
    call  ProcName
  TestFailed:
  }

; END OF FILE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;