/** fMSX: portable MSX emulator ******************************/
/**                                                         **/
/**                           MSX.c                         **/
/**                                                         **/
/** This file contains implementation for the MSX-specific  **/
/** hardware: slots, memory mapper, PPIs, VDP, PSG, clock,  **/
/** etc. Initialization code and definitions needed for the **/
/** machine-dependent drivers are also here.                **/
/**                                                         **/
/** 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.                               **/
/*************************************************************/

#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"

#define PutImage \
	memcpy((char *)0xA0000,(char *)XBuf+WIDTH*(HEIGHT-200)/2,320*200)

#define DRIVERNAME "320X200.DRV"


int Verbose;
byte *RAM;
byte *ROMMap=
	{NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};							 /* Blocks for ROM Mappers */
byte CurrentRom=1;
byte CurrentKey=0;
byte KeypadMask=0;
byte PowerReg=0x0;
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 Mapper pages. This function is supposed to be ***/
/*** to be called when ROM page registers are written to.     ***/
/****************************************************************/
void MapROM(register byte NewRom)
{
	byte *ptr;

	NewRom &=7;

	if (!(CurrentRom==NewRom) ) {
		CurrentRom=NewRom;
		MemCopy(RAM+0x4000,ROMMap[NewRom],PAGESIZE);
	}

}





/****************************************************************/
/*** Allocate memory, load ROM images, initialize mapper, VDP ***/
/*** CPU and start the emulation. This function returns 0 in  ***/
/*** the case of failure.                                     ***/
/****************************************************************/
int StartTI85(void)
{
	word A;
	reg R;
	int *T,J;


	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 byte[4*PAGESIZE])) return(0);


	if(Verbose) printf("OK\nAllocating 8x16kB ROM pages...");

	for (J=0;J<8;J++) {
		if(!(ROMMap[J]=new byte[PAGESIZE])) {
			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;
	ROMMap[0][0xd02]=0x15;
	ROMMap[0][0xd03]=0x0b;
	ROMMap[0][0xd04]=0x00;

	MemCopy(RAM,ROMMap[0],PAGESIZE);
	MemCopy(RAM+0x4000,ROMMap[1],PAGESIZE);
	memset(RAM+0x8000,0,2*PAGESIZE);


	R.PC.W=0;R.SP.W=0xF000;R.IFF=0;

	if(Verbose) printf("OK\nRUNNING ROM CODE...\n");
	A=Z80(RAM,R);

	if(Verbose) printf("EXITED at PC = %Xh.\n",A);
	return(1);
}



/****************************************************************/
/*** Free memory allocated with StartMSX().                   ***/
/****************************************************************/
void TrashTI85(void)
{
	byte I;

	union REGS R;

	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 a ROM page from the file F into slot P, subslot S,  ***/
/*** page A. Return memory address of a page or 0 if failed.  ***/
/****************************************************************/
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, ROMMap[RomNo], PAGESIZE)) == -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)
{
	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[0xFc00+(y<<4)+x] >> (7-bit))&1;
	memcpy(abuf,ScrBuf,320*64);


}

void InitScreen(void)
{
	setvga256();
}

void MemCopy(byte *Dest, byte *Source,unsigned int Size)
{
	long Count;

	for (Count=0;Count<Size;Count++) {
		*(Dest++)=*(Source++);
	}
}