:TITLE[Initialize];

%Ed Fiala 26 May 1983: add rdcLongPointer1 init.
Ed Fiala 10 September 1982

RM assignments are subject to a number of constraints:

1) Must not smash the registers needed to continue LoadRAM; these are
LP, LPhi, and xfTemp (RM 66, 67, 72); RTemp1 must be set even (to believe
starting address) and xfTemp1 must be set odd (for normal tasking) before
continuing LoadRAM.
2) Registers needed valid after DeviceInit or used during DeviceInit must not
be in the range 40-57, which is used as a buffer during DeviceInit.
Registers constrained by this are AllOnes, R400, MDS, MDShi, PCB, BootType,
PageCount, xCNT, ?
3) BootType may not be in the range 0-17 because it is referenced by
BootTask (task 1, 2, or 3).
4) PCB (RM 30) must not be smashed on soft boot.

xCNT (35) is used throughout init; xBuf to xBuf3 (44 - 47),
RTemp and RTemp1 (52 - 53) are also used except during DeviceInit.
%
RV[PageCount,37];
*Count of available real pages in system.
RV[BootType,36];
*even => hard boot, odd => soft boot, where
*soft boot = hard boot except preserve disk
*partition and PCB for start;
*negative => ether, positive => disk
*200 disk partition 2
*RM address must not be < 20b nor 40b to 57b.
RV4[iniBf0,iniBf1,iniBf2,iniBf3,14];

*Registers for map and storage initialization
RV[CompFlag,5];
RV2[ZWord,ZWordhi,6];
*Base reg
RV[MapAddr,10];
*current map location
RV[RealPage,11];
*current real storage page

*In addition to the registers below, RTemp, RTemp1, and DMA are used prior
*to DiskBoot/EtherBoot code; xCNT, DevIndex, contemp, DMA, and DMAhi must
*not be in 40-57 area.

RV[RBuf1,34];
*used in DeviceInit
RV[Assigned,65];
*used in DeviceInit
RV[DevIndex,12];
*used in DeviceInit and DiskBoot
RM[ErrorCnt,IP[RTemp]];
*used in DiskBoot
RM[ErrorCountx,IP[RTemp1]];
*used in DiskBoot

MC[ReturnOnSE,100000];
*FFault bit 0=crash on MC2 errors, 1=return

*The DTab macro formats device table entries. A DTab entry consists of the
*task and uPC value for the device’s init routine (0 if no init routine).
*To add a new controller to the system, it is only necessary to add an entry
*to the device table (and add the controller’s microcode).

