--- nono/vm/bitmap.cpp 2026/04/29 17:04:29 1.1.1.2 +++ nono/vm/bitmap.cpp 2026/04/29 17:04:52 1.1.1.6 @@ -1,10 +1,13 @@ // // nono -// Copyright (C) 2018 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // #include "bitmap.h" #include "bt454.h" +#include "cgrom.h" +#include "mpu.h" // // ビットマッププレーン @@ -34,27 +37,43 @@ // FC: 共通ファンクションセット // Fn: プレーン#n ファンクションセット // 1区画が 64KB。空白のところは未調査。 +// 未装着のプレーン(#4..#7とか)は 0xff が読めるようだ。 +// ファンクションセットが 64バイトで折り返してるか未調査だが、 +// b130'0000 も b133'0000 も 0xff が読めるのでこの範囲全域でミラーかも。 +// b150'0000 以降(b1ff'ffffまで) は BUSERR のようだ。 +// -BitmapDevice *gBitmap; +std::unique_ptr gBitmap; BitmapDevice::BitmapDevice() { logname = "bitmap"; devname = "Bitmap"; devaddr = 0xb1000000; - monitor.Init(42, 11); + monitor_size = nnSize(42, 11); +} +BitmapDevice::~BitmapDevice() +{ +} + +bool +BitmapDevice::Init() +{ // XXX とりあえず。実際は設定による nplane = 1; // プレーン数によってメモリを確保 - mem = new uint8 [0x40000 * nplane]; + mem.reset(new uint8 [0x40000 * nplane]); + + return true; } -BitmapDevice::~BitmapDevice() +bool +BitmapDevice::PowerOn() { - if (mem) { - delete[] mem; - } + // XXX 実際は不定値だと思うけど、観測手段がないのでとりあえず。 + memset(&mem[0], 0xff, 0x40000 * nplane); + return true; } void @@ -63,7 +82,10 @@ BitmapDevice::ResetHard() // 厳密にどの時点でリセットされるのかは知らないけど bmsel = 0; - // RFCNT の初期値は観測困難なので、とりあえず無視。 + // XXX 初期値の観測は困難なので適当 + rfcnt = 0; + hscroll = 0; + vscroll = 0; for (int i = 0; i < MAX_PLANES; i++) { fcs[i] = 5; @@ -77,6 +99,9 @@ BitmapDevice::ResetHard() uint64 BitmapDevice::Read8(uint32 addr) { + // XXX とりあえず RAM と同じウェイトを入れておく (未調査) + gMPU->AddCycle(1); + if (addr < plane0base) { // 書き込み専用 return 0xff; @@ -92,6 +117,9 @@ BitmapDevice::Read8(uint32 addr) uint64 BitmapDevice::Read16(uint32 addr) { + // XXX とりあえず RAM と同じウェイトを入れておく (未調査) + gMPU->AddCycle(1); + if (addr < plane0base) { // 書き込み専用 return 0xffff; @@ -107,6 +135,9 @@ BitmapDevice::Read16(uint32 addr) uint64 BitmapDevice::Read32(uint32 addr) { + // XXX とりあえず RAM と同じウェイトを入れておく (未調査) + gMPU->AddCycle(1); + if (addr < plane0base) { // 書き込み専用 return 0xffffffff; @@ -122,6 +153,9 @@ BitmapDevice::Read32(uint32 addr) uint64 BitmapDevice::Write8(uint32 addr, uint32 data) { + // XXX とりあえず RAM と同じウェイトを入れておく (未調査) + gMPU->AddCycle(1); + switch (addr) { case 0xb1000000: case 0xb1000001: @@ -174,6 +208,9 @@ BitmapDevice::Write8(uint32 addr, uint32 uint64 BitmapDevice::Write16(uint32 addr, uint32 data) { + // XXX とりあえず RAM と同じウェイトを入れておく (未調査) + gMPU->AddCycle(1); + switch (addr) { case 0xb1000000: case 0xb1000002: @@ -222,6 +259,9 @@ BitmapDevice::Write16(uint32 addr, uint3 uint64 BitmapDevice::Write32(uint32 addr, uint32 data) { + // XXX とりあえず RAM と同じウェイトを入れておく (未調査) + gMPU->AddCycle(1); + switch (addr) { case 0xb1000000: // RFCNT @@ -315,8 +355,8 @@ BitmapDevice::Peek8(uint32 addr) return 0xff; } -bool -BitmapDevice::MonitorUpdate() +void +BitmapDevice::MonitorUpdate(TextScreen& monitor) { int i; @@ -346,8 +386,8 @@ BitmapDevice::MonitorUpdate() // 012345 // $0:RGB const uint8 *pal = gBT454->reg.color; - monitor.Print(29, 1, ""); - monitor.Print(32, 2, "RGB RGB"); + monitor.Puts(29, 1, ""); + monitor.Puts(32, 2, "RGB RGB"); for (i = 0; i < 16; i++) { uint rgb = ((pal[i * 3 + 0] & 0xf0) << 4) + @@ -355,8 +395,6 @@ BitmapDevice::MonitorUpdate() ((pal[i * 3 + 2] & 0xf0) >> 4); monitor.Print(29 + (i / 8) * 7, (i % 8) + 3, "$%x:%03x", i, rgb); } - - return true; } // 全画面の更新フラグを立てる @@ -562,3 +600,132 @@ BitmapDevice::WritePlane32(uint plane, u *(uint32 *)&mem[memoffset] = (data & mask[plane]) | (m & ~mask[plane]); atomic_dirty[offset / 256] = 1; } + + +// エミュレータによる画面出力 +// +// VM 内からの操作ではなく、エミュレータ本体から画面を操作する +// エミュレーション ROM によるモニタコンソール用の機能。 +// サービスを使いたい側 (EmuROM など) が任意の大きさの TextScreen を +// 用意し EmuConsoleOpen() をコールすると (現在のパレットのまま) 画面を +// クリアし、画面中央をこのテキスト VRAM の表示エリアとみなす。 +// 以降は EmuConsoleUpdate() で TextScreen の内容に従って画面を更新し、 +// 使用後は EmuConsoleClose() で再び画面をクリア (およびリソースの解放) を +// すること。 + +// エミュレータによる出力開始 +void +BitmapDevice::EmuConsoleOpen(TextScreen *t) +{ + textscr = t; + + screen_w = textscr->GetCol(); + screen_h = textscr->GetRow(); + prev.resize(screen_w * screen_h); + for (int i = 0; i < screen_w * screen_h; i++) { + prev[i] = 0x0020; + } + + // ざっくりセンタリングする + origin_x = (1280 - screen_w * 12) / 2; + origin_y = (1024 - screen_h * 24) / 2; + // ただし横は 48ドット境界から始める + // こうすると1(,2)文字目の24ビットが必ずワード境界から始まる。 + origin_x = (origin_x / 48) * 48; + + // 同時書き込み設定 + bmsel = (1 << nplane) - 1; + + // 全画面をクリア + for (uint32 addr = 0xb1080000; addr < 0xb10c0000; addr += 4) { + WriteCommonBitmap32(addr, 0); + } +} + +// エミュレータによる出力終了 +void +BitmapDevice::EmuConsoleClose() +{ + // 全画面をクリア + for (uint32 addr = 0xb1080000; addr < 0xb10c0000; addr += 4) { + WriteCommonBitmap32(addr, 0); + } + // 同時書き込みも元に戻す + bmsel = 0; + + textscr = NULL; + prev.clear(); +} + +// エミュレータによる出力 +// ts が出力するテキスト情報、oldts は前回の情報。 +void +BitmapDevice::EmuConsoleUpdate() +{ + auto& text = textscr->GetBuf(); + + // 12ドットフォントなので2文字ずつ (24ビットずつ) 更新する + for (int y = 0; y < screen_h; y++) { + for (int x = 0; x < screen_w; x += 2) { + // 2文字分の文字と属性 + uint32 t32 = *(const uint32 *)&text[y * screen_w + x]; + uint32 p32 = *(const uint32 *)&prev[y * screen_w + x]; + if (t32 == p32) { + continue; + } + // ここからは変更があったので出力 + *(uint32 *)&prev[y * screen_w + x] = t32; + // 2文字分として直接32ビットキャストして取り出した t32 だと + // エンディアンによって上下が変わるので、ここでは改めて + // 順序通りに2回取り出す + uint32 ch0 = text[y * screen_w + x]; + uint32 ch1 = text[y * screen_w + x + 1]; + // その文字のフォントデータ上でのオフセット + const uint8 * const fo0 = BuiltinCGROM::Get12x24(ch0 & 0xff); + const uint8 * const fo1 = BuiltinCGROM::Get12x24(ch1 & 0xff); + + // 1,2文字目なら true、3,4文字目なら false + bool evenpair = ((x & 2) == 0); + + // ビットマップ上の書き始めアドレス + uint32 addr = 0xb1080000 + + ((origin_y + y * 24) * 256) + + ((origin_x + x * 12) / 8); + + for (int i = 0; i < 24; i++) { + // その文字のフォントデータ + uint32 fdat0 = be16toh(*(const uint16*)(fo0 + i * 2)); + uint32 fdat1 = be16toh(*(const uint16*)(fo1 + i * 2)); + + // 属性はとりあえず反転のみサポート + if ((ch0 & TA::On)) { + fdat0 = ~fdat0 & 0xfff0; + } + if ((ch1 & TA::On)) { + fdat1 = ~fdat1 & 0xfff0; + } + + // 1文字目と2文字目を組み合わせて 24bit にする + uint32 data = (fdat0 << 8) | (fdat1 >> 4); + + // 書き込みは一度に 3バイトずつになるので、 + // 効率化のため上位下位ペアで書き込みパターンを変える。 + // + // | chr1 | chr2 | chr3 | chr4 | + // +----+----+----+----+----+----+ + // | +0 | +1 | +2 | +3 | +4 | +5 | + // +----+----+----+----+----+----+ + // |<-.Word->|<.B>|<.B>|<-.Word->| + if (evenpair) { + WriteCommonBitmap16(addr, data >> 8); + WriteCommonBitmap8(addr + 2, data & 0xff); + } else { + WriteCommonBitmap8(addr, data >> 16); + WriteCommonBitmap16(addr + 1, data & 0xffff); + } + + addr += 256; // 1ライン進める + } + } + } +}