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