; MesaGateCP.mu -- Top-level microcode source for Alto Gateway running ; Mesa and using 1822 and CommProc. ; Mast modified September 18, 1978 4:47 PM #AltoConsts23.mu; ; Reset locations of the tasks to be started in the Ram (Emulator, MRT, ; CommProc, 1822, Interval Timer, and one extra Ethernet). ; Extra Ethernet is assumed to be run by task 2. !17, 20, Emulator, IntTimerTask, 1EREST, , , IMLOOP, CommProcTask, 0EREST, MRT; ; 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, 400, SilentBoot, SetLineTab, PupChecksum, ChangeControlReg; %7, 1777, 420, SetConPtr, PREncrypt; ; 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; ; **** EIA microcode **** #CommProc.mu; ; **** Extra Ethernet microcode **** $0ECNTR $R12; These belong to the normal Ethernet Task $0EPNTR $R13; $0ELOC $600; $1ECNTR $R11; Same as CLOCKTEMP, which has been abolished $1EPNTR $R14; Not used by Mesa or Nova emulator $1ELOC $630; #ChainEther0.mu; #ChainEther1.mu; ; **** 1822 microcode **** #Mesa1822.mu; ; Mesa emulator conventions $next $L 4400, 0, 0; Emulator entry point $temp $R35; Emulator-level temporaries $temp2 $R36; $mpc $R15; Mesa PC $ib $R10; Leftover instruction byte (0 if none) $wdc $R70; Wakeup disable counter $stkp $R16; Stack pointer (0 = empty, 10 = full) $stk0 $R60; Stack (bottom) $stk1 $R61; $stk2 $R62; $stk3 $R63; $stk4 $R64; $stk5 $R65; $stk6 $R66; $stk7 $R67; Stack (top) ; **** Emulator Task **** ; Degenerate -- just returns control to emulator in Rom1 (for silent boot). Emulator: SWMODE; Switch to Rom1 :next; 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 #PREncrypt.mu;