/*
	DOWNLOAD.C

	The download manager for the Rugged Giant.

	The serial portion of this program will run on other Z-World boards
	that has the z180 serial port 0. Other boards have different IO
	combination that can be used to help this program distinguish what to
	do on power up. Also, earlier Z-World board has the ram address start
	at 0x40000 instead of the 0x80000 for the Rugged Giant. 
		
	Additional libraries needed: z0232.lib, modem232.lib.
	Activate libraries through lib.dir.

	ON POWER UP or RESET:

	The download manager checks whether cols 1 and 6 of the top row of the
	keypad are pressed. If the keys are not pressed, then the download manager
	will proceed to check the CRC values of the uploaded code in RAM. A jump to 
	the uploaded code is done, if the CRC values check out. The uploaded program 
	takes control. Note that the CRC checking process takes some time.
	If cols 1 and 6 are pressed or if the CRC values of the uploaded code
	did not check out, then the Download Manager will continue to execute.

	DOWNLOAD MANAGER OPERATION:

	Task 0 of the download manager periodically hits the watchdog timer
	while monitoring for cols 1 and cols 2 of the top row keypad. If cols 1 and 2
	are pressed continuously for ~2.5 sec, then a watchdog reset is forced and
	the program will restart.

	Task 1 of the download manager runs a serial communication program. The
	user can communicate with the download manager via a PC dumb terminal program
	like PROCOMM or TERMINAL of Windows. The download manager monitors for a 
	proper password from the dumb terminal. Once the correct password is 
	received, the main or help menu is shown on the dumb terminal.

	The following commands are available:
	'm'<CR>  - shows the main menu
	'r'<CR>  - prompts the user for the staring hex address of data to be
				  read. 16 bytes of data are read and displayed on the dumb
				  terminal.
	'w'<CR>  - prompts the user for the starting hex address and a byte 
				  data.  The byte data is written to the specified address.
	'u'<CR>  - Upload a program to the nxtBaseAddress with xmodem file
				  transfer available with PROCOMM or with TERMINAL under 
				  Windows.
	'c'<CR>  - Change the base address nxtBaseAddress. nxtBaseAddress is
				  the starting location of the program uploaded with command 'u'.
	'j'<CR>  - Will compute the CRC values of the uploaded code and jump to
					the uploaded code if the CRC values checked out.
	'p'<CR>  - Prompts the user for new password ( 9 character max)

	All commands are terminated with <CR>. A NULL entry followed by <CR> will
	terminate a command without executing it.

	Note that Terminal under Windows may not communicate properly with the
	download manager if the baud rate is too high. 9600 bps seems to work well 
	for both Procomm and Terminal.

	GENERATING CODE FOR RAM DOWNLOAD:

	Dynamic C can generate code that can be uploaded into ram. Executing
	the function newbbr(....) will force a jump to the uploaded code, at which 
	point the uploaded code takes control.

	The starting ram location of the uploaded code should not be lower than
	8C000. This is because the data space of the downloaded program (whether in
	EPROM or RAM) starts at location 8bfff downward. If during development, 
	the download manager is being run from RAM with xmem code, the starting
	RAM location of the code to be uploaded has to be adjusted to the next 
	available start address of a 4k page.

	To generate the ram code for download, the user has to select the ff:
	(1) Hex for download to RAM under the Memory Map Option.
	(2) Change the staring ram address to 8C000 or higher under the
		 Ram Map Option.

	The code is then compiled with <CNTRL> F3. If the compilation is
	successful, the user will be instructed to exit Dynamic C. Outside of
	Dynamic C for DOS the user runs:
	
			  DCROM filename

	DCROM will generate the filename.hex and filename.bin. The bin file is
	used during the xmodem file transfer to upload the code to the download
	manager. While creating the bin file, DCROM may flash the message:

		  "Address past 512K bytes in updateBin()."

	This just warns you that the RAM code location is above the 512K bytes 
	location and does not do anything to invalidate the generated bin file.

	RETURNING CONTROL TO THE DOWNLOAD MANAGER:

	On reset or power up, the download manager gains control. In the user's
	uploaded program, a forced watchdog time out will automatically cause the 
	board to reset. If the user's program calls the function uplc_init(), any 
	2 keys pressed simultaneously on the top row will force a watch dog timeout.

	LIB ERROR:

	Sys.lib has a function called x_makadr. This function should have double
	colos or:: following it instead of:.

	z0232.lib

	Xmodem file transfer implementation was unnecessary delaying too long.
	New version of the library has this problem fixed.
*/

