;//
;// Copyright (C) 2007-2008 ARM Limited
;//
;// Licensed under the Apache License, Version 2.0 (the "License");
;// you may not use this file except in compliance with the License.
;// You may obtain a copy of the License at
;//
;//      http://www.apache.org/licenses/LICENSE-2.0
;//
;// Unless required by applicable law or agreed to in writing, software
;// distributed under the License is distributed on an "AS IS" BASIS,
;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;// See the License for the specific language governing permissions and
;// limitations under the License.
;//
;//
;// 
;// File Name:  armCOMM_s.h
;// OpenMAX DL: v1.0.2
;// Revision:   12290
;// Date:       Wednesday, April 9, 2008
;// 
;// 
;// 
;//
;// ARM optimized OpenMAX common header file
;//

;// Protect against multiple inclusion
 IF :LNOT::DEF:ARMCOMM_S_H
 GBLL ARMCOMM_S_H

        REQUIRE8            ;// Requires 8-byte stack alignment
        PRESERVE8           ;// Preserves 8-byte stack alignment
        
        GBLL    ARM_ERRORCHECK
ARM_ERRORCHECK  SETL {FALSE}

;// Globals

        GBLS    _RRegList   ;// R saved register list
        GBLS    _DRegList   ;// D saved register list
        GBLS    _Variant    ;// Selected processor variant
        GBLS    _CPU        ;// CPU name
        GBLS    _Struct     ;// Structure name
        
        GBLL    _InFunc     ;// Inside function assembly flag
        GBLL    _SwLong     ;// Long switch flag
        
        GBLA    _RBytes     ;// Number of register bytes on stack
        GBLA    _SBytes     ;// Number of scratch bytes on stack 
        GBLA    _ABytes     ;// Stack offset of next argument
        GBLA    _Workspace  ;// Stack offset of scratch workspace
        GBLA    _F          ;// Function number
        GBLA    _StOff      ;// Struct offset
        GBLA    _SwNum      ;// Switch number
        GBLS    _32         ;// Suffix for 32 byte alignmnet
        GBLS    _16         ;// Suffix for 16 byte alignmnet
        
_InFunc         SETL    {FALSE}
_SBytes         SETA    0
_F              SETA    0
_SwNum          SETA    0
_32             SETS    "ALIGN32"
_16             SETS    "ALIGN16"

;/////////////////////////////////////////////////////////
;// Override the tools settings of the CPU if the #define
;// USECPU is set, otherwise use the CPU defined by the
;// assembler settings.
;/////////////////////////////////////////////////////////

       IF :DEF: OVERRIDECPU
_CPU       SETS  OVERRIDECPU
       ELSE
_CPU       SETS    {CPU}       
       ENDIF



;/////////////////////////////////////////////////////////
;// Work out which code to build
;/////////////////////////////////////////////////////////

        IF :DEF:ARM1136JS:LOR::DEF:CortexA8:LOR::DEF:ARM_GENERIC
            INFO 1,"Please switch to using M_VARIANTS"
        ENDIF

        ;// Define and reset all officially recongnised variants
        MACRO
        _M_DEF_VARIANTS
        _M_DEF_VARIANT ARM926EJS
        _M_DEF_VARIANT ARM1136JS
        _M_DEF_VARIANT ARM1136JS_U
        _M_DEF_VARIANT CortexA8
        _M_DEF_VARIANT ARM7TDMI
        MEND
        
        MACRO
        _M_DEF_VARIANT $var
        GBLL $var
        GBLL _ok$var
$var    SETL {FALSE}
        MEND        
        

        ;// Variant declaration
        ;//
        ;// Define a list of code variants supported by this
        ;// source file. This macro then chooses the most
        ;// appropriate variant to build for the currently configured
        ;// core.
        ;//        
        MACRO
        M_VARIANTS $v0,$v1,$v2,$v3,$v4,$v5,$v6,$v7        
        ;// Set to TRUE variants that are supported
        _M_DEF_VARIANTS
        _M_VARIANT $v0
        _M_VARIANT $v1
        _M_VARIANT $v2
        _M_VARIANT $v3
        _M_VARIANT $v4
        _M_VARIANT $v5
        _M_VARIANT $v6
        _M_VARIANT $v7
        
        ;// Look for first available variant to match a CPU
        ;// _M_TRY cpu, variant fall back list
