--- nono/vm/tvram.cpp 2026/04/29 17:05:14 1.1.1.8 +++ nono/vm/tvram.cpp 2026/04/29 17:05:33 1.1.1.12 @@ -12,21 +12,25 @@ // バイトアクセス時は HLB()、ワードアクセス時は HLW() マクロを使用のこと。 #include "tvram.h" -#include "mpu.h" +#include "textscreen.h" #include "videoctlr.h" +// InsideOut p.135 +static const busdata wait = busdata::Wait(9 * 40_nsec); + // コンストラクタ TVRAMDevice::TVRAMDevice() - : inherited("TVRAM", 1024, 1024) + : inherited(1024, 1024) { - devaddr = baseaddr; - devlen = 512 * 1024; + SetName("TVRAM"); + ClearAlias(); + AddAlias("TVRAM"); + + uint devlen = 512 * 1024; // プレーン数は4枚固定 nplane = NPLANE; - modify_mask = 0xff00; - mem.reset(new uint8[devlen]); } @@ -35,168 +39,164 @@ TVRAMDevice::~TVRAMDevice() { } +// 初期化 bool TVRAMDevice::Init() { + if (inherited::Init() == false) { + return false; + } + + videoctlr = GetVideoCtlrDevice(); + // 加工済みパレットを取得 - palette = gVideoCtlr->GetHostPalette(); + palette = &(videoctlr->GetHostPalette())[0]; return true; } -// リセット -void -TVRAMDevice::ResetHard(bool poweron) -{ - // XXX ここ? - Invalidate(); -} - -inline uint64 +inline uint32 TVRAMDevice::Decoder(uint32 addr) const { return addr - baseaddr; } -uint64 -TVRAMDevice::Read8(uint32 addr) +busdata +TVRAMDevice::Read(busaddr addr) { - gMPU->AddCycle(9); // Inside/Out p.135 - uint32 offset = Decoder(addr); - return mem[HLB(offset)]; + busdata data; + uint32 offset = Decoder(addr.Addr()); + data = *(uint32 *)&mem[offset & ~3U]; + data |= wait; + data |= BusData::Size4; + return data; +} + +busdata +TVRAMDevice::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; + + uint32 mask; + if (tvram.men) { + if (datasize == 1) { + if ((offset & 1U) == 0) { + mask = tvram.mask >> 8; + } else { + mask = tvram.mask & 0xff; + } + } else { + mask = tvram.mask; + } + } else { + mask = 0; + } + + uint32 poffset = offset & 0x1ffff; + if (datasize == 1) { + Set8(offset, poffset, data, mask); + } else { + Set16(offset, poffset, data, mask); + } + SetDirty(poffset); + + busdata r = wait; + r |= busdata::Size(datasize); + return r; } -uint64 -TVRAMDevice::Read16(uint32 addr) +busdata +TVRAMDevice::Peek1(uint32 addr) { - gMPU->AddCycle(9); // Inside/Out p.135 uint32 offset = Decoder(addr); - return *(uint16 *)&mem[HLW(offset)]; + return mem[HLB(offset)]; } -uint64 -TVRAMDevice::Write8(uint32 addr, uint32 data) +bool +TVRAMDevice::Poke1(uint32 addr, uint32 data) { - uint32 offset = Decoder(addr); - uint8 *ptr; - uint8 mask8; - - gMPU->AddCycle(9); // Inside/Out p.135 - - // XXX とりあえずね - putlog(3, "Write $%06x.B <- $%02x", addr, data); - - if (tvram.men) { - if ((addr & 1) == 0) { - mask8 = tvram.mask >> 8; - } else { - mask8 = tvram.mask; - } - } else { - mask8 = 0; + if ((int32)data >= 0) { + uint32 offset = Decoder(addr); + uint32 poffset = offset & 0x1ffff; + mem[HLB(offset)] = data; + // この場合も画面を更新する + SetDirty(poffset); } + return true; +} +// マスクと同時アクセスを含むバイト書き込み。 +void +TVRAMDevice::Set8(uint32 offset, uint32 poffset, uint32 data, uint32 mask) +{ if (tvram.sa) { // 同時アクセス - uint32 poffset = offset & 0x1ffff; for (int plane = 0; plane < 4; plane++) { if (tvram.ap[plane]) { - ptr = &mem[HLB(0x20000 * plane + poffset)]; - *ptr = (data & ~mask8) | (*ptr & mask8); + uint8 *p = &mem[HLB(0x20000 * plane + poffset)]; + *p = (data & ~mask) | (*p & mask); } } } else { // 通常アクセス - ptr = &mem[HLB(offset)]; - *ptr = (data & ~mask8) | (*ptr & mask8); + uint8 *p = &mem[HLB(offset)]; + *p = (data & ~mask) | (*p & mask); } - SetDirty(offset); - return 0; } -uint64 -TVRAMDevice::Write16(uint32 addr, uint32 data) +// マスクと同時アクセスを含むワード書き込み。 +void +TVRAMDevice::Set16(uint32 offset, uint32 poffset, uint32 data, uint32 mask) { - uint32 offset = Decoder(addr); - uint16 *ptr; - uint16 mask16; - - gMPU->AddCycle(9); // Inside/Out p.135 - - // XXX とりあえずね - putlog(3, "Write $%06x.W <- $%04x", addr, data); - - if (tvram.men) { - mask16 = tvram.mask; - } else { - mask16 = 0; - } - if (tvram.sa) { // 同時アクセス - uint32 poffset = offset & 0x1ffff; for (int plane = 0; plane < 4; plane++) { if (tvram.ap[plane]) { - ptr = (uint16 *)&mem[HLW(0x20000 * plane + poffset)]; - *ptr = (data & ~mask16) | (*ptr & mask16); + uint16 *p = (uint16 *)&mem[HLW(0x20000 * plane + poffset)]; + *p = (data & ~mask) | (*p & mask); } } } else { // 通常アクセス - ptr = (uint16 *)&mem[HLW(offset)]; - *ptr = (data & ~mask16) | (*ptr & mask16); + uint16 *p = (uint16 *)&mem[HLW(offset)]; + *p = (data & ~mask) | (*p & mask); } - SetDirty(offset); - return 0; -} - -uint64 -TVRAMDevice::Peek8(uint32 addr) -{ - uint32 offset = Decoder(addr); - return mem[HLB(offset)]; } // offset の位置に対応する更新フラグを立てる。 -// offset は TVRAM 先頭からのバイトオフセット (プレーン先頭からではない)。 +// offset はプレーン先頭からのバイトオフセット。 inline void TVRAMDevice::SetDirty(uint32 offset) { - // VRAM は横 1024 ドット(128バイト) x 縦 1024 ドットで、これを - // 横 128 ドット(16バイト) x 縦 16 ドットずつのブロックに分割する。 - // ブロック数は横 8個 x 縦 64個。 + // VRAM は横 1024 ドット(128バイト) x 縦 1024 ドット。 // // 1 0 // 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0 // +-----------+-------+-----+-------+-----+ // VRAM | Y座標 (1024dot) | X (128byte) : 8bit| // +-----------+-------+-----+-------+-----+ - // : : : : : - // +-----------+-------+-----+-------------+ - // Block | Y(64blks) | 16dot | X(8)| 128 dot | *: X は 8 blocks - // +-----------+-------+-----+-------------+ - // \ \ | | - // \ \ | | - // \ \ | | - // +-----------+-----+ - // Dirty | Y(64blks) | X(8)| - // +-----------+-----+ - // 8 7 6 5 4 3 2 1 0 - - // x 座標(バイト単位 7 bit)の上位 3 bit - int x = (offset >> 4) & 0x07; - // y 座標(10bit) の上位 6 bit - int y = (offset >> 8) & 0x1f8; - - // からインデックスを求める - int b = x + y; - - dirty[b] = 1; - -#if defined(PERF_HIT) - // どのロングワードが更新されたか - dirty_word[(offset & 0x1ffff) >> 2] = true; -#endif + // : : + // +-------------------+ + // Dirty | | + // +-------------------+ + // 9 8 7 6 5 4 3 2 1 0 + + // 更新フラグはラスタ単位 + uint y = offset >> 7; + dirty.line[y] = true; +} + +// X 方向にはみ出したときに対応する仮想画面の Y 座標を返す。 +int +TVRAMDevice::GetWrapY(int y) const +{ + // はみ出すと次のラスタの先頭から表示するが、 + // TVRAM は 4 ラスタがひとかたまりで構成されていて、 + // 末尾 2bit が 3 のラスタの次は、末尾だけ 0 のラスタに戻る。ようだ。 + return (y & ~3) + ((y + 1) & 3); } void @@ -209,7 +209,7 @@ void TVRAMDevice::SetScrollY(int y) { yscroll = y; - gRenderer->Invalidate2(); + Invalidate2(); } // CRTC R21 での同時アクセス設定を反映する。 @@ -252,7 +252,7 @@ TVRAMDevice::SetAccessMask(uint16 data) // ラスターコピーを開始する。 // src_ras, dst_ras はライン番号 (レジスタ設定値を4倍したもの)。 void -TVRAMDevice::RasterCopy(int src_ras, int dst_ras) +TVRAMDevice::RasterCopy(uint src_ras, uint dst_ras) { for (int plane = 0; plane < 4; plane++) { if (tvram.cp[plane]) { @@ -263,23 +263,60 @@ TVRAMDevice::RasterCopy(int src_ras, int } // dst からの4ラインを全部 dirty にする。 -#if !defined(PERF_HIT) - // dirty は、1ブロックが縦 16dot 収容しているので、4の倍数から始まる - // 4ラインは必ず1ブロックに収まる。横方向は1ブロックにつき 128dot 収容して - // いるので全部で8ブロック。 - // よって dirty を立てるだけなら 8回の呼び出しで済む。 - for (int x = 0; x < 8; x++) { - SetDirty(dst_ras * 128 + (x << 4)); + for (int y = 0; y < 4; y++) { + SetDirty((dst_ras + y) * 128); } -#else - // PERF_HIT の更新を行う場合は律儀に全プレーンの全ロングワード分ループする - for (int plane = 0; plane < 4; plane++) { - for (int i = 0; i < 4; i++) { - int y = dst_ras + i; - for (int x = 0; x < 128 / sizeof(uint32); x++) { - SetDirty(0x20000 * plane + y * 128 + x * sizeof(uint32)); - } +} + +// (x, y) 位置のピクセル情報を screen に描画。(ビットマップモニタの情報欄) +void +TVRAMDevice::UpdateInfo(TextScreen& screen, int x, int y) const +{ + screen.Clear(); + + //0 1 2 3 4 5 6 + //012345678901234567890123456789012345678901234567890123456789012345 + //X=1024 Y=2024: (P0) $e01234: $00 (%00000000) ColorCode=$d + + screen.Puts(0, 0, "X= Y="); + for (int i = 0; i < nplane; i++) { + screen.Print(15, i, "(P%u)", i); + } + + // カーソルが範囲外ならここまで。 + if (x < 0) { + return; + } + + screen.Print(2, 0, "%4d Y=%4d", x, y); + + // 1プレーン内のバイトオフセット + uint32 planeoffset = ((uint)y * 128) + ((uint)x / 8); + + uint cc = 0; + for (int i = 0; i < nplane; i++) { + // プレーン0先頭からのバイトオフセット + uint32 totaloffset = 0x20000 * i + planeoffset; + + uint8 data = mem[HLB(totaloffset)]; + screen.Print(15, i, "(P%u) $%06x: $%02x (%%", + i, baseaddr + totaloffset, data); + + // ビットパターン + for (int m = 0x80; m != 0; m >>= 1) { + screen.Putc((data & m) ? '1' : '0'); + } + screen.Putc(')'); + + // カーソル位置をハイライト + screen.Locate(35 + (x % 8), i); + screen.SetAttr(TA::Em); + + // カラーコードを計算 + if ((data & (0x80U >> (x % 8))) != 0) { + cc |= (1U << i); } } -#endif + + screen.Print(45, 0, "ColorCode=$%x", cc); }