-- MicroFloat.mesa
-- Copywrite Xerox Corporation 1980
-- mesa interface to floating point microcode
-- L. Stewart March 11, 1980 1:49 PM
DIRECTORY
ControlDefs: FROM "ControlDefs" USING [StateVector],
ImageDefs: FROM "ImageDefs" USING [AddCleanupProcedure, CleanupItem, CleanupMask],
MiscDefs: FROM "MiscDefs" USING [DestroyFakeModule],
Mopcodes: FROM "Mopcodes" USING [zDCOMP, zJRAM, zLIW],
NovaOps: FROM "NovaOps" USING [CleanupReason],
RamDefs: FROM "RamDefs" USING [LoadRamAndBoot, MuImage],
RealDefs: FROM "RealDefs" USING [Fix, FloatingError],
SDDefs: FROM "SDDefs",
SegmentDefs: FROM "SegmentDefs" USING [FileSegmentAddress, FileSegmentHandle, SwapIn, SwapOut, Unlock],
uFloatDefs: FROM "uFloatDefs" USING [uCodeImage];
MicroFloat: PROGRAM IMPORTS ImageDefs, MiscDefs, RamDefs, RealDefs, SegmentDefs, uFloatDefs EXPORTS RealDefs =
BEGIN

uLocFloat: CARDINAL = 540B;
-- uLocFix: CARDINAL = 541B;
uLocMul: CARDINAL = 542B;
uLocDiv: CARDINAL = 543B;
uLocAdd: CARDINAL = 544B;
uLocSub: CARDINAL = 545B;
sFError: CARDINAL = 137B;
firstTime: BOOLEAN ← TRUE;
cleanUpItem: ImageDefs.CleanupItem ← [NIL, ImageDefs.CleanupMask[Restore]+ImageDefs.CleanupMask[Restart], FloatCleanup];
uImageFile: SegmentDefs.FileSegmentHandle;
uImageOffset: CARDINAL;
FloatingPointError: PUBLIC SIGNAL[f: RealDefs.FloatingError] = CODE;
-- Here are the procedures which call the mircrocode. The only reason for having two copies is that we cannot export MACHINE CODE things directly.
FFix: PROCEDURE [a: REAL] RETURNS [LONG INTEGER] =
BEGIN
RETURN[RealDefs.Fix[a]];
END;
-- These procedures take REALs as arguments, but since we are just stuffing them into SD, they are not actually declared correctly. The compiler produces slightly less code this way.
-- PROCEDURE [LONG INTEGER] RETURNS [REAL]
zFloat: PROCEDURE =
BEGIN
zzFloat[];
END;
zzFloat: PROCEDURE =
MACHINE CODE BEGIN
Mopcodes.zLIW, uLocFloat/256, uLocFloat MOD 256;
Mopcodes.zJRAM;
END;
-- PROCEDURE [a, b: REAL] RETURNS [INTEGER]
FComp: PROCEDURE =
BEGIN
zFComp[];
END;
zFComp: PROCEDURE =
MACHINE CODE BEGIN
Mopcodes.zDCOMP;
END;
-- PROCEDURE [a,b: REAL] RETURNS [REAL]
FMul: PROCEDURE =
BEGIN
zFMul[];
END;
zFMul: PROCEDURE =
MACHINE CODE BEGIN
Mopcodes.zLIW, uLocMul/256, uLocMul MOD 256;
Mopcodes.zJRAM;
END;
FDiv: PROCEDURE =
BEGIN
zFDiv[];
END;
zFDiv: PROCEDURE =
MACHINE CODE BEGIN
Mopcodes.zLIW, uLocDiv/256, uLocDiv MOD 256;
Mopcodes.zJRAM;
END;
FAdd: PROCEDURE =
BEGIN
zFAdd[];
END;
zFAdd: PROCEDURE =
MACHINE CODE BEGIN
Mopcodes.zLIW, uLocAdd/256, uLocAdd MOD 256;
Mopcodes.zJRAM;
END;
FSub: PROCEDURE =
BEGIN
zFSub[];
END;
zFSub: PROCEDURE =
MACHINE CODE BEGIN
Mopcodes.zLIW, uLocSub/256, uLocSub MOD 256;
Mopcodes.zJRAM;
END;
-- This procedure is called directly by the microcode on error, with the stack set appropriately. By means of the complicated ControlDefs stuff, it is possible to RESUME an operation which trapped. FError tries to return a reasonable value on RESUME - a vary large positive or negative value on exponent overflow, for example.
FError: PROCEDURE [a: INTEGER, f: INTEGER] =
BEGIN
state: ControlDefs.StateVector;
nf: RealDefs.FloatingError;
state ← STATE;
nf ← SELECT f FROM
1 => FixRangeOverflow,
2, 4, 5, 6 => ExponentOverflow,
3 => DivideBy0,
ENDCASE => ERROR;
SIGNAL FloatingPointError[nf];
state.instbyte ← 0;
state.dest ← state.source;
state.source ← 0;
SELECT f FROM
1, 2, 3 =>
BEGIN
IF a>=0 THEN
BEGIN
state.stk[state.stkptr] ← 177777B;
state.stk[state.stkptr+1] ← 077777B; -- Most positive
END
ELSE
BEGIN
state.stk[state.stkptr] ← 000001B;
state.stk[state.stkptr+1] ← 100000B; -- Most negative (almost)
END;
state.stkptr ← state.stkptr+1;
END;
4 => state.stk[state.stkptr] ← LAST[CARDINAL];
5 => state.stk[state.stkptr] ← FIRST[CARDINAL];
6 => state.stk[state.stkptr] ← IF a>=0 THEN LAST[INTEGER] ELSE FIRST[INTEGER];
ENDCASE;
state.stkptr ← state.stkptr+1;
RETURN WITH state; -- return from floating operation
END;
InitFloat: PUBLIC PROCEDURE =
BEGIN
IF firstTime THEN
BEGIN
firstTime ← FALSE;
-- The following piece of incredible code is intended to load the RAM from a RamImage created by PackMu and converted into a .bcd by the TableCompiler
-- First convert the module into a file segment.
[uImageFile, uImageOffset] ← MiscDefs.DestroyFakeModule[ LOOPHOLE[uFloatDefs.uCodeImage]];
ImageDefs.AddCleanupProcedure[@cleanUpItem];
END;
LoadRam[];
-- Copy procedure descriptors into system dispatch table.
SDDefs.SD[SDDefs.sFADD] ← FAdd;
SDDefs.SD[SDDefs.sFSUB] ← FSub;
SDDefs.SD[SDDefs.sFMUL] ← FMul;
SDDefs.SD[SDDefs.sFDIV] ← FDiv;
SDDefs.SD[SDDefs.sFCOMP] ← FComp;
SDDefs.SD[SDDefs.sFLOAT] ← zFloat;
SDDefs.SD[SDDefs.sFIX] ← FFix;
SDDefs.SD[sFError] ← FError;
END;
LoadRam: PROCEDURE =
BEGIN OPEN RamDefs, SegmentDefs;
uCode: MuImage;
-- Swapin and lock the file segment containing the microcode.
SwapIn[uImageFile];
-- a MuImage is a pointer to a .br file produced by PackMu, the TableCompiler strips of the 20B word header, so we must add it back on. With luck, this won’t be below location 20B...
uCode ← LOOPHOLE[ LOOPHOLE[ FileSegmentAddress[uImageFile], CARDINAL] + uImageOffset - 20B, MuImage];
IF LoadRamAndBoot[uCode,FALSE]~=0 THEN ERROR;
Unlock[uImageFile];
SwapOut[uImageFile];
END;
FloatCleanup: PROCEDURE [why: NovaOps.CleanupReason] =
BEGIN
LoadRam[];
END;
-- Main Line Code
InitFloat[];
END.