DIRECTORY Basics USING [BITLSHIFT, BITAND], CirioMemory, CirioNubAccess, CirioTargets, CirioTypes, IO, Rope, LoadStateAccess USING[LoadedModuleInfo], ObjectFiles USING [Parsed], ObjectFilesPrivate USING [Module], SGI USING [FindMatchingProcDescr, WireProcDescriptorBody]; CirioTargetsImpl: CEDAR PROGRAM IMPORTS Basics, CirioMemory, CirioNubAccess, IO, Rope, SGI EXPORTS CirioTargets = BEGIN OPEN CirioMemory, CirioTargets; BitAddr: TYPE ~ CirioTypes.BitAddr; Mem: TYPE ~ CirioTargets.Mem; Target: TYPE ~ CirioTargets.Target; LoadedModuleInfo: TYPE ~ LoadStateAccess.LoadedModuleInfo; Error: PUBLIC ERROR [msg: ROPE] ~ CODE; CreateTarget: PUBLIC PROC [h: CirioNubAccess.Handle] RETURNS [Target] ~ { SPARCTarget: PROC [h: CirioNubAccess.Handle, instrSet, opSys: Rope.ROPE] RETURNS[Target] ~ { target: Target; SELECT TRUE FROM Rope.Equal[opSys, SunOS4] => target ¬ NEW[CirioTargets.TargetRep ¬ [ nub: h, bitsPerAu: 8, bitsPerPtr: 32, ausPerPtr: 4, ptrSize: [4, 0], instrSet: instrSet, opSys: opSys, FpFromSp: SPARCSunOS4FpFromSp, NextSp: SPARCSunOS4NextSp, ReadPc: SPARCSunOS4ReadPc, ReadReg: SPARCSunOS4ReadReg, WriteReg: SPARCSunOS4WriteReg, RegAddr: SPARCSunOS4RegAddr, DescriptorFromPC: SPARCSunOS4DescriptorFromPC, CNameToLoaderName: SPARCSunOS4CNameToLoaderName ]]; Rope.Equal[opSys, SunOS5] => target ¬ NEW[CirioTargets.TargetRep ¬ [ nub: h, bitsPerAu: 8, bitsPerPtr: 32, ausPerPtr: 4, ptrSize: [4, 0], instrSet: instrSet, opSys: opSys, FpFromSp: SPARCSunOS4FpFromSp, NextSp: SPARCSunOS4NextSp, ReadPc: SPARCSunOS4ReadPc, ReadReg: SPARCSunOS4ReadReg, WriteReg: SPARCSunOS4WriteReg, RegAddr: SPARCSunOS4RegAddr, DescriptorFromPC: SPARCSunOS4DescriptorFromPC, CNameToLoaderName: SPARCSunOS5CNameToLoaderName ]]; ENDCASE => Error[Rope.Cat["Unsupported opSys: ", opSys, "\N"]]; RETURN[target]; }; RS6000Target: PROC [h: CirioNubAccess.Handle, instrSet, opSys: Rope.ROPE] RETURNS[Target] ~ { target: Target; IF NOT Rope.Equal[opSys, AIX] THEN Error[Rope.Cat["Unsupported opSys: ", opSys, "\N"]]; target ¬ NEW[CirioTargets.TargetRep ¬ [ nub: h, bitsPerAu: 8, bitsPerPtr: 32, ausPerPtr: 4, ptrSize: [4, 0], instrSet: instrSet, opSys: opSys, FpFromSp: RS6000AIXFpFromSp, NextSp: RS6000AIXNextSp, ReadPc: RS6000AIXReadPc, ReadReg: RS6000AIXReadReg, WriteReg: RS6000AIXWriteReg, RegAddr: RS6000AIXRegAddr, DescriptorFromPC: RS6000AIXDescriptorFromPC, CNameToLoaderName: RS6000AIXCNameToLoaderName ]]; RETURN[target]; }; MIPSTarget: PROC [h: CirioNubAccess.Handle, instrSet, opSys: Rope.ROPE] RETURNS[Target] ~ { target: Target; IF NOT (Rope.Equal[opSys, IRIX]) THEN Error[Rope.Cat["Unsupported opSys: ", opSys, "\N"]]; target ¬ NEW[CirioTargets.TargetRep ¬ [ nub: h, bitsPerAu: 8, bitsPerPtr: 32, ausPerPtr: 4, ptrSize: [4, 0], instrSet: instrSet, opSys: opSys, FpFromSp: MIPSFpFromSp, NextSp: MIPSNextSp, ReadPc: MIPSReadPc, ReadReg: MIPSReadReg, WriteReg: MIPSWriteReg, RegAddr: MIPSRegAddr, DescriptorFromPC: MIPSDescriptorFromPC, CNameToLoaderName: MIPSCNameToLoaderName ]]; RETURN[target]; }; SPARC: Rope.ROPE ¬ "SPARC"; RS6000: Rope.ROPE ¬ "RS6000"; MIPSEL: Rope.ROPE ¬ "MIPSEL"; -- MIPS little endian MIPSEB: Rope.ROPE ¬ "MIPSEB"; -- MIPS big endian SunOS4: Rope.ROPE ¬ "SunOS4"; SunOS5: Rope.ROPE ¬ "SunOS5"; AIX: Rope.ROPE ¬ "AIX"; IRIX: Rope.ROPE ¬ "IRIX"; instrSet, opSys: Rope.ROPE; [instrSet, opSys] ¬ CirioNubAccess.GetInstructionSetAndOperatingSystem[h]; SELECT TRUE FROM Rope.Equal[instrSet, SPARC] => RETURN[SPARCTarget[h, instrSet, opSys]]; Rope.Equal[instrSet, RS6000] => RETURN[RS6000Target[h, instrSet, opSys]]; Rope.Equal[instrSet, MIPSEL] => RETURN[MIPSTarget[h, instrSet, opSys]]; Rope.Equal[instrSet, MIPSEB] => RETURN[MIPSTarget[h, instrSet, opSys]]; ENDCASE => Error[Rope.Cat["Unsupported instrSet: ", instrSet, "\N"]]; }; SPARCSunOS4FpFromSp: PROC [loadedModule: REF LoadedModuleInfo, t: Target, sp: BitAddr, absPC: CARD] RETURNS [BitAddr] ~ { stackOffset: INT ¬ 14*4; RETURN[ReadValue[t, sp, stackOffset]]; }; SPARCSunOS4NextSp: PROC [loadedModule: REF LoadedModuleInfo, t: Target, sp, fp: BitAddr, absPC: CARD] RETURNS [BitAddr] ~ { RETURN[fp]; }; SPARCSunOS4ReadPc: PROC [loadedModule: REF LoadedModuleInfo, t: Target, sp, fp: BitAddr, absPC: CARD] RETURNS [BitAddr] ~ { stackOffset: INT ¬ 15*4; RETURN[ReadValue[t, sp, stackOffset]]; }; SPARCSunOS4ReadReg: PROC [loadedModule: REF LoadedModuleInfo, t: Target, frame: Mem, reg: INT, absPC: CARD] RETURNS [CARD] ~ { regMem: Mem; IF reg < 16 OR reg > 31 THEN Error[IO.PutFR1["Can't read register: %g\N", IO.int[reg]]]; regMem ¬ frame.MemPtrRegIndirect[sp, AusToBa[32], BaCons[(reg-16)*4, 0], FALSE]; RETURN[regMem.MemRead[32, zeroBA]]; }; SPARCSunOS4WriteReg: PROC [loadedModule: REF LoadedModuleInfo, t: Target, frame: Mem, reg: INT, val: CARD, absPC: CARD] ~ { regMem: Mem; IF reg < 16 OR reg > 31 THEN Error[IO.PutFR1["Can't write register: %g\N", IO.int[reg]]]; regMem ¬ frame.MemPtrRegIndirect[sp, AusToBa[32], BaCons[(reg-16)*4, 0], FALSE]; regMem.MemWrite[val, 32, zeroBA]; RETURN; }; SPARCSunOS4RegAddr: PROC [loadedModule: REF LoadedModuleInfo, t: Target, frame: Mem, reg: INT, absPC: CARD] RETURNS [BitAddr] ~ { addr: BitAddr; addrBS: BitStretch; IF reg < 16 OR reg > 31 THEN Error[IO.PutFR1["Can't find address for register: %g\N", IO.int[reg]]]; addrBS ¬ frame.MemReadPtrReg[sp]; addr ¬ BaAdd[addrBS.start, AusToBa[(reg-16)*4]]; RETURN[addr]; }; SPARCSunOS4DescriptorFromPC: PROC [t: Target, pc: BitAddr] RETURNS [BitAddr] ~ { RETURN[pc]; }; SPARCSunOS4CNameToLoaderName: PROC [t: Target, cName: Rope.ROPE] RETURNS [Rope.ROPE] ~ { IF cName = NIL THEN RETURN [cName]; IF cName.IsEmpty THEN RETURN [cName]; RETURN[Rope.Concat["_", cName]]; }; SPARCSunOS5CNameToLoaderName: PROC [t: Target, cName: Rope.ROPE] RETURNS [Rope.ROPE] ~ { RETURN [cName]; }; RS6000AIXFpFromSp: PROC [loadedModule: REF LoadedModuleInfo, t: Target, sp: BitAddr, absPC: CARD] RETURNS [BitAddr] ~ { RETURN[sp]; }; RS6000AIXNextSp: PROC [loadedModule: REF LoadedModuleInfo, t: Target, sp, fp: BitAddr, absPC: CARD] RETURNS [BitAddr] ~ { stackOffset: INT ¬ 0*4; RETURN[ReadValue[t, sp, stackOffset]]; }; RS6000AIXReadPc: PROC [loadedModule: REF LoadedModuleInfo, t: Target, sp, fp: BitAddr, absPC: CARD] RETURNS [BitAddr] ~ { stackOffset: INT ¬ 2*4; newfp: BitAddr ¬ ReadValue[t, fp, 0*4]; IF newfp = CirioMemory.zeroBA THEN RETURN[CirioMemory.unspecdBA]; RETURN[ReadValue[t, newfp, stackOffset]]; }; RS6000AIXReadReg: PROC [loadedModule: REF LoadedModuleInfo, t: Target, frame: Mem, reg: INT, absPC: CARD] RETURNS [CARD] ~ { offset: INT; regMem: Mem; IF reg = 0 OR reg > 10 THEN Error[IO.PutFR1["Can't read register: %g\N", IO.int[reg]]]; offset ¬ IF reg = 1 THEN 0*4 ELSE (reg+3)*4; regMem ¬ frame.MemPtrRegIndirect[sp, AusToBa[32], BaCons[offset, 0], FALSE]; RETURN[regMem.MemRead[32, zeroBA]]; }; RS6000AIXWriteReg: PROC [loadedModule: REF LoadedModuleInfo, t: Target, frame: Mem, reg: INT, val: CARD, absPC: CARD] ~ { offset: INT; regMem: Mem; IF reg = 0 OR reg > 10 THEN Error[IO.PutFR1["Can't read register: %g\N", IO.int[reg]]]; offset ¬ IF reg = 1 THEN 0*4 ELSE (reg+3)*4; regMem ¬ frame.MemPtrRegIndirect[sp, AusToBa[32], BaCons[offset, 0], FALSE]; regMem.MemWrite[val, 32, zeroBA]; RETURN; }; RS6000AIXRegAddr: PROC [loadedModule: REF LoadedModuleInfo, t: Target, frame: Mem, reg: INT, absPC: CARD] RETURNS [BitAddr] ~ { addr: BitAddr; offset: INT; addrBS: BitStretch; IF reg = 0 OR reg > 10 THEN Error[IO.PutFR1["Can't find address for register: %g\N", IO.int[reg]]]; offset ¬ IF reg = 1 THEN 0*4 ELSE (reg+3)*4; addrBS ¬ frame.MemReadPtrReg[sp]; addr ¬ BaAdd[addrBS.start, AusToBa[offset]]; RETURN[addr]; }; RS6000AIXDescriptorFromPC: PROC [t: Target, pc: BitAddr] RETURNS [BitAddr] ~ { symEntry, tocEntry: CirioNubAccess.SymEntry; tocName: Rope.ROPE; symEntry ¬ CirioNubAccess.LookupSymEntryByValue[t.nub, BaToAus[pc], 0]; IF symEntry = NIL THEN RETURN[CirioMemory.unspecdBA]; tocName ¬ Rope.Substr[symEntry.name, 1, Rope.Length[symEntry.name] - 1]; tocEntry ¬ CirioNubAccess.LookupSymEntryByName[t.nub, tocName, FALSE, FALSE, 0]; IF tocEntry = NIL THEN RETURN[CirioMemory.unspecdBA]; RETURN[AusToBa[tocEntry.value]]; }; RS6000AIXCNameToLoaderName: PROC [t: Target, cName: Rope.ROPE] RETURNS [Rope.ROPE] ~ { IF cName = NIL THEN RETURN [cName]; IF cName.IsEmpty THEN RETURN [cName]; RETURN[Rope.Concat[".", cName]]; }; MIPSFpFromSp: PROC [loadedModule: REF LoadedModuleInfo, t: Target, sp: BitAddr, absPC: CARD] RETURNS [BitAddr] ~ TRUSTED { module: ObjectFilesPrivate.Module ¬ LOOPHOLE[loadedModule.module, ObjectFilesPrivate.Module]; parsed: ObjectFiles.Parsed ¬ LOOPHOLE[module.whole, ObjectFiles.Parsed]; currentPDR: SGI.WireProcDescriptorBody; found: BOOLEAN; frameReg: INT32; frameSize: INT32; fpAddr : BitAddr; dotORel: CARD; dotORel ¬ absPC - loadedModule.lsi[text].base; [currentPDR, found] ¬ SGI.FindMatchingProcDescr [dotORel, parsed]; IF found = FALSE THEN RETURN [zeroBA]; frameReg ¬ currentPDR.framereg; frameSize ¬ currentPDR.frameoffset; fpAddr ¬ BaAdd [sp, BaCons [frameSize, 0]]; RETURN[fpAddr]; }; MIPSNextSp: PROC [loadedModule: REF LoadedModuleInfo, t: Target, sp, fp: BitAddr, absPC: CARD] RETURNS [BitAddr] ~ TRUSTED { module: ObjectFilesPrivate.Module ¬ LOOPHOLE[loadedModule.module, ObjectFilesPrivate.Module]; parsed: ObjectFiles.Parsed ¬ LOOPHOLE[module.whole, ObjectFiles.Parsed]; currentPDR: SGI.WireProcDescriptorBody; found: BOOLEAN; fpReg : INT32; dotORel: CARD; dotORel ¬ absPC - loadedModule.lsi[text].base; [currentPDR, found] ¬ SGI.FindMatchingProcDescr [dotORel, parsed]; IF found = FALSE THEN RETURN [zeroBA]; fpReg ¬ currentPDR.framereg; IF fpReg = 8 THEN { RETURN[ReadValue[t, BaAdd [sp, BaCons [32, 0]], 0]]; }; RETURN[fp]; }; MIPSReadPc: PROC [loadedModule: REF LoadedModuleInfo, t: Target, sp, fp: BitAddr, absPC: CARD] RETURNS [BitAddr] ~ TRUSTED { module: ObjectFilesPrivate.Module ¬ LOOPHOLE[loadedModule.module, ObjectFilesPrivate.Module]; parsed: ObjectFiles.Parsed ¬ LOOPHOLE[module.whole, ObjectFiles.Parsed]; currentPDR: SGI.WireProcDescriptorBody; found: BOOLEAN; fpReg : INT32; pcReg : INT32; fpAddr : BitAddr; dotORel: CARD; dotORel ¬ absPC - loadedModule.lsi[text].base; [currentPDR, found] ¬ SGI.FindMatchingProcDescr [dotORel, parsed]; IF found = FALSE THEN RETURN [zeroBA]; fpReg ¬ currentPDR.framereg; pcReg ¬ currentPDR.pcreg; fpAddr ¬ MIPSFpFromSp [loadedModule, t, sp, absPC]; IF fpReg = 8 THEN { RETURN[ReadValue[t, BaAdd [sp, BaCons [28, 0]], 0]]; }; IF pcReg = 31 THEN { frameOffset : INT32 ¬ currentPDR.regoffset; frameSize : INT32 ¬ currentPDR.frameoffset; offset : BitAddr ¬ BaCons [(frameSize + frameOffset), 0]; IF frameOffset # 0 THEN RETURN[ReadValue[t, BaAdd [sp, offset], 0]] ELSE { RETURN[ReadValue[t, BaSub [sp, BaCons [4, 0]], 0]]; }; } ELSE Error["Can't find correct PC reg\N"]; }; FindPosOfRegOnMipsStack: PROC [reg: INT, procDescrRegmask: INT32] RETURNS [INT] = { RegArray: TYPE = ARRAY [1..10] OF INT; n: INT; regMask, bitMask: INT32; savedReg: RegArray ¬ RegArray[31, 30, 23, 22, 21, 20, 19, 18, 17, 16]; pos: INT ¬ 1; regMask ¬ Basics.BITLSHIFT[1, reg]; IF Basics.BITAND[regMask, procDescrRegmask] = 1 THEN { FOR n IN [1..10] DO bitMask ¬ Basics.BITLSHIFT[1, savedReg[n]]; IF Basics.BITAND[bitMask, procDescrRegmask] = 1 THEN { pos ¬ pos+1; IF bitMask = regMask THEN GO TO stopedAtRegister; }; ENDLOOP; EXITS stopedAtRegister => RETURN [pos]; }; RETURN [-1]; }; MIPSReadReg: PROC [loadedModule: REF LoadedModuleInfo, t: Target, frame: Mem, reg: INT, absPC: CARD] RETURNS [CARD] ~ TRUSTED { module: ObjectFilesPrivate.Module ¬ LOOPHOLE[loadedModule.module, ObjectFilesPrivate.Module]; parsed: ObjectFiles.Parsed ¬ LOOPHOLE[module.whole, ObjectFiles.Parsed]; currentPDR: SGI.WireProcDescriptorBody; found: BOOLEAN; frameOffset : INT32; frameSize : INT32; posInStack : INT; offset : BitAddr; regMem: Mem; dotORel: CARD; dotORel ¬ absPC - loadedModule.lsi[text].base; [currentPDR, found] ¬ SGI.FindMatchingProcDescr [dotORel, parsed]; IF found = FALSE THEN RETURN [0]; frameOffset ¬ currentPDR.regoffset; frameSize ¬ currentPDR.frameoffset; posInStack ¬ FindPosOfRegOnMipsStack [reg, currentPDR.regmask]; IF reg < 16 OR (reg > 23 AND reg # 30) THEN Error[IO.PutFR1["Can't read register: %g\N", IO.int[reg]]]; offset ¬ BaCons [(frameSize + frameOffset - (posInStack * 4)), 0]; regMem ¬ frame.MemPtrRegIndirect[sp, AusToBa[32], offset, FALSE]; RETURN[regMem.MemRead[32, zeroBA]]; }; MIPSWriteReg: PROC [loadedModule: REF LoadedModuleInfo, t: Target, frame: Mem, reg: INT, val: CARD, absPC: CARD] ~ TRUSTED { module: ObjectFilesPrivate.Module ¬ LOOPHOLE[loadedModule.module, ObjectFilesPrivate.Module]; parsed: ObjectFiles.Parsed ¬ LOOPHOLE[module.whole, ObjectFiles.Parsed]; currentPDR: SGI.WireProcDescriptorBody; found: BOOLEAN; frameOffset : INT32; frameSize : INT32; posInStack : INT; offset : BitAddr; regMem: Mem; dotORel: CARD; dotORel ¬ absPC - loadedModule.lsi[text].base; [currentPDR, found] ¬ SGI.FindMatchingProcDescr [dotORel, parsed]; IF found = FALSE THEN RETURN; frameOffset ¬ currentPDR.regoffset; frameSize ¬ currentPDR.frameoffset; posInStack ¬ FindPosOfRegOnMipsStack [reg, currentPDR.regmask]; IF reg < 16 OR (reg > 23 AND reg # 30) THEN Error[IO.PutFR1["Can't write register: %g\N", IO.int[reg]]]; offset ¬ BaCons [(frameSize + frameOffset - (posInStack * 4)), 0]; regMem ¬ frame.MemPtrRegIndirect[sp, AusToBa[32], offset, FALSE]; regMem.MemWrite[val, 32, zeroBA]; RETURN; }; MIPSRegAddr: PROC [loadedModule: REF LoadedModuleInfo, t: Target, frame: Mem, reg: INT, absPC: CARD] RETURNS [BitAddr] ~ TRUSTED { module: ObjectFilesPrivate.Module ¬ LOOPHOLE[loadedModule.module, ObjectFilesPrivate.Module]; parsed: ObjectFiles.Parsed ¬ LOOPHOLE[module.whole, ObjectFiles.Parsed]; currentPDR: SGI.WireProcDescriptorBody; found: BOOLEAN; frameOffset : INT32; frameSize : INT32; posInStack : INT; offset : BitAddr; addr: BitAddr; addrBS: BitStretch; dotORel: CARD; dotORel ¬ absPC - loadedModule.lsi[text].base; [currentPDR, found] ¬ SGI.FindMatchingProcDescr [dotORel, parsed]; IF found = FALSE THEN RETURN [zeroBA]; frameOffset ¬ currentPDR.regoffset; frameSize ¬ currentPDR.frameoffset; posInStack ¬ FindPosOfRegOnMipsStack [reg, currentPDR.regmask]; IF reg < 16 OR (reg > 23 AND reg # 30) THEN Error[IO.PutFR1["Can't find address for register: %g\N", IO.int[reg]]]; addrBS ¬ frame.MemReadPtrReg[sp]; offset ¬ BaCons [(frameSize + frameOffset - (posInStack * 4)), 0]; addr ¬ BaAdd[addrBS.start, offset]; RETURN[addr]; }; MIPSDescriptorFromPC: PROC [t: Target, pc: BitAddr] RETURNS [BitAddr] ~ { RETURN[pc]; }; MIPSCNameToLoaderName: PROC [t: Target, cName: Rope.ROPE] RETURNS [Rope.ROPE] ~ { IF cName = NIL THEN RETURN [cName]; IF cName.IsEmpty THEN RETURN [cName]; RETURN[cName]; }; ReadValue: PROC[t: Target, base: BitAddr, offset: INT] RETURNS[BitAddr] ~ { addr: BitAddr ¬ BaAdd[base, AusToBa[offset]]; value: BitAddr ¬ PtrToBa[CirioNubAccess.RaFromCi[t.nub, LOOPHOLE[BaToAus[addr]], 0].Read32BitsAsCard]; RETURN[value]; }; WriteValue: PROC[t: Target, base: BitAddr, offset: INT, val: CARD] ~ { addr: BitAddr ¬ BaAdd[base, AusToBa[offset]]; CirioNubAccess.RaFromCi[t.nub, LOOPHOLE[BaToAus[addr]], 0].WriteCardAs32Bits[val]; RETURN; }; END. f CirioTargetsImpl.mesa Copyright Σ 1991, 1992, 1993 by Xerox Corporation. All rights reserved. Laurie Horton, February 28, 1992 10:16 am PST Last tweaked by Mike Spreitzer September 19, 1991 9:31 am PDT Philip James, February 28, 1992 11:20 am PST Katsuyuki Komatsu December 11, 1992 8:54 pm PST Jas, January 5, 1993 3:25 pm PST Willie-s, January 22, 1993 2:33 pm PST instruction set names operating system names OSF1: Rope.ROPE _ "OSF1"; SPARC-SunOS4 procs The parsed and absPC arguments are for the MIPS targets only For SPARCS, the registers are stored on the stack as follows: globals r0 - r7 not available on the stack outputs r8 - r15 @ fp(callee's)+(reg-8)*4 locals r16-r23 @ sp+(reg-16)*4 inputs r24-r29 @ sp+(reg-16)*4 frame pointer (fp) (caller's sp) r30 @ sp+(reg-16)*4 return address (pc) r31 @ sp+(reg-16)*4 RS6000-AIX procs The parsed and absPC arguments are for the MIPS targets only For RS6000s, the registers are stored on the stack as follows: back chain r1 @ sp + 0*4 saved TOC r2 @ sp + (reg+3)*4 augument list r3 - r10 @ sp + (reg+3)*4 ?? r11-r12 ?? gen purpose regs r13-r31 @ sp + ?? MIPS IRIX target procs Special case, Do the right thing for a crazy XR_Jump6 the normal case Special case, Do the right thing for a crazy XR_Jump6 -- the normal case, pc stored as $31 This is a special case for a leaf proc that was gotten to by a XR_Jump6 Set a local variable to the bit number of the register we are interested in. Check and make sure that the register was saved. Loop through the registers that could be saved counting the position of saved registers until the register that we are interested in is found. This is the register we are at. Check if the current register was saved. If we found the register that we are interested in we're done. For Mips, the registers are stored in the stack on an if used basis and is described as follows: return address (pc) r31 registers are in the stack highest register number saved general registers r30, r23-r16 first and then in decreasing order. N = 0 being the position of the highest register on the stack ($31), N = 1 for the next highest register saved, etc. @sp+(framesize+frameoffset-(N*4)) Common procs ΚΥ•NewlineDelimiter –"cedarcode" style™code™Kšœ Οeœ=™HK™-K™=K™,K™/K™ K™&—K˜šΟk œ˜ Kšœžœž œžœœ˜!Kšœ7žœ˜@Kšœžœ˜(Kšœ žœ ˜Kšœžœ ˜#Kšžœžœ1˜:—K˜šΟnœžœž˜Kšžœ&žœž˜:Kšžœ˜—Kšžœžœ˜%K˜Kšœ žœ˜#Kšœžœ˜Kšœžœ˜#Kšœžœ$˜:K˜Kš Ÿœžœžœžœžœ˜'K˜šŸ œžœžœžœ ˜Hšœ˜šŸ œžœ2žœžœ ˜\Kšœ˜šž˜šœž˜šœ žœ˜'K˜Kšœ ˜ Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ ˜ KšŸœ˜KšŸœ˜KšŸœ˜KšŸœ˜KšŸœ˜KšŸœ˜KšŸœ˜.KšŸœ˜/K˜——šœ˜šœ žœ˜'K˜Kšœ ˜ Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ ˜ KšŸœ˜KšŸœ˜KšŸœ˜KšŸœ˜KšŸœ˜KšŸœ˜KšŸœ˜.KšŸœ˜/K˜——Kšœ?˜?—Kšžœ ˜K˜K˜—šŸ œžœ2žœžœ ˜]Kšœ˜Kšžœžœžœžœ5˜Wšœ žœ˜'K˜Kšœ ˜ Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ ˜ KšŸœ˜KšŸœ˜KšŸœ˜KšŸœ˜KšŸœ˜KšŸœ˜KšŸœ˜,KšŸœ˜-K˜—Kšžœ ˜K˜K˜—šŸ œžœ2žœžœ ˜]Kšœ˜Kšžœžœžœžœ5˜Zšœ žœ˜'K˜Kšœ ˜ Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ ˜ KšŸœ˜KšŸœ ˜KšŸœ ˜KšŸœ˜KšŸœ˜KšŸœ˜KšŸœ˜'KšŸœ˜(K˜—Kšžœ ˜K˜—K˜K˜™Kšžœžœ ˜Kšžœžœ ˜Kšœ žœΟc˜7Kšœ žœ ˜4—K˜™KšŸœžœ ˜KšŸœžœ ˜Kšžœžœ ˜Kšžœžœ ™Kšžœžœ ˜K™—Kšœžœ˜K˜JK˜šžœžœž˜Kšœžœžœ"˜GKšœžœžœ#˜IKšœ žœ!˜GKšœ žœ!˜GKšžœ>˜E——Kšœ˜—K˜šœ™K™K™<š Ÿœžœžœ2žœžœ˜yKšœ žœ˜Kšžœ ˜&Kšœ˜—K˜š Ÿœžœžœ6žœžœ˜{Kšžœ˜ Kšœ˜K˜—š Ÿœžœžœ6žœžœ˜{Kšœ žœ˜Kšžœ ˜&Kšœ˜K˜—šŸœžœžœ/žœ žœžœžœ˜~Kšœ ˜ Kš žœ žœ žœžœ%žœ ˜XKšœIžœ˜PKšžœ˜#šΟt˜K˜——š Ÿœžœžœ/žœžœ žœ˜{Kšœ ˜ Kš žœ žœ žœžœ&žœ ˜YKšœIžœ˜PKšœ!˜!Kšžœ˜š‘˜K˜——š Ÿœžœžœ/žœ žœžœ˜šœžœ3™=Kšœ+™+Kšœ*™*Kšœ ™ Kšœ™Kšœ™Kšœ#™#Kšœ'™'K™—Kšœ˜Kšœ˜Kš žœ žœ žœžœ1žœ ˜dK˜!K˜0Kšžœ˜ Kš‘˜K˜—šŸœžœžœ˜PKšžœ˜ Kš‘˜K˜—š Ÿœžœžœžœžœ˜XKšžœ žœžœžœ ˜#Kšžœžœžœ ˜%Kšžœ˜ Kš‘˜K˜—š Ÿœžœžœžœžœ˜XKšžœ ˜Kš‘˜K˜—K˜—šœ™K™K™<š Ÿœžœžœ2žœžœ˜wKšžœ˜ Kšœ˜—K˜š Ÿœžœžœ6žœžœ˜yKšœ žœ˜Kšžœ ˜&Kšœ˜K˜—š Ÿœžœžœ6žœžœ˜yKšœ žœ˜K˜'Kšžœžœžœ˜AKšžœ#˜)Kšœ˜—K˜šŸœžœžœ/žœ žœžœžœ˜|Kšœžœ˜ Kšœ ˜ Kš žœ žœ žœžœ%žœ ˜WKšœ žœ žœžœ ˜,KšœEžœ˜LKšžœ˜#Kšœ˜—K˜š Ÿœžœžœ/žœžœ žœ˜yKšœžœ˜ Kšœ ˜ Kš žœ žœ žœžœ%žœ ˜WKšœ žœ žœžœ ˜,KšœEžœ˜LKšœ!˜!Kšžœ˜Kšœ˜—K˜š Ÿœžœžœ/žœ žœžœ˜šœžœ3™>K™Kšœ™Kšœ'™'K™Kšœ#™#K™—Kšœ˜Kšœžœ˜ Kšœ˜Kš žœ žœ žœžœ1žœ ˜cKšœ žœ žœžœ ˜,K˜!K˜,Kšžœ˜ Kšœ˜K˜—šŸœžœžœ˜NKšœ,˜,Kšœžœ˜K˜K˜GKšžœ žœžœžœ˜5K˜HKšœ?žœžœ˜PKšžœ žœžœžœ˜5Kšžœ˜ Kšœ˜K˜—š Ÿœžœžœžœžœ˜VKšžœ žœžœžœ ˜#Kšžœžœžœ ˜%Kšžœ˜ Kš‘˜K˜——šœ™K˜š Ÿ œžœžœ2žœžœ žœ˜{K˜Kšœ$žœ1˜]Kšœžœ#˜HKšœ'˜'Kšœžœ˜Kšœ žœ˜Kšœ žœ˜K˜Kšœ žœ˜K˜K˜.K˜BKšžœ žœž œ ˜&K˜K˜#K˜˜+K˜—Kšžœ ˜Kšœ˜—K˜š Ÿ œžœžœ6žœžœ žœ˜|Kšœ$žœ1˜]Kšœžœ#˜HKšœ'˜'Kšœžœ˜Kšœžœ˜Kšœ žœ˜K˜K˜.K˜BKšžœ žœž œ ˜&K˜K˜šžœ žœ˜K™6Kšžœ.˜4K˜—K˜K™Kšžœ˜ Kšœ˜K˜—š Ÿ œžœžœ6žœžœ žœ˜~K˜Kšœ$žœ1˜]Kšœžœ#˜HKšœ'˜'Kšœžœ˜Kšœžœ˜Kšœžœ˜K˜Kšœ žœ˜K˜K˜.K˜BKšžœ žœž œ ˜&K˜K˜K˜3K˜šžœ žœ˜K™6Kšžœ.˜4K˜—˜K™$—šžœ žœ˜Kšœžœ˜+Kšœ žœ˜,K˜9šžœžœ˜Kšžœ%˜+—šž˜K˜K™GKšžœ-˜3K˜—Kšœžœ'˜-—Kšœ˜K˜—š Ÿœžœžœžœžœžœ˜UK˜Kš œ žœžœ žœžœ˜&K˜Kšœžœ˜Kšœžœ˜K˜GKšœžœ˜ K˜K™LKšœž œ ˜#K˜K™0šžœžœ žœ˜6K˜K™DK™Išžœžœ žœ˜K˜K™Kšœž œ˜+K˜K™(šžœžœ žœ˜6K˜ K˜K™>šžœž˜Kšžœžœ˜—K˜K˜——Kšžœ˜K˜Kšž˜Kšœžœ˜"K˜K˜—Kšžœ˜ K˜K˜—šŸ œžœžœ/žœ žœžœžœžœ˜€K˜Kšœ$žœ1˜]Kšœžœ#˜HKšœ'˜'Kšœžœ˜Kšœžœ˜Kšœ žœ˜Kšœ žœ˜K˜Kšœ ˜ Kšœ žœ˜K˜K˜.K˜BKšžœ žœž œ˜!K˜#K˜$K˜?K˜Kš žœ žœ žœ žœžœ%žœ ˜gK˜K˜BKšœ:žœ˜AK˜Kšžœ˜#š‘˜K˜——šŸ œžœžœ/žœžœ žœžœ˜|K˜Kšœ$žœ1˜]Kšœžœ#˜HKšœ'˜'Kšœžœ˜Kšœžœ˜Kšœ žœ˜Kšœ žœ˜K˜Kšœ ˜ Kšœ žœ˜K˜K˜.K˜BKšžœ žœžœžœ˜K˜#K˜$K˜?K˜Kš žœ žœ žœ žœžœ&žœ ˜hK˜K˜BKšœ:žœ˜AKšœ!˜!K˜Kšžœ˜š‘˜K˜——šŸ œžœžœ/žœ žœžœ žœ˜ƒK˜K™H™K™\K™ͺK™[K™AK™N—K˜Kšœ$žœ1˜]Kšœžœ#˜HKšœ'˜'Kšœžœ˜Kšœžœ˜Kšœ žœ˜Kšœ žœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ žœ˜K˜K˜.K˜BKšžœ žœžœžœ ˜&K˜#K˜$K˜?K˜Kš žœ žœ žœ žœžœ1žœ ˜sK˜K˜!K˜BK˜#K˜Kšžœ˜ Kš‘˜K˜—šŸœžœžœ˜IKšžœ˜ Kš‘˜K˜—š Ÿœžœžœžœžœ˜QKšžœ žœžœžœ ˜#Kšžœžœžœ ˜%K˜Kšžœ˜Kš‘˜K˜—K˜—šœ ™ K™šŸ œžœ#žœžœ ˜KK˜-Kšœ8žœ&˜fKšžœ˜Kšœ˜—K˜šŸ œžœ#žœžœ˜FK˜-Kšœžœ+˜RKšžœ˜Kšœ˜—K˜—K˜K˜Kšžœ˜—…—;vW±