// MDscan -- mark IFU entries, compute parameters, scan for errors
// last edited July 15, 1980  7:38 PM

	get "mddecl.d"

external	// defined here
[	Scan	// ()
		// Statics
	@PageSize; @nPages; @PageMask; @WordMask
	@globalZero; @nGlobalPages
	@ifuZero; @nIfuPages; @pageIfuMax; @ifuMask
	@calledMask; @goedtoMask; @jbctMask
	@evenCall; @callerMask; @slowJump; @jbcSubpage
]

external
[		// MDinit
	Kludge
		// MDmain
	@IP
	@NInstructions
	@IFUM; @NIFUM
	@DMachine
	IgnoreOnPage
	Version
		// MDerr
	Err
	PutAddress
]


static	// Might as well initialize for one configuration
[	PageSize = D1PageSize
	nPages
	PageMask
	WordMask
	globalZero = globalZero0
	nGlobalPages
	ifuZero = ifuZero0
	nIfuPages
	pageIfuMax = pageIfuMax0
	ifuMask = ifuMask0
	calledMask = calledMask0
	goedtoMask = goedtoMask0
	jbctMask = jbctMask0
	evenCall = false
	callerMask = -1
	slowJump = false
	jbcSubpage = false
]


let Scan() be
[	Err(PassMessage, "Checking for errors...")
	PreScan()
	RealScan()
]

and PreScan() be
[
	// Compute parameters
	switchon DMachine into
	[ case 0:	// D0
		PageSize = D0PageSize
		calledMask, goedtoMask = -1, -1
		evenCall = Version ls 1
		callerMask = (evenCall? evenMask, -1)
		slowJump = Version ls 2
		// other parameters not relevant
		endcase
	  case 1:	// Dorado model 0
		jbcSubpage = true
		endcase
	  case 2:	// Dorado model 1
		globalZero = globalZero1
		ifuZero, pageIfuMax, ifuMask = ifuZero1, pageIfuMax1, ifuMask1
		calledMask, goedtoMask, jbctMask = calledMask1, goedtoMask1, jbctMask1
		endcase
	]
	nPages, PageMask, WordMask = IMsize/PageSize, IMsize-PageSize, PageSize-1
	nGlobalPages = (IMsize-1-globalZero)/PageSize+1
	nIfuPages = (IMsize-1-ifuZero)/PageSize+1

	// Miscellaneous checking and cleanup:
	//   Remove onPage if appropriate
	//   Remove calls if oddcall set (since call does not return)
	//     If callerMask=-1, remove oddcall as well (i.e. RCALL=GOTO)
	//   Remove iscond if returns set (for "conditional" IFU jumps)
	//   Prohibit conditional and FF-busy branches to external symbols
	//   Reset IFUE bit for IFU entry loop

	for i = 0 to NInstructions-1 do
	[ let ip = IP(i)
	  if IgnoreOnPage then ip>>IM.onPage = 0
	  if DMachine eq 0 then
	   if ip>>IM.oddcall then
	    [ ip>>IM.calls = 0
	      if callerMask eq -1 then ip>>IM.oddcall = 0
	    ]
	  if (ip>>IM.W1 eq WExt) % (ip>>IM.W2 eq WExt) then
	   test ip>>IM.iscond
	    ifso Err(PassFatal, "$P....Conditional branch to external symbol", PutAddress, i)
	    ifnot
	   if ip>>IM.usesFN then
	    Err(PassFatal, "$P....Branches to external symbol, but FF is busy", PutAddress, i)
	  if ip>>IM.returns then ip>>IM.iscond = 0
	  ip>>IM.IFUE = 0
	]

	// Indicate IFU entries
	let nIFUE = 0
	let ifuerr(j) be
	  Err(PassFatal, "$P....Overlapping IFU entry sequences", PutAddress, j)
	for k = 0 to NIFUM-1 do
	[ let i = (IFUM+k*lIFUM)>>IFUM.IFAD
	  if i eq 7777b loop
	  let ip = IP(i)
	  test ip>>IM.IFUE eq 0
	   ifso
	  [ nIFUE = nIFUE+1
	    let i1 = i+(IFUM+k*lIFUM)>>IFUM.NENT
	    for j = i to i1 do
	    [ ip = IP(j)
	      if ip>>IM.IFUE ne 0 then ifuerr(j)
	      if ip>>IM.IFUseq ne 0 then	// *** IFUseq=global
	        Err(PassFatal, "$P....Both IFU entry and global", PutAddress, j)
	      ip>>IM.IFUE = 1
	      if j ne i then ip>>IM.IFUseq = 1
	    ]
	  ]
	   ifnot
	    if ip>>IM.IFUseq ne 0 then ifuerr(i)
	]
	let maxIFUE = pageIfuMax*nIfuPages
	if nIFUE gr maxIFUE then Err(PassFatal, "Too many IFU entries (>$D)", maxIFUE)
]

