DIRECTORY DragonProcessOffsets, DragOpsCross, DragOpsCrossProcess, DragOpsCrossUtils, HandCoding, HandCodingSupport, HandCodingPseudos; GenStack: CEDAR PROGRAM IMPORTS DragOpsCrossUtils, HandCoding, HandCodingSupport, HandCodingPseudos = BEGIN OPEN DragonProcessOffsets, HandCoding, HandCodingSupport, HandCodingPseudos; StackedStatusWord: TYPE = DragOpsCross.StackedStatusWord; Word: TYPE = DragOpsCross.Word; useCST: BOOL _ FALSE; bogusPadBits: NAT _ 2; markPadBits: NAT _ 1; bogusStatus: StackedStatusWord = [ padBits: bogusPadBits, userMode: FALSE, trapsEnabled: FALSE]; bogusStatusWord: Word = LOOPHOLE[bogusStatus]; initialFrames: NAT = --5*--256; localFrameArraySize: NAT = --2*--16; framesToTransfer: NAT = localFrameArraySize/2; StackSize: NAT = 128; StackLog: NAT = BITS[[0..StackSize)]; statusMask: StackedStatusWord = [userMode: TRUE, trapsEnabled: TRUE, lBase: StackSize-1]; statusMaskWord: Word = LOOPHOLE[statusMask]; regsPerNacho: NAT = 16; RegArray: TYPE = ARRAY [0..regsPerNacho) OF Word; Nacho: TYPE = LONG POINTER TO NachoRep; NachoRep: TYPE = RECORD [ link: Nacho, -- link to previous frame lastPC: Word, -- continuation PC for this frame nRegs: Word, -- number of regs used for this frame at last save others: Nacho, -- if # NIL, then it is a pointer to an aux frame regs: RegArray -- addressable by local regs ]; linkOffset: NAT = 0; constLink: ConstSpec = const0; lastPCOffset: NAT = 1; constLastPC: ConstSpec = const1; nRegsOffset: NAT = 2; constNRegs: ConstSpec = const2; othersOffset: NAT = 3; constOthers: ConstSpec = const3; regOff: CARDINAL = 4; -- offset of DumpBlock.regs LocalAllocationRep: TYPE = RECORD [ lockPtr: GlobalAllocationPtr, -- pointer to global lock for frame allocation ptrs: LocalArray -- start of frame pointers ]; LocalArray: TYPE = ARRAY [0..localFrameArraySize] OF Nacho; GlobalAllocationPtr: TYPE = LONG POINTER TO GlobalAllocationRep; GlobalAllocationRep: TYPE = RECORD [ lock: Word, -- global spin lock for frame allocation gFree: Word, -- pointer to the next free frame in frames emergency: Word, -- pointer to the emergency limit for frames frames: GlobalArray -- space for the global pool of nachos ]; GlobalArray: TYPE = ARRAY [0..initialFrames+1] OF Nacho; StackMargin: NAT _ 17; stackUnderflowTrap: Label _ NIL; nakedSaveFrame: Label _ NIL; globalFreeNacho: Label _ NIL; globalAllocNacho: Label _ NIL; StackUnderflowTrap: PROC [stackUnderflowTrap: Label] = { area: HandCodingSupport.Area _ HandCodingSupport.GetCurrentArea[]; dispatchData: Label = GenDataLabel[area, StackSize*DragOpsCross.bytesPerWord]; WordAlign[]; { underflowEntry1: Label = GenLabelHere[]; GetEldestStatus[]; drRVSUB[topDst, topSrc, belowSrc]; ExtractField[first: 32-StackLog, bits: StackLog]; drDUP[]; SetYoungestStatus[]; drDUP[]; drSUBB[StackMargin]; ExtractField[first: 32-StackLog, bits: StackLog]; SetSPLimit[]; LRegI[hook, constLastPC]; drRETN[]; WordAlign[]; { MakeLabelGlobal["DragonStack.RestoreFrame", stackUnderflowTrap]; GetEldestPC[]; drDIS[]; }; WordAlign[]; SetLabel[stackUnderflowTrap]; MakeLabelGlobal["DragonStack.StackUnderflowTrap", stackUnderflowTrap]; LRegI[hook, constNRegs]; drLIQB[statusMaskWord]; drAND[]; drLFC[UseLabel16[underflowEntry1]]; SetEldestPC[]; Dupl2[]; drSHDR[DragOpsCrossUtils.FieldDescriptorToCard[[insert: TRUE, mask: StackLog]]]; SetEldestStatus[]; PushByteAddr[area, stackUnderflowTrap]; SetEldestPC[]; drADDQB[bogusStatusWord]; SetEldestStatus[]; ExtractField[first: 32-StackLog, bits: StackLog]; PushWordAddr[area, dispatchData]; drQRX[topAtop, belowSrc]; MoveReg[temp, hook]; MoveRegI[hook, hook, constLink]; drJSD[]; }; { singleLabel: Label = GenLabel[]; multiLabel: Label = GenLabel[]; fatalLabel: Label = GenLabel[]; SetLabel[singleLabel]; FOR i: NAT DECREASING IN [0..regsPerNacho) DO drRAI[reg1: [reg[i]], reg2: temp, disp: regOff+i]; ENDLOOP; MoveReg[topDst, temp]; FreeNacho[]; MakeLabelGlobal["DragonStack.StackUnderflowReturn", GenLabelHere[]]; drRETN[]; SetLabel[fatalLabel]; Pause[]; SetLabel[multiLabel]; FOR i: NAT IN [0..regsPerNacho) DO drRAI[reg1: [reg[i]], reg2: temp, disp: regOff+i]; ENDLOOP; LReg[temp]; MoveRegI[temp, temp, constOthers]; FreeNacho[]; drSUBB[regsPerNacho]; PushWordAddr[area, dispatchData]; drQRX[topAtop, belowSrc]; drAL[regsPerNacho]; drJSD[]; { oldPC: CARD _ HandCodingSupport.GetOutputPC[]; newPC: CARD _ dispatchData.offset; HandCodingSupport.SetOutputPC[newPC]; FOR nr: NAT DECREASING IN [0..regsPerNacho] DO OutputByteAddr[area, singleLabel, 3*nr]; ENDLOOP; FOR nr: NAT IN (regsPerNacho..regsPerNacho*4] DO OutputByteAddr[area, multiLabel, 0]; ENDLOOP; FOR nr: NAT IN (4*regsPerNacho..StackSize) DO OutputByteAddr[area, fatalLabel, 0]; ENDLOOP; HandCodingSupport.SetOutputPC[oldPC]; }; }; }; StackOverflowTrap: PROC [underflowLabel: Label] = { area: HandCodingSupport.Area _ HandCodingSupport.GetCurrentArea[]; internalSaveSetup: Label _ GenLabel[]; exitLabel: Label _ GenLabel[]; dispatchLabel: Label = GenLabel[]; dispatchData: Label = GenDataLabel[area, StackSize*DragOpsCross.bytesPerWord]; { ifuEntryLabel: Label = GenLabel[]; ProcedureEntry[ifuEntryLabel, 0, TRUE]; MakeLabelGlobal["DragonStack.IFUStackOverflowTrap", ifuEntryLabel]; FillTrap[IFUStackOverflowTrap, ifuEntryLabel]; GetEldestPC[]; drRUADD[topDst, const0, const0]; GetEldestStatus[]; AllocNacho[]; drLFC[UseLabel16[internalSaveSetup]]; SetLabel[dispatchLabel]; PushWordAddr[area, dispatchData]; drQRX[topAtop, belowSrc]; drSFC[]; SetLabel[exitLabel]; MakeLabelGlobal["DragonStack.StackOverflowExit", exitLabel]; PushByteAddr[area, underflowLabel]; SetEldestPC[]; ExtractField[first: 32-StackLog, bits: StackLog]; drDUP[]; drADDQB[bogusStatusWord]; SetEldestStatus[]; drSUBB[StackMargin]; ExtractField[first: 32-StackLog, bits: StackLog]; SetSPLimit[]; MakeLabelGlobal["DragonStack.StackOverflowReturn", GenLabelHere[]]; drRUSUB[topDst, const0, popSrc]; drRETN[]; }; { euEntryLabel: Label = GenLabel[]; ProcedureEntry[euEntryLabel, 0, TRUE]; MakeLabelGlobal["DragonStack.EUStackOverflowTrap", euEntryLabel]; FillTrap[EUStackOverflowTrap, euEntryLabel]; GetEldestPC[]; drRUADD[topDst, const0, const0]; GetEldestStatus[]; { loopLabel: Label = GenLabelHere[]; AllocNacho[]; drLFC[UseLabel16[internalSaveSetup]]; drRJNEBJ[left: topSrc, right: const0, dist: UseLabel8B[dispatchLabel]]; drRJEBJ[popSrc, topSrc, UseLabel8B[loopLabel]]; }; }; { exitEntryLabel: Label = GenLabel[]; ProcedureEntry[exitEntryLabel, 0, TRUE]; drQADD[pushA0, const0]; GetEldestStatus[]; MakeLabelGlobal["DragonStack.InstallBogusFrame", exitEntryLabel]; drJB[UseLabel8A[exitLabel]]; }; { exitEntryLabel: Label = GenLabel[]; ProcedureEntry[exitEntryLabel, 0]; MakeLabelGlobal["DragonStack.RemoveBogusFrame", exitEntryLabel]; GetEldestPC[]; drRUADD[topDst, const0, const0]; GetEldestStatus[]; ProcedureExit[1]; }; { eldestStatus: RegSpec = reg0; localHook: RegSpec = reg1; localStatus: RegSpec = reg2; ProcedureEntry[internalSaveSetup, 2]; LReg[hook]; drPSB[linkOffset]; PReg[hook]; PReg[temp]; LReg[eldestStatus]; ExtractField[first: 32-StackLog, bits: StackLog]; SetYoungestStatus[]; GetEldestPC[]; drSRIn[localHook, lastPCOffset]; GetEldestStatus[]; drRVSUB[c: pushDst, a: localStatus, b: eldestStatus]; MoveReg[eldestStatus, localStatus]; drSHDR[DragOpsCrossUtils.FieldDescriptorToCard[[insert: TRUE, mask: StackLog]]]; drWRI[localStatus, localHook, nRegsOffset]; drSHDR[DragOpsCrossUtils.FieldDescriptorToCard[[mask: StackLog]]]; ProcedureExit[2]; }; { singleLabel: Label = GenLabel[]; multiLabel: Label = GenLabel[]; fatalLabel: Label = GenLabel[]; shortLabel: Label = GenLabel[]; SetLabel[nakedSaveFrame]; MakeLabelGlobal["DragonStack.NakedSaveFrame", nakedSaveFrame]; AllocNacho[]; drLFC[UseLabel16[internalSaveSetup]]; PushWordAddr[area, dispatchData]; drQRX[topAtop, belowSrc]; drJSD[]; SetLabel[singleLabel]; FOR i: NAT DECREASING IN [0..regsPerNacho) DO drWAI[reg1: [reg[i]], reg2: temp, disp: regOff+i]; ENDLOOP; drDIS[]; drRETN[]; SetLabel[fatalLabel]; Pause[]; SetLabel[multiLabel]; FOR i: NAT IN [0..regsPerNacho) DO drWAI[reg1: [reg[i]], reg2: temp, disp: regOff+i]; ENDLOOP; LReg[temp]; AllocNacho[]; -- EU stack high water mark: 8 regs PReg[temp]; drWSB[othersOffset]; drSUBB[regsPerNacho]; PushWordAddr[area, dispatchData]; drQRX[topAtop, belowSrc]; drAL[regsPerNacho]; drJSD[]; { oldPC: CARD _ HandCodingSupport.GetOutputPC[]; HandCodingSupport.SetOutputPC[dispatchData.offset]; FOR nr: NAT DECREASING IN [0..regsPerNacho] DO OutputByteAddr[area, singleLabel, 3*nr]; ENDLOOP; FOR nr: NAT IN (regsPerNacho..regsPerNacho*4] DO OutputByteAddr[area, multiLabel, 0]; ENDLOOP; FOR nr: NAT IN (4*regsPerNacho..StackSize) DO OutputByteAddr[area, fatalLabel, 0]; ENDLOOP; HandCodingSupport.SetOutputPC[oldPC]; }; }; }; AllocNacho: PROC = { exitLabel: Label = GenLabel[]; enterLabel: Label = GenLabelHere[]; LRegI[free, const0]; drLC0[]; drRJNEBJ[left: topSrc, right: belowSrc, dist: UseLabel8B[exitLabel]]; drLFC[UseLabel16[globalAllocNacho]]; drJB[UseLabel8A[enterLabel]]; SetLabel[exitLabel]; AddReg[free, const1]; drPSB[othersOffset]; }; GlobalAllocNacho: PROC = { exitLabel: Label = GenLabel[]; globalAllocNacho _ GenLabelHere[]; MakeLabelGlobal["DragonStack.GlobalAllocNacho", globalAllocNacho]; drRRX[c: belowDst, a: base, b: popSrc]; LReg[process]; IF useCST THEN { drLC0[]; {retryLabel: Label = GenLabelHere[]; drCST[0]; drRJNEB[left: popSrc, right: belowSrc, dist: UseLabel8B[retryLabel]]; drAS[256-2]; }; } ELSE { drPSB[0]; }; drRSB[1]; drLIB[framesToTransfer]; drRVSUB[free, free, popSrc]; FOR i: NAT IN [0..framesToTransfer) DO drRSB[i]; LReg[free]; drWB[i]; ENDLOOP; drADDB[framesToTransfer]; drPSB[1]; drLC0[]; drWSB[0]; drRETN[]; }; FreeNacho: PROC [] = { exitLabel: Label = GenLabel[]; enterLabel: Label = GenLabelHere[]; drRVSUB[c: pushDst, a: free, b: const1]; drRJNEBJ[left: topSrc, right: base, dist: UseLabel8B[exitLabel]]; drLFC[UseLabel16[globalFreeNacho]]; SetLabel[exitLabel]; PReg[free]; drWB[0]; }; GlobalFreeNacho: PROC = { lockReg: RegSpec = reg0; globalFreeNacho _ GenLabel[]; ProcedureEntry[globalFreeNacho, 1]; MakeLabelGlobal["DragonStack.GlobalFreeNacho", globalFreeNacho]; drRB[0]; LReg[process]; IF useCST THEN { drLC0[]; {retryLabel: Label = GenLabelHere[]; drCST[0]; drRJNEB[left: popSrc, right: belowSrc, dist: UseLabel8B[retryLabel]]; drAS[256-2]; } } ELSE { drPSB[0]; }; drRSB[1]; drLIB[framesToTransfer]; { gFreeReg: RegSpec = reg1; dataReg: RegSpec = reg2; drRVSUB[gFreeReg, gFreeReg, topSrc]; FOR i: NAT IN [0..framesToTransfer) DO drRAI[reg1: dataReg, reg2: free, disp: i]; drWRI[reg1: dataReg, reg2: gFreeReg, disp: i]; ENDLOOP; drDIS[]; }; drPSB[1]; drLC0[]; drWSB[0]; drLIB[framesToTransfer-1]; drRVADD[c: topDst, a: topSrc, b: free]; ProcedureExit[1]; }; GenFramesInit: PROC = { allocator: Label = HandCodingPseudos.GetGlobalLabel["Basics.AllocVector"]; rZero: RegSpec = reg0; rLB: RegSpec = reg1; -- base of local frame table rSB: RegSpec = reg2; -- base of shared frame table tPtr: RegSpec = reg3; -- pointer to next slot in global table drLC0[]; -- init rZero drLIB[SIZE[LocalAllocationRep]/2]; drLFC[UseLabel16[allocator]]; drROR[base, topSrc, const0]; drLIDB[framesToTransfer*2+initialFrames]; drLFC[UseLabel16[allocator]]; drWRI[rSB, rLB, 0]; drWRI[rZero, rSB, 0]; drWRI[rZero, rSB, 2]; drRVADD[pushDst, rSB, const3]; drWRI[tPtr, rSB, 1]; { loopLabel: Label = GenLabelHere[]; drLIB[20]; drLFC[UseLabel16[allocator]]; drSRIn[tPtr, 0]; drRVADD[tPtr, tPtr, const1]; drLRn[tPtr]; drSUBDB[initialFrames]; drRJNEBJ[left: popSrc, right: rSB, dist: UseLabel8B[loopLabel]]; }; drLIB[17]; drRADD[free, base, popSrc]; drWRI[rZero, rLB, 17]; drAS[256-4]; }; All: PROC = { area: HandCodingSupport.Area _ HandCodingSupport.GetCurrentArea[]; continueLabel: Label = GenLabel[]; toUserLabel: Label = GenLabel[]; underflowLabel: Label = GenLabel[]; nakedSaveFrame _ GenLabel[]; GenFramesInit[]; drLFC[UseLabel16[continueLabel]]; GlobalAllocNacho[]; GlobalFreeNacho[]; StackUnderflowTrap[underflowLabel]; StackOverflowTrap[underflowLabel]; SetLabel[continueLabel]; PushWordAddr[area, underflowLabel]; SetYoungestPC[]; GetYoungestStatus[]; drLIQB[LOOPHOLE[DragOpsCross.StackedStatusWord[trapsEnabled: TRUE]]]; drOR[]; drDFC[UseLabel32[HandCodingPseudos.GetGlobalLabel["Basics.SetStatus"]]]; drLFC[UseLabel16[toUserLabel]]; Pause[]; SetLabel[toUserLabel]; drLC0[]; MakeLabelGlobal["DragonStack.ExitToUser", toUserLabel]; }; FillTrap: PROC [tx: DragOpsCross.TrapIndex, dest: Label] = { area: HandCodingSupport.Area = HandCodingSupport.GetCurrentArea[]; oldPC: CARD = HandCodingSupport.GetOutputPC[area]; SetOutputPC[DragOpsCrossUtils.TrapIndexToBytePC[tx]]; drJDB[UseLabel16[dest]]; HandCodingSupport.SetOutputPC[oldPC]; }; Dupl2: PROC = { drQOR[pushA0, belowSrc]; drQOR[pushA0, belowSrc]; }; END. OXGenStack.mesa Copyright ำ 1985, 1986, 1987 by Xerox Corporation. All rights reserved. Russ Atkinson (RRA) May 7, 1987 9:08:01 pm PDT GenStack.All[] generates code for stack saving and restoring. For the current SoftCard the CST instruction does not really work. We can avoid it on single-processor systems by fancy coding. Size parameters (the only difference between GenStack.mesa and GenBigStack.mesa) Initial frames to allocate. # of frames in localArray Number of frames to transfer between local and global frame pools. (this # should be explored) Note: StackSize = 2^StackLog The mask for the "interesting" bits in the stacked status word. type of holder for frames per processor, limit is quite arbitrary (need not be fixed) NIL is stored at the very end to make test for empty easy type of holder for frames, limit is quite arbitrary (need not be fixed) NIL is stored at the very end to make test for empty easy Number of words in EU stack to reserve to handle stack overflow. Will this really be enough? This has hardware implications as well (see Don Curry) Global labels Entry point for handling stack underflow (aka DragonStack.StackUnderflowTrap). Also used as the PC for the bogus frame. Entry point for the internal routine (aka DragonStack.NakedSaveFrame) that saves the eldest frame. Entry point for the internal routine that frees up a nacho to the central pool. Entry point for the internal routine that allocates up a nacho to the central pool. This code is used both to handle stack underflow and to restore a frame. At the underflow point we have no frames on the IFU stack, provided that we are in kernel mode. No code in user mode can fake this entry in kernel mode, so we don't have to check. At this point the stack may contain return values from the procedure that just returned to us, so the words from [L] through [S] must be preserved. This is an internal routine, which is the way we dynamically set L. At the time this routine is called, the eldest frame in the IFU stack has the value for L that limits the restored register range. This can occur because the eldest frame is the only frame (the case for stack underflow) or because the eldest frame is a real frame (when we are calling NakedRestoreEldest). The idea is to calculate the new value for the L of the frame to restore, and to set L to that value on return from the call to underflowEntry1. This routine also sets the new stack limit. We assume that the hook register points at a valid nacho! [S] = nRegs field of the restored frame (with status bits) [S] = [S-1] = the new value for L, properly masked (no status bits) [S-2] = nRegs field of the restored frame Set the new youngest L, which will set L on return. [S] = the new value for L (no status bits) [S-1] holds the nRegs field of the restored frame Calculate & set the new stack limit. [S] = [S-1] = new L (properly masked) [S-2] = nRegs field of the restored frame [S] = return PC for restored frame [S-1] = new L (masked) [S-2] = nRegs field of the restored frame. This entry is used to restore a frame. We always strip off the bogus frame, then enter the underflow handler, which restores the eldest frame and puts on a bogus frame. This entry is useful in trap handlers (like page fault) that want to make sure that a certain number of frames have been restored before returning from the trap. On entry we assume that there is a valid hook, and that we are in kernel mode with traps disabled. At the point of call there must be at least 4 available frames on the IFU stack. {max: caller frame, restored frame, bogus frame, FreeNacho frame} At the point of call there must be at least 7 available registers on the EU stack. (plus the registers needed to restore the frame) {max: 5 = 2 at use of FreeNacho, 3 for FreeNacho} Flush the bogus frame (puts eldest PC on EU stack) Restore the EU stack to its entry point This point is used in two ways: the PC of the bogus frame points here, and this is the proper point to enter when we want to restore the eldest frame. We assume that there is a valid hook, and that we are in kernel mode with traps disabled. [S] = nRegs field of the restored frame [S] = nRegs field & status bits for the restored frame This call sets L and [S] to the new L. [S] = return PC for restored frame [S-1] = new L (no status bits) [S-2] = nRegs field of the restored frame. Creates the frame that will receive the restored information & sets the return PC. [S] = new L (no status bits) [S-1] = nRegs field of the restored frame (with status bits, masked) Duplicate top two words on stack [S] = new L & status bits for the restored frame [S-1] = new L (no status bits) [S-2] = nRegs field of the restored frame Sets the proper status for the restored frame. Create the bogus frame [S] = new L (no status bits) [S-1] = nRegs field of the restored frame Create the bogus frame & set the PC Set the bogus bits to mark the bogus frame the bogus frame is now correct [S] = #regs (no status bits) for the restored frame At this point L is correctly set as well to the base of the frame to be restored. The hook register is pointing at the nacho chain to be used to restore the frame. [S] = dispatch PC [S-1] = #regs (no status bits) temp has the nacho to chase hook now has the next frame's head nacho Dispatch to restore the proper # of regs; on arrival: [S] = #regs (no status bits) This section contains the various routines for restoring varying numbers of registers and the table for dispatching to those routines. At this point we execute a varying number of RAIs to restore the regs [S] = #regs (no status bits) free the nacho pointed to by temp (& flush the # regs) Now S is back at its initial value, hook is properly set Return from kernel, no change to S. We should never have this happen! Control only gets here for a truly smashed nacho! Now do regsPerNacho reads into the stack registers from the nacho. push the nacho to free; point hook at the next nacho in the chain [S] = nextNacho, [S-1] = #regs free the nacho pointed to by [S] [S] = # regs EU stack high water mark: 7 registers adjust #regs [S] = #regs left adjust L to account for the registers restored Dispatch to restore the proper # of regs [S] = # of regs to save Generate the dispatch table Note: we assume that there is sufficient reserve space on the EU stack and the IFU stack to complete the saving of one frame. Traps are automatically disabled on entry to this routine, and must be reenabled on the way out. DragonStack.IFUStackOverflowTrap: This part is for IFU stack overflow. Note that we are NOT assured of a non-empty frame on the IFU stack. We get no arguments. discard the bogus frame [S] holds the PC for the bogus frame Capture the carry bit [S] holds the carry bit [S] holds the eldest status, [S-1] holds the carry bit Allocate the next save block to use ([S] = newBlock, [S-1] = eldest status) Init the new save block. Its address is left in hook (and in temp). L gets set to the L for the frame we want to save. [S] = #regs, [S-1] = eldest status We get here to do the dispatch to the appropriate register saving code. Fetch the dispatch address Dispatch to restore the proper # of regs On return: [S] = eldest status, [S-1] = sampled carry bit This is the common exit stuff for the stack overflow handlers. The idea is to keep the stack limit register set with enough reserve space to be able to prevent wraparound when we execute code where traps are disabled. Also, the saved L for the bogus frame must be consistent with the number of registers in the youngest saved frame (referenced by hook). At this point: [S] holds the eldest status, [S-1] holds the carry bit Install the new bogus frame & set its PC (creating a new eldest frame) Calculate & set the L for the bogus frame. [S] still holds that L. Calculate & set the new stack limit. S is now back to its original depth. [S] holds the carry bit Set the carry bit that was saved on the overflow At this point we go back to the code that overflowed. DragonStack.EUStackOverflowTrap: If we save a frame, but don't get any more space in the EU stack, then we have to keep saving frames until a frame is saved that has at least one EU register saved with it. We know that there is a non-empty frame on the register stack, otherwise why would we get an EU stack overflow? We get no arguments. discard the bogus frame [S] holds the PC for the bogus frame Capture the carry bit [S] holds the carry bit [S] holds the eldest status, [S-1] holds the carry bit [S] = eldest status Allocate the next save block to use [S] = newBlock, [S-1] = eldest status Init the new save block. Its address is left in hook (and in temp). L gets set to the L for the frame we want to save. [S] = #regs, [S-1] = eldest status If we have registers to save, then go do them. Discard [S] and go loop (unconditionally, really). [S] = eldest status InstallBogusFrame is useful for external callers of DragonStack.NakedSaveFrame. Once the necessary frames have been saved, the bogus frame can be created using this routine. Capture (& clear) the carry bit push the eldest status [S] holds the eldest status, [S-1] holds the carry bit RemoveBogusFrame is useful for external callers of DragonStack.NakedSaveFrame. Before NakedSaveFrame can be called, the bogus frame must be removed, and the carry bit & current eldest status must be pushed. discard the bogus frame [S] holds the PC for the bogus frame Capture (& clear) the carry bit [S] holds the carry bit [S] holds the eldest status, [S-1] holds the carry bit return the new eldest status & carry bit This is an internal routine that we use to setup stuff (especially L) prior to saving the frame. It needs to be a procedure because we have to set L (sigh). before entry: hook = frame chain [S] = newBlock, [S-1] = eldest status after exit: L = L of frame to save [S] = #regs, [S-1] = eldest status At call requires at least 1 IFU frame & 4 EU stack regs. Make our local L good for a few locals, we get one sent to us on the stack store the hook into the new dump block [S] = localHook = newBlock; (localHook+linkOffset)^ = hook store the new dump block addr into hook and temp [S] = localHook = hook = temp = newBlock Get the L for the frame we wish to save, put it into localStatus and youngest L [S] = localStatus = L of frame to save; [S-1] = localHook, [S-2] = eldest status (note that the status bits must be clear in [S]) Store the continuation PC and remove the eldest PC from the ifuStack. ((localHook+lastPCOffset)^ = PC of frame to save) Sample & save the new eldest status [S] (aka localStatus) = eldest status (the new version) [S-1] = localHook [S-2] = eldest status (the old version) Calculate the number of regs in this frame [S] = # of regs to save (with garbage in top bits) [S-1] (aka localStatus) = status [S-2] = localHook [S-3] = eldest status [S] = # of regs to save (with garbage in top bits) [S-1] (aka localStatus) = eldest status (the new version) [S-2] = localHook [S-3] = eldest status (the new version) insert # of regs into the saved status word (aka localStatus) [S] (aka localStatus) = #regs (+ status bits) [S-1] (aka localHook) [S-2] = eldest status Save #regs into the save block & into eldestStatus (localHook+nRegsOffset)^ _ localStatus [S] = #regs, [S-1] = localHook, [S-2] = eldest status [S] = #regs, [S-1] = eldest status return the values [S] = #regs, [S-1] = eldest status DragonStack.NakedSaveFrame: This is the entry point for saving the eldest stack frame in the IFU stack. We assume that the bogus frame has been removed, and that we are in kernel mode with traps disabled. The top two words must be as they were set by RemoveBogusFrame. At entry: [S] = the sampled eldest status [S-1] = the sampled carry bit At the point of call there must be at least 2 available frames on the IFU stack. {max: caller frame, internalSaveSetup/AllocNacho frame} At the point of call there must be at least 6 available registers on the EU stack. {max: 6 = 2 at first use of AllocNacho + 4 for AllocNacho} At exit: [S] = the sampled eldest status [S-1] = the sampled carry bit Allocate the next save block to use [S] = newBlock, [S-1] = eldest status, [S-2] = sampled carry bit Init the new save block. Its address is left in hook. L gets set to the L for the frame we want to save. [S] = #regs, [S-1] = eldest status, [S-2] = sampled carry bit Fetch the dispatch address Dispatch to save the proper # of regs after JSD: [S] = #regs, [S-1] = eldest status, [S-2] = sampled carry bit Discard #regs This return does not enable traps, and does not disturb S, but does restore L, which was previously clobbered. [S] = new eldest status, [S-1] = sampled carry bit For now we just crash. Eventually we should be more robust, saving away the regs, then giving the offending process a "stack fault". At this point we have regsPerNacho more EU regs available than when we started, since we just saved that many. The EU high-water mark must therefore occur before registers are saved. Push the address of the completed save block [S] = temp = oldBlock; [S-1] = #regs; [S-2] = eldest status; [S-3] = sampled carry Allocate the next save block to use (it gets left on the stack) [S] = newBlock; [S-1] = temp = oldBlock; [S-2] = #regs [S-3] = eldest status; [S-4] = sampled carry set temp to the new dump block addr also store the new block addr into the old block (temp = (temp'+othersOffset)^ = newBlock; [S] = #regs) adjust #regs [S] = #regs, [S-1] = eldest status Fetch the dispatch address adjust L for the restored regs Dispatch to save the proper # of regs [S] = #regs, [S-1] = eldest status, [S-2] = sampled carry bit Generate the dispatch table Allocate a nacho using the local array of nachos, defaulting to the global array of nachos if not immediately successful. The address of the allocated nacho is left on the stack. There is no provision for failure from the global array at this time. Requires at least 1 available IFU frame & 4 available EU stack registers. Push the next nacho address. If none in our local cache, a 0 will be pushed. push 0 for comparison (this cycle is free due to the preceding fetch) Jump if we got the nacho; leave 0 on stack if not. Predict success. Call to do this the expensive way (S_S-2) Go to retry the allocation from the start (stack is back to ground level). This is the successful exit point. The allocated nacho is on the stack. Adjust the free pointer, since we got a nacho [S] = 0, [S-1] = new nacho always clears the others field on allocation [S] = new nacho Replenish the local nacho array from the global nacho array. There is no provision for failure from the global array at this time. At entry, [S] = [S-1] = 0, at exit S_S-2, but the local array has nachos. AllocNacho is the only caller of this routine! Replace [S-1] (was 0) with @lock; also S_S-1 [S] = @lock Push the desired new value for the lock field [S] = process; [S-1] = @lock Push the assumed old value of the lock field (i.e. free) [S] = 0; [S-1] = process; [S-2] = @lock Try for the global lock for the nacho allocator. We store our process ID into the lock if the lock was free, and return the sampled value of the lock at the start of the instruction in any case. [S] = sample; [S-1] = 0; [S-2] = process; [S-3] = @lock Jump back to retry if we did not get it (predict success), and pop the sampled value. If this loop causes us to hold the bus too often, we can always insert a spin loop that does not use CST. [S] = 0; [S-1] = process; [S-2] = @lock On success, flush the old & new values, since we don't need them. [S] = @lock No locking is OK for the single-processor SoftCard. Get the global free pointer (gFree) on the stack @base.gFree = @base.lock + 1 [S] = gFree; [S-1] = @lock Adjust the free pointer to accomodate the next transfers. EU stack high water mark: 8 regs [S] = free, [S-1] = word from nacho, [S-2] = gFree, [S-3] = @lock (free+i)^ _ (gFree+i)^ adjust gFree for the number of nachos allocated [S] = new gFree; [S-1] = @lock Write gFree back, leaving the addr of the lock on the stack. [S] = @lock Clear out the lock, which lets other processors get their chance. stack is now at original level Free a nacho (in [S]) using the local array of nachos, defaulting to the global array of nachos if not immediately successful. There is no provision for failure from the global array at this time. At usage, requires at least 1 available IFU frame (for globalFreeNacho call). At usage, requires at least 3 available EU stack registers (not including argument). [S] = free-1; [S-1] = nacho to free Determine if free-1 will collide with base, jump if not. Free-1 is left on the stack for use in storing away the freed frame. Predict success. In the hard case we call a routine to munch on the global table. EU stack high water mark: 3 regs (not including argument) At this point the address of the slot to hold the nacho is on the stack. First we store that address to free, then we load the nacho to be freed and store it into the slot. (free _ [S])^ _ [S-1]; S_S-2 This routine ensures that there is enough room in the processor free nacho cache to free up a nacho, and we know that the nacho cache is currently full of free nachos. We have already tested for the fast case, and we call this when [S] = base. At exit, [S] = new free. At entry: [S] = base = free - 1 At the call, we need at least 1 available IFU frame & 2 available EU stack registers. (does not include the argument register) At entry: [S] = new free = old free + framesToTransfer - 1 Put the addr of the lock on the stack. We get to have a pop for free here. [S] = @lock Push the desired new value for the lock field [S] = process; [S-1] = @lock Push the assumed old value of the lock field (i.e. free) [S] = 0; [S-1] = process; [S-2] = @lock EU stack high water mark: 2 registers (not including argument) Try for the global lock for the nacho allocator. We store our process ID into the lock if the lock was free, and return the sampled value of the lock at the start of the instruction in any case. [S] = sample; [S-1] = 0; [S-2] = process; [S-3] = @lock Jump back to retry if we did not get it (predict success), and pop the sampled value. If this loop causes us to hold the bus too often, we can always insert a spin loop that does not use CST. [S] = 0; [S-1] = process; [S-2] = @lock On success, flush the old & new values, since we don't need them. The lock address is left on the stack. [S] = @lock No locking is OK for the single-processor SoftCard. get the global free pointer (gFree) on the stack (leaving the lock addr under it) [S] = gFree; [S-1] = @lock [S] = framesToTransfer; [S-1] = gFree; [S-2] = @lock gFree _ gFree - framesToTransfer [S] = framesToTransfer; [S-1] = new gFree; [S-2] = @lock (gFree+i)^ _ (free+i)^ EU stack high water mark: 2 registers (not including argument) [S] = gFree; [S-1] = @lock write gFree back, leaving the lock addr on the stack [S] = @lock clear out the lock, which lets other processors get theirs stack is now at original level [S] = free + (framesToTransfer-1) This is the successful exit point. There is room between free and base. [S] = new free ptr allocate the per processor frame base (LocalAllocationRep) also put that frame into base and leave in rLB allocate space for the shared frame base (GlobalAllocationRep) also store that address into rLB.lockPtr and leave it in rSB clear out the shared frame base init rSB.gFree and tPtr Allocate the new frames from the basic system allocator. Each frame gets stuck into the shared frame base (GlobalAllocationRep) in a new slot. Allocate and store the new frame stop when tPtr gets far enough from the base now make free point at the next local slot to fill also, clear out that slot Finally, drop the junk from the stack Entry point for the internal routine (aka DragonStack.NakedSaveFrame) that saves the eldest frame. Order of generation is important! Setup the bogus frame & enable traps (to detect stack overflow). This is to ensure that there is a non-bogus frame at the bottom of the stack, which is more-or-less required for stack save/restore. Someday we may want to remove this restriction. RRA: April 21, 1987 Duplicates the top two words on the stack before: [S] = X, [S-1] = Y after: [S] = X, [S-1] = Y; [S-2] = X; [S-3] = Y ส ˜codešœ ™ KšœH™HK™.—˜šœ=™=K™šฯk ˜ Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜———headšœ œ˜KšœD˜KKšœœœH˜TK˜K–20 sp tabStopsšœœ"˜9K–20 sp tabStopsšœœ˜K–20 sp tabStops˜–20 sp tabStopsšœœœ˜K–20 sp tabStops™€—K–20 sp tabStops˜K–20 sp tabStopsšœœ˜K–20 sp tabStopsšœ œ˜K–20 sp tabStops˜–20 sp tabStopsšœDœœ˜`K–20 sp tabStops˜—–20 sp tabStopsšœœ˜.K–20 sp tabStops˜—–20 sp tabStops™PJ–20 sp tabStops˜–20 sp tabStopsšœœ ˜J–20 sp tabStopsšœ™—–20 sp tabStopsšœœ ˜$J–20 sp tabStopsšœ™—šœœ˜.šœB™BK™——J–20 sp tabStops™—–20 sp tabStopsšœ œ˜K–20 sp tabStopsšœ œœ˜%K–20 sp tabStopsšœ™—K–20 sp tabStops˜šœ+œœ˜YKšœœ ˜,Kšœ?™?K˜—K–20 sp tabStopsšœœ˜K–20 sp tabStopsšœ œœœ˜1K–20 sp tabStopsš œœœœœ ˜'–36 sp tabStopsšœ œœ˜K–36 sp tabStopsšœ ฯc˜&K–36 sp tabStopsšœž!˜/K–36 sp tabStopsšœ ž2˜?K–36 sp tabStopsšœž1˜@K–36 sp tabStopsšœž˜+K–36 sp tabStopsšœ˜–36 sp tabStopsšœ œ˜K–36 sp tabStopsšœ˜—–36 sp tabStopsšœœ˜K–36 sp tabStopsšœ ˜ —–36 sp tabStopsšœ œ˜K–36 sp tabStopsšœ˜—–36 sp tabStopsšœœ˜K–36 sp tabStopsšœ ˜ —K–36 sp tabStopsšœœž˜1—K˜–36 sp tabStopsšœœœ˜#K–36 sp tabStopsšœž.˜LK–36 sp tabStopsšœž˜+K–36 sp tabStopsšœ˜–24 sp tabStopsšœ œœœ˜;K–24 sp tabStopsšœU™UK–24 sp tabStops™9——K–24 sp tabStops˜K–24 sp tabStopsš œœœœœ˜@–24 sp tabStopsšœœœ˜$K–24 sp tabStopsšœ ž(˜4K–24 sp tabStopsšœ ž+˜8K–24 sp tabStopsšœž,˜=K–24 sp tabStopsšœž&˜:K–24 sp tabStopsšœ˜–24 sp tabStopsšœ œœœ˜8K–24 sp tabStopsšœG™GK–24 sp tabStops™9——K˜šœ œ˜Kšœ•™•—K–24 sp tabStops™–24 sp tabStopsšœ ™ K–24 sp tabStops˜–24 sp tabStopsšœœ˜ K–24 sp tabStopsšœx™x—–24 sp tabStopsšœœ˜K–24 sp tabStopsšœb™b—–24 sp tabStopsšœœ˜K–24 sp tabStopsšœO™O—–24 sp tabStopsšœœ˜K–24 sp tabStopsšœS™S——K–24 sp tabStops™šฯnœœ ˜8K™HK˜KšœB˜BKšœN˜NK˜K˜ šœ˜Kšœษ™ษK™šœ(˜(K˜šœ๑™๑Kšœ:™:—K™Kšฯbœ˜Kšœ"˜"Kšœ1˜1˜K™CKšœ)™)—š œ˜Kšœ3™3K™*Kšœ1™1—K˜Kšœ$™$K˜˜K™%Kšœ)™)—Kšœ˜Kšœ1˜1Kš  œ˜ K˜šœ˜K™"K™Kšœ*™*—K˜ K˜—K˜ ˜šœ œ˜@Kšœฬ™ฬK™b™PKšœA™A—™RK™0Kšœ1™1—K™—š  œ˜K™2—˜K™'—K˜K™—K˜ K˜Kšœ˜šœ œ˜FK˜K™๑K™šœ˜Kšœ'™'—K˜Kšœ˜˜Kšœ6™6K™—˜#šœ&™&K™"K™Kšœ*™*——K˜š  œ˜K™RK™KšœD™DK˜—šœ˜K™ —šœ8œ˜PK™0K™Kšœ)™)—š œ˜K™.—K˜™K™Kšœ)™)—K™Kšœ'˜'š  œ˜K™#—šœ˜K™*—š œ˜K™K™—šœ1˜1Kšœ3™3K™—Kšœค™คK™Kšœ!˜!šœ˜K™Kšœ™—˜K™—šœ ˜ K™(—˜šœ5™5Kšœ™——K˜K˜——˜K™†Kšœ ˜ K˜šœ˜K™—šœ˜™EKšœ™—š œœ œœ˜-Kšœ2˜2Kšœ˜—šœ#˜#Kšœ6™6—K˜šœ  œ˜DK™8K™—šœ ˜ Kšœ#™#——K˜šœ˜K™TK˜K˜—˜Kšžœ ž)œž™Bšœœœ˜"Kšœ2˜2Kšœ˜K˜—šœ.˜.šœA™AKšœ™——˜ šœ ™ šœ ™ K™%———šœ˜šœ ™ Kšœ™——Kšœ!˜!Kšœ˜šœ˜Kšœ.™.—˜™(K™——K˜—˜Kšœ™Kšœœ#˜.Kšœœ˜"Kšœ%˜%š œœ œœ˜.Kšœ(˜(Kšœ˜—šœœœ ˜0Kšœ$˜$Kšœ˜—šœœœ˜-Kšœ$˜$Kšœ˜—Kšœ%˜%K˜K˜—K˜—K˜K˜—šŸœœ˜3Kšœ฿™฿KšœB˜BKšœ&˜&K˜Kšœ"˜"KšœN˜NK˜˜Kš  œk™‹Kšœ"˜"šœ!œ˜'Kšœ™—K˜Kšœ  œ˜CKšœ.˜.K˜š  œ˜šœ™Kšœœ™$——šœ ˜ ™K™——š œ˜K™6K™—˜ šœ#™#Kšœ'™'——K˜šœ%˜%šœD™DKšœ2™2Kšœ"™"——K˜šœ˜K™GKšœ!˜!šœ˜K™—˜™(Kšœ9™9——K™—Kšœใ™ใK˜šœ œ˜K˜—˜ šœ#™#Kšœ@™@——K˜šœ%˜%šœj™jKšœ=™=——K˜K˜!šœ˜K™—˜™%KšœH™H——K˜˜š œœ œœ˜-Kšœ2˜2Kšœ˜—K˜˜K™ —šœ ˜ Kšœn™nKšœ2™2—K™—˜K™…K˜Kšœ˜K™—˜šœœœ˜"Kšœ2˜2šœ˜Kšœท™ท——K˜šœ ˜ šœ,™,KšœR™R——šœž#˜1šœ?™?Kšœ6™6Kšœ,™,——šœ!˜!K™#™0Kšœ6™6——šœ˜™ Kšœ"™"——Kšœ!˜!šœ˜K™—šœ˜K™—˜™%Kšœ=™=——K™—˜Kšœ™Kšœœ#˜.Kšœ3˜3š œœ œœ˜.Kšœ(˜(Kšœ˜—šœœœ ˜0Kšœ$˜$Kšœ˜—šœœœ˜-Kšœ$˜$Kšœ˜—Kšœ%˜%K˜—K˜—Kšœ˜K˜—šŸ œœ˜Kšœต E™๚K˜K™IK˜Kšœ˜Kšœ#˜#K˜šœ˜KšœM™M—K˜šœ˜KšœE™E—K˜šœE˜EKšœD™DK˜˜$Kšœ)™)—šœ˜KšœJ™J——K˜˜KšœH™H—K˜šœ˜šœ-™-K™——šœ˜šœ,™,K™——K˜K˜—šŸœœ˜Kšœ> E™ƒK™yK˜Kšœ˜Kšœ"˜"Kšœ œ˜BK˜˜'™,K™ ——šœ˜šœ-™-K™——šœ˜ šœ˜˜šœ8™8K™'——K˜˜$šœ ˜ šœGœz™รK™7——šœE˜Ešœผœ™ภK™'——˜ šœA™AK™ ——K˜—K˜—šœ˜Kšœœ#™3K˜ K˜——K˜˜ šœ0™0Kšœ™K™——šœ6˜6Kšœ9™9—šœœœ˜&šœ˜šž ™ K™A——šœ˜K™—Kšœ˜—šœ˜šœ/™/K™——˜ šœ<™—K™——˜$šœ ˜ šœGœz™รK™7——šœE˜Ešœผœ™ภK™'——˜ šœi™iK™ ——K˜—K˜—šœ˜K™3K˜ K˜K˜——˜ šœQ™QK™——šœ˜Kšœ4™4—˜K˜K˜˜$Kšœ ™ Kšœ8™8—šœœœ˜&Kšœ*˜*šœ.˜.šœ™K™>——Kšœ˜—˜K™—K˜—˜ šœ4™4K™ ——˜šœ:™:K™——šœC˜CKšœ!™!—K˜KšœH™H˜K™—K˜K˜—šŸ œœ˜KšœJ˜JK˜Kšœž˜2Kšœž˜3Kšœž'˜>K˜Kšœ ž ˜Kšœœ˜"šœ;˜;Kšœ:™:Kšœ.™.—K˜Kšœ)˜)Kšœ˜šœ˜Kšœ>™>Kšœ<™<—K˜šœ,˜,Kšœ™—K˜šœ4˜4Kšœ™—K˜˜Kšœ‘™‘K˜"šœ;˜;Kšœ ™ —Kšœ˜Kšœ%˜%šœ@˜@Kšœ,™,—K˜K˜—šœ?˜?Kšœ2™2K™—K˜˜ Kšœ%™%—K˜K˜—šŸœœ˜ K˜KšœB˜BK˜"K˜ K˜#K˜–24 sp tabStopsšœ˜K–24 sp tabStopsšœb™b—Kšœ˜K˜K˜!K˜Kšœ!™!K˜K˜Kšœ#˜#Kšœ"˜"K˜K˜K˜Kšœ@™@K˜Kšœ#˜#Kš  œ˜Kš œ˜Kšœœ.œ˜EK˜KšœH˜HK˜šœ˜K™ส—K˜K˜K˜K˜K˜K˜Kšœ œ˜7K˜K˜—šŸœœ.˜