--- nono/vm/interrupt.cpp 2026/04/29 17:05:10 1.1.1.6 +++ nono/vm/interrupt.cpp 2026/04/29 17:05:28 1.1.1.10 @@ -23,57 +23,108 @@ // | | // | | +---------------+ // | +--| LunaInterrupt | (LUNA-I の割り込みコントローラ) -// | +---------------+ +// | | +---------------+ +// | | +// | | +---------------+ +// | +--| NewsInterrupt | (NEWS の割り込みコントローラ) +// | | +---------------+ +// | | +// | | +------------------+ +// | +--| Virt68kInterrupt | (virt-m68k の割り込みコントローラ) +// | +------------------+ // | // +-- PEDECDevice (X68030 の Lv1 担当コントローラ) -// +-- SysCtlrDevice (LUNA88K の割り込みコントローラ) +// +-- SysCtlrDevice (LUNA-88K の割り込みコントローラ) +// +-- GFPICDevice (virt-m68k の各レベル担当コントローラ) #include "interrupt.h" #include "dmac.h" +#include "goldfish_pic.h" #include "lance.h" #include "pedec.h" #include "m68030.h" #include "mfp.h" #include "mpu680x0.h" +#include "newsctlr.h" #include "nmi.h" +#include "rtl8019as.h" #include "scc.h" #include "sio.h" #include "spc.h" #include "sysclk.h" -// グローバル参照用 -InterruptDevice *gInterrupt; - // -// 仮想割り込みコントローラ (共通部) +// 割り込みコントローラ (共通部) // -// コンストラクタ(オブジェクト名指定あり) -InterruptDevice::InterruptDevice(const std::string& objname_) - : inherited(objname_) +// コンストラクタ(オブジェクト名指定なし) +InterruptDevice::InterruptDevice() + : InterruptDevice(OBJ_INTERRUPT) // 移譲 { } -// コンストラクタ(オブジェクト名指定なし) -InterruptDevice::InterruptDevice() - : InterruptDevice("Interrupt") +// コンストラクタ(オブジェクト名指定あり) +InterruptDevice::InterruptDevice(uint objid_) + : inherited(objid_) { } // デストラクタ InterruptDevice::~InterruptDevice() { - gInterrupt = NULL; } // リセット void InterruptDevice::ResetHard(bool poweron) { - intmap = IntmapSentinel; memset(&counter, 0, sizeof(counter)); } +// 子デバイスを登録する。 +void +InterruptDevice::RegistINT(uint32 intmap_, const char *name_, Device *source_) +{ + for (const auto& m : intmap_list) { + assertmsg(m.intmap != intmap_, + "intmap=%08x already registered", intmap_); + if (source_ != NULL) { + assertmsg(m.source != source_, + "source=%p already registered", source_); + } + } + intmap_list.emplace_back(intmap_, name_, source_); + + // 登録された intmap の和 + intmap_avail |= intmap_; +} + +// デバイスから Intmap 値を引く +uint32 +InterruptDevice::GetIntmap(const Device *source) const +{ + for (const auto& m : intmap_list) { + if (m.source == source) { + return m.intmap; + } + } + + VMPANIC("Unknown interrupt source?"); +} + +// Intmap 値から名前を引く +const char * +InterruptDevice::GetIntName(uint32 intmap_) const +{ + for (const auto& m : intmap_list) { + if (m.intmap == intmap_) { + return m.name; + } + } + + return "???"; +} + // 子デバイスが割り込み信号線をアサートした void InterruptDevice::AssertINT(Device *source) @@ -81,11 +132,11 @@ InterruptDevice::AssertINT(Device *sourc uint32 oldmap; uint32 srcmap; - oldmap = intmap; + oldmap = intmap_status; srcmap = GetIntmap(source); - intmap |= srcmap; + intmap_status |= srcmap; - if ((oldmap ^ intmap) != 0) { + if ((oldmap ^ intmap_status) != 0) { // 立ち上がりエッジでカウント counter[__builtin_clz(srcmap)]++; ChangeInterrupt(); @@ -99,29 +150,38 @@ InterruptDevice::NegateINT(Device *sourc uint32 oldmap; uint32 srcmap; - oldmap = intmap; + oldmap = intmap_status; srcmap = GetIntmap(source); - intmap &= ~srcmap; + intmap_status &= ~srcmap; - if ((oldmap ^ intmap) != 0) { + if ((oldmap ^ intmap_status) != 0) { ChangeInterrupt(); } } +// 割り込み信号線の状態を取得 +bool +InterruptDevice::GetINT(const Device *source) const +{ + uint32 srcmap; + + srcmap = GetIntmap(source); + + return (intmap_status & srcmap); +} + + // // 仮想割り込みコントローラ (m680x0 共通部) // -// CPU コアからの割り込みアクノリッジを受け付けるグローバル関数 -int -m68030_interrupt_acknowledge(int lv) -{ - return gInterrupt->InterruptAcknowledge(lv); -} - // コンストラクタ M680x0Interrupt::M680x0Interrupt() { + intmap_status = IntmapSentinel; + // m680x0 仮想割り込みコントローラにはマスクがないので使わない。 + // 念のためそれっぽい値を入れておく。 + intmap_enable = 0xfffffff0; } // デストラクタ @@ -129,26 +189,38 @@ M680x0Interrupt::~M680x0Interrupt() { } +// 初期化 +bool +M680x0Interrupt::Init() +{ + if (inherited::Init() == false) { + return false; + } + + mpu680x0 = GetMPU680x0Device(mpu); + + return true; +} + // リセット void M680x0Interrupt::ResetHard(bool poweron) { inherited::ResetHard(poweron); - ipl = 0; } void M680x0Interrupt::ChangeInterrupt() { - int newipl; + uint newipl; - // intmap は最下位ビットを常に立ててあるので 0 にならない - newipl = (uint)(31 - __builtin_clz(intmap)) / 4; + // intmap_status は最下位ビットを常に立ててあるので 0 にならない + newipl = (uint)(31 - __builtin_clz(intmap_status)) / 4; // 割り込みレベルが変わったら、新しいレベルを通知 if (newipl != ipl) { ipl = newipl; - gMPU->Interrupt(ipl); + mpu->Interrupt(ipl); } } @@ -161,7 +233,6 @@ M680x0Interrupt::ChangeInterrupt() X68030Interrupt::X68030Interrupt() { monitor.func = ToMonitorCallback(&X68030Interrupt::MonitorUpdate); - monitor.SetSize(25, 11); monitor.Regist(ID_MONITOR_INTERRUPT); } @@ -170,30 +241,59 @@ X68030Interrupt::~X68030Interrupt() { } -// デバイスから Intmap 値を引く -uint32 -X68030Interrupt::GetIntmap(Device *source) const -{ - if (__predict_true(source == gMFP)) { - return IntmapMFP; +// 初期化 +bool +X68030Interrupt::Init() +{ + if (inherited::Init() == false) { + return false; + } + + dmac = GetDMACDevice(); + mfp = GetMFPDevice(); + nmi = GetNMIDevice(); + pedec = GetPEDECDevice(); + scc = GetSCCDevice(); + for (int i = 0; i < 2; i++) { + net[i] = gMainApp.FindObject(OBJ_ETHERNET(i)); + } + + // 検索順 + RegistINT(IntmapMFP, "MFP", mfp); + RegistINT(IntmapDMAC, "DMAC", dmac); + RegistINT(IntmapPEDEC, "PEDEC", pedec); + RegistINT(IntmapSCC, "SCC", scc); + if (net[0]) { + RegistINT(IntmapNereid0,"Nereid0", net[0]); + } + if (net[1]) { + RegistINT(IntmapNereid1,"Nereid1", net[1]); + } + RegistINT(IntmapNMI, "NMI", nmi); + + // 表示順 + // XXX const uint32 がそのまま push_back() 出来ないようだ + disp_list.clear(); + disp_list.push_back((uint32)IntmapNMI); + disp_list.push_back((uint32)IntmapMFP); + disp_list.push_back((uint32)IntmapSCC); + if (net[0]) { + disp_list.push_back((uint32)IntmapNereid0); + } + if (net[1]) { + disp_list.push_back((uint32)IntmapNereid1); } - if (__predict_true(source == gSCC)) { - return IntmapSCC; - } - if (__predict_true(source == gDMAC)) { - return IntmapDMAC; - } - if (__predict_true(source == gPEDEC)) { - return IntmapPEDEC; - } - if (source == gNMI) { - return IntmapNMI; - } - VMPANIC("Unknown interrupt source?"); + disp_list.push_back((uint32)IntmapDMAC); + disp_list.push_back((uint32)IntmapPEDEC); + + // 行数が確定したのでサイズ設定 + monitor.SetSize(41, 1 + disp_list.size() + 5/*PEDEC*/); + + return true; } // 割り込みアクノリッジに応答する -int +busdata X68030Interrupt::InterruptAcknowledge(int lv) { switch (lv) { @@ -201,60 +301,67 @@ X68030Interrupt::InterruptAcknowledge(in return AutoVector; case 6: - return gMFP->InterruptAcknowledge(); + return mfp->InterruptAcknowledge(); case 5: - return gSCC->InterruptAcknowledge(); + return scc->InterruptAcknowledge(); + + case 4: + if ((intmap_status & IntmapNereid0)) { + return net[0]->InterruptAcknowledge(); + } + if ((intmap_status & IntmapNereid1)) { + return net[1]->InterruptAcknowledge(); + } + break; case 3: - return gDMAC->InterruptAcknowledge(); + return dmac->InterruptAcknowledge(); case 1: - return gPEDEC->InterruptAcknowledge(lv); + return pedec->InterruptAcknowledge(lv); default: break; } - VMPANIC("Unknown interrupt acknowledge lv=%d", lv); + + return BusData::BusErr; } void X68030Interrupt::MonitorUpdate(Monitor *, TextScreen& screen) { int y; - struct { - const char *name; - uint32 map; - } table[] = { - { "NMI", IntmapNMI }, - { "MFP", IntmapMFP }, - { "SCC", IntmapSCC }, - { "DMAC", IntmapDMAC }, - { "PEDEC", IntmapPEDEC }, - }; // CPU の割り込み可否は本当は関係ないけど、便宜的に一緒に表示したい - uint intr_mask = gMPU680x0->GetCPU()->reg.intr_mask; + uint intr_mask = mpu680x0->reg.intr_mask; screen.Clear(); + // 0 1 2 3 4 + // 01234567890123456789012345678901234567890 + // Lv Device IM=7 Count + // 1 Nereid0 01234567890123456789012345 + // SPC Mask + y = 0; - screen.Print(0, y++, "Lv Device Count IM=%d", intr_mask); - for (int i = 0; i < countof(table); i++) { - uint32 map = table[i].map; + screen.Print(0, y, "Lv Device IM=%u", intr_mask); + screen.Puts(36, y, "Count"); + y++; + for (uint32 map : disp_list) { int clz = __builtin_clz(map); int lv = (31 - clz) / 4; - screen.Print(0, y, "%d", lv); - screen.Print(2, y, TA::OnOff(intmap & map), "%s", table[i].name); - screen.Print(8, y, "%11" PRIu64, counter[clz]); + screen.Print(0, y, "%u", lv); + screen.Puts(3, y, TA::OnOff(intmap_status & map), GetIntName(map)); if (lv <= intr_mask) { - screen.Puts(21, y, "Mask"); + screen.Puts(11, y, "Mask"); } + screen.Print(15, y, "%26s", format_number(counter[clz]).c_str()); y++; } // PEDEC 分も一緒に表示したい - gPEDEC->MonitorUpdate(screen, intr_mask, y); + pedec->MonitorUpdate(screen, intr_mask, y); } @@ -266,7 +373,6 @@ X68030Interrupt::MonitorUpdate(Monitor * LunaInterrupt::LunaInterrupt() { monitor.func = ToMonitorCallback(&LunaInterrupt::MonitorUpdate); - monitor.SetSize(25, 6); monitor.Regist(ID_MONITOR_INTERRUPT); } @@ -275,30 +381,42 @@ LunaInterrupt::~LunaInterrupt() { } -// デバイスから Intmap 値を引く -uint32 -LunaInterrupt::GetIntmap(Device *source) const -{ - if (__predict_true(source == gSIO)) { - return IntmapSIO; - } - if (__predict_true(source == gSysClk)) { - return IntmapSysClk; - } - if (__predict_true(source == gEthernet)) { - return IntmapLance; - } - if (__predict_true(source == gSPC)) { - return IntmapSPC; - } - if (source == gNMI) { - return IntmapNMI; - } - VMPANIC("Unknown interrupt source?"); +// 初期化 +bool +LunaInterrupt::Init() +{ + if (inherited::Init() == false) { + return false; + } + + // 検索順 + RegistINT(IntmapSysClk, "Clock", GetSysClkDevice()); + RegistINT(IntmapSPC, "SPC", GetSPCDevice()); + RegistINT(IntmapSIO, "SIO", GetSIODevice()); + RegistINT(IntmapLance, "Lance", GetLanceDevice()); + RegistINT(IntmapXPHigh, "XP(Hi)", DEVICE_XPINT_HIGH); + RegistINT(IntmapXPLow, "XP(Lo)", DEVICE_XPINT_LOW); + RegistINT(IntmapNMI, "NMI", GetNMIDevice()); + + // 表示順 + disp_list = { + IntmapNMI, + IntmapSIO, + IntmapSysClk, + IntmapXPHigh, + IntmapLance, + IntmapSPC, + IntmapXPLow, + }; + + // 行数が確定したのでサイズ設定 + monitor.SetSize(40, 1 + disp_list.size()); + + return true; } // 割り込みアクノリッジに応答する -int +busdata LunaInterrupt::InterruptAcknowledge(int lv) { // LUNA は全てオートベクタ @@ -309,34 +427,264 @@ void LunaInterrupt::MonitorUpdate(Monitor *, TextScreen& screen) { int y; - struct { - const char *name; - uint32 map; - } table[] = { - { "NMI", IntmapNMI }, - { "SIO", IntmapSIO }, - { "Clock", IntmapSysClk }, - { "Lance", IntmapLance }, - { "SPC", IntmapSPC }, + + // CPU の割り込み可否は本当は関係ないけど、便宜的に一緒に表示したい + uint intr_mask = mpu680x0->reg.intr_mask; + + screen.Clear(); + + // 0 1 2 3 4 + // 01234567890123456789012345678901234567890 + // Lv Device IM=7 Count + // 1 XP(Hi) Mask01234567890123456789012345 + + y = 0; + screen.Print(0, y, "Lv Device IM=%u", intr_mask); + screen.Puts(35, y, "Count"); + y++; + for (uint32 map : disp_list) { + int clz = __builtin_clz(map); + int lv = (31 - clz) / 4; + screen.Print(0, y, "%u", lv); + screen.Puts(3, y, TA::OnOff(intmap_status & map), GetIntName(map)); + if (lv <= intr_mask) { + screen.Puts(10, y, "Mask"); + } + screen.Print(14, y, "%26s", format_number(counter[clz]).c_str()); + y++; + } +} + + +// +// NEWS 仮想割り込みコントローラ +// + +// コンストラクタ +NewsInterrupt::NewsInterrupt() +{ + monitor.func = ToMonitorCallback(&NewsInterrupt::MonitorUpdate); + monitor.Regist(ID_MONITOR_INTERRUPT); +} + +// デストラクタ +NewsInterrupt::~NewsInterrupt() +{ +} + +// 初期化 +bool +NewsInterrupt::Init() +{ + if (inherited::Init() == false) { + return false; + } + + // 割り込みアクノリッジで使う + scc = GetSCCDevice(); + + // 検索順 + RegistINT(IntmapTimer, "Timer", GetNewsCtlrDevice()); + RegistINT(IntmapLance, "Lance", GetLanceDevice()); + RegistINT(IntmapSCC, "SCC", scc); + + // 表示順 + disp_list = { + IntmapTimer, + IntmapSCC, + IntmapLance, }; + // 行数が確定したのでサイズ設定 + monitor.SetSize(40, 1 + disp_list.size()); + + return true; +} + +// 割り込みアクノリッジに応答する +busdata +NewsInterrupt::InterruptAcknowledge(int lv) +{ + // NEWS では 5 の SCC だけベクタで、他はオートベクタ? + switch (lv) { + case 5: + return scc->InterruptAcknowledge(); + + default: + return AutoVector; + } +} + +// ステータスバイトを読み出す。NewsCtlr から呼ばれる。 +uint8 +NewsInterrupt::PeekStatus() const +{ + uint8 data = 0; + + if ((intmap_status & IntmapSIC)) { + data |= 0x80; + } + if ((intmap_status & IntmapLance)) { + data |= 0x04; + } + + return data; +} + +void +NewsInterrupt::MonitorUpdate(Monitor *, TextScreen& screen) +{ + int y; + // CPU の割り込み可否は本当は関係ないけど、便宜的に一緒に表示したい - uint intr_mask = gMPU680x0->GetCPU()->reg.intr_mask; + uint intr_mask = mpu680x0->reg.intr_mask; screen.Clear(); + // 0 1 2 3 4 + // 01234567890123456789012345678901234567890 + // Lv Device IM=7 Count + // 1 XP(Hi) Mask01234567890123456789012345 + y = 0; - screen.Print(0, y++, "Lv Device Count IM=%d", intr_mask); - for (int i = 0; i < countof(table); i++) { - uint32 map = table[i].map; + screen.Print(0, y, "Lv Device IM=%u", intr_mask); + screen.Puts(35, y, "Count"); + y++; + for (uint32 map : disp_list) { int clz = __builtin_clz(map); int lv = (31 - clz) / 4; - screen.Print(0, y, "%d", lv); - screen.Print(2, y, TA::OnOff(intmap & map), "%s", table[i].name); - screen.Print(8, y, "%11" PRIu64, counter[clz]); + screen.Print(0, y, "%u", lv); + screen.Puts(3, y, TA::OnOff(intmap_status & map), GetIntName(map)); if (lv <= intr_mask) { - screen.Puts(21, y, "Mask"); + screen.Puts(10, y, "Mask"); } + screen.Print(14, y, "%26s", format_number(counter[clz]).c_str()); y++; } } + + +// +// virt-m68k 仮想割り込みコントローラ +// + +// コンストラクタ +Virt68kInterrupt::Virt68kInterrupt() +{ + monitor.func = ToMonitorCallback(&Virt68kInterrupt::MonitorUpdate); + monitor.Regist(ID_MONITOR_INTERRUPT); +} + +// デストラクタ +Virt68kInterrupt::~Virt68kInterrupt() +{ +} + +// 初期化 +bool +Virt68kInterrupt::Init() +{ + if (inherited::Init() == false) { + return false; + } + + // [0] は無視。 + gfpic[6] = GetGFPICDevice(6); + gfpic[5] = GetGFPICDevice(5); + gfpic[4] = GetGFPICDevice(4); + gfpic[3] = GetGFPICDevice(3); + gfpic[2] = GetGFPICDevice(2); + gfpic[1] = GetGFPICDevice(1); + + // 検索順 + RegistINT(IntmapGFPIC6, "GFPIC6", gfpic[6]); + RegistINT(IntmapGFPIC5, "GFPIC5", gfpic[5]); + RegistINT(IntmapGFPIC4, "GFPIC4", gfpic[4]); + RegistINT(IntmapGFPIC3, "GFPIC3", gfpic[3]); + RegistINT(IntmapGFPIC2, "GFPIC2", gfpic[2]); + RegistINT(IntmapGFPIC1, "GFPIC1", gfpic[1]); + + // 表示順 + disp_list = { + IntmapGFPIC6, + IntmapGFPIC5, + IntmapGFPIC4, + IntmapGFPIC3, + IntmapGFPIC2, + IntmapGFPIC1, + }; + + // ここまでの行数でサイズ設定。 + // この後各割り込みソースの Init() から GFPIC への RegistIRQ() する + // たびに増やされていく。 + monitor.SetSize(77, 2 + disp_list.size() + 1); + + return true; +} + +void +Virt68kInterrupt::IncMonitorHeight() +{ + auto size = monitor.GetSize(); + monitor.SetSize(size.width, size.height + 1); +} + +// 割り込みアクノリッジに応答する +busdata +Virt68kInterrupt::InterruptAcknowledge(int lv) +{ + // ? + return AutoVector; +} + +void +Virt68kInterrupt::MonitorUpdate(Monitor *, TextScreen& screen) +{ + int y; + + // CPU の割り込み可否は本当は関係ないけど、便宜的に一緒に表示したい + uint intr_mask = mpu680x0->reg.intr_mask; + // 各 PIC の状況も一緒に表示したい。picmap の添字はレベル(1-6) + std::array picmap; + for (int lv = 1; lv < picmap.size(); lv++) { + picmap[lv] = gfpic[lv]->intmap_status; + } + + screen.Clear(); + +// 0 1 2 3 4 5 6 7 +// 01234567890123456789012345678901234567890123456789012345678901234567890123456 +// Lv Device IM=7 IRQ 20 10 1 Count +// 1 GFPIC1 Mask 21098765 43210987 65432109 87654321 01234567890123456789012345 +// E:Enable D:Disable .:NotConnected + + y = 0; + screen.Print(0, y, "Lv Device IM=%u IRQ 20 10 1", + intr_mask); + screen.Puts(72, y, "Count"); + y++; + for (uint32 map : disp_list) { + uint clz = __builtin_clz(map); + uint lv = (uint)(31 - clz) / 4; + screen.Print(0, y, "%u", lv); + screen.Puts(3, y, TA::OnOff(intmap_status & map), GetIntName(map)); + if (lv <= intr_mask) { + screen.Puts(10, y, "Mask"); + } + + // 各入力ピンの状態も表示しないと何も分からない。 + gfpic[lv]->MonitorUpdateSummary(screen, 15, y); + + screen.Print(51, y, "%26s", format_number(counter[clz]).c_str()); + y++; + } + screen.Puts(15, y, "E:Enable D:Disable .:NotConnected"); + y++; + y++; + + for (uint32 map : disp_list) { + int clz = __builtin_clz(map); + int lv = (31 - clz) / 4; + y = gfpic[lv]->MonitorUpdateDetail(screen, y, lv); + } +}