File:  [Isaki's NoNo m68k/m88k emulator] / nono / debugger / debugger_m680x0.cpp
Revision 1.1.1.13 (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
//

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

#include "debugger_m680x0.h"
#include "debugger_memory.h"
#include "debugger.h"
#include "exttostr.h"
#include "m680x0disasm.h"
#include "m68030mmu.h"
#include "mainbus.h"
#include "vectortable.h"

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

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

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

// アドレス変換
busaddr
DebuggerMD_m680x0::TranslateAddr(busaddr laddr) const
{
	return cpu->TranslatePeek(laddr);
}

// op が 680x0 の条件命令 Bcc, Scc, TRAPcc, DBcc なら true を返す。
/*static*/ bool
DebuggerMD_m680x0::IsOPcc(uint16 op)
{
	uint16 cc;

	if ((op & 0xf000) == 0x6000) {
		cc = (op >> 8) & 0xf;
		if (cc == 0 || cc == 1) {		// BRA, BSR
			return false;
		} else {
			return true;				// Bcc
		}
	}

	if ((op & 0xf0c0) == 0x50c0) {		// Scc, TRAPcc, DBcc
		return true;
	}

	return false;
}

// op が DBcc 命令なら true を返す。
/*static*/ bool
DebuggerMD_m680x0::IsDBcc(uint16 op)
{
	if ((op & 0xf0f8) == 0x50c8) {		// DBcc
		return true;
	}
	return false;
}

// ir[] が FBcc, FScc, FDBcc, FTRAPcc 命令なら condition のあるワード位置を
// 返す。これらの条件命令でなければ (FNOP も含む)、-1 を返す。
/*static*/ int
DebuggerMD_m680x0::IsFXcc(const std::vector<uint16>& ir)
{
	if ((ir[0] & 0xfe00) != 0xf200) {
		return -1;
	}
	if (ir.size() < 2) {
		return -1;
	}

	// FNOP は除く。
	if (ir[0] == 0xf280 && ir[1] == 0) {
		return -1;
	}

	uint type = (ir[0] >> 6) & 7;
	if (type == 1) {		// FScc/FDBcc/FTRAPcc
		return 1;
	} else if (type == 2) { // FBcc.W
		return 0;
	} else if (type == 3) {	// FBcc.L
		return 0;
	}
	return -1;
}

// op の cccc 条件が現在の CCR で成立するなら true を返す。
bool
DebuggerMD_m680x0::IsCond(uint16 op) const
{
	bool N = cpu->reg.ccr.IsN();
	bool Z = cpu->reg.ccr.IsZ();
	bool V = cpu->reg.ccr.IsV();
	bool C = cpu->reg.ccr.IsC();

	switch ((op >> 8) & 0x0f) {
	 case 0:	// T
		return true;
	 case 1:	// F
		return false;
	 case 2:	// HI
		return (!C) && (!Z);
	 case 3:	// LS
		return C || Z;
	 case 4:	// CC
		return !C;
	 case 5:	// CS
		return C;
	 case 6:	// NE
		return !Z;
	 case 7:	// EQ
		return Z;
	 case 8:	// VC
		return !V;
	 case 9:	// VS
		return V;
	 case 10:	// PL
		return !N;
	 case 11:	// MI
		return N;
	 case 12:	// GE
		return (N && V) || ((!N) && (!V));
	 case 13:	// LT
		return (N && (!V)) || ((!N) && V);
	 case 14:	// GT
		return (N && V && (!Z)) || ((!N) && (!V) && (!Z));
	 case 15:	// LE
		return Z || (N && (!V)) || ((!N) && V);
	 default:
		__unreachable();
	}
}

// addr 位置の命令が条件命令なら、成立可否などの文字列を返す。
// Bcc, Scc, TRAPcc 命令なら、条件が成立するかどうか。
// DBcc 命令ならブランチするかどうか。
const char *
DebuggerMD_m680x0::CondStr(const std::vector<uint16>& ir) const
{
	uint16 op = ir[0];

	// DBcc 命令なら、ブランチするかどうか
	// IsOPcc() は DBcc も含んでいるため、IsDBcc() の判定のほうが先。
	// DBcc は内部でカウンタレジスタを減算してから比較するため、
	// 命令実行前のカウンタが 0 の時点でループ終了となることに注意。
	if (IsDBcc(op)) {
		// 成立したら何もしない
		if (IsCond(op)) {
			return " (will fall)";
		}
		// カウンタが -1 なら何もしない
		uint rr = op & 7;
		if ((cpu->reg.R[rr] & 0xffff) == 0) {
			return " (will expire)";
		}
		// それ以外はブランチ
		return " (will take)";
	}

	// Bcc, Scc, TRAPcc 命令なら条件が成立するかどうか
	if (IsOPcc(op)) {
		if (IsCond(op)) {
			return " (will take)";
		} else {
			return " (will not take)";
		}
	}

	// FXcc
	int n = IsFXcc(ir);
	if (n >= 0) {
		struct fpemu fe0;
		struct fpframe fpf0;
		memset(&fe0, 0, sizeof(fe0));
		fe0.fe_fpframe = &fpf0;
		fe0.fe_fpsr = cpu->reg.fpframe.fpf_fpsr;
		int result = test_cc(&fe0, ir[n]);
		if (result < 0) {
			return " (will take)";
		} else if (result == 0) {
			return " (will not take)";
		}
	}

	// (ここで知ってる)条件命令ではない
	return "";
}

void
DebuggerMD_m680x0::SetStepOut()
{
	so_a7 = cpu->reg.A[7];
	so_sr = cpu->GetSR() & 0x3000;
}

bool
DebuggerMD_m680x0::IsStepOut() const
{
	return (cpu->reg.A[7] > so_a7 || (cpu->GetSR() & 0x3000) != so_sr);
}

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

	if ((op & 0xfff0) == 0x4e40		// TRAP
	 || (op == 0x4e76)				// TRAPV
	 || (op & 0xffc0) == 0x4e80		// JSR
	 || (op & 0xf0f8) == 0x50f8		// TRAPcc
	 || (op & 0xff00) == 0x6100		// BSR
	 || (op & 0xfff8) == 0xf278		// FTRAPcc
	 || (op & 0xfe00) == 0xfe00		// DOS($FFxx), FPACK($FExx)
	) {
		return true;
	}

	// IOCS コール(に逆アセンブラで見えるやつ)も一命令扱いにしておく。
	// XXX 本当は X68k の時だけだが今の所副作用なさげなので
	if ((op & 0xff00) == 0x7000) {	// MOVEQ to %d0
		uint32 op2 = mem.Read(inst_bytes);
		if (op2 == 0x4e4f) {		// TRAP #15
			return true;
		}
	}

	return false;
}

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

	if ((*name | 0x20) == 'a') {
		if (name[1] < '0' || name[1] > '7') {
			return (uint64)-1;
		}
		addr = cpu->reg.A[name[1] - '0'];

	} else if ((*name | 0x20) == 'd') {
		if (name[1] < '0' || name[1] > '7') {
			return (uint64)-1;
		}
		addr = cpu->reg.D[name[1] - '0'];

	} else if (strcmp(name, "sp") == 0) {
		addr = cpu->reg.A[7];

	} else if (strcmp(name, "usp") == 0) {
		if (cpu->reg.s == false) {
			addr = cpu->reg.A[7];
		} else {
			addr = cpu->reg.usp;
		}

	} else if (strcmp(name, "isp") == 0) {
		if (cpu->reg.s && cpu->reg.m == false) {
			addr = cpu->reg.A[7];
		} else {
			addr = cpu->reg.isp;
		}

	} else if (strcmp(name, "msp") == 0) {
		if (cpu->reg.s && cpu->reg.m) {
			addr = cpu->reg.A[7];
		} else {
			addr = cpu->reg.msp;
		}

	} else if (strcmp(name, "ssp") == 0) {
		if (cpu->reg.s) {
			addr = cpu->reg.A[7];
		} else {
			if (cpu->reg.m) {
				addr = cpu->reg.msp;
			} else {
				addr = cpu->reg.isp;
			}
		}

	} else if (strcmp(name, "pc") == 0) {
		addr = cpu->reg.pc;

	} else if (strcmp(name, "vbr") == 0) {
		addr = cpu->GetVBR();

	} else if (strcmp(name, "srp") == 0) {
		if (cpu->GetMPUType() == m680x0MPUType::M68030) {
			auto cpu68030 = dynamic_cast<MPU68030Device *>(cpu);
			addr = cpu68030->GetSRPl();
		} else {
			auto cpu68040 = dynamic_cast<MPU68040Device *>(cpu);
			addr = cpu68040->GetSRP();
		}

	} else if (strcmp(name, "crp") == 0) {
		auto cpu68030 = dynamic_cast<MPU68030Device *>(cpu);
		if (cpu68030) {
			addr = cpu68030->GetCRPl();
		} else {
			return (uint64)-1;
		}

	} else if (strcmp(name, "urp") == 0) {
		auto cpu68040 = dynamic_cast<MPU68040Device *>(cpu);
		if (cpu68040) {
			addr = cpu68040->GetURP();
		} else {
			return (uint64)-1;
		}

	} else {
		return (uint64)-1;
	}
	return addr;
}

