;	A L T O C O D E 2 4 . M U

;***Derived from ALTOCODE23.MU, as last modified by
;	Ingalls, August 11, 1976  10:39 PM
;***E. McCreight, editor
;***modified by McCreight, September 19, 1977  4:34 PM
;	removed STM3: dependence on saving R40 across tasks
;***modified by Boggs, September 20, 1977  8:02 PM
;	moved constants and symbols into AltoConsts23.mu

;Get the symbol and constant definitions
#AltoConsts23.mu;

;LABEL PREDEFINITIONS

;The reset locations of the tasks:

!17,20,NOVEM,,,,KSEC,,,EREST,MRT,DWT,CURT,DHT,DVT,PART,KWDX,;

;Locations which may need to be accessible from the Ram, or Ram
;  locations which are accessed from the Rom (TRAP1):
!37,20,START,RAMRET,RAMCYCX,,,,,,,,,,,,,TRAP1;

;Macro-op dispatch table:
!37,20,DOINS,DOIND,EMCYCLE,NOPAR,JSRII,U5,U6,U7,,,,,,,RAMTRAP,TRAP;

;Parameterless macro-op sub-table:
!37,40,DIR,EIR,BRI,RCLK,SIO,BLT,BLKS,SIT,JMPR,RDRM,WTRM,DIRS,VERS,V15,V16,V17,MUL,DIV,V22,V23,BITBLT,,,,,,,,,,,;

;Cycle dispatch table:
!37,20,L0,L1,L2,L3,L4,L5,L6,L7,L8,R7,R6,R5,R4,R3X,R2X,R1X;

;some global R-Registers
$NWW		$R4;		State of interrupt system
$R37		$R37;		Used by MRT, interval timer and EIA
$MTEMP		$R25;		Public temporary R-Register

;The Display Controller

; its R-Registers:
$CBA		$R22;
$AECL		$R23;
$SLC		$R24;
$HTAB		$R26;
$YPOS		$R27;
$DWA		$R30;
$CURX		$R20;
$CURDATA	$R21;

; its task specific functions:
$EVENFIELD	$L024010,000000,000000; F2 = 10 DHT DVT
$SETMODE	$L024011,000000,000000; F2 = 11 DHT
$DDR		$L026010,000000,124100; F2 = 10 DWT

!1,2,DVT1,DVT11;
!1,2,MOREB,NOMORE;
!1,2,NORMX,HALFX;
!1,2,NODD,NEVEN;
!1,2,DHT0,DHT1;
!1,2,NORMODE,HALFMODE;
!1,2,DWTZ,DWTY;
!1,2,DOTAB,NOTAB;
!1,2,XNOMORE,DOMORE;

;Display Vertical Task

DVT:	MAR← L← DASTART+1;
	CBA← L, L← 0;
	CURDATA← L;
	SLC← L;
	T← MD;			CAUSE A VERTICAL FIELD INTERRUPT
	L← NWW OR T;
	MAR← CURLOC;		SET UP THE CURSOR
	NWW← L, T← 0-1;
	L← MD XOR T;		HARDWARE EXPECTS X COMPLEMENTED
	T← MD, EVENFIELD;
	CURX← L, :DVT1;

DVT1:	L← BIAS-T-1, TASK, :DVT2;	BIAS THE Y COORDINATE 
DVT11:	L← BIAS-T, TASK;

DVT2:	YPOS← L, :DVT;

;Display Horizontal Task.
;11 cycles if no block change, 17 if new control block.

DHT:	MAR← CBA-1;
	L← SLC -1, BUS=0;
	SLC← L, :DHT0;

DHT0:	T← 37400;		MORE TO DO IN THIS BLOCK
	SINK← MD;
	L← T← MD AND T, SETMODE;
	HTAB← L LCY 8, :NORMODE;

NORMODE:L← T← 377 . T;
	AECL← L, :REST;	

HALFMODE: L← T←  377 . T;
	AECL← L, :REST, T← 0;

REST:	L← DWA + T,TASK;	INCREMENT DWA BY 0 OR NWRDS
NDNX:	DWA← L, :DHT;

DHT1:	L← T← MD+1, BUS=0;
	CBA← L, MAR← T, :MOREB;

NOMORE:	BLOCK, :DNX;
MOREB:	T← 37400;
	L← T← MD AND T, SETMODE;
	MAR← CBA+1, :NORMX, EVENFIELD;

NORMX:	HTAB← L LCY 8, :NODD;
HALFX:	HTAB← L LCY 8, :NEVEN;

NODD:	L←T← 377 . T;
	AECL← L, :XREST;	ODD FIELD, FULL RESOLUTION

NEVEN:	L← 377 AND T;		EVEN FIELD OR HALF RESOLUTION
	AECL←L, T←0;

XREST:	L← MD+T;
	T←MD-1;
DNX:	DWA←L, L←T, TASK;
	SLC←L, :DHT;

;Display Word Task

DWT:	T← DWA;
	T←-3+T+1;
	L← AECL+T,BUS=0,TASK;	AECL CONTAINS NWRDS AT THIS TIME
	AECL←L, :DWTZ;

DWTY:	BLOCK;
	TASK, :DWTF;

DWTZ:	L←HTAB-1, BUS=0,TASK;
	HTAB←L, :DOTAB;

DOTAB:	DDR←0, :DWTZ;
NOTAB:	MAR←T←DWA;
	L←AECL-T-1;
	ALUCY, L←2+T;
	DWA←L, :XNOMORE;

DOMORE:	DDR←MD, TASK;
	DDR←MD, :NOTAB;

XNOMORE:DDR← MD, BLOCK;
	DDR← MD, TASK;

DWTF:	:DWT;

;Alto Ethernet Microcode, Version III, Boggs and Metcalfe

;4-way branches using NEXT6 and NEXT7
!17,20,EIFB00,EODOK,EOEOK,ENOCMD,EIFB01,EODPST,EOEPST,EOREST,EIFB10,EODCOL,EOECOL,EIREST,EIFB11,EODUGH,EOEUGH,ERBRES;

;2-way branches using NEXT7
;EOCDW1, EOCDWX, and EIGO are all related.  Be careful!
!7,10,EIDOK,EIFOK,,EOCDW1,EIDPST,EIFBAD,EOCDWX,EIGO;

;Miscellaenous address constraints
!7,10,,EOCDW0,EODATA,,,EOCDRS,EIDATA,EPOST;
!1,1,EIFB1;
!1,1,EIFRST;

;2-way branches using NEXT9
!1,2,EOINPR,EOINPN;
!1,2,EODMOR,EODEND;
!1,2,EOLDOK,EOLDBD;
!1,2,EIDMOR,EIDFUL;
!1,2,EIFCHK,EIFPRM;
!1,2,EOCDWT,EOCDGO;
!1,2,ECNTOK,ECNTZR;
!1,2,EIFIGN,EISET;
!1,2,EIFNBC,EIFBC;

;R Memory Locations

$ECNTR	$R12;	Remaining words in buffer
$EPNTR	$R13;	points BEFORE next word in buffer

;Ethernet microcode Status codes

$ESIDON	$377;	Input Done
$ESODON	$777;	Output Done
$ESIFUL	$1377;	Input Buffer full - words lost from tail of packet
$ESLOAD	$1777;	Load location overflowed
$ESCZER	$2377;	Zero word count for input or output command
$ESABRT	$2777;	Abort - usually caused by reset command
$ESNEVR	$3377;	Never Happen - Very bad if it does

;Main memory locations in page 1 reserved for Ethernet

$EPLOC	$600;	Post location
$EBLOC	$601;	Interrupt bit mask

$EELOC	$602;	Ending count location
$ELLOC	$603;	Load location

$EICLOC	$604;	Input buffer Count
$EIPLOC	$605;	Input buffer Pointer

$EOCLOC	$606;	Output buffer Count
$EOPLOC	$607;	Output buffer Pointer

$EHLOC	$610;	Host Address

;Function Definitions

$EIDFCT	$L000000,014004,000100;	BS = 4,  Input data
$EILFCT	$L016013,070013,000100;	F1 = 13, Input Look
$EPFCT	$L016014,070014,000100;	F1 = 14, Post
$EWFCT	$L016015,000000,000000;	F1 = 15, Wake-Up

$EODFCT	$L026010,000000,124000;	F2 = 10, Output data
$EOSFCT	$L024011,000000,000000;	F2 = 11, Start output
$ERBFCT	$L024012,000000,000000;	F2 = 12, Rest branch
$EEFCT	$L024013,000000,000000;	F2 = 13, End of output
$EBFCT	$L024014,000000,000000;	F2 = 14, Branch
$ECBFCT	$L024015,000000,000000;	F2 = 15, Countdown branch
$EISFCT	$L024016,000000,000000;	F2 = 16, Start input

; - Whenever a label has a pending branch, the list of possible
;   destination addresses is shown in brackets in the comment field.
; - Special functions are explained in a comment near their first ;use.
; - To avoid naming conflicts, all labels and special functions
;   have "E" as the first letter.

;Top of Ethernet Task loop

;Ether Rest Branch Function - ERBFCT
;merge ICMD and OCMD Flip Flops into NEXT6 and NEXT7
;ICMD and OCMD are set from AC0 [14:15] by the SIO instruction
;	00  neither 
;	01  OCMD - Start output
;	10  ICMD - Start input
;	11  Both - Reset interface

;in preparation for a hack at EIREST, zero EPNTR

EREST:	L← 0,ERBFCT;		What's happening ?
	EPNTR← L,:ENOCMD;	[ENOCMD,EOREST,EIREST,ERBRES]

ENOCMD:	L← ESNEVR,:EPOST;	Shouldn't happen
ERBRES:	L← ESABRT,:EPOST;	Reset Command

;Post status and halt.  Microcode status in L.
;Put microstatus,,hardstatus in EPLOC, merge c(EBLOC) into NWW.
;Note that we write EPLOC and read EBLOC in one operation

;Ether Post Function - EPFCT.  Gate the hardware status
;(LOW TRUE) to Bus [10:15], reset interface.

EPOST:	MAR← EELOC;
	EPNTR← L,TASK;		Save microcode status in EPNTR
	MD← ECNTR;		Save ending count

	MAR← EPLOC;		double word reference
	T← NWW;
	MD← EPNTR,EPFCT;	BUS AND EPNTR with Status
	L← MD OR T,TASK;	NWW OR c(EBLOC)
	NWW← L,:EREST;		Done.  Wait for next command

;This is a subroutine called from both input and output (EOCDGO
;and EISET).  The return address is determined by testing ECBFCT,
;which will branch if the buffer has any words in it, which can
;only happen during input.

ESETUP:	NOP;
	L← MD,BUS=0;		check for zero length
	T← MD-1,:ECNTOK;	[ECNTOK,ECNTZR] start-1

ECNTZR:	L← ESCZER,:EPOST;	Zero word count.  Abort

;Ether Countdown Branch Function - ECBFCT.
;NEXT7 = Interface buffer not empty.

ECNTOK:	ECNTR← L,L← T,ECBFCT,TASK;
	EPNTR← L,:EODATA;	[EODATA,EIDATA]

;Ethernet Input

;It turns out that starting the receiver for the first time and
;restarting it after ignoring a packet do the same things.

