%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;(1795)\13360f1 32f0 96f1 167f0