|
|
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);
}
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.