:TITLE[Initialize];*Last edited: 23 April 1980 by Fiala

*Registers for IMAP
*PC used as base register
*xBuf quadword used for XMap and PFetch4
*IBuf quadword used for PStore4
RV[MapEntry,10];
*current map location
RV[ZPage,10];
*page being cleared
RV[RealPage,11];
*current real storage page
RV[ZWord,11];
RV[PageCount,12];
*count of available real pages in system
RV[CompFlag,13];
RV[BootType,0];
* even => hard boot, odd => soft boot;
*negative => ether, positive => disk

*Registers for other sections of initialization
*RTemp, RTemp1, DMA, RCNT, MDS, MDShi, AC0, AC1, AC2 are used
*RLink0, xCNT, DevIndex, and ConTemp must not be in 40-57 area, which
*is used as a buffer
RV[rlink0,IP[zBuf]];
*subroutine return link, used everywhere
RV[xCNT,IP[zBuf1]];
*used everywhere
RV[DevIndex,IP[zBuf2]];
*used in DeviceInit
MC[pDX,IP[DevIndex]];
*pointer to DevIndex
RV[contemp,IP[zBuf3]];
*used in DeviceInit
*IBuf quadword used in DiskBoot
RV[ErrorCnt,IP[RTemp]];
*used in DiskBoot
RV[ErrorCountx,IP[RTemp1]];
*used in DiskBoot

*Maintenance Panel Normal Operation Codes:
MC[StartMapInit,144];
*100d
MC[StartDeviceInit,156];
*110d
MC[StartDiskBoot,170];
*120d

*Maintenance Panel Failure Codes:
MC[NotEnoughMemory,145];
*101d
MC[BadMap,146];
*102d
MC[NoDiskStatus,171];
*121d
MC[BadBoot,172];
*122d

SetTask[0];

Set[Qloc,230]; *This location is on Page 0 so that it will remain intact
*even if the initialization code is overwritten.
MC[QxL,And[Qloc,377]];
MC[QxH,OR[150000,And[Qloc,7400]]];

Set[QretLoc,Add[InitBase,7]];
MC[QRetL,And[QretLoc,377]];
MC[QretH,And[QretLoc,7400]];

ONPAGE[InitPage];
*Machine initialization begins here - notify to task 0
EGO:
xCNT ← EInitLocL, goto[Startx]; *Midas Ether Boot Start Location
KGO:
xCNT ← KInitLocL, goto[Startx]; *Midas Disk Boot Start Location
Start:
xCNT←InitLocL, at[HardStart]; *send control to "Qtask" in task 0
Startx:
xCNT ← (xCNT) or (InitLocH);
apc&apctask←xCNT, goto[initRET];

SoftBoot:
BootType ← 1c, goto[RegInit1], at[SoftStart];* This is a soft boot;

EtherStartx: BootType ← 100000c, goto[Qtask], at[EtherStart];
DiskStartx: BootType ← 0c, goto[Qtask], at[DiskStart];

Qtask:
xCNT ← QxL, AT[InitBase]; *Quiesce tasks 15b - 1
xCNT ← (xCNT) or (QxH);
DevIndex ← pDX;
stkp ← DevIndex;
stack ← QRetL;
stack ← (stack) or (QRetH);
stack&+1 ← 0c;
QNotify:
APC&APCTASK ← xCNT, goto[initRet];

*Notify comes here. Clear device register 0, leave task’s TPC pointing at Qxy.
Qx:
Output[Stack,0], AT[Qloc]; *pop stack
APC&APCTASK ← stack&+1, call[PNRet];
Qxy:
breakpoint, return; *Wakeup from unknown device


