--- nono/vm/interrupt.cpp 2026/04/29 17:04:55 1.1.1.4 +++ nono/vm/interrupt.cpp 2026/04/29 17:05:20 1.1.1.8 @@ -4,6 +4,30 @@ // Licensed under nono-license.txt // +// +// 割り込みコントローラ +// + +// IODevice +// | +// | +-----------------+ +// +--| InterruptDevice | (割り込みコントローラの共通部) +// +-----------------+ +// | +-----------------+ +// +--| M680x0Interrupt | (m68k 割り込みコントローラの共通部) +// | +-----------------+ +// | | +// | | +-----------------+ +// | +--| X68030Interrupt | (X68030 の割り込みコントローラ) +// | | +-----------------+ +// | | +// | | +---------------+ +// | +--| LunaInterrupt | (LUNA-I の割り込みコントローラ) +// | +---------------+ +// | +// +-- PEDECDevice (X68030 の Lv1 担当コントローラ) +// +-- SysCtlrDevice (LUNA-88K の割り込みコントローラ) + #include "interrupt.h" #include "dmac.h" #include "lance.h" @@ -11,88 +35,49 @@ #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" -// IODevice -// | -// v -// InterruptDevice -// | -// +------------------------+-------------------------+ -// | | | -// | v | -// | M680x0Interrupt | -// | | | -// | +--------+--------+ | -// v v v v -// PEDECDevice X68030Interrupt LunaInterrupt SysCtlrDevice -// (X68030のLv1 (X68030) (LUNA-I) (LUNA88K) -// カスケード) - -std::unique_ptr gInterrupt; - // // 仮想割り込みコントローラ (共通部) // -// コンストラクタ(オブジェクト名指定あり) -InterruptDevice::InterruptDevice(const std::string& objname_) - : inherited(objname_) -{ -} - // コンストラクタ(オブジェクト名指定なし) InterruptDevice::InterruptDevice() - : InterruptDevice("Interrupt") -{ -} - -// デストラクタ -InterruptDevice::~InterruptDevice() -{ -} - - -// -// 仮想割り込みコントローラ (m680x0 共通部) -// - -// CPU コアからの割り込みアクノリッジを受け付けるグローバル関数 -int -m68030_interrupt_acknowledge(int lv) + : InterruptDevice(OBJ_INTERRUPT) // 移譲 { - return gInterrupt->InterruptAcknowledge(lv); } -// コンストラクタ -M680x0Interrupt::M680x0Interrupt() +// コンストラクタ(オブジェクト名指定あり) +InterruptDevice::InterruptDevice(int objid_) + : inherited(objid_) { } // デストラクタ -M680x0Interrupt::~M680x0Interrupt() +InterruptDevice::~InterruptDevice() { } // リセット void -M680x0Interrupt::ResetHard() +InterruptDevice::ResetHard(bool poweron) { - ipl = 0; - intmap = 0x00000001; + intmap = IntmapSentinel; memset(&counter, 0, sizeof(counter)); } -// 子デバイスが割り込み信号線をアサートした (m680x0 共通) +// 子デバイスが割り込み信号線をアサートした void -M680x0Interrupt::AssertINT(Device *source) +InterruptDevice::AssertINT(Device *source) { uint32 oldmap; uint32 srcmap; - int newipl; oldmap = intmap; srcmap = GetIntmap(source); @@ -101,33 +86,83 @@ M680x0Interrupt::AssertINT(Device *sourc if ((oldmap ^ intmap) != 0) { // 立ち上がりエッジでカウント counter[__builtin_clz(srcmap)]++; + ChangeInterrupt(); } +} - newipl = (uint)(31 - __builtin_clz(intmap)) / 4; +// 子デバイスが割り込み信号線をネゲートした +void +InterruptDevice::NegateINT(Device *source) +{ + uint32 oldmap; + uint32 srcmap; - // アサートによって割り込みレベルが上がったら、新しいレベルを通知 - // (現在通知中より低い割り込みは受け付けただけでここで保留) - if (newipl > ipl) { - ipl = newipl; - gMPU->Interrupt(ipl); + oldmap = intmap; + srcmap = GetIntmap(source); + intmap &= ~srcmap; + + if ((oldmap ^ intmap) != 0) { + ChangeInterrupt(); } } -// 子デバイスが割り込み信号線をネゲートした (m680x0 共通) +// 割り込み信号線の状態を取得 +bool +InterruptDevice::GetINT(const Device *source) const +{ + uint32 srcmap; + + srcmap = GetIntmap(source); + + return (intmap & srcmap); +} + + +// +// 仮想割り込みコントローラ (m680x0 共通部) +// + +// コンストラクタ +M680x0Interrupt::M680x0Interrupt() +{ +} + +// デストラクタ +M680x0Interrupt::~M680x0Interrupt() +{ +} + +// 初期化 +bool +M680x0Interrupt::Init() +{ + if (inherited::Init() == false) { + return false; + } + + return true; +} + +// リセット +void +M680x0Interrupt::ResetHard(bool poweron) +{ + inherited::ResetHard(poweron); + ipl = 0; +} + void -M680x0Interrupt::NegateINT(Device *source) +M680x0Interrupt::ChangeInterrupt() { int newipl; - intmap &= ~GetIntmap(source); - // intmap の最下位ビットは常に立ててあり intmap は 0 にならない + // intmap は最下位ビットを常に立ててあるので 0 にならない newipl = (uint)(31 - __builtin_clz(intmap)) / 4; - // ネゲートによって割り込みレベルが下がったら、新しいレベルを通知 - // (以前に保留されていた低いレベルがあればここで見えるようになる) - if (newipl < ipl) { + // 割り込みレベルが変わったら、新しいレベルを通知 + if (newipl != ipl) { ipl = newipl; - gMPU->Interrupt(ipl); + mpu->Interrupt(ipl); } } @@ -139,8 +174,8 @@ M680x0Interrupt::NegateINT(Device *sourc // コンストラクタ X68030Interrupt::X68030Interrupt() { - monitor.func = (MonitorCallback_t)&X68030Interrupt::MonitorUpdate; - monitor.SetSize(24, 11); + monitor.func = ToMonitorCallback(&X68030Interrupt::MonitorUpdate); + monitor.SetSize(41, 13); monitor.Regist(ID_MONITOR_INTERRUPT); } @@ -149,27 +184,53 @@ X68030Interrupt::~X68030Interrupt() { } +// 初期化 +bool +X68030Interrupt::Init() +{ + if (inherited::Init() == false) { + return false; + } + + 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(Device *source) const +X68030Interrupt::GetIntmap(const Device *source) const { - if (__predict_true(source == gMFP.get())) { + if (__predict_true(source == mfp)) { return IntmapMFP; } - if (__predict_true(source == gSCC.get())) { + if (__predict_true(source == scc)) { return IntmapSCC; } - if (__predict_true(source == gDMAC.get())) { + if (__predict_true(source == net[0])) { + return IntmapNereid0; + } + if (__predict_true(source == net[1])) { + return IntmapNereid1; + } + if (__predict_true(source == dmac)) { return IntmapDMAC; } - if (__predict_true(source == gPEDEC.get())) { + if (__predict_true(source == pedec)) { return IntmapPEDEC; } - if (source == NULL) { - // XXX デバイスまだいないのでとりあえず + if (source == nmi) { return IntmapNMI; } - PANIC("Unknown interrupt source?"); + VMPANIC("Unknown interrupt source?"); } // 割り込みアクノリッジに応答する @@ -177,22 +238,33 @@ int X68030Interrupt::InterruptAcknowledge(int lv) { switch (lv) { + case 7: + return AutoVector; + case 6: - return gMFP->InterruptAcknowledge(); + return mfp->InterruptAcknowledge(); case 5: - return gSCC->InterruptAcknowledge(); + return scc->InterruptAcknowledge(); + + case 4: + for (int i = 0; i < 2; i++) { + if (net[i]) { + return net[i]->InterruptAcknowledge(); + } + } + break; case 3: - return gDMAC->InterruptAcknowledge(); + return dmac->InterruptAcknowledge(); case 1: - return gPEDEC->InterruptAcknowledge(lv); + return pedec->InterruptAcknowledge(lv); default: break; } - PANIC("Unknown interrupt acknowledge lv=%d", lv); + VMPANIC("Unknown interrupt acknowledge lv=%d", lv); } void @@ -203,54 +275,55 @@ X68030Interrupt::MonitorUpdate(Monitor * const char *name; uint32 map; } table[] = { - { "NMI", IntmapNMI }, - { "MFP", IntmapMFP }, - { "SCC", IntmapSCC }, - { "DMAC", IntmapDMAC }, - { "PEDEC", IntmapPEDEC }, - }, tablePEDEC[] = { - { "SPC", PEDECDevice::IntmapSPC }, - { "FDC", PEDECDevice::IntmapFDC }, - { "FDD", PEDECDevice::IntmapFDD }, - { "HDD", PEDECDevice::IntmapHDD }, - { "PRT", PEDECDevice::IntmapPRT }, + { "NMI", IntmapNMI }, + { "MFP", IntmapMFP }, + { "SCC", IntmapSCC }, + { "Nereid0", IntmapNereid0 }, + { "Nereid1", IntmapNereid1 }, + { "DMAC", IntmapDMAC }, + { "PEDEC", IntmapPEDEC }, }; // CPU の割り込み可否は本当は関係ないけど、便宜的に一緒に表示したい - uint intr_level = gMPU680x0->GetCPU()->reg.intr_level; - // PEDEC 分も一緒に表示したい - uint32 pintr = gPEDEC->GetInt(); + 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 Nereid0 01234567890123456789012345 + // SPC Mask + y = 0; - screen.Print(0, y++, "Lv Device Count IM=%d", intr_level); + 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; 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, "%10u", counter[clz]); - if (lv <= intr_level) { - screen.Puts(20, y, "Mask"); + 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); } - y++; - } - - // pintr は下位ワードが割り込み状態、上位ワードが許可状態。 - uint32 pmask = pintr >> 16; - for (int i = 0; i < countof(tablePEDEC); i++) { - uint32 map = tablePEDEC[i].map; - screen.Puts(3, y, TA::OnOff(pintr & map), tablePEDEC[i].name); - // XXX カウンタはまだない - if (intr_level >= 1 || (pmask & map) == 0) { - screen.Puts(20, y, "Mask"); + if (lv <= intr_mask) { + screen.Puts(11, y, "Mask"); } + screen.Print(15, y, "%26s", format_number(counter[clz]).c_str()); y++; } + + // PEDEC 分も一緒に表示したい + pedec->MonitorUpdate(screen, intr_mask, y); } + // // LUNA-I 仮想割り込みコントローラ // @@ -258,8 +331,8 @@ X68030Interrupt::MonitorUpdate(Monitor * // コンストラクタ LunaInterrupt::LunaInterrupt() { - monitor.func = (MonitorCallback_t)&LunaInterrupt::MonitorUpdate; - monitor.SetSize(24, 5); + monitor.func = ToMonitorCallback(&LunaInterrupt::MonitorUpdate); + monitor.SetSize(40, 8); monitor.Regist(ID_MONITOR_INTERRUPT); } @@ -268,24 +341,49 @@ LunaInterrupt::~LunaInterrupt() { } +// 初期化 +bool +LunaInterrupt::Init() +{ + if (inherited::Init() == false) { + return false; + } + + lance = GetLanceDevice(); + nmi = GetNMIDevice(); + sio = GetSIODevice(); + spc = GetSPCDevice(); + sysclk = GetSysClkDevice(); + + return true; +} // デバイスから Intmap 値を引く uint32 -LunaInterrupt::GetIntmap(Device *source) const +LunaInterrupt::GetIntmap(const Device *source) const { - if (__predict_true(source == gSIO.get())) { + if (__predict_true(source == sio)) { return IntmapSIO; } - if (__predict_true(source == gSysClk.get())) { + if (__predict_true(source == sysclk)) { return IntmapSysClk; } - if (__predict_true(source == gEthernet.get())) { + if (__predict_true(source == lance)) { return IntmapLance; } - if (__predict_true(source == gSPC.get())) { + if (__predict_true(source == spc)) { return IntmapSPC; } - PANIC("Unknown interrupt source?"); + 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?"); } // 割り込みアクノリッジに応答する @@ -304,29 +402,161 @@ LunaInterrupt::MonitorUpdate(Monitor *, const char *name; uint32 map; } table[] = { + { "NMI", IntmapNMI }, { "SIO", IntmapSIO }, { "Clock", IntmapSysClk }, + { "XP(Hi)", IntmapXPHigh }, { "Lance", IntmapLance }, { "SPC", IntmapSPC }, + { "XP(Lo)", IntmapXPLow }, }; // CPU の割り込み可否は本当は関係ないけど、便宜的に一緒に表示したい - uint intr_level = gMPU680x0->GetCPU()->reg.intr_level; + 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 XP(Hi) Mask01234567890123456789012345 + + y = 0; + 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; + 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); + 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.SetSize(40, 5); + monitor.Regist(ID_MONITOR_INTERRUPT); +} + +// デストラクタ +NewsInterrupt::~NewsInterrupt() +{ +} + +// 初期化 +bool +NewsInterrupt::Init() +{ + if (inherited::Init() == false) { + return false; + } + + lance = GetLanceDevice(); + newsctlr = GetNewsCtlrDevice(); + scc = GetSCCDevice(); + + return true; +} + +// デバイスから 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?"); +} + +// 割り込みアクノリッジに応答する +int +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 & IntmapSIC)) { + data |= 0x80; + } + if ((intmap & IntmapLance)) { + data |= 0x04; + } + + return data; +} + +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(); + + // 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_level); + 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; 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, "%10u", counter[clz]); - if (lv <= intr_level) { - screen.Puts(20, y, "Mask"); + screen.Print(3, y, TA::OnOff(intmap & map), "%s", table[i].name); + if (lv <= intr_mask) { + screen.Puts(10, y, "Mask"); } + screen.Print(14, y, "%26s", format_number(counter[clz]).c_str()); y++; } }