// レジスタ表示系コマンドのヘルプ
/*static*/ const HelpMessages
DebuggerMD_m680x0::HelpListReg = {
	{ "r",		"Show general registers" },
	{ "ra",		"Show ATC" },
	{ "rf",		"Show FPU registers" },
	{ "rm",		"Show MMU registers" },
	{ "ro",		"Show other registers" },
};

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

	Shows main registers.
	)**" },

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

	Shows m68030 ATC(Address Translation Cache) information.  Note that
	nono's ATC implementation is very different from the real one (to
	improve performance).
	)**" },

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

	Shows 68881/68882's FPU registers.
	)**" },

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

	Shows registers related to MMU and cache.
	)**" },

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

	Shows other registers.
	)**" },
};

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

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

// レジスタ表示系コマンド
bool
DebuggerMD_m680x0::ShowRegister(FILE *cons,
	const std::vector<std::string>& args)
{
	// 余分な引数は無視する?

	if (args[0] == "r") {
		ShowRegMain(cons);
		return true;
	}
	if (args[0] == "ra") {
		ShowRegATC();
		return true;
	}
	if (args[0] == "rf") {
		ShowRegFPU(cons);
		return true;
	}
	if (args[0] == "rm") {
		if (cpu->GetMPUType() == m680x0MPUType::M68030) {
			ShowRegMMU30(cons);
		} else {
			ShowRegMMU40(cons);
		}
		return true;
	}
	if (args[0] == "ro") {
		ShowRegOther();
		return true;
	}

	// 該当なし
	return false;
}


// レジスタ表示 (基本セット)
void
DebuggerMD_m680x0::ShowRegMain(FILE *cons)
{
/*
D0:00000070 D4:CCCCCCCC  A0:00FFAA32 A4:CCCCCCCC  SR=F810(S I=0 X----)
D1:0000FFFF D5:00000000  A1:00000A7A A5:00000A7A
D2:00FF0000 D6:00000000  A2:CCCCCCCC A6:CCCCCCCC
D3:CCCCCCCC D7:00000003  A3:CCCCCCCC A7:00001FD8
*/
	bool vr[16];

	for (int i = 0; i < countof(cpu->reg.R); i++) {
		vr[i] = (cpu->reg.R[i] != prev.R[i]);
	}

	// 1行目
	fprintf(cons, "%sD%d:%08x%s %sD%d:%08x%s  %sA%d:%08x%s %sA%d:%08x%s ",
		BOLDIF(vr[ 0]),  0, cpu->reg.R[ 0], NORM,
		BOLDIF(vr[ 4]),  4, cpu->reg.R[ 4], NORM,
		BOLDIF(vr[ 8]),  0, cpu->reg.R[ 8], NORM,
		BOLDIF(vr[12]),  4, cpu->reg.R[12], NORM);

	// 1行目 SR
	uint16 sr = cpu->GetSR();
	uint16 prevsr = prev.GetSR();
	// SR は上位バイトと下位バイトが変化したくらいでいいか?
	fprintf(cons, " SR:%s%02x%s%s%02x%s",
		BOLDIF((sr & 0xff00) != (prevsr & 0xff00)),
		sr >> 8,
		NORM,
		BOLDIF((sr & 0x00ff) != (prevsr & 0x00ff)),
		sr & 0xff,
		NORM);
	fprintf(cons, "(%c I=%d %c%c%c%c%c)\n",
		(sr & 0x2000) ? 'S' : '-',
		(sr >> 8) & 7,
		(sr & M68K::CCR_X) ? 'X' : '-',
		(sr & M68K::CCR_N) ? 'N' : '-',
		(sr & M68K::CCR_Z) ? 'Z' : '-',
		(sr & M68K::CCR_V) ? 'V' : '-',
		(sr & M68K::CCR_C) ? 'C' : '-');

	// 2行目
	fprintf(cons, "%sD%d:%08x%s %sD%d:%08x%s  %sA%d:%08x%s %sA%d:%08x%s\n",
		BOLDIF(vr[ 1]),  1, cpu->reg.R[ 1], NORM,
		BOLDIF(vr[ 5]),  5, cpu->reg.R[ 5], NORM,
		BOLDIF(vr[ 9]),  1, cpu->reg.R[ 9], NORM,
		BOLDIF(vr[13]),  5, cpu->reg.R[13], NORM);

	// 3行目
	fprintf(cons, "%sD%d:%08x%s %sD%d:%08x%s  %sA%d:%08x%s %sA%d:%08x%s\n",
		BOLDIF(vr[ 2]),  2, cpu->reg.R[ 2], NORM,
		BOLDIF(vr[ 6]),  6, cpu->reg.R[ 6], NORM,
		BOLDIF(vr[10]),  2, cpu->reg.R[10], NORM,
		BOLDIF(vr[14]),  6, cpu->reg.R[14], NORM);

	// 4行目
	fprintf(cons, "%sD%d:%08x%s %sD%d:%08x%s  %sA%d:%08x%s %sA%d:%08x%s\n",
		BOLDIF(vr[ 3]),  3, cpu->reg.R[ 3], NORM,
		BOLDIF(vr[ 7]),  7, cpu->reg.R[ 7], NORM,
		BOLDIF(vr[11]),  3, cpu->reg.R[11], NORM,
		BOLDIF(vr[15]),  7, cpu->reg.R[15], NORM);
}

