/** 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 <dos.h>
#include <conio.h>
#include "wgtgfx.h"


/***************************************************************/
/** The z80 emulator performs it's operations on a continuous **/
/** block of memory (Called RAM). The ROM pages are stored    **/
/** contiguouslty in a block of memory called ROMMap. When    **/
/** the emualtion calles the**/



int Verbose;
byte huge RAM[4L*PAGESIZE];
byte huge ROMMap[8][PAGESIZE];
byte CurrentRom=1;
byte CurrentKey=0;
byte KeypadMask=0;
byte PowerReg=0;
byte DisplayContrast=0;
byte LinkReg=0x0F;

byte *ScrBuf;


byte PCto85KeyMap[256-32] =
{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,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,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
			return CurrentKey;
		case 2:
			return (DisplayContrast & 0x1f);
		case 3:
			return 0xc;
		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)
{
	long Count;

	NewRom &=7;

	if (!(CurrentRom==NewRom) ) {
		CurrentRom=NewRom;
		for (Count=0;Count<PAGESIZE;Count++) {
			RAM[Count+PAGESIZE]=ROMMap[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;
	long Count;


	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);

	if(Verbose) printf("Allocating 64kB for address space...");
	//if(!(RAM=new huge byte[(4L*PAGESIZE)])) return(0);


	if(Verbose) printf("OK\nAllocating 8x16kB ROM pages...");

	//if((ROMMap=new huge byte[8][PAGESIZE])==NULL)
	//{
	 //	printf("Error allocating ROM blocks");
	//	return(0);
	//}



	if(Verbose)
		printf("OK\nLoading TI85 ROM:\n");

	LoadROM("ti85rom.bin");

	// fd 21 46 83
	ROMMap[0][0xd01]=0xCD;                			// fix the rom so it clears the
	ROMMap[0][0xd02]=0x15;                     // memory first...
	ROMMap[0][0xd03]=0x0b;
	ROMMap[0][0xd04]=0x00;

	for (Count=0;Count<PAGESIZE;Count++) {
		RAM[Count]=ROMMap[0][Count];
	}
	for (Count=0;Count<PAGESIZE;Count++) {
		RAM[Count+PAGESIZE]=ROMMap[1][Count];
	}
	for (Count=0;Count<(2L*PAGESIZE);Count++) {
		RAM[Count+(2L*PAGESIZE)]=0;
	}

	R.PC.W=0;R.SP.W=0xF000;R.IFF=0;

	if(Verbose) printf("OK\nRUNNING ROM CODE...\n");
	A=Z80((byte *)RAM,R);

	if(Verbose) printf("EXITED at PC = %Xh.\n",A);
	return(1);
}



/****************************************************************/
/*** Free memory allocated with StartTI85().                  ***/
/****************************************************************/
void TrashTI85(void)
{

	delete ScrBuf;

	delete RAM;

	delete ROMMap;

	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;

	FILE *in;

	if ((in = fopen(filename, "rb"))
		== NULL)
	{
		fprintf(stderr, "Cannot open ROM file.\n");
		TI85Exit();
	}

	fread(ROMMap,0x4000,8, in);
}




/****************************************************************/
/*** Set sound parameters, refresh screen, check keyboard and ***/
/*** sprites. Call this function on each interrupt.           ***/
/****************************************************************/
void Interrupt(void)
{
	ReadKeypad();
	DrawScreen();
}

void ReadKeypad(void)
{
	char key;

	if(kbhit()) {
		key=getch();
		if (key=='#') TI85Exit();
		CurrentKey=PCto85KeyMap[key];
	}
}

void DrawScreen(void)
{
	int x,y,bit,cpy;


	for(y=0;y<64;y++)
		for(x=0;x<16;x++)
			for(bit=0;bit<8;bit++)
				ScrBuf[(y*320)+(x<<3)+bit]=(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++);
	}
}