--- nono/vm/mpu680x0.h 2026/04/29 17:04:32 1.1 +++ nono/vm/mpu680x0.h 2026/04/29 17:05:17 1.1.1.11 @@ -1,97 +1,362 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // -// MPU (m680x0) +// +// MPU (MC68030) +// #pragma once #include "mpu.h" +#include "branchhistory.h" +#include "event.h" #include "m68030.h" +#include "m68030mmu.h" +#include "mainbus.h" + +class InterruptDevice; class MPU680x0Device : public MPUDevice { - typedef MPUDevice inherited; - public: - MPU680x0Device(uint32); - virtual ~MPU680x0Device(); - - virtual bool Init(); - virtual void ResetHard(); + using inherited = MPUDevice; + friend class m68030MMU; - virtual uint32 Run(uint64 request); + // CPU の実行状態 + static const uint32 CPU_STATE_NORMAL = 0; + static const uint32 CPU_STATE_STOP = 1; + static const uint32 CPU_STATE_HALT = 2; - virtual bool MonitorUpdate(); + public: + MPU680x0Device(); + virtual ~MPU680x0Device() override; + bool Init() override; + void ResetHard(bool poweron) override; // 現在の PPC を取得 - virtual uint32 GetPPC() const { return RegPPC; } + uint32 GetPPC() const override { return ppc; } - // 現在アクセス中の論理アドレス、物理アドレスを取得。 - virtual uint32 GetLaddr() const { return cpu->bus.laddr; } - virtual uint32 GetPaddr() const { return cpu->bus.paddr; } + // 現在の VBR を取得 + uint32 GetVBR() const override { return reg.vbr; } - // 割り込み発生を MPU に通知 (オートベクタ) - virtual void Interrupt(int level); + bool IsSuper() const { return reg.s; } + bool IsMaster() const { return reg.m; } - // 割り込み発生を MPU に通知 (ベクタ) - void Interrupt(int level, int vector); + // CPU の実行状態 (uint32) を返す。 + uint32 GetState() const { return cpu_state; } - // MPU の処理をこの命令で中断させる。 - // スケジューラのイベントリストに追加が発生した時に VM 側から - // MPU へ処理の中断を指示する。 - virtual void Release() { cpu->atomic_reqflag |= CPU_REQ_RELEASE; } + // 現在アクセス中の論理アドレス、物理アドレスを取得。 + uint32 GetLaddr() const override { return bus.laddr; } + uint32 GetPaddr() const override { return bus.paddr; } - // 基準サイクル数を取得 - virtual uint64 total_cycle() const { return cpu->total_cycle; } + // アクセスウェイトを加算 + void AddCycle(int32 cycle) override { used_cycle += cycle; } + + // 割り込みレベルが変わったことを MPU に通知 + void Interrupt(int level) override; // A-Line, F-Line 命令エミュレーションのコールバックを指定する。 // (Human68k エミュレーションで使用) - void SetALineCallback(bool (*callback)(m68kcpu*, void*), void *arg); - void SetFLineCallback(bool (*callback)(m68kcpu*, void*), void *arg); + void SetALineCallback(bool (*callback)(MPU680x0Device*, void*), void *arg); + void SetFLineCallback(bool (*callback)(MPU680x0Device*, void*), void *arg); // FPU を持っているか - bool HaveFPU() const { - return cpu->has_fpu; + bool HaveFPU() const { return has_fpu; } + + // MPU からのアクセスをエミュレートする。 + // アドレスは論理アドレスで、空間は現在のデータ空間。 + // バスエラーなら (uint64)-1 を返す。 + uint64 Read8(uint32 addr) override; + uint64 Read16(uint32 addr) override; + uint64 Read32(uint32 addr) override; + uint64 Write8(uint32 addr, uint32 data) override; + uint64 Write16(uint32 addr, uint32 data) override; + uint64 Write32(uint32 addr, uint32 data) override; + + // ホールト + void Halt(); + + // 基本アクセス。内部から使用する。 + // アドレスは論理アドレスで、空間は現在の命令/データ空間。 + // バスエラーなら C++ の例外をスローする。 + uint32 fetch_8(); + uint32 fetch_16(); + uint32 fetch_32(); + uint32 read_8(uint32 addr); + uint32 read_16(uint32 addr); + uint32 read_32(uint32 addr); + void write_8(uint32 addr, uint32 data); + void write_16(uint32 addr, uint32 data); + void write_32(uint32 addr, uint32 data); + + // MMU アクセス + uint64 TranslatePeek(uint32 laddr, uint fc, bool do_search); + + // レジスタ + uint16 GetIR() const { return ir; } + uint16 GetIR2() const { return ir2; } + // SR を変更する (Human68k から使う) + void SetSR(uint16); + uint16 GetSR() const { return reg.GetSR(); } + + uint64 GetSRP() const { return reg.srp.q; } + uint32 GetSRPh() const { return reg.srp.h; } + uint32 GetSRPl() const { return reg.srp.l; } + uint64 GetCRP() const { return reg.crp.q; } + uint32 GetCRPh() const { return reg.crp.h; } + uint32 GetCRPl() const { return reg.crp.l; } + uint64 GetTT(int idx) const { return reg.tt[idx]; } + uint32 GetTC() const { return reg.tc; } + uint16 GetMMUSR() const { return reg.mmusr; } + + private: + // リセット例外コールバック + void ResetCallback(Event& ev); + + // 命令実行系コールバック + void ExecShort(Event& ev); + void ExecFull(Event& ev); + void ExecTrace(Event& ev); + + // トレース設定。デバッガから呼び出される。 + void SetTrace(bool trace_on) override; + + // 割り込みイベントコールバック + void InterruptCallback(Event& ev); + + // 動作モード変更 + void ChangeState(uint32 new_state); + + // 次の実行サイクルを Full にする。 + void MakeNextFull(); + + inline void CYCLE(int cc) { + used_cycle += cc; + } + inline void CYCLE2(int cc, int ncc) { + used_cycle += cc; + cycle_nocache += ncc - cc; } - // CPU コアを取得 (主にデバッグ用途) - m68kcpu *GetCPU() const { - return cpu; + // PC を addr に変更する。 + // ベクタへのジャンプはここではなく JumpVector()。 + inline void Jump(uint32 addr) { + // 先にブランチ履歴に記録 + brhist.AddEntry(ppc | (IsSuper() ? 1 : 0), addr, ir << 16); + + if (__predict_false((addr & 1) != 0)) { + Exception(M68K::EXCEP_ADDRERR); + return; + } + reg.pc = addr; } - private: - m68kcpu *cpu = NULL; -}; + // -(An) 実行前にレジスタを保存する。 + // バスエラーからの復帰用。 + inline void save_reg_pd(int n) { + // 該当ビットのフラグを立てる + flag_pd |= (1 << n); -// ATC モニタ -class MPUATC : public Object -{ - public: - MPUATC(m68kcpu *cpu); - virtual ~MPUATC() { } + // 保存 + save_pd[n] = reg.A[n]; + } + + // (An)+ 実行前にレジスタを保存する。 + // バスエラーからの復帰用。 + inline void save_reg_pi(int n) { + // 該当ビットのフラグを立てる + flag_pi |= (1 << n); - virtual bool MonitorUpdate(); + // 保存 + save_pi[n] = reg.A[n]; + } + + void restore_reg_pi(); + void restore_reg_pd(); + void JumpVector(int vector); + + protected: +#include "m68030ea.h" private: - m68kcpu *cpu = NULL; -}; +#include "m68030bus.h" + inline uint32 fetch_16_bus(); + inline uint32 fetch_32_bus(); + inline uint32 read_8_bus(); + inline uint32 read_16_bus(); + inline uint32 read_32_bus(); + inline void write_8_bus(); + inline void write_16_bus(); + inline void write_32_bus(); + // FC 指定 + uint32 read_8_fc(uint32 addr); + uint32 read_16_fc(uint32 addr); + uint32 read_32_fc(uint32 addr); + void write_8_fc(uint32 addr, uint32 data); + void write_16_fc(uint32 addr, uint32 data); + void write_32_fc(uint32 addr, uint32 data); + + // 基本アクセスルーチンを使った便利サブルーチン + inline void push_16(uint32 data) { + reg.A[7] -= 2; + write_16(reg.A[7], data); + } + inline void push_32(uint32 data) { + reg.A[7] -= 4; + write_32(reg.A[7], data); + } + inline uint32 pop_16() { + uint32 data = read_16(reg.A[7]); + reg.A[7] += 2; + return data; + } + inline uint32 pop_32() { + uint32 data = read_32(reg.A[7]); + reg.A[7] += 4; + return data; + } + +#define OP_PROTO(name) void __CONCAT(op_,name)() +#include "m68030ops.h" +#undef OP_PROTO + + void ops_divu_32_32(int r, int q); + void ops_divu_64_32(int r, int q); + void ops_divs_32_32(int r, int q); + void ops_divs_64_32(int r, int q); + void ops_movec_rc_rn(); + void ops_movec_rn_rc(); + void ops_movep(); + // もともとソース内のテンプレートだった + void ops_movem_list_ea(int bytesize); + void ops_movem_ea_list(int bytesize); + void ops_trapcc(uint cond); + inline void ops_bra(); + + // 68030core.cpp + void ops_rte(); + void ops_stop(); + // vm/mpu680x0.cpp + void ops_reset(); + +#include "m68030fpu.h" + void fpu_op_start(); + + // MMU + bool SetSRP(uint32, uint32); + bool SetCRP(uint32, uint32); + void SetTT(int idx, uint32); + bool SetTC(uint32); + void SetMMUSR(uint16); + + bool TranslateFetch(); + bool TranslateRead(); + bool TranslateWrite(); + + bool CheckTT() const; + uint32 get_fc(); + void mmu_op_illg2(); + void mmu_op_pflush(); + void mmu_op_pflush_ea(); + void mmu_op_pload(); + void mmu_op_ptest(); + // MMU 有効状態を更新。(TC と TT の変更で呼ばれる) + void mmu_enable_changed(); + + // 例外 + void GenerateExframe0(int vector, uint32 pc, uint16 sr); + void GenerateExframe2(int vector, uint16 sr); + void GenerateExframeB(int vector, uint16 sr); + int ExceptionReset(); + void ExceptionBuserr(int); + int ExceptionInterrupt(); + void Exception(int); + + // 二重バスフォールト + void DoubleBusFault(const char *where); -// ブランチ履歴モニタ -class MPUBrHist : public Object -{ public: - MPUBrHist(m68kcpu *cpu); - virtual ~MPUBrHist() { } + // レジスタ + m68kreg reg {}; + + // MMU work + m68030TT mmu_tt[2] /*{}*/; + uint8 mmu_tt_quick[256] {}; + m68030TC mmu_tc /*{}*/; + m68030ATC atc {this}; + bool mmu_enable {}; // MMU が有効なら true - virtual bool MonitorUpdate(); + // バス + m68kbus bus {}; private: - m68kcpu *cpu = NULL; -}; + uint16 ir {}; + uint16 ir2 {}; + // 現在実行中の命令の先頭アドレス + uint32 ppc {}; + + // レジスタ(状態保存用) + uint32 save_pd[8] {}; // -(An) を保存 + uint32 save_pi[8] {}; // (An)+ を保存 + uint8 flag_pd {}; // -(An) のどのレジスタを保存したか + uint8 flag_pi {}; // (An)+ のどのレジスタを保存したか + + // FPU + int has_fpu {}; // 0:FPUなし、1:68881、2:68882(?) + bool fpu_dirty {}; + struct fpemu fe {}; + + // CPU の実行状態 + uint32 cpu_state {}; + + // 割り込みペンディング + bool intr_pending {}; + + // 現在の外部割り込みレベル + int intr_level {}; + + // 電源オンからの累積消費サイクル + uint64 used_cycle {}; + + int32 cycle_nocache {}; // ノーキャッシュケースの時の追加分 + + // A-Line, F-Line 命令エミュレーションのコールバックとユーザ引数。 + // ユーザ引数には関与しないので呼び出し側が自由に指定してよい。 + // コールバック側で処理完了したら true を返す。 + // デフォルトの処理(すなわちm68k例外)にする場合は false を返す。 + bool (*aline_callback)(MPU680x0Device *, void *) {}; + void *aline_arg {}; + bool (*fline_callback)(MPU680x0Device *, void *) {}; + void *fline_arg {}; + + // ブランチ履歴 (例外履歴を含む) + BranchHistory_m680x0 brhist { OBJ_MPU_BRHIST }; + // 例外履歴のみ + BranchHistory_m680x0 exhist { OBJ_MPU_EXHIST }; + + // 割り込みイベント + Event intr_event { this }; + + // トレースでコールバックを差し替えることができるように + // イベントコールバックを変数に入れてある + EventCallback_t exec_short {}; + EventCallback_t exec_full {}; + + // レジスタモニター + DECLARE_MONITOR_CALLBACK(MonitorUpdateReg); + Monitor reg_monitor { this }; + + // ATC モニター + DECLARE_MONITOR_CALLBACK(MonitorUpdateATC); + Monitor atc_monitor { this }; -extern std::unique_ptr gMPUATC; -extern std::unique_ptr gMPUBrHist; + InterruptDevice *interruptdev {}; + VectorTable *vectortable {}; +}; -#define gMPU680x0 (dynamic_cast(gMPU.get())) +static inline MPU680x0Device *GetMPU680x0Device() { + return Object::GetObject(OBJ_MPU); +}