// FPU レジスタ表示
void
DebuggerMD_m680x0::ShowRegFPU(FILE *cons)
{
/*
FP0:0000_12345678_12345678 = -0.1234567890123456789)
FPCR:     1234                   BS,SN,OE,OF,UF,DZ,X2,X1  RP=.X RM=Minus
FPSR: 12345678 N Z Inf NAN Q=$xx BS,SN,OE,OF,UF,DZ,X2,X1  AV AO AU AZ AX
FPIAR:12345678
*/

	uint32 fpcr, fpsr, fpiar;
	uint32 ppcr, ppsr, ppiar;
	static const char * const rmstr[] = {
		"Near",
		"Zero",
		"Minus",
		"Plus",
	};

	fpcr  = cpu->reg.fpframe.fpf_fpcr;
	fpsr  = cpu->reg.fpframe.fpf_fpsr;
	fpiar = cpu->reg.fpframe.fpf_fpiar;
	ppcr  = prev.fpframe.fpf_fpcr;
	ppsr  = prev.fpframe.fpf_fpsr;
	ppiar = prev.fpframe.fpf_fpiar;

	for (uint n = 0; n < 8; n++) {
		uint32 *c_ = cpu->reg.fpframe.fpf_regs + (n) * 3;
		uint32 *p_ = prev.fpframe.fpf_regs + (n) * 3;
		bool d = (c_[0] ^ p_[0]) | (c_[1] ^ p_[1]) | (c_[2] ^ p_[2]);
		fprintf(cons, "%sFP%u:%04x_%08x_%08x%s = %s\n",
			BOLDIF(d), n, c_[0] >> 16, c_[1], c_[2], NORM,
			ExtToStr(c_).c_str());
	}

	fprintf(cons, "%sFPCR:     %04x%s%20s",
		BOLDIF(fpcr != ppcr), fpcr, NORM, "");
	fprintf(cons, "%s %s %s %s %s %s %s %s  ",
		(fpcr & 0x8000) ? "BS" : "--",
		(fpcr & 0x4000) ? "SN" : "--",
		(fpcr & 0x2000) ? "OE" : "--",
		(fpcr & 0x1000) ? "OF" : "--",
		(fpcr & 0x0800) ? "UF" : "--",
		(fpcr & 0x0400) ? "DZ" : "--",
		(fpcr & 0x0200) ? "X2" : "--",
		(fpcr & 0x0100) ? "X1" : "--");
	fprintf(cons, "RP=.%c RM=%s\n",
		"XSD?"[(fpcr >> 6) & 3],
		rmstr[(fpcr >> 4) & 3]);

	uint cc = fpsr >> 24;
	fprintf(cons, "%sFPSR: %08x%s ", BOLDIF(fpsr != ppsr), fpsr, NORM);
	fprintf(cons, "%c %c %s %s Q=$%02x  ",
		(cc & 0x08) ? 'N' : '-',
		(cc & 0x04) ? 'Z' : '-',
		(cc & 0x02) ? "Inf" : "---",
		(cc & 0x01) ? "NAN" : "---",
		(fpsr >> 16) & 0xff);
	fprintf(cons, "%s %s %s %s %s %s %s %s  ",
		(fpsr & 0x8000) ? "BS" : "--",
		(fpsr & 0x4000) ? "SN" : "--",
		(fpsr & 0x2000) ? "OP" : "--",
		(fpsr & 0x1000) ? "OV" : "--",
		(fpsr & 0x0800) ? "UF" : "--",
		(fpsr & 0x0400) ? "DZ" : "--",
		(fpsr & 0x0200) ? "I2" : "--",
		(fpsr & 0x0100) ? "I1" : "--");
	fprintf(cons, "%s %s %s %s %s\n",
		(fpsr & 0x80) ? "AV" : "--",
		(fpsr & 0x40) ? "AO" : "--",
		(fpsr & 0x20) ? "AU" : "--",
		(fpsr & 0x10) ? "AZ" : "--",
		(fpsr & 0x08) ? "AX" : "--");

	fprintf(cons, "%sFPIAR:%08x%s\n", BOLDIF(fpiar != ppiar), fpiar, NORM);
}

