#include <iostream.h>
#include <windows.h>
#include <math.h>

//----------------------------------------------------------------------------------------------
//--Class Definition----------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------

class PPort
{
public:
	
	PPort();
	// Initializes the addresses and bits to use for various things, loads the dll and processes

	~PPort();
	// Closes the loaded inpout32.dll 

	void SetPPort(short bAddr=0x378, short rSAddr=2, unsigned char rSBit=2, short eAddr=2, unsigned char eBit=0);
	//Set the base address, default is 378h for most computers.  set the address for the rs bit and e bit
	//  relative to the base address, and what number their bit on that address is
	
	//void SetPPort(short bAddr=0x378, short rSAddr=0x37A, unsigned char rSBit=2, short eAddr=0x37A, unsigned char eBit=0);
	// If the user wants to specify nonstandard addresses and bits for these things, then so be it  (Old version with non relative addresses)

	void SetByte(unsigned char data, unsigned char relBase=0);
	//Sets the data on the data port to the byte in these parameters, relBase is
	//  how many bytes from the base address you are

	bool SetBit(unsigned char thisBit, unsigned char relBase=0);
	//Sets the specified bit (#0-7) of the data port to 1, returns false if it aleady was 1
	//  relBase is how many bytes from the base address you are

	bool ResetBit(unsigned char thisBit, unsigned char relBase=0);
	//Resets the specified bit (#0-7) to 0, returns false if it already was 0
	//  relBase is how many bytes from the base address you are

	bool ChangeToInstructionRegister();
	//Switches to connected controller's instruction register by RS=0, false if it already was 

	bool ChangeToDataRegister();
	//Switched to connected contoller's data register by setting RS=1, false if it already was

	void StrobeEPin();
	//brings the E pin high, holds for at least 450ns, then returns to low
		
	//This function converts a decimal byte to the bool [8] binary array, with binArray[0] the least significant bit
	//  binArray[8] the most
	void DecToBin(unsigned char byte, bool* binArray);
	
	//Converts a 8 bit boolean array to decimal assuming bits[0] is least significant bit, and bits[7] the most significant
	unsigned char BinToDec(bool bits[]);
	
private:

	//This function outputs to the addr the given data
	void POut(short addr, short data);

	//Refreshes the bool DataBits [8] onto the data port
	void RefreshData(unsigned char relBase=0);

	//Stores the status of what, should at least, be on the parallel ports pins
	//the address is base address plus the number inside the first set of elements
	bool DataBits [3][8];
	
	//0 if on instruction register, 1 if on data register
	bool RegisterStatus;

	//status fo the E pin
	bool* EStatus;

	//typedefs for input and output functions
	typedef short (_stdcall *pinp) (short);
	typedef void (_stdcall *pout) (short, short);
	HMODULE hMod; //handle module for the .dll

	//the input and output function pointer variables
	pinp inp;
	pout out;
		
	//stores the values for the base address, the address with the RS pin, and then E pin
	short BAddr, RSAddr, EAddr;	
	
	//which bit the RS and E pin are (0-7) on their respective addresses
	unsigned char RSBit, EBit;


};

//----------------------------------------------------------------------------------------------
//--Class Implementation------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------

