        SUBT    System wide macro definitions => &.Hdr.Macros

OldOpt  SETA    {OPT}
        OPT     OptNoList+OptNoP1List

; ***********************************
; ***    C h a n g e   L i s t    ***
; ***********************************

; Date       Name  Description
; ----       ----  -----------
; 28-Sep-87  TMD   Modified CallAVector, VDWS for new soft-load version
; 29-Sep-87  TMD   Modified CallAVector again
; 05-Sep-87  SKS   Removed $hat option from Push
; 22-Oct-87  TMD   Modified CallAVector yet again
; 17-Dec-87  NDR   Modified Swap macro (conditional added)
; 15-Jan-88  BC    Fixed Byte and Word so that the actual parms may be expr.
; 04-Feb-88  SKS   Fixed BYTEWS macro. AAsm thinks :LOR is ok ?!
; 10-Mar-88  NDR   Implemented XError macro
; 11-Mar-88  NDR   Improved XError macro
; 19-Apr-88  SKS   Fixed BYTEWS macro. Added Immediate macro
; 21-Apr-88  SKS   Took debugging out of Immediate macro!. Fixed it to produce
;                  less rubbish in error cases
; 11-May-88  SKS   Added Command_LastName to Command macro
; 16-May-88  SKS   Added optimising addr macro. My apologies to anyone it
;                  screws up! Removed pre-OS 1.20 changes from list.
; 24-May-88  TMD   Added offset to AlignSpace.
; 31-May-88  SKS   addr now does register relative symbols too (need AAsm 1.48)
; 01-Jun-88  SKS   addr now understands different amounts of verbosity
; 03-Jun-88  SKS   make wsaddr to get round absolute problems
; 11-Jun-88  SKS   tweaked |#|
; 18-Jul-88  BC    Made PHPSEI & PLP use a register argument
; ------------------------- RISC OS 2.00 release ------------------------------
; 18-Oct-88  SKS   added AlignForModule macro
; 22-Feb-89  BC    Added file type definition macros
;  8-Sep-89  NDR   Added AddFSError
; 31-Oct-89  JSR   Added StringContains
; 06-Nov-89  BC    Added RRX macro (like ASR, ASL, ROR etc.)
; 22-Nov-89  NDR   Added GetIf macro
; 18-Jan-90  GJS   Added list of macros at the top for reference
; 06-Feb-90  TMD   Corrected date of the line above (said -89)
;                  Corrected syntax messages of AddFile and AddFileDescending; added description of AddFileDescending
; 26-Jul-90  BC    Improved ROR to accept (and modulo) large arguments, and fault zero
; 18-Sep-90  GJS   Added ADDL macro - Add immediate Rn, Rn, #number, which may take more than one instruction
; 11-Mar-90  OSS   Added MakeInternatErrorBlock - puts the tag rather than the string in the error block.
; 21-Mar-91  ECN   Added $tag to MakeInternatErrorBlock
; 10-Apr-91  DDV   Changed NOP to not used a MOVNV, simply does a MOV r0, r0
; 21-Apr-91  RM    Added ChkKernelVersion calls OS_ReadSysInfo to stop
;                  modules from being soft-loaded on 2.00.
; 19-Dec-91  BC    Added SWIChunk to do better SWI chunk allocation and checking
;
; *********************************************
; ***  List of macros in alphabetical order ***
; *********************************************
;        AddError $name,$text,$value                            Create an error structure
;        AddFSError      $class, $baseerr, $fsname, $fsnumber   Create a filing-system error structure
;$Value  AddFile  $FileType,$FileTypeName,$PostValue            Generate a File type label, incrementing current number
;$Value  AddFileDescending  $FileType,$FileTypeName,$PostValue  Generate a File type label, decrementing current number
;$label  ADDL    $reg, $var                                     Long range ADD $reg, $reg, #value
;$label  ADDR    $reg, $dest, $cond                             Long range ADR - but use addr if possible
;$label  addr    $reg, $object, $cc                             Better long range ADR
;$label  wsaddr    $reg, $object, $cc                           ???
;        AddSWI  $SWIName,$value                                Generate a SWI label
;$label  AlignSpace $value, $offset                             Align workspace to a boundary and offset
;$label  AlignForModule                                         Alignment for a module
;$label  ASL     $reg, $val, $cc                                Generate an ASL instruction
;$label  ASR     $reg, $val, $cc                                Generate an ASR instruction
;$label  BADDR   $reg, $dest, $cond                             Same as ADDR
;$label  BSR     $dest                                          BL but preserving R14
;$label  Byte    $value, $count                                 Add a byte of workspace
;        BYTEWS  $reg                                           Get pointer to OsbyteVars
;$label  CallAVector $cond                                      Call a vector
;$label  ChkKernelVersion                                       Call OS_ReadSysInfo(1) to stop modules from
;                                                               being soft loaded on 2.00.
;$label  CLC     $cond                                          Clear the carry flag (PSR -> nzcv)
;$label  CLRPSR  $bits, $regtmp, $cond                          Clear bits of the PSR
;$label  CLRV    $cond                                          Clear the overflow flag (PSR -> nzCv)
;        Command $cmd, $max, $min, $optbits, $cmdlabel          Generate a *command block
;$label  DEC     $reg,$by                                       Decrement a register (by a value)
;$label  DECS    $reg,$by                                       Decrement a register (by a value) settng PSR
;$label  DivRem  $rc, $ra, $rb, $rtemp                          Get DIV and REM of two values
;$label  DoCallTable $jumpreg, $tablename, $work                Call a routine in a jump table
;$label  DoFastJumpTable $jumpreg, $trash                       Call a routine in a jump table
;$label  DoJumpTable $jumpreg, $tablename, $work1, $work2       Call a routine in a jump table
;$label  DoSVCCallTable $jumpreg, $tablename                    Call a routine in a jump table
;$label  DoSVCJumpTable $jumpreg, $tablename                    Call a routine in a jump table
;$label  Error   $errno, $errstr                                Generate an immediate error
;$label  XError     $errsym, $c1, $c2                           Generate an error from an error block
;$label  ExitSWIHandler $cond                                   Exit macro for SWI handlers
;$label  GRAB    $reglist, $cond, $hat                          Pull registers from the stack
;        GetIf   $filename, $cc                                 Get a file conditional on an assembly-time flag
;$label  GRABS   $reglist, $cond                                Pull registers from stack with warning
;$label  INC     $reg,$by                                       Increment a register (by a value)
;$label  INCS    $reg,$by                                       Increment a register (by a value) setting the PSR
;        InfoWord $max, $min, $optbits                          Set the info word for a module command table
;        Immediate $var                                         Set flag if value is immediate
;$label  LDROSB  $reg, $var, $cond                              Load OSByte variable
;$label  LDW     $dest, $addr, $temp1, $temp2                   Load word from unknown alignment
;$label  LowerCase $reg, $wrk                                   Lowercase A-Z
;$label  LSL     $reg, $val, $cc                                Generate an LSL instruction
;$label  LSR     $reg, $val, $cc                                Generate an LSR instruction
;$label  MakeErrorBlock $name, $noalign                         Create an error block
;$label  MakeInternatErrorBlock $name, $noalign                 Create an internationalised error block
;$label  MakeStarSet $name                                      Create a *Set command for a filetype
;$label  MULTIPLY $rc, $ra, $rb                                 Multiply numbers together
;$label  NOP                                                    A do-nothing instruction
;$label  Overlap $master, $slave                                ???
;$lab    PHPSEI  $register                                      Preserve interrupt state then disable IRQs
;$lab    PLP     $register                                      Restore IRQ's from a PHPSEI
;$label  Pull    $reglist, $cond, $hat                          Pull registers from the stack
;$label  Push    $reglist, $cond, $hat                          Push registers onto the stack
;$label  RETURN  $cond                                          End a BL'ed routine
;$label  RETURNS $cond                                          End a BL'ed routine setting the PSR
;$label  ROR     $reg, $val, $cc                                Generate an ROR instruction
;$label  RRX     $reg, $cc                                      Generate an RRX instruction
;$label  SCPSR   $set, $clr, $regtmp, $cond                     Set and clear bits in the PSR
;$label  SEC     $cond                                          Set the Carry flag (PSR -> nzCv)
;$label  SETPSR  $bits, $regtmp, $cond                          Set bits in the PSR
;$label  SETV    $cond                                          Set the overflow flag
;$label  STASH   $reglist, $cond, $hat                          Push registers onto the stack
;$label  STRIM   $string                                        Output an immediate string
;$Answer StringContains $string,$substring                      Assembly-time INSTR function
;$label  STROSB  $reg, $var, $temp, $cond                       Store an OSByte variable
;$label  Swap    $ra, $rb, $cc                                  Swap two registers
;$n      SWIChunk        $v,$s,$o                               Allocate a SWI chunk
;$label  TOGPSR  $bits, $regtmp, $cond                          Toggle bits in the PSR
;$label  UpperCase $reg, $wrk                                   Uppercase a-z
;        VDWS    $reg                                           Get pointer to VduDriverWorkSpace
;$label  Word    $value, $count                                 Add word to workspace
;$label  WRLN    $string                                        WriteLn a string
;$var    |#|     $bytes                                         Allocate workspace downwards rather than upwards


