{ File name: CoreInitial.mc Description: Core of the initial microcode booting sequence, Author: Jarvis, Created: November 12, 1980, Last Edited: BJackson, 18-Dec-85 18:12:55 Added realBankCnt to identify memory size BJackson, 7-Feb-86 1:00:19 Syntactic Sugar, try to fix bug I created. Fiala, 20-May-86 16:32:01 Added Bank ← and MCtl change at "go" for 4MB storage. Fiala, 30-May-86 15:57:43 Removed commented out No-ops; added comments; removed some Noops in the memory-finding code; added code to map up to 4MB storage. This code worked on the DandeTiger storage boards (3.5 megabyte) but not on the 3.0 megabyte modified Dandelion storage boards. Fiala, 2-Jun-86 11:14:53 Changed MCtl value from 8000 (works for 3.5 megabyte storage boards) to 8002 (works for both 3.5 and 3.0 megabyte storage boards). Fiala & BJackson, 17-Jul-86 18:02:30 Incorporate code from the current product Initial and other changes as follows: (1) Conditional assembly for Trident initialization if Config = 2. (2) Symbolic definitions for some real and virtual page values. (3) MoveGerm subroutine formerly in SAxInitial, EtherInitial, etc. device specific modules moved here and deleted elsewhere. (4) MapGerm subroutine added. (5) Every time check for IO page while building the map. Fiala 25-Jul-86 12:12:14 Cosmetic edits. Fiala 30-Jul-86 11:35:00 Cosmetic edits. } {save room for boot kernel} Reserve[ProtectStart, ProtectFence], Reserve[0FE0, 0FFF]; {IO page offsets} Set[DPYCtlWord, 0EC]; {8000=> turn display off} SetTask[0], StartAddress[go]; Set[DisplayBank, 0]; Set[mapBank, 1]; Set[diskBufVirtual, 0FE]; Set[IOPage.DSCB.syncCmd, 0EC]; {8000 => turn display off} {MCtl ← 8000 => bit 8 from rH will be an address bit rather than a flag bit. MCtl 2 => enables the upper 1.5 mb on 3.0 mb storage boards; also works on DTiger 3.5 mb storage boards. Bits 12 and 13 of Bank ← are loaded into MS.0 and MS.1, while bits 14 and 15 are the control store bank number. MS[0..1] control which banks are display banks. With 4 flag bits, MS[0..1] = 0 causes banks 0, 10, 80, and 90 to all cause waiting for processor access; since bits 8 and 9 from rH are both flag bits, this really means that any bank 0 access causes waiting. MS[0..1] = 1 causes banks 0 and 10 to be display banks; when bit 8 in rH is an address bit instead of a flag bit, this is appropriate. MS[0..1] = 2 means only bank 0 is a display bank, appropriate if bit 11 is an address bit instead of a flag bit. Also see the discussion in DTigerManual.memo. } go: rB ← 80, CANCELBR[$, 0F], c1; rB ← rB LRot8, c2; rB ← rB + 2, c3; Bank ← 4, c1; MCtl ← rB {8002h}, c2; {Quiesce the io tasks.} IOPCtl ← 0, c3; {Currently defined configurations are 0 = regular, 1 = Raven, 2 = Trident} IfGreater[Config, 1, SkipTo[TridentInit],]; {SAx000/Quantum init} KCtl ← 0, c1; SkipTo[NoTridentInit]; TridentInit! {Trident init} RCnt ← 0F, c1; KCtl ← RCnt LRot12, c2; acR ← 4, c3; KCmd ← acR LRot8, c1; NoTridentInit! DCtl ← 3, c2; {display black, enable task} PCtl ← 0, c3; EICtl ← 0, c1; EOCtl ← 0, c2; Noop, c3; {Map initialization First, write the page number into the first word of each page; the display bank (first 64k words) and the map (first 16k words in the second bank) are skipped. The code allows for a maximum 4MB storage configuration in which the valid word addresses are 0 to 1M and 8M to 9M. The gap is caused by the fact that the high-order address bit is bit 8, separated from the next address bit in bit 12 by 3 flag bits in [9..11]. Since there are 256 words/page and 256 pages/bank, the code looks for pages in banks [0..F) and [80..8F). The map size, currently 16k words, is controlled by parameters in Boot.dfn. The current convention is that the IOPage lies immediately after the map, so its location must also change when the map moves; however, the code here is supposed to allow the IO page to be moved anywhere else above the map. After marking each possible page, all 16k map entries are initialized to "vacant" by writing the proper code into storage [64k..80k) which is the map. The next step is to read the mark in each page beginning with the page after the map; if the mark is equal to the page number, then the page is assumed to exist and is mapped in the next map entry; however, the real and virtual locations for the IOPage are skipped. Finally, all mapped pages are zeroed. After mapInit returns to mapRet, all but the first two pages of bank 0 will also be cleared. Timing with 4 megabytes of storage is roughly (2M/256 - 256 - 64 pages) * (2 for markPages + 3 for mapBuild + 5 for mapLoop + 259 for clearPage) clicks * .411 microseconds/click = 0.86 seconds. So only the clearPage code has significant execution time. Register usage acR page number (initially first page after the map) rB, rBrh pointer to first word of next page Q temporary rC temporary rD stopping page number rE, rErh pointer to next map entry } @mapInit: acR ← acR xor ~acR {-1}, c1; passTraps ← acR {-1 = catch faults}, c2; rB ← rBrh ← FirstRealPageToMapHigh {1}, c3; acR ← rB LRot8, c1; rB ← FirstRealPageToMap {40}, c2; acR ← acR or rB {page number of 1st page written}, c3; rB ← rB LRot8 {Low part of 1st word in 1st page}, c1; rD ← 10, c2; rD ← rD LRot8 {1000 = stopping page}, c3; {mark the first word of each page with its page number} markPages1: MAR ← [rBrh, rB + 0], c1; MDR ← acR, rC ← 0FF + 1, c2; acR ← acR + 1, c3; [] ← acR xor rD, ZeroBr {completed current megaword?}, c1; rB ← rB + rC, CarryBr, BRANCH[$, markM2], c2; Q ← rBrh, BRANCH[markPages1, $], c3; Q ← Q + 1, c1; rBrh ← Q LRot0, c2; GOTO[markPages1], c3; markM2: rB ← rBrh ← 80, CANCELBR[$], c3; acR ← rB LRot8 {next page number}, c1; rB ← 0, c2; rD ← 90, c3; rD ← rD LRot8 {stopping page}, c1; Noop, c2; Noop, c3; markPages2: MAR ← [rBrh, rB + 0], c1; MDR ← acR, rC ← 0FF + 1, c2; acR ← acR + 1, c3; [] ← acR xor rD, ZeroBr {completed current megaword?}, c1; rB ← rB + rC, CarryBr, BRANCH[$, mapBuild], c2; Q ← rBrh, BRANCH[markPages2, $], c3; Q ← Q + 1, c1; rBrh ← Q LRot0, c2; GOTO[markPages2], c3; {Here point <rB, rBrh> at the first word of the map and <rC, rCrh> at the second word of the map. Mark all pages vacant by first storing the value for a vacant page in word 0 of the map and then BLT'ing this value from i to i+1 for all the pages in the map. } mapBuild: Noop, CANCELBR[$], c3; rBrh ← MapRealAddrHigh, c1; rB ← MapRealAddrLow, c2; acR ← mapPages, c3; MAR ← [rBrh, rB + 0], c1; MDR ← vacant, c2; acR ← acR LRot8, L0 ← L0.vmSetup, c3; acR ← acR - 1, c1; rC ← rB + 1, c2; rCrh ← MapRealAddrHigh, CALL[BLT], c3; {Now put existing storage underneath the map entries beginning with the first real page after the map; if the value found in word 0 is the value written earlier, then map the page into the next map entry. Here setup [rErh, rE] to point at the first map entry in storage; [rBrh, rB] to point at the first word in the first page after the map; acR to hold the page number stored at location 0 in the first page after the map; rD to hold the real address of the Cedar IO page. Note that rC is smashed on double-bit errors. The first 64k words (bank 0) are assumed to exist and used for the display bitmap. The first 16k (or 32k or 64k) words in bank 1 are assumed to exist and used for the map. } vmSetup: rE ← MapRealAddrLow {0}, c1, RtnBLT[L0.vmSetup]; rErh ← MapRealAddrHigh {1}, c2; {map in bank 1} {IOPageHigh = FirstRealPageToMapHigh} rBrh ← rB ← FirstRealPageToMapHigh {1}, c3; acR ← rB LRot8, c1; acR ← acR or FirstRealPageToMap {page number}, c2; rB ← FirstRealPageToMap {40}, c3; rB ← rB LRot8, c1; rD ← cedarIOPageHigh, c2; rD ← rD LRot8, c3; rD ← rD or cedarIOPage, c1; Noop, c2; Noop, c3; mapLoop1: MAR ← [rBrh, rB + 0], c1; Q ← rB or rBrh {map entry excluding flags}, c2; rC ← MD {double error bit error possible here}, c3; Noop, c1; [] ← rC xor acR, ZeroBr, c2; BRANCH[nextReal, $], c3; [] ← rD xor acR, ZeroBr {real page = IOPage?}, c1; rC ← IOPageVirtual {0FF}, BRANCH[NotIOPageReal, $], c2; GOTO[nextReal] {Skip the io page}, c3; {If this is the map entry for the IOPage, map the IOPage now and do not increment the real page number since no real page is used.} NotIOPageReal: [] ← rC xor rE, NZeroBr, c3; MAR ← [rErh, rE + 0], BRANCH[$, NotIOPageVirt], c1; rC ← rD LRot8 {IOPage map entry}, c2; Noop, c3; MAR ← [rErh, rE + 0], c1; MDR ← rC or present, c2; rE ← rE + 1, GOTO[mapLoop1], c3; NotIOPageVirt: MDR ← Q or present {map entry}, c2; rE ← rE + 1, c3; {Jump here after double-bit errors} nextReal: rC ← 0FF + 1, c1; rB ← rB + rC, CarryBr, c2; acR ← acR + 1, BRANCH[mapLoop1, nextBank], c3; {Advance to the next bank and check for done.} nextBank: Q ← rBrh, c1; [] ← Q xor 0F, ZeroBr, c2; rC ← 08F, BRANCH[$, megaChange], c3; [] ← Q xor rC, ZeroBr, c1; Q ← Q + 1, BRANCH[$, clearMem], c2; rBrh ← Q LRot0, GOTO[mapLoop1], c3; {0 to 1M now done; next do 8M to 9M} megaChange: rB ← rBrh ← 80, c1; acR ← rB LRot8, c2; rB ← 0, GOTO[mapLoop1], c3; {clear all mapped pages} clearMem: topPage ← rE, c3; {save away} rE ← 0, c1; {word offset} rD ← 0, c2; {source of zero} passTraps ← rD, c3; {die on double bit errors} clearLoop: MAR ← [rErh, rE + 0], c1; {read the map} rC ← 0, c2; acR ← MD, c3; rB ← acR and 08F, c1; rBrh ← rB LRot0, c2; rB ← acR and ~0FF, c3; {write into every word of the page} clearPage: MAR← [rBrh, rC + 0] ,c1; MDR← rD, rC← rC + 1, PgCarryBr ,c2; acR← topPage, BRANCH[clearPage, $] ,c3; rE← rE + 1 ,c1; []← rE xor acR, ZeroBr {compare to topPage}, c2; BRANCH[clearLoop, $] ,c3; Noop, c1; {Map initialization has finished. Clear all but the first two pages of bank 0. } mapRet: acR ← 0, c2; passTraps ← acR, c3; rD ← 2, c1; rD ← rD LRot8, c2; rDrh ← 0, c3; clear: MAR ← [rDrh, rD+0], c1; MDR ← acR, rD ← rD+1, ZeroBr, c2; BRANCH[clear, $], c3; Noop, c1; acR ← 0FF+1, c2; uBootStart ← acR, GOTO[OnceOnlyInit], c3; {OnceOnlyInit in InitDLion.mc transfers to DoneOnceOnlyInit in the device specific initial microcode. When that finishes, control passes to exitToEmulator. } exitToEmulator: Noop, c1; rErh ← cedarIOPageHigh, c2; {Make sure the display is off when the germ starts.} acR ← RShift1 0 {8000}, SE ← 1, c3; rE ← uIOPage {IOPage real address}, c1; rB ← mapPages {40}, c2; rBrh ← 0, c3; MAR ← [rErh, IOPage.DSCB.syncCmd+0], c1; MDR ← acR, c2; {VMMSize = mapPages LRot8} rB ← rB LRot8, c3; SetVMMSize: MAR ← [rErh, IOPage.VMMSize+0], c1; MDR ← rB, c2; {****rB isn't used below****} enableIOP: rB ← 0, c3; MAR← [rBrh, 0+0] ,c1; MDR← 0FF ,c2; Noop ,c3; MAR← [rBrh, 0+1] ,c1; MDR← uBootStart, CANCELBR[$, 0] ,c2; IOPCtl← IOPInMode, GOTOABS[IdleLoc] ,c3; {subroutines and end matter} { trap catcher, gets here with rC← RRot1 ErrnIBnStkp } error: Xbus← rC LRot0, XwdDisp, c2, at[ErrorHandlerLoc]; DISP2[errorType], c3; GOTO[death], c1, at[0, 4, errorType]; {control store parity error} Xbus← MStatus, XLDisp, GOTO[memFault], c1, at[1, 4, errorType]; GOTO[death], c1, at[2, 4, errorType]; {stack error} GOTO[death], c1, at[3, 4, errorType]; {instruction buffer empty} death: GOTO[death], c*; {A memory error of some type occurred. If MStatus[8]=1, then a virtual address used during a Map ← was out of range; the hardware PROM currently assumes that virtual addresses are 22 bits wide, so if the two high-order bits of the rh register used for Map ← are not 0, then this trap occurs. Otherwise, the error is an emulator double-bit memory error; io task double-bit errors do not cause this trap. At most one additional emulator click can execute after the memory read click before the trap to location 0. The XLDisp above dispatches on bits 8 and 15, so the branch below separates the VA out-of-bounds from the double-bit read error traps.} memFault: BRANCH[death, $, 1] ,c2; {VA out of bound?} GOTO[nextReal], c3; {no, double bit error} {Block transfer acR/ count rB/ from rC/ to Returns first word past from block in rE } BLT2: Noop ,c2; BLT3: Noop ,c3; BLT: MAR← [rBrh, rB+0] ,c1; []← acR, ZeroBr ,c2; rE← MD, BRANCH[$, endBLT] ,c3; MAR← [rCrh, rC+0] ,c1; MDR← rE ,c2; acR← acR-1 ,c3; rB← rB+1 ,c1; rC← rC+1 ,c2; GOTO[BLT] ,c3; endBLT: Noop ,c1; endBLT1: pRet0 ,c2; endBLT2: RET[subrRet] ,c3; {swaps two locations in memory, takes address in rE and rB, clobbers acR } memSwap2: Noop ,c2; memSwap3: Noop ,c3; memSwap: MAR← [rErh, rE+0] ,c1; Noop ,c2; acR← MD ,c3; MAR← [rBrh, rB+0] ,c1; MDR← acR ,c2; acR← MD ,c3; MAR← [rErh, rE+0] ,c1; MDR← acR, pRet0 ,c2; RET[subrRet] ,c3; {++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ MoveGerm The requested code has been retrieved successfully by the device specific Initial and placed into storage at ?? in the first 64k of storage. Move the germ from this physical address to virtual address 100h. Subsequently, mapGerm will move the germ from virtual address 100h to the desired run location by interchanging virtual pages. <germPageHigh, germPageLow> are the virtual address to which the germ is being moved. rB: germStart (LRot8 is the page # of the begining of the germ) rD: nextPage (the page after the germ) +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ } MoveGerm: rE ← rB LRot8 ,c3; rD ← nextPage ,c1; rD ← rD - rE, ZeroBr ,c2; transferCount ← rD, BRANCH[StartMove, $] ,c3; acR ← bootNullGerm, GOTOABS[Maintenance2Loc] ,c1; {null germ?} {rB, rBrh/ "from" address for BLT rF, rFrh/ virtual "to" address for germ rC, rCrh/ "to" address for BLT acr/ word count for BLT (100) rD/ number of pages in the germ (non-0) } StartMove: {Noop} ,c1; {Move the germ.} {germStart ← acR xor ~acR} rFrh ← germPageHigh, rF ← 0 + germPageLow ,c2; rF ← rF LRot8 ,c3; MoveGermLoop: Map ← [rFrh, rF] ,c1; rBrh ← 0 ,c2; rCrh ← rC ← MD ,c3; acR ← 0FF + 1 {word count}, c1; rC ← rC and ~0FF, L0 ← L0.mgLoop ,c2; CALL[BLT] ,c3; mgLoop: rF ← rF + 0FF + 1 ,c1, RtnBLT[L0.mgLoop]; rD ← rD - 1, ZeroBr ,c2; rE ← LShift1 0FF, SE ← 1, BRANCH[MoveGermLoop, mapGerm] ,c3; {rE=@SD[]-1 PrincOps4.0} {++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Well, We've got a problem here. PrincOps 3.0 expects the germ at .mv [3E, 0200] and PrincOps 4.0 expects the germ at .mv [00, 0100]. transferCount contains the # of pages we have to move and we have to move them from Map[0001] to Map[3E02] CoreInitial sets up map and leaves next available page (virtual) in topPage. Since everybody's been nice enough so far to move things around and play the game according the the PrincOps 4.0 rules (like Burdock), how about if we just swap the approprite map entries and leave everything as it already is? Well, that leaves a funny hole in VM at [00, 0100], So... So we do an even cuter swap put the germ where it belongs: [01, 3E02] <==> [01, 0001] move some pages back under .vm 0100 to be *nice* [01, (topPage-1)-transferCount] <==> [01, 0001] Of course, there may ultimately be a big problem here if Cedar's GermSwapImpl *really* expects the germ to live in the display bank! +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ } mapGerm: rB ← germPageHigh ,c1; rB ← rB LRot8 ,c2; rB ← rB or germPageLow ,c3; {Map entry for 4.0 germ} rE ← cedarGermPageHigh ,c1; rE ← rE LRot8 ,c2; rE ← rE or cedarGermPageLow ,c3; {Map entry for 3.0 germ} rC ← transferCount ,c1; {number pages in germ} rErh ← MapRealAddrHigh {1}, c2; rBrh ← MapRealAddrHigh {1}, c3; looper: {Noop} ,c1; L0 ← L0.germExch ,c2; CALL[memSwap] ,c3; germExch: rB ← rB + 1 ,c1, RtnBLT[L0.germExch]; {make pages vacant} rC ← rC - 1, ZeroBr ,c2; rE ← rE + 1, BRANCH[looper, swipe] ,c3; { Now move a swatch of physical memory *present* in top vm to sit under where .vm 0100 used to be! } swipe: rB ← germPageHigh ,c1; rB ← rB LRot8 ,c2; rB ← rB or germPageLow ,c3; {Map entry for 4.0 germ} rC ← transferCount ,c1; {number pages in germ} rE ← topPage ,c2; rE ← rE - rC ,c3; {Map entry for 3.0 germ} looper2: rFrh ← cedarGermPageHigh ,c1; {preload for SetRequest} L0 ← L0.vswap ,c2; CALL[memSwap] ,c3; vswap: rB ← rB + 1 ,c1, RtnBLT[L0.vswap]; {make pages vacant} rC ← rC - 1, ZeroBr ,c2; rE ← rE + 1, BRANCH[looper2, vswapdone] ,c3; { Next on the list is the placement of the special disk buffer which is used by the germ and mapped at .mv 0FE. Of course, with the new mapping scheme, this guy is already mapped, so we've got a Noop here! But... What if he has to be in the display bank? } vswapdone: rE ← 24 ,c1; rE ← rE LRot4 ,c2; rE ← rE - 1, GOTO[SetRequest] ,c3; {Assert: rE=@SD[]-1} {the end...}