// 68030 MMU レジスタ表示
void
DebuggerMD_m680x0::ShowRegMMU30(FILE *cons)
{
/*
SRP:00001111_22223333 TT0:00001111 TC:00001111 (---)
CRP:00001111_22223333 TT1:00001111 MMUSR: 0011 (
*/
	auto cpu68030 = dynamic_cast<MPU68030Device *>(cpu);
	uint64 srp, osrp;
	uint64 crp, ocrp;
	uint32 tt0, ott0;
	uint32 tt1, ott1;
	uint32 tc,  otc;
	uint16 sr,  osr;
	bool p;
	bool t;
	bool c;

	srp = cpu68030->GetSRP();
	crp = cpu68030->GetCRP();
	tt0 = cpu68030->GetTT(0);
	tt1 = cpu68030->GetTT(1);
	tc  = cpu68030->GetTC();
	sr  = cpu68030->GetMMUSR();

	osrp = prev.srp.q;
	ocrp = prev.crp.q;
	ott0 = prev.tt[0];
	ott1 = prev.tt[1];
	otc  = prev.tc;
	osr  = prev.mmusr;

	// 1行目
	p = (srp != osrp);
	t = (tt0 != ott0);
	c = (tc  != otc);
	fprintf(cons, "%sSRP:%08x_%08x%s  %sTT0:%08x%s(%c%c%c)",
		BOLDIF(p), (uint32)(srp >> 32), (uint32)srp, NORM,
		BOLDIF(t), tt0, NORM,
		(tt0 & m68030TT::E)   ? 'E' : '-',
		(tt0 & m68030TT::CI)  ? 'C' : '-',
		(tt0 & m68030TT::RWM) ? '-' : ((tt0 & m68030TT::RW)  ? 'R' : 'W'));
	fprintf(cons, "  %sTC:%08x%s(%c%c%c)\n",
		BOLDIF(c), tc, NORM,
		(tc & m68030TC::TC_E)   ? 'E' : '-',
		(tc & m68030TC::TC_SRE) ? 'S' : '-',
		(tc & m68030TC::TC_FCL) ? 'F' : '-');

	// 2行目
	p = (crp != ocrp);
	t = (tt1 != ott1);
	c = (sr  != osr);
	fprintf(cons, "%sCRP:%08x_%08x%s  %sTT1:%08x%s(%c%c%c)",
		BOLDIF(p), uint32(crp >> 32), (uint32)crp, NORM,
		BOLDIF(t), tt1, NORM,
		(tt1 & m68030TT::E)   ? 'E' : '-',
		(tt1 & m68030TT::CI)  ? 'C' : '-',
		(tt1 & m68030TT::RWM) ? '-' : ((tt1 & m68030TT::RW)  ? 'R' : 'W'));
	fprintf(cons, "  %sMMUSR: %04x%s(%c%c%c%c%c%c%c N=%d)\n",
		BOLDIF(c), sr,  NORM,
		(sr & m68030MMUSR::B) ? 'B' : '-',
		(sr & m68030MMUSR::L) ? 'L' : '-',
		(sr & m68030MMUSR::S) ? 'S' : '-',
		(sr & m68030MMUSR::W) ? 'W' : '-',
		(sr & m68030MMUSR::I) ? 'I' : '-',
		(sr & m68030MMUSR::M) ? 'M' : '-',
		(sr & m68030MMUSR::T) ? 'T' : '-',
		(sr & m68030MMUSR::N));
}

// 68040 MMU レジスタ表示
void
DebuggerMD_m680x0::ShowRegMMU40(FILE *cons)
{
	fprintf(cons, "Not implemented\n");
}

// ATC を表示
void
DebuggerMD_m680x0::ShowRegATC()
{
	parent->ShowMonitor(gMonitorManager->Get(ID_MONITOR_MPUATC));
}

// その他のレジスタを表示
void
DebuggerMD_m680x0::ShowRegOther()
{
	TextScreen s(80, 2);

	// 0         1         2         3         4         5         6
	// 012345678901234567890123456789012345678901234567890123456789012345
	// CACR:01234567  VBR:01234567  USP:01234567
	// CAAR:01234567  SFC:0  DFC:0  MSP:01234567

#define EM(name) ((cpu->reg.name != prev.name) ? TA::Em : TA::Normal)
	s.Print(0, 0, EM(cacr), "CACR:%08x", cpu->reg.cacr);
	s.Print(0, 1, EM(caar), "CAAR:%08x", cpu->reg.caar);

	s.Print(15, 0, EM(vbr), "VBR:%08x", cpu->reg.vbr);
	s.Print(15, 1, EM(sfc), "SFC:%d", cpu->reg.GetSFC());
	s.Print(22, 1, EM(dfc), "DFC:%d", cpu->reg.GetDFC());

	// スタック欄はモードによって表示を変える。
	//      	ユーザ	割り込み	マスタ
	// 1行目	ISP		USP			USP
	// 2行目	MSP		MSP			ISP
	if (!cpu->IsSuper()) {
		s.Print(29, 0, EM(isp), "ISP:%08x", cpu->reg.isp);
	} else {
		s.Print(29, 0, EM(usp), "USP:%08x", cpu->reg.usp);
	}
	if (!cpu->IsMaster()) {
		s.Print(29, 1, EM(msp), "MSP:%08x", cpu->reg.msp);
	} else {
		s.Print(29, 1, EM(isp), "ISP:%08x", cpu->reg.isp);
	}

	parent->ShowTextScreen(s);
}

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

	// dis.bin は uint8 列なのでこれを uint16 列に変換
	std::vector<uint16> words;
	for (int i = 0; i < dis.bin.size(); i += 2) {
		words.push_back((dis.bin[i] << 8) | dis.bin[i + 1]);
	}

	// 16進ダンプ
	std::string dumpbuf;
	for (auto w : words) {
		dumpbuf += string_format("%04x ", w);
	}

	// ダンプは5ワード分 (越えたら知らん)
	std::string str = string_format("%-25s", dumpbuf.c_str());
	// ニーモニック
	str += dis.text;
	// (あれば) 条件判断
	str += CondStr(words);

	return str;
}

// 命令ニーモニックに対するバイナリを文字列形式で返す。
// 対応しない場合は "" を返す。cmd_bi 用。
std::string
DebuggerMD_m680x0::ParseInst(const std::string& inst) const
{
	if (inst == "frestore")	return "f340:ffc0";
	if (inst == "fsave")	return "f300:ffc0";
	if (inst == "nop")		return "4e71";
	if (inst == "ptestr")	return "f0008200:ffc0e200";
	if (inst == "ptestw")	return "f0008000:ffc0e200";
	if (inst == "rte")		return "4e73";

	return "";
}

// 例外フレーム処理用の便利クラス。
// 例外フレーム内は基本ワード単位なのに、仕様書のオフセットはバイト単位
// なのでこういうヘルパーがないと事故が多発する。
class StackData
{
 public:
	StackData(DebuggerMemoryStream& mem_)
		: mem(mem_)
	{
	}

	// 合計が words [ワード] になるまで追加で読み込む。
	void ReadWordUntil(size_t words);
	// 合計が bytes (偶数) [バイト] になるまで追加で読み込む。
	void ReadByteUntil(size_t bytes);

