; SlotMc.mu - Microcode for Slot, TriCon

;MICROCODE FOR 64K SLOT CARD  
;
; This stuff uses task 1; BLV 177775 (bit 14)
; (disk uses 3,15; BLV 77767 (bits 0, 12))
; Auto-Boot feature means start emulator in RAM BLV 177776
; "and" all these together to get 77764

;Put in packed RAM format with BLV as version:
;	PackMu SlotMc.Mb SlotMc.Br 77764
;

%0, 1777, 1, LOC1;		Slot start
$LREG	$R40;
#TriConMc.Mu;

; Location 720 points to a control block:
;
;0	blank bit 14; COM bit 2
;			COM: BeamOn=0, Status=1, Reset=2, Print=3
;1	BlowUp bit		-- for factor of 2 increase in each direction
;	LastPage bit		-- marks this command as the last page
;	invert bit		-- inverts the page
;	bitsPerLine bit 13	-- for servoing the scan motor
;
;2	bottomMargin word	-- # bits at bottom margin: divides by 16
;3	scanLineWc word		-- # words in scan line /2
;4	leftMargin word		-- # scans before imaging
;5	scansPerPage word	-- total number of scan lines of data
;6	bufferPtr word		-- pointer to first buffer -- must be even
;					buffers are linked by next=current!-1
;7	scanLineWcInc	-- how far (in words) to advance within buffer to find
;					next scan line -- must be even
;10	scansPerBuf word	-- number of scan lines in each buffer
;11	currentBuff word	-- points to current buffer
;12	currentLine		-- counts up to scansPerPage
;13	status:
;	 blank bit 9
;	 Wait bit		-- 0 true
;	 PaperJam bit		-- 0 true
;	 AddPaper bit		-- 0 true
;	 SelectB bit
;	 Ready bit
;	 LineSync bit
;	 PageSync bit	-- 0 true

;So processing of buffer pointed to by b is:
;	currentBuff=b
;	for i=0 to scansPerBuff-1 do
;		Send data starting at b+i*scanLineWcInc for scanLineWc*2 words
;	b=b!-1


;    REGISTER DEFINITIONS FOR SLOT
;     SLOT DEVICE DEFINITIONS

;F1's
$SLOTResetDone	$L16015, 00000, 000000;  non-data
$SLOTSTOP	$L16016, 00000, 000000;  non-data
$SLOTBEAM	$L16017, 00000, 000000;  non-data

;F2's
$SLOTBLOCK	$L24010, 00000, 000000;  non-data
$SLOTDATA	$L26011, 00000, 124000;  non-data
$SLOTADDR	$L26012, 00000, 124000;  destination
$SLOTSERVO	$L26013, 00000, 124000;  destination
$SLOTPRINT	$L24014, 00000, 000000;  non-data
$SLOTRESET	$L24015, 00000, 000000;  non-data
$SLOTDISABLE	$L24016, 00000, 000000;  non-data
$SLOTSTATUS	$L24017, 00000, 000000;  non-data

$DCBADDR$R41;	Normally points to currentLine entry
$PBM	$R42;	Bottom margin
$PDWC	$R43;	scanLineWc (in double words)
$LM	$R44;	Left margin
$MSL	$R45;	scansPerPage
$PBBA	$R46;	Current print buffer address
$PBSLI	$R47;	scanLineWcInc
$PBL	$R50;	Scan lines/buffer
$InitOnesPairs  $R51;  How many pairs of ones to init the buffers with
$CSA	$R53;	CURRENT SCAN ADDRESS
$CRC	$R54;	FIRST SERVO COUNT THEN CURRENT RING COUNT
$CLA	$R55;	CURRENT LAST ADDRESS
$CSBA	$R52;	CURRENT SCAN BASE ADDRESS
$InitReturn  $R52;  Init return BUS byte with sign bit for which buffer
$Command  $R56;
$InitPairs  $R56;  InitBuffers' pair counter
  
; 	Constants known elsewhere by other names:
$600	$600;
$12	$12;

;    PREDEFINITIONS

!3,4,BEAMON,SLOTSTAT,RESETSLOT,PRINT;
!1,2,Invert,InitBuffers;
!1,2,LOC1+1,CKPAGE;
!1,2,STARTPG,PAGESYNC;
!1,2,MARWAIT,MARDONE;
!1,2,MOREWORDS,LASTWORDS;
!1,2,InvertNextWord,NEXTWORD;
!1,2,InvertMoreWords,InvertLastWords;
!1,2,BUFFEROK,RESETBUFFER;
!1,2,NEXTLINE,LINESDONE;
!1,2,InitZeroLoop,InitBufferDone;
!1,2,InitDone,InitSecond;
!1,2,PAGEWAIT,PAGEDONE;
!1,2,RET0,RET1;
!1,2,InitOnes,InitZero;