PPort::PPort()
{
	//Set addresses and bits
	SetPPort();
/*
	//Load inpout32.dll and get the function pointers set up
	hMod = LoadLibrary("inpout32.dll");

	if (!hMod) 
	{
		cout<<"Load library failed\n";
	}

	// get the address of the function 

	inp = (pinp)GetProcAddress(hMod,"Inp32");

	if(!inp)
	{
		cout<<"Error getting inp's proc address\n";
	}
	
	out = (pout)GetProcAddress(hMod,"Out32");
	if (!out)
	{
		cout<<"GetProcAddress for out Failed.\n";
	}
	*/
}
//----------------------------------------------------------------------------------------------
PPort::~PPort()
{
	FreeLibrary(hMod);
}
//----------------------------------------------------------------------------------------------
void PPort::SetPPort(short bAddr, short rSAddr, unsigned char rSBit, short eAddr, unsigned char eBit)
{
		//Load inpout32.dll and get the function pointers set up
	hMod = LoadLibrary("inpout32.dll");

	if (!hMod) 
	{
		cout<<"Load library failed\n";
	}

	// get the address of the function 

	inp = (pinp)GetProcAddress(hMod,"Inp32");

	if(!inp)
	{
		cout<<"Error getting inp's proc address\n";
	}
	
	out = (pout)GetProcAddress(hMod,"Out32");
	if (!out)
	{
		cout<<"GetProcAddress for out Failed.\n";
	}
	
	//Put the parameters into the class's private variables
	BAddr=bAddr;
	RSAddr=rSAddr;
	EAddr=eAddr;
	RSBit=rSBit;
	EBit=eBit;
}

//----------------------------------------------------------------------------------------------
void PPort::DecToBin(unsigned char byte, bool* binArray)
//Pre: byte is a value 0-255
//Post: global var DataBits [8] will contain a binary representation of byte, ie byte=b'10100' and DataBits={1,0,1,0,0,}
{
	unsigned char curNum = byte;
	int dec [] = {128,64,32,16,8,4,2,1};
	short binArrayIterator = 7;
	for(int i=0; i<=7; i++)
	{
		if(curNum - dec[i] >= 0)
		{
			curNum -= dec[i];
			binArray[binArrayIterator] = 1;
		}
		else
			binArray[binArrayIterator] = 0;
		binArrayIterator--;
	}
}
//----------------------------------------------------------------------------------------------
unsigned char PPort::BinToDec(bool bits[])
//Pre: bits has no more than 8 elements
//Post: returned is the decimal equivalent of those 8 elements
{
	unsigned char final =0;
	//int value[] = {128,64,32,16,8,4,2,1}; //okay so it's a cheap trick i did because i was in a hurry, so what
	int value[] = {1,2,4,8,16,32,64,128};
	for(int i=0; i<=7; i++)
	{
		if(bits[i]==1)
			final += value[i];
	}
	return final;
}
//----------------------------------------------------------------------------------------------
void PPort::POut(short addr, short data)
{
	out(addr, data);
}
//----------------------------------------------------------------------------------------------
void PPort::RefreshData(unsigned char relBase)
{
	POut(BAddr + relBase, BinToDec(DataBits[relBase]));	
}
//----------------------------------------------------------------------------------------------
void PPort::SetByte(unsigned char data, unsigned char relBase)
{
	DecToBin(data, DataBits[relBase]);
	RefreshData(relBase);
}
//----------------------------------------------------------------------------------------------
bool PPort::SetBit(unsigned char thisBit, unsigned char relBase)
{
	if(DataBits[relBase][thisBit]==1)
		return false;
	else
		DataBits[relBase][thisBit]=1;
	RefreshData(relBase);
	return true;
}
//----------------------------------------------------------------------------------------------
bool PPort::ResetBit(unsigned char thisBit, unsigned char relBase)
{
	if(DataBits[relBase][thisBit]==0)
		return false;
	else
		DataBits[relBase][thisBit]=0;
	RefreshData(relBase);
	return true;
}
//----------------------------------------------------------------------------------------------
bool PPort::ChangeToInstructionRegister()
{
	if(DataBits[RSAddr][RSBit] == 0)
		return false;
	else
		ResetBit(RSBit, RSAddr);
	return true;
}
//----------------------------------------------------------------------------------------------
bool PPort::ChangeToDataRegister()
{
	if(DataBits[RSAddr][RSBit] == 1)
		return false;
	else
		SetBit(RSBit, RSAddr);
	return true;
}
	
//----------------------------------------------------------------------------------------------
void PPort::StrobeEPin()
{
	SetBit(EBit, EAddr);
	_sleep(1);//to hold for the 450ns, even though this is a shitload of ns's
	ResetBit(EBit, EAddr);
}

