/** TI85: TI85 EMulator    ***********************************/
/**                                                         **/
/**                           TI85.CPP                      **/
/**                                                         **/
/** This file contains implementation for the TI85-specific **/
/** hardware: Rom mapper, i/o ports etc                     **/
/** Copyright (C) Marat Fayzullin 1994,1995                 **/
/**     You are not allowed to distribute this software     **/
/**     commercially. Please, notify me, if you make any    **/
/**     changes to this file.                               **/
/** Modifications to MSX.C Copyright (C) Robert Taylor 1995.**/
/**                                                         **/
/*************************************************************/

#include "TI85.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <io.h>
#include <sys\stat.h>
#include <fcntl.h>
#include <share.h>
#include <dos.h>
#include <conio.h>
#include <alloc.h>
#include "wgtgfx.h"


/***************************************************************/
/** The z80 emulator performs it's operations on a continuous **/
/** block of memory (Called memory.ram). The ROM pages are    **/
/** stored separately in an array memory.rom[].               **/
/***************************************************************/



int Verbose;
byte CurrentRom=1;
byte CurrentKey=0;
byte KeypadMask=0;
byte PowerReg=0;
byte DisplayContrast=0;
byte LinkReg=0x0F;
int IntCount=0;
int KeyPressed=0;
byte *ScrBuf;

typedef struct {	byte *rom[8];
									byte *ram;
									} ti85memory;

ti85memory memory;


byte RealKeyMap[8] =
{0x7f,0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf
};



byte PCto85KeyMap[256] =
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x0F,0x20,0x00,0x00,0x00,0x09,0x00,0x00,
 0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x00,
 0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x1d,0x15,0x0c,0x0a,0x2b,0x0b,0x19,0x0d,
 0x21,0x22,0x1a,0x12,0x23,0x1b,0x13,0x24,
 0x1c,0x14,0x00,0x00,0x00,0x2a,0x00,0x00,
 0x00,0x00,0x00,0x17,0x00,0x37,0x00,0x2F,
 0x04,0x00,0x00,0x02,0x00,0x03,0x00,0x38,
 0x01,0x00,0x1F,0x27,0x00,0x00,0x00,0x00,
 0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x2e,0x26,0x1e,0x16,0x0e,0x2d,0x25,
 0x1d,0x15,0x0d,0x2c,0x24,0x1c,0x14,0x0c,
 0x2b,0x23,0x1b,0x13,0x0b,0x22,0x1a,0x12,
 0x0a,0x21,0x19,0x00,0x00,0x00,0x00,0x20,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};

byte PCEto85KeyMap[256] =
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x35,0x34,0x33,0x32,0x31,
 0x00,0x00,0x00,0x36,0x30,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};



/****************************************************************/
/*** Write number into given IO port.                         ***/
/****************************************************************/
void DoOut(register byte Port,register byte Value)
{

	switch(Port)
	{
		case 1:
			KeypadMask=Value;
			break;
		case 2:
			DisplayContrast=Value;
		case 5:
			MapROM(Value);
			break;
		case 6:
			PowerReg=Value;
			break;
		case 7:
			LinkReg=(LinkReg & 0xF3) | (Value & 0xC);
			break;

	}
}



/****************************************************************/
/*** Read number from given IO port.                          ***/
/****************************************************************/
byte DoIn(register byte Port)
{
	switch(Port)
	{
		case 1:                   //Keypad
			if ((KeypadMask&1) && (CurrentKey<8))
			{
				return 0xff;
			}

			if ((KeypadMask&2) && (CurrentKey<16) && (CurrentKey>8))
			{
				return 0xff;
			}

			if ((KeypadMask&4) && (CurrentKey<24) && (CurrentKey>16))
			{
				return 0xff;
			}

			if ((KeypadMask&8) && (CurrentKey<33) && (CurrentKey>24))
			{
				return 0xff;
			}

			if ((KeypadMask&16) && (CurrentKey<41) && (CurrentKey>32))
			{
				return 0xff;
			}

			if ((KeypadMask&32) && (CurrentKey<49) && (CurrentKey>41))
			{
				return 0xff;
			}

			if ((KeypadMask&64) && (CurrentKey<57) && (CurrentKey>48))
			{
				return 0xff;
			}
			//printf("%x",CurrentKey);
			if (CurrentKey)
			{
				return RealKeyMap[CurrentKey&7];
			}
			else
			{
				return 0xff;
			}
		case 2:
			printf("changed contrast");
			return (DisplayContrast & 0x1f);
		case 3:
			{
				return 0xa;
			}
		case 5:
			return CurrentRom;
		case 6:
			return PowerReg;
		case 7:
			return LinkReg;

		default:
			return 0xFF;
	}
}



/****************************************************************/
/*** Switch ROM pages: this routine copys the data from the   ***/
/*** relavent ROM into the mapped ROM space (0x4000-0x7fff)   ***/
/****************************************************************/
void MapROM(register byte NewRom)
{
	word Count;

	NewRom &=7;

	if (!(CurrentRom==NewRom) ) {
		CurrentRom=NewRom;
		for (Count=0;Count<0x4000;Count++) {
			memory.ram[Count+0x4000]=memory.rom[NewRom][Count];
		}
	}

}





