; DoradoMc.Mu -- Alto microcode to facilitate controlling Dorado
;	Last editted: April 6, 1979  4:42 PM


; Dedicated S-registers
$UTILOUT $R52;		Address of Diablo Printer output register
$UTILIN	$R53;		Address of Diablo Printer input register

; Sensible names for existing constants!
$1	$1;
$400	$400;
$4000	$4000;
$10000	$10000;

; RDMux instruction
; AC0: clock bits (DAddrBit and Strobe must be 0)
; AC1: starting DMux address (must be 0)
; AC3: pointer to DMuxTab

; Reads all 2048 DMux addresses except 2047
; Stores DMux bit i into bit i mod 16 of word i/16 of DMuxTab, which
; should be zeroed initially.

; AC1← 0 upon normal termination.  If an interrupt occurs, AC1 ←  some
; intermediate address at which the instruction resumes when the interrupt
; is dismissed.

; This code is optimized for Alto-II, though it should work on Alto-I also.
; Timing: 223 + (27 * number of zero data bits) + (35 * number of one bits)
; microinstructions (assuming no interrupts).
; Execution time (assuming equal number of zeroes and ones):
; = 63711 microinstructions = 10.83 ms

!1,2,DInitL,DInitD;
!1,2,DMaybe,DNoInt;
!1,2,DDoInt,DDisab;
!7,10,,,IRBr2,IRBr3,,,IRBr6,IRBr7;
!1,2,DData1,DData0;
!17,20,DBit0,DBit1,DBit2,DBit3,DBit4,DBit5,DBit6,DBit7,
	DBit8,DBit9,DBit10,DBit11,DBit12,DBit13,DBit14,DBit15;
!1,2,RDLoop,DDone;
!1,2,NDone,Exit;


; Initialize by shifting four zeroes and then the high-order 10 bits of
; the DMux address into the address register.
RDMux:	L← AC1;			Initial DMux address
	SAD← L LSH 1;
	L← 16, TASK;		Iteration count
	XREG← L;

; Initialization loop
DInitL:	T← SAD;
	MAR← UTILOUT;		Start store of address bit with strobe off
	T← 100000 . T;		Extract address bit
	L← T← AC0 OR T;		Merge into clock bits as DAddrBit
	MD← M;
	L← SAD;
	MAR← UTILOUT;		Start store of address bit with strobe on
	SAD← L LSH 1;		SAD← address lsh 1 for next iteration
	L← 100 OR T;		Strobe = 100
	MD← M;
	MAR← UTILOUT;		Start store of address bit with strobe off
	L← XREG-1;		Decrement and test count
	XREG← L, L← T, SH=0, TASK;
	MD← M, :DInitL;		[DInitL, DInitD]

; SAD now has the low-order address bit in bit 0, rest zeroes
DInitD:	T← SAD;
	L← AC0 OR T;		Set DAddrBit if appropriate
	AC0← L;			Store clock bits for first main iteration
	L← AC1, TASK;		SAD← current address rsh 1
	SAD← L RSH 1;

