/* File XCPTest.c */
#include "XCports.h"
#include "XCStates.h"	/* Tvalue defs and EvTable space */
#include "XCDefs.h"
#include "XCTables.h"	/* EventTable and PreCycle table */
#define xcpcode
#include "XCInts.h"	/* The interrupt defs and code */

/* ===================================================================== */
/* 	 Some global defs						 */
/* ===================================================================== */
static char *ETable;			/* pointer into EventTable	*/
static unsigned curEvent;		/* twiddled on interrupt level and ZeroWait */
static char zeroEvent = FALSE;		/* init to false */
static char inputs;			/* read port 3B into this byte */


/* ===================================================================== */
/* 	 ParamsTable: referenced by entries in EventTable		 */
/* ===================================================================== */
char ParamsTable[]
{0, 0};	/* These are booleans evaluated by event-driven code */


/* ===================================================================== */
/* 		 Indices to ParamsTable 				 */
/* ===================================================================== */
#define ifPrintRequest 00
#define ifnotPageRequest ifPrintRequest | not
#define ifMainDrive 01
#define ifnotMainDrive ifMainDrive | not


/* ===================================================================== */
/* This is the keeper of the PIO output registers 			 */
/* ===================================================================== */
char Ports[]
{0,0,0,0,0,0};

/* ==================================================================== */
/*									*/
/* 		 Procedures						*/
/*									*/
/* =====================================================================*/
/* 	MainMonitor							*/
/* =====================================================================*/
/* See if a key is being typed, and also if PrintRequest on		*/
MainMonitor()
{static char keyboard  " ";

	inputs = *PDR3B;	/* refresh inputs */
	if (inputs & RD←PRINT←REQ)
		ParamsTable[ifPrintRequest] = TRUE;

#ifdef printing
	if (CtlCk()) {keyboard = getchar();
			if (keyboard=='s" || keyboard=='S')
				{ShutDown();
				printf("\n Stopping\n";
				ExitToCPM();
			};
#endif
}


/* ===================================================================== */
/* 	WaitForEvent							 */
/* ===================================================================== */
WaitForEvent(evNum)
unsigned evNum;
{	if (curEvent > evNum) Malfunction(EventTimeError);
	while (evNum != curEvent) MainMonitor();
}


/* =====================================================================*/
/* 	ZeroWait  -- don't call if already at Zero Event		*/
/* =====================================================================*/
ZeroWait(tState)	/* waits for zero clock */
unsigned tState;
{
	zeroEvent = FALSE;
	while (~zeroEvent) MainMonitor();
	curEvent = tState;
	zeroEvent = FALSE;
}

/* =====================================================================*/
/* 	Explicit Engine Control						*/
/* 									*/
/* 									*/
/* =====================================================================*/
/* 	OnMainDrive							*/
/* =====================================================================*/
/* turn on main drive.  Maybe enable interrupts here too */
OnMainDrive()
{	ParamsTable[ifMainDrive] = TRUE;
	*PDR1A = Ports[PORT1A] = (Ports[PORT1A] & (~(DRUM←DRIVE)));
	StartInterrupts();
}

/* =====================================================================*/
/* 	OffMainDrive							*/
/* =====================================================================*/
/* turn off main drive and all Interrupts				*/
OffMainDrive()
{	ParamsTable[ifMainDrive] = FALSE;
	IntsOff();
	*PDR1A = Ports[PORT1A] = (Ports[PORT1A] | DRUM←DRIVE);
}

/* =====================================================================*/
/* 	OffFuserTransport							*/
/* =====================================================================*/
OffFuserTransport()
{	*PDR1B = Ports[PORT1B] = (Ports[PORT1B] & ~(FUS←CLOCK));
	*PDR1B = Ports[PORT1B] = (Ports[PORT1B] | FUS←DIR);
}


/* ===================================================================== */
/* 	ShutDown							 */
/* ===================================================================== */

/* ===================================================================== */
/* 			Error Handling					 */
/* 									 */
/* 									 */

/* ===================================================================== */
/* 	TypeError							 */
/* ===================================================================== */
TypeError(str)
char *str;
{#ifdef printing
	printf("Fatal Error\n");
	printf(str);
	printf("\nType any character to exit");
	while (~CtlCk()) {};
#endif
}

/* ===================================================================== */
/* 	ExitToCPM							 */
/* ===================================================================== */
ExitToCPM()
{
/* JMP 00 */
#asm
DB 0C3H,00H,00H
#endasm
}

/* ===================================================================== */
/* 	Malfunction							 */
/* ===================================================================== */
Malfunction(FnNum)
char FnNum;
{	ShutDown();		/* stop machine and make quiescent */
 	switch (FnNum)
		{
		 case BadFcn: TypeError("Bad function called from EventTable.");
				break;
		 case EventTimeError:
				TypeError("Timing anomoly.");
				break;
		};
	ExitToCPM();
}

/* ===================================================================== */
/* 		Some Interrupt and I/O stuff				 */
/* 									 */


/* ===================================================================== */
/* 	IntsOff							 */
/* ===================================================================== */
IntsOff()
{
/* set all control registers to no interrupts; access allowed to Peripheral reg's */

*CReg3B = *CReg3A = *CReg2B = *CReg2A = *CReg1B = *CReg1A = PERREG;
	*CRegT2 = selCR1Bit;	/* address CR1 */
	*CRegT1 = preset;		/* reset and hold timers */
}

/* ===================================================================== */
/* 	IOConfigure							 */
/* ===================================================================== */
IOConfigure()
{

/* enable addressing of the Data Direction Registers */
*CReg3B = *CReg3A = *CReg2B = *CReg2A = *CReg1B = *CReg1A = '\0';

*PDR1A = *PDR1B = *PDR2A = *PDR2B = *PDR3A = 0XFF;/* config to outs*/
*PDR3B = 0X00;	/* configure all lines to inputs for now in this register*/

/* and set up control register to allow data to go out the peripheral reg */
	IntsOff();	/* -- a side effect of IntsOff  */
}

/* ===================================================================== */
/* 	InitOValues							 */
/* ===================================================================== */
InitOValues()	/* routine to set up PIO ports to talk to data registers */
{
	Ports[PORT1A] = 0XF7;
	Ports[PORT1B] = 0XB0;
	Ports[PORT2A] = 0XFF;
	Ports[PORT2B] = 0XFF;
	Ports[PORT3A] = 0X17;
}

/* ===================================================================== */
/* 	DoIO								 */
/* ===================================================================== */
DoIO()
{	*PDR1A = Ports[PORT1A];
	*PDR1B = Ports[PORT1B];
	*PDR2A = Ports[PORT2A];
	*PDR2B = Ports[PORT2B];
	*PDR3A = Ports[PORT3A];
}


/* =====================================================================*/
/* 	PowerUp							*/
/* =====================================================================*/

PowerUp()
{	IntsOff();	/* Turn off interrupts */
	IOConfigure();	/* Configure the PIO registers for proper I/O */
	InitOValues();	/* Set up the initial signals */
	DoIO();	/* Now send them out */
}

/* =====================================================================*/
/* 	PreCycle							*/
/* =====================================================================*/

PreCycle()
{static char response = ' ';

	ETable = &PreCyTable[0] - 1;
	DoProc(*(++ETable);
	DoProc(*(++ETable);
	DoProc(*(++ETable);
	DoProc(*(++ETable);	/* Diverter up */
	MsWait(30);
	DoProc(*(++ETable);	/* Erase lamp */
	DoProc(*(++ETable);	/* more 1A stuff */
	MsWait(30);
	DoProc(*(++ETable);	/* 1B orbits */	
	MsWait(30);
	DoProc(*(++ETable);	/* 2B andbits *
	for (i=7; --i !=0; ZeroWait(0));	/* do 6 zero clocks */
	WaitForEvent(80);
	*PDR1A = Ports[PORT1A] = (DRUM←DRIVE | ERASE | C←B←MOTOR | C←R←MOTOR | C←B←BIAS | C←R←BIAS);
	*PDR1B = Ports[PORT1B] = ~(FUS←CLOCK | FUS←DIR);
	*PDR2B = Ports[PORT2B] = (Ports[PORT2B] | (CHARGE | TRANSFER | PRE←CLEAN | DETACK));
 #ifdef printing
	printf("\nType a \"W\" when fuser is warm: ");
	while (response != 'W' && response != 'w')
	   if (CtlCk()) response = getchar();
#endif
}

/* ===================================================================== */
/* 		 DoProc						 */
/* ===================================================================== */
DoProc(ProcID)
char ProcID;
{	switch (ProcID) {
		case OrBits: FcOrBits(); break;
		case AndBits: FcAndBits(); break;
		case Normalize: FcNormalize(); break;
		case AnotherPage: FcAnotherPage(); break;
		case DriveOff: FcOffMainDrive(); break;
		default: Malfunction(BadFcn); break;}
}

/* ===================================================================== */
/* Functions dispatched to by DoProc					 */
/* ===================================================================== */
FcOrBits()  /* update the port spec'd by table with data spec'd by table */
{static char portNum;       /* can change to non-static later if desired */

	Ports[portNum] = Ports[portNum = *(++ETable)] | *(++ETable);
}

FcAndBits()
{static char portNum;		/*like FcOrBits only do an AND */
	Ports[portNum] = Ports[portNum = *(++ETable)] & *(++ETable);
}

FcNormalize()
{	/* jam load the number into curEvent for resynchronization	*/
ZeroWait(EvLookup[*(++ETable)]);
}

FcAnotherPage()
{	ZeroWait(0);	/* Will zero out curEvent  */
	ETable = &PageEventTable[0] - 1;
}

FcOffMainDrive()
{	OffMainDrive();
}

/* ===================================================================== */
/* 	Eat Args							 */
/* ===================================================================== */
EatArgs()
{while (and & *ETable) ++ETable; /* Get past compound booleans */
++ETable;	/* position pointer to entry just before next entry */	
++ETable;
}
/* =====================================================================*/
/* 	EvEval								*/
/* =====================================================================*/
/* EvEval evaluate booleans in the event table. Return TRUE to proceed with
   operation following the booleans, or FALSE if we should skip the op.
   Enters with *ETable pointing to first boolean.  Returns with *ETable pointing
   to entry before function name (if returning TRUE,) or to entry before next
   event table entry (if returning FALSE).  */

char EvEval() 
{
/* evaluate the current boolean. If "and" bit on, continue eval.
   Check 'not' bit during all this too */
static char curBool, curval;	/* static now (for debug) but change later */

--ETable;
for (curBool = allOnes; (curBool & and); )
 {curval = (idxMask & (curBool = *(++ETable))); /* curval<-index part of entry*/

  if (curBool & not)
     if (paramsTable[curval])		/* here when boolean wants false */
        {EatArgs(); return FALSE;}	/* but was true */
  else if (!(paramsTable[curval]))	/* here when bool wants state true */
     {EatArgs(); return FALSE;};	/* but was false */

  /* here if boolean evaluated as specified in the Event Table
     now go back and see if the 'and' bit was on in the entry
     If so, continue evaluating the booleans until exhausted or false eval. */
 };

/* Here when done processing booleans.  Go ahead and do the function. */

return TRUE;
}

/* ===================================================================== */
/* 	PrintPage							 */
/* ===================================================================== */

PrintPage();
{ static char numCmds, cmd;
  static int nextEvent;
  ETable = &PreEventTable[0] - 1;

  OnMainDrive();	/* turn on main drive */
  ZeroWait(0);	/* wait for zero clock, and reset curEvent */

   /* Loop on Events */
  while ((nextEvent = EvLookup[*(++ETable)]) != allOnes)

  {WaitForEvent(nextEvent);
   numCmds = *(++ETable);

   for (cmd = 0; ++cmd <= numCmds; )

      /* Loop on commands for this event */
     {if (*(++ETable) == always)	/* see if fast boolean */
	   DoProc(*(++ETable));
       else if (EvEval())		/* evaluate the booleans in table */
	   DoProc(*(++ETable));

       /* *ETable should be setting above next command entry here */
     };/* end of "for (cmd = 0; ++cmd <= numCmds; )" */

   /* Refresh outputs then go wait for the next event */   
   DoIO();
   }; /* end of while EvLookup */

  MsWait(1430);  /* let paper get out of fuser */
  OffFuserTransport();

    /* Here when at end of the event table; perhaps after printing n pages.
    (We could have sensed that another PrintRequest was sent and jammed
    the *ETable pointer to do another page.)
    When we get here, the main drive and the fuser transport should be off
    i.e, a quiescent machine.  */
}

/* ===================================================================== */
/* 	main								 */
/* ===================================================================== */

main()
{

/*  Set up interrupt code to refresh outputs (if main drive on) */
/* Do power up, init ports, et al */
  PowerUp();
  PreCycle();


/* the way out of this loop is to type an "S" to the keyboard. MainMonitor()
   will see it */

while (TRUE)
{ while (~priRequest) MainMonitor();	/* wait here for print request */
   PrintPage();
 }