DIRECTORY
Atom USING [PropList, PutPropOnList],
DragOpsCross USING [FieldDescriptor, IFUStatusRec, ioRescheduleRequest, ioResetRequest, JDist8, ProcessorRegister, Word],
DragOpsCrossUtils USING [CardToWord, HalfToCard, IntToWord, IOOperandToCard, WordToBytes, WordToHalves],
HandCoding USING [const0, ConstSpec, drADDB, drADDDB, drALS, drFSDB, drJS, drJ1, drLC1, drLFC, drLIP, drLIQB, drLRn, drIOS, drRADD, drRET, drRETN, drRETK, drROR, drRRX, drRSUB, drSHL, drSHR, drSIP, drSRn, Lit16, Lit8, popSrc, pushDst, RegSpec, topSrc],
HandCodingPseudos USING [Label, LabelRep],
HandCodingSupport USING [Area, GetCurrentArea, OIcommon, OutputAlphaBeta, OutputAlphaBetaGammaDelta, OutputByte, 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, 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;
CARD: TYPE = LONG CARDINAL;
Label: TYPE = HandCodingPseudos.Label;
LabelRep: TYPE = HandCodingPseudos.LabelRep;
LabelUsageList: TYPE = LIST OF LabelUsage;
LabelUsage:
TYPE =
RECORD [
area: Area,
offset: INT ← 0,
delta: INTEGER ← 0,
distWidth: [0..4] ← 1,
fixoff: [0..4] ← 0
];
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
HandCodingSupport.OutputAlphaBetaGammaDelta[
area, DragOpsCrossUtils.IntToWord[here - use.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];
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]]];
};
DeltaLabels:
PROC [label: Label, plus:
INTEGER ← 0, width: [0..4] ← 1, fixoff:
NAT ← 1]
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.
IF width = 4
THEN RETURN [label.offset-plus]
ELSE RETURN [label.offset - pc - plus];
};
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], 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:
BOOL ←
TRUE, 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]], [integer[label.offset]]]
ELSE IO.PutF[st, " -- %w: %g\n", [integer[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] = {
IF reg.kind = reg
THEN drLRn[reg]
ELSE drROR[c: pushDst, a: reg, b: const0];
};
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] = {
drRRX[c: pushDst, a: reg, b: const];
};
IndexedJump:
PUBLIC
PROC [dest: Label, long:
BOOL ←
FALSE, back:
BOOL ←
FALSE] = {
area: Area = HandCodingSupport.GetCurrentArea[];
thisPC: CARD ← area.currentPC;
IF dest #
NIL
THEN
IF long
THEN drADDDB[DeltaLabels[dest, 3, 2]]
ELSE drADDB[DeltaLabels[dest, 2, 1]];
drJS[];
};
ProcedureEntry:
PUBLIC
PROC [label: Label, args: [0..15], dontChangeRL:
BOOL ←
FALSE] = {
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:
BOOL ←
FALSE, enableTraps:
BOOL ←
FALSE] = {
generates the procedure exit instruction, if enableTraps then RETK else RET.
RETK expects new Status on the stack.
lit: Lit8 ← (LOOPHOLE[rets, CARDINAL]-1) MOD 256;
SELECT
TRUE
FROM
enableTraps => drRETK[lit];
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 corretc 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]];
};
EnableTraps:
PUBLIC
PROC = {
... enables reschedule interrupts and stack overflow.
status: DragOpsCross.IFUStatusRec ← [ ];
status.userModeKeep ← TRUE;
status.rescheduleKeep ← TRUE;
status.trapsEnabledKeep ← FALSE;
status.trapsEnabled ← TRUE;
drLIQB[LOOPHOLE[status]];
StoreProcessorReg[ifuStatus];
};
DisableTraps:
PUBLIC
PROC = {
... disables reschedule interrupts and stack overflow.
status: DragOpsCross.IFUStatusRec ← [ ];
status.userModeKeep ← TRUE;
status.rescheduleKeep ← TRUE;
status.trapsEnabledKeep ← FALSE;
status.trapsEnabled ← FALSE;
drLIQB[LOOPHOLE[status]];
StoreProcessorReg[ifuStatus];
};
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.
drLC1[];
drLIQB[DragOpsCrossUtils.CardToWord[DragOpsCross.ioRescheduleRequest]];
drIOS[DragOpsCrossUtils.IOOperandToCard[
[pCmd: [register: first, mode: kernelOnly, direction: write]]]];
};
CauseReset:
PUBLIC
PROC = {
... causes a reset trap.
drLC1[];
drLIQB[DragOpsCrossUtils.CardToWord[DragOpsCross.ioResetRequest]];
drIOS[DragOpsCrossUtils.IOOperandToCard[
[pCmd: [register: first, mode: kernelOnly, direction: write]]]];
};
GetSPLimit:
PUBLIC
PROC = {
... enables/disables stack overflow trapping.
LoadProcessorReg[ifuSLimit];
};
SetSPLimit:
PUBLIC
PROC = {
... enables/disables stack overflow trapping.
StoreProcessorReg[ifuSLimit];
};
GetL:
PUBLIC
PROC = {
... returns the current value of L.
drLFC[5]; -- stupid call to put L on the IFU stack
HandCodingSupport.OIcommon[dJ5];
-- jump around the dumb little routine
drJ1[];
LoadProcessorReg[ifuYoungestL];
drRETN[];
};
SetL:
PUBLIC
PROC = {
... sets the current value of L.
drLFC[5]; -- stupid call to put L on the IFU stack
HandCodingSupport.OIcommon[dJ5];
-- jump around the dumb little routine
drJ1[];
StoreProcessorReg[ifuYoungestL];
drRETN[];
};
GetYoungestPC:
PUBLIC PROC = {
... gets the youngest PC entry in the IFU stack (into [S]). S←S+1.
LoadProcessorReg[ifuYoungestPC];
};
GetYoungestL:
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];
};
GetEldestL:
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];
};
SetYoungestL:
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];
};
SetEldestL:
PUBLIC PROC = {
... sets the RL part of the eldest IFU stack entry (from [S]).
StoreProcessorReg[ifuEldestL];
};