#include <clip.h>

/////////////////////////////////////////////////////////////////////////////
#define DIR	"C:/Home/Share/Arcadia/"
//#define FNAME "3dsoccer.bin"
#define FNAME "alieninv.bin"
//#define FNAME "astroinv.bin"
//#define FNAME "autorace.bin"
//#define FNAME "baseball.bin"
//#define FNAME "basket.bin"
//#define FNAME "battle.bin"
//#define FNAME "blackjac.bin"
//#define FNAME "bowling.bin"
//#define FNAME "boxing.bin"
//#define FNAME "brainqui.bin"
//#define FNAME "breakawa.bin"
//#define FNAME "capture.bin"
//#define FNAME "cattrax.bin"
//#define FNAME "circus.bin"
//#define FNAME "combat.bin"
//#define FNAME "crazycli.bin"
//#define FNAME "crazyg.bin"
//#define FNAME "doraemon.bin"
//#define FNAME "dr-slump.bin"
//#define FNAME "escape.bin"
//#define FNAME "football.bin"
//#define FNAME "frogger.bin"
//#define FNAME "funkfish.bin"
//#define FNAME "fussball.bin"
//#define FNAME "golf.bin"
//#define FNAME "grndslam.bin"
//#define FNAME "gundam-.bin"
//#define FNAME "hobo.bin"
//#define FNAME "horserac.bin"
//#define FNAME "jtron.bin"
//#define FNAME "jumpbuga.bin"
//#define FNAME "jumpbugb.bin"
//#define FNAME "jungler.bin"
//#define FNAME "macross.bin"
//#define FNAME "missilew.bin"
//#define FNAME "monaco.bin"
//#define FNAME "nibble.bin"
//#define FNAME "oceanbat.bin"
//#define FNAME "parashot.bin"
//#define FNAME "pleiades.bin"
//#define FNAME "r2dtank.bin"
//#define FNAME "redclash.bin"
//#define FNAME "robotkil.bin"
//#define FNAME "route16.bin"
//#define FNAME "smission.bin"
//#define FNAME "soccer.bin"
//#define FNAME "spacewar.bin"
//#define FNAME "spcatta.bin"
//#define FNAME "spcattb.bin"
//#define FNAME "spcsquad.bin"
//#define FNAME "spiders.bin"
//#define FNAME "sraiders.bin"
//#define FNAME "starches.bin"
//#define FNAME "supergob.bin"
//#define FNAME "tanksalo.bin"
//#define FNAME "tetris.bin"
//#define FNAME "the-end.bin"
//#define FNAME "turtles.bin"
//#define FNAME "vidlex.bin"
//#define FNAME "vidlexe.bin"
//#define FNAME "vidlexeg.bin"
//#define FNAME "vidlexg.bin"
//#define FNAME "vidlexge.bin"
//#define FNAME "vulture.bin"
/////////////////////////////////////////////////////////////////////////////
#define APPNAME	"ARCWIN"
#define WIDTH	(128)
#define HEIGHT	(208)
#define SCALE	2
unsigned char xbuff[WIDTH * HEIGHT];
unsigned char mem[0x8000];
unsigned char UDLR;
unsigned char SW0;
unsigned char SW1;
unsigned char SW2;
unsigned char SW3;
/////////////////////////////////////////////////////////////////////////////
#define NWHDR		8	/* これ以上増やすとwaveOutWriteでエラーになるみたい */
HWAVEOUT g_wout;
WAVEFORMATEX g_fmt;
WAVEHDR g_whdr[NWHDR];
short g_wbuff[NWHDR][UVIBUFLEN];
CRITICAL_SECTION snd_cs;
void CALLBACK
waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
	static int I;
	int retval;

	if(uMsg != WOM_DONE) return;

	EnterCriticalSection(&snd_cs);
	uvi_mix(g_wbuff[I]);
	LeaveCriticalSection(&snd_cs);

	retval = waveOutWrite(hwo, &g_whdr[I], sizeof g_whdr[I]);
	assert(retval == 0);

	I = (I + 1) % NWHDR;
}
void
InitSnd()
{
	int retval;
	int i;

	InitializeCriticalSection(&snd_cs);

	/* init stream */
	memset(&g_fmt, 0, sizeof g_fmt);
	g_fmt.wFormatTag = WAVE_FORMAT_PCM;
	g_fmt.nChannels = 1;
	g_fmt.nSamplesPerSec = 16000;
	g_fmt.nAvgBytesPerSec = 16000 * 2;
	g_fmt.nBlockAlign = 2;
	g_fmt.wBitsPerSample = 16;
	retval = waveOutOpen(&g_wout, WAVE_MAPPER, &g_fmt, (DWORD)waveOutProc, 0, CALLBACK_FUNCTION);
	assert(retval == 0);
	for(i = 0; i < NWHDR; i++) {
		memset(&g_whdr[i], 0, sizeof g_whdr[i]);
		g_whdr[i].lpData = (char*)g_wbuff[i];
		g_whdr[i].dwBufferLength = sizeof g_wbuff[i];
		retval = waveOutPrepareHeader(g_wout, &g_whdr[i], sizeof g_whdr[i]);
		assert(retval == 0);
	}

	/* start playing */
	for(i = 0; i < NWHDR; i++) {
		retval = waveOutWrite(g_wout, &g_whdr[i], sizeof g_whdr[i]);
		assert(retval == 0);
	}
}
/////////////////////////////////////////////////////////////////////////////
BITMAPINFO* pBMI;
HWND MainWnd;
LRESULT CALLBACK
WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CHAR:
		if(wParam == 'q') {
			DestroyWindow(hWnd);
			return 0;
		}
		return 0;
	case WM_KEYDOWN:
		switch(wParam) {
		case VK_UP:
			UDLR |= (1<<3);
			break;
		case VK_DOWN:
			UDLR |= (1<<2);
			break;
		case VK_LEFT:
			UDLR |= (1<<1);
			break;
		case VK_RIGHT:
			UDLR |= (1<<0);
			break;
		case VK_SPACE:
			SW1 |= (1<<3);
			break;
		case VK_RETURN:
			SW3  |= (1<<0);
			break;
		case VK_BACK:
			SW3  |= (1<<1);
			break;
		}
		break;
	case WM_KEYUP:
		switch(wParam) {
		case VK_UP:
			UDLR &= ~(1<<3);
			break;
		case VK_DOWN:
			UDLR &= ~(1<<2);
			break;
		case VK_LEFT:
			UDLR &= ~(1<<1);
			break;
		case VK_RIGHT:
			UDLR &= ~(1<<0);
			break;
		case VK_SPACE:
			SW1 &= ~(1<<3);
			break;
		case VK_RETURN:
			SW3  &= ~(1<<0);
			break;
		case VK_BACK:
			SW3  &= ~(1<<1);
			break;
		}
		break;
	}
	return DefWindowProc(hWnd, msg, wParam, lParam);
}
int
DoMsg()
{
	MSG msg;
	while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
		if(msg.message == WM_QUIT) {
			return 0;
		}
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return 1;
}
void
InitUI()
{
	int i;
	BITMAPINFOHEADER* bi;
	RGBQUAD* rgb;
	HINSTANCE hInst;
	WNDCLASS wc;
	RECT rcWin;
	RECT rcCli;
	int ncW;
	int ncH;

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

	hInst = GetModuleHandle(NULL);
	//
	memset(&wc, 0, sizeof wc);
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WndProc;
	wc.hInstance = hInst;
	wc.hCursor = LoadCursor(NULL, IDC_CROSS);
	wc.lpszClassName = APPNAME;
	RegisterClass(&wc);
	//
	MainWnd = CreateWindow(APPNAME, APPNAME,
		WS_OVERLAPPED,
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hInst, NULL);
	//
	GetWindowRect(MainWnd, &rcWin);
	GetClientRect(MainWnd, &rcCli);
	ncW = (rcWin.right - rcWin.left) - (rcCli.right - rcCli.left);
	ncH = (rcWin.bottom - rcWin.top) - (rcCli.bottom - rcCli.top);
	MoveWindow(MainWnd, 0, 0,
		ncW + bi->biWidth  * SCALE,
		ncH - bi->biHeight * SCALE, 0);
	//
	ShowWindow(MainWnd, SW_SHOW);
	UpdateWindow(MainWnd);
}
/////////////////////////////////////////////////////////////////////////////
void
InitROM(const char* fname)
{
	FILE* fp;

	memset(mem, 0, sizeof mem);

	if(!fname) {
		fname = DIR FNAME;
	}
	fp = fopen(fname, "rb");
	if(!fp) {
		DIE();
	}

	fread(&mem[0x0000], 1, 0x1000, fp);	/* $0000-$0FFF: 先頭 4KB      */
	fread(&mem[0x2000], 1, 0x6000, fp);	/* $2000-$7FFF: 残り24KB(max) */

	fclose(fp);
}

