--- nono/vm/mpu680x0.cpp 2026/04/29 17:04:42 1.1.1.4 +++ nono/vm/mpu680x0.cpp 2026/04/29 17:04:52 1.1.1.8 @@ -16,23 +16,16 @@ std::unique_ptr gMPUATC; // コンストラクタ -// -// reset_vector にはリセットベクタの番地を指定する。 -// 本来の MPU の動作は、リセット例外時にメモリの $0, $4 番地 (もっと言えば -// FC がプログラム空間の) から SP, PC を読み込むのだが、そのためにメモリ空間 -// を切り替える処理はリセット時にしか必要ないわりに結構手間である。そしてこの -// m68k 恒例のブートストラップ動作はソフトウェア的には一切観測する手段がない。 -// であれば、リセット例外時にこのアドレスから SP, PC の値を直接読み込んだと -// しても動作にはまったく違いは出ない。そしてバス・メモリ空間の実装も簡略化 -// できる。すごい楽。 -MPU680x0Device::MPU680x0Device(uint32 reset_vector) +MPU680x0Device::MPU680x0Device() { monitor_size = nnSize(79, 16); - cpu = m68030_init(reset_vector); + cpu = m68030_init(); - // リセット例外イベントコールバック設定 - reset_event.func = (DeviceCallback_t)&MPU680x0Device::ResetCallback; + // イベントコールバック設定 + event.dev = this; + event.SetName("MPU"); + event.func = (DeviceCallback_t)&MPU680x0Device::Callback; // モニタ用の別オブジェクト gMPUATC.reset(new MPUATC(cpu)); @@ -46,6 +39,15 @@ MPU680x0Device::~MPU680x0Device() bool MPU680x0Device::Init() { + // 親クラス + if (inherited::Init() == false) { + return false; + } + // MPU クロックは親 Init() で読み込んで、ここで cpu にセットする + m68030_set_clockspeed(cpu, clock_khz); + // 割り込み時のディレイ + intdelay = cpu->Cycle2Vtime(4); + // FPU (6888x) if (gMainApp.GetVMType() == VMTYPE_LUNA1) { // LUNA は 68881 固定 (設定は見ない) @@ -63,38 +65,68 @@ MPU680x0Device::Init() return true; } -// リセット例外イベントコールバック +bool +MPU680x0Device::PowerOn() +{ + m68030_power_on(cpu); + return true; +} + void -MPU680x0Device::ResetCallback(Event& ev) +MPU680x0Device::ResetHard() { - m68030_exception_reset(cpu); + // リセット例外 + m68030_request_reset_exception(cpu); } -// MPU を request で指定されたクロックサイクルだけ実行する。 -// 今回の実行サイクル数が request を越えた命令境界まで実行するので -// 実際には少しはみ出る。 -// また実行中にデバイスアクセスなどによりスケジューラにイベントが -// 追加されるとその次の命令境界で処理を打ち切って戻ってくる。 -// 戻り値は CPU の outer フラグ。 +// イベントコールバック +// ev.code は +// 0x0 〜 0x7: 新しい割り込みレベルを通知 (0-7) +void +MPU680x0Device::Callback(Event& ev) +{ + int level = ev.code; + m68030_interrupt(cpu, level); +} + +// MPU を vtime [nsec] 分実行する。 +// 戻り値は outer フラグ。 uint32 -MPU680x0Device::Run(uint64 request) +MPU680x0Device::Run(uint32 vtime) { - return m68030_run(cpu, request); + return m68030_run(cpu, vtime); } -// 割り込み発生を MPU に通知 (オートベクタ) -void -MPU680x0Device::Interrupt(Object *src, int level) +// 現在の仮想時刻 [nsec] を取得する +uint64 +MPU680x0Device::GetVirtTime() const { - int vector = M68K_EXCEP_LEVEL_BASE + level; - m68030_interrupt(cpu, level, vector); + return m68030_get_vtime(cpu); } -// 割り込み発生を MPU に通知 (ベクタ) +// 割り込みレベルが変わったことを MPU に通知。 void -MPU680x0Device::Interrupt(Object *src, int level, int vector) +MPU680x0Device::Interrupt(int level) { - m68030_interrupt(cpu, level, vector); + assertmsg(0 <= level && level <= 7, "level=%d", level); + + // 外部デバイスが 68030 の IPL0-2 をアサートしてから、68030 がそれを + // 認識(確定)するのに 2クロックかかる。ここでレベルを比較して割り込みを + // 上げる場合は /IPEND をアサートする。 + // そこからさらに2クロック後以降の命令境界で割り込みを受け付ける(?) + // らしい? + // この 4クロック待つところはイベントスケジューラがある VM 側で担当する。 + // 本来、レベル比較はここから2クロック後に行われるはずだが、ちょっと省略。 + // 今はコア側でペンディング処理をしているのでこっち側は起きたことを全部 + // 通知する。 + // 割り込みレベルが 0 に下がる時にはこのディレイは不要のはず。 + if (level != 0) { + event.time = intdelay; + } else { + event.time = 0; + } + event.code = level; + event.Start(); } // A-Line 命令エミュレーションのコールバックを指定。 @@ -305,7 +337,7 @@ MPU680x0Device::MonitorUpdate(TextScreen void m68030_reset_inst(m68kcpu *cpu) { - gVM->ResetSoft(); + gVM->ResetByMPU(); } //