File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / bt45x.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
//

//
// Bt454/458
//

// 仕様書には $c100_0000 と書いてあるが、PROM も NetBSD カーネルも
// $c110_0000 にアクセスしている。実際にはアドレスバスの上位寄りは未接続の
// 箇所が多くほとんどの場所でミラーが見えているので問題はない。
//
// LUNA-I、LUNA-88K どちらでも (というかここはビデオボードなので本体関係なく)
// Bt454 ならバイトポートが連続配置 (BusIO_B 相当)、
// Bt458 ならバイトポートがロングワード配置 (たぶん BusIO_BFFF 相当、未確認)
// となっているようだ。(mainbus_luna 参照)
//
// ソフトウェアから判別は以下の要領で行える。
// 先頭の1バイトは 1/4bpp ボード(連続配置)、8bpp ボード(ロングワード配置)の
// どちらでもアドレスレジスタなので、ここに $00 を書き込んでから読み返す。
// $0f が読み出せたら Bt454 なので 1/4bpp ボード、$00 が読み出せたら Bt458
// なので 8bpp ボードと分かる。Bt458 のデータバスは8ビットクリーンだが、
// Bt454 のデータバスは4ビットしかなく下位ニブルは常に $f になるため。
//
// LUNA-I の PROM v4.22 は 8bpp ボードに対応していない。

#include "bt45x.h"
#include "config.h"
#include "lunafb.h"
#include "mainapp.h"
#include "monitor.h"
#include "prom.h"
#include "uimessage.h"

//
// Bt454/458 共通部分
//

// コンストラクタ
BT45xDevice::BT45xDevice()
	: inherited(OBJ_BT45x)
{
	// func, size は継承先で決まる
	monitor = gMonitorManager->Regist(ID_MONITOR_BT45x, this);
}

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

// 設定からプレーン数を取得する。
// 値が不正ならエラーメッセージを表示して -1 を返す。
// (mainbus_luna から呼ばれる)
/*static*/ int
BT45xDevice::GetConfigPlaneCount()
{
	int n;

	const ConfigItem& item = gConfig->Find("luna-video-plane");
	if (item.TryInt(&n) == false) {
		item.Err();
		return -1;
	}
	if (n != 1 && n != 4 && n != 8) {
		item.Err("Must be 1, 4, or 8");
		return -1;
	}

	return n;
}

// 初期化
bool
BT45xDevice::Init()
{
	int n = GetConfigPlaneCount();
	// ここに来ているということはエラーは起きなかったはず。
	assert(n >= 0);
	nplane = n;

	lunafb = GetLunafbDevice();
	uimessage = gMainApp.GetUIMessage();

	// 継承先のコンストラクタでセットしている。
	assert(palettes != 0);
	color.resize(palettes * 3);
	hostcolor.resize(palettes);

	return true;
}

// リセット
void
BT45xDevice::ResetHard(bool poweron)
{
	idx = 0;

	// ReadMask、BlinkMask は初期化されない (初期値も不明)

	// XXX Bt45x のレジスタの初期値は不明なのでとりあえず全部黒にしておく
	memset(&color[0], 0, color.size());
	for (int i = 0; i < color.size(); i++) {
		MakeHostColor(i);
	}
	Notify();
}

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

	switch (offset) {
	 case 0:	// アドレスレジスタ
		data = PeekAddress();
		// アドレスレジスタアクセスで ab カウンタは 0 にリセットされる
		idx = (idx / 3) * 3;

		putlog(2, "Address Register -> $%02x", data.Data());
		break;

	 case 1:	// カラーパレットレジスタ
		data = PeekPalette();
		putlog(2, "Color Palette $%x(%c) -> $%02x",
			idx / 3, rgbstr[idx % 3], data.Data());
		// アクセスするたびにインクリメントする。
		idx++;
		if (idx >= color.size()) {
			idx = 0;
		}
		break;

	 case 2:	// Bt454 ならリセット(?)、Bt458 なら各種制御レジスタ
		data = ReadControl();
		break;

	 case 3:
		putlog(0, "Read Overlay Register (NOT IMPLEMENTED)");
		data = 0xff;
		break;

	 default:
		VMPANIC("corrupted offset=%u", offset);
	}

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