#use "default.h"
#use cplc.lib
#use rtk.lib

#if !((BOARD_TYPE==CPLC_BOARD))
#fatal "This program only works with the Rugged Giant."
#endif

/*** Begin Globals real_time_kernel  */
int task0(void), task1(void), backgnd(void);
int (*Ftask[])() =
{
	 task0, task1, backgnd
};

#define NTASKS 3
#define TASK0  0
#define TASK1  1

/***End Globals real_time_kernel */

#define RUNKERNEL 1

#define MASKWORD 0x6254      // Magic word to load first password.
#define TBUFSIZE 300         // circular transmit buffer size
#define RBUFSIZE 200         //  circular receive buffer size
#define IBAUD    9600        // baud rate
#define KRELOAD  0x7b        // first and six columns of top row
#define KRESET   0x3f        // first and second columns of top row

char isecho  = 1;            // automatic echoing of serial characters
char ismodem = 0;            // modem not selected

char tbuf[TBUFSIZE];         // circular transmit buffer
char rbuf[RBUFSIZE];         // circular receive buffer
char password[10];           // password, max is ten character
int no_of_pages;    // number of 4k pages of downloaded code
unsigned int next_bbr;       // BBR of the downloaded code
unsigned long nxtBaseAddress;// base address of the downloaded code
char readbuf[RBUFSIZE + 1];  // dummy array for reading circular receive buffer.
int firsttime;               // remember first time for password default
int nkeypress;               // count number of times cols 1 and 2 are
									  //    continually pressed to force watchdog reset

void ProcessCommand(char *buf);
void MainMenu(void);
void RecFileUp(void);
void ReadMem(void);
void WriteMem(void);
void ChangeRamBase(void);
void ShowBaseAddress(unsigned long);
unsigned long AtoLH(char *buf);
int  CheckCrc(void);
int  ParseData(char *mydata, int nth_block, unsigned long baseAddress, 
	  int dest);
int  ReadCom(char *buf);
int  WriteCom(char *buf, int len);
int  EnterPassword(void);
void ChangePassword(void);
void ResetOutputs(void);
int  CheckStartMode(void);
int  MakeCrcTable(void);
unsigned int FastCrc(char *, unsigned int, unsigned int);

root main()
{
	 int  i;

	 nkeypress = 0;
	 outport(DCNTL, 0x30);
	 //GLOBAL_INIT();           // init calibration variables
	 ResetOutputs();          // housekeeping, make sure all outputs are 
									  // off (or on)
						  // check for first time eprom is used.
	 if( firsttime != MASKWORD)
	 {
		  strcpy(password, "pass"); // default password at first time
		  no_of_pages = 0;     // no data uploaded yet
		  nxtBaseAddress = 0x8c000L; // default next base address to 8c000.
		  next_bbr = (unsigned int)(((nxtBaseAddress - 0x2000) >> 12) | 0xe200);
		  firsttime = MASKWORD;
	 }

	 lc_init();               // initialize LCD
	 hitwd();
	 MakeCrcTable();
	 lc_printf("\x1be");      // erase screen and zero cursor
									  // checks if cols 1 and 6 of top row are pressed.
									  // 8-11-94
	 if( CheckStartMode() == 0) 
	 {
		  for (i = 0; i < 1000; i++)
				;                // delay for the lcd
		  lc_printf("Checking Program    ");
		  if (CheckCrc())      // check for valid program
		  {
				resetZ180Int();  // turn off all interrupts
				newbbr(0x2200, next_bbr); // jump to uploaded program
									  // no return from this
		  }
		  lc_printf("\x1be");  // erase screen again if no valid program
	 }
	 di();                    // disable interrupts while initializing the kernel
	 init_kernel();           // initialize the real time kernel
	 run_every(TASK0, 1);     // task0 is run every 25 ms
	 init_timer0(7680);       // run every 25 ms
	 ei();
	 backgnd();               // this runs after everything else
}

