File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / interrupt.cpp
Revision 1.1.1.14 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:06:00 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
//

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

// IODevice
//    |
//    |  +-----------------+
//    +--| InterruptDevice | (割り込みコントローラの共通部)
//       +-----------------+
//          |  +-----------------+
//          +--| M680x0Interrupt | (m68k 割り込みコントローラの共通部)
//          |  +-----------------+
//          |     |
//          |     |  +-----------------+
//          |     +--| X68030Interrupt | (X68030 の割り込みコントローラ)
//          |     |  +-----------------+
//          |     |
//          |     |  +---------------+
//          |     +--| LunaInterrupt |   (LUNA-I の割り込みコントローラ)
//          |     |  +---------------+
//          |     |
//          |     |  +---------------+
//          |     +--| NewsInterrupt |   (NEWS の割り込みコントローラ)
//          |     |  +---------------+
//          |     |
//          |     |  +------------------+
//          |     +--| Virt68kInterrupt | (virt-m68k の割り込みコントローラ)
//          |        +------------------+
//          |
//          +-- PEDECDevice   (X68030 の Lv1 担当コントローラ)
//          +-- SysCtlrDevice (LUNA-88K の割り込みコントローラ)
//          +-- GFPICDevice   (virt-m68k の各レベル担当コントローラ)

#include "interrupt.h"
#include "dmac.h"
#include "goldfish_pic.h"
#include "lance.h"
#include "pedec.h"
#include "m680x0.h"
#include "mfp.h"
#include "monitor.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"

//
// 割り込みコントローラ (共通部)
//

// コンストラクタ(オブジェクト名指定なし)
InterruptDevice::InterruptDevice()
	: InterruptDevice(OBJ_INTERRUPT)	// 移譲
{
}

// コンストラクタ(オブジェクト名指定あり)
InterruptDevice::InterruptDevice(uint objid_)
	: inherited(objid_)
{
}

// デストラクタ
InterruptDevice::~InterruptDevice()
{
}

// リセット
void
InterruptDevice::ResetHard(bool poweron)
{
	memset(&counter, 0, sizeof(counter));
}

// 子デバイスを登録する。
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_;
}

// デバイスから 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?");
}

// Intmap 値から名前を引く
const char *
InterruptDevice::GetIntName(uint32 intmap_) const
{
	for (const auto& m : intmap_list) {
		if (m.intmap == intmap_) {
			return m.name;
		}
	}

	return "???";
}

// 子デバイスが割り込み信号線をアサートした
void
InterruptDevice::AssertINT(Device *source)
{
	uint32 oldmap;
	uint32 srcmap;

	oldmap = intmap_status;
	srcmap = GetIntmap(source);
	intmap_status |= srcmap;

	if ((oldmap ^ intmap_status) != 0) {
		// 立ち上がりエッジでカウント
		counter[__builtin_clz(srcmap)]++;
		ChangeInterrupt();
	}
}

// 子デバイスが割り込み信号線をネゲートした
void
InterruptDevice::NegateINT(Device *source)
{
	uint32 oldmap;
	uint32 srcmap;

	oldmap = intmap_status;
	srcmap = GetIntmap(source);
	intmap_status &= ~srcmap;

	if ((oldmap ^ intmap_status) != 0) {
		ChangeInterrupt();
	}
}

// 割り込み信号線の状態を取得
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()
{
	mpu680x0 = GetMPU680x0Device(mpu);

	return true;
}

void
M680x0Interrupt::ChangeInterrupt()
{
	uint newipl;

	// intmap_status は最下位ビットを常に立ててあるので 0 にならない
	newipl = (uint)(31 - __builtin_clz(intmap_status)) / 4;

	// 割り込みレベルが変わったら、新しいレベルを通知
	if (newipl != ipl) {
		ipl = newipl;
		mpu->Interrupt(ipl);
	}
}


//
// X68030 仮想割り込みコントローラ
//

// コンストラクタ
X68030Interrupt::X68030Interrupt()
{
	monitor = gMonitorManager->Regist(ID_MONITOR_INTERRUPT, this);
	monitor->SetCallback(&X68030Interrupt::MonitorScreen);
}

// デストラクタ
X68030Interrupt::~X68030Interrupt()
{
}

// 初期化
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<RTL8019ASDevice>(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);
	}
	disp_list.push_back((uint32)IntmapDMAC);
	disp_list.push_back((uint32)IntmapPEDEC);

	// 行数が確定したのでサイズ設定
	monitor->SetSize(41, 1 + disp_list.size() + 5/*PEDEC*/);

	return true;
}