	// 改行込みで出力。
	void Print(FILE *out, size_t byteoffset, uint bytes) const;
	void Print(FILE *out, size_t byteoffset, uint bytes,
		const char *, ...) const __printflike(5, 6);

	// 共通の表示用文字列を作成して返す。戻り値は改行なしの文字列。
	std::string Format(size_t byteoffset, uint bytes) const;
	std::string Format(size_t byteoffset, uint bytes,
		const char *, ...) const __printflike(4, 5);

	// byteoffset の位置のデータを表示用文字列にする。
	std::string FormatWord(size_t byteoffset) const;
	std::string FormatLong(size_t byteoffset) const;

	// byteoffset の位置からのワードデータを返す。バスエラーなら -1。
	uint32 Word(size_t byteoffset) const;
	// byteoffset の位置からのロングワードを返す。バスエラーなら (uint64)-1。
	uint64 Long(size_t byteoffset) const;

	// データサイズをバイト単位で返す。
	size_t bytesize() const noexcept { return framebuf.size() * 2; }

 private:
	std::string FormatV(size_t byteoffset, uint bytes,
		const char *, va_list) const __printflike(4, 0);

	DebuggerMemoryStream mem;
	std::vector<uint32> framebuf {};
};

void
StackData::ReadWordUntil(size_t words)
{
	while (framebuf.size() < words) {
		framebuf.push_back(mem.Read(2));
	}
}

void
StackData::ReadByteUntil(size_t bytes)
{
	assert((bytes % 1) == 0);
	size_t words = bytes / 2;
	while (framebuf.size() < words) {
		framebuf.push_back(mem.Read(2));
	}
}

// 改行込みで表示する。bytes は 2, 4, 16 のみ。追加情報なし。
void
StackData::Print(FILE *out, size_t byteoffset, uint bytes) const
{
	fprintf(out, "%s\n", Format(byteoffset, bytes).c_str());
}

// 改行込みで表示する。bytes は 2, 4, 16 のみ。
void
StackData::Print(FILE *out, size_t byteoffset, uint bytes,
	const char *fmt, ...) const
{
	va_list ap;

	assert(bytes == 2 || bytes == 4 || bytes == 16);

	va_start(ap, fmt);
	fprintf(out, "%s\n", FormatV(byteoffset, bytes, fmt, ap).c_str());
	va_end(ap);
}

// 共通の表示用文字列を作成して返す。戻り値は改行なしの文字列。
std::string
StackData::Format(size_t byteoffset, uint bytes) const
{
	std::string s = string_format("+$%02x: ", (uint)byteoffset);
	if (bytes == 2) {
		s += FormatWord(byteoffset);
	} else if (bytes == 4) {
		s += FormatLong(byteoffset);
	} else {
		s += FormatLong(byteoffset);
		s += ' ';
		s += FormatLong(byteoffset + 4);
		s += ' ';
		s += FormatLong(byteoffset + 8);
		s += ' ';
		s += FormatLong(byteoffset + 12);
	}

	return s;
}

// 共通の表示用文字列を作成して返す。戻り値は改行なしの文字列。
std::string
StackData::Format(size_t byteoffset, uint bytes, const char *fmt, ...) const
{
	std::string str;
	va_list ap;

	va_start(ap, fmt);
	str = FormatV(byteoffset, bytes, fmt, ap);
	va_end(ap);

	return str;
}

// 共通の表示用文字列を作成して返す。戻り値は改行なしの文字列。
std::string
StackData::FormatV(size_t byteoffset, uint bytes,
	const char *fmt, va_list ap) const
{
	std::string s = Format(byteoffset, bytes);

	char text[128];
	vsnprintf(text, sizeof(text), fmt, ap);
	if (text[0]) {
		if (bytes == 2) {
			s += "    ";
		}
		s += string_format(" : %s", text);
	}
	return s;
}

// byteoffset の位置からのワードデータを表示用文字列にする。
std::string
StackData::FormatWord(size_t byteoffset) const
{
	uint32 data = Word(byteoffset);
	if ((int32)data < 0) {
		return "----";
	} else {
		char buf[16];
		snprintf(buf, sizeof(buf), "%04x", data);
		return buf;
	}
}

// byteoffset の位置からのロングワードデータを表示用文字列にする。
std::string
StackData::FormatLong(size_t byteoffset) const
{
	uint64 data = Long(byteoffset);
	if ((int64)data < 0) {
		return "--------";
	} else {
		char buf[16];
		snprintf(buf, sizeof(buf), "%08x", (uint32)data);
		return buf;
	}
}

uint32
StackData::Word(size_t byteoffset) const
{
	size_t wordoffset = byteoffset / 2;
	assert(wordoffset < framebuf.size());
	return framebuf[wordoffset];
}

uint64
StackData::Long(size_t byteoffset) const
{
	uint32 hi = Word(byteoffset);
	uint32 lo = Word(byteoffset + 2);
	if ((int32)hi < 0 || (int32)lo < 0) {
		return (uint64)-1;
	}
	return (hi << 16) | lo;
}

// 例外フレームを表示する。
void
DebuggerMD_m680x0::PrintExceptionFrame(FILE *out, busaddr addr)
{
	DebuggerMemoryStream mem(this, addr);
	StackData frame(mem);

	fprintf(out, "Exception stack at $%08x\n", addr.Addr());

	frame.ReadWordUntil(4);

	// ここからフレーム別。
	uint32 format = frame.Word(6) >> 12;
	switch (format) {
	 case 0x0:	// 034 Four word stack
		PrintExceptionFrame0(out, frame);
		break;

	 case 0x1:	// 034 Throwaway four word stack
		PrintExceptionFrameCommon(out, frame, true);
		break;

	 case 0x2:	// 034 Six word stack
		PrintExceptionFrame2(out, frame);
		break;

	 case 0x3:	// --4 Floating-point post-instruction stack
		PrintExceptionFrame3(out, frame);
		break;

	 case 0x4:	// --L Eight-word stack
		PrintExceptionFrame4(out, frame);
		break;

	 case 0x7:	// --4 Access error stack
		PrintExceptionFrame7(out, frame);
		break;

	 case 0xb:	// -3- Long bus cycle stack
		PrintExceptionFrameB(out, frame);
		break;

	 case 9:	// -3- Coprocessor mid-instruction Stack
	 case 0xa:	// -3- Short bus cycle stack (nono は生成しない)
	 default:
		fprintf(out, "Format $%x not supported\n", format);
		break;
	}
}