/**************************************************************************
 Task 0 hits the watchdog periodically. It also monitors cols 1 and 2 of
 the top keypad row. If the 2 keys are pressed continuously for ~2.5 sec,
 a watchdog reset is forced to restart the download manager.
***************************************************************************/
indirect task0(void)
{
	 char oldkey, newkey;
	 
	 outport(KEYR2, 1);
	 newkey = inport(DREG1);
	 outport(KEYR2, 0);
	 if ((oldkey == newkey) && (newkey == KRESET))
	 {
		  if(++nkeypress > 100)
				while (1)
					 ;            // force a watch dog time out
	 }                        
	 else 
		  nkeypress = 0;
	 oldkey = newkey;
	 hitwd();                 // service watchdog
}

/****************************************************************************
Task1 provides a serial link from any RS232 dumb terminal to the download
manager. As a precaution, a password is requested before the user can 
actually proceed to talk to the download manager.
*****************************************************************************/
indirect task1(void)
{
	 int ercode;

	 lc_printf("DownLoad Manager    ");

#if ROM==0
	 reload_vec(14, Dz0_circ_int); // Reload vector during development in RAM
											 // takes over serial port from Dynamic C
#endif
	 Dinit_z0(rbuf, tbuf, RBUFSIZE, TBUFSIZE, 4, IBAUD / 1200, ismodem, isecho);
	 while (1)
	 {
		  while (!EnterPassword()); // has to wait for proper password
		  MainMenu();               // show main menu or help menu
		  Dz0send_prompt();
		  while(1)    // wait for serial reception before executing commands
		  {
				ercode = ReadCom(readbuf);
				if (ercode == 1)      // a non NULL stream is received.
					 ProcessCommand(readbuf); // a non NULL stream is received.
				if (ercode == 2)      // a NULL stream is received.
					 Dz0send_prompt(); // a NULL stream is received.
		  }
	 }
}

/********************************************************************
The background task
	  Low priority task can be run under the background task.
	  The real time kernel needs to have a background task that would be
	  running if the higher priority tasks are not running.
**********************************************************************/
indirect backgnd(void)
{
	 request(TASK1);
	 while (1)
		  ;
}

/**********************************************
void ProcessCommand(char *buf)
	 - process the serial stream for command.
***********************************************/
void ProcessCommand(char *buf)
{
	 Dz0send_prompt();
	 switch (*(buf))
	 {
		  case 'm':
		  case 'M':
				MainMenu();      // display command choices
				break;
		  case 'u':
		  case 'U':
				RecFileUp();     // receive binary file
				break;
		  case 'r':
		  case 'R':
				ReadMem();       // read memory
				break;
		  case 'w':
		  case 'W':
				WriteMem();      // write byte to memory
				break;
		  case 'p':
		  case 'P':
				ChangePassword();// change password
				break;
		  case 'c':
		  case 'C':
				ChangeRamBase(); // change ram base address
				break;
		  case 'j':
		  case 'J':
				WriteCom("Checking Download File", 22);
				Dz0send_prompt();
				if (CheckCrc() == 1)
				{
					 resetZ180Int();           // turn off interrupts
					 newbbr(0x2200, next_bbr); // jump to uploaded program
				}
				WriteCom("bad crc", 7);
				break;
		  default:
				WriteCom("bad command", 11);
				break;
	 }
	 Dz0send_prompt();
	 Dz0send_prompt();
}

/***************************************************
void MainMenu()
	- Display the main/help menu.
****************************************************/
void MainMenu(void)
{
	 WriteCom("DownLoad Manager", 16);
	 Dz0send_prompt();
	 
	 WriteCom("(m) Main Menu", 13);
	 Dz0send_prompt();
	 
	 WriteCom("(r) Read from memory location", 29);
	 Dz0send_prompt();
	 
	 WriteCom("(w) Write to memory location", 28);
	 Dz0send_prompt();
	 
	 WriteCom("(j) Jump to uploaded program", 28);
	 Dz0send_prompt();
	 
	 WriteCom("(u) Upload bin file", 19);
	 Dz0send_prompt();
	 
	 WriteCom("(c) Change Base Address of Ram Code", 35);
	 Dz0send_prompt();
	 
	 WriteCom("(p) Password Change", 19);
	 Dz0send_prompt();
	 
	 WriteCom("<CR> to accept stream", 21);
	 Dz0send_prompt();
	 
	 WriteCom("NULL<CR> terminates a command", 29);
}

