--- nono/vm/mpu88xx0.h 2026/04/29 17:04:32 1.1 +++ nono/vm/mpu88xx0.h 2026/04/29 17:05:34 1.1.1.15 @@ -1,53 +1,253 @@ // // nono -// Copyright (C) 2020 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // -// MPU (m88xx0) +// +// MPU (M88xx0) +// #pragma once #include "mpu.h" +#include "branchhistory.h" +#include "event.h" #include "m88100.h" +#include "m88200.h" +#include -class MPU88xx0Device : public MPUDevice +class MPU88xx0Device : public MainMPUDevice, private M88100 { - typedef MPUDevice inherited; - public: - MPU88xx0Device(uint32 reset_addr); - virtual ~MPU88xx0Device(); + using inherited = MainMPUDevice; + friend class Luna88kPROMEmuDevice; - virtual void ResetHard(); + static const uint32 CPU_STATE_NORMAL = 0; + static const uint32 CPU_STATE_STOP = 1; // 疑似 STOP 状態 - virtual bool MonitorUpdate(); + enum ExceptionKind { + NORMAL, + DATA, + ERROR, + INTR, + TRAP, + }; - // MPU を request サイクル分実行する - virtual uint32 Run(uint64 request) { return cpu->Run(request); } + public: + MPU88xx0Device(); + ~MPU88xx0Device() override; - // 基準サイクル数を取得 - virtual uint64 total_cycle() const { return cpu->total_cycle; } + bool Init() override; + void ResetHard(bool poweron) override; - // MPU の処理をこの命令で中断させる。 - virtual void Release() { cpu->Release(); } + // キャッシュ禁止通知 (LUNA-88K では使っていない?) + void SetCI() override { } // 現在実行中の命令の PC を取得 - virtual uint32 GetPPC() const { return cpu->Getxip(); } + uint32 GetPPC() const override { return reg.xip; } + + // 現在の VBR を取得 + uint32 GetVBR() const override { return reg.vbr; } + + // MPU 名を取得 + const char *GetMPUName() const override { return "MC88100"; } + + // スーパーバイザ/ユーザモードなら true + bool IsSuper() const { return (reg.psr & PSR_SUPER) != 0; } + bool IsUser() const { return (reg.psr & PSR_SUPER) == 0; } + + // 割り込みが許可なら true + bool IsIntrEnable() const { return (reg.psr & PSR_IND) == 0; } + + // CPU の実行状態 (uint32) を返す。 + uint32 GetState() const { return cpu_state; } // 現在アクセス中の論理アドレス、物理アドレスを取得。 - virtual uint32 GetLaddr() const { return 0; } // XXX NotImplemented - virtual uint32 GetPaddr() const { return 0; } // XXX NotImplemented + uint32 GetLaddr() const override; + uint32 GetPaddr() const override; + + // アクセスウェイト [clock] を加算。m88200 が呼ぶ。 + void AddCycle(int32 cycle) { + // XXX 本来は今アクセスしてきている(= バスマスターの) CMMU に + // 対して加算するべき?かもだが、まだその辺の概念がない。 + used_cycle += cycle; + } + + // アクセスウェイト [nsec] を加算。m88200 が呼ぶ。 + void AddWait(uint32 nsec) { + buswait += nsec; + } // 割り込み発生を MPU に通知 - virtual void Interrupt(int dummy) { cpu->Interrupt(); } + void Interrupt(int level) override; - // CPU コアを取得 (主にデバッグ用途) - m88kcpu *GetCPU() const { return cpu.get(); } + // DOS call エミュレーションのコールバックを指定する + void SetFLineCallback(bool (*callback)(MPU88xx0Device *, void *), + void *arg); + + // op が負数ならバスエラーが起きた + static bool OpIsBusErr(uint64 op) { + return (int64)op < 0; + } + + m88100reg reg {}; + + // 直近のデータアクセスアドレス。 + // XXX メモリブレークポイント用だがもう少しなんとかしたい + uint64 lastaddr {}; private: - // リセットベクタ - uint32 reset_vector; + // リセット例外コールバック + void ResetCallback(Event& ev); + + // 命令実行系コールバック + void ExecNormal(Event& ev); + void ExecTrace(Event& ev); + + // トレース設定。デバッガから呼び出される。 + void SetTrace(bool trace_on) override; + + uint64 fetch() { + reg.nip = reg.fip; + reg.opF = cmmu[0]->load_4(reg.fip); + reg.fip += 4; + return reg.opF; + } + + void Prefetch(); + + // PSR を newpsr に変更する。 + // CY ビットは特別にこの関数を経由しないで変更してよいことにする。 + void SetPSR(uint32 newpsr) { + reg.psr = newpsr; + SetPSR(); + } + // 更新された psr に基づいて、PSR 変更によって必要な処理をする。 + // CY ビットを変更した場合は呼ばなくてよいことにする。 + void SetPSR() { + // CMMU に S/U 信号を出す + cmmu[0]->SetSuper(IsSuper()); + cmmu[1]->SetSuper(IsSuper()); + } + + // キャリーフラグが立っていれば 1 を返す + uint32 GetCY() const { return (reg.psr >> 28) & 1; } + + // キャリーフラグをセットする。 + // cy のうち最下位ビットのみ参照する (呼び出し側はマスクせず渡してよい) + void SetCY(uint cy) { + reg.psr &= ~PSR_C; + reg.psr |= ((cy & 1) << 28); + } + + // SFU1(FPU)が有効なら true + bool IsFPUEnable() const { return (reg.psr & PSR_SFD1) == 0; } + + // MXM (Misaligned Access Enable) なら true + bool IsMXM() const { return (reg.psr & PSR_MXM) != 0; } + + // FPxS1, FPxS2 レジスタを設定 + static void SetFPxS(uint32 *h, uint32 *l, double src, uint32 t); + void SetFPS1(double src, uint32 t1); + void SetFPS2(double src, uint32 t2); + void SetFPS1(uint32 src); + void SetFPS2(uint32 src); + // FP Result を FPIT, FPRH, FPRL レジスタに設定 + void SetFPRx(double src); + + void Exception(int vec); + void ExceptionCore(int vec, ExceptionKind cause); + uint32 Calcdmt(uint32 flag); + void ReadDataException32(uint32 addr, uint32 flag); + void ReadDataException64(uint32 addr, uint32 flag); + void WriteDataException32(uint32 addr, uint32 flag); + void WriteDataException64(uint32 addr, uint32 flag); + void XmemDataException(uint32 addr, uint32 flag); + void FPPreciseException(uint32 cause, double s2); + void FPPreciseException(uint32 cause, double s1, double s2); + void FPPreciseException(uint32 cause); + void FPImpreciseException(uint32 cause); + + // ブランチ処理用 + void DoBranch(uint32 toaddr, bool next_exec); + uint32 nop_counter {}; // STOP 検出用の nop 命令カウンタ + + // ロードストア命令用の下処理 + uint32 ldst_scale(uint32 size); + + // 浮動小数点数命令用の処理 + void ops_int(int mode); + +#define OP_PROTO(name) void __CONCAT(op_,name)() +#include "m88100ops.h" +OP_PROTO(illegal); +#undef OP_PROTO + + void fpu_unimpl(); + + // float,double と整数値の相互変換 + static float u2f(uint32 u) { + float v; + memcpy(&v, &u, sizeof(v)); + return v; + } + static double u2d(uint64 u) { + double v; + memcpy(&v, &u, sizeof(v)); + return v; + } + static uint32 f2u(float v) { + uint32 u; + memcpy(&u, &v, sizeof(u)); + return u; + } + static uint64 d2u(double v) { + uint64 u; + memcpy(&u, &v, sizeof(u)); + return u; + } + + // [0] 命令バス + // [1] データバス + std::array, 2> cmmu {}; + + // ブランチ履歴 (例外履歴を含む) + BranchHistory_m88xx0 brhist { OBJ_MPU_BRHIST }; + // 例外履歴のみ + BranchHistory_m88xx0 exhist { OBJ_MPU_EXHIST }; + + // 今のところサイクルを正確に実装するのは無理なので + // 適当に全部積み上げてある。 + + // 電源オンからの累積消費サイクル + uint64 used_cycle {}; + + // 今回のバスウェイト [nsec] + uint64 buswait {}; + + // ペンディング中の割り込みレベル + int intr_pending {}; + + // CPU の状態 + void ChangeState(uint32 new_state); + uint32 cpu_state {}; + + // 疑似 STOP 状態に入る機能を有効にする場合 true + bool pseudo_stop_enable {}; + + // DOS call エミュレーション + bool (*fline_callback)(MPU88xx0Device *, void *) = NULL; + void *fline_arg {}; + + // レジスタモニター + DECLARE_MONITOR_CALLBACK(MonitorUpdate); + Monitor *monitor {}; - std::unique_ptr cpu; + // イベントコールバック + EventCallback_t exec_normal {}; }; -#define gMPU88xx0 (dynamic_cast(gMPU.get())) +// これを呼びたい人は mpu を持ってるはず。 +static inline MPU88xx0Device *GetMPU88xx0Device(Device *mpu_) { + return dynamic_cast(mpu_); +}