; PupTestCPMc.mu -- main microcode source for PupTest ComProc version
; Copyright Xerox Corporation 1979
; Last modified September 10, 1978  6:15 PM by Boggs

#AltoConsts23.mu;

; Reset locations of the tasks to be started in the Ram.
!17, 20, Emulator, xREST, EREST, , , , CommProcTask, , MRT;

; Ram entry point for fielding undefined opcodes.
!37, 1, TRAP1;

; 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;

; **** ComProc microcode ****
#CommProc.mu;

; **** Extra Ethernet microcode ****
$ECNTR	$R14;
$EPNTR	$R15;
#ExtraEther1.mu;

$xCNTR	$R16;
$xPNTR	$R17;
#ExtraEther2.mu;

; Nova emulator conventions

$START	$L4020, 0, 0;

$AC3	$R0;
$AC2	$R1;
$AC1	$R2;
$AC0	$R3;
$NWW	$R4;
$SAD	$R5;
$PC	$R6;
$XREG	$R7;
$XH	$R10;
$LREG	$R40;

Emulator:
	SWMODE;			Switch to Rom1
	:START;			Nova emulator entry point


; Trap handler and dispatcher for instructions that trap into
; the RAM.  In the following predefinition, the tags correspond
; to opcodes 60000, 60400, 61000, 61400, ... 77400.
; Note that opcodes 60000, 60400, 61000, 64400, 65000, 67000, and 77400
; cannot be used since control never gets to the RAM for these.

; 61400 GetFrame
; 62000 Return
; 62400 BcplUtility
; 63000	SilentBoot
; 63400	EnableEIA
; 63400 SetLineTab (ComProc)
; 64000	PupChecksum
; 65400 ChangeControlReg (ComProc)
; 66000 BlockEq

!37,40, TrapDispatch, , , GetFrame, Return, BcplUtility, SilentBoot, SetLineTab, PupChecksum, , , ChangeControlReg, BlockEq;

; Control comes here with the instruction LCY 8 in XREG
TRAP1:	T← 37;
	L← XREG AND T;
TrapDispatch:
	SINK← M, BUS, TASK;
	:TrapDispatch;

; **** BCPL runtime microcode ****
#GetFrame.mu;
#BcplUtil.mu;

; SilentBoot instruction:
; Accepts in:
;	AC0/	boot locus vector
; Sets the Boot Locus Vector and does a silent boot.

SilentBoot:
	RMR← AC0;		Set BLV from arg
	SINK← 100000, STARTF, :Emulator;  Boot the machine, resume emulator

; SetLineTab subroutine:
; Accepts in:
;	AC0/	LINTAB
; Specifies the address of the Line Table (LINTAB).

SetLineTab:
	L← AC0, TASK;			LINTAB← AC0
	LINTAB← L, :Emulator;

; ChangeControlReg subroutine:
; Accepts in:
;	AC0/	lineTimes4
;	AC1/	changeMask
; 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← AC0;				lineTimes4
	LINE*4← L;
	T← AC1;				T← changeMask
	L← LCRetX#, :LIMCon0;		L← LIMCon0 return index
LCRetX:	:Emulator;

; Pup Checksum instruction
; Computes the ones-complement add-and-cycle checksum over the block.
; Accepts in:
;	AC0/ 0 initially
;	AC1/ address of block
;	AC3/ length of block (words)
; Returns:
;	AC0/ result
;	AC1/ address+length-1
;	AC3/ 0
; This instruction is interruptible.  If an interrupt occurs, intermediate
; state is stored in the ACs and the PC is backed up so the instruction will
; start over when the interrupt is dismissed.
; Timing: 9 cycles/word
; 2484 cycles (= 422 microseconds) per maximum-length Pup

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

PupChecksum:
	MAR← L← AC1, :PCLp1;		Start fetch of first word

; Top of main loop
PCLoop:	MAR← L← AC1+1;			Start fetch of next word
PCLp1:	AC1← L;				Update pointer
	L← NWW, BUS=0;			Test for interrupts
	T← AC0, SH<0, :PCMayI;		[PCMayI, PCNoI] Get partial sum
PCNoI:	L← MD+T, :PCDisI;		Add new word
PCDisI:	T← M, ALUCY;			Test for carry out
	L← AC3-1, :PCNoCy;		[PCNoCy, PCCy] Decrement count
PCNoCy:	AC3← L, L← T, SH=0, TASK, :PCLast;  No carry
PCCy:	AC3← L, L← T← 0+T+1, SH=0, TASK;  Do end-around carry
PCLast:	AC0← L MLSH 1, :PCLoop;	[PCLoop, PCDone] Left cycle 1

; Here when done
PCDone:	L← AC0+1;			Test for minus zero (ones-complement)
	L← PC, SH=0;
PCDn1:	PC← L, L← 0, TASK, :PCNoMZ;	[PCNoMZ, PCMinZ]
PCNoMZ:	:Emulator;
PCMinZ:	AC0← L, :Emulator;		Minus zero, change to plus zero

; Here when have potential interrupt;  branch pending on disable bit.
PCMayI:	L← MD+T, :PCDoI;		[PCDoI, PCDisI]
PCDoI:	L← PC-1, :PCDn1;		Back up PC

; Fast Block compare instruction
; Accepts in:
;	AC0/	address of block0
;	AC1/	address of block1
;	AC3/	number of words to compare
; Returns:
;	AC3/	offset of first not equal pair of words

!1,2,BeMore,BeDone;
!1,2,BeMaybeInt,BeNoInt;
!1,2,BeError,BeMain;
!1,2,BeDoInt,BeIntOff;

BlockEq: L← AC1-1;
	AC1← L;
	MAR← L← AC0, :Be2;

; Main loop is 12 cycles - runs memory at full (single word) speed
BeMain:	MAR← L ← AC0+1;		Fetch a word from block 0
Be2:	AC0← L;
	L← AC3-1, BUS=0;	Update count, check for done
	AC3← L, :BeMore;	[BeMore,BeDone]
BeMore:	T← MD;
	MAR← L← AC1+1;		Fetch a word from block 1
	AC1← L;
	L← NWW, BUS=0;		Check for interrupts
	SH<0, :BeMaybeInt;	[BeMaybeInt,BeNoInt]
BeNoInt: L← MD-T;		Check for words equal
BeIntOff: SH=0, TASK;
	:BeError;		[BeError,BeMain]

BeMaybeInt: L← MD-T, :BeDoInt;	[BeDoInt,BeIntOff]
BeDoInt: L← PC-1, TASK;		Save state for interrupt
	PC← L;
BeError: SWMODE, L← AC3+1, :Be3; Come here on compare error
BeDone:	SWMODE, L← AC3+1;	Come here when done
Be3:	AC3← L, :START;