#include "headers.h"

int currentMem, memshowRange, MemAddr=0, MemPage=0;
bitset<16> ac(0);
bitset<16> pc(0);
bitset<16> ir(0);
bitset<16> mar(0);
bitset<16> marlower(0);
bitset<16> marupper(0);
bitset<16> mdr(0);
bitset<16> mdrlower;
bitset<16> mdrupper;
bitset<16> bus(0);
bitset<16> memory[2][4095];

int main()
{
	int choice;
		
	srand(time(NULL));

	pc = rand();
	ac = rand();
	ir = rand();
	mar = rand();
	mdr = rand();
	bus = 0;

	Banner4(true, '#', "Welcome to the simulation of this old computer!");
	resetmemory();
			
	while (true)
	// Loop draws menu and prints out values of registers, exit if 4.
	{
		Banner1(false, '*', "8-Instruction, 16-bit Computer Simulation", 1);

		cout << "Bus -> " << bus << endl;
		cout << "MDR -> " << mdr << endl;
		cout << "MAR -> " << mar << endl;
		cout << "IR  -> " << ir << endl;
		cout << "AC  -> " << ac << endl;
		cout << "PC  -> " << pc << endl;

		cout << endl << "Make a selection";
		cout << endl << "----------------------------------";
		cout << endl << "	1. Howto use this program";
		cout << endl << "	2. Enter data into memory";
		cout << endl << "	3. Boot computer in step mode";
		cout << endl << "	4. Boot computer in regular mode";
		cout << endl << "	5. Exit the simulation";
		cout << endl << "\nChoice: ";
		cin >> choice;

		if (choice==1)
			help();
			
		if (choice==2)
			entermem();

		if (choice==3)
			boot(1);
		
		if (choice==4)
			boot(0);

		if (choice==5)
			return 0;
		
		system("cls");
	}

	return 0;
}


void help()
{

}


/* 	REALLY Need to clean this up.. create a function to understand my own
	code of memory address and instructions. I need to remove redundant code
	the code converting is on hold for right now because its gonna be a pain.
*/
void entermem()
{
	int choice;
	string buffer;
	
	if (currentMem == NULL)
	{
		currentMem = 0;
		MemPage=0;
	}
	
	if (memshowRange == NULL)
		memshowRange = 9;
	
	while (choice != 5)
	{
		if (currentMem>5)
			MemAddr = currentMem - memshowRange/2;	
		if (currentMem<4096 && currentMem>(4096-memshowRange))
			MemAddr = 4096 - memshowRange;
		else
			MemAddr = 0;

		Banner1(false, 'x', "Memory", 0);

		cout << "---- displaying "<< memshowRange <<" locations of memory ----\n" << endl;
	
		for (int m=0; m < memshowRange; m++)
		{
			cout << " Location [" << MemPage << "-" << MemAddr << "]: " << memory[MemPage][MemAddr];

			if (MemAddr==currentMem)
				cout << "      <-- Current";

			cout << endl;

			MemAddr++;
		}

		cout << endl << "Make a selection";
		cout << endl << "----------------------------------";
		cout << endl << "	1. Change amount of locations to view";
		cout << endl << "	2. Change current memory location";
		cout << endl << "	3. Edit current memory location";
		cout << endl << "	4. Reset all memory to default";
		cout << endl << "	5. Return to previous menu";
		cout << endl << "\nChoice: ";
		cin >> choice;

		if (choice==1)
		{
			cout << "\nLocations to view: ";
			cin >> memshowRange;

			while(memshowRange > 9)
			{
				cout << "Invalid Amount! New Amount: ";
				cin >> memshowRange;
			}	
		}

			
		if (choice==2)
		{
			cout << "New Page: ";
			cin >> MemPage;

			while(MemPage > 1)
			{
				cout << "Invalid Page!\nNew Page: ";
				cin >> MemPage;
			}
			
			cout << "New Addr: ";
			cin >> currentMem;

			while(currentMem > 4095)
			{
				cout << "Invalid Addr!\nNew Addr: ";
				cin >> currentMem;
			}
		}
			

		if (choice==3)
		{
			cout << "Enter value: ";
			cin >> memory[MemPage][currentMem];
		}

		if (choice==4)
			resetmemory();
	
		system("cls");
	}

}



