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