--- nono/vm/tvram.cpp 2026/04/29 17:04:28 1.1 +++ nono/vm/tvram.cpp 2026/04/29 17:05:14 1.1.1.8 @@ -1,132 +1,232 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // -// TVRAM -// - -#include "renderer.h" -#include "tvram.h" -#include "vicon.h" // -// TVRAM はワード単位でホストバイトオーダ配置なので、 -// バイトアクセス時は HB() マクロを使用のこと。 +// TVRAM // -TVRAMDevice *gTVRAM; +// TVRAM (PlaneVRAM) はロングワード単位でホストバイトオーダ配置なので、 +// バイトアクセス時は HLB()、ワードアクセス時は HLW() マクロを使用のこと。 + +#include "tvram.h" +#include "mpu.h" +#include "videoctlr.h" +// コンストラクタ TVRAMDevice::TVRAMDevice() + : inherited("TVRAM", 1024, 1024) { - logname = "tvram"; - devname = "TVRAM"; devaddr = baseaddr; devlen = 512 * 1024; - mem = new uint8[devlen](); + // プレーン数は4枚固定 + nplane = NPLANE; + + modify_mask = 0xff00; + + mem.reset(new uint8[devlen]); } +// デストラクタ TVRAMDevice::~TVRAMDevice() { - if (mem) { - delete[] mem; - } } +bool +TVRAMDevice::Init() +{ + // 加工済みパレットを取得 + palette = gVideoCtlr->GetHostPalette(); + + return true; +} + +// リセット void -TVRAMDevice::ResetHard() +TVRAMDevice::ResetHard(bool poweron) { // XXX ここ? Invalidate(); } +inline uint64 +TVRAMDevice::Decoder(uint32 addr) const +{ + return addr - baseaddr; +} + uint64 TVRAMDevice::Read8(uint32 addr) { - uint32 offset = addr - baseaddr; - return mem[HB(offset)]; + gMPU->AddCycle(9); // Inside/Out p.135 + uint32 offset = Decoder(addr); + return mem[HLB(offset)]; } uint64 TVRAMDevice::Read16(uint32 addr) { - uint32 offset = addr - baseaddr; - return *(uint16 *)&mem[offset]; + gMPU->AddCycle(9); // Inside/Out p.135 + uint32 offset = Decoder(addr); + return *(uint16 *)&mem[HLW(offset)]; } uint64 TVRAMDevice::Write8(uint32 addr, uint32 data) { - uint32 offset = addr - baseaddr; + uint32 offset = Decoder(addr); + uint8 *ptr; + uint8 mask8; + + gMPU->AddCycle(9); // Inside/Out p.135 // XXX とりあえずね - putlog(3, "Write $%06x <- $%02x", addr, data); + 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 (tvram.sa) { // 同時アクセス uint32 poffset = offset & 0x1ffff; for (int plane = 0; plane < 4; plane++) { if (tvram.ap[plane]) { - mem[HB(0x20000 * plane + poffset)] = data; + ptr = &mem[HLB(0x20000 * plane + poffset)]; + *ptr = (data & ~mask8) | (*ptr & mask8); } } } else { // 通常アクセス - mem[HB(offset)] = data; + ptr = &mem[HLB(offset)]; + *ptr = (data & ~mask8) | (*ptr & mask8); } - atomic_dirty[(offset % 0x20000) / 128] = 1; + SetDirty(offset); return 0; } uint64 TVRAMDevice::Write16(uint32 addr, uint32 data) { - uint32 offset = addr - baseaddr; + uint32 offset = Decoder(addr); + uint16 *ptr; + uint16 mask16; + + gMPU->AddCycle(9); // Inside/Out p.135 // XXX とりあえずね - putlog(3, "Write $%06x <- $%04x", addr, data); + 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]) { - *(uint16 *)&mem[0x20000 * plane + poffset] = data; + ptr = (uint16 *)&mem[HLW(0x20000 * plane + poffset)]; + *ptr = (data & ~mask16) | (*ptr & mask16); } } } else { // 通常アクセス - *(uint16 *)&mem[offset] = data; + ptr = (uint16 *)&mem[HLW(offset)]; + *ptr = (data & ~mask16) | (*ptr & mask16); } - atomic_dirty[(offset % 0x20000) / 128] = 1; + SetDirty(offset); return 0; } uint64 TVRAMDevice::Peek8(uint32 addr) { - return mem[HB(addr - baseaddr)]; + uint32 offset = Decoder(addr); + return mem[HLB(offset)]; +} + +// offset の位置に対応する更新フラグを立てる。 +// offset は TVRAM 先頭からのバイトオフセット (プレーン先頭からではない)。 +inline void +TVRAMDevice::SetDirty(uint32 offset) +{ + // VRAM は横 1024 ドット(128バイト) x 縦 1024 ドットで、これを + // 横 128 ドット(16バイト) x 縦 16 ドットずつのブロックに分割する。 + // ブロック数は横 8個 x 縦 64個。 + // + // 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 +} + +void +TVRAMDevice::SetScrollX(int x) +{ + xscroll = x; } -// 全画面の更新フラグを立てる void -TVRAMDevice::Invalidate() +TVRAMDevice::SetScrollY(int y) { - for (int i = 0; i < 1024; i++) { - atomic_dirty[i] = 1; - } + yscroll = y; + gRenderer->Invalidate2(); } -// CRTC R21 での同時アクセス設定を反映する -// CRTC から呼ばれる +// CRTC R21 での同時アクセス設定を反映する。 +// CRTC から呼ばれる。 void -TVRAMDevice::set_crtc_21(uint16 data) +TVRAMDevice::SetAccessPlane(uint16 data) { - tvram.sa = (data & 0x100); + tvram.cp[0] = (data & 0x01); + tvram.cp[1] = (data & 0x02); + tvram.cp[2] = (data & 0x04); + tvram.cp[3] = (data & 0x08); tvram.ap[0] = (data & 0x10); tvram.ap[1] = (data & 0x20); tvram.ap[2] = (data & 0x40); tvram.ap[3] = (data & 0x80); + tvram.sa = (data & 0x100); + tvram.men = (data & 0x200); if (0) { if (tvram.sa) { @@ -141,91 +241,45 @@ TVRAMDevice::set_crtc_21(uint16 data) } } -// テキスト画面合成 -bool -TVRAMDevice::Render(uint8 *imagebuf) +// CRTC R23 でのアクセスマスク設定を反映する。 +// CRTC から呼ばれる。 +void +TVRAMDevice::SetAccessMask(uint16 data) { - const uint16 *palette_w; - uint8 pal[16][3]; - const uint16 *plane0; - const uint16 *plane1; - const uint16 *plane2; - const uint16 *plane3; - uint8 *dst0; - bool updated; - - dst0 = imagebuf; - - // パレット取得 - palette_w = gVicon->GetPalette(); - // パレット前半 256 ワードはグラフィックパレット、 - // テキストパレットは後半。 - palette_w += 0x100; - - // パレット情報から RGB24 色データを作成 - // XXX ちょっと輝度は置いとく - for (int i = 0; i < 16; i++) { - pal[i][0] = ((palette_w[i] >> 6) & 0x1f) << 3; // R - pal[i][1] = ((palette_w[i] >> 11)) << 3; // G - pal[i][2] = ((palette_w[i] >> 1) & 0x1f) << 3; // B - } - - updated = false; - - // dirty は VRAM への書き込みがあれば常に 1 にする。 - // レンダリング時に dirty が 1 なら 2 にしてレンダリング。 - // レンダリング終了時に dirty が 2 なら 0 に戻す。 - // これによりレンダリング中に VRAM への書き込みがあれば次回描画される。 - - // sy はメモリ上の Y 座標 - // dy はレンダラ画面の Y 座標 (CRTC のXXX を考慮) - // XXX スクロールは未実装 - - // テキスト画面 - plane0 = (uint16 *)mem; - plane1 = (uint16 *)(mem + 0x20000); - plane2 = (uint16 *)(mem + 0x40000); - plane3 = (uint16 *)(mem + 0x60000); - int sy = 0; - for (int dy = 0; dy < 512; dst0 += 768 * Renderer::BPP, dy++, sy++) { - sy &= 0x3ff; - - if (atomic_cas_8(&atomic_dirty[sy], 1, 2) == 1) { - uint8 *dst = dst0; - // sx はメモリ上の X オフセット (ワード単位) - // dx はレンダラ画面の X オフセット (ワード単位) - - int sx = sy * 1024 / 16; - for (int dx = 0; dx < 768 / 16; dx++, sx++) { - uint16 t0 = plane0[sx]; - uint16 t1 = plane1[sx]; - uint16 t2 = plane2[sx]; - uint16 t3 = plane3[sx]; - - // 1ワードの 16bit が横 16dot に相当 - for (int j = 0; j < 16; j++) { - uint c = 0; - // カラーコード(パレット番号)を作成 - if ((t0 & 0x8000)) c |= 1; - if ((t1 & 0x8000)) c |= 2; - if ((t2 & 0x8000)) c |= 4; - if ((t3 & 0x8000)) c |= 8; - t0 <<= 1; - t1 <<= 1; - t2 <<= 1; - t3 <<= 1; - - *dst++ = pal[c][0]; - *dst++ = pal[c][1]; - *dst++ = pal[c][2]; - } - } + tvram.mask = data; +} - // 2の時だけ0に戻す - atomic_cas_8(&atomic_dirty[sy], 2, 0); - updated = true; +// ラスターコピーを開始する。 +// src_ras, dst_ras はライン番号 (レジスタ設定値を4倍したもの)。 +void +TVRAMDevice::RasterCopy(int src_ras, int dst_ras) +{ + for (int plane = 0; plane < 4; plane++) { + if (tvram.cp[plane]) { + uint8 *src = &mem[0x20000 * plane + src_ras * 128]; + uint8 *dst = &mem[0x20000 * plane + dst_ras * 128]; + memcpy(dst, src, 128 * 4); } } - return updated; + // 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)); + } +#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)); + } + } + } +#endif }