:TITLE[Initialize];
%
Ed Fiala 16 May 1984: add gcZCT ← 3C for Cedar.
Ed Fiala 12 March 1982: add display of no. ’good’ pages from Initial,
new 10 mb Ethernet DTab entries; delete old 10 mb Ethernet DTab entries;
increase viewing delay; default init for vCrystal; conditionals on DTab
entries; device init changes from AMesa system; preserve refresh sequence
from Initial and use normal refresh during LoadRAM calls; eliminate
InitPage2, initpc, contemp, rlink0; speed up PNIP.
Ed Fiala 19 February 1982: delete unused MP code defs, removed
ErrorCnt, ErrorCountx, initr0-3; changed StartDeviceInit and NPages MP
codes with delay so they can be seen.
%
RV[xCNT,20];
*Used everywhere
RV[DevIndex,21];
*Used in DeviceInit
RV[Assigned,23];
*Used in DeviceInit
RV[rbuf0,24];
*Used in DeviceInit
RV[rbuf1,25];
*Used in DeviceInit

MC[NextDiskAddr,237];
RV[BootDiskAddr,37];

*Machine initialization begins here; start here from any task.
SoftStart:
xCNT ← HiA[Task0StartLoc], At[SoftStartLoc];

OnPage[InitPage];

xCNT ← (xCNT) or (LoA[Task0StartLoc]), GoTo[InitNotify];

Qtask:
xCNT ← LoA[Qloc], At[Task0StartLoc];*Quiesce tasks 15b to 1
xCNT ← (xCNT) or (HiA[Qloc,15]);
DevIndex ← IP[DevIndex]C;
StkP ← DevIndex;
DevIndex ← LoA[QretLoc];
DevIndex ← (DevIndex) or (HiA[QretLoc]);
InitNotify:
Qloop:
APCTask&APC ← xCNT;
initRET:
Return;*goes to Qx

*Notify comes here. Leave task’s TPC pointing at Qxy.
Qx:
APCTask&APC ← Stack, Call[PFExit], At[Qloc];
*BadWakeup is on a non-overlaid page so that it will remain intact after
*initialization has completed. Wakeup from a device that isn’t reset
*properly comes here.
BadWakeup:
SetFault, GoTo[.];


