--- nono/vm/crtc.cpp 2026/04/29 17:05:13 1.1.1.9 +++ nono/vm/crtc.cpp 2026/04/29 17:05:24 1.1.1.11 @@ -17,27 +17,26 @@ #include "crtc.h" #include "mfp.h" -#include "mpu.h" #include "renderer.h" #include "scheduler.h" #include "tvram.h" -// グローバル参照用 -CRTCDevice *gCRTC; +// InsideOut p.135 +const busdata wait = busdata::Wait(9 * 40_nsec); // コンストラクタ CRTCDevice::CRTCDevice() - : inherited("CRTC") + : inherited(OBJ_CRTC) { - devaddr = 0xe80000; - devlen = 0x2000; - hsync_event.func = ToEventCallback(&CRTCDevice::HSyncCallback); hsync_event.Regist("CRTC H-Sync"); vdisp_event.func = ToEventCallback(&CRTCDevice::VDispCallback); vdisp_event.Regist("CRTC V-Disp"); - // ラスターコピーにかかる時間は最小 190nsec * 2 らしいので + // ラスターコピーは実際には CRTC の機能ではなく TVRAM (VRAM チップ) の + // 機能だが、イベントの時間管理が発生するのは CRTC 側なのでここで管理。 + // X68030 搭載の VRAM チップは HM514402A だが、ここでは X68000 XVI 搭載の + // HM53461 の値を使用し、これが最小 190nsec * 2 らしいので // とりあえず 400nsec としておく。 raster_event.func = ToEventCallback(&CRTCDevice::RasterCallback); raster_event.time = 400_nsec; @@ -51,7 +50,21 @@ CRTCDevice::CRTCDevice() // デストラクタ CRTCDevice::~CRTCDevice() { - gCRTC = NULL; +} + +// 初期化 +bool +CRTCDevice::Init() +{ + if (inherited::Init() == false) { + return false; + } + + mfp = GetMFPDevice(); + renderer = GetRenderer(); + tvram = GetTVRAMDevice(); + + return true; } // リセット @@ -65,99 +78,164 @@ CRTCDevice::ResetHard(bool poweron) // すぐに水平同期と垂直帰線期間を開始する hsync_event.time = 0; hsync_event.code = 1; - gScheduler->RestartEvent(hsync_event); + scheduler->RestartEvent(hsync_event); vdisp_event.time = 0; vdisp_event.code = 1; - gScheduler->RestartEvent(vdisp_event); + scheduler->RestartEvent(vdisp_event); } -uint64 -CRTCDevice::Read(uint32 offset) +busdata +CRTCDevice::Read8(uint32 addr) { - uint64 data; + busdata data = Peek16(addr); - gMPU->AddCycle(9); // InsideOut p.135 - - if (offset < 0x400 / 2) { // CRTC レジスタ - // 0x40 バイトでミラー - offset &= (0x40 / 2) - 1; - // R20, R21 のみ読み出し可能 - switch (offset) { - case 20: - data = crtc.r[offset]; - putlog(2, "R%02d -> $%04x", offset, (uint32)data); - break; - case 21: - // R21 へのアクセスは多いのでログレベルを上げておく - data = crtc.r[offset]; - putlog(3, "R%02d -> $%04x", offset, (uint32)data); - break; - default: - // それ以外は読み出し不可 ($00 が読める) - data = 0; - if (offset < countof(crtc.r)) { - putlog(3, "R%02d -> $%04x", offset, (uint32)data); + if (__predict_false(loglevel >= 2)) { + uint32 offset = addr & 0xfff; + if (offset < 0x400) { + // CRTC レジスタは 0x40 バイトでミラー + uint32 regno = (offset & 0x3f) / 2; + // R20, R21 のみ読み出し可能だが、 + // R21 へのアクセスは多いのでログレベルを上げておく。 + if (regno == 21) { + putlog(3, "R%02d.%c -> $%02x", regno, + (((addr & 1) == 0) ? 'H' : 'L'), data.Data()); + } else if (regno < countof(crtc.r)) { + putlogn("R%02d.%c -> $%02x", regno, + (((addr & 1) == 0) ? 'H' : 'L'), data.Data()); } else { - putlog(3, "$%06x -> $%04x", gMPU->GetPaddr(), (uint32)data); + putlog(3, "$%06x -> $%02x", addr, data.Data()); } - break; } + } - } else { // 動作ポート - if ((offset & (0x80 / 2)) == 0) { // +$00..+$7f - data = (uint64)-1; - } else { // +$80..+$ff - data = crtc.op; + data |= wait; + return data; +} + +busdata +CRTCDevice::Read16(uint32 addr) +{ + busdata data = Peek16(addr); + + if (__predict_false(loglevel >= 2)) { + uint32 offset = addr & 0xfff; + if (offset < 0x400) { + // CRTC レジスタは 0x40 バイトでミラー + uint32 regno = (offset & 0x3f) / 2; + // R20, R21 のみ読み出し可能だが、 + // R21 へのアクセスは多いのでログレベルを上げておく + if (regno == 21) { + putlog(3, "R%02d -> $%04x", regno, data.Data()); + } else if (regno < countof(crtc.r)) { + putlog(2, "R%02d -> $%04x", regno, data.Data()); + } else { + putlog(3, "$%06x -> $%04x", addr, data.Data()); + } } } + data |= wait; return data; } -uint64 -CRTCDevice::Write(uint32 offset, uint32 data) +busdata +CRTCDevice::Write8(uint32 addr, uint32 data) { - gMPU->AddCycle(9); // InsideOut p.135 + busdata r; - if (offset < 0x400 / 2) { // CRTC レジスタ - // 0x40 バイトでミラー - offset &= (0x40 / 2) - 1; - if (offset < countof(crtc.r)) { - SetReg(offset, data); + uint32 offset = addr & 0xfff; + if (offset < 0x400) { + // CRTC レジスタは 0x40 バイトでミラー + uint32 regno = (offset & 0x3f) / 2; + if (regno < countof(crtc.r)) { + uint32 tmp; + if ((addr & 1) == 0) { + tmp = (data << 8) | (crtc.r[regno] & 0xff); + } else { + tmp = (crtc.r[regno] & 0xff00) | data; + } + SetReg(regno, tmp); } else { // ここ何がいるんだ? - putlog(2, "$%06x <- $%04x", gMPU->GetPaddr(), data); + putlog(2, "$%06x <- $%02x", addr, data); + } + } else { + // 動作ポート + if ((offset & 0x80) == 0) { // +$00..+$7f + r.SetBusErr(); + } else { // +$80..+$ff + WriteOp(data); } + } - } else { // 動作ポート - if ((offset & (0x80 / 2)) == 0) { // +$00..+$7f - return (uint64)-1; + r |= wait; + return r; +} + +busdata +CRTCDevice::Write16(uint32 addr, uint32 data) +{ + busdata r; + + uint32 offset = addr & 0xfff; + if (offset < 0x400) { + // CRTC レジスタは 0x40 バイトでミラー + uint32 regno = (offset & 0x3f) / 2; + if (regno < countof(crtc.r)) { + SetReg(regno, data); } else { + // ここ何がいるんだ? + putlog(2, "$%06x <- $%04x", addr, data); + } + } else { // 動作ポート + if ((offset & 0x80) == 0) { // +$00..+$7f + r.SetBusErr(); + } else { // +$80..+$ff WriteOp(data); } } - return 0; + r |= wait; + return r; } -uint64 -CRTCDevice::Peek(uint32 offset) +busdata +CRTCDevice::Peek8(uint32 addr) { - uint64 data; + busdata data = Peek16(addr); + if (data.IsOK()) { + if ((addr & 1) == 0) { + return data.Data() >> 8; + } else { + return data.Data() & 0xff; + } + } else { + return busdata::BusErr; + } +} - if (offset < 0x400 / 2) { // CRTC レジスタ - // 0x40 バイトでミラー - offset &= (0x40 / 2) - 1; - if (offset == 20 || offset == 21) { +// addr のワードとしての読み出し値を返す。 +// これは内部用で Read*() からも呼ばれるので、 +// バスエラーなら busdata::BusErr を返す。 +busdata +CRTCDevice::Peek16(uint32 addr) const +{ + busdata data; + + uint32 offset = addr & 0xfff; + if (offset < 0x400) { + // CRTC レジスタは 0x40 バイトでミラー + uint32 regno = (offset & 0x3f) / 2; + if (regno == 20 || regno == 21) { // R20, R21 のみ読み出し可能 - data = crtc.r[offset]; + data = crtc.r[regno]; } else { data = 0; } - - } else { // 動作ポート - if ((offset & (0x80 / 2)) == 0) { - data = (uint64)-1; + } else { + // 動作ポート + if ((offset & 0x80) == 0) { + data.SetBusErr(); } else { data = crtc.op; } @@ -247,16 +325,16 @@ CRTCDevice::SetReg(uint32 reg, uint32 da switch (reg) { case CRTC::R10: putlog(2, "R%02d <- $%04x", reg, data); - gTVRAM->SetScrollX(crtc.r[10]); + tvram->SetScrollX(crtc.r[10]); break; case CRTC::R11: putlog(2, "R%02d <- $%04x", reg, data); - gTVRAM->SetScrollY(crtc.r[11]); + tvram->SetScrollY(crtc.r[11]); break; case CRTC::R21: // 頻度が高いのでログレベルを上げておく putlog(3, "R%02d <- $%04x", reg, data); - gTVRAM->SetAccessPlane(crtc.r[21]); + tvram->SetAccessPlane(crtc.r[21]); break; case CRTC::R22: putlog(3, "R%02d <- $%04x", reg, data); @@ -264,7 +342,7 @@ CRTCDevice::SetReg(uint32 reg, uint32 da case CRTC::R23: // 頻度が高いのでログレベルを上げておく putlog(3, "R%02d <- $%04x", reg, data); - gTVRAM->SetAccessMask(crtc.r[23]); + tvram->SetAccessMask(crtc.r[23]); break; default: putlog(2, "R%02d <- $%04x (NOT IMPLEMENTED)", reg, data); @@ -319,7 +397,7 @@ CRTCDevice::HSyncCallback(Event& ev) ev.code++; // GPIP の状態を更新 - gMFP->SetHSync(true); + mfp->SetHSync(true); // ラスターコピーが指示されていたら実行 if (raster_copy) { @@ -327,10 +405,10 @@ CRTCDevice::HSyncCallback(Event& ev) int src = (crtc.r[22] >> 8) * 4; int dst = (crtc.r[22] & 0xff) * 4; - gTVRAM->RasterCopy(src, dst); + tvram->RasterCopy(src, dst); // 所定時間後に完了させる - gScheduler->RestartEvent(raster_event); + scheduler->RestartEvent(raster_event); } break; @@ -340,11 +418,11 @@ CRTCDevice::HSyncCallback(Event& ev) ev.code = 0; // GPIP の状態を更新 - gMFP->SetHSync(false); + mfp->SetHSync(false); break; } - gScheduler->StartEvent(ev); + scheduler->StartEvent(ev); } // ラスターコピー完了イベント @@ -363,12 +441,12 @@ CRTCDevice::VDispCallback(Event& ev) int vdisp = ev.code; // GPIP の状態を更新 - gMFP->SetVDisp(vdisp); + mfp->SetVDisp(vdisp); // 今はここで1画面まるごと作成 if (vdisp) { // Render に作画指示 - gRenderer->Render(); + renderer->Render(); } // 次のタイミングを計算 @@ -382,5 +460,5 @@ CRTCDevice::VDispCallback(Event& ev) ev.time = 191_usec + 1111_usec + 476_usec; ev.code = 1; } - gScheduler->StartEvent(ev); + scheduler->StartEvent(ev); }