**Dorado Smalltalk Microcode -- Model 1, XM version
** last edited July 9, 1979 9:58 AM by Deutsch

** By convention, labels starting with ’.’ are used only within the immediate vicinity
** (same subroutine or other local piece of code).

** RBase is maintained as State throughout this code

** last edited on July 14, 1980 3:35 PM by wsh
** changed IFU management so that Pcb is set to -1 iff smalltalk does
*** an IFUJump!!!

Title[DsmallOps.Mc];

Top Level;

KnowRBase[State];


* The Bytecode Interpreter proper

StartByterp:
*here after Bytes or Rtrn/Rend
Call[StartIfu];
Byterp:
SupMod ← T ← T - T - 1;*T ← (-1c)
AOop ← T;
BOop ← T;
Mode ← T - T;
Pcb← T, IfuJump[0];* will do one ifujump

*Load data for stack
* Enter here from byte dispatch with MemBase, RBase(=State), and T/Id set up

* Load instance variable
MIFU[0,17,IFU1[==,SelfBr,I.Vars,=]];
MIFU[17,1,IFU1[==,SelfBr,I.Vars17,=]];

* Load activation variable
MIFU[20,17,IFU1[==,TFrameBr,I.Vars,=]];
MIFU[37,1,IFU1[==,TFrameBr,I.Vars17,=]];

* Load literal
MIFU[40,17,IFU1[==,LocFrameBr,I.Vars,=]];
MIFU[57,17,IFU1[==,LocFrameBr,I.Vars17,=]];
MIFU[76,2,IFU1[==,LocFrameBr,I.Vars36,=]];

* Load indirect through literal
MIFU[100,17,IFU1[==,LocFrameBr,I.IndVars,=]];
MIFU[117,17,IFU1[==,LocFrameBr,I.IndVars17,=]];
MIFU[136,17,IFU1[==,LocFrameBr,I.IndVars36,=]];
MIFU[155,3,IFU1[==,LocFrameBr,I.IndVars55,=]];

* Load from arec
MIFU[160,10,IFU1[==,ArecBr,I.Vars,=]];

* Extended loads (no N, so first ID is alpha)
IFU2[210,SelfBr,I.Vars,17];
IFU2[211,TFrameBr,I.Vars,17];
IFU2[212,LocFrameBr,I.Vars,17];
IFU2[213,LocFrameBr,I.IndVars,17];

I.IndVars:
Fetch ← Id, Branch[.IndVars1];
I.IndVars17:
T ← (Id) + (17c), Branch[IndVars];
I.IndVars36:
T ← (Id) + (36c), Branch[IndVars];
I.IndVars55:
T ← (Id) + (55c), Branch[IndVars];

I.Vars:
T ← Id, B ← Mode, DblBranch[Push, Pull, R Even];
I.Vars17:
T ← (Id) + (17c), Branch[Vars];
I.Vars36:
T ← (Id) + (36c), Branch[Vars];

IndVars:
Fetch ← T;*load @ localframe
.IndVars1:
Arg1 ← Md, Call[Hash];*Hash indirect literal object reference
A ← Mode, Branch[AemVars, R Even];*Only dirty if store
Call[Dirty];*Hash indirect literal object reference, dirty
MemBase ← AemMemBase, Branch[Vars];*duplicated for placement
AemVars:
MemBase ← AemMemBase;
Vars:
A ← Mode, DblBranch[Push, Pull, R Even];

Push:
*Stack to Data
Fetch ← T;
StackP ← (StackP) + 1, T ← Md;

Shove:
*Replace top of stack with T, same as pop and push
Top ← T, Call[RefCkLInc];

RefX4:
T ← StackP;

*Stor:
*T = new stack pointer
*Top = New oop
*Tempframe[Stackp ← T] ← Top

Stor:
MemBase ← TFrameBr;
.Stor1:
StackP ← Fetch ← T;
Arg1 ← Md;
Store ← T, DBuf ← Top, Call[RefCkDec];
Branch[Byterp];

* Load system constants
IFU1[170,TFrameBr,I.ConstM1,17];
IFU1[171,TFrameBr,I.IntConst,0];
IFU1[172,TFrameBr,I.IntConst,1];
IFU1[173,TFrameBr,I.IntConst,2];
IFU1[174,TFrameBr,I.IntConst,12];
IFU1[175,TFrameBr,I.ConstNil,17];
IFU1[176,TFrameBr,I.ConstFalse,17];
IFU1[177,TFrameBr,I.ConstTrue,1];

I.ConstM1:
Top ← T ← OopM1.c, Branch[.Atom];
I.IntConst:
Top ← T ← (Id) + (Oop00.c), Branch[.Atom];
I.ConstNil:
Top ← T ← -1c, Branch[.Atom];
I.ConstFalse:
Top ← T ← FalseOop.c, Branch[.NonAtom];
I.ConstTrue:
Top ← T ← (Id) + (TrueOopm1.c), Branch[.NonAtom];*Id=1

.NonAtom:
Arg1 ← T, Call[RefInc];
.Atom:
T ← (StackP) + 1, MemBase ← TFrameBr, Branch[.Stor1];

*Smash, SmashPop:
*Top = Data to be stored
*Store T into location described by next byte

IFU1[200,TFrameBr,SmashPop,17];
IFU1[201,TFrameBr,Smash,17];
IFU1[202,TFrameBr,Pop,17];

SmashPop:
StackP ← (StackP) - 1;

Smash:
Mode ← (1c);*Resched prevented when Mode#0
Pcb← T - T - 1, IfuJump[0];* did a Smalltalk opcode

Pop:
StackP ← (StackP) - 1;
Pcb← T - T - 1, IfuJump[0];* did a Smalltalk opcode

