; Copyright 1996 Acorn Computers Ltd
;
; 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.
;

       SUBT     Useful procedure entry/exit macros => &.Hdr.Proc

OldOpt SETA     {OPT}
       OPT      OptNoList+OptNoP1List

       GBLS     Proc_RegList    ; Which registers to preserve
       GBLA     Proc_LocalStack ; And any ADJSP on entry/exit for local vars
       GBLL     Proc_SavedCPSR  ; CPSR was preserved
       GBLA     Proc_RegOffset  ; Offset of first register on stack

       GBLL     Proc_Debug      ; Whether to dump procedure name in image
Proc_Debug SETL {FALSE}

       GBLS     Proc_GetMachine
 [ :LNOT: :DEF: Included_Hdr_Machine_Machine
Proc_GetMachine SETS "GET Hdr:Machine.<Machine>"
 |
Proc_GetMachine SETS ""
 ]
       $Proc_GetMachine

       GBLS     Proc_GetCPU26
 [ :LNOT: :DEF: Included_Hdr_CPU_Generic26
Proc_GetCPU26 SETS "GET Hdr:CPU.Generic26"
 |
Proc_GetCPU26 SETS ""
 ]
        $Proc_GetCPU26

       GBLS     Proc_GetCPU32
 [ :LNOT: :DEF: Included_Hdr_CPU_Generic32
Proc_GetCPU32 SETS "GET Hdr:CPU.Generic32"
 |
Proc_GetCPU32 SETS ""
 ]
        $Proc_GetCPU32



; *****************************************************************************
; *** Keep a note of local stack and register use at the routine entry      ***
; *** point so that an exit may be effected anywhere in the body without    ***
; *** remembering how many (and which) registers to destack and ADJSP.      ***
; *** Also ensures that the code entry label is word-aligned.               ***
; *****************************************************************************
        MACRO
$label  Entry   $reglist,$framesize
        ALIGN
Proc_RegList SETS "$reglist"
 [ "$framesize" = ""
Proc_LocalStack SETA 0
 |
Proc_LocalStack SETA $framesize
 ]