busdata
BT45xDevice::WritePort(uint32 offset, uint32 data)
{
	switch (offset) {
	 case 0:	// アドレスレジスタ
		WriteAddress(data);
		break;

	 case 1:	// カラーパレットレジスタ
		WritePalette(data);
		break;

	 case 2:	// Bt454 ならリセット(?)、Bt458 なら各種制御レジスタ
		WriteControl(data);
		break;

	 case 3:
		putlog(0, "Overlay Register <- $%02x (NOT IMPLEMENTED)", data);
		break;

	 default:
		VMPANIC("corrupted offset=%u", offset);
	}

	busdata r = BusData::Size1;
	return r;
}

busdata
BT45xDevice::PeekPort(uint32 offset)
{
	switch (offset) {
	 case 0:	// アドレスレジスタ
		return PeekAddress();

	 case 1:	// カラーパレットレジスタ
		return PeekPalette();

	 case 2:	// Bt454 ならリセット(?)、Bt458 なら各種制御レジスタ
		return PeekControl();

	 case 3:
		// 未実装
		return 0xff;

	 default:
		return 0xff;
	}
}

bool
BT45xDevice::PokePort(uint32 offset, uint32 data)
{
	return false;
}

// アドレスレジスタ読み出しのうち副作用のない部分。
uint32
BT45xDevice::PeekAddress() const
{
	uint32 data;

	// 今からアクセスするパレット番号を返す。
	data = idx / 3;

	if (bt454) {
		data = (data << 4) | low_nibble;
	} else {
		// Bt458 ならこれをマスクする?
		data &= readmask;
	}
	return data;
}

// パレットレジスタ読み出しのうち副作用のない部分。
uint32
BT45xDevice::PeekPalette() const
{
	// Bt454 の場合 color[] の上位下位4ビットは同じ値にしてある。
	return color[idx] | low_nibble;
}

// アドレスレジスタ書き込み。
void
BT45xDevice::WriteAddress(uint32 data)
{
	// 今からアクセスするパレット番号。
	// アドレスレジスタアクセスで ab カウンタは 0 にリセットされる。
	// data はパレット番号だが idx は配列のインデックスなので 3倍。
	putlog(2, "Address Register <- $%02x", data);
	if (bt454) {
		data >>= 4;
	}
	idx = data * 3;
}

// パレットレジスタ書き込み。
void
BT45xDevice::WritePalette(uint32 data)
{
	// Bt454 なら上位下位4ビットに同じ値を置くことで 0 .. 255 にする。
	if (bt454) {
		data = (data & 0xf0) | ((data >> 4) & 0x0f);
	}
	color[idx] = data;

	if (loglevel == 1) {
		// ログレベル1なら RGB が書き込まれた時点で表示。
		// 大体はこれくらいの表示で構わないはず。
		if (idx % 3 == 2) {
			if (bt454) {
				putlogn("Color Palette $%x = (%x,%x,%x)",
					idx / 3,
					color[idx - 2] >> 4,
					color[idx - 1] >> 4,
					color[idx    ] >> 4);
			} else {
				putlogn("Color Palette $%02x = (%02x,%02x,%02x)",
					idx / 3,
					color[idx - 2],
					color[idx - 1],
					color[idx    ]);
			}
		}
	} else if (loglevel >= 2) {
		if (bt454) {
			putlogn("Color Palette $%x(%c) <- $%02x",
				idx / 3, rgbstr[idx % 3], data);
		} else {
			putlogn("Color Palette $%02x(%c) <- $%02x",
				idx / 3, rgbstr[idx % 3], data);
		}
	}

	// ホストカラーを計算。
	// (Bt458 では?) Blue を書き込んだところで色情報がストアされるっぽい?
	MakeHostColor(idx);
	Notify();

	// ホストカラー計算後にインクリメントする。
	idx++;
	if (idx >= color.size()) {
		idx = 0;
	}
}

