: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];(1795)\13115f1 32f0 86f1 211f0