--- nono/vm/opm.cpp 2026/04/29 17:05:09 1.1.1.4 +++ nono/vm/opm.cpp 2026/04/29 17:05:13 1.1.1.5 @@ -9,8 +9,10 @@ // #include "opm.h" -#include "mpu.h" +#include "adpcm.h" #include "fdc.h" +#include "mpu.h" +#include "scheduler.h" // グローバル参照用 OPMDevice *gOPM; @@ -29,17 +31,38 @@ OPMDevice::~OPMDevice() gOPM = NULL; } +// リセット +void +OPMDevice::ResetHard(bool poweron) +{ + busy_origin = 0; + busy_start = 0; + busy_end = 0; + + gFDC->SetForceReady(false); +} + uint64 OPMDevice::Read(uint32 offset) { + uint8 data; + + gMPU->AddCycle(19); // InsideOut p.135 + switch (offset) { case 0: - putlog(0, "未実装読み込み $%06x", gMPU->GetPaddr()); + putlog(0, "Read $%06x (NOT IMPLEMENTED)", gMPU->GetPaddr()); return 0; case 1: // OPM ステータスレジスタ - putlog(0, "未実装OPMステータスレジスタ読み込み"); - return 0; + data = 0; + if (IsBusy()) { + data |= 0x80; + } + // タイマ関係の bit1, bit0 は未実装 + + putlog(1, "STAT -> $%02x", data); + return data; default: __unreachable(); } @@ -48,20 +71,54 @@ OPMDevice::Read(uint32 offset) uint64 OPMDevice::Write(uint32 offset, uint32 data) { + gMPU->AddCycle(53); // InsideOut p.135 + + uint64 now = gScheduler->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 * gMPU->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); + gADPCM->SetClock(data & 0x80); + gFDC->SetForceReady(data & 0x40); // 他のビットは未対応 break; default: - putlog(0, "未実装書き込み $%06x <- $%02x", gMPU->GetPaddr(), data); + putlog(0, "Write $%06x <- $%02x (NOT IMPLEMENTED)", + gMPU->GetPaddr(), data); break; } break; @@ -78,3 +135,12 @@ OPMDevice::Peek(uint32 offset) // XXX 未実装 return 0xff; } + +// OPM ステータスレジスタの B (BUSY) ビット +bool +OPMDevice::IsBusy() const +{ + uint64 now = gScheduler->GetVirtTime(); + + return (busy_start <= now && now < busy_end); +}