/*************************************************************************
void RecFileUp()
	 - Receive binary file to memory location.
	 - This function will prompt for the starting hex location of the file.
	 - The starting location of the code should be at least 8c000.
**************************************************************************/
void RecFileUp(void)
{
	 char  mydata[10];
	 
	 WriteCom("Upload Program", 14);
	 Dz0send_prompt();
	 switch (Dxmodem_z0up(nxtBaseAddress, &no_of_pages, 0, ParseData))
	 {
		  case 0:
				WriteCom("TIMEOUT", 7);
				break;
		  case 1:
				WriteCom("TRANSFER OKAY", 13);
				break;
		  case 2:
				WriteCom("CANCELLED", 9);
				break;
		  default:
				break;
	 }
	 Dreset_z0rbuf();
}

/**********************************************************
void ReadMem()
	 - Read 16 bytes of data from requested memory location.
***********************************************************/
void ReadMem(void)
{
	 unsigned long longAddress;
	 int   i, h_nib, l_nib;
	 char  mybuf[16];
	 char  send_buf[48];

	 WriteCom("Enter Hex Address >> ", 21);
	 if (ReadCom(readbuf) == 2) 
		  return;
	 Dz0send_prompt();
	 longAddress = AtoLH(readbuf);
	 xmem2root(longAddress, mybuf, 0x10);
	 for (i = 0; i < 16; i++)
	 {
		  h_nib = (mybuf[i] & 0xf0) >> 4;
		  if (h_nib > 9) 
				send_buf[3 * i] = h_nib - 10 + 0x61;
		  else 
				send_buf[3 * i] = h_nib + 0x30;

		  l_nib = (mybuf[i] & 0x0f);
		  if (l_nib > 9) 
				send_buf[3 * i + 1] = l_nib - 10 + 0x61;
		  else 
				send_buf[3 * i + 1] = l_nib + 0x30;

		  send_buf[3 * i + 2] = ' ';
	 }
	 WriteCom(send_buf, 48);
}

/********************************************************
void WriteMem()
	- Write byte data to requested memory location.
*********************************************************/
void WriteMem(void)
{
	 char mybuf[2];
	 unsigned long longAddress;
	 int  h_nib, l_nib;
	 
	 WriteCom("Enter Hex Address >> ", 21);
	 if (ReadCom(readbuf) == 2) 
		  return;
	 longAddress = AtoLH(readbuf);
	 Dz0send_prompt();
	 WriteCom("Enter Hex Byte >> ", 18);
	 if (ReadCom(readbuf) == 2) 
		  return;
	 h_nib = *(readbuf);
	 l_nib = *(readbuf + 1);

	 if (h_nib >= '0' && h_nib <= '9') 
		  h_nib = (h_nib - 0x30) << 4;
	 else if (h_nib >= 'a' && h_nib <= 'f') 
		  h_nib = (h_nib - 0x61 + 10) << 4;

	 else if (h_nib >= 'A' && h_nib <= 'F') 
		  h_nib = (h_nib - 0x41 + 10) << 4;

	 if (l_nib >= '0' && l_nib <= '9') 
		  l_nib = (l_nib - 0x30);
	 else if (l_nib >= 'a' && l_nib <= 'f') 
		  l_nib = (l_nib - 0x61 + 10);
	 else if (l_nib >= 'A' && l_nib <= 'F') 
		  l_nib = (l_nib - 0x41 + 10);

	 mybuf[0] = h_nib + l_nib;
	 root2xmem(mybuf, longAddress, 1);
}

/**********************************************************************
void ChangeRamBase()
  - Change the Starting Address for the Uploaded Code
***********************************************************************/
void ChangeRamBase(void)
{
	 unsigned long tempBaseAddress;

	 WriteCom("Current Base Address >>  ", 25);
	 ShowBaseAddress(nxtBaseAddress);
	 Dz0send_prompt();
	 WriteCom("Enter New Hex Address >> ", 25);
	 if (ReadCom(readbuf) == 2) 
		  return;              // NULL string
	 Dz0send_prompt();
	 tempBaseAddress = AtoLH(readbuf);
	 if (tempBaseAddress < 0x8c000L)
	 {
		  WriteCom("Load Address Below 8c000", 24);
		  Dz0send_prompt();
		  WriteCom("Base Address Not Changed", 24);
	 }
	 else
	 {
		  nxtBaseAddress = tempBaseAddress;
		  next_bbr = (unsigned)((nxtBaseAddress - 0x2000) >> 12) | 0xe200;
		  WriteCom("New Base Address >> ", 20);
		  ShowBaseAddress(nxtBaseAddress);
	 }
}

