--- nono/vm/bitmap.cpp 2026/04/29 17:04:28 1.1 +++ nono/vm/bitmap.cpp 2026/04/29 17:04:38 1.1.1.5 @@ -1,10 +1,12 @@ // // 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" // // ビットマッププレーン @@ -34,27 +36,29 @@ // 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); // XXX とりあえず。実際は設定による nplane = 1; // プレーン数によってメモリを確保 - mem = new uint8 [0x40000 * nplane]; + mem.reset(new uint8 [0x40000 * nplane]); } BitmapDevice::~BitmapDevice() { - if (mem) { - delete[] mem; - } } void @@ -315,8 +319,8 @@ BitmapDevice::Peek8(uint32 addr) return 0xff; } -bool -BitmapDevice::MonitorUpdate() +void +BitmapDevice::MonitorUpdate(TextScreen& monitor) { int i; @@ -346,8 +350,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 +359,6 @@ BitmapDevice::MonitorUpdate() ((pal[i * 3 + 2] & 0xf0) >> 4); monitor.Print(29 + (i / 8) * 7, (i % 8) + 3, "$%x:%03x", i, rgb); } - - return true; } // 全画面の更新フラグを立てる @@ -385,14 +387,7 @@ BitmapDevice::WriteRFCNT(uint32 data) hscroll *= 4; // ラスタ(垂直)カウンタ - // マニュアルには +26 オフセットすると書いてあるように読めるが、 - // 手元の実機では +49 でいい感じになるようだ。なんでかは知らん。 - // ROM モニタで - // > b1000000;,a - // > h - // > h - // とした時の画面上下のヘルプメッセージのフォントの見切れ方で検証。 - vscroll = (rfcnt + 49) & 0x3ff; + vscroll = (rfcnt + 26) & 0x3ff; // 全画面更新 Invalidate(); @@ -569,3 +564,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]; + // その文字のフォントデータ上でのオフセット + 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ライン進める + } + } + } +}