-- ColorVersatecDriverImpl.mesa
-- Jim Gasbarro 10-Dec-84 13:29:11

DIRECTORY
	ColorVersatecDriver,
	ColorVersatecUtils,
	DicentraInputOutput,
	Inline,
	OthelloDefs,
	Process,
	SpecialRuntime;
	
ColorVersatecDriverImpl: MONITOR 
	IMPORTS 
	  OthelloDefs,
	  ColorVersatecUtils, DicentraInputOutput, Inline, Process, SpecialRuntime
	EXPORTS ColorVersatecDriver 
	= BEGIN
	
bpi: CARDINAL = ColorVersatecDriver.bpi;
pageWidthInches: CARDINAL = ColorVersatecDriver.pageWidthInches; 
white: CARDINAL = ColorVersatecDriver.white;
wordsPerScan: CARDINAL = ColorVersatecDriver.wordsPerScan;

versatecBaseAdd: DicentraInputOutput.IOAddress = LOOPHOLE[LONG[4000H]];

ColorPreamble: TYPE = MACHINE DEPENDENT RECORD [
	escape: [0..256) ← 9BH, -- escape
	header: CHARACTER,
	byteCountHigh: [0..256) ← 0,
	byteCountLow: [0..256) ← 4,
	lengthHigh: [0..256) ← 0,
	lengthLow: [0..256) ← 0,
	modeAndColor: ModeAndColor,
	reserved: [0..256) ← 0
	];

CommandPreamble: TYPE = MACHINE DEPENDENT RECORD [
	escape: [0..256) ← 9BH, -- escape
	header: CHARACTER,
	byteCountHigh: [0..256) ← 0,
	byteCountLow: [0..256) ← 0
	];

ModeAndColor: TYPE = MACHINE DEPENDENT RECORD [
  padding: [0..16) ← 0,
  mode: ColorVersatecDriver.ColorMode,
  color: ColorVersatecDriver.Color ← Black ];

-- Controller registers
ControllerRegister: TYPE = {
	dMABaseLow,
	wordCountReg,
	unused2,
	unused3,
	unused4,
	unused5,
	unused6,
	unused7,
	i8237CommandStatus,
	request,
	writeSingleMask,
	mode,
	clearBytePointer,
	i8237MasterClear,
	clearMask,
	writeAllMask,
	dMABaseHigh,
	interruptCommandStatus,
	versatecControlStatus,
	versatecProgrammedDataOut,
	unused14,
	unused15,
	interruptEnableRegister,
	boardReset
	};

-- i8237 Command Register  (08, Write)
	disablei8237: CARDINAL			= 04H;
	memToMemTransfer: CARDINAL		= 01H;

-- i8237 Status Register  (08, Read)
	ch0Request: CARDINAL			= 10H;
	ch0ReachedTermCount: CARDINAL		= 01H;

-- i8237 Internal Request Register  (09, Write)
	setInternalRequestChan0: CARDINAL	= 04H;

-- i8237 Single Mask Register  (0A, Write)
	clearInternalMaskChan0: CARDINAL	= 00H;


-- i8237 Mode Register  (0B, Write)
	chanCharacteristics: CARDINAL		= 48H;

-- i8237 Byte Pointer FF Command  (0C, Write)
	clearPointer: CARDINAL			= 00H;

-- i8237 Master Clear  (0D, Write)
	powerUp: CARDINAL			= 00H;

-- i8237 Temporary Register  (0D, Read)
	-- unused
-- 
-- i8237 Clear Mask Register  (0E, Write)
	-- unused
-- 
-- i8237 Mask Register  (0F, Write)
	-- unused

-- Interrupt Command Register Bits  (11H, Write)
	clearVersatecChannel: CARDINAL		= 80H;
	clearAttnInterrupt: CARDINAL 		= 40H;
	disableAttn: CARDINAL			= 20H;
	enableAttn: CARDINAL			= 10H;
	clearDone: CARDINAL			= 08H;
	setDoneOnComplete: CARDINAL		= 04H;
	setDoneOnReady: CARDINAL 		= 02H;
	transferOdd: CARDINAL			= 01H;

-- Interrupt Status Register Bits  (11H, Read)
	-- unused				= 80H;
	attn: CARDINAL 				= 40H;
	latchedEOP: CARDINAL			= 20H;
	memoryError: CARDINAL			= 10H;
	done: CARDINAL				= 08H;
	doneOnTransferComplete: CARDINAL	= 04H;
	doneOnVersatecReady: CARDINAL 	= 02H;
	oddTransfer: CARDINAL			= 01H;
 