and RealScan() be
[
// Check constraints:
//	For D0, check LOADPAGE/RETURN constraints
//	For both D0 and Dorado, forbid branches beyond end of program
	for i = 0 to NInstructions-1 do
  [	let ip = IP(i)
	let i1, i2 = ip>>IM.W1, ip>>IM.W2
	// Check for branch beyond end
	if (i1 ge NInstructions) & usesW1(ip) then
	 unless (ip>>IM.returns ne 0) & (ip>>IM.calls eq 0) do
	  Err(PassFatal, "$P....successor is beyond end of program", PutAddress, i)
	if (i2 ge NInstructions) & usesW2(ip) then
	  test ip>>IM.iscond
	   ifso Err(PassFatal, "$P....one successor is beyond end of program", PutAddress, i)
	   ifnot Err(PassFatal, "$P....successor is beyond end of program", PutAddress, i)
	// Check local constraints (LOADPAGE)
	if DMachine eq 0 then
	[ if ip>>IM.swpage then swpcheck(i, i1)
	]
  ]
]

and swpcheck(i, i1) be
[	let ip = IP(i)
	test ip>>IM.returns
	 ifso Err(PassFatal, "$P....has both LOADPAGE[n] and RETURN", PutAddress, i)
	 ifnot
	[ let newpage = ip>>IM.newpage
	  let ip1 = IP(i1)
	 unless Kludge do	// *** Kludge so RETURN can be used for abs goto
	  test slowJump
	   ifso	// LOADPAGE effect is deferred for 1 instruction
	    test ip1>>IM.returns
	     ifso Err(PassFatal, "$P....has RETURN with LOADPAGE[n] in predecessor $P", PutAddress, i1, PutAddress, i)
	     ifnot
	    [ let i2 = ip1>>IM.W1
	      if i2 eq WExt return
	      let ip2 = IP(i2)
	      if (ip2>>IM.onPage ne 0) & (ip2>>IM.W0/PageSize ne newpage) then
	        Err(PassFatal, "$P....on page $O, conflicts with LOADPAGE[$O] at $P (reached via $P)", PutAddress, i2, ip2>>IM.W0/PageSize, ip>>IM.newpage, PutAddress, i, PutAddress, i1)
	    ]
	   ifnot	// LOADPAGE takes effect immediately
	    [ if i1 eq WExt return
	      if (ip1>>IM.onPage ne 0) & (ip1>>IM.W0/PageSize ne newpage) then
	        Err(PassFatal, "$P....on page $O, conflicts with LOADPAGE[$O] at $P", PutAddress, i1, ip1>>IM.W0/PageSize, ip>>IM.newpage, PutAddress, i1)
	    ]
	]
]


and usesW1(ip) =
	((ip>>IM.returns eq 0) % (ip>>IM.calls ne 0)) & (ip>>IM.W1 ne WExt)

and usesW2(ip) =
	((ip>>IM.iscond ne 0) % (ip>>IM.calls ne 0)) & (ip>>IM.W1 ne WExt)