//D1mem.bcpl	memory read/write procedures
//	19 May 1983

get "mcommon.d"
get "d1.d"
manifest [ get "d1instrs.d" ]
manifest [ get "d1regmem.d" ]
//manifest [ get "d1dmux.d" ] "TOO MANY NAMES, SO.."
manifest [ dCIAINC=#1; dCIA=#2; dTNIA=#5; dBNPC=#6; dOLDCIA=#165
	dIMOUT=#172 ]

external [
// OS
	Zero; DoubleAdd

// MINIT0
	@MBlock; MStatus

// MASM
	DoubleNeg; @OddParity

// MMPRGN
	UpdateMPDValues

// MCMD
	ErrorAbort

// MGO
	@CantContinue

// MLOAD
	DoingLoad

// D1I0
	@DMuxTab; @OldDMuxTab; DWrong; DChecked; DMuxSelect
	@DHistTab; @SaveMIR; HWStatus; SaveBR36

// D1TABLES
	@MEMCON

// D1ASM
	ReadDMux; LoadDMD; LoadMIR; @DoStrobe
	@SetALUF; @SetRSTK; @Xct; @XctR16; @XctR16C; @XctR16Link
	@XctL16; @XctL16C; @XctLFF; @XctL16T; @XctL16Q
	@SelectTask; ConvertAV; IMtoMIR; MIRtoIM
	@MCXctR; @MCXct; ReadIMX; LoadDWatch; SelFltSrn
	@MADDRL; @MADDRH; stUndAdr; stReadOnly

// D1REG
	MGetChecks; MPutChecks

// D1RES
	BR36Saved; BreakTask

// D1VM
	LookUpVA; LookUpAA; @VirtualP

// D1LOAD
	AddToVM

// D1CONFIG
	CacheAMask0; CacheAMask1; log2rows; IMXmask

//Defined here for use by machine-independent code
	GetMemData; MGetMemData; PutMemData; MPutMemData; MDATAmemx

// Defined here for D1 code
	RdCACHEA; GetMic2; FixClock; TurnOffRefresh; ClearIMBDAddr
	@SaveTask; @SaveTPC; @OldTask; OldTPC; SaveALUFM0
	SaveALUFM16; SaveSTKP; SaveTIOA; @SaveLINK; @SaveQ; @SaveSRN
	@SaveMCR; @SaveT; @SaveRBase; @SaveMBase; SaveR0
]

static [
	MDATAmemx = MDATAx
	@SaveTask; @SaveTPC; @OldTask; OldTPC; SaveALUFM0; SaveALUFM16
	@SaveLINK; @SaveQ; @SaveSRN; @SaveMCR; @SaveT; @SaveRBase
	@SaveMBase; @SaveBRHI; @SaveBRLO; SaveSTKP; SaveTIOA; SaveR0
]

let FixIMVA(DVec) be
[	if VirtualP then DVec!0 = LookUpVA(DVec!0)
]


and GetMic2(DVec) be
[	DVec>>lh = MCXctR(BInt+BHoldInt+BCFetchInc)
	DVec>>rh = MCXctR(BInt+BHoldInt+BCFetchInc)
]


//GetMemData is called directly by MLOAD, MTEST, and some Dorado-specific
//actions.  Other places in machine-dependent code call MGetMemData.
//GetMemData freely smashes "volatile" stuff, but shouldn't clobber
//anything that may be loaded from a .MB file.  MGetMemData restores
//whatever GetMemData clobbers.
and GetMemData(MemX,DVec,AVec) = valof
[	let T,DVX = nil,vec DMUXlen
	switchon ConvertAV(AVec,MemX) into
	[
case ABSOLx:	DVec!0 = MCXctR(BInt+BHoldInt+BCFetchInc) lshift 8
		MCXct(BInt+BCNoop); FixClock(); endcase
case MSTATx:	GetMic2(DVec); DVec = DVec+1
case ABSx:	GetMic2(DVec); MCXct(BInt+BCNoop); FixClock(); endcase
case TPCx:	test MADDRL eq SaveTask
		ifso DVec!0 = SaveTPC
		ifnot
		[ XctL16C(RTPC,MADDRL); XctR16Link(RLINK,DVec)
		]
		FixIMVA(DVec); endcase
case TLINKx:	SaveLINK = not XctR16Link(RLINK,DVec)
		DVec!0 = SaveLINK; FixIMVA(DVec); endcase
case OLINKx:	T = not XctR16Link(RLINK,DVec)
		DVec!0 = (T & 177700B)+((T-1) & 77B)
		FixIMVA(DVec); endcase
//ConvertAV(..) cleared MIR, but do it directly anyway.
case IMBDx:	LoadDMD(IMControl+IMAddressen+ResetCBMIR)
		T = DMuxTab; DMuxTab = DVX
		ReadDMux(); MIRtoIM(DVec,DMuxTab+dIMOUT)
		DMuxTab = T
		ClearIMBDAddr(); endcase
case IMx:	MADDRL = LookUpAA(MADDRL,DVec)
		if MADDRL < 0 do	//Outside VM
		[ Zero(DVec,3); resultis false
		]
case IMXx:	ReadIMX(DVec,DVX); endcase
case ALUFMx:	test MADDRL eq 16B
		ifso DVec!0 = SaveALUFM16
		ifnot test MADDRL eq 0
		  ifso DVec!0 = SaveALUFM0
		  ifnot
		  [ Xct(SetALUF(TFAF0,MADDRL)); XctR16(RT,DVec)
		  ]
		DVec!0 = DVec!0 lshift 8; endcase
//Have Task←17, ProcSRN←1, MemBase[17]←36, virtual address from CACHEA
//with word bits in BR 36, T←0, Mcr←NoWake+DisHold or NoWake+DisHold+DisCF
//according to Vacant+WP in CacheA entry.
case CACHEDx:
//MapFaults and data errors ignored for VM.
case VMx:	Xct(FETCHM)
case MDx:	Xct(TFMD)
case Tx:	XctR16(RT,DVec); endcase
case RBASEx:	Xct(TFPTRS); DVec!0 =
			(XctR16(RT,DVec))<<Pointers.RBase lshift 12
		endcase
case TIOAx:	Xct(TFTIOA); DVec!0 = XctR16(RT,DVec) & 177400B; endcase
case MEMBASEx:	Xct(TFPTRS); DVec!0 =
			(XctR16(RT,DVec))<<Pointers.MemBase lshift 11
		endcase
case RMx:	XctR16(SetRSTK(RRM0,MADDRL),DVec); endcase
case STKXx:
case STKx:	XctR16(RSTACK,DVec); endcase
case BRXx:
case BRx:	XctL16T(0); Xct(DUMMYFT); Xct(NOOP)
		XctR16C(RVAHI,DVec); XctR16C(RVALO,DVec+1)
		if MADDRL eq 36B do
		[ MBlock(SaveBR36,DVec,2); BR36Saved = true
		]
		endcase
//MADDRL has two low bits of address = column positioned in McrV field
//MADDRH has n row address bits positioned with 4 zeroes to the right
//Task 17 is selected and ProcSRN contains 1.
//VA[4:31] wind up in DVec[4:31], flags in DVec[0:3].
case CACHEAx:	DVec>>nib0 = (RdCACHEA(DVec))<<nib2
		DVec!0 = DVec!0 & CacheAMask0
		DVec!1 = DVec!1 & CacheAMask1
		endcase
//BR 36 is selected by MemBase and has VA in position; MCR contains
//DisHold+NoWake; task 17 is selected; SRN 1 is selected.
//MAP shows (MapDirtyb, MapParity, MapPE), (Ref, WP, Dirty), and (RP[0:15])
//as 3 fields
case MAPx:	XctLFF(LRB0,0); XctL16(LRM0,0); Xct(RDMAP)
		XctR16(RMAP,DVec+1)
		T = XctR16(RERRS,DVec) rshift 12
		T = (T & 3B)+((T & 10B) rshift 1)+
			((DVec!0 & 44000B) eq 0 ? 10B,0)
		DVec!0 = ((XctR16(RCONFG,DVec) & 3) lshift 4)+T
		endcase
case IFUMx:	XctR16(RIFL,DVec+1); DVec!0 = XctR16(RIFH,DVec) & 3777B
		DVec!1 = FixIFUMParity(DVec!0,DVec!1)
		endcase
case LDRx:	MIRtoIM(DVec,MADDRL); endcase
case MDATAx:	MBlock(DVec,MADDRL,3); endcase
case MADDRx:	MBlock(DVec,MADDRL,2); endcase
case DMUXx:	DVec!0 = DMuxSelect!MADDRL
		switchon MADDRL into
		[ case dCIA: case dBNPC: case dCIAINC: case dTNIA:
		  case dOLDCIA:
		  if (DMuxSelect eq DMuxTab) %
			(DMuxSelect eq OldDMuxTab) then FixIMVA(DVec)
		]
		endcase
//History of a particular signal over 32 ReadDMux()'s
case DHISTx:	MBlock(DVec,MADDRL,3); endcase
//Value of all 32 history signals at a particular clock time
case VHx:	DVec!0 = HistShift(0); DVec!1 = HistShift(16*3); endcase
//Legitimately get here only when ConvertAV fails; presently this can
//only happen for the STKX memory, which has addresses that may be
//OK'ed by CertifyAV but which might subsequently become undefined.
default:	resultis false
	]
	resultis true
]


and HistShift(I) = valof
[	let T = nil
	for J = MADDRH+I to MADDRH+I+(15*3) by 3 do
	[ T = (T lshift 1)+((DHistTab!I rshift MADDRL) & 1)
	]
	resultis T
]


and MGetMemData(MemX,DVec,AVec,Ext) = valof
[	unless MGetChecks(MEMCON!MemX) do resultis false
	let AVec1 = vec 1; MBlock(AVec1,AVec,2)
//Translate ROW memory into 4 instances of CACHEA followed by the
//victim and next-victim
	test MemX eq ROWx
	ifso
	[ test Ext eq 4
	  ifso	//victim, next-victim
	  [ SelFltSrn(); XctL16T(DisBR+DisHold+DisCF); Xct(MCRFT)
	    XctL16T(AVec!1 lshift 4); Xct(DUMMYFT); Xct(NOOP)
	    DVec!0 = XctR16C(RPIPE5,DVec) lshift 12
	    RestoreBR36(); RestoreMemStuff(); Xct(NOOP); LoadMIR(SaveMIR)
	    resultis true
	  ]
	  ifnot	//one of the CACHEA's
	  [ MemX = CACHEAx; AVec1!1 = AVec!1 + (Ext lshift log2rows)
	  ]
	]
//Show Pipe0 to Pipe5 all high-true on three display lines
	ifnot if MemX eq PIPEx do
	[ unless ConvertAV(AVec,MemX) eq PIPEx do resultis false
	  switchon Ext into
	  [
case 0:	    DVec!0 = XctR16C(RVAHI,DVec) & 7777B
	    XctR16C(RVALO,DVec+1); endcase
case 1:	    XctR16(RPIPE2,DVec); XctR16(RMAP,DVec+1); endcase
case 2:	    DVec!0 = XctR16(RERRS,DVec) xor 47416B
	    DVec!1 = XctR16C(RPIPE5,DVec+1) xor 4000B; endcase
	  ]
	  XctL16T(SaveSRN); Xct(SRNFT); XctL16T(SaveT); Xct(NOOP)
	  LoadMIR(SaveMIR); resultis true
	]
	if GetMemData(MemX,DVec,AVec1) do
	[ switchon MemX into
	  [
case TPCx:	if MADDRL eq SaveTask then resultis true
		XctL16C(LLINK,SaveLINK); endcase
case IMBDx:	RestoreIMBD(); endcase 
case IMx:	//if undef., GetMemData returns false here
case IMXx:	XctL16C(LLINK,SaveLINK); endcase
case RMx:	XctLFF(LRB0,SaveRBase); endcase
case ALUFMx:	if (MADDRL eq 16B) % (MADDRL eq 0) then resultis true
		XctL16T(SaveT); endcase
case MAPx:	XctL16(LRM0,SaveR0); XctLFF(LRB0,SaveRBase)
case VMx: case CACHEDx:
case CACHEAx:	RestoreBR36()
case BRXx:
case BRx:	RestoreMemStuff(); endcase
case MEMBASEx: case TIOAx: case MDx:
case RBASEx:	XctL16T(SaveT)
case TLINKx: case OLINKx:
case Tx:	SelectTask(OldTask); endcase
default:	resultis true
case STKXx:
case STKx:	SelectTask(OldTask); XctL16Q(SaveSTKP); Xct(STKPFQ)
case IFUMx:	XctL16Q(SaveQ); endcase
	  ]
	  Xct(NOOP); LoadMIR(SaveMIR); resultis true
	]
	resultis false
]


and RestoreBR36() be
[	XctL16T(DisHold+DisCF+NoWake); Xct(MCRFT)
	XctL16T(SaveBR36!0); Xct(BRHIFT)
	XctL16T(SaveBR36!1); Xct(BRLOFT)
]


and RestoreMemStuff() be
[	XctLFF(LMB0,SaveMBase)
	XctL16T(SaveSRN); Xct(SRNFT)
	XctL16T(SaveMCR); Xct(MCRFT)
	XctL16T(SaveT); SelectTask(OldTask)
]


and ClearIMBDAddr() be
[	LoadDMD(IMAddr0); LoadDMD(IMAddr1)
	LoadDMD(IMAddr2+HWStatus>>HWStatus.MIRdebugging)
	LoadDMD(IMControl)
]


and RestoreIMBD() be
[	if BreakTask ne 0 then
		CantContinue = CantContinue % didIMBD
	XctL16C(LLINK,OldTPC); XctL16C(LTPC,17B); Xct(NOOP)
	XctL16C(LLINK,SaveLINK); SelectTask(OldTask)
	DoStrobe(Clock+UseCPReg+ClrReady); DoStrobe(Clock+UseCPReg)
]


//Used from ConvertAV for CACHED setup and for CACHEA read
and RdCACHEA(DVec) = valof
[	XctL16T(dVAfVic+UseMcrV+NoWake+FDMiss+DisBR+DisHold+MADDRL)
	Xct(MCRFT); XctL16T(MADDRH); Xct(NOOP)
//For some strange reason, it is essential to do ←PIPE5 right after
//DUMMYREF← and before ←PIPE0 or ←PIPE1.
	let CE = TurnOffRefresh()
	Xct(DUMMYFT); Xct(NOOP)
	let T = XctR16C(RPIPE5,DVec)
	LoadDMD(CE)
	DVec!0 = XctR16C(RVAHI,DVec) & 7777B; XctR16C(RVALO,DVec+1)
	resultis T
]


and FixIFUMParity(D0,D1) =
	(D1 & 107777B)+
	(OddParity(D0 & 1400B,D1 & 40317B) & 40000B)+
	(OddParity(D0 & 377B,D1 & 20000B) & 20000B)+
	(OddParity(D0 & 2000B,D1 & 117460B) & 10000B)


//Procedure to turn off RunRefresh.  This has to be done carefully,
//first turning off EnRefreshPeriod', then RunRefresh.  Needed when
//reading or writing cache flags.
and TurnOffRefresh() = valof
[	let CE = RunEnable+HWStatus>>HWStatus.RunControl
	LoadDMD(CE % EnRefreshPeriodx)
	LoadDMD(RunEnable+ECLup+IOResetx+EnRefreshPeriodx)
	resultis CE
]


and FixClock() be
[	if MStatus>>MStatus.MachRunning eq 0 then DoStrobe(Clock+UseCPReg)
]

//PutMemData is called directly by MLOAD and MTEST (where speed is
//important) and by some Dorado-specific actions.  Other places in
//machine-independent code call MPutMemData.  PutMemData freely smashes
//"volatile" stuff, but must be sure to leave both the hardware and
//any Midas statics in good shape for the Get and MGet procedures to
//deliver accurate values.  MPutMemData calls PutMemData and then
//restores any smashed volatile registers.
and PutMemData(MemX,DVec,AVec) = valof
[	let D0,D1,D2 = DVec!0,DVec!1,DVec!2
	let TVec,TVec1,DVX = vec 1,vec 1,vec 10
	switchon ConvertAV(AVec,MemX) into
	[
case ABSx:	MCXct(BInt+BCStoreInc+(D0<<lh))
		MCXct(BInt+BCStoreInc+(D0<<rh))
		FixClock(); resultis true
case ABSOLx:	MCXct(BInt+BCStoreInc+(D0<<lh)); FixClock(); resultis true
//Illegal virtual addresses as data for TPC or TLINK will be given a false
//exit here, then ErrorAbort from MPutMemData.
case TPCx:	if VirtualP do
		[ D0 = LookUpAA(D0)
		  if D0 < 0 then resultis false
		]
		test MADDRL eq SaveTask
		ifso SaveTPC = D0
		ifnot
		[ XctL16C(LLINK,D0); XctL16C(LTPC,MADDRL)
		]
		endcase
case TLINKx:	if VirtualP do
		[ D0 = LookUpAA(D0)
		  if D0 < 0 then resultis false
		]
		SaveLINK = D0
		XctL16C(LLINK,SaveLINK); endcase
case IMBDx:	IMtoMIR(DVX,DVec)	//Convert good/bad to parity bit
		LoadIMData(D0,D1,DVX!1,IMlh)
		LoadIMData((D1 lshift 1)+(D2 rshift 15),
			D2 lshift 1,DVX!3,0)
		ClearIMBDAddr()
		LoadDMD(IMDataA); LoadDMD(IMDataB); LoadDMD(IMDataC)
		resultis true
//VM can only be built during a Ld, LdSyms, etc. and the VM mapping
//cannot subsequently be changed.
case IMx:	test DoingLoad
//AddToVM will ErrorAbort(..) on AA too large
		ifso MADDRL = AddToVM(MADDRL,DVec)
		ifnot
		[ MADDRL = LookUpAA(MADDRL,DVec)
		  if MADDRL < 0 then resultis false	//not in VM
		]
case IMXx:	XctL16C(LLINK,MADDRL)
		XctL16C((D0 < 0 ? LIMLK,LIMLFK)+((D2 & 20000B) rshift 11),
			(D0 lshift 1)+(D1 rshift 15))
		XctL16C(LLINK,MADDRL)
		XctL16C(((D1 & 40000B) ne 0 ? LIMRK,LIMRFK)+
			((D2 & 10000B) rshift 10),
			(D1 lshift 2)+(D2 rshift 14))
		endcase
case ALUFMx:	D0 = D0<<lh
		test MADDRL eq 16B
		ifso SaveALUFM16 = D0
		ifnot test MADDRL eq 0
		  ifso SaveALUFM0 = D0
		  ifnot
		  [ XctL16Q(D0); Xct(SetALUF(LAF0,MADDRL))
		  ]
		endcase
case Tx:	SaveT = D0; XctL16T(SaveT); endcase
case RBASEx:	SaveRBase = D0 rshift 12
		XctLFF(LRB0,SaveRBase); endcase
case TIOAx:	SaveTIOA = D0; XctL16Q(D0); Xct(TIOAFQ); endcase
case MEMBASEx:	SaveMBase = D0 rshift 11
		XctLFF(LMB0,SaveMBase); endcase
case RMx:	XctL16(SetRSTK(LRM0,MADDRL),D0); endcase
case STKXx:
case STKx:	XctL16(WSTACK,D0); endcase
case BRXx:	BR36Saved = false
case BRx:	XctL16T(D0); Xct(BRHIFT); XctL16T(D1); Xct(BRLOFT)
		if MADDRL eq 36B then BR36Saved = false
		endcase
//MADDRL has 2 low bits of address = column positioned in McrV field
//MADDRH has n row address bits positioned with 4 zeroes to the right
//Relevant bits of VA[4:31] are in DVec[4:31], flags in DVec[0:3].
//(With 256-word pages and 1k ic's in cache data section, VA[7:21] are
//relevant).  Use BR 36 from task 17 to reference CACHEA.
//Because NoRef is true, CACHED will not be updated from memory
case CACHEAx:	XctL16T(FDMiss+DisCF+UseMcrV+DisHold+NoRef+NoWake+MADDRL)
		Xct(MCRFT)
//BR36←VA[4:31] - value for flags, T←value for flags, then reference
//T+BR.  This cleverly accomplishes Mar←value for flags while
//BR+Mar has desired VA[4:31].
		TVec!0 = D0 & CacheAMask0 & #7777  //VA[7:15] for BRHI
		TVec!1 = MADDRH+(D1 & CacheAMask1) //VA[16:21] for BRLO
		D1 = ((not D0) rshift 8) & 360B	   //Flags for CFLAGS←T
		TVec1!0 = 0; TVec1!1 = D1; DoubleNeg(TVec1)
		DoubleAdd(TVec,TVec1)
		XctL16T(TVec!0); Xct(BRHIFT)
		XctL16T(TVec!1); Xct(BRLOFT)
//FETCH is done twice to prevent tag-bit screw up
		XctL16T(D1); Xct(NOOP); Xct(FETCHM); Xct(NOOP)
		Xct(FETCHM); Xct(NOOP)
		XctL16T(FDMiss+UseMcrV+DisHold+NoRef+NoWake+MADDRL)
		Xct(MCRFT); XctL16T(D1); Xct(NOOP)
//Flags written in a separate reference because of problem with Dirty bit.
//The sequence only works with RunRefresh off.
		D0 = TurnOffRefresh()
		Xct(DUMMYFT); Xct(CFLAGSFT)
		LoadDMD(D0); endcase
//BR has VA in position.  Task 17, ProcSRN 1 selected, Mcr has NoWake.
//Everything except the Ref bit is handled by Map←.  WP is loaded from
//TIOA.0 and Dirty from TIOA.1.
case MAPx:	XctL16T(0); Xct(NOOP); XctL16Q(D0 lshift 14); Xct(TIOAFQ)
		XctL16Q(D1); Xct(MAPTQ); endcase
case CACHEDx:
case VMx:	XctL16Q(D0); Xct(STORETQ); endcase
case IFUMx:	XctL16Q(D0); Xct(IFHFQ); Xct(RQ)
		XctL16Q(FixIFUMParity(D0,D1)); Xct(IFLFQ); Xct(RQ); endcase
case LDRx:	IMtoMIR(MADDRL,DVec); resultis true
case MDATAx:	MADDRL!2 = D2 & 170000B
case MADDRx:	MBlock(MADDRL,DVec,2)
		LoadDWatch(); resultis true
case DMUXx:	DMuxSelect!MADDRL = D0; resultis true
case DHISTx:	MBlock(MADDRL,DVec,3); resultis true
default:	resultis false
	]
	Xct(NOOP); resultis true
]


and MPutMemData(MemX,DVec,AVec,Extension) be
[	let AVec1 = vec 1; MBlock(AVec1,AVec,2)
	test MemX eq ROWx
	ifso
	[ if Extension eq 4 then ErrorAbort(stReadOnly)
	  MemX = CACHEAx; AVec1!1 = AVec!1 + (Extension lshift log2rows)
	]
	ifnot if MemX eq DMUXx do
	  [ if DMuxSelect ne DChecked then ErrorAbort(stReadOnly)
	  ]
	MPutChecks(MEMCON!MemX)	//Will ErrorAbort(..) if illegal
	if PutMemData(MemX,DVec,AVec1) do
	[ switchon MemX into
	  [
case TPCx:	if MADDRL eq SaveTask then
		[ UpdateMPDValues(); return
		]
case IMx:
case IMXx:	XctL16C(LLINK,SaveLINK); Xct(NOOP); endcase
case RMx:	XctLFF(LRB0,SaveRBase); endcase
case STKXx:
case STKx:	XctL16Q(SaveSTKP); Xct(STKPFQ)
case TIOAx:	XctL16Q(SaveQ); Xct(NOOP)
case MEMBASEx: case TLINKx: case Tx:
case RBASEx:	SelectTask(OldTask); endcase
case IFUMx:	XctL16Q(SaveQ); Xct(NOOP); endcase
case ALUFMx:	if (MADDRL ne 16B) & (MADDRL ne 0) do
		[ XctL16Q(SaveQ); Xct(NOOP); endcase
		]
default:	UpdateMPDValues(); return
case IMBDx:	RestoreIMBD(); endcase
case MAPx:	XctL16Q(SaveTIOA); Xct(TIOAFQ)
case CACHEDx:
case VMx:	XctL16Q(SaveQ); Xct(NOOP)
case CACHEAx:	RestoreBR36()
case BRXx:
case BRx:	RestoreMemStuff(); endcase
	  ]
	  LoadMIR(SaveMIR); ReadDMux()
	  UpdateMPDValues(); return
	]
	ErrorAbort(stUndAdr)
]


//Do manifold load of IM half-word
and LoadIMData(d0,d1,dpar,lhalf) be
[	LoadDMD(IMDataA+((d0 rshift 9) & 77B))
	LoadDMD(IMDataB+((d0 rshift 3) & 77B))
	LoadDMD(IMDataC+((d0 lshift 3) & 77B)+((d1 rshift 13) & 4)+
		((dpar rshift 6) & 2B))
	let dcon = IMControl+IMAddressen+ResetCBMIR+
		((d0 rshift 14) & 2B)+lhalf
	LoadDMD(dcon); LoadDMD(dcon+IMWriteen); LoadDMD(dcon)
]