--- nono/vm/sysctlr.cpp 2026/04/29 17:04:36 1.1.1.1 +++ nono/vm/sysctlr.cpp 2026/04/29 17:04:56 1.1.1.5 @@ -7,6 +7,11 @@ // LUNA88K のシステムコントローラのような何か #include "sysctlr.h" +#include "ethernet.h" +#include "mpu88xx0.h" +#include "sio.h" +#include "spc.h" +#include "sysclk.h" // この辺を全部一つのデバイスで受け付けておくか。BusIO はまた後で考える。 // $6500000n.b HW 割り込みレベル (n*4 = CPU#n) @@ -18,14 +23,6 @@ // $6d00000n CPU#n をリセット(?) // $6d000010 全 CPU をリセット -// intmask[] について: -// I/O ポート 0x6500_00xx での設定値は、 -// bit 31 30 29 28 27 26 25... -// lvl 6 5 4 3 2 1 (not used) -// の対応になっている。intmask では 25bit 右シフトして、 -// ビット番号が割り込みレベルに対応するようにマップした。 - - // LUNA88K // 割り込みコントロールレジスタ // $6500_000n @@ -93,12 +90,13 @@ // +-------------------------+ // |プライオリティエンコーダ |-> (R:bit31-29) // +-------------------------+ -// (入力のWired-OR) -// | -// [M88K INT] +// | | | +// V V V +// (Wired-OR) +// | +// [M88K INT] // -// LUNA-I とはレベル配置が違う。1 レベルのズレに加え、シリアルが -// 6 -> 5 に変更されているようだ? +// LUNA-I とはレベル配置が違う。 // // 割り込みソース調査状況 // 1:SOFTINT レジスタ $6900000n @@ -146,16 +144,15 @@ // 要はアドレスバスとR/W信号線しか見ていない。 -std::unique_ptr gSysCtlr; - - // コンストラクタ SysCtlrDevice::SysCtlrDevice() + : inherited("SystemCtlr") { - logname = "sysctlr"; - devname = "SysCtlr"; + AddAlias("SysCtlr"); - monitor.Init(60, 5); + monitor.func = (MonitorCallback_t)&SysCtlrDevice::MonitorUpdate; + monitor.SetSize(24, 10); + monitor.Regist(ID_MONITOR_INTERRUPT); } // デストラクタ @@ -163,47 +160,47 @@ SysCtlrDevice::~SysCtlrDevice() { } +void +SysCtlrDevice::ResetHard() +{ + // さすがにリセットくらいはされるだろう… + intmap = 0x00000001; + memset(&counter, 0, sizeof(counter)); + for (int i = 0; i < 4; i++) { + // XXX 初期値は分からないがとりあえず割り込み禁止にしておく + cpu[i].maskval = 0xfc000000; + cpu[i].intmask = MakeIntMask(cpu[i].maskval, i); + cpu[i].ipl = 0; + } +} + uint64 SysCtlrDevice::Read32(uint32 addr) { - int n = (addr & 15) >> 2; + int n = (addr >> 2) & 3; - // 読み込みでは割り込みレベルを返すようだ - // bit 31..29: アクティブな割り込みのレベル - // 24 level7 割り込みが発生したら 1 - // 23 level6 .. - // 22 level5 - // 21 level4 - // 20 level3 - // 19 level2 - // 18 level1 - // XXX: もう資料がないとわからない if (0x65000000 <= addr && addr < 0x65000010) { - int maxlev = 0; - // 7 to 1 - for (int i = 7; i > 0; i--) { - if (intlevel[n] & (1 << i)) { - maxlev = i; - break; - } - } - - uint32 data = (maxlev << 29) | (intlevel[n] << 17); + // bit31-29 に発生している最上位の割り込みレベル + // bit23-18 に設定したレベル 6-1 の割り込みマスク + uint32 data = (cpu[n].ipl << 29) | (cpu[n].maskval >> 8); putlog(2, "割り込み保持読み出し(CPU#%d) -> $%08x", n, data); return data; } if (0x69000000 <= addr && addr < 0x69000010) { - putlog(1, "ソフトウェア割り込み(CPU#%d)クリア(未実装)", n); + // 読み出しで SOFTINT ネゲート (値は関係ない) + putlog(1, "Softint(CPU#%d) Negate", n); + intmap &= ~(IntmapSoft0 << n); + ChangeInterrupt(); return 0xffffffff; } if (0x6d000000 <= addr && addr < 0x6d000010) { - putlog(1, "CPU%d リセット 未実装読み込み", n); + putlog(0, "CPU%d リセット 未実装読み込み", n); return 0xffffffff; } if (addr == 0x6d000010) { - putlog(1, "CPU ALL リセット 未実装読み込み"); + putlog(0, "CPU ALL リセット 未実装読み込み"); return 0xffffffff; } @@ -217,19 +214,28 @@ SysCtlrDevice::Write32(uint32 addr, uint int n = (addr & 15) >> 2; if (0x65000000 <= addr && addr < 0x65000010) { - intmask[n] = data >> 25; - - // XXX: クリアしてみる - intlevel[n] = 0; + data &= 0xfc000000; + // 割り込みレベル変えるたびに表示されるのですごく多い。 + // さらに同レベルが書き込まれることも多い…。 + if (cpu[n].maskval != data) { + putlog(2, "割り込みマスク(CPU#%d) <- $%08x", n, data); + } else { + putlog(3, "割り込みマスク(CPU#%d) <- $%08x", n, data); + } - putlog(1, "割り込みマスク(CPU#%d) <- $%08x", n, intmask[n] << 25); + // 設定値を保存 + cpu[n].maskval = data; + // 内部マスクを書き換えて割り込み状態を更新 + cpu[n].intmask = MakeIntMask(data, n); + ChangeInterrupt(); return 0; } if (0x69000000 <= addr && addr < 0x69000010) { - // 詳細不明。0xFFFFFFFF が書き込まれている、ようだ。 - intlevel[n] &= ~data; - // bit 表示したい - putlog(0, "割り込み(CPU#%d)クリア 0x%x 残り=0x%x", n, data, intlevel[n]); + // 書き込みで SOFTINT アサート (値は関係ない) + putlog(1, "Softint(CPU#%d) Assert 0x%x", n, data); + counter[__builtin_clz(intmap)]++; + intmap |= IntmapSoft0 << n; + ChangeInterrupt(); return 0; } if (0x6d000000 <= addr && addr < 0x6d000010) { @@ -237,7 +243,8 @@ SysCtlrDevice::Write32(uint32 addr, uint return 0; } if (addr == 0x6d000010) { - putlog(0, "CPU ALL リセット 未実装書き込み"); + putlog(1, "Reset All CPUs"); + gMPU->ResetHard(); return 0; } @@ -248,11 +255,11 @@ SysCtlrDevice::Write32(uint32 addr, uint uint64 SysCtlrDevice::Peek8(uint32 addr) { + int n = (addr >> 2) & 3; uint32 data; - int n = (addr & 15) >> 2; if (0x65000000 <= addr && addr < 0x65000010) { - data = intmask[n] << 25; + data = (cpu[n].ipl << 29) | (cpu[n].maskval >> 8); } else { data = 0xffffffff; } @@ -271,14 +278,154 @@ SysCtlrDevice::Peek8(uint32 addr) __unreachable(); } -bool -SysCtlrDevice::MonitorUpdate() +void +SysCtlrDevice::MonitorUpdate(Monitor *, TextScreen& screen) { - monitor.Clear(); - monitor.Puts(6, 0, "IntrEnable SWIntr"); - for (int i = 0; i < countof(intmask); i++) { - monitor.Print(0, i + 1, "CPU#%d $%02x", i, intmask[i]); + int y; + struct { + const char *name; + uint32 map; + } table[] = { + { "NMI", IntmapNMI }, + { "Clock", IntmapSysClk }, + { "SIO", IntmapSIO }, + { "Lance", IntmapLance }, + { "SPC", IntmapSPC }, + { "Soft#0", IntmapSoft0 }, + { "Soft#1", IntmapSoft1 }, + { "Soft#2", IntmapSoft2 }, + { "Soft#3", IntmapSoft3 }, + }; + + // CPU の割り込み可否は本当は関係ないけど、便宜的に一緒に表示したい + bool ei = gMPU88xx0->GetCPU()->IsIntrEnable(); + + screen.Clear(); + y = 0; + // XXX CPU の実装数によってウィンドウ幅を変える。 + // はみ出したところは描画されないのでヘッダは4つ分出力しておく + screen.Print(0, y++, "Lv Device Count CPU0 CPU1 CPU2 CPU3"); + 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]); + + // CPU ごとのマスク + // XXX 今は CPU は1つしかない + for (int n = 0; n < 1; n++) { + // Softint の他 CPU 分は + // 動作としては常にマスクされてて通過しないから問題ないが、 + // 見せる分にはマスクされているではなく無効と見せたほうがいい。 + if (lv == 1 && map != (IntmapSoft0 << n)) { + screen.Puts(20 + n * 5, y, "----"); + } else if ((cpu[n].intmask & map) == 0) { + screen.Puts(20 + n * 5, y, "Mask"); + } else if (!ei) { + screen.Puts(20 + n * 5, y, "IND"); + } + } + + y++; } +} - return true; +// デバイスから Intmap 値を引く +uint32 +SysCtlrDevice::GetIntmap(Device *source) const +{ + if (__predict_true(source == gSysClk.get())) { + return IntmapSysClk; + } + if (__predict_true(source == gSIO.get())) { + return IntmapSIO; + } + if (__predict_true(source == gEthernet.get())) { + return IntmapLance; + } + if (__predict_true(source == gSPC.get())) { + return IntmapSPC; + } + PANIC("Unknown interrupt source?"); } + +// 外部デバイスが割り込み信号線をアサートした +void +SysCtlrDevice::AssertINT(Device *source) +{ + uint32 oldmap; + uint32 srcmap; + + oldmap = intmap; + srcmap = GetIntmap(source); + intmap |= srcmap; + + if ((oldmap ^ intmap) != 0) { + // 立ち上がりエッジでカウント + counter[__builtin_clz(srcmap)]++; + } + + ChangeInterrupt(); +} + +// 外部デバイスが割り込み信号線をネゲートした +void +SysCtlrDevice::NegateINT(Device *source) +{ + intmap &= ~GetIntmap(source); + + ChangeInterrupt(); +} + +// 割り込みアクノリッジはない +int +SysCtlrDevice::InterruptAcknowledge(int lv) +{ + return 0; +} + +// 割り込み状態を更新 +void +SysCtlrDevice::ChangeInterrupt() +{ + // XXX MPU 側がまだマルチプロセッサに対応してないので CPU#0 だけ + for (int i = 0; i < 1; i++) { + uint32 localmap = intmap & cpu[i].intmask; + + // intmap の最下位ビットは常に立ててあり localmap は 0 にならない + int newipl = (uint)(31 - __builtin_clz(localmap)) / 4; + + // アサートによって割り込みレベルが変わったら、新しいレベルを通知 + if (newipl != cpu[i].ipl) { + cpu[i].ipl = newipl; + // MPU 側は 1 レベルしかない + gMPU->Interrupt(cpu[i].ipl ? 1 : 0); + } + } +} + +// 割り込みマスク設定レジスタの書き込み値から内部用のマスクを作成 +// data は bit31-26 のみ使用。他のビットは不問。 +// n は CPU# (Softint 用)。 +/*static*/ uint32 +SysCtlrDevice::MakeIntMask(uint32 data, int n) +{ + uint32 val = 0xf0000001; // Lv7 は NMI、最下位は番兵用 + + uint32 mask = 0x0f000000; + for (int i = 6; i >= 1; i--) { + if ((int32)data < 0) { + val |= mask; + } + mask >>= 4; + data <<= 1; + } + + // Softint は他 CPU 分のビットを下ろす + val &= (0xffffff0f | (IntmapSoft0 << n)); + + return val; +} +