EIREST:	:EIFIGN;		Hack

;Address filtering code.

;When the first word of a packet is available in the interface
;buffer, a wakeup request is generated.  The microcode then
;decides whether to accept the packet.  Decision must be reached
;before the buffer overflows, within about 14*5.44 usec.
;if EHLOC is zero, machine is 'promiscuous' - accept all packets
;if destination byte is zero, it is a 'broadcast' packet, accept.
;if destination byte equals EHLOC, packet is for us, accept.

;EIFRST is really a subroutine that can be called from EIREST
;or from EIGO, output countdown wait.  If a packet is ignored
;and EPNTR is zero, EIFRST loops back and waits for more
;packets, else it returns to the countdown code.

;Ether Branch Function - EBFCT
;NEXT7 = IDL % OCMD % ICMD % OUTGONE % INGONE (also known as POST)
;NEXT6 = COLLision - Can't happen during input

EIFRST:	MAR← EHLOC;		Get Ethernet address
	T← 377,EBFCT;		What's happening?
	L← MD AND T,BUS=0,:EIFOK;[EIFOK,EIFBAD] promiscuous?

EIFOK:	MTEMP← LLCY8,:EIFCHK;	[EIFCHK,EIFPRM] Data wakeup

EIFBAD:	ERBFCT,TASK,:EIFB1;	[EIFB1] POST wakeup; xCMD FF set?
EIFB1:	:EIFB00;		[EIFB00,EIFB01,EIFB10,EIFB11]

EIFB00:	:EIFIGN;		IDL or INGONE, restart rcvr
EIFB01:	L← ESABRT,:EPOST;	OCMD, abort
EIFB10:	L← ESABRT,:EPOST;	ICMD, abort
EIFB11:	L← ESABRT,:EPOST;	ICMD and OCMD, abort

EIFPRM:	TASK,:EIFBC;		Promiscuous. Accept

;Ether Look Function - EILFCT.  Gate the first word of the 
;data buffer to the bus, but do not increment the read pointer.

EIFCHK:	L← T← 177400,EILFCT;	Mask off src addr byte (BUS AND)
	L← MTEMP-T,SH=0;	Broadcast?
	SH=0,TASK,:EIFNBC;	[EIFNBC,EIFBC] Our Address?

EIFNBC:	:EIFIGN;		[EIFIGN,EISET]

EIFBC:	:EISET;			[EISET] Enter input main loop

;Ether Input Start Function - EISFCT.  Start receiver.  Interface
;will generate a data wakeup when the first word of the next
;packet arrives, ignoring any packet currently passing.

EIFIGN:	SINK← EPNTR,BUS=0,EPFCT;Reset; Called from output?
	EISFCT,TASK,:EOCDWX;	[EOCDWX,EIGO] Restart rcvr

EOCDWX:	EWFCT,:EOCDWT;		Return to countdown wait loop

EISET:	MAR← EICLOC,:ESETUP;	Double word reference

;Input Main Loop

;Ether Input Data Function - EIDFCT.  Gate a word of data to
;the bus from the interface data buffer, increment the read ptr.
;		* * * * * W A R N I N G * * * * *
;The delay from decoding EIDFCT to gating data to the bus is
;marginal, so this loop causes SysClk to stop for one cycle by
;referencing MD in cycle 4.

EIDATA:	L← MAR← EPNTR+1,EBFCT;	What's happening?
	T← ECNTR-1,BUS=0,:EIDOK;[EIDOK,EIDPST] word count zero?
EIDOK:	EPNTR← L,L← T,:EIDMOR;	[EIDMOR,EIDFUL]
EIDMOR:	MD← EIDFCT,TASK;	Read a word from interface
	ECNTR← L,:EIDATA;

EIDPST:	L← ESIDON,:EPOST;	[EPOST] Presumed to be INGONE

EIDFUL:	L← ESIFUL,:EPOST;	Input buffer overrun

;Ethernet output

;It is possible to get here due to a collision.  If a collision
;happened, the interface was reset (EPFCT) to shut off the
;transmitter.  EOSFCT is issued to guarantee more wakeups while
;generating the countdown.  When this is done, the interface is
;again reset, without really doing an output.

EOREST:	MAR← ELLOC;		Get load
	L← R37;			Use clock as random # gen
	EPNTR← LLSH1;		Use bits [2:9]
	L← MD,EOSFCT;		L← current load
	SH<0,ECNTR← L;		Overflowed?
	MTEMP← LLSH1,:EOLDOK;	[EOLDOK,EOLDBD]

EOLDBD:	L← ESLOAD,:EPOST;	Load overlow

EOLDOK:	MAR← ELLOC;		Write updated load
	L← MTEMP+1;
	MTEMP← L,TASK;
	MD← MTEMP,:EORST1;	New load = (old lshift 1) + 1

EORST1:	L← EPNTR;		Continue making random #
	EPNTR← LLSH1;
	T← 177400;
	L← EPNTR AND T,TASK;
	EPNTR← LLCY8,:EORST2;

;At this point, EPNTR has 0,,random number, ENCTR has old load.

EORST2:	MAR← EICLOC;		Has an input buffer been set up?
	T← ECNTR;
	L← EPNTR AND T;		L← Random & Load
	SINK← MD,BUS=0;
	ECNTR← L,SH=0,EPFCT,:EOINPR;[EOINPR,EOINPN] 

EOINPR:	EISFCT,:EOCDWT;		[EOCDWT,EOCDGO] Enable in under out

EOINPN:	:EOCDWT;		[EOCDWT,EOCDGO] No input.

;Countdown wait loop.  MRT will wake generate a wakeup every
;37 usec which will decrement ECNTR.  When it is zero, start
;the transmitter.

;Ether Wake Function - EWFCT.  Sets a flip flop which will cause
;a wakeup to this task the next time MRT wakes up (every 37 usec).
;Wakeup is cleared when Ether task next runs.  EWFCT must be
;issued in the instruction AFTER a task.

EOCDWT:	L← 177400,EBFCT;	What's happening?
	EPNTR← L,ECBFCT,:EOCDW0;[EOCDW0,EOCDRS] Packet coming in?
EOCDW0:	L← ECNTR-1,BUS=0,TASK,:EOCDW1; [EOCDW1,EIGO]
EOCDW1:	ECNTR← L,EWFCT,:EOCDWT;	[EOCDWT,EOCDGO]

EOCDRS:	L← ESABRT,:EPOST;	[EPOST] POST event

EIGO:	:EIFRST;		[EIFRST] Input under output

;Output main loop setup

EOCDGO:	MAR← EOCLOC;		Double word reference
	EPFCT;			Reset interface
	EOSFCT,:ESETUP;		Start Transmitter

;Ether Output Start Function - EOSFCT.  The interface will generate
;a burst of data requests until the interface buffer is full or the
;memory buffer is empty, wait for silence on the Ether, and begin
;transmitting.  Thereafter it will request a word every 5.44 us.

;Ether Output Data Function - EODFCT.  Copy the bus into the
;interface data buffer, increment the write pointer, clears wakeup
;request if the buffer is now nearly full (one slot available).

;Output main loop

EODATA:	L← MAR← EPNTR+1,EBFCT;	What's happening?
	T← ECNTR-1,BUS=0,:EODOK; [EODOK,EODPST,EODCOL,EODUGH]
EODOK:	EPNTR← L,L← T,:EODMOR;	[EODMOR,EODEND]
EODMOR:	ECNTR← L,TASK;
	EODFCT← MD,:EODATA;	Output word to transmitter

EODPST:	L← ESABRT,:EPOST;	[EPOST] POST event

EODCOL:	EPFCT,:EOREST;		[EOREST] Collision

EODUGH:	L← ESABRT,:EPOST;	[EPOST] POST + Collision

;Ether EOT Function - EEFCT.  Stop generating output data wakeups,
;the interface has all of the packet.  When the data buffer runs
;dry, the interface will append the CRC and then generate an
;OUTGONE post wakeup.

EODEND:	EEFCT;			Disable data wakeups
	TASK;			Wait for EEFCT to take
	:EOEOT;			Wait for Outgone

;Output completion.  We are waiting for the interface buffer to
;empty, and the interface to generate an OUTGONE Post wakeup.

EOEOT:	EBFCT;			What's happening?
	:EOEOK;			[EOEOK,EOEPST,EOECOL,EOEUGH]

EOEOK:	L← ESNEVR,:EPOST;	Runaway Transmitter. Never Never.

EOEPST:	L← ESODON,:EPOST;	POST event.  Output done

EOECOL:	EPFCT,:EOREST;		Collision

EOEUGH:	L← ESABRT,:EPOST;	POST + Collision


;Memory Refresh Task,
;Mouse Handler,
;EIA Handler,
;Interval Timer,
;Calender Clock, and
;part of the cursor.

!17,20,TX0,TX6,TX3,TX2,TX8,TX5,TX1,TX7,TX4,,,,,,,;
!1,2,DOTIMER,NOTIMER;
!1,2,NOTIMERINT,TIMERINT;
!1,2,DOCUR,NOCUR;
!1,2,SHOWC,WAITC;
!1,2,SPCHK,NOSPCHK;

!1,2,NOCLK,CLOCK;
!1,1,DTODD;
!1,1,MRTLAST;
!1,2,CNOTLAST,CLAST;

$CLOCKTEMP$R11;

MRT:	SINK← MOUSE, BUS;	MOUSE DATA IS ANDED WITH 17B
MRTA:	L← T← -2, :TX0;		DISPATCH ON MOUSE CHANGE
TX0:	L← T← R37 AND NOT T;	CHECK FOR INTERVAL TIMER/EIA
	SH=0, L←T← 77+T+1;	
	:DOTIMER, R37← L, ALUCY;	
NOTIMER:L← CURX, :NOCLK;
NOCLK:	T← REFMSK, SH=0;
	MAR← R37 AND T, :DOCUR;
NOCUR:	CURDATA← L, TASK;
MRTLAST:CURDATA← L, :MRT;	END OF MAIN LOOP


DOTIMER:MAR←EIALOC;		INTERVAL TIMER/EIA INTERFACE
DTODD:	L←2 AND T;
	SH=0, L←T←BIAS.T;
	CURDATA←L, :SPCHK;	CURDATA←CURRENT TIME WITHOUT CONTROL BITS

SPCHK:	SINK←MD, BUS=0, TASK;	CHECK FOR EIA LINE SPACING
SPIA:	:NOTIMERINT, CLOCKTEMP←L;

NOSPCHK:L←MD;			CHECK FOR TIME=NOW
	MAR←TRAPDISP-1;		CONTAINS TIME AT WHICH INTERRUPT SHOULD HAPPEN
	MTEMP←L;		IF INTERRUPT IS CAUSED, LINE STATE WILL BE STORED
	L← MD-T;
	SH=0, TASK, L←MTEMP, :SPIA;

TIMERINT: MAR← ITQUAN;		STORE THE THING IN CLOCKTEMP AT ITQUAN
	L← CURDATA;
	R37← L;
	T←NWW;			AND CAUSE AN INTERRUPT ON THE CHANNELS 
	MD←CLOCKTEMP;		SPECIFIED BY ITQUAN+1
	L←MD OR T, TASK;
	NWW←L;