/*******************************************************************
void ShowBaseAddress(unsigned long baseaddress)
	 - Convert a long variable to a 6 byte ASCII stream and send stream
		to the serial port.
********************************************************************/
void ShowBaseAddress(unsigned long baseaddress)
{
	 int  i;
	 char nib;
	 char mybuf[5];

	 for (i = 0; i < 5; i++)
	 {
		  nib = (char)((baseaddress >> ((4 - i) * 4)) & 0x0f);
		  if (nib > 9) 
				mybuf[i] = nib + 0x57;
		  else 
				mybuf[i] = nib + 0x30;
	 }
	 WriteCom(mybuf, 5);      // write stream to serial port
}

/****************************************************
unsigned long AtoLH(char *buf)
	- convert ASCII stream to long hex data.
*****************************************************/
unsigned long AtoLH(char *buf)
{
	 int i;
	 int j;
	 unsigned long c;
	 unsigned long data;

	 data = 0;
	 j = strlen(buf);
	 for (i = 0; i < j; i++)
	 {
		  c = buf[i];
		  if (c <= '9' && c >= '0') 
				c = c - 0x30;
		  else if (c <= 'F' && c >= 'A') 
				c = c - 0x41 + 10;
		  else if (c <= 'f' && c >= 'a') 
				c = c - 0x61 + 10;
		  else 
				return 0;
		  data = data + (c << (4 * (j - i - 1)));
	 }
	 return data;
}

/************************************************************************
function to check the CRC of the ram code
variables nxtBaseAddress, no_of_pages and next_bbr are globally declared
***************************************************************************/
int CheckCrc(void)
{
	 int           i, j;
	 char          mybuf[128]; // A block for the checksum
	 char          lastblocksize;
	 unsigned int  blockout;
	 unsigned int  rootcode;
	 unsigned int  accum;
	 unsigned int  data;
	 unsigned int  blocks_of_128;
	 unsigned long longAddress;
	 unsigned long crcAddress;
	 unsigned char crc_lowbyte, crc_hibyte;
	 unsigned long src_bbr;
	 unsigned long dest_bbr;
	 
	 hitwd();                  // hit watchdog
	 if (no_of_pages == 0) 
		  return 0;             // no file loaded
	 if (no_of_pages > 128) 
		  return 0;             // cannot exceed 128 pages

	 crcAddress = (((long) next_bbr & 0xff) << 12) + 0x2305;
	 xmem2root(crcAddress, mybuf, 5);
	 crc_lowbyte = mybuf[0];
	 crc_hibyte  = mybuf[1];

	 rootcode = (mybuf[4] << 8) + mybuf[3];
	 rootcode = rootcode - 0x2000 + 1; // subtract 8k of library code offset
												  // and add 1 for the last byte
	 blockout = rootcode / 128;        // start blockout
	 lastblocksize = rootcode - blockout * 128;

	 accum = 0;
	 blocks_of_128 = no_of_pages * 32;
	 for (i = 0; i < blocks_of_128; i++)
	 {
		  hitwd();   // a potentially long loop, hit watchdog since CheckCrc
						 // is called during power up before the RTK is started.
		  if((i<=blockout) || (i >=384))    // CRC of code area in this region
		  {
				longAddress = nxtBaseAddress + i * 128L;
				xmem2root(longAddress, mybuf, 128);
				if (i == 6)
				{
					 mybuf[5] = mybuf[6] = 0;
				}
				if(i==blockout)     
				{   // zero out image of data area for CRC computation
					 for (j = lastblocksize; j < 128; j++) 
						  mybuf[j] = 0;
				}
		  }
		  else
		  {
				for (j = 0; j < 128; j++) 
					 mybuf[j] = 0; // zero out image of data area for CRC comp.
		  }                    
		  accum = FastCrc(mybuf, 128, accum);
	 }
	 if (((accum >> 8) & 0xff) == crc_hibyte && (accum & 0xff) == crc_lowbyte)
	 {
		  src_bbr = inport(BBR);
		  src_bbr = (src_bbr << 12) + 0xde00;
		  dest_bbr = (((long) next_bbr & 0xff) << 12) + 0xde00;
		  for (i = 0; i < 4; i++)
		  {                    // copy 512 bytes of library data area
				xmem2root(src_bbr + i * 128L, mybuf, 128);
				root2xmem(mybuf, dest_bbr + i * 128L, 128);
		  }
		  return 1;
	 }
	 else 
		  return 0;
}

