{
 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...}