//D0I2.BCPL -- machine-dependent part of Init2()
//	Last edited: 1 March 1982
//Implements final machine-dependent init when starting Midas, doing
//"Run-Prog", or doing "Boot".  All initialization needed to restore
//the display to its initial arrangement should be carried out.

get "mcommon.d"
get "d0.d"
manifest [ get "d0regmem.d" ]

external [
// OS
	SetBlock; Zero

// MINIT0
	@MBlock; MStatus

// MINIT2
	Initialized

// MASM
	@WssCSS; WssCS1; ResetsCSS; ResetsCS1; Wait; GetStorage

// MDATA
	MDATAtab; MADDRtab; @BitsChecked; @HighAddress; @AddrIncrement
	MCTimeOut

// MIDAS
	MidasSwat

// MMENU
	@WsMarkA; AbortingCFile

// MMPRGN
	UpdateMPDValues

// MCMD
	WnsCS1D; WnsCSSD; SetAbortPure; CmdAbort; @CmdAbortAct

// MGO
	@CantContinue

// D0I0
	HWStatus; BPTable; BootTable; NBootInst

// D0TABLES
	@MEMNAM; @MEMLEN

// D0ASM
	recvbyte; recvword; sendbyte; sendword; @utilin; @utilout
	d0recvbyte; d0recvword; d0sendbyte; d0sendword
	ReadPrinter; WritePrinter

// D0REG
	ReadAllRegs

// D0MEM
	readrr; sovlcurr

// D0GO
	Stop; d0Stop

// D0VM
	SetVirtP; IMstab; RMstab

// Defined here
	InitHardware; DefMemName
]

static [
	DefMemName
	KernelStartLoc = #7000 //********may change if kernel.mb changes
	BootEStr; BootEFlg
]