NOTIMERINT: SINK←CURDATA, BUS=0, :NOTIMER;

CLOCK:	MAR← CLOCKLOC;		R37 OVERFLOWED. UPDATE CLOCK
	NOP;
	L← MD+1;
	MAR← CLOCKLOC;
	MTEMP← L, TASK;
	MD← MTEMP, :NOTIMER;

DOCUR:	L← T← YPOS;		CHECK FOR VISIBLE CURSOR ON THIS SCAN
	SH < 0, L← 20-T-1;
	SH<0, L← 2+T, :SHOWC;

WAITC:	YPOS← L, L← 0, TASK, :MRTLAST;
SHOWC:	MAR← CLOCKLOC+T+1, :CNOTLAST;

CNOTLAST: T← CURX, :CURF;
CLAST:	T← 0;
CURF:	YPOS← L, L← T;
	CURX← L;
	L← MD, TASK;
	CURDATA← L, :MRT;


;AFTER THIS DISPATCH, T WILL CONTAIN XCHANGE, L WILL CONTAIN YCHANGE-1

TX1:	L← T← ONE +T, :M00;		Y=0, X=1
TX2:	L← T← ALLONES, :M00;		Y=0, X=-1
TX3:	L← T← 0, :M00;			Y=1, X= 0
TX4:	L← T← ONE AND T, :M00;		Y=1, X=1
TX5:	L← T← ALLONES XOR T, :M00;	Y=1, X=-1
TX6:	T← 0, :M00;			Y= -1, X=0
TX7:	T← ONE, :M00;			Y= -1, X=1
TX8:	T← ALLONES, :M00;		Y= -1, X= -1

M00:	MAR← MOUSELOC;			START THE FETCH OF THE COORDINATES
	MTEMP← L;			YCHANGE -1
	L← MD+ T;			X+ XCHANGE
	T← MD;				Y
	MAR← MOUSELOC;			NOW RESTORE THE UPDATED COORDINATES
	T← MTEMP+ T+1;			Y+ (YCHANGE-1) + 1
	MTEMP← L, L← T;
	MD← MTEMP;
	MAR← MOUSELOC+1;
	MTEMP← L, TASK;
	MD← MTEMP, :MRTA;


;CURSOR TASK

;Cursor task specific functions
$XPREG		$L026010,000000,124000; F2 = 10
$CSR		$L026011,000000,124000; F2 = 11

CURT:	XPREG← CURX, TASK;
	CSR← CURDATA, :CURT;


;PREDEFINITION FOR PARITY TASK
!7,10,PR0,,PR2,PR3,PR4,PR5,PR6,PR7;

;NOVA EMULATOR

$SAD	$R5;
$PC	$R6;		USED BY MEMORY INIT


!7,10,Q0,Q1,Q2,Q3,Q4,Q5,Q6,Q7;
!1,2,FINSTO,INCPC;
!1,2,EReRead,FINJMP;	***X21 addition.
!1,2,EReadDone,EContRead;	***X21 addition.
!1,2,EtherBoot,DiskBoot;	***X21 addition.

NOVEM:	IR←L←MAR←0, :INXB,SAD← L;  LOAD SAD TO ZERO THE BUS. STORE PC AT 0
Q0:	L← ONE, :INXA;	EXECUTED TWICE
Q1:	L← TOTUWC, :INXA;
Q2:	L←402, :INXA;	FIRST READ HEADER INTO 402, THEN
Q3:	L← 402, :INXA;	STORE LABEL AT 402
Q4:	L← ONE, :INXA;	STORE DATA PAGE STARTING AT 1
Q5:	L←377+1, :INXE;	Store Ethernet Input Buffer Length ***X21.
Q6:	L←ONE, :INXE;	Store Ethernet Input Buffer Pointer ***X21.
Q7:	MAR← DASTART;		CLEAR THE DISPLAY POINTER
	T← BIAS;
	L← R37 AND T;
	R37← L;
	MD← 0;
	MAR← 177034;			FETCH KEYBOARD
	L← 100000;
	NWW← L, T← 0-1;
	L← MD XOR T, BUSODD;	*** X21 change.
	MAR← BDAD, :EtherBoot;	[EtherBoot, DiskBoot]  *** X21 change.
		; BOOT DISK ADDRESS GOES IN LOCATION 12
DiskBoot: SAD← L, L← 0+1;
	MD← SAD;
	MAR← KBLKADR, :FINSTO;


; Ethernet boot section added in X21.
$NegBreathM1	$177175;
$EthNovaGo	$3;	First data location of incoming packet

EtherBoot: L←EthNovaGo, :EReRead; [EReRead, FINJMP]

EReRead:MAR← EHLOC;	Set the host address to 377 for breath packets
	TASK;
	MD← 377;

	MAR← EPLOC;	Zero the status word and start 'er up
	SINK← 2, STARTF;
	MD ← 0;

EContRead: MAR← EPLOC;	See if status is still 0
	T← 377;		Status for correct read
	L← MD XOR T, TASK, BUS=0;
	SAD← L, :EReadDone; [EReadDone, EContRead]

EReadDone: MAR← 2;	Check the packet type
	T← NegBreathM1;	-(Breath-of-life)-1
	T←MD+T+1;
	L←SAD OR T;
	SH=0, :EtherBoot;


; SUBROUTINE USED BY INITIALIZATION TO SET UP BLOCKS OF MEMORY
$EIOffset	$576;

INXA:	T←ONE, :INXCom;	***X21 change.
INXE:	T←EIOffset, :INXCom;		***X21 addition.

INXCom: MAR←T←IR← SAD+T;	*** X21 addition.
	PC← L, L← 0+T+1;	*** X21 change.
INXB:	SAD← L;
	SINK← DISP, BUS,TASK;
	MD← PC, :Q0;


;REGISTERS USED BY NOVA EMULATOR 
$AC0	$R3;	AC'S ARE BACKWARDS BECAUSE THE HARDWARE SUPPLIES THE
$AC1	$R2;	COMPLEMENT ADDRESS WHEN ADDRESSING FROM IR
$AC2	$R1;
$AC3	$R0;
$XREG	$R7;


;PREDEFINITIONS FOR NOVA

!17,20,GETAD,G1,G2,G3,G4,G5,G6,G7,G10,G11,G12,G13,G14,G15,G16,G17;
!17,20,XCTAB,XJSR,XISZ,XDSZ,XLDA,XSTA,CONVERT,,,,,,,,,;
!3,4,SHIFT,SH1,SH2,SH3;
!1,2,MAYBE,NOINT;
!1,2,DOINT,DIS0;
!1,2,SOMEACTIVE,NOACTIVE;
!1,2,IEXIT,NIEXIT;
!17,1,ODDCX;
!1,2,EIR0,EIR1;
!7,1,INTCODE;
!1,2,INTSOFF,INTSON;***X21 addition for DIRS
!7,10,EMCYCRET,RAMCYCRET,CYX2,CYX3,CYX4,CONVCYCRET,,;
!7,2,MOREBLT,FINBLT;
!1,2,DOIT,DISABLED;

; ALL INSTRUCTIONS RETURN TO START WHEN DONE

START:	T← MAR←PC+SKIP;
START1:	L← NWW, BUS=0;	BUS# 0 MEANS DISABLED OR SOMETHING TO DO
	:MAYBE, SH<0, L← 0+T+1;  	SH<0 MEANS DISABLED
MAYBE:	PC← L, L← T, :DOINT;
NOINT:	PC← L, :DIS0;

DOINT:	MAR← WWLOC, :INTCODE;	TRY TO CAUSE AN INTERRUPT

;DISPATCH ON FUNCTION FIELD IF ARITHMETIC INSTRUCTION,
;OTHERWISE ON INDIRECT BIT AND INDEX FIELD

DIS0:	L← T← IR← MD;	SKIP CLEARED HERE

;DISPATCH ON SHIFT FIELD IF ARITHMETIC INSTRUCTION,
;OTHERWISE ON THE INDIRECT BIT OR IR[3-7]

DIS1:	T← ACSOURCE, :GETAD;

;GETAD MUST BE 0 MOD 20
GETAD: T← 0, :DOINS;			PAGE 0
G1:	T← PC -1, :DOINS;		RELATIVE
G2:	T← AC2, :DOINS;			AC2 RELATIVE
G3:	T← AC3, :DOINS;			AC3 RELATIVE
G4:	T← 0, :DOINS;			PAGE 0 INDIRECT
G5:	T← PC -1, :DOINS;		RELATIVE INDIRECT
G6:	T← AC2, :DOINS;			AC2 RELATIVE INDIRECT
G7:	T← AC3, :DOINS;			AC3 RELATIVE INDIRECT
G10:	L← 0-T-1, TASK, :SHIFT;		COMPLEMENT
G11:	L← 0-T, TASK, :SHIFT;		NEGATE
G12:	L← 0+T, TASK, :SHIFT;		MOVE
G13:	L← 0+T+1, TASK, :SHIFT;		INCREMENT
G14:	L← ACDEST-T-1, TASK, :SHIFT;	ADD COMPLEMENT
G15:	L← ACDEST-T, TASK, :SHIFT;	SUBTRACT
G16:	L← ACDEST+T, TASK, :SHIFT;	ADD
G17:	L← ACDEST AND T, TASK, :SHIFT;

SHIFT:	DNS← L LCY 8, :START; 	SWAP BYTES
SH1:	DNS← L RSH 1, :START;	RIGHT 1
SH2:	DNS← L LSH 1, :START;	LEFT 1
SH3:	DNS← L, :START;		NO SHIFT

DOINS:	L← DISP + T, TASK, :SAVAD, IDISP;	DIRECT INSTRUCTIONS
DOIND:	L← MAR← DISP+T;				INDIRECT INSTRUCTIONS
	XREG← L;
	L← MD, TASK, IDISP, :SAVAD;

BRI:	L← MAR← PCLOC	;INTERRUPT RETURN BRANCH
BRI0:	T← 77777;
	L← NWW AND T, SH < 0;
	NWW← L, :EIR0;	BOTH EIR AND BRI MUST CHECK FOR INTERRUPT
;			REQUESTS WHICH MAY HAVE COME IN WHILE
;			INTERRUPTS WERE OFF

EIR0:	L← MD, :DOINT;
EIR1:	L← PC, :DOINT;

;***X21 addition
; DIRS - 61013 - Disable Interrupts and Skip if they were On
DIRS:	T←100000;
	L←NWW AND T;
	L←PC+1, SH=0;

;DIR - 61000 - Disable Interrupts
DIR:	T← 100000, :INTSOFF;
INTSOFF: L← NWW OR T, TASK, :INTZ;

INTSON: PC←L, :INTSOFF;

;EIR - 61001 - Enable Interrupts
EIR:	L← 100000, :BRI0;

;SIT - 61007 - Start Interval Timer by ORing AC0 into R37
SIT:	T← AC0;
	L← R37 OR T, TASK;
	R37← L, :START;

FINJSR:	L← PC;
	AC3← L, L← T, TASK;
FINJMP:	PC← L, :START;
SAVAD:	SAD← L, :XCTAB;

