**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];