//Result is 0 or an alternate command-menu forming procedure used
//to select among several target machines.
let InitHardware() = valof
[
//Check engineering number to determine whether Midas is running on an
//Alto (EngNum = 0 to 3), Dolphin (EngNum=4), or Dorado (EngNum=5).
//Different printer interface is used on the Dolphin, and Midas won't run
//on a Dorado.
	let EngNum = (table[ #61014; #1401 ] )()
	EngNum = EngNum rshift 12
	test EngNum < 5
	ifso if EngNum eq 4 do
	  [ Init2Boot = D0Init2Boot
	    Init2WaitAck = D0Init2WaitAck
	    SendBootByte = D0SendBootByte
	    recvbyte,recvword = d0recvbyte,d0recvword
	    sendbyte,sendword = d0sendbyte,d0sendword
	    Stop = d0Stop
	  ]
	ifnot MidasSwat(2019)	//"Midas only runs on Altos and Dolphins"
	SetVirtP(false)
	DefMemName = MEMNAM!VMx
//Clear the breakpoint table
	SetBlock(BPTable,#7777,BPlen*4)
//Zero the error counters and initialize test-control stuff in fake
//memories.
	Zero(MDATAtab,MDATAlen*3)
	Zero(MADDRtab,MADDRlen*2)
	MBlock(BitsChecked,table [ -1; -1; #170000 ] , 3)
	SetBlock(HighAddress,-1,2)
	AddrIncrement!1 = 1
//Initialize inverted tables for FastSearch
//(Can't use GetZStorage becasue it is swapped out)
	let IMstablen = MEMLEN!(IMXx+IMXx+1) rshift (BlockShift+1)
	IMstab = GetStorage(IMstablen); Zero(IMstab,IMstablen)
	let RMstablen = (MEMLEN!(RMx+RMx+1)+1) rshift 1
	RMstab = GetStorage(RMstablen); Zero(RMstab,RMstablen)

	MStatus>>MStatus.MachRunning = false
//	MStatus>>MStatus.MachRunning = not CheckStopped()
	sovlcurr = 0	//clear last overlay number
	if HWStatus>>HWStatus.ConnectedMachine eq -1 do
	[ CantContinue = didBoot
//If the boot fails, indicate that we are connected anyway
	  HWStatus>>HWStatus.ConnectedMachine = 0
	  ResetsCSS(); ResetsCS1()
	  for BootFailCount = 0 to 9 do
	  [ Init2Boot()
	    Wait(2000)	//200 msec to let it boot
	    let BootBad = false
//Transfer m-i from MIM to hardware
	    for I = BootTable by 3 to BootTable+((NBootInst-1)*3) do
	    [ if SendBootWord(I!2 & #7777) then
	        if SendBootWord(I!0) then
		  if SendBootWord(I!1) then
		    if SendBootByte(I!2 rshift 12) then loop
	      BootBad = true
	      MCTimeOut!6 = (I-BootTable)/3	//How far into boot
	      break
	    ]
	    test BootBad
	    ifso
	    [ BootEStr = "Communication error during KERNEL transmission"
	      BootEFlg = true
	    ]
	    ifnot	//start kernel
	    [ if SendBootByte(#200 % (KernelStartLoc rshift 8)) then
	        if SendBootByte(KernelStartLoc & #377) do
//**What is this waiting for???
		[ for i = 0 to #20000 do
		  [ if Init2WaitAck() eq 0 then break
		  ]
		  if recvbyte() eq #100 do	//Successfully booted
		  [ ReadAllRegs()
		    if BootFailCount ne 0 do
		    [ WssCSS("Boot succeeded after ")
		      WnsCSSD(BootFailCount); WssCSS(" failures")
		    ]
		    resultis 0
		  ]
		]
	      BootEStr = "KERNEL did not start up"
	      BootEFlg = false
	    ]
	  ]
	  SetAbortPure(lv BootAbort,nil)
	  resultis BootFailMenu	//Boot failed return
	]
	UpdateMPDValues(); resultis 0
]


and BootAbort(nil,nil,nil) be
[	ReadAllRegs()
	if MStatus>>MStatus.CFileStream ne 0 then
		AbortingCFile = 1
	CmdAbort()
]


and BootFailMenu(S,nil) be
[	WssCSS("Boot failed 10 times: ")
	WssCSS(BootEStr)
	if BootEFlg do
	[ WssCS1(((MCTimeOut!7 & #170000) ne 0 ?
	    "Uack didn't drop after acknowledge",
	    ((MCTimeOut!7 & #7400) ne 0 ?
	      "Didn't receive Uack",
	      ((MCTimeOut!7 & #360) ne 0 ?
		"Uack not clear initially","Direction incorrect"))))
	  WssCS1(" transmitting microinstruction ")
	  WnsCS1D(MCTimeOut!6)
	]
	WsMarkA(CmdAbortAct)
]


and Init2Boot() be	//**Alto only
[
//Set quiescent state
	@utilout = not #440; @utilout = not #40440; @utilout = not #440
//Boot it
	@utilout = not #400; @utilout = not #40400; @utilout = not #400
//Set quiescent state
	@utilout = not #440; @utilout = not #40440; @utilout = not #440
]


and D0Init2Boot() be	//**Dolphin only
[	WritePrinter(#20400)
	WritePrinter(#400)
	WritePrinter(#20400)
]


and Init2WaitAck() = valof	//**Alto only
[	@utilout = not #1400
	resultis (@utilin & #10000)
]


and D0Init2WaitAck() = valof	//**Dolphin only
[	resultis (ReadPrinter() & #4000)
]

//Send one byte to D0 via printer interface.  
//We expect the d0 to have uack cleared, bus direction set
//read nibble 1, keep drive off, utilout is inverted, utilin is not
and SendBootByte(bite) = valof	//**Alto only
[	@utilout = not #1400
	let tst = nil
//Check for direction incorrect (count in nib3)
	if (@utilin & #10000) eq 0 then 
	[ MCTimeOut!7 = MCTimeOut!7 + 1; resultis false
	]
//Check for write ack not clear initially (count in nib2)
	if (@utilin & #40000) ne 0 then
	[ MCTimeOut!7 = MCTimeOut!7 + #20; resultis false
	]

//Set the upper byte to quiescent state (boot bit high), drive data
	@utilout = not #40; @utilout = not #40040; @utilout = not #40

//Load low byte of tester output register
	@utilout = not bite; @utilout = not (bite % #100000)
	@utilout = not bite

//Send iack (?write strobe?)
	@utilout = not #240; @utilout = not #40240; @utilout = not #240

//The d0 should respond instantly by raising write ack (#2000).  The
//bit we expect is the second bit of nibble 1, on Utilin[01]

//Set to read nibble from tester
	@utilout = not #1000
//Check for write ack not coming up (count in nib1)
	if (@utilin & #40000) eq 0 then
	[ MCTimeOut!7 = MCTimeOut!7 + #400; resultis false
	]

//Drop isend (?write strobe?), keeping boot bit high, turn drive off
	@utilout = not #40; @utilout = not #40440; @utilout = not #440

//Expect the d0 to drop write ack immediately
	@utilout = not #1400
//Check for write ack not dropping immediately (count in nib0)
	if (@utilin & #40000) ne 0 then
	[ MCTimeOut!7 = MCTimeOut!7 + #10000; resultis false
	]
	resultis true
]

//NOTE: The only difference between use of the printer interface during
//booting and during regular operation is that during booting wprinter is
//acknowledged by the #10000 bit in rprinter becoming 1; during normal
//operation it is acknowledged by the #100000 bit becoming 1.
and D0SendBootByte(bite) = valof	//**Midas running on Dolphin only
[	WritePrinter(#20400)	//#20000 = not booting
				//  #400 = debugger's direction to debugger
	let tst = nil		//Tiny delay
//Verify debuggee's direction to debuggee (count errors in nib3)
	if (ReadPrinter() & #4000) eq 0 then
	[ MCTimeOut!7 = MCTimeOut!7 + 1; resultis false
	]
//Verify debuggee's write ack clear initially (count errors in nib2)
	if (ReadPrinter() & #10000) ne 0 then
	[ MCTimeOut!7 = MCTimeOut!7 + #20; resultis false
	]
//Set the upper byte to quiescent state (boot bit high), drive data
	WritePrinter(#20000+bite)
	tst = nil
	WritePrinter(#120000+bite)	//Turn on write strobe
	tst = nil			//Tiny delay
//The D0 should respond instantly by raising boot write ack (#10000).
//Check for write ack not coming up (count errors in nib1)
	if (ReadPrinter() & #10000) eq 0 then
	[ MCTimeOut!7 = MCTimeOut!7 + #400; resultis false
	]
//Turn off write strobe and leave direction bit in a good state
	WritePrinter(#20400)
	tst = nil		//Tiny delay
//Check for write ack not dropping immediately (count in nib0)
	if (ReadPrinter() & #10000) ne 0 then
	[ MCTimeOut!7 = MCTimeOut!7 + #10000; resultis false
	]
	resultis true
]


and SendBootWord(wurd) = valof
[	if SendBootByte(wurd rshift 8) then
	  if SendBootByte(wurd & #377) then resultis true
	resultis false
]