_Variant SETS ""                
        _M_TRY ARM926EJ-S,   ARM926EJS
        _M_TRY ARM1176JZ-S,  ARM1136JS
        _M_TRY ARM1176JZF-S, ARM1136JS
        _M_TRY ARM1156T2-S,  ARM1136JS
        _M_TRY ARM1156T2F-S, ARM1136JS
        _M_TRY ARM1136J-S,   ARM1136JS
        _M_TRY ARM1136JF-S,  ARM1136JS
        _M_TRY MPCore,       ARM1136JS
        _M_TRY falcon-vfp, ARM1136JS
        _M_TRY falcon-full-neon, CortexA8
        _M_TRY Cortex-A8NoNeon, ARM1136JS
        _M_TRY Cortex-A8,    CortexA8, ARM1136JS
        _M_TRY Cortex-R4,    ARM1136JS
        _M_TRY ARM7TDMI
        
        ;// Select the correct variant
        _M_DEF_VARIANTS
        IF _Variant=""
            INFO 1, "No match found for CPU '$_CPU'"
        ELSE
$_Variant   SETL {TRUE}
        ENDIF
        MEND
        
        ;// Register a variant as available
        MACRO
        _M_VARIANT $var
        IF "$var"=""
            MEXIT
        ENDIF
        IF :LNOT::DEF:_ok$var
            INFO 1, "Unrecognized variant '$var'"
        ENDIF
$var    SETL {TRUE}
        MEND
        
        ;// For a given CPU, see if any of the variants supporting
        ;// this CPU are available. The first available variant is
        ;// chosen
        MACRO
        _M_TRY $cpu, $v0,$v1,$v2,$v3,$v4,$v5,$v6,$v7
        IF "$cpu"<>_CPU
            MEXIT
        ENDIF
        _M_TRY1 $v0
        _M_TRY1 $v1
        _M_TRY1 $v2
        _M_TRY1 $v3
        _M_TRY1 $v4
        _M_TRY1 $v5
        _M_TRY1 $v6
        _M_TRY1 $v7
        ;// Check a match was found
        IF _Variant=""
            INFO 1, "No variant match found for CPU '$_CPU'"
        ENDIF
        MEND
        
        MACRO
        _M_TRY1 $var
        IF "$var"=""
            MEXIT
        ENDIF
        IF (_Variant=""):LAND:$var
_Variant SETS "$var"
        ENDIF
        MEND
        
;////////////////////////////////////////////////////////
;// Structure definition
;////////////////////////////////////////////////////////

        ;// Declare a structure of given name
        MACRO
        M_STRUCT $sname
_Struct SETS "$sname"
_StOff  SETA 0
        MEND
        
        ;// Declare a structure field
        ;// The field is called $sname_$fname
        ;// $size   = the size of each entry, must be power of 2 
        ;// $number = (if provided) the number of entries for an array
        MACRO
        M_FIELD $fname, $size, $number
        IF (_StOff:AND:($size-1))!=0
_StOff      SETA _StOff + ($size - (_StOff:AND:($size-1)))
        ENDIF
$_Struct._$fname EQU _StOff
        IF "$number"<>""
_StOff      SETA _StOff + $size*$number
        ELSE
_StOff      SETA _StOff + $size
        ENDIF
        MEND
        
        
        MACRO
        M_ENDSTRUCT
sizeof_$_Struct EQU _StOff
_Struct SETS ""
        MEND

;//////////////////////////////////////////////////////////
;// Switch and table macros
;//////////////////////////////////////////////////////////

        ;// Start a relative switch table with register to switch on
        ;//
        ;// $v = the register to switch on
        ;// $s = if specified must be "L" to indicate long
        ;//      this allows a greater range to the case code
        MACRO
        M_SWITCH $v, $s
        ASSERT "$s"="":LOR:"$s"="L"
_SwLong SETL {FALSE}
        IF "$s"="L"
_SwLong     SETL {TRUE}
        ENDIF