Qret:
lu ← ldf[xCNT,0,3], AT[QretLoc]; *xCNT points to this location. Check for all tasks done.
xCNT ← (xCNT) - (10000C), goto[QNotify,ALU#0];

DoMap:
xCNT ← AND[377,TimerInitLoc]C; *set up to notify to InitTimers
xCNT ← (xCNT) or (OR[lshift[TTask,14], AND[7400,TimerInitLoc]]C), call[QNotify];


*The following section tests the map as a memory, then determines the
*amount of real storage in the system and sets up the first
*N map entries to point to this storage (remaining map entries
*are initialized to VACANT), then clears storage.
*IMAP:
loadpage[0]; *Timer initialization returns here
*
T ← StartMapInit, CallP[PNIP];
CompFlag ← (zero)-1,goto[imCompx];
imAloop:
T ← (CompFlag) xor (T);
xBuf ← T, call[imWriteMap]; *T ← xBuf on return from imWriteMap
call[imReadMap]; *read and check
MapEntry ← T ← (MapEntry) + 1;
goto[imAloop,nocarry];
MapEntry ← 140000C;
imRloop:
T ← MapEntry;
T ← (CompFlag) xor (T), call[imReadMap];
MapEntry ← T ← (MapEntry) + 1;
lu ← CompFlag, goto[imRloop,nocarry];
skip[ALU=0], CompFlag ← (zero);
imCompx:
goto[imAloop], MapEntry ← T ← 140000C;


RealPage ← (10000C); *max real page +1
MapEntry ← 140000C; *carries beyond max VM cause ALUCY
PageCount ← 0C;
rlink0 ← FFaultAdd; *location in fault handler
stkp ← rlink0;


*First, 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 real memory backwards so that hole in 96k modules will not screw up non-hole banks.
imFloop:
RealPage ← T ← (RealPage)-1;
skip[ALU>=0];
T ← RealPage ← 170000C, goto[imTloop];
xBuf ← T;
call[imWriteMap]; *does not change T
call[BlockSet]; *stores T into IBuf - IBuf3
PStore4[PC,IBuf,0]; *store page number
T ← (zero) xnor (T), call[BlockSet];
PStore4[PC,IBuf,4]; *store page number complement
T ← 0c, call[BlockSet];
IBuf← 100000c, call[initRet]; *wait for IBuf write
PStore4[PC,IBuf,10]; *store 100000,0,0,0
IBuf ← 0c;
Ibuf1 ← 1c, call[initRet]; *wait for IBuf write
PStore4[PC,IBuf,14]; *store 0,1,0,0
Ibuf1 ← 0c;
Ibuf2 ← 40000c;
T ← 20c;
PStore4[PC,IBuf], goto[imFloop]; *store 0,0,40000,0

BlockSet:
IBuf ← T;
Ibuf1 ← T;
Ibuf2 ← T;
Ibuf3 ← T, goto[initRet];


*we sweep upward through real storage and the map, and use any real pages discovered.
imTloop:
T ← (RealPage) and not (70000c);
xBuf ← T, call[imWriteMap];*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.
call[imChkPage], stack ← (Stack) or (100000c);*turn on the fault handler

imPageBad:
T ← RealPage ← (RealPage) + 1;*get here on a fault, or when a page check fails
stack ← (stack) and not (100000c), goto[imTloop, nocarry]; *check for done
goto[imMarkVacant];

imChkPage:
PFetch4[PC,IBuf,0];*fetch. Will cause fault if page is bad
T ← ldf[RealPage,4,14]; *page address
lu ← (IBuf) xor (T);
PFetch4[PC,IBuf,4], skip[ALU = 0];
PError:
lu ← IBuf, goto[imPageBad];*page number didn’t compare
lu ← (IBuf) xnor (T); *check page complement
PFetch4[PC,IBuf,10], skip[ALU=0]; *fetch from the other 3 quadwords to provoke a fault
PCError:
lu ← IBuf, goto[imPageBad];
PFetch4[PC,IBuf,14];
T ← 20c;
PFetch4[PC,IBuf];
lu ← IBuf;
imPageGood:
xBuf ← (xBuf) and not (100000c), call[imWriteMap]; *turn off LogSE
MapEntry ← (MapEntry) + 1;*go to the next map location
PageCount ← (PageCount) + 1, goto[imPageBad];

imMarkVacant:
xBuf ← 60000C; *this loop marks that portion of the map with no corresponding storage vacant.
Call[imWriteMap];
MapEntry ← (MapEntry) + 1;
T ← PageCount, goto[imMarkVacant, nocarry];*done with all map entries?


imCoreZap:
Zpage ← T;

imZapLoop:
ZPage ← (ZPage) -1;
T ← lhmask[ZPage], goto[imDone, ALU<0];
PChi ← T; *set up a base register for the page
T ← lsh[Zpage,10];
PC ← T;
ZWord ← 400C;
T ← 0c, call[BlockSet];

imZPloop:
Zword ← T ← (Zword) - (4C);
goto[imZapLoop, ALU<0];
PStore4[PC,IBuf], return;

imDone:
lu ← (PageCount) - (3000c); *don’t try to run with less than 384K (3000b pages)
T ← NotEnoughMemory, goto[RegInit1, ALU>=0];

InitFail:
loadpage[0];
callp[PNIP];
goto[Start];

*SUBROUTINE imWriteMap writes the data in xBuf into map location MapEntry
imWriteMap:
T ← (MapEntry) and not (140000C);
PChi ← T;
PChi ← (PChi) and not (377C);
T ← lsh[MapEntry,10];
PC ← T;
Xmap[PC,xBuf,0];
xBuf ← T ← xBuf, return; *interlock

*SUBROUTINE imReadMap reads one entry from MapEntry
*into xBuf, then compares it with (MapEntry xor CompFlag)
imReadMap:
xBuf ← T, usectask;
T ← apc&apctask;
rlink0 ← T, call[imWriteMap];
T ← lsh[xBuf3, 10]; *flags, card, blk.0 bits
xBuf ← T;
T ← (xBuf1) and (377C);
xBuf ← (xBuf) xor (T);
T ← MapEntry;
T ← (CompFlag) xor (T);
lu ← (xBuf) xnor (T); *note, Map data is complemented, so we xnor
goto[imGoodEntry, ALU=0];
imBadMap:
T ← BadMap, goto[InitFail]; *Some map entry was bad

imGoodEntry:
apc&apctask ← rlink0, goto[initRET];


*We have initialized the map and memory. Before initializing and
*starting devices, set up the R memory locations that
*must be valid. Also, start the 64us timer.

RegInit1:
MDS ← zero;
MDShi ← zero;
R400 ← (400c);
AllOnes ← (zero)-1;
RZero ← zero;
xCNT ← 325c; *Pointer to RTCLOW
stkp ← xCNT;
stack ← 0c, goto[DeviceInit]; *making RTCLOW even starts RTC updating. It is
*safe to do so now since the memory has been initialized and the reference to VM 430 done
*by the timer will not cause a fault.



*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 the control store) 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, it is only necessary
*to add an entry to the device table (and add the controller’s
*microcode).

