--- nono/vm/gvram.cpp 2026/04/29 17:04:55 1.1.1.5 +++ nono/vm/gvram.cpp 2026/04/29 17:05:51 1.1.1.11 @@ -4,68 +4,255 @@ // Licensed under nono-license.txt // -#include "gvram.h" -#include "mpu.h" -#include "videoctlr.h" - // // GVRAM // + // GVRAM はワード単位でホストバイトオーダ配置なので、 // バイトアクセス時は HB() マクロを使用のこと。 -// -std::unique_ptr gGVRAM; +#include "gvram.h" +#include "event.h" +#include "textscreen.h" +#include "videoctlr.h" +// InsideOut p.135 +/*static*/ const busdata +GVRAMDevice::wait = busdata::Wait(9 * 40_nsec); + +// コンストラクタ GVRAMDevice::GVRAMDevice() - : inherited("GVRAM") + : inherited(OBJ_GVRAM) { - devaddr = 0xc00000; - devlen = 0x200000; // XXX とりあえず - - mem.reset(new uint8[devlen]); + composite.Create(1024, 1024); } +// デストラクタ GVRAMDevice::~GVRAMDevice() { } -uint64 -GVRAMDevice::Read8(uint32 addr) +// 初期化 +bool +GVRAMDevice::Init() { - gMPU->AddCycle(9); // Inside/Out p.135 - uint32 offset = addr - devaddr; - return mem[HB(offset)]; + uint devlen = 0x20'0000; // XXX とりあえず + mem.reset(new(std::nothrow) uint8[devlen]); + if ((bool)mem == false) { + warnx("Cannot allocate %u bytes at %s", devlen, __method__); + return false; + } + + videoctlr = GetVideoCtlrDevice(); + + // 加工済みグラフィックパレットを取得。 + palette = &(videoctlr->GetHostPalette())[0]; + + return true; } -uint64 -GVRAMDevice::Read16(uint32 addr) +void +GVRAMDevice::ResetHard(bool poweron) { - gMPU->AddCycle(9); // Inside/Out p.135 - uint32 offset = addr - devaddr; - return *(uint16 *)&mem[offset]; + // XXX ここ? + Invalidate(); } -uint64 -GVRAMDevice::Write8(uint32 addr, uint32 data) +// 全画面の更新フラグを立てる。 +void +GVRAMDevice::Invalidate() { - gMPU->AddCycle(9); // Inside/Out p.135 - uint32 offset = addr - devaddr; - mem[HB(offset)] = data; - return 0; + dirty.line.set(); } -uint64 -GVRAMDevice::Write16(uint32 addr, uint32 data) +inline uint32 +GVRAMDevice::Decoder(uint32 addr) const { - gMPU->AddCycle(9); // Inside/Out p.135 - uint32 offset = addr - devaddr; - *(uint16 *)&mem[offset] = data; - return 0; + return addr - baseaddr; +} + +busdata +GVRAMDevice::Read(busaddr addr) +{ + uint32 offset = Decoder(addr.Addr()); + busdata data; + + data = *(uint16 *)&mem[offset & ~1U]; + data |= wait; + data |= BusData::Size2; + return data; +} + +busdata +GVRAMDevice::Write(busaddr addr, uint32 data) +{ + uint32 offset = Decoder(addr.Addr()); + uint32 reqsize = addr.GetSize(); + uint32 datasize = std::min(2 - (offset & 1U), reqsize); + data >>= (reqsize - datasize) * 8; + + // 現状 1024x1024 (4bit) しか対応していない。 + if (__predict_false(datasize == 1)) { + if ((offset & 1) != 0) { + mem[HB(offset)] = data & 0x0f; + } + } else { + *(uint16 *)&mem[offset] = data & 0x0f; + } + SetDirty(offset); + + busdata r = wait; + r |= busdata::Size(datasize); + return r; +} + +busdata +GVRAMDevice::Peek1(uint32 addr) +{ + uint32 offset = Decoder(addr); + return mem[HB(offset)]; } -uint64 -GVRAMDevice::Peek8(uint32 addr) +bool +GVRAMDevice::Poke1(uint32 addr, uint32 data) { - return mem[HB(addr - devaddr)]; + if ((int32)data >= 0) { + uint32 offset = Decoder(addr); + // 現状 1024x1024 モードしか対応していないため + // 有効なのはワード中下位4ビット。 + if ((offset & 1) != 0) { + mem[HB(offset)] = data & 0x0f; + // この場合も画面を更新する。 + SetDirty(offset); + } + } + return true; +} + +// offset の位置に対応する更新フラグを立てる。 +// offset はページ先頭からのバイトオフセット。 +inline void +GVRAMDevice::SetDirty(uint32 offset) +{ + // GVRAM (1024x1024 モード) は + // 横 1024 ドット(2048バイト) 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 + // +-------------------+---------------------+ + // GVRAM | Y座標 (1024dot) | X座標 (1024word) | + // +-------------------+---------------------+ + // : : + // +-------------------+ + // | | + // +-------------------+ + // 9 8 7 6 5 4 3 2 1 0 + + // 更新フラグはラスタ単位 + uint y = offset >> 11; + dirty.line.set(y); +} + +// 画面更新する必要があるか。 +// VM スレッドで呼ばれる。 +bool +GVRAMDevice::Snap() +{ + bool update_needed; + + // 更新 (dirty) があれば pending に重ねる。 + { + std::lock_guard lock(mtx); + pending.line |= dirty.line; + dirty.line.reset(); + + if (__predict_false(dirty.invalidate2)) { + pending.invalidate2 = true; + dirty.invalidate2 = false; + } + + update_needed = pending.line.any() || pending.invalidate2; + } + + return update_needed; +} + +// 画面合成。 +// レンダラスレッドで VideoCtlr から呼ばれる。 +// dst を更新すれば true を返す。 +bool +GVRAMDevice::Render(BitmapRGBX& dst) +{ + bool updated = false; + + // ローカルにコピー。 + ModifyInfo modified; + { + std::lock_guard lock(mtx); + modified.line = pending.line; + modified.invalidate2 = pending.invalidate2; + pending.line.reset(); + pending.invalidate2 = false; + } + + // GVRAM に更新があれば composite を更新。 + if (modified.line.any()) { + Render1024ToComposite(modified.line); + updated = true; + } + + // composite に更新があるか(updated)、 + // 無条件に更新するか(modified.invalidate2) なら dst を更新。 + if (updated || modified.invalidate2) { + RenderCompositeToRGBX(dst, modified); + updated = true; + } + + return updated; +} + +// GVRAM (1024x1024、16色モード) から composite 画面を合成する。 +// レンダラスレッドから呼ばれる。 +void +GVRAMDevice::Render1024ToComposite(const bitset1024& modified) +{ + constexpr uint xend = 1024; + constexpr uint yend = 1024; + for (uint y = 0; y < yend; y++) { + if (__predict_true(modified.test(y) == false)) { + continue; + } + + const uint16 *src = (const uint16 *)&mem[0]; + src += y * 1024; + uint8 *dst = (uint8 *)composite.GetRowPtr(y); + for (uint x = 0; x < xend; x++) { + *dst++ = *src++; + } + } +} + +void +GVRAMDevice::RenderCompositeToRGBX(BitmapRGBX& dst, + const ModifyInfo& modified) +{ + dst.DrawBitmapI8(0, 0, composite, palette); +} + +// (x, y) 位置のピクセル情報を screen に出力。(ビットマップモニタの情報欄) +void +GVRAMDevice::UpdateInfo(TextScreen& screen, int x, int y) const +{ + screen.Clear(); + + // 0 1 2 3 4 5 6 + // 012345678901234567890123456789012345678901234567890123456789012345 + // X=1023 Y=1023: ColorCode=$d + + screen.Puts(0, 0, "X= Y="); + if (x >= 0) { + const uint16 *mem16 = (const uint16 *)&mem[0]; + uint16 cc = mem16[y * 1024 + x]; + screen.Print(2, 0, "%4d Y=%4d Colorcode=$%x", x, y, cc); + } }