; =============================================
; Macros should be kept in ALPHABETICAL order !
; =============================================

        MACRO
        AddError $name,$text,$value
   [    "$value" = ""
ErrorNumber_$name # 1
   |
ErrorNumber_$name * $value
   ]
        GBLS ErrorString_$name
ErrorString_$name SETS "$text"
        MEND

        MACRO
        AddFSError      $class, $baseerr, $fsname, $fsnumber
        LCLA    value
value   SETA    &10000 + $fsnumber*&100 + ErrorNumber_$baseerr._Pre
        LCLS    string
string  SETS    ErrorString_$baseerr._Pre :CC: "$fsname" :CC: ErrorString_$baseerr._Post
        LCLS    string2
string2 SETS    "&":CC:(:STR:value)
        AddError        $class$baseerr, "$string", $string2
        MEND

; **************************************************************
; ***  Generate File type name labels assuming ^ type defs   ***
; **************************************************************
;
;          Generates a label of the form "FileType_Text" with the
;       value &FFF, or "FileType_PostScript" with the value &FF5.
;       Also generates a global string variable (GBLS) of the form
;       "FileTypeText_FFF" with the value "Text    ", or 
;       "FileTypeText_FF5" with the value "PoScript".  If the second
;       argument is given then it will be used as the text value, if
;       not then the FileType will be used.

        MACRO
