InstructionsImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Levin on September 20, 1983 11:14 am
Russ Atkinson, February 27, 1985 8:10:00 pm PST
DIRECTORY
Basics USING [BITSHIFT, bitsPerByte, bitsPerWord, bytesPerWord, DivMod, LongDiv, LongDivMod, LongMult, LongNumber, RawBytes],
MesaRuntimeInit USING [],
PrincOps USING [BBptr, BBTableSpace, ByteBltBlock, GlobalFrameHandle, NullLink, sBLTE, sBLTEC, sBLTECL, sBLTEL, sBoundsFault, sBYTBLTE, sBYTBLTEC, sBYTBLTECL, sBYTBLTEL, SD, sLongDiv, sLongDivMod, sLongMod, sLongMul, sLongStringCheck, sPointerFault, sSignedDiv, sStringInit, StateVector, sULongDiv, sULongDivMod, sULongMod, zKFCB],
PrincOpsUtils USING [AlignedBBTable, BITBLT, Codebase, GetReturnFrame, GetReturnLink, LongCopy];
InstructionsImpl: PROGRAM
IMPORTS Basics, PrincOpsUtils
EXPORTS MesaRuntimeInit, PrincOpsUtils
= BEGIN
Unimplemented instructions. This stuff will be moved to the processor head as part of the architectural bootstrap.
BlockEqual: PROC [p1: POINTER, n: CARDINAL, p2: POINTER] RETURNS [BOOL] = {
BLTE instruction
FOR i: CARDINAL IN [0 .. n) DO
IF (p1+i)^ ~= (p2+i)^ THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE]
};
BlockEqualCodeLong: PROC [p1: LONG POINTER, n, offset: CARDINAL] RETURNS [BOOL] = {
BLTECL instruction
p2: LONG POINTER = PrincOpsUtils.Codebase[PrincOpsUtils.GetReturnFrame[].accesslink]+offset;
FOR i: CARDINAL IN [0 .. n) DO
IF (p1+i)^ ~= (p2+i)^ THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE]
};
BlockEqualCode: PROC [p1: POINTER, n, offset: CARDINAL] RETURNS [BOOL] = {
BLTEC instruction
p2: LONG POINTER = PrincOpsUtils.Codebase[PrincOpsUtils.GetReturnFrame[].accesslink]+offset;
FOR i: CARDINAL IN [0 .. n) DO
IF (p1+i)^ ~= (p2+i)^ THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE]
};
BlockEqualLong: PROC [p1: LONG POINTER, n: CARDINAL, p2: LONG POINTER] RETURNS [BOOL] = {
BLTEL instruction
FOR i: CARDINAL IN [0 .. n) DO
IF (p1+i)^ ~= (p2+i)^ THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE]
};
ByteBlockEqual: PROC [p1: PPA, n: CARDINAL, p2: PPA] RETURNS [BOOL] = {
BYTBLTE instruction
RETURN[BlockEqual[p1: p1, p2: p2, n: n/2] AND p1[n-1] = p2[n-1]];
};
ByteBlockEqualCode: PROC [p1: POINTER, n, offset: CARDINAL] RETURNS [BOOL] = {
BYTBLTEC instruction
codebase: LONG POINTER = PrincOpsUtils.Codebase[PrincOpsUtils.GetReturnFrame[].accesslink];
RETURN[ByteBlockEqualLong[p2: codebase+offset, p1: p1, n: n]]
};
ByteBlockEqualCodeLong: PROC [p1: LONG POINTER, n, offset: CARDINAL] RETURNS [result: BOOL] = {
BYTBLTECL instruction
codebase: LONG POINTER = PrincOpsUtils.Codebase[PrincOpsUtils.GetReturnFrame[].accesslink];
RETURN[ByteBlockEqualLong[p2: codebase+offset, p1: p1, n: n]]
};
PPA: TYPE = POINTER TO Basics.RawBytes;
LPPA: TYPE = LONG POINTER TO Basics.RawBytes;
ByteBlockEqualLong: PROC [p1: LPPA, n: CARDINAL, p2: LPPA] RETURNS [BOOL] = {
BYTBLTEL instruction
RETURN[BlockEqualLong[p1: p1, p2: p2, n: n/2] AND p1[n-1]=p2[n-1]]};
ByteBlt
ByteBlt: PUBLIC PROC [to, from: PrincOps.ByteBltBlock] RETURNS [nBytes: CARDINAL] = {
NB: to&from are RECORDs, not POINTERs to RECORDs, so we can update them
toBytes, fromBytes: LPPA;
moved: CARDINAL ← 0;
This check is necessary since subtracting CARDINALs gives big numbers
IF to.startIndex>to.stopIndexPlusOne OR from.startIndex>from.stopIndexPlusOne THEN ERROR;
IF (nBytes ← MIN[to.stopIndexPlusOne-to.startIndex,from.stopIndexPlusOne-from.startIndex]) = 0 THEN RETURN;
toBytes ← to.blockPointer; fromBytes ← from.blockPointer;
Move the first odd byte (if any) to be sure that "to" is word aligned
IF to.startIndex MOD 2 ~= 0 THEN {
toBytes[to.startIndex] ← fromBytes[from.startIndex];
moved ← 1;
to.startIndex ← to.startIndex+1;
from.startIndex ← from.startIndex+1;
};
IF from.startIndex MOD 2 = 0
THEN {
Fast case: both are word aligned
words: CARDINAL = (nBytes-moved)/2;
PrincOpsUtils.LongCopy[
to: toBytes+to.startIndex/2, from: fromBytes+from.startIndex/2, nwords: words];
IF moved+2*words ~= nBytes THEN
Move the one and only remaining byte
toBytes[to.startIndex+2*words] ← fromBytes[from.startIndex+2*words];
}
Slow case: have to ripple things
ELSE {
BitBlt is not interruptable except at the end of each scan line, so we break things up into chunks in order to maintain reasonable interrupt latency for the IO devices. It takes about 200microsec to move 50 bytes with the display off.
bba: PrincOps.BBTableSpace;
bbt: PrincOps.BBptr = PrincOpsUtils.AlignedBBTable[@bba];
lineWidth: CARDINAL = 16; -- words per scan line: controls interrupt latency
bitsPerLine: CARDINAL = lineWidth*Basics.bitsPerWord;
bytesPerLine: CARDINAL =lineWidth*Basics.bytesPerWord;
lines: CARDINAL = (nBytes-moved)/bytesPerLine; -- bytes left to move with first BitBlt
tail: CARDINAL = (nBytes-moved) MOD bytesPerLine; -- bytes left to move with second BitBlt
bbt^ ← [
dst: [word: toBytes+to.startIndex/2, bit: 0], dstBpl: bitsPerLine,
src: [word: fromBytes+from.startIndex/2, bit: 8], srcDesc: [srcBpl[bitsPerLine]],
width: bitsPerLine, height: lines,
flags: [direction: forward, disjoint: TRUE, disjointItems: TRUE, gray: FALSE,
srcFunc: null, dstFunc: null]];
This BitBlt moves a rectangle that is lineWidth words wide by as many lines high as will fit. NB: It cheats and actually reads a byte from beyond the edge of the rectangle. This is not really legal, but works out OK for any reasonable inplementation of BitBlt.
IF lines ~= 0 THEN PrincOpsUtils.BITBLT[bbt];
update the pointers to reflect the work done, and then move one line that is less than lineWidth words wide.
bbt.dst.word ← bbt.dst.word + lines*lineWidth;
bbt.src.word ← bbt.src.word + lines*lineWidth;
bbt.width ← Basics.bitsPerByte*tail; bbt.height ← 1;
IF tail ~= 0 THEN PrincOpsUtils.BITBLT[bbt];
};
};
Data shuffling
StringInit: PROC [coffset, n: CARDINAL, reloc, dest: POINTER] = {
g: PrincOps.GlobalFrameHandle = PrincOpsUtils.GetReturnFrame[].accesslink;
codebase: LONG POINTER = PrincOpsUtils.Codebase[g] + coffset;
FOR i: CARDINAL IN [0..n) DO (dest+i)^ ← (codebase+i)^ + reloc; ENDLOOP;
};
Long, signed and mixed mode arithmetic:
Number: TYPE = Basics.LongNumber;
DivMod: PROC [n, d: CARDINAL] RETURNS [QR] = LOOPHOLE[Basics.DivMod];
QR: TYPE = RECORD [q, r: INTEGER];
PQR: TYPE = POINTER TO QR;
SignDivide: PROC = {
state: PrincOps.StateVector;
qr: QR;
p: PQR;
t: CARDINAL;
negnum, negden: BOOL;
state ← STATE;
state.stkptr ← t ← state.stkptr-1;
state.dest ← PrincOpsUtils.GetReturnLink[];
state.source ← PrincOps.NullLink;
p ← LOOPHOLE[@state.stk[t-1]];
qr ← p^;
IF negden ← (qr.r < 0) THEN qr.r ← -qr.r;
IF negnum ← (qr.q < 0) THEN qr.q ← -qr.q;
qr ← DivMod[n: qr.q, d: qr.r];
IF negnum ~= negden THEN qr.q ← -qr.q;
IF negnum THEN qr.r ← -qr.r;
p^ ← qr;
RETURN WITH state;
};
DDivMod: PROC [num, den: Number] RETURNS [quotient, remainder: Number] = {
negNum, negDen: BOOLFALSE;
IF LOOPHOLE[num.highbits, INTEGER] < 0 THEN {negNum ← TRUE; num.li ← -num.li};
IF LOOPHOLE[den.highbits, INTEGER] < 0 THEN {negDen ← TRUE; den.li ← -den.li};
[quotient: quotient, remainder: remainder] ← DUnsignedDivMod[num: num, den: den];
IF negNum ~= negDen THEN quotient.li ← -quotient.li;
IF negNum THEN remainder.li ← -remainder.li;
};
DDiv: PROC [a, b: Number] RETURNS [Number] = {
RETURN[DDivMod[a, b].quotient]};
DMod: PROC [a, b: Number] RETURNS [Number] = {
RETURN[DDivMod[a, b].remainder]};
DMultiply: PROC [a, b: Number] RETURNS [product: Number] = {
product.lc ← Basics.LongMult[a.lowbits, b.lowbits];
product.highbits ← product.highbits + a.lowbits*b.highbits + a.highbits*b.lowbits;
};
DUnsignedDivMod: PROC [num, den: Number] RETURNS [quotient, remainder: Number] = {
qq: CARDINAL;
count: [0..31);
lTemp: Number;
IF den.highbits = 0 THEN {
[quotient.highbits, qq] ← Basics.LongDivMod[
LOOPHOLE[Number[num[lowbits: num.highbits, highbits: 0]]], den.lowbits];
[quotient.lowbits, remainder.lowbits] ← Basics.LongDivMod[
LOOPHOLE[Number[num[lowbits: num.lowbits, highbits: qq]]], den.lowbits];
remainder.highbits ← 0;
}
ELSE {
count ← 0;
quotient.highbits ← 0;
lTemp ← den;
WHILE lTemp.highbits ~= 0 DO -- normalize
lTemp.lowbits ← Basics.BITSHIFT[lTemp.lowbits, -1] + Basics.BITSHIFT[lTemp.highbits, 15];
lTemp.highbits ← Basics.BITSHIFT[lTemp.highbits, -1];
count ← count + 1;
ENDLOOP;
IF num.highbits >= lTemp.lowbits
THEN {
subtract off 2^16*divisor and fix up count
div: Number = Number[num[lowbits: 0, highbits: lTemp.lowbits]];
qq ← Basics.LongDiv[num.lc-div.lc, lTemp.lowbits]/2 + 100000B;
count ← count - 1;
}
ELSE qq ← Basics.LongDiv[num.lc, lTemp.lowbits]; -- trial quotient
qq ← Basics.BITSHIFT[qq, -count];
lTemp.lc ← Basics.LongMult[den.lowbits, qq]; -- multiply by trial quotient
lTemp.highbits ← lTemp.highbits + den.highbits*qq;
UNTIL lTemp.lc <= num.lc DO
decrease quotient until product is small enough
lTemp.lc ← lTemp.lc - den.lc;
qq ← qq - 1;
ENDLOOP;
quotient.lowbits ← qq;
remainder.lc ← num.lc - lTemp.lc;
};
};
DUnsignedDiv: PROC [a, b: Number] RETURNS [Number] = {
RETURN[DUnsignedDivMod[a, b].quotient]};
DUnsignedMod: PROC [a, b: Number] RETURNS [Number] = {
RETURN[DUnsignedDivMod[a, b].remainder]};
Other
LongStringCheck: PROC = {
state: PrincOps.StateVector;
tos, index: CARDINAL;
p: POINTER TO LONG STRING;
BoundsFault: PROC = MACHINE CODE {PrincOps.zKFCB, PrincOps.sBoundsFault};
PointerFault: PROC = MACHINE CODE {PrincOps.zKFCB, PrincOps.sPointerFault};
state ← STATE;
tos ← state.stkptr;
index ← state.stk[tos];
p ← LOOPHOLE[@state.stk[tos-2]];
IF p^ = NIL THEN PointerFault[];
IF index >= p^.maxlength THEN BoundsFault[];
state.dest ← PrincOpsUtils.GetReturnLink[];
state.source ← PrincOps.NullLink;
RETURN WITH state;
};
Initialization
Initialize: PROC = {
pSD: POINTER TO ARRAY NAT OF PROC ANY RETURNS ANYLOOPHOLE[PrincOps.SD];
pSD[PrincOps.sBLTE] ← BlockEqual;
pSD[PrincOps.sBYTBLTE] ← ByteBlockEqual;
pSD[PrincOps.sBLTEC] ← BlockEqualCode;
pSD[PrincOps.sBYTBLTEC] ← ByteBlockEqualCode;
pSD[PrincOps.sBLTECL] ← BlockEqualCodeLong;
pSD[PrincOps.sBYTBLTECL] ← ByteBlockEqualCodeLong;
pSD[PrincOps.sBLTEL] ← BlockEqualLong;
pSD[PrincOps.sBYTBLTEL] ← ByteBlockEqualLong;
pSD[PrincOps.sLongDiv] ← DDiv;
pSD[PrincOps.sLongDivMod] ← DDivMod;
pSD[PrincOps.sLongMod] ← DMod;
pSD[PrincOps.sLongMul] ← DMultiply;
pSD[PrincOps.sLongStringCheck] ← LongStringCheck;
pSD[PrincOps.sULongDivMod] ← DUnsignedDivMod;
pSD[PrincOps.sULongMod] ← DUnsignedMod;
pSD[PrincOps.sULongDiv] ← DUnsignedDiv;
pSD[PrincOps.sSignedDiv] ← SignDivide;
pSD[PrincOps.sStringInit] ← StringInit;
};
Initialize[];
END.