--- nono/vm/videoctlr.cpp 2026/04/29 17:05:24 1.1.1.9 +++ nono/vm/videoctlr.cpp 2026/04/29 17:05:33 1.1.1.11 @@ -16,7 +16,8 @@ // 以上の 4KB をミラー。 #include "videoctlr.h" -#include "mpu.h" +#include "mainapp.h" +#include "planevram.h" #include "renderer.h" #include "scheduler.h" #include "uimessage.h" @@ -36,11 +37,9 @@ VideoCtlrDevice::VideoCtlrDevice() #if defined(HAVE_AVX2) enable_avx2 = gMainApp.enable_avx2; #endif - - // 約30fps。これ以上速くても分からない。 - contrast_event.func = ToEventCallback(&VideoCtlrDevice::ContrastCallback); - contrast_event.time = 30_msec; - contrast_event.Regist("VideoCtlr Analog Contrast"); +#if defined(HAVE_NEON) + enable_neon = gMainApp.enable_neon; +#endif } // デストラクタ @@ -59,6 +58,12 @@ VideoCtlrDevice::Init() planevram = GetPlaneVRAMDevice(); renderer = GetRenderer(); + // 約30fps。これ以上速くても分からない。 + contrast_event.func = ToEventCallback(&VideoCtlrDevice::ContrastCallback); + contrast_event.time = 30_msec; + contrast_event.SetName("VideoCtlr Analog Contrast"); + scheduler->RegistEvent(contrast_event); + return true; } @@ -75,133 +80,86 @@ VideoCtlrDevice::ResetHard(bool poweron) } } -inline uint32 -VideoCtlrDevice::Decoder(uint32 addr) const +/*static*/ inline uint32 +VideoCtlrDevice::Decoder(uint32 addr) { return addr & 0xfff; } busdata -VideoCtlrDevice::Read8(uint32 addr) -{ - busdata data; - - uint32 offset = Decoder(addr); - if (offset < 0x400) { // パレット - data = palette[HB(offset)]; - putlog(3, "Palette $%06x.B -> $%02x", addr, data.Data()); - - } else if (offset < 0x700) { // R0,R1,R2 - uint nr = Offset2NR(offset); - data = GetNR(nr); - putlog(3, "R%d.%c -> $%02x", - (nr / 2), - (nr % 2) == 0 ? 'H' : 'L', - data.Data()); - - } else { - data = 0; - } - - data |= read_wait; - return data; -} - -busdata -VideoCtlrDevice::Read16(uint32 addr) +VideoCtlrDevice::Read(busaddr addr) { + uint32 paddr = addr.Addr(); + uint32 offset = Decoder(paddr); busdata data; - uint32 offset = Decoder(addr); - if (offset < 0x400) { // パレット - data = *(uint16 *)&palette[offset]; - putlog(3, "Palette $%06x.W -> $%04x", addr, data.Data()); - - } else if (offset < 0x700) { // R0,R1,R2 - uint nr = Offset2NR(offset); - data = GetNR(nr) << 8; - data |= GetNR(nr + 1); - putlog(3, "R%d -> $%04x", (nr / 2), data.Data()); - - } else { - data = 0; + data = Get16(offset & ~1U); + if (__predict_false(loglevel >= 3)) { + if (offset < 0x400) { // パレット + putlogn("Palette $%06x -> $%04x", (paddr & ~1U), data.Data()); + } else if (offset < 0x700) { // R0,R1,R2 + uint rn = Offset2Rn(offset); + putlogn("R%u -> $%04x", rn, data.Data()); + } } - data |= read_wait; + data |= BusData::Size2; return data; } busdata -VideoCtlrDevice::Write8(uint32 addr, uint32 data) +VideoCtlrDevice::Write(busaddr addr, uint32 data) { - uint32 offset = Decoder(addr); - if (offset < 0x400) { // パレット - putlog(2, "Palette $%06x.B <- $%02x", addr, data); - palette[HB(offset)] = data; - MakePalette(); - - } else if (offset < 0x700) { // R0,R1,R2 - uint nr = Offset2NR(offset); - putlog(2, "R%d.%c <- $%02x (NOT IMPLEMENTED)", - (nr / 2), - (nr % 2) == 0 ? 'H' : 'L', - data); - SetNR(nr, data); - - } else { - // たぶん何も起きない? + uint32 offset = Decoder(addr.Addr()); + uint32 reqsize = addr.GetSize(); + uint32 datasize = std::min(2 - (offset & 1U), reqsize); + data >>= (reqsize - datasize) * 8; + + if (__predict_false(loglevel >= 2)) { + if (offset < 0x400) { // パレット + putlogn("Palette $%06x.%c <- $%0*x", addr.Addr(), + (datasize == 1 ? 'B' : 'W'), datasize * 2, data); + } else if (offset < 0x700) { // R0,R1,R2 + uint rn = Offset2Rn(offset); + if (datasize == 1) { + putlogn("R%u.%c <- $%02x (NOT IMPLEMENTED)", + rn, ((offset & 1U) ? 'H' : 'L'), data); + } else { + putlogn("R%u <- $%04x (NOT IMPLEMENTED)", rn, data); + } + } } - return write_wait; -} - -busdata -VideoCtlrDevice::Write16(uint32 addr, uint32 data) -{ - uint32 offset = Decoder(addr); - if (offset < 0x400) { // パレット - putlog(2, "Palette $%06x.W <- $%04x", addr, data); - *(uint16 *)&palette[offset] = data; - MakePalette(); - - } else if (offset < 0x700) { // R0,R1,R2 - uint nr = Offset2NR(offset); - putlog(3, "R%d <- $%04x (NOT IMPLEMENTED)", (nr / 2), data); - SetNR(nr, data >> 8); - SetNR(nr + 1, data & 0xff); - + if (datasize == 1) { + Set8(offset, data); } else { - // たぶん何も起きない? + Set16(offset, data); } - - return write_wait; + busdata r = write_wait; + r |= busdata::Size(datasize); + return r; } busdata -VideoCtlrDevice::Peek8(uint32 addr) +VideoCtlrDevice::Peek1(uint32 addr) { uint32 offset = Decoder(addr); - if (offset < 0x400) { // パレット - return palette[HB(offset)]; - - } else if (offset < 0x700) { // R0,R1,R2 - uint nr = Offset2NR(offset); - return GetNR(nr); - + uint32 data = Get16(offset & ~1U); + if ((offset & 1U) == 0) { + return data >> 8; } else { - return 0x00; + return data & 0xff; } } bool -VideoCtlrDevice::Poke8(uint32 addr, uint32 data) +VideoCtlrDevice::Poke1(uint32 addr, uint32 data) { + // パレットのみ編集可能とする。 uint32 offset = Decoder(addr); if (offset < 0x400) { if ((int32)data >= 0) { - palette[HB(offset)] = data; - // Poke による編集でもパレットに反映させる - MakePalette(); + Set8(offset, data); } return true; } @@ -209,59 +167,75 @@ VideoCtlrDevice::Poke8(uint32 addr, uint return false; } -// R0, R1, R2 のオフセットから内部仮想レジスタ番号 NR0-NR5 に変換する。 +// R0, R1, R2 のアドレスオフセットからレジスタ番号 0, 1, 2 を返す。 // offset は R0, R1, R2 の領域を指していること。 -inline uint -VideoCtlrDevice::Offset2NR(uint32 offset) const +/*static*/ inline uint32 +VideoCtlrDevice::Offset2Rn(uint32 offset) { assert(0x400 <= offset && offset < 0x700); - return ((offset / 0x100) - 4) * 2 + (offset & 1); + return (offset >> 8) - 4; } -// 内部仮想レジスタ NR0-NR5 を読み出す。 +// offset の位置の内容をワードで返す。 +// offset は偶数であること。 uint32 -VideoCtlrDevice::GetNR(uint reg) const +VideoCtlrDevice::Get16(uint32 offset) const { - return nreg[reg]; + assert((offset & 1U) == 0); + + if (offset < 0x400) { // パレット + return palette[offset >> 1]; + } else if (offset < 0x700) { // R0,R1,R2 + uint rn = Offset2Rn(offset); + return reg[rn]; + } else { + return 0x00; + } +} + +// offset の位置のバイトを更新する。 +void +VideoCtlrDevice::Set8(uint32 offset, uint32 data) +{ + uint32 offset2 = offset & ~1U; + + uint32 tmp = Get16(offset2); + if ((offset & 1U) == 0) { + tmp = (data << 8) | (tmp & 0xff); + } else { + tmp = (tmp & 0xff00) | data; + } + Set16(offset2, tmp); } -// 内部仮想レジスタ NR0-NR5 にバイトデータを書き込む。 +// offset の位置のワードを更新する。 +// offset は偶数であること。 void -VideoCtlrDevice::SetNR(uint reg, uint32 data) +VideoCtlrDevice::Set16(uint32 offset, uint32 data) { - // XXX まだ値を保存する以外何もしていない - assert(reg < 6); - switch (reg) { - case 0: // R0.H - nreg[reg] = 0; - break; - case 1: // R0.L - nreg[reg] = data & 0x03; - break; - - case 2: // R1.H - nreg[reg] = data & 0x3f; - break; - case 3: // R1.L - nreg[reg] = data & 0xff; - break; - - case 4: // R2.H - nreg[reg] = data & 0xff; - break; - case 5: // R2.L - nreg[reg] = data & 0x7f; - break; + assert((offset & 1U) == 0); - default: - __unreachable(); + if (offset < 0x400) { // パレット + palette[offset >> 1] = data; + MakePalette(); + } else if (offset < 0x700) { // R0,R1,R2 + uint rn = Offset2Rn(offset); + switch (rn) { + case 0: reg[rn] = data & 0x0003U; break; + case 1: reg[rn] = data & 0x3fffU; break; + case 2: reg[rn] = data & 0xff7fU; break; + default: + break; + } + } else { + // nop } } // コントラストを設定する。 // 指定値は 0-15 だが、内部では 0-xff で保持。 void -VideoCtlrDevice::SetContrast(int contrast_) +VideoCtlrDevice::SetContrast(uint contrast_) { target_contrast = contrast_ * 0x11; @@ -329,9 +303,6 @@ VideoCtlrDevice::ContrastCallback(Event& if (new_contrast != running_contrast) { running_contrast = new_contrast; - - // 強制再描画をレンダラに通知 - renderer->Invalidate2(); } scheduler->RestartEvent(ev); @@ -345,22 +316,23 @@ VideoCtlrDevice::MakePalette() // パレット前半 256 ワードはグラフィックパレット、 // テキストパレットは後半。 - p = (uint16 *)&palette[512]; + p = &palette[256]; // パレット情報から Color 形式の色データを作成 // X680x0 のパレットは %GGGGG'RRRRR'BBBBB'I で並んでいる。 for (int i = 0; i < 16; i++) { - int I = (p[i] & 1) << 2; // 輝度ビット - int R = (((p[i] >> 6) & 0x1f) << 3) + I; - int G = (((p[i] >> 11)) << 3) + I; - int B = (((p[i] >> 1) & 0x1f) << 3) + I; + uint I = (p[i] & 1) << 2; // 輝度ビット + uint R = (((p[i] >> 6) & 0x1f) << 3) + I; + uint G = (((p[i] >> 11)) << 3) + I; + uint B = (((p[i] >> 1) & 0x1f) << 3) + I; hostcolor[i].r = R; hostcolor[i].g = G; hostcolor[i].b = B; } - // パレット変更が起きたことをレンダラに通知 - renderer->Invalidate2(); + // パレット変更が起きたことをテキストレンダラに通知。 + // この後ここでグラフィックレンダラにも通知。 + planevram->Invalidate2(); // UI にも通知 UIMessage::Post(UIMessage::PALETTE); @@ -372,26 +344,49 @@ const std::vector VideoCtlrDevice::GetIntPalette() const { std::vector ipal; - const uint16 *p = (const uint16 *)&palette[0]; + const uint16 *p = &palette[256]; for (int i = 0; i < 16; i++) { - ipal.push_back(p[256 + i]); + ipal.push_back(p[i]); } return ipal; } +// 画面の作成。CRTC から VDisp のタイミングで呼ばれる。 +// Renderer に作画指示を出すかどうかを決める。 +void +VideoCtlrDevice::VDisp() +{ + bool update_needed = false; + + // 今はテキスト画面しかない。 + if (planevram->Snap()) { + update_needed = true; + } + + // コントラストが変わっていても再描画。 + if (__predict_false(rendering_contrast != running_contrast)) { + pending_contrast = running_contrast; + update_needed = true; + } + + if (update_needed) { + renderer->NotifyRender(); + } +} + // 画面合成。 // レンダリングスレッドから呼ばれる。 bool -VideoCtlrDevice::Render(BitmapRGBX& dst, ModifyInfo& modify) +VideoCtlrDevice::Render(BitmapRGBX& dst) { // 今はテキスト画面しかない。 - bool updated = planevram->Render(textbmp, modify); + bool updated = planevram->Render(textbmp); // 最後にコントラスト適用。 - // アトミックじゃなくても大丈夫のはず。 - if (__predict_false(rendering_contrast != running_contrast)) { - rendering_contrast = running_contrast; + int contrast = pending_contrast.exchange(rendering_contrast); + if (contrast != rendering_contrast) { + rendering_contrast = contrast; updated = true; } if (__predict_false(updated)) { @@ -405,7 +400,7 @@ VideoCtlrDevice::Render(BitmapRGBX& dst, #define PERFCONT #include "stopwatch.h" static uint64 perf_total; -static int perf_count; +static uint perf_count; #endif // コントラストを適用。 @@ -430,6 +425,11 @@ VideoCtlrDevice::RenderContrast(BitmapRG RenderContrast_avx2(dst, src, contrast); } else #endif +#if defined(HAVE_NEON) + if (__predict_true(enable_neon)) { + RenderContrast_neon(dst, src, contrast); + } else +#endif { RenderContrast_gen(dst, src, contrast); } @@ -438,7 +438,7 @@ VideoCtlrDevice::RenderContrast(BitmapRG sw.Stop(); perf_total += sw.Elapsed(); if (++perf_count % 100 == 0) { - printf("%" PRIu64 "\n", perf_total / perf_count); + printf("%" PRIu64 " usec\n", perf_total / perf_count / 1000); } #endif } @@ -451,27 +451,26 @@ VideoCtlrDevice::RenderContrast_gen(Bitm { std::array lut; - for (int i = 0; i < lut.size(); i++) { + for (uint i = 0; i < lut.size(); i++) { lut[i] = (i * contrast) >> 8; } - for (int y = 0, yend = src.GetHeight(); y < yend; y++) { - const uint32 *s = (const uint32 *)src.GetRowPtr(y); - uint32 *d = (uint32 *)dst.GetRowPtr(y); - - const uint32 *send = s + src.GetWidth(); - for (; s < send; ) { - // 1ピクセルずつ処理する - // 445.1 usec - - uint32 c = htole32(*s++); - - uint32 r = lut[c & 0xff]; - uint32 g = lut[(c >> 8) & 0xff]; - uint32 b = lut[(c >> 16) & 0xff]; + // この変換は一次元配列とみなしても行える。 + // また必ずテキスト画面全域なので端数の考慮は不要。 + uint pxlen = src.GetWidth() * src.GetHeight(); + const uint32 *s32 = (const uint32 *)src.GetRowPtr(0); + const uint32 *s32end = s32 + pxlen; + uint32 *d32 = (uint32 *)dst.GetRowPtr(0); + + // 1ピクセルずつ処理する + for (; s32 < s32end; ) { + Color cs(*s32++); + + uint32 r = lut[cs.r]; + uint32 g = lut[cs.g]; + uint32 b = lut[cs.b]; - c = r | (g << 8 ) | (b << 16); - *d++ = le32toh(c); - } + Color cd(r, g, b); + *d32++ = cd.u32; } }