;JSRII - 64400 - JSR double indirect, PC relative.  Must have X=1 in opcode
;JSRIS - 65000 - JSR double indirect, AC2 relative.  Must have X=2 in opcode
JSRII:	MAR← DISP+T;	FIRST LEVEL
	IR← JSRCX;	<JSR 0>
	T← MD, :DOIND;	THE IR← INSTRUCTION WILL NOT BRANCH	

;TRAP ON UNIMPLEMENTED OPCODES.  SAVES  PC AT
;TRAPPC, AND DOES A JMP@ TRAPVEC ! OPCODE.
TRAP:	XREG← L LCY 8;	THE INSTRUCTION
TRAP1:	MAR← TRAPPC;***X13 CHANGE: TAG 'TRAP1' ADDED
	IR← T← 37;
	T← XREG.T;
	T← TRAPCON+T+1;		T NOW CONTAINS 471+OPCODE
	MD← PC, :DOIND;		THIS WILL DO JMP@ 530+OPCODE


;***X21 CHANGE: ADDED TAG RAMTRAP
RAMTRAP: SWMODE, :TRAP;

; Parameterless operations come here for dispatch.

!1,2,NPNOTRAP,NPTRAP;

NOPAR:	XREG←L LCY 8;	***X21 change. Checks < 25.
	T←25;		***X21. Greatest defined op is 24.
	L←DISP-T;
	ALUCY;
	SINK←DISP, SINK←X37, BUS, TASK, :NPNOTRAP;

NPNOTRAP: :DIR;

NPTRAP: :TRAP1;

;***X21 addition for debugging w/ expanded DISP Prom
U5:	:RAMTRAP;
U6:	:RAMTRAP;
U7:	:RAMTRAP;

;***X21 change. Traps numbered instead of lettered.
V15:	:TRAP1;		;Alto II DREAD
V16:	:TRAP1;		;Alto II DWRITE
V17:	:TRAP1;		;Alto II DEXCH
V22:	:TRAP1;		;Alto II DIOG1
V23:	:TRAP1;		;Alto II DIOG2

;MAIN INSTRUCTION TABLE.  GET HERE:
;		(1) AFTER AN INDIRECTION
;		(2) ON DIRECT INSTRUCTIONS 

