//D0Go.bcpl	stuff associated with stop and go of the microprocessor
//	Last edited: 20 October 1981

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

external [
// MIDAS
	MidasSwat

// MRGN
	RemoveFromEveryTimeList

// MMPRGN
	UpdateMPDValues

// MPATTERN
	@PATTERN

// MASM
	PutsCSS; ResetsCSS; @WssCSS; WssCS1

// MDATA
	GoVec

// MSYM
	SearchBlocks

// MCMD
	DisplayError; ErrorAbort; WnsCSS; FormCmdMenu
	CmdCommentStream; QuitCmdOverlay

// MLOAD
	DoingLoad

// MINIT0
	MStatus

// MGO
	@CantContinue; @QuitF

// MPRINS
	NWss

// D0I0
	DVx; BPTable

// D0I1
	d0go

// D0TABLES
	@MEMNAM

// D0ASM
	sendbyte; sendword; recvbyte; WritePrinter
	@utilout; stNotInVM; stNotIMA

// D0REG
	ReadAllRegs; ReadRegisters; PutAllRegs
 
// D0MEM
	GetMemData; PutMemData; sovlput; readtpc

// D0VM
	LookUpVA; LookUpAA; @VirtualP

//Defined here
	DefaultGoMemory; @CheckStopped; Stop; d0Stop; MStopped
	SetupIMA; PrCCV; AddBp; BreakAddr
]

static
[	stoppedcode = 0
	BreakAddr; BreakTask
	MouseHalt
]

//Setup for step or go.  Arg1 is true to setup for go, false for step.
//MemX is either IMx or IMXx; NA is 1, indicating that AVec and MemX
//aren't supplied so setup to continue from last breakpoint, or 3 to
//indicate a new go or step for the current task.
//SetupIMA leaves its results in GoVec as described in the "Go"
//structure def in MCOMMON.D.