*Pull Stack to Data--T+MemBase=Addr. of data, Top=new oop to be stored in data

Pull:
Fetch ← T;
Arg1 ← Md;
Store ← T, DBuf ← Top, Call[RefCkDec];*RefD old oop
T ← Top, Call[RefCkLInc];*Do it again, RefI new oop
A ← Mode, Mode ← (0c), Branch[.+2, R Odd];
Branch[RiSubEnd];*placement -- finish subscript store
Pcb← T - T - 1, IfuJump[0];* did a Smalltalk opcode


*Return control to caller (Return from Eval)
* MemBase=TFrameBr (from IFU)

IFUP1[204,TFrameBr,Rend,17];

Rend:
Call[StopIfu];*Smashes MemBase
T ← (StackP) - 1, MemBase ← TFrameBr, Call[DoReturn];
StackP ← (StackP) - (2c);*Can’t do in 1 instr. because of placement conflict
Call[Stash];
Branch[.ReturnEnd];

*Return to sender
* MemBase=ArecBr (from IFU)

IFUP1[203,ArecBr,Rtrn,17];

Rtrn:

Call[StopIfu];*Smashes MemBase
MemBase ← ArecBr;
T ← Senderf.s, Call[DoReturn];*Address sender

.ReturnEnd:
T ← Ctxt, Call[HashL];*Hash current context, dirty...
Call[Dirty];*Only legal after hash call
MemBase ← ArecBr;
BrLo ← T;*T ← Core address of current activation
ArecBase ← T;

* Restore Pcb from arec

T ← MOop00.c;*MOop00.c is -(Oop00.c)
Fetch ← PcF.s;
Pcb ← T + (Md);

* Restore StackP similarly

Fetch ← StackPF.s;
StackP ← T + (Md);

* Restore instance

Fetch ← InstF.s;
T ← Md;
PD ← (T) - (MinAt);
Arg1 ← Md, DblBranch[.NoHash, .DoHash, Carry];

.NoHash:
T ← T - T - 1, Branch[.GotSelf];

.DoHash:
Call[Hash];*T ← C.A.[Arec[Inst]]
Call[Dirty];

.GotSelf:
MemBase ← SelfBr;
BrLo ← T;
SelfBase ← T;
MemBase ← ArecBr;
Fetch ← CodeF.s, Call[HashMd];*get method
Call[MapCode];
MemBase ← ArecBr;
Fetch ← TFrameF.s, Call[HashMd];*get tempframe
Call[Dirty];
MemBase ← TFrameBr;*Referencing TFrame
BrLo ← T; *T ← C.A.[Arec[Tframe]]
TFrameBase ← T;
T ← Name, Call[RefCkLDec];*Zap me
T ← StackP ← (StackP) + 1, MemBase ← TFrameBr;*And push top of old stack on new
Fetch ← T;
Arg1 ← Md;
Store ← T, DBuf ← Top, Call[RefCkDec];
Branch[StartByterp];

*DoReturn--perform an actual control return
*Temp1 ← T = address of context to return to

Subroutine;

DoReturn:
Temp1 ← Fetch ← T;
T ← Ctxt, Ctxt ← Md;*Ctxt ← My sender
Name ← T;*Name ← Current context
Store ← Temp1, DBuf ← AllOnes.c;*Nil my sender
MemBase ← TFrameBr;
Store ← StackP, DBuf ← AllOnes.c, Return;*Nil ref to value (RefI)

Top Level;

*Current--Push current context

IFU1[205,TFrameBr,Current,17];

Current:
StackP ← (StackP) + 1;
T ← Ctxt, Branch[Shove];*Shove takes argument in T

*Super -- send message to superclass

IFU1[206,TFrameBr,Super,17];

Super:
T ← (33c), Branch[NovaCall];*NovaCall 33

SupRet:
Pcb← T - T - 1, IfuJump[0];*Resched prevented when SupMod#nil

*Unimplemented bytecodes

IFUP1[207,TFrameBr,Unimp,17];
MIFU[215,3,IFUP1[==,TFrameBr,Unimp,17]];

Unimp:
Breakpoint;

KnowRBase[State];

*ShortJumps--Control transfer operation of one to eight
* Displacement in T if no IFU

MIFU[220,10,IFUJ1[==,TFrameBr,I.ShortJmp,add[=,2]]];
MIFU[230,10,IFUJ1[==,TFrameBr,I.ShortBfp,add[=,2]]];

I.ShortJmp:
Pcb← T - T - 1, IfuJump[0];*IFU did it

I.ShortBfp:
PD ← (Top) - (FalseOop.c);
StackP ← (StackP) - 1, Branch[.NoJump, Alu=0];*IFU did the jump
T ← (Id) - (PCX’) - 1, Branch[JumpIfu];*Id = Length
.NoJump:
Pcb← T - T - 1, IfuJump[0];

*LongJumps-- Control transfer operations of -1024 to 1023

MIFU[240,10,IFUP2[==,TFrameBr,I.LongJmp,=]];
MIFU[250,10,IFU2[==,TFrameBr,I.LongBfp,=]];

I.LongJmp:
T ← (Id) - (4c);
.LJmp1:
T ← Lsh[T, 10];
T ← (Id) + T;*compute biased displacement
T ← (Id) + T;*add Id = Length
T ← T - (PCX’) - 1, Branch[JumpIfu];

I.LongBfp:
PD ← (Top) - (FalseOop.c);
StackP ← (StackP) - 1, Branch[.+2, Alu=0];
Pcb← T - T - 1, IfuJump[0];*continue in sequence
T ← (Id) - (4c), Branch[.LJmp1];*placement


END;