Qret:
LU ← LdF[xCNT,0,3], At[QretLoc]; *xCNT points to this location
xCNT ← (xCNT) - (10000C), GoTo[Qloop,ALU#0];

*Zap devices.
T ← RTemp1 ← 177400C;
RTemp ← 300C;
ZapDloop1:
Output[RTemp];*send 300 to register 0 of all devices
T ← RTemp1 ← (RTemp1) + (20C);
GoTo[ZapDloop1,ALU<0];
T ← RTemp1 ← 177400C;
RTemp ← 0C;
ZapDloop2:
Output[RTemp];*send 0 to register 0 of all devices
T ← RTemp1 ← (RTemp1) + (20C);
GoTo[ZapDloop2,ALU<0];

*During the Pilot1 LoadRAM, xfTemp1 was a state variable for inline refresh.
*Put xfTemp1 back into Refr for regular refresh using a 2560 cycle timer.
*This avoids having some RAMs go unrefreshed for a longer than normal period.
T ← (xfTemp1) and not (17C);
xfTemp1 ← IP[Refr]C;
StkP ← xfTemp1;
Stack ← T;
*xfTemp1 odd for normal refresh on the LoadRAM calls during device init.
*Also need RTemp1 even (to believe starting address) and ZapDloop2 above
*leaves it even.
xfTemp1 ← 1C;
xCNT ← HiA[TimerInitLoc,16];
xCNT ← (xCNT) or (LoA[TimerInitLoc]), Call[InitNotify];

*Before initializing and starting devices, set up the RM locations that
*must be valid.

RegInit1:
R400 ← 400C;
AllOnes ← (Zero) - 1;
RZero ← Zero;

LoadPage[PNIPPage];
T ← StartDeviceInit, CallP[PNIP];
*Wait ~0.4 seconds for user to view StartDeviceInit 0104. Outer loop
*repeats 32k times, so wait ~ 32k*123 cycles.
xBuf ← 0C, Call[MPWait];
xBuf ← (xBuf) + 1, GoTo[MPWait,R>=0];

*Default vCrystal to 240b (.eq. 320d/2), appropriate with a 40 mHz processor
*crystal. If the Dolphin has a UTVFC, then DisplayInit.Mc will correct the
*value in vCrystal for 44.5 mHz or 50 mHz crystals.
xBuf ← IP[vCrystal]C;
StkP ← xBuf;
Stack ← 240C;

*Get next disk address from reg 237 and save in reg 37.
xCNT ← NextDiskAddr;
StkP ← xCNT;
T ← Stack;
BootDiskAddr ← T, GoTo[DeviceInit];

*Delay subroutine executes 121d (=7+2*(56+1)) cycles before returning.
MPWaita:
xBuf1 ← 70C, Skip;
MPWait:
xBuf1 ← 70C;
xBuf1 ← (xBuf1) - 1, GoTo[.,R>=0];
Return;

%Find and initialize all I/O devices:

The idea is to use RM 40-57 as a "slot table" with one entry per potential
I/O controller. First the table is filled with dummy controller addresses,
and these are clocked out to the controllers. Then, each controller is
interrogated for it’s Device ID, and these names are put in the table.
Then, for each slot, the device ID is looked up in a table (in IMX) of
potential devices, and if a match is found, the entry from the device table
is put into the slot table. A device table entry consists of the uPC value
of the device’s initialization routine (12 bits), and the task number for the
controller. When all slots have been looked up, the task numbers are clocked
out to the controllers, and the associated initialization routines are called
in turn.

To add a new controller to the system, simply add both a device table entry
here and its driver microcode. Note that each entry should be CONDITIONAL
upon having suitable microcode at the device’s starting address to avoid a
wild branch. A system with a DTAB entry but no driver microcode won’t run
on a Dolphin which has the device.

NOTE: ORDERING IS IMPORTANT HERE; CURRENT ORDER IS NOT DEBUGGED VERY WELL.
There are some problems:
1) The table is overlying RM 40-57 and DisplayInit.Mc may call LoadRAM.
When the old LoadRAM is called, xBuf-xBuf3 (RM 44-47) and RTemp (RM 52) will
be smashed and RTemp1 (RM 53) must be even. This means that the UTVFC must
be in chasis slots 0 to 3 or table entries not yet handled will be smashed.
For the new LoadRAM, yBuf-yBuf2 (50-52) will be smashed, so the UTVFC must be
in chasis slots 0 to 7. For both old and new LoadRAM, there must be fewer
than 13b controller mounted or RTemp1 might not be even (this could be fixed
easily).
2) ?The order in which tasks are used up is important when allowing a
device such as the CDC to coexist with the ethernet controllers. Here it
is (?) necessary for the CDC DTab entry to appear before the Ethernet
controller entries (or is it necessary for its board to be above?) so that
its task won’t be used up when it is initialized. Maybe there should be
several cdcTasks to allow more flexibility? This needs some thought.
%
*First, a macro to allow nice formatting of the device table entries...
Macro[DTab,IFE[#4,0,,IMData[LH[#1] RH[LShift[#2,4],#3] At[DTabLoc]]
Set[DTabLoc,Add[DTabLoc,1]]]];

Set[DTabBase,Add[InitBase,100]];
Set[DTabLoc,DTabBase];

*DTab[5400,FDinit,FDtask,1];
*IFDC (floppy disk--not supported)

*Since DES and FP boards have no init microcode, assemble unconditionally.
DTab[12000,0,0,1];
*DES board
DTab[127000,0,0,1];
*FP board

DTab[127400,cdcInitLoc,cdcTask,WithCDC];
*Color display

DTab[5000,uibInitLoc,uibTask,WithTOR];
DTab[7000,eomHiTaskInitLoc,eomHiTask,WithTOR];
DTab[7400,eomLoTaskInitLoc,eomLoTask,WithTOR];

DTab[10000,ioInitLoc,ioTask1,WithMIOC];
*1st MIOC
DTab[10000,ioInitLoc,ioTask2,WithMIOC];
*2nd MIOC

DTab[3400,xwInInitLoc,xiTask,With3MB];
*1st 3mb Ethernet input
DTab[3000,xwOutInitLoc,xoTask,With3MB];
*1st 3mb Ethernet output
DTab[3400,xwInInitLoc,xiTask2,With3MB];
*2nd 3mb Ethernet input
DTab[3000,xwOutInitLoc2,xoTask2,With3MB];
*2nd 3mb Ethernet output

DTab[12400,enxInitLoc,enxTask,With10MB];
*1st 10mb Ethernet
DTab[12400,enxInitLoc2,enxTask2,With10MB];
*2nd 10mb Ethernet
DTab[12400,enxInitLoc3,enxTask3,With10MB];
*3rd 10mb Ethernet