/**********************************************************************
int ParseData( .... )
  This function is called by Dxmodem_z0up.
  Writes the 128 bytes of packet into specified ram location
***********************************************************************/
indirect int ParseData(char *mydata, int nth_block,
				 unsigned long baseAddress, int dest)
{
	 unsigned long longAddress;
	 char          send_buf[255];

	 longAddress = baseAddress + 128L * (nth_block - 1);
	 root2xmem(mydata, longAddress, 128);
	 return 1;
}

/*************************************************************
int ReadCom(char *buf)
 Wait and read the serial receive buffer for ENTER terminated streams.
 Suspends to allow background task to execute ( if necessary).
**************************************************************/
int ReadCom(char *buf)
{
	 while (!Dread_z0(buf, ENTER)) 
		  suspend(1);
	 if (strlen(buf) != 0) 
		  return 1;
	 else 
		  return 2;
}

/*********************************************************************
void WriteCom(char *buf, int len)
	Write stream to transmit buffer. Waits for transmit buffer to be
	clear. Suspends to allow background task to execute ( if necessary).
***********************************************************************/
int WriteCom(char *buf, int len)
{
	 while (!Dwrite_z0(buf, len)) 
		  suspend(1);
}

/*************************************************************************
int EnterPassword()
	 Waits for valid password from the dumb terminal.
	 Returns 1 if valid password was received; else 0.
	 Returns 0 if more than 9 characters were keyed in.
	 z0_echo is a global variable in z0232.lib that controls echoing.
***************************************************************************/
int EnterPassword(void)
{
	 char buf[11];
	 char data;
	 int  count;
	 extern char z0_echo;   // 6-23-95   
									// has to declare extern to work with nointerleave
										
	 Dz0send_prompt();
	 WriteCom("Enter Password", 14);
	 Dz0send_prompt();
	 count   = 0;
	 z0_echo = 0;             // set to no echo
	 while (count < 10)
	 {
		  if (Dread_z01ch(&data))
		  {
				if (data == ENTER)
				{
					 z0_echo = isecho;
					 buf[count] = '\0';
					 if (strcmp(buf, password) == 0)
					 {
						  Dz0send_prompt();
						  return 1;
					 }
					 else 
						  return 0;
				}
				buf[count++] = data;
				Dwrite_z01ch('*');
		  }
	 }
	 z0_echo = isecho;        // reset the echo mode
	 return 0;
}


/********************************************************************
void ChangePassword()
	Change password through the dumb terminal.
	z0_echo is a global variable in z0232.lib that controls echoing.
*********************************************************************/
void ChangePassword(void)
{
	 char buf[11];
	 char data;
	 int  count;

	 WriteCom("Enter New Password", 18);
	 Dz0send_prompt();
	 count   = 0;
	 z0_echo = 0;             // set to no echo
	 while (count < 10)
	 {
		  if (Dread_z01ch(&data))
		  {
				if (data == 0x1b)
				{
					 z0_echo = isecho;
					 Dz0send_prompt();
					 WriteCom("Password Not Changed", 19);
					 return;
				}
				if (data == ENTER)
				{
					 buf[count] = '\0';
					 Dz0send_prompt();
					 if (strlen(buf) == 0) WriteCom("Password Not Changed", 19);
					 else
					 {
						  WriteCom("Password Changed", 16);
						  strcpy(password, buf);
					 }
					 z0_echo = isecho;
					 return;
				}
				buf[count++] = data;
				Dwrite_z01ch('*');
		  }
	 }
	 z0_echo = isecho;        // reset echo mode
}

