--- nono/m680x0/m68030.h 2026/04/29 17:04:28 1.1 +++ nono/m680x0/m68030.h 2026/04/29 17:04:49 1.1.1.7 @@ -1,6 +1,7 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // #pragma once @@ -9,6 +10,7 @@ // 外部公開用 // +#include "branchhistory.h" #include #include #include @@ -30,6 +32,7 @@ class m68kcpu; #define CPU_REQ_PROMPT (0x00000008) // デバッガプロンプト #define CPU_REQ_RELEASE (0x00000010) // 命令後に CPU 実行中断 #define CPU_REQ_INTR (0x00000020) // 命令後に割り込みチェックが必要 +#define CPU_REQ_RESET (0x00000040) // リセット例外 #define M68K_SR_T (0xc000) #define M68K_SR_S (0x2000) @@ -75,24 +78,6 @@ class m68kcpu; #include "m68030ccr.h" #include "m68030mmu.h" -// 64ビット数と2つの32ビット数を扱う共用体。 -// 構造はまったく同じだが名前だけ場所に合わせてつけたい。 -#if _BYTE_ORDER == _LITTLE_ENDIAN -#define DEF_UNION64(NAME, Q, H, L) \ - union NAME { \ - uint64 Q; \ - struct { uint32 L, H; }; \ - } -#else -#define DEF_UNION64(NAME, Q, H, L) \ - union NAME { \ - uint64 Q; \ - struct { uint32 H, L; }; \ - } -#endif - -DEF_UNION64(union64, q, h, l); - // レジスタイメージを保持する構造体。 // この構造体はコピーとか比較をするのでポインタとかは置かないこと。 struct m68kreg { @@ -127,10 +112,7 @@ struct m68kreg { struct fpframe fpframe; - // CPU の実行状態 - // 0 .. 通常状態 - // CPU_REQ_STOP .. STOP 状態 - // CPU_REQ_HALT .. HALT 状態 + // CPU の実行状態 (SCHED_CPU_*) uint32 state; public: @@ -140,14 +122,6 @@ struct m68kreg { } }; -// ブランチ履歴 -struct m68kbrhist { - uint32 count; // 連続通過回数 - uint32 from; // ジャンプ元アドレス(VA) - uint32 to; // ジャンプ先アドレス(VA) - uint32 padding; -} __packed; - // バス状態 struct m68kbus { // 論理アドレスと SSW。 @@ -178,24 +152,24 @@ struct m68kbus { uint GetFC() const { return ssw & SSW_FC_MASK; } }; -class m68kcpu { +class m68kcpu final { public: m68kcpu() { } - virtual ~m68kcpu() { } + ~m68kcpu() { } public: m68kreg reg {}; - uint32 ppc = 0; // 現在実行中の命令の先頭アドレス - uint16 ir = 0; - uint16 ir2 = 0; + uint32 ppc {}; // 現在実行中の命令の先頭アドレス + uint16 ir {}; + uint16 ir2 {}; uint32 save_pd[8] {}; // -(An) を保存 uint32 save_pi[8] {}; // (An)+ を保存 - uint8 flag_pd = 0; // -(An) のどのレジスタを保存したか - uint8 flag_pi = 0; // (An)+ のどのレジスタを保存したか + uint8 flag_pd {}; // -(An) のどのレジスタを保存したか + uint8 flag_pi {}; // (An)+ のどのレジスタを保存したか // MMU 状態 - bool mmu_enable = false; + bool mmu_enable {}; // SRP void SetSRP(uint32, uint32); uint64 GetSRP() const { return reg.srp.q; } @@ -216,70 +190,52 @@ class m68kcpu { void SetMMUSR(uint16); uint16 GetMMUSR() const { return reg.mmusr; } // MMU work - m68030TT mmu_tt[2]; + m68030TT mmu_tt[2] {}; uint8 mmu_tt_quick[256] {}; - m68030TC mmu_tc; + m68030TC mmu_tc {}; m68030ATC atc {this}; private: void mmu_enable_changed(); public: - int has_fpu = 0; // 0:FPUなし、1:68881、2:68882(?) - bool fpu_dirty = false; + int has_fpu {}; // 0:FPUなし、1:68881、2:68882(?) + bool fpu_dirty {}; struct fpemu fe {}; std::atomic atomic_reqflag {}; // 外部ループ & リクエストフラグ - // 割り込みペンディング - // intr_vector[1] がレベル1割り込みのペンディング中ベクタ - // : - // intr_vector[7] がレベル7割り込みのペンディング中ベクタ - // - // [0] を intr_pending (割り込みペンディング) として使う。 - // ペンディングになっているレベルのビットを立てる。 - // - // bit 7 6 5 4 3 2 1 0 - // Level 0 1 2 3 4 5 6 7 - // - // intrmtx 取ってからアクセスすること。 - uint8 intr_vector[8] {}; -#define intr_pending intr_vector[0] - - // 割り込みマスク - // 現在の割り込みレベルで割り込みを受け付けるレベルのビットを立てる - // I=0 だと割り込みレベル 1..7 を受け付けるので %01111111 - // I=5 だと割り込みレベル 6,7 を受け付けるので %00000011 - // I=6 だと割り込みレベル 7 を受け付けるので %00000001 - // ただし I=7 はこれを使わず別処理する - // bit 7 6 5 4 3 2 1 0 - // Level 0 1 2 3 4 5 6 7 - // - // intrmtx 取ってからアクセスすること。 - uint8 intr_mask = 0; - - // 割り込み要求は別スレッドから呼ばれる場合もあるので - // (自スレッドから呼び出した関数の向こうから呼ばれる場合もあることに注意) - // それらの変数はこれで保護する。 - // 最終的には atomic_reqflag に CPU_REQ_INTR を立てることでメインループ - // に通知する (この通知そのものは atomic なので特に保護しなくてもよい)。 - std::mutex intrmtx {}; - - uint64 total_cycle = 0; // 積算実行サイクル数 - int32 cycle_cache = 0; // キャッシュケースの所用サイクル数 - int32 cycle_nocache = 0; // ノーキャッシュケースの時の *追加分* - uint64 inst_count = 0; // 命令実行数 - - // ブランチ履歴 - // 1エントリは64ビット。上位32ビットがブランチ先アドレス、下位32ビットが - // ブランチ元命令のアドレス。 - // brhist_top がカーソル(次に書き込む位置)を示しており8ビットなので勝手に - // 循環する。.count = 0 は書き込み前の無効エントリ。 - // 16バイト単位のテーブルなので16バイト境界に整列がよかろう。 - alignas(16) m68kbrhist brhist[256] {}; - // 直近に使用したインデックスを指す。 - // ループ回数カウントのため、次に書き込むインデックスでないことに注意。 - uint8 brhist_top = 0; + // 割り込みペンディングレベル + // 外部から入力されている(最も高い)割り込みレベルを保持する。 + int intr_pending {}; + + // 仮想時刻 [nsec] + uint64 total_vtime {}; + + // サイクル数を仮想時間 [nsec] に変換 + uint64 Cycle2Vtime(int32 cycle) const { + return (int64)cycle * c2v; + } + + // 仮想時間 [nsec] をサイクル数に変換 + uint32 Vtime2Cycle(uint32 vtime) const { + return ((uint64)vtime * v2c_factor) >> v2c_shift; + } + + uint32 used_cycle {}; // 現在のターンでこれまでに消費したサイクル数 + uint32 goal_cycle {}; // 現在のターンで消費すべきサイクル数 + int32 cycle_nocache {}; // ノーキャッシュケースの時の追加分 + uint32 clock_khz {}; // MPU クロック [kHz] + uint32 c2v {}; // サイクル数を nsec に変換する際の係数 + uint64 v2c_factor {}; // nsec をサイクル数に変換する際の乗算係数 + uint32 v2c_shift {}; // nsec をサイクル数に変換する際のシフト係数 + + uint64 inst_count {}; // 命令実行数 + + // ブランチ履歴 (例外履歴を含む) + BranchHistory_m680x0 brhist {}; + // 例外履歴のみ + BranchHistory_m680x0 exhist {}; // バス m68kbus bus {}; @@ -287,16 +243,20 @@ class m68kcpu { // 手抜きのためリセットベクタ取得アドレスを知っている。 // リセット時のためだけにデバイス側に細工を入れるのは実行時の // オーバーヘッドが大きそうなので。 - uint32 reset_vector = 0; + uint32 reset_vector {}; // A-Line, F-Line 命令エミュレーションのコールバックとユーザ引数。 // ユーザ引数には関与しないので呼び出し側が自由に指定してよい。 // コールバック側で処理完了したら true を返す。 // デフォルトの処理(すなわちm68k例外)にする場合は false を返す。 - bool (*aline_callback)(m68kcpu *, void *) = NULL; - void *aline_arg = NULL; - bool (*fline_callback)(m68kcpu *, void *) = NULL; - void *fline_arg = NULL; + bool (*aline_callback)(m68kcpu *, void *) {}; + void *aline_arg {}; + bool (*fline_callback)(m68kcpu *, void *) {}; + void *fline_arg {}; + + // ダブルバスフォールトのコールバックとユーザ引数。 + void (*halt_callback)(void *) {}; + void *halt_arg {}; }; #define RegD(n) (cpu->reg.da[(n)]) @@ -330,9 +290,13 @@ class m68kcpu { // 外部公開ルーチン extern m68kcpu *m68030_init(uint32 reset_vector); -extern uint32 m68030_run(m68kcpu *cpu, uint64 request); -extern void m68030_exception_reset(m68kcpu *cpu); -extern void m68030_interrupt(m68kcpu *cpu, int level, int vector); +extern uint32 m68030_run(m68kcpu *cpu, uint32 delta); +extern void m68030_set_clockspeed(m68kcpu *cpu, uint32 clock_khz); +extern uint64 m68030_get_vtime(m68kcpu *cpu); +extern void m68030_request_reset_exception(m68kcpu *cpu); +extern void m68030_interrupt(m68kcpu *cpu, int level); +extern const char *m68030_get_exception_name(int vector); // 外部で定義されるコールバック extern void m68030_reset_inst(m68kcpu *cpu); +extern int m68030_interrupt_acknowledge(int level);