--- nono/vm/mpu680x0.h 2026/04/29 17:05:29 1.1.1.14 +++ nono/vm/mpu680x0.h 2026/04/29 17:06:00 1.1.1.19 @@ -5,37 +5,43 @@ // // -// MPU (MC68030) +// MPU (MC68030/MC68040) // +// ログレベル基準。 +// 1: 状態変化などの概要 +// 2: 命令などによる詳細 +// 3: メモリアクセスの概要 +// 4: メモリアクセスの詳細(MMU等) +// 5: デバッガアクセスのデバッグ用 + #pragma once #include "mpu.h" #include "branchhistory.h" #include "bus.h" -#include "event.h" -#include "m68030.h" +#include "m680x0.h" #include "m68030mmu.h" +#include class InterruptDevice; class m68030Cache; struct m68030CacheLine; +enum class m68030MPUType; +class m68040TT; +class m68040TC; +class m68040ATC; +class m68040ATCEntry; class MPU680x0Device : public MainMPUDevice { using inherited = MainMPUDevice; - friend class m68030MMU; - - // CPU の実行状態 - static const uint32 CPU_STATE_NORMAL = 0; - static const uint32 CPU_STATE_STOP = 1; - static const uint32 CPU_STATE_HALT = 2; + friend class m68030PageTableOp; public: MPU680x0Device(); ~MPU680x0Device() override; - void SetLogLevel(int loglevel_) override; bool Init() override; void ResetHard(bool poweron) override; @@ -45,19 +51,19 @@ class MPU680x0Device : public MainMPUDev // 現在の VBR を取得 uint32 GetVBR() const override { return reg.vbr; } + // MPU 種別を取得 + m680x0MPUType GetMPUType() const { return mpu_type; } + + // FPU + m680x0FPUType GetFPUType() const { return fpu_type; } + bool HasFPU() const { return fpu_type.Is4060FPU() || fpu_type.Is6888x(); } + // MPU 名を取得 - const char *GetMPUName() const override { return "MC68030"; } + const char *GetMPUName() const override { return mpu_name; } bool IsSuper() const { return reg.s; } bool IsMaster() const { return reg.m; } - // CPU の実行状態 (uint32) を返す。 - uint32 GetState() const { return cpu_state; } - - // 現在アクセス中の論理アドレス、物理アドレスを取得。 - uint32 GetLaddr() const override { return bus.laddr.Addr(); } - uint32 GetPaddr() const override { return bus.paddr.Addr(); } - // 割り込みレベルが変わったことを MPU に通知 void Interrupt(int level) override; @@ -66,9 +72,6 @@ class MPU680x0Device : public MainMPUDev void SetALineCallback(bool (*callback)(MPU680x0Device*, void*), void *arg); void SetFLineCallback(bool (*callback)(MPU680x0Device*, void*), void *arg); - // FPU を持っているか - bool HaveFPU() const { return has_fpu; } - // バーストアクセスをサポートするか void EnableBurstAccess(bool enable) { has_burst_access = enable; } @@ -82,8 +85,8 @@ class MPU680x0Device : public MainMPUDev // 基本アクセス。内部から使用する。 // アドレスは論理アドレスで、空間は現在の命令/データ空間。 // バスエラーなら C++ の例外をスローする。 - uint32 fetch_2(); - uint32 fetch_4(); + virtual uint32 fetch_2() = 0; + virtual uint32 fetch_4() = 0; uint32 read_n(uint32 laddr, uint size) { busaddr addr = busaddr(laddr) | fc_data | BusAddr::R | busaddr::Size(size); @@ -102,9 +105,9 @@ class MPU680x0Device : public MainMPUDev void write_4(uint32 laddr, uint32 data) { write_n(laddr, 4, data); } // MMU アクセス - busaddr TranslatePeek(busaddr laddr); + virtual busaddr TranslatePeek(busaddr laddr) = 0; // 統計 - void CalcStat(); + virtual void CalcStat() = 0; // レジスタ uint16 GetIR() const { return ir; } @@ -113,33 +116,20 @@ class MPU680x0Device : public MainMPUDev 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: + protected: // リセット例外コールバック - void ResetCallback(Event& ev); + void ResetCallback(Event *); // 命令実行系コールバック - void ExecShort(Event& ev); - void ExecFull(Event& ev); - void ExecTrace(Event& ev); + void ExecShort(Event *); + void ExecFull(Event *); + void ExecTrace(Event *); // トレース設定。デバッガから呼び出される。 - void SetTrace(bool trace_on) override; + void SetTrace(bool enable) override; // 割り込みイベントコールバック - void InterruptCallback(Event& ev); - - // 動作モード変更 - void ChangeState(uint32 new_state); + void InterruptCallback(Event *); // 次の実行サイクルを Full にする。 void MakeNextFull(); @@ -147,10 +137,8 @@ class MPU680x0Device : public MainMPUDev inline void CYCLE(int cc) { used_cycle += cc; } - inline void CYCLE2(int cc, int ncc) { - used_cycle += cc; - cycle_nocache += ncc - cc; - } + // 副作用前提のマクロなので do-while にしない。 +#define CYCLE3(name) used_cycle += cycle_table[__CONCAT(cycidx_,name)] // PC を addr に変更する。 // ベクタへのジャンプはここではなく JumpVector()。 @@ -169,7 +157,7 @@ class MPU680x0Device : public MainMPUDev // バスエラーからの復帰用。 inline void save_reg_pd(int n) { // 該当ビットのフラグを立てる - flag_pd |= (1 << n); + flag_pd |= (1U << n); // 保存 save_pd[n] = reg.A[n]; @@ -179,7 +167,7 @@ class MPU680x0Device : public MainMPUDev // バスエラーからの復帰用。 inline void save_reg_pi(int n) { // 該当ビットのフラグを立てる - flag_pi |= (1 << n); + flag_pi |= (1U << n); // 保存 save_pi[n] = reg.A[n]; @@ -188,14 +176,14 @@ class MPU680x0Device : public MainMPUDev // -(An) 実行前にレジスタを(まだ保存してなければ)保存する。 // 1命令で同じレジスタを2回 -(An) する可能性がある命令用。 inline void save_reg_pd_if(int n) { - if ((flag_pd & (1 << n)) == 0) { + if ((flag_pd & (1U << n)) == 0) { save_reg_pd(n); } } // (An)+ 実行前にレジスタを(まだ保存してなければ)保存する。 // 1命令で同じレジスタを2回 (An)+ する可能性がある命令用。 inline void save_reg_pi_if(int n) { - if ((flag_pi & (1 << n)) == 0) { + if ((flag_pi & (1U << n)) == 0) { save_reg_pi(n); } } @@ -205,9 +193,9 @@ class MPU680x0Device : public MainMPUDev void JumpVector(uint vector, bool from_buserr); protected: -#include "m68030ea.h" +#include "m680x0ea.h" - private: + protected: // 基本アクセスルーチンを使った便利サブルーチン inline void push_2(uint32 data) { reg.A[7] -= 2; @@ -229,78 +217,114 @@ class MPU680x0Device : public MainMPUDev } // バスアクセス - uint32 read_data(busaddr addr); - busdata read_nocache(busaddr addr); - busdata read_cache(m68030Cache *cache, busaddr addr); - busdata read_line(m68030CacheLine *, uint32 tag, uint32 opermask); - busdata read_single(busdata bd, uint32 readmask, uint32 opermask); - - void write_data(busaddr addr, uint32 data); - busdata write_bus(busaddr addr, uint32 data); + virtual uint32 read_data(busaddr addr) = 0; + virtual void write_data(busaddr addr, uint32 data) = 0; - busdata read_phys(); - busdata write_phys(uint32 data); - bool read_phys_burst(uint32 *dst); + // 物理バスアクセス + busdata read_phys(const busaddr); + busdata write_phys(const busaddr, uint32 data); + bool read_phys_burst(busaddr, uint32 *dst); + busdata read_phys_4(uint32 addr); + busdata write_phys_4(uint32 addr, uint32 data); #define OP_PROTO(name) void __CONCAT(op_,name)() -#include "m68030ops.h" +#include "m680x0ops.h" #undef OP_PROTO void ops_divu_32_32(uint r, uint q); void ops_divu_64_32(uint r, uint q); void ops_divs_32_32(uint r, uint q); void ops_divs_64_32(uint r, uint q); + void ops_link(int32 imm); void ops_movec_rc_rn(); void ops_movec_rn_rc(); + virtual bool ops_movec_rc_rn_cpu(uint c, uint n) = 0; + virtual bool ops_movec_rn_rc_cpu(uint c, uint n) = 0; 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(); + busaddr translate_fc40(busaddr); - // 68030core.cpp void ops_rte(); void ops_stop(); - // vm/mpu680x0.cpp void ops_reset(); + virtual void ops_mmu30() = 0; + virtual void ops_mmu40_pflush() = 0; -#include "m68030fpu.h" - void fpu_op_start(); + void ResetFPU(bool hard); + void fpu_init(); + uint32 cea_fpu(int bytes); + uint32 cea_fpulc(int bytes); + void read_8(uint32 addr, uint32 *data); + void read_12(uint32 addr, uint32 *data); + void write_8(uint32 addr, const uint32 *data); + void write_12(uint32 addr, const uint32 *data); + + void fpu_op_illg(); + void fpu_op_illg2(); + + void fpu_op_fgen_reg(); + void fpu_op_fgen_mem(); + void fgen(const uint32 *srcdata, uint srcsize_id); + void fgen881pre(uint dstnum, const uint32 *srcdata, uint srcsize_id); + bool fgen040pre(uint opmode, uint dst, const uint32 *src, uint srcsize); + void fpu_op_fmovecr(); + void fpu_op_fmove_to_mem(); + void fpu_op_fmove_b_mem(); + void fpu_op_fmove_w_mem(); + void fpu_op_fmove_l_mem(); + void fpu_op_fmove_s_mem(); + void fpu_op_fmove_d_mem(); + void fpu_op_fmove_x_mem(); + void fpu_op_fmove_p_mem(); + void fpu_op_fmove_ea2ctl(); + void fpu_op_fmovem_ea2ctl(); + void fpu_op_fmove_ctl2ea(); + void fpu_op_fmovem_ctl2ea(); + void fpu_op_fmovem_ea2reg(); + void fpu_op_fmovem_reg2ea(); + void fpu_op_fscc(); + void fpu_op_fdbcc(); + void fpu_op_ftrapcc_w(); + void fpu_op_ftrapcc_l(); + void fpu_op_ftrapcc(); + void fpu_op_fbcc_w(); + void fpu_op_fbcc_l(); + void fpu_op_fsave(); + void fpu_op_frestore(); + void fpu_op_fsave_frame_noimpl(std::array&); + + static struct fpframe initial_fpframe; + static const uint32 fsave_frame_null[1]; + static const uint32 fsave_frame_idle_68881[7]; + static const uint32 fsave_frame_idle_68040[1]; // キャッシュ - void SetCACR(uint32); - - // MMU - bool SetSRP(uint32, uint32); - bool SetCRP(uint32, uint32); - void SetTT(int idx, uint32); - bool SetTC(uint32); - void SetMMUSR(uint16); - - bool TranslateRead(); - bool TranslateWrite(); - - bool CheckTT(); - 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(); + virtual void SetCACR(uint32) = 0; // 例外 - void GenerateExframe0(uint vector, uint32 pc, uint16 sr); - void GenerateExframe2(uint vector, uint16 sr); + void GenerateExframe0(uint vector, uint16 sr, uint32 pc); + void GenerateExframe2(uint vector, uint16 sr, uint32 pc, uint32 addr); + void GenerateExframe4(uint vector, uint16 sr, uint32 ea); + void GenerateExframe7(uint vector, uint16 sr); void GenerateExframeB(uint vector, uint16 sr); int ExceptionReset(); void ExceptionBuserr(uint vector); uint64 ExceptionInterrupt(); + void ExceptionTrap0(); void ExceptionTrap15(); + void ExceptionFLine(); + void ExceptionFP(uint vector); + void ExceptionFPLC(uint ea); void Exception(uint vector); - void ExceptionGeneric(uint vector, uint32 inst); + void ExceptionGeneric(uint ext_vector, uint32 info); + + // リセット例外の CPU 固有部分。 + virtual void ResetCache() = 0; + virtual void ResetMMU() = 0; // 二重バスフォールト void DoubleBusFault(const char *where); @@ -308,11 +332,6 @@ class MPU680x0Device : public MainMPUDev public: // レジスタ m68kreg reg {}; - - // MMU work - m68030TT mmu_tt[2] /*{}*/; - m68030TC mmu_tc /*{}*/; - m68030ATC atc {this}; bool mmu_enable {}; // MMU が有効なら true // バス @@ -321,29 +340,70 @@ class MPU680x0Device : public MainMPUDev busaddr fc_inst {}; // こっちは R 含む busaddr fc_data {}; // こっちは R/W 含まない - private: + protected: uint16 ir {}; uint16 ir2 {}; // 現在実行中の命令の先頭アドレス uint32 ppc {}; + // サイクル数表 + const int8 *cycle_table {}; + static const int8 cycle_table_030cc[]; + static const int8 cycle_table_030ncc[]; + static const int8 cycle_table_040[]; + // レジスタ(状態保存用) uint32 save_pd[8] {}; // -(An) を保存 uint32 save_pi[8] {}; // (An)+ を保存 uint8 flag_pd {}; // -(An) のどのレジスタを保存したか uint8 flag_pi {}; // (An)+ のどのレジスタを保存したか - // キャッシュ - std::unique_ptr icache /*{}*/; - std::unique_ptr dcache /*{}*/; - // FPU - int has_fpu {}; // 0:FPUなし、1:68881、2:68882(?) - bool fpu_dirty {}; + enum { + FPU_STATE_NULL, // 内部状態なし + FPU_STATE_IDLE, // FRESTORE 以降に FPU 命令を実行した + FPU_STATE_BUSY, // ビジー状態 (実装していない) + FPU_STATE_NOIMPL, // 浮動小数点未実装命令 + } fpu_state {}; struct fpemu fe {}; - // CPU の実行状態 - uint32 cpu_state {}; + // 68040 FPU 用の内部状態 + struct FPU40 { + uint32 reg1b; // CMDREG1B + uint32 ea; // 計算した EA + enum { + // NONE は未決定だが Unnormal/Denormal ではないいずれかなので + // UNNORMAL より小さい値にしておく。(大小比較する) + TAG_NONE = -1, + TAG_NORMAL = 0, + TAG_ZERO = 1, + TAG_INF = 2, + TAG_NAN = 3, + TAG_UNNORMAL = 4, // .X denormal or unnormal + TAG_DENORMAL = 5, // .S/.D denormal + + // 内部表現 (下位3ビットは維持したまま上位で詳細を区別する) + TAG_DENORMAL_S = (0x10 | TAG_DENORMAL), + TAG_DENORMAL_D = (0x20 | TAG_DENORMAL), + TAG_DENORMAL_X = (0x30 | TAG_UNNORMAL), + TAG_UNNORMAL_X = (0x40 | TAG_UNNORMAL), + // PACKED のときタグは undefined となっている。仕方ないので 7。 + TAG_PACKED = 0x07 , + }; + int32 stag; + int32 dtag; + uint32 etemp[3]; + uint32 fptemp[3]; + enum { + E1 = 0x04000000, + E3 = 0x02000000, + }; + uint32 eflag; + bool post; // T bit, post-op exception + + // 表示用のフラグ。例外発生から FSAVE の間までが有効。 + bool enable; + } fpu40 {}; // 割り込みペンディング bool intr_pending {}; @@ -360,7 +420,10 @@ class MPU680x0Device : public MainMPUDev // 外部バスがバースト転送に対応していれば true bool has_burst_access {}; - int32 cycle_nocache {}; // ノーキャッシュケースの時の追加分 + // MPU/FPU 種別 + m680x0MPUType mpu_type {}; + m680x0FPUType fpu_type {}; + const char *mpu_name {}; // A-Line, F-Line 命令エミュレーションのコールバックとユーザ引数。 // ユーザ引数には関与しないので呼び出し側が自由に指定してよい。 @@ -376,34 +439,209 @@ class MPU680x0Device : public MainMPUDev // 例外履歴のみ BranchHistory_m680x0 exhist { OBJ_MPU_EXHIST }; + // NetBSD カーネルのエントリポイント。 + // TRAP#0 の履歴でシステムコールを判定するのに使う。 + uint32 netbsd_entrypoint {}; + // 割り込みイベント - Event intr_event { this }; + Event *intr_event {}; // トレースでコールバックを差し替えることができるように // イベントコールバックを変数に入れてある EventCallback_t exec_short {}; EventCallback_t exec_full {}; - // レジスタモニター - DECLARE_MONITOR_CALLBACK(MonitorUpdateReg); - void MonitorUpdateFPU(TextScreen&, int y, m68kreg&); - Monitor reg_monitor { this }; - - // ATC モニター - DECLARE_MONITOR_CALLBACK(MonitorUpdateATC); - Monitor atc_monitor { this }; - - // キャッシュモニター - DECLARE_MONITOR_CALLBACK(MonitorUpdateCache); - int MonitorUpdateCache1(TextScreen&, int y, m68030Cache *, - char (*)(const m68030CacheLine&)); - Monitor cache_monitor { this }; + // モニター + void MonitorScreenRegCommon(TextScreen&, m68kreg&, uint32, uint32); + int MonitorScreenFPU(TextScreen&, int y, m68kreg&); + Monitor *reg_monitor {}; + Monitor *atc_monitor {}; + Monitor *cache_monitor {}; InterruptDevice *interruptdev {}; VectorTable *vectortable {}; }; +class MPU68030Device : public MPU680x0Device +{ + using inherited = MPU680x0Device; + + public: + MPU68030Device(); + ~MPU68030Device() override; + + void SetLogLevel(int loglevel_) override; + void ResetHard(bool poweron) override; + + 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; } + + busaddr TranslatePeek(busaddr laddr) override; + void CalcStat() override; + + // fc に対する文字列("UD"とか)を返す。 + static const char *FCstr(uint fc_) { + return &fcstr[fc_ * 4]; + } + + private: + // リセット + void ResetCache() override; + void ResetMMU() override; + + // 命令 + bool ops_movec_rc_rn_cpu(uint c, uint n) override; + bool ops_movec_rn_rc_cpu(uint c, uint n) override; + void ops_mmu30() override; + void ops_mmu40_pflush() override; + uint32 get_fc(); + void ops_mmu30_illg2(); + void ops_mmu30_pflusha(); + void ops_mmu30_pflush(); + void ops_mmu30_pflush_ea(); + void ops_mmu30_pload(); + void ops_mmu30_ptest(); + uint32 ptest_atc(const busaddr); + uint32 ptest_search(uint level, const busaddr, uint rn); + + // バスアクセス + uint32 fetch_2() override; + uint32 fetch_4() override; + uint32 read_data(busaddr addr) override; + busdata read_nocache(busaddr addr); + void write_data(busaddr addr, uint32 data) override; + + busdata read_cache(m68030Cache *cache, busaddr addr); + busdata read_single(busaddr); + busdata fill_single(busaddr, busdata); + busdata write_bus(busaddr addr, uint32 data); + + // キャッシュ + void SetCACR(uint32) override; + + // MMU + busaddr TranslateRead(const busaddr laddr); + busaddr TranslateWrite(const busaddr laddr); + busaddr TTMatch(const busaddr laddr) const; + void PageTableSearch(const busaddr laddr, m68030ATCLine *); + + bool SetSRP(uint32, uint32); + bool SetCRP(uint32, uint32); + void SetTT(int idx, uint32); + bool SetTC(uint32); + void SetMMUSR(uint16); + // MMU 有効状態を更新。(TC と TT の変更で呼ばれる) + void mmu_enable_changed(); + + public: + // MMU work + m68030TT mmu_tt[2] /*{}*/; + m68030TC mmu_tc /*{}*/; + m68030ATC atc {this}; + + private: + DECLARE_MONITOR_SCREEN(MonitorScreenReg); + DECLARE_MONITOR_SCREEN(MonitorScreenATC); + DECLARE_MONITOR_SCREEN(MonitorScreenCache); + int MonitorScreenMMU(TextScreen&, int, m68kreg&); + int MonitorScreenCache1(TextScreen&, int y, m68030Cache *, + char (*)(const m68030CacheLine&)); + + // キャッシュ + std::unique_ptr icache /*{}*/; + std::unique_ptr dcache /*{}*/; + + static const char fcstr[]; +}; + +class MPU68040Device : public MPU680x0Device +{ + using inherited = MPU680x0Device; + friend class WXPageTable68040Window; + + public: + MPU68040Device(); + ~MPU68040Device() override; + + uint32 GetITT(int n) const { return reg.itt[n]; } + uint32 GetDTT(int n) const { return reg.dtt[n]; } + uint32 GetSRP() const { return reg.srp40; } + uint32 GetURP() const { return reg.urp40; } + uint32 GetTC() const { return reg.tc40; } + uint32 GetMMUSR() const { return reg.mmusr40; } + + busaddr TranslatePeek(busaddr laddr) override; + void CalcStat() override; + + // デバッガ (pf コマンド) からも使う。 + static std::string GetReg1BName(uint32 opclass, uint32 sz, uint32 cmd); + static std::string GetFPUTagName(int32); + + private: + // リセット + void ResetCache() override; + void ResetMMU() override; + + // 命令 + bool ops_movec_rc_rn_cpu(uint c, uint n) override; + bool ops_movec_rn_rc_cpu(uint c, uint n) override; + void ops_mmu30() override; + void ops_mmu40_pflush() override; + + // バスアクセス + uint32 fetch_2() override; + uint32 fetch_4() override; + uint32 read_data(busaddr addr) override; + busdata read_nocache(busaddr addr); + void write_data(busaddr addr, uint32 data) override; + + // キャッシュ + void SetCACR(uint32) override; + + // MMU + busaddr TranslateRead(const busaddr laddr); + busaddr TranslateWrite(const busaddr laddr); + m68040TT *TTMatch(std::array, 2>&, + const busaddr) const; + busaddr LookupATC(m68040ATC&, const busaddr); + void Search(m68040ATCEntry *, const busaddr); + uint32 PeekDesc(uint32 base, uint32 idx); + + void SetTT(m68040TT *, uint32); + void SetSRP(uint32); + void SetURP(uint32); + void SetTC(uint32); + // MMU 有効状態を更新。(TC と *TTn の変更で呼ばれる) + void mmu_enable_changed(); + + DECLARE_MONITOR_SCREEN(MonitorScreenReg); + DECLARE_MONITOR_SCREEN(MonitorScreenATC); + int MonitorScreenMMU(TextScreen&, int, m68kreg&); + void MonitorScreenTT(TextScreen&, int x, int y, const char *, uint32); + void MonitorScreenATC1(TextScreen&, int, bool, const m68040ATC *); + + // FPU + void MonitorScreenFPU40(TextScreen&, int y, const FPU40&); + + std::array, 2> mmu_itt /*{}*/; + std::array, 2> mmu_dtt /*{}*/; + std::unique_ptr mmu_tc /*{}*/; + + std::unique_ptr atc_inst /*{}*/; + std::unique_ptr atc_data /*{}*/; +}; + +// ブートストラップ +extern MPU680x0Device *NewMPU680x0Device(); + // これを呼びたい人は mpu を持ってるはず。 -static inline MPU680x0Device *GetMPU680x0Device(Device *mpu_) { +inline MPU680x0Device *GetMPU680x0Device(Device *mpu_) { return dynamic_cast(mpu_); }