/****************************************************************************
 *	
 ****************************************************************************/

int
s2650_read(int addr)
{
	if(addr & ~0x7fff) DIE();
	       if(addr < 0x1800) {	/* $0000-$0FFF: ROM, $1000-$17FF: 空き */
		/* FALLTHRU */
	} else if(addr < 0x1c00) {	/* $1800-$1BFF: UVI、RAM、I/O */
		return uvi_read(addr & 0x3ff);
	} else {			/* $1C00-$1FFF: 空き, $2000-$7FFF: ROM */
		/* FALLTHRU */
	}
	return mem[addr];
}
void
s2650_write(int addr, int data)
{
	if(addr & ~0x7fff) DIE();
	if(data & ~  0xff) DIE();
	       if(addr < 0x1800) {	/* $0000-$0FFF: ROM, $1000-$17FF: 空き */
		/** no job **/
	} else if(addr < 0x1c00) {	/* $1800-$1BFF: UVI、RAM、I/O */
		uvi_write(addr & 0x3ff, data);
	} else {			/* $1C00-$1FFF: 空き, $2000-$7FFF: ROM */
		/** no job **/
	}
}
int  s2650_read_io (int addr) { return 0; }
void s2650_write_io(int addr, int data) { }

/****************************************************************************
 *	
 ****************************************************************************/

