// ---------------------------------------------------------------------------
//	M88 - PC-8801 emulator
//	バックトレース表示ウインドウ
//	Copyright (C) 2026
//	2-clause BSD license
// ---------------------------------------------------------------------------

#include "headers.h"
#include "resource.h"
#include "pc88/pc88.h"
#include "tracemon.h"
#include "misc.h"

using namespace PC8801;

// ---------------------------------------------------------------------------
//	構築/消滅
//
TraceMonitor::TraceMonitor()
: core(0)
{
}

TraceMonitor::~TraceMonitor()
{
}

// ---------------------------------------------------------------------------
//	初期化
//
bool TraceMonitor::Init(WinCore* wincore)
{
	core = wincore;
	if (!WinMonitor::Init(MAKEINTRESOURCE(IDD_TRACEMON), "MonitorTrace", 60, 16))
		return false;
	if (!diagmain.Init(core->GetMemoryAccess1()) || !diagsub.Init(core->GetMemoryAccess2()))
		return false;
	// 自動更新はしない。実行中はリングバッファが回り続けるだけで意味が無く、
	// 表示は Refresh() 経由でポーズ/ブレーク時にだけ更新する
	return true;
}

// ---------------------------------------------------------------------------
//	ウインドウが実際に開かれた直後(hwnd 有効化後)に呼ばれる。
//	D列(呼び出し深さ)は SP による自己修復込みの推定値であり、正確な
//	呼び出し履歴ではないことを常時見えるところに明記しておく(仕様書§7.2)。
//	EnableStatus は内部で GetHWnd() を親にステータスバーを作るため、
//	hwnd がまだ無い Init() 側で呼んでも失敗して何も表示されない
//
void TraceMonitor::Start()
{
	EnableStatus(true);
	PutStatus("D(深さ)は目安です。JPによるテールコール等で実際の呼び出し階層とずれることがあります。");
}

// ---------------------------------------------------------------------------
//	WM_M88_DEBUGBREAK から呼ばれる
//
void TraceMonitor::Refresh()
{
	if (IsOpen())
		Update();
}

// ---------------------------------------------------------------------------
//	テキスト更新。0行目は列見出し。新しいものが上(-1 が直近)。
//	逆アセンブルは「表示時点のメモリ」を使うので、自己書き換えコードでは
//	実行当時の内容と異なって見えることがある(仕様として許容)。
//	VM の MemoryManager を経由するので、ウォッチと同じ理由で
//	SetSuppressBreak(true) で読み出し区間を囲む(§5.3 の落とし穴)
//
void TraceMonitor::UpdateText()
{
	core->Lock();
	Debugger* dbg = core->GetDebugger();
	int count = dbg->GetTraceCount();

	SetLines(Max(1, count));
	int rows = Max(0, GetHeight() - 1);
	int top = GetLine();

	SetTxCol(0xc0c0c0);
	SetBkCol(RGB(0, 0, 0x50));
	Putf("%4s  %-4s  %-4s  %-4s  %-2s  %s\n",
		 "#", "CPU", "PC", "SP", "D", "Disassemble");

	dbg->SetSuppressBreak(true);
	for (int y = 0; y < rows; y++)
	{
		int index = top + y;
		Debugger::TraceEntry e;
		if (index < count && dbg->GetTraceEntry(index, &e))
		{
			char buf[128];
			Z80Diag& diag = (e.cpu == Debugger::cpu_sub) ? diagsub : diagmain;
			diag.Disassemble(e.pc, buf);

			SetTxCol(0xffffff);
			SetBkCol(RGB(0, 0, 0x20));
			Putf("%4d  %-4s  %.4x  %.4x  %-2d  %s\n",
				 -(index + 1),
				 e.cpu == Debugger::cpu_sub ? "SUB" : "MAIN",
				 e.pc, e.sp, e.depth, buf);
		}
		else
		{
			SetBkCol(RGB(0, 0, 0x20));
			Puts("\n");
		}
	}
	dbg->SetSuppressBreak(false);
	core->Unlock();
}

// ---------------------------------------------------------------------------
//	ダイアログ処理(専用のコマンドは無し。基底に委譲するだけ)
//
BOOL TraceMonitor::DlgProc(HWND hdlg, UINT msg, WPARAM wp, LPARAM lp)
{
	return WinMonitor::DlgProc(hdlg, msg, wp, lp);
}
