File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / goldfish_pic.cpp
Revision 1.1.1.5 (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) 2024 nono project
// Licensed under nono-license.txt
//

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

// 32本の独立した割り込みソースを持ち、独立してマスクできるコントローラ。
// オリジナルの Google のドキュメントは用語がひどすぎてアレ。
//
// レジスタ仕様は次の通り。
//  +$00.L STATUS		R-: ペンディングしている割り込み数 (0-32)。
//  +$04.L PENDING		R-: 最も優先度の高いペンディング割り込み取得。
//  +$08.L CLEAR_ALL	-W: 割り込みペンディングをすべてクリアする。
//  +$0c.L DISABLE		-W: 指定の割り込みを無効にする。
//  +$10.L ENABLE		-W: 指定の割り込みを有効にする。
//
// bit0 (IRQ1) なら $00000001、bit31 (IRQ32) なら $80000000 で、
// IRQ1 側 (LSB 側) が優先度が高い。
//
// PENDING は最も優先度の高いペンディングのビットを(たぶん1つ)返す。
// ペンディング割り込みがなければ 0 を返す。
//
// CLEAR_ALL はすべての割り込みソース (ENABLE されていないものも含む)
// のレベルを low に落とす。enable 状態は変えない。
//
// ENABLE/DISABLE は指定したビット位置の割り込みを有効/無効にする。
//
// PENDING/ENABLE/DISABLE はいずれも
//
// nono では intmap の構造を流用するがここではレベル判定が不要なので、
// Goldfish の仕様通り 32ビットすべてで割り込みを受け付ける。
//
// ここまでが単体の Goldfish 割り込みコントローラの仕様で、このコントローラ
// にはレベルの概念はないので m68k で使う場合はこれを Lv1 から Lv6 までの
// 6個用意して、各自目的のレベルのコントローラに配線するという仕組み。
//
// MPU <--- Virt68kInterrupt <-+- GFPICDevice(Lv6)
//                             +- GFPICDevice(Lv5)
//                             +- GFPICDevice(Lv4)
//                             +- GFPICDevice(Lv3)
//                             +- GFPICDevice(Lv2)
//                             +- GFPICDevice(Lv1)

#include "goldfish_pic.h"
#include "monitor.h"
#include <algorithm>

// コンストラクタ
GFPICDevice::GFPICDevice(uint id_)
	: inherited(OBJ_GFPIC(id_))
{
}

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

// 初期化
bool
GFPICDevice::Init()
{
	interrupt = GetInterruptDevice();

	return true;
}

// 電源オン/リセット
void
GFPICDevice::ResetHard(bool poweron)
{
	inherited::ResetHard(poweron);

	intmap_enable = 0;
	ChangeInterrupt();
}

busdata
GFPICDevice::ReadPort(uint32 offset)
{
	busdata data;

	switch (offset) {
	 case STATUS:
		data = GetSTATUS();
		putlog(2, "STATUS -> $%08x", data.Data());
		break;

	 case PENDING:
		data = GetPENDING();
		putlog(2, "PENDING -> $%08x", data.Data());
		break;

	 default:
		data.SetBusErr();
		break;
	}

	data |= BusData::Size4;
	return data;
}

busdata
GFPICDevice::WritePort(uint32 offset, uint32 data)
{
	busdata r;

	switch (offset) {
	 case CLEAR_ALL:
		putlog(1, "CLEAR_ALL");
		intmap_status = 0;
		ChangeInterrupt();
		break;

	 case DISABLE:
		putlog(1, "DISABLE $%08x", data);
		intmap_enable &= ~data;
		break;

	 case ENABLE:
		putlog(1, "ENABLE  $%08x", data);
		intmap_enable |= data;
		break;

	 default:
		r.SetBusErr();
		break;
	}

	r |= BusData::Size4;
	return r;
}

busdata
GFPICDevice::PeekPort(uint32 offset)
{
	switch (offset) {
	 case STATUS:
		return GetSTATUS();

	 case PENDING:
		return GetPENDING();

	 default:
		return BusData::BusErr;
	}
}

// Virt68kInterrupt の下請け。上のサマリ部分。
void
GFPICDevice::MonitorScreenSummary(TextScreen& screen, int x, int y)
{
	for (int i = 0; i < 32; i++) {
		uint32 bit = 0x8000'0000 >> i;
		if ((intmap_avail & bit) != 0) {
			bool status = (intmap_status & bit);
			bool enable = (intmap_enable & bit);
			screen.Putc(x, y, TA::OnOff(status), (enable ? 'E' : 'D'));
		} else {
			screen.Putc(x, y, '.');
		}
		x++;
		if ((i % 8) == 7) {
			x++;
		}
	}
}

// Virt68kInterrupt の下請け。下の個別部分。
int
GFPICDevice::MonitorScreenDetail(TextScreen& screen, int y, int lv)
{
// 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
//
// GFPIC1 IRQ22 XXXXX                                 01234567890123456789012345

	for (auto intmap : disp_list) {
		int clz = __builtin_clz(intmap);
		int irq = 32 - clz;
		const char *name = GetIntName(intmap);

		screen.Print(0, y, "GFPIC%u IRQ%2u", lv, irq);
		screen.Puts(13, y, TA::OnOff(intmap_status & intmap), name);
		screen.Print(51, y, "%26s", format_number(counter[clz]).c_str());
		y++;
	}

	return y;
}

// STATUS レジスタを返す。
uint32
GFPICDevice::GetSTATUS() const
{
	return __builtin_popcount(intmap_status);
}

// PENDING レジスタを返す。
uint32
GFPICDevice::GetPENDING() const
{
	// 一番下の立っているビットだけにして返す。
	return intmap_status & -intmap_status;
}

busdata
GFPICDevice::InterruptAcknowledge(int lv)
{
	// たぶん何もしない。
	return 0;
}

// 割り込み信号線の状態を変える
void
GFPICDevice::ChangeInterrupt()
{
	bool irq = (intmap_enable & intmap_status);
	interrupt->ChangeINT(this, irq);
}

// この PIC に IRQ を登録する。
// ここだけ子デバイスが自発的に登録を行う方式。
// irq は 1-32。
void
GFPICDevice::RegistIRQ(int irq, const char *name, Device *source)
{
	uint32 intmap = 1U << (irq - 1);

	assertmsg((intmap_avail & intmap) == 0, "IRQ=%u already registered", irq);

	RegistINT(intmap, name, source);

	// 表示順は IRQ01 -> IRQ32
	disp_list.push_back(intmap);
	std::sort(disp_list.begin(), disp_list.end());

	// ここで親の Virt68kInterrupt のモニタ行数を増やす。
	// 本当は登録がすべて終わった頃に Virt68kInterrupt から各 GFPIC
	// に問い合わせたいが、どちらも Init() での処理であり、オブジェクトの
	// 依存関係から無理なので、こっちから都度プッシュする。
	auto virt68kinterrupt = dynamic_cast<Virt68kInterrupt*>(interrupt);
	virt68kinterrupt->IncMonitorHeight();
}

unix.superglobalmegacorp.com

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