--- nono/vm/interrupt.cpp 2026/04/29 17:04:52 1.1.1.3 +++ nono/vm/interrupt.cpp 2026/04/29 17:05:10 1.1.1.6 @@ -4,6 +4,30 @@ // Licensed under nono-license.txt // +// +// 割り込みコントローラ +// + +// IODevice +// | +// | +-----------------+ +// +--| InterruptDevice | (割り込みコントローラの共通部) +// +-----------------+ +// | +-----------------+ +// +--| M680x0Interrupt | (m68k 割り込みコントローラの共通部) +// | +-----------------+ +// | | +// | | +-----------------+ +// | +--| X68030Interrupt | (X68030 の割り込みコントローラ) +// | | +-----------------+ +// | | +// | | +---------------+ +// | +--| LunaInterrupt | (LUNA-I の割り込みコントローラ) +// | +---------------+ +// | +// +-- PEDECDevice (X68030 の Lv1 担当コントローラ) +// +-- SysCtlrDevice (LUNA88K の割り込みコントローラ) + #include "interrupt.h" #include "dmac.h" #include "lance.h" @@ -11,45 +35,78 @@ #include "m68030.h" #include "mfp.h" #include "mpu680x0.h" +#include "nmi.h" #include "scc.h" #include "sio.h" #include "spc.h" #include "sysclk.h" -// IODevice -// | -// v -// InterruptDevice -// | -// +------------------------+-------------------------+ -// | | | -// | v | -// | M680x0Interrupt | -// | | | -// | +--------+--------+ | -// v v v v -// PEDECDevice X68030Interrupt LunaInterrupt SysCtlrDevice -// (X68030のLv1 (X68030) (LUNA-I) (LUNA88K) -// カスケード) - -std::unique_ptr gInterrupt; +// グローバル参照用 +InterruptDevice *gInterrupt; // // 仮想割り込みコントローラ (共通部) // -// コンストラクタ +// コンストラクタ(オブジェクト名指定あり) +InterruptDevice::InterruptDevice(const std::string& objname_) + : inherited(objname_) +{ +} + +// コンストラクタ(オブジェクト名指定なし) InterruptDevice::InterruptDevice() + : InterruptDevice("Interrupt") { - logname = "interrupt"; - devname = "Interrupt"; } // デストラクタ InterruptDevice::~InterruptDevice() { + gInterrupt = NULL; +} + +// リセット +void +InterruptDevice::ResetHard(bool poweron) +{ + intmap = IntmapSentinel; + memset(&counter, 0, sizeof(counter)); +} + +// 子デバイスが割り込み信号線をアサートした +void +InterruptDevice::AssertINT(Device *source) +{ + uint32 oldmap; + uint32 srcmap; + + oldmap = intmap; + srcmap = GetIntmap(source); + intmap |= srcmap; + + if ((oldmap ^ intmap) != 0) { + // 立ち上がりエッジでカウント + counter[__builtin_clz(srcmap)]++; + ChangeInterrupt(); + } } +// 子デバイスが割り込み信号線をネゲートした +void +InterruptDevice::NegateINT(Device *source) +{ + uint32 oldmap; + uint32 srcmap; + + oldmap = intmap; + srcmap = GetIntmap(source); + intmap &= ~srcmap; + + if ((oldmap ^ intmap) != 0) { + ChangeInterrupt(); + } +} // // 仮想割り込みコントローラ (m680x0 共通部) @@ -74,53 +131,22 @@ M680x0Interrupt::~M680x0Interrupt() // リセット void -M680x0Interrupt::ResetHard() +M680x0Interrupt::ResetHard(bool poweron) { + inherited::ResetHard(poweron); ipl = 0; - intmap = 0x00000001; - memset(&counter, 0, sizeof(counter)); } -// 子デバイスが割り込み信号線をアサートした (m680x0 共通) void -M680x0Interrupt::AssertINT(Device *source) +M680x0Interrupt::ChangeInterrupt() { - uint32 oldmap; - uint32 srcmap; int newipl; - oldmap = intmap; - srcmap = GetIntmap(source); - intmap |= srcmap; - - if ((oldmap ^ intmap) != 0) { - // 立ち上がりエッジでカウント - counter[__builtin_clz(srcmap)]++; - } - + // intmap は最下位ビットを常に立ててあるので 0 にならない newipl = (uint)(31 - __builtin_clz(intmap)) / 4; - // アサートによって割り込みレベルが上がったら、新しいレベルを通知 - // (現在通知中より低い割り込みは受け付けただけでここで保留) - if (newipl > ipl) { - ipl = newipl; - gMPU->Interrupt(ipl); - } -} - -// 子デバイスが割り込み信号線をネゲートした (m680x0 共通) -void -M680x0Interrupt::NegateINT(Device *source) -{ - int newipl; - - intmap &= ~GetIntmap(source); - // intmap の最下位ビットは常に立ててあり intmap は 0 にならない - newipl = (uint)(31 - __builtin_clz(intmap)) / 4; - - // ネゲートによって割り込みレベルが下がったら、新しいレベルを通知 - // (以前に保留されていた低いレベルがあればここで見えるようになる) - if (newipl < ipl) { + // 割り込みレベルが変わったら、新しいレベルを通知 + if (newipl != ipl) { ipl = newipl; gMPU->Interrupt(ipl); } @@ -134,7 +160,9 @@ M680x0Interrupt::NegateINT(Device *sourc // コンストラクタ X68030Interrupt::X68030Interrupt() { - monitor_size = nnSize(24, 11); + monitor.func = ToMonitorCallback(&X68030Interrupt::MonitorUpdate); + monitor.SetSize(25, 11); + monitor.Regist(ID_MONITOR_INTERRUPT); } // デストラクタ @@ -146,23 +174,22 @@ X68030Interrupt::~X68030Interrupt() uint32 X68030Interrupt::GetIntmap(Device *source) const { - if (__predict_true(source == gMFP.get())) { + if (__predict_true(source == gMFP)) { return IntmapMFP; } - if (__predict_true(source == gSCC.get())) { + if (__predict_true(source == gSCC)) { return IntmapSCC; } - if (__predict_true(source == gDMAC.get())) { + if (__predict_true(source == gDMAC)) { return IntmapDMAC; } - if (__predict_true(source == gPEDEC.get())) { + if (__predict_true(source == gPEDEC)) { return IntmapPEDEC; } - if (source == NULL) { - // XXX デバイスまだいないのでとりあえず + if (source == gNMI) { return IntmapNMI; } - PANIC("Unknown interrupt source?"); + VMPANIC("Unknown interrupt source?"); } // 割り込みアクノリッジに応答する @@ -170,6 +197,9 @@ int X68030Interrupt::InterruptAcknowledge(int lv) { switch (lv) { + case 7: + return AutoVector; + case 6: return gMFP->InterruptAcknowledge(); @@ -185,11 +215,11 @@ X68030Interrupt::InterruptAcknowledge(in default: break; } - PANIC("Unknown interrupt acknowledge lv=%d", lv); + VMPANIC("Unknown interrupt acknowledge lv=%d", lv); } void -X68030Interrupt::MonitorUpdate(TextScreen& monitor) +X68030Interrupt::MonitorUpdate(Monitor *, TextScreen& screen) { int y; struct { @@ -201,49 +231,33 @@ X68030Interrupt::MonitorUpdate(TextScree { "SCC", IntmapSCC }, { "DMAC", IntmapDMAC }, { "PEDEC", IntmapPEDEC }, - }, tablePEDEC[] = { - { "SPC", PEDECDevice::IntmapSPC }, - { "FDC", PEDECDevice::IntmapFDC }, - { "FDD", PEDECDevice::IntmapFDD }, - { "HDD", PEDECDevice::IntmapHDD }, - { "PRT", PEDECDevice::IntmapPRT }, }; // CPU の割り込み可否は本当は関係ないけど、便宜的に一緒に表示したい - uint intr_level = gMPU680x0->GetCPU()->reg.intr_level; - // PEDEC 分も一緒に表示したい - uint32 pintr = gPEDEC->GetInt(); + uint intr_mask = gMPU680x0->GetCPU()->reg.intr_mask; - monitor.Clear(); + screen.Clear(); y = 0; - monitor.Print(0, y++, "Lv Device Count IM=%d", intr_level); + screen.Print(0, y++, "Lv Device Count IM=%d", intr_mask); for (int i = 0; i < countof(table); i++) { uint32 map = table[i].map; int clz = __builtin_clz(map); int lv = (31 - clz) / 4; - monitor.Print(0, y, "%d", lv); - monitor.Print(2, y, TA::OnOff(intmap & map), "%s", table[i].name); - monitor.Print(8, y, "%10u", counter[clz]); - if (lv <= intr_level) { - monitor.Puts(20, y, "Mask"); + 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]); + if (lv <= intr_mask) { + screen.Puts(21, y, "Mask"); } y++; } - // pintr は下位ワードが割り込み状態、上位ワードが許可状態。 - uint32 pmask = pintr >> 16; - for (int i = 0; i < countof(tablePEDEC); i++) { - uint32 map = tablePEDEC[i].map; - monitor.Puts(3, y, TA::OnOff(pintr & map), tablePEDEC[i].name); - // XXX カウンタはまだない - if (intr_level >= 1 || (pmask & map) == 0) { - monitor.Puts(20, y, "Mask"); - } - y++; - } + // PEDEC 分も一緒に表示したい + gPEDEC->MonitorUpdate(screen, intr_mask, y); } + // // LUNA-I 仮想割り込みコントローラ // @@ -251,7 +265,9 @@ X68030Interrupt::MonitorUpdate(TextScree // コンストラクタ LunaInterrupt::LunaInterrupt() { - monitor_size = nnSize(24, 5); + monitor.func = ToMonitorCallback(&LunaInterrupt::MonitorUpdate); + monitor.SetSize(25, 6); + monitor.Regist(ID_MONITOR_INTERRUPT); } // デストラクタ @@ -259,24 +275,26 @@ LunaInterrupt::~LunaInterrupt() { } - // デバイスから Intmap 値を引く uint32 LunaInterrupt::GetIntmap(Device *source) const { - if (__predict_true(source == gSIO.get())) { + if (__predict_true(source == gSIO)) { return IntmapSIO; } - if (__predict_true(source == gSysClk.get())) { + if (__predict_true(source == gSysClk)) { return IntmapSysClk; } - if (__predict_true(source == gEthernet.get())) { + if (__predict_true(source == gEthernet)) { return IntmapLance; } - if (__predict_true(source == gSPC.get())) { + if (__predict_true(source == gSPC)) { return IntmapSPC; } - PANIC("Unknown interrupt source?"); + if (source == gNMI) { + return IntmapNMI; + } + VMPANIC("Unknown interrupt source?"); } // 割り込みアクノリッジに応答する @@ -288,13 +306,14 @@ LunaInterrupt::InterruptAcknowledge(int } void -LunaInterrupt::MonitorUpdate(TextScreen& monitor) +LunaInterrupt::MonitorUpdate(Monitor *, TextScreen& screen) { int y; struct { const char *name; uint32 map; } table[] = { + { "NMI", IntmapNMI }, { "SIO", IntmapSIO }, { "Clock", IntmapSysClk }, { "Lance", IntmapLance }, @@ -302,21 +321,21 @@ LunaInterrupt::MonitorUpdate(TextScreen& }; // CPU の割り込み可否は本当は関係ないけど、便宜的に一緒に表示したい - uint intr_level = gMPU680x0->GetCPU()->reg.intr_level; + uint intr_mask = gMPU680x0->GetCPU()->reg.intr_mask; - monitor.Clear(); + screen.Clear(); y = 0; - monitor.Print(0, y++, "Lv Device Count IM=%d", intr_level); + screen.Print(0, y++, "Lv Device Count IM=%d", intr_mask); for (int i = 0; i < countof(table); i++) { uint32 map = table[i].map; int clz = __builtin_clz(map); int lv = (31 - clz) / 4; - monitor.Print(0, y, "%d", lv); - monitor.Print(2, y, TA::OnOff(intmap & map), "%s", table[i].name); - monitor.Print(8, y, "%10u", counter[clz]); - if (lv <= intr_level) { - monitor.Puts(20, y, "Mask"); + 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]); + if (lv <= intr_mask) { + screen.Puts(21, y, "Mask"); } y++; }