File:  [Isaki's NoNo m68k/m88k emulator] / nono / debugger / memdump.cpp
Revision 1.1.1.7 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:58 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, HEAD
nono 1.7.0

//
// nono
// Copyright (C) 2022 nono project
// Licensed under nono-license.txt
//

//
// メモリダンプ/逆アセンブル
//

#include "memdump.h"
#include "debugger_memory.h"
#include "debugger_private.h"
#include "hd64180disasm.h"
#include "m680x0disasm.h"
#include "m88100disasm.h"
#include "mainbus.h"
#include "monitor.h"

//
// メモリダンプ (基本クラス)
//

// コンストラクタ
Memdump::Memdump(uint objid_)
	: inherited(objid_)
{
	// ログは不要
	ClearAlias();

	// 初期値
	saddr = BusAddr::SRead | BusAddr::Physical;
	SetFormat(Word);
}

// コンストラクタ (メモリダンプ用にデバッガから呼ばれるほう)
Memdump::Memdump(DebuggerMD *md_)
	: Memdump(OBJ_NONE)	// 移譲
{
	InitMD(md_);
}

// コンストラクタ (逆アセンブル用にデバッガから呼ばれるほう)
Memdump::Memdump(DebuggerMD *md_, CPUArch asm_arch)
	: Memdump(OBJ_NONE)	// 移譲
{
	InitMD(md_);

	// asm_arch を Format に変換してセット
	Format fmt_;
	switch (asm_arch) {
	 case CPUArch::M680x0:
		fmt_ = M680x0Disasm;
		break;
	 case CPUArch::M88xx0:
		fmt_ = M88100Disasm;
		break;
	 case CPUArch::HD64180:
		fmt_ = HD64180Disasm;
		break;
	 default:
		PANIC("corrupted asm_arch=%d", (int)asm_arch);
	}
	SetFormat(fmt_);
}

// デストラクタ
Memdump::~Memdump()
{
}

// md を初期化(代入)する。
void
Memdump::InitMD(DebuggerMD *md_)
{
	md  = md_;
	bus = md->GetBus();
}

// busaddr を設定する。
void
Memdump::SetAddr(busaddr saddr_)
{
	saddr = saddr_;
	MaskAddr();
}

// アドレスを設定する。
void
Memdump::SetAddr(uint32 laddr_)
{
	saddr.ChangeAddr(laddr_);
	MaskAddr();
}

// 今の表示形式での境界に揃える。
void
Memdump::MaskAddr()
{
	saddr &= ~(GetStride() - 1);
}

// アドレスをバイト単位で加算(減算)する。
void
Memdump::Offset(int bytes)
{
	saddr += (uint32)bytes;
}

// 表示フォーマットを設定する。
void
Memdump::SetFormat(Memdump::Format fmt_)
{
	fmt = fmt_;

	line.reset();
	fixed = 0;
	inst_bytes = 0;

	switch (fmt) {
	 case Byte:
	 case Word:
	 case Long:
		fixed = 16;
		break;
	 case M68030PageShort:
	 case M68040TableDesc:
	 case M68040PageDesc:
	 case M88200Page:
		fixed = 4;
		break;
	 case M68030PageLong:
		fixed = 8;
		break;

	 case M680x0Disasm:
		line.reset(new m680x0disasm());
		inst_bytes = 2;
		break;
	 case M88100Disasm:
		line.reset(new m88100disasm());
		inst_bytes = 4;
		fixed = 4;		// 固定長フォーマットでもある
		break;
	 case HD64180Disasm:
		line.reset(new hd64180disasm());
		inst_bytes = 1;
		break;
	}

	// 表示形式を切り替えたことによってアドレスに端数が出るのを防ぐ
	MaskAddr();
}

// 物理メモリ paddr 以降の内容を越権的に読み出す。bytes は 1, 2, 4 を想定。
// バスエラーなら負数を返す。
uint64
Memdump::Peek(uint32 paddr, int bytes) const
{
	uint32 data = 0;

	while (--bytes >= 0) {
		busdata bd = bus->Peek1(paddr++);
		if (__predict_false(bd.IsBusErr())) {
			return bd;
		}
		data = (data << 8) | bd.Data();
	}
	return data;
}

// 物理メモリ paddr 以降の内容を越権的に書き換える。
// bytes は 1, 2, 4 を想定しており、paddr はこれに整列しているはず。
// バスエラーが起きたらその場で処理を中止して false を返す。
bool
Memdump::Poke(uint32 paddr, uint32 data, int bytes)
{
	while (--bytes >= 0) {
		uint32 d = data >> (bytes * 8);
		if (bus->Poke1(paddr++, d & 0xff) == false) {
			return false;
		}
	}

	return true;
}

// この paddr が Poke() 可能なら true を返す。
bool
Memdump::CanPoke(uint32 paddr) const
{
	return bus->Poke1(paddr, -1);
}

// コンソールに出力。
// 出力後 saddr は更新されている。
void
Memdump::Print(FILE *cons, int row)
{
	DebuggerMemoryStream mem(md, saddr);

	for (int y = 0; y < row; y++) {
		std::string addrbuf;
		bool accessible;

		accessible = mem.FormatAddr(addrbuf);
		if (accessible == false) {
			fprintf(cons, "%s\n", addrbuf.c_str());
			mem.Offset(GetStride());
			continue;
		}
		// addrbuf は "VA:" もしくは "VA:PA:" の形式で返ってくる。
		// 本来は、どちらの形式であってもダンプは同じ桁から始めたいが、
		// バイト表示では 80桁に収まらない。
		// VA!=PA の場合はどうやっても 80桁に収まらないので諦めてそのまま
		// 出力するが、VA==PA の時は PA 用の空き地を削れば 80桁に収められる
		// ので、その努力はしてみる。
		if (fmt == Byte) {
			fprintf(cons, "%s ", addrbuf.c_str());
		} else {
			fprintf(cons, "%-18s ", addrbuf.c_str());
		}

		switch (fmt) {
		 case Byte:
		 case Word:
		 case Long:
		 {
			std::vector<int> vec = Read(mem, fixed);
			std::string dumpbuf = DumpHex(vec);
			std::string charbuf = DumpChar(vec);
			// 左詰めでいいか
			fprintf(cons, "%s %s\n", dumpbuf.c_str(), charbuf.c_str());
			break;
		 }

		 case M68030PageShort:
		 {
			std::vector<int> vec = Read(mem, fixed);
			std::string dumpbuf = Dump68030PageShort(vec);
			// 文字ダンプどうすべ
			fprintf(cons, "%s\n", dumpbuf.c_str());
			break;
		 }
		 case M88200Page:
		 {
			std::vector<int> vec = Read(mem, fixed);
			std::string dumpbuf = Dump88200Page(vec);
			// 文字ダンプどうすべ
			fprintf(cons, "%s\n", dumpbuf.c_str());
			break;
		 }

		 case M680x0Disasm:
		 case M88100Disasm:
		 case HD64180Disasm:
			line->Exec(&mem);
			fprintf(cons, "%s%s\n", line->dump.c_str(), line->text.c_str());
			break;

		 default:
			fprintf(cons, "fmt=%d not supported\n", (int)fmt);
			mem.Offset(GetStride());
			break;
		}
	}

	// アドレスを更新
	saddr = mem.laddr;
}

// メモリストリームから bytes バイト分読み込んでベクタにして返す。
// 各要素はバイトデータで、バスエラーは -1 になる。
std::vector<int>
Memdump::Read(DebuggerMemoryStream& mem, int bytes)
{
	std::vector<int> vec(bytes);

	for (int i = 0; i < bytes; i++) {
		vec[i] = mem.Read1();
	}

	return vec;
}

// vec[] の文字ダンプ文字列を返す。
std::string
Memdump::DumpChar(const std::vector<int>& vec)
{
	std::string charbuf;

	for (auto c : vec) {
		if (c < 0) {
			charbuf += ' ';
		} else {
			charbuf += (0x20 <= c && c <= 0x7e) ? c : '.';
		}
	}

	return charbuf;
}

// vec[] の HEX ダンプ文字列を返す。
// CLI, GUI 共通。
std::string
Memdump::DumpHex(const std::vector<int>& vec)
{
	std::string dumpbuf;
	int span;

	switch (fmt) {
	 case Byte:	span = 1;	break;
	 case Word: span = 2;	break;
	 case Long: span = 4;	break;
	 default:
		PANIC("corrupted fmt=%d", (int)fmt);
	}

	int s = span;
	for (auto c : vec) {
		if (c < 0) {
			dumpbuf += "--";
		} else {
			dumpbuf += strhex(c, 2);
		}

		if (--s == 0) {
			dumpbuf += ' ';
			s = span;
		}
	}

	return dumpbuf;
}

// 68030 のショートフォーマットディスクリプタのダンプ文字列を返す。
std::string
Memdump::Dump68030PageShort(const std::vector<int>& vec)
{
	std::string dumpbuf;

	//  2         3         4         5         6         7
	// 9012345678901234567890123456789012345678901234567890123
	// 00000000  DT=0(Invalid)
	// ffffff00  DT=1(Page)       $00000000 CI M U WP
	// fffffff0  DT=2(Next Short) $00000000      U WP

	uint32 data = 0;
	for (auto b : vec) {
		if (b < 0) {
			return "BusErr";
		}
		data = (data << 8) | (uint8)b;
	}

	uint32 dt = (data & 0x03);
	dumpbuf = string_format("%08x  DT=%d%s", data, dt, dtname[dt]);

	if (dt != 0) {
		if (dt == 1) {
			dumpbuf += string_format(" $%08x %s %c",
				(data & 0xffff'ff00),
				(data & 0x40) ? "CI" : "- ",
				(data & 0x10) ? 'M' : '-');
		} else {
			dumpbuf += string_format(" $%08x    ", data & 0xffff'fff0);
		}
		dumpbuf += string_format(" %c %s",
			(data & 0x08) ? 'U' : '-',
			(data & 0x04) ? "WP" : "- ");
	}

	return dumpbuf;
}

// 88200 のセグメントディスクリプタとページディスクリプタ
std::string
Memdump::Dump88200Page(const std::vector<int>& vec)
{
	std::string dumpbuf;

	//  2         3         4         5         6         7
	// 9012345678901234567890123456789012345678901234567890123
	// 00000000  $00000'000 WT SP G CI M U WP V

	uint32 data = 0;
	for (auto b : vec) {
		if (b < 0) {
			return "BusErr";
		}
		data = (data << 8) | (uint8)b;
	}

	dumpbuf = string_format("%08x  $%05x'000 %s %s %c %s %c %c %s %c",
		data,
		(data >> 12),
		(data & 0x200) ? "WT" : "- ",
		(data & 0x100) ? "SP" : "- ",
		(data & 0x080) ? 'G'  : '-',
		(data & 0x040) ? "CI" : "- ",
		(data & 0x010) ? 'M'  : '-',
		(data & 0x008) ? 'U'  : '-',
		(data & 0x004) ? "WP" : "- ",
		(data & 0x001) ? 'V'  : '-');

	return dumpbuf;
}

/*static*/ const char * const
Memdump::dtname[4] = {
	"(Invalid)   ",
	"(Page)      ",
	"(Next Short)",
	"(Next Long) ",
};

/*static*/ const char * const
Memdump::udtname[4] = {
	"(Invalid) ",
	"(Invalid) ",
	"(Resident)",
	"(Resident)",
};

/*static*/ const char * const
Memdump::pdtname[4] = {
	"(Invalid) ",
	"(Resident)",
	"(Indirect)",
	"(Resident)",
};


//
// メモリダンプモニタ
//

// コンストラクタ
MemdumpMonitor::MemdumpMonitor(uint objid_, int monid_)
	: inherited(objid_)
{
	monitor = gMonitorManager->Regist(monid_, this);
	monitor->SetCallback(&MemdumpMonitor::MonitorScreen);
	monitor->SetSize(84, 32);
}

// デストラクタ
MemdumpMonitor::~MemdumpMonitor()
{
}

// モニタ更新
void
MemdumpMonitor::MonitorScreen(Monitor *, TextScreen& screen)
{
	DebuggerMemoryStream mem(md, saddr);

	screen.Clear();

	switch (fmt) {
	 case Byte:
	 case Word:
	 case Long:
	 case M68030PageShort:
	 case M68040TableDesc:
	 case M68040PageDesc:
	 case M88200Page:
		MonitorScreenMemdump(screen, mem);
		break;

	 case M68030PageLong:
		screen.Puts(0, 0, "68030PageLong not supported");
		break;

	 case M680x0Disasm:
	 case M88100Disasm:
	 case HD64180Disasm:
		MonitorScreenDisasm(screen, mem);
		break;
	}
}

// モニタ更新 (メモリダンプ、ディスクリプタの場合)
void
MemdumpMonitor::MonitorScreenMemdump(TextScreen& screen,
	DebuggerMemoryStream& mem)
{
	// モニタでは 80桁を超えて必要サイズ確保してあるので
	// "VA:" も "VA:PA:" も同じ幅を占有し、
	// ダンプフィールドは常に 19桁目から開始する。(see Print())
	int dx = 19;

	// 文字列ダンプは桁数によらず右詰め
	int cx = screen.GetCol() - GetStride();

	int row = screen.GetRow();
	for (int y = 0; y < row; y++) {
		std::string addrbuf;
		bool accessible;

		// アドレス欄
		accessible = mem.FormatAddr(addrbuf);
		screen.Puts(0, y, addrbuf.c_str());
		if (accessible == false) {
			mem.Offset(GetStride());
			continue;
		}

		// 文字ダンプ
		std::vector<int> vec = Read(mem, GetStride());
		std::string charbuf = DumpChar(vec);
		screen.Puts(cx, y, charbuf.c_str());

		// ダンプ本体
		switch (fmt) {
		 case Byte:
		 case Word:
		 case Long:
		 {
			std::string dumpbuf = DumpHex(vec);
			screen.Puts(dx, y, dumpbuf.c_str());
			break;
		 }
		 case M68030PageShort:
			Update68030PageShort(screen, dx, y, vec);
			break;
		 case M68040TableDesc:
			Update68040TableDesc(screen, dx, y, vec);
			break;
		 case M68040PageDesc:
			Update68040PageDesc(screen, dx, y, vec);
			break;
		 case M88200Page:
			Update88200Page(screen, dx, y, vec);
			break;
		 default:
			PANIC("corrupted fmt=%d", (int)fmt);
		}
	}
}

// (x, y) に 68030 ページディスクリプタのダンプを出力する。
void
MemdumpMonitor::Update68030PageShort(TextScreen& screen, int x, int y,
	const std::vector<int>& vec)
{
	//  2         3         4         5         6         7
	// 9012345678901234567890123456789012345678901234567890123
	// 00000000  $00000000 CI M U WP DT=1(Page)
	// 00000000  $00000000      U WP DT=2(Next Short)

	uint32 data = 0;
	for (auto b : vec) {
		if (b < 0) {
			screen.Puts(x, y, "BusErr");
			return;
		}
		data = (data << 8) | (uint8)b;
	}

	screen.Puts(x, y, strhex(data).c_str());

	uint32 dt = (data & 0x03);
	TA attr = (dt != 0) ? TA::Normal : TA::Disable;

	if (dt == 1) {
		screen.Putc(x + 10, y, attr, '$');
		screen.Puts(x + 11, y, attr, strhex(data & 0xffff'ff00).c_str());
		screen.Puts(x + 20, y, attr, (data & 0x40) ? "CI" : "-");
		screen.Putc(x + 23, y, attr, (data & 0x10) ? 'M'  : '-');
	} else {
		screen.Putc(x + 10, y, attr, '$');
		screen.Puts(x + 11, y, attr, strhex(data & 0xffff'fff0).c_str());
	}
	screen.Putc(x + 25, y, attr, (data & 0x08) ? 'U'  : '-');
	screen.Puts(x + 27, y, attr, (data & 0x04) ? "WP" : "-");
	screen.Print(x + 30, y, attr, "DT=%d%s", dt, dtname[dt]);
}

// (x, y) に 68040 テーブルディスクリプタのダンプを出力する。
void
MemdumpMonitor::Update68040TableDesc(TextScreen& screen, int x, int y,
	const std::vector<int>& vec)
{
	//  2         3         4         5         6         7
	// 9012345678901234567890123456789012345678901234567890123
	// 00000000  $00000000 U W UDT=0(Invalid)

	uint32 data = 0;
	for (auto b : vec) {
		if (b < 0) {
			screen.Puts(x, y, "BusErr");
			return;
		}
		data = (data << 8) | (uint8)b;
	}

	screen.Puts(x, y, strhex(data).c_str());

	bool resident = (data & 2);
	TA attr = resident ? TA::Normal : TA::Disable;

	// 1段(23bit)、2段4K(24bit)、2段8K(25bit) はとりあえず区別しない。
	screen.Putc(x + 10, y, attr, '$');
	screen.Puts(x + 11, y, attr, strhex(data & 0xffff'ff80).c_str());
	screen.Putc(x + 20, y, attr, (data & 0x0008) ? 'U' : '-');
	screen.Putc(x + 22, y, attr, (data & 0x0004) ? 'W' : '-');
	uint32 udt = data & 3;
	screen.Print(x + 24, y, attr, "UDT=%u%s", udt, udtname[udt]);
}

// (x, y) に 68040 ページディスクリプタのダンプを出力する。
void
MemdumpMonitor::Update68040PageDesc(TextScreen& screen, int x, int y,
	const std::vector<int>& vec)
{
	//  2         3         4         5         6         7
	// 9012345678901234567890123456789012345678901234567890123456
	// 00000000  $00000000 UR G U1 U0 S CMx M U W PDT=0(Resident)

	uint32 data = 0;
	for (auto b : vec) {
		if (b < 0) {
			screen.Puts(x, y, "BusErr");
			return;
		}
		data = (data << 8) | (uint8)b;
	}

	screen.Puts(x, y, strhex(data).c_str());

	uint32 pdt = data & 3;
	bool resident = (pdt != 0);
	TA attr = resident ? TA::Normal : TA::Disable;

	// 4K/8K ページサイズはとりあえず区別しない。
	screen.Putc(x + 10, y, attr, '$');
	screen.Puts(x + 11, y, attr, strhex(data & 0xffff'f000).c_str());
	screen.Puts(x + 20, y, attr, (data & 0x0800) ? "UR" : "--");
	screen.Putc(x + 23, y, attr, (data & 0x0400) ? 'G' : '-');
	screen.Puts(x + 25, y, attr, (data & 0x0200) ? "U1" : "--");
	screen.Puts(x + 28, y, attr, (data & 0x0100) ? "U0" : "--");
	screen.Putc(x + 31, y, attr, (data & 0x0080) ? 'S' : '-');
	screen.Print(x + 33, y, attr, "CM%u", (data & 0x0060) >> 5);
	screen.Putc(x + 37, y, attr, (data & 0x0010) ? 'M' : '-');
	screen.Putc(x + 39, y, attr, (data & 0x0008) ? 'U' : '-');
	screen.Putc(x + 41, y, attr, (data & 0x0004) ? 'W' : '-');
	screen.Print(x + 43, y, attr, "PDT=%u%s", pdt, pdtname[pdt]);
}

// (x, y) に 88200 ページディスクリプタのダンプを出力する。
void
MemdumpMonitor::Update88200Page(TextScreen& screen, int x, int y,
	const std::vector<int>& vec)
{
	//  2         3         4         5         6         7
	// 9012345678901234567890123456789012345678901234567890123
	// 00000000  $00000'000 WT SP G CI M U WP V

	uint32 data = 0;
	for (auto b : vec) {
		if (b < 0) {
			screen.Puts(x, y, "BusErr");
			return;
		}
		data = (data << 8) | (uint8)b;
	}

	screen.Puts(x, y, strhex(data).c_str());

	TA attr = (data & 0x001) ? TA::Normal : TA::Disable;
	screen.Print(x + 10, y, attr, "$%05x'000 %s %s %c %s %c %c %s %c",
		(data >> 12),
		(data & 0x200) ? "WT" : "- ",
		(data & 0x100) ? "SP" : "- ",
		(data & 0x080) ? 'G'  : '-',
		(data & 0x040) ? "CI" : "- ",
		(data & 0x010) ? 'M'  : '-',
		(data & 0x008) ? 'U'  : '-',
		(data & 0x004) ? "WP" : "- ",
		(data & 0x001) ? 'V'  : '-');
}

// モニタ更新 (逆アセンブルの場合)
void
MemdumpMonitor::MonitorScreenDisasm(TextScreen& screen,
	DebuggerMemoryStream& mem)
{
	// モニタでは 80桁を超えて必要サイズ確保してあるので
	// "VA:" も "VA:PA:" も同じ幅を占有し、
	// ダンプフィールドは常に 19桁目から開始する。(see Print())
	int dx = 19;

	int row = screen.GetRow();

	// 逆アセンブラ用の次行アドレスバッファを必要ならリサイズ
	if ((row + 1) != nextaddr.size()) {
		nextaddr.resize(row + 1);
	}

	nextaddr[0] = mem.laddr.Addr();
	for (int y = 0; y < row; y++) {
		std::string addrbuf;
		bool accessible;
		TA attr;

		// PC のいる行をボールドに
		if (__predict_false(mem.laddr.Addr() == md->GetPC())) {
			attr = TA::Em;
		} else {
			attr = TA::Normal;
		}

		// アドレス欄
		accessible = mem.FormatAddr(addrbuf);
		screen.Puts(0, y, attr, addrbuf.c_str());
		if (accessible == false) {
			mem.Offset(GetStride());
			continue;
		}

		line->Exec(&mem);
		screen.Print(dx, y, attr, "%s%s",
			line->dump.c_str(), line->text.c_str());

		// 次行の開始アドレスを覚えておく。
		// 逆アセンブラで  n 行下へ移動する時のため。
		nextaddr[y + 1] = mem.laddr.Addr();
	}
}

// n 行下の行までのバイトオフセットを返す。
int
MemdumpMonitor::GetLineOffset(int n) const
{
	if (fixed) {
		// 固定長フォーマットなら fixed 倍するだけで求まる。
		return n * fixed;
	} else {
		// 可変長フォーマット
		if (0 <= n && n < nextaddr.size()) {
			return nextaddr[n] - nextaddr[0];
		} else {
			// なければ仕方ないので適当に返す
			return n * inst_bytes;
		}
	}
}

// 前ページ位置(-1)、次ページ位置(1) までのバイトオフセットを返す。
int
MemdumpMonitor::GetPageOffset(int n) const
{
	// 前ページ/次ページ開始位置は、メモリダンプと逆アセンブルで動作を変える。
	// メモリダンプならきっちり次のページを表示してほしい。例えば 1画面が
	// 16行(256バイト) で、現在 $100 にいて次ページに飛ぶ時は $200 になって
	// ほしい。
	// 逆アセンブルでは実際にこの動作をすると連続性が失われて読みにくいので
	// 1行だぶらせることにする。

	int row = monitor->GetRow();
	if (inst_bytes) {
		// 逆アセンブルの場合
		if (n < 0) {
			// 戻る時は、(最短)命令長分ずつ戻るしかない。
			return -inst_bytes * (row - 1);
		} else {
			return GetLineOffset(row - 1);
		}
	} else {
		// メモリダンプ系の場合は単純に row 分前か後ろというだけ
		return GetLineOffset(row * n);
	}
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.