File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / gvram.cpp
Revision 1.1.1.12 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:06:01 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, HEAD
nono 1.7.0

//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//

//
// GVRAM
//

// GVRAM はワード単位でホストバイトオーダ配置なので、
// バイトアクセス時は HB() マクロを使用のこと。

#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(OBJ_GVRAM)
{
	composite.Create(1024, 1024);
}

// デストラクタ
GVRAMDevice::~GVRAMDevice()
{
}

// 初期化
bool
GVRAMDevice::Init()
{
	size_t devlen = 0x20'0000;	// XXX とりあえず
	mem.reset(new(std::nothrow) uint8[devlen]);
	if ((bool)mem == false) {
		warnx("Could not allocate %zu bytes at %s", devlen, __method__);
		return false;
	}

	videoctlr = GetVideoCtlrDevice();

	// 加工済みグラフィックパレットを取得。
	palette = &(videoctlr->GetHostPalette())[0];

	return true;
}

void
GVRAMDevice::ResetHard(bool poweron)
{
	// XXX ここ?
	Invalidate();
}

// 全画面の更新フラグを立てる。
void
GVRAMDevice::Invalidate()
{
	dirty.line.set();
}

inline uint32
GVRAMDevice::Decoder(uint32 addr) const
{
	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)];
}

bool
GVRAMDevice::Poke1(uint32 addr, uint32 data)
{
	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<std::mutex> 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<std::mutex> 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);
	}
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.