--- nono/vm/interrupt.cpp 2026/04/29 17:05:20 1.1.1.8 +++ nono/vm/interrupt.cpp 2026/04/29 17:05:37 1.1.1.12 @@ -23,17 +23,28 @@ // | | // | | +---------------+ // | +--| LunaInterrupt | (LUNA-I の割り込みコントローラ) -// | +---------------+ +// | | +---------------+ +// | | +// | | +---------------+ +// | +--| NewsInterrupt | (NEWS の割り込みコントローラ) +// | | +---------------+ +// | | +// | | +------------------+ +// | +--| Virt68kInterrupt | (virt-m68k の割り込みコントローラ) +// | +------------------+ // | // +-- PEDECDevice (X68030 の Lv1 担当コントローラ) // +-- 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 "m680x0.h" #include "mfp.h" +#include "monitor.h" #include "mpu680x0.h" #include "newsctlr.h" #include "nmi.h" @@ -44,7 +55,7 @@ #include "sysclk.h" // -// 仮想割り込みコントローラ (共通部) +// 割り込みコントローラ (共通部) // // コンストラクタ(オブジェクト名指定なし) @@ -54,7 +65,7 @@ InterruptDevice::InterruptDevice() } // コンストラクタ(オブジェクト名指定あり) -InterruptDevice::InterruptDevice(int objid_) +InterruptDevice::InterruptDevice(uint objid_) : inherited(objid_) { } @@ -68,10 +79,53 @@ InterruptDevice::~InterruptDevice() 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) @@ -79,11 +133,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(); @@ -97,11 +151,11 @@ 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(); } } @@ -114,7 +168,7 @@ InterruptDevice::GetINT(const Device *so srcmap = GetIntmap(source); - return (intmap & srcmap); + return (intmap_status & srcmap); } @@ -125,6 +179,10 @@ InterruptDevice::GetINT(const Device *so // コンストラクタ M680x0Interrupt::M680x0Interrupt() { + intmap_status = IntmapSentinel; + // m680x0 仮想割り込みコントローラにはマスクがないので使わない。 + // 念のためそれっぽい値を入れておく。 + intmap_enable = 0xfffffff0; } // デストラクタ @@ -136,28 +194,18 @@ 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) { @@ -174,9 +222,8 @@ M680x0Interrupt::ChangeInterrupt() // コンストラクタ X68030Interrupt::X68030Interrupt() { - monitor.func = ToMonitorCallback(&X68030Interrupt::MonitorUpdate); - monitor.SetSize(41, 13); - monitor.Regist(ID_MONITOR_INTERRUPT); + monitor = gMonitorManager->Regist(ID_MONITOR_INTERRUPT, this); + monitor->func = ToMonitorCallback(&X68030Interrupt::MonitorUpdate); } // デストラクタ @@ -194,47 +241,49 @@ X68030Interrupt::Init() dmac = GetDMACDevice(); mfp = GetMFPDevice(); - // Nereid は拡張ボードなので存在しない可能性がある - for (int i = 0; i < 2; i++) { - net[i] = gMainApp.FindObject(OBJ_ETHERNET(i)); - } nmi = GetNMIDevice(); pedec = GetPEDECDevice(); scc = GetSCCDevice(); - - return true; -} - -// デバイスから Intmap 値を引く -uint32 -X68030Interrupt::GetIntmap(const Device *source) const -{ - if (__predict_true(source == mfp)) { - return IntmapMFP; - } - if (__predict_true(source == scc)) { - return IntmapSCC; - } - if (__predict_true(source == net[0])) { - return IntmapNereid0; - } - if (__predict_true(source == net[1])) { - return IntmapNereid1; - } - if (__predict_true(source == dmac)) { - return IntmapDMAC; + for (int i = 0; i < 2; i++) { + net[i] = gMainApp.FindObject(OBJ_ETHERNET(i)); } - if (__predict_true(source == pedec)) { - return IntmapPEDEC; + + // 検索順 + 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 (source == nmi) { - return IntmapNMI; + if (net[1]) { + disp_list.push_back((uint32)IntmapNereid1); } - 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) { @@ -248,10 +297,11 @@ X68030Interrupt::InterruptAcknowledge(in return scc->InterruptAcknowledge(); case 4: - for (int i = 0; i < 2; i++) { - if (net[i]) { - return net[i]->InterruptAcknowledge(); - } + if ((intmap_status & IntmapNereid0)) { + return net[0]->InterruptAcknowledge(); + } + if ((intmap_status & IntmapNereid1)) { + return net[1]->InterruptAcknowledge(); } break; @@ -264,28 +314,16 @@ X68030Interrupt::InterruptAcknowledge(in 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 }, - { "Nereid0", IntmapNereid0 }, - { "Nereid1", IntmapNereid1 }, - { "DMAC", IntmapDMAC }, - { "PEDEC", IntmapPEDEC }, - }; // CPU の割り込み可否は本当は関係ないけど、便宜的に一緒に表示したい - auto mpu680x0 = dynamic_cast(mpu); uint intr_mask = mpu680x0->reg.intr_mask; screen.Clear(); @@ -297,21 +335,14 @@ X68030Interrupt::MonitorUpdate(Monitor * // SPC Mask y = 0; - screen.Print(0, y, "Lv Device IM=%d", intr_mask); + screen.Print(0, y, "Lv Device IM=%u", intr_mask); screen.Puts(36, y, "Count"); y++; - for (int i = 0; i < countof(table); i++) { - uint32 map = table[i].map; + for (uint32 map : disp_list) { int clz = __builtin_clz(map); int lv = (31 - clz) / 4; - screen.Print(0, y, "%d", lv); - if ((map == IntmapNereid0 && net[0] == NULL) || - (map == IntmapNereid1 && net[1] == NULL)) - { - screen.Puts(3, y, TA::Disable, table[i].name); - } else { - screen.Puts(3, y, TA::OnOff(intmap & map), table[i].name); - } + screen.Print(0, y, "%u", lv); + screen.Puts(3, y, TA::OnOff(intmap_status & map), GetIntName(map)); if (lv <= intr_mask) { screen.Puts(11, y, "Mask"); } @@ -331,9 +362,8 @@ X68030Interrupt::MonitorUpdate(Monitor * // コンストラクタ LunaInterrupt::LunaInterrupt() { - monitor.func = ToMonitorCallback(&LunaInterrupt::MonitorUpdate); - monitor.SetSize(40, 8); - monitor.Regist(ID_MONITOR_INTERRUPT); + monitor = gMonitorManager->Regist(ID_MONITOR_INTERRUPT, this); + monitor->func = ToMonitorCallback(&LunaInterrupt::MonitorUpdate); } // デストラクタ @@ -349,45 +379,34 @@ LunaInterrupt::Init() return false; } - lance = GetLanceDevice(); - nmi = GetNMIDevice(); - sio = GetSIODevice(); - spc = GetSPCDevice(); - sysclk = GetSysClkDevice(); + // 検索順 + 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, + }; - return true; -} + // 行数が確定したのでサイズ設定 + monitor->SetSize(40, 1 + disp_list.size()); -// デバイスから Intmap 値を引く -uint32 -LunaInterrupt::GetIntmap(const Device *source) const -{ - if (__predict_true(source == sio)) { - return IntmapSIO; - } - if (__predict_true(source == sysclk)) { - return IntmapSysClk; - } - if (__predict_true(source == lance)) { - return IntmapLance; - } - if (__predict_true(source == spc)) { - return IntmapSPC; - } - if (__predict_true(source == DEVICE_XPINT_HIGH)) { - return IntmapXPHigh; - } - if (__predict_true(source == DEVICE_XPINT_LOW)) { - return IntmapXPLow; - } - if (source == nmi) { - return IntmapNMI; - } - VMPANIC("Unknown interrupt source?"); + return true; } // 割り込みアクノリッジに応答する -int +busdata LunaInterrupt::InterruptAcknowledge(int lv) { // LUNA は全てオートベクタ @@ -398,21 +417,8 @@ void LunaInterrupt::MonitorUpdate(Monitor *, TextScreen& screen) { int y; - struct { - const char *name; - uint32 map; - } table[] = { - { "NMI", IntmapNMI }, - { "SIO", IntmapSIO }, - { "Clock", IntmapSysClk }, - { "XP(Hi)", IntmapXPHigh }, - { "Lance", IntmapLance }, - { "SPC", IntmapSPC }, - { "XP(Lo)", IntmapXPLow }, - }; // CPU の割り込み可否は本当は関係ないけど、便宜的に一緒に表示したい - auto mpu680x0 = dynamic_cast(mpu); uint intr_mask = mpu680x0->reg.intr_mask; screen.Clear(); @@ -423,15 +429,14 @@ LunaInterrupt::MonitorUpdate(Monitor *, // 1 XP(Hi) Mask01234567890123456789012345 y = 0; - screen.Print(0, y, "Lv Device IM=%d", intr_mask); + screen.Print(0, y, "Lv Device IM=%u", intr_mask); screen.Puts(35, y, "Count"); y++; - for (int i = 0; i < countof(table); i++) { - uint32 map = table[i].map; + for (uint32 map : disp_list) { int clz = __builtin_clz(map); int lv = (31 - clz) / 4; - screen.Print(0, y, "%d", lv); - screen.Print(3, y, TA::OnOff(intmap & map), "%s", table[i].name); + 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"); } @@ -448,9 +453,8 @@ LunaInterrupt::MonitorUpdate(Monitor *, // コンストラクタ NewsInterrupt::NewsInterrupt() { - monitor.func = ToMonitorCallback(&NewsInterrupt::MonitorUpdate); - monitor.SetSize(40, 5); - monitor.Regist(ID_MONITOR_INTERRUPT); + monitor = gMonitorManager->Regist(ID_MONITOR_INTERRUPT, this); + monitor->func = ToMonitorCallback(&NewsInterrupt::MonitorUpdate); } // デストラクタ @@ -466,31 +470,29 @@ NewsInterrupt::Init() return false; } - lance = GetLanceDevice(); - newsctlr = GetNewsCtlrDevice(); + // 割り込みアクノリッジで使う scc = GetSCCDevice(); - return true; -} + // 検索順 + RegistINT(IntmapTimer, "Timer", GetNewsCtlrDevice()); + RegistINT(IntmapLance, "Lance", GetLanceDevice()); + RegistINT(IntmapSCC, "SCC", scc); + + // 表示順 + disp_list = { + IntmapTimer, + IntmapSCC, + IntmapLance, + }; -// デバイスから Intmap 値を引く -uint32 -NewsInterrupt::GetIntmap(const Device *source) const -{ - if (__predict_true(source == newsctlr)) { - return IntmapTimer; - } - if (__predict_true(source == scc)) { - return IntmapSCC; - } - if (__predict_true(source == lance)) { - return IntmapLance; - } - VMPANIC("Unknown interrupt source?"); + // 行数が確定したのでサイズ設定 + monitor->SetSize(40, 1 + disp_list.size()); + + return true; } // 割り込みアクノリッジに応答する -int +busdata NewsInterrupt::InterruptAcknowledge(int lv) { // NEWS では 5 の SCC だけベクタで、他はオートベクタ? @@ -509,10 +511,10 @@ NewsInterrupt::PeekStatus() const { uint8 data = 0; - if ((intmap & IntmapSIC)) { + if ((intmap_status & IntmapSIC)) { data |= 0x80; } - if ((intmap & IntmapLance)) { + if ((intmap_status & IntmapLance)) { data |= 0x04; } @@ -523,17 +525,8 @@ void NewsInterrupt::MonitorUpdate(Monitor *, TextScreen& screen) { int y; - struct { - const char *name; - uint32 map; - } table[] = { - { "Timer", IntmapTimer }, - { "SCC", IntmapSCC }, - { "Lance", IntmapLance }, - }; // CPU の割り込み可否は本当は関係ないけど、便宜的に一緒に表示したい - auto mpu680x0 = dynamic_cast(mpu); uint intr_mask = mpu680x0->reg.intr_mask; screen.Clear(); @@ -544,15 +537,14 @@ NewsInterrupt::MonitorUpdate(Monitor *, // 1 XP(Hi) Mask01234567890123456789012345 y = 0; - screen.Print(0, y, "Lv Device IM=%d", intr_mask); + screen.Print(0, y, "Lv Device IM=%u", intr_mask); screen.Puts(35, y, "Count"); y++; - for (int i = 0; i < countof(table); i++) { - uint32 map = table[i].map; + for (uint32 map : disp_list) { int clz = __builtin_clz(map); int lv = (31 - clz) / 4; - screen.Print(0, y, "%d", lv); - screen.Print(3, y, TA::OnOff(intmap & map), "%s", table[i].name); + 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"); } @@ -560,3 +552,129 @@ NewsInterrupt::MonitorUpdate(Monitor *, y++; } } + + +// +// virt-m68k 仮想割り込みコントローラ +// + +// コンストラクタ +Virt68kInterrupt::Virt68kInterrupt() +{ + monitor = gMonitorManager->Regist(ID_MONITOR_INTERRUPT, this); + monitor->func = ToMonitorCallback(&Virt68kInterrupt::MonitorUpdate); +} + +// デストラクタ +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); + } +}