let SetupIMA(GoP,AVec,MemX,NA,Str,MB) be
[	let DVec1,BPindex = vec 3,nil
	test NA ge 3
	ifso			//New go for current task
	[ NA = AVec!1
	  switchon MemX into
	  [
case IMx:	NA = LookUpAA(NA)
		if NA < 0 then ErrorAbort(stNotInVM)
case IMXx:	(lv GoVec>>Go.AVec)!1 = NA; endcase
default:	ErrorAbort(stNotIMA)
	  ]
	]
	ifnot			//Continue
	[ if CantContinue ne 0 do
	  [ ResetsCSS(); WssCSS("Can't continue after")
	    PATTERN = CantContinue
	    NWss(" Test,",didTest)
	    NWss(" Load,")
//	    NWss(" PEscan,")	//Unused on D0
//	    NWss(" Call,")	//Unused on D0
	    NWss(" LoadPage,",didLoadPage)
	    NWss(" Boot,")
	    NWss(" IMX PE,")
	    NWss(" R bus PE,")
	    NWss(" Memory Error,")
	    NWss(" Stack Overflow,")
	    NWss(" Multiple BP's at same Page address,")
	    DisplayError(0,"Try to continue",0,0)
	  ]
	  DVx>>srbus.ctask = BreakTask
	]
	WssCSS(Str)
	WnsCSS(DVx>>srbus.ctask); PutsCSS($:)
	PrCCV(lv GoVec>>Go.AVec,IMXx)
	CantContinue = 0
	GoVec>>Go.Branch = false
	test GoP
	ifso
	[ GoVec>>Go.RunP = GoRun
	  //if run from bp, set RunP to GoRunbp
	  for I = BPmin to BPlen-1 do
	  [ if (BPTable>>BP↑I.Addr eq (lv GoVec>>Go.AVec)!1) &
		(BPTable>>BP↑I.InUse) do
	    [ GoVec>>Go.RunP = GoRunbp; break
	    ]
	  ]
	]
	ifnot GoVec>>Go.RunP = GoStep
	test GoVec>>Go.RunP eq GoRun
	ifso MapAllBp(InsertBp)	//Insert bp's from table
	ifnot			//insert step breakpoint(s)
	[ //calculate the effective address, get real m-i from D0
	  unless GetMemData(IMXx,DVec1,lv GoVec>>Go.AVec) do
		MidasSwat(GoVecErr)
	  //assume m-i in DVec1!0,DVec1!1,DVec1!2
	  let Ea = nil
	  let Ja = ((DVec1!1 & #176) rshift 1) %
		   ((DVec1!2 & #30000) rshift 6)
	  let Pg = (lv GoVec>>Go.AVec)!1 & #7400
	  let Jc = (DVec1!1 & #1600) rshift 7
	  switchon Jc into
	  [
case 6:		//RETURN
		unless ((DVec1!0 & #77) eq #47) &
			((DVec1!1 & #30000) eq #30000) do  //test is for control store R/W
		[ Ea = DVx>>srbus.apc; endcase;
		]
case 4:		//GOTO
case 5:		//CALL
		Ea = Pg % Ja; endcase
case 7:		//DISPATCH
		Ea = Pg % (Ja & #360) % (#17 & DVx>>srbus.apc)
		endcase
default:		//BRANCH
		Ea = Pg % Ja
		GoVec>>Go.Branch=true;
		//insert extra bp at Ea xor 1
		InsertBp(1,Ea xor 1);	//Put the breakpoint in the table
	  ]
	  InsertBp(0,Ea)	//Set bp at effective address (puts it in the table)
	]
	StartD0()
]


and StartD0() be
[	PutAllRegs()
	sendbyte(sovlput(d0go))
	sendword((DVx>>srbus.ctask lshift 12) + (lv GoVec>>Go.AVec)!1)
	MStatus>>MStatus.MachRunning = true
]


and DefaultGoMemory() = VirtualP ? MEMNAM!IMx, MEMNAM!IMXx


and PrCCV(AVec,MemX) be
[	let VA = AVec!1	//Correct if MemX eq IMx
	let AA = VA	//Correct if MemX eq IMXx
	switchon MemX into
	[
default:	MidasSwat(PrCCVMemXErr)
case IMXx:	VA = LookUpVA(AA); endcase
case IMx:	if VA < 0 do
		[ WssCSS(stNotInVM); return
		]
		AA = LookUpAA(VA)
//If VA ge 0 then VA is valid IM address, so LookUpAA must always succeed
		if AA < 0 then MidasSwat(PrCCVAAErr)
		endcase
	]
	if VirtualP do
	[ if VA ge 0 do
	  [ let AVec1 = vec 1; AVec1!0 = 0; AVec1!1 = VA
	    SearchBlocks(CmdCommentStream,IMx,AVec1); return
	  ]
	  WssCSS("abs ")
	]
	WnsCSS(AA)
]

//Called from SingleStepM, HaltWait, and HaltProc in MGO.  A message like
//"Go at 3:FOO" was printed on CmdCommentStream by SetupIMA; the caller
//may append other text to CmdCommentStream before calling MStopped.
//MStopped appends a string to describe the BP location on
//CmdCommentStream, prints error information on CmdCS1, and cleans up
//after the SS, Go, or whatever.
//GoFlag is omitted for keyboard halts, true for BP or error halts,
//false for SS actions; MStopped returns when GoFlag is false, but does
//QuitCmdOverlay when it is omitted or true.

and MStopped(GoFlag; numargs NA) be
[	MStatus>>MStatus.MachRunning = false
	DVx>>srbus.ncia = #7777 xor BreakAddr
	if QuitF ge 0 do RemoveFromEveryTimeList(QuitF)
	QuitF = -1
	UpdateMPDValues()
	let DVec = vec 3
//print breakpoint task & address (if any), or print
//out the fact that we stopped by zapping the machine
	test stoppedcode eq #101
	ifso		//Fault
	[ WssCSS(", fault at ")
	  let Errors = DVx>>srbus.parity
	  if (Errors & #10) ne 0 then WssCS1("StackOvf  ")
	  if (Errors & #4) ne 0 then WssCS1("IMX PE  ")
	  if (Errors & #2) ne 0 then WssCS1("R Bus PE  ")
	  if (Errors & #1) ne 0 then WssCS1("Memory Error  ")
	  if (Errors & #17) eq 0 then WssCS1("Unknown fault reason  ")
	]
	ifnot WssCSS(MouseHalt ? ", Mouse halt at ", ", BP at ")
	MouseHalt = false
	WnsCSS(DVx>>srbus.ctask); PutsCSS($:)
	PrCCV(lv GoVec>>Go.AVec,IMXx)
	if (NA < 1) % GoFlag then QuitCmdOverlay()
]


and CheckStopped()  = valof
[	stoppedcode = recvbyte()
	unless (stoppedcode & #177776) eq #100 do resultis 0
	ReadRegisters()
//Undo bp's so that the m-i will show the correct contents.
	test GoVec>>Go.RunP eq GoRun
	ifso MapAllBp(UndoBp)
//On SS or on a Go for which the starting address has a bp,
//Midas inserts bp's only at the successor to the instruction being
//started, or to both successors if the instruction has a branch
//condition.  Remove these now.
	ifnot		//GoRunbp or GoStep
	[ UndoBp(0)
	  if GoVec>>Go.Branch then UndoBp(1) 
	]

	BreakAddr = DVx>>srbus.ncia xor #7777
	BreakTask = DVx>>srbus.ctask
	MouseHalt = false
	test stoppedcode eq #100
	ifso		//Normal bp
	[ if DVx>>srbus.ctask eq #16 do
	  [ MouseHalt = true
	    for i = 0 to BPlen-1 do
	    [ if BPTable>>BP↑i.InUse eq 0 then loop
//Restart address for mouse halt is at loc+1
	      if BPTable>>BP↑i.Addr eq BreakAddr do
	      [ MouseHalt = false; break
	      ]
	    ]
	  ]
	  test MouseHalt
	  ifso BreakAddr = BreakAddr+1
	  ifnot
	  [
//If the BP instruction followed a LoadPage, its Page bits will be incorrect.
//We scan the BP table.  If there was a BP at BreakAddr, breakOK ← true.
//After the scan, breakCnt contains the no. of BPs with the same page address
//as BreakAddr, and xbreakAddr contains the address of the last such BP found.
	    let breakOK,breakCnt,xbreakAddr = false,0,0
//we only look through BPs that were set (0-1 if stepping, 2-BPlen otherwise).
	    let first,last = 0,1
	    if GoVec>>Go.RunP eq GoRun do [ first,last = 2,BPlen-1 ]

	    for i = first to last do
	    [ if BPTable>>BP↑i.InUse eq 0 then loop
	      if BPTable>>BP↑i.Addr eq BreakAddr then [ breakOK = true; loop ] 
	      if (BPTable>>BP↑i.Addr & #377) ne (BreakAddr & #377) then loop
	      breakCnt = breakCnt+1
	      xbreakAddr = BPTable>>BP↑i.Addr
	    ]

	    unless breakOK do //check for real BP where we stopped.
	      test breakCnt gr 1
//we are confused - set multiple BP flag
	      ifso CantContinue = CantContinue % didMultBP
//no confusion about which BP - didLoadPage will be set later..
	      ifnot BreakAddr = xbreakAddr
	  ]

	]

//On other faults, the instruction is aborted before execution, so
//CIA is still correct.
	ifnot		//#101 faults except bp
	[ 
	  let Errors = DVx>>srbus.parity
	  if (Errors & #10) ne 0 then
		CantContinue = CantContinue % didStkOvf
	  if (Errors & #4) ne 0 then
		CantContinue = CantContinue % didIMXPE
	  if (Errors & #2) ne 0 then
		CantContinue = CantContinue % didRBusPE
	  if (Errors & #1) ne 0 then
		CantContinue = CantContinue % didMemoryError
	]

//Breakpoints 0 and 1 are no longer needed.  Zap them.
	for i = 0 to 1 do BPTable>>BP↑i.InUse = 0

	(lv GoVec>>Go.AVec)!1 = BreakAddr
	if DVx>>srbus.page ne (BreakAddr rshift 8) then
	  CantContinue = CantContinue % didLoadPage
//When we started at an instruction which is a bp, we first step
//that instruction, then undo the step breakpoints, insert all the
//bp's from the table and continue.  Return 0 indicating no halt in
//this case.
	if GoVec>>Go.RunP eq GoRunbp do
	[ GoVec>>Go.RunP = GoRun
	  unless CantContinue ne 0 do
	  [ MapAllBp(InsertBp)
	    StartD0(); resultis 0
	  ]
	]

	resultis stoppedcode
]


and Stop() be
[	@utilout = not 460b
	@utilout = not 40460b
	@utilout = not 460b
	//wait a while
	let x = nil
	for i = 1 to 100 do x = 0
]


and d0Stop() be
[	WritePrinter(#30400)
	WritePrinter(#130400)
	let x = nil
	for i = 1 to 100 do x = 0
	WritePrinter(#20400)
]

//Called from BreakIML and AddToVM
and AddBp(AA) = valof
[	let BPindex = 0
	for I = BPmin to BPlen-1 do
	[ test BPTable>>BP↑I.InUse
	  ifso if (BPTable>>BP↑I.Addr eq AA) then resultis 1
	  ifnot if BPindex eq 0 then BPindex = I
	]
//test if BPtable was full
	if BPindex eq 0 then resultis 0
//add instruction to breakpoint list
	BPTable>>BP↑BPindex.Added = not DoingLoad
	BPTable>>BP↑BPindex.InUse = true
	BPTable>>BP↑BPindex.Addr = AA
	resultis -1
]


//Apply Proc to every bp.
and MapAllBp(Proc) be
[	for I = BPmin to BPlen-1 do
	[  if BPTable>>BP↑I.InUse then Proc(I)
	]
]


//Rewrite the saved m-i into the bp location
and UndoBp(Ival) be
[	let DVec,AVec = vec 3, vec 2
	AVec!0 = 0
	AVec!1 = BPTable>>BP↑Ival.Addr
	unless PutMemData(IMXx,lv BPTable>>BP↑Ival.w0,AVec) do
		MidasSwat(CantUndoBP)
]


//Read and save the m-i at the bp address in BPTable and overwrite the
//m-i with a breakpoint m-i.  This is called at the beginning of a
//"Go" or "SS" sequence.
and InsertBp(BPindex,Addr; numargs NA) be
[	let DVec,AVec = vec 3, vec 2
	if NA ge 2 then [ BPTable>>BP↑BPindex.Addr = Addr; BPTable>>BP↑BPindex.InUse = 1 ]
	AVec!0 = 0
	AVec!1 = BPTable>>BP↑BPindex.Addr
//save real m-i in BPTable
	unless GetMemData(IMXx,lv BPTable>>BP↑BPindex.w0,AVec) do
		MidasSwat(CantGetBPInst)
//build and insert breakpoint
	DVec!0 = BpI0
	DVec!1 = BpI1 % (((AVec!1) lshift 1) & #176)
	DVec!2 = #140000 % (((AVec!1) & #300) lshift 6)
	unless PutMemData(IMXx,DVec,AVec) do MidasSwat(CantSetBP)
]