**Dorado Smalltalk Microcode -- Model 1, XM version
** last edited July 9, 1979 10:14 AM by Deutsch
** last edited September 20, 1982 10:08 AM by Haugeland
** Interface to the Nova emulator and the IFU
Title[DsmallInt.MC];
Top Level;
M[TrapXIfuReg, IFUReg[#1, 2, MDS, rbAemRegs, #2, 17, 0, 0]];
M[TrapXIfuPause, IFUPause[#1, 2, MDS, rbAemRegs, #2, 17, 0, 0]];
InsSet[AEmuInsSet, 1];* assemble as Alto emulator instruction
*** All Smalltalk returns to the Nova emulator come through here:
*EMStart is the normal entry point to the Emulator
EMStart:
Q← T, Call[RestoreEMState], global;
Call[StopIfu];
T← Q, Branch[Start];*Start is the emulator’s actual entry point
*EMTrap is the entry point to the Emulator trap
*EMTrap:
*Call[RestoreEMState];
*Branch[NPTrap];*Go to emulator trap
* Subroutine to restore Emulator state
Subroutine;
RestoreEMState:
RBase ← RBase[Arg1];
T ← Arg1, RBase ← RBase[AemRegs];
StkP← spAC0;
Stack ← T, Return;* don’t need to set membase
Top Level;
*EMRun is for trapped instructions that don’t want Ac0 ← Arg1
** not needed any more
**EMRun:
**RBase ← RBase[AemRegs];
**MemBase ← AemMemBase;
**Branch[Start];
*** The Nova emulator enters Smalltalk here:
* RBase = AemRegs, IReg = trapped instruction
** After dispatching, control arrives at the instruction code with
** RBase = AemRegs except for NovaReturns, which get RBase = State
** replaced by ifujumps to the various labels dispatched to below
%
TrapX:
KnowRBase[AEMRegs];
T ← Ac0;
Arg1 ← T;
T ← Ldf[Ireg, 4, 10];*Ireg has actual instruction//load low 4 bits
BigBDispatch ← T;
Branch[OpCodeDispatch];
OpCodeDispatch:
Branch[Extract],At[Dispatch1, 0];*700xx
Branch[Inject],At[Dispatch1, 1];*704xx
Branch[RefCt],At[Dispatch1, 2];*710xx
Branch[Trap],At[Dispatch1, 3];*714xx
Branch[Sundry],At[Dispatch1, 4];*720xx
Branch[ReadWriteR],At[Dispatch1, 5];*724xx
Branch[Trap],At[Dispatch1, 6];
Branch[Trap],At[Dispatch1, 7];
Branch[Trap],At[Dispatch1, 10];
Branch[Trap],At[Dispatch1, 11];
Branch[StartByterp],At[Dispatch1, 12];*750xx
Branch[Trap],At[Dispatch1, 13];
Branch[Trap],At[Dispatch1, 14];
Branch[Trap],At[Dispatch1, 15];
Branch[NovaRet],At[Dispatch1, 16];*770xx
Branch[Trap],At[Dispatch1, 17];
%
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
StartByterp0:
Call[GetPC];
SavPc← T + 1;* save Alto PC
StkP← spAC0;
T← Stack;
Arg1← T,branch[StartByterp];
TrapXIfuReg[172, StartByterp0];* 75000-75377
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Sundry:*720xx arrive here, i.e. Nova special ops
StkP← spAC0;* preserve AC0
T← Stack;
Arg1← T;
call[GetPC], ETemp← Id;
SavPc← T + 1;
BDispatch ← ETemp;*dispatch is on low 3 bits only
Branch[OpsDispatch];
TrapXIfuPause[164, Sundry];* 72000-72377
OpsDispatch:
Branch[IvalTP],At[Dispatch2, 0];
Branch[HashT],At[Dispatch2, 1];
Branch[Snat],At[Dispatch2, 4];
** end Sundry code
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*NovaCall and NovaRet--Call to Nova from microcode and return
DontKnowRbase;
NovaCall:
MemBase ← MDS;
T ← T + (400c);*Page1BR has disappeared
T ← T + (130c);*Load trap op from T (400+130+T)
Fetch ← T;
RBase ← RBase[AemRegs];
T ← Ireg;
SavDisp ← T;
T ← Md, Branch[EmStart];*jump into trap vector (to Nova Instrs.)
KnowRBase[AemRegs];
NovaRet:
ETemp1 ← (Id) and (37C);*Get low 4 bits of Alto instruction
call[GetPC];
SavPc← T + 1;
StkP← spAC0;
T← Stack;
Arg1← T;
call[StartIFU];* start IFU for smalltalk
RBase ← RBase[SavDisp];
T ← SavDisp, RBase ← RBase[AemRegs];
Ireg ← T;
BDispatch ← ETemp1;
RBase ← RBase[State], Branch[NovaReturns];
TrapXIfuPause[176, NovaRet];*77000-77377
NovaReturns:
Branch[OvRet],At[Dispatch8, 0];
Branch[FiRet],At[Dispatch8, 1];
Branch[FltRet],At[Dispatch8, 2];
Branch[AllocRet],At[Dispatch8, 3];
Branch[PrimFail],At[Dispatch8, 4];
Branch[SndMsg],At[Dispatch8, 5];
Branch[SupRet],At[Dispatch8, 6];
Branch[PrimRet],At[Dispatch8, 7];
FetchTPc:
Fetch ← T;
T ← Md, Branch[EMStart];
*ReadR/WriteR: Read an R register, with argument in Ac0--returns in/stores Ac1
KnowRBase[AemRegs];
ReadWriteR:
ETemp1← Id;
StkP← spAC0;
ETemp1, DblBranch[.ReadR, .WriteR, R Even];*0=Read, 1=Write
.ReadR:
BigBDispatch ← Stack&+1;
RBase ← RBase[State], Branch[.RRegs];
.WriteR:
KnowRBase[AemRegs];
BigBDispatch ← Stack&+1;
T ← Stack, RBase ← RBase[State], Branch[.WRegs];
.RRegs:
Nop,At[Dispatch9, 0];*to make dispatch work
Breakpoint,At[Dispatch9, 14];
Branch[.ReadPcb],At[Dispatch9, 15];
T ← AOop, Branch[.ReadOut],At[Dispatch9, 17];
T ← Name, Branch[.ReadOut],At[Dispatch9, 44];
RBase ← RBase[ACoreBase],At[Dispatch9, 52];
T ← ACoreBase, Branch[.ReadOut]; KnowRBase[State];
T ← Father, Branch[.ReadOut],At[Dispatch9, 53];
T ← MinAt, Branch[.ReadOut],At[Dispatch9, 54];
Breakpoint,At[Dispatch9, 55];
Breakpoint,At[Dispatch9, 56];
RBase ← RBase[ArecBase],At[Dispatch9, 60];
T ← ArecBase, Branch[.ReadOut]; KnowRBase[State];
Branch[.ReadSavDisp],At[Dispatch9, 62];
RBase ← RBase[BCoreBase],At[Dispatch9, 63];
T ← BCoreBase, Branch[.ReadOut]; KnowRBase[State];
T ← Name, Branch[.ReadOut],At[Dispatch9, 64];
T ← BOop, Branch[.ReadOut],At[Dispatch9, 65];
RBase ← RBase[LocFrameBase],At[Dispatch9, 66];
T ← LocFrameBase, Branch[.ReadOut]; KnowRBase[State];
T ← StackP, Branch[.ReadOut],At[Dispatch9, 67];
T ← Top, Branch[.ReadOut],At[Dispatch9, 70];
Branch[.ReadCaddr],At[Dispatch9, 71];
RBase ← RBase[TFrameBase],At[Dispatch9, 72];
T ← TFrameBase, Branch[.ReadOut]; KnowRBase[State];
RBase ← RBase[SelfBase],At[Dispatch9, 73];
T ← SelfBase, Branch[.ReadOut]; KnowRBase[State];
T ← SupMod, Branch[.ReadOut],At[Dispatch9, 74];
T ← Ctxt, Branch[.ReadOut],At[Dispatch9, 76];
.WRegs:
Nop,At[Dispatch10, 0];*to make dispatch work
MemBase ← CoreBaseBr, Branch[.WBr],At[Dispatch10, 14];
Branch[.WritePcb],At[Dispatch10, 15];
AOop ← T, Branch[.JumpOut],At[Dispatch10, 17];
Name ← T, Branch[.JumpOut],At[Dispatch10, 44];
ACoreBase ← T,At[Dispatch10, 52];
MemBase ← ACoreBr, Branch[.WBr];
*Father ← T, Branch[.JumpOut],At[Dispatch10, 53];
Father ← T,At[Dispatch10, 53];
** this initialization used to be done in the .mb file loaded by midas
** but with etherloading, that doesn’t work
** WriteReg #53 is the first time Smalltalk touches the ucode!!!
Pcb← T - T - 1;
T← Rot0Em.c;
Rot0Em← T;
T← RotCMsk.c;
RotCMsk← T;
MemBx← 0S;
Branch[.JumpOut];
MinAt ← T, Branch[.JumpOut],At[Dispatch10, 54];
MemBase ← PmBaseBr, Branch[.WBr],At[Dispatch10, 55];
Branch[.WriteRotBase],At[Dispatch10, 56];
ArecBase ← T,At[Dispatch10, 60];
MemBase ← ArecBr, Branch[.WBr];
Branch[.WriteSavDisp],At[Dispatch10, 62];
BCoreBase ← T,At[Dispatch10, 63];
MemBase ← BCoreBr, Branch[.WBr];
Name ← T, Branch[.JumpOut],At[Dispatch10, 64];
BOop ← T, Branch[.JumpOut],At[Dispatch10, 65];
LocFrameBase ← T,At[Dispatch10, 66];
MemBase ← LocFrameBr, Branch[.WBr];
StackP ← T, Branch[.JumpOut],At[Dispatch10, 67];
Top ← T, Branch[.JumpOut],At[Dispatch10, 70];
Branch[.WriteCaddr],At[Dispatch10, 71];
TFrameBase ← T,At[Dispatch10, 72];
MemBase ← TFrameBr, Branch[.WBr];
SelfBase ← T,At[Dispatch10, 73];
MemBase ← SelfBr, Branch[.WBr];
SupMod ← T, Branch[.JumpOut],At[Dispatch10, 74];
Ctxt ← T, Branch[.JumpOut],At[Dispatch10, 76];
TrapXIfuPause[165, ReadWriteR];* 72400-72777
.ReadPcb:
RBase ← RBase[Pcb];
T ← Pcb, Branch[.ReadOut];
.WritePcb:
RBase ← RBase[AemRegs];
StkP← spAC1;
T ← Stack&+2;
Pcb ← T, Branch[.rjo];
KnowRBase[State];
.WriteRotBase:
RBase ← RBase[Hash];
T ← T - (RotCMsk);*address end of Rot
RotBase ← T;
MemBase ← RotBaseBr;
BrLo ← T;
RBase ← RBase[AemRegs];
T ← (EmuBrHiReg) - 1;*always carries into high part
BrHi ← T, Branch[.Initialize];
KnowRBase[State];
.ReadSavDisp:
Breakpoint;*Smalltalk not allowed to read SavDisp
.WriteSavDisp:
PD ← T - (207c);
Branch[.+2, Alu=0];
Breakpoint;*Smalltalk only allowed to write 207 (SendAgain) into SavDisp
SavDisp ← T - T - 1, Branch[.JumpOut];*see FetchMsg/.Nid1 for why -1
.ReadCaddr:
RBase ← RBase[Caddr];
T ← Caddr, Branch[.ReadOut];
.WriteCaddr:
RBase ← RBase[AemRegs];
StkP← spAC1;
T ← Stack&+2;
Caddr ← T, Branch[.rjo];
.WBr:
BrLo ← T, Branch[.JumpOut];
.ReadOut:
RBase ← RBase[AEMRegs];
StkP← spAC0;
Stack&+3 ← T;
.rjo: T← Stack, branch[start];*Jmp @.return (StkP -> AC3)
.JumpOut:
RBase ← RBase[AEMRegs];
StkP← spAC3, branch[.rjo];
*Initialize: set all base register high bytes to zero (i.e., use low 64K)
.Initialize:
RBase ← RBase[AemRegs];
T ← EmuBrHiReg;
MemBase ← CoreBaseBr,Call[.LoadBrHi];
MemBase ← PmBaseBr,Call[.LoadBrHi];
MemBase ← LocFrameBr,Call[.LoadBrHi];
MemBase ← ArecBr,Call[.LoadBrHi];
MemBase ← TFrameBr,Call[.LoadBrHi];
MemBase ← SelfBr,Call[.LoadBrHi];
MemBase ← ACoreBr,Call[.LoadBrHi];
MemBase ← BCoreBr,Call[.LoadBrHi];
MemBase ← NamesBr,Call[.LoadBrHi];
MemBase ← CodeBr,Call[.LoadBrHi];
call[InitIFU];
Branch[.JumpOut];
Subroutine;
.LoadBrHi:
BrHi ← T, Return;
Top Level;
*Nova Hash trap
HashT:
RBase ← RBase[State];
Call[Hash];
AT[NHRetLoc!],*So failure will go to HFail0
RBase ← RBase[SavPc];
SavPc ← (SavPc) + 1, RBase ← RBase[Hash];
Q ← MyTemp;*old Rot0 left here
Residue ← (Residue) - (RpcBit.c);*undo final inc of reprobe count
T ← LDF[Residue, 3, 10], Branch[.LoadAcs];*extract reprobe count
HFail0:*Hash leaves reprobe count in MyTemp
RBase ← RBase[Hash];
T ← Rsh[Residue, 13];
T ← A ← MyTemp, Q ← B ← T, Branch[.LoadAcs];*Ac3 ← Res for miss (Insert)
.LoadAcs:
rbase← rbase[AemRegs];
StkP← spAC3;
Stack&-2 ← Q;*Ac3 ← old Rot0 for hits, Residue for misses
Stack&+1 ← T;*AC1← reprobe count
rbase← rbase[RotA];
T ← RotA;
T ← (RotBase) + T;
Stack ← T, RBase ← RBase[SavPc];
T ← SavPc, Branch[EMStart];
*Nova RefCt trap
KnowRBase[AemRegs];
RefCt:
StkP← spAC0;
T← Stack;
Arg1← T;
Call[GetPC], ETemp← Id;* 2nd byte of Alto instruction
SavSp ← T + 1;
A ← ETemp, RBase ← RBase[State], Branch[.NRefD, R Odd];
Nop;*Placement
Call[RefCkInc];*ETemp even, do RefI
Branch[RsSp];
TrapXIfuPause[162, RefCt];* 71000-71377
.NRefD:
Call[RefCkDec];*ETemp odd, do RefD
RsSp:
RBase ← RBase[SavSp];
T ← SavSp, Branch[EMStart];
*Nova Ival trap
KnowRBase[AemRegs];
IvalTp:*Assumes RBase = AemRegs
Rbase← Rbase[SavPc];
SavSp← SavPc;
RBase ← RBase[State];
Call[Ival];
AT[NIRetLoc!],*So failure in Ival will go to PrimFail
Arg1 ← T, Branch[RsSp];*EMStart does Ac0 ← Arg1
*Skip not Atom
KnowRBase[AemRegs];
Snat:
T ← Stack, RBase ← RBase[MinAt];* stack = AC0
PD ← T - (MinAt);
RBase ← RBase[SavPc], Branch[NotAt, Carry];
T← (SavPc) + 1, branch[EMStart];* do skip return
NotAt:
T← SavPc, branch[EMStart];
*Extract // 70000 Disp = <Width><Shift>
KnowRBase[AemRegs];
Extract:
Stkp← spAC0;
Call[.LoadLshWidth];*Execute common code
T ← (20c);
T ← T - (ETemp1);*ETemp1 is LShift
T ← T and (17c);
T ← Lsh[T, 10];*Put it in SC field of SHC
T ← (T) + (20c);
T ← (T) - (ETemp2);*Low nybble is LMask=(16-Width)
SHC ← T;*Load shift control word
T ← ShiftLMask[Stack];* shift AC0
Stack+1 ← T, IFUJump[0];* result in AC1
TrapXIfuReg[160, Extract];* 70000-70377
*Inject // 70400 Disp = <Width><Shift>
KnowRBase[AemRegs];
Inject:
Stkp← spAC0;
Call[.LoadLshWidth];*Execute common code
T ← Lsh[ETemp1, 10];*Put LShift in SC
MyTemp ← Rsh[T, 4];
T ← T + (MyTemp);*Put LShift in RMask
T ← (T) + (20c);
T ← (T) - (ETemp2);
T ← (T) - (ETemp1);*Put 16-(Width+LShift) in LMask
MyTemp ← T - (SHC ← T) - 1;*Load SHC, also set MyTemp ← -1
T ← ShiftBothMasks[MyTemp];*Put 1’s in field of zeros
Stack ← (Stack) and Not (T);*put zeroes in field of Ac0
Stack&+1;
T ← ShiftBothMasks[Stack];*Use same WF parameter
Stack&-1;
Stack ← (Stack) or (T), IFUJump[0];*Lor in Ac1
TrapXIfuReg[161, Inject];* 70400-70777
Subroutine;
KnowRBase[AemRegs];
.LoadLshWidth:
T ← Id;
ETemp1 ← Ldf[T, 4, 0];*Lshift field--low nybble of low byte
ETemp2 ← Ldf[T, 4, 4], Return;*Width field--high nybble of low byte
Top Level;
** IFU interface
** IFU exceptional conditions
IfuFault:
AT[200], Breakpoint;
IfuFGParity:
AT[204], Breakpoint;
IfuReschedule:
AT[214], Branch[ReSched];
IfuNotReady:
AT[234], IfuJump[0];
IfuRamPE:
AT[274], Breakpoint;
ReSched:
NoReschedule;*do first to handle race with higher priority tasks
RBase ← RBase[AemRegs];
PD ← Nww, RBase ← RBase[State], Branch[.DontInt, R<0];*interrupts disabled
PD ← Mode, Branch[.DontInt1, Alu=0];*no interrupt
PD ← (SupMod)+1, RBase ← RBase[IfuState], Branch[.DontInt2, Alu#0];*don’t separate Smash and following bytecode
T ← 2c, Branch[.DontInt3, Alu#0];*don’t separate Super and following byte
Q← T;*trap to loc. 2 to handle interrupt
Call[StopForReSched];
T← Q, RescheduleNow, Branch[EMStart];*so Nova emulator will notice Nww
.DontInt2:
Reschedule, Branch[.TestLoad];*interrupt again after current bytecode
.DontInt3:
Reschedule, Branch[.TestLoad];*interrupt again after current bytecode
.DontInt:
RBase ← RBase[IfuState], Branch[.TestLoad];
.DontInt1:
RBase ← RBase[IfuState], Branch[.TestLoad];
.TestLoad:
A ← Pcb, Branch[.Loaded, R<0];*check if reload required
Nop;*placement
Call[StartIfu];
Pcb← T - T - 1, IfuJump[0];* know rbase is OK
.Loaded:
T ← Not (PCX’), Branch[JumpIfu];*do preempted instruction
* Load state and start IFU, if not already fetching
Subroutine;
StartIfu:
NoReschedule;
RBase ← RBase[IfuState];
InsSetOrEvent← SmtInset;
MemBX← 0S;
T ← (Pcb) rsh 1, Branch[.Started, R<0];
T ← (Caddr) - T, MemBase ← CodeBr;*compute code base
Brlo ← T;
CodeBase ← T;
PCF ← Pcb;
.Started:
ReSchedule;
RBase ← RBase[State], Return;*Pcb is set to -1 by IFUJump[0]
* Set code base to T
MapCode:
RBase ← RBase[IfuState];
Caddr ← T, MemBase ← LocFrameBr;
T ← T + (3c);
BrLo ← T;
LocFrameBase ← T;
T ← (Pcb) rsh 1;
Caddr ← (Caddr) + T, RBase ← RBase[State], Return;
* Stop IFU and unload state, if not already stopped
*** Not OK to StopIfu if no IfuJump since last StartIfu
** if Pcb<0 then we know Smalltalk has done an IFUJump[0]
StopIfu:
RBase ← RBase[IfuState];
A ← Pcb, MemBase ← CodeBr, Branch[.Stopped, R>=0];
Pcb ← (Id) - (PCX’) - 1, Branch[.GetCaddr];* Id = length
StopForReSched:*same as StopIfu, but Pcb=PCX
RBase ← RBase[IfuState];
A ← Pcb, MemBase ← CodeBr, Branch[.Stopped1, R>=0];
Pcb ← Not (PCX’);
.GetCaddr:
T ← (Pcb) rsh 1;
Caddr ← (CodeBase) + T;
.Stopped:
RBase ← RBase[State], Return;
.Stopped1:
RBase ← RBase[State], Return;
* Initialize the IFU
DontKnowRBase;
InitIfu:
T ← 100400c;*instruction set 1
SmtInset ← T, Return;
Top Level;
* Set PCF to T and restart IFU
JumpIfu:
PCF ← T;
Nop;*hardware glitch
IfuJump[0];