--- nono/vm/mpu680x0.cpp 2026/04/29 17:04:52 1.1.1.8 +++ nono/vm/mpu680x0.cpp 2026/04/29 17:05:37 1.1.1.19 @@ -4,31 +4,75 @@ // Licensed under nono-license.txt // -// MPU (m680x0) +// +// MPU (MC68030/MC68040) +// + +// MPU/FPU 種別について: +// +// 構造上やむをえないが MPU が 68030 なら FPU は fpu-type で指定し、 +// MPU が 68040/68LC040 なら mpu-type で MPU/FPU を指定する。 +// +// mpu-type fpu-type +// -------- -------- +// 68030 none : 68030、FPU なし +// 68030 68881 : 68030 + 68881 +// 68LC040 - : 68040 (FPU なし) +// 68040 - : 68040 (FPU あり) #include "mpu680x0.h" #include "config.h" -#include "m68030excep.h" +#include "debugger.h" +#include "exttostr.h" +#include "interrupt.h" +#include "m68030cache.h" +#include "m68040mmu.h" #include "mainapp.h" -#include "vm.h" -#include +#include "mainbus.h" +#include "scheduler.h" +#include "syncer.h" +#include "uimessage.h" +#include "vectortable.h" + +// ブートストラップ。設定に応じてインスタンスを生成して返す。 +// ここではとりあえず 68040 かそれ以外なら 68030 を生成しておき、 +// Init() でもう一度 (機種情報も含めて) チェックする。 +// Init() (か Create()) まで進まないとエラー終了出来ないが、 +// その時点では MainMPU のインスタンスは必要なので。 +MPU680x0Device * +NewMPU680x0Device() +{ + const ConfigItem& item = gConfig->Find("mpu-type"); + std::string mpu_type = item.AsString(); + + // 設定に応じて継承側のクラスを作成する。 + // この段階では知らないものは一旦 68030 にしておく。 + if (mpu_type == "68040" || strcasecmp(mpu_type.c_str(), "68LC040") == 0) { + return new MPU68040Device(); + } else { + return new MPU68030Device(); + } +} -std::unique_ptr gMPUATC; + +// +// 68030/040 共通クラス +// // コンストラクタ MPU680x0Device::MPU680x0Device() + : inherited() { - monitor_size = nnSize(79, 16); + // MPU 種別は継承側で設定する。設定されないままなら Init() でエラー。 + mpu_type = m680x0MPUType::NONE; - cpu = m68030_init(); + // FPU + fpu_init(); - // イベントコールバック設定 - event.dev = this; - event.SetName("MPU"); - event.func = (DeviceCallback_t)&MPU680x0Device::Callback; + // 例外カウンタ + excep_counter.resize(256); - // モニタ用の別オブジェクト - gMPUATC.reset(new MPUATC(cpu)); + SetTrace(false); } // デストラクタ @@ -36,72 +80,241 @@ MPU680x0Device::~MPU680x0Device() { } +// 初期化 bool MPU680x0Device::Init() { - // 親クラス if (inherited::Init() == false) { return false; } - // MPU クロックは親 Init() で読み込んで、ここで cpu にセットする - m68030_set_clockspeed(cpu, clock_khz); - // 割り込み時のディレイ - intdelay = cpu->Cycle2Vtime(4); - - // FPU (6888x) - if (gMainApp.GetVMType() == VMTYPE_LUNA1) { - // LUNA は 68881 固定 (設定は見ない) - cpu->has_fpu = 1; + + // なる早で、もう一度 MPU 設定を読んで判定。そのついでに FPU も判定。 + // "mpu-type" の設定値が不正な場合でも (コンストラクト時点ではまだエラーを + // 報告できない構造なので) 一旦 MPU68030Device インスタンスとしてここに + // 来る。なのでここで改めてチェックしてエラー報告する必要がある。 + // mpu_type (メンバ変数) 自体はすでにセットされている。 + const ConfigItem& mputype_item = gConfig->Find("mpu-type"); + std::string mputype = mputype_item.AsString(); + if (mputype == "68030") { + // FPU はこのすぐ次で機種ごとに選ぶ。 + } else if (mputype == "68040") { + fpu_type = m680x0FPUType::M68040; + } else if (strcasecmp(mputype.c_str(), "68LC040") == 0) { + fpu_type = m680x0FPUType::M68LC040; + // 名前も再設定? + mpu_name = "MC68LC040"; + SetName("MPU(68LC040)"); } else { - // X68k は設定による - const ConfigItem& item = gConfig->Find("mpu-has-fpu"); - cpu->has_fpu = item.AsInt(); - if (cpu->has_fpu < 0 || cpu->has_fpu > 2) { - item.Err(); + mputype_item.Err(); + return false; + } + + // 機種ごとに MPU/FPU のサポート状況が違う。 + // 68030 なら fpu_type はここでセットする。 + switch (gMainApp.GetVMType()) { + case VMType::LUNA1: + case VMType::NEWS: + // NEWS は元々 68030 モデルのみ。 + // うち NWS-1750 は 68030/68882 固定のようだがここでは 68881 とする。 + // LUNA-I は基本的に 68030/68881 モデルのみ。 + if (mpu_type != m680x0MPUType::M68030) { + mputype_item.Err("Only 68030 can be specified for vmtype=%s", + gMainApp.GetVMTypeStr().c_str()); return false; } + fpu_type = m680x0FPUType::M68881; + break; + + case VMType::X68030: + case VMType::VIRT68K: + // 68030/68040 をサポート。mpu_type は継承コンストラクタで設定済み。 + if (mpu_type == m680x0MPUType::M68030) { + // 68030 なら FPU は設定で指定可能。 + const ConfigItem& fputype_item = gConfig->Find("fpu-type"); + std::string fputype = fputype_item.AsString(); + if (fputype == "none" || fputype == "0") { + fpu_type = m680x0FPUType::M68030_NOFPU; + } else if (fputype == "68881" || fputype == "1") { + fpu_type = m680x0FPUType::M68881; + } else { + fputype_item.Err(); + return false; + } + } else { + // 68040 なら FPU はすぐ上で設定済み。 + } + break; + + default: + VMPANIC("vmtype=%s not configured", gMainApp.GetVMTypeStr().c_str()); + break; + } + + debugger = GetDebugger(); + interruptdev = GetInterruptDevice(); + vectortable = GetVectorTable(); + + // 割り込みイベントコールバック設定。 + // + // 外部デバイスが 68030 の IPL0-2 をアサートしてから、68030 がそれを + // 認識(確定)するのに 1 ~ 2 クロックかかる。 + // その後レベルを比較するのは次のアップエッジ。 + // その後 /IPEND をアサートするのはその次のアップエッジ。 + // 合わせると、IPL0-2 が変化してから /IPEND がアサートされるまでは + // 2 ~ 3 クロックかかる。(Figure 8-4) + intr_event.func = ToEventCallback(&MPU680x0Device::InterruptCallback); + intr_event.time = 3 * clock_nsec; + intr_event.SetName(GetName() + " Interrupt"); + scheduler->RegistEvent(intr_event); + + // レジスタモニタの行数。 + int height = 9; + if (mpu_type == m680x0MPUType::M68030) { + height++; + } + if (HasFPU()) { + height += 12; + if (GetFPUType().Is4060FPU()) { + height += 3; + } } + reg_monitor->SetSize(78, height); return true; } -bool -MPU680x0Device::PowerOn() +// リセット +void +MPU680x0Device::ResetHard(bool poweron) { - m68030_power_on(cpu); - return true; + if (poweron) { + // サイクルを初期化。 + used_cycle = 0; + + // 履歴を初期化。電源(再)投入時のみ行う。 + exhist.Clear(); + brhist.Clear(); + + for (int i = 0; i < 16; i++) { + reg.R[i] = 0xcccccccc; + } + // XXX ただし X68030 IPLROM には A6 を初期化せずに書き込んでいるバグが + // あるため、A6 を概ね RAM のあるあたりに指定しておく…。 + reg.A[6] = 0x000ccccc; + + // この後起きるリセット例外で PC を初期化する前に参照することになるので + // これだけここでも初期化しておく。 + reg.pc = 0; + } + resetting = true; + + // リセット例外を 520 クロック後に起こす。 + // 520 は、電源オン時、Vcc が最小動作規定値に達してから最低 520 クロック + // の間アサートしなければならないという値であって、実際これだけかかる + // とかいう値ではない。が、こちら側の都合で、この後実行される RAM の + // ResetHard() がブートページを用意した後で ExceptionReset() で + // SP, PC をフェッチするという順序にしないといけないため、リセット例外が + // 起動するまでに時間がかかるというところを都合よく真似ておく。 + + exec_event.func = ToEventCallback(&MPU680x0Device::ResetCallback); + exec_event.time = 520 * clock_nsec; + exec_event.SetName(GetName() + " Reset"); + scheduler->RestartEvent(exec_event); } +// リセット例外コールバック。 +// MPU リセットから規定時間後に呼ばれる。 void -MPU680x0Device::ResetHard() +MPU680x0Device::ResetCallback(Event& ev) { - // リセット例外 - m68030_request_reset_exception(cpu); + resetting = false; + intr_pending = false; + + // リセット例外を実行 + int cycle = ExceptionReset(); + + // コールバックを ResetCallback から Exec* に差し替える前のここで + // トレース状態を初期化する。 + SetTrace(debugger->IsTrace()); + + // 以降は通常走行。 + if (cpu_state == CPU_STATE_NORMAL) { + exec_event.func = exec_short; + exec_event.time = cycle * clock_nsec; + exec_event.SetName(GetName() + " Execute"); + scheduler->RestartEvent(exec_event); + } else { + // リセット例外でダブルバスフォールトならここで停止。 + } } -// イベントコールバック -// ev.code は -// 0x0 〜 0x7: 新しい割り込みレベルを通知 (0-7) +// 割り込みチェック、STOP/HALT 処理を含んだ完全な実行サイクルのコールバック。 +// SR の I2:I0 が変わった直後などに呼ばれる。 +// ここでは割り込みをチェックし、必要なら割り込み処理を起動、 +// そうでなければ通常の命令実行を行う。 void -MPU680x0Device::Callback(Event& ev) +MPU680x0Device::ExecFull(Event& ev) { - int level = ev.code; - m68030_interrupt(cpu, level); + if (cpu_state == CPU_STATE_HALT) { + // HALT なら停止。 + return; + } + + // マスクよりも割り込みレベルのほうが高ければ割り込み処理。 + // この時点で intr_pending が true の可能性もあることに留意。 + // 割り込みレベルは変化してないので Level 7 の特別処理は不要 + // (割り込みレベルの変化はここではなく InterruptCallback() で処理)。 + if (intr_level > reg.intr_mask) { + intr_pending = true; + } + + if (intr_pending) { + // 割り込み処理を起動。 + + intr_pending = false; + + // 前の命令実行後となるここで割り込みを起動 + ev.time = ExceptionInterrupt(); + scheduler->StartEvent(ev); + } else { + if (cpu_state == CPU_STATE_NORMAL) { + // コールバックを通常処理に戻してから... + exec_event.func = exec_short; + + // この場で次の命令を実行。 + ExecShort(ev); + } else { + // STOP ならここで停止。 + } + } } -// MPU を vtime [nsec] 分実行する。 -// 戻り値は outer フラグ。 -uint32 -MPU680x0Device::Run(uint32 vtime) +// トレース実行 +void +MPU680x0Device::ExecTrace(Event& ev) { - return m68030_run(cpu, vtime); + debugger->ExecMain(); + ExecFull(ev); } -// 現在の仮想時刻 [nsec] を取得する -uint64 -MPU680x0Device::GetVirtTime() const +// トレース状態を設定する +void +MPU680x0Device::SetTrace(bool enable) { - return m68030_get_vtime(cpu); + if (enable) { + exec_short = ToEventCallback(&MPU680x0Device::ExecTrace); + exec_full = ToEventCallback(&MPU680x0Device::ExecTrace); + } else { + exec_short = ToEventCallback(&MPU680x0Device::ExecShort); + exec_full = ToEventCallback(&MPU680x0Device::ExecFull); + } + exec_event.func = exec_full; + + // STOP 命令中にオンにするには、停止しているイベントを起こす。 + // STOP 命令中にオフにする場合は、何もしなくていい。 + if (enable && exec_event.IsRunning() == false) { + scheduler->StartEvent(exec_event); + } } // 割り込みレベルが変わったことを MPU に通知。 @@ -110,254 +323,435 @@ MPU680x0Device::Interrupt(int level) { assertmsg(0 <= level && level <= 7, "level=%d", level); - // 外部デバイスが 68030 の IPL0-2 をアサートしてから、68030 がそれを - // 認識(確定)するのに 2クロックかかる。ここでレベルを比較して割り込みを - // 上げる場合は /IPEND をアサートする。 - // そこからさらに2クロック後以降の命令境界で割り込みを受け付ける(?) - // らしい? - // この 4クロック待つところはイベントスケジューラがある VM 側で担当する。 - // 本来、レベル比較はここから2クロック後に行われるはずだが、ちょっと省略。 - // 今はコア側でペンディング処理をしているのでこっち側は起きたことを全部 - // 通知する。 - // 割り込みレベルが 0 に下がる時にはこのディレイは不要のはず。 - if (level != 0) { - event.time = intdelay; - } else { - event.time = 0; + intr_event.code = level; + if (intr_event.IsRunning() == false) { + scheduler->StartEvent(intr_event); + } +} + +// 外部割り込みイベントコールバック。 +// ev.code 0-7 は新しい割り込みレベル。 +// +// 割り込みは本来命令境界ごとにチェックするが、実際に割り込みが起きうるのは +// 1) 割り込み信号線が変化した時 +// 2) 割り込みマスクを変更した時 +// しかないはずなので、この時だけ命令境界でチェックする。 +// ここはその 1) のほう。その2のほうは core.cpp:SetSR() 参照。 +// +// 外部割り込みが来たので、レベル比較などをして、必要なら次の命令境界で +// 割り込みチェックを行うようにするところ。 +void +MPU680x0Device::InterruptCallback(Event& ev) +{ + uint level = ev.code; + uint mask = reg.intr_mask; + + // 信号線が 2 クロック期間安定していること云々は省略 + + // 割り込みを起こすかどうかに関わらずここで更新。 + intr_level = level; + + // ホールトならここまで。 + if (cpu_state == CPU_STATE_HALT) { + return; + } + + // 割り込み例外を起動するのは + // o レベルがマスクより高い + // o マスクに関係なく、レベルが下位から 7 に変化した場合 + // (ここは変化した時だけ呼ばれるのと 7 より上はないので + // level==7 なら必ず「下位から 7 に変化」のはず) + if (level > mask || level == 7) { + // 通知した結果割り込みが受け付けられたので、 + // この命令終了時のコールバックを割り込み処理起動に差し替える。 + MakeNextFull(); + intr_pending = true; + + // STOP 状態だったらここでイベントを開始する + if (cpu_state == CPU_STATE_STOP) { + // STOP から抜ける時間? + exec_event.time = 1 * clock_nsec; + scheduler->StartEvent(exec_event); + } + } +} + +// 次の命令境界は Full にする。 +void +MPU680x0Device::MakeNextFull() +{ + exec_event.func = exec_full; +} + +void +MPU680x0Device::ChangeState(uint32 new_state) +{ + // new_mode (CPU_STATE_*) と + // RequestCPUMode() の引数 Syncer::SYNCMODE_CPU_* は + // 実は同じ値なのでそのまま渡してよいことにする。 + static_assert(CPU_STATE_NORMAL == Syncer::SYNCMODE_CPU_NORMAL, ""); + static_assert(CPU_STATE_STOP == Syncer::SYNCMODE_CPU_STOP, ""); + static_assert(CPU_STATE_HALT == Syncer::SYNCMODE_CPU_HALT, ""); + + if (cpu_state != new_state) { + cpu_state = new_state; + // スケジューラに通知 + syncer->RequestCPUMode(cpu_state); } - event.code = level; - event.Start(); } // A-Line 命令エミュレーションのコールバックを指定。 void -MPU680x0Device::SetALineCallback(bool (*callback)(m68kcpu *, void*), void *arg) +MPU680x0Device::SetALineCallback(bool (*callback)(MPU680x0Device *, void*), + void *arg) { - cpu->aline_callback = callback; - cpu->aline_arg = arg; + aline_callback = callback; + aline_arg = arg; } // F-Line 命令エミュレーションのコールバックを指定。 void -MPU680x0Device::SetFLineCallback(bool (*callback)(m68kcpu *, void*), void *arg) +MPU680x0Device::SetFLineCallback(bool (*callback)(MPU680x0Device *, void*), + void *arg) { - cpu->fline_callback = callback; - cpu->fline_arg = arg; + fline_callback = callback; + fline_arg = arg; } -// ダブルバスフォールトのコールバックを指定。 +// RESET 命令 void -MPU680x0Device::SetHaltCallback(void (*callback)(void *), void *arg) +MPU680x0Device::ops_reset() { - cpu->halt_callback = callback; - cpu->halt_arg = arg; + auto mainbus = GetMainbusDevice(); + mainbus->ResetByMPU(); } -// モニター更新 (レジスタウィンドウ) +// ホールト状態 void -MPU680x0Device::MonitorUpdate(TextScreen& monitor) +MPU680x0Device::Halt() { - m68kreg reg; - uint32 ppc; - int x; - int y; + ChangeState(CPU_STATE_HALT); - monitor.Clear(); + // この命令完了後に停止するため exec を差し替える。 + MakeNextFull(); - // ローカルにコピー - // ただし PPC だけ reg 構造体ではなく cpu クラス側にある - reg = cpu->reg; - ppc = cpu->ppc; + // UI に通知 (メッセージボックス表示とか) + UIMessage::Post(UIMessage::HALT); +} + +// モニター更新の下請け (レジスタ共通部分) +void +MPU680x0Device::MonitorUpdateRegCommon(TextScreen& screen, + m68kreg& tmp, uint32 tmp_ppc, uint32 tmp_state) +{ + int x; // データレジスタ、アドレスレジスタ for (int i = 0; i < 4; i++) { - monitor.Print(0, i, "D%d:%08x D%d:%08x A%d:%08x A%d:%08x", - i + 0, reg.da[i], - i + 4, reg.da[i + 4], - i + 0, reg.da[i + 8], - i + 4, reg.da[i + 12]); + screen.Print(0, i, "D%u:%08x D%u:%08x A%u:%08x A%u:%08x", + i + 0, tmp.R[i], + i + 4, tmp.R[i + 4], + i + 0, tmp.R[i + 8], + i + 4, tmp.R[i + 12]); } // 5列目 x = 50; // SR - monitor.Print(x, 0, "SR:%04x(%c%c%c%c%c)", - (reg.sr_h() | reg.ccr.Get()), - (reg.ccr.IsX()) ? 'X' : '-', - (reg.ccr.IsN()) ? 'N' : '-', - (reg.ccr.IsZ()) ? 'Z' : '-', - (reg.ccr.IsV()) ? 'V' : '-', - (reg.ccr.IsC()) ? 'C' : '-'); - - // VBR - monitor.Print(x, 1, "VBR:%08x", reg.vbr); + screen.Print(x, 0, "SR:%04x(%c%c%c%c%c)", + tmp.GetSR(), + (tmp.ccr.IsX()) ? 'X' : '-', + (tmp.ccr.IsN()) ? 'N' : '-', + (tmp.ccr.IsZ()) ? 'Z' : '-', + (tmp.ccr.IsV()) ? 'V' : '-', + (tmp.ccr.IsC()) ? 'C' : '-'); // *SP - uint ms = (reg.s ? 2 : 0) | (reg.m ? 1 : 0); // T1 T0 S M + uint ms = (tmp.s ? 2 : 0) | (tmp.m ? 1 : 0); // T1 T0 S M if (ms == 3) { // Supervisor (Master) mode (A7=MSP, USP/ISP) - monitor.Print(x, 2, TA::Disable, "USP:%08x", reg.usp); - monitor.Print(x, 3, TA::Disable, "ISP:%08x", reg.isp); + screen.Print(x, 1, TA::Disable, "USP:%08x", tmp.usp); + screen.Print(x, 2, TA::Disable, "ISP:%08x", tmp.isp); } else if (ms == 2) { // Supervisor (Interrupt) mode (A7=ISP, USP/MSP) - monitor.Print(x, 2, TA::Disable, "USP:%08x", reg.usp); - monitor.Print(x, 3, TA::Disable, "MSP:%08x", reg.msp); + screen.Print(x, 1, TA::Disable, "USP:%08x", tmp.usp); + screen.Print(x, 2, TA::Disable, "MSP:%08x", tmp.msp); } else { // User mode (A7=USP, ISP/MSP) - monitor.Print(x, 2, TA::Disable, "ISP:%08x", reg.isp); - monitor.Print(x, 3, TA::Disable, "MSP:%08x", reg.msp); + screen.Print(x, 1, TA::Disable, "ISP:%08x", tmp.isp); + screen.Print(x, 2, TA::Disable, "MSP:%08x", tmp.msp); } // 6列目 - // PC: 01234567 - // SFC:1 DFC:1 - // CACR:01234567 - // CAAR:01234567 + // PC: 01234567 + // SFC:1 DFC:1 + // VBR:01234567 x = 66; - monitor.Print(x, 0, "PC: %08x", ppc); - monitor.Print(x, 1, "SFC:%d DFC:%d", reg.sfc, reg.dfc); - monitor.Print(x, 2, "CACR:%08x", reg.cacr); - monitor.Print(x, 3, "CAAR:%08x", reg.caar); - - // XXX どこに置くのがいいか - if (reg.state == 0) { - monitor.Puts(25, 4, "State: Running"); - } else if (reg.state == CPU_REQ_STOP) { - monitor.Puts(25, 4, "State: STOP instruction"); - } else if (reg.state == CPU_REQ_HALT) { - monitor.Puts(25, 4, TA::On, "State: Double Bus Fault"); + screen.Print(x, 0, "PC: %08x", tmp_ppc); + screen.Print(x, 1, "SFC:%u DFC:%u", tmp.GetSFC(), tmp.GetDFC()); + screen.Print(x, 2, "VBR:%08x", tmp.vbr); + + switch (tmp_state) { + case CPU_STATE_NORMAL: + screen.Puts(50, 3, "State: Running"); + break; + case CPU_STATE_STOP: + screen.Puts(50, 3, "State: STOP instruction"); + break; + case CPU_STATE_HALT: + screen.Puts(50, 3, TA::On, "State: Double Bus Fault"); + break; + default: + screen.Print(50, 3, TA::On, "corrupted cpu_state=%u", tmp_state); + break; } +} - // MMU - x = 0; - y = 4; - monitor.Puts(x, y++, ""); - monitor.Print(x, y, "SRP:%08x_%08x", reg.srp.h, reg.srp.l); - monitor.Print(x, y + 1, "CRP:%08x_%08x", reg.crp.h, reg.crp.l); - x += 23; - for (int i = 0; i < 2; i++) { - uint32 tt = reg.tt[i]; - monitor.Print(x, y + i, "TT%d:%08x(%c%c%c)", - i, tt, - (tt & m68030TT::E) ? 'E' : '-', - (tt & m68030TT::CI) ? 'C' : '-', - (tt & m68030TT::RWM) ? '-' : ((tt & m68030TT::RW) ? 'R' : 'W')); - } - x += 19; - monitor.Print(x, y, "TC:%08x(%c%c%c)", - reg.tc, - (reg.tc & m68030TC::TC_E) ? 'E' : '-', - (reg.tc & m68030TC::TC_SRE) ? 'S' : '-', - (reg.tc & m68030TC::TC_FCL) ? 'F' : '-'); - monitor.Print(x, y + 1, "MMUSR: %04x(%c%c%c%c%c%c%c N=%d)", - reg.mmusr, - (reg.mmusr & m68030MMUSR::B) ? 'B' : '-', - (reg.mmusr & m68030MMUSR::L) ? 'L' : '-', - (reg.mmusr & m68030MMUSR::S) ? 'S' : '-', - (reg.mmusr & m68030MMUSR::W) ? 'W' : '-', - (reg.mmusr & m68030MMUSR::I) ? 'I' : '-', - (reg.mmusr & m68030MMUSR::M) ? 'M' : '-', - (reg.mmusr & m68030MMUSR::T) ? 'T' : '-', - (reg.mmusr & m68030MMUSR::N)); +// モニタ更新の下請け (FPU) +int +MPU680x0Device::MonitorUpdateFPU(TextScreen& screen, int y, m68kreg& tmp) +{ + int x; + +/* +0123456789012345678901234567890123456789012345678901234567890123456789012345 +FP0:7fff_ffffffff_ffffffff = NAN +FPCR: 0000 BS SN OP OV UF DZ I2 I1 Prec=.X Mode=Minus +FPSR: 00000000 N Z INF NAN Q=$xx BS SN OP OV UF DZ I2 I1 AV AO AU AD AI +FPIAR:00000000 +*/ - // FPU x = 0; - y = 7; - monitor.Puts(x, y++, ""); + screen.Puts(x, y++, ""); for (int i = 0; i < 8; i++) { - monitor.Print(x, y + i, "FP%d:%04x_%08x_%08x (%-20s)", + screen.Print(x, y++, "FP%u:%04x_%08x_%08x = %s", i, - reg.fpframe.fpf_regs[i * 3 + 0] >> 16, - reg.fpframe.fpf_regs[i * 3 + 1], - reg.fpframe.fpf_regs[i * 3 + 2], - "not yet"); + tmp.fpframe.fpf_regs[i * 3 + 0] >> 16, + tmp.fpframe.fpf_regs[i * 3 + 1], + tmp.fpframe.fpf_regs[i * 3 + 2], + ExtToStr(&tmp.fpframe.fpf_regs[i * 3]).c_str()); } - x = 51; - // FPCR - static const char * const rpstr[] = { - ".EXT", - ".SGL", - ".DBL", - ".???", - }; - static const char * const rmstr[] = { - "Near", - "Zero", - "Minus", - "Plus", - }; - uint32 fpcr = reg.fpframe.fpf_fpcr; - monitor.Print(x, y++, "FPCR:%04x", fpcr); - monitor.Print(x + 2, y++, "%s %s %s %s %s %s %s %s", - (fpcr & 0x8000) ? "BS" : "--", - (fpcr & 0x4000) ? "SN" : "--", - (fpcr & 0x2000) ? "OP" : "--", - (fpcr & 0x1000) ? "OV" : "--", - (fpcr & 0x0800) ? "UF" : "--", - (fpcr & 0x0400) ? "DZ" : "--", - (fpcr & 0x0200) ? "I2" : "--", - (fpcr & 0x0100) ? "I1" : "--"); - monitor.Print(x + 2, y++, "RP=%s RM=%s", - rpstr[(fpcr >> 6) & 3], + uint32 fpcr = tmp.fpframe.fpf_fpcr; + screen.Puts(0, y, "FPCR:"); + screen.Print(10, y, "%04x", fpcr); + x = 34; + screen.Puts(x + 0, y, TA::OnOff(fpcr & 0x8000), "BS"); + screen.Puts(x + 3, y, TA::OnOff(fpcr & 0x4000), "SN"); + screen.Puts(x + 6, y, TA::OnOff(fpcr & 0x2000), "OE"); + screen.Puts(x + 9, y, TA::OnOff(fpcr & 0x1000), "OF"); + screen.Puts(x + 12, y, TA::OnOff(fpcr & 0x0800), "UF"); + screen.Puts(x + 15, y, TA::OnOff(fpcr & 0x0400), "DZ"); + screen.Puts(x + 18, y, TA::OnOff(fpcr & 0x0200), "X2"); + screen.Puts(x + 21, y, TA::OnOff(fpcr & 0x0100), "X1"); + screen.Print(x + 25, y, "RP=.%c RM=%s", + "XSD?"[(fpcr >> 6) & 3], rmstr[(fpcr >> 4) & 3]); + y++; // FPSR - uint32 fpsr = reg.fpframe.fpf_fpsr; + uint32 fpsr = tmp.fpframe.fpf_fpsr; uint32 cc = fpsr >> 24; - monitor.Print(x, y++, "FPSR:%08x", fpsr); - monitor.Print(x + 2, y++, "%c %c %s %s Q=$%02x", - (cc & 0x08) ? 'N' : '-', - (cc & 0x04) ? 'Z' : '-', - (cc & 0x02) ? "Inf" : "---", - (cc & 0x01) ? "NAN" : "---", - (fpsr >> 16) & 0xff); - monitor.Print(x + 2, y++, "%s %s %s %s %s %s %s %s", - (fpsr & 0x8000) ? "BS" : "--", - (fpsr & 0x4000) ? "SN" : "--", - (fpsr & 0x2000) ? "OP" : "--", - (fpsr & 0x1000) ? "OV" : "--", - (fpsr & 0x0800) ? "UF" : "--", - (fpsr & 0x0400) ? "DZ" : "--", - (fpsr & 0x0200) ? "I2" : "--", - (fpsr & 0x0100) ? "I1" : "--"); - monitor.Print(x + 2, y++, "%s %s %s %s %s", - (fpsr & 0x80) ? "IOP" : "---", - (fpsr & 0x40) ? "OVFL" : "----", - (fpsr & 0x20) ? "UNFL" : "----", - (fpsr & 0x10) ? "DZ" : "--", - (fpsr & 0x08) ? "INEX" : "----"); + screen.Print(0, y, "FPSR: %08x", fpsr); + x = 15; + screen.Puts(x + 0, y, TA::OnOff(cc & 0x08), "N"); + screen.Puts(x + 2, y, TA::OnOff(cc & 0x04), "Z"); + screen.Puts(x + 4, y, TA::OnOff(cc & 0x02), "Inf"); + screen.Puts(x + 8, y, TA::OnOff(cc & 0x01), "NAN"); + screen.Print(x + 12, y, "Q=$%02x", (fpsr >> 16) & 0xff); + x = 34; + screen.Puts(x + 0, y, TA::OnOff(fpsr & 0x8000), "BS"); + screen.Puts(x + 3, y, TA::OnOff(fpsr & 0x4000), "SN"); + screen.Puts(x + 6, y, TA::OnOff(fpsr & 0x2000), "OE"); + screen.Puts(x + 9, y, TA::OnOff(fpsr & 0x1000), "OF"); + screen.Puts(x + 12, y, TA::OnOff(fpsr & 0x0800), "UF"); + screen.Puts(x + 15, y, TA::OnOff(fpsr & 0x0400), "DZ"); + screen.Puts(x + 18, y, TA::OnOff(fpsr & 0x0200), "X2"); + screen.Puts(x + 21, y, TA::OnOff(fpsr & 0x0100), "X1"); + screen.Puts(x + 25, y, TA::OnOff(fpsr & 0x80), "AV"); + screen.Puts(x + 28, y, TA::OnOff(fpsr & 0x40), "AO"); + screen.Puts(x + 31, y, TA::OnOff(fpsr & 0x20), "AU"); + screen.Puts(x + 34, y, TA::OnOff(fpsr & 0x10), "AZ"); + screen.Puts(x + 37, y, TA::OnOff(fpsr & 0x08), "AX"); + y++; // FPIAR - monitor.Print(x, y++, "FPIAR:%08x", reg.fpframe.fpf_fpiar); + screen.Print(0, y++, "FPIAR:%08x", tmp.fpframe.fpf_fpiar); + + return y; +} + + +// +// 68030 +// + +// コンストラクタ +MPU68030Device::MPU68030Device() + : inherited() +{ + mpu_type = m680x0MPUType::M68030; + mpu_name = "MC68030"; + SetName("MPU(68030)"); + + // MMU.xRP.DT == 0 になってはいけない(MMU 構成例外)。 + SetSRP(0x00000001, 0); + SetCRP(0x00000001, 0); + + // キャッシュ + icache.reset(new m68030Cache()); + dcache.reset(new m68030Cache()); + + // レジスタモニター (サイズは Init で確定する) + reg_monitor = gMonitorManager->Regist(ID_MONITOR_MPUREG, this); + reg_monitor->func = ToMonitorCallback(&MPU68030Device::MonitorUpdateReg); + + // ATC モニター + atc_monitor = gMonitorManager->Regist(ID_MONITOR_MPUATC, this); + atc_monitor->func = ToMonitorCallback(&MPU68030Device::MonitorUpdateATC); + atc_monitor->SetSize(114, m68030ATCTable::LINES + 6); + + // キャッシュモニター + cache_monitor = gMonitorManager->Regist(ID_MONITOR_MPUCACHE, this); + cache_monitor->func = + ToMonitorCallback(&MPU68030Device::MonitorUpdateCache); + cache_monitor->SetSize(48, 20 * 2 + 1); +} + +// デストラクタ +MPU68030Device::~MPU68030Device() +{ } -// CPU 側からのコールバック。RESET 命令ハンドラ void -m68030_reset_inst(m68kcpu *cpu) +MPU68030Device::SetLogLevel(int loglevel_) { - gVM->ResetByMPU(); + inherited::SetLogLevel(loglevel_); + + atc.SetLogLevel(loglevel_); } -// -// ATC モニタ -// -MPUATC::MPUATC(m68kcpu *cpu_) +static int +sort_atc(const void *a, const void *b) +{ + const uint32 *ua = (const uint32 *)a; + const uint32 *ub = (const uint32 *)b; + return *ub - *ua; +} + +// モニター更新 (レジスタ) +void +MPU68030Device::MonitorUpdateReg(Monitor *, TextScreen& screen) { - cpu = cpu_; -#if !defined(ATC_SINGLE) - monitor_size = nnSize(131, m68030ATCTable::LINES + 3); -#else - monitor_size = nnSize(31, 24); -#endif + m68kreg tmp; + uint32 tmp_ppc; + uint32 tmp_state; + int y; + + screen.Clear(); + + // ローカルにコピーする。 + // ただし一部は reg ではなく cpu クラス本体にある。 + tmp = reg; + tmp_ppc = ppc; + tmp_state = cpu_state; + + // 共通部分。 + MonitorUpdateRegCommon(screen, tmp, tmp_ppc, tmp_state); + + // Cache/MMU 部分。 + y = 4; + y = MonitorUpdateMMU(screen, y, tmp); + + // FPU はある時だけ表示。 + if (HasFPU()) { + MonitorUpdateFPU(screen, y, tmp); + } +} + +// モニター更新の下請け (Cache/MMU 部分) +int +MPU68030Device::MonitorUpdateMMU(TextScreen& screen, int y, m68kreg& tmp) +{ + int x; + + // Cache + screen.Puts(0, y++, ""); + screen.Print(0, y, "CACR:%08x(", tmp.cacr); + screen.Puts(14, y, TA::OnOff(tmp.cacr & M68030::CACR_WA), "WA"); + screen.Puts(17, y, TA::OnOff(tmp.cacr & M68030::CACR_DBE), "DBE"); + screen.Puts(21, y, "- -"); + screen.Puts(25, y, TA::OnOff(tmp.cacr & M68030::CACR_FD), "FD"); + screen.Puts(28, y, TA::OnOff(tmp.cacr & M68030::CACR_ED), "ED"); + screen.Puts(31, y, "000"); + screen.Puts(35, y, TA::OnOff(tmp.cacr & M68030::CACR_IBE), "IBE"); + screen.Puts(39, y, "- -"); + screen.Puts(43, y, TA::OnOff(tmp.cacr & M68030::CACR_FI), "FI"); + screen.Puts(46, y, TA::OnOff(tmp.cacr & M68030::CACR_EI), "EI"); + screen.Putc(48, y, ')'); + screen.Print(50, y, "CAAR:%08x", tmp.caar); + y++; + + // MMU + x = 0; + screen.Puts(x, y++, ""); + screen.Print(x, y, "SRP:%08x_%08x", tmp.srp.h, tmp.srp.l); + screen.Print(x, y + 1, "CRP:%08x_%08x", tmp.crp.h, tmp.crp.l); + x += 25; + for (int i = 0; i < 2; i++) { + uint32 tt = tmp.tt[i]; + screen.Print(x, y + i, + "TT%u:%08x(%c%c%c Addr=$%02x Mask=$%02x FC=%u FCMask=%u)", + i, tt, + (tt & m68030TT::E) ? 'E' : '-', + (tt & m68030TT::CI) ? 'C' : '-', + (tt & m68030TT::RWM) ? '-' : ((tt & m68030TT::RW) ? 'R' : 'W'), + (tt >> 24), + (tt >> 16) & 0xff, + (tt >> 4) & 0x07, + (tt ) & 0x07); + } + y += 2; + screen.Print(0, y, + "TC:%08x(%c%c%c IS=$%x TIA-D=%x:%x:%x:%x PS=$%x)", + tmp.tc, + (tmp.tc & m68030TC::TC_E) ? 'E' : '-', + (tmp.tc & m68030TC::TC_SRE) ? 'S' : '-', + (tmp.tc & m68030TC::TC_FCL) ? 'F' : '-', + (tmp.tc >> 16) & 0xf, + (tmp.tc >> 12) & 0xf, + (tmp.tc >> 8) & 0xf, + (tmp.tc >> 4) & 0xf, + (tmp.tc ) & 0xf, + (tmp.tc >> 20) & 0xf); + x = 49; + screen.Print(x, y, "MMUSR:%04x(", tmp.mmusr); + x += 11; + screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::B) | 'B'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::L) | 'L'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::S) | 'S'); + screen.Putc(x++, y, '-'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::W) | 'W'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::I) | 'I'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::M) | 'M'); + screen.Puts(x, y, "- -"); + x += 3; + screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::T) | 'T'); + screen.Print(x, y, "---N=%u)", tmp.mmusr & m68030MMUSR::N); + y++; + + return y; } +// モニター更新 (68030 ATC) void -MPUATC::MonitorUpdate(TextScreen& monitor) +MPU68030Device::MonitorUpdateATC(Monitor *, TextScreen& screen) { m68030ATCTable *table; -#if !defined(ATC_SINGLE) + + screen.Clear(); + struct { int fc; const char *name; @@ -368,98 +762,353 @@ MPUATC::MonitorUpdate(TextScreen& monito { 6, "Super/Program" }, }; - monitor.Clear(); - - // 行数は左端にだけ - for (int i = 0; i < m68030ATCTable::LINES; i++) { - monitor.Print(0, i + 2, "%02d:", i); - } - - for (int t = 0; t < countof(fclist); t++) { - int x = t * 32 + 4; + for (uint t = 0; t < countof(fclist); t++) { + int x = t * 29; int fc = fclist[t].fc; const char *name = fclist[t].name; - double r; - int i; - table = &cpu->atc.tables[fc]; - monitor.Print(x, 0, "FC=%d %s", fc, name); - monitor.Puts(x, 1, "LAddr PAddr Flag Hit% Age"); - // 先に統計用の母数を計算 - uint64 total = 0; - for (i = 0; i < countof(table->hit); i++) { - total += table->hit[i]; + table = atc.fctables[fc]; + screen.Print(x, 0, "FC=%u %s", fc, name); + screen.Puts(x, 1, "No. LAddr PAddr Flag"); + + std::array sbuf; + for (uint i = 0; i < m68030ATCTable::LINES; i++) { + sbuf[i * 2 + 0] = table->line[i].age; + sbuf[i * 2 + 1] = i; } - total += table->hit_head; + qsort(&sbuf[0], m68030ATCTable::LINES, sizeof(uint32) * 2, sort_atc); // エントリ表示 - for (i = 0; i < countof(table->line); i++) { - m68030ATCLine& a = table->line[i]; + for (uint i = 0; i < m68030ATCTable::LINES; i++) { + int idx = sbuf[i * 2 + 1]; + m68030ATCLine& a = table->line[idx]; TA attr; if (a.IsInvalid()) { attr = TA::Disable; - } else if (table->head == &a) { - attr = TA::Em; } else { attr = TA::Normal; } - monitor.Print(x, i + 2, attr, "%08x %08x %c%c%c", + screen.Print(x, i + 2, attr, "[%02x] %08x %08x %c%c%c%c", + idx, a.GetLAddr(), a.GetPAddr(), a.IsBusError() ? 'B' : '-', + a.IsCInhibit() ? 'C' : '-', a.IsWProtect() ? 'P' : '-', a.IsModified() ? 'M' : '-'); } - // ヘッドのヒット率 - monitor.Puts(x + 0, i + 2, "head"); - r = (double)table->hit_head * 100 / total; - if (!isnan(r)) { - monitor.Print(x + 5, i + 2, "%4.1f%%", r); - } - // 配列のヒット率とミス率 - monitor.Puts(x + 17, i + 2, "miss"); - for (i = 0; i < countof(table->hit); i++) { - r = (double)(table->hit[i] * 100) / total; - if (!isnan(r)) { - monitor.Print(x + 22, i + 2, "%4.1f%%", r); + + // ヒット率とミス率。 + uint64 total = 0; + uint64 tthit = 0; + uint64 miss = 0; + bool tt_once_hit; + + { + std::unique_lock lock(table->atchist_mtx); + for (int i = 0; i < table->atchist.Length(); i++) { + const auto& s = table->atchist.Peek(i); + total += s.total; + tthit += s.tthit; + miss += s.miss; } + tt_once_hit = table->atchist_tt_once_hit; + } + + int y = m68030ATCTable::LINES + 2; + screen.Puts(x + 1, y, "TT Match"); + if (tt_once_hit) { + screen.Print(x + 10, y, "%5.1f%%", (double)(tthit * 100) / total); + } else { + screen.Puts(x + 12, y, "0"); + } + y++; + + screen.Puts(x + 1, y, "ATC Match"); + if (total == 0) { + screen.Puts(x + 12, y, "0"); + } else { + screen.Print(x + 10, y, "%5.1f%%", + (double)((total - miss - tthit) * 100) / total); + } + y++; + + screen.Puts(x + 1, y, "ATC Miss"); + if (miss == 0) { + screen.Puts(x + 12, y, "0"); + } else { + screen.Print(x + 10, y, "%5.1f%%", (double)(miss * 100) / total); } - // カウンタ - // 分かりやすさのため最大値からの差(距離)を表示 - for (i = 0; i < countof(table->line); i++) { - m68030ATCLine& a = table->line[i]; - if (!a.IsInvalid()) { - monitor.Print(x + 27, i + 2, "%4d", table->head->age - a.age); + y++; + } + + screen.Print(0, m68030ATCTable::LINES + 5, + "Note: The real 68030 ATC is single FC-mixed 22-entries. " + "This ATC with FC-separated %u-entries is by nono.", + m68030ATCTable::LINES); +} + +// 統計情報を更新 (Syncer から呼ばれる) +void +MPU68030Device::CalcStat() +{ + // ATC のヒット率。 + for (auto& table : atc.tables) { + // ここはロックなしでやってしまう + m68030ATCStat s; + s.total = table->atcstat.total; + s.tthit = table->atcstat.tthit; + s.miss = table->atcstat.miss; + table->atcstat.total = 0; + table->atcstat.tthit = 0; + table->atcstat.miss = 0; + + // こっちはロックする + { + std::unique_lock lock(table->atchist_mtx); + + table->atchist.EnqueueForce(s); + if (__predict_false(s.tthit != 0)) { + table->atchist_tt_once_hit = true; } } } -#else - m68030ATCLine *a; - int i; - - table = &cpu->atc.tables[0]; - monitor.Clear(); - - monitor.Puts(0, 0, "No. FC LAddr PAddr Flag"); - for (a = table->head, i = 0; a; a = a->next, i++) { - monitor.Print(0, i + 1, "%02d:", i); - if (!a->IsInvalid()) { - char dp; - switch (a->fc & 3) { - case 1: dp = 'D'; break; - case 2: dp = 'P'; break; - default: dp = '?'; break; + + // キャッシュのヒット率。 + icache->CalcStat(); + dcache->CalcStat(); +} + +static char +icache_fc2str(const m68030CacheLine& l) +{ + return l.IsSuper() ? 'S' : 'U'; +} + +static char +dcache_fc2str(const m68030CacheLine& l) +{ + return "0U234S67"[l.GetFC()]; +} + +// モニタ更新 (68030 キャッシュ) +void +MPU68030Device::MonitorUpdateCache(Monitor *, TextScreen& screen) +{ + int y; + + screen.Clear(); + + // + // 01234567890123456789012345678901234567890123456789 + // Tag Address Data + // S.012345'00: 00000000 00000000 -------- 00000000 + + y = 0; + screen.Puts(0, y++, ""); + y = MonitorUpdateCache1(screen, y, icache.get(), icache_fc2str); + + y++; + screen.Puts(0, y++, ""); + MonitorUpdateCache1(screen, y, dcache.get(), dcache_fc2str); +} + +// モニタ更新 (68030 キャッシュ片方の下請け) +int +MPU68030Device::MonitorUpdateCache1(TextScreen& screen, int y, + m68030Cache *cache, char (*fc2str)(const m68030CacheLine&)) +{ + screen.Puts(0, y, "Tag Address Data[0]"); + screen.Puts(13 + 1 * 9, y, "[1]"); + screen.Puts(13 + 2 * 9, y, "[2]"); + screen.Puts(13 + 3 * 9, y, "[3]"); + y++; + + TA attr = cache->enable ? TA::Normal : TA::Disable; + for (int idx = 0; idx < cache->line.size(); idx++) { + auto& l = cache->line[idx]; + screen.Print(0, y, attr, "%c.%06x'%x0:", + fc2str(l), (l.TagAddr() >> 8), idx); + + for (int i = 0; i < 4; i++) { + if (l.valid[i]) { + screen.Print(13 + 9 * i, y, attr, "%08x", l.data[i]); + } else { + screen.Puts(13 + 9 * i, y, TA::Disable, "--------"); } - monitor.Print(4, i + 1, "%d(%c%c):%08x %08x %c%c%c", - a->fc, - (a->fc & 4) ? 'S' : 'U', - dp, - a->GetLAddr(), a->GetPAddr(), - a->IsBusError() ? 'B' : '-', - a->IsWProtect() ? 'W' : '-', - a->IsModified() ? 'M' : '-'); } + y++; } -#endif + + // 統計。 + float hit = 0.0; + { + std::unique_lock lock(cache->hist_mtx); + int len = cache->hist.Length(); + if (len != 0) { + for (int i = 0; i < cache->hist.Length(); i++) { + const auto& s = cache->hist.Peek(i); + hit += s.hit; + } + hit /= len; + } + } + + y++; + screen.Print(1, y++, attr, "Cache Read Hit %5.1f%%", hit); + + return y; +} + + +// +// 68040 +// + +// コンストラクタ +MPU68040Device::MPU68040Device() + : inherited() +{ + mpu_type = m680x0MPUType::M68040; + mpu_name = "MC68040"; + SetName("MPU(68040)"); + + mmu_itt[0].reset(new m68040TT("ITT0", ®.itt[0])); + mmu_itt[1].reset(new m68040TT("ITT1", ®.itt[1])); + mmu_dtt[0].reset(new m68040TT("DTT0", ®.dtt[0])); + mmu_dtt[1].reset(new m68040TT("DTT1", ®.dtt[1])); + mmu_tc.reset(new m68040TC()); + atc_inst.reset(new m68040ATC()); + atc_data.reset(new m68040ATC()); + + // レジスタモニター (サイズは Init で確定する) + reg_monitor = gMonitorManager->Regist(ID_MONITOR_MPUREG, this); + reg_monitor->func = ToMonitorCallback(&MPU68040Device::MonitorUpdateReg); + + // ATC モニター (Inst/Data) + atc_monitor = gMonitorManager->Regist(ID_MONITOR_MPUATC, this); + atc_monitor->func = ToMonitorCallback(&MPU68040Device::MonitorUpdateATC); + atc_monitor->SetSize(121, 34); +} + +// デストラクタ +MPU68040Device::~MPU68040Device() +{ +} + +// モニター更新 (レジスタ) +void +MPU68040Device::MonitorUpdateReg(Monitor *, TextScreen& screen) +{ + m68kreg tmp; + uint32 tmp_ppc; + uint32 tmp_state; + FPU40 tmp_fpu40; + int y; + + screen.Clear(); + + // ローカルにコピーする。 + // ただし一部は reg ではなく cpu クラス本体にある。 + tmp = reg; + tmp_ppc = ppc; + tmp_state = cpu_state; + tmp_fpu40 = fpu40; + + // 共通部分。 + MonitorUpdateRegCommon(screen, tmp, tmp_ppc, tmp_state); + + // Cache/MMU 部分。 + y = 4; + y = MonitorUpdateMMU(screen, y, tmp); + + // FPU はある時だけ表示。 + if (HasFPU()) { + y = MonitorUpdateFPU(screen, y, tmp); + MonitorUpdateFPU40(screen, y, tmp_fpu40); + } +} + +// モニター更新の下請け (Cache/MMU 部分) +int +MPU68040Device::MonitorUpdateMMU(TextScreen& screen, int y, m68kreg& tmp) +{ + int x; + + screen.Puts(0, y++, ""); + screen.Print(0, y, "SRP:%08x TC:%04x( )", tmp.srp40, tmp.tc40); + screen.Putc(24, y, TA::OnOff(tmp.tc40 & m68040TC::E) | 'E'); + screen.Putc(26, y, TA::OnOff(tmp.tc40 & m68040TC::P) | 'P'); + y++; + screen.Print(0, y++, "URP:%08x", tmp.urp40); + screen.Print(0, y, "MMUSR:%05x'%03x", + (tmp.mmusr40 >> 12), + (tmp.mmusr40 & 0xfff)); + x = 16; + screen.Putc(x++, y, TA::OnOff(tmp.mmusr40 & m68040MMUSR::B) | 'B'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr40 & m68040MMUSR::G) | 'G'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr40 & m68040MMUSR::U1)| 'U'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr40 & m68040MMUSR::U0)| 'U'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr40 & m68040MMUSR::S) | 'S'); + x++; + screen.Print(x, y, "CM%u", (tmp.mmusr40 & m68040MMUSR::CM) >> 5); + x += 4; + screen.Putc(x++, y, TA::OnOff(tmp.mmusr40 & m68040MMUSR::M) | 'M'); + screen.Putc(x++, y, '-'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr40 & m68040MMUSR::W) | 'W'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr40 & m68040MMUSR::T) | 'T'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr40 & m68040MMUSR::R) | 'R'); + y++; + + screen.Print(0, y, "CACR:%08x(", tmp.cacr); + screen.Puts(14, y, TA::OnOff(tmp.cacr & M68040::CACR_DE), "DE"); + screen.Puts(17, y, TA::OnOff(tmp.cacr & M68040::CACR_IE), "IE"); + screen.Putc(19, y, ')'); + y++; + + y -= 4; + x = 33; + MonitorUpdateTT(screen, x, y++, "ITT0", tmp.itt[0]); + MonitorUpdateTT(screen, x, y++, "ITT1", tmp.itt[1]); + MonitorUpdateTT(screen, x, y++, "DTT0", tmp.dtt[0]); + MonitorUpdateTT(screen, x, y++, "DTT1", tmp.dtt[1]); + + return y; +} + +// *TTn 1つ分の下請け。 +void +MPU68040Device::MonitorUpdateTT(TextScreen& screen, int x, int y, + const char *name, uint32 tt) +{ + bool e = (tt & m68040TT::E); + + screen.Print(x, y, "%s:%08x(", name, tt); + x += 14; + screen.Putc(x++, y, TA::OnOff(e) | 'E'); + x++; + screen.Print(x, y, (e ? TA::Normal : TA::Disable), + "Base=$%02x Mask=$%02x", (tt >> 24), (tt >> 16) & 0xff); + + x += 18; + if (e) { + screen.Putc(x++, y, TA::OnOff(tt & m68040TT::S_IGN) | 'S'); + screen.Putc(x++, y, TA::OnOff(tt & m68040TT::S_FC2) | 'S'); + screen.Putc(x++, y, TA::OnOff(tt & m68040TT::U1) | 'U'); + screen.Putc(x++, y, TA::OnOff(tt & m68040TT::U0) | 'U'); + x++; + screen.Print(x, y, "CM%u", (tt & m68040TT::CM) >> 5); + x += 4; + screen.Putc(x++, y, TA::OnOff(tt & m68040TT::W) | 'W'); + } else { + screen.Puts(x, y, TA::Disable, "---- --- -"); + x += 10; + } + screen.Putc(x, y, ')'); +} + +void +MPU68040Device::CalcStat() +{ }