; RDMux instruction (cont'd)

; Main DMux address generator loop.
; AC0: clock bits (DAddrBit = low bit of current DMux address, Strobe = 0)
; AC1: current DMux address
; AC3: pointer to DMuxTab
; SAD: current DMux address right-shifted 1
RDLoop:	MAR← UTILOUT;		Start store of address bit with strobe off
	L← NWW, BUS=0;		Test for interrupts
	L← SAD, SH<0, :DMaybe;	[DMaybe, DNoInt] Get DMux address rsh 1
DMaybe:	MD← T← AC0, :DDoInt;	[DDoInt, DDisab]
DNoInt:	MD← T← AC0;		Pending branch can't take
DDisab:	SAD← L RSH 1;		SAD ← DMux address rsh 2
	MAR← UTILOUT;		Start store of address bit with strobe on
	L← 100 OR T;		Strobe = 100
	T← SAD;
	MD← M, L← T, TASK;
	SAD← L RSH 1;		SAD ←  DMux address rsh 3

	MAR← UTILOUT;		Start store of address bit with strobe off
	IR← AC1;		Load DISP, branch on DMux address bits 5-7
	T← 100000, :IRBr2;	DAddrBit -- prepare to set next address bit
IRBr2:	MD← AC0, L← AC0 OR T, TASK, :IRBrX;  Bit 5 = bit 7, generate a 1
IRBr7:	MD← AC0, L← AC0 OR T, TASK, :IRBrX;
IRBr3:	MD← AC0, L← AC0 AND NOT T, TASK, :IRBrX;  Bit 5 # bit 7, generate a 0
IRBr6:	MD← AC0, L← AC0 AND NOT T, TASK, :IRBrX;
IRBrX:	AC0← L;			Update clock bits for next iteration

; *** Delay to permit clock (trailing edge of strobe) to get all the way
; *** out to the Dorado and the resulting data to get all the way back.
	NOP;
	NOP;
	NOP;
	TASK;
	NOP;

	MAR← UTILIN;		Start fetch of DMux data
	L← SAD;
	SAD← L RSH 1;		SAD ←  DMux address rsh 4
	T← 4000;
	L← MD AND T;		Mask DMux data bit
	T← SAD, SH=0;		DMux address rsh 4 = word offset in table
	MAR← L← AC3+T, :DData1;	[DData1, DData0] Start fetch of DMuxTab word

; Data bit is a 1.  Set bit in table.  IR (DISP) contains DMux address.
DData1:	SINK← DISP, SINK← X17, BUS;  16-way branch on bit number
	:DBit0;			[DBit0 .. DBit15]

DBit0:	T← 100000, :DBitS;
DBit1:	T← 40000, :DBitS;
DBit2:	T← 20000, :DBitS;
DBit3:	T← 10000, :DBitS;
DBit4:	T← 4000, :DBitS;
DBit5:	T← 2000, :DBitS;
DBit6:	T← 1000, :DBitS;
DBit7:	T← 400, :DBitS;
DBit8:	T← 200, :DBitS;
DBit9:	T← 100, :DBitS;
DBit10:	T← 40, :DBitS;
DBit11:	T← 20, :DBitS;
DBit12:	T← 10, :DBitS;
DBit13:	T← 4, :DBitS;
DBit14:	T← 2, :DBitS;
DBit15:	T← 1, :DBitS;

DBitS:	T← MD OR T;		Merge bit into existing table word
	MAR← M;			Store it back
	L← T, TASK;
	MD← M;

; RDMux instruction (cont'd)

; Here if data bit is a 0.  Now generate next address.
; High-order bit (= DAddrBit) of AC0 already contains next address bit.
DData0:	T← AC1;			Current DMux address
	L← 1777 AND T;		Strip off high bit
	SAD← L;			Store next address rsh 1 for next iteration
	T← AC0, SH=0, TASK;
	AC1← L MLSH 1, :RDLoop;	[RDLoop, DDone]

; Maybe we are done -- make sure
DDone:	SINK← AC1, BUS=0, TASK;	Whole address zero?
	:NDone;			[NDone, Exit]
NDone:	:RDLoop;		No, repeat

; Here if an interrupt occurs
DDoInt:	T← 100000;		Mask out DAddrBit
	L← AC0 AND NOT T;
	AC0← L;
	L← PC-1, TASK;		Back up pc
	PC← L;

Exit:	SWMODE;
	:START;


; InitMc instruction
; Initializes S-registers needed by the DMux-reading instructions

InitMc:	T← 177000;
	L← 16 OR T;		Manufacture constant 177016
	UTILOUT← L;
	L← 30 OR T, TASK;	Manufacture constant 177030
	UTILIN← L, :Exit;


; DStrobe instruction
; Strobes data into the Dorado control register
; AC0: control bits (Strobe should be zero)

DStrobe: MAR← UTILOUT;		Start store with Strobe off
	NOP;
	MD← T← AC0;
	MAR← UTILOUT;		Start store with Strobe on
	L← 100 OR T;		Strobe = 100
	MD← M;
	MAR← UTILOUT;		Start store with Strobe off
	TASK;
	MD← AC0, :Exit;

; MIRLoad instruction
; Accepts the following arguments:
; AC0: control word (Strobe should be zero)
; AC1: pointer to 4-word block of data for MIR0..MIR3
; Executes the following sequence:
;	DStrobe[AC0]
;	DStrobe[AC0+ClrStop+ClrMIR]
;	DStrobe[AC0]
;	DStrobe[AC1!0]
;	DStrobe[AC1!1]
;	DStrobe[AC1!2]
;	DStrobe[AC1!3]

; Execution time: 130 microinstructions = 22 microseconds

!7, 10, DStrR0, DStrR1, DStrR2, DStrR3, DStrR4, DStrR5, DStrR6;

MIRLoad:
	MAR← UTILOUT;		DStrobe[AC0]
	IR← 0;			Init return index to 0
	L← T← AC0, :DStrS1;

DStrR0:	T← AC0;			DStrobe[AC0+42000]
	T← 40000 OR T;
	T← 2000 OR T, :DStrSub;

DStrR1:	T← AC0, :DStrSub;	DStrobe[AC0]

DStrR2:	MAR← L← AC1, :MIRL1;	DStrobe[AC1!0]

DStrR3:	MAR← L← XREG+1, :MIRL1;	DStrobe[AC1!1]

DStrR4:	MAR← L← XREG+1, :MIRL1;	DStrobe[AC1!2]

DStrR5:	MAR← L← XREG+1, :MIRL1;	DStrobe[AC1!3]

DStrR6:	:Exit;

MIRL1:	XREG← L;
	T← MD, :DStrSub;

; DStrobe subroutine; called with control word in T and
; return index -1 in IR; increments IR.
DStrSub:
	MAR← UTILOUT;		Start store with Strobe off
	L← DISP+1;		Increment return index
	IR← M, L← T;
DStrS1:	MD← M;
	MAR← UTILOUT;		Start store with Strobe on
	SAD← L;
	L← 100 OR T, TASK;	Strobe = 100
	MD← M;

	MAR← UTILOUT;		Start store with Strobe off
	SINK← DISP, BUS, TASK;	Dispatch on return index
	MD← SAD, :DStrR0;	[DStrR0..DStrR6]