GenDebugger.mesa
Copyright © 1987 by Xerox Corporation. All rights reserved.
Edward Fiala April 9, 1987 2:11:58 pm PST.
This module implements the Pause (opcode 0) and Halt (opcode 377B) Xops and the Reset trap; together these provide a debugging interface to the Softcard for other diagnostics. Another diagnostic GenFoo is then run from the Softcard with "quad -cx GenDebugger GenFoo". The code here saves the IFU/EU state, stores 1 in debuggerProceed, waits for debuggerProceed to be made zero, restores state from the table, and resumes the program by returning from the Xop. Pause and Halt as defined here only work when executed in Kernel mode. A diagnostic that wants to use similar facilities from User mode must define another Xop which first enters Kernel mode using a KFC trap and then jumps here to debuggerBasePC.
The abbreviateReset boolean at the beginning of this module controls assembly of code needed to placate Lizard when this module is itself debugged. This switch causes assembly of extra initialization code to zero every register, and it turns off assembly of the debugger's wait loop. When abbreviateReset = TRUE, Lizard can still be used, but the program must terminate when Pause[] or Halt[] occurs; when abbreviateReset = FALSE, it is possible to continue from the Pause[] or Halt[] into the Xop traps defined below.
Note that the euJunk and euToKBus registers are not saved or restored; also, [S], [S+1], [S+2], and the next IFU stack frame are smashed at entry to the debugger. However, the saved state in the table can be modified prior to resuming; and the values put in smashed table locations [S], [S+1], and [S+2] will be restored.
The Dragon has opcode and other traps in the word range [1,000,000B..1,002,400B) or the PC range [4,000,000B..4,012,000B). When running simulated under Lizard, there are no particular limits on addressing, but running on the Softcard, all memory addresses are mapped into [0B..1,000,000B); i.e., the memory address bits higher than 777,777B are discarded.
DIRECTORY
DebuggerDefs,
DragOpsCross USING [XopBase, TrapBase, TrapWidthBytes, TrapIndex],
--Word, wordsPerPage, bytesPerWord, charsPerWord, bitsPerByte, bitsPerCharacter, bitsPerWord, bytesPerPage, logWordsPerPage, logBitsPerByte, logBitsPerChar, logBytesPerWord, logCharsPerWord, logBitsPerWord, logBytesPerPage, PageCount, PageNumber, maxPagesInVM, SixBitIndex, FiveBitIndex, TwoWords, FourBitIndex, Half, ThreeBitIndex, FourHalves, TwoHalves, Byte, ZerosByte, OnesByte, EightBytes, FourBytes, ByteIndex, BytesPerWord, TwoBytes, Comparison, ByteAddress, WordAddress, FieldDescriptor, RegIndex, PadByte, Lit8, Op4, Op8, JDist8, Inst, OIFormat, OQBformat, LRformat, QRformat, ShortRegQR, OBformat, LRBformat, RRformat, ODBformat, LRRBformat, RJBformat, ShortRegRJB, JBBformat, TrapWidthWords, TrapWidthBytes, XopBase, TrapBase, KernalLimit, TrapIndex, StackUnderflowTrap, IFUPageFaultTrap, ResetTrap, IFUStackOverflowTrap, EUStackOverflowTrap, RescheduleTrap, ALUCondFalse, ALUCondEZ, ALUCondLZ, ALUCondLE, ALUCondSpare, ALUCondNE, ALUCondGE, ALUCondGZ, ALUCondOver, ALUCondBC, ALUCondIL, ALUCondDO, ALUCondNotOver, ALUCondNB, ALUCondNI, ModeFault, MemAccessFault, IOAccessFault, EUPageFault, EUWriteFault, AUFault, euStack, euJunk, euToKBus, euMAR, euField, euConstant, euAux, euBogus, euLast, ifuYoungestL, ifuYoungestPC, ifuEldestL, ifuEldestPC, ifuSLimit, ifuBogus, ifuL, ifuS, ifuPC, ifuLast, EURegs, EULegalRegs, IFURegs, IFULegalRegs, StackedStatusWord, IFUStackIndex, IFUStackSize, IFUOverflow, EUStackIndex, EUStackSize, EULocalIndex, EULocals, EUAuxIndex, EUAuxRegs, EUConstIndex, EUConstants, IOLocation, ioRescheduleRequest, ioResetRequest, IOOperand, PCmdFormat, PCmdByteSelect, PCmdClass, PCmdSpace, PCmdDirection
DragOpsCrossUtils USING [CardToWord],
--InstToBytes, InstToFormat, BytePCToWordAddress, WordAddressToBytePC, IOOperandToCard, CardToIOOperand, FieldDescriptorToCard, CardToFieldDescriptor, BytesToWord, BytesToHalf, WordToBytes, HalfToBytes, HalvesToWord, WordToHalves, HighHalf, LowHalf, LeftHalf, RightHalf, SwapHalves, WordToInt, IntToWord, WordToCard, HalfToCard, ByteToCard, CardToWord, CardToHalf, CardToByte, DragAnd, DragOr, DragXor, DragNot, VanillaAdd, VanillaSub, AddDelta, HalfNot, HalfAnd, HalfOr, HalfXor, HalfShift, DoubleWordShiftLeft, SingleWordShiftLeft, SingleWordShiftRight, XopToBytePC, TrapIndexToBytePC, FieldUnit
HandCoding, --Has opcode and register defs.
HandCodingPseudos, --Label, SetLabel, GenLabel, GenLabelHere, UseLabel8A, UseLabel8B, UseLabel16, UseLabel32, LReg, PReg, SReg, AddReg, SubReg, SetRegConst, MoveReg, MoveRegI, LRegI, IndexedJump, ProcedureEntry, ProcedureExit, SetupField, ExtractField, ShiftLeft, LoadProcessorReg, StoreProcessorReg, CauseReschedule, CauseReset, GetSPLimit, SetSPLimit, GetYoungestPC, GetYoungestStatus, GetEldestPC, GetEldestStatus, SetYoungestPC, SetYoungestStatus, SetEldestPC, SetEldestStatus, Pause, Halt
HandCodingSupport; --Area, GetProc, PutProc, ProcList, NewArea, GenWithArea, Gen1WithArea, ForceOut, GetCurrentArea, LoadArea, GetOutputPC, SetOutputPC, WordAlign, ReserveData, OutputByte, OutputOneByte, OutputAlphaBeta, OutputAlphaBetaGammaDelta, OutputWord
GenDebugger: CEDAR PROGRAM
IMPORTS DragOpsCrossUtils, HandCoding, HandCodingPseudos, HandCodingSupport
= BEGIN OPEN DebuggerDefs, DragOpsCrossUtils, HandCoding, HandCodingPseudos, HandCodingSupport;
TrapWidthBytes: NAT = DragOpsCross.TrapWidthBytes;
All: PROC = {
abbreviateReset: BOOLEAN = TRUE;
debuggerCacheClearAddr: LONG CARDINAL = (debuggerBasePC / bytesPerWord) + 10000B;
area: Area = GetCurrentArea[];
enterDebug: Label = GenLabel[];
Start: Label = GenLabel[];
A trap's location is TrapIndex*TrapWidthBytes + TrapBase*bytesPerWord =
1,002,000B + 20B * TrapIndex. TrapIndex definitions are in DragOpsCross.
FillTrap: PROC [tx: DragOpsCross.TrapIndex, dest: Label] = {
SetOutputPC[LOOPHOLE[tx, CARDINAL] * TrapWidthBytes + DragOpsCross.TrapBase * bytesPerWord];
drJQB[UseLabel32[dest]];
};
Come here in Kernel mode with at least 1 IFU stack slot and 3 EU stack registers free and at lease 1 IFU stack slot in use; also, the PC in the most recent IFU stack slot must not be identical to the PC's in any earlier IFU stack slots. Entry by a Pause or Halt Xop normally ensures at least 1 IFU stack slot will be in use and unduplicated. The values in [S], [S+1], [S+2], and the next IFU stack slot are smashed here. Before entry here, the a, ab, or abcd argument of the Xop causing debugger entry (if any) has been stored in debugBase+debuggerAlphaBeta; [S] = reg0 holds the opcode number causing debugger entry; and L = the value of S at the onset of the trap + 1. The Pause (opcode 0) and Halt (opcode 377B) defined here as Xops can be used to enter the debugger only when the program is in Kernel mode. Otherwise, the program wishing to enter the debugger must define some other Xop trap like Pause[] but with Kernel entry via KFC prior to jumping here.
GenDebug: PROC = {
readS: Label = GenLabel[];
setL: Label = GenLabel[];
noMoreFrames: Label = GenLabel[];
wait: Label = GenLabel[];
SetOutputPC[debuggerBasePC];
SetLabel[enterDebug];
drALS[0]; --L ← S equals the value of S at the onset of the entry Xop + 1.
drLIQB[CardToWord[debugBase]];
drWB[debuggerOpcode]; --Save opcode number
Put a table pointer retained through state save and restore below into reg0 = [S]
drLIQB[CardToWord[debugBase]];
Save registers. Register descriptions here are copied from DragOpsCross.mesa.
euStack  (0), beginning of EU Stack
euJunk  (128), the non-matching EU register (not saved)
euToKBus  (129), send result on K bus to IFU (not saved)
euMAR  (130), MemoryAddressRegister
euField  (131), Field register
euConstant  (132), Base of EU constant registers (12 regs)
euAux  (144), Base of EU aux registers (16 regs)
euBogus  (160), [euBogus..euLast] not legal (NA) (80 regs)
euLast  (239), last possible EU reg (NA)
These IFU register addresses are noticed within the IFU. Destinations are changed to references to the "toKBus" register. Sources are changed so that ALURightSource (an EU control input) = kBus. Note that L, other status, S, and PC for the current frame are unreadable.

ifuYoungestL (240), youngest L & status in IFU stack
ifuYoungestPC (241), youngest PC in IFU stack
ifuEldestL  (242), eldest L & status in IFU stack
ifuEldestPC  (243), eldest PC in IFU stack (rd removes, wt adds)
ifuSLimit  (244), stack limit register
drRADD[pushDst, const0, const0]; drSRIn[reg0, debuggerCarry];
Read, save, and clear the IFU stack.
GetYoungestPC[]; --Youngest PC into LR1
FOR reg: CARDINAL IN [0..IFUStackSize) DO
{
moreFrames: Label ← GenLabel[];
GetEldestStatus[]; drSRIn[reg0, ifuRegBase + reg + reg];
GetEldestPC[]; --Recover the PC of the old frame
drRJNEBJ[topSrc, reg1, UseLabel8B[moreFrames]];
drSRIn[reg0, ifuRegBase + reg + reg + 1];
drLIB[reg]; drJDB[UseLabel16[noMoreFrames]];
SetLabel[moreFrames];
drSRIn[reg0, ifuRegBase + reg + reg + 1];
}
ENDLOOP;
drLIB[IFUStackSize]; --Shouldn't ever get here
SetLabel[noMoreFrames];
drSRIn[reg0, debuggerFrame]; --No. frames including the one created by Halt.
drDIS[]; --Pop the youngest PC off the stack.
Build a frame in which to access L and Status of this frame, which equals S+1 in the frame where the halt occurred. Return with the current value of L pushed onto the stack and with traps disabled.
drLFC[UseLabel16[readS]]; drSUBB[1]; drSRIn[reg0, debuggerS];
GetSPLimit[]; drSRIn[reg0, debuggerSLimit]; --ifuSLimit
The stack is saved by doing EUStackSize - 1 SRIn's, the first one saving the word immediately underneath reg0 (which contains debugBase); this word is saved in the table entry at EUStackSize - 2. During restoration, debugBase will be in reg0 and S will point at reg0; EUStackSize registers will be restored with LRIn[reg0, euRegBase+n]'s; the first word restored will be from the 0th table entry; the stack word containing debugBase is overwritten on the final load from the 127th table entry, after which the stack is popped once.
drAS[377B]; --Point S at the entry underneath debugBase
FOR reg: CARDINAL IN [0..EUStackSize - 1) DO
drSRIn[reg0, euRegBase + EUStackSize - 2 - reg]; --Save the EU stack
ENDLOOP;
S now points at reg0 containing debugBase.
FOR reg: CARDINAL IN [131..160) DO --EU registers except stack
drLIP[reg]; drSRIn[reg0, euRegBase + reg];
ENDLOOP;
Now the state has been saved, and the IFU stack is empty. Loop here until the debugging software signals "resume". Then reload registers and return from the most recent call; if the debugging software has changed nothing, then the "resume" will restore state and continue.
drLIB[1]; drSRIn[reg0, debuggerProceed]; --Indicate "stopped"
SetLabel[wait];
IF abbreviateReset THEN {
Setup to clear Softcard cache.
drLIQB[CardToWord[debugBase + debuggerProceed + 10000B]];
drRB[0]; drDIS[]; --Clear cache
drLIQB[CardToWord[debugBase + debuggerProceed]];
drRB[0];
drRJNEBJ[popSrc, const0, UseLabel8B[wait]];
};
Set L = debuggerS + 1 by calling a procedure, changing caller's L within the procedure and returning. Then repush debugBase onto the stack.
drLRIn[reg0, debuggerS]; drADDB[1]; drLFC[UseLabel16[setL]]; drASL[377B];
drLIQB[CardToWord[debugBase]];
Restore the IFU stack from the saved state. The oldest frame was the first one saved, in location 0 of the table, so this must be the last frame reloaded.
drLRIn[reg0, debuggerFrame]; --reg1 ← last frame to reload;
FOR reg: CARDINAL ← IFUStackSize - 1, reg - 1 DO
{
notYet: Label ← GenLabel[];
drLIB[reg]; drRJGB[popSrc, belowSrc, UseLabel8B[notYet]];
drLRIn[reg0, ifuRegBase + reg + reg + 1]; SetEldestPC[];
drLRIn[reg0, ifuRegBase + reg + reg]; SetEldestStatus[];
SetLabel[notYet];
};
IF reg = 0 THEN EXIT;
ENDLOOP;
drAS[377B];
drLRIn[reg0, debuggerSLimit]; SetSPLimit[];
drLRIn[reg0, debuggerCarry];
drLIQB[CardToWord[37777777777B]];
drRUADD[belowDst, popSrc, belowSrcPop]; --Reload Carry
drLRIn[reg0, euRegBase + 131]; drSIP[131]; --Reload euField
FOR reg: CARDINAL IN [133..160) DO --Reload constants except const0 and aux registers
drLRIn[reg0, euRegBase + reg]; drSIP[reg];
ENDLOOP;
Finish by reloading the EU stack. The first register reloaded from the 0th table entry is the one immediately above debugBase; the last register reloaded is the one containing debugBase. Then pop the stack on the return from the trap.
FOR reg: CARDINAL IN [0..EUStackSize) DO
drLRIn[reg0, euRegBase + reg];
ENDLOOP;
Resume program at location after Xop with S restored.
drRET[377B];
Procedure to read the halting opcode's S = L for the calling context and to disable traps.
WordAlign[area];
SetLabel[readS];
GetYoungestStatus[]; drLIB[377B]; drAND[];
drDUP[]; SetYoungestStatus[]; drRETN[];
WordAlign[area];
SetLabel[setL];
SetYoungestStatus[]; drRETN[];
};
XPause[] = Pause[] but uses opcode 1 instead of 0.
XPause: PROC = {
HandCodingSupport.OutputByte[HandCodingSupport.GetCurrentArea[], LOOPHOLE[1]];
};
GenReset is the last code module instantiated below, so it establishes the initial PC of the diagnostic whose code is loaded afterwards. In order not to get a Lizard error, all the registers which are saved and restored by the debugger must be initialized here (0 is used, but the value doesn't matter).
GenReset: PROC = {
fixL: Label = GenLabel[];
WordAlign[area];
SetLabel[Start]; --Softcard comes here on Reset.
drLIQB[CardToWord[debugBase]];
--drLC0[];
--drWSB[debuggerLoopCount];
IF NOT abbreviateReset THEN {
FOR I: CARDINAL IN [0..EUStackSize) DO
drLC0[]; --Initialize the stack
ENDLOOP;
drSIP[130]; --euMAR
drSIP[131]; --euField
SetSPLimit[]; --ifuSLimit
FOR I: CARDINAL IN [1..12+16) DO
drSIP[132 + I]; --Initialize the constants (except 0) and aux regs
ENDLOOP;
};
drLIB[1]; drLFC[UseLabel16[fixL]];
drASL[377B]; --S ← 0
drJQB[CardToWord[userBasePC]];
WordAlign[area];
SetLabel[fixL];
SetYoungestStatus[];
spLimit is set with room for 17 overflow words (just in case). A stack overflow trap occurs when S is in [spLimit..spLimit + 16). Since this diagnostic program never enables the trap, it is not necessary to execute the code here.
--drLIB[128-16-1];
--SetSPLimit[];
drRETN[]; --L ← 1 in caller's frame
SetOutputPC[userBasePC];
Begin the program with all registers containing 0, L=0, and S = 377B.
};
GenTest is a test program for the debugger itself.
--GenTest: PROC = {
--drLFC[3];
--drLIB[33]; drLIB[133]; Pause[];
--drLFC[3];
--drLIB[44]; drLIB[144]; Pause[];
--drLFC[3];
--drLIB[55]; drLIB[155]; Pause[];
--drLFC[3];
--drLIB[66]; drLIB[166]; Pause[];
--Halt[177777B];
--};
FillTrap[ResetTrap, Start];
Xops trap at word location XopBase + opcode*TrapWidthWords = a PC of XopBase*bytesPerWord + opcode * TrapWidthWords * bytesPerWord = 4,000,000B + 20B * opcode.
Pause (opcode 0) entry (Note: Lizard intercepts this, so only works on Softcard.).
SetOutputPC[0 * TrapWidthBytes + DragOpsCross.XopBase * bytesPerWord];
drLIQB[CardToWord[debugBase]];
drLIP[130]; drWSB[euRegBase + 130]; --euMAR
drLC0[]; --Opcode number of Pause[];
drJDB[UseLabel16[enterDebug]];
Halt (opcode 377B) entry (Note: Lizard intercepts this, so only works on Softcard.).
SetOutputPC[377B * TrapWidthBytes + DragOpsCross.XopBase * bytesPerWord];
AlphaBeta already on the stack.
drLIQB[CardToWord[debugBase]];
drLIP[130]; drPSB[euRegBase + 130]; --euMAR
drWB[debuggerAlphaBeta];
drLIB[377B]; --Opcode number of Halt[ab];
drJDB[UseLabel16[enterDebug]];
GenDebug[];
GenReset[];
--GenTest[];
Fall through into first instruction of user's code in following module.
};
END.