|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
5: //
6:
1.1.1.4 root 7: //
8: // OPM (YM2151)
9: //
10:
1.1 root 11: #include "opm.h"
1.1.1.5 ! root 12: #include "adpcm.h"
1.1.1.4 root 13: #include "fdc.h"
1.1.1.5 ! root 14: #include "mpu.h"
! 15: #include "scheduler.h"
1.1 root 16:
1.1.1.4 root 17: // グローバル参照用
18: OPMDevice *gOPM;
1.1 root 19:
1.1.1.4 root 20: // コンストラクタ
1.1 root 21: OPMDevice::OPMDevice()
1.1.1.3 root 22: : inherited("OPM")
1.1 root 23: {
24: devaddr = baseaddr;
25: devlen = 0x2000;
26: }
27:
1.1.1.4 root 28: // デストラクタ
1.1 root 29: OPMDevice::~OPMDevice()
30: {
1.1.1.4 root 31: gOPM = NULL;
1.1 root 32: }
33:
1.1.1.5 ! root 34: // リセット
! 35: void
! 36: OPMDevice::ResetHard(bool poweron)
! 37: {
! 38: busy_origin = 0;
! 39: busy_start = 0;
! 40: busy_end = 0;
! 41:
! 42: gFDC->SetForceReady(false);
! 43: }
! 44:
1.1 root 45: uint64
1.1.1.2 root 46: OPMDevice::Read(uint32 offset)
1.1 root 47: {
1.1.1.5 ! root 48: uint8 data;
! 49:
! 50: gMPU->AddCycle(19); // InsideOut p.135
! 51:
1.1.1.4 root 52: switch (offset) {
53: case 0:
1.1.1.5 ! root 54: putlog(0, "Read $%06x (NOT IMPLEMENTED)", gMPU->GetPaddr());
1.1.1.4 root 55: return 0;
56: case 1:
57: // OPM ステータスレジスタ
1.1.1.5 ! root 58: data = 0;
! 59: if (IsBusy()) {
! 60: data |= 0x80;
! 61: }
! 62: // タイマ関係の bit1, bit0 は未実装
! 63:
! 64: putlog(1, "STAT -> $%02x", data);
! 65: return data;
1.1.1.4 root 66: default:
67: __unreachable();
68: }
1.1 root 69: }
70:
71: uint64
1.1.1.2 root 72: OPMDevice::Write(uint32 offset, uint32 data)
1.1 root 73: {
1.1.1.5 ! root 74: gMPU->AddCycle(53); // InsideOut p.135
! 75:
! 76: uint64 now = gScheduler->GetVirtTime();
! 77:
1.1.1.4 root 78: switch (offset) {
79: case 0:
80: putlog(1, "REG <- $%02x", data);
1.1.1.5 ! root 81: // アドレス選択が反映されるまで 8 クロックかかるらしい
! 82: // が、顕在化しないと思うので実装していない
1.1.1.4 root 83: reg = data;
84: break;
85: case 1:
86: putlog(1, "DATA <- $%02x", data);
1.1.1.5 ! root 87: // CPU が命令中のどのタイミングでライトするかを用意していないので
! 88: // 実際に OPM にライトされるタイミングは正確にはわからない。
! 89: // なので正確な時刻の起点は不明。
! 90: //
! 91: // BUSY がアサートされるまで OPM クロックで 8 から 12 クロックかかる
! 92: // らしい。
! 93: //
! 94: // BUSY のアサート期間は 68 クロックらしい。
! 95: // YM2151 アプリケーションマニュアル(英語版) 2.1.1
! 96:
! 97: // BUSY フラグのみならず、書き込み処理期間中のときは保証されていない。
! 98: // とりあえず無視する。
! 99: if (busy_origin <= now && now < busy_end) {
! 100: putlog(0, "Write on Busy");
! 101: break;
! 102: }
! 103:
! 104: // 時刻の起点は CPU 側の命令によって異なるはずだが
! 105: // わからないので命令のぶんは無視しておく。
! 106: // 53 クロックのウェイトのほうは足す。
! 107: busy_origin = now + 53 * gMPU->GetClock_nsec();
! 108:
! 109: // busy_start のほうはとりあえず 10 クロックにしておく。
! 110: busy_start = busy_origin + 10 * clk;
! 111: busy_end = busy_origin + 68 * clk;
! 112:
1.1.1.4 root 113: switch (reg) {
114: case 0x1b:
1.1.1.5 ! root 115: gADPCM->SetClock(data & 0x80);
! 116: gFDC->SetForceReady(data & 0x40);
1.1.1.4 root 117: // 他のビットは未対応
118: break;
119: default:
1.1.1.5 ! root 120: putlog(0, "Write $%06x <- $%02x (NOT IMPLEMENTED)",
! 121: gMPU->GetPaddr(), data);
1.1.1.4 root 122: break;
123: }
124: break;
125: default:
126: __unreachable();
127: }
128:
1.1 root 129: return 0;
130: }
131:
132: uint64
1.1.1.2 root 133: OPMDevice::Peek(uint32 offset)
1.1 root 134: {
135: // XXX 未実装
136: return 0xff;
137: }
1.1.1.5 ! root 138:
! 139: // OPM ステータスレジスタの B (BUSY) ビット
! 140: bool
! 141: OPMDevice::IsBusy() const
! 142: {
! 143: uint64 now = gScheduler->GetVirtTime();
! 144:
! 145: return (busy_start <= now && now < busy_end);
! 146: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.