--- nono/vm/videoctlr.cpp 2026/04/29 17:04:39 1.1.1.2 +++ nono/vm/videoctlr.cpp 2026/04/29 17:05:17 1.1.1.8 @@ -4,7 +4,9 @@ // Licensed under nono-license.txt // -#include "videoctlr.h" +// +// ビデオコントローラ +// // e82000..e823ff パレット // e82400..e824ff R0(e82400.w) のミラー @@ -13,40 +15,95 @@ // e82700..e82fff $00 が読めるようだ // 以上の 4KB をミラー。 -std::unique_ptr gVideoCtlr; +#include "videoctlr.h" +#include "mpu.h" +#include "renderer.h" +#include "uimessage.h" +// コンストラクタ VideoCtlrDevice::VideoCtlrDevice() + : inherited(OBJ_VIDEOCTLR) { - logname = "videoctlr"; - devname = "VideoCtlr"; - devaddr = baseaddr; - devlen = 0x2000; + hostcolor.resize(16); } +// デストラクタ VideoCtlrDevice::~VideoCtlrDevice() { } -// addr は何ワード目のポートかという意味になるので -// $E82002 が addr=1 (番目のワード)。 +// 初期化 +bool +VideoCtlrDevice::Init() +{ + if (inherited::Init() == false) { + return false; + } + + renderer = GetRenderer(); + + return true; +} + +// リセット +void +VideoCtlrDevice::ResetHard(bool poweron) +{ + // XXX not yet +} + +inline uint32 +VideoCtlrDevice::Decoder(uint32 addr) const +{ + return addr & 0xfff; +} + uint64 -VideoCtlrDevice::Read(uint32 addr) +VideoCtlrDevice::Read8(uint32 addr) { uint32 data; - if (addr < 0x400 / 2) { // パレット - data = palette.w[addr]; + mpu->AddCycle(9); // InsideOut p.135 + + uint32 offset = Decoder(addr); + if (offset < 0x400) { // パレット + data = palette[HB(offset)]; + putlog(3, "Palette $%06x.B -> $%02x", addr, data); + + } else if (offset < 0x700) { // R0,R1,R2 + uint nr = Offset2NR(offset); + data = GetNR(nr); + putlog(3, "R%d.%c -> $%02x", + (nr / 2), + (nr % 2) == 0 ? 'H' : 'L', + data); + + } else { + data = 0; + } + + return data; +} - } else if (addr < 0x500 / 2) { // $E82400 (R0) - data = videoctlr.r0; +uint64 +VideoCtlrDevice::Read16(uint32 addr) +{ + uint32 data; - } else if (addr < 0x600 / 2) { // $E82500 (R1) - data = videoctlr.r1; + mpu->AddCycle(9); // InsideOut p.135 - } else if (addr < 0x700 / 2) { // $E82600 (R2) - data = videoctlr.r2; + uint32 offset = Decoder(addr); + if (offset < 0x400) { // パレット + data = *(uint16 *)&palette[offset]; + putlog(3, "Palette $%06x.W -> $%04x", addr, data); + + } else if (offset < 0x700) { // R0,R1,R2 + uint nr = Offset2NR(offset); + data = GetNR(nr) << 8; + data |= GetNR(nr + 1); + putlog(3, "R%d -> $%04x", (nr / 2), data); - } else { // それ以降は $00 が読めるようだ + } else { data = 0; } @@ -54,74 +111,134 @@ VideoCtlrDevice::Read(uint32 addr) } uint64 -VideoCtlrDevice::Write(uint32 addr, uint32 data) +VideoCtlrDevice::Write8(uint32 addr, uint32 data) { - if (addr < 0x400 / 2) { // パレット - putlog(3, "パレット $%06x.W <- $%04x", baseaddr + addr * 2, data); - palette.w[addr] = data; - MakePalette(); - - } else if (addr < 0x500 / 2) { // $E82400 (R0) - SetR0(data); + mpu->AddCycle(11); // InsideOut p.135 - } else if (addr < 0x600 / 2) { // $E82500 (R1) - SetR1(data); + uint32 offset = Decoder(addr); + if (offset < 0x400) { // パレット + putlog(2, "Palette $%06x.B <- $%02x", addr, data); + palette[HB(offset)] = data; + MakePalette(); - } else if (addr < 0x700 / 2) { // $E82600 (R2) - SetR2(data); + } else if (offset < 0x700) { // R0,R1,R2 + uint nr = Offset2NR(offset); + putlog(2, "R%d.%c <- $%02x (NOT IMPLEMENTED)", + (nr / 2), + (nr % 2) == 0 ? 'H' : 'L', + data); + SetNR(nr, data); - } else { // それ以降はたぶん何も起きない? + } else { + // たぶん何も起きない? } return 0; } uint64 -VideoCtlrDevice::Peek(uint32 addr) +VideoCtlrDevice::Write16(uint32 addr, uint32 data) { - uint32 data; + mpu->AddCycle(11); // InsideOut p.135 - if (addr < 0x400 / 2) { // パレット - data = palette.w[addr]; + uint32 offset = Decoder(addr); + if (offset < 0x400) { // パレット + putlog(2, "Palette $%06x.W <- $%04x", addr, data); + *(uint16 *)&palette[offset] = data; + MakePalette(); - } else if (addr < 0x500 / 2) { // $E82400 (R0) - data = videoctlr.r0; + } else if (offset < 0x700) { // R0,R1,R2 + uint nr = Offset2NR(offset); + putlog(3, "R%d <- $%04x (NOT IMPLEMENTED)", (nr / 2), data); + SetNR(nr, data >> 8); + SetNR(nr + 1, data & 0xff); - } else if (addr < 0x600 / 2) { // $E82500 (R1) - data = videoctlr.r1; + } else { + // たぶん何も起きない? + } - } else if (addr < 0x700 / 2) { // $E82600 (R2) - data = videoctlr.r2; + return 0; +} - } else { // それ以降は $00 が読めるようだ - data = 0; +uint64 +VideoCtlrDevice::Peek8(uint32 addr) +{ + uint32 offset = Decoder(addr); + if (offset < 0x400) { // パレット + return palette[HB(offset)]; + + } else if (offset < 0x700) { // R0,R1,R2 + uint nr = Offset2NR(offset); + return GetNR(nr); + + } else { + return 0x00; } +} - return data; +uint64 +VideoCtlrDevice::Poke8(uint32 addr, uint32 data) +{ + uint32 offset = Decoder(addr); + if (offset < 0x400) { + if ((int32)data >= 0) { + palette[HB(offset)] = data; + // Poke による編集でもパレットに反映させる + MakePalette(); + } + return 0; + } + + return -1; } -// R0 への書き込み -void -VideoCtlrDevice::SetR0(uint32 data) +// R0, R1, R2 のオフセットから内部仮想レジスタ番号 NR0-NR5 に変換する。 +// offset は R0, R1, R2 の領域を指していること。 +inline uint +VideoCtlrDevice::Offset2NR(uint32 offset) const { - putlog(0, "R0 <- $%04x 未実装書き込み", data); - videoctlr.r0 = data & 3; + assert(0x400 <= offset && offset < 0x700); + return ((offset / 0x100) - 4) * 2 + (offset & 1); } -// R1 への書き込み -void -VideoCtlrDevice::SetR1(uint32 data) +// 内部仮想レジスタ NR0-NR5 を読み出す。 +uint32 +VideoCtlrDevice::GetNR(uint reg) const { - putlog(0, "R1 <- $%04x 未実装書き込み", data); - videoctlr.r1 = data & 0x3fff; + return nreg[reg]; } -// R2 への書き込み +// 内部仮想レジスタ NR0-NR5 にバイトデータを書き込む。 void -VideoCtlrDevice::SetR2(uint32 data) +VideoCtlrDevice::SetNR(uint reg, uint32 data) { - putlog(0, "R2 <- $%04x 未実装書き込み", data); - videoctlr.r2 = (data & 0xff7f); + // XXX まだ値を保存する以外何もしていない + assert(reg < 6); + switch (reg) { + case 0: // R0.H + nreg[reg] = 0; + break; + case 1: // R0.L + nreg[reg] = data & 0x03; + break; + + case 2: // R1.H + nreg[reg] = data & 0x3f; + break; + case 3: // R1.L + nreg[reg] = data & 0xff; + break; + + case 4: // R2.H + nreg[reg] = data & 0xff; + break; + case 5: // R2.L + nreg[reg] = data & 0x7f; + break; + + default: + __unreachable(); + } } // @@ -132,15 +249,37 @@ VideoCtlrDevice::MakePalette() // パレット前半 256 ワードはグラフィックパレット、 // テキストパレットは後半。 - p = &palette.w[256]; + p = (uint16 *)&palette[512]; - // パレット情報から xBGR32 形式の色データを作成 + // パレット情報から Color 形式の色データを作成 // X680x0 のパレットは %GGGGG'RRRRR'BBBBB'I で並んでいる。 for (int i = 0; i < 16; i++) { int I = (p[i] & 1) << 2; // 輝度ビット int R = (((p[i] >> 6) & 0x1f) << 3) + I; int G = (((p[i] >> 11)) << 3) + I; int B = (((p[i] >> 1) & 0x1f) << 3) + I; - cooked_palette[i] = R | (G << 8) | (B << 16); + hostcolor[i].r = R; + hostcolor[i].g = G; + hostcolor[i].b = B; + } + + // パレット変更が起きたことをレンダラに通知 + renderer->Invalidate2(); + + // UI にも通知 + UIMessage::Post(UIMessage::PALETTE); +} + +// ゲストパレットを uint32 形式にしたものの一覧を返す。(GUI 用) +// XXX 今の所テキスト16色しか考えてない +const std::vector +VideoCtlrDevice::GetIntPalette() const +{ + std::vector ipal; + const uint16 *p = (const uint16 *)&palette[0]; + + for (int i = 0; i < 16; i++) { + ipal.push_back(p[256 + i]); } + return ipal; }