InstructionsImpl.mesa
last modified by Levin on September 20, 1983 11:14 am
DIRECTORY
Basics USING [
BITSHIFT, bitsPerByte, bitsPerWord, Byte,bytesPerWord, DIVMOD, LDIVMOD, LongDiv, LongDivMod, LongMult, LongNumber],
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, Codebase, BITBLT, 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
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
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
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
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
{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
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
codebase: LONG POINTER = PrincOpsUtils.Codebase[PrincOpsUtils.GetReturnFrame[].accesslink];
RETURN[ByteBlockEqualLong[p2: codebase+offset, p1: p1, n: n]]
};
PPA: TYPE = POINTER TO PACKED ARRAY [0..0) OF Basics.Byte;
ByteBlockEqualLong: PROC [p1: LONG PPA, n: CARDINAL, p2: LONG PPA] RETURNS [BOOL] = -- BYTBLTEL
{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: LONG POINTER TO PACKED ARRAY [0..0) OF [0..377B);
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];
LDIVMOD: PROC [nlow, nhigh, d: CARDINAL] RETURNS [QR] = LOOPHOLE[Basics.LDIVMOD];
QR: TYPE = RECORD [q, r: INTEGER];
PQR: TYPE = POINTER TO QR;
SignDivide: PROC = {
state: PrincOps.StateVector;
p: PQR;
t: CARDINAL;
negnum, negden: BOOL;
state ← STATE;
state.stkptr ← t ← state.stkptr-1;
state.dest ← PrincOpsUtils.GetReturnLink[];
state.source ← PrincOps.NullLink;
p ← @state.stk[t-1];
IF negden ← (p.r < 0) THEN p.r ← -p.r;
IF negnum ← (p.q < 0) THEN p.q ← -p.q;
p^ ← DIVMOD[n: p.q, d: p.r];
IF negnum ~= negden THEN p.q ← -p.q;
IF negnum THEN p.r ← -p.r;
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] = {
OPEN B: Basics;
qq: CARDINAL;
count: [0..31);
lTemp: Number;
IF den.highbits = 0 THEN {
[quotient.highbits, qq] ← B.LongDivMod[
LOOPHOLE[Number[num[lowbits: num.highbits, highbits: 0]]], den.lowbits];
[quotient.lowbits, remainder.lowbits] ← B.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 ← B.BITSHIFT[lTemp.lowbits, -1] + B.BITSHIFT[lTemp.highbits, 15];
lTemp.highbits ← B.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 ← B.LongDiv[num.lc-div.lc, lTemp.lowbits]/2 + 100000B;
count ← count - 1;
}
ELSE qq ← B.LongDiv[num.lc, lTemp.lowbits]; -- trial quotient
qq ← B.BITSHIFT[qq, -count];
lTemp.lc ← B.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 ← @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 [0..0) OF UNSPECIFIED ← 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.