;-----------------------------------------------------------------
; XMesaRAM.Mu - Overflow XMesa microcode from ROM1
; version 6, compatible with main microcode >=39
; Last modified by  GWilliams on June 4, 1981  10:45 AM
;  (changed %1,1777,20,GoToRom to %0,1777,20,GoToRom
;-----------------------------------------------------------------

; Separate assembly requires...

#Mesab.mu;

;-----------------------------------------------------------------
; Entry Point Definitions:
;
;  The definitions below must correspond to those in Mesab.
;-----------------------------------------------------------------


%0,1777,20,GoToROM;						must match ramMgo

%1,1777,402,BLTintpend,BLTloop;					BLTloop must match ramBLTloop
%3,1777,404,BLTnoint,BLTint,BLTLnoint,BLTLint;			BLTint must match ramBLTint

%1,1777,410,Overflow;						must correspond to ramOverflow

%1,1777,411,JramBITBLT;						advertized place to do BITBLT

;-----------------------------------------------------------------
; BITBLT linkage:
;	An additional constraint peculiar to the BITBLT microcode is that
;	the high-order 7 bits of the return address be 1's.  Hence,
;	the recommended values are:
;	  no ROM1 extant or emulator in ROM1 => BITBLTret = 177577B 
;	  ROM1 extant and emulator in RAM  => BITBLTret = 177175B
;-----------------------------------------------------------------
$ROMBITBLT	$L004124,0,0;			BITBLT routine address (124B) in ROM0

$BITBLTret	$177175;			(may be even or odd)

; The third value in the following pre-def must be:  (BITBLTret AND 777B)-1

%1,1777,174,BITBLTintr,BITBLTdone;		return addresses from BITBLT in ROM0


;-----------------------------------------------------------------
; Overflow instruction dispatch
;-----------------------------------------------------------------
!17,20,RR,BLTL,WR,MISC,DADD,DSUB,DCOMP,DUCOMP,BITBLT,,,,,,,;	dispatched in ROM1

GoToROM:	L←ONE, SWMODE;					smash G to disable
		gp←L, :romMgo;					optimization on initial entry

;-----------------------------------------------------------------
; D o u b l e - P r e c i s i o n   A r i t h m e t i c
;-----------------------------------------------------------------

; !1,1,DSUBsub;							shake B/A dispatch
!3,4,DAStail,,,DCOMPr;						returns from DSUBsub
!1,1,Dsetstkp;							shake ALUCY dispatch
!1,1,Setstkp;							shake IDISP from BLTnoint

Overflow:	:RR;						dispatch pending
;								TASK pending for doubles

;-----------------------------------------------------------------
; DADD - add two double-word quantities, assuming:
;	stack contains precisely 4 elements
;-----------------------------------------------------------------
; !1,1,DADDx;							shake B/A dispatch
!1,2,DADDnocarry,DADDcarry;

DADD:		T←stk2, :DADDx;					T:low bits of right operand
DADDx:		L←stk0+T;					L:low half of sum
		stk0←L, ALUCY;					stash, test carry
		T←stk3, :DADDnocarry;				T:high bits of right operand

DADDnocarry:	L←stk1+T, :DASCtail;				L:high half of sum
DADDcarry:	L←stk1+T+1, :DASCtail;				L:high half of sum


;-----------------------------------------------------------------
; DSUB - subtract two double-word quantities, assuming:
;	stack contains precisely 4 elements
;-----------------------------------------------------------------

DSUB:		IR←msr0, :DSUBsub;


;-----------------------------------------------------------------
; Double-precision subtract subroutine
;-----------------------------------------------------------------
!1,2,DSUBborrow,DSUBnoborrow;
!7,1,DSUBx;							shake IR← dispatch

DSUBsub:	T←stk2, :DSUBx;					T:low bits of right operand
DSUBx:		L←stk0-T;					L:low half of difference
		stk0←L, ALUCY;					borrow = ~carry
		T←stk3, :DSUBborrow;				T:high bits of right operand

DSUBborrow:	L←stk1-T-1, IDISP, :DASCtail;			L:high half of difference
DSUBnoborrow:	L←stk1-T, IDISP, :DASCtail;			L:high half of difference


;-----------------------------------------------------------------
; Common exit code
;-----------------------------------------------------------------

DASCtail:	stk1←L, ALUCY, :DAStail;			carry used by double compares
DAStail:	T←2, :Dsetstkp;					adjust stack pointer

Dsetstkp:	L←stkp-T, SWMODE, :Setstkp;
Setstkp:	stkp←L, :romnext;				'next' has proper SWMODE bit

;-----------------------------------------------------------------
; DCOMP - compare two long integers, assuming:
;	stack contains precisely 4 elements
;	result left on stack is -1, 0, or +1 (single-precision)
;-----------------------------------------------------------------
; !1,1,DCOMPxa;							shake B/A dispatch
!10,1,DCOMPxb;							shake IR← dispatch
!1,2,DCOMPnocarry,DCOMPcarry;
!1,2,DCOMPgtr,DCOMPequal;

DCOMP:		IR←T←100000, :DCOMPxa;				IR←msr0, must shake dispatch
DCOMPxa:	L←stk1+T, :DCOMPxb;				scale left operand
DCOMPxb:	stk1←L;
		L←stk3+T, TASK;					scale right operand
		stk3←L, :DSUBsub;				do DSUB, return to DCOMPr

DCOMPr:		T←stk0, :DCOMPnocarry;				L: stk1, ALUCY pending

DCOMPnocarry:	L←0-1, BUS=0, :DCOMPsetT;			left opnd < right opnd
DCOMPcarry:	L←M OR T;					L: stk0 OR stk1
		SH=0;
DCOMPsetT:	T←3, :DCOMPgtr;					T: amount to adjust stack

DCOMPgtr:	L←0+1, :DCOMPequal;				left opnd > right opnd
DCOMPequal:	stk0←L, :Dsetstkp;				stash result


;-----------------------------------------------------------------
; DUCOMP - compare two long cardinals, assuming:
;	stack contains precisely 4 elements
;	result left on stack is -1, 0, or +1 (single-precision)
;	  (i.e. result = sign(stk1,,stk0 DSUB stk3,,stk2) )
;-----------------------------------------------------------------

DUCOMP:		IR←sr3, :DSUBsub;				returns to DCOMPr

;-----------------------------------------------------------------
; E m u l a t o r   A c c e s s
;-----------------------------------------------------------------


;-----------------------------------------------------------------
; RR - push <emulator register alpha>, where:
;	RR is A-aligned (also ensures no pending branch at entry)
;	alpha:	1 => wdc, 2 => XTSreg, 3 => XTPreg, 4 => ATPreg,
;		5 => OTPreg
;-----------------------------------------------------------------
!7,10,RR0,RR1,RR2,RR3,RR4,RR5,,;

RR:		SINK←ib, BUS;					dispatch on alpha
RR0:		L←0, SWMODE, :RR0;				(so SH=0 below will branch)

RR1:		L←wdc, SH=0, :romUntail;			will go to pushTA
RR2:		L←XTSreg, SH=0, :romUntail;			will go to pushTA
RR3:		L←XTPreg, SH=0, :romUntail;			will go to pushTA
RR4:		L←ATPreg, SH=0, :romUntail;			will go to pushTA
RR5:		L←OTPreg, SH=0, :romUntail;			will go to pushTA


;-----------------------------------------------------------------
; WR - emulator register alpha ← <TOS> (popped), where:
;	WR is A-aligned (also ensures no pending branch at entry)
;	alpha: 1 => wdc, 2 => XTSreg
;-----------------------------------------------------------------
!7,10,WR0,WR1,WR2,,,,,;

; WR:		L←ret3, TASK, :Xpopsub;				performed in ROM

WR:		SINK←ib, BUS;					dispatch on alpha
WR0:		SWMODE, :WR0;

WR1:		wdc←L, :romnextA;
WR2:		XTSreg←L, :romnextA;

;-----------------------------------------------------------------
; BLT - block transfer
;	assumes stack has precisely three elements:
;	  stk0 - address of first word to read
;	  stk1 - count of words to move
;	  stk2 - address of first word to write
;	the instruction is interruptible and leaves a state suitable
;	  for re-execution if an interrupt must be honored.
;-----------------------------------------------------------------
!1,2,BLTmore,BLTdone;
!1,2,BLTsource,BLTCsource;
!1,2,BLTeven,BLTodd;
!1,1,BLTintx;							shake branch from BLTloop

;  Entry sequence in ROM1; actual entry is at BLTloop

;BLT:		stk7←L, SWMODE, :BLTx;				stk7=0 <=> branch pending
;BLTx:		IR←msr0, :ramBLTloop;				IR← is harmless


BLTloop:	L←T←stk1-1, BUS=0, :BLTnoint;
BLTnoint:	stk1←L, L←BUS AND ~T, IDISP, :BLTmore;		L←0 on last iteration (value
;								on bus is irrelevant, since T
;								will be -1).  IDISP on last
;								cycle requires that Setstkp
;								be odd.

BLTmore:	T←cp, :BLTsource;

BLTsource:	MAR←stk0, :BLTupdate;				start data source fetch
BLTCsource:	XMAR←stk0+T, :BLTupdate;			start code source fetch

BLTupdate:	L←stk0+1;
		stk0←L;						update source pointer
		L←stk2+1;
		T←MD;						source data
		MAR←stk2;					start dest. write
		stk2←L, L←T;					update dest. pointer
		SINK←NWW, BUS=0, TASK;				check pending interrupts
		MD←M, :BLTintpend;				loop or check further

BLTintpend:	SINK←wdc, BUS=0, :BLTloop;			check if interrupts enabled

;	Must take an interrupt if here (via BLT or BITBLT)

BLTint:		SINK←stk7, BUS=0, :BLTintx;			test even/odd pc
BLTintx:	L←mpc-1, :BLTeven;				prepare to back up

BLTeven:	mpc←L, L←0, :BLTodd;				even - back up pc, clear ib
BLTodd:		ib←L, SWMODE;					odd - set ib non-zero
		:romIntstop;

;	BLT completed

BLTdone:	SINK←stk7, BUS=0, SWMODE, :Setstkp;		stk7=0 => return to 'nextA'

;-----------------------------------------------------------------
; BLTL - block transfer (long pointers)
;	assumes stack has precisely five words:
;	  stk0, stk1 - address of first word to read
;	  stk2	     - count of words to move
;	  stk3, stk4 - address of first word to write
;	the instruction is interruptible and leaves a state suitable
;	  for re-execution if an interrupt must be honored.
;	the following are used as temporaries (here and BITBLT):
;	  stk7 - saved B/A flag from instruction dispatch
;	  stk6 - saved value of emulator bank register
;-----------------------------------------------------------------
!7,1,BLTLsetBR;							shake BUS=0 and IR←
!7,1,BLTLsetBRx;						shake IR←
!7,10,BLTLret0,BLTLret1,BLTLret2,BBret3,BBret4,,,;
!1,2,BLTLintpend,BLTLloop;
!1,2,BLTLmore,BLTLdone;
;!1,2,BLTLnoint,BLTLint;					appears above

; Note:  ROM1 code does stk7←L


BLTL:		MAR←BankReg;					access bank register
		T←stk1;						high source bits
		L←stk1+T;					L: high source *2
		temp←L LSH 1, IR←msr0;				temp: high source *4;
		L←MD, TASK;					L: old bank register
		stk6←L;					stk6: stashed register
		T←stk4;						T: high dest bits
		T←3.T, :BLTLsetBR;				(would like to avoid this)
;								returns to BLTLret0

BLTLloop:	L←T←stk2-1, BUS=0, :BLTLnoint;			decrement count, test done
BLTLnoint:	stk2←L, :BLTLmore;				T: -1 the last time

BLTLmore:	MAR←stk0;					fetch source word
		L←stk0+1;					bump source pointer
		stk0←L;
		L←stk3+1;					bump destination pointer
		T←MD;
		XMAR←stk3;					initiate store
		stk3←L, L←T;					L: data
		SINK←NWW, BUS=0, TASK;				check for possible interrupt
BLTLret0:	MD←M, :BLTLintpend;				stash data

BLTLintpend:	SINK←wdc, BUS=0, :BLTLloop;			check if enabled

BLTLint:	IR←sr2, :BLTLsetBR;				restore bank before interrupt
BLTLret2:	MD←stk6, :BLTint;				BLTint shakes branch

BLTLdone:	IR←sr1, :BLTLsetBR;				restore bank before exit
BLTLret1:	MD←stk6, L←stk6 AND NOT T, :BLTdone;		BLTdone shakes branch, L←0


BLTLsetBR:	MAR←BankReg, :BLTLsetBRx;
BLTLsetBRx:	L←temp OR T, IDISP;				(used by BLTLret0 only)
		SINK←0, BUS=0, :BLTLret0;			force branch for BLTLret0
;								others must shake

;-----------------------------------------------------------------
; BITBLT - do BITBLT using ROM0 subroutine
;	If BITBLT A-aligned, B byte will be ignored
;	temporaries (in addition to BLTL):
;	  stk5 - 0=>short BITBLT, 2=>long BITBLT
;	  temp - holds value from second word of bbtable
;-----------------------------------------------------------------
; from ROM
;!1,1,BITBLTx;							shake B/A dispatch

;BITBLT:		stk7←L, :BITBLTx;			save even/odd across ROM call
;BITBLTx:	L←10, SWMODE, :DoRamRWB;



!1,2,IntOff,TestLong;
!1,2,LongBB,DoBITBLT;						also shake SetBR branch
%2,3,1,BBshortDone,BBlongDone;

JramBITBLT:	L←ib, TASK;					save alignment for interrupts
		stk7←L;
		L←3, TASK;					set stkp for interrupts
		stkp←L, :BITBLT;

BITBLT:		L←stk0;
		AC2←L, L←0, TASK;
		stk5←L;
		SINK←wdc, BUS=0;				check if Mesa interrupts off
		T←100000, :IntOff;
IntOff:		L←NWW OR T;					if so, shut off Nova's
		NWW←L, :TestLong;

TestLong:	MAR←stk0+1;					fetch word for long check
		L←stk1;
		AC1←L;						stash intermediate state
		L←MD, BUS=0, TASK;
		temp←L, :LongBB;				BR word

!7,1,BBx;				shake IR←sr3

LongBB:		MAR←BankReg;
		L←2;
		stk5←L;
		IR←sr3;
		L←MD, TASK;					old Bank reg
BBx:		stk6←L, :BLTLsetBR;
BBret3:		MD←temp, :DoBITBLT;

DoBITBLT:	L←BITBLTret, SWMODE;				get return address
		PC←L, L←0, :ROMBITBLT;				L←0 for Alto II ROM0 "feature"

BITBLTdone:	T←100000;
		L←NWW AND NOT T;
		SINK←stk5, BUS;
		NWW←L, L←T←0, :BBshortDone;

BBlongDone:	IR←sr4, :BLTLsetBR;
BBret4:		MD←stk6, L←T, :BBshortDone;

BBshortDone:	brkbyte←L, BUS=0, SWMODE, :Setstkp;		don't bother to validate stkp

BITBLTintr:L←AC1;						pick up intermediate state
		SINK←stk5, BUS;
		stk1←L, :BLTint;				stash intermediate state