/***************************************************
void ResetOutputs()
  - House keeping function during startup.
****************************************************/
void ResetOutputs(void)
{
	 outport(KEYR1, 0);
	 outport(KEYR2, 0);
	 outport(KEYR3, 0);
	 outport(KEYR4, 0);       // make sure all keypad drivers are off
	 up_setout(1, 0);         // output 1 is off ( could also be on )
	 up_setout(2, 0);         // output 2 is off
	 up_setout(3, 0);
	 up_setout(4, 0);
	 up_setout(5, 0);
	 up_setout(6, 0);
	 up_setout(7, 0);
	 up_setout(8, 0);
	 up_setout(9, 0);
	 up_setout(10, 0);
	 up_setout(11, 0);
	 up_setout(12, 0);
	 up_daccal(0);            // dac output is also zero
}

/************************************************************************
CheckStartMode()
	- Check the start up mode of the CPLC based on the key pressed on the
	  keypad.
*************************************************************************/
int CheckStartMode(void)
{
	 int  i;
	 char readkey1, readkey2;

	 outport(KEYR2, 1);
	 readkey1 = inport(DREG1);
	 outport(KEYR2, 0);
	 if (readkey1 != KRELOAD) 
		  return 0; // keys not pressed
	 for (i = 0; i < 1000; i++)
		  ;                    // delay a bit for debouncing
	 outport(KEYR2, 1);
	 readkey2 = inport(DREG1);
	 outport(KEYR2, 0);
	 if (readkey2 != KRELOAD) 
		  return 0;
	 return 1;
}


int crctable[256];
/*************************************************************************
 int MakeCrcTable()
	  make crc table for faster computation of crc values using lookup table.
***************************************************************************/
#asm
MakeCrcTable::
		  ld c, 0
		  ld de, crctable
		  push de
do_data:
		  ld h,0
		  ld l,0
		  ld d,c    ; use d for current data
		  ld b,8    ; rotate and xor data 8 times
eight_times:
		  ld a,h    ; xor data to accum
		  xor d
		  rlca
		  jr nc,no_poly   ; no carry, no need to xor polynomial
		  add hl,hl
		  ld a,0x10       ; xor 0x1021 to accum
		  xor h
		  ld h,a
		  ld a,0x21
		  xor l
		  ld l,a
		  jr rotate_data
no_poly:                ; only need to shift accum
			add hl,hl
rotate_data:
		  xor a
		  rl d
		  djnz eight_times   ; rotate data eight times
		  pop de
		  ex de,hl
		  ld (hl),e
		  inc hl
		  ld (hl),d
		  inc hl
		  ex de,hl
		  push de
		  inc c             ; compute 256 values
		  jr nz,do_data
		  pop de
		  ret
#endasm

/**************************************************************************
unsigned int FastCrc(char *dataarray, char count, int accum)
  get the crc value given the char array and the size of the array
  also pass a previous crc value for accumulation or 0 if no need
  return the 16 bit crc in hl
  This is the fast method, using the crc table generated with MakeCrcTable.
***************************************************************************/
#asm
FastCrc::
		  ld hl,0002   ; locate first 2 byte argument
		  add hl,sp
		  ld e,(hl)
		  inc hl
		  ld d,(hl)
		  push de      ; de has the address of the data
		  inc hl       ; two more increment get the second 1 byte argument
		  ld b,(hl)    ;  b has the number of counts
		  inc hl       ; now pointing to the low byte of accum
		  inc hl
		  ld c,(hl)    ; c has the low byte of the accum
		  inc hl
		  ld h,(hl)    ; h has the high byte of the accum
do_data:            ; compute crc for (de+1) bytes of data
		  pop de     ;
		  ld a,(de) ; load first data
		  inc de    ; update to next memory location
		  push de   ; keep last memory location
		  xor h     ; xor data with high byte of the accum
		  ld l,a
		  ld h,0
		  add hl,hl  ; index to interger table
		  ld de,crctable
		  add hl,de
		  ld e, (hl) ; low byte
		  inc hl
		  ld d, (hl) ; high byte
		  ld a,c     ; low byte of accumulator
		  xor d
		  ld h,a
		  xor a
		  xor e
		  ld c,a
		  djnz do_data
		  ld l,c    ; copy low byte of the accumulator to l
		  pop de    ; pop one last time to balance loop
		  ret
#endasm


