File:  [Isaki's NoNo m68k/m88k emulator] / nono / debugger / vectortable.cpp
Revision 1.1.1.9 (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 "vectortable.h"
#include "mainapp.h"
#include "mainbus.h"
#include "monitor.h"
#include "mpu.h"

// コンストラクタ
VectorTable::VectorTable(VMType vmtype)
	: inherited(OBJ_VECTOR_TABLE)
{
	// ログは不要
	ClearAlias();

	monitor = gMonitorManager->Regist(ID_SUBWIN_VECTOR, this);

	// 表示名を作成。それぞれ微妙に処理が違う…
	int w = 75;
	switch (vmtype) {
	 case VMType::X68030:
	 case VMType::LUNA1:
	 case VMType::NEWS:
	 case VMType::VIRT68K:
		InitTableM680x0(vmtype);
		monitor->SetCallback(&VectorTable::MonitorScreenM680x0);
		break;
	 case VMType::LUNA88K:
		InitTableLuna88k();
		monitor->SetCallback(&VectorTable::MonitorScreenM88xx0);
		// ベクタが(16進数で)3桁必要。
		w++;
		break;
	 default:
		PANIC("invalid vmtype %d", (int)vmtype);
	}

	monitor->SetSize(w, 1 + 32);	// ヘッダの1行と全エントリ数
	monitor->SetMaxHeight(1 + Size());
}

// m68k 機種の表示名テーブル初期化。コンストラクタの一部。
void
VectorTable::InitTableM680x0(VMType vmtype)
{
	nametable.clear();
	nametable.resize(256);

	// まず m680x0 標準の名称で埋める
	for (int v = 0; v < 64; v++) {
		if (name_m680x0[v]) {
			nametable[v] = name_m680x0[v];
		}
	}

	// 機種ごとに必要なところだけ上書き
	const std::map<int, const char * const> *names;
	switch (vmtype) {
	 case VMType::X68030:
		names = &name_x68030;
		break;
	 case VMType::LUNA1:
		names = &name_luna1;
		break;
	 case VMType::NEWS:
		names = &name_news;
		break;
	 case VMType::VIRT68K:
		names = &name_virt68k;
		break;
	 default:
		names = NULL;
		break;
	}
	if (names) {
		for (const auto& p : *names) {
			int v = p.first;
			const char *name = p.second;
			nametable[v] = name;
		}
	}
}

// LUNA-88K の表示名テーブルを初期化。コンストラクタの一部。
void
VectorTable::InitTableLuna88k()
{
	nametable.clear();
	nametable.resize(512);

	for (const auto& p : name_luna88k) {
		int v = p.first;
		const char *name = p.second;
		nametable[v] = name;
	}
}

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

// 初期化
bool
VectorTable::Init()
{
	mainbus = GetMainbusDevice();
	mpu = GetMPUDevice();

	return true;
}

// 例外名を返す (例外履歴用)。
// 名前がなければ NULL を返す。
const char *
VectorTable::GetExceptionName(int vector) const
{
	assertmsg(0 <= vector && vector < nametable.size(), "vector=%d", vector);

	if (__predict_false(vector < 2)) {
		if (gMainApp.Has(VMCap::M68K)) {
			// ベクタテーブル的には 0:"Reset(SP)"、1:"Reset(PC)" のほうが
			// 分かりやすいが、例外の名前は 0 が "Reset Exception" で
			// 1 は存在しない。
			if (vector == 0) {
				return "Reset Exception";
			} else {
				return "(vector 1?)";
			}
		}
	}

	return nametable[vector];
}

// モニタ更新 (M680x0)
void
VectorTable::MonitorScreenM680x0(Monitor *, TextScreen& screen)
{
// 0         1         2         3         4         5         6         7
// 012345678901234567890123456789012345678901234567890123456789012345678901234
// No.      Offset Address   Name                  ><                    Count
// $02(  2) $0008  $12345678 0123456789012345678901201234567890123456789012345

	uint32 vbr = mpu->GetVBR();

	// userdata が表示開始位置
	int v = (int)screen.userdata;
	int row = screen.GetRow();
	int vend = v + row - 1;

	screen.Clear();

	// 最初の1行は常にヘッダ
	screen.Print(0, 0, "No.      Offset Address   Name");
	screen.Print(70, 0, "Count");

	for (int y = 1; v < vend; v++, y++) {
		screen.Print(0, y, "$%02x(%3u) $%04x", v, v, v * 4);
		busdata addr = mainbus->Peek4(vbr + v * 4);
		if (__predict_false(addr.IsBusErr())) {
			screen.Print(16, y, "BusErr");
		} else {
			screen.Print(16, y, "$%08x", addr.Data());
		}
		screen.Print(26, y, "%-23s", nametable[v] ?: "");
		screen.Print(49, y, "%26s",
			format_number(mpu->excep_counter[v]).c_str());
	}
}

// モニタ更新 (M88xx0)
void
VectorTable::MonitorScreenM88xx0(Monitor *, TextScreen& screen)
{
// 0         1         2         3         4         5         6         7
// 0123456789012345678901234567890123456789012345678901234567890123456789012346
// No.       Offset Address   Name                  ><                    Count
// $002(  2) $0010 @$12345678 0123456789012345678901201234567890123456789012345

	uint32 vbr = mpu->GetVBR();

	// userdata が表示開始位置
	int v = (int)screen.userdata;
	int row = screen.GetRow();
	int vend = v + row - 1;

	screen.Clear();

	// 最初の1行は常にヘッダ
	screen.Print(0, 0, "No.       Offset Address   Name");
	screen.Print(71, 0, "Count");

	for (int y = 1; v < vend; v++, y++) {
		screen.Print(0, y, "$%03x(%3u) $%04x", v, v, v * 8);

		// m88100 は各ベクタが2ワード分あり、ここに命令を書く。
		// ただし1ワード目は実質使えなかったので or r0,r0,r0 (NOP) を置く
		// ことになっており、実質2ワード目の1命令だけでジャンプしないと
		// いけない (アドレス全域にジャンプ出来るには最低2命令必要なので
		// 2命令分のスペースを取ったのだろうけど…どうして…)。
		// そこで、1ワード目が $f4005800 (NOP) で 2ワード目が何らかの分岐
		// 命令であれば、その分岐先をこのベクタのアドレスとして表示する。
		// 命令列が条件を満たさない時は仕方ないのでアドレスの前に @ をつけて
		// 現在位置 (1ワード目が置かれているアドレス) を表示するか。
		uint32 vecaddr = vbr + v * 8;
		busaddr addr = VectorToAddr(vecaddr);
		if (__predict_false(addr.IsBusErr())) {
			screen.Print(17, y, "BusErr");
		} else if (__predict_false(addr.IsPhysical())) {
			// ジャンプ先が拾えなかったら Physical ビットを立てて返してくる。
			// 空いてるビットを使いまわしてるだけでそれ以上の意味はない。
			screen.Print(16, y, "@$%08x", vecaddr);
		} else {
			screen.Print(17, y, "$%08x", addr.Addr());
		}
		screen.Print(27, y, "%-23s", nametable[v] ?: "");
		screen.Print(50, y, "%26s",
			format_number(mpu->excep_counter[v]).c_str());
	}
}

// m88100 の2ワードのベクタからこのベクタの再分岐先を調べて返す。
// このベクタのフェッチ自体がバスエラーなら BusErr を返す。
// 分岐先を特定出来なければ、Physical ビットを立てて返す。
busaddr
VectorTable::VectorToAddr(uint32 vecaddr)
{
	busdata inst1 = mainbus->Peek4(vecaddr);
	busdata inst2 = mainbus->Peek4(vecaddr + 4);
	if (__predict_false(inst1.IsBusErr() || inst2.IsBusErr())) {
		return BusAddr::BusErr;
	}

	// 1命令目は or r0,r0,r0 の NOP 固定でお願いしたい…。
	if (__predict_false(inst1.Data() != 0xf4005800)) {
		return BusAddr::Physical;
	}

	// 2命令目はさすがに br しか置く必要ないと思いたい…。
	uint32 op = inst2.Data();
	int32 offset = 0;
	if (__predict_true((op & 0xfc00'0000) == 0xc000'0000)) {	// br
		offset = ((int32)(op << 6) >> 4);
	} else {
		return BusAddr::Physical;
	}

	return busaddr(vecaddr + 4 + offset);
}

// ベクタ名 (MC680x0)。
// とりあえず CPU の区別はないことにする。
/*static*/ std::array<const char * const, 64> VectorTable::name_m680x0 = {
	//           01234567890123456789012
	/*  0 */	"Reset (SP)",
	/*  1 */	"Reset (PC)",
	/*  2 */	"Bus Error",
	/*  3 */	"Address Error",
	/*  4 */	"Illegal Instruction",
	/*  5 */	"Zero Divide",
	/*  6 */	"CHK/CHK2 Instruction",
	/*  7 */	"TRAP* Instruction",
	/*  8 */	"Privilege Violation",
	/*  9 */	"Trace",
	/* 10 */	"Line A emulator",
	/* 11 */	"Line F emulator",
	/* 12 */	NULL,
	/* 13 */	"Copro Proto Violation",	// 68030 only
	/* 14 */	"Format Error",
	/* 15 */	"Uninitialized Interrupt",
	/* 16 */	NULL, NULL, NULL, NULL,
	/* 20 */	NULL, NULL, NULL, NULL,
	/* 24 */	"Spurious Interrupt",
	/* 25 */	"Level 1 Interrupt",
	/* 26 */	"Level 2 Interrupt",
	/* 27 */	"Level 3 Interrupt",
	/* 28 */	"Level 4 Interrupt",
	/* 29 */	"Level 5 Interrupt",
	/* 30 */	"Level 6 Interrupt",
	/* 31 */	"Level 7 Interrupt",
	/* 32 */	"Trap #0",
	/* 33 */	"Trap #1",
	/* 34 */	"Trap #2",
	/* 35 */	"Trap #3",
	/* 36 */	"Trap #4",
	/* 37 */	"Trap #5",
	/* 38 */	"Trap #6",
	/* 39 */	"Trap #7",
	/* 40 */	"Trap #8",
	/* 41 */	"Trap #9",
	/* 42 */	"Trap #10",
	/* 43 */	"Trap #11",
	/* 44 */	"Trap #12",
	/* 45 */	"Trap #13",
	/* 46 */	"Trap #14",
	/* 47 */	"Trap #15",
	//           01234567890123456789012
	/* 48 */	"FPCP Branch",
	/* 49 */	"FPCP Inexcact Result",
	/* 50 */	"FPCP Divide by Zero",
	/* 51 */	"FPCP UnderFlow",
	/* 52 */	"FPCP Operand Error",
	/* 53 */	"FPCP OverFlow",
	/* 54 */	"FPCP Signaling NAN",
	/* 55 */	"FPCP Unsupp Data Type",	// 68040 only
	/* 56 */	"MMU Configuration Error",	// 68030 only
	/* 57 */	NULL,
	/* 58 */	NULL,
	/* 59 */	NULL,
	/* 60 */	NULL,
	/* 61 */	NULL,
	/* 62 */	NULL,
	/* 63 */	NULL,
};

// ベクタ名 (X68030)
/*static*/ std::map<int, const char * const> VectorTable::name_x68030 = {
	//           01234567890123456789012
	{ 0x40,		"MFP Alarm" },
	{ 0x41,		"MFP EXPON" },
	{ 0x42,		"MFP POWSW" },
	{ 0x43,		"MFP FM IRQ" },
	{ 0x44,		"MFP Timer-D" },
	{ 0x45,		"MFP Timer-C" },
	{ 0x46,		"MFP V-Disp" },
	{ 0x47,		"MFP GPIP5" },
	{ 0x48,		"MFP Timer-B" },
	{ 0x49,		"MFP TX Error" },
	{ 0x4a,		"MFP TX Empty" },
	{ 0x4b,		"MFP RX Error" },
	{ 0x4c,		"MFP RX Full" },
	{ 0x4d,		"MFP Timer-A" },
	{ 0x4e,		"MFP CRTC IRQ" },
	{ 0x4f,		"MFP H-Sync" },

	{ 0x50,		"SCC#B TX Empty" },
	{ 0x52,		"SCC#B E/S" },
	{ 0x54,		"SCC#B RX Intr" },
	{ 0x56,		"SCC#B Special Condition" },
	{ 0x58,		"SCC#A TX Empty" },
	{ 0x5a,		"SCC#A E/S" },
	{ 0x5c,		"SCC#A RX Intr" },
	{ 0x5e,		"SCC#A Special Condition" },

	//           01234567890123456789012
	{ 0x60,		"I/O FDC Interrupt" },
	{ 0x61,		"I/O FDD Interrupt" },
	{ 0x62,		"I/O HDC Interrupt" },
	{ 0x63,		"I/O PRN Interrupt" },
	{ 0x64,		"DMAC#0 Complete" },
	{ 0x65,		"DMAC#0 Error" },
	{ 0x66,		"DMAC#1 Complete" },
	{ 0x67,		"DMAC#1 Error" },
	{ 0x68,		"DMAC#2 Complete" },
	{ 0x69,		"DMAC#2 Error" },
	{ 0x6a,		"DMAC#3 Complete" },
	{ 0x6b,		"DMAC#3 Error" },
	{ 0x6c,		"SPC Interrupt" },

	//           01234567890123456789012
	{ 0xf0,		"PSX16x50 Interrupt" },
	{ 0xf6,		"ExSPC Interrupt" },
	//0xf7,		TS-6BSI
	{ 0xf8,		"Nereid#1 Interrupt" },
	{ 0xf9,		"Neptune-X Interrupt" },
	{ 0xfa,		"Nereid#1 USB" },
	{ 0xfb,		"Nereid#0 USB" },
};

// LUNA-I
/*static*/ std::map<int, const char * const> VectorTable::name_luna1 = {
	//           01234567890123456789012
	{ 0x19,		"Lv1 Intr (XP Low)" },
	{ 0x1a,		"Lv2 Intr (SPC)" },
	{ 0x1b,		"Lv3 Intr (Lance)" },
	{ 0x1c,		"Lv4 Intr (XP High)" },
	{ 0x1d,		"Lv5 Intr (System Clock)" },
	{ 0x1e,		"Lv6 Intr (SIO)" },
	{ 0x1f,		"Lv7 Intr (NMI)" },
};

// NEWS はレベル割り込みと、一部が vectored */
/*static*/ std::map<int, const char * const> VectorTable::name_news = {
	//           01234567890123456789012
	{ 0x19,		"Lv1 Intr (AST)" },
	{ 0x1a,		"Lv2 Intr" },
	{ 0x1b,		"Lv3 Intr" },
	{ 0x1c,		"Lv4 Intr (SIC/LAN)" },
	{ 0x1d,		"Lv5 Intr (SCC)" },
	{ 0x1e,		"Lv6 Intr (System Timer)" },
	{ 0x1f,		"Lv7 Intr" },

	{ 0x40,		"SCC" },
};

// virt-m68k
/*static*/ std::map<int, const char * const> VectorTable::name_virt68k = {
	//           01234567890123456789012
	{ 0x19,		"Lv1 Intr (GFPIC1)" },
	{ 0x1a,		"Lv2 Intr (GFPIC2)" },
	{ 0x1b,		"Lv3 Intr (GFPIC3)" },
	{ 0x1c,		"Lv4 Intr (GFPIC4)" },
	{ 0x1d,		"Lv5 Intr (GFPIC5)" },
	{ 0x1e,		"Lv6 Intr (GFPIC6)" },
	{ 0x1f,		"Lv7 Intr" },
};

// LUNA-88K
/*static*/ std::map<int, const char * const> VectorTable::name_luna88k = {
	//           01234567890123456789012 <- 例外履歴欄の横幅
	{ 0x00,		"Reset Exception" },
	{ 0x01,		"Interrupt Exception" },
	{ 0x02,		"Inst Access Exception" },
	{ 0x03,		"Data Access Exception" },
	{ 0x04,		"Misaligned Access Excep" },
	{ 0x05,		"Unimplemented Opcode" },
	{ 0x06,		"Priv. Violation Excep." },
	{ 0x07,		"Bounds Check Violation" },
	{ 0x08,		"Illegal Integer Divide" },
	{ 0x09,		"Int Overflow Exception" },
	{ 0x0a,		"Error Exception" },
	// SFU1 (SFU2〜7 はなくていいか…)
	{ 0x72,		"SFU1 Precise Exception" },
	{ 0x73,		"SFU1 ImpreciseException" },

	// 0x80 は OpenBSD では sigsys、NetBSD/luna88k (非公式)ではシステムコール。
	{ 0x80,		"NetBSD System Call" },
	// XXX 本来は OpenBSD 稼働時に限定すべきだろうけど、そうする意味もない
	{ 0x81,		"OpenBSD sigsys" },
	{ 0x82,		"OpenBSD break" },
	{ 0x83,		"OpenBSD trace" },
	{ 0x84,		"OpenBSD entry" },
	{ 450,		"OpenBSD System Call" },
	{ 451,		"OpenBSD Cache Flush" },
	{ 503,		"Division by Zero(GCC)" },
	{ 504,		"OpenBSD stepbpt" },
	{ 511,		"OpenBSD userbpt" },
};

unix.superglobalmegacorp.com

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