; PupChecksum.mu -- Ram ByteCode to compute PupChecksums
;  and other things needed by all Mesa variations of Gateway microcode

;	Last modified HGM March 28, 1979  10:27 PM


;  Assumes that MesaXRAM.Mu (which includes MesabROM.Mu) has been included
;  A predef such as: 
;   %7, 1777, 1400, SilentBoot, xxx, PupChecksum, xxx;
;   should occurr in the program that includes this module 


; **** Emulator Task ****
; Degenerate -- just returns control to emulator in Rom1 (for silent boot).
; Also used as a handy return point by ByteCodes

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 = 1400B;  -- 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 1402.

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


; 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)  NB: Zero won't work!
; Returns the ones-complement add-and-cycle checksum over the block.
; Entry point is Ram address 1402.
; 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