$Value  AddFile  $FileType,$FileTypeName,$PostValue
  [     ("$FileType" = "") :LOR: (("$Value" <> "") :LAND: ("$PostValue" <> ""))
        !       1, "Syntax: [<value>] AddFile <File Type> [< File Type Name>]"
        !       1, "    Or: AddFile <File Type> [< File Type Name> [<value>]]"
        MEXIT
  ]
  [     ("$Value" = "") :LAND: ("$PostValue" = "")
FileType_$FileType # 1
  |
  [     "$Value" = ""
FileType_$FileType * $PostValue
  |
FileType_$FileType * $Value
  ]
  ]
  [     FileType_$FileType > &FFF
        !       1, "Value out of range"
  ]
        LCLS    Number
Number  SETS    (:STR: FileType_$FileType) :RIGHT: 3
        GBLS    FileTypeText_$Number
  [     "$FileTypeName" = ""
FileTypeText_$Number SETS ("$FileType" :CC: "        ") :LEFT: 8
  |
FileTypeText_$Number SETS ("$FileTypeName" :CC: "        ") :LEFT: 8
  ]
        MEND

        MACRO
$Value  AddFileDescending  $FileType,$FileTypeName,$PostValue
  [     ("$FileType" = "") :LOR: (("$Value" <> "") :LAND: ("$PostValue" <> ""))
        !       1, "Syntax: [<value>] AddFileDescending <File Type> [< File Type Name>]"
        !       1, "    Or: AddFileDescending <File Type> [< File Type Name> [<value>]]"
        MEXIT
  ]
  [     ("$Value" = "") :LAND: ("$PostValue" = "")
        #       -1
FileType_$FileType # 1
        #       -1
  |
  [     "$Value" = ""
FileType_$FileType * $PostValue
  |
FileType_$FileType * $Value
  ]
  ]
  [     FileType_$FileType > &FFF
        !       1, "Value out of range"
  ]
        LCLS    Number
Number  SETS    (:STR: FileType_$FileType) :RIGHT: 3
        GBLS    FileTypeText_$Number
  [     "$FileTypeName" = ""
FileTypeText_$Number SETS ("$FileType" :CC: "        ") :LEFT: 8
  |
FileTypeText_$Number SETS ("$FileTypeName" :CC: "        ") :LEFT: 8
  ]
        MEND

; ***************************************************************
; ***  Macro ADDL $reg, $var - add immediate value var to reg ***
; ***************************************************************
        MACRO
$label  ADDL    $reg, $var
        LCLA    count
        LCLA    varcopy
        LCLA    value
varcopy SETA    $var
count   SETA    0
        WHILE   varcopy > 0
      [ varcopy :AND: 3 = 0
varcopy SETA    varcopy :SHR: 2
count   SETA    count + 2
      |
value   SETA    (varcopy :AND: 255) :SHL: (count)
        ADD     $reg, $reg, #&$value
varcopy SETA    varcopy :SHR: 8
count   SETA    count + 8
      ]
        WEND
        MEND

; ***************************************************
; ***  Put address of $dest in $reg; $dest > .    ***
; ***  !!! Please use addr wherever possible !!!  ***
; ***************************************************
        MACRO
$label  ADDR    $reg, $dest, $cond
$label  ADR$cond.L $reg, $dest
        MEND

; *****************************************************************************
; ***  Optimising ADR/ADRL for addressing object backwards from current pc  ***
; ***  or register relative symbol. Symbol MUST be defined on first pass    ***
; *****************************************************************************
                GBLA    addr_verbose
addr_verbose    SETA    0

        MACRO
$label  addr    $reg, $object, $cc
        LCLA    value
value   SETA    .-$object+8
        Immediate &$value
 [ immediate
$label  ADR$cc  $reg, $object
  [ addr_verbose :AND: 1 <> 0
 ! 0,"addr saved a word"
  ]
 |
$label  ADR$cc.L $reg, $object
  [ addr_verbose :AND: 2 <> 0
 ! 0,"addr didn't save a word"
  ]
 ]
        MEND

        MACRO
$label  wsaddr    $reg, $object, $cc
        LCLA    value
value   SETA    :INDEX: $object
        Immediate &$value
 [ immediate
$label  ADR$cc  $reg, $object
  [ addr_verbose :AND: 1 <> 0
 ! 0,"wsaddr saved a word"
  ]
 |
$label  ADR$cc.L $reg, $object
  [ addr_verbose :AND: 2 <> 0
 ! 0,"wsaddr didn't save a word"
  ]
 ]
        MEND


; ***************************************************
; ***  Generate SWI labels assuming ^ type defs   ***
; ***  Also assumes the global variable SWIClass  ***
; ***************************************************
        GBLS    SWIClass
        MACRO
        AddSWI  $SWIName,$value
  [     "$value" = ""
$SWIClass._$SWIName # 1
  |
$SWIClass._$SWIName * $value
  ]
X$SWIClass._$SWIName * $SWIClass._$SWIName + Auto_Error_SWI_bit
        MEND