;    THE CODE

LOC1:	T←600;			Task starts here on StartIO
LOC1+1:	L←100+T,TASK;
	DCBADDR←L;	**	Manufacture #720
;
	T←20;
	MAR←L←DCBADDR+T;	Get pointer to DCB from @(720)
	NOP;
	L←MD,TASK;
	DCBADDR←L;
;
	MAR←DCBADDR;
	T←3;
	L←MD AND T;		Mask command to 2 bits
	T←MD;
	Command←L,L←T,TASK;		Get command
	CRC←L;				Get servo count etc.
;
	MAR←T←DCBADDR;	Clear command to indicate taken
	L←2+T;
	DCBADDR←L,TASK;
	MD←0;
;
	MAR←T←DCBADDR;		@(#720)+2
	L←2+T;
	DCBADDR←L;
	T←MD;				Get bottomMargin
	L←MD;				Get scanLineWc
	PDWC←L;
	L←177777-T,TASK;
	PBM←L;
;
	T←2;
	L←PDWC-T,TASK;
	PDWC←L;
;
	MAR←T←DCBADDR;		@(#720)+4
	L←2+T;
	DCBADDR←L;
	L←MD;				Get leftMargin
	T←MD;				Get scansPerPage
	LM←L,L←T,TASK;
	MSL←L;
;
	MAR←T←DCBADDR;		@(#720)+6
	L←2+T;
	DCBADDR←L;
	L←MD;				Get bufferPtr
	T←MD;				Get scanLineWcInc
	PBBA←L,L←T,TASK;
	PBSLI←L;
;
	MAR←T←DCBADDR;		@(#720)+10
	L←2+T;
	DCBADDR←L;
	L←MD,TASK;			Get scansPerBuff
	PBL←L;
;
	SINK←Command,BUS;	Branch on command
	T← CRC, :BEAMON;	[BEAMON,SLOTSTAT,RESETSLOT,PRINT]
;	(T← CRC is for PRINT - reduces time between TASKs)
;
;
BEAMON:	SLOTSERVO←CRC;
	SLOTBEAM;
	SLOTResetDone, :SLOTSTAT;
;
RESETSLOT: SLOTRESET;
	SLOTSTOP,:SLOTSTAT;	Stop printing
;
SLOTSTAT: MAR←DCBADDR+1,SLOTDISABLE;	Return status
	L←377,SLOTSTATUS;
	TASK;
	MD←LREG,:LOC1;	**	Task stops here until new StartIO
;
;
PRINT:	L← 20000 AND T;  T has CRC, Test the Invert bit
	SLOTBEAM, SH=0;
	InitOnesPairs← L, SLOTPRINT, TASK, :Invert;  [Invert, InitBuffers]
Invert:	NOP;
	T← CRC;
	T← 177577 +T+1;  i.e. T← CRC - 128
	L← 17777 ANDT;  Just bitsPerLine - 128
	MTEMP← L RSH 1;  divide by 32, truncating
	L← MTEMP, TASK;
	InitOnesPairs← L;
	L← InitOnesPairs;
	MTEMP← L RSH 1;
	L← MTEMP;
	MTEMP← L RSH 1;
	L← MTEMP, TASK;
	InitOnesPairs← L;
	L← InitOnesPairs;
	MTEMP← L RSH 1;
	L← MTEMP;
	T← 0, MTEMP← L RSH 1;
	L← MTEMP;
	L← T, InitOnesPairs← L, TASK, :InitBuffers;  (L← 0)
;
RET0:	SLOTSERVO←CRC;
;
PAGESYNC: T←12,SLOTBLOCK;
	MAR←DCBADDR-T; WAIT FOR PAGE SYNC & MARGIN
	T←ONE;
	L←ONE AND T,SLOTSTATUS;
	SINK←MD,BUS=0;	TEST FOR NO NEW COMMAND
	T←600,:LOC1+1;	IFSO GOTO CKPAGE
;
CKPAGE:	SH=0,TASK;	CHECK PAGE NOT STARTED
	:STARTPG;	IFSO GOTO PAGESYNC
;
STARTPG: NOP;
MARWAIT: L←LM-1,SLOTBLOCK;	WAIT FOR MARGIN
	NOP;
	LM←L,TASK,SH<0;	TEST FOR MARGIN SENT
	:MARWAIT;	IFSO GOTO MARDONE
;
MARDONE:
	SLOTSTOP, L←PBBA;	Initialize current scan base address
	CSBA←L;
	L←PBL,TASK;
	CRC←L;			INITIALIZE CURRENT RING COUNT
;
NEXTLINE: SLOTADDR←PBM;		RESET LEFT MARGIN
	L←CSBA;			RESET CURRENT SCAN ADDRESS
	CSA←L;
	T←PDWC;			RESET LAST ADDRESS
	L←PDWC+T;
	CLA←L;
	T←CLA;
	L←CSA+T,TASK;
	CLA←L;
	SINK← InitOnesPairs, BUS=0, TASK;  Check if we're inverting
	:InvertNextWord;  [InvertNextWord, NEXTWORD]
;
;        MAIN WORD TRANSFER LOOP
;
NEXTWORD: MAR←T←CSA; **SLOT HDWARE REQUIRES 3 INS. AFTER MAR←
	L←CLA-T;
	L←2+T,SH<0;	TEST FOR WORDS SENT
	CSA←L,:MOREWORDS;IFSO GOTO LASTWORDS
;
MOREWORDS: SLOTDATA←MD,TASK;
	SINK←MD,:NEXTWORD
;
;        Inverting Word Transfer Loop
;
InvertNextWord:
	MAR← T← CSA; **SLOT HDWARE REQUIRES 3 INS. AFTER MAR←
	;  I take the previous comment to mean that the clock cannot stop
	;  for the two cycles following the SLOTDATA - Butterfield - 10/6
	L← CLA -T;
	L← 2 +T, SH<0;	TEST FOR WORDS SENT
	CSA← L, :InvertMoreWords;  [InvertMoreWords, InvertLastWords]
;
InvertMoreWords:
	T← MD;
	L← MD;
	L← 0 -T-1, MTEMP← L;
	T← MTEMP;
	L← 0 -T-1, MTEMP← L;
	SLOTDATA← MTEMP, TASK;
	SINK← LREG, :InvertNextWord;
;
InvertLastWords:
	T← MD;
	L← MD;
	L← 0 -T-1, MTEMP← L;
	T← MTEMP;
	L← 0 -T-1, MTEMP← L;
	SLOTDATA← MTEMP, TASK;
	SINK← LREG, :FinishScanLine;
;
;
LASTWORDS: SLOTDATA←MD,TASK;
	SINK←MD;
;
;
;      FINISH SCANLINE
;
FinishScanLine:
	T←PBSLI;		INC CURRENT SCAN BASE ADDR
	L←CSBA+T;
	CSBA←L;
	L←CRC-1;
	CRC←L,SH=0,TASK;	CHECK FOR BUFFER COMPLETED
	:BUFFEROK,SLOTBLOCK; IFSO GOTO RESETBUFFER
;
RESETBUFFER: MAR←PBBA-1;	GET POINTER FOR NEXT BUFF ADDR  
	L←PBL;			AND GET FIRST BUFFER ADDR
	CRC←L;
	L←MD;
	MAR←DCBADDR-1;
	PBBA←L;
	CSBA←L;
	MD←PBBA;
;
BUFFEROK:L←MSL-1;
	MSL←L,TASK,SH=0;	TEST FOR LAST SCAN LINE OF PAGE 
	:NEXTLINE;	IFSO GOTO LINESDONE
;
LINESDONE: L←ONE,:InitBuffers,TASK;		ZERO BOTH BUFFERS
;
RET1:	NOP;
PAGEWAIT: T←ONE,SLOTBLOCK;
	L←ONE AND T,SLOTSTATUS;
	SH=0,TASK;	TEST FOR END OF PAGE
	:PAGEWAIT;	IFSO GOTO PAGEDONE
;
PAGEDONE: :SLOTSTAT;


;
; Subroutine to initialize the buffers.  Returns to RET0 or RET1
;
InitBuffers:
	InitReturn← L;  Save return index and initialize buffer bit
InitSecond:
	SLOTADDR← 177777;  Start at the beginning
	SLOTDATA← 0;  with 64 zero bits
	SINK← 0;
	SLOTDATA← 0;
	SINK← 0;
	L← InitOnesPairs, TASK;  Now for InitOnesPairs pair of ones
	InitPairs← L;
InitOnesLoop:
	L← InitPairs -1, BUS=0, TASK;
	InitPairs← L, :InitOnes;  [InitOnes, InitZero]
InitOnes:
	L← 177777;  see if using L helps put in the ones
	SLOTDATA← LREG;
	SINK← LREG, :InitOnesLoop;

InitZero:
	T ← InitOnesPairs +1;
	L← 377 -T-1, TASK;  i.e. 253 - InitOnesPairs
	InitPairs← L;  (this can get squeezed out if necessary)
InitZeroLoop:
	SLOTDATA← 0;
	SINK← 0;
	L← InitPairs -1, BUS=0, TASK;
	InitPairs← L, :InitZeroLoop;  [InitZeroLoop, InitBufferDone]
InitBufferDone:
	T← 100000, SLOTBLOCK;
	L← InitReturn XORT;
	InitReturn← L, SH<0, TASK;  Test for last buffer
	:InitDone;  [InitDone, InitSecond]
InitDone:
	SINK← InitReturn, BUS, TASK;
	:RET0;  [RET0, RET1]