// 共通の4ワード目までを表示する。
bool
DebuggerMD_m680x0::PrintExceptionFrameCommon(FILE *out,
	const StackData& frame, bool pc_is_next)
{
	// $06: Format|Vector
	uint32 format_vector = frame.Word(6);
	uint32 format = format_vector >> 12;
	std::string s6;
	if ((int32)format_vector < 0) {
		s6 += "Format+Vector";
	} else {
		uint32 vector = (format_vector >> 2) & 0xff;
		s6 += string_format("Format=$%x Vector=$%02x", format, vector);

		// ベクタ番号からベクタ名を引いてみる。
		// Vector=$b は本来 Format=$0 で F ライン例外だが、
		// Format=$2 Vector=$b なら 68040 の未実装 FP 命令例外なので区別する。
		const char *exname = NULL;
		if (format == 2 && vector == 0x0b) {
			exname = "Unimplemented FP Instruction";
		} else {
			auto vectortable = GetVectorTable();
			exname = vectortable->GetExceptionName(vector);
		}
		if (exname) {
			s6 += " \"";
			s6 += exname;
			s6 += '\"';
		}
	}

	frame.Print(out, 0, 2, "SR");
	frame.Print(out, 2, 4, "%sPC", pc_is_next ? "Next ": "");
	frame.Print(out, 6, 2, "%s", s6.c_str());

	// ここがバスエラーならもう出来ることはない。
	if ((int32)format_vector < 0) {
		return false;
	}
	return true;
}

void
DebuggerMD_m680x0::PrintExceptionFrame0(FILE *out, StackData& frame)
{
	// Format$0 の PC は
	// 種類							PC
	// ----
	// ・割り込み					次の命令
	// ・TRAP#N						次の命令
	// ・フォーマットエラー			当該命令(RTE/FRESTORE)
	// ・不当命令					当該命令
	// ・A/F ライン命令				当該命令
	// ・特権違反					当該命令

	bool next;
	uint vector = (frame.Word(0x06) & 0xfff) >> 2;
	switch (vector) {
	 case M68K::EXCEP_FORMAT:
	 case M68K::EXCEP_ILLEGAL:
	 case M68K::EXCEP_ALINE:
	 case M68K::EXCEP_FLINE:
	 case M68K::EXCEP_PRIV:
		next = false;
		break;
	 default:
		next = true;
		break;
	}
	PrintExceptionFrameCommon(out, frame, next);
}

void
DebuggerMD_m680x0::PrintExceptionFrame2(FILE *out, StackData& frame)
{
	// Format$2 の PC と Address は
	// 種類							PC			Address
	// ----							--			-------
	// ・CHK/CHK2/TRAP*/			次の命令	例外を起こした命令
	//   FTRAPcc/Trace/ZeroDiv
	// ・MMU 構成例外				次の命令	例外を起こした命令
	// ・040 未実装FP命令			次の命令	FPが計算したEA
	// ・040 アドレスエラー			当該命令	参照アドレス-1

	bool next;
	const char *addr;
	uint vector = (frame.Word(0x06) & 0xfff) >> 2;
	switch (vector) {
	 case M68K::EXCEP_ZERODIV:
	 case M68K::EXCEP_CHK:
	 case M68K::EXCEP_TRAPV:
	 case M68K::EXCEP_TRACE:
	 case M68K::EXCEP_MMU_CONFIG:
		next = true;
		addr = "Caused instruction address";
		break;

	 case M68K::EXCEP_FLINE:	// これは 040 の EXCEP_FP_UNIMPL
		next = true;
		addr = "Calculated EA";
		break;

	 case M68K::EXCEP_ADDRERR:
		next = false;
		addr = "Reference Address - 1";
		break;

	 default:
		next = false;
		addr = "?";
		break;
	}

	frame.ReadWordUntil(6);
	if (PrintExceptionFrameCommon(out, frame, next) == false) {
		return;
	}
	frame.Print(out, 0x08, 4, "%s", addr);
}

void
DebuggerMD_m680x0::PrintExceptionFrame3(FILE *out, StackData& frame)
{
	frame.ReadWordUntil(6);
	if (PrintExceptionFrameCommon(out, frame, true) == false) {
		return;
	}
	frame.Print(out, 0x08, 4, "Effective Address");
}

void
DebuggerMD_m680x0::PrintExceptionFrame4(FILE *out, StackData& frame)
{
	frame.ReadWordUntil(8);
	if (PrintExceptionFrameCommon(out, frame, true) == false) {
		return;
	}
	frame.Print(out, 0x08, 4, "Effective Address");
	frame.Print(out, 0x0c, 4, "PC of Faulted Instruction");
}

void
DebuggerMD_m680x0::PrintExceptionFrame7(FILE *out, StackData& frame)
{
	static const char * const sizestr[] = {
		".L",
		".B",
		".W",
		"Line",
	};

	frame.ReadWordUntil(30);
	if (PrintExceptionFrameCommon(out, frame, true) == false) {
		return;
	}
	frame.Print(out, 0x08, 4, "Effective Address");

	// SSW
	uint16 ssw = frame.Word(0x0c);
	frame.Print(out, 0x0c, 2,
		"SSW (%s %s %s %s %s %s %s RW=%c size=%s TT=%u TM=%u)",
		((ssw & M68040::SSW_CP)  ? "CP" : "--"),
		((ssw & M68040::SSW_CU)  ? "CU" : "--"),
		((ssw & M68040::SSW_CT)  ? "CT" : "--"),
		((ssw & M68040::SSW_CM)  ? "CM" : "--"),
		((ssw & M68040::SSW_MA)  ? "MA" : "--"),
		((ssw & M68040::SSW_ATC) ? "ATC" : "---"),
		((ssw & M68040::SSW_LK)  ? "LK" : "--"),
		((ssw & M68040::SSW_RW)  ? 'R' : 'W'),
		sizestr[(ssw >> 5) & 3],
		((ssw >> 3) & 3),
		(ssw & 7));

	// WB[321]S
	for (uint i = 0; i < 3; i++) {
		size_t off = 0x0e + i * 2;
		uint16 wbs = frame.Word(off);
		std::string stbuf;
		if ((wbs & 0x80)) {
			stbuf = "Valid";
			stbuf += string_format(" size=%s", sizestr[(wbs >> 5) & 3]);
			stbuf += string_format(" TT=%u", (wbs >> 3) & 3);
			stbuf += string_format(" TM=%u", wbs & 7);
		} else {
			stbuf = "no pending";
		}
		frame.Print(out, off, 2, "WB%u Status (%s)", 3 - i, stbuf.c_str());
	}

	frame.Print(out, 0x14, 4, "Fault Address");
	frame.Print(out, 0x18, 4, "WB3 Address");
	frame.Print(out, 0x1c, 4, "WB3 Data");
	frame.Print(out, 0x20, 4, "WB2 Address");
	frame.Print(out, 0x24, 4, "WB2 Data");
	frame.Print(out, 0x28, 4, "WB1 Address");
	frame.Print(out, 0x2c, 4, "WB1 Data / PushData 0");
	frame.Print(out, 0x30, 4, "Push Data 1");
	frame.Print(out, 0x34, 4, "Push Data 2");
	frame.Print(out, 0x38, 4, "Push Data 3");
}

