File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / tvram.cpp
Revision 1.1.1.16 (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
//

//
// TVRAM
//

// 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)
{
	SetName("TVRAM");
	ClearAlias();
	AddAlias("TVRAM");

	// プレーン数は4枚固定
	nplane = NPLANE;
}

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

// 初期化
bool
TVRAMDevice::Init()
{
	if (inherited::Init() == false) {
		return false;
	}

	constexpr size_t devlen = 512 * 1024;
	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->GetHostPalettePtr(256);

	return true;
}

inline uint32
TVRAMDevice::Decoder(uint32 addr) const
{
	return addr - baseaddr;
}

busdata
TVRAMDevice::Read(busaddr addr)
{
	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;
}

busdata
TVRAMDevice::Peek1(uint32 addr)
{
	uint32 offset = Decoder(addr);
	return mem[HLB(offset)];
}

bool
TVRAMDevice::Poke1(uint32 addr, uint32 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) {
		// 同時アクセス
		for (int plane = 0; plane < 4; plane++) {
			if (tvram.ap[plane]) {
				uint8 *p = &mem[HLB(0x20000 * plane + poffset)];
				*p = (data & ~mask) | (*p & mask);
			}
		}
	} else {
		// 通常アクセス
		uint8 *p = &mem[HLB(offset)];
		*p = (data & ~mask) | (*p & mask);
	}
}

// マスクと同時アクセスを含むワード書き込み。
void
TVRAMDevice::Set16(uint32 offset, uint32 poffset, uint32 data, uint32 mask)
{
	if (tvram.sa) {
		// 同時アクセス
		for (int plane = 0; plane < 4; plane++) {
			if (tvram.ap[plane]) {
				uint16 *p = (uint16 *)&mem[HLW(0x20000 * plane + poffset)];
				*p = (data & ~mask) | (*p & mask);
			}
		}
	} else {
		// 通常アクセス
		uint16 *p = (uint16 *)&mem[HLW(offset)];
		*p = (data & ~mask) | (*p & mask);
	}
}

// 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)
{
	xscroll = x;
}

void
TVRAMDevice::SetScrollY(int y)
{
	yscroll = y;
	Invalidate2();
}

// CRTC R21 での同時アクセス設定を反映する。
// CRTC から呼ばれる。
void
TVRAMDevice::SetAccessPlane(uint16 data)
{
	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) {
			putlog(2, "テキスト画面同時アクセス有効 %c%c%c%c",
				tvram.ap[0] ? '0' : '-',
				tvram.ap[1] ? '1' : '-',
				tvram.ap[2] ? '2' : '-',
				tvram.ap[3] ? '3' : '-');
		} else {
			putlog(2, "テキスト画面同時アクセス無効");
		}
	}
}

// CRTC R23 でのアクセスマスク設定を反映する。
// CRTC から呼ばれる。
void
TVRAMDevice::SetAccessMask(uint16 data)
{
	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(')');

		// カーソル位置をハイライト
		screen.Locate(35 + (x % 8), i);
		screen.SetAttr(TA::Em);

		// カラーコードを計算
		if ((data & (0x80U >> (x % 8))) != 0) {
			cc |= (1U << i);
		}
	}

	screen.Print(45, 0, "ColorCode=$%x", cc);
}

unix.superglobalmegacorp.com

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