Annotation of nono/vm/opm.cpp, revision 1.1.1.5

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: }

unix.superglobalmegacorp.com

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