#include <clip.h>

#define	NTSC_COLOR_SUBCARRIER	3579545	/* NTSCカラーサブキャリア周波数[Hz] */

typedef struct _DRIVER {
	Z80 cpu;
	unsigned char mem[0x10000];
	//
	TMS9918 vdp;
} DRIVER;
DRIVER _driver;
DRIVER* driver = &_driver;

static void
z80_write(Z80* z80, unsigned short addr, unsigned char data)
{
//	if(addr == 0xc240 + 0x1d) {
//		printf("%04X %04X %02x\n", z80->pc, addr, data);
//	}
//	if(addr == 0xc27e) {
//		printf("%04X %04X %02x\n", z80->pc, addr, data);
//	}
	z80_internal_write(z80, addr, data);
}

static int sel = -1;

unsigned char
z80_in(Z80* z80, unsigned short addr)
{
	int data = -1;

	addr &= 0xff;
	switch(addr) {
	case 0xdc: /* 8255 Port#A (input) */
		if((sel & 7) != 7) return -1;
		if(GetAsyncKeyState(VK_UP   ) & 0x8000) data &= ~0x01;
		if(GetAsyncKeyState(VK_DOWN ) & 0x8000) data &= ~0x02;
		if(GetAsyncKeyState(VK_LEFT ) & 0x8000) data &= ~0x04;
		if(GetAsyncKeyState(VK_RIGHT) & 0x8000) data &= ~0x08;
		if(GetAsyncKeyState('Z') & 0x8000) data &= ~0x10;
		if(GetAsyncKeyState('X') & 0x8000) data &= ~0x20;
		break;
	case 0xdd: /* 8255 Port#B (input)  */
		break;
	case 0xde: /* 8255 Port#C (output)  */
//		printf("IN  %02x\n", addr);
		return sel;
	case 0xdf: /* 8255 Control (output) */
		break;
	case 0xbe: /* TMS9918A Port#0 */
	case 0xbf: /* TMS9918A Status */
		data = tms9918_read(&driver->vdp, addr & 1);
		break;
	default:
		printf("IN  %02x\n", addr);
		break;
	}
	return data;
}

void
z80_out(Z80* z80, unsigned short addr, unsigned char data)
{
	addr &= 0xff;
	switch(addr) {
	case 0x7f: /* SN76489 */
		break;
	case 0xbe: /* TMS9918A Port#0 */
	case 0xbf: /* TMS9918A Port#1 */
		tms9918_write(&driver->vdp, addr & 1, data);
		break;
	case 0xdc: /* 8255 Port#A (input) */
		break;
	case 0xdd: /* 8255 Port#B (input)  */
		break;
	case 0xde: /* 8255 Port#C (output)  */
//		printf("OUT %02x, %02x\n", addr, data);
		sel = data;
		break;
	case 0xdf: /* 8255 Control (output) */
		break;
	default:
		printf("OUT %02x, %02x\n", addr, data);
		break;
	}
}

unsigned char xbuff[256 * 192];

BITMAPINFO* bmi;

void
init()
{
	BITMAPINFOHEADER* bi;
	RGBQUAD* rgb;
	int i;

	bmi = (BITMAPINFO*)calloc(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256, 1);
	bi = &bmi->bmiHeader;
	rgb = &bmi->bmiColors[0];
	bi->biSize = sizeof(BITMAPINFOHEADER);
#ifdef TMS9918_GRAYSCALE
	bi->biWidth = 128;
	bi->biHeight = -88;
#else 
	bi->biWidth = 256;
	bi->biHeight = -192;
#endif
	bi->biPlanes = 1;
	bi->biBitCount = 8;
	for(i = 0; i < 16; i++) {
#ifdef TMS9918_GRAYSCALE
		rgb[i].rgbRed   = 255 * i / 15 ^ 255;
		rgb[i].rgbGreen = 255 * i / 15 ^ 255;
		rgb[i].rgbBlue  = 255 * i / 15 ^ 255;
#else
		rgb[i].rgbRed   = tms9918_color_table[i][0];
		rgb[i].rgbGreen = tms9918_color_table[i][1];
		rgb[i].rgbBlue  = tms9918_color_table[i][2];
#endif
	}
}

int
main(int argc, char* argv[])
{
	const char* fname;
	FILE* fp;
	HDC hdc;
	int t0;
	int t1;

	init();

	//fname = "C:\\Home\\Emulator\\sms\\roms\\N-Sub (SG-1000).sg";
	//fname = "C:\\Home\\Emulator\\sms\\roms\\Exerion (SG-1000) [!].sg";
	//fname = "C:\\Home\\Emulator\\sms\\roms\\Girl's Garden (SG-1000).sg";
	//fname = "C:\\Home\\Emulator\\sms\\roms\\Bank Panic (SG-1000) [!].sg";
	//fname = "C:\\Home\\Emulator\\sms\\roms\\Penguin Land (SG-1000).sg";
	//fname = "C:\\Home\\Emulator\\sms\\roms\\Gulkave (SG-1000) [!].sg";
	//fname = "C:\\Home\\Emulator\\sms\\roms\\Star Jacker (SG-1000) [!].sg";
	//fname = "C:\\Home\\Emulator\\sms\\roms\\Champion Baseball (SG-1000).sg";
	//fname = "C:\\Home\\Emulator\\sms\\roms\\Zaxxon (SG-1000).sg";
	//fname = "C:\\Home\\Emulator\\sms\\roms\\Choplifter (SG-1000) [!].sg";
	fname = "C:\\Home\\Emulator\\sms\\roms\\Hustle Chummy (SG-1000) [!].sg";

	if(argc >= 1 + 1) fname = argv[1];
	fp = fopen(fname, "rb");

	if(!fp) DIE();
	fread(driver->mem, 1, 0x10000, fp);
	fclose(fp);

	tms9918_reset(&driver->vdp);
	z80_reset(&driver->cpu, NULL, z80_write, z80_in, z80_out);

	t0 = GetTickCount();
	for(;;) {
		z80_run(&driver->cpu, NTSC_COLOR_SUBCARRIER / 60);
		if(tms9918_update(&driver->vdp, xbuff) & 0x80) {
			z80_int(&driver->cpu, 0);
		}
#ifdef TMS9918_GRAYSCALE
		tms9918_reduce(xbuff, xbuff);
#endif

		hdc = GetDC(NULL);
		StretchDIBits(hdc,
			0, 0, 128 * 2, 88 * 2,
			0, 0, bmi->bmiHeader.biWidth, -bmi->bmiHeader.biHeight,
			xbuff,
			bmi,
			DIB_RGB_COLORS,
			SRCCOPY);
		ReleaseDC(NULL, hdc);

		for(;;) {
			t1 = GetTickCount();
			if(t1 - t0 >= 1000 / 60) break;
			Sleep(0);
		}
		t0 += 1000 / 60;

		if(GetAsyncKeyState(VK_F5) & 0x8000) exit(0);
	}
}