Proc_SavedCPSR SETL {FALSE}
Proc_RegOffset SETA Proc_LocalStack
 [ "$label" <> ""
  [ Proc_Debug
        B       $label
        DCB     "$label", 0
        ALIGN
  ]
$label  ROUT
 ]
 [ "$Proc_RegList" = ""
        STR     lr, [sp, #-4]!
 |
        Push    "$Proc_RegList, lr"
 ]
 [ Proc_LocalStack <> 0
        SUB     sp, sp, #Proc_LocalStack
 ]
        MEND

; *****************************************************************************
; *** Keep a note of local stack and register use at the routine entry      ***
; *** point so that an exit may be effected anywhere in the body without    ***
; *** remembering how many (and which) registers to destack and ADJSP.      ***
; *** Also ensures that the code entry label is word-aligned.               ***
; *** You must use this macro if you want to use EXITS in 32-bit mode.      ***
; *****************************************************************************
        MACRO
$label  EntryS  $reglist,$framesize
        ALIGN
 [ No32bitCode
$label  Entry   "$reglist",$framesize
 |
Proc_RegList SETS "$reglist"
 [ "$framesize" = ""
Proc_LocalStack SETA 0
 |
Proc_LocalStack SETA $framesize
 ]
Proc_SavedCPSR SETL {TRUE}
Proc_RegOffset SETA Proc_LocalStack + 4
 [ "$label" <> ""
  [ Proc_Debug
        B       $label
        DCB     "$label", 0
        ALIGN
  ]
$label  ROUT
 ]
 [ "$Proc_RegList" = ""
        STR     lr, [sp, #-4]!
 |
        Push    "$Proc_RegList, lr"
 ]
        mymrs   AL, lr, CPSR
        STR     lr, [sp, #-4]!
 [ Proc_LocalStack <> 0
        SUB     sp, sp, #Proc_LocalStack
 ]
 ]
        MEND


; *****************************************************************************
; *** Another entry point so we can use the same routine body. NOROUT also  ***
; *** Stacks the same registers as does the corresponding ENTRY macro       ***
; *****************************************************************************
        MACRO
$label  AltEntry
        ALIGN
 [ "$label" <> ""
  [ Proc_Debug
        B       $label
        DCB     "$label", 0
        ALIGN
  ]
$label ; NOROUT
 ]
 [ "$Proc_RegList" = ""
        STR     lr, [sp, #-4]!
 |
        Push    "$Proc_RegList, lr"
 ]
 [ Proc_SavedCPSR
        mymrs   AL, lr, CPSR
        STR     lr, [sp, #-4]!
 ]
 [ Proc_LocalStack <> 0
        SUB     sp, sp, #Proc_LocalStack
 ]
        MEND

        MACRO
$label  ALTENTRY
$label  AltEntry
        MEND

; *****************************************************************************
; *** Exit procedure, restore stack and saved registers to values on entry  ***
; *****************************************************************************
        MACRO
$label  Exit    $cond
$label
 [ Proc_SavedCPSR
        ADD$cond sp, sp, #Proc_LocalStack + 4
 |
 [ Proc_LocalStack <> 0
        ADD$cond sp, sp, #Proc_LocalStack
 ]
 ]
 [ "$Proc_RegList" = ""
        LDR$cond pc, [sp], #4
 |
        Pull    "$Proc_RegList, pc",$cond
 ]
        MEND

        MACRO
$label  EXIT    $cond
$label  Exit    $cond
        MEND

; *****************************************************************************
; *** Exit procedure : restore stack and saved registers + psr to values on ***
; *** entry. No longer copes with 3um ARM bug fix (world is 2um'ised)       ***
; *****************************************************************************
        MACRO
$label  ExitS   $cond,$fields
$label
 [ Proc_SavedCPSR
  [ "$cond" <> "AL" :LAND: "$cond" <> ""
        ; branch over on opposite condition
        DCD     &1A000000 :EOR: Cond_$cond + ((%FT01 - (. + 8))/4)
  ]
 [ Proc_LocalStack <> 0
        ADD     sp, sp, #Proc_LocalStack
 ]
        LDR     lr, [sp], #4
 [ "$fields"=""
        mymsr  ,CPSR_f, lr
 |
        mymsr  ,CPSR_$fields, lr
 ]
 [ "$Proc_RegList" = ""
        LDR     pc, [sp], #4
 |
        Pull    "$Proc_RegList, pc"
 ]
01
 |
 ; 26-bit version
 [ No26bitCode
        ! 1, "ExitS without EntryS"
 ]
 [ Proc_LocalStack <> 0
        ADD$cond sp, sp, #Proc_LocalStack
 ]
 [ "$Proc_RegList" = ""
        Pull    pc,$cond,^
 |
        Pull    "$Proc_RegList, pc",$cond,^
 ]
 ]
        MEND

        MACRO
$label  EXITS   $cond,$fields
$label  ExitS   $cond,$fields
        MEND

; *****************************************************************************
; *** Exit procedure : restore stack and saved registers + psr (except V)   ***
; *** to values on entry. Return with V unaltered from current state.       ***
; *****************************************************************************
        MACRO
$label  ExitV   $fields
$label
 [ Proc_LocalStack <> 0
        ADD     sp, sp, #Proc_LocalStack
 ]
 [ Proc_SavedCPSR
        LDR     lr, [sp], #4
        BICVC   lr, lr, #V_bit
        ORRVS   lr, lr, #V_bit
 [ "$fields"=""
        mymsr  ,CPSR_f, lr
 |
        mymsr  ,CPSR_$fields, lr
 ]
 [ "$Proc_RegList" = ""
        LDR     pc, [sp], #4
 |
        Pull    "$Proc_RegList, pc"
 ]
 |
 [ No26bitCode
        ! 1, "ExitV without EntryS"
 ]
 [ "$Proc_RegList" = ""
        LDR     lr, [sp], #4
 |
        Pull    "$Proc_RegList, lr"
 ]
        BICVCS  pc, lr, #V_bit
        ORRVSS  pc, lr, #V_bit
 ]
        MEND

        MACRO
$label  EXITV   $fields
$label  ExitV   $fields
        MEND

; *****************************************************************************
; *** Exit procedure : restore stack and saved registers + psr (except V)   ***
; *** to values on entry. Return with V clear.                              ***
; *****************************************************************************
        MACRO
$label  ExitVC  $fields
$label
 [ Proc_LocalStack <> 0
        ADD     sp, sp, #Proc_LocalStack
 ]
 [ Proc_SavedCPSR
        LDR     lr, [sp], #4
        BIC     lr, lr, #V_bit
 [ "$fields"=""
        mymsr  ,CPSR_f, lr
 |
        mymsr  ,CPSR_$fields, lr
 ]
 [ "$Proc_RegList" = ""
        LDR     pc, [sp], #4
 |
        Pull    "$Proc_RegList, pc"
 ]
 |
 [ No26bitCode
        ! 1, "ExitVC without EntryS"
 ]
 [ "$Proc_RegList" = ""
        LDR     lr, [sp], #4
 |
        Pull    "$Proc_RegList, lr"
 ]
        BICS    pc, lr, #V_bit
 ]
        MEND

        MACRO
$label  EXITVC  $fields
$label  ExitVC  $fields
        MEND

; *****************************************************************************
; *** Exit procedure : restore stack and saved registers + psr (except V)   ***
; *** to values on entry. Return with V set.                                ***
; *****************************************************************************
        MACRO
$label  ExitVS  $fields
$label
 [ Proc_LocalStack <> 0
        ADD     sp, sp, #Proc_LocalStack
 ]
 [ Proc_SavedCPSR
        LDR     lr, [sp], #4
        ORR     lr, lr, #V_bit
 [ "$fields"=""
        mymsr  ,CPSR_f, lr
 |
        mymsr  ,CPSR_$fields, lr
 ]
 [ "$Proc_RegList" = ""
        LDR     pc, [sp], #4
 |
        Pull    "$Proc_RegList, pc"
 ]
 |
 [ No26bitCode
        ! 1, "ExitVS without EntryS"
 ]
 [ "$Proc_RegList" = ""
        LDR     lr, [sp], #4
 |
        Pull    "$Proc_RegList, lr"
 ]
        ORRS    pc, lr, #V_bit
 ]
        MEND

        MACRO
$label  EXITVS  $fields
$label  ExitVS  $fields
        MEND

; *****************************************************************************
; *** Restore stack and saved registers, lr to values on entry to procedure ***
; *****************************************************************************
        MACRO
$label  PullEnv $cond
$label
 [ Proc_RegOffset <> 0
        ADD$cond sp, sp, #Proc_RegOffset
 ]
 [ "$Proc_RegList" = ""
        LDR$cond lr, [sp], #4
 |
        Pull    "$Proc_RegList, lr", $cond
 ]
        MEND

; *****************************************************************************
; *** Restore stack and saved registers, lr and CPSR to values on entry to  ***
; *** procedure                                                             ***
; *****************************************************************************
        MACRO
$label  PullEnvS $cond,$fields
$label
 [ Proc_SavedCPSR
  [ "$cond" <> "AL" :LAND: "$cond" <> ""
        ; branch over on opposite condition
        DCD     &1A000000 :EOR: Cond_$cond + ((%FT01 - (. + 8))/4)
  ]
 [ Proc_LocalStack <> 0
        ADD     sp, sp, #Proc_LocalStack
 ]
        LDR     lr, [sp], #4
 [ "$fields"=""
        mymsr  ,CPSR_f, lr
 |
        mymsr  ,CPSR_$fields, lr
 ]
 [ "$Proc_RegList" = ""
        LDR     lr, [sp], #4
 |
        Pull    "$Proc_RegList, lr"
 ]
01
 |
; 26-bit form
 [ Proc_LocalStack <> 0
        ADD$cond sp, sp, #Proc_LocalStack
 ]
 [ "$Proc_RegList" = ""
        LDR$cond lr, [sp], #4
 |
        Pull    "$Proc_RegList, lr", $cond
 ]
 ]
        MEND

; *****************************************************************************


; *****************************************************************************
; *** FramLDR/FramSTR macros                                                ***
; *** That let you access the register values stacked on Entry              ***
; *** to replace LDR     r1, [sp, #Proc_LocalStack + 1*4]                   ***
; ***                                                                       ***
; *** The new macros allow non-continuous regs to be used, and will generate***
; *** an error if you try to access a register not stored on the stack      ***
; ***                                                                       ***
; *** FramLDR r0         load r0 stored on stack frame by Entry macro       ***
; *** FramLDR r5,CS      load r5 from stack frame if CS                     ***
; *** FramLDR r0,,r8     load r8 on stack frame into r0                     ***
; *** FramLDR r0,HI,r10  load r10 on stack frame into r0 if HI              ***
; ***                                                                       ***
; *** A particularly useful way of using this macro is:                     ***
; *** FramSTR r8,,r0     change r0 value that will be restored on Exit      ***
; *** ...more code                                                          ***
; *** Exit                                                                  ***
; *****************************************************************************

        GBLA Fram_SpecRegOffset  ;offset to load/store particular reg
        GBLS Fram_RegStr
        GBLS Fram_CopyRegs
        GBLS Fram_LowRegStr

        MACRO
        FramSPL $origvar,$newvar,$sep

$newvar SETS ""
        WHILE (($origvar):CC:" ":LEFT:1 <> "$sep"):LAND:($origvar >"")
$newvar SETS $newvar:CC:($origvar:LEFT:1)
 [ ($origvar >"")
$origvar SETS $origvar:RIGHT:(:LEN:$origvar-1)
 ]

        WEND
        ;skip remaining seperator
 [ ($origvar >"")
$origvar SETS $origvar:RIGHT:(:LEN:$origvar-1)
 ]

        MEND

        MACRO
        FramCOM $dstreg,$framereg
        LCLA actframe
        LCLA counter

actframe SETA $dstreg
 [ "$framereg" <>""
actframe SETA $framereg
 ]

        LCLA regmask
Fram_CopyRegs SETS Proc_RegList
        WHILE Fram_CopyRegs > ""

        FramSPL "Fram_CopyRegs","Fram_RegStr",","

        ;regno =r0-r8 or a single reg r5
        LCLA lowreg
        LCLA highreg

        FramSPL "Fram_RegStr","Fram_LowRegStr","-"
lowreg  SETA $Fram_LowRegStr
 [ Fram_RegStr =""
highreg SETA lowreg
 |
highreg SETA $Fram_RegStr
 ]

        ;set bits between lowreg and highreg in our mask
        WHILE lowreg <=highreg
regmask SETA regmask:OR:(1:SHL:lowreg)
lowreg  SETA lowreg +1
        WEND

        WEND

        ;calculate Fram_SpecRegOffset using regmask
Fram_SpecRegOffset SETA 0
        LCLA counter

        WHILE counter <actframe
 [ (regmask:AND:(1:SHL:counter)) >0
Fram_SpecRegOffset SETA Fram_SpecRegOffset+4
 ]
counter SETA counter +1
        WEND

 [ (regmask:AND:(1:SHL:counter)) =0
        LCLS tempstr
  [ actframe >9
actframe SETA actframe -10
tempstr SETS "1"
  ]
        !       1,"r$tempstr":CC:("$actframe":RIGHT:1):CC:" not in frame!"
 ]

        MEND

        MACRO
$label  FramLDR $dstreg,$cond,$framereg
$label  FramCOM $dstreg,$framereg
        LDR$cond $dstreg,[sp ,#Proc_RegOffset +Fram_SpecRegOffset]
        MEND

        MACRO
$label  FRAMLDR $dstreg,$cond,$framereg
$label  FramLDR $dstreg,$cond,$framereg
        MEND

        MACRO
$label  FramSTR $dstreg,$cond,$framereg
$label  FramCOM $dstreg,$framereg
        STR$cond $dstreg,[sp ,#Proc_RegOffset +Fram_SpecRegOffset]
        MEND

        MACRO
$label  FRAMSTR $dstreg,$cond,$framereg
$label  FramSTR $dstreg,$cond,$framereg
        MEND
; *****************************************************************************

        OPT     OldOpt
        END
