#define  MaxCount     0x00003FFF       /*Max # that the HC393s can count*/
#define  DataPort     BaseAddress      /*LPT Data port address*/
#define  StatusPort   BaseAddress + 1  /*LPT Satus port address*/
#define  ControlPort  BaseAddress + 2  /*LPT Control port address*/

#define  HI           0xFF
#define  LO           0x00
#define  IN           0xFF
#define  OUT          0x00

unsigned int  BaseAddress;             /*Base address of LPT port found*/
unsigned char *EPROM;                  /*Pointer to mem alloc'ed for ROM*/
unsigned char DDataPort;               /*Dummy Data port*/
unsigned char DControlPort;            /*Dummy Control port*/

void Init(void);
void Exit(int ErrLevel);
int  ResetCounter(void);
int  AddressClock(void);
void ClearPins(void);
void ClearMemory(void);
void SetData(unsigned char OutByte);
void SerDataAB(char State);
void SerClock(void);
void VCC(char Volts);
void VPP(char Volts);
void OEData(char State);
void OEEpromA15(char State);
void CEPGM(char State);
void A14PGM(char State);
void PrintMenu(void);
void DoMenu(int  MenuOption);
void ReadS19File(void);
void ReadHEXFile(void);
void ReadRAWFile(void);
void WriteRAWFile(void);
int  FastProgram(void);
int  SlowProgram(void);
int  Verify(unsigned char CheckData);
char ReadData(void);
int  Jumble(int AddressIn);
int  UnJumble(int AddressIn);
void BackSpace(int  Total);


void Init(void)
/*
Put DDataPort (Dummy Data Port) in known state
Put DControlPort (Dummy Control Port) in known state

Try to reset the HC393 counter at all three possible addresses
(0x0378, 0x0278 and 0x03BC)

If ResetCounter can generate an overflow at any of the three address
then remember that address

If the programmer was FoundAt any address set the BaseAddress
If the programmer was not found print error and exit

Allocate memory for the EPROM data (exit on fail)

Clear the memory to all FF's

Clear all the pins on the EPROM to 0V so it is safe to insert a chip
*/
{
unsigned int  FoundAt = 0;

DDataPort = 0;
DControlPort = 0;

puts("Waiting for programmer response");
BaseAddress = 0x0378;
if(!ResetCounter())
    {
     FoundAt = BaseAddress;
    }
BaseAddress = 0x0278;
if(!ResetCounter())
    {
     FoundAt = BaseAddress;
    }
BaseAddress = 0x03EB;
if(!ResetCounter())
    {
     FoundAt = BaseAddress;
    }
if(FoundAt)
    {
     BaseAddress = FoundAt;
     printf("Programmer found at %3X\n", FoundAt);
    }
else
    {
     puts("Error programmer not found");
     Exit(1);
    }

if ((EPROM = malloc(EPROMSize)) == NULL)
    {
     puts("Error allocating memory");
     Exit(2);
    }

ClearMemory();
ClearPins();
}


void Exit(int ErrLevel)
/*
Set all pins on EPROM to 0v to make it safe to remove chip

If exiting with an error level of three (SetData Failed) the don't
try to use SetData
*/
{
if(ErrLevel != 3)
    {
     SetData(0x00);
    }

OEData(HI);
OEEpromA15(LO);
VPP(0);
CEPGM(LO);
A14PGM(LO);
ResetCounter();
VCC(0);

exit(ErrLevel);
}


int  ResetCounter(void)
/*
The HC393's on the hardware have the 14th output conected as an overflow
to the parrallel port.  This is so the computer can both reset the counter
and tell if the counter is working.

To reset the counter the computer keeps clocking the HC393's until an
overflow occours.  At this point the lowwer 13 address lines are 0.

If an overflow does not occour after 16384 (2^14) clocks the counter is
not working.

Set the check counter TotalCLocks to 0

Keep clocking the 393's until either an overflow is generated by
AddressClock() or more than 16384 (MaxCount) clocks have occoured.

If an overflow is generated return 00
If to many counts (hardware not working) return FF
*/
{
unsigned int  TotalClocks = 0;

while (!AddressClock())
    {
     TotalClocks ++;
     if (TotalClocks > MaxCount)
	 {
	  return(0xFF);
	 }
    }

return(0x00);
}


