--- nono/vm/tvram.cpp 2026/04/29 17:04:32 1.1.1.2 +++ nono/vm/tvram.cpp 2026/04/29 17:05:51 1.1.1.15 @@ -1,136 +1,237 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org -// -// TVRAM +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // -#include "renderer.h" -#include "tvram.h" -#include "vicon.h" - // -// TVRAM はワード単位でホストバイトオーダ配置なので、 -// バイトアクセス時は HB() マクロを使用のこと。 +// TVRAM // -std::unique_ptr gTVRAM; +// TVRAM (PlaneVRAM) はロングワード単位でホストバイトオーダ配置なので、 +// バイトアクセス時は HLB()、ワードアクセス時は HLW() マクロを使用のこと。 +#include "tvram.h" +#include "event.h" +#include "textscreen.h" +#include "videoctlr.h" + +// InsideOut p.135 +/*static*/ const busdata +TVRAMDevice::wait = busdata::Wait(9 * 40_nsec); + +// コンストラクタ TVRAMDevice::TVRAMDevice() + : inherited(1024, 1024) { - logname = "tvram"; - devname = "TVRAM"; - devaddr = baseaddr; - devlen = 512 * 1024; + SetName("TVRAM"); + ClearAlias(); + AddAlias("TVRAM"); - mem.reset(new uint8[devlen]); + // プレーン数は4枚固定 + nplane = NPLANE; } +// デストラクタ TVRAMDevice::~TVRAMDevice() { } -void -TVRAMDevice::ResetHard() +// 初期化 +bool +TVRAMDevice::Init() { - // XXX ここ? - Invalidate(); + if (inherited::Init() == false) { + return false; + } + + constexpr uint devlen = 512 * 1024; + 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->GetHostPalettePtr(256); + + return true; } -inline uint64 +inline uint32 TVRAMDevice::Decoder(uint32 addr) const { return addr - baseaddr; } -uint64 -TVRAMDevice::Read8(uint32 addr) +busdata +TVRAMDevice::Read(busaddr addr) { - uint32 offset = Decoder(addr); - return mem[HB(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) { uint32 offset = Decoder(addr); - return *(uint16 *)&mem[offset]; + return mem[HLB(offset)]; } -uint64 -TVRAMDevice::Write8(uint32 addr, uint32 data) +bool +TVRAMDevice::Poke1(uint32 addr, uint32 data) { - uint32 offset = Decoder(addr); - - // XXX とりあえずね - putlog(3, "Write $%06x <- $%02x", addr, data); + 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]) { - mem[HB(0x20000 * plane + poffset)] = data; + uint8 *p = &mem[HLB(0x20000 * plane + poffset)]; + *p = (data & ~mask) | (*p & mask); } } } else { // 通常アクセス - mem[HB(offset)] = data; + uint8 *p = &mem[HLB(offset)]; + *p = (data & ~mask) | (*p & mask); } - atomic_dirty[(offset % 0x20000) / 128] = 1; - return 0; } -uint64 -TVRAMDevice::Write16(uint32 addr, uint32 data) +// マスクと同時アクセスを含むワード書き込み。 +void +TVRAMDevice::Set16(uint32 offset, uint32 poffset, uint32 data, uint32 mask) { - uint32 offset = Decoder(addr); - - // XXX とりあえずね - putlog(3, "Write $%06x <- $%04x", addr, data); - if (tvram.sa) { // 同時アクセス - uint32 poffset = offset & 0x1ffff; for (int plane = 0; plane < 4; plane++) { if (tvram.ap[plane]) { - *(uint16 *)&mem[0x20000 * plane + poffset] = data; + uint16 *p = (uint16 *)&mem[HLW(0x20000 * plane + poffset)]; + *p = (data & ~mask) | (*p & mask); } } } else { // 通常アクセス - *(uint16 *)&mem[offset] = data; + uint16 *p = (uint16 *)&mem[HLW(offset)]; + *p = (data & ~mask) | (*p & mask); } - atomic_dirty[(offset % 0x20000) / 128] = 1; - return 0; } -uint64 -TVRAMDevice::Peek8(uint32 addr) +// offset の位置に対応する更新フラグを立てる。 +// offset はプレーン先頭からのバイトオフセット。 +inline void +TVRAMDevice::SetDirty(uint32 offset) +{ + // 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| + // +-----------+-------+-----+-------+-----+ + // : : + // +-------------------+ + // 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 +TVRAMDevice::SetScrollX(int x) { - uint32 offset = Decoder(addr); - return mem[HB(offset)]; + xscroll = x; } -// 全画面の更新フラグを立てる void -TVRAMDevice::Invalidate() +TVRAMDevice::SetScrollY(int y) { - for (int i = 0; i < 1024; i++) { - atomic_dirty[i] = 1; - } + yscroll = y; + 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) { @@ -145,91 +246,82 @@ 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[0x00000]; - 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; +} + +// ラスターコピーを開始する。 +// src_ras, dst_ras はライン番号 (レジスタ設定値を4倍したもの)。 +void +TVRAMDevice::RasterCopy(uint src_ras, uint 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); + } + } + + // dst からの4ラインを全部 dirty にする。 + for (int y = 0; y < 4; y++) { + SetDirty((dst_ras + y) * 128); + } +} + +// (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(')'); - // 2の時だけ0に戻す - atomic_cas_8(&atomic_dirty[sy], 2, 0); - updated = true; + // カーソル位置をハイライト + screen.Locate(35 + (x % 8), i); + screen.SetAttr(TA::Em); + + // カラーコードを計算 + if ((data & (0x80U >> (x % 8))) != 0) { + cc |= (1U << i); } } - return updated; + screen.Print(45, 0, "ColorCode=$%x", cc); }