void
DebuggerMD_m680x0::PrintExceptionFrameB(FILE *out, StackData& frame)
{
	frame.ReadWordUntil(46);
	if (PrintExceptionFrameCommon(out, frame, false) == false) {
		return;
	}
	frame.Print(out, 0x08, 2);	// internal

	// SSW
	uint16 ssw = frame.Word(0x0a);
	frame.Print(out, 0x0a, 2,
		"SSW (%s %s %s %s %s %s RW=%c size=%c FC=%u)",
		((ssw & M68030::SSW_FC) ? "FC" : "--"),
		((ssw & M68030::SSW_FB) ? "FB" : "--"),
		((ssw & M68030::SSW_RC) ? "RC" : "--"),
		((ssw & M68030::SSW_RB) ? "RB" : "--"),
		((ssw & M68030::SSW_DF) ? "DF" : "--"),
		((ssw & M68030::SSW_RM) ? "RM" : "--"),
		((ssw & M68030::SSW_RW) ? 'R'  : 'W'),
		"LBW3"[(ssw >> 4) & 3],
		(ssw & 7));

	frame.Print(out, 0x0c, 2, "Instruction Pipe Stage C");
	frame.Print(out, 0x0e, 2, "Instruction Pipe Stage B");
	frame.Print(out, 0x10, 4, "Data Cycle Fault Address");
	frame.Print(out, 0x14, 4);
	frame.Print(out, 0x18, 4, "Data Output Buffer");
	frame.Print(out, 0x1c, 4);
	frame.Print(out, 0x20, 4);
	frame.Print(out, 0x24, 4, "Stage B Address");
	frame.Print(out, 0x28, 4);
	frame.Print(out, 0x2c, 4, "Data Input Buffer");

	// 残りは内部データなのでまとめる。
	frame.Print(out, 0x30, 4, "(internal registers)");
	// +$36 にちょっと構造があるけどとりあえず無視。
	for (uint i = 0x34; i < frame.bytesize(); i += 4) {
		if (i == 0x34 || i == 0x48) {
			fprintf(out, "+$%02x:", i);
		}
		fprintf(out, " %s", frame.FormatLong(i).c_str());
		if (i == 0x44) {
			fprintf(out, "\n");
		}
	}
	fprintf(out, "\n");
}

void
DebuggerMD_m680x0::PrintFPStateFrame(FILE *out, busaddr addr)
{
	DebuggerMemoryStream mem(this, addr);
	StackData frame(mem);

	fprintf(out, "Floating-point state frame at $%08x\n", addr.Addr());

	frame.ReadByteUntil(4);

	// 第1ワードはバージョンと後続の長さ。
	uint64 w00 = frame.Long(0x00);
	uint32 ver = (w00 >> 24) & 0xff;
	uint32 len = (w00 >> 16) & 0xff;
	// VER LEN	Name
	// $00 ANY	Null State Frame
	// ver $18	68881 IDLE
	// ver $38	68882 IDLE
	// ver $B4	68881 BUSY
	// ver $D4	68882 BUSY
	// $41 $00	68040 IDLE
	// $41 $30	68040 Unimplemented Instruction
	// $41 $60	68040 BUSY
	if (ver == 0x00) {
		frame.Print(out, 0x00, 4, "Null State Frame");
	} else {
		// NULL フレーム以外は残り len バイトある。
		frame.ReadByteUntil(len + 4);

		if (ver == 0x41) {
			switch (len) {
			 case 0x00:
				frame.Print(out, 0x00, 4, "68040 Idle State Frame");
				break;
			 case 0x30:
				frame.Print(out, 0x00, 4,
					"68040 Unimplemented Instruction State Frame");
				PrintFPFrame68040Unimpl(out, frame, 0);
				break;
			 case 0x60:
				frame.Print(out, 0x00, 4, "68040 Busy State Frame");
				PrintFPFrame68040Busy(out, frame);
				break;
			 default:
				frame.Print(out, 0x00, 4, "68040 Unknown State Frame?");
				break;
			}
		} else {
			switch (len) {
			 case 0x18:
				frame.Print(out, 0x00, 4, "68881 Idle State Frame");
				PrintFPFrame6888xIdle(out, frame, len);
				break;
			 case 0x38:
				frame.Print(out, 0x00, 4, "68882 Idle State Frame");
				PrintFPFrame6888xIdle(out, frame, len);
				break;
			 case 0xb4:
				frame.Print(out, 0x00, 4, "68881 Busy State Frame");
				PrintFPFrame6888xBusy(out, frame, len);
				break;
			 case 0xd4:
				frame.Print(out, 0x00, 4, "68882 Busy State Frame");
				PrintFPFrame6888xBusy(out, frame, len);
				break;
			 default:
				frame.Print(out, 0x00, 4, "Unknown State Frame?");
				break;
			}
		}
	}
}

void
DebuggerMD_m680x0::PrintFPFrame6888xIdle(FILE *out, StackData& frame,
	uint32 len)
{
	bool is68882 = (len == 0x38);

	frame.Print(out, 0x04, 2, "Command/Condition Register");
	frame.Print(out, 0x06, 2);
	uint32 pos = 0x08;
	if (is68882) {
		for (; pos < 0x28; pos += 16) {
			frame.Print(out, pos, 16, "(internal register)");
		}
	}
	for (uint i = 0; i < 12; i += 4) {
		frame.Print(out, pos, 4, "Exceptional Operand");
		pos += 4;
	}
	frame.Print(out, pos, 4, "Operand Register");
	pos += 4;
	frame.Print(out, pos, 4, "BIU Flags");
}