; *******************************************************************
; ***  Align workspace to given power-of-two boundary and offset  ***
; *******************************************************************
        MACRO
$label  AlignSpace $value, $offset
 [ "$value" = ""
$label  #       3 :AND: ($offset-:INDEX: @)
 |
$label  #       (($value)-1) :AND: ($offset-:INDEX: @)
 ]
        MEND


        MACRO
$label  AlignForModule
        ALIGN   16,12                   ; So $label at offset 0 when RMLoaded
$label
        MEND


        MACRO
$label  ASL     $reg, $val, $cc
$label  MOV$cc  $reg, $reg, ASL #$val
        MEND

        MACRO
$label  ASR     $reg, $val, $cc
$label  MOV$cc  $reg, $reg, ASR #$val
        MEND

; *************************************************
; ***  Put address of $dest in $reg; $dest < .  ***
; *************************************************
        MACRO
$label  BADDR   $reg, $dest, $cond
$label  ADR$cond.L $reg, $dest
        MEND

; ****************************************************
; ***  Macro BSR - Branch to subroutine saving R14 ***
; ****************************************************
        MACRO
$label  BSR     $dest
$label  Push    R14
        BL      $dest
        Pull    R14
        MEND

; *******************************
; ***  Add byte to workspace  ***
; *******************************
        MACRO
$label  Byte    $value, $count
  [     "$label" = ""
    [   "$count" = ""
$value  # 1
  |
$value  # ($count)
    ]
  |
    [   "$value" = ""
$label  # 1
    |
$label  # ($value)
    ]
  ]
        MEND

; ********************************************
; ***  Macro BYTEWS - Point to OsbyteVars  ***
; ********************************************
        MACRO
        BYTEWS  $reg
        Immediate OsbyteVars
 [ immediate
        MOV     $reg, #OsbyteVars
 |
        MOV     $reg, #(OsbyteVars :AND: &FF)
        ORR     $reg, $reg, #(OsbyteVars :AND: :NOT: &FF)
 ]
        MEND

        MACRO
$label  CallAVector $cond
     [ AssemblingArthur
$label  B$cond   CallVector
     |
      [ Module
$label  B$cond  %FT10
        Push    "R8,R9"
        MOV     R8, PC
        AND     R8, R8, #3              ; just get mode bits
        EOR     R8, R8, #SVC_mode       ; eored with SVC mode
        TEQP    R8, PC
        MOVNV   R0, R0
        Push    R14
        MOV     R9, R10
        SWI     XOS_CallAVector
        Pull    R14
        TEQP    R8, PC
        MOVNV   R0, R0
        Pull    "R8,R9"
        MOV     PC, R14
10
      |
$label  LDR$cond PC, =CallVecAddr
       [ "$cond" = ""
        LTORG     ; Can't conditionally execute constants ! Use your own LTORG
       ]
      ]
     ]
        MEND

; *********************************************
; * Call OS_ReadSysInfo (1)                   *
; * Stops modules from working on 2.00 kernel *
; *********************************************

        MACRO
$label  ChkKernelVersion
$label  Push    "r0-r3"    
        MOV     r0,#1
        SWI     XOS_ReadSysInfo
        Pull    "r0-r3"
        MEND

; ******************************************
; ***  Clear carry flag - will set nzcv  ***
; ******************************************
        MACRO
$label  CLC     $cond
$label  CMN$cond pc, #0
        MEND

; **************************************************************************
; ***  Clear bits in PSR from the mask in $bits, using register $regtmp  ***
; **************************************************************************
        MACRO
$label  CLRPSR  $bits, $regtmp, $cond
$label  MVN$cond   $regtmp, #$bits
        TST$cond.P $regtmp, pc
        MEND

; *******************************************
; *** Clear overflow flag - will set nzCv ***
; *******************************************
        MACRO
$label  CLRV    $cond
$label  CMP$cond pc, #0
        MEND

; *********************************************
; *** Generates a help/syntax/command block ***
; ***    for a Module star command table    ***
; *** Needs a variable Module_BaseAddr set  ***
; *********************************************
        GBLA    Command_LastName ; Offset to command string within module
        MACRO
        Command $cmd, $max, $min, $optbits, $cmdlabel
        LCLA    temp
        LCLS    cmdlab
 [ "$optbits" = ""
temp    SETA    0
 |
temp    SETA    $optbits
 ]
 [ "$cmdlabel" = ""
cmdlab  SETS    "$cmd"
 |
cmdlab  SETS    "$cmdlabel"
 ]
Command_LastName SETA .-Module_BaseAddr
        DCB     "$cmd", 0
        ALIGN
        DCD     $cmdlab._Code  -Module_BaseAddr
        DCD     ($min) + (($max) :SHL: 16) + temp
        DCD     $cmdlab._Syntax-Module_BaseAddr
        DCD     $cmdlab._Help  -Module_BaseAddr
        MEND

        MACRO
$label  DEC     $reg,$by
        [ "$by" = ""
$label  SUB     $reg,$reg,#1
        |
$label  SUB     $reg,$reg,#$by
        ]
        MEND

        MACRO
$label  DECS    $reg,$by
        [ "$by" = ""
$label  SUBS    $reg,$reg,#1
        |
$label  SUBS    $reg,$reg,#$by
        ]
        MEND

