--- nono/vm/bitmap.cpp 2026/04/29 17:04:29 1.1.1.2 +++ nono/vm/bitmap.cpp 2026/04/29 17:04:31 1.1.1.3 @@ -5,6 +5,7 @@ #include "bitmap.h" #include "bt454.h" +#include "cgrom.h" // // ビットマッププレーン @@ -34,8 +35,13 @@ // 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() { @@ -47,14 +53,11 @@ BitmapDevice::BitmapDevice() // XXX とりあえず。実際は設定による nplane = 1; // プレーン数によってメモリを確保 - mem = new uint8 [0x40000 * nplane]; + mem.reset(new uint8 [0x40000 * nplane]); } BitmapDevice::~BitmapDevice() { - if (mem) { - delete[] mem; - } } void @@ -562,3 +565,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.reset(new uint16[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.reset(); +} + +// エミュレータによる出力 +// ts が出力するテキスト情報、oldts は前回の情報。 +void +BitmapDevice::EmuConsoleUpdate() +{ + const uint16 *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]; + // その文字のフォントデータ上でのオフセット + uint32 fo0 = 0x3d000 + (ch0 & 0xff) * 48; + uint32 fo1 = 0x3d000 + (ch1 & 0xff) * 48; + + // 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(*(uint16*)(&builtin_cgrom[fo0 + i * 2])); + uint32 fdat1 = be16toh(*(uint16*)(&builtin_cgrom[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ライン進める + } + } + } +}