|
|
nono 1.7.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
//
// MPU (MC68030/MC68040)
//
// ログレベル基準。
// 1: 状態変化などの概要
// 2: 命令などによる詳細
// 3: メモリアクセスの概要
// 4: メモリアクセスの詳細(MMU等)
// 5: デバッガアクセスのデバッグ用
#pragma once
#include "mpu.h"
#include "branchhistory.h"
#include "bus.h"
#include "m680x0.h"
#include "m68030mmu.h"
#include <array>
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 m68030PageTableOp;
public:
MPU680x0Device();
~MPU680x0Device() override;
bool Init() override;
void ResetHard(bool poweron) override;
// 現在の PPC を取得
uint32 GetPPC() const override { return ppc; }
// 現在の 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 mpu_name; }
bool IsSuper() const { return reg.s; }
bool IsMaster() const { return reg.m; }
// 割り込みレベルが変わったことを MPU に通知
void Interrupt(int level) override;
// A-Line, F-Line 命令エミュレーションのコールバックを指定する。
// (Human68k エミュレーションで使用)
void SetALineCallback(bool (*callback)(MPU680x0Device*, void*), void *arg);
void SetFLineCallback(bool (*callback)(MPU680x0Device*, void*), void *arg);
// バーストアクセスをサポートするか
void EnableBurstAccess(bool enable) { has_burst_access = enable; }
void SetCI() override {
bus.ci = true;
}
// ホールト
void Halt();
// 基本アクセス。内部から使用する。
// アドレスは論理アドレスで、空間は現在の命令/データ空間。
// バスエラーなら C++ の例外をスローする。
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);
return read_data(addr);
}
void write_n(uint32 laddr, uint size, uint32 data) {
busaddr addr =
busaddr(laddr) | fc_data | BusAddr::W | busaddr::Size(size);
write_data(addr, data);
}
uint32 read_1(uint32 laddr) { return read_n(laddr, 1); }
uint32 read_2(uint32 laddr) { return read_n(laddr, 2); }
uint32 read_4(uint32 laddr) { return read_n(laddr, 4); }
void write_1(uint32 laddr, uint32 data) { write_n(laddr, 1, data); }
void write_2(uint32 laddr, uint32 data) { write_n(laddr, 2, data); }
void write_4(uint32 laddr, uint32 data) { write_n(laddr, 4, data); }
// MMU アクセス
virtual busaddr TranslatePeek(busaddr laddr) = 0;
// 統計
virtual void CalcStat() = 0;
// レジスタ
uint16 GetIR() const { return ir; }
uint16 GetIR2() const { return ir2; }
// SR を変更する (Human68k から使う)
void SetSR(uint16);
uint16 GetSR() const { return reg.GetSR(); }
protected:
// リセット例外コールバック
void ResetCallback(Event *);
// 命令実行系コールバック
void ExecShort(Event *);
void ExecFull(Event *);
void ExecTrace(Event *);
// トレース設定。デバッガから呼び出される。
void SetTrace(bool enable) override;
// 割り込みイベントコールバック
void InterruptCallback(Event *);
// 次の実行サイクルを Full にする。
void MakeNextFull();
inline void CYCLE(int cc) {
used_cycle += cc;
}
// 副作用前提のマクロなので do-while にしない。
#define CYCLE3(name) used_cycle += cycle_table[__CONCAT(cycidx_,name)]
// PC を addr に変更する。
// ベクタへのジャンプはここではなく JumpVector()。
inline void Jump(uint32 addr) {
// 先にブランチ履歴に記録
brhist.AddEntry(ppc | (IsSuper() ? 1 : 0), addr, ir);
if (__predict_false((addr & 1) != 0)) {
ExceptionBuserr(M68K::EXCEP_ADDRERR);
return;
}
reg.pc = addr;
}
// -(An) 実行前にレジスタを保存する。
// バスエラーからの復帰用。
inline void save_reg_pd(int n) {
// 該当ビットのフラグを立てる
flag_pd |= (1U << n);
// 保存
save_pd[n] = reg.A[n];
}
// (An)+ 実行前にレジスタを保存する。
// バスエラーからの復帰用。
inline void save_reg_pi(int n) {
// 該当ビットのフラグを立てる
flag_pi |= (1U << n);
// 保存
save_pi[n] = reg.A[n];
}
// -(An) 実行前にレジスタを(まだ保存してなければ)保存する。
// 1命令で同じレジスタを2回 -(An) する可能性がある命令用。
inline void save_reg_pd_if(int n) {
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 & (1U << n)) == 0) {
save_reg_pi(n);
}
}
void restore_reg_pi();
void restore_reg_pd();
void JumpVector(uint vector, bool from_buserr);
protected:
#include "m680x0ea.h"
protected:
// 基本アクセスルーチンを使った便利サブルーチン
inline void push_2(uint32 data) {
reg.A[7] -= 2;
write_2(reg.A[7], data);
}
inline void push_4(uint32 data) {
reg.A[7] -= 4;
write_4(reg.A[7], data);
}
inline uint32 pop_2() {
uint32 data = read_2(reg.A[7]);
reg.A[7] += 2;
return data;
}
inline uint32 pop_4() {
uint32 data = read_4(reg.A[7]);
reg.A[7] += 4;
return data;
}
// バスアクセス
virtual uint32 read_data(busaddr addr) = 0;
virtual void write_data(busaddr addr, uint32 data) = 0;
// 物理バスアクセス
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 "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);
void ops_rte();
void ops_stop();
void ops_reset();
virtual void ops_mmu30() = 0;
virtual void ops_mmu40_pflush() = 0;
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<uint32, 13>&);
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];
// キャッシュ
virtual void SetCACR(uint32) = 0;
// 例外
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 ext_vector, uint32 info);
// リセット例外の CPU 固有部分。
virtual void ResetCache() = 0;
virtual void ResetMMU() = 0;
// 二重バスフォールト
void DoubleBusFault(const char *where);
public:
// レジスタ
m68kreg reg {};
bool mmu_enable {}; // MMU が有効なら true
// バス
m68kbus bus {};
// 現在の特権状態でのプログラム空間とデータ空間アクセス用。
busaddr fc_inst {}; // こっちは R 含む
busaddr fc_data {}; // こっちは R/W 含まない
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)+ のどのレジスタを保存したか
// FPU
enum {
FPU_STATE_NULL, // 内部状態なし
FPU_STATE_IDLE, // FRESTORE 以降に FPU 命令を実行した
FPU_STATE_BUSY, // ビジー状態 (実装していない)
FPU_STATE_NOIMPL, // 浮動小数点未実装命令
} fpu_state {};
struct fpemu fe {};
// 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 {};
// 現在の外部割り込みレベル
int intr_level {};
// 電源オンからの累積消費サイクル
uint64 used_cycle {};
// 今回のバスウェイト [nsec]
uint64 buswait {};
// 外部バスがバースト転送に対応していれば true
bool has_burst_access {};
// MPU/FPU 種別
m680x0MPUType mpu_type {};
m680x0FPUType fpu_type {};
const char *mpu_name {};
// 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 };
// NetBSD カーネルのエントリポイント。
// TRAP#0 の履歴でシステムコールを判定するのに使う。
uint32 netbsd_entrypoint {};
// 割り込みイベント
Event *intr_event {};
// トレースでコールバックを差し替えることができるように
// イベントコールバックを変数に入れてある
EventCallback_t exec_short {};
EventCallback_t exec_full {};
// モニター
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<m68030Cache> icache /*{}*/;
std::unique_ptr<m68030Cache> 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<std::unique_ptr<m68040TT>, 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<std::unique_ptr<m68040TT>, 2> mmu_itt /*{}*/;
std::array<std::unique_ptr<m68040TT>, 2> mmu_dtt /*{}*/;
std::unique_ptr<m68040TC> mmu_tc /*{}*/;
std::unique_ptr<m68040ATC> atc_inst /*{}*/;
std::unique_ptr<m68040ATC> atc_data /*{}*/;
};
// ブートストラップ
extern MPU680x0Device *NewMPU680x0Device();
// これを呼びたい人は mpu を持ってるはず。
inline MPU680x0Device *GetMPU680x0Device(Device *mpu_) {
return dynamic_cast<MPU680x0Device*>(mpu_);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.