_SwNum  SETA _SwNum+1        
        IF {CONFIG}=16
            ;// Thumb
            IF _SwLong
                TBH [pc, $v, LSL#1]
            ELSE
                TBB [pc, $v]
            ENDIF
_Switch$_SwNum
        ELSE
            ;// ARM
            ADD pc, pc, $v, LSL #2
            NOP
        ENDIF
        MEND
        
        ;// Add a case to the switch statement
        MACRO
        M_CASE  $label
        IF {CONFIG}=16
            ;// Thumb
            IF _SwLong
                DCW ($label - _Switch$_SwNum)/2
            ELSE
                DCB ($label - _Switch$_SwNum)/2
            ENDIF
        ELSE
            ;// ARM
            B   $label
        ENDIF
        MEND
        
        ;// End of switch statement
        MACRO
        M_ENDSWITCH
        ALIGN 2
        MEND       


;////////////////////////////////////////////////////////
;// Data area allocation
;////////////////////////////////////////////////////////

        ;// Constant table allocator macro
        ;//
        ;// Creates a new section for each constant table
        ;// $name is symbol through which the table can be accessed.
        ;// $align is the optional alignment of the table, log2 of 
        ;//  the byte alignment - $align=4 is 16 byte aligned
        MACRO
        M_TABLE  $name, $align
        ASSERT :LNOT:_InFunc
        IF "$align"=""
            AREA |.constdata|, READONLY, DATA
        ELSE
            ;// AREAs inherit the alignment of the first declaration.
            ;// Therefore for each alignment size we must have an area
            ;// of a different name.
            AREA constdata_a$align, READONLY, DATA, ALIGN=$align
            
            ;// We also force alignment incase we are tagging onto
            ;// an already started area.
            ALIGN (1<<$align)
        ENDIF
$name
        MEND
        
;/////////////////////////////////////////////////////
;// Macros to allocate space on the stack
;//
;// These all assume that the stack is 8-byte aligned
;// at entry to the function, which means that the 
;// 32-byte alignment macro needs to work in a
;// bit more of a special way...
;/////////////////////////////////////////////////////

        


        ;// Allocate 1-byte aligned area of name
        ;// $name size $size bytes.
        MACRO
        M_ALLOC1  $name, $size
        ASSERT :LNOT:_InFunc
$name$_F   EQU _SBytes
_SBytes SETA _SBytes + ($size)
        MEND
            
        ;// Allocate 2-byte aligned area of name
        ;// $name size $size bytes.
        MACRO
        M_ALLOC2  $name, $size
        ASSERT :LNOT:_InFunc
        IF (_SBytes:AND:1)!=0
_SBytes     SETA _SBytes + (2 - (_SBytes:AND:1))
        ENDIF
$name$_F   EQU _SBytes
_SBytes SETA _SBytes + ($size)
        MEND
            
        ;// Allocate 4-byte aligned area of name
        ;// $name size $size bytes.
        MACRO
        M_ALLOC4  $name, $size
        ASSERT :LNOT:_InFunc
        IF (_SBytes:AND:3)!=0
_SBytes     SETA _SBytes + (4 - (_SBytes:AND:3))
        ENDIF
$name$_F   EQU _SBytes
_SBytes SETA _SBytes + ($size)
        MEND
            
        ;// Allocate 8-byte aligned area of name
        ;// $name size $size bytes.
        MACRO
        M_ALLOC8  $name, $size
        ASSERT :LNOT:_InFunc
        IF (_SBytes:AND:7)!=0
_SBytes     SETA _SBytes + (8 - (_SBytes:AND:7))
        ENDIF
$name$_F   EQU _SBytes
_SBytes SETA _SBytes + ($size)
        MEND        

        
        ;// Allocate 8-byte aligned area of name
        ;// $name size ($size+16) bytes.
        ;// The extra 16 bytes are later used to align the pointer to 16 bytes
        
        MACRO
        M_ALLOC16  $name, $size
        ASSERT :LNOT:_InFunc
        IF (_SBytes:AND:7)!=0
_SBytes     SETA _SBytes + (8 - (_SBytes:AND:7))
        ENDIF
$name$_F$_16   EQU (_SBytes + 8)
_SBytes SETA _SBytes + ($size) + 8
        MEND        
        
        ;// Allocate 8-byte aligned area of name
        ;// $name size ($size+32) bytes.
        ;// The extra 32 bytes are later used to align the pointer to 32 bytes
        
        MACRO
        M_ALLOC32  $name, $size
        ASSERT :LNOT:_InFunc
        IF (_SBytes:AND:7)!=0
_SBytes     SETA _SBytes + (8 - (_SBytes:AND:7))
        ENDIF
$name$_F$_32   EQU (_SBytes + 24)
_SBytes SETA _SBytes + ($size) + 24
        MEND        
        
        
        
        
        ;// Argument Declaration Macro
        ;//
        ;// Allocate an argument name $name
        ;// size $size bytes
        MACRO
        M_ARG     $name, $size
        ASSERT _InFunc
$name$_F    EQU _ABytes
_ABytes SETA _ABytes + ($size)
        MEND        
        
;///////////////////////////////////////////////
;// Macros to access stacked variables
;///////////////////////////////////////////////

        ;// Macro to perform a data processing operation
        ;// with a constant second operand
        MACRO
        _M_OPC $op,$rd,$rn,$const
        LCLA    _sh
        LCLA    _cst
_sh     SETA    0
_cst    SETA    $const
        IF _cst=0
        $op $rd, $rn, #_cst
            MEXIT
        ENDIF
        WHILE (_cst:AND:3)=0
_cst        SETA _cst>>2
_sh         SETA _sh+2
        WEND
        $op $rd, $rn, #(_cst:AND:0x000000FF)<<_sh
        IF _cst>=256
            $op $rd, $rd, #(_cst:AND:0xFFFFFF00)<<_sh
        ENDIF
        MEND

        ;// Macro to perform a data access operation
        ;// Such as LDR or STR
        ;// The addressing mode is modified such that
        ;// 1. If no address is given then the name is taken
        ;//    as a stack offset
        ;// 2. If the addressing mode is not available for the
        ;//    state being assembled for (eg Thumb) then a suitable
        ;//    addressing mode is substituted.
        ;//
        ;// On Entry:
        ;// $i = Instruction to perform (eg "LDRB")
        ;// $a = Required byte alignment
        ;// $r = Register(s) to transfer (eg "r1")
        ;// $a0,$a1,$a2. Addressing mode and condition. One of:
        ;//     label {,cc}
        ;//     [base]                    {,,,cc}
        ;//     [base, offset]{!}         {,,cc}
        ;//     [base, offset, shift]{!}  {,cc}
        ;//     [base], offset            {,,cc}
        ;//     [base], offset, shift     {,cc}
        MACRO
        _M_DATA $i,$a,$r,$a0,$a1,$a2,$a3
        IF "$a0":LEFT:1="["
            IF "$a1"=""
                $i$a3   $r, $a0
            ELSE
                IF "$a0":RIGHT:1="]"
                    IF "$a2"=""
                        _M_POSTIND $i$a3, "$r", $a0, $a1
                    ELSE
                        _M_POSTIND $i$a3, "$r", $a0, "$a1,$a2"
                    ENDIF
                ELSE
                    IF "$a2"=""
                        _M_PREIND  $i$a3, "$r", $a0, $a1
                    ELSE
                        _M_PREIND  $i$a3, "$r", $a0, "$a1,$a2"
                    ENDIF
                ENDIF
            ENDIF
        ELSE
            LCLA    _Offset
_Offset     SETA    _Workspace + $a0$_F
            ASSERT  (_Offset:AND:($a-1))=0
            $i$a1   $r, [sp, #_Offset]
        ENDIF
        MEND
        
        ;// Handle post indexed load/stores
        ;// op  reg, [base], offset
        MACRO
        _M_POSTIND $i,$r,$a0,$a1
        LCLS _base
        LCLS _offset
        IF {CONFIG}=16 ;// Thumb
_base       SETS ("$a0":LEFT:(:LEN:"$a0"-1)):RIGHT:(:LEN:"$a0"-2)   ;// remove []
_offset     SETS "$a1"
            IF _offset:LEFT:1="+"
_offset         SETS _offset:RIGHT:(:LEN:_offset-1)
            ENDIF
            $i  $r, $a0
            IF _offset:LEFT:1="-"
_offset         SETS _offset:RIGHT:(:LEN:_offset-1)
                SUB $_base, $_base, $_offset
            ELSE                
                ADD $_base, $_base, $_offset
            ENDIF
        ELSE ;// ARM
            $i  $r, $a0, $a1
        ENDIF
        MEND
        
        ;// Handle pre indexed load/store
        ;// op  reg, [base, offset]{!}
        MACRO
        _M_PREIND $i,$r,$a0,$a1
        LCLS _base
        LCLS _offset
        IF ({CONFIG}=16):LAND:(("$a1":RIGHT:2)="]!")
_base       SETS "$a0":RIGHT:(:LEN:("$a0")-1)
_offset     SETS "$a1":LEFT:(:LEN:("$a1")-2)
            $i $r, [$_base, $_offset]
            ADD $_base, $_base, $_offset
        ELSE
            $i  $r, $a0, $a1
        ENDIF
        MEND

        ;// Load unsigned byte from stack
        MACRO
        M_LDRB  $r,$a0,$a1,$a2,$a3
        _M_DATA "LDRB",1,$r,$a0,$a1,$a2,$a3
        MEND
        
        ;// Load signed byte from stack
        MACRO
        M_LDRSB $r,$a0,$a1,$a2,$a3
        _M_DATA "LDRSB",1,$r,$a0,$a1,$a2,$a3
        MEND
        
        ;// Store byte to stack
        MACRO
        M_STRB  $r,$a0,$a1,$a2,$a3
        _M_DATA "STRB",1,$r,$a0,$a1,$a2,$a3
        MEND
        
        ;// Load unsigned half word from stack
        MACRO
        M_LDRH  $r,$a0,$a1,$a2,$a3
        _M_DATA "LDRH",2,$r,$a0,$a1,$a2,$a3
        MEND
        
        ;// Load signed half word from stack
        MACRO
        M_LDRSH $r,$a0,$a1,$a2,$a3
        _M_DATA "LDRSH",2,$r,$a0,$a1,$a2,$a3
        MEND
        
        ;// Store half word to stack
        MACRO
        M_STRH  $r,$a0,$a1,$a2,$a3
        _M_DATA "STRH",2,$r,$a0,$a1,$a2,$a3
        MEND

        ;// Load word from stack
        MACRO
        M_LDR   $r,$a0,$a1,$a2,$a3
        _M_DATA "LDR",4,$r,$a0,$a1,$a2,$a3
        MEND
        
        ;// Store word to stack
        MACRO
        M_STR   $r,$a0,$a1,$a2,$a3
        _M_DATA "STR",4,$r,$a0,$a1,$a2,$a3
        MEND

        ;// Load double word from stack
        MACRO
        M_LDRD  $r0,$r1,$a0,$a1,$a2,$a3
        _M_DATA "LDRD",8,"$r0,$r1",$a0,$a1,$a2,$a3
        MEND
                
        ;// Store double word to stack
        MACRO
        M_STRD  $r0,$r1,$a0,$a1,$a2,$a3
        _M_DATA "STRD",8,"$r0,$r1",$a0,$a1,$a2,$a3
        MEND
        
        ;// Get absolute address of stack allocated location
        MACRO
        M_ADR   $a, $b, $cc
        _M_OPC  ADD$cc, $a, sp, (_Workspace + $b$_F)
        MEND
        
        ;// Get absolute address of stack allocated location and align the address to 16 bytes
        MACRO
        M_ADR16 $a, $b, $cc
            _M_OPC  ADD$cc, $a, sp, (_Workspace + $b$_F$_16)
        
            ;// Now align $a to 16 bytes
            BIC$cc  $a,$a,#0x0F
        MEND
        
        ;// Get absolute address of stack allocated location and align the address to 32 bytes
        MACRO
        M_ADR32 $a, $b, $cc
            _M_OPC  ADD$cc, $a, sp, (_Workspace + $b$_F$_32)
        
            ;// Now align $a to 32 bytes
            BIC$cc  $a,$a,#0x1F
        MEND

;//////////////////////////////////////////////////////////
;// Function header and footer macros
;//////////////////////////////////////////////////////////      
        
        ;// Function Header Macro    
        ;// Generates the function prologue
        ;// Note that functions should all be "stack-moves-once"
        ;// The FNSTART and FNEND macros should be the only places
        ;// where the stack moves.
        ;//    
        ;// $name  = function name
        ;// $rreg  = ""   don't stack any registers
        ;//          "lr" stack "lr" only
        ;//          "rN" stack registers "r4-rN,lr"
        ;// $dreg  = ""   don't stack any D registers
        ;//          "dN" stack registers "d8-dN"
        ;//
        ;// Note: ARM Archicture procedure call standard AAPCS
        ;// states that r4-r11, sp, d8-d15 must be preserved by
        ;// a compliant function.
        MACRO
        M_START $name, $rreg, $dreg
        ASSERT :LNOT:_InFunc
        ASSERT "$name"!=""
_InFunc SETL {TRUE}
_RBytes SETA 0
_Workspace SETA 0

        ;// Create an area for the function        
        AREA    |.text|, CODE
        EXPORT  $name
$name   FUNCTION
        
        ;// Save R registers
        _M_GETRREGLIST $rreg
        IF _RRegList<>""
            STMFD   sp!, {$_RRegList, lr}
        ENDIF
                
        ;// Save D registers
        _M_GETDREGLIST  $dreg        
        IF _DRegList<>""
            VSTMFD  sp!, {$_DRegList}
        ENDIF            
            
                    
        ;// Ensure size claimed on stack is 8-byte aligned
        IF ((_SBytes:AND:7)!=0)
_SBytes     SETA _SBytes + (8 - (_SBytes:AND:7))
        ENDIF
        
        IF (_SBytes!=0)
            _M_OPC SUB, sp, sp, _SBytes
        ENDIF
        
        
_ABytes SETA _SBytes + _RBytes - _Workspace

                        
        ;// Print function name if debug enabled
        M_PRINTF "$name\n",
        MEND
        
        ;// Work out a list of R saved registers
        MACRO
        _M_GETRREGLIST $rreg
        IF "$rreg"=""
_RRegList   SETS ""
            MEXIT
        ENDIF        
        IF "$rreg"="lr":LOR:"$rreg"="r4"
_RRegList   SETS "r4"
_RBytes     SETA _RBytes+8
            MEXIT
        ENDIF
        IF "$rreg"="r5":LOR:"$rreg"="r6"
_RRegList   SETS "r4-r6"
_RBytes     SETA _RBytes+16
            MEXIT
        ENDIF
        IF "$rreg"="r7":LOR:"$rreg"="r8"
_RRegList   SETS "r4-r8"
_RBytes     SETA _RBytes+24
            MEXIT
        ENDIF
        IF "$rreg"="r9":LOR:"$rreg"="r10"
_RRegList   SETS "r4-r10"
_RBytes     SETA _RBytes+32
            MEXIT
        ENDIF
        IF "$rreg"="r11":LOR:"$rreg"="r12"
_RRegList   SETS "r4-r12"
_RBytes     SETA _RBytes+40
            MEXIT
        ENDIF
        INFO 1, "Unrecognized saved r register limit '$rreg'"
        MEND        
        
        ;// Work out a list of D saved registers
        MACRO
        _M_GETDREGLIST $dreg
        IF "$dreg"=""
_DRegList   SETS ""
            MEXIT
        ENDIF        
        IF "$dreg"="d8"
_DRegList   SETS "d8"
_RBytes     SETA _RBytes+8
            MEXIT
        ENDIF
        IF "$dreg"="d9"
_DRegList   SETS "d8-d9"
_RBytes     SETA _RBytes+16
            MEXIT
        ENDIF
        IF "$dreg"="d10"
_DRegList   SETS "d8-d10"
_RBytes     SETA _RBytes+24
            MEXIT
        ENDIF
        IF "$dreg"="d11"
_DRegList   SETS "d8-d11"
_RBytes     SETA _RBytes+32
            MEXIT
        ENDIF
        IF "$dreg"="d12"
_DRegList   SETS "d8-d12"
_RBytes     SETA _RBytes+40
            MEXIT
        ENDIF
        IF "$dreg"="d13"
_DRegList   SETS "d8-d13"
_RBytes     SETA _RBytes+48
            MEXIT
        ENDIF
        IF "$dreg"="d14"
_DRegList   SETS "d8-d14"
_RBytes     SETA _RBytes+56
            MEXIT
        ENDIF
        IF "$dreg"="d15"
_DRegList   SETS "d8-d15"
_RBytes     SETA _RBytes+64
            MEXIT
        ENDIF
        INFO 1, "Unrecognized saved d register limit '$dreg'"
        MEND
        
        ;// Produce function return instructions
        MACRO
        _M_RET $cc
        IF _DRegList<>""
            VPOP$cc {$_DRegList}
        ENDIF
        IF _RRegList=""
            BX$cc lr
        ELSE
            LDM$cc.FD sp!, {$_RRegList, pc}
        ENDIF
        MEND        
        
        ;// Early Function Exit Macro
        ;// $cc = condition to exit with
        ;// (Example: M_EXIT EQ)
        MACRO
        M_EXIT  $cc
        ASSERT  _InFunc
        IF  _SBytes!=0
            ;// Restore stack frame and exit
            B$cc  _End$_F
        ELSE
            ;// Can return directly
            _M_RET $cc
        ENDIF        
        MEND        

        ;// Function Footer Macro        
        ;// Generates the function epilogue
        MACRO
        M_END
        ASSERT _InFunc
_InFunc SETL {FALSE}
_End$_F

        ;// Restore the stack pointer to its original value on function entry
        IF _SBytes!=0
            _M_OPC ADD, sp, sp, _SBytes
        ENDIF
        _M_RET
        ENDFUNC

        ;// Reset the global stack tracking variables back to their 
        ;// initial values, and increment the function count
_SBytes        SETA 0
_F             SETA _F+1
        MEND

                
;//==========================================================================
;// Debug Macros
;//==========================================================================

        GBLL    DEBUG_ON
DEBUG_ON SETL   {FALSE}
        GBLL    DEBUG_STALLS_ON
DEBUG_STALLS_ON SETL {FALSE}
        
        ;//==========================================================================
        ;// Debug call to printf
        ;//  M_PRINTF $format, $val0, $val1, $val2
        ;//
        ;// Examples:
        ;//  M_PRINTF "x=%08x\n", r0
        ;//
        ;// This macro preserves the value of all registers including the
        ;// flags.
        ;//==========================================================================

        MACRO
        M_PRINTF  $format, $val0, $val1, $val2
        IF DEBUG_ON
        
        IMPORT  printf
        LCLA    nArgs
nArgs	SETA    0
        
        ;// save registers so we don't corrupt them
        STMFD   sp!, {r0-r12, lr}
        
        ;// Drop stack to give us some workspace
        SUB     sp, sp, #16
        
        ;// Save registers we need to print to the stack
        IF "$val2" <> ""
            ASSERT "$val1" <> ""
            STR    $val2, [sp, #8]
nArgs       SETA   nArgs+1
        ENDIF
        IF "$val1" <> ""
            ASSERT "$val0" <> ""
            STR    $val1, [sp, #4]
nArgs	    SETA   nArgs+1
        ENDIF
        IF "$val0"<>""
            STR    $val0, [sp]
nArgs	    SETA   nArgs+1
        ENDIF
        
        ;// Now we are safe to corrupt registers
        ADR     r0, %FT00
        IF nArgs=1
          LDR   r1, [sp]
        ENDIF
        IF nArgs=2
          LDMIA sp, {r1,r2}
        ENDIF
        IF nArgs=3
          LDMIA sp, {r1,r2,r3}
        ENDIF
        
        ;// print the values
        MRS     r4, cpsr        ;// preserve flags
        BL      printf
        MSR     cpsr_f, r4      ;// restore flags
        B       %FT01
00      ;// string to print
        DCB     "$format", 0
        ALIGN
01      ;// Finished
        ADD     sp, sp, #16
        ;// Restore registers
        LDMFD	sp!, {r0-r12,lr}

        ENDIF   ;// DEBUG_ON
        MEND


        ;// Stall Simulation Macro
        ;// Inserts a given number of NOPs for the currently
        ;//  defined platform
        MACRO
        M_STALL $plat1stall, $plat2stall, $plat3stall, $plat4stall, $plat5stall, $plat6stall
        IF DEBUG_STALLS_ON
            _M_STALL_SUB $plat1stall    
            _M_STALL_SUB $plat2stall    
            _M_STALL_SUB $plat3stall    
            _M_STALL_SUB $plat4stall    
            _M_STALL_SUB $plat5stall    
            _M_STALL_SUB $plat6stall    
        ENDIF
        MEND
        
        MACRO
        _M_STALL_SUB $platstall
        IF "$platstall"!=""
            LCLA _pllen
            LCLS _pl
            LCLL _pllog
_pllen      SETA :LEN:"$platstall"
_pl         SETS "$platstall":LEFT:(_pllen - 2)
            IF :DEF:$_pl
                IF $_pl
                    LCLS _st
                    LCLA _stnum
_st                 SETS "$platstall":RIGHT:1        
_stnum              SETA $_st
                    WHILE _stnum>0
			MOV sp, sp
_stnum                  SETA _stnum - 1
                    WEND
                ENDIF
            ENDIF
        ENDIF
        MEND
        
        
        
;//==========================================================================
;// Endian Invarience Macros
;// 
;// The idea behind these macros is that if an array is
;// loaded as words then the SMUL00 macro will multiply
;// array elements 0 regardless of the endianess of the
;// system. For little endian SMUL00=SMULBB, for big
;// endian SMUL00=SMULTT and similarly for other packed operations.
;//
;//==========================================================================

        MACRO
        LIBI4   $comli, $combi, $a, $b, $c, $d, $cc
        IF {ENDIAN}="big"
        $combi.$cc $a, $b, $c, $d
        ELSE
        $comli.$cc $a, $b, $c, $d
        ENDIF
        MEND
        
        MACRO
        LIBI3   $comli, $combi, $a, $b, $c, $cc
        IF {ENDIAN}="big"
        $combi.$cc $a, $b, $c
        ELSE
        $comli.$cc $a, $b, $c
        ENDIF
        MEND
        
        ;// SMLAxy macros
        
        MACRO
        SMLA00  $a, $b, $c, $d, $cc
        LIBI4 SMLABB, SMLATT, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLA01  $a, $b, $c, $d, $cc
        LIBI4 SMLABT, SMLATB, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLA0B  $a, $b, $c, $d, $cc
        LIBI4 SMLABB, SMLATB, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLA0T  $a, $b, $c, $d, $cc
        LIBI4 SMLABT, SMLATT, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLA10  $a, $b, $c, $d, $cc
        LIBI4 SMLATB, SMLABT, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLA11  $a, $b, $c, $d, $cc
        LIBI4 SMLATT, SMLABB, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLA1B  $a, $b, $c, $d, $cc
        LIBI4 SMLATB, SMLABB, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLA1T  $a, $b, $c, $d, $cc
        LIBI4 SMLATT, SMLABT, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLAB0  $a, $b, $c, $d, $cc
        LIBI4 SMLABB, SMLABT, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLAB1  $a, $b, $c, $d, $cc
        LIBI4 SMLABT, SMLABB, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLAT0  $a, $b, $c, $d, $cc
        LIBI4 SMLATB, SMLATT, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLAT1  $a, $b, $c, $d, $cc
        LIBI4 SMLATT, SMLATB, $a, $b, $c, $d, $cc
        MEND
        
        ;// SMULxy macros
        
        MACRO
        SMUL00  $a, $b, $c, $cc
        LIBI3 SMULBB, SMULTT, $a, $b, $c, $cc
        MEND
        
        MACRO
        SMUL01  $a, $b, $c, $cc
        LIBI3 SMULBT, SMULTB, $a, $b, $c, $cc
        MEND
        
        MACRO
        SMUL0B  $a, $b, $c, $cc
        LIBI3 SMULBB, SMULTB, $a, $b, $c, $cc
        MEND
        
        MACRO
        SMUL0T  $a, $b, $c, $cc
        LIBI3 SMULBT, SMULTT, $a, $b, $c, $cc
        MEND
        
        MACRO
        SMUL10  $a, $b, $c, $cc
        LIBI3 SMULTB, SMULBT, $a, $b, $c, $cc
        MEND
        
        MACRO
        SMUL11  $a, $b, $c, $cc
        LIBI3 SMULTT, SMULBB, $a, $b, $c, $cc
        MEND
        
        MACRO
        SMUL1B  $a, $b, $c, $cc
        LIBI3 SMULTB, SMULBB, $a, $b, $c, $cc
        MEND
        
        MACRO
        SMUL1T  $a, $b, $c, $cc
        LIBI3 SMULTT, SMULBT, $a, $b, $c, $cc
        MEND
        
        MACRO
        SMULB0  $a, $b, $c, $cc
        LIBI3 SMULBB, SMULBT, $a, $b, $c, $cc
        MEND
        
        MACRO
        SMULB1  $a, $b, $c, $cc
        LIBI3 SMULBT, SMULBB, $a, $b, $c, $cc
        MEND
        
        MACRO
        SMULT0  $a, $b, $c, $cc
        LIBI3 SMULTB, SMULTT, $a, $b, $c, $cc
        MEND
        
        MACRO
        SMULT1  $a, $b, $c, $cc
        LIBI3 SMULTT, SMULTB, $a, $b, $c, $cc
        MEND
        
        ;// SMLAWx, SMULWx macros
        
        MACRO
        SMLAW0  $a, $b, $c, $d, $cc
        LIBI4 SMLAWB, SMLAWT, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLAW1  $a, $b, $c, $d, $cc
        LIBI4 SMLAWT, SMLAWB, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMULW0  $a, $b, $c, $cc
        LIBI3 SMULWB, SMULWT, $a, $b, $c, $cc
        MEND
        
        MACRO
        SMULW1  $a, $b, $c, $cc
        LIBI3 SMULWT, SMULWB, $a, $b, $c, $cc
        MEND

        ;// SMLALxy macros


        MACRO
        SMLAL00  $a, $b, $c, $d, $cc
        LIBI4 SMLALBB, SMLALTT, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLAL01  $a, $b, $c, $d, $cc
        LIBI4 SMLALBT, SMLALTB, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLAL0B  $a, $b, $c, $d, $cc
        LIBI4 SMLALBB, SMLALTB, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLAL0T  $a, $b, $c, $d, $cc
        LIBI4 SMLALBT, SMLALTT, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLAL10  $a, $b, $c, $d, $cc
        LIBI4 SMLALTB, SMLALBT, $a, $b, $c, $d, $cc
        MEND

        MACRO
        SMLAL11  $a, $b, $c, $d, $cc
        LIBI4 SMLALTT, SMLALBB, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLAL1B  $a, $b, $c, $d, $cc
        LIBI4 SMLALTB, SMLALBB, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLAL1T  $a, $b, $c, $d, $cc
        LIBI4 SMLALTT, SMLALBT, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLALB0  $a, $b, $c, $d, $cc
        LIBI4 SMLALBB, SMLALBT, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLALB1  $a, $b, $c, $d, $cc
        LIBI4 SMLALBT, SMLALBB, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLALT0  $a, $b, $c, $d, $cc
        LIBI4 SMLALTB, SMLALTT, $a, $b, $c, $d, $cc
        MEND
        
        MACRO
        SMLALT1  $a, $b, $c, $d, $cc
        LIBI4 SMLALTT, SMLALTB, $a, $b, $c, $d, $cc
        MEND
        
  ENDIF ;// ARMCOMM_S_H
            
  END
