// D0load.bcpl -- machine-dependent subroutines called by MLOAD.BCPL
//	Last edited: 21 October 1981

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

external [
// OS
	Zero

// MIDAS
	MidasSwat; Initialized

// MASM
	VUsc; StrSize

// MDATA
	MCTimeOut

// MSYM
	MapSymBlocks; @BHsize

// MMPRGN
	UpdateMPDValues

// MCMD
	DisplayError; ErrorAbort

// MGO
	@CantContinue

// D0TABLES
	@MEMLEN; @MEMWID

// D0MEM
	readrr

// D0GO
	AddBp

// D0VM
	SetVirtP; LookUpAA; RetrieveBlock; VAtab; AAtab; IMstab; RMstab

// Defined here
	PrepareLoad; RestoreAfterLoad; PutMDSymOnly; AddToVM; LoadCleanUp
]

static [ oldMDATAwid; TypeOfLoad ]

//Verify that the hardware is in good shape for the Ld, LdData, LdSyms,
//Cmpr, or Dump and do other setup.  Call DisplayError if probably not
//ok to do the operation; if DisplayError returns (indicating user wants
//to go ahead with the operation), then do ResetsCSS().
let PrepareLoad(LoadType) be
[	if Initialized & (LoadType ne 0) do
	[ CantContinue = CantContinue % didLoad
	  //DisplayError("Power is off", "Continue-loading")
	  //ResetsCSS()
	  Zero(MCTimeOut,6)	//Zero error counters
	]
	TypeOfLoad = LoadType
	oldMDATAwid = MEMWID!MDATAx
	MEMWID!MDATAx = MDATAwid
]


and RestoreAfterLoad() = valof
[	MEMWID!MDATAx = oldMDATAwid
//2nd arg to SetVirtP causes UpdateMPDValues.
//Have to update the display even on LdSyms because virtual addresses
//may be displayed (e.g., CIA or TPC 20).
//***FormCmdMenu may be called redundantly here, once by SetVirtP and
//***once by InitLoad.
	if LookUpAA(0) ge 0 then SetVirtP(true,nil)
	if VUsc(MCTimeOut,table [ 0; 0; 0; 0; 0; 0 ] ,6) ne 0 then
		ErrorAbort("****Communication errors occurred****")
]


//Special kludge subroutine used by MLOAD on "LdSyms" because the
//virtual to absolute correspondence table has to be loaded even though
//we are only loading symbols and we have to record the BP's in the BP
//table for the load (???)
and PutMDSymOnly(MemX,DVec,AVec) = valof
[	if MemX eq IMx do
	[ if VUsc(AVec,MEMLEN+MemX+MemX,2) ge 0 then resultis false
	  let AA = AddToVM(AVec!1,DVec)
	  if AA < 0 then resultis false	//AA exceeds phsyical microstore
	]
	resultis true
]


//Called only during Ld, LdSyms, or LdData by PutMemData or PutMDSymOnly.
//VA must be valid.
and AddToVM(VA,DVec) = valof
[	let DV3 = DVec!3
	if DV3<<IMV.Undef ne 0 then resultis -1
	let AA = DV3<<IMV.Addr
//Don't insert BP's on LdSyms
	if TypeOfLoad eq 0 then DV3 = DV3 & #37777
//Load the VM except on LdData
	if TypeOfLoad ne 1 do
	[ let Block = RetrieveBlock(VAtab+(VA rshift BlockShift),VAKind,
		2,0,#17777)
	  Block!(VA & BlockMask) = DV3
	  Block = RetrieveBlock(AAtab+(AA rshift BlockShift),AAKind,
		2,0,#17777)
	  Block!(AA & BlockMask) = VA+(DV3 & not AA)
	]
//Insert BP, if any, unless LdSyms
	if (DV3 & #140000) ne 0 do
	[ switchon AddBp(DV3<<IMV.Addr) into
	  [ case -1:		//Inserted BP ok
	    case 1: endcase	//Already in BP table
	    case 0: MidasSwat(BPTableOvf)
	  ]
	]
	resultis AA
]

//Build IMstab indexed by IM address, contains BlockAddr for the symbol
//block which contains the nearest IM symbol le each IM address.
//Similarly, build RMstab indexed by RM address.
and LoadCleanUp() be
[	if not Initialized then return
	MapSymBlocks(BuildAddrInvert,1)
	let BestBlockAddr = 0
//Fill gaps in IMstab table
	for I = 0 to (MEMLEN!(IMx+IMx+1) rshift (BlockShift+1))-1 do
	[ let Block = RetrieveBlock(IMstab+I,IMKind,1)
	  if Block ne 0 then
		BestBlockAddr = ExtendSyms(BestBlockAddr,Block,BlockSize*2)
	]
	ExtendSyms(0,RMstab,MEMLEN!(RMx+RMx+1))
]


and ExtendSyms(BestBlockAddr,Table,Length) = valof
[	for I = 0 to Length-1 do
	[ let W = Table>>Bytes.Byte↑I
	  test W eq 0
	  ifso Table>>Bytes.Byte↑I = BestBlockAddr
	  ifnot BestBlockAddr = W
	]
	resultis BestBlockAddr
]


//Called from MapSymBlocks.  Fills an IMstab entry for each IM address symbol
//with BlockAddr for the block containing the symbol; fill RMstab entry for
//each RM symbol with BlockAddr for the block containng the symbol.
and BuildAddrInvert(B,nil,nil,nil,nil) be
[	let Block,BlockAddr = B>>BT.Core,B>>BT.BlockAddr
	let Blk = nil
	for I = BHsize to Block>>BH.LastPntr do
	[ let Sym = Block+Block!I
	  let Body = Sym+StrSize(Sym)
	  let Addr = Body>>Symb.A.A2
	  switchon Body!0 into
	  [
case (AddrSymb*#400)+IMx:
	    Blk = RetrieveBlock(IMstab+(Addr rshift (BlockShift+1)),
		IMKind,1,Block,0)
	    Addr = Addr & (BlockMask+BlockMask+1); endcase
case (AddrSymb*#400)+RMx:
	    Blk = RMstab; endcase
default:    loop
	  ]
	  Blk>>Bytes.Byte↑Addr = BlockAddr
	]
]