// idx 番目のレジスタ値からホストパレットを作成。
void
BT45xDevice::MakeHostColor(uint idx_)
{
	assert(idx_ < color.size());
	uint col = idx_ / 3;
	uint ab  = idx_ % 3;

	if (nplane == 1) {
		// 1bpp (モノクロプレーン) なら R,G,B のうち G の値だけを使用
		if (ab == 1) {
			hostcolor[col].r = color[col * 3 + 1];
			hostcolor[col].g = color[col * 3 + 1];
			hostcolor[col].b = color[col * 3 + 1];
		}
	} else {
		// カラープレーンなら代入するだけ
		switch (ab) {
		 case 0:
			hostcolor[col].r = color[col * 3 + ab];
			break;
		 case 1:
			hostcolor[col].g = color[col * 3 + ab];
			break;
		 case 2:
			hostcolor[col].b = color[col * 3 + ab];
			break;
		 default:
			VMPANIC("corrupted ab=%u", ab);
		}
	}

	// 通知が必要なら別途行うこと。
}

// 通知
void
BT45xDevice::Notify()
{
	// パレット変更が起きたことをレンダラに通知
	lunafb->Invalidate2();

	// UI にも通知
	uimessage->Post(UIMessage::PALETTE);
}

/*static*/ const char
BT45xDevice::rgbstr[3] = { 'R', 'G', 'B' };


//
// Bt454
//

// コンストラクタ
BT454Device::BT454Device()
	: inherited()
{
	SetName("Bt454");
	bt454 = true;
	palettes = 16;
	low_nibble = LOW_NIBBLE;
	// 使わないけど一応
	readmask = 0x0f;

	monitor->SetCallback(&BT454Device::MonitorScreen);
	monitor->SetSize(63, 3);
}

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

void
BT454Device::MonitorScreen(Monitor *, TextScreen& screen)
{
	screen.Clear();
	screen.Puts(0, 0, "Model: Bt454");

	// 01234567
	// $0  $1
	// RGB RGB

	for (int i = 0; i < 16; i++) {
		screen.Print(i * 4, 1, "$%x", i);
		screen.Puts(i * 4, 2, strhex(PaletteToInt(i), 3).c_str());
	}
}

// 制御レジスタの読み出し。(Bt454 ではリセット)
uint32
BT454Device::ReadControl()
{
	// ADDRa,b を 0 にする? D0-D3 は 0 になる??
	// データシートが雑でよく分からん…
	idx = (idx / 3) * 3;
	putlog(2, "reset ab");
	return PeekControl();
}

// 制御レジスタへの書き込み。(Bt454 ではリセット)
void
BT454Device::WriteControl(uint32 data)
{
	// ab カウンタを 0 にする?
	// データは無視されるようだ
	idx = (idx / 3) * 3;
	putlog(2, "reset ab");
}

uint32
BT454Device::PeekControl() const
{
	return 0x00 | LOW_NIBBLE;
}

// パレット n の色を uint32 表現したものを返す。
uint32
BT454Device::PaletteToInt(uint n) const
{
	return ((color[n * 3 + 0] & 0x0f) << 8) |
	       ((color[n * 3 + 1] & 0x0f) << 4) |
	       ((color[n * 3 + 2] & 0x0f)     );
}


//
// Bt458
//

// コンストラクタ
BT458Device::BT458Device()
	: inherited()
{
	SetName("Bt458");
	bt454 = false;
	palettes = 256;
	low_nibble = 0;
	// 初期化されないそうなのでゴミを入れておく
	readmask = 0xcc;

	monitor->SetCallback(&BT458Device::MonitorScreen);
	monitor->SetSize(116, 18);
}

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

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

	// LUNA-I の PROM v4.22 は 8bpp ボードをサポートしておらず PROM の
	// 画面は真っ暗のままになる。その後 OS が再初期化すれば使えるようには
	// なるが、PROM が映らずびっくりするのでメッセージだけ表示して、
	// 実行は継続する。他の ROM バージョンは未調査。
	auto prom = dynamic_cast<PROMDevice *>(GetPROMDevice());
	if (gMainApp.IsLUNA1() && prom != NULL) {
		uint ver = prom->GetROMVer();
		if (ver <= 422) {
			warnx("Warning: PROM v%u.%02u does not support 8bpp board.",
				ver / 100, ver % 100);
			// 実行は継続
		}
	}

	return true;
}