int  AddressClock(void)
/*
Read the state of HC393 bit 14 (Overflow) int DummyOverflow

Send HC393 CLK High then Low

Compare DummyOverflow with Overflow after the clock signal.

If there has been a change in Overflow return FF
else return 00
*/
{
char DummyOverflow;

DummyOverflow = (inportb(StatusPort) & 0x08);

DControlPort = DControlPort | 0x02;
outportb(ControlPort, DControlPort);

DControlPort = DControlPort & 0xFD;
outportb(ControlPort, DControlPort);

if (DummyOverflow != (inportb(StatusPort) & 0x08))
    {
     return(0xFF);
    }
return(0x00);
}


void ClearPins(void)
/*
Set all pins on ZIF socket to 0v
*/
{
SetData(0x00);
OEData(HI);
OEEpromA15(LO);
VPP(0);
CEPGM(LO);
A14PGM(LO);
ResetCounter();
VCC(0);

puts("Safe to insert/remove EPROM");
}


void ClearMemory(void)
/*
Set all memory locations in EPROM data to FF (erased state)
*/
{
long int  AddressCounter;

for(AddressCounter = 0; AddressCounter <= EPROMSize; AddressCounter++)
    {
     EPROM[AddressCounter] = 0xFF;
    }
}


void SetData(unsigned char OutByte)
/*
Clocks out eight bits of Data by setting the data with SerDataAB()
followed by a clock on SerClock().

The eight bits go out in the order 21076534 due to the routing on
thePCB.

A final SerClock() is made because the output buffer on the HC595
is one clock behind the shift register.
*/
{
if (OutByte & 0x04) SerDataAB(HI);
    else            SerDataAB(LO);
SerClock();
if (OutByte & 0x02) SerDataAB(HI);
    else            SerDataAB(LO);
SerClock();
if (OutByte & 0x01) SerDataAB(HI);
    else            SerDataAB(LO);
SerClock();
if (OutByte & 0x80) SerDataAB(HI);
     else           SerDataAB(LO);
SerClock();
if (OutByte & 0x40) SerDataAB(HI);
    else            SerDataAB(LO);
SerClock();
if (OutByte & 0x20) SerDataAB(HI);
    else            SerDataAB(LO);
SerClock();
if (OutByte & 0x08) SerDataAB(HI);
    else            SerDataAB(LO);
SerClock();
if (OutByte & 0x10) SerDataAB(HI);
    else            SerDataAB(LO);
SerClock();
SerClock();

DataDirection(IN);
if (OutByte != ReadData())
    {
     puts("Error in hardware (595/157)");
     Exit(3);
    }
}


void SerDataAB(char State)
/*
Sets the pin on the parrallel port to control A/!B and SerIn

The Serial In pin (HC595) and the A/!B select pin (HC157) share
an out put pin on the parrallel port.
*/
{
if (State)
    {
     DControlPort = DControlPort & 0xF7;
    }
else
    {
     DControlPort = DControlPort | 0x08;
    }
outportb(ControlPort, DControlPort);
}


void SerClock(void)
/*
Send SerClock on the HC595 High then Low.
*/
{
DControlPort = DControlPort & 0xFB;
outportb(ControlPort, DControlPort);
DControlPort = DControlPort | 0x04;
outportb(ControlPort, DControlPort);
}


void VCC(char Volts)
/*
Turns on appropriate transistors to set 0,5 or 6 volts on VCC
*/
{
switch (Volts)
    {
     case 0 :
	 {
	  DDataPort = DDataPort | 0x20;
	  DDataPort = DDataPort & 0xBF;
	  break;
	 }
     case 5 :
	 {
	  DDataPort = DDataPort & 0xDF;
	  DDataPort = DDataPort & 0xBF;
	  break;
	 }
     case 6 :
	 {
	  DDataPort = DDataPort & 0xDF;
	  DDataPort = DDataPort | 0x40;
	  break;
	 }
    }
outportb(DataPort, DDataPort);
}


void VPP(char Volts)
/*
Turns on appropriate transistors to set 0,5 or 12.5 volts on VPP
*/
{
switch (Volts)
    {
     case 0 :
	 {
	  DDataPort = DDataPort | 0x01;
	  DDataPort = DDataPort & 0xFB;
	  break;
	 }
     case 5 :
	 {
	  DDataPort = DDataPort & 0xFE;
	  DDataPort = DDataPort & 0xFB;
	  break;
	 }
     case 12 :
	 {
	  DDataPort = DDataPort & 0xFE;
	  DDataPort = DDataPort | 0x04;
	  break;
	 }
    }
outportb(DataPort, DDataPort);
}


