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

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

//
// Nereid Ethernet/メモリ/USB 複合ボード (のうち Ethernet/バンクメモリ)
//

// Nereid の DIPSW
//
//  SW1,2   : RAM をメインメモリとして使う場合の容量
//            OFF,OFF = 割り当てない (この時だけバンクメモリが 16MB 使える)
//  SW3,4,5 : RAM をメインメモリとして使う場合の開始アドレス
//  SW6     : RAM をバンクメモリとして使う場合のウィンドウ
//            OFF = $ee0000
//            ON  = $ef0000
//  SW7     : アドレス、割り込みベクタ設定
//            OFF = $ece3xx, $f9, $fb
//            ON  = $ecebxx, $f8, $fa
//  SW8     : バンクメモリ有効無効
//            OFF = 無効
//            ON  = 有効
//
//  バンクメモリの容量は、SW1-2 でメインメモリに割り当てた余りのうち
//  4MB か 16MB のどちらか。Nereid のメモリをメインメモリに割り当てる機能は
//  (意味がないので) 実装せず、SW1-2 を設定した結果バンクメモリ容量が 4MB か
//  16MB にできるところだけ再現する。
//
//  Nereid は SW-6,7 により2枚まで同時に運用することが可能。
//  SW6 と SW7 の組み合わせは本来自由だが、そこまで自由度を上げるメリットは
//  なさそうなので、ここでは
//    Nereid#0 が SW6=OFF、SW7=OFF
//    Nereid#1 が SW6=ON、 SW7=ON
//  と固定して扱う。

// 各デバイスのアドレス配置、割り込みベクタ
//
//  #0 (SW7=OFF)   #1 (SW7=ON)
//  -------------- --------------
//  $ece300-ece33f $eceb00-eceb3f : RTL8019AS (偶数アドレスのみ)
//  $ece380-ece383 $eceb80-eceb83 : SL811HS/T (奇数アドレスのみ)
//  $ece3f0        $ecebf0        : バンクページ選択
//  $ece3f1        $ecebf1        : 制御ポート
//
//  $f9            $f8            : RTL8019AS 割り込みベクタ
//  $fb            $fa            : SL811HS/T 割り込みベクタ

#include "nereid.h"
#include "bankram.h"
#include "config.h"
#include "monitor.h"
#include "rtl8019as.h"

//
// Nereid 空間担当デバイス
//

// boards[0]、board[1] を所有し、アクセスをディスパッチする。

// コンストラクタ
NereidDevice::NereidDevice()
	: inherited(OBJ_NEREID)
{
	monitor = gMonitorManager->Regist(ID_MONITOR_NEREID, this);
	monitor->SetCallback(&NereidDevice::MonitorScreen);
	monitor->SetSize(41, 9);
}

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

// デバイス作成
bool
NereidDevice::Create()
{
	// ボードの有無は nereid%u-enable だけで決まる。
	for (int i = 0; i < 2; i++) {
		std::string name = string_format("nereid%u-enable", i);
		bool enable = gConfig->Find(name).AsBool();

		if (enable) {
			try {
				boards[i].reset(new NereidBoardDevice(i));
			} catch (...) { }
			if ((bool)boards[i] == false) {
				warnx("Failt to initialize NereidBoardDevice(%u) at %s",
					i, __method__);
				return false;
			}

			// 配置場所
			if (i == 0) {
				loc[i] = 0x03;	// $ece300-
			} else {
				loc[i] = 0x0b;	// $eceb00-
			}
		} else {
			// アドレス比較で一致させないため -1 にしておく。
			loc[i] = -1;
		}
	}

	return true;
}

// Nereid が1枚でもささっていれば true。
// (X68kIO.Init() から呼ばれる)
bool
NereidDevice::IsInstalled() const
{
	return (bool)boards[0] || (bool)boards[1];
}

inline uint32
NereidDevice::Decoder(uint32 addr) const
{
	// ここで返すのはオフセットではなく、チップセレクトに近い。
	// $ece3xx or $ecebxx の $3 か $b のところ (厳密にはそれを含む
	// 5ビット) を返し、あらかじめ用意してある loc[i] と比較する。
	return (addr >> 8) & 0x1f;
}

busdata
NereidDevice::Read(busaddr addr)
{
	uint32 paddr = addr.Addr();

	putlog(2, "Read  $%06x.%c", paddr, (addr.GetSize() == 1 ? 'B' : 'W'));

	uint32 cs = Decoder(paddr);
	for (int i = 0; i < boards.size(); i++) {
		if (cs == loc[i]) {
			return boards[i]->Read(addr);
		}
	}

	return BusData::BusErr;
}

busdata
NereidDevice::Write(busaddr addr, uint32 data)
{
	uint32 paddr = addr.Addr();

	putlog(2, "Write $%06x.%c", paddr, (addr.GetSize() == 1 ? 'B' : 'W'));

	uint32 cs = Decoder(paddr);
	for (int i = 0; i < boards.size(); i++) {
		if (cs == loc[i]) {
			return boards[i]->Write(addr, data);
		}
	}
	return BusData::BusErr;
}

