--- nono/vm/interrupt.cpp 2026/04/29 17:05:17 1.1.1.7 +++ nono/vm/interrupt.cpp 2026/04/29 17:05:24 1.1.1.9 @@ -23,13 +23,23 @@ // | | // | | +---------------+ // | +--| LunaInterrupt | (LUNA-I の割り込みコントローラ) -// | +---------------+ +// | | +---------------+ +// | | +// | | +---------------+ +// | +--| NewsInterrupt | (NEWS の割り込みコントローラ) +// | | +---------------+ +// | | +// | | +------------------+ +// | +--| Virt68kInterrupt | (virt68k の割り込みコントローラ) +// | +------------------+ // | // +-- PEDECDevice (X68030 の Lv1 担当コントローラ) // +-- SysCtlrDevice (LUNA-88K の割り込みコントローラ) +// +-- GFPICDevice (virt68k の各レベル担当コントローラ) #include "interrupt.h" #include "dmac.h" +#include "goldfish_pic.h" #include "lance.h" #include "pedec.h" #include "m68030.h" @@ -37,13 +47,14 @@ #include "mpu680x0.h" #include "newsctlr.h" #include "nmi.h" +#include "rtl8019as.h" #include "scc.h" #include "sio.h" #include "spc.h" #include "sysclk.h" // -// 仮想割り込みコントローラ (共通部) +// 割り込みコントローラ (共通部) // // コンストラクタ(オブジェクト名指定なし) @@ -67,10 +78,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) @@ -78,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(); @@ -96,15 +150,27 @@ 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 共通部) // @@ -112,6 +178,10 @@ InterruptDevice::NegateINT(Device *sourc // コンストラクタ M680x0Interrupt::M680x0Interrupt() { + intmap_status = IntmapSentinel; + // m680x0 仮想割り込みコントローラにはマスクがないので使わない。 + // 念のためそれっぽい値を入れておく。 + intmap_enable = 0xfffffff0; } // デストラクタ @@ -127,6 +197,8 @@ M680x0Interrupt::Init() return false; } + mpu680x0 = GetMPU680x0Device(mpu); + return true; } @@ -135,7 +207,6 @@ void M680x0Interrupt::ResetHard(bool poweron) { inherited::ResetHard(poweron); - ipl = 0; } void @@ -143,8 +214,8 @@ M680x0Interrupt::ChangeInterrupt() { int newipl; - // intmap は最下位ビットを常に立ててあるので 0 にならない - newipl = (uint)(31 - __builtin_clz(intmap)) / 4; + // intmap_status は最下位ビットを常に立ててあるので 0 にならない + newipl = (uint)(31 - __builtin_clz(intmap_status)) / 4; // 割り込みレベルが変わったら、新しいレベルを通知 if (newipl != ipl) { @@ -162,7 +233,6 @@ M680x0Interrupt::ChangeInterrupt() X68030Interrupt::X68030Interrupt() { monitor.func = ToMonitorCallback(&X68030Interrupt::MonitorUpdate); - monitor.SetSize(40, 11); monitor.Regist(ID_MONITOR_INTERRUPT); } @@ -184,34 +254,46 @@ X68030Interrupt::Init() nmi = GetNMIDevice(); pedec = GetPEDECDevice(); scc = GetSCCDevice(); - - return true; -} - -// デバイスから Intmap 値を引く -uint32 -X68030Interrupt::GetIntmap(Device *source) const -{ - if (__predict_true(source == mfp)) { - return IntmapMFP; - } - if (__predict_true(source == scc)) { - return IntmapSCC; - } - 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) { @@ -224,6 +306,15 @@ X68030Interrupt::InterruptAcknowledge(in case 5: 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 dmac->InterruptAcknowledge(); @@ -233,50 +324,39 @@ 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 }, - { "DMAC", IntmapDMAC }, - { "PEDEC", IntmapPEDEC }, - }; // CPU の割り込み可否は本当は関係ないけど、便宜的に一緒に表示したい - auto mpu680x0 = dynamic_cast(mpu); uint intr_mask = mpu680x0->reg.intr_mask; screen.Clear(); // 0 1 2 3 4 // 01234567890123456789012345678901234567890 - // Lv Device IM=7 Count - // 1 PEDEC 01234567890123456789012345 - // SPC Mask + // Lv Device IM=7 Count + // 1 Nereid0 01234567890123456789012345 + // SPC Mask y = 0; - screen.Print(0, y, "Lv Device IM=%d", intr_mask); - screen.Puts(35, y, "Count"); + screen.Print(0, y, "Lv Device IM=%d", 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); - screen.Print(3, y, TA::OnOff(intmap & map), "%s", table[i].name); + screen.Puts(3, y, TA::OnOff(intmap_status & map), GetIntName(map)); if (lv <= intr_mask) { - screen.Puts(10, y, "Mask"); + screen.Puts(11, y, "Mask"); } - screen.Print(14, y, "%26s", format_number(counter[clz]).c_str()); + screen.Print(15, y, "%26s", format_number(counter[clz]).c_str()); y++; } @@ -293,7 +373,6 @@ X68030Interrupt::MonitorUpdate(Monitor * LunaInterrupt::LunaInterrupt() { monitor.func = ToMonitorCallback(&LunaInterrupt::MonitorUpdate); - monitor.SetSize(40, 8); monitor.Regist(ID_MONITOR_INTERRUPT); } @@ -310,45 +389,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(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 は全てオートベクタ @@ -359,21 +427,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(); @@ -387,12 +442,11 @@ LunaInterrupt::MonitorUpdate(Monitor *, screen.Print(0, y, "Lv Device IM=%d", 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.Puts(3, y, TA::OnOff(intmap_status & map), GetIntName(map)); if (lv <= intr_mask) { screen.Puts(10, y, "Mask"); } @@ -410,7 +464,6 @@ LunaInterrupt::MonitorUpdate(Monitor *, NewsInterrupt::NewsInterrupt() { monitor.func = ToMonitorCallback(&NewsInterrupt::MonitorUpdate); - monitor.SetSize(40, 5); monitor.Regist(ID_MONITOR_INTERRUPT); } @@ -427,31 +480,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(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 だけベクタで、他はオートベクタ? @@ -470,10 +521,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; } @@ -484,17 +535,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(); @@ -508,12 +550,11 @@ NewsInterrupt::MonitorUpdate(Monitor *, screen.Print(0, y, "Lv Device IM=%d", 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.Puts(3, y, TA::OnOff(intmap_status & map), GetIntName(map)); if (lv <= intr_mask) { screen.Puts(10, y, "Mask"); } @@ -521,3 +562,129 @@ NewsInterrupt::MonitorUpdate(Monitor *, y++; } } + + +// +// virt68k 仮想割り込みコントローラ +// + +// コンストラクタ +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=%d IRQ 20 10 1", + intr_mask); + screen.Puts(72, 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.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); + } +}