%insert[d0lang];
NOMIDASINIT;
*MULTDIB;
insert[GlobalDefs]; *task and page assignments
%
*Micro/O/U GlobalDefs/R dtestinitialize

TITLE[DTestInitialize];

*last edit by Thacker, July 3, 1979 for Alto UTVFC and Alto RDC
*edit by Johnsson, June 13, 1979 4:53 PM new FFault register
*edit by Chang, June 3, 1979 4:11 PM Overlay booting
*edit by Johnsson, May 15, 1979 3:27 PM, PNIP fix
*edit by Chang, May 10, 1979 8:31 AM, add new Ethernet ID
*edit by Sandman, April 6, 1979 3:50 PM

*Modified March 6, 1979 by CPT. Added fault handling



*Registers for IMAP
RV[xmad0,22]; *base register
RV[xmad1,23];
RV[xmbuf0,24]; *quadword buffer for XMap and PFetch4
RV[rbuf0,24];
RV[xmbuf1,25];
RV[rbuf1,25];
rv[xmbuf2,26];
RV[rbuf2,26];
RV[xmbuf3,27];
RV[rbuf3,27];
RV[wbuf0,30]; *quadword buffer for PStore4
RV[wbuf1,31];
RV[wbuf2,32];
RV[wbuf3,33];
RV[rlink0,34]; *subroutine return link
RV[MapEntry,35]; *current map location
RV[ZPage,35]; *page being cleared
RV[RealPage,36]; *current real storage page
RV[ZWord,36];
RV[PageCount,37]; *count of available real pages in system
RV[CompFlag,40];
RV[BootType,0]; * even => hard boot, odd => soft boot; negative => ether, positive => disk

*Registers for other sections of initialization
RV[xCNT,20]; *used everywhere
RV[DevIndex,21]; *used in DeviceInit
MC[pDX,21]; *pointer to DevIndex
RV[contemp,22]; *used in DeviceInit
RV[initr0,40]; *used in DiskBoot
RV[initr1,41]; *used in DiskBoot
RV[initr2,42]; *used in DiskBoot
RV[initr3,43]; *used in DiskBoot
RV[ErrorCnt,44]; *used in DiskBoot
RV[ErrorCountx,45]; *used in DiskBoot

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

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

SETTASK[0];

SET[InitBase,ADD[lshift[InitPage,10],200]];
SET[HardStart, ADD[InitBase,1]];
SET[SoftStart, ADD[InitBase,2]];
SET[DiskStart, ADD[InitBase,3]];
SET[EtherStart, ADD[InitBase,4]];
MC[InitLocL,AND[InitBase,377]];
MC[KInitLocL, ADD[AND[InitBase,377],3]];
MC[EInitLocL, ADD[AND[InitBase,377],4]];
MC[InitLocH,AND[InitBase,7400]];

SET[Qloc,ADD[InitBase,5]];
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
*NO KGO,EGO for dtest
GO:
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;
DevIndex ← QRetL;
DevIndex ← (DevIndex) or (QRetH);
Qloop:
APC&APCTASK ← xCNT;
return; *goes to Qx

Qx:
APC&APCTASK ← stack,call[initRET], AT[Qloc]; *Notify comes here. Leave task’s TPC pointing at Qxy.
Qxy:
goto[initRET]; *must spend two instructions in the task


Qret:
lu ← ldf[xCNT,0,3], AT[QretLoc]; *xCNT points to this location
xCNT ← (xCNT) - (10000C), dblgoto[ZapDevices,Qloop,ALU=0];

ZapDevices:
T ← 177400C;
xCNT ← 0c;
ZapDloop:
OUTPUT[xCNT];*send a 0 to all registers of all devices, hopefully quiescing them
T ← (zero) + (T) + 1;
dblgoto[ZapDloop,DoMap,ALU<0];

