--- nono/vm/lunafb.cpp 2026/04/29 17:05:10 1.1.1.2 +++ nono/vm/lunafb.cpp 2026/04/29 17:05:24 1.1.1.5 @@ -38,21 +38,20 @@ // バイトアクセス、ワードアクセス時は HLB(), HLW() マクロを使用のこと。 #include "lunafb.h" -#include "bt454.h" -#include "cgrom.h" -#include "config.h" +#include "bt45x.h" +#include "builtinrom.h" #include "mpu.h" // コンストラクタ LunafbDevice::LunafbDevice() - : inherited("Lunafb", 2048, 1024) + : inherited(2048, 1024) { - devaddr = 0xb1000000; - - modify_mask = 0xffff; + SetName("Lunafb"); + ClearAlias(); + AddAlias("Lunafb"); monitor.func = ToMonitorCallback(&LunafbDevice::MonitorUpdate); - monitor.SetSize(42, 11); + monitor.SetSize(39, 11); monitor.Regist(ID_MONITOR_LUNAVC); } @@ -61,33 +60,30 @@ LunafbDevice::~LunafbDevice() { } -// 初期化。 -// -// 呼び出し順が際どいので注意。Lunafb::Init() と BT454::Init() とが相互に -// 参照しているが、このうち Lunafb::Init() が呼んでる BT454::GetHostPalette() -// は BT454::Init() 実行前でも取得できる。逆に BT454::Init() が呼んでる -// Lunafb::GetPlaneCount() は Lunafb::Init() の後でないと行えないので、 -// vm_luna.cpp での初期化順は PlaneVRAM -> BT454 の順を維持すること。 +// 初期化 bool LunafbDevice::Init() { - const ConfigItem& item = gConfig->Find("luna-video-plane"); - if (item.TryInt(&nplane) == false) { - item.Err(); - return false; - } - if (nplane != 1 && nplane != 4) { - item.Err("must be 1 or 4"); + if (inherited::Init() == false) { return false; } + auto bt45x = GetBT45xDevice(); + + nplane = bt45x->GetPlaneCount(); + assert(nplane >= 0); + plane_end = plane0base + 0x40000 * nplane; + fcset_end = fcset0base + 0x40000 * nplane; // プレーン数によってメモリを確保 mem.reset(new uint8 [0x40000 * nplane]); // ホストパレットを取得。 - palette = gBT454->GetHostPalette(); + palette = &(bt45x->GetHostPalette())[0]; + + // XXX とりあえず RAM と同じウェイトを入れておく (未調査) + wait = busdata::Wait(1 * mpu->GetClock_nsec()); return true; } @@ -96,6 +92,8 @@ LunafbDevice::Init() void LunafbDevice::ResetHard(bool poweron) { + inherited::ResetHard(poweron); + if (poweron) { // XXX 実際は不定値だと思うけど、観測手段がないのでとりあえず。 memset(&mem[0], 0xff, 0x40000 * nplane); @@ -110,65 +108,65 @@ LunafbDevice::ResetHard(bool poweron) yscroll = 0; for (int i = 0; i < MAX_PLANES; i++) { - fcset[i] = 5; + fcset[i] = ROP::THRU; fcmask[i] = 0xffffffff; } - - // XXX ここ? - Invalidate(); } -uint64 +busdata LunafbDevice::Read8(uint32 addr) { - // XXX とりあえず RAM と同じウェイトを入れておく (未調査) - gMPU->AddCycle(1); + busdata data; if (plane0base <= addr && addr < plane_end) { // プレーンの読み出し - return mem[HLB(addr - plane0base)]; + data = mem[HLB(addr - plane0base)]; + } else { + // 残りは書き込み専用または不在 + putlog(1, "Read not-readable $%08x.B", addr); + data = 0xff; } - // 残りは書き込み専用 - putlog(1, "%s: 未接続アドレス $%08x", __func__, addr); - return 0xff; + data |= wait; + return data; } -uint64 +busdata LunafbDevice::Read16(uint32 addr) { - // XXX とりあえず RAM と同じウェイトを入れておく (未調査) - gMPU->AddCycle(1); + busdata data; if (plane0base <= addr && addr < plane_end) { // プレーンの読み出し - return *(uint16 *)&mem[HLW(addr - plane0base)]; + data = *(uint16 *)&mem[HLW(addr - plane0base)]; + } else { + // 残りは書き込み専用または不在 + putlog(1, "Read not-readable $%08x.W", addr); + data = 0xffff; } - // 残りは書き込み専用 - putlog(1, "%s: 未接続アドレス $%08x", __func__, addr); - return 0xffff; + data |= wait; + return data; } -uint64 +busdata LunafbDevice::Read32(uint32 addr) { - // XXX とりあえず RAM と同じウェイトを入れておく (未調査) - gMPU->AddCycle(1); + busdata data; if (plane0base <= addr && addr < plane_end) { // プレーンの読み出し - return *(uint32 *)&mem[addr - plane0base]; + data = *(uint32 *)&mem[addr - plane0base]; + } else { + // 残りは書き込み専用または不在 + putlog(1, "Read not-readable $%08x.L", addr); + data = 0xffffffff; } - // 残りは書き込み専用 - putlog(1, "%s: 未接続アドレス $%08x", __func__, addr); - return 0xffffffff; + data |= wait; + return data; } -uint64 +busdata LunafbDevice::Write8(uint32 addr, uint32 data) { - // XXX とりあえず RAM と同じウェイトを入れておく (未調査) - gMPU->AddCycle(1); - switch (addr) { case 0xb1000000: case 0xb1000001: @@ -182,49 +180,49 @@ LunafbDevice::Write8(uint32 addr, uint32 case 0xb1040002: case 0xb1040003: // BMSEL はバイトアクセスできる? - putlog(0, "未実装バイト書き込み $%08x <- $%02x", addr, data); - return 0; + putlog(0, "BMSEL $%08x <- $%02x (NOT IMPLEMENTED)", addr, data); + return wait; } if (addr < 0xb1080000) { - putlog(0, "未実装バイト書き込み $%08x <- $%02x", addr, data); - return 0; + // 空き。どうなる? + putlog(0, "Vacant $%08x <- $%02x (NOT IMPLEMENTED)", addr, data); + return wait; } if (addr < plane0base) { // 共通ビットマッププレーン WriteCommonBitmap8(addr, data); - return 0; + return wait; } - if (addr < plane7end) { - // ビットマッププレーン + if (__predict_true(addr < plane_end)) { + // 存在するビットマッププレーン uint32 reladdr = addr - plane0base; uint plane = reladdr / 0x40000; uint32 offset = reladdr % 0x40000; - if (plane < nplane) { - // ビットマッププレーンN - putlog(2, "プレーン%d書き込み $%08x <- $%02x", plane, addr, data); - WritePlane8(plane, offset, data); - SetDirty(offset); - return 0; - } - // HW 設定によっては存在しないプレーンへの書き込み - putlog(3, "不在プレーン%d書き込み $%08x <- $%02x", - plane, addr, data); - return 0; + putlog(2, "Plane%d $%08x <- $%02x", plane, addr, data); + WritePlane8(plane, offset, data); + SetDirty(offset); + return wait; + } + + if (addr < plane7end) { + // HW 設定によっては存在しないプレーン + uint32 reladdr = addr - plane0base; + uint plane = reladdr / 0x40000; + putlog(3, "Not-equipped Plane%d $%08x <- $%02x", plane, addr, data); + return wait; } - putlog(0, "未実装バイト書き込み $%08x <- $%02x", addr, data); - return 0; + // XXX ファンクションセットはバイトアクセスだとどうなる? + putlog(0, "$%08x <- $%02x (NOT IMPLEMENTED)", addr, data); + return wait; } -uint64 +busdata LunafbDevice::Write16(uint32 addr, uint32 data) { - // XXX とりあえず RAM と同じウェイトを入れておく (未調査) - gMPU->AddCycle(1); - switch (addr) { case 0xb1000000: case 0xb1000002: @@ -234,124 +232,125 @@ LunafbDevice::Write16(uint32 addr, uint3 case 0xb1040000: case 0xb1040002: // BMSEL はワードアクセスできる? - putlog(0, "未実装ワード書き込み $%08x <- $%04x", addr, data); - return 0; + putlog(0, "BMSEL $%08x <- $%04x (NOT IMPLEMENTED)", addr, data); + return wait; } if (addr < 0xb1080000) { - putlog(0, "未実装ワード書き込み $%08x <- $%04x", addr, data); - return 0; + // 空き。どうなる? + putlog(0, "Vacant $%08x <- $%04x (NOT IMPLEMENTED)", addr, data); + return wait; } if (addr < plane0base) { // 共通ビットマッププレーン WriteCommonBitmap16(addr, data); - return 0; + return wait; } - if (addr < plane7end) { - // ビットマッププレーン + if (__predict_true(addr < plane_end)) { + // 存在するビットマッププレーン uint32 reladdr = addr - plane0base; uint plane = reladdr / 0x40000; uint32 offset = reladdr % 0x40000; - if (plane < nplane) { - // ビットマッププレーンN - putlog(2, "プレーン%d書き込み $%08x <- $%04x", plane, addr, data); - WritePlane16(plane, offset, data); - SetDirty(offset); - return 0; - } - // HW 設定によっては存在しないプレーンへの書き込み - putlog(3, "不在プレーン%d書き込み $%08x <- $%04x", - plane, addr, data); - return 0; + putlog(2, "Plane%d $%08x <- $%04x", plane, addr, data); + WritePlane16(plane, offset, data); + SetDirty(offset); + return wait; } - putlog(0, "未実装ワード書き込み $%08x <- $%04x", addr, data); - return 0; + if (addr < plane7end) { + // HW 設定によっては存在しないプレーン + uint32 reladdr = addr - plane0base; + uint plane = reladdr / 0x40000; + putlog(3, "Not-equipped Plane%d $%08x <- $%04x", plane, addr, data); + return wait; + } + + // XXX ファンクションセットはワードアクセスだとどうなる? + putlog(0, "$%08x <- $%04x (NOT IMPLEMENTED)", addr, data); + return wait; } -uint64 +busdata LunafbDevice::Write32(uint32 addr, uint32 data) { - // XXX とりあえず RAM と同じウェイトを入れておく (未調査) - gMPU->AddCycle(1); - switch (addr) { case 0xb1000000: // RFCNT WriteRFCNT(data); - return 0; + return wait; case 0xb1040000: // BMSEL bmsel = data & 0xf; putlog(1, "BMSEL <- $%08x", data); - return 0; + return wait; } if (addr < 0xb1080000) { - putlog(0, "未実装ロング書き込み $%08x <- $%08x", addr, data); - return 0; + putlog(0, "Vacant $%08x <- $%08x (NOT IMPLEMENTED)", addr, data); + return wait; } if (addr < plane0base) { // 共通ビットマッププレーン WriteCommonBitmap32(addr, data); - return 0; + return wait; } - if (addr < plane7end) { - // ビットマッププレーン + if (__predict_true(addr < plane_end)) { + // 存在するビットマッププレーン uint32 reladdr = addr - plane0base; uint plane = reladdr / 0x40000; uint32 offset = reladdr % 0x40000; - if (plane < nplane) { - // ビットマッププレーンN - putlog(3, "プレーン%d書き込み $%08x <- $%08x", plane, addr, data); - WritePlane32(plane, offset, data); - SetDirty(offset); - return 0; - } - // HW 設定によっては存在しないプレーンへの書き込み - putlog(3, "不在プレーン%d書き込み $%08x <- $%08x", - plane, addr, data); - return 0; + putlog(3, "Plane%d $%08x <- $%08x", plane, addr, data); + WritePlane32(plane, offset, data); + SetDirty(offset); + return wait; + } + + if (__predict_false(addr < plane7end)) { + // HW 設定によっては存在しないプレーン + uint32 reladdr = addr - plane0base; + uint plane = reladdr / 0x40000; + putlog(3, "Not-equipped Plane%d $%08x <- $%08x", plane, addr, data); + return wait; } if (addr < 0xb1300000) { // 共通ファンクションセット uint rop = (addr & 0x3f) / 4; - putlog(2, "共通FCSet 書き込み $%08x <- $%08x (%s)", + putlog(2, "Common FCSet $%08x <- $%08x (%s)", addr, data, RopStr(rop)); WriteCommonFCSet(rop, data); - return 0; + return wait; } - if (addr < fcset7end) { - // ファンクションセット + if (__predict_true(addr < fcset_end)) { + // 存在するファンクションセット int plane = (addr - 0xb1300000) / 0x40000; - - if (plane < nplane) { - // ファンクションセットN - uint rop = (addr & 0x3f) / 4; - putlog(2, "FCSet%d書き込み $%08x <- $%08x (%s)", + uint rop = (addr & 0x3f) / 4; + putlog(2, "FCSet%d $%08x <- $%08x (%s)", plane, addr, data, RopStr(rop)); - fcset[plane] = rop; - fcmask[plane] = data; - return 0; - } - // HW 設定によっては存在しない FCSet への書き込み - putlog(3, "不在FCSet%d書き込み $%08x <- $%08x", - plane, addr, data); - return 0; + fcset[plane] = rop; + fcmask[plane] = data; + return wait; } - putlog(0, "未実装ロング書き込み $%08x <- $%08x", addr, data); - return 0; + if (addr < fcset7end) { + // HW 設定によっては存在しないファンクションセット + int plane = (addr - 0xb1300000) / 0x40000; + putlog(3, "Not-equipped FCSet%d $%08x <- $%08x", plane, addr, data); + return wait; + } + + // 残りは全部空きのはずだが挙動未調査。 + putlog(0, "$%08x <- $%08x (NOT IMPLEMENTED)", addr, data); + return wait; } -uint64 +busdata LunafbDevice::Peek8(uint32 addr) { if (plane0base <= addr && addr < plane_end) { @@ -362,6 +361,24 @@ LunafbDevice::Peek8(uint32 addr) return 0xff; } +bool +LunafbDevice::Poke8(uint32 addr, uint32 data) +{ + // 通常プレーンのみ越権書き込み可能とする? + if (plane0base <= addr && addr < plane_end) { + if ((int32)data >= 0) { + uint32 reladdr = addr - plane0base; + uint32 offset = reladdr % 0x40000; + // ROP の影響は受けずにメモリの内容を変更 + mem[HLB(reladdr)] = data; + // この場合も画面を更新する + SetDirty(offset); + } + return true; + } + return false; +} + void LunafbDevice::MonitorUpdate(Monitor *, TextScreen& screen) { @@ -373,9 +390,9 @@ LunafbDevice::MonitorUpdate(Monitor *, T // 012345678901234567890123456789012345678901 // RFCNT: $12345678 (X=-123dot, Y=1023dot) // - // BMSEL=$4 ROP Mask RGB RGB - // Plane#0 Sel THRU $ffffffff $0:RGB $8:RGB - screen.Print(0, 0, "RFCNT: $%08x (X=%ddot, Y=%ddot)", + // BMSEL=$4 ROP Mask + // Plane#0 Sel THRU $ffffffff + screen.Print(0, 0, "RFCNT: $%08x (X=%4ddot, Y=%4ddot)", rfcnt, xscroll, yscroll); screen.Print(3, 2, "BMSEL=$%x ROP Mask", bmsel); @@ -388,62 +405,38 @@ LunafbDevice::MonitorUpdate(Monitor *, T for (; i < MAX_PLANES; i++) { screen.Print(0, 3 + i, TA::Disable, "Plane#%d (Not installed)", i); } - - // ついでなのでここで BT454 のパレットも表示。 - // 012345 - // $0:RGB - const uint8 *pal = gBT454->reg.color; - screen.Puts(29, 1, ""); - screen.Puts(32, 2, "RGB RGB"); - for (i = 0; i < 16; i++) { - uint rgb = - ((pal[i * 3 + 0] & 0xf0) << 4) + - ((pal[i * 3 + 1] & 0xf0) ) + - ((pal[i * 3 + 2] & 0xf0) >> 4); - screen.Print(29 + (i / 8) * 7, (i % 8) + 3, "$%x:%03x", i, rgb); - } } // offset の位置に対応する更新フラグを立てる。 -// offset はプレーン先頭から (VRAM 先頭からでも同じ) のバイトオフセット。 +// offset はプレーン先頭からのバイトオフセット。 inline void LunafbDevice::SetDirty(uint32 offset) { - // VRAM は横 2048 ドット(256バイト) x 縦 1024 ドットで、これを - // 横 128 ドット(16バイト) x 縦 16 ドットずつのブロックに分割する。 - // ブロック数は横 16個 x 縦 64個。 + // VRAM は横 2048 ドット(256バイト) x 縦 1024 ドット。 // // 1 0 // 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0 // +-----------+-------+-------+-------+-----+ // VRAM | Y座標 (1024dot) | X (256byte) : 8bit| // +-----------+-------+-------+-------+-----+ - // : : : : : - // +-----------+-------+-------+-------------+ - // Block | Y(64blks) | 16dot | X(16) | 128dot | *: X は 16 blocks - // +-----------+-------+-------+-------------+ - // \ \ | | - // \ \ | | - // \ \ | | - // +-----------+-------+ - // Dirty | Y(64blks) | X(16) | - // +-----------+-------+ - // 9 8 7 6 5 4 3 2 1 0 - - // x 座標(バイト単位 8 bit)の上位 4 bit - int x = (offset >> 4) & 0x0f; - // y 座標(10bit) の上位 6 bit - int y = (offset >> 8) & 0x3f0; - - // からインデックスを求める - int b = x + y; - - dirty[b] = 1; - -#if defined(PERF_HIT) - // どのロングワードが更新されたか - dirty_word[offset >> 2] = true; -#endif + // : : + // +-------------------+ + // Dirty | | + // +-------------------+ + // 9 8 7 6 5 4 3 2 1 0 + + // 更新フラグはラスタ単位 + int y = (offset >> 8); + dirty[y] = 1; +} + +// X 方向にはみ出したときに対応する仮想画面の Y 座標を返す。 +int +LunafbDevice::GetWrapY(int y) const +{ + // Lunafb の VRAM は、はみ出すと 256 ラスタ先のラスタの先頭から + // 表示されるようだ。 + return (y + 256) % 1024; } // RFCNT への書き込み @@ -453,23 +446,31 @@ LunafbDevice::WriteRFCNT(uint32 data) // レジスタ値 rfcnt = data; - // 水平オフセットを計算 - // XXX マニュアルでは -8 まで指定できるように読めるが - // 指定する方法が分からない。 - // XXX 193 以上を指定するとどうなるのか (とりあえずほっとく) - // 実機の挙動を目分量で見ると、右にはみ出た部分は - // 256 ラスタ先のラスタデータを描画しているようだ。 - xscroll = ((rfcnt >> 16) & 0xff) + 8; - xscroll *= 4; + // 水平オフセットを計算。 + // レジスタに設定できる値は 0..255 で、何ピクセル目から表示を開始するかを + // 4 ピクセル単位で指定するもの。 + // ただし、実際に表示されるのは、(4ピクセル単位で) 設定値 +9 の位置から + // (謎資料には 8 (というか -8) と書いてあるが実測は +9 のようだ)。 + // 設定値が 0 の時 X=36 ピクセル目から 1315 ピクセル目までを表示。 + // : + // 設定値が 183 の時 X=768 ピクセル目から 2047 ピクセル目までを表示。 + // これが正常に表示できる範囲となる。 + // 184 以降を指定すると、右のはみ出る部分 (2048 ピクセル目以降に相当) は + // 256 ラスタ先の X=0 からのデータが描画される (GetWrapY() 参照)。 + // 表示開始位置は設定値に必ず +9 (*4 [pixel]) されるため、X=0..35 の + // ピクセルを (折り返しにより Y がずれた位置に見えることを除けば) + // まともに表示する方法はない。まじかよ。 + xscroll = (rfcnt >> 16) & 0xff; + xscroll = (xscroll + 9) * 4; - // ラスタ(垂直)カウンタ + // ラスタ(垂直)カウンタ。 + // XXX 26 は実際には CRTC2 によるパラメータ yscroll = (rfcnt + 26) & 0x3ff; // 全画面更新 Invalidate(); - putlog(1, "RFCNT <- $%08x (水平 %ddot, 垂直 %ddot)", - data, xscroll, yscroll); + putlog(1, "RFCNT <- $%08x (H=%ddot, V=%ddot)", data, xscroll, yscroll); } // 共通ビットマッププレーン バイト書き込み @@ -477,7 +478,7 @@ void LunafbDevice::WriteCommonBitmap8(uint32 addr, uint32 data) { uint32 offset = addr - 0xb1080000; - putlog(2, "共通プレーン書き込み $%08x <- $%02x", addr, data); + putlog(2, "Common Plane $%08x <- $%02x", addr, data); for (int i = 0; i < nplane; i++) { if ((bmsel & (1 << i))) { WritePlane8(i, offset, data); @@ -491,7 +492,7 @@ void LunafbDevice::WriteCommonBitmap16(uint32 addr, uint32 data) { uint32 offset = addr - 0xb1080000; - putlog(2, "共通プレーン書き込み $%08x <- $%04x", addr, data); + putlog(2, "Common Plane $%08x <- $%04x", addr, data); for (int i = 0; i < nplane; i++) { if ((bmsel & (1 << i))) { WritePlane16(i, offset, data); @@ -505,7 +506,7 @@ void LunafbDevice::WriteCommonBitmap32(uint32 addr, uint32 data) { uint32 offset = addr - 0xb1080000; - putlog(2, "共通プレーン書き込み $%08x <- $%08x", addr, data); + putlog(2, "Common Plane $%08x <- $%08x", addr, data); for (int i = 0; i < nplane; i++) { if ((bmsel & (1 << i))) { WritePlane32(i, offset, data); @@ -543,14 +544,17 @@ LunafbDevice::RasterOp(uint rop, uint32 vram = vram & ~data; break; case ROP::cmd: + { // 使用不可。本当はここにシリアルモード変更コマンドがあるらしい。 // ログを出すためにはインスタンスが必要だが、普段は static 関数で // 十分なので、ログを出す時だけグローバルインスタンスを参照する。 - if (gPlaneVRAM->loglevel >= 0) { - gPlaneVRAM->putlogn("ROP 3 is used"); + auto planevram = GetPlaneVRAMDevice(); + if (planevram->loglevel >= 0) { + planevram->putlogn("ROP 3 is used"); } break; + } case ROP::AND3: vram = ~vram & data; break; @@ -588,7 +592,7 @@ LunafbDevice::RasterOp(uint rop, uint32 vram = 0xffffffff; break; default: - __unreachable(); + VMPANIC("corrupted rop=%d", (int)rop); } return vram; } @@ -669,6 +673,59 @@ LunafbDevice::WritePlane32(uint plane, u *(uint32 *)&mem[memoffset] = (data & mask) | (vram & ~mask); } +// (x, y) 位置のピクセル情報を screen に描画。(ビットマップモニタの情報欄) +void +LunafbDevice::UpdateInfo(TextScreen& screen, int x, int y) const +{ + screen.Clear(); + + //0 1 2 3 4 5 6 + //012345678901234567890123456789012345678901234567890123456789012345 + //X=1024 Y=2024: (P0) $b10c0000: $00 (%00000000) ColorCode=$d + + screen.Puts(0, 0, "X= Y="); + for (int i = 0; i < nplane; i++) { + screen.Print(15, i, "(P%d)", i); + } + + // カーソルが範囲外ならここまで。 + if (x < 0) { + return; + } + + screen.Print(2, 0, "%4d Y=%4d", x, y); + + // 1プレーン内のバイトオフセット + uint32 planeoffset = ((uint)y * 256) + ((uint)x / 8); + + uint cc = 0; + for (int i = 0; i < nplane; i++) { + // プレーン0先頭からのバイトオフセット + uint32 totaloffset = 0x40000 * i + planeoffset; + + uint8 data = mem[HLB(totaloffset)]; + screen.Print(15, i, "(P%d) $%08x: $%02x (%%", + i, plane0base + totaloffset, data); + + // ビットパターン + for (int m = 0x80; m != 0; m >>= 1) { + screen.Putc((data & m) ? '1' : '0'); + } + screen.Putc(')'); + + // カーソル位置をハイライト + screen.Locate(37 + (x % 8), i); + screen.SetAttr(TA::Em); + + // カラーコードを計算 + if ((data & (0x80U >> (x % 8))) != 0) { + cc |= (1U << i); + } + } + + screen.Print(47, 0, "ColorCode=$%x", cc); +} + // エミュレータによる画面出力 // @@ -690,6 +747,9 @@ LunafbDevice::EmuInitScreen() // 全画面をクリア EmuFill(0, 0, 2048, 1024, 0); + + // 書き込みはプレーン#0 だけ。 + bmsel = 0x01; } // 矩形領域を data で埋める。 @@ -724,6 +784,7 @@ LunafbDevice::EmuPutc(uint x, uint y, ui { uint32 addr = 0xb1080000 + (x / 32) * 4 + y * 256; uint32 bofs = x % 32; + int height = (c < 0x80) ? 24 : 12; // マスク設定。上から 12bit にマスクを置いといて.. union64 mask {}; @@ -733,9 +794,11 @@ LunafbDevice::EmuPutc(uint x, uint y, ui mask.q >>= bofs; // その文字のフォントデータ上でのアドレス - const uint8 * const fontaddr = BuiltinCGROM::Get12x24(c & 0xff); + const uint8 * const fontaddr = (c < 0x80) + ? Builtin::CGROM12x24(c) + : Builtin::CGROM12x12(c & 0x7f); - for (int i = 0; i < 24; i++) { + for (int i = 0; i < height; i++) { // その文字のフォントデータ。 // データは MSB 側 12bit に入っているのでこれを 64bit MSB 詰めにする union64 data {};