void OEData(char State)
/*
Sets the state on the OE pin on the HC595
*/
{
if (State)
    {
     DDataPort = DDataPort | 0x02;
    }
else
    {
     DDataPort = DDataPort & 0xFD;
    }
outportb(DataPort, DDataPort);
}


void OEEpromA15(char State)
/*
Sets the state on the OE (A15 on 27C512) pin on the EPROM
*/
{
if (State)
    {
     DDataPort = DDataPort | 0x08;
    }
else
    {
     DDataPort = DDataPort & 0xF7;
    }
outportb(DataPort, DDataPort);
}


void CEPGM(char State)
/*
Sets the state on the CE/PGM pin on the EPROM
*/
{
if (State)
    {
     DDataPort = DDataPort | 0x80;
    }
else
    {
     DDataPort = DDataPort & 0x7F;
    }
outportb(DataPort, DDataPort);
}


void A14PGM(char State)
/*
Sets the state on the A14/PGM pin on the EPROM
*/
{
if (State)
    {
     DDataPort = DDataPort | 0x10;
    }
else
    {
     DDataPort = DDataPort & 0xEF;
    }
outportb(DataPort, DDataPort);
}


void PrintMenu(void)
{
clrscr();
puts(TitleMessage);
puts("");
puts(" 1.BlankCheck");
puts(" 2.Program EPROM From Memory");
puts(" 3.Read EPROM to Memory");
puts(" 4.Compare EPROM with Memory");
puts(" 5.Read S19 Record From Disk");
puts(" 6.Read Intel Hex Record From Disk");
puts(" 7.Read RAW Data From Disk");
puts(" 8.Clear Memory");
puts(" 9.Write Raw Data to Disk");
puts(" E.Exit\n");

ClearPins();

puts("\nEnter Option (0-9,E) ->");
}


void DoMenu(int  MenuOption)
{
puts("");
switch (MenuOption)
    {
     case '1' :
	 {
	  BlankCheck();
	  break;
	 }
     case '2' :
	 {
	  WriteEprom();
	  VerifyEprom();
	  break;
	 }
     case '3' :
	 {
	  ReadEprom();
	  break;
	 }
     case '4' :
	 {
	  VerifyEprom();
	  break;
	 }
     case '5' :
	 {
	  ReadS19File();
	  break;
	 }
     case '6' :
	 {
	  ReadHEXFile();
	  break;
	 }
     case '7' :
	 {
	  ReadRAWFile();
	  break;
	 }
     case '8' :
	 {
	  ClearMemory();
	  break;
	 }
     case '9' :
	 {
	  WriteRAWFile();
	  break;
	 }
    }
}


void ReadS19File(void)
/*
Reads an S19 file from disk to memory

Prompt user for file name
Open file or exit if invalid

Process each line till EOF
  Read a line
  Check is data (S1) record
  Get address from front of record
  Get length of record
  Read all Data bytes
*/
{
char FileName[80];
FILE *FileHandle;
char LineRead[128];
char *DummyString;
int  ValueRead;
long int  BytesRead;
long int  OutOfRange;
long int  Length;
long int  ReadAddress;

BytesRead  = 0;
OutOfRange = 0;

printf("\nPlease Enter File Name ->");
gets(FileName);

if (NULL == (FileHandle = fopen(FileName, "r")))
    {
     puts("Cant Find File");
     return;
    }
rewind(FileHandle);

while (!feof(FileHandle))
    {
     fgets(LineRead, 128, FileHandle);
     if (LineRead[1] == '1')
	 {
	  sscanf(&LineRead[2], "%2x", &Length);
	  sscanf(&LineRead[4], "%4x", &ReadAddress);
	  Length--;
	  Length--;
	  Length--;
	  DummyString = &LineRead[8];
	  while (Length != 0)
	      {
	       if(ReadAddress <= EPROMSize)
		   {
		    BytesRead++;
		    printf(".");
		    sscanf(DummyString, "%2x", &ValueRead);
		    EPROM[ReadAddress] = ValueRead;
		   }
	       else
		   {
		    OutOfRange++;
		   }
	       ReadAddress++;
	       Length--;
	       DummyString = &DummyString[2];
	      }
	 }
    }
fclose(FileHandle);
printf("\n%4d Bytes read, %4d Bytes out of range\n",
       BytesRead,
       OutOfRange);
}