; **********************************************************
; ***   Macro DIVREM - rc := ra DIV rb; ra := ra REM rb  ***
; ***   rb preserved, rtemp corrupt                      ***
; ***   Now up to 37% faster                             ***
; **********************************************************
        MACRO
$label  DivRem  $rc, $ra, $rb, $rtemp
$label
        MOV     $rtemp, $rb
        CMP     $rtemp, $ra, LSR #1
01
        MOVLS   $rtemp, $rtemp, LSL #1
        CMPLS   $rtemp, $ra, LSR #1
        BLS     %BT01
        MOV     $rc, #0
02
        CMP     $ra, $rtemp
        SUBCS   $ra, $ra, $rtemp
        ADC     $rc, $rc, $rc
        MOV     $rtemp, $rtemp, LSR #1
        CMP     $rtemp, $rb
        BCS     %BT02
        MEND

; **********************************************************
; ***   Macro DIVREM - rc := ra DIV rb; ra := ra REM rb  ***
; ***   OLD version NB. rb, rtemp corrupt                ***
; **********************************************************
;        MACRO
;$label  DivRem  $rc, $ra, $rb, $rtemp
;$label  MOV     $rtemp, #1
;01      CMP     $rb, #&80000000
;        CMPCC   $rb, $ra
;        MOVCC   $rb, $rb, ASL #1
;        MOVCC   $rtemp, $rtemp, ASL #1
;        BCC     %BT01
;        MOV     $rc, #0
;02      CMP     $ra, $rb
;        SUBCS   $ra, $ra, $rb
;        ADDCS   $rc, $rc, $rtemp
;        MOVS    $rtemp, $rtemp, LSR #1
;        MOVNE   $rb, $rb, LSR #1
;        BNE     %BT02
;        MEND

        MACRO
