--- nono/vm/interrupt.cpp 2026/04/29 17:04:45 1.1 +++ nono/vm/interrupt.cpp 2026/04/29 17:05:24 1.1.1.9 @@ -4,45 +4,69 @@ // Licensed under nono-license.txt // +// +// 割り込みコントローラ +// + +// IODevice +// | +// | +-----------------+ +// +--| InterruptDevice | (割り込みコントローラの共通部) +// +-----------------+ +// | +-----------------+ +// +--| M680x0Interrupt | (m68k 割り込みコントローラの共通部) +// | +-----------------+ +// | | +// | | +-----------------+ +// | +--| X68030Interrupt | (X68030 の割り込みコントローラ) +// | | +-----------------+ +// | | +// | | +---------------+ +// | +--| 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" #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() + : InterruptDevice(OBJ_INTERRUPT) // 移譲 +{ +} + +// コンストラクタ(オブジェクト名指定あり) +InterruptDevice::InterruptDevice(int objid_) + : inherited(objid_) { - logname = "interrupt"; - devname = "Interrupt"; } // デストラクタ @@ -50,79 +74,153 @@ InterruptDevice::~InterruptDevice() { } - -// -// 仮想割り込みコントローラ (m680x0 共通部) -// - -// CPU コアからの割り込みアクノリッジを受け付けるグローバル関数 -int -m68030_interrupt_acknowledge(int lv) +// リセット +void +InterruptDevice::ResetHard(bool poweron) { - return gInterrupt->InterruptAcknowledge(lv); + memset(&counter, 0, sizeof(counter)); } -// コンストラクタ -M680x0Interrupt::M680x0Interrupt() +// 子デバイスを登録する。 +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_; } -// デストラクタ -M680x0Interrupt::~M680x0Interrupt() +// デバイスから 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?"); } -// リセット -void -M680x0Interrupt::ResetHard() +// Intmap 値から名前を引く +const char * +InterruptDevice::GetIntName(uint32 intmap_) const { - ipl = 0; - intmap = 0x00000001; - memset(&counter, 0, sizeof(counter)); + for (const auto& m : intmap_list) { + if (m.intmap == intmap_) { + return m.name; + } + } + + return "???"; } -// 子デバイスが割り込み信号線をアサートした (m680x0 共通) +// 子デバイスが割り込み信号線をアサートした void -M680x0Interrupt::AssertINT(Device *source) +InterruptDevice::AssertINT(Device *source) { uint32 oldmap; uint32 srcmap; - int newipl; - 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(); } +} - 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_status; + srcmap = GetIntmap(source); + intmap_status &= ~srcmap; + + if ((oldmap ^ intmap_status) != 0) { + ChangeInterrupt(); } } -// 小デバイスが割り込み信号線をネゲートした (m680x0 共通) +// 割り込み信号線の状態を取得 +bool +InterruptDevice::GetINT(const Device *source) const +{ + uint32 srcmap; + + srcmap = GetIntmap(source); + + return (intmap_status & srcmap); +} + + +// +// 仮想割り込みコントローラ (m680x0 共通部) +// + +// コンストラクタ +M680x0Interrupt::M680x0Interrupt() +{ + intmap_status = IntmapSentinel; + // m680x0 仮想割り込みコントローラにはマスクがないので使わない。 + // 念のためそれっぽい値を入れておく。 + intmap_enable = 0xfffffff0; +} + +// デストラクタ +M680x0Interrupt::~M680x0Interrupt() +{ +} + +// 初期化 +bool +M680x0Interrupt::Init() +{ + if (inherited::Init() == false) { + return false; + } + + mpu680x0 = GetMPU680x0Device(mpu); + + return true; +} + +// リセット void -M680x0Interrupt::NegateINT(Device *source) +M680x0Interrupt::ResetHard(bool poweron) +{ + inherited::ResetHard(poweron); +} + +void +M680x0Interrupt::ChangeInterrupt() { int newipl; - intmap &= ~GetIntmap(source); - // intmap の最下位ビットは常に立ててあり intmap は 0 にならない - newipl = (uint)(31 - __builtin_clz(intmap)) / 4; - - // ネゲートによって割り込みレベルが下がったら、新しいレベルを通知 - // (以前に保留されていた低いレベルがあればここで見えるようになる) - if (newipl < ipl) { + // intmap_status は最下位ビットを常に立ててあるので 0 にならない + newipl = (uint)(31 - __builtin_clz(intmap_status)) / 4; + + // 割り込みレベルが変わったら、新しいレベルを通知 + if (newipl != ipl) { ipl = newipl; - gMPU->Interrupt(ipl); + mpu->Interrupt(ipl); } } @@ -134,7 +232,8 @@ M680x0Interrupt::NegateINT(Device *sourc // コンストラクタ X68030Interrupt::X68030Interrupt() { - monitor_size = nnSize(24, 6); + monitor.func = ToMonitorCallback(&X68030Interrupt::MonitorUpdate); + monitor.Regist(ID_MONITOR_INTERRUPT); } // デストラクタ @@ -142,89 +241,130 @@ X68030Interrupt::~X68030Interrupt() { } -// デバイスから Intmap 値を引く -uint32 -X68030Interrupt::GetIntmap(Device *source) const -{ - if (__predict_true(source == gMFP.get())) { - return IntmapMFP; - } - if (__predict_true(source == gSCC.get())) { - return IntmapSCC; - } - if (__predict_true(source == gDMAC.get())) { - return IntmapDMAC; +// 初期化 +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 == gPEDEC.get())) { - return IntmapPEDEC; - } - if (source == NULL) { - // XXX デバイスまだいないのでとりあえず - return IntmapNMI; - } - PANIC("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) { + case 7: + 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; } - PANIC("Unknown interrupt acknowledge lv=%d", lv); + + return busdata::BusErr; } void -X68030Interrupt::MonitorUpdate(TextScreen& monitor) +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_level = gMPU680x0->GetCPU()->reg.intr_level; + uint intr_mask = mpu680x0->reg.intr_mask; + + screen.Clear(); - monitor.Clear(); + // 0 1 2 3 4 + // 01234567890123456789012345678901234567890 + // Lv Device IM=7 Count + // 1 Nereid0 01234567890123456789012345 + // SPC Mask y = 0; - monitor.Print(0, y++, "Lv Device Count IM=%d", intr_level); - for (int i = 0; i < countof(table); i++) { - uint32 map = table[i].map; + screen.Print(0, y, "Lv Device IM=%d", intr_mask); + screen.Puts(36, y, "Count"); + y++; + for (uint32 map : disp_list) { 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.Puts(3, y, TA::OnOff(intmap_status & map), GetIntName(map)); + if (lv <= intr_mask) { + screen.Puts(11, y, "Mask"); } + screen.Print(15, y, "%26s", format_number(counter[clz]).c_str()); y++; } - // XXX PEDEC 分もここに表示したい + + // PEDEC 分も一緒に表示したい + pedec->MonitorUpdate(screen, intr_mask, y); } + // // LUNA-I 仮想割り込みコントローラ // @@ -232,7 +372,8 @@ X68030Interrupt::MonitorUpdate(TextScree // コンストラクタ LunaInterrupt::LunaInterrupt() { - monitor_size = nnSize(24, 5); + monitor.func = ToMonitorCallback(&LunaInterrupt::MonitorUpdate); + monitor.Regist(ID_MONITOR_INTERRUPT); } // デストラクタ @@ -240,65 +381,310 @@ LunaInterrupt::~LunaInterrupt() { } +// 初期化 +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()); -// デバイスから Intmap 値を引く -uint32 -LunaInterrupt::GetIntmap(Device *source) const + return true; +} + +// 割り込みアクノリッジに応答する +busdata +LunaInterrupt::InterruptAcknowledge(int lv) { - if (__predict_true(source == gSIO.get())) { - return IntmapSIO; + // LUNA は全てオートベクタ + return AutoVector; +} + +void +LunaInterrupt::MonitorUpdate(Monitor *, TextScreen& screen) +{ + int y; + + // 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=%d", 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.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++; } - if (__predict_true(source == gSysClk.get())) { - return IntmapSysClk; +} + + +// +// NEWS 仮想割り込みコントローラ +// + +// コンストラクタ +NewsInterrupt::NewsInterrupt() +{ + monitor.func = ToMonitorCallback(&NewsInterrupt::MonitorUpdate); + monitor.Regist(ID_MONITOR_INTERRUPT); +} + +// デストラクタ +NewsInterrupt::~NewsInterrupt() +{ +} + +// 初期化 +bool +NewsInterrupt::Init() +{ + if (inherited::Init() == false) { + return false; } - if (__predict_true(source == gEthernet.get())) { - return IntmapLance; + + // 割り込みアクノリッジで使う + 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; } - if (__predict_true(source == gSPC.get())) { - return IntmapSPC; +} + +// ステータスバイトを読み出す。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 = 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 (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"); + } + screen.Print(14, y, "%26s", format_number(counter[clz]).c_str()); + y++; } - PANIC("Unknown interrupt source?"); +} + + +// +// 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); } // 割り込みアクノリッジに応答する -int -LunaInterrupt::InterruptAcknowledge(int lv) +busdata +Virt68kInterrupt::InterruptAcknowledge(int lv) { - // LUNA は全てオートベクタ + // ? return AutoVector; } void -LunaInterrupt::MonitorUpdate(TextScreen& monitor) +Virt68kInterrupt::MonitorUpdate(Monitor *, TextScreen& screen) { int y; - struct { - const char *name; - uint32 map; - } table[] = { - { "SIO", IntmapSIO }, - { "Clock", IntmapSysClk }, - { "Lance", IntmapLance }, - { "SPC", IntmapSPC }, - }; // CPU の割り込み可否は本当は関係ないけど、便宜的に一緒に表示したい - uint intr_level = gMPU680x0->GetCPU()->reg.intr_level; + 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; + } - monitor.Clear(); + 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; - monitor.Print(0, y++, "Lv Device Count IM=%d", intr_level); - for (int i = 0; i < countof(table); i++) { - uint32 map = table[i].map; + 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; - 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.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); + } }