DoMap:
loadpage[0];
T ← StartMapInit, CallP[PNIP];
xCNT ← AND[377,TimerInitLoc]C, call[.+2]; *set up to notify to InitTimers
*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:
CompFlag ← (zero)-1,goto[imCompx]; *Timer initialization returns to here
xCNT ← (xCNT) or (OR[lshift[TTask,14], AND[7400,TimerInitLoc]]C), goto[Qloop];
imAloop:
T ← (CompFlag) xor (T);
xmbuf0 ← T, call[imWriteMap];
T ← MapEntry;
T ← (CompFlag) xor (T), call[imReadMap];
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];
goto[.+2,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;

*fill first quadword of each real page with its page number and some constants.
*go through real memory backwards so that hole in 96k modules will not
*screw up non-hole banks.

wbuf1 ← 326C; *random constant
wbuf2 ← 134000C;
wbuf3 ← (zero) ;
imFloop:
RealPage ← T ← (RealPage)-1;
xmbuf0 ← T, goto[.+2,ALU>=0];
T ← RealPage ← 170000C, goto[imTloop];
wbuf0 ← T;
call[imWriteMap];
PStore4[xmad0,wbuf0,0], goto[imFloop];

*during this phase, we sweep upward through real storage and the map, and
*use any real pages discovered.

imTloop:
xmbuf0 ← T;
xmbuf0 ← (xmbuf0) and not (170000C), call[imWriteMap]; *set base reg to point to MapEntry,
*set MapEntry to point to RealPage. Set all map flags off.
nop;
call[.+2], stack ← (Stack) or (100000c);*turn on fault handler
imFault:
goto[imPageBad], stack ← (Stack) and not (100000c); *get here on a fault - turn off fault handler
PFetch4[xmad0,rbuf0,0]; *fetch. Will cause fault if page is bad
T ← rbuf0;
lu ← (ldf[RealPage,4,14]) xor (T);
goto[.+2, ALU = 0], stack ← (Stack) and not (100000c);*turn off fault handler
goto[imPageBad]; *page number didn’t compare
T ← (rbuf1) xor (326C); *a final check against constants
rbuf2 ← (rbuf2) xor (134000C);
T ← (rbuf2) or (T);
T ← (rbuf3) or (T);
dblgoto[imPageGood, imPageBad, ALU=0];

imPageGood:
MapEntry ← (MapEntry) + 1;
PageCount ← (PageCount) + 1;
imPageBad: T ← RealPage ← (RealPage) + 1;
goto[imTloop, nocarry]; *done with all of real memory?
wbuf1 ← 0C; *clear wbuf1 in preparation for core zap.
imMarkVacant:
xmbuf0 ← 60000C; *page vacant
Call[imWriteMap];
MapEntry ← (MapEntry) + 1;
T ← PageCount, goto[imMarkVacant, nocarry]; *done with all map entries?

imCoreZap:
wbuf3 ← 0C;
wbuf2 ← 0c;
Zpage ← T;

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

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

imDone:
lu ← (PageCount) -(400c); *don’t try to run with less than 64K
T ← NotEnoughMemory, goto[InitFail, ALU<0];
goto[RegInit1];

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

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

*SUBROUTINE imReadMap reads one entry from MapEntry
*into rbuf0, then compares it with (MapEntry xor CompFlag)
imReadMap:
xmbuf0 ← T, usectask;
T ← apc&apctask;
rlink0 ← T, call[imWriteMap];
T ← lsh[xmbuf3, 10]; *flags, card, blk.0 bits
rbuf0 ← T;
T ← (xmbuf1) and (377C);
rbuf0 ← (rbuf0) xor (T);
T ← MapEntry;
T ← (CompFlag) xor (T);
lu ← (rbuf0) 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 38us timer.

RegInit1:
Nova ← zero;
Novah ← zero;
DMAh ← zero;
R400 ← (400c);
AllOnes ← (zero)-1;
RZero ← zero;
xCNT ← pTMR38Con;
stkp ← xCNT;
LoadTimer[Stack], goto[DeviceInit];



*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...
Macro[dtab,
DATA[(LH[#1], RH[LSHIFT[#2,4],#3], dp[#1,#2,#3], at[dtabloc])]
SET[dtabloc,ADD[dtabloc,1]]];

*also, we must make the parity of an entry correct, or Midas will correct it for us...
Macro[dp,set[dpx,xor[1,#1,#2,#3]]
set[dpx,xor[dpx,rshift[dpx,10]]]
set[dpx,xor[dpx,rshift[dpx,4]]]
set[dpx,xor[dpx,rshift[dpx,2]]]
set[dpx,xor[dpx,rshift[dpx,1]]]
RX@[and[1,dpx]]];

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

*Here is the device table
*NOT FOR DTEST *dtab[3400,EtherInitLoc,EITask]; *New Ethernet input = 3400b
*NOT FOR DTEST *dtab[3000,0,EOTask]; *New Ethernet output=3000b(No Initialization Required)
dtab[1000,DisplayInitLoc,DpTask]; *Display (UTVFC) = 1000b
*NOT FOR DTEST *dtab[1400,DiskInitLoc,KTask]; *Disk (Alto RDC) = 1400b
*NOT FOR DTEST *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

*NOT FOR DTEST INSERT[EtherDefs]; *to get displacements for Etherboot

BootEmulators:
loadpage[nepage];
gotop[DisplayTest];


% NOT FOR DTEST
lu ← BootType, goto[EtherBoot,R<0];
loadpage[DiskInitPage];
gotop[DiskBoot];


*Etherboot, Alto style
EtherBoot:
PCB ← 0C;
PCBh ← 0C;
AC0 ← T ← 400C;
T ← (AC0) + (EICLOC1);
AC1 ← 1C;
PStore2[PCB,AC0];*EICLoc ← 400,EIBLoc ← 1;
T ← (AC0) + (EHLOC1),TASK;
AC1 ← 377C;
PStore1[PCB,AC1];*ESLOC ← 377 (serial number for breath of life packets)
T ← (AC0) + (EPLOC1),TASK;
AC1 ← ZERO;
PStore1[PCB,AC1];*EPLOC ← 0(status word)
RTEMP ← AND[0377, EIStartLoc]C, CALL[.+2];
AC0← 400C,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 ← (EDisableInputOutput);
T ← EOReset;
OUTPUT[RCNT];
APC&APCTASK ← RTEMP, goto[initRET];


EContRead:
T ← (AC0) + (EPLOC1);
PFetch1[PCB,AC1], call[initRET];
LU ← AC1;
AC1 ← (AC1) xor (377C),GOTO[EContRead,ALU=0];
EReadDone: T ← 2C;
PFetch1[PCB,AC2];
T ← (AC0)+(203C);
T ← (ZERO) - T;*-(Breath-of-life)-1
AC2 ← (AC2)+T+1;
GOTO[BootEmulators,ALU#0];

*got the breath of life!!!! go to emulator with PC=3;
loadpage[nepage];
T ← 3c, goto[Jmp];

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];
initr0 ← 0c; *word 520 - not used by disk
initr1 ← 0c; *word 521 - IOCB pointer
initr2 ← 0c; *word 522 - disk status
initr3 ← 0c; *word 523 = -1 to force a seek
T←(R400) or (120c);
pstore4[Nova,initr0],CALL[kbRET]; *520-523 ← 0,0,0,0

*Set up the IOCB at 1000b
initr2 ← 44000C; *disk command goes at location 1002 (read, read, read)
initr3 ← 2000C; *header goes at 2000 (unlike ALTO)
DMA←1000C;
PStore4[DMA,initr0,0], CALL[kbRET]; *1000-1003 ← 0,0,44000,2000
initr2 ← 400c;
initr2 ← (initr2) + (2c); *label goes at 402
initr3 ← 1C; *data goes at 1
PStore2[DMA,initr2,4], 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
PStore1[DMA,initr3,11], CALL[kbRET]; *initr1 = 1.. Disk address 0, with RESTORE bit at 1009


*Start the disk
initr1 ← 1000c; *word 521 - IOCB pointer
initr2 ← 0c;
PStore4[Nova,initr0], 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:
Pfetch1[DMA,xCNT,1]; *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[nePage]; *send control to location 1
T ← 1c, gotop[JMP];

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;

DTEST Deletion ends here %


*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 ← CTASK, 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];
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;
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;