HandCodingPseudosImpl.mesa
Copyright Ó 1984, 1985, 1986, 1987 by Xerox Corporation. All rights reserved.
Russ Atkinson (RRA) April 24, 1987 5:02:57 pm PDT
McCreight, December 31, 1985 3:14:26 pm PST
DIRECTORY
Atom USING [PropList, PutPropOnList],
Basics USING [LowHalf],
DragOpsCross USING [Byte, ByteAddress, bytesPerPage, bytesPerWord, FieldDescriptor, ioRescheduleRequest, ioResetRequest, JDist8, PCmdFormat, ProcessorRegister, Word, ZerosByte],
DragOpsCrossUtils USING [BytePCToWordAddress, CardToWord, HalfToCard, IntToWord, WordToBytes, WordToHalves],
HandCoding USING [const0, const1, ConstSpec, drADDB, drADDDB, drADDQB, drALS, drDUP, drFSDB, drJSR, drLC1, drLIP, drLIQB, drLRn, drIODA, drQOR, drQRX, drRADD, drRET, drRETN, drROR, drRRX, drRSUB, drSHL, drSHR, drSIP, drSRn, Lit16, Lit8, popSrc, pushDst, RegSpec, topSrc],
HandCodingPseudos USING [Label, LabelRep],
HandCodingSupport USING [Area, GetCurrentArea, OutputAlphaBeta, OutputAlphaBetaGammaDelta, OutputByte, OutputWord, SetOutputPC, WordAlign],
IO USING [PutF, PutRope, STREAM],
PriorityQueue USING [Empty, Insert, Predict, Ref, Remove, SortPred],
Rope USING [Compare, ROPE],
SymTab USING [Create, EachPairAction, Fetch, GetSize, Pairs, Ref, Store];
HandCodingPseudosImpl: CEDAR PROGRAM
IMPORTS Atom, Basics, DragOpsCrossUtils, HandCoding, HandCodingSupport, IO, PriorityQueue, Rope, SymTab
EXPORTS HandCodingPseudos
= BEGIN OPEN DragOpsCross, HandCoding;
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
Labelling
Area: TYPE = HandCodingSupport.Area;
Label: TYPE = HandCodingPseudos.Label;
LabelRep: TYPE = HandCodingPseudos.LabelRep;
LabelUsageList: TYPE = LIST OF LabelUsage;
LabelUsage: TYPE = RECORD [
area: Area,
offset: INT ← 0,
delta: INT ← 0,
distWidth: [0..4] ← 1,
fixoff: [0..4] ← 0,
wordAddr: BOOL
];
LabellingError: PUBLIC ERROR [why: ROPE] = CODE;
SetLabel: PUBLIC PROC [label: Label] = {
area: Area = HandCodingSupport.GetCurrentArea[];
here: CARD ← area.currentPC;
IF label.area # NIL THEN ERROR LabellingError["duplicate definition"];
label.offset ← here;
label.area ← area;
FOR each: LabelUsageList ← NARROW[label.uses], each.rest UNTIL each = NIL DO
use: LabelUsage = each.first;
where: INT = use.offset;
delta: INT = here - where + use.delta;
word: Word = DragOpsCrossUtils.IntToWord[delta];
IF use.area # area THEN ERROR LabellingError["wrong area"];
HandCodingSupport.SetOutputPC[where+use.fixoff, area];
SELECT use.distWidth FROM
1 => {
IF delta < -128 OR delta > 127 THEN
ERROR LabellingError["delta not in a byte"];
HandCodingSupport.OutputByte[area, DragOpsCrossUtils.WordToBytes[word][3]];
};
2 => {
IF delta < FIRST[INTEGER] OR delta > LAST[INTEGER] THEN
ERROR LabellingError["delta not in two bytes"];
HandCodingSupport.OutputAlphaBeta[
area,
DragOpsCrossUtils.HalfToCard[DragOpsCrossUtils.WordToHalves[word][1]]];
};
4 => {
4-byte addresses are absolute, not relative
delta: Word ← DragOpsCrossUtils.IntToWord[here - use.delta];
IF use.wordAddr THEN {
offset: [0..bytesPerWord) ← 0;
[delta, offset] ← DragOpsCrossUtils.BytePCToWordAddress[[delta]];
IF offset # 0 THEN ERROR LabellingError["label not on word boundary"];
};
HandCodingSupport.OutputAlphaBetaGammaDelta[area, delta];
};
ENDCASE;
HandCodingSupport.SetOutputPC[here, area];
ENDLOOP;
};
GenLabel: PUBLIC PROC RETURNS [label: Label] = {
RETURN [NEW[LabelRep ← [area: NIL, name: NIL, offset: -1, uses: NIL]]];
};
GenLabelHere: PUBLIC PROC RETURNS [label: Label] = {
label ← GenLabel[];
SetLabel[label];
};
UseLabel8A: PUBLIC PROC [label: Label] RETURNS [JDist8] = {
offset: INT = DeltaLabels[label, 0, 1, 1];
IF offset < -128 OR offset > 127 THEN
ERROR LabellingError["delta not in a byte"];
RETURN [IF offset < 0 THEN 256+offset ELSE offset];
};
UseLabel8B: PUBLIC PROC [label: Label] RETURNS [JDist8] = {
offset: INT = DeltaLabels[label, 0, 1, 2];
IF offset < -128 OR offset > 127 THEN
ERROR LabellingError["delta not in a byte"];
RETURN [IF offset < 0 THEN 256+offset ELSE offset];
};
UseLabel16: PUBLIC PROC [label: Label] RETURNS [Lit16] = {
offset: INT = DeltaLabels[label, 0, 2, 1];
IF offset < FIRST[INTEGER] OR offset > LAST[INTEGER] THEN
ERROR LabellingError["delta not in two bytes"];
RETURN [IF offset < 0 THEN (offset + LAST[CARDINAL]) + 1 ELSE offset];
};
UseLabel32: PUBLIC PROC [label: Label] RETURNS [Word] = {
RETURN [DragOpsCrossUtils.IntToWord[DeltaLabels[label, 0, 4, 1]]];
};
GenDataLabel: PUBLIC PROC [area: Area, bytes: CARD] RETURNS [label: Label ← NIL] = {
Reserves a given number of data bytes and returns a label to the start of the bytes. No alignment takes place.
zerosByte: DragOpsCross.Byte ← DragOpsCross.ZerosByte;
old: CARD ← (IF area = NIL
THEN area ← HandCodingSupport.GetCurrentArea[]
ELSE area).currentPC;
pc: CARD ← area.currentData;
HandCodingSupport.SetOutputPC[pc, area];
label ← GenLabelHere[];
IF bytes # 0 THEN DO
mod: CARDINAL ← Basics.LowHalf[pc] MOD bytesPerPage;
rem: CARDINAL ← bytesPerPage - mod;
HandCodingSupport.OutputByte[area, zerosByte];
IF bytes > rem
THEN {
pc ← pc + rem;
bytes ← bytes - rem;
HandCodingSupport.SetOutputPC[pc, area];
}
ELSE {
area.currentData ← pc + bytes;
bytes ← 0;
EXIT;
};
ENDLOOP;
HandCodingSupport.SetOutputPC[old, area];
};
OutputWordAddr: PUBLIC PROC [area: Area, label: Label, offset: INT ← 0] = {
Puts out the label as a word address into the code stream. No alignment takes place.
addr: INT ← DeltaLabels[label, offset, 4, 0, TRUE];
HandCodingSupport.OutputWord[area, DragOpsCrossUtils.IntToWord[addr], FALSE];
};
OutputByteAddr: PUBLIC PROC [area: Area, label: Label, offset: INT ← 0] = {
Puts out the label as a byte address into the code stream. No alignment takes place.
addr: INT ← DeltaLabels[label, offset, 4, 0, FALSE];
HandCodingSupport.OutputWord[area, DragOpsCrossUtils.IntToWord[addr], FALSE];
};
PushWordAddr: PUBLIC PROC [area: Area, label: Label, offset: INT ← 0] = {
Pushes the label as a word address. No alignment takes place.
addr: INT ← DeltaLabels[label, offset, 4, 1, TRUE];
drLIQB[DragOpsCrossUtils.IntToWord[addr]];
};
PushByteAddr: PUBLIC PROC [area: Area, label: Label, offset: INT ← 0] = {
Pushes the label as a byte address. No alignment takes place.
addr: INT ← DeltaLabels[label, offset, 4, 1, FALSE];
drLIQB[DragOpsCrossUtils.IntToWord[addr]];
};
AddWordAddr: PUBLIC PROC [area: Area, label: Label, offset: INT ← 0] = {
Adds the label as a word address to [S]. No alignment takes place.
addr: INT ← DeltaLabels[label, offset, 4, 1, TRUE];
drADDQB[DragOpsCrossUtils.IntToWord[addr]];
};
AddByteAddr: PUBLIC PROC [area: Area, label: Label, offset: INT ← 0] = {
Adds the label as a byte address to [S]. No alignment takes place.
addr: INT ← DeltaLabels[label, offset, 4, 1, FALSE];
drADDQB[DragOpsCrossUtils.IntToWord[addr]];
};
DeltaLabels: PROC
[label: Label, plus: INT, width: [0..4], fixoff: NAT, word: BOOLFALSE]
RETURNS [INT] = {
Return the number of bytes between the current PC and the label. The current PC is remembered as the place to do fixups. The plus argument is used to pretend that the current PC is that many bytes forward (reduces delta) from where it is for the purpose of determining delta, but not for the purpose of determining the PC for fixup. The width of the label is necessary for fixups and sign extension. The fixoff is necessary to determine how many bytes lie between the pc and the start of the label.
pc: CARD;
area: Area = HandCodingSupport.GetCurrentArea[];
pc ← area.currentPC;
SELECT label.area FROM
area => {
This label is known and in the same area, so generating the offset is easy. Notice that 32-bit labels are absolute, not relative.
delta: INT ← label.offset + plus;
SELECT TRUE FROM
width # 4 => delta ← delta - pc;
word => delta ← LOOPHOLE[delta, CARD] / CARD[bytesPerWord];
ENDCASE;
RETURN [delta];
};
NIL => {
the label is not yet defined, so save the given location for fixup
uses: LabelUsageList ← NARROW[label.uses];
uses ← CONS[[area: area, offset: pc, distWidth: width, delta: plus, fixoff: fixoff, wordAddr: word], uses];
label.uses ← uses;
RETURN [0];
};
ENDCASE =>
the label is not in the same area, so complain about it
ERROR LabellingError["wrong area"];
};
SymHolder: TYPE = RECORD[tab: SymTab.Ref];
MakeLabelGlobal: PUBLIC PROC [name: ROPE, label: Label] = {
area: Area = HandCodingSupport.GetCurrentArea[];
tab: SymTab.Ref ← GetGlobalLabelTable[area];
IF tab = NIL THEN {
tab ← SymTab.Create[29, FALSE];
area.props ← Atom.PutPropOnList[area.props, $GlobalLabels, NEW[SymHolder ← [tab]]];
};
[] ← SymTab.Store[tab, name, label];
label.name ← name;
};
LabelNameSort: PriorityQueue.SortPred = {
xx: HandCodingPseudos.Label = NARROW[x];
yy: HandCodingPseudos.Label = NARROW[y];
RETURN [Rope.Compare[xx.name, yy.name, FALSE] = less];
};
LabelAddrSort: PriorityQueue.SortPred = {
xx: HandCodingPseudos.Label = NARROW[x];
yy: HandCodingPseudos.Label = NARROW[y];
RETURN [xx.offset < yy.offset];
};
ShowGlobalLabelTable: PUBLIC PROC [st: IO.STREAM, sortNames: BOOLTRUE, area: HandCodingSupport.Area ← NIL] = {
shows the global label table for the given area to the given stream. If sortNames, then the labels come out sorted by name, otherwise sorted by address.
tab: SymTab.Ref = GetGlobalLabelTable[area];
Now, if there are any, output any global symbols that have accumulated. We go to a little extra trouble to ensure that the symbols are sorted by name.
IF tab # NIL THEN {
action: SymTab.EachPairAction = {
[key: Key, val: Val] RETURNS [quit: BOOL]
label: HandCodingPseudos.Label = NARROW[val];
PriorityQueue.Insert[pq, label];
quit ← FALSE;
};
pq: PriorityQueue.Ref = PriorityQueue.Predict[
SymTab.GetSize[tab], IF sortNames THEN LabelNameSort ELSE LabelAddrSort, NIL];
[] ← SymTab.Pairs[tab, action];
WHILE NOT PriorityQueue.Empty[pq] DO
label: HandCodingPseudos.Label = NARROW[PriorityQueue.Remove[pq]];
IF sortNames
THEN IO.PutF[st, " %g = %w\n", [rope[label.name]], [cardinal[label.offset]]]
ELSE IO.PutF[st, " -- %w: %g\n", [cardinal[label.offset]], [rope[label.name]]];
ENDLOOP;
IO.PutRope[st, "\n"];
};
};
GetGlobalLabel: PUBLIC PROC [name: ROPE] RETURNS [Label] = {
tab: SymTab.Ref ← GetGlobalLabelTable[NIL];
IF tab = NIL THEN RETURN [NIL];
RETURN [NARROW[SymTab.Fetch[tab, name].val]];
};
GetGlobalLabelTable: PUBLIC PROC [area: Area] RETURNS [tab: SymTab.Ref ← NIL] = {
IF area = NIL THEN area ← HandCodingSupport.GetCurrentArea[];
FOR each: Atom.PropList ← area.props, each.rest WHILE each # NIL DO
IF each.first.key = $GlobalLabels THEN {
symHold: REF SymHolder ← NARROW[each.first.val];
tab ← symHold.tab;
EXIT;
};
ENDLOOP;
};
Macros
LReg: PUBLIC PROC [reg: RegSpec] = {
SELECT TRUE FROM
reg.kind = reg => drLRn[reg];
reg = topSrc => drDUP[];
ENDCASE => drQOR[pushA0, reg];
};
PReg: PUBLIC PROC [reg: RegSpec] = {
drROR[c: reg, a: topSrc, b: const0];
};
SReg: PUBLIC PROC [reg: RegSpec] = {
IF reg.kind = reg
THEN drSRn[reg]
ELSE drROR[c: reg, a: popSrc, b: const0];
};
AddReg: PUBLIC PROC [reg: RegSpec, const: ConstSpec] = {
drRADD[c: reg, a: reg, b: const];
};
SubReg: PUBLIC PROC [reg: RegSpec, const: ConstSpec] = {
drRSUB[c: reg, a: reg, b: const];
};
SetRegConst: PUBLIC PROC [reg: RegSpec, const: ConstSpec] = {
drROR[c: reg, a: const, b: const0];
};
MoveReg: PUBLIC PROC [dst,src: RegSpec] = {
We go through extra stuff here to legally mix aux and reg kinds. Further checking is performed by the opcode routines themselves.
SELECT TRUE FROM
dst.kind = aux AND src.kind = reg => {
drLRn[reg: src];
drROR[c: dst, a: popSrc, b: const0];
};
dst.kind = reg AND src.kind = aux => {
drROR[c: pushDst, a: src, b: const0];
drSRn[reg: dst];
};
ENDCASE =>
drROR[c: dst, a: src, b: const0];
};
MoveRegI: PUBLIC PROC [dst,src: RegSpec, const: ConstSpec] = {
drRRX[c: dst, a: src, b: const];
};
LRegI: PUBLIC PROC [reg: RegSpec, const: ConstSpec] = {
SELECT const FROM
const0 => drQRX[pushA0, reg];
const1 => drQRX[pushA1, reg];
ENDCASE => drRRX[c: pushDst, a: reg, b: const];
};
IndexedJump: PUBLIC PROC [dest: Label, long: BOOLFALSE, back: BOOLFALSE] = {
area: Area = HandCodingSupport.GetCurrentArea[];
thisPC: CARD ← area.currentPC;
IF dest # NIL THEN
IF long
THEN drADDDB[DeltaLabels[dest, -3, 2, 1]]
ELSE drADDB[DeltaLabels[dest, -2, 1, 1]];
drJSR[];
};
ProcedureEntry: PUBLIC PROC [label: Label, args: [0..15], dontChangeRL: BOOLFALSE] = {
Notes procedure entry.
lit: Lit8 ← (1-LOOPHOLE[args, CARDINAL]) MOD 256;
HandCodingSupport.WordAlign[];
IF label # NIL THEN SetLabel[label];
IF NOT dontChangeRL THEN drALS[lit];
};
ProcedureExit: PUBLIC PROC [rets: [0..15], dontChangeSP: BOOLFALSE] = {
generates the procedure exit instruction
lit: Lit8 ← (LOOPHOLE[rets, CARDINAL]-1) MOD 256;
SELECT TRUE FROM
dontChangeSP => drRETN[];
ENDCASE => drRET[lit];
};
SetupField: PUBLIC PROC [fd: FieldDescriptor] = {
... generates the appropriate instruction to extract the specified bits from [S], which is performed by left shifting the pair ([S], [S]) until the field is in the corretc position, then masking out the correct # of bits.
drFSDB[LOOPHOLE[fd, Lit16]];
};
ExtractField: PUBLIC PROC [first: [0..31], bits: [0..31]] = {
... generates the appropriate instruction to extract the specified bits from [S], which is performed by left shifting the pair ([S], [S]) until the field is in the correct position, then masking out the correct # of bits.
fd: FieldDescriptor = [
reserved: 0,
insert: FALSE,
mask: bits,
shift: first + bits
];
drSHR[LOOPHOLE[fd, Lit16]];
};
ShiftLeft: PUBLIC PROC [bits: [0..31]] = {
generates the appropriate instruction to shift [S] left by bits
fd: FieldDescriptor = [
reserved: 0,
insert: FALSE,
mask: 0,
shift: bits
];
drSHL[LOOPHOLE[fd, Lit16]];
};
IFU primitives
LoadProcessorReg: PUBLIC PROC [which: DragOpsCross.ProcessorRegister] = {
Loads the specified IFU or EU register onto the stack.
drLIP[LOOPHOLE[which]];
};
StoreProcessorReg: PUBLIC PROC [which: DragOpsCross.ProcessorRegister] = {
Pops the stack into the specified IFU register.
drSIP[LOOPHOLE[which]];
};
CauseReschedule: PUBLIC PROC = {
... causes a reschedule interrupt when interrupts are next enabled (possibly immediately). We do this by writing anything to a peculiar IO location.
pCmd: DragOpsCross.PCmdFormat ← [cache[space: io, direction: write]];
drLC1[];
drLIQB[DragOpsCrossUtils.CardToWord[DragOpsCross.ioRescheduleRequest]];
drIODA[LOOPHOLE[pCmd]];
};
CauseReset: PUBLIC PROC = {
... causes a reset trap.
pCmd: DragOpsCross.PCmdFormat ← [cache[space: io, direction: write]];
drLC1[];
drLIQB[DragOpsCrossUtils.CardToWord[DragOpsCross.ioResetRequest]];
drIODA[LOOPHOLE[pCmd]];
};
GetSPLimit: PUBLIC PROC = {
... enables/disables stack overflow trapping.
LoadProcessorReg[ifuSLimit];
};
SetSPLimit: PUBLIC PROC = {
... enables/disables stack overflow trapping.
StoreProcessorReg[ifuSLimit];
};
GetYoungestPC: PUBLIC PROC = {
... gets the youngest PC entry in the IFU stack (into [S]). S←S+1.
LoadProcessorReg[ifuYoungestPC];
};
GetYoungestStatus: PUBLIC PROC = {
... gets the youngest L entry in the IFU stack (into [S]). S←S+1.
LoadProcessorReg[ifuYoungestL];
};
GetEldestPC: PUBLIC PROC = {
... flushes the eldest entry in the IFU stack and puts the PC into [S+1]. S←S+1. The results are undefined if there was no PC to get.
LoadProcessorReg[ifuEldestPC];
};
GetEldestStatus: PUBLIC PROC = {
... pushes the RL part of the eldest IFU stack entry.
LoadProcessorReg[ifuEldestL];
};
SetYoungestPC: PUBLIC PROC = {
... sets the youngest PC entry in the IFU stack (from [S]). S←S-1.
StoreProcessorReg[ifuYoungestPC];
};
SetYoungestStatus: PUBLIC PROC = {
... sets the youngest L entry in the IFU stack (from [S]). S←S-1.
StoreProcessorReg[ifuYoungestL];
};
SetEldestPC: PUBLIC PROC = {
... causes a new eldest IFU stack entry to be created and sets the PC part of that entry (from [S]). S←S-1.
StoreProcessorReg[ifuEldestPC];
};
SetEldestStatus: PUBLIC PROC = {
... sets the RL part of the eldest IFU stack entry (from [S]).
StoreProcessorReg[ifuEldestL];
};
Interaction with the interpreter
Pause: PUBLIC PROC = {
... Instructs the interpreter to pause.
HandCodingSupport.OutputByte[HandCodingSupport.GetCurrentArea[], LOOPHOLE[0]];
};
Halt: PUBLIC PROC [code: CARDINAL] = {
... Instructs the interpreter to pause.
area: Area = HandCodingSupport.GetCurrentArea[];
HandCodingSupport.OutputByte[area, LOOPHOLE[377B]];
HandCodingSupport.OutputAlphaBeta[area, code];
};
END.