/****************************************************************/
/*** Allocate memory, load ROM images, initialize screen,     ***/
/*** CPU and start the emulation. This function returns 0 in  ***/
/*** the case of failure.                                     ***/
/****************************************************************/
int StartTI85(void)
{
	word A;
	reg R;
	int *T;
	word Count;
	int RomNo;
	long longCount;


	T=(int *)"\01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
#ifdef LSB_FIRST
	if(*T!=1)
	{
		printf("********** This machine is high-endian. **********\n");
		printf("Take #define LSB_FIRST out and compile Em85 again.\n");
		return(0);
	}
#else
	if(*T==1)
	{
		printf("********* This machine is low-endian. **********\n");
		printf("Insert #define LSB_FIRST and compile Em85 again.\n");
		return(0);
	}
#endif

	InitScreen();

	if(!(ScrBuf=new byte[320*200])) {
		printf("couldnt allcode screen mem");
		return(0);
	}

	memset(ScrBuf,0,320*199);



	for(RomNo=0;RomNo<8;RomNo++)
	{
		if((memory.rom[RomNo]=new byte[16384])==NULL) {
			printf("big pag in the memory department!!...");
			return(0);
		}
	}

	if((memory.ram=new byte[65535U])==NULL) {
		printf("big pag in the memory department!!...");
		return(0);
	}

	for (longCount=0;longCount<65535L;longCount++)
	{
		memory.ram[longCount]=0;
	}


	if(Verbose)
		printf("OK\nLoading TI85 ROM:\n");

	LoadROM("ti85rom.bin");

	// fd 21 46 83
	memory.rom[0][0xd01]=0xCD;                			// fix the rom so it clears the
	memory.rom[0][0xd02]=0x15;                     // memory first...
	memory.rom[0][0xd03]=0x0b;
	memory.rom[0][0xd04]=0x00;

	for (Count=0;Count<0x4000;Count++) {
		memory.ram[Count]=memory.rom[0][Count];
	}
	for (Count=0;Count<0x4000;Count++) {
		memory.ram[Count+0x4000]=memory.rom[1][Count];
	}


	R.PC.W=0;R.SP.W=0xF000;R.IFF=0;

	if(Verbose) printf("OK\nRUNNING ROM CODE...\n");
	A=Z80(memory.ram,R);

	if(Verbose) printf("EXITED at PC = %Xh.\n",A);
	return(1);
}



/****************************************************************/
/*** Free memory allocated with StartTI85().                  ***/
/****************************************************************/
void TrashTI85(void)
{
	int RomNo;

	delete ScrBuf;

	delete memory.ram;

	for (RomNo=0;RomNo<8;RomNo++)
	{
		delete memory.rom[RomNo];
	}

	if (Verbose) printf("Exiting");
}


/*Main routines*/

int main(void)
{
	Verbose=1;
	StartTI85();
	TrashTI85();
	return 0;
}


void TI85Exit(void)
{
	TrashTI85();
	exit(1);
}


/****************************************************************/
/*** Load the ROM image into the ROM store                     **/
/****************************************************************/
void LoadROM(char *filename)
{

	int RomNo;

	int handle;


	 if ((handle =
			 open(filename, O_RDONLY | O_BINARY, S_IWRITE | S_IREAD)) == -1)
	 {
			printf("Error Opening File\n");
			exit(1);
	 }
	 for(RomNo=0;RomNo<8;RomNo++) {
		 if ((_read(handle, memory.rom[RomNo], 0x4000)) == -1) {
				printf("Read Failed.\n");
				exit(1);
		 }

	 }

}




/****************************************************************/
/*** Set sound parameters, refresh screen, check keyboard and ***/
/*** sprites. Call this function on each interrupt.           ***/
/****************************************************************/
void Interrupt(void)
{
	if ((++IntCount)>=5)
	{

		ReadKeypad();
		DrawScreen();
  }
}

void ReadKeypad(void)
{
	int key;

	if(kbhit()) {
		key=getch();
		if (key)
		{
			if (key=='#') TI85Exit();
			CurrentKey=PCto85KeyMap[(unsigned char) key];
			KeyPressed=1;
		}
		else
		{
			key=getch();
			CurrentKey=PCEto85KeyMap[(unsigned char) key];
			KeyPressed=1;
		}

		 //	printf("%x",CurrentKey);

	}
	else
	{
		KeyPressed=0;
	}
}

void DrawScreen(void)
{
	int x,y,bit;


	for(y=0;y<64;y++)
		for(x=0;x<16;x++)
			for(bit=0;bit<8;bit++)
				ScrBuf[(y*320)+(x<<3)+bit]=(memory.ram[0xFC00U+(y<<4)+x] >> (7-bit))&1;
	memcpy(abuf,ScrBuf,320*64);


}



void InitScreen(void)
{
	setvga256();
}

void MemCopy(byte *Dest, byte *Source,long Size)
{
	long Count;

	for (Count=0;Count<Size;Count++) {
		*(Dest++)=*(Source++);
	}
}