-- Versatec Control Register Bits  (12H, Write)
	plotMode: CARDINAL			= 80H;
	sPPMode: CARDINAL 			= 40H;
	-- unused				= 20H;
	sendRemoteEOT: CARDINAL			= 10H;
	sendRemoteFF: CARDINAL			= 08H;
	sendRemoteLineTerm: CARDINAL		= 04H;
	sendRemoteClear: CARDINAL 		= 02H;
	sendRemoteReset: CARDINAL		= 01H;

-- Versatec Status Register Bits  (12H, Read)
	inPlotMode: CARDINAL			= 80H;
	inSPPMode: CARDINAL 			= 40H;
	-- unused				= 20H;
	dataRequest: CARDINAL 			= 10H;
	isReady: CARDINAL			= 08H;
	hasPaper: CARDINAL			= 04H;
	isOnLine: CARDINAL			= 02H;
	isOK: CARDINAL 				= 01H;

-- PUBLIC PROCEDURES AND VARIABLES
	
K64Words: LONG CARDINAL = LONG[64]*1024;
PlotBuffer: PUBLIC PROC [wordCount: LONG CARDINAL, hyperOffset: LONG CARDINAL] =
	BEGIN
		WHILE wordCount>0 DO
		toNextBoundary: LONG CARDINAL ← K64Words - Inline.LowHalf[hyperOffset];
		xferSize: CARDINAL ← CARDINAL[MIN[wordCount, toNextBoundary, K64Words-1]];
		IF Inline.HighHalf[ColorVersatecUtils.hyperStart+hyperOffset] #
		   Inline.HighHalf[ColorVersatecUtils.hyperStart+hyperOffset+xferSize-1] THEN ERROR;
		VersatecDMASend[wordCount: xferSize, hyperOffset: hyperOffset];
		hyperOffset ← hyperOffset + xferSize;
		wordCount ← wordCount - xferSize;
		ENDLOOP;
	END;
  
VersatecDMASend: ENTRY PROC [wordCount: CARDINAL, hyperOffset: LONG CARDINAL] =
	BEGIN
	addr: LONG CARDINAL = LOOPHOLE[ColorVersatecUtils.hyperStart + hyperOffset];
	-- WHILE DMABusy[] DO ENDLOOP;
	-- Clear byte pointer FF, don't need to do this, but I'm paranoid...
	WriteByte[clearPointer, clearBytePointer];
	-- The i8237 wants word addresses in the low two bytes of the base address
	WriteByte[Inline.LowByte[Inline.LowHalf[addr]], dMABaseLow];
	WriteByte[Inline.HighByte[Inline.LowHalf[addr]], dMABaseLow];
	-- The i8237 wants a byte address in the high order byte of the base address
	WriteByte[Inline.LowByte[Inline.HighHalf[addr * 2]], dMABaseHigh];
	-- The i8237 wants the transfer count in words
	-- NOTE: The 8237 transfers wordCount + 1 words, I subtract 1 to nomalize
	WriteByte[Inline.LowByte[wordCount - 1], wordCountReg];
	WriteByte[Inline.HighByte[wordCount - 1], wordCountReg];
	-- Clear odd/even byte count FF, Clear Done, set Done on Xfer complete
	WriteByte[0CH, interruptCommandStatus];
	-- Stop Clearing Done, set Done on Xfer complete
	WriteByte[04H, interruptCommandStatus];
	--  Let'er rip
	WriteByte[clearInternalMaskChan0, writeSingleMask];
	EnableInterrupts[];
	WHILE DMABusy[] DO 
		WAIT cv↑;
		ENDLOOP;
	DisableInterrupts[];
	END;
	
EnableInterrupts: PROC [] =
	BEGIN
	WriteByte[01H, interruptEnableRegister];
	END;

DisableInterrupts: PROC [] =
	BEGIN
	WriteByte[00H, interruptEnableRegister];
	END;

DMABusy: PROC [] RETURNS [BOOLEAN] =
	BEGIN 
	bits: CARDINAL ← ReadByte[interruptCommandStatus];
	IF Inline.BITAND[bits, memoryError] # 0 THEN ERROR;
	RETURN[Inline.BITAND[bits, done] = 0];
	END;

