--- nono/vm/interrupt.h 2026/04/29 17:04:55 1.1.1.2 +++ nono/vm/interrupt.h 2026/04/29 17:05:33 1.1.1.9 @@ -4,37 +4,67 @@ // Licensed under nono-license.txt // +// +// 割り込みコントローラ +// + #pragma once #include "device.h" -#include "monitor.h" +#include +#include -// -// 割り込みを受け付けるデバイス +// XP からの割り込み用。 +// これだけ1デバイスで2本あるので、仕方なく識別子を用意。 +#define DEVICE_XPINT_HIGH (reinterpret_cast((intptr_t)1)) +#define DEVICE_XPINT_LOW (reinterpret_cast((intptr_t)2)) + +class DMACDevice; +class GFPICDevice; +class MFPDevice; +class MPU680x0Device; +class NMIDevice; +class PEDECDevice; +class RTL8019ASDevice; +class SCCDevice; +class SPCDevice; + +// 割り込みを受け付けるデバイス。 // これ自体は IODevice である必要はないが、PEDEC や SysCtlr がこれを // 継承するため。 -// class InterruptDevice : public IODevice { using inherited = IODevice; + + // intmap と名前の紐付け + struct mapname { + uint32 intmap {}; + const char *name {}; + Device *source {}; + + mapname(uint32 intmap_, const char *name_, Device *source_) { + intmap = intmap_; + name = name_; + source = source_; + } + }; + public: InterruptDevice(); - InterruptDevice(const std::string& objname_); - virtual ~InterruptDevice() override; + InterruptDevice(uint objid_); + ~InterruptDevice() override; - // 継承クラスの ResetHard() は保持している割り込み情報を全てキャンセルする。 - // 各子デバイスはその ResetHard() で NegateINT() を明示的に呼び出して - // 割り込みをキャンセルする必要はなく、もし自身で独自に割り込み信号線の - // 状態に関する変数を持っていればそれをクリアするだけでよい。 + // リセットで割り込みを下げるのは各割り込み送出デバイスの責任。 + void ResetHard(bool poweron) override; // AssertINT()、NegateINT()、ChangeINT() の呼び出し側は、 // (アサート中に AssertINT() を呼ぶというような) 現在の状態が // 変わらないような呼び出しをしても構わない。特に副作用はない。 // 子デバイスが割り込み信号線をアサートした - virtual void AssertINT(Device *source) = 0; + void AssertINT(Device *source); // 子デバイスが割り込み信号線をネゲートした - virtual void NegateINT(Device *source) = 0; + void NegateINT(Device *source); // 子デバイスが割り込み信号線を val の状態に変えた。 // true ならアサート、false ならネゲート。 @@ -47,32 +77,29 @@ class InterruptDevice : public IODevice } // MPU からの割り込みアクノリッジに応答する - virtual int InterruptAcknowledge(int lv) = 0; -}; - -// -// m680x0 仮想割り込みコントローラ (共通部) -// -class M680x0Interrupt : public InterruptDevice -{ - using inherited = InterruptDevice; - public: - static const int AutoVector = -1; // オートベクタ + virtual busdata InterruptAcknowledge(int lv) = 0; - public: - M680x0Interrupt(); - virtual ~M680x0Interrupt() override; - - void ResetHard() override; - - void AssertINT(Device *source) override; - void NegateINT(Device *source) override; + // 割り込み信号線の状態を取得する + bool GetINT(const Device *source) const; protected: - // Intmap は 32bit のビットマップ値で、値は機種ごとに定義する。 - // 1つの割り込みレベルにつき 4つまでデバイスをマッピングでき、また、 - // 同一レベル内での複数のデバイスの割り込み信号線の状態を独立して保持 - // できるようにするため。 + // 割り込みを上位デバイス(MPUなど)に伝達する。 + // 子デバイスが割り込み信号線の状態を変えたときに呼び出される。 + virtual void ChangeInterrupt() = 0; + + // Intmap について + // + // Intmap は 32bit のビットマップ値で、最大 31 個(といいつつ今の所 28 個) + // の割り込み発生の有無を個別に管理できる。 + // 発生している割り込みのうち最も優先度が高い 1つを高速に検索するため、 + // MSB 側を最も優先度が高い割り込みとして、Count-Leading-Zero で検索 + // することにする。 + // __builtin_clz(0) は動作未定義になるので、最下位ビットを常に立てて + // おくことで、割り込みなし(上位31bitがすべて0)の場合でもこの式が使える + // ようにしておく。 + // + // 具体的にどの割り込みをどのビットに割り当てるかは継承先の実装によるが、 + // m680x0 の場合、以下のように 1レベル 4つずつ割り当てることにする。 // // 3 2 1 0 // 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 @@ -80,30 +107,81 @@ class M680x0Interrupt : public Interrupt // |7 7 7 7|6 6 6 6|5 5 5 5|4 4 4 4|3 3 3 3|2 2 2 2|1 1 1 1| %0001 | // +-------+-------+-------+-------+-------+-------+-------+-------+ // - // bit31-4 でレベル 7-1 の割り込みを表す。 - // アサートされている信号線のうち最もレベルの高いものは以下で求まる。 - // ipl = (31 - clz(intmap)) / 4 - // 例えば bit30 と bit20 が立っている場合 (これは割り込みレベル 7 と 5 が - // 同時に要求されている状態)、最高レベルは (31 - 1) / 4 = 7 となる。 - // また __builtin_clz(0) は未定義なので使わない最下位ビットを常に立てて - // おくことで、割り込みなし(上位28bitがすべて0)の場合でもこの式が使える。 + // これにより同一レベルのデバイス 4 つまでの割り込み信号線の状態を独立 + // して保持しつつ、アサートされている信号線のうち最もレベルの高いものは + // 以下だけで求めることが出来る。 + // + // ipl = (31 - __builtin_clz(intmap_status)) / 4; + // + // LUNA-88K の外部割り込みコントローラであるシステムコントローラも + // 概ねこれに近いので、sysctlr もこの構造を使う。 + // + // PEDEC は 7レベル分けする必要ないので、適当に割り当ててある。 + // + // Goldfish PIC は 7レベル分け不要なのと 32ビット必要そうなので、 + // 番兵を使わず 32ビット全域を使う。 + // また元の仕様が LSB 側から数えるスタイルなのでここでもそれに従う。 + + static const uint32 IntmapSentinel = 0x00000001; + + // 割り込みソースを登録する。 + void RegistINT(uint32 intmap_, const char *name_, Device *source_); // デバイスから Intmap 値を引く。 - virtual uint32 GetIntmap(Device *source) const = 0; + uint32 GetIntmap(const Device *source) const; - // 現在の割り込みレベル - int ipl {}; + // Intmap 値から名前を引く。 + const char *GetIntName(uint32 intmap_) const; + + // 割り込みソース一覧 (概ね使用頻度の高い順) + std::vector intmap_list {}; + + // モニタでの表示順 (概ね優先度の高い順) + std::vector disp_list {}; + + // 割り込みソースが存在するところは %1 + uint32 intmap_avail {}; - // 割り込みマップ - uint32 intmap {}; + // 割り込み状態 (各 %1 ならアサート) + uint32 intmap_status {}; + + // (必要なら) 割り込みマスク (各 %1 なら有効) + // 割り込みマスク機能を持ってない人は使わなくてもよい。 + uint32 intmap_enable {}; // 割り込み回数 (アサートのエッジでカウントアップする) // [0] が Intmap の bit31 割り込みのカウント。 // [27] が Intmap の bit 4 割り込みのカウント。 - uint32 counter[28] {}; + uint64 counter[32] {}; + + Monitor *monitor {}; +}; + +// m680x0 仮想割り込みコントローラ (共通部) +class M680x0Interrupt : public InterruptDevice +{ + using inherited = InterruptDevice; + + public: + // 割り込みアクノリッジの特殊値。 + static const uint32 AutoVector = -1; // オートベクタ要求 + + public: + M680x0Interrupt(); + ~M680x0Interrupt() override; + + bool Init() override; + void ResetHard(bool poweron) override; + + protected: + void ChangeInterrupt() override; + + // 現在の割り込みレベル + int ipl {}; + + MPU680x0Device *mpu680x0 {}; }; -// // X68k 仮想割り込みコントローラ // // Level @@ -127,24 +205,29 @@ class X68030Interrupt : public M680x0Int static const uint32 IntmapNMI = 0x80000000; static const uint32 IntmapMFP = 0x08000000; static const uint32 IntmapSCC = 0x00800000; + static const uint32 IntmapNereid0 = 0x00080000; + static const uint32 IntmapNereid1 = 0x00040000; static const uint32 IntmapDMAC = 0x00008000; static const uint32 IntmapPEDEC = 0x00000080; public: X68030Interrupt(); - virtual ~X68030Interrupt() override; + ~X68030Interrupt() override; - int InterruptAcknowledge(int lv) override; + bool Init() override; + busdata InterruptAcknowledge(int lv) override; private: - uint32 GetIntmap(Device *soruce) const override; - DECLARE_MONITOR_CALLBACK(MonitorUpdate); - Monitor monitor { this }; + DMACDevice *dmac {}; + MFPDevice *mfp {}; + NMIDevice *nmi {}; + PEDECDevice *pedec {}; + std::array net {}; + SCCDevice *scc {}; }; -// // LUNA-I 仮想割り込みコントローラ // // Level @@ -161,25 +244,104 @@ class LunaInterrupt : public M680x0Inter using inherited = M680x0Interrupt; // 割り込みマップ 76543210 + static const uint32 IntmapNMI = 0x80000000; static const uint32 IntmapSIO = 0x08000000; static const uint32 IntmapSysClk = 0x00800000; + static const uint32 IntmapXPHigh = 0x00400000; static const uint32 IntmapLance = 0x00008000; static const uint32 IntmapSPC = 0x00000800; + static const uint32 IntmapXPLow = 0x00000080; public: LunaInterrupt(); - virtual ~LunaInterrupt() override; + ~LunaInterrupt() override; + + bool Init() override; + busdata InterruptAcknowledge(int lv) override; + + private: + DECLARE_MONITOR_CALLBACK(MonitorUpdate); +}; + +// NEWS 仮想割り込みコントローラ +// +// Level +// 7 +// 6 Timer +// 5 SCC(vectored) +// 4 SIC, Lance +// 3 +// 2 +// 1 +// +class NewsInterrupt : public M680x0Interrupt +{ + using inherited = M680x0Interrupt; - int InterruptAcknowledge(int lv) override; + // 割り込みマップ 76543210 + static const uint32 IntmapNMI = 0x80000000; + static const uint32 IntmapTimer = 0x08000000; + static const uint32 IntmapSCC = 0x00800000; + static const uint32 IntmapSIC = 0x00080000; + static const uint32 IntmapLance = 0x00040000; + + public: + NewsInterrupt(); + ~NewsInterrupt() override; + + bool Init() override; + busdata InterruptAcknowledge(int lv) override; + + // 割り込みステータスを返す (NewsCtlr から呼ばれる) + uint8 PeekStatus() const; private: - uint32 GetIntmap(Device *source) const override; + DECLARE_MONITOR_CALLBACK(MonitorUpdate); + + SCCDevice *scc {}; +}; + +// virt-m68k 仮想割り込みコントローラ +// +// Level +// 7 +// 6 GFPIC6 +// 5 GFPIC5 +// 4 GFPIC4 +// 3 GFPIC3 +// 2 GFPIC2 +// 1 GFPIC1 +// +class Virt68kInterrupt : public M680x0Interrupt +{ + using inherited = M680x0Interrupt; + + // 割り込みマップ 76543210 + static const uint32 IntmapGFPIC6 = 0x08000000; + static const uint32 IntmapGFPIC5 = 0x00800000; + static const uint32 IntmapGFPIC4 = 0x00080000; + static const uint32 IntmapGFPIC3 = 0x00008000; + static const uint32 IntmapGFPIC2 = 0x00000800; + static const uint32 IntmapGFPIC1 = 0x00000080; + + public: + Virt68kInterrupt(); + ~Virt68kInterrupt() override; + bool Init() override; + busdata InterruptAcknowledge(int lv) override; + + // モニタを1行増やす。割り込みソース登録ごとに GFPIC から呼ばれる。 + void IncMonitorHeight(); + + private: DECLARE_MONITOR_CALLBACK(MonitorUpdate); - Monitor monitor { this }; + std::array gfpic {}; }; -// LUNA88K は sysctlr.cpp 参照。 +// LUNA-88K は sysctlr.cpp 参照。 -extern std::unique_ptr gInterrupt; +static inline InterruptDevice *GetInterruptDevice() { + return Object::GetObject(OBJ_INTERRUPT); +}