File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / opm.cpp
Revision 1.1.1.11 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:59 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, HEAD
nono 1.7.0

//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//

//
// OPM (YM2151)
//

#include "opm.h"
#include "adpcm.h"
#include "event.h"
#include "fdc.h"
#include "mainbus.h"
#include "mpu.h"
#include "scheduler.h"

// コンストラクタ
OPMDevice::OPMDevice()
	: inherited(OBJ_OPM)
{
}

// デストラクタ
OPMDevice::~OPMDevice()
{
}

// 初期化
bool
OPMDevice::Init()
{
	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, "Read $%06x (NOT IMPLEMENTED)",
			GetMainbusDevice()->GetPaddr());
		data = 0;
		break;
	 case 1:
		// OPM ステータスレジスタ
		data = 0;
		if (IsBusy()) {
			data |= 0x80;
		}
		// タイマ関係の bit1, bit0 は未実装

		putlog(1, "STAT -> $%02x", data.Data());
		break;
	 default:
		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;
}

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_tsec();

		// busy_start のほうはとりあえず 10 クロックにしておく。
		busy_start = busy_origin + 10 * clk;
		busy_end = busy_origin + 68 * clk;

		switch (reg) {
		 case 0x1b:
			adpcm->SetClock(data & 0x80);
			fdc->SetForceReady(data & 0x40);
			// 他のビットは未対応
			break;
		 default:
			putlog(0, "Write $%06x <- $%02x (NOT IMPLEMENTED)",
				GetMainbusDevice()->GetPaddr(), data);
			break;
		}
		break;
	 default:
		VMPANIC("corrupted offset=%u", offset);
		break;
	}

	// InsideOut p.135
	const busdata write_wait = busdata::Wait(53 * 40_nsec);

	busdata r = write_wait;
	r |= BusData::Size1;
	return r;
}

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);
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.