; 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