--- nono/vm/opm.cpp 2026/04/29 17:05:09 1.1.1.4 +++ nono/vm/opm.cpp 2026/04/29 17:05:28 1.1.1.8 @@ -9,72 +9,164 @@ // #include "opm.h" -#include "mpu.h" +#include "adpcm.h" #include "fdc.h" - -// グローバル参照用 -OPMDevice *gOPM; +#include "mpu.h" +#include "scheduler.h" // コンストラクタ OPMDevice::OPMDevice() - : inherited("OPM") + : inherited(OBJ_OPM) { - devaddr = baseaddr; - devlen = 0x2000; } // デストラクタ OPMDevice::~OPMDevice() { - gOPM = NULL; } -uint64 -OPMDevice::Read(uint32 offset) +// 初期化 +bool +OPMDevice::Init() +{ + if (inherited::Init() == false) { + return false; + } + + adpcm = GetADPCMDevice(); + fdc = GetFDCDevice(); + + return true; +} + +// リセット +void +OPMDevice::ResetHard(bool poweron) +{ + busy_origin = 0; + busy_start = 0; + busy_end = 0; + + fdc->SetForceReady(false); +} + +busdata +OPMDevice::ReadPort(uint32 offset) { + busdata data; + switch (offset) { case 0: - putlog(0, "未実装読み込み $%06x", gMPU->GetPaddr()); - return 0; + putlog(0, "Read $%06x (NOT IMPLEMENTED)", mpu->GetPaddr()); + data = 0; + break; case 1: // OPM ステータスレジスタ - putlog(0, "未実装OPMステータスレジスタ読み込み"); - return 0; + data = 0; + if (IsBusy()) { + data |= 0x80; + } + // タイマ関係の bit1, bit0 は未実装 + + putlog(1, "STAT -> $%02x", data.Data()); + break; default: - __unreachable(); + VMPANIC("corrupted offset=%u", offset); + data = 0xff; + break; } + + // InsideOut p.135 + const busdata read_wait = busdata::Wait(19 * 40_nsec); + data |= read_wait; + + data |= BusData::Size1; + return data; } -uint64 -OPMDevice::Write(uint32 offset, uint32 data) +busdata +OPMDevice::WritePort(uint32 offset, uint32 data) { + uint64 now = scheduler->GetVirtTime(); + switch (offset) { case 0: putlog(1, "REG <- $%02x", data); + // アドレス選択が反映されるまで 8 クロックかかるらしい + // が、顕在化しないと思うので実装していない reg = data; break; case 1: putlog(1, "DATA <- $%02x", data); + // CPU が命令中のどのタイミングでライトするかを用意していないので + // 実際に OPM にライトされるタイミングは正確にはわからない。 + // なので正確な時刻の起点は不明。 + // + // BUSY がアサートされるまで OPM クロックで 8 から 12 クロックかかる + // らしい。 + // + // BUSY のアサート期間は 68 クロックらしい。 + // YM2151 アプリケーションマニュアル(英語版) 2.1.1 + + // BUSY フラグのみならず、書き込み処理期間中のときは保証されていない。 + // とりあえず無視する。 + if (busy_origin <= now && now < busy_end) { + putlog(0, "Write on Busy"); + break; + } + + // 時刻の起点は CPU 側の命令によって異なるはずだが + // わからないので命令のぶんは無視しておく。 + // 53 クロックのウェイトのほうは足す。 + busy_origin = now + 53 * mpu->GetClock_nsec(); + + // busy_start のほうはとりあえず 10 クロックにしておく。 + busy_start = busy_origin + 10 * clk; + busy_end = busy_origin + 68 * clk; + switch (reg) { case 0x1b: - gFDC->SetForceReady(reg & 0x40); + adpcm->SetClock(data & 0x80); + fdc->SetForceReady(data & 0x40); // 他のビットは未対応 break; default: - putlog(0, "未実装書き込み $%06x <- $%02x", gMPU->GetPaddr(), data); + putlog(0, "Write $%06x <- $%02x (NOT IMPLEMENTED)", + mpu->GetPaddr(), data); break; } break; default: - __unreachable(); + VMPANIC("corrupted offset=%u", offset); + break; } - return 0; + // InsideOut p.135 + const busdata write_wait = busdata::Wait(53 * 40_nsec); + + busdata r = write_wait; + r |= BusData::Size1; + return r; } -uint64 -OPMDevice::Peek(uint32 offset) +busdata +OPMDevice::PeekPort(uint32 offset) { // XXX 未実装 return 0xff; } + +bool +OPMDevice::PokePort(uint32 offset, uint32 data) +{ + return false; +} + +// OPM ステータスレジスタの B (BUSY) ビット +bool +OPMDevice::IsBusy() const +{ + uint64 now = scheduler->GetVirtTime(); + + return (busy_start <= now && now < busy_end); +}