XCTAB:	L← SAD, TASK, :FINJMP;	JMP
XJSR:	T← SAD, :FINJSR;	JSR
XISZ:	MAR← SAD, :ISZ1;	ISZ
XDSZ:	MAR← SAD, :DSZ1;	DSZ
XLDA:	MAR← SAD, :FINLOAD;	LDA 0-3
XSTA:	MAR← SAD;		/*NORMAL
	L← ACDEST, :FINSTO;	/*NORMAL

;	BOUNDS-CHECKING VERSION OF STORE
;	SUBST ";**<CR>" TO "<CR>;**" TO ENABLE THIS CODE:
;**	!1,2,XSTA1,XSTA2;
;**	!1,2,DOSTA,TRAPSTA;
;**XSTA:	MAR← 10;	LOCS 10,11 CONTAINS HI,LO BOUNDS
;**	T← SAD
;**	L← MD-T;	HIGHBOUND-ADDR
;**	T← MD, ALUCY;
;**	L← SAD-T, :XSTA1;	ADDR-LOWBOUND
;**XSTA1:	TASK, :XSTA3;
;**XSTA2:	ALUCY, TASK;
;**XSTA3:	L← 177, :DOSTA;
;**TRAPSTA:	XREG← L, :TRAP1;	CAUSE A SWAT
;**DOSTA:	MAR← SAD;	DO THE STORE NORMALLY
;**	L← ACDEST, :FINSTO;
;**

DSZ1:	T← ALLONES, :FINISZ;
ISZ1:	T← ONE, :FINISZ;

FINSTO:	SAD← L,TASK;
FINST1:	MD←SAD, :START;

FINLOAD: NOP;
LOADX:	L← MD, TASK;
LOADD:	ACDEST← L, :START;

FINISZ:	L← MD+T;
	MAR← SAD, SH=0;
	SAD← L, :FINSTO;

INCPC:	L← PC+1;
	PC← L, TASK, :FINST1;

;DIVIDE.  THIS DIVIDE IS IDENTICAL TO THE NOVA DIVIDE EXCEPT THAT
;IF THE DIVIDE CANNOT BE DONE, THE INSTRUCTION FAILS TO SKIP, OTHERWISE
;IT DOES.  CARRY IS UNDISTURBED.

!1,2,DODIV,NODIV;
!1,2,DIVL,ENDDIV;
!1,2,NOOVF,OVF;
!1,2,DX0,DX1;
!1,2,NOSUB,DOSUB;

DIV:	T← AC2;
DIVX:	L← AC0 - T;	DO THE DIVIDE ONLY IF AC2>AC0
	ALUCY, TASK, SAD← L, L← 0+1;
	:DODIV, SAD← L LSH 1;		SAD← 2.  COUNT THE LOOP BY SHIFTING

NODIV:	:FINBLT;		***X21 change.
DODIV:	L← AC0, :DIV1;

DIVL:	L← AC0;
DIV1:	SH<0, T← AC1;	WILL THE LEFT SHIFT OF THE DIVIDEND OVERFLOW?
	:NOOVF, AC0← L MLSH 1, L← T← 0+T;	L← AC1, T← 0

OVF:	AC1← L LSH 1, L← 0+INCT, :NOV1;		L← 1. SHIFT OVERFLOWED
NOOVF:	AC1← L LSH 1 , L← T;			L← 0. SHIFT OK

NOV1:	T← AC2, SH=0;
	L← AC0-T, :DX0;

DX1:	ALUCY;		DO THE TEST ONLY IF THE SHIFT DIDN'T OVERFLOW.  IF 
;			IT DID, L IS STILL CORRECT, BUT THE TEST WOULD GO
;			THE WRONG WAY.
	:NOSUB, T← AC1;

DX0:	:DOSUB, T← AC1;

DOSUB:	AC0← L, L← 0+INCT;	DO THE SUBTRACT
	AC1← L;			AND PUT A 1 IN THE QUOTIENT

NOSUB:	L← SAD, BUS=0, TASK;
	SAD← L LSH 1, :DIVL;

ENDDIV:	L← PC+1, TASK, :DOIT; ***X21 change. Skip if divide was done.


;MULTIPLY.  THIS IS AN EXACT EMULATION OF NOVA HARDWARE MULTIPLY.
;AC2 IS THE MULTIPLIER, AC1 IS THE MULTIPLICAND.
;THE PRODUCT IS IN AC0 (HIGH PART), AND AC1 (LOW PART).
;PRECISELY: AC0,AC1 ← AC1*AC2  + AC0

!1,2,DOMUL,NOMUL;
!1,2,MPYL,MPYA;
!1,2,NOADDIER,ADDIER;
!1,2,NOSPILL,SPILL;
!1,2,NOADDX,ADDX;
!1,2,NOSPILLX,SPILLX;


MUL:	L← AC2-1, BUS=0;
MPYX:	XREG←L,L← 0, :DOMUL;	GET HERE WITH AC2-1 IN L. DON'T MUL IF AC2=0
DOMUL:	TASK, L← -10+1;
	SAD← L;		COUNT THE LOOP IN SAD

MPYL:	L← AC1, BUSODD;
	T← AC0, :NOADDIER;

NOADDIER: AC1← L MRSH 1, L← T, T← 0, :NOSPILL;
ADDIER:	L← T← XREG+INCT;
	L← AC1, ALUCY, :NOADDIER;

SPILL:	T← ONE;
NOSPILL: AC0← L MRSH 1;
	L← AC1, BUSODD;
	T← AC0, :NOADDX;

NOADDX:	AC1← L MRSH 1, L← T, T← 0, :NOSPILLX;
ADDX:	L← T← XREG+ INCT;
	L← AC1,ALUCY, :NOADDX;

SPILLX:	T← ONE;
NOSPILLX: AC0← L MRSH 1;
	L← SAD+1, BUS=0, TASK;
	SAD← L, :MPYL;

NOMUL:	T← AC0;
	AC0← L, L← T, TASK;	CLEAR AC0
	AC1← L;			AND REPLACE AC1 WITH AC0
MPYA:	:FINBLT;		***X21 change.

;CYCLE AC0 LEFT BY DISP MOD 20B, UNLESS DISP=0, IN WHICH
;CASE CYCLE BY AC1 MOD 20B
;LEAVES AC1 = CYCLE COUNT-1 MOD 20B

$CYRET		$R5;	Shares space with SAD.
$CYCOUT		$R7;	Shares space with XREG.

!1,2,EMCYCX,ACCYCLE;
!1,1,Y1;
!1,1,Y2;
!1,1,Y3;
!1,1,Z1;
!1,1,Z2;
!1,1,Z3;

EMCYCLE: L← DISP, SINK← X17, BUS=0;	CONSTANT WITH BS=7
CYCP:	T← AC0, :EMCYCX;

ACCYCLE: T← AC1;
	L← 17 AND T, :CYCP;

EMCYCX: CYCOUT←L, L←0, :RETCYCX;

RAMCYCX: CYCOUT←L, L←0+1;

RETCYCX: CYRET←L, L←0+T;
	SINK←CYCOUT, BUS;
	TASK, :L0;

;TABLE FOR CYCLE
R4:	CYCOUT← L MRSH 1;
Y3:	L← T← CYCOUT, TASK;
R3X:	CYCOUT← L MRSH 1;
Y2:	L← T← CYCOUT, TASK;
R2X:	CYCOUT← L MRSH 1;
Y1:	L← T← CYCOUT, TASK;
R1X:	CYCOUT← L MRSH 1, :ENDCYCLE;

L4:	CYCOUT← L MLSH 1;
Z3:	L← T← CYCOUT, TASK;
L3:	CYCOUT← L MLSH 1;
Z2:	L← T← CYCOUT, TASK;
L2:	CYCOUT← L MLSH 1;
Z1:	L← T← CYCOUT, TASK;
L1:	CYCOUT← L MLSH 1, :ENDCYCLE;
L0:	CYCOUT← L, :ENDCYCLE;

L8:	CYCOUT← L LCY 8, :ENDCYCLE;
L7:	CYCOUT← L LCY 8, :Y1;
L6:	CYCOUT← L LCY 8, :Y2;
L5:	CYCOUT← L LCY 8, :Y3;

R7:	CYCOUT← L LCY 8, :Z1;
R6:	CYCOUT← L LCY 8, :Z2;
R5:	CYCOUT← L LCY 8, :Z3;

ENDCYCLE: SINK← CYRET, BUS, TASK;
	:EMCYCRET;

EMCYCRET: L←CYCOUT, TASK, :LOADD;

RAMCYCRET: T←PC, BUS, SWMODE, :TORAM;

; Scan convert instruction for characters. Takes DWAX (Destination
; word address)-NWRDS in AC0, and a pointer to a .AL-format font
; in AC3. AC2+displacement contains a pointer to a two-word block
; containing NWRDS and DBA (Destination Bit Address).

$XH		$R10;
$DWAX		$R35;
$MASK		$R36;

!1,2,HDLOOP,HDEXIT;
!1,2,MERGE,STORE;
!1,2,NFIN,FIN;
!17,2,DOBOTH,MOVELOOP;

CONVERT: MAR←XREG+1;	Got here via indirect mechanism which
;			left first arg in SAD, its address in XREG. 
	T←17;
	L←MD AND T;

	T←MAR←AC3;
	AC1←L;		AC1←DBA&#17
	L←MD+T, TASK;
	AC3←L;		AC3←Character descriptor block address(Char)

	MAR←AC3+1;
	T←177400;
	IR←L←MD AND T;		IR←XH
	XH←L LCY 8, :ODDCX;	XH register temporarily contains HD
ODDCX:	L←AC0, :HDENTER;

HDLOOP: T←SAD;			(really NWRDS)
	L←DWAX+T;

HDENTER: DWAX←L;		DWAX ← AC0+HD*NWRDS
	L←XH-1, BUS=0, TASK;
	XH←L, :HDLOOP;

HDEXIT:	T←MASKTAB;
	MAR←T←AC1+T;		Fetch the mask.
	L←DISP;
	XH←L;			XH register now contains XH
	L←MD;
	MASK←L, L←0+T+1, TASK;
	AC1←L;			***X21. AC1 ← (DBA&#17)+1

	L←5;			***X21. Calling conventions changed.
	IR←SAD, TASK;
	CYRET←L, :MOVELOOP;	CYRET←CALL5

MOVELOOP: L←T←XH-1, BUS=0;
	MAR←AC3-T-1, :NFIN;	Fetch next source word
NFIN:	XH←L;
	T←DISP;			(really NWRDS)
	L←DWAX+T;		Update destination address
	T←MD;
	SINK←AC1, BUS;
	DWAX←L, L←T, TASK, :L0;	Call Cycle subroutine

CONVCYCRET: MAR←DWAX;
	T←MASK, BUS=0;
	T←CYCOUT.T, :MERGE;	Data for first word. If MASK=0
				; then store the word rather than
				; merging, and do not disturb the
				; second word.
MERGE:	L←XREG AND NOT T;	Data for second word.
	T←MD OR T;		First word now merged,
	MAR←DWAX;			restore it.
	XREG←L, L←T;
	MTEMP←L;
	SINK←XREG, BUS=0, TASK;
	MD←MTEMP, :DOBOTH;	XREG=0 means only one word
				; is involved.

DOBOTH: MAR←DWAX+1;
	T←XREG;
	L←MD OR T;
	MAR←DWAX+1;
	XREG←L, TASK;		***X21. TASK added.
STORE:	MD←XREG, :MOVELOOP;

FIN:	L←AC1-1;		***X21. Return AC1 to DBA&#17.
	AC1←L;			*** ... bletch ...
	IR←SH3CONST;
	L←MD, TASK, :SH1;


;RCLK - 61003 - Read the Real Time Clock into AC0,AC1
RCLK:	MAR← CLOCKLOC;
	L← R37;
	AC1← L, :LOADX;

;SIO - 61004 - Put AC0 on the bus, issue STARTF,
;Read Host address from Ethernet interface into AC0.
SIO:	L← AC0, STARTF;
	T←77777;		***X21 sets AC0[0] to 0
	L← RSNF AND T;
LTOAC0:	AC0← L, TASK, :TOSTART;


;ENGBUILD is a constant returned by VERS that contains a discription
;of the Alto and it's Microcode. The conposition of ENGBUILD is:
;	bits 0-3	Alto engineering number
;	bits 4-7	Alto build
;	bits 8-15	Version number of Microcode
;Use of the Alto Build number has been abandoned.
$EngNumber	$1;	This is an Alto I

VERS:	T← EngNumber;		***X21 addition
	L← 3+T, :LTOAC0;	Altocode24 is called ucode version 3

;BLT - 61005 - Block Transfer
;BLKS - 61006 - Block Store
; Accepts in
;	AC0/ BLT: Address of first word of source block-1
;	     BLKS: Data to be stored
;	AC1/ Address of last word of destination block 
;	AC3/ NEGATIVE word count
; Leaves
;	AC0/ BLT: Address of last word of source block+1
;	     BLKS: Unchanged
;	AC1/ Unchanged
;	AC2/ Unchanged
;	AC3/ 0
; These instructions are interruptable.  If an interrupt occurs,
; the PC is decremented by one, and the ACs contain the intermediate
; so the instruction can be restarted when the interrupt is dismissed.

!1,2,PERHAPS, NO;

BLT:	L← MAR← AC0+1;
	AC0← L;
	L← MD, :BLKSA;

BLKS:	L← AC0;
BLKSA:	T← AC3+1, BUS=0;
	MAR← AC1+T, :MOREBLT;

MOREBLT: XREG← L, L← T;
	AC3← L, TASK;
	MD← XREG;		STORE
	L← NWW, BUS=0;		CHECK FOR INTERRUPT
	SH<0, :PERHAPS, L← PC-1;	Prepare to back up PC.

NO:	SINK← DISP, SINK← M7, BUS, :DISABLED;

PERHAPS:SINK← DISP, SINK← M7, BUS, :DOIT;

DOIT:	PC←L, :FINBLT;	***X21. Reset PC, terminate instruction.

DISABLED::DIR;	GOES TO BLT OR BLKS

FINBLT:	T←777;	***X21. PC in [177000-177777] means Ram return
	L←PC+T+1;
	L←PC AND T, TASK, ALUCY;
TOSTART: XREG←L, :START;

RAMRET: T←XREG, BUS, SWMODE;
TORAM:	:NOVEM;

;PARAMETERLESS INSTRUCTIONS FOR DIDDLING THE WCS.

;JMPRAM - 61010 - JUMP TO THE RAM ADDRESS SPECIFIED BY AC1
JMPR:	T←AC1, BUS, SWMODE, :TORAM;


;RDRAM - 61011 - READ THE RAM WORD ADDRESSED BY AC1 INTO AC0
RDRM:	T← AC1, RDRAM;
	L← ALLONES, TASK, :LOADD;


;WRTRAM - 61012 - WRITE AC0,AC3 INTO THE RAM LOCATION ADDRESSED BY AC1
WTRM:	T← AC1;
	L← AC0, WRTRAM;
	L← AC3, :FINBLT;

;INTERRUPT SYSTEM.  TIMING IS 0 CYCLES IF DISABLED, 18 CYCLES
;IF THE INTERRUPTING CHANEL IS INACTIVE, AND 36+6N CYCLES TO CAUSE
;AN INTERRUPT ON CHANNEL N

INTCODE:PC← L, IR← 0;	
	T← NWW;
	T← MD OR T;
	L← MD AND T;
	SAD← L, L← T, SH=0;		SAD HAD POTENTIAL INTERRUPTS
	NWW← L, L←0+1, :SOMEACTIVE;	NWW HAS NEW WW

NOACTIVE: MAR← WWLOC;		RESTORE WW TO CORE
	L← SAD;			AND REPLACE IT WITH SAD IN NWW
	MD← NWW, TASK;
INTZ:	NWW← L, :START;

SOMEACTIVE: MAR← PCLOC;	STORE PC AND SET UP TO FIND HIGHEST PRIORITY REQUEST
	XREG← L, L← 0;
	MD← PC, TASK;

ILPA:	PC← L;
ILP:	T← SAD;
	L← T← XREG AND T;
	SH=0, L← T, T← PC;
	:IEXIT, XREG← L LSH 1;

NIEXIT:	L← 0+T+1, TASK, :ILPA;
IEXIT:	MAR← PCLOC+T+1;		FETCH NEW PC. T HAS CHANNEL #, L HAS MASK

	XREG← L;
	T← XREG;
	L← NWW XOR T;	TURN OFF BIT IN WW FOR INTERRUPT ABOUT TO HAPPEN
	T← MD;
	NWW← L, L← T;
	PC← L, L← T← 0+1, TASK;
	SAD← L MRSH 1, :NOACTIVE;	SAD← 1B5 TO DISABLE INTERRUPTS

;
;	************************
;	* BIT-BLT - 61024 *
;	************************
;
;	/* NOVA REGS
;	AC2 -> BLT DESCRIPTOR TABLE, AND IS PRESERVED
;	AC1 CARRIES LINE COUNT FOR RESUMING AFTER AN
;		INTERRUPT. MUST BE 0 AT INITIAL CALL
;	AC0 AND AC3 ARE SMASHED TO SAVE S-REGS
;
;	/* ALTO REGISTER USAGE
;DISP CARRIES:	TOPLD(100), SOURCE(14), OP(3)

$MASK1		$R0;
$YMUL		$R2;	HAS TO BE AN R-REG FOR SHIFTS
$RETN		$R2;
$SKEW		$R3;
$TEMP		$R5;
$WIDTH		$R7;
$PLIER		$R7;	HAS TO BE AN R-REG FOR SHIFTS
$DESTY		$R10;
$WORD2		$R10;
$STARTBITSM1	$R35;
$SWA		$R36;
$DESTX		$R36;
$LREG		$R40;	HAS TO BE R40 (COPY OF L-REG)
$NLINES		$R41;
$RAST1		$R42;
$SRCX		$R43;
$SKMSK		$R43;
$SRCY		$R44;
$RAST2		$R44;
$CONST		$R45;
$TWICE		$R45;
$HCNT		$R46;
$VINC		$R46;
$HINC		$R47;
$NWORDS		$R50;
$MASK2		$R51;	WAS $R46;
;
$LASTMASKP1	$500;	MASKTABLE+021
$170000		$170000;
$CALL3		$3;	SUBROUTINE CALL INDICES
$CALL4		$4;
$DWAOFF		$2;	BLT TABLE OFFSETS
$DXOFF		$4;
$DWOFF		$6;
$DHOFF		$7;
$SWAOFF		$10;
$SXOFF		$12;
$GRAYOFF	$14;	GRAY IN WORDS 14-17
$LASTMASK	$477;	MASKTABLE+020	**NOT IN EARLIER PROMS!


;	BITBLT SETUP - CALCULATE RAM STATE FROM AC2'S TABLE
;----------------------------------------------------------
;
;	/* FETCH COORDINATES FROM TABLE
	!1,2,FDDX,BLITX;
	!1,2,FDBL,BBNORAM;
	!17,20,FDBX,,,,FDX,,FDW,,,,FSX,,,,,;	FDBL RETURNS (BASED ON OFFSET)
;	        (0)     4    6      12
BITBLT:	L← 0;
	SINK← LREG, BUSODD;	SINK← -1 IFF NO RAM
	L← T← DWOFF, :FDBL;
BBNORAM: TASK, :NPTRAP;		TRAP IF NO RAM
;
FDW:	T← MD;			PICK UP WIDTH, HEIGHT
	WIDTH← L, L← T, TASK, :NZWID;
NZWID:	NLINES← L;
	T← AC1;
	L← NLINES-T;
	NLINES← L, SH<0, TASK;
	:FDDX;
;
FDDX:	L← T← DXOFF, :FDBL;	PICK UP DEST X AND Y
FDX:	T← MD;
	DESTX← L, L← T, TASK;
	DESTY← L;
;
	L← T← SXOFF, :FDBL;	PICK UP SOURCE X AND Y
FSX:	T← MD;
	SRCX← L, L← T, TASK;
	SRCY← L, :CSHI;
;
;	/* FETCH DOUBLEWORD FROM TABLE (L← T← OFFSET, :FDBL)
FDBL:	MAR← AC2+T;
	SINK← LREG, BUS;
FDBX:	L← MD, :FDBX;
;
;	/* CALCULATE SKEW AND HINC
	!1,2,LTOR,RTOL;
CSHI:	T← DESTX;
	L← SRCX-T-1;
	T← LREG+1, SH<0;	TEST HORIZONTAL DIRECTION
	L← 17.T, :LTOR;	SKEW ← (SRCX - DESTX) MOD 16
RTOL:	SKEW← L, L← 0-1, :AH, TASK;	HINC ← -1
LTOR:	SKEW← L, L← 0+1, :AH, TASK;	HINC ← +1
AH:	HINC← L;
;
;	CALCULATE MASK1 AND MASK2
	!1,2,IFRTOL,LNWORDS;
	!1,2,POSWID,NEGWID;
CMASKS:	T← DESTX;
	T← 17.T;
	MAR← LASTMASKP1-T-1;
	L← 17-T;		STARTBITS ← 16 - (DESTX.17)
	STARTBITSM1← L;
	L← MD, TASK;
	MASK1← L;		MASK1 ← @(MASKLOC+STARTBITS)
	L← WIDTH-1;
	T← LREG-1, SH<0;
	T← DESTX+T+1, :POSWID;
POSWID:	T← 17.T;
;	T← 0+T+1;	**
;	MAR← LASTMASKP1-T-1;	**REPLACE THESE 2 BY 1 BELOW IN #21
	MAR← LASTMASK-T-1;
	T← ALLONES;		MASK2 ← NOT
	L← HINC-1;
	L← MD XOR T, SH=0, TASK;	@(MASKLOC+(15-((DESTX+WIDTH-1).17)))
	MASK2← L, :IFRTOL;
;	/* IF RIGHT TO LEFT, ADD WIDTH TO X'S AND EXCH MASK1, MASK2
IFRTOL:	T← WIDTH-1;	WIDTH-1
	L← SRCX+T;
	SRCX← L;		SRCX ← SCRX + (WIDTH-1)
	L← DESTX+T;
	DESTX← L;	DESTX ← DESTX + (WIDTH-1)
	T← DESTX;
	L← 17.T, TASK;
	STARTBITSM1← L;	STARTBITS ← (DESTX.17) + 1
	T← MASK1;
	L← MASK2;
	MASK1← L, L← T,TASK;	EXCHANGE MASK1 AND MASK2
	MASK2←L;
;
;	/* CALCULATE NWORDS
	!1,2,LNW1,THIN;
LNWORDS:T← STARTBITSM1+1;
	L← WIDTH-T-1;
	T← 177760, SH<0;
	T← LREG.T, :LNW1;
LNW1:	L← CALL4;		NWORDS ← (WIDTH-STARTBITS)/16
	CYRET← L, L← T, :R4, TASK; CYRET←CALL4
;	**WIDTH REG NOW FREE**
CYX4:	L← CYCOUT, :LNW2;
THIN:	T← MASK1;	SPECIAL CASE OF THIN SLICE
	L←MASK2.T;
	MASK1← L, L← 0-1;	MASK1 ← MASK1.MASK2, NWORDS ← -1
LNW2:	NWORDS← L;	LOAD NWORDS
;	**STARTBITSM1 REG NOW FREE**
;
;	/* DETERMINE VERTICAL DIRECTION
	!1,2,BTOT,TTOB;
	T← SRCY;
	L← DESTY-T;
	T← NLINES-1, SH<0;
	L← 0, :BTOT;	VINC ← 0 IFF TOP-TO-BOTTOM
BTOT:	L← ALLONES;	ELSE -1
BTOT1:	VINC← L;
	L← SRCY+T;		GOING BOTTOM TO TOP
	SRCY← L;			ADD NLINES TO STARTING Y'S
	L← DESTY+T;
	DESTY← L, L← 0+1, TASK;
	TWICE←L, :CWA;
;
TTOB:	T← AC1, :BTOT1;		TOP TO BOT, ADD NDONE TO STARTING Y'S
;	**AC1 REG NOW FREE**;
;
;	/* CALCULATE WORD ADDRESSES - DO ONCE FOR SWA, THEN FOR DWAX
CWA:	L← SRCY;	Y HAS TO GO INTO AN R-REG FOR SHIFTING
	YMUL← L;
	T← SWAOFF;		FIRST TIME IS FOR SWA, SRCX
	L← SRCX;
;	**SRCX, SRCY REG NOW FREE**
DOSWA:	MAR← AC2+T;		FETCH BITMAP ADDR AND RASTER
	XREG← L;
	L←CALL3;
	CYRET← L;		CYRET←CALL3
	L← MD;
	T← MD;
	DWAX← L, L←T, TASK;
	RAST2← L;
	T← 177760;
	L← T← XREG.T, :R4, TASK;	SWA ← SWA + SRCX/16
CYX3:	T← CYCOUT;
	L← DWAX+T;
	DWAX← L;
;
	!1,2,NOADD,DOADD;
	!1,2,MULLP,CDELT;	SWA ← SWA + SRCY*RAST1
	L← RAST2;
	SINK← YMUL, BUS=0, TASK;	NO MULT IF STARTING Y=0
	PLIER← L, :MULLP;
MULLP:	L← PLIER, BUSODD;		MULTIPLY RASTER BY Y
	PLIER← L RSH 1, :NOADD;
NOADD:	L← YMUL, SH=0, TASK;	TEST NO MORE MULTIPLIER BITS
SHIFTB:	YMUL← L LSH 1, :MULLP;
DOADD:	T← YMUL;
	L← DWAX+T;
	DWAX← L, L←T, :SHIFTB, TASK;
;	**PLIER, YMUL REG NOW FREE**
;
	!1,2,HNEG,HPOS;
	!1,2,VPOS,VNEG;
	!1,1,CD1;	CALCULATE DELTAS = +-(NWORDS+2)[HINC] +-RASTER[VINC]
CDELT:	L← T← HINC-1;	(NOTE T← -2 OR 0)
	L← T← NWORDS-T, SH=0;	(L←NWORDS+2 OR T←NWORDS)
CD1:	SINK← VINC, BUSODD, :HNEG;
HNEG:	T← RAST2, :VPOS;
HPOS:	L← -2-T, :CD1;	(MAKES L←-(NWORDS+2))
VPOS:	L← LREG+T, :GDELT, TASK;	BY NOW, LREG = +-(NWORDS+2)
VNEG:	L← LREG-T, :GDELT, TASK;	AND T = RASTER
GDELT:	RAST2← L;
;
;	/* END WORD ADDR LOOP
	!1,2,ONEMORE,CTOPL;
	L← TWICE-1;
	TWICE← L, SH<0;
	L← RAST2, :ONEMORE;	USE RAST2 2ND TIME THRU
ONEMORE:	RAST1← L;
	L← DESTY, TASK;	USE DESTY 2ND TIME THRU
	YMUL← L;
	L← DWAX;		USE DWAX 2ND TIME THRU
	T← DESTX;	CAREFUL - DESTX=SWA!!
	SWA← L, L← T;	USE DESTX 2ND TIME THRU
	T← DWAOFF, :DOSWA;	AND DO IT AGAIN FOR DWAX, DESTX
;	**TWICE, VINC REGS NOW FREE**
;
;	/* CALCULATE TOPLD
	!1,2,CTOP1,CSKEW;
	!1,2,HM1,H1;
	!1,2,NOTOPL,TOPL;
CTOPL:	L← SKEW, BUS=0, TASK;	IF SKEW=0 THEN 0, ELSE
CTX:	IR← 0, :CTOP1;
CTOP1:	T← SRCX;	(SKEW GR SRCX.17) XOR (HINC EQ 0)
	L← HINC-1;
	T← 17.T, SH=0;	TEST HINC
	L← SKEW-T-1, :HM1;
H1:	T← HINC, SH<0;
	L← SWA+T, :NOTOPL;
HM1:	T← LREG;		IF HINC=-1, THEN FLIP
	L← 0-T-1, :H1;	THE POLARITY OF THE TEST
NOTOPL:	SINK← HINC, BUSODD, TASK, :CTX;	HINC FORCES BUSODD
TOPL:	SWA← L, TASK;		(DISP ← 20 FOR TOPLD)
	IR← 20, :CSKEW;
;	**HINC REG NOW FREE**
;
;	/* CALCULATE SKEW MASK
	!1,2,THINC,BCOM1;
	!1,2,COMSK,NOCOM;
CSKEW:	T← SKEW, BUS=0;	IF SKEW=0, THEN COMP
	MAR← LASTMASKP1-T-1, :THINC;
THINC:	L←HINC-1;
	SH=0;			IF HINC=-1, THEN COMP
BCOM1:	T← ALLONES, :COMSK;
COMSK:	L← MD XOR T, :GFN;
NOCOM:	L← MD, :GFN;
;
;	/* GET FUNCTION
GFN:	MAR← AC2;
	SKMSK← L;
	T← 17;	**THIS MASK IS ONLY FOR SAFETY
	T← MD.T;
	L← DISP+T, TASK;
	IR← LREG, :BENTR;		DISP ← DISP .OR. FUNCTION

;	BITBLT WORK - VERT AND HORIZ LOOPS WITH 4 SOURCES, 4 FUNCTIONS
;-----------------------------------------------------------------------
;
;	/* VERTICAL LOOP: UPDATE SWA, DWAX
	!1,2,DO0,VLOOP;
VLOOP:	T← SWA;
	L← RAST1+T;	INC SWA BY DELTA
	SWA← L;
	T← DWAX;
	L← RAST2+T, TASK;	INC DWAX BY DELTA
	DWAX← L;
;
;	/* TEST FOR DONE, OR NEED GRAY
	!1,2,MOREV,DONEV;
	!1,2,BMAYBE,BNOINT;
	!1,2,BDOINT,BDIS0;
	!1,2,DOGRAY,NOGRAY;
BENTR:	L← T← NLINES-1;		DECR NLINES AND CHECK IF DONE
	NLINES← L, SH<0;
	L← NWW, BUS=0, :MOREV;	CHECK FOR INTERRUPTS
MOREV:	L← 3.T, :BMAYBE, SH<0;	CHECK DISABLED
BNOINT:	SINK← DISP, SINK← lgm10, BUS=0, :BDIS0, TASK;
BMAYBE:	SINK← DISP, SINK← lgm10, BUS=0, :BDOINT, TASK;	TEST IF NEED GRAY(FUNC=8,12)
BDIS0:	CONST← L, :DOGRAY;
;
;	/* INTERRUPT SUSPENSION (POSSIBLY)
	!1,1,DOI1;	MAY GET AN OR-1
BDOINT:	:DOI1;	TASK HERE
DOI1:	T← AC2;
	MAR← DHOFF+T;		NLINES DONE = HT-NLINES-1
	T← NLINES;
	L← PC-1;		BACK UP THE PC, SO WE GET RESTARTED
	PC← L;
	L← MD-T-1, :BLITX, TASK;	...WITH NO LINES DONE IN AC1
;
;	/* LOAD GRAY FOR THIS LINE (IF FUNCTION NEEDS IT)
	!1,2,PRELD,NOPLD;
DOGRAY:	T← CONST-1;
	T← GRAYOFF +T+1;
	MAR← AC2+T;
	NOP;	UGH
	L← MD;
NOGRAY:	SINK← DISP, SINK← lgm100, BUS=0, TASK;	TEST TOPLD
	CONST← L, :PRELD;
;
;	/* NORMAL COMPLETION
NEGWID:	L← 0, :BLITX, TASK;
DONEV:	L← 0, :BLITX, TASK;	MAY BE AN OR-1 HERE!
BLITX:	AC1← L, :FINBLT;
;
;	/* PRELOAD OF FIRST SOURCE WORD (DEPENDING ON ALIGNMENT)
PRELD:	T← HINC;
	MAR← SWA-T;	PRELOAD SOURCE PRIOR TO MAIN LOOP
	NOP;
	L← MD, TASK;
	WORD2← L, :NOPLD;
;
;
;	/* HORIZONTAL LOOP - 3 CALLS FOR 1ST, MIDDLE AND LAST WORDS
	!1,2,FDISPA,LASTH;
	%17,17,14,DON0,,DON2,DON3;		CALLERS OF HORIZ LOOP
;	NOTE THIS IGNORES 14-BITS, SO lgm14 WORKS LIKE L←0 FOR RETN
	!14,1,LH1;	IGNORE RESULTING BUS
NOPLD:	L← 3, :FDISP;		CALL #3 IS FIRST WORD
DON3:	L← NWORDS;
	HCNT← L, SH<0;		HCNT COUNTS WHOLE WORDS
DON0:	L← HCNT-1, :DO0;	IF NEG, THEN NO MIDDLE OR LAST
DO0:	HCNT← L, SH<0;		CALL #0 (OR-14!) IS MIDDLE WORDS
;	UGLY HACK SQUEEZES 2 INSTRS OUT OF INNER LOOP:
	L← DISP, SINK← lgm14, BUS, TASK, :FDISPA;	(WORKS LIKE L←0)
LASTH:	:LH1;	TASK AND BUS PENDING
LH1:	L← 2, :FDISP;		CALL #2 IS LAST WORD
DON2:	:VLOOP;
;
;
;	/* HERE ARE THE SOURCE FUNCTIONS
	!17,20,,,,F0,,,,F1,,,,F2,,,,F3;	IGNORE OP BITS IN FUNCTION CODE
	!17,20,,,,F0A,,,,F1A,,,,F2A,,,,;	SAME FOR WINDOW RETURNS
	!3,4,OP0,OP1,OP2,OP3;
FDISP:	SINK← DISP, SINK←lgm14, BUS, TASK;
FDISPA:	RETN← L, :F0;
F0:	:WIND;			FUNC 0 - WINDOW
F1:	:WIND;			FUNC 1 - NOT WINDOW
F1A:	T← CYCOUT;
	L← ALLONES XOR T, TASK, :F3A;
F2:	:WIND;			FUNC 2 - WINDOW .AND. GRAY
F2A:	T← CYCOUT;
	L← ALLONES XOR T;
	TEMP← L;		TEMP ← NOT WINDOW
	MAR← DWAX;
	L← CONST AND T;		WINDOW .AND. GRAY
	T← TEMP;
	T← MD .T;		DEST.AND.NOT WINDOW
	L← LREG OR T, TASK, :F3A;	(TRANSPARENT)
F3:	L← CONST, TASK;	FUNC 3 - CONSTANT (COLOR)
F3A:	CYCOUT← L;
;
;
;	/* HERE ARE THE OPERATIONS - ENTER WITH SOURCE IN CYCOUT
	%16,17,15,STFULL,STMSK;	MASKED OR FULL STORE (LOOK AT 2-BIT)
F0A:	SINK← DISP, SINK← lgm3, BUS;	DISPATCH ON OP
OPX:	T← MAR← DWAX, :OP0;	OP 0 - SOURCE
OP0:	SINK← RETN, BUS;	TEST IF UNMASKED
OP0A:	L← HINC+T, :STFULL;	ELSE :STMSK
OP1:	T← CYCOUT;		OP 1 - SOURCE .OR. DEST
	L← MD OR T, :OPN, TASK;
OP2:	T← CYCOUT;		OP 2 - SOURCE .XOR. DEST
	L← MD XOR T, :OPN, TASK;
OP3:	T← CYCOUT;		OP 3 - (NOT SOURCE) .AND. DEST
	L← 0-T-1;
	T← LREG;
	L← MD AND T, TASK;
OPN:	CYCOUT← L, :OPX;
;
;
;	/* STORE MASKED INTO DESTINATION
	!1,2,STM2,STM1;
STMSK:	L← MD;
	SINK← RETN, BUSODD, TASK;	DETERMINE MASK FROM CALL INDEX
	TEMP← L, :STM2;		STACHE DEST WORD IN TEMP
STM1:	T←MASK1, :STM3;
STM2:	T←MASK2, :STM3;
STM3:	L← CYCOUT AND T;  ***X24. Removed TASK clause.
	CYCOUT← L, L← 0-T-1;	AND INTO SOURCE
	T← LREG;		T← MASK COMPLEMENTED
	T← TEMP .T;		AND INTO DEST
	L← CYCOUT OR T, TASK;
	CYCOUT← L;		OR TOGETHER THEN GO STORE
	T← MAR← DWAX, :OP0A;
;
;	/* STORE UNMASKED FROM CYCOUT (L=NEXT DWAX)
STFULL:	MD← CYCOUT;
STFUL1:	SINK← RETN, BUS, TASK;
	DWAX← L, :DON0;
;
;
;	/* WINDOW SOURCE FUNCTION
;	TASKS UPON RETURN, RESULT IN CYCOUT
	!1,2,DOCY,NOCY;
	!17,1,WIA;
	!1,2,NZSK,ZESK;
WIND:	MAR← SWA;		ENTER HERE (7 INST TO TASK)
	L← T← SKMSK;
	L← WORD2.T, SH=0;
	CYCOUT← L, L← 0-T-1, :NZSK;	CYCOUT← OLD WORD .AND. MSK
ZESK:	L← MD;	ZERO SKEW BYPASSES LOTS
	CYCOUT← L, :NOCY;
NZSK:	T← MD;
	L← LREG.T;
	TEMP← L, L←T, TASK;	TEMP← NEW WORD .AND. NOTMSK
	WORD2← L;
	T← TEMP;
	L← T← CYCOUT OR T;		OR THEM TOGETHER
	CYCOUT← L, L← 0+1, SH=0;	DONT CYCLE A ZERO ***X21.
	SINK← SKEW, BUS, :DOCY;
DOCY:	CYRET← L LSH 1, L← T, :L0;	CYCLE BY SKEW ***X21.
NOCY:	T← SWA, :WIA;	(MAY HAVE OR-17 FROM BUS)
CYX2:	T← SWA;
WIA:	L← HINC+T;
	SINK← DISP, SINK← lgm14, BUS, TASK;	DISPATCH TO CALLER 
	SWA← L, :F0A;

;	THE DISK CONTROLLER

;	ITS REGISTERS:
$DCBR		$R34;
$KNMAR		$R33;
$CKSUMR		$R32;
$KWDCT		$R31;
$KNMARW		$R33;
$CKSUMRW	$R32;
$KWDCTW		$R31;

;	ITS TASK SPECIFIC FUNCTIONS AND BUS SOURCES:
$KSTAT		$L020012,014003,124100;	DF1 = 12 (LHS) BS = 3 (RHS)
$RWC		$L024011,000000,000000;	NDF2 = 11
$RECNO		$L024012,000000,000000;	NDF2 = 12
$INIT		$L024010,000000,000000;	NDF2 = 10
$CLRSTAT	$L016014,000000,000000;	NDF1 = 14
$KCOMM		$L020015,000000,124000;	DF1 = 15 (LHS only) Requires bus def
$SWRNRDY	$L024014,000000,000000;	NDF2 = 14
$KADR		$L020016,000000,124000;	DF1 = 16 (LHS only) Requires bus def
$KDATA		$L020017,014004,124100;	DF1 = 17 (LHS)  BS = 4 (RHS)
$STROBE		$L016011,000000,000000;	NDF1 = 11
$NFER		$L024015,000000,000000;	NDF2 = 15
$STROBON	$L024016,000000,000000;	NDF2 = 16
$XFRDAT		$L024013,000000,000000;	NDF2 = 13
$INCRECNO	$L016013,000000,000000;	NDF1 = 13

;	THE DISK CONTROLLER COMES IN TWO PARTS. THE SECTOR
;	TASK HANDLES DEVICE CONTROL AND COMMAND UNDERSTANDING
;	AND STATUS REPORTING AND THE LIKE. THE WORD TASK ONLY
;	RUNS AFTER BEING ENABLED BY THE SECTOR TASK AND
;	ACTUALLY MOVES DATA WORDS TO AND FRO. 

;   THE SECTOR TASK

;	LABEL PREDEFINITIONS:
!1,2,COMM,NOCOMM;
!1,2,COMM2,IDLE1;
!1,2,BADCOMM,COMM3;
!1,2,COMM4,ILLSEC;
!1,2,COMM5,WHYNRDY;
!1,2,STROB,CKSECT;
!1,2,STALL,CKSECT1;
!1,2,KSFINI,CKSECT2;
!1,2,IDLE2,TRANSFER;
!1,2,STALL2,GASP;
!1,2,INVERT,NOINVERT;

KSEC:	MAR← KBLKADR2;
KPOQ:	CLRSTAT;	RESET THE STORED DISK ADDRESS
	MD←L←ALLONES+1, :GCOM2;	ALSO CLEAR DCB POINTER

GETCOM:	MAR←KBLKADR;	GET FIRST DCB POINTER
GCOM1:	NOP;
	L←MD;
GCOM2:	DCBR←L,TASK;
	KCOMM←TOWTT;	IDLE ALL DATA TRANSFERS

	MAR←KBLKADR3;	GENERATE A SECTOR INTERRUPT
	T←NWW;
	L←MD OR T;

	MAR←KBLKADR+1;	STORE THE STATUS
	NWW←L, TASK;
	MD←KSTAT;

	MAR←KBLKADR;	WRITE THE CURRENT DCB POINTER
	KSTAT←5;	INITIAL STATUS IS INCOMPLETE
	L←DCBR,TASK,BUS=0;
	MD←DCBR, :COMM;

;	BUS=0 MAPS COMM TO NOCOMM

COMM:	T←2;	GET THE DISK COMMAND
	MAR←DCBR+T;
	T←TOTUWC;
	L←MD XOR T, TASK, STROBON;
	KWDCT←L, :COMM2;

;	STROBON MAPS COMM2 TO IDLE1

COMM2:	T←10;	READ NEW DISK ADDRESS
	MAR←DCBR+T+1;
	T←KWDCT;
	L←ONE AND T;
	L←-400 AND T, SH=0;
	T←MD, SH=0, :INVERT;

;	SH=0 MAPS INVERT TO NOINVERT

INVERT:	L←2 XOR T, TASK, :BADCOMM;
NOINVERT: L←T, TASK, :BADCOMM;

;	SH=0 MAPS BADCOMM TO COMM3

COMM3:	KNMAR←L;

	MAR←KBLKADR2;	WRITE THE NEW DISK ADDRESS
	T←SECT2CM;	CHECK FOR SECTOR > 13
	L←T←KDATA←KNMAR+T;	NEW DISK ADDRESS TO HARDWARE
	KADR←KWDCT,ALUCY;	DISK COMMAND TO HARDWARE
	L←MD XOR T,TASK, :COMM4;	COMPARE OLD AND NEW DISK ADDRESSES

;	ALUCY MAPS COMM4 TO ILLSEC

COMM4:	CKSUMR←L;

	MAR←KBLKADR2;	WRITE THE NEW DISK ADDRESS
	T←CADM,SWRNRDY;	SEE IF DISK IS READY
	L←CKSUMR AND T, :COMM5;

;	SWRNRDY MAPS COMM5 TO WHYNRDY

COMM5:	SH=0,TASK;
	MD←KNMAR, :STROB;	COMPLETE THE WRITE

;	SH=0 MAPS STROB TO CKSECT

CKSECT:	T←KNMAR,NFER;
	L←KSTAT XOR T, :STALL;

;	NFER MAPS STALL TO CKSECT1

CKSECT1: CKSUMR←L,XFRDAT;
	T←CKSUMR, :KSFINI;

;	XFRDAT MAPS KSFINI TO CKSECT2

CKSECT2: L←SECTMSK AND T;
KSLAST:	BLOCK,SH=0;
GASP:	TASK, :IDLE2;

;	SH=0 MAPS IDLE2 TO TRANSFER

TRANSFER: KCOMM←TOTUWC;	TURN ON THE TRANSFER

!1,2,ERRFND,NOERRFND;
!1,2,EF1,NEF1;

DMPSTAT: MAR←DCBR+1;	WRITE FINAL STATUS
	T←COMERR1;	SEE IF STATUS REPRESENTS ERROR
	L←KSTAT AND T;
	KWDCT←L,TASK,SH=0;
	MD←KSTAT,:ERRFND;

;	SH=0 MAPS ERRFND TO NOERRFND

NOERRFND: T←6;	PICK UP NO-ERROR INTERRUPT WORD

INTCOM:	MAR←DCBR+T;
	T←NWW;
	L←MD OR T;
	SINK←KWDCT,BUS=0,TASK;
	NWW←L,:EF1;

;	BUS=0 MAPS EF1 TO NEF1

NEF1:	MAR←DCBR,:GCOM1;	FETCH ADDRESS OF NEXT CONTROL BLOCK

ERRFND:	T←7,:INTCOM;	PICK UP ERROR INTERRUPT WORD

EF1:	:KSEC;

NOCOMM:	L←ALLONES,CLRSTAT,:KSLAST;

IDLE1:	L←ALLONES,:KSLAST;

IDLE2:	KSTAT←LOW14, :GETCOM;	NO ACTIVITY THIS SECTOR

BADCOMM: KSTAT←7;	ILLEGAL COMMAND ONLY NOTED IN KBLK STAT
	BLOCK;
	TASK,:EF1;

WHYNRDY: NFER;
STALL:	BLOCK, :STALL2;

;	NFER MAPS STALL2 TO GASP

STALL2:	TASK;
	:DMPSTAT;

ILLSEC:	KSTAT←7, :STALL;	ILLEGAL SECTOR SPECIFIED

STROB:	CLRSTAT;
	L←ALLONES,STROBE,:CKSECT1;

KSFINI:	KSTAT←4, :STALL;	COMMAND FINISHED CORRECTLY


;DISK WORD TASK
;WORD TASK PREDEFINITIONS
!37,37,,,,RP0,INPREF1,CKP0,WP0,,PXFLP1,RDCK0,WRT0,REC1,,REC2,REC3,,,REC0RC,REC0W,R0,,CK0,W0,,R2,,W2,,REC0,,KWD;
!1,2,RW1,RW2;
!1,2,CK1,CK2;
!1,2,CK3,CK4;
!1,2,CKERR,CK5;
!1,2,PXFLP,PXF2;
!1,2,PREFDONE,INPREF;
!1,2,,CK6;
!1,2,CKSMERR,PXFLP0;

KWD:	BLOCK,:REC0;

;	SH<0 MAPS REC0 TO REC0
;	ANYTHING=INIT MAPS REC0 TO KWD

REC0:	L←2, TASK;	LENGTH OF RECORD 0 (ALLOW RELEASE IF BLOCKED) 
	KNMARW←L;

	T←KNMARW, BLOCK, RWC;	 GET ADDR OF MEMORY BLOCK TO TRANSFER
	MAR←DCBR+T+1, :REC0RC;

;	WRITE MAPS REC0RC TO REC0W
;	INIT MAPS REC0RC TO KWD

REC0RC:	T←MFRRDL,BLOCK, :REC12A;	FIRST RECORD READ DELAY
REC0W:	T←MFR0BL,BLOCK, :REC12A;	FIRST RECORD 0'S BLOCK LENGTH

REC1:	L←10, INCRECNO;	 LENGTH OF RECORD 1 
	T←4, :REC12;
REC2:	L←PAGE1, INCRECNO;	 LENGTH OF RECORD 2 
	T←5, :REC12;
REC12:	MAR←DCBR+T, RWC;	 MEM BLK ADDR FOR RECORD
	KNMARW←L, :RDCK0;

;	RWC=WRITE MAPS RDCK0 INTO WRT0
;	RWC=INIT MAPS RDCK0 INTO KWD

RDCK0:	T←MIRRDL, :REC12A;
WRT0:	T←MIR0BL, :REC12A;

REC12A:	L←MD;
	KWDCTW←L, L←T;
COM1:	KCOMM← STUWC, :INPREF0;

INPREF:	L←CKSUMRW+1, INIT, BLOCK;
INPREF0: CKSUMRW←L, SH<0, TASK, :INPREF1;

;	INIT MAPS INPREF1 TO KWD

INPREF1: KDATA←0, :PREFDONE;

;	SH<0 MAPS PREFDONE TO INPREF

PREFDONE: T←KNMARW;	COMPUTE TOP OF BLOCK TO TRANSFER
KWDX:	L←KWDCTW+T,RWC;		(ALSO USED FOR RESET)
	KNMARW←L,BLOCK,:RP0;

;	RWC=CHECK MAPS RP0 TO CKP0
;	RWC=WRITE MAPS RP0 AND CKP0 TO WP0
;	RWC=INIT MAPS RP0, CKP0, AND WP0 TO KWD

RP0:	KCOMM←STRCWFS,:WP1;

CKP0:	L←KWDCTW-1;	ADJUST FINISHING CONDITION BY 1 FOR CHECKING ONLY
	KWDCTW←L,:RP0;

WP0:	KDATA←ONE;	WRITE THE SYNC PATTERN
WP1:	L←KBLKADR,TASK,:RW1;	INITIALIZE THE CHECKSUM AND ENTER XFER LOOP


XFLP:	MAR←T←L←KNMARW-1;	BEGINNING OF MAIN XFER LOOP
	KNMARW←L,RWC;
	L←KWDCTW-T,:R0;

;	RWC=CHECK MAPS R0 TO CK0
;	RWC=WRITE MAPS R0 AND CK0 TO W0
;	RWC=INIT MAPS R0, CK0, AND W0 TO KWD

R0:	T←CKSUMRW,SH=0,BLOCK;
	MD←L←KDATA XOR T,TASK,:RW1;

;	SH=0 MAPS RW1 TO RW2

RW1:	CKSUMRW←L,:XFLP;

W0:	T←CKSUMRW,BLOCK;
	KDATA←L←MD XOR T,SH=0;
	TASK,:RW1;

;	AS ALREADY NOTED, SH=0 MAPS RW1 TO RW2

CK0:	T←KDATA,BLOCK,SH=0;
	L←MD XOR T,BUS=0,:CK1;

;	SH=0 MAPS CK1 TO CK2

CK1:	L←CKSUMRW XOR T,SH=0,:CK3;

;	BUS=0 MAPS CK3 TO CK4

CK3:	TASK,:CKERR;

;	SH=0 MAPS CKERR TO CK5

CK5:	CKSUMRW←L,:XFLP;

CK4:	MAR←KNMARW, :CK6;

;	SH=0 MAPS CK6 TO CK6

CK6:	CKSUMRW←L,L←0+T;
	MTEMP←L,TASK;
	MD←MTEMP,:XFLP;

CK2:	L←CKSUMRW-T,:R2;

;	BUS=0 MAPS R2 TO R2

RW2:	CKSUMRW←L;

	T←KDATA←CKSUMRW,RWC;	THIS CODE HANDLES THE FINAL CHECKSUM
	L←KDATA-T,BLOCK,:R2;

;	RWC=CHECK NEVER GETS HERE
;	RWC=WRITE MAPS R2 TO W2
;	RWC=INIT MAPS R2 AND W2 TO KWD

R2:	L←MRPAL, SH=0;	SET READ POSTAMBLE LENGTH, CHECK CKSUM
	KCOMM←TOTUWC, :CKSMERR;

;	SH=0 MAPS CKSMERR TO PXFLP0

W2:	L←MWPAL, TASK;	SET WRITE POSTAMBLE LENGTH
	CKSUMRW←L, :PXFLP;

CKSMERR: KSTAT←0,:PXFLP0;	0 MEANS CHECKSUM ERROR .. CONTINUE

PXFLP:	L←CKSUMRW+1, INIT, BLOCK;
PXFLP0:	CKSUMRW←L, TASK, SH=0, :PXFLP1;

;	INIT MAPS PXFLP1 TO KWD

PXFLP1:	KDATA←0,:PXFLP;

;	SH=0 MAPS PXFLP TO PXF2

PXF2:	RECNO, BLOCK;	DISPATCH BASED ON RECORD NUMBER
	:REC1;

;	RECNO=2 MAPS REC1 INTO REC2
;	RECNO=3 MAPS REC1 INTO REC3
;	RECNO=INIT MAPS REC1 INTO KWD

REC3:	KSTAT←4,:PXFLP;	4 MEANS SUCCESS!!!

CKERR:	KCOMM←TOTUWC;	TURN OFF DATA TRANSFER
	L←KSTAT←6, :PXFLP1;	SHOW CHECK ERROR AND LOOP

;The Parity Error Task
;Its label predefinition is way earlier
;It dumps the following interesting registers:
;614/ DCBR	Disk control block
;615/ KNMAR	Disk memory address
;616/ DWA	Display memory address
;617/ CBA	Display control block
;620/ PC	Emulator program counter
;621/ SAD	Emulator temporary register for indirection

PART:	T← 7;
	L← SAD, :PX;
PR7:	L← PC, :PX;
PR6:	L← CBA, :PX;
PR5:	L← DWA, :PX;
PR4:	L← KNMAR, :PX;
PR3:	L← DCBR, :PX;
PR2:	L← NWW OR T, TASK;	T CONTAINS 1 AT THIS POINT
PR0:	NWW← L, :PART;

PX:	MAR← 612+T;
	MTEMP← L, L← T;
	CURDATA← L;		THIS CLOBBERS THE CURSOR FOR ONE 
	T← CURDATA-1, BUS;	FRAME WHEN AN ERROR OCCURS
	MD← MTEMP, :PR0;