File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / mainbus.cpp
Revision 1.1.1.8 (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
//

//
// メインバス (共通部分)
//

// メインバス付近に必要な機能は次のようになっている。
//
//				Luna	News	X68k	X68kIO	XPbus
// InitMainbus	o		o		o		o		x
// FC access	o		o		o		o		x
// SwitchBoot	o		o		o		o		x
// ResetByMPU	o		o		o		*1		x	(*1:どっちでもいい)
//
// Access Map	o		o		o		x		x
// HV access	o		o		o		x		?
// Peek4		o		o		o		x		x
// Singleton	o		o		o		x		x
//
// もともと FC 付きアクセス機能を Mainbus からX68kIO に伸ばすために
// MainbusDevice を導入したが、一方モニタの登録などでは Mainbus は
// システムに複数存在してはならないため、これらは同時に成立しない。
// そのため、表の上半分の機能を MainbusBaseDevice として独立させ、
// X68kIO はここから派生。表の下半分の機能を MainbusDevice とする。
//
// XPbus はどちらも不要なので (XP プロセッサから見ればメインバスだが、
// ここの Mainbus はそういう意味ではないので) ただの IODevice。

// IODevice
//   |
//   |  +-------------------+
//   +--| MainbusBaseDevice | (InitMainbus() と FC アクセスを持つ)
//   |  +-------------------+
//   |    |
//   |    |  +---------------+
//   |    +--| MainbusDevice | (これがメインバス、システムに1つ)
//   |    |  +---------------+
//   |    |    |
//   |    |    |  +-----------------+
//   |    |    +--| Mainbus24Device | (上位8ビットがテーブルで表せるメインバス)
//   |    |    |  +-----------------+
//   |    |    |    |
//   |    |    |    +-- LunaMainbus
//   |    |    |    |    +-- Luna1Mainbus
//   |    |    |    |    +-- Luna88kMainbus
//   |    |    |    +-- NewsMainbus
//   |    |    |    +-- Virt68kMainbus
//   |    |    +-- X68kMainbus (上位8ビットが IODevice で表せないため)
//   |    |
//   |    +-- X68kIODevice
//   |
//   +-- XPbusDevice (これはただの IODevice)

#include "mainbus.h"
#include "monitor.h"

//
// メインバスの素質を持つ基本クラス
//

// コンストラクタ
MainbusBaseDevice::MainbusBaseDevice(uint objid_)
	: inherited(objid_)
{
}

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

// MPU の RESET 命令によるリセット
void
MainbusBaseDevice::ResetByMPU()
{
}

// ブートページを切り替える際の共通処理。
// 派生クラスから呼ぶこと。
void
MainbusBaseDevice::SwitchBootPage(bool isrom)
{
	// ログ表示
	if (isrom) {
		putlog(1, "SwitchBootPage ROM (Boot)");
	} else {
		putlog(1, "SwitchBootPage RAM (Normal)");
	}
}

// モニタ表示用にデバイス名を整形。
// 7文字に切り詰めるのと、括弧があれば取り除いて Disable 色にする。
/*static*/ std::pair<std::string, TA>
MainbusBaseDevice::FormatDevName(const Device *dev)
{
	std::string name = dev->GetName();
	TA attr;

	if (name[0] == '(') {
		// 括弧付きなら、括弧を取り除いて Disable 色で表示
		int len = std::min(7, (int)name.size() - 2);
		name = name.substr(1, len);
		attr = TA::Disable;
	} else {
		name = name.substr(0, 7);
		attr = TA::Normal;
	}

	return std::make_pair(name, attr);
}


//
// メインバス (基本クラス)
//

// コンストラクタ
MainbusDevice::MainbusDevice(uint objid_)
	: inherited(objid_)
{
	accstat_monitor = gMonitorManager->Regist(ID_MONITOR_ACCSTAT, this);
	accstat_monitor->SetCallback(&MainbusDevice::MonitorScreenAccStat);
	// 表示行数が機種ごとに異なるので継承側でセットしている。

	// 通常は 32bit 空間全体 (そうでない機種はコンストラクタで上書きする)
	accstat_baseaddr = 0;
	accstat_bitlen = 32;
}

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

// 初期化
bool
MainbusDevice::Init()
{
	// アクセス状況の表示用配列。
	accstat_rw.resize(AccStat::MAINLEN >> (32 - accstat_bitlen));

	return true;
}

// リセット
void
MainbusDevice::ResetHard(bool poweron)
{
	inherited::ResetHard(poweron);

	std::fill(accstat_read.begin(), accstat_read.end(), 0);
	std::fill(accstat_write.begin(), accstat_write.end(), 0);
}

// ハイパーバイザ的読み込み (ミスアライン可)。
// addr はアドレスとサイズを指定すること。
// バスエラーが起きればその時点で BusData::BusErr を返す。
busdata
MainbusDevice::HVReadN(busaddr addr)
{
	addr |= BusAddr::SRead;

	uint32 reqsize = addr.GetSize();
	busdata data;
	do {
		busdata bd = Read(addr);
		if (__predict_false(bd.IsBusErr())) {
			return bd;
		}
		data |= DYNAMIC_BUS_SIZING_R(addr, bd);
	} while (addr.GetSize() != 0);

	data |= busdata::Size(reqsize);
	return data;
}

// ハイパーバイザ的読み込み
busdata
MainbusDevice::HVRead1(uint32 paddr)
{
	busaddr addr = busaddr(paddr) | BusAddr::Size1;
	return HVReadN(addr);
}

// ハイパーバイザ的読み込み (ミスアライン可)
busdata
MainbusDevice::HVRead2(uint32 paddr)
{
	busaddr addr = busaddr(paddr) | BusAddr::Size2;
	return HVReadN(addr);
}

// ハイパーバイザ的読み込み (ミスアライン可)
busdata
MainbusDevice::HVRead4(uint32 paddr)
{
	busaddr addr = busaddr(paddr) | BusAddr::Size4;
	return HVReadN(addr);
}

// ハイパーバイザ的書き込み (ミスアライン可)。
// addr はアドレスとサイズを指定すること。属性は SRead 固定。
// バスエラーが起きればその時点で busdata::BusErr を返す。
busdata
MainbusDevice::HVWriteN(busaddr addr, uint32 data)
{
	addr |= BusAddr::SWrite;
	do {
		busdata r = Write(addr, data);
		if (__predict_false(r.IsBusErr())) {
			return r;
		}
		DYNAMIC_BUS_SIZING_W(addr, data, r);
	} while (addr.GetSize() != 0);

	return 0;
}

// ハイパーバイザ的書き込み
busdata
MainbusDevice::HVWrite1(uint32 paddr, uint32 data)
{
	busaddr addr = busaddr(paddr) | BusAddr::Size1;
	return HVWriteN(addr, data);
}

// ハイパーバイザ的書き込み (ミスアライン可)
busdata
MainbusDevice::HVWrite2(uint32 paddr, uint32 data)
{
	busaddr addr = busaddr(paddr) | BusAddr::Size2;
	return HVWriteN(addr, data);
}

// ハイパーバイザ的書き込み (ミスアライン可)
busdata
MainbusDevice::HVWrite4(uint32 paddr, uint32 data)
{
	busaddr addr = busaddr(paddr) | BusAddr::Size4;
	return HVWriteN(addr, data);
}

// 連続する4バイトを Peek する。(ミスアライン可)
uint64
MainbusDevice::Peek4(uint32 addr)
{
	uint64 data;

	data  = Peek1(addr++) << 24;
	data |= Peek1(addr++) << 16;
	data |= Peek1(addr++) << 8;
	data |= Peek1(addr);
	return data;
}

void
MainbusDevice::MonitorScreenAccStat(Monitor *, TextScreen& screen)
{
	std::array<char, 64 + 5> buf;

#define UPDATE_RW(rw_ptr)	do {			\
	uint64 dst = *(uint64 *)(rw_ptr);		\
	dst <<= 2;								\
	dst &= 0xfcfcfcfc'fcfcfcfc;				\
	dst |= src;								\
	*(uint64 *)(rw_ptr) = dst;				\
} while (0)

	// R|W をマージしながらコピー。
	if (__predict_true(accstat_rw.size() == AccStat::MAINLEN)) {
		// 全域。
		for (int i = 0; i < AccStat::MAINLEN; i += 8) {
			uint64 src = *(uint64 *)&accstat_read[i]
					   | *(uint64 *)&accstat_write[i];
			UPDATE_RW(&accstat_rw[i]);
		}
	} else {
		// NEWS なら表示に使うのは前 1/4 のみで、後ろはミラー。
		// ただしアドレス表記は $c000'0000 以降(後ろの 1/4)。
		uint offset = accstat_rw.size();
		uint ndiv = accstat_read.size() / offset;
		for (int i = 0, iend = accstat_rw.size(); i < iend; i += 8) {
			uint64 src = 0;
			for (int j = 0; j < ndiv; j++) {
				src |= *(uint64 *)&accstat_read[i + j * offset];
				src |= *(uint64 *)&accstat_write[i + j * offset];
			}
			UPDATE_RW(&accstat_rw[i]);
		}
	}

	// 読んだのでリセット。
	std::fill(accstat_read.begin(), accstat_read.end(), 0);
	std::fill(accstat_write.begin(), accstat_write.end(), 0);

	screen.Clear();

	static_assert(AccStat::SHIFT >= 20, "");
	screen.Print(0, 0,
		"Shows %ubit space. %uMB/char. 'R':Read, 'W':Write, 'A':Read+Write",
		accstat_bitlen, 1U << (AccStat::SHIFT - 20));
	screen.Print(12, 1,
		"+$00    +$01     +$02    +$03     +$04    +$05     +$06    +$07");

	uint32 baseaddr = accstat_baseaddr;
	for (int y = 0, yend = accstat_rw.size() / 64; y < yend; y++) {
		const uint8 *a = &accstat_rw[y * 64];
		uint8 avail = accstat_avail[y];
		char *d = &buf[0];
		std::fill(buf.begin(), buf.end() - 1, ' ');
		for (int i = 0; i < 8; i++) {
			// 先頭に空白を入れといて、参照時は buf+1 から参照する。
			if ((i & 1) == 0) {
				d++;
			}
			if ((int8)avail < 0) {
				// 何かしらデバイスがある。16MB、8文字分。
				for (int j = 0; j < 8; j++) {
					uint op = *a & 3;
					*d++ = ".RWA"[op];
					a++;
				}
			} else {
				// この 16MB には何のデバイスもない。
				d += 8;
				a += 8;
			}
			avail <<= 1;
		}
		*d = '\0';
		screen.Print(0, y + 2, "$%02x00'0000: %s",
			baseaddr + y * 0x08, &buf[1]);
	}
}


//
// 上位8ビットがテーブルで表せる構造のメインバス。
//

// コンストラクタ (オブジェクト名指定なし)
Mainbus24Device::Mainbus24Device()
	: MainbusDevice(OBJ_MAINBUS)
{
	monitor = gMonitorManager->Regist(ID_MONITOR_MAINBUS, this);
	monitor->SetCallback(&Mainbus24Device::MonitorScreen);
	monitor->SetSize(75, 33);
}

Mainbus24Device::~Mainbus24Device()
{
}

// アクセス状況のエリア情報初期化。
// 継承側の InitMainbus() の終わりで呼ぶこと。
void
Mainbus24Device::InitAccStat()
{
	for (int i = 0; i < accstat_avail.size(); i++) {
		uint bits = 0;
		for (int j = 0; j < 8; j++) {
			int n = i * 8 + j;
			int id = devtable[n]->GetId();
			bits <<= 1;
			if (id != OBJ_BUSERR && id != OBJ_NOPIO) {
				bits |= 1;
			}
		}
		accstat_avail[i] = bits;
	}

	// デバッグ用
	if (0) {
		for (int i = 0; i < accstat_avail.size(); i++) {
			printf("%02x: ", i);
			uint8 a = accstat_avail[i];
			for (int j = 0; j < 8; j++) {
				if ((int8)a < 0) {
					printf("1");
				} else {
					printf("0");
				}
				a <<= 1;
			}
			printf("\n");
		}
	}
}

inline IODevice *
Mainbus24Device::Decoder(uint32 addr) const
{
	return devtable[addr >> 24];
}

busdata
Mainbus24Device::Read(busaddr addr)
{
	uint32 pa = addr.Addr();
	lastaddr = pa;
	accstat_read[pa >> AccStat::SHIFT] = AccStat::READ;
	IODevice *dev = Decoder(pa);
	return dev->Read(addr);
}

busdata
Mainbus24Device::Write(busaddr addr, uint32 data)
{
	uint32 pa = addr.Addr();
	lastaddr = pa;
	accstat_write[pa >> AccStat::SHIFT] = AccStat::WRITE;
	IODevice *dev = Decoder(pa);
	return dev->Write(addr, data);
}

busdata
Mainbus24Device::ReadBurst16(busaddr addr, uint32 *dst)
{
	uint32 pa = addr.Addr();
	lastaddr = pa;	// 開始アドレスしか残らない…
	IODevice *dev = Decoder(pa);
	busdata r = dev->ReadBurst16(addr, dst);
	if (__predict_true(r.IsBusErr() == false)) {
		accstat_read[pa >> AccStat::SHIFT] = AccStat::READ;
	}
	return r;
}

busdata
Mainbus24Device::WriteBurst16(busaddr addr, const uint32 *src)
{
	uint32 pa = addr.Addr();
	lastaddr = pa;	// 開始アドレスしか残らない…
	IODevice *dev = Decoder(pa);
	busdata r = dev->WriteBurst16(addr, src);
	if (__predict_true(r.IsBusErr() == false)) {
		accstat_write[pa >> AccStat::SHIFT] = AccStat::WRITE;
	}
	return r;
}

busdata
Mainbus24Device::Peek1(uint32 addr)
{
	IODevice *dev = Decoder(addr);
	return dev->Peek1(addr);
}

bool
Mainbus24Device::Poke1(uint32 addr, uint32 data)
{
	IODevice *dev = Decoder(addr);
	return dev->Poke1(addr, data);
}

void
Mainbus24Device::MonitorScreen(Monitor *, TextScreen& screen)
{
	screen.Clear();

	// 012345678901234567890
	// $0000'0000: 1234567

	for (int i = 0; i < 8; i++) {
		screen.Print(12 + i * 8, 0, "+$0%x", i);
	}

	int idx = 0;
	for (int i = 0; i < devtable.size() / 8; i++) {
		screen.Print(0, i + 1, "$%02x00'0000:", idx);
		for (int j = 0; j < 8; j++) {
			auto pair = FormatDevName(devtable[idx]);
			const std::string& name = pair.first;
			TA attr = pair.second;
			screen.Print(12 + j * 8, i + 1, attr, "%s", name.c_str());
			idx++;
		}
	}
}

unix.superglobalmegacorp.com

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