; MesaGateCP.mu -- Top-level microcode source for Alto Gateway running
;			Mesa and using CommProc.

;	Last modified HGM July 19, 1980  5:52 PM -- Convert to Mesa 6

#AltoConsts23.mu;


; Reset locations of the tasks to be started in the Ram (Emulator, MRT,
; CommProc, Interval Timer, and one extra Ethernet).
; Extra Ethernet is assumed to be run by task 2.

!17, 20, Emulator, IntTimerTask, EREST, , , , CommProcTask, , MRT;
;
;	Now bring in Mesa overflow microcode  (From XMesaOverflow.mu)
;

#XMesaRAM.mu;

;-----------------------------------------------------------------
; MISC - Miscellaneous instructions specified by alpha
;	alpha=11 => RCLK has been handled by ROM
;	T contains alpha on arrival at MISC in RAM
;-----------------------------------------------------------------

; Precisely one of the following lines must be commented out.

MISC:		L←0, SWMODE, :Setstkp;			dummy MISC implementation

;#MesaMisc.mu;						real implementation


; Ram entry vector, for access via Mesa JRAM instruction.
; Note that only Ram locations 400-777 and 1400-1777 are reachable from Rom1.

%7, 1777, 1400, SilentBoot, SetLineTab, PupChecksum, ChangeControlReg;


; Reserve 774-1003 for Ram Utility Area.

%7, 1777, 774, RU774, RU775, RU776, RU777, RU1000, RU1001, RU1002, RU1003;

; For the moment, just throw these locations away.  This is done only
; to squelch the "unused predef" warnings that would otherwise occur.
; If we ever run short of Ram, assign these to real instructions somewhere
; in microcode executed only by the Emulator.

RU774:	NOP;
RU775:	NOP;
RU776:	NOP;
RU777:	NOP;
RU1000:	NOP;
RU1001:	NOP;
RU1002:	NOP;
RU1003:	NOP;



; **** Modified Memory Refresh Task ****

#CPMRT.mu;


; **** CommProc microcode ****

#CommProc1.mu;
#CommProc2.mu;


; **** Extra Ethernet microcode ****

$ECNTR	$R11;		Same as CLOCKTEMP, which has been abolished
$EPNTR	$R14;		Not used by Mesa or Nova emulator

#ExtraEther1.mu;

; **** Emulator Task ****
; Degenerate -- just returns control to emulator in Rom1 (for silent boot).

Emulator:
	SWMODE;				Switch to Rom1
	:romnextA;				Mesa emulator entry point


; Microcode subroutines are defined and called from Mesa programs
; as shown in the following example:

; silentBootAddr: CARDINAL = 400B;  -- Ram address of SilentBoot microcode --
; SilentBoot: PROCEDURE[bootLocusVector: WORD] =
;   MACHINE CODE BEGIN
;   Mopcodes.zLIW, silentBootAddr/256, silentBootAdr MOD 256;
;   Mopcodes.zJRAM;
;   END;

; SilentBoot[177376B];  -- the call --

; All these routines assume they are called with a clean stack.
; Hence, an invocation such as "SilentBoot[177376B]" must be written
; as a complete statement, not as an embedded expression.
; If the routine returns a value, it must be called in a statement
; of the form "simpleVariable ← Routine[args]".
; This permits the Ram subroutine to access fixed S-registers for
; arguments and return values.  It must still adjust the stack pointer
; appropriately, however.


; SilentBoot: PROCEDURE[bootLocusVector: WORD]
; Sets the Boot Locus Vector and does a silent boot.
; Entry point is Ram address 400.

SilentBoot:
	RMR← stk0;			Set BLV from arg on stack
	L← stkp-1, TASK;		stkp← stkp-1
	stkp← L;
	SINK← 100000, STARTF, :Emulator; Boot the machine


; SetLineTab: PROCEDURE[lineTab: POINTER]
; Specifies the address of the Line Table (LINTAB).
; Entry point is Ram address 401.

SetLineTab:
	L← stk0;			LINTAB← top of stack
	LINTAB← L;
	L← 0, TASK;			Empty the stack
	stkp← L, :Emulator;