void
input_update()
{
	int pot;

	pot = 0x70;
	if(UDLR & (1<<3)) {
		pot = 0x00;
	} else if(UDLR & (1<<2)) {
		pot = 0xf0;
	}
	uvi_set_pot(0, pot);
	uvi_set_pot(1, pot);

	pot = 0x70;
	if(UDLR & (1<<1)) {
		pot = 0x00;
	} else if(UDLR & (1<<0)) {
		pot = 0xf0;
	}
	uvi_set_pot(2, pot);
	uvi_set_pot(3, pot);

	mem[0x1900] = mem[0x1904] = SW0;
	mem[0x1901] = mem[0x1905] = SW1;
	mem[0x1902] = mem[0x1906] = SW2;
	mem[0x1908]               = SW3;
}

int
main(int argc, char* argv[])
{
	int t0;
	int t1;
	HDC hDC;
	RECT rc;

	InitUI();
	InitROM(argv[1]);
	InitSnd();

	s2650_reset();
	uvi_reset(&mem[0x1800]);

	t0 = GetTickCount();
	hDC = GetDC(MainWnd);
	GetClientRect(MainWnd, &rc);
	for(;;) {
		if(!DoMsg()) {
			break;
		}
		input_update();

		//-----------------------------------------------------------
		uvi_new_frame(xbuff);
		s2650_set_sense(0); /* Vblank=Off */
		do {
			s2650_run(ARCADIA_CLOCK/60/262);
		} while(!uvi_next_line());
		s2650_set_sense(1); /* Vblank=On */
		do {
			s2650_run(ARCADIA_CLOCK/60/262);
		} while(uvi_next_line());
		//-----------------------------------------------------------

#ifdef UVI_GRAYSCALE
		uvi_reduce(xbuff, xbuff);
#endif /*UVI_GRAYSCALE*/
		StretchDIBits(hDC,
			rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
			0, 0, pBMI->bmiHeader.biWidth, -pBMI->bmiHeader.biHeight,
			xbuff,
			pBMI,
			DIB_RGB_COLORS,
			SRCCOPY);

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