VersatecIsOK: PUBLIC PROC [] RETURNS [BOOLEAN] =
	BEGIN   -- has paper and is on line
	bits: CARDINAL ← ReadByte[versatecControlStatus];
	RETURN[Inline.BITAND[bits, isOK] # 0];
	END;

InitVersatecDMA: PROC [] =
	BEGIN
	mask: WORD;
	
	VersatecInitialCheckout[];
	-- Reset the world
	WriteByte[0, boardReset];
	WriteByte[0, i8237MasterClear];
	-- Clear Versatec channel, clear Done, set Done on transfer complete
	WriteByte[9CH, interruptCommandStatus];
	WriteByte[0CH, interruptCommandStatus];
	WriteByte[sendRemoteClear, versatecControlStatus];
	-- Set operational mode on all i8237 channels
	WriteByte[chanCharacteristics+0, mode];
	WriteByte[chanCharacteristics+1, mode];
	WriteByte[chanCharacteristics+2, mode];
	WriteByte[chanCharacteristics+3, mode];
	
	[cv, mask] ← SpecialRuntime.AllocateNakedCondition[];
	[] ← DicentraInputOutput.SetWakeupMask[bits: mask, intLevel: display];
	Process.SetTimeout[cv, Process.SecondsToTicks[300]];
	END;
	
VersatecInitialCheckout: PUBLIC PROC [] =
	BEGIN
	WHILE NOT VersatecIsOK[] DO
		OthelloDefs.WriteLine["Versatec is Offline or out of paper"];
		Process.Pause[Process.SecondsToTicks[2]];
		IF NOT VersatecIsOK[] THEN
			BEGIN
			OthelloDefs.WriteLine["   Versatec is Offline or out of paper"];
			Process.Pause[Process.SecondsToTicks[3]];
			END;
		ENDLOOP;
	END;
	
SetColorMode: PUBLIC PROC [inches: CARDINAL, mode: ColorVersatecDriver.ColorMode, color: ColorVersatecDriver.Color ← Black, cmdOffset: CARDINAL] =
	BEGIN   
	p: LONG POINTER ← @preamble;
	preamble: ColorPreamble ← [
	  header: 'P,		-- color header preamble
	  lengthLow: inches,	-- length in inches
	  modeAndColor: [mode: mode, color: color]
	  ];
	SetPrintMode[];
	ColorVersatecUtils.HyperStore [sourcePtr: p, destOffset: cmdOffset, wordCount: SIZE[ColorPreamble]];
	VersatecDMASend [wordCount: SIZE[ColorPreamble], hyperOffset: cmdOffset];
	SetPlotMode[];
	END;
	
	
Rewind: PUBLIC PROC [cmdOffset: CARDINAL] =
	BEGIN   
	p: LONG POINTER ← @preamble;
	preamble: CommandPreamble ← [header: 'W]; -- rewind
	SetPrintMode[];
	ColorVersatecUtils.HyperStore [sourcePtr: p, destOffset: cmdOffset, wordCount: SIZE[CommandPreamble]];
	VersatecDMASend [wordCount: SIZE[CommandPreamble], hyperOffset: cmdOffset];
	SetPlotMode[];
	END;
	
SetPrintMode: PROC [] =
	BEGIN
	WriteByte[0H, versatecControlStatus];
	END;
	
SetPlotMode: PROC [] =
	BEGIN
	WriteByte[80H, versatecControlStatus];
	END;
	
SendEOT: PUBLIC PROC [] =
	BEGIN
	bits: CARDINAL ← ReadByte[versatecControlStatus];
	WriteByte[Inline.BITOR[Inline.BITAND[bits, plotMode], sendRemoteEOT], versatecControlStatus];
	END;

FormFeed: PUBLIC PROC [] =
	BEGIN
	bits: CARDINAL ← ReadByte[versatecControlStatus];
	WriteByte[Inline.BITOR[Inline.BITAND[bits, plotMode], sendRemoteFF], versatecControlStatus];
	END;
	
SendRLT: PUBLIC PROC [] =
	BEGIN
	bits: CARDINAL ← ReadByte[versatecControlStatus];
	WriteByte[Inline.BITOR[Inline.BITAND[bits, plotMode], sendRemoteLineTerm], versatecControlStatus];
	END;


-- BEWARE HACKED TO BE BACKWARDS
baseAddress: DicentraInputOutput.IOAddress = LOOPHOLE[LONG[4010H]];
ReadByte: PROC [y: ControllerRegister] RETURNS [CARDINAL] =
  BEGIN
  byteOffset: CARDINAL = y.ORD;
  IF (byteOffset MOD 2) # 0 THEN
    RETURN[DicentraInputOutput.InputByteEven[baseAddress+byteOffset/2]]
  ELSE
    RETURN[DicentraInputOutput.InputByteOdd[baseAddress+byteOffset/2]];
  END;
	
WriteByte: PROC [x: CARDINAL, y: ControllerRegister] =
  BEGIN
  byteOffset: CARDINAL = y.ORD;
  IF (byteOffset MOD 2) # 0 THEN
    DicentraInputOutput.OutputByteEven[x, baseAddress+byteOffset/2]
  ELSE
    DicentraInputOutput.OutputByteOdd[x, baseAddress+byteOffset/2];
  END;
  
cv: LONG POINTER TO CONDITION;

InitVersatecDMA[];
END.