--- nono/vm/interrupt.h 2026/04/29 17:04:45 1.1.1.1 +++ nono/vm/interrupt.h 2026/04/29 17:05:10 1.1.1.4 @@ -4,35 +4,40 @@ // Licensed under nono-license.txt // +// +// 割り込みコントローラ +// + #pragma once #include "device.h" +#include "monitor.h" -// -// 割り込みを受け付けるデバイス +// 割り込みを受け付けるデバイス。 // これ自体は IODevice である必要はないが、PEDEC や SysCtlr がこれを // 継承するため。 -// class InterruptDevice : public IODevice { using inherited = IODevice; public: InterruptDevice(); + InterruptDevice(const std::string& objname_); virtual ~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 ならネゲート。 @@ -46,31 +51,25 @@ 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; // オートベクタ - - public: - M680x0Interrupt(); - virtual ~M680x0Interrupt() override; - - void ResetHard() override; - - void AssertINT(Device *source) override; - void NegateINT(Device *source) override; 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 @@ -78,30 +77,51 @@ 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)) / 4; + // + // LUNA88K の外部割り込みコントローラであるシステムコントローラも + // 概ねこれに近いので、sysctlr もこの構造を使う。 + // + // PEDEC は 7レベル分けする必要ないので、適当に割り当ててある。 + + static const uint32 IntmapSentinel = 0x00000001; // デバイスから Intmap 値を引く。 virtual uint32 GetIntmap(Device *source) const = 0; - // 現在の割り込みレベル - int ipl {}; - // 割り込みマップ uint32 intmap {}; // 割り込み回数 (アサートのエッジでカウントアップする) // [0] が Intmap の bit31 割り込みのカウント。 // [27] が Intmap の bit 4 割り込みのカウント。 - uint32 counter[28] {}; + uint64 counter[28] {}; +}; + +// m680x0 仮想割り込みコントローラ (共通部) +class M680x0Interrupt : public InterruptDevice +{ + using inherited = InterruptDevice; + public: + static const int AutoVector = -1; // オートベクタ + + public: + M680x0Interrupt(); + virtual ~M680x0Interrupt() override; + + void ResetHard(bool poweron) override; + + protected: + void ChangeInterrupt() override; + + // 現在の割り込みレベル + int ipl {}; }; -// // X68k 仮想割り込みコントローラ // // Level @@ -132,15 +152,16 @@ class X68030Interrupt : public M680x0Int X68030Interrupt(); virtual ~X68030Interrupt() override; - void MonitorUpdate(TextScreen& monitor) override; - int InterruptAcknowledge(int lv) override; private: uint32 GetIntmap(Device *soruce) const override; + + DECLARE_MONITOR_CALLBACK(MonitorUpdate); + + Monitor monitor { this }; }; -// // LUNA-I 仮想割り込みコントローラ // // Level @@ -157,6 +178,7 @@ 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 IntmapLance = 0x00008000; @@ -166,14 +188,16 @@ class LunaInterrupt : public M680x0Inter LunaInterrupt(); virtual ~LunaInterrupt() override; - void MonitorUpdate(TextScreen& monitor) override; - int InterruptAcknowledge(int lv) override; private: uint32 GetIntmap(Device *source) const override; + + DECLARE_MONITOR_CALLBACK(MonitorUpdate); + + Monitor monitor { this }; }; // LUNA88K は sysctlr.cpp 参照。 -extern std::unique_ptr gInterrupt; +extern InterruptDevice *gInterrupt;