void resetmemory()
{
	for (int p=0; p<2; p++)
	{
		for (int a=0; a<4096; a++)
			memory[p][a] = 0;
	}
}


void convertcode(string code)
{
	// Future Expansion	
}


void splitmar()
{
	for (int x=0; x <12; x++)
		marlower[x] = mar[x];
	
	for (int x=0; x<4; x++)
		marupper[x] = mar[x+12];

}

void splitmdr()
{
	for (int x=0; x <12; x++)
		mdrlower[x] = mdr[x];
	
	for (int x=0; x<4; x++)
		mdrupper[x] = mdr[x+12];
}


void boot(int step)
{
	pc = 0;
	fetch();
}


void fetch()
{
	bool running = true;

	while (running)
	{
		// MAR, MDR, IR, PC, AC are bitsets
	
		mar = pc.to_ulong();
		splitmar();
		// Have to split up the bit set to get the upper 4 lower 12
		// have method to split bits
		mdr = memory[marupper.to_ulong()][marlower.to_ulong()].to_ulong();
	
		// Good so far to here
		pc = pc.to_ulong() + 1;
	
		splitmdr();
		ir = mdrupper;

		// Look at IR to figure out what to do next
		// once it knows which instruction is in the ir call that method

		if (ir.to_ulong()==0)
			add(0);
	
		if (ir.to_ulong()==1)
			and(0);
	
		if (ir.to_ulong()==2)
			load(0);
	
		if (ir.to_ulong()==3)
			store(0);
	
		if (ir.to_ulong()==4)
			branch(0);
	
		if (ir.to_ulong()==5)
			jump(0);
	
		if (ir.to_ulong()==6)
			not();
	
		if (ir.to_ulong()==7)
			shiftright();
	
		if (ir.to_ulong()==8)
			add(1);
	
		if (ir.to_ulong()==9)
			and(1);
	
		if (ir.to_ulong()==10)
			load(1);
	
		if (ir.to_ulong()==11)
			store(1);
	
		if (ir.to_ulong()==12)
			branch(1);
	
		if (ir.to_ulong()==13)
			jump(1);

		if (memory[marupper.to_ulong()][marlower.to_ulong()+1]==0)
			running = false;

	}
	
}


void load(int ind)
{
	if (ind==1)
	{
		indirect(0);
	}
	
	mdr = memory[0][mdrlower.to_ulong()].to_ulong();

	ac = mdr.to_ulong();

}


void add(int ind)
{
	if (ind==1)
	{
		indirect(0);
	}

	mdr = memory[0][mdrlower.to_ulong()].to_ulong();

	ac = mdr.to_ulong() + ac.to_ulong();
	
}


void and(int ind)
{
	if (ind==1)
	{
		indirect(0);
	}

	mdr = memory[0][mdrlower.to_ulong()].to_ulong();
	
	ac &= mdr.to_ulong();

}


void store(int ind)
{
	if (ind==1)
	{
		indirect(0);
	}

	mdr = ac;

	memory[0][mdrlower.to_ulong()] = ac;
	
}


void branch(int ind)
{
	if (ind==1)
	{
		jump(1);
	}

	if (ac[15]==1)
		jump(0);

}


void jump(int ind)
{
	if (ind==1)
	{
		indirect(1);
	}

	pc = mar.to_ulong();
	//mdr = memory[marupper.to_ulong()][marlower.to_ulong()];

}


void not()
{
	ac.flip();
}


void shiftright()
{
	ac.operator >>=(1);
}


void indirect(int loc)
{
	mdr = memory[marupper.to_ulong()][marlower.to_ulong()];

	// Passing this function a 1 it will set the pc equal to mar
	// the only instructions that will use this is branch and jump
	if (loc==1)
		pc = mar;
	else
		mar = mdr;
		splitmar();

}