// 割り込みアクノリッジに応答する
busdata
X68030Interrupt::InterruptAcknowledge(int lv)
{
	switch (lv) {
	 case 7:
		return AutoVector;

	 case 6:
		return mfp->InterruptAcknowledge();

	 case 5:
		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 dmac->InterruptAcknowledge();

	 case 1:
		return pedec->InterruptAcknowledge(lv);

	 default:
		break;
	}

	return BusData::BusErr;
}

void
X68030Interrupt::MonitorScreen(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  Nereid0     01234567890123456789012345
	//     SPC    Mask

	y = 0;
	screen.Print(0, y, "Lv Device  IM=%u", intr_mask);
	screen.Puts(36, y, "Count");
	y++;
	for (uint32 map : disp_list) {
		int clz = __builtin_clz(map);
		int lv = (31 - clz) / 4;
		screen.Print(0, y, "%u", 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++;
	}

	// PEDEC 分も一緒に表示したい
	pedec->MonitorScreen(screen, intr_mask, y);
}


//
// LUNA-I 仮想割り込みコントローラ
//

// コンストラクタ
LunaInterrupt::LunaInterrupt()
{
	monitor = gMonitorManager->Regist(ID_MONITOR_INTERRUPT, this);
	monitor->SetCallback(&LunaInterrupt::MonitorScreen);
}

// デストラクタ
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());

	return true;
}

// 割り込みアクノリッジに応答する
busdata
LunaInterrupt::InterruptAcknowledge(int lv)
{
	// LUNA は全てオートベクタ
	return AutoVector;
}

void
LunaInterrupt::MonitorScreen(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=%u", 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, "%u", 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++;
	}
}


//
// NEWS 仮想割り込みコントローラ
//

// コンストラクタ
NewsInterrupt::NewsInterrupt()
{
	monitor = gMonitorManager->Regist(ID_MONITOR_INTERRUPT, this);
	monitor->SetCallback(&NewsInterrupt::MonitorScreen);
}

// デストラクタ
NewsInterrupt::~NewsInterrupt()
{
}

// 初期化
bool
NewsInterrupt::Init()
{
	if (inherited::Init() == false) {
		return false;
	}

	// 割り込みアクノリッジで使う
	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;
	}
}

// ステータスバイトを読み出す。NewsCtlr から呼ばれる。
uint8
NewsInterrupt::PeekStatus() const
{
	uint8 data = 0;

	if ((intmap_status & IntmapSIC)) {
		data |= 0x80;
	}
	if ((intmap_status & IntmapLance)) {
		data |= 0x04;
	}

	return data;
}

void
NewsInterrupt::MonitorScreen(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=%u", 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, "%u", 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++;
	}
}


//
// virt-m68k 仮想割り込みコントローラ
//

// コンストラクタ
Virt68kInterrupt::Virt68kInterrupt()
{
	monitor = gMonitorManager->Regist(ID_MONITOR_INTERRUPT, this);
	monitor->SetCallback(&Virt68kInterrupt::MonitorScreen);
}

// デストラクタ
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()
{
	monitor->AddHeight(1);
}

// 割り込みアクノリッジに応答する
busdata
Virt68kInterrupt::InterruptAcknowledge(int lv)
{
	// ?
	return AutoVector;
}

void
Virt68kInterrupt::MonitorScreen(Monitor *, TextScreen& screen)
{
	int y;

	// CPU の割り込み可否は本当は関係ないけど、便宜的に一緒に表示したい
	uint intr_mask = mpu680x0->reg.intr_mask;
	// 各 PIC の状況も一緒に表示したい。picmap の添字はレベル(1-6)
	std::array<uint32, 7> picmap;
	for (int lv = 1; lv < picmap.size(); lv++) {
		picmap[lv] = gfpic[lv]->intmap_status;
	}

	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;
	screen.Print(0, y, "Lv Device IM=%u IRQ         20         10         1",
		intr_mask);
	screen.Puts(72, y, "Count");
	y++;
	for (uint32 map : disp_list) {
		uint clz = __builtin_clz(map);
		uint lv = (uint)(31 - clz) / 4;
		screen.Print(0, y, "%u", lv);
		screen.Puts(3, y, TA::OnOff(intmap_status & map), GetIntName(map));
		if (lv <= intr_mask) {
			screen.Puts(10, y, "Mask");
		}

		// 各入力ピンの状態も表示しないと何も分からない。
		gfpic[lv]->MonitorScreenSummary(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]->MonitorScreenDetail(screen, y, lv);
	}
}

unix.superglobalmegacorp.com

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