|
|
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);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.