busdata
NereidDevice::Peek1(uint32 addr)
{
	uint32 cs = Decoder(addr);

	for (int i = 0; i < boards.size(); i++) {
		if (cs == loc[i]) {
			return boards[i]->Peek1(addr);
		}
	}
	return BusData::BusErr;
}

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

	screen.Clear();

	// 0123456789012345678901234567890123456789
	//                #0           #1
	// Board        : NotInstalled NotInstalled
	//  Ethernet    : Enable
	//   Address    : $ece300
	//   Vector     : $f9
	//  Bank Memory : 16MB
	//   Page Select: $ece380
	//   Bank Window: $ee0000
	//   CurrentPage: $00

	y = 1;
	screen.Puts(0, y++, "Board        :");
	screen.Puts(1, y++,  "Ethernet    :");
	screen.Puts(2, y++,   "Address    :");
	screen.Puts(2, y++,   "Vector     :");
	screen.Puts(1, y++,  "Bank Memory :");
	screen.Puts(2, y++,   "Page Select:");
	screen.Puts(2, y++,   "Bank Window:");
	screen.Puts(2, y++,   "CurrentPage:");

	for (int i = 0; i < 2; i++) {
		int x = 15 + i * 14;
		TA attr;

		y = 0;
		screen.Print(x, y++, "#%u", i);

		// ボードがささっているか
		bool installed = (bool)boards[i];
		if (installed) {
			screen.Puts(x, y, "Installed");
		} else {
			screen.Puts(x, y, "NotInstalled");
		}
		y++;

		// イーサネット
		attr = TA::Disable;
		if (installed) {
			if (boards[i]->IsNetEnable()) {
				screen.Puts(x, y, "Available");
				attr = TA::Normal;
			} else {
				screen.Puts(x, y, "NotAvailable");
			}
		} else {
			screen.Puts(x, y, attr, "-");
		}
		y++;
		screen.Print(x, y++, attr, "$%06x", 0xece300 + i * 0x800);
		screen.Print(x, y++, attr, "$%02x", 0xf9 - i);

		// バンクメモリ
		attr = TA::Disable;
		int ramsize = 0;
		if (installed) {
			ramsize = boards[i]->GetBankRAMSize();
			if (ramsize > 0) {
				screen.Print(x, y, "%u MB", ramsize);
				attr = TA::Normal;
			} else {
				screen.Puts(x, y, "Disabled");
			}
		} else {
			screen.Puts(x, y, attr, "-");
		}
		y++;
		screen.Print(x, y++, attr, "$%06x", 0xece3f0 + i * 0x800);
		screen.Print(x, y++, attr, "$%06x", 0xee0000 + i * 0x10000);
		if (installed) {
			if (ramsize > 0) {
				attr = TA::Normal;
			} else {
				attr = TA::Disable;
			}
			screen.Print(x, y, attr, "$%02x", boards[i]->GetPage());
		} else {
			screen.Puts(x, y, attr, "---");
		}
	}
}


//
// Nereid ボード1枚分
//

// コンストラクタ
NereidBoardDevice::NereidBoardDevice(int id_)
	: inherited(OBJ_NEREID_BOARD(id_))
{
	id = id_;
}

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

bool
NereidBoardDevice::Create()
{
	std::string netname = string_format("nereid%u-net", id);
	std::string ramname = string_format("nereid%u-ram-size", id);

	bool net_enable = gConfig->Find(netname).AsBool();

	const ConfigItem iramsize = gConfig->Find(ramname);
	ramsize_MB = iramsize.AsInt();
	switch (ramsize_MB) {
	 case 0:
	 case 4:
	 case 16:
	 case -4:
	 case -16:
		// 負数は、バンクメモリ無効時の制御ポート bit6 を明示したい場合用。
		break;
	 default:
		iramsize.Err("Valid value is one of 0, 4, 16 (or -4, -16)");
		return false;
	}

	// ネットワークが無効の場合 (これは正常な実機では起きないが)、
	// RTL8019ASDevice インスタンスの生成自体を行わず、net は NULL。
	if (net_enable) {
		int vector;
		if (id == 0) {
			vector = 0xf9;
		} else {
			vector = 0xf8;
		}
		try {
			net.reset(new RTL8019ASDevice(id, vector));
		} catch (...) { }
		if ((bool)net == false) {
			warnx("Failed to initialize RTL8019ASDevice(%u) at %s",
				id, __method__);
			return false;
		}
	}

	// バンクメモリは (DIPSW によって) 有効/無効どちらであっても
	// BankRAMDevice インスタンスの生成自体は行う。
	// ページレジスタは存在している(はずの)ため。
	try {
		if (ramsize_MB <= 0) {
			bank.reset(new BankRAMDevice(OBJ_BANKRAM(id), 0));
		} else {
			bank.reset(new BankRAMDevice(OBJ_BANKRAM(id), ramsize_MB));
			// I/O 空間への登録は X68kIO 側から行っている。
		}
	} catch (...) { }
	if ((bool)bank == false) {
		warnx("Failed to initialize BankRAMDevice(%u) at %s", id, __method__);
		return false;
	}
	return true;
}