M@[DTab,IMData[LH[#1] RH[LShift[#2,4],#3] At[DTabLoc]] Set[DTabLoc,Add[DTabLoc,1]]];

Set[DTabLoc,DTabBase];

**12400 for 10 mb Ethernet.
DTab[12000,0,0];
*DES board
DTab[127000,0,0];
*FP board
**NOTE: The CDC is an optional device; perhaps we should require software to
**manually start the controller’s microcode with JRAM 50000b+cdcInitLoc and
**not initialize the device here, so that this table won’t change when the
**uCode is not present.
DTab[127400,cdcInitLoc,cdcTask];
*Color display = 53400b;
DTab[3400,EtherInitLoc,eiTask]; *New Ethernet input = 3400b
DTab[3000,0,eoTask];
*New Ethernet output=3000b(No init required)
DTab[1000,DisplayInitLoc,DpTask]; *Display (UTVFC) = 1000b
DTab[1400,rdcInitLoc,rdcTask];
*Disk (Alto RDC) = 1400b
DTab[2400,rdcInitLoc,rdcTask];
*Disk (Alto RDC) = 2400b (kludge if service late is on)
DTab[0,0,0];
*final table entry must be zero

OnPage[InitPage];

%KGO, EGO, and KGOP2 are Midas starting addresses which can be entered in
any task. Initial sends control to KGO if the disk is ready, else to EGOx,
where EGOx and KGOP2x are suitable starting addresses only if the current
task is the emulator (or tasks 1 to 3).
***EGOx AND KGOx PLACEMENT IS KNOWN TO MAKELOADERFILE COMMAND FILES***

On keyboard boot, Display.Mc leaves KB0 word in BootTask’s T register
(because T registers of unused tasks safely hold data across a boot).
1’s in KB0 represent keys up, 0’s keys down; bits are: 5, 4, 6, E, 7, D,
U, V, 0, K, -, P, /, \, LF, BS.
"0" (177577b) is interpreted here as a partition 2 disk boot;
BS (177776b) is ether boot;
all other values do a partition 1 disk boot.

Soft booting transfers control between variant microcode systems (e.g., Lisp,
Smalltalk, or Mesa each with an Alto emulator) without smashing storage; the
disk partition and PCB are also preserved. The program initiating a soft
boot first cleans up the Map (if necessary), places the microcode image and
its overlays into storage, and calls xoLRJ. LoadRAM then transfers control
to SoftGO (7202), the starting address; control eventually resumes at PCB+1
in the (new) Alto emulator. SoftGO is available only when NoOverlays is false
(I.e., it is available in release systems but not in debugging systems--in
debugging systems, storage is reinitialized here, so it doesn’t make sense
to return to the program.).

When NoOverlays is false, all entries here assume that the good page count
is in xPageCount (from Initial) and the map is set up correspondingly.
The code here copies xPageCount into both StoragePages (for compatibility
with old Lisp systems which look there for the count) and into PageCount
for use during initialization here.

When NoOverlays is true, PageCount is computed by the mini-Initial code
here but xPageCount and StoragePages are not computed.
**The Alto MemCfg opcode won’t work.
%

KGOP2:
xBuf ← LoA[Add[InitBase,0]], GoTo[.+3];
EGO:
xBuf ← LoA[Add[InitBase,4]], Skip;
KGO:
xBuf ← LoA[Add[InitBase,6]], At[InitBase,3];
xBuf ← (xBuf) or (HiA[InitBase,BootTask]);
XNotify:
APCTask&APC ← xBuf, GoTo[iniRET];
*BootTask .le. 3, so it can address all emulator RM registers except
*RM 0 to 17b, but SetTask here checks against illegal RM refs.
SetTask[BootTask];
BootType ← T, At[InitBase,6];
T ← (BootType) xnor (200C);*Must modify T so next boot not same
LU ← (BootType) xnor (1C), Skip[ALU#0];*Par 2 boot on "0" key
KGOP2x:
BootType ← 200C, GoTo[Start], At[InitBase,0];*KGO on partition 2
BootType ← T ← 0C, GoTo[Start,ALU#0];*Ether boot on BS key
EGOx:
BootType ← 100000C, GoTo[Start], At[InitBase,4];*Ether boot

:UNLESS[NoOverlays]; ***************************
SoftGO:
BootType ← 1C, GoTo[Start], At[InitBase,2];
:ENDIF; ****************************************

*Boot failures halt and wait for reboot.
InitFail:
Call[iPNIP];
GoTo[.];

SetTask[0];

*Begin here as any task. Quiesce tasks 15 to 1 by notifying each one at Qx,
*which clears device register 0 and notifies the next task until ctask is 0.
*All devices are supposed to be disabled by outputting 0 to register 0, but
*the EtherNet controller has a bug requiring an extra output, which is done
*first below for all tasks.

Start:
xBuf ← IP[xBuf]C, At[InitBase,5];
StkP ← xBuf;
Stack ← HiA[Qloc,15];
Stack ← (Stack) or (LoA[Qloc]);
QNotify:
Stack&+1 ← 0C;
Stack&+1 ← 300C;
*Output a 300 to device 0 for this task (in case this is the ethernet task);
*then output a 0 to device 0; finally point task’s TPC at BadWakeup.
Output[Stack,0];
Nop;
Output[Stack,0];
APCTask&APC ← Stack;
LoadPageExternal[InitPage], Return;

*Start and QNext notify here.
Qx:
T ← LdF[Stack,0,4], CallP[QNext], At[Qloc];
*BadWakeup on non-overlay page to remain intact after init ucode is
*overwritten. Wakeup from a device that isn’t reset properly comes here.
BadWakeup:
SetFault, GoTo[.];

OnPage[InitPage];

QNext:
Stack ← (Stack) - (10000C), GoTo[QNotify,ALU#0];
*Now do notify for timer initialization.
xBuf ← LoA[TimerInitLoc];
xBuf ← (xBuf) or (HiA[TimerInitLoc,TTask]), Call[XNotify];
*Return here in task 0 after timer task blocks.

:IF[NoOverlays]; *******************************

*Test the map as a memory, then determine the amount of real storage and
*set up the first N map entries to point to this storage (initialize
*remaining map entries to VACANT), then clear storage.

iMap:
CompFlag ← (Zero) - 1;
*CompFlag is originally -1, so iRWMap tests map entries with LogSE=WP=0,
*Dirty=Ref=1, and complement of address; then CompFlag is 0 to test with
*these bits complemented.
imCompx:
MapAddr ← T ← 140000C;
imAloop:
T ← (CompFlag) xor T;
xBuf ← T, Call[iRWMap];*T ← xBuf on Return from iRWMap
*RealPage ← max real page + 1 for later; this second map write reads back
*what was written with the first map write.
RealPage ← 10000C, Call[iRWMap];
PageCount ← IP[FFault]C, Call[imCheckMap];*Crash if data bad
LU ← CompFlag, GoTo[imAloop,Carry’];
T ← CompFlag ← Zero, GoTo[imCompx,ALU#0];
StkP ← PageCount, PageCount ← T, NoRegILockOK;
MapAddr ← 140000C;*carries beyond max VM cause ALUCY

*At this point the map has been tested somewhat with each bit correctly
*assuming both its 1 and 0 states. Next, write each map entry with 0 flags
*and the corresponding real page number, and fill first 5 quadwords of each
*real page with their page numbers and complements and some constants
*selected to make all the check bits 1 and 0. Go through storage backwards
*so that hole in 96k modules will not screw up non-hole banks.
imFloop:
RealPage ← T ← (RealPage) - 1, Call[imFloop1];
Call[BlockSet];*fills iniBf with T
PStore4[ZWord,iniBf0,0];*store page number
T ← (Zero) xnor T, Call[BlockSet];
PStore4[ZWord,iniBf0,4], Call[ZBlockSet];*Store page no. complement
iniBf0 ← 100000C, Call[iniRET];*Wait for ZWord write
PStore4[ZWord,iniBf0,10], Call[ZBlockSet];*Store 100000,0,0,0
iniBf1 ← 1C, Call[iniRET];*Wait for ZWord1 write
PStore4[ZWord,iniBf0,14], Call[ZBlockSet];*Store 0,1,0,0
iniBf2 ← 40000C;
T ← 20C;
PStore4[ZWord,iniBf0], GoTo[imFloop];*Store 0,0,40000,0

imFloop1:
xBuf ← T, Skip[ALU>=0];
***Patch this mi and one indicated below to allow imperfect storage.
T ← RealPage ← 170000C, GoTo[imTloop];
*SUBROUTINE iRWMap writes the data in xBuf into map location MapAddr
*and returns xBuf in T, map entry in xBuf1 to xBuf3.
iRWMap:
T ← (MapAddr) and (37400C);
ZWordhi ← T, LoadPage[17];
T ← LSh[MapAddr,10], GoToP[.+1];
OnPage[17];
ZWord ← T;
XMap[ZWord,xBuf,0];
xBuf ← T ← xBuf, Return; *interlock
OnPage[InitPage];

*Sweep upward through storage and map and use any real pages discovered.
*Set up base register, set map entry, LogSE on (FFAULT[0] ← 1). This informs
*the fault handler that the test program is willing to take the fault; the
*fault handler RETURNs rather than sending control to Midas.
imTloop:
T ← (RealPage) and not (70000C);
xBuf ← T, Call[iRWMap];
Stack ← (Stack) or (ReturnOnSE), Call[iChkPg];

*Return here on fault, or when iChkPage detects a problem.
imPageBad:
T ← RealPage ← (RealPage) + 1;
***Patch this mi and one indicated above to allow imperfect storage.
Stack ← (Stack) and not (ReturnOnSE), GoTo[imTloop,Carry’];
*Fall through here after handling the maximum amount of real storage
Nop;
*This loop marks that portion of the map with no corresponding storage vacant.
imMarkVacant:
xBuf ← 60000C;
Call[iRWMap];
MapAddr ← (MapAddr) + 1;
*Loop over all map entries
T ← LHMask[PageCount], GoTo[imMarkVacant,Carry’];

*Zap all storage by filling with zeroes; initialize ZWord/ZWordhi to point
*one larger than the largest legal address.
ZWordhi ← T;
T ← LSh[PageCount,10];
ZWord ← T, Call[ZBlockSet];
*Loop zeroing all storage
ZWord ← (ZWord) - (4C);
T ← NotEnoughMemory, Skip[Carry’];
PStore4[ZWord,iniBf0,0], Return;
ZWordhi ← (ZWordhi) - (400C);
LU ← (PageCount) - (MinPageCount), Skip[ALU<0];
PStore4[ZWord,iniBf0,0], Return;
*Must copy PageCount into xPageCount for normal use and into StoragePages for
*backward compatibility.
RTemp ← Sub[IP[StoragePages],1]C, GoTo[InitFail,ALU<0];
T ← PageCount, GoTo[RegInit0];

iChkPg:
PFetch4[ZWord,iniBf0,0];*Fetch will cause fault if page bad
T ← LdF[RealPage,4,14];*page address
LU ← (iniBf0) xor T;
PFetch4[ZWord,iniBf0,4], Skip[ALU=0];
LU ← iniBf0, Return;*page number didn’t compare
LU ← (iniBf0) xnor T;*check page complement
*fetch from other 3 quadwords to provoke a fault
PFetch4[ZWord,iniBf0,10], Skip[ALU=0];
LU ← iniBf0, Return;
PFetch4[ZWord,iniBf0,14];
T ← 20C;
PFetch4[ZWord,iniBf0];
LU ← iniBf0;
*Page is good.
***Remove the xBuf ← to enable single-error logging***
xBuf ← (xBuf) and not (100000C), Call[iRWMap]; *turn off LogSE
MapAddr ← (MapAddr) + 1;*go to the next map location
PageCount ← (PageCount) + 1, GoTo[imPageBad];

*SUBROUTINE imCheckMap compares (MapAddr xor CompFlag) against xBuf
imCheckMap:
T ← LSh[xBuf3,10];*flags, card, blk.0 bits
T ← (RHMask[xBuf1]) or T;
T ← (MapAddr) xor T;
T ← (CompFlag) xnor T;*note, Map data is complemented, so we xnor
UseCTask, Skip[ALU=0];
T ← BadMap, GoTo[InitFail]; *Some map entry was bad
MapAddr ← T ← (MapAddr) + 1, Return;

RegInit0:
StkP ← RTemp, Call[IniSp1];
RTemp ← Sub[IP[xPageCount],1]C, Call[IniTS];

:ELSE; *****************************************

*Copy xPageCount (setup by Initial) into PageCount for use here and into
*StoragePages for backward compatibility with Lisp and Smalltalk.
PageCount ← IP[xPageCount]C;
StkP ← PageCount;
T ← Stack;
PageCount ← IP[StoragePages]C;
StkP ← PageCount, PageCount ← T, NoRegILockOK;
Stack ← T;
BootType, GoTo[HardContinueInit,R Even];
*Prepare to preserve the disk partition when disk initialization is called.
LoadPage[XMiscPage];
RTemp ← 0C, Call[MXPar];*Return default partition (1 or 2)
BootType ← T;
*Set bit 8 if partition 2; bit 15 indicates soft boot (garbage in bit 9 if
*partition 1 is don’t care).
BootType ← (LSh[BootType,6]) + 1;
*Continue refresh from its state at the end of LoadRAM.
HardContinueInit:
T ← (xfTemp1) and not (17C);
xfTemp1 ← IP[Refr]C;
StkP ← xfTemp1;
Stack ← T;
xfTemp1 ← 1C;*Setup LoadRAM arg for normal tasking.

*Memory initialization was completed by Initial.
*Wait for user to view MP left by Initial.
*Outer loop repeats 24k times, so wait is 24k * 123 cycles ~ 0.302 sec
xBuf ← 20000C, Call[MPWait];
xBuf ← (xBuf) + 1, GoTo[MPWait,R>=0];
LU ← (PageCount) - (MinPageCount);
GoTo[RegInit1,ALU>=0];
T ← NotEnoughMemory, GoTo[InitFail];

:ENDIF; ****************************************

*Here after devices are quiescent, RM is zeroed, timer task initialized, and
*(debugging system only) map and storage are initialized. Initialize RM
*constants and start 256 us clock update.

RegInit1:
AllOnes ← (Zero) - 1;
R400 ← 400C;
MDShi ← T ← 0C;
MDS ← T;*MDS = RZero
**High Base registers = MDShi and common to both Alto and Mesa emulators are
**initialized here.
*TimerBasehi must be initialized before initializing RConstantHi/Lo.
RTemp ← Sub[IP[TimerBasehi],1]C, Call[IniTS];
RTemp ← Add[LShift[eiTask,4],6]C, Call[IniTS];*eMDS600hi
RTemp ← Add[LShift[cdcTask,4],12]C, Call[IniTS];*cdcMDS400hi
RTemp ← Add[LShift[And[DpTask,14],4],76]C, Call[IniTS];*vMDShi
Stack&-2 ← T;*vMDS420hi
Stack&-2 ← T;*vMDS177000hi
RTemp ← Add[LShift[rdcTask,4],2]C, Call[IniTS];*rdcLongPointer1
RTemp ← Add[LShift[rdcTask,4],16]C, Call[IniTS];*rdcDCBhi
Stack&-2 ← T;*rdcMDS520hi
%Timer.Mc zeroed RConstantHi/Lo earlier, so RTCLow wouldn’t overflow and cause
a memory reference of VM 430 during storage init. Now initialize to values
correct for a 40 mhz processor clock. RConstantLo, RConstantHi, and
StoragePages are known to be in consecutive RM locations. Processor clock
values for 2560 cycles timer:
40 mhz656,,012172b
44.5 mhz602,,113274b
50 mhz530,,005330b
DisplayInit will change the 40 mhz value if the processor clock is 44.5 or 50
mhz, but we initialize here in case there is no attached display.
%
RTemp ← Sub[IP[RConstantLo],1]C, Call[IniTS];
Stack ← LoA[012172];
Stack ← (Stack) or (HiA[012172]);
T ← (R400) + (LoA[656]), Call[IniSp1];
*StoragePages (RM 326) for the Lisp emulator and Alto MEMCFG opcode.
T ← PageCount, Call[IniSp1];
DeviceInit:
T ← StartDeviceInit, CallP[iPNIP];*MP code = 104d
:UNLESS[NoOverlays]; ***************************
*Wait ~.31 sec for user to view StartDeviceInit, indicating that Initial is
*complete and that this microcode has commenced.
xBuf ← 20000C, Call[MPWait];
xBuf ← (xBuf) + 1, Skip[R<0];
GoTo[MPWait];
:ENDIF; ****************************************

%RM 40-57 are a "slot table" with one entry per potential io controller.
First, the table is filled with dummy task numbers which are clocked out to
the controllers. Then, each task is interrogated for it’s Device ID, and
these names are put in the table (I think that RM 40 winds up with the
device nearest the processor in the card cage.). Next, for each slot, the
device ID is looked up in the DTab table (in IM) of potential devices, and if
a match is found, the DTab entry is put into the slot table. 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.
%
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. 57 is 40b).
LU ← (xCNT) xor (77C);
Stack&+1 ← T, Skip[ALU=0];
xCNT ← T ← (xCNT) + 1, Return;
Assigned ← 0C;*Bit mask of assigned tasks
*Send dummy controller addresses to devices; wind up with (n lsh 14b) + 3 in
*RM (40b+n).
xCNT ← 57C, Call[ClockOutPattern];
*StkP = 57b here; read controller ID’s from register 0 of all devices
xCNT ← T ← 177400C, Call[iniRET];*allow xCNT & T to be written
Input[Stack];
xCNT ← T ← (xCNT) + (20C), Skip[R>=0];
DMA ← 400C, Return;*Advance to next device; init DMA for later
*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. RM 40b represents nearest processor, 57b furthest away.

*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 IM
DevIndex ← (DevIndex) or (LoA[DTabBase]);
DI4y:
T ← 0C, Call[GetCon];*get device ID from table
LU ← (LHMask[Stack]) xor T;*compare to high 8 bits of slot table entry
LU ← T, GoTo[DevFound,ALU=0];
*check for end of table (zero entry)
DevIndex ← (DevIndex) + 1, GoTo[DI4y,ALU#0];
Stack ← 17C; *end of table reached without match - set slot’s task to 17 (unused)
DI4z:
xCNT ← (xCNT) - 1;*check for all slots processed
Stack&+1, DblGoTo[DI4x,DI5,ALU>=0]; *set to next slot

DevFound:
DMA ← (DMA) or (200C);
T ← 1C, Call[GetCon];
RBuf1 ← T;
RBuf1 ← LSh[RBuf1,4];*10:13b ← task number about to be assigned
CycleControl ← RBuf1;
T ← WFA[AllOnes];
LU ← (Assigned) and T;
*Replace slot table entry with device table entry if task available, else
*keep looking if task already assigned.
Assigned ← (Assigned) or T, Skip[ALU=0];
DevIndex ← (DevIndex) + 1, GoTo[DI4y];
T ← CSData;
Stack ← T, GoTo[DI4z];

*Have 6, 174427b, 174172b, 174070b, and 17b to end of table here
DI5:
xCNT ← 57C, Call[ClockOutPattern];*Clock out new controller ID’s

%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. Since DisplayInit may jump to LoadRAM,
**OLD LOADRAM**
RTemp1 (53) must be even and xBuf-xBuf3 and RTemp (44-47 and 52) will be
smashed, so the display controller must be in chasis slots 0 to 3 or LoadRAM
will smash table entries not yet handled, and there must be fewer than 13b
controllers mounted or RTemp1 might not be even.
**NEW LOADRAM**
RTemp1 (53) must be even and yBuf-yBuf2 (50-52) will be smashed, so the
display controller must be in chasis slots 0 to 7, and there must be fewer
than 13b controllers mounted or RTemp1 might not be even.
%
T ← 17C;
*DpTask transfers to task 0 to roll in or pass over the CSL monitor overlay.
*It continues initialization by transferring to DI6 with xCNT in T rather
*than by simply returning as other device initialization routines do.
DI6:
xCNT ← T, Call[.+1];*Setup TPC for loop below
*Loop here to call all the init routines
T ← xCNT ← (xCNT) - 1, Skip[R>=0];
ErrorCnt ← 10C, GoTo[BootEmulators];*Set up retry count
*Unless InitPC = 0 (no init required), call device init routine and loop
LU ← LdF[(Stack),4,14];
APCTask&APC ← Stack&-1, Skip[ALU#0];
MNBR ← BootType;
iniRET:
Return;

IniTS:
StkP ← RTemp;
IniSp1:
Stack&+1 ← T, Return;

ZBlockSet:
iniBf0 ← T ← 0C, Skip;
BlockSet:
iniBf0 ← T;
iniBf1 ← T;
iniBf2 ← T;
iniBf3 ← T, GoTo[iniRET];

iPNIP:
LoadPage[PNIPPage];
GoToP[PNIP];

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

ClockOutPattern:
*Call with 57C in xCNT
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
DMAhi ← 0C, Return;*DMAhi ← 0 for EtherBoot and DiskBoot

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

:IF[NoOverlays]; *******************************
BootEmulators:
BootType, T ← StartEtherBoot, NoRegILockOK, GoTo[DiskBoot,R>=0];
:ELSE; *****************************************
BootEmulators:
BootType, GoTo[Booted,R Odd];
BootType, T ← StartEtherBoot, NoRegILockOK, GoTo[DiskBoot,R>=0];
:ENDIF; ****************************************
*Setup here is VM 604 ← 400, VM 605 ← 1, VM 610 ← 377, VM 600 ← 0;
*then start the Ethernet controller and wait for VM 600 # 0.
*DMAhi,,DMA were setup as 0,,600 earlier for this code.
EtherBoot:
xBuf1 ← 1C, Call[iPNIP];*114d MP code
xBuf ← 400C;
EtherBootx:
PStore2[DMA,xBuf,4], Task;*VM 604 = eiCLoc ← 400, eiBLoc ← 1
xBuf2 ← 377C;
*ESLOC ← 377 (serial no. for breath-of-life packets)
PStore1[DMA,xBuf2,10], Call[iniRET];
PStore1[DMA,RZero,0];*ePLoc ← 0 (status word)
RTempLoA[eiStartLoc], Call[eStartRead];
*Here (in task 0) after Ether has started and tasked
T ← (DMA) + (2C);
PFetch1[DMA,AC1,0], Call[iniRET];
LU ← AC1;
AC1 ← (AC1) xor (377C), GoTo[.-3,ALU=0];*Wait for packet
PFetch1[MDS,AC2,2];
AC2 ← (AC2) - T;*AC2 ← (AC2) - (Breath-of-life)
AC0 ← cEtherBoot, GoTo[EtherBootx,ALU#0];
*got the breath of life!!!! start Alto emulator with PC=5 and complement
*of KB1 word in AC0 (=10b for NetExec, =100b for MesaNetExec);
PCB ← 5C;
:IF[NoOverlays]; *******************************
Booted:
T ← PageCount, Call[iPNIP];
:ELSE; *****************************************
Booted:
T ← GotBreathOfLife, Call[iPNIP];*118d MP code
xBuf ← 20000C, Call[MPWait];*Delay .31 sec
xBuf ← (xBuf) + 1, Skip[R<0];
GoTo[MPWait];
T ← PageCount, Call[iPNIP];*No. of good pages in MP
:ENDIF; ****************************************
:IF[FinalOverlay]; *****************************
LoadPageExternal[LRJPage];
RTemp1 ← 0C, GoToExternal[LRJContinue];
:ELSE; *****************************************
LoadPage[neStartPage];
GoToP[StartNova];
:ENDIF; ****************************************

EStartRead:
RTemp ← (RTemp) or (HiA[eiStartLoc,eiTask]);
LoadPage[eePage];
RTemp1 ← 300C, GoTo[eIOReset];*Disable EtherNet input/output

DiskBoot:
T ← StartDiskBoot;
DMA ← 1000C, Call[iPNIP];
*Read disk Sector 0 into page 0 (starting at location 1).
DiskBootx:
ErrorCountx ← 20C, Call[ZBlockSet];*word 520 - not used by disk
*word 521 - IOCB pointer
*word 522 - disk status
*word 523 = -1 to force a seek
T ← (R400) or (120C);
PStore4[MDS,iniBf0], Call[iniRET]; *520-523 ← 0,0,0,0

*Set up the IOCB at 1000b
iniBf2 ← 44000C;*disk command goes at location 1002 (read, read, read)
iniBf3 ← 2000C;*header goes at 2000 (unlike ALTO)
PStore4[DMA,iniBf0,0], Call[iniRET]; *1000-1003 ← 0,0,44000,2000
iniBf2 ← 400C;
iniBf2 ← (iniBf2) + (2C); *label goes at 402
iniBf3 ← 1C;*data goes at 1
PStore2[DMA,iniBf2,4], Call[iniRET]; *1004-1005 ← 402,1

*Since we will initialize the interrupt system later, we do not need to worry
*about 1006 or 1007; 1008 is unused
PStore1[DMA,iniBf3,11];
*iniBf1 = 1.. Disk address 0, with RESTORE bit at 1009
*Start the disk
iniBf1 ← 1000C, Call[iniRET];*word 521 - IOCB pointer
iniBf2 ← 0C;
T ← (R400) + (120C);
PStore4[MDS,iniBf0], Call[iniRET]; *520-523 ← 0,1000,0,1
*Wait for the disk to store good status in the DCB, retry if status is bad
DWSet:
DevIndex ← 40000C;*loop count for status wait
DiskWait:
PFetch1[DMA,xCNT,1];*fetch status word at 1001b
T ← 17C, Call[iniRET];
LU ← (LdF[xCNT,4,4]) xor T;
LU ← LdF[xCNT,10,10], GoTo[StatusStored,ALU=0];
DevIndex ← (DevIndex) - 1, GoTo[DiskWait,R>=0];
ErrorCountx ← (ErrorCountx) - 1, GoTo[DWSet,R>=0];
T ← NoDiskStatus, GoTo[InitFail];*timed out waiting for disk to store status

StatusStored:
ErrorCnt ← (ErrorCnt) - 1, Skip[ALU#0];
PCB ← 1C, GoTo[Booted];*Good status--send control to VM 1
T ← BadBoot, Skip[ALU<0];
GoTo[DiskBootx];
GoTo[InitFail];

%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 uses the top bits of RTemp to space successive IncMPanel’s.
**ClearMPanel and IncMPanel may be illegal in the same mi with branch burp.
**For large numbers, map/storage refresh may fail when called by an io task.

Timing: 16*T + 20 cycles.
%
OnPage[PNIPPage];

PNIP:
UseCTask, RTemp ← T;
T ← APCTask&APC, ClearMPanel, Call[.+1];
PNIPl:
RTemp ← (RTemp) + (40000C), GoTo[.,R>=0];
RTemp ← (LdF[RTemp,2,16]) - 1;
RTemp1 ← T, Skip[ALU>=0];
APCTask&APC ← RTemp1, GoTo[PFExit];
LU ← LdF[RTemp1,0,4];
Skip[ALU=0];*Skip if emulator (tasking allowed)
IncMPanel, GoTo[PNIPl];*else non-skip (no tasking)
IncMPanel, Return;


%DoInt ORs bits from T into NWW and sets IntPending. Uses registers 0 and 1
in whatever task calls. DoIntR is the same, but with the bits from register
0 rather than T.
%
RV[IntTemp1,0];
RV[IntTemp2,1];

DoInt:
IntTemp1 ← T, At[DoIntLoc];*Abs placement for CSLKeyboard overlay
DoIntR:
IntTemp2 ← IP[NWW]C;
T ← (SStkP&NStkP) xor (377C);
StkP ← IntTemp2, IntTemp2 ← T, NoRegILockOK;
T ← (IntTemp1) and not (100000C);
Stack ← (Stack) or T, Skip[ALU#0];*set bits in NWW
StkP ← IntTemp2, Return;*Restore StkP and return
IntTemp1 ← IP[RSImage]C;
StkP ← IntTemp1;
T ← Stack ← (Stack) or (IntPendingBit);*Set IntPending
LU ← StkP ← IntTemp2, RS232 ← T, Return;

:END[Initialize];