|
|
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" },
};
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.