$label  DoCallTable $jumpreg, $tablename, $work
$label  Push    "$work, pc"
        ADR     $work, $tablename
        LDR     lr, [$work, $jumpreg, LSL #2]
        ADD     $work, $work, lr
        STR     $work, [stack, #4]
        MOV     lr, pc ; ADR lr, %FT99 with correct mode bits
        Pull    "$work, pc"
;                0      4
        ASSERT $jumpreg <> $work
        ASSERT $jumpreg <> lr
        ASSERT $jumpreg <> pc
99 ; Return here from called routine
        MEND

; ****************************************************************************
; ***  Probably the fastest jump table mechanism for PIC - a mere 4S + 2N  ***
; ***  cycles.  Jump table directly follows the macro, and is a list of    ***
; ***  offsets done by eg.  DCD routine_address-table-4. Needs only one    ***
; ***  register temp (Use lr mostly).                                      ***
; ****************************************************************************
        MACRO
$label  DoFastJumpTable $jumpreg, $trash
$label  LDR     $trash, [pc, $jumpreg, LSL #2]
        ADD     pc, pc, $trash
        MEND

; *********************************************
; ***  Jump table preserving all registers  ***
; *********************************************
        MACRO
$label  DoJumpTable $jumpreg, $tablename, $work1, $work2
$label  Push    "$work1, $work2, pc"      ; pc is just a dummy reg here
        ADR     $work1, $tablename
        LDR     $work2, [$work1, $jumpreg, LSL #2]
        ADD     $work1, $work1, $work2
        STR     $work1, [stack, #8]
        Pull    "$work1, $work2, pc"
;                0       4       8
        ASSERT $jumpreg <> $work1
        ASSERT $jumpreg <> $work2
        ASSERT $jumpreg <> pc
        MEND

        MACRO
$label  DoSVCCallTable $jumpreg, $tablename
$label  ADR     SVCWK1, $tablename
        LDR     SVCWK0, [SVCWK1, $jumpreg, LSL #2]
        MOV     lr, pc ; ADR lr, %FT99 with correct mode bits
        ADD     pc, SVCWK1, SVCWK0
        ASSERT $jumpreg <> lr
        ASSERT $jumpreg <> pc
99 ; Return here from called routine
        MEND

; ***************************************************************************
; ***  Jump table using SVC mode temporary registers. Use with caution !  ***
; ***************************************************************************
        MACRO
$label  DoSVCJumpTable $jumpreg, $tablename
$label  ADR     SVCWK1, $tablename
        LDR     SVCWK0, [SVCWK1, $jumpreg, LSL #2]
        ADD     pc, SVCWK1, SVCWK0
        ASSERT $jumpreg <> lr
        ASSERT $jumpreg <> pc
        MEND

        MACRO
$label  Error   $errno, $errstr
$label  ADR     R0, %FT01
        SWI     OS_GenerateError
01
        &       $errno
        =       "$errstr", 0
        ALIGN
        MEND

        MACRO
$label  XError     $errsym, $c1, $c2
$label  ADR$c1$c2  R0,ErrorBlock_$errsym
        SETV       $c1
        MEND

; ***********************************************************************
; ***  Exit macro for SWI handlers.                                   ***
; ***  Jump to 17M in the Sam-hacked Brazil, for installed handlers.  ***
; ***  Do it directly if really in system                             ***
; ***********************************************************************
SWIHandlerExit * 17*1024*1024
CallVecAddr    * SWIHandlerExit+4
        MACRO
$label  ExitSWIHandler $cond
        [ AssemblingArthur
$label  B$cond   SLVK
        |
        [ Module
$label  LDR$cond PC, =BranchToSWIExit
        |
$label  MOV$cond PC, #SWIHandlerExit
        ]
        ]
        MEND

        MACRO
$label  GRAB    $reglist, $cond, $hat
$label  LDM$cond.FD r13!, {$reglist}$hat
        MEND

; ********************************************
; ***  Conditional GET macro               ***
; ***  Call it as follows:                 ***
; ***     GetIf   <filename>, <condition>  ***
; ***     $GetConditionally                ***
; ********************************************

        GBLS    GetConditionally

        MACRO
        GetIf   $filename, $cc
      [ $cc
GetConditionally SETS " GET $filename"
      |
GetConditionally SETS "; no GET required"
      ]
        MEND

; *****************************************************************************
; ***  Pull registers and restore PSR (if R15 loaded). Use with extreme     ***
; ***  caution : there are bugs in 3um ARM with PSR update in non-USR modes.***
; *****************************************************************************
        MACRO
$label  GRABS   $reglist, $cond
$label  LDM$cond.FD r13!, {$reglist}^
        [ :LEN: "$reglist" <= 3
 ! 0,"GRABS used dangerously - check your code !"
        ]
        MEND

        MACRO
$label  INC     $reg,$by
        [ "$by" = ""
$label  ADD     $reg,$reg,#1
        |
$label  ADD     $reg,$reg,#$by
        ]
        MEND

        MACRO
$label  INCS    $reg,$by
        [ "$by" = ""
$label  ADDS    $reg,$reg,#1
        |
$label  ADDS    $reg,$reg,#$by
        ]
        MEND

; ****************************************************
; ***  Generates the InfoWord for a command table  ***
; ****************************************************
        MACRO
        InfoWord $max, $min, $optbits
 [ "$optbits" = ""
        DCD     ($min) + (($max) :SHL: 16)
 |
        DCD     ($min) + (($max) :SHL: 16) + $optbits
 ]
        MEND

; **********************************************************
; ***  Macro Immediate - set flag if value is immediate  ***
; **********************************************************
        GBLL    immediate

        MACRO
        Immediate $var
immediate SETL  {FALSE}
        LCLA    count
        LCLA    varcopy
varcopy SETA    $var
        WHILE   count <= 30
 [ ((varcopy:SHL:count) + (varcopy:SHR:(32-count))) :AND: (:NOT: &FF) = 0
immediate SETL  {TRUE}
        MEXIT
 ]
count   SETA    count + 2
        WEND
        MEND

; *********************************************
; ***  Macro LDROSB - Load Osbyte variable  ***
; *********************************************
        MACRO
$label  LDROSB  $reg, $var, $cond
$label  MOV$cond $reg, #0
        LDR$cond.B $reg, [$reg, #OsbyteVars+$var-OSBYTEFirstVar]
        MEND

; ******************************************************
; ***  Macro LDW - Load word from unknown alignment  ***
; ******************************************************
        MACRO
$label  LDW     $dest, $addr, $temp1, $temp2
        ASSERT  $dest < $temp2
$label  BIC     $temp1, $addr, #3
        LDMIA   $temp1, {$dest, $temp2}
        AND     $temp1, $addr, #3
        MOVS    $temp1, $temp1, LSL #3
        MOVNE   $dest, $dest, LSR $temp1
        RSBNE   $temp1, $temp1, #32
        ORRNE   $dest, $dest, $temp2, LSL $temp1
        MEND

; ***********************************************************************
; ***  Lowercasing macro. Needs temp register; only lowercases A-Z !  ***
; ***********************************************************************
        MACRO
$label  LowerCase $reg, $wrk
$label  CMP     $reg, #"A"
        RSBGES  $wrk, $reg, #"Z"        ; inverse compare
        ADDGE   $reg, $reg, #"a"-"A"
        MEND

        MACRO
$label  LSL     $reg, $val, $cc
$label  MOV$cc  $reg, $reg, LSL #$val
        MEND

        MACRO
$label  LSR     $reg, $val, $cc
$label  MOV$cc  $reg, $reg, LSR #$val
        MEND

        MACRO
$label  MakeErrorBlock $name, $noalign
        ALIGN
$label
ErrorBlock_$name
        DCD     ErrorNumber_$name
        DCB     ErrorString_$name
        DCB     0
    [   "$noalign" = ""
        ALIGN
    ]
        MEND

; OSS This is a plug in replacement for MakeErrorBlock. The only difference is that it puts
; the error tag ($name, the macro parameter) in as the error text instead of the string.
; ECN Added $tag for those of us who don't want huge tag names

        MACRO
$label  MakeInternatErrorBlock $name, $noalign, $tag
        ALIGN
$label
ErrorBlock_$name
        DCD     ErrorNumber_$name
    [   "$tag" = ""
        DCB     "$name"
    |
        DCB     "$tag"
    ]
        DCB     0
    [   "$noalign" = ""
        ALIGN
    ]
        MEND

; *****************************************************
; ***  Generates code like                          ***
; ***  StarSetText DCB "Set File$Type_FFF Text", 0  ***
; *****************************************************

        MACRO
$label  MakeStarSet $name
        LCLS    Label
        [       "$label" <> ""
$label
        |
StarSet$name
        ]
        LCLS    Value
Value   SETS    "FileType_$name"                ; "FileType_Text"
Value   SETS    (:STR: $Value) :RIGHT: 3        ; "FFF"
        DCB     "Set File$Type_$Value "
Value   SETS    "FileTypeText_$Value"           ; "FileTypeText_FFF"
        DCB     $Value, 0                       ; Read text value out
        MEND

; ****************************************
; ***  Macro MULTIPLY - rc := ra * rb  ***
; ***       NB. ra, rb corrupt         ***
; ****************************************
        MACRO
$label  MULTIPLY $rc, $ra, $rb

    [ {FALSE}                  ; old boring one
$label  MOV     $rc, #0
01      MOVS    $ra, $ra, LSR #1
        ADDCS   $rc, $rc, $rb
        ADD     $rb, $rb, $rb
        BNE     %BT01
    |
$label  MUL     $rc, $rb, $ra ; sexy 2u version with regs in the right order
    ]
        MEND

; *******************************************************
; ***  Sweet FA macro to keep Tutu out of mischief !  ***
; *******************************************************
        MACRO
$label  NOP
$label  MOV     R0, R0
        MEND


        GBLA    OvrlpV
OvrlpV  SETA    0

        MACRO
$label  Overlap $master, $slave
        [       "$label" = ""
OvrlpV  SETA    OvrlpV + 1
        LCLS    reg
reg     SETS    "OvReg" :CC: :STR: OvrlpV
$reg    RN      :BASE:$master
        ^       :INDEX:$master, $reg
$slave  #       ?$master
        |
        !       0, "You what?"
        ]
        MEND

; **********************************************************************
; ***  Disable IRQs, saving an old interrupt state indicator in R14  ***
; ***  NB This macro preserves the C and V flags                     ***
; **********************************************************************
        MACRO
$lab    PHPSEI  $register
  [     "$register" = ""
$lab    MOV     R14, #I_bit
        TST     R14, PC                 ; is I_bit set ?
        TEQEQP  R14, PC                 ; no, then set it (and R14 = I_bit)
        MOVNE   R14, #0                 ; yes, then leave alone (and R14=0)
  |
$lab    MOV     $register, #I_bit
        TST     $register, PC           ; is I_bit set ?
        TEQEQP  $register, PC           ; no, then set it (and $reg. = I_bit)
        MOVNE   $register, #0           ; yes, then leave alone (and R14=0)
  ]
        MEND

; ************************************************************************
; ***  Restore IRQ state from the indicator in R14 (set up by PHPSEI)  ***
; ***  NB This macro preserves the C and V flags                       ***
; ************************************************************************
        MACRO
$lab    PLP     $register
  [     "$register" = ""
$lab    TEQP    R14, PC
  |
$lab    TEQP    $register, PC
  ]
        MEND

; *****************************************
; ***  Pull registers given in reglist  ***
; *****************************************
        MACRO
$label  Pull    $reglist, $cond, $hat
$label  LDM$cond.FD r13!, {$reglist}$hat
        MEND

; *****************************************
; ***  Push registers given in reglist  ***
; *****************************************
        MACRO
$label  Push   $reglist, $cond
$label  STM$cond.FD r13!, {$reglist}
        MEND

        MACRO
$label  RETURN  $cond
$label  MOV$cond pc, lr
        MEND

        MACRO
$label  RETURNS $cond
$label  MOV$cond.S pc, lr
        MEND

        MACRO
$label  ROR     $reg, $val, $cc
$label
        LCLA    modval
modval  SETA    ($val) :AND: &1F
        [       modval = 0
        !       0, "No code generated for ""ROR ":CC:"$reg":CC:", 0, ":CC:"$cc"""
        |
        MOV$cc  $reg, $reg, ROR #modval
        ]
        MEND

        MACRO
$label  RRX     $reg, $cc
$label  MOV$cc  $reg, $reg, RRX
        MEND

; **************************************************
; ***  Set and clear bits in PSR from the masks  ***
; ***  $set, $clr, using register $regtmp        ***
; **************************************************
        MACRO
$label  SCPSR   $set, $clr, $regtmp, $cond
$label  MOV$cond   $regtmp, pc
        ORR$cond   $regtmp, $regtmp, #($set) :OR: ($clr)
        TEQ$cond.P $regtmp, #$clr
        MEND

; ****************************************
; ***  Set carry flag - will set nzCv  ***
; ****************************************
        MACRO
$label  SEC     $cond
$label  CMP$cond pc, #0
        MEND

; ************************************************************************
; ***  Set bits in PSR from the mask in $bits, using register $regtmp  ***
; ************************************************************************
        MACRO
$label  SETPSR  $bits, $regtmp, $cond
$label  MOV$cond   $regtmp, pc
        ORR$cond   $regtmp, $regtmp, #$bits
        TEQ$cond.P $regtmp, #0
        MEND

; *******************************************
; ***  Set overflow flag - will set NzcV  ***
; *******************************************
        MACRO
$label  SETV    $cond
$label  CMP$cond pc, #&80000000
        MEND

        MACRO
$label  STASH   $reglist, $cond, $hat
$label  STM$cond.FD r13!, {$reglist}$hat
        MEND

; *******************************
; ***  String immediate out.  ***
; *******************************
        MACRO
$label  STRIM   $string
        [ :LEN: "$string" = 1
$label  SWI     XOS_WriteI+"$string"
        |
$label  SWI     XOS_WriteS
        DCB     "$string", 0
        ALIGN
        ]
        MEND

; **********************************************************
; ***  Macro StringContains                              ***
; ***  Label StringContains "string1","string2"          ***
; ***  Sets Label to {TRUE} if string1 contains string2  ***
; ***  or {FALSE} otherwise                              ***
; **********************************************************

        MACRO
$Answer StringContains $string,$substring
      [ (:LEN: "$string") < (:LEN: "$substring")
$Answer SETL    {FALSE}
      |
      [ ("$string" :LEFT: (:LEN:"$substring")) = "$substring"
$Answer SETL    {TRUE}
      |
        LCLS    temp
temp    SETS    ("$string" :RIGHT: ((:LEN: "$string") - 1))
$Answer StringContains  "$temp","$substring"
      ]
      ]
        MEND


; **********************************************
; ***  Macro STROSB - Store Osbyte variable  ***
; **********************************************
        MACRO
$label  STROSB  $reg, $var, $temp, $cond
$label  MOV$cond $temp, #0
        STR$cond.B $reg, [$temp, #OsbyteVars+$var-OSBYTEFirstVar]
        MEND

; *****************************************
; ***  Macro Swap - Swap two registers  ***
; *****************************************
        MACRO
$label  Swap    $ra, $rb, $cc
$label  EOR$cc  $ra, $ra, $rb
        EOR$cc  $rb, $ra, $rb
        EOR$cc  $ra, $ra, $rb
        MEND

; **************************************
; ***  Macro SWIChunk - Allocates    ***
; ***  and checks SWI chunk numbers  ***
; **************************************
        GBLL            SWI_Name_Clash                  ; Disable extra code
SWI_Name_Clash SETL     {FALSE}                         ; To check for SWI name clashes

        MACRO                                           ; See Hdr:System for use
$n      SWIChunk        $v,$s,$o
        LCLS            sn
sn      SETS            "$n" :CC: "SWI"
$sn     #               1
        ASSERT          (&$v :AND: &1FFFF) = (&40 * $sn)
        [               SWI_Name_Clash :LAND: ( :LNOT: ( "$o" = "NameOK" ) )
        [               "$s" = ""
sn      SETS            "SWI_String_" :CC: "$n"
        |
sn      SETS            "SWI_String_" :CC: "$s"
        ]
$sn     EQU             0
        ]
        MEND

; ***************************************************************************
; ***  Toggle bits in PSR from the mask in $bits, using register $regtmp  ***
; ***************************************************************************
        MACRO
$label  TOGPSR  $bits, $regtmp, $cond
$label  MOV$cond   $regtmp, pc
        TEQ$cond.P $regtmp, #$bits
        MEND

; ***********************************************************************
; ***  Uppercasing macro. Needs temp register; only uppercases a-z !  ***
; ***********************************************************************
        MACRO
$label  UpperCase $reg, $wrk
$label  CMP     $reg, #"a"
        RSBGES  $wrk, $reg, #"z"        ; inverse compare
        SUBGE   $reg, $reg, #"a"-"A"
        MEND

; **********************************************************
; ***  Macro VDWS - Point to our new VduDriverWorkSpace  ***
; **********************************************************
        MACRO
        VDWS    $reg
 [ AssemblingArthur :LOR: Module
        MOV     $reg, #VduDriverWorkSpace
 |
 ! 0, "This is a real waste if using Hdr.NewSpace"
        MOV     $reg, #(VduDriverWorkSpace :AND: &FF000000)
        ORR     $reg, $reg, #(VduDriverWorkSpace :AND: &00FF0000)
        ORR     $reg, $reg, #(VduDriverWorkSpace :AND: &0000FFFF)
 ]
        MEND

; *******************************
; ***  Add word to workspace  ***
; *******************************
        MACRO
$label  Word    $value, $count
  [     ( ( :INDEX: @ ) :AND: 3 ) <> 0
        #       4 - ( ( :INDEX: @ ) :AND: 3 )
  ]
  [     "$label" = ""
    [   "$count" = ""
      [ "$value" = ""
      |
$value  #       4
      ]
    |
$value  #       ($count) * 4
    ]
  |
    [   "$value" = ""
$label  #       4
    |
$label  #       ($value) * 4
    ]
  ]
        MEND

; *************************
; ***  WriteLn a string ***
; *************************
        MACRO
$label  WRLN    $string
$label  SWI     XOS_WriteS
        DCB     "$string", 10,13, 0
        ALIGN
        MEND

; ****************************************************************************
; ***  |#| - macro for allocating workspace downwards rather than upwards  ***
; ****************************************************************************

        MACRO
$var    |#|     $bytes
 [ "$bytes" = ""
        !       1, "Syntax: [<variable>] |#| <bytes>"
        MEXIT
 ]
        #       -($bytes)
 [ "$var" <> ""
$var    #       $bytes          ; Declare correct size
        #       -($bytes)
 ]
        MEND


        OPT OldOpt
        END