void
BT458Device::MonitorScreen(Monitor *, TextScreen& screen)
{
	screen.Clear();
	screen.Puts(0, 0, "Model: Bt458");

	// 012345678901
	//       +$0    +$1
	// $00: RRGGBB RRGGBB

	for (int i = 0; i < 16; i++) {
		screen.Print(5 + i * 7, 1, "+$%x", i);
	}
	int c = 0;
	for (int y = 0; y < 16; y++) {
		screen.Print(0, y + 2, "$%02x:", y * 16);
		for (int x = 0; x < 16; x++) {
			screen.Print(5 + x * 7, y + 2, "%06x", PaletteToInt(c));
			c++;
		}
	}
}

// 各種制御レジスタの読み出し。
uint32
BT458Device::ReadControl()
{
	uint32 data = PeekControl();

	if (__predict_false(loglevel >= 2)) {
		uint addr = idx / 3;
		switch (addr) {
		 case 0x04:
			putlog(2, "Read ReadMask -> $%02x", data);
			break;
		 case 0x05:
			putlog(2, "Read BlinkMask -> $%02x", data);
			break;
		 case 0x06:
			putlog(2, "Read Command -> $%02x", data);
			break;
		 case 0x07:
			putlog(2, "Read Test -> $%02x", data);
			break;
		 default:
			putlog(0, "Read Control $%02x (NOT IMPLEMENTED)", addr);
			break;
		}
	}

	return data;
}

uint32
BT458Device::PeekControl() const
{
	uint addr = idx / 3;
	switch (addr) {
	 case 0x04:
		return readmask;
	 case 0x05:
		return blinkmask;
	 case 0x06:
		return command;
	 case 0x07:
		return testreg;
	 default:
		// XXX ?
		return 0;
	}
}

// 各種制御レジスタへの書き込み。
void
BT458Device::WriteControl(uint32 data)
{
	// この時点のアドレスによってコマンド処理。
	uint addr = idx / 3;
	switch (addr) {
	 case 0x04:
		WriteRMask(data);
		break;
	 case 0x05:
		WriteBMask(data);
		break;
	 case 0x06:
		WriteCommand(data);
		break;
	 case 0x07:
		WriteTest(data);
		break;
	 default:
		putlog(1, "Unknown command $%02x <- $%02x", addr, data);
		break;
	}
}

// Bt458 Read Mask の書き込み。
void
BT458Device::WriteRMask(uint32 data)
{
	readmask = data;
	putlog(1, "Write ReadMask <- $%02x", readmask);
}

// Bt458 Blink Mask の書き込み。
void
BT458Device::WriteBMask(uint32 data)
{
	// 未実装
	blinkmask = data;
	if (blinkmask != 0x00) {
		putlog(0, "Write BlinkMask <- $%02x (NOT IMPLEMENTED)", blinkmask);
	} else {
		putlog(1, "Write BlinkMask <- $%02x", blinkmask);
	}
}

// Bt458 コマンドレジスタへの書き込み。
void
BT458Device::WriteCommand(uint32 data)
{
	command = data;
	putlog(1, "Write Command <- $%02x", command);

	// 未実装
	if ((command & 0x80)) {
		putlog(0, "5:1 Multiplex (NOT IMPLEMENTED)");
	}
	if ((command & 0x40) == 0) {
		putlog(0, "Use overlay color0 (NOT IMPLEMENTED)");
	}
	if ((command & 0x0c)) {
		putlog(0, "Blink enable (NOT IMPLEMENTED)");
	}
	if ((command & 0x03)) {
		putlog(0, "Display enable (NOT IMPLEMENTED)");
	}
}

// Bt458 テストレジスタへの書き込み。
void
BT458Device::WriteTest(uint32 data)
{
	// 未実装
	testreg = data;
	if (testreg != 0x00) {
		putlog(0, "Write Test <- $%02x (NOT IMPLEMENTED)", testreg);
	} else {
		// カーネルが初期化時にアクセスに来る
		putlog(1, "Write Test <- $%02x (NOT IMPLEMENTED)", testreg);
	}
}

// パレット n の色を uint32 表現したものを返す。
uint32
BT458Device::PaletteToInt(uint n) const
{
	return (color[n * 3 + 0] << 16) |
	       (color[n * 3 + 1] <<  8) |
	        color[n * 3 + 2];
}

unix.superglobalmegacorp.com

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