inline uint32
NereidBoardDevice::Decoder(uint32 addr) const
{
	return addr & 0xff;
}

busdata
NereidBoardDevice::Read(busaddr addr)
{
	busdata data = BusData::BusErr;

	uint32 paddr = addr.Addr();
	uint32 offset = Decoder(paddr);

	if (offset < 0x40) {
		if ((bool)net) {
			return net->Read(addr);
		}
	} else {
		uint32 reqsize = addr.GetSize();
		uint32 datasize = std::min(2 - (paddr & 1), reqsize);
		if (datasize == 1) {
			if (offset == 0xf0) {
				data = GetPage();
			} else if (offset == 0xf1) {
				data = GetCtrl();
			}
			data |= BusData::Size1;
		} else {
			if (offset == 0xf0) {
				data  = GetPage() << 8;
				data |= GetCtrl();
			}
			data |= BusData::Size2;
		}
	}
	return data;
}

busdata
NereidBoardDevice::Write(busaddr addr, uint32 data)
{
	busdata r;

	uint32 paddr = addr.Addr();
	uint32 offset = Decoder(paddr);

	if (offset < 0x40) {
		if ((bool)net) {
			return net->Write(addr, data);
		}
	} else {
		uint32 reqsize = addr.GetSize();
		uint32 datasize = std::min(2 - (paddr & 1), reqsize);
		data >>= (reqsize - datasize) * 8;
		if (datasize == 1) {
			if (offset == 0xf0) {
				SetPage(data);
			} else if (offset == 0xf1) {
				SetCtrl(data);
			} else {
				r.SetBusErr();
			}
			r |= BusData::Size1;
		} else {
			if (offset == 0xf0) {
				SetPage(data >> 8);
				SetCtrl(data & 0xff);
			} else {
				r.SetBusErr();
			}
			r |= BusData::Size2;
		}
	}
	return r;
}

busdata
NereidBoardDevice::Peek1(uint32 addr)
{
	uint32 offset = Decoder(addr);

	if (offset < 0x40) {
		if ((bool)net) {
			return net->Peek1(addr);
		}
	} else if (offset == 0xf0) {
		return GetPage();

	} else if (offset == 0xf1) {
		return GetCtrl();
	}
	return BusData::BusErr;
}

// バンクページ選択レジスタの値を取得。
uint32
NereidBoardDevice::GetPage() const
{
	return bank->GetPage();
}

// バンクページ選択レジスタに値を設定。
void
NereidBoardDevice::SetPage(uint32 data)
{
	bank->SetPage(data);
}

// 制御ポートの値を取得。
uint32
NereidBoardDevice::GetCtrl() const
{
	uint32 data;

	// bit7: バンクメモリ有効 (%0=無効、%1=有効)、DIPSW8 が見える
	// bit6: バンクメモリサイズ (%0=4M、%1=16M)
	// bit5: バンクメモリウィンドウ (%0=$EE0000、%1=$EF0000)、DIPSW6 が見える
	// bit4
	// bit3
	// bit2: USB 割り込み
	// bit1: USB 電源
	// bit0: USB 有効

	// bit5、バンクメモリウィンドウは $EE0000 なら %0、$EF0000 なら %1 だが、
	// 現状はボード #0 が $EE0000、ボード #1 が $EF0000 と固定してある。
	data = id << 5;

	// bit7、bit6。
	//
	// bit6 は DIPSW1=OFF, DIPSW2=OFF (メインメモリへの割り当てなし、
	// つまり 16MB 全部をバンクメモリに割ける) 時に %1 になる。
	// まったく実用性はないけど再現できるようにしておく。
	//
	// DIP8 DIP1 DIP2   bit7 bit6   BankRAM   nereid?-ram-size=
	// ---------------+-----------+----------+------
	// ON   OFF  OFF  :    1    1 : 16MB     | 16
	// ON   *    *    :    1    0 : 4MB      | 4
	// OFF  OFF  OFF  :    0    1 : Disabled | 0
	// OFF  OFF  OFF  :    0    1 : Disabled | -16
	// OFF  *    *    :    0    0 : Disabled | -4
	if (ramsize_MB > 0) {
		data |= 0x80;
		if (ramsize_MB != 4) {
			data |= 0x40;
		}
	} else {
		if (ramsize_MB != -4) {
			data |= 0x40;
		}
	}

	// USB 関連は無効なままなので %0

	return data;
}

void
NereidBoardDevice::SetCtrl(uint32 data)
{
	// USB はないので書き込みは無効
}

unix.superglobalmegacorp.com

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