File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / interrupt.h
Revision 1.1.1.11 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:59 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, HEAD
nono 1.7.0

//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//

//
// 割り込みコントローラ
//

#pragma once

#include "device.h"
#include <array>
#include <vector>

// XP からの割り込み用。
// これだけ1デバイスで2本あるので、仕方なく識別子を用意。
#define DEVICE_XPINT_HIGH	(reinterpret_cast<Device *>((intptr_t)1))
#define DEVICE_XPINT_LOW	(reinterpret_cast<Device *>((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(uint objid_);
	~InterruptDevice() override;

	// リセットで割り込みを下げるのは各割り込み送出デバイスの責任。
	void ResetHard(bool poweron) override;

	// AssertINT()、NegateINT()、ChangeINT() の呼び出し側は、
	// (アサート中に AssertINT() を呼ぶというような) 現在の状態が
	// 変わらないような呼び出しをしても構わない。特に副作用はない。

	// 子デバイスが割り込み信号線をアサートした
	void AssertINT(Device *source);
	// 子デバイスが割り込み信号線をネゲートした
	void NegateINT(Device *source);

	// 子デバイスが割り込み信号線を val の状態に変えた。
	// true ならアサート、false ならネゲート。
	void ChangeINT(Device *source, bool val) {
		if (val) {
			AssertINT(source);
		} else {
			NegateINT(source);
		}
	}

	// MPU からの割り込みアクノリッジに応答する
	virtual busdata InterruptAcknowledge(int lv) = 0;

	// 割り込み信号線の状態を取得する
	bool GetINT(const Device *source) const;

 protected:
	// 割り込みを上位デバイス(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
	// +-------+-------+-------+-------+-------+-------+-------+-------+
	// |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 |
	// +-------+-------+-------+-------+-------+-------+-------+-------+
	//
	// これにより同一レベルのデバイス 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 値を引く。
	uint32 GetIntmap(const Device *source) const;

	// Intmap 値から名前を引く。
	const char *GetIntName(uint32 intmap_) const;

	// 割り込みソース一覧 (概ね使用頻度の高い順)
	std::vector<mapname> intmap_list {};

	// モニタでの表示順 (概ね優先度の高い順)
	std::vector<uint32> disp_list {};

	// 割り込みソースが存在するところは %1
	uint32 intmap_avail {};

	// 割り込み状態 (各 %1 ならアサート)
	uint32 intmap_status {};

	// (必要なら) 割り込みマスク (各 %1 なら有効)
	// 割り込みマスク機能を持ってない人は使わなくてもよい。
	uint32 intmap_enable {};

	// 割り込み回数 (アサートのエッジでカウントアップする)
	// [0]  が Intmap の bit31 割り込みのカウント。
	// [27] が Intmap の bit 4 割り込みのカウント。
	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;

 protected:
	void ChangeInterrupt() override;

	// 現在の割り込みレベル
	int ipl {};

	MPU680x0Device *mpu680x0 {};
};

// X68k 仮想割り込みコントローラ
//
// Level
//  7    INTERRUPT スイッチ
//  6    MFP
//  5    SCC
//  4    (拡張ボード)
//  3    DMAC
//  2    (拡張ボード)
//  1    PEDEC   <--+-- SPC
//                  +-- HDD (SASI)
//                  +-- FDC
//                  +-- FDD
//                  +-- PRN
//
class X68030Interrupt : public M680x0Interrupt
{
	using inherited = M680x0Interrupt;

	// 割り込みマップ            		    76543210
	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();
	~X68030Interrupt() override;

	bool Init() override;
	busdata InterruptAcknowledge(int lv) override;

 private:
	DECLARE_MONITOR_SCREEN(MonitorScreen);

	DMACDevice *dmac {};
	MFPDevice *mfp {};
	NMIDevice *nmi {};
	PEDECDevice *pedec {};
	std::array<RTL8019ASDevice *, 2> net {};
	SCCDevice *scc {};
};

// LUNA-I 仮想割り込みコントローラ
//
// Level
//  7
//  6    SIO
//  5    SystemClock
//  4     (XP high)
//  3    Lance
//  2    SPC
//  1     (XP low)
//
class LunaInterrupt : public M680x0Interrupt
{
	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();
	~LunaInterrupt() override;

	bool Init() override;
	busdata InterruptAcknowledge(int lv) override;

 private:
	DECLARE_MONITOR_SCREEN(MonitorScreen);
};

// NEWS 仮想割り込みコントローラ
//
// Level
//  7
//  6    Timer
//  5    SCC(vectored)
//  4    SIC, Lance
//  3
//  2
//  1
//
class NewsInterrupt : public M680x0Interrupt
{
	using inherited = M680x0Interrupt;

	// 割り込みマップ            		    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:
	DECLARE_MONITOR_SCREEN(MonitorScreen);

	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_SCREEN(MonitorScreen);

	std::array<GFPICDevice *, 7> gfpic {};
};

// LUNA-88K は sysctlr.cpp 参照。

inline InterruptDevice *GetInterruptDevice() {
	return Object::GetObject<InterruptDevice>(OBJ_INTERRUPT);
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.