*first, a macro to allow nice formatting of the device table entries...
M@[dtab,IMDATA[LH[#1] RH[LSHIFT[#2,4],#3] At[dtabloc]] SET[dtabloc,ADD[dtabloc,1]]];

SET[DtabBase,add[InitBase,100]];
SET[dtabloc,DtabBase];

*Here is the device table
dtab[127400,cdcInitLoc,cdcTask]; *Color display = 53400b
dtab[3400,EtherInitLoc,EITask]; *New Ethernet input = 3400b
dtab[3000,0,EOTask]; *New Ethernet output=3000b(No Initialization Required)
dtab[1000,DisplayInitLoc,DpTask]; *Display (UTVFC) = 1000b
dtab[1400,DiskInitLoc,KTask]; *Disk (Alto RDC) = 1400b
dtab[2400,DiskInitLoc,KTask]; *Disk (Alto RDC) = 2400b (kludge if service late is on)
dtab[0,0,0];
*final entry in the table must be zero

DeviceInit: loadpage[0];
T← StartDeviceInit,callp[PNIP];
xCNT ← 57c;
stkp ← xCNT, xCNT ← T ← (xCNT)+1, call[DI1];

*stkp = 57b, xCNT = T = 60b here.

*Write 60-77 into RM 40-57
DI1:
stack&+1 ← T;
lu ← (xCNT) xor (77C);
xCNT ← T ← (xCNT) + 1, goto[DI2, alu=0];
return; *return to DI1

*Send dummy controller addresses to devices
DI2:
xCNT ← 57c;
stkp ← xCNT, call[ClockOutPattern];

*stkp = 57b here
xCNT ← T ← 177400c, call[DI3]; *Read controller ID’s from register 0 of all devices
DI3:
INPUT[stack];
nop; *allow xCNT & T to be written
xCNT ← T ← (xCNT) + (20C), goto[DI4,R>=0]; *advance to next device
return; *return to DI3

*Look up each slot table entry in the device table
DI4:
xCNT ← 17c;
DI4x:
DevIndex ← 0c; *base of device table in control store
DI4y:
T ← DevIndex, call[GetCon]; *get a device ID from the device table
lu ← (lhmask[stack]) xor (T); *compare with the slot table entry (high eight bits only)
goto[DevFound, alu=0], lu ← T;
goto[DI4y, ALU#0], DevIndex ← (DevIndex) + (2c); *check for end of table (zero entry)
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:
nop; *allocation constraint
T ← (DevIndex)+1, call[GetCon];
stack ← T, goto[DI4z]; *replace slot table entry with device table entry

*Clock out new controller ID’s
DI5:
xCNT ← 57c;
stkp ← xCNT, call[ClockOutPattern];

*Call all the Init routines
xCNT ← 17c, call[DI6]; *do call to set up TPC for loop below
DI6:
xCNT ← (xCNT)-1, goto[DI7, R<0];
lu ← ldf[(stack),4,14]; *check for Init PC = 0 (no initialization required)
goto[.+3, ALU=0], T ← Stack&-1;
DevIndex ← T; *set up to call Init routine for device
APC&APCTask ← DevIndex;*call Init routine for controller - returns to DI6
initRET:
return;

DI7:
ErrorCnt ← (10c), goto[BootEmulators]; *Set up retry count for disk boot

ClockOutPattern:
usectask;
T ← APC&APCTASK;
rlink0 ← T;
xCNT ← 17c; *go through the slot table backwards
COP1:
DevIndex ← 2c; *DevIndex used for loop count
COP2:
T ← (stack) xor (1C); *complement bit
GENSRCLOCK; *send bit
stack ← rcy[stack,1]; *get next bit
DevIndex ← (DevIndex) - 1, goto[COP2, r>=0];
xCNT ← (xCNT) -1; *all slot table entries done?
stack&-1, goto[COP1, alu>=0]; *get next word
APC&APCTASK ← rlink0, goto[initRET];

GetCon:
contemp ← T; *word index into Dtab
contemp ← (contemp) + (AND[lshift[DtabBase,1], 17400]C);
contemp ← (contemp) or (AND[lshift[DtabBase,1], 377]C);
contemp ← rsh[contemp,1]; *instruction address in CS
T ← (ldf[AllOnes,17,1]) and (T); *low bit tells which half
APC&APCTask ← contemp;
READCS;
T ← CSDATA, return, AT[InitBase,10]; *location must be even


BootEmulators:
lu ← BootType, goto[EtherBoot,R<0];
loadpage[DiskInitPage];
gotop[DiskBoot];


*Etherboot, Alto style
EtherBoot:
AC0 ← 400C;
T ← (R400) + (204C);*Input buffer pointer (EIBLoc)
PStore1[MDS,AC0];*EICLoc ← 400
AC1 ← 1C;
T ← (R400) + (205C);
PStore1[MDS,AC1];*EIBLoc ← 1;
T ← (R400) + (210c), Task; *Host address (EHLoc)
AC1 ← 377C;
PStore1[MDS,AC1];*ESLOC ← 377 (serial number for breath of life packets)
T ← (R400) + (200c), Task; *Post location (EPLoc)
AC1 ← ZERO;
PStore1[MDS,AC1];*EPLoc ← 0(status word)
RTEMP ← AND[0377, EIStartLoc]C, CALL[.+2];
GOTO[EContRead]; *get here (in task 0) after Ether has started and tasked
RTEMP ← (RTEMP) OR (OR[lshift[EITask,14],AND[007400, EIStartLoc]]C);
RCNT ← (300c); *Disable input/output
T ← (LSHIFT[EOTask,4]C); *Ethernet reset register
OUTPUT[RCNT];
APC&APCTASK ← RTEMP, goto[initRET];


EContRead:
T ← (R400) + (200c); *Get post code
PFetch1[MDS,AC1], call[initRET];
LU ← AC1;
AC1 ← (AC1) xor (377C),GOTO[EContRead,ALU=0];
EReadDone: T ← 2C;
PFetch1[MDS,AC2];
T ← (R400)+(203C);
T ← (ZERO) - T;*-(Breath-of-life)-1
AC2 ← (AC2)+T+1;
AC0 ← 10c, GOTO[BootEmulators,ALU#0]; *Ether boot hack to get net exec without
*typing singlequote.

*got the breath of life!!!! go to emulator with PC=5;
loadpage[0];
PC ← 5c, goto[StartNova];

OnPage[DiskInitPage];

*Read disk Sector 0 into page 0 (starting at location 1).
DiskBoot:

ErrorCnt ← 10c; *we will try this sequence 10 times before giving up
DiskBootx:

loadpage[0];
T← StartDiskBoot, callp[PNIP];
IBuf ← 0c; *word 520 - not used by disk
IBuf1 ← 0c; *word 521 - IOCB pointer
IBuf2 ← 0c; *word 522 - disk status
IBuf3 ← 0c; *word 523 = -1 to force a seek
T←(R400) or (120c);
pstore4[MDS,IBuf],CALL[kbRET]; *520-523 ← 0,0,0,0

*Set up the IOCB at 1000b
IBuf2 ← 44000C; *disk command goes at location 1002 (read, read, read)
IBuf3 ← 2000C; *header goes at 2000 (unlike ALTO)
DMA ← T ←1000C;
PStore4[MDS,IBuf], CALL[kbRET]; *1000-1003 ← 0,0,44000,2000
IBuf2 ← 400c;
IBuf2 ← (IBuf2) + (2c); *label goes at 402
IBuf3 ← 1C; *data goes at 1
T ← (DMA) + (4c);
PStore2[MDS,IBuf2], CALL[kbRET]; *1004-1005 ← 402,1

*Since we will initialize the interrupt system later, we do not need to worry about 1006 or 1007
*Location 1008 is unused
T ← (DMA) + (11c);
PStore1[MDS,IBuf3], CALL[kbRET]; *IBuf1 = 1.. Disk address 0, with RESTORE bit at 1009


*Start the disk
IBuf1 ← 1000c; *word 521 - IOCB pointer
IBuf2 ← 0c;
T ← (R400) + (120c);
PStore4[MDS,IBuf], call[kbRET]; *520-523 ← 0,1000,0,1 (T contains 520 here)

*Wait for the disk to store good status in the DCB, retry if status is bad
ErrorCountX ← 20c;
DWSet:
DevIndex ← 40000c; *loop count for status wait
DiskWait:
T ← (DMA) + 1;
Pfetch1[MDS,xCNT]; *fetch status word at 1001b
T ← 17C, call[kbRET];
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];
NoStatus:
T ← NoDiskStatus, goto[kbFail]; *timed out waiting for disk to store status

StatusStored: dblgoto[GoodStatus,IncErCnt,ALU=0];

GoodStatus: loadpage[0]; *send control to location 1
PC ← 1c, gotop[StartNova];

IncErCnt:
ErrorCnt ← (ErrorCnt)-1, goto[DiskBootx,R>=0];
T ← BadBoot, goto[kbFail]; *read 10 times, but header was wrong

kbFail:
loadpage[initPage];
gotop[InitFail];

kbRET:
return;

*SUBROUTINE PNIP puts the number in T into the maintenance panel
*It will be used after initialization is complete
*Does not task unless called from task 0
ONPAGE[0];

PNIP:
usectask, RTEMP ← T, at[PNIPBase,20];
T ← APC&APCTask, at[PNIPBase,17];
RCNT ← T, ClearMPanel, call[.+1], at[PNIPBase,0];
PNloop:
RTEMP1 ← 4C, at[PNIPBase,1];
RTEMP1 ← (RTEMP1)-1, dblgoto[.+1,.,ALU<0], at[PNIPBase,6];
RTEMP ← (RTEMP) - 1, at[PNIPBase,7];
lu ← ldf[RCNT,0,4], goto[PNdone, ALU<0], at[PNIPBase,16];
skip[alu#0], at[PNIPBase,4];
IncMPanel, return, at[PNIPBase,2]; * task 0, tasking ok
IncMPanel, goto[PNloop], at[PNIPBase,3]; * task #0, tasking not allowed
PNdone:
APC&APCTask ← RCNT, at[PNIPBase,5];
PNRet:
return, at[PNIPBase,15];

*SUBROUTINE DoInt ORs the bits into NWW and sets
*IntPending. Uses registers 0 and 1 in whatever task calls.
*If bits are in T, call DoTIntNT (for a notask return) or DoTIntT (for a tasking return).
*If bits are in register 0, call DoRIntNT (notask) or DoRIntT (tasking).

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

DoTIntNT:
IntTemp1 ← T;
DoRIntNT:
T ← 377c, goto[DoIntx];

DoTIntT:
IntTemp1 ← T;
DoRIntT:
T ← (Zero) - 1, goto[DoIntx];

DoIntx:
IntTemp2 ← pNWW;
T ← (Stkp) xor (T);
Stkp ← IntTemp2, IntTemp2 ← T, NoRegILockOK;
T ← (IntTemp1) and not (100000C);
IntTemp1 ← pRSImage;
Stack ← (Stack) or (T);* set bits in NWW
Stkp ← IntTemp1, skip[ALU=0];
T ← Stack ← (Stack) or (IntPendingBit), goto[.+2];
T ← Stack;
Stkp ← IntTemp2, skip[R<0];
usectask;
RS232 ← T, return;* set IntPending

:END[Initialize];