; ChangeControlReg: PROCEDURE[lineTimes4: CARDINAL, changeMask: WORD]
; Changes (sets or resets) bits in the control word for a line.
; The change is atomic with respect to CommProc task execution.
; lineTimes4: 4 times the line number to be affected.
; changeMask: Bits 4:15 are a mask of bits to be affected and bit 0 is
;	the desired new value of those bits.

ChangeControlReg:
	L← 0, TASK;			Empty the stack (TASK vital here)
	stkp← L;
	L← stk0;			lineTimes4
	LINE*4← L;
	T← stk1;			T← changeMask
	L← LCRetX#, :LIMCon0;		L← LIMCon0 return index
LCRetX:	:Emulator;

; PupChecksum:
;   PROCEDURE[initialSum: CARDINAL, address: POINTER, count: CARDINAL]
;   RETURNS[resultSum: CARDINAL]
; initialSum: must be zero initially (used to restart after interrupts).
; address: address of block.
; count: length of block (words)
; Returns the ones-complement add-and-cycle checksum over the block.
; Entry point is Ram address 402.
; Timing: 9 cycles/word
; 2484 cycles (= 422 microseconds) per maximum-length Pup

$NWW	$R4;
$MTEMP	$R25;

!1,2,PCMayI,PCNoI;
!1,2,PCDisI,PCDoI;
!1,2,PCOkCy,PCZCy;
!1,2,PCNoCy,PCCy;
!1,2,PCLoop,PCDone;
!1,2,PCNoMZ,PCMinZ;
!1,1,PCDoI1;

PupChecksum:
	L← stk0;			Must keep partial sum in R reg (not S)
	temp← L;
	MAR← L← stk1, :PCLp1;		Start fetch of first word

; Top of main loop.
; Each iteration adds the next data word to the partial sum, adds 1 if
; the addition caused a carry, and left-cycles the result one bit.
; Due to ALU function availability, the first addition is actually done
; as (new data word)+(partial sum -1)+1, which causes an erroneous carry
; if (partial sum)=0.  Hence we make a special test for this case.
PCLoop:	MAR← L← stk1+1;			Start fetch of next word
PCLp1:	SINK← NWW, BUS=0;		Test for interrupts
	stk1← L, :PCMayI;		[PCMayI, PCNoI] Update pointer
PCNoI:	T← temp-1, BUS=0, :PCDisI;	[PCDisI, PCDoI] Get partial sum -1
PCDisI:	L← T← MD+T+1, :PCOkCy;		[PCOkCy, PCZCy] Add new word +1
PCOkCy:	L← stk2-1, ALUCY;		Test for carry out, decrement count
PCLp2:	stk2← L, :PCNoCy;		[PCNoCy, PCCy] Update count
PCNoCy:	L← T, SH=0, TASK, :PCLast;	No carry, test count=0
PCCy:	MTEMP← L, L← T← 0+T+1, SH=0, TASK;  Do end-around carry, test count=0
PCLast:	temp← L MLSH 1, :PCLoop;	[PCLoop, PCDone] Left cycle 1

; Here if partial sum was zero -- suppress test of bogus carry caused by
; MD+(temp-1)+T+1 computation.
PCZCy:	L← stk2-1, :PCLp2;

; Here when done
PCDone:	L← temp+1;			Test for minus zero (ones-complement)
	L← 1, SH=0;			Define stack to contain one thing
PCDn1:	stkp← L, L← 0, :PCNoMZ;		[PCNoMZ, PCMinZ]
PCNoMZ:	L← temp, TASK, :PCGoEm;
PCMinZ:	TASK;				Minus zero, change to plus zero
PCGoEm:	stk0← L, :Emulator;		Put result on stack

; Here when possible interrupt pending.
; Note that if the interrupt does not take, we read MD one cycle too late.
; This works only on Alto-II.
PCMayI:	SINK← wdc, BUS=0, :PCNoI;	Let it take only if wdc=0

; Here when interrupt definitely pending.
; Assume that the JRAM was the A-byte, so back up mpc and set ib to zero
; to force the interpreter to re-fetch the current word and also test again
; for the interrupt we know is pending.
PCDoI:	L← mpc-1;			[PCDOI1] Back up mpc, squash BUS=0
PCDoI1:	mpc← L, L← 0, TASK;
	ib← L;				ib← 0
	L← stkp+1, :PCDn1;		Push Ram address back onto stack