void
DebuggerMD_m680x0::PrintFPFrame6888xBusy(FILE *out, StackData& frame,
	uint32 len)
{
	uint pos = 4;
	uint cnt = len / 16;
	for (uint i = 0; i < cnt; i++) {
		frame.Print(out, pos, 16, "(internal register)");
		pos += 16;
	}
	for (; pos < len + 4; pos += 4) {
		frame.Print(out, pos, 4, "(internal register)");
	}
}

// 68040 の未実装命令ステートフレーム。
// 68040 のビジーフレームからも呼ばれる。base には Unimpl の原点を指定するが
// +$00 からの4バイト分の表示は呼び出し側の担当。
void
DebuggerMD_m680x0::PrintFPFrame68040Unimpl(FILE *out, StackData& frame,
	uint base)
{
	// +$04.L: CMDREG3B
	uint32 reg3b = frame.Word(base + 0x04);
	frame.Print(out, base + 0x04, 4, "CMDREG3B=$%03x", reg3b);

	// +$08.L: (reserved)
	uint16 exc = frame.Word(base + 0x08);
	frame.Print(out, base + 0x08, 4,
		"(NMNEXC %s %s %s %s %s) (NMCEXC %s %s %s %s %s)",
		((exc & 0x0200) ? "UN" : "--"),
		((exc & 0x0100) ? "SN" : "--"),
		((exc & 0x0080) ? "OE" : "--"),
		((exc & 0x0040) ? "OF" : "--"),
		((exc & 0x0020) ? "UF" : "--"),
		((exc & 0x0010) ? "UN" : "--"),
		((exc & 0x0008) ? "SN" : "--"),
		((exc & 0x0004) ? "OE" : "--"),
		((exc & 0x0002) ? "OF" : "--"),
		((exc & 0x0001) ? "UF" : "--"));

	// +$0c.L: STAG...
	uint32 w0c = frame.Word(base + 0x0c);
	uint32 stag = (w0c >> 13) & 7;
	frame.Print(out, base + 0x0c, 4,
		"STAG=%u%s (E=%u) M66=%u M1=%u M0=%u SBIT=%u",
		stag, MPU68040Device::GetFPUTagName(stag).c_str(),
		((w0c & 0x1000) ? 1 : 0),
		((w0c & 0x0400) ? 1 : 0),
		((w0c & 0x0200) ? 1 : 0),
		((w0c & 0x0100) ? 1 : 0),
		((w0c & 0x0080) ? 1 : 0));

	// +$10.L: CMDREG1B
	uint32 reg1b = frame.Word(base + 0x10);
	uint32 opclass = reg1b >> 13;
	uint32 rx = (reg1b >> 10) & 7;
	uint32 ry = (reg1b >>  7) & 7;
	uint32 cmd = reg1b & 0x7f;
	std::string cmdname = MPU68040Device::GetReg1BName(opclass, rx, cmd);
	frame.Print(out, base + 0x10, 4,
		"CMDREG1B=$%04x: OPCLASS=%x Rs=%u Rd=%u CMD=$%02x(%s)",
		reg1b,
		opclass, rx, ry,
		cmd, cmdname.c_str());

	// +$14.L: DTAG...
	uint32 w14 = frame.Word(base + 0x14);
	uint32 dtag = (w14 >> 13) & 7;
	frame.Print(out, base + 0x14, 4,
		"DTAG=%u%s (FPTEMP15=%u) WBTEMP15=%u",
		dtag, MPU68040Device::GetFPUTagName(dtag).c_str(),
		((w14 & 0x1000) ? 1 : 0),
		((w14 & 0x0010) ? 1 : 0));

	// +$18.L: E1,E3,T
	uint32 w18 = frame.Word(base + 0x18);
	frame.Print(out, base + 0x18, 4,
		"E1=%u E3=%u (S=%u X=%u U=%u) T=%u",
		((w18 & 0x0400) ? 1 : 0),
		((w18 & 0x0200) ? 1 : 0),
		((w18 & 0x0100) ? 1 : 0),
		((w18 & 0x0080) ? 1 : 0),
		((w18 & 0x0020) ? 1 : 0),
		((w18 & 0x0010) ? 1 : 0));

	// +$1C.L: FPTS,FPTE
	uint32 w1c = frame.Word(base + 0x1c);
	frame.Print(out, base + 0x1c, 4, "FPTS=%u FPTE=$%04x",
		((w1c & 0x8000) ? 1 : 0),
		 (w1c & 0x7fff));

	// +$20.L: FPTM
	// +$24.L: FPTM
	frame.Print(out, base + 0x20, 4, "FPTM[63:32]");
	frame.Print(out, base + 0x24, 4, "FPTM[31:00]");

	// +$28.L: ETS,ETE
	uint32 w28 = frame.Word(base + 0x28);
	frame.Print(out, base + 0x28, 4, "ETS=%u ETE=$%04x",
		((w28 & 0x8000) ? 1 : 0),
		 (w28 & 0x7fff));

	// +$2C.L: ETM
	// +$30.L: ETM
	frame.Print(out, base + 0x2c, 4, "ETM[63:32]");
	frame.Print(out, base + 0x30, 4, "ETM[31:00]");
}

void
DebuggerMD_m680x0::PrintFPFrame68040Busy(FILE *out, StackData& frame)
{
	frame.Print(out, 0x04, 4, "(reserved)");

	// $08 CU_SAVEPC
	uint32 w08 = frame.Word(0x08);
	frame.Print(out, 0x08, 4, "CU_SAVEPC=$%02x", w08 >> 25);

	frame.Print(out, 0x0c, 4, "(reserved)");
	frame.Print(out, 0x10, 4, "(reserved)");
	frame.Print(out, 0x14, 4, "(reserved)");

	// $18 WBT
	uint32 w18 = frame.Word(0x18);
	frame.Print(out, 0x18, 4, "WBT S=%u E=$%04x",
		((w18 & 0x8000) ? 1 : 0),
		 (w18 & 0x7fff));
	frame.Print(out, 0x1c, 4, "WBTM[63:32]");
	frame.Print(out, 0x20, 4, "WBTM[31:00]");

	frame.Print(out, 0x24, 4, "(reserved)");
	frame.Print(out, 0x28, 4, "FPIARCU");
	frame.Print(out, 0x2c, 4, "(reserved)");
	frame.Print(out, 0x30, 4, "(reserved)");

	// これ以降は Unimpl と同じ。
	PrintFPFrame68040Unimpl(out, frame, 0x30);
}

unix.superglobalmegacorp.com

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