DTab[1400,rdcInitLoc,rdcTask,1];
*SA4000
DTab[2400,rdcInitLoc,rdcTask,1];
*SA4000 kludge
DTab[1000,DisplayInitLoc,DisplayTask,1];
*UTVFC ID = 1000b
DTab[0,0,0,1];
*Final DTab entry must be Zero

OnPage[InitPage];

DeviceInit:
xCNT ← 57C;
xCNT ← T ← (StkP ← xCNT) + 1, Call[.+1];
*StkP = 57b, xCNT = T = 60b here; write 60-77 into RM 40-57 (StkP counts
*mod 20b, so StkP+1 when StkP .eq. 57b is 40b).
LU ← (xCNT) xor (77C);
Stack&+1 ← T, Skip[ALU=0];
xCNT ← T ← (xCNT) + 1, Return;
*Send dummy controller addresses to devices; wind up with (n lsh 14b) + 3 in
*RM (40b+n).
Assigned ← 0C;*Bit mask of assigned tasks
xCNT ← 57C, Call[ClockOutPattern];

*StkP = 57b here; read controller ID’s from register 0 of all devices.
xCNT ← T ← 177400C, Call[devRET];*Allow xCNT & T to be written
Input[stack];
xCNT ← T ← (xCNT) + (20C), Skip[R>=0];*Advance to next device
devRET:
Return;*Loop
*StkP is now back at 40b since it wraps around the last time it was bumped.
*Have 3000b, 3400b, 1055b, 1417b, etc. followed by 177777b for all undefined
*slots. Look up each slot table entry in the device table. Scan devices
*nearest the CPU first.
xCNT ← 17C;
DI4x:
DevIndex ← HiA[DTabBase];*Base of device table in control store
DevIndex ← (DevIndex) or (LoA[DTabBase]);
DI4y:
T ← 0C, Call[GetCon];*Get a device ID from the device table
*Compare with the slot table entry (high byte)
LU ← (LHMask[Stack]) xor T;
*Check for end of table (Zero entry)
LU ← T, GoTo[DevFound,ALU=0];
DI4u:
DevIndex ← (DevIndex) + 1, GoTo[DI4y,ALU#0];
*End of table reached without match--set slot’s task to 17 (unused)
Stack ← 17C;
DI4z:
xCNT ← (xCNT) - 1;*check for all slots processed
Stack&+1, DblGoTo[DI4x,DI5,ALU>=0]; *set to next slot

DevFound:
T ← (SStkP&NStkP) xor (377C);
rbuf0 ← T;
T ← 1C, Call[GetCon];
Stack ← T;*replace slot table entry with device table entry
rbuf1 ← T;*device table entry contains task assignment
rbuf1 ← LSh[rbuf1,4];*10:13b ← task number about to be assigned
CycleControl ← rbuf1;
T ← WFA[AllOnes];
LU ← (Assigned) and T;
Assigned ← (Assigned) or T, Skip[ALU=0];
*Keep looking, task already assigned
DevIndex ← (DevIndex) + 1, GoTo[DI4y];
*Write 177400 + 20b*task into RM 20b*task; this addresses the CSB for the
*device automatically.
StkP ← rbuf1;
T ← (rbuf1) or (177400C);
Stack ← T;
StkP ← rbuf0, GoTo[DI4z];

*Clock out new controller ID’s
DI5:
xCNT ← 57C, Call[ClockOutPattern];

%Here the table has task and uPC for existing controllers and 170000b for
non-existent or undefined controllers. NOTE: StkP is 57b, so entries are
handled in the order 57, 56, ..., 41.
%
*Call all the Init routines
xCNT ← 17C;
*Come here from DisplayInit
DI5a:
Call[devRET];*do Call to set up TPC for loop below
DI6:
xCNT ← (xCNT) - 1, GoTo[BootEmulators,R<0];
LU ← LdF[Stack,4,14];*check for Init PC = 0 (no initialization required)
*Skip to call init routine for controller.
APCTask&APC ← Stack&-1, Skip[ALU#0];
Nop;
Return;

ClockOutPattern:
StkP ← xCNT;
xCNT ← 17C;*go through the slot table backwards
COP1:
DevIndex ← 3C;*DevIndex used for loop count
COP2:
T ← (Stack) xor (1C);*complement bit
DevIndex ← (DevIndex) - 1, GenSRClock;*send bit
Stack ← RCy[Stack,1], GoTo[COP2,ALU>=0];*get next bit
xCNT ← (xCNT) - 1;*all slot table entries done?
Stack&-1, GoTo[COP1,ALU>=0];*get next word
Return;

GetCon:
APCTask&APC ← DevIndex;
ReadCS;
T ← CSData, Return, DispTable[1,1,0];*Force even placement

*Initialize Mesa registers except StkP and FFault; LoadRAM will jump to
*InitEndLoc (in MesaX.Mc) which will setup these two registers and jump to
*Xfer which sets up CODE, GLOBAL, etc.

MC[PilotMDS,76];

BootEmulators:

PrPsbIndexMask ← HiA[7774];
PrPsbIndexMask ← (PrPsbIndexMask) or (LoA[7774]), Task;
prCurrentPsb ← 0C;
:IF[WithGarbCollect]; ************************************************
gcZCT ← 3C;*Turn off Cedar microcode initially
:ENDIF; ***************************************************************
MemStat ← Normal, Task;
xfMX ← 1000C;
xfMX ← (xfMX) or (376C), Task;
xfWDC ← 1C;
MDS ← 0C, Task;
xfGFIWord ← 0C;
xfXTSReg ← 0C;
xfBrkByte ← 40400C;*2001b rcy 2
*NWW, RSImage, and RS232 were zeroed by Timer.Mc initialization.

*Show count of ’good’ pages from Initial.
xBuf ← IP[xPageCount]C;
StkP ← xBuf;
LoadPage[PNIPPage];
T ← Stack, CallP[PNIP];
*On soft boot delay before overlaying control store because io microcode
*may not be finished initializing yet and will be overwritten.
xBuf ← 0C, Call[MPWaita];
xBuf ← (xBuf) + 1, GoTo[MPWaita,R>=0];

RTemp ← IP[FFault]C;
StkP ← RTemp;
T ← GLOBALhi ← PilotMDS;
T ← GLOBALhi ← (LSh[GLOBALhi,10]) or T;
LOCALhi ← T;
xfTemp1 ← 1C;*Odd (normal tasking)
RTemp1 ← 0C;*Even (believe starting address), .ge. 0 (resume Mesa)
MDShi ← T, LoadPageExternal[LRJPage];
LOCAL ← 0C, GoToExternal[LRJContinue];

%PNIP displays the number in T in the maintenance panel and returns, where T
is meaningfully in the range 0 to 9999d (0 to 23417b). Does not task unless
Called from task 0. Its registers, RTemp and RTemp1, should not conflict
with those for the Midas Kernel or with any used by tasks 14 to 17 because of
Calls from Fault.Mc, which might subsequently go to Midas; any io task that
Calls PNIP and expects to continue should be wary of the fact that PNIP’s
registers are defined for task 0 and might conflict.

The loop below must allow 800 ns after ClearMPanel and 400 ns after IncMPanel.
If necessary the high two bits of RTemp can be used to increase loop time.
ClearMPanel and IncMPanel may be illegal in the same mi with a true branch
condition. For large numbers, map/storage refresh may fail when Called by an
io task.
%

PNIP:
RTemp ← T, ClearMPanel, At[PNIPStart];
UseCTask;
T ← APCTask&APC, UseCTask, Call[PFExit];
*14 cycles after ClearMPanel and 8 cycles after IncMPanel should be safe
*on the fastest machines.
PNIPl:
RTemp ← (LdF[RTemp,2,16]) - 1;
RTemp1 ← T, Skip[ALU>=0];
APCTask&APC ← RTemp1, GoTo[PFExit];
LU ← LdF[RTemp1,0,4];
Skip[ALU=0];
IncMPanel, GoTo[PNIPl];
IncMPanel, Return;


*NotifyInterrupt ORs T into NWW and sets IntPending. Uses registers
*0 and 1 in whatever task Calls.

RV[IntTemp1,0];
RV[IntTemp2,1];

NotifyInterrupt:

IntTemp1 ← T, At[NotifyInterruptLoc];*Save interupt mask
IntTemp2 ← IP[NWW]C, Skip[ALU#0];*Point to NWW register
Return;*No change
T ← (SStkP&NStkP) xor (377C);*T ← StkP
StkP ← IntTemp2, IntTemp2 ← T, NoRegILockOK;
T ← IntTemp1;*T ← interrupt mask
Stack ← (Stack) or T;*set bits in NWW
Stack&-1;*Point StkP at RSImage
T ← Stack ← (Stack) or (IntPendingBit);
LU ← StkP ← IntTemp2, RS232 ← T, Return;

:END[Initialize];