// ---------------------------------------------------------------------------
//  M88 - PC-8801 emulator
//	Copyright (C) cisc 1999.
// ---------------------------------------------------------------------------
//	$Id: regmon.cpp,v 1.1 2000/11/02 12:43:51 cisc Exp $

#include "headers.h"
#include "resource.h"
#include "regmon.h"
#include "misc.h"
#include "pc88/pc88.h"
#include "wincore.h"

using namespace PC8801;

// ---------------------------------------------------------------------------
//	表示上の行/列とレジスタの対応表
//	(メインCPU側の座標。サブCPU側は列に subcpuoffset を足したもの)
//
const Z80RegMonitor::RegField Z80RegMonitor::fields[reg_max] =
{
//	 row  col  width  valcol  vallen  name
	{  0,   0,   7,      3,      4,   "PC"  },	// reg_pc
	{  0,   8,   7,     11,      4,   "SP"  },	// reg_sp
	{  1,   0,   7,      3,      4,   "AF"  },	// reg_af
	{  1,   8,   7,     11,      4,   "BC"  },	// reg_bc
	{  2,   0,   7,      3,      4,   "HL"  },	// reg_hl
	{  2,   8,   7,     11,      4,   "DE"  },	// reg_de
	{  3,   0,   7,      3,      4,   "IX"  },	// reg_ix
	{  3,   8,   7,     11,      4,   "IY"  },	// reg_iy
	{  4,   0,   7,      3,      4,   "AF'" },	// reg_af2
	{  4,   8,   7,     11,      4,   "BC'" },	// reg_bc2
	{  5,   0,   7,      3,      4,   "HL'" },	// reg_hl2
	{  5,   8,   7,     11,      4,   "DE'" },	// reg_de2
	{  6,   0,   5,      3,      2,   "I"   },	// reg_i
	{  6,   8,   7,     14,      1,   "IM"  },	// reg_im
};

// ---------------------------------------------------------------------------
//	構築/消滅
//
Z80RegMonitor::Z80RegMonitor()
: core(0), editcpu(0), editid(0)
{
}

Z80RegMonitor::~Z80RegMonitor()
{
}

bool Z80RegMonitor::Init(WinCore* _core)
{
	if (!WinMonitor::Init(MAKEINTRESOURCE(IDD_REGMON), "MonitorRegister", 34, 8))
		return false;

	core = _core;
	SetUpdateTimer(50);

	return true;
}

void Z80RegMonitor::DrawMain(HDC hdc, bool)
{
	RECT rect;
	GetClientRect(GetHWnd(), &rect);

	HBRUSH hbr = CreateSolidBrush(0x113300);
	hbr = (HBRUSH) SelectObject(hdc, hbr);
	PatBlt(hdc, rect.left, rect.top, rect.right, rect.bottom, PATCOPY);
	DeleteObject(SelectObject(hdc, hbr));

	SetTextColor(hdc, 0xffffff);
	WinMonitor::DrawMain(hdc, true);
}

// ---------------------------------------------------------------------------
//	クリック位置からレジスタを特定する
//
bool Z80RegMonitor::FindField(POINT p, int* cpu, int* id)
{
	for (int c = 0; c < 2; c++)
	{
		int coloffset = c * subcpuoffset;
		for (int i = 0; i < reg_max; i++)
		{
			const RegField& f = fields[i];
			if (p.y == f.row &&
				p.x >= f.col + coloffset && p.x < f.col + coloffset + f.width)
			{
				*cpu = c;
				*id = i;
				return true;
			}
		}
	}
	return false;
}

// ---------------------------------------------------------------------------
//	レジスタの値を取得/設定する
//	PC は Z80C 内部でポインタとして持たれているため GetPC/SetPC 経由、
//	それ以外は GetRegRef() で直接触る
//
uint Z80RegMonitor::GetRegValue(int cpu, int id)
{
	PC88::Z80* z = cpu ? core->GetCPU2() : core->GetCPU1();
	if (id == reg_pc)
		return z->GetPC();

	Z80Reg& r = z->GetRegRef();
	switch (id)
	{
	case reg_sp:	return r.r.w.sp;
	case reg_af:	return r.r.w.af;
	case reg_bc:	return r.r.w.bc;
	case reg_hl:	return r.r.w.hl;
	case reg_de:	return r.r.w.de;
	case reg_ix:	return r.r.w.ix;
	case reg_iy:	return r.r.w.iy;
	case reg_af2:	return r.r_af;
	case reg_bc2:	return r.r_bc;
	case reg_hl2:	return r.r_hl;
	case reg_de2:	return r.r_de;
	case reg_i:		return r.ireg;
	case reg_im:	return r.intmode;
	}
	return 0;
}

void Z80RegMonitor::SetRegValue(int cpu, int id, uint value)
{
	PC88::Z80* z = cpu ? core->GetCPU2() : core->GetCPU1();
	if (id == reg_pc)
	{
		z->SetPC(value);
		return;
	}

	Z80Reg& r = z->GetRegRef();
	switch (id)
	{
	case reg_sp:	r.r.w.sp = value; break;
	case reg_af:	r.r.w.af = value; break;
	case reg_bc:	r.r.w.bc = value; break;
	case reg_hl:	r.r.w.hl = value; break;
	case reg_de:	r.r.w.de = value; break;
	case reg_ix:	r.r.w.ix = value; break;
	case reg_iy:	r.r.w.iy = value; break;
	case reg_af2:	r.r_af = value; break;
	case reg_bc2:	r.r_bc = value; break;
	case reg_hl2:	r.r_hl = value; break;
	case reg_de2:	r.r_de = value; break;
	case reg_i:		r.ireg = (uint8) value; break;
	case reg_im:	r.intmode = (uint8) value; break;
	}
}

