File:  [Isaki's NoNo m68k/m88k emulator] / nono / debugger / debugger_m88xx0.cpp
Revision 1.1.1.16 (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) 2020 nono project
// Licensed under nono-license.txt
//

//
// デバッガ (M88xx0 依存部分)
//

#include "debugger_m88xx0.h"
#include "debugger_memory.h"
#include "debugger.h"
#include "m88100disasm.h"
#include "m88100acc.h"

// newval が oldval から変化していれば太字属性にするマクロ
#define TSBOLD(newval, oldval)	(((newval) != (oldval)) ? TA::Em : TA::Off)

// cpu->reg.name が変化していれば太字属性にするマクロ
#define TSBOLDC(name) TSBOLD(cpu->reg.name, prev.name)

// name の mask で示される部分が変化していれば太字属性にする
#define TSBOLDM(name, mask) TSBOLD((cpu->reg.name & (mask)), (prev.name & (mask)))

// コンストラクタ
DebuggerMD_m88xx0::DebuggerMD_m88xx0(Debugger *parent_)
	: inherited(parent_, CPUArch::M88xx0)
{
	// この時点で Get*Device() は使える
	cpu = GetMPU88xx0Device(parent->mpu);
	bus = GetMainbusDevice();

	// 機種依存変数
	inst_bytes = 4;
	lasize = 32;
	pasize = 32;
	name = "mpu";

	cmp_rd = -1;

	// 今の所 CPU は1つしかないので決め打ち
	cmmu0 = gMainApp.GetObject<m88200>(OBJ_M88200_7);
	cmmu1 = gMainApp.GetObject<m88200>(OBJ_M88200_6);

	// 逆アセンブラで使う
	brhist = gMainApp.GetObject<BranchHistory>(OBJ_MPU_BRHIST);
}

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

bool
DebuggerMD_m88xx0::MMUEnabled() const
{
	// XXX 命令/データの区別がまだないので、とりあえず命令のほうだけ見る
	uint32 xapr = cmmu0->GetAPR(IsSuper() ? 1 : 0);
	return (xapr & m88200::APR_TE);
}

// アドレス変換
busaddr
DebuggerMD_m88xx0::TranslateAddr(busaddr laddr) const
{
	m88200 *cmmu;

	// XXX とりあえずね
	if (laddr.IsData() == false) {
		cmmu = cmmu0;
	} else {
		cmmu = cmmu1;
	}

	return cmmu->TranslatePeek(laddr);
}

// op が bb0 命令なら true を返す
/*static*/ bool
DebuggerMD_m88xx0::IsBB0(uint32 op)
{
	return ((op & 0xf8000000) == 0xd0000000);		// bb0
}

// op が tb0 命令なら true を返す
/*static*/ bool
DebuggerMD_m88xx0::IsTB0(uint32 op)
{
	return ((op & 0xfc00fe00) == 0xf000d000);		// tb0
}

// op が bb0/tb0 命令なら true を返す
/*static*/ bool
DebuggerMD_m88xx0::IsBBTB0(uint32 op)
{
	return IsBB0(op) || IsTB0(op);
}

// op が bb1 命令なら true を返す
/*static*/ bool
DebuggerMD_m88xx0::IsBB1(uint32 op)
{
	return ((op & 0xf8000000) == 0xd8000000);		// bb1
}

// op が tb1 命令なら true を返す
/*static*/ bool
DebuggerMD_m88xx0::IsTB1(uint32 op)
{
	return ((op & 0xfc00fe00) == 0xf000d800);		// tb1
}

// op が bb1/tb1 命令なら true を返す
/*static*/ bool
DebuggerMD_m88xx0::IsBBTB1(uint32 op)
{
	return IsBB1(op) || IsTB1(op);
}

void
DebuggerMD_m88xx0::BackupRegs()
{
	prev = cpu->reg;
}

void
DebuggerMD_m88xx0::SetStepOut()
{
	so_r1 = cpu->reg.r[1];
}

bool
DebuggerMD_m88xx0::IsStepOut() const
{
	return (cpu->reg.xip == so_r1);
}

// この命令がステップイン出来るなら true を返す
bool
DebuggerMD_m88xx0::IsOpStepIn(DebuggerMemoryStream& mem)
{
	uint32 op = mem.Read(inst_bytes);

	if ((op & 0xf800'0000) == 0xc800'0000		// bsr
	 || IsTB0(op)								// tb0
	 || (IsTB1(op) && ((op >> 16) & 0x1f) != 0)	// tb1 (except r0)
	 || (op & 0xfc00'fe00) == 0xf000'e800		// tcnd
	 || (op & 0xfc00'fb00) == 0xf400'c800		// jsr
	 || (op & 0xfc00'0000) == 0xf800'0000		// tbnd imm
	 || (op & 0xfc00'ffe0) == 0xf400'f800		// tbnd rs2
	) {
		return true;
	}

	return false;
}

// レジスタ名からそのレジスタ値を返す。
// メモリダンプのような用途なのでアドレスとして使うレジスタのみ。
uint64
DebuggerMD_m88xx0::GetRegAddr(const char *name) const
{
	char buf[8];

	if (strcmp(name, "xip") == 0) return cpu->reg.xip;
	if (strcmp(name, "nip") == 0) return cpu->reg.nip;
	if (strcmp(name, "fip") == 0) return cpu->reg.fip;

	// 制御レジスタ
	//if (strcmp(name, "pid") == 0)	return cpu->reg.pid;
	//if (strcmp(name, "psr") == 0)	return cpu->reg.psr;
	//if (strcmp(name, "epsr") == 0)	return cpu->reg.epsr;
	//if (strcmp(name, "ssbr") == 0)	return cpu->reg.ssbr;
	if (strcmp(name, "sxip") == 0)	return cpu->reg.sxip & M88100::SIP_MASK;
	if (strcmp(name, "snip") == 0)	return cpu->reg.snip & M88100::SIP_MASK;
	if (strcmp(name, "sfip") == 0)	return cpu->reg.sfip & M88100::SIP_MASK;
	if (strcmp(name, "vbr") == 0)	return cpu->reg.vbr;
	//if (strcmp(name, "dmt0") == 0)	return cpu->reg.dmt0;
	if (strcmp(name, "dmd0") == 0)	return cpu->reg.dmd0;
	if (strcmp(name, "dma0") == 0)	return cpu->reg.dma0;
	//if (strcmp(name, "dmt1") == 0)	return cpu->reg.dmt1;
	if (strcmp(name, "dmd1") == 0)	return cpu->reg.dmd1;
	if (strcmp(name, "dma1") == 0)	return cpu->reg.dma1;
	//if (strcmp(name, "dmt2") == 0)	return cpu->reg.dmt2;
	if (strcmp(name, "dmd2") == 0)	return cpu->reg.dmd2;
	if (strcmp(name, "dma2") == 0)	return cpu->reg.dma2;
	if (strcmp(name, "sr0") == 0)	return cpu->reg.sr[0];
	if (strcmp(name, "sr1") == 0)	return cpu->reg.sr[1];
	if (strcmp(name, "sr2") == 0)	return cpu->reg.sr[2];
	if (strcmp(name, "sr3") == 0)	return cpu->reg.sr[3];

	for (int i = 0; i < countof(cpu->reg.cr); i++) {
		snprintf(buf, sizeof(buf), "cr%u", i);
		if (strcmp(name, buf) == 0) {
			return cpu->reg.cr[i];
		}
	}

	// FPU レジスタ
	// XXX not implement

	// 通常レジスタ
	for (int i = 0; i < countof(cpu->reg.r); i++) {
		snprintf(buf, sizeof(buf), "r%u", i);
		if (strcmp(name, buf) == 0) {
			return cpu->reg.r[i];
		}
	}

	return (uint64)-1;
}

// レジスタ表示系コマンドのヘルプ
/*static*/ const HelpMessages
DebuggerMD_m88xx0::HelpListReg = {
	{ "r",		"Show general registers" },
	{ "rc",		"Show (integer) control registers" },
	{ "rf",		"Show floating point control registers" },
	{ "ro",		"Show other (shadow) registers" },
	{ "ra<id>",	"Show ATC on CMMU<id>" },
	{ "rd<id>",	"Show data cache on CMMU<id>" },
	{ "rm<id>",	"Show CMMU<id> registers" },
};

/*static*/ const HelpMessages
DebuggerMD_m88xx0::HelpReg = {
	//-----
	{ "r", R"**(
	Command: r

	Shows general registers.
	)**" },

	//-----
	{ "rc", R"**(
	Command: rc

	Shows the (integer) control registers.
	)**" },

	//-----
	{ "rf", R"**(
	Command: rf

	Shows the floating point control registers.
	)**" },

	//-----
	{ "ro", R"**(
	Command: ro

	Shows the other registers.
	)**" },

	//-----
	{ "ra", R"**(
	Command: ra<id>   <id> := 7, 6

	Shows BATC/PATC on CMMU<id>.
	CMMU7 is instruction CMMU of CPU#0.
	CMMU6 is data        CMMU of CPU#0.
	)**" },
	{ "ra6", "=ra" },
	{ "ra7", "=ra" },

	//-----
	{ "rd", R"**(
	Command: rd<id>   <id> := 7, 6

	Shows data cache on CMMU<id>.
	CMMU7 is instruction CMMU of CPU#0.
	CMMU6 is data        CMMU of CPU#0.
	)**" },
	{ "rd6", "=rd" },
	{ "rd7", "=rd" },

	//-----
	{ "rm", R"**(
	Command: rm<id>   <id> := 7, 6

	Shows CMMU<id> registers.
	CMMU7 is instruction CMMU of CPU#0.
	CMMU6 is data        CMMU of CPU#0.
	)**" },
	{ "rm6", "=rm" },
	{ "rm7", "=rm" },
};

const HelpMessages&
DebuggerMD_m88xx0::GetHelpListReg() const
{
	return HelpListReg;
}

const HelpMessages&
DebuggerMD_m88xx0::GetHelpReg() const
{
	return HelpReg;
}

// レジスタ表示系コマンド
bool
DebuggerMD_m88xx0::ShowRegister(FILE *cons,
	const std::vector<std::string>& args)
{
	int cmmu_id;

	// 余分な引数は無視する?

	if (args[0] == "r") {
		ShowRegMain();
		return true;
	}
	if (args[0] == "rc") {
		ShowRegCtrl();
		return true;
	}
	if (args[0] == "rf") {
		ShowRegFPU();
		return true;
	}
	if (args[0] == "ro") {
		ShowRegOther();
		return true;
	}

	if (MatchCMMUCmd(args[0], "ra", &cmmu_id)) {
		Monitor *mon = gMonitorManager->Find(ID_MONITOR_ATC(cmmu_id));
		if (mon) {
			parent->ShowMonitor(mon);
			return true;
		}
		goto notfound;
	}
	if (MatchCMMUCmd(args[0], "rd", &cmmu_id)) {
		return ShowRegCache(cons, args, cmmu_id);
	}
	if (MatchCMMUCmd(args[0], "rm", &cmmu_id)) {
		Monitor *mon = gMonitorManager->Find(ID_MONITOR_CMMU(cmmu_id));
		if (mon) {
			parent->ShowMonitor(mon);
			return true;
		}
		goto notfound;
	}

	// 該当なし
 notfound:
	return false;
}

// CMMU ID 入りコマンド名を照合する。
// cmdname が basename + <id> の時、*idp に id を格納して true を返す。
// 一致しないか id が(数値でないなど)不正なら false を返す。
// 実際にその id の CMMU が存在するかはここではチェックしない。
bool
DebuggerMD_m88xx0::MatchCMMUCmd(const std::string& cmdname,
	const char *basename, int *idp)
{
	int baselen = strlen(basename);

	if (strncmp(cmdname.c_str(), basename, baselen) != 0) {
		return false;
	}

	// 先頭が一致したら id を調べる
	int id;
	const char *start = cmdname.c_str() + baselen;
	char *end;
	errno = 0;
	id = strtoul(start, &end, 10);
	if (*start == '\0' || *end != '\0' || errno == ERANGE) {
		return false;
	}

	*idp = id;
	return true;
}

void
DebuggerMD_m88xx0::ShowRegMain()
{
	TextScreen s(80, 8);

/*
r0 :00000000  r8 :00000000  r16:00000000  r24:00000000   b:bs=1 a:lo=1 8:ls=0
r1 :00700000  r9 :00000000  r17:00000000  r25:00000000
r2 :0070e1a0  r10:00000000  r18:00000000  r26:00000000
*/

	for (int i = 0; i < countof(cpu->reg.r); i++) {
		s.Print((i / 8) * 14, i % 8, TSBOLDC(r[i]), "r%-2d:%08x",
			i, cpu->reg.r[i]);
	}

	// cmp 以降最初の分岐命令で結果レジスタの条件ビットマップを表示する。
	// 分岐命令は bb1 #3 みたいな形式なのでそれだけでは条件演算子が分からない
	// のと、bb1 #n 自体は cmp 以外に対しても使えるので、cmp 命令以降最初の
	// 分岐命令でのみ表示してみる。
	if (cmp_rd >= 0 && (IsBBTB0(cpu->reg.opX) || IsBBTB1(cpu->reg.opX))) {
		static const char * const cmpstr[] = {
			NULL, NULL, "eq", "ne", "gt", "le", "lt", "ge",
			"hi", "ls", "lo", "hs",
		};
		int x = 0;
		int y = 0;
		uint32 res = cpu->reg.r[cmp_rd];
		for (int b = 11; b >= 2; b--) {
			s.Print(57 + x * 7, y, "%x:%s=%u", b, cmpstr[b],
				(res & (1U << b)) ? 1 : 0);
			x++;
			if (x > 2) {
				x = 0;
				y++;
			}
		}
	}

	parent->ShowTextScreen(s);
}

void
DebuggerMD_m88xx0::ShowRegCtrl()
{
	TextScreen s(80, 6);
	int x;
	int y;

/*
0         1         2         3         4         5         6         7
0123456789012345678901234567890123456789012345678901234567890123456789012345678
pid (cr0):12345678(Arch=$00 Ver=$00 M/C=Checker)       sr0(cr17):00000000
psr (cr1):12345678(S,LE,SER,Cy SFD1,MXM,IND,SFRZ)      sr1(cr18):00000000
epsr(cr2):12345678(S,LE,SER,Cy SFD1,MXM,IND,SFRZ)      sr2(cr19):00000000
xip:00000000  opX:00000000(--)  sxip(cr4):00000000:VE  sr3(cr20):00000000
nip:00000000  opF:00000000(--)  snip(cr5):00000000:--  ssbr(cr3):00000000
fip:00000000                    sfip(cr6):00000000:VE  vbr (cr7):00000000
*/

#define PRINT_B(x, y, r, mask, fmt, arg)	do { \
	uint32 _new = cpu->reg.r & (mask);	\
	uint32 _old = prev.r & (mask);	\
	s.Print((x), (y), TSBOLD(_new, _old), fmt, (arg));	\
} while (0)

	// pid(cr0)
	x = 0;
	y = 0;
	s.Print(x,      y, TSBOLDC(pid), "pid (cr0):%08x(", cpu->reg.pid);
	PRINT_B(x + 19, y, pid, M88100::PID_REV_MASK, "Arch=$%02x",
		(_new >> 8) & 0xff);
	PRINT_B(x + 28, y, pid, M88100::PID_VER_MASK, "Ver=$%02x",
		(_new >> 1) & 0x7f);
	PRINT_B(x + 36, y, pid, M88100::PID_MASTER, "M/C=%s)",
		(_new & M88100::PID_MASTER) ? "Master" : "Checker");

	// psr(cr1)
	y++;
	s.Print(x,      y, TSBOLDC(psr), "psr (cr1):%08x", cpu->reg.psr);
	s.Print(x + 18, y, "(s,le,ser,cy sfd1,mxm,ind,sfrz)");
	PRINT_B(x + 19, y, psr, M88100::PSR_SUPER, "%c", _new ? 'S' : '-');
	PRINT_B(x + 21, y, psr, M88100::PSR_BO_LE, "%s", _new ? "LE" : "BE");
	PRINT_B(x + 24, y, psr, M88100::PSR_SER,   "%s", _new ? "CON" : "SER");
	PRINT_B(x + 28, y, psr, M88100::PSR_C,     "%s", _new ? "Cy" : "--");
	PRINT_B(x + 31, y, psr, M88100::PSR_SFD1,  "%s", _new ? "SFD1" : "----");
	PRINT_B(x + 36, y, psr, M88100::PSR_MXM,   "%s", _new ? "MXM" : "---");
	PRINT_B(x + 40, y, psr, M88100::PSR_IND,   "%s", _new ? "IND" : "---");
	PRINT_B(x + 44, y, psr, M88100::PSR_SFRZ,  "%s", _new ? "SFRZ" : "----");

	// epsr(cr2)
	y++;
	s.Print(x,      y, TSBOLDC(epsr), "epsr(cr2):%08x", cpu->reg.epsr);
	s.Print(x + 18, y, "(s,le,ser,cy sfd1,mxm,ind,sfrz)");
	PRINT_B(x + 19, y, epsr, M88100::PSR_SUPER, "%c", _new ? 'S' : '-');
	PRINT_B(x + 21, y, epsr, M88100::PSR_BO_LE, "%s", _new ? "LE" : "BE");
	PRINT_B(x + 24, y, epsr, M88100::PSR_SER,   "%s", _new ? "CON" : "SER");
	PRINT_B(x + 28, y, epsr, M88100::PSR_C,     "%s", _new ? "Cy" : "--");
	PRINT_B(x + 31, y, epsr, M88100::PSR_SFD1,  "%s", _new ? "SFD1":"----");
	PRINT_B(x + 36, y, epsr, M88100::PSR_MXM,   "%s", _new ? "MXM" : "---");
	PRINT_B(x + 40, y, epsr, M88100::PSR_IND,   "%s", _new ? "IND" : "---");
	PRINT_B(x + 44, y, epsr, M88100::PSR_SFRZ,  "%s", _new ? "SFRZ":"----");

	// *IP
	x = 0;
	y = 3;
	s.Print(x,      y, "xip:%08x  opX:%08x(%c)",
		cpu->reg.xip, (uint32)cpu->reg.opX,
		cpu->OpIsBusErr(cpu->reg.opX) ? 'B' : '-');
	s.Print(x,  y + 1, "nip:%08x  opF:%08x(%c)",
		cpu->reg.nip, (uint32)cpu->reg.opF,
		cpu->OpIsBusErr(cpu->reg.opF) ? 'B' : '-');
	s.Print(x,  y + 2, "fip:%08x", cpu->reg.fip);

	// S*IP
	x = 32;
	for (int i = 0; i < 3; i++) {
		s.Print(x, y + i, TSBOLDC(cr[4 + i]), "%s(cr%u):%08x:%c%c",
			m88100reg::sipname[i], 4 + i,
			(cpu->reg.cr[4 + i] & M88100::SIP_MASK),
			(cpu->reg.cr[4 + i] & M88100::SIP_V) ? 'V' : '-',
			(cpu->reg.cr[4 + i] & M88100::SIP_E) ? 'E' : '-');
	}

	x = 55;
	y = 0;
	// SR*
	for (int i = 0; i < 4; i++) {
		int rn = 17 + i;
		s.Print(x, y++, TSBOLDC(cr[rn]), "sr%u(cr%u):%08x",
			i, rn, cpu->reg.cr[rn]);
	}
	// ssbr(cr3)
	s.Print(x, y++, TSBOLDC(ssbr), "ssbr(cr3):%08x", cpu->reg.ssbr);
	// vbr(cr7)
	s.Print(x, y++, TSBOLDC(vbr),  "vbr (cr7):%08x", cpu->reg.vbr);

	parent->ShowTextScreen(s);
}

// CMMU のデータキャッシュを表示
// rd<N>       なら概要表示。
// rd<N> <set> なら個別セットの詳細表示。
// id を受け付ければ true を、そうでなければ false を返す。
bool
DebuggerMD_m88xx0::ShowRegCache(FILE *cons,
	const std::vector<std::string>& args, int id)
{
	m88200 *cmmu;

	cmmu = gMainApp.FindObject<m88200>(OBJ_M88200(id));
	if (cmmu == NULL) {
		return false;
	}

	if (args.size() < 2) {
		ShowRegCacheOverview(cmmu);
	} else {
		int set;
		char *end;
		errno = 0;
		set = strtol(args[1].c_str(), &end, 16);
		if (end == &args[1][0] || *end != '\0' || errno == ERANGE) {
			fprintf(cons, "<set> must be a number.\n");
			goto done;
		}
		if (set < 0 || set > 256) {
			fprintf(cons, "<set> must be in 00..ff.\n");
			goto done;
		}
		ShowRegCacheSet(cmmu, set);
	}
 done:
	return true;
}

// CMMU データキャッシュの概要表示
void
DebuggerMD_m88xx0::ShowRegCacheOverview(m88200 *cmmu)
{
	TextScreen s(70, 17);

	cmmu->MonitorCacheOverview(s, 0, false);
	parent->ShowTextScreen(s);
}

// CMMU データキャッシュの指定セットの詳細表示
void
DebuggerMD_m88xx0::ShowRegCacheSet(m88200 *cmmu, int setidx)
{
	TextScreen s(70, 5);

	cmmu->MonitorCacheSet(s, 0, setidx);
	parent->ShowTextScreen(s);
}

void
DebuggerMD_m88xx0::ShowRegFPU()
{
	static const char * const fcrname[] = {
		"fpecr",
		"fphs1",
		"fpls1",
		"fphs2",
		"fpls2",
		"fppt",
		"fprh",
		"fprl",
		"fpit",
		"fpsr",	// [9]  62
		"fpcr",	// [10] 63
	};
	TextScreen s(80, 4);

	for (uint i = 0; i < 9; i++) {
		s.Print((i / 4) * 22, i % 4, TSBOLDC(fcr[i]),
			"%-5s(fcr%u):%08x", fcrname[i], i, cpu->reg.fcr[i]);
	}

	s.Print(44, 2, TSBOLDC(fpsr), "fpsr(fcr62):%08x", cpu->reg.fpsr);
	s.Print(44, 3, TSBOLDC(fpcr), "fpcr(fcr63):%08x", cpu->reg.fpcr);

	parent->ShowTextScreen(s);
}

void
DebuggerMD_m88xx0::ShowRegOther()
{
	TextScreen s(80, 3);
/*
0         1         2         3         4         5         6         7
0123456789012345678901234567890123456789012345678901234567890123456789012345678
dmt0(cr8) :0000(B,S,D,L,Rxx,S,---B,W,V) dmd0(cr9) :00000000 dma0(cr10):00000000
dmt0(cr11):0000(B,U,D,L,Rxx,S,HH--,W,V) dmd0(cr9) :00000000 dma0(cr10):00000000
dmt0(cr14):0000(B, ,D,L,Rxx,S,LLLL,W,V) dmd0(cr9) :00000000 dma0(cr10):00000000
*/
	for (uint i = 0; i <= 2; i++) {
		uint dt =  8 + i * 3;
		uint dd =  9 + i * 3;
		uint da = 10 + i * 3;

		s.Print(0, i, TSBOLDC(cr[dt]), "dmt%u(cr%-2u):%04x",
			i, dt, (cpu->reg.cr[dt] & 0xffff));
		s.Print(15, i, "(b,s,d,l,rx, s,en  ,w,v)");
		PRINT_B(16, i, cr[dt], M88100::DM_BO,     "%c", _new ? 'B' : '-');
		PRINT_B(18, i, cr[dt], M88100::DM_DAS,    "%c", _new ? 'S' : 'U');
		PRINT_B(20, i, cr[dt], M88100::DM_DOUB1,  "%c", _new ? 'D' : '-');
		PRINT_B(22, i, cr[dt], M88100::DM_LOCK,   "%c", _new ? 'L' : '-');
		// カンマを前詰め。かつ DREG がボールドでもカンマはボールドにしない
		PRINT_B(24, i, cr[dt], M88100::DM_DREG_MASK, "r%u", _new >> 7);
		s.Puts(",");
		PRINT_B(28, i, cr[dt], M88100::DM_SIGNED, "%c", _new ? 'S' : '-');
		PRINT_B(30, i, cr[dt], M88100::DM_EN_MASK,"%s",
			m88100reg::dmt_en_str[(_new >> 2) & 0xf]);
		PRINT_B(35, i, cr[dt], M88100::DM_WRITE,  "%c", _new ? 'W' : '-');
		PRINT_B(37, i, cr[dt], M88100::DM_VALID,  "%c", _new ? 'V' : '-');

		s.Print(40, i, TSBOLDC(cr[dd]), "dmd%u(cr%-2u):%08x",
			i, dd, cpu->reg.cr[dd]);
		s.Print(60, i, TSBOLDC(cr[dd]), "dma%u(cr%2u):%08x",
			i, da, cpu->reg.cr[da]);
	}

	parent->ShowTextScreen(s);
}

// オンライン用逆アセンブル
std::string
DebuggerMD_m88xx0::Disassemble(DebuggerMemoryStream& mem)
{
	m88100disasm dis;
	if (dis.Exec(&mem) == false) {
		return "dis.Exec() failed.";
	}

	// ニーモニック
	std::string mnemonic = dis.text;
	// 別名追加
	if (!dis.alttext.empty()) {
		int len = mnemonic.length() % 8;
		mnemonic += std::string(8 - len, ' ');
		mnemonic += dis.alttext;
	}

	// 遅延スロットは分かりやすく表示したい
	bool delay = false;
	if (cpu->reg.xip + 4 != cpu->reg.nip) {
		delay = true;
	} else {
		const auto& e = brhist->entry[brhist->top];
		if (e.from + 4 == cpu->reg.xip) {
			// 直前の位置にあるブランチが遅延ブランチかどうか調べる
			if ((e.info & 0xfc000000) == 0xf4000000) {
				// jmp, jsr
				delay = (e.info >> 10) & 1;
			} else if (0xc0000000 <= e.info && e.info < 0xfc000000) {
				// br, bsr, bcnd
				delay = (e.info >> 26) & 1;
			}
		}
	}

	// 条件判断
	auto bin = dis.bin;
	uint32 op = (bin[0] << 24) | (bin[1] << 16) | (bin[2] << 8) | bin[3];
	const char *cond = CondStr(op);

	std::string str;
	str = string_format("%08x ", op);
	if (delay) {
		str += "(delayed) ";
	}
	str += mnemonic;
	str += cond;

	return str;
}

#define TAKE_IF(expr) do { \
	if ((expr)) \
		return " (will take)";	\
	else \
		return " (will not take)";	\
} while (0)

// 条件命令なら、成立可否などの文字列を返す。
const char *
DebuggerMD_m88xx0::CondStr(uint32 op)
{
	uint32 s = m88100opf_S1(op);

	if (IsBBTB0(op)) {
		// 11010N BBBBB SSSSS dddddd dd dddddddd	bb0
		// 111100 BBBBB SSSSS 110100 0V VVVVVVVV	tb0
		// rS の bit B が %0 ならブランチ/トラップ
		cmp_rd = -1;
		uint32 b = m88100opf_B5(op);
		TAKE_IF((cpu->reg.r[s] & (1U << b)) == 0);
	}
	if (IsBB1(op)) {
		// 11011N BBBBB SSSSS dddddd dd dddddddd	bb1
		// rS の bit B が %1 ならブランチ
		cmp_rd = -1;
		// 対 r0 なら絶対成立しないので nop
		if (s == 0) {
			return " (nop)";
		}
		uint32 b = m88100opf_B5(op);
		TAKE_IF((cpu->reg.r[s] & (1U << b)) != 0);
	}
	if (IsTB1(op)) {
		// 111100 BBBBB SSSSS 110110 0V VVVVVVVV	tb1
		// rS の bit B が %1 ならトラップ
		cmp_rd = -1;
		// 対 r0 なら sync として使っており逆アセンブラ側で表示を加工してある
		// のでこちら側では対処不要。
		if (s == 0) {
			return "";
		}
		uint32 b = m88100opf_B5(op);
		TAKE_IF((cpu->reg.r[s] & (1U << b)) != 0);
	}
	if ((op & 0xf8000000) == 0xe8000000 ||	// bcnd
	    (op & 0xfc00fe00) == 0xf000e800)	// tcnd
	{
		// 11101N MMMMM SSSSS dddddd dd dddddddd	bcnd
		// rS が M5 で示される条件にマッチすればブランチ/トラップ
		cmp_rd = -1;
		uint32 m5 = m88100opf_M5(op);
		TAKE_IF(
		    (m5 == 0x2 && cpu->reg.r[s] == 0) ||		// eq0
		    (m5 == 0xd && cpu->reg.r[s] != 0) ||		// ne0
		    (m5 == 0x1 && (int32)cpu->reg.r[s] > 0)  ||	// gt0
		    (m5 == 0xc && (int32)cpu->reg.r[s] < 0)  ||	// lt0
		    (m5 == 0x3 && (int32)cpu->reg.r[s] >= 0) ||	// ge0
		    (m5 == 0xe && (int32)cpu->reg.r[s] <= 0)	// le0
		);
	}

	if ((op & 0xfc00fce0) == 0xf4007c00 ||	// cmp rD,rS1,rS2
	    (op & 0xfc000000) == 0x7c000000   )	// cmp rD,rS1,imm16
	{
		// 次の条件分岐命令で結果レジスタを表示するため、rD だけ覚えておく
		cmp_rd = m88100opf_D(op);
	}

	return "";
}

// 命令ニーモニックに対するバイナリを文字列形式で返す。
// 対応しない場合は "" を返す。cmd_bi 用。
std::string
DebuggerMD_m88xx0::ParseInst(const std::string& inst) const
{
	if (inst == "rte")		return "f400fc00";

	return "";
}

unix.superglobalmegacorp.com

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