void ReadHEXFile(void)
/*
Reads an Intel Hex file from disk to memory

Prompt user for file name
Open file or exit if invalid

Process each line till EOF
  Read a line
  Check is data (S1) record
  Get address from front of record
  Get length of record
  Read all Data bytes
*/
{
char FileName[80];
FILE *FileHandle;
char LineRead[128];
char *DummyString;
int  ValueRead;
long int  BytesRead;
long int  OutOfRange;
long int  Length;
long int  ReadAddress;

BytesRead  = 0;
OutOfRange = 0;

printf("\nPlease Enter File Name ->");
gets(FileName);

if (NULL == (FileHandle = fopen(FileName, "r")))
    {
     puts("Cant Find File");
     return;
    }
rewind(FileHandle);

while (!feof(FileHandle))
    {
     fgets(LineRead, 128, FileHandle);
     if (LineRead[0] == ':')
	 {
	  sscanf(&LineRead[1], "%2x", &Length);
	  sscanf(&LineRead[3], "%4x", &ReadAddress);
	  DummyString = &LineRead[9];
	  while (Length != 0)
	      {
	       if(ReadAddress <= EPROMSize)
		   {
		    BytesRead++;
		    printf(".");
		    sscanf(DummyString, "%2x", &ValueRead);
		    EPROM[ReadAddress] = ValueRead;
		   }
	       else
		   {
		    OutOfRange++;
		   }
	       ReadAddress++;
	       Length--;
	       DummyString = &DummyString[2];
	      }
	 }
    }
fclose(FileHandle);
printf("\n%4d Bytes read, %4d Bytes out of range\n",
       BytesRead,
       OutOfRange);
}


void ReadRAWFile(void)
/*
Reads an RAW file from disk to memory

Prompt user for file name
Open file or exit if invalid

Read each byte till EOF
*/
{
char FileName[80];
FILE *FileHandle;
char ByteRead;
long int BytesRead;
long int ReadAddress;

printf("\nPlease Enter File Name ->");
gets(FileName);

if (NULL == (FileHandle = fopen(FileName, "rb")))
    {
     puts("Cant Find File");
     return;
    }
rewind(FileHandle);

ReadAddress = 0;
BytesRead = 0;

while ((!feof(FileHandle)) && (ReadAddress <= EPROMSize))
    {
     fscanf(FileHandle, "%c", &ByteRead);
     EPROM[ReadAddress] = ByteRead;
     ReadAddress++;
     BytesRead++;
    }
fclose(FileHandle);
printf("\n%4d Bytes read\n", BytesRead);
}


void WriteRAWFile(void)
/*
Writes an RAW file to disk

Prompt user for file name
Open file or exit if invalid

Write each byte till EOF
*/
{
char FileName[80];
FILE *FileHandle;
long int BytesRead;
long int WriteAddress;

printf("\nPlease Enter File Name ->");
gets(FileName);

if (NULL == (FileHandle = fopen(FileName, "wb")))
    {
     puts("File Name Not Valid");
     return;
    }
rewind(FileHandle);

for(WriteAddress = 0; WriteAddress <= EPROMSize; WriteAddress++)
    {
     putc(EPROM[WriteAddress], FileHandle);
    }
fclose(FileHandle);
printf("File Written");
}


int  FastProgram(void)
{
int TotalTime = 0;

do
    {
     TotalTime++;
    }
while((!ProgramPulse(1)) && (TotalTime < 12));

TotalTime = TotalTime * 4;
ProgramPulse(TotalTime);
return(TotalTime);
}

int  SlowProgram(void)
{
DataDirection(IN);
ProgramPulse(50);
return(50);
}


int  Verify(unsigned char CheckData)
{
DataDirection(OUT);

if (CheckData != ReadData())
    {
     return(LO);
    }
else
    {
     return(HI);
    }
}