// ---------------------------------------------------------------------------
//	ダイアログ処理
//
BOOL Z80RegMonitor::DlgProc(HWND hdlg, UINT msg, WPARAM wp, LPARAM lp)
{
	switch (msg)
	{
	case WM_LBUTTONDBLCLK:
		{
			if (!core->IsPaused())
			{
				EnableStatus(true);
				PutStatus("ポーズ中のみ編集できます");
				break;
			}

			POINT p;
			p.x = LOWORD(lp);
			p.y = HIWORD(lp);
			if (GetTextPos(&p) && FindField(p, &editcpu, &editid))
			{
				DialogBoxParam(GetHInst(), MAKEINTRESOURCE(IDD_REG_EDIT),
							hdlg, DLGPROC((void*)EDlgProcGate), (LPARAM)this);
				Update();
			}
		}
		return 0;
	}
	return WinMonitor::DlgProc(hdlg, msg, wp, lp);
}

// ---------------------------------------------------------------------------
//	値編集ダイアログ
//
BOOL Z80RegMonitor::EDlgProc(HWND hdlg, UINT msg, WPARAM wp, LPARAM lp)
{
	char buf[16];
	const RegField& f = fields[editid];

	switch (msg)
	{
	case WM_INITDIALOG:
		wsprintf(buf, "%s (%s):", f.name, editcpu ? "sub" : "main");
		SetDlgItemText(hdlg, IDC_REG_EDIT_TEXT, buf);
		wsprintf(buf, f.vallen == 4 ? "%.4x" : (f.vallen == 2 ? "%.2x" : "%x"),
				 GetRegValue(editcpu, editid));
		SetDlgItemText(hdlg, IDC_REG_EDITBOX, buf);
		SetFocus(GetDlgItem(hdlg, IDC_REG_EDITBOX));
		SendDlgItemMessage(hdlg, IDC_REG_EDITBOX, EM_SETSEL, 0, -1);
		return 0;

	case WM_COMMAND:
		if (HIWORD(wp) == BN_CLICKED)
		{
			switch (LOWORD(wp))
			{
				char* dum;
			case IDOK:
				GetDlgItemText(hdlg, IDC_REG_EDITBOX, buf, 5);
				if (isxdigit((uint8) buf[0]))
				{
					uint value = strtoul(buf, &dum, 16);
					core->Lock();
					SetRegValue(editcpu, editid, value);
					core->Unlock();
				}
			case IDCANCEL:
				EndDialog(hdlg, false);
				break;
			}
		}
		return 0;

	case WM_CLOSE:
		EndDialog(hdlg, false);
		return true;

	default:
		return false;
	}
}

BOOL CALLBACK Z80RegMonitor::EDlgProcGate
(HWND hwnd, UINT m, WPARAM w, LPARAM l)
{
	Z80RegMonitor* regmon = 0;

	if (m == WM_INITDIALOG) {
		regmon = reinterpret_cast<Z80RegMonitor*>(l);
		if (regmon) {
			::SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)regmon);
		}
	} else {
		regmon = (Z80RegMonitor*)::GetWindowLongPtr(hwnd, GWLP_USERDATA);
	}

	if (regmon) {
		return regmon->EDlgProc(hwnd, m, w, l);
	} else {
		return FALSE;
	}
}

// ---------------------------------------------------------------------------
//	状態を表示
//
void Z80RegMonitor::UpdateText()
{
	PC88::Z80* c1 = core->GetCPU1();
	PC88::Z80* c2 = core->GetCPU2();
	const Z80Reg& r1 = c1->GetReg();
	const Z80Reg& r2 = c2->GetReg();

	const uint m = 0xffff;

	Putf("PC:%.4x SP:%.4x   PC:%.4x SP:%.4x\n",	c1->GetPC() & m, r1.r.w.sp & m, c2->GetPC() & m, r2.r.w.sp & m);
	Putf("AF:%.4x BC:%.4x   AF:%.4x BC:%.4x\n",	r1.r.w.af & m,   r1.r.w.bc & m, r2.r.w.af & m,   r2.r.w.bc & m);
	Putf("HL:%.4x DE:%.4x   HL:%.4x DE:%.4x\n",	r1.r.w.hl & m,   r1.r.w.de & m, r2.r.w.hl & m,   r2.r.w.de & m);
	Putf("IX:%.4x IY:%.4x   IX:%.4x IY:%.4x\n",	r1.r.w.ix & m,   r1.r.w.iy & m, r2.r.w.ix & m,   r2.r.w.iy & m);
	Putf("AF'%.4x BC'%.4x   AF'%.4x BC'%.4x\n",	r1.r_af & m,     r1.r_bc & m,   r2.r_af & m,     r2.r_bc   & m);
	Putf("HL'%.4x DE'%.4x   HL'%.4x DE'%.4x\n",	r1.r_hl & m,     r1.r_de & m,   r2.r_hl & m,     r2.r_de & m);
	Putf("I :%.2x   IM:   %x   I :%.2x   IM:   %x\n",	r1.ireg, r1.intmode, r2.ireg, r2.intmode);
}
