-- ColorVersatecDriverImpl.mesa
--   Jim Gasbarro 10-Dec-84 13:29:11
--   Tim Diebert,  5-Sep-86  9:50:00

DIRECTORY
   ColorVersatecDriver,
   ColorVersatecUtils,
   DicentraInputOutput,
   Inline,
   OthelloDefs,
   PDQueue USING [LogMessage],
   Process,
   SpecialRuntime;
   
ColorVersatecDriverImpl: MONITOR 
   IMPORTS 
     OthelloDefs, ColorVersatecUtils, DicentraInputOutput, Inline, PDQueue,
       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
   loopCnt: CARDINAL ← 0;  -- For timeout check....
   messageSent: BOOL ← FALSE;
   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↑;
      loopCnt ← loopCnt + 1;
      IF loopCnt > 3 AND NOT messageSent THEN BEGIN
         PDQueue.LogMessage["DMA to Versatec timeout!!!!  Boot needed!!!\n\l"];
	 messageSent ← TRUE;
	 END;
      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.