; 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." | 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 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