char ReadData(void)
{
int BytePort = 0;
int ByteData = 0;

SerDataAB(LO);

BytePort = inportb(StatusPort);

if (!(BytePort & 0x80)) ByteData = ByteData + 0x02;
if (BytePort & 0x10)    ByteData = ByteData + 0x10;
if (BytePort & 0x20)    ByteData = ByteData + 0x08;
if (BytePort & 0x40)    ByteData = ByteData + 0x40;

SerDataAB(HI);

BytePort = inportb(StatusPort);

if (!(BytePort & 0x80)) ByteData = ByteData + 0x01;
if (BytePort & 0x10)    ByteData = ByteData + 0x04;
if (BytePort & 0x20)    ByteData = ByteData + 0x20;
if (BytePort & 0x40)    ByteData = ByteData + 0x80;

return(ByteData);
}


int  UnJumble(int AddressIn)
{
int DummyAddress = 0;

if (AddressIn & 0x0001)
    {
     DummyAddress = DummyAddress | 0x0001;
    }
if (AddressIn & 0x0002)
    {
     DummyAddress = DummyAddress | 0x1000;
    }
if (AddressIn & 0x0004)
    {
     DummyAddress = DummyAddress | 0x2000;
    }
if (AddressIn & 0x0008)
    {
     DummyAddress = DummyAddress | 0x0008;
    }
if (AddressIn & 0x0010)
    {
     DummyAddress = DummyAddress | 0x0800;
    }
if (AddressIn & 0x0020)
    {
     DummyAddress = DummyAddress | 0x0010;
    }
if (AddressIn & 0x0040)
    {
     DummyAddress = DummyAddress | 0x0100;
    }
if (AddressIn & 0x0080)
    {
     DummyAddress = DummyAddress | 0x0200;
    }
if (AddressIn & 0x0100)
    {
     DummyAddress = DummyAddress | 0x0040;
    }
if (AddressIn & 0x0200)
    {
     DummyAddress = DummyAddress | 0x0020;
    }
if (AddressIn & 0x0400)
    {
     DummyAddress = DummyAddress | 0x0002;
    }
if (AddressIn & 0x0800)
    {
     DummyAddress = DummyAddress | 0x0004;
    }
if (AddressIn & 0x1000)
    {
     DummyAddress = DummyAddress | 0x0400;
    }
if (AddressIn & 0x2000)
    {
     DummyAddress = DummyAddress | 0x0080;
    }
if (AddressIn & 0x4000)
    {
     DummyAddress = DummyAddress | 0x4000;
    }
if (AddressIn & 0x8000)
    {
     DummyAddress = DummyAddress | 0x8000;
    }

return(DummyAddress);
}


int  Jumble(int AddressIn)
{
int DummyAddress = 0;

if (AddressIn & 0x0001)
    {
     DummyAddress = DummyAddress | 0x0001;
    }
if (AddressIn & 0x0002)
    {
     DummyAddress = DummyAddress | 0x0400;
    }
if (AddressIn & 0x0004)
    {
     DummyAddress = DummyAddress | 0x0800;
    }
if (AddressIn & 0x0008)
    {
     DummyAddress = DummyAddress | 0x0008;
    }
if (AddressIn & 0x0010)
    {
     DummyAddress = DummyAddress | 0x0020;
    }
if (AddressIn & 0x0020)
    {
     DummyAddress = DummyAddress | 0x0200;
    }
if (AddressIn & 0x0040)
    {
     DummyAddress = DummyAddress | 0x0100;
    }
if (AddressIn & 0x0080)
    {
     DummyAddress = DummyAddress | 0x2000;
    }
if (AddressIn & 0x0100)
    {
     DummyAddress = DummyAddress | 0x0040;
    }
if (AddressIn & 0x0200)
    {
     DummyAddress = DummyAddress | 0x0080;
    }
if (AddressIn & 0x0400)
    {
     DummyAddress = DummyAddress | 0x1000;
    }
if (AddressIn & 0x0800)
    {
     DummyAddress = DummyAddress | 0x0010;
    }
if (AddressIn & 0x1000)
    {
     DummyAddress = DummyAddress | 0x0002;
    }
if (AddressIn & 0x2000)
    {
     DummyAddress = DummyAddress | 0x0004;
    }
if (AddressIn & 0x4000)
    {
     DummyAddress = DummyAddress | 0x4000;
    }
if (AddressIn & 0x8000)
    {
     DummyAddress = DummyAddress | 0x8000;
    }

return(DummyAddress);
}


void BackSpace(int  Total)
/*
prints Back space to screen X times
*/
{
int Count;

for (Count = 0; Count < Total; Count++)
    {
     printf("%c", 0x08);
    }
}
