|
|
1.1 root 1: //
2: // nono
1.1.1.2 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.13 root 7: //
8: // MPU (MC68030)
9: //
1.1 root 10:
11: #include "mpu680x0.h"
12: #include "config.h"
1.1.1.13 root 13: #include "debugger.h"
1.1.1.15! root 14: #include "interrupt.h"
1.1 root 15: #include "mainapp.h"
1.1.1.13 root 16: #include "scheduler.h"
1.1.1.15! root 17: #include "syncer.h"
1.1.1.10 root 18: #include "uimessage.h"
1.1.1.15! root 19: #include "vectortable.h"
1.1 root 20: #include "vm.h"
1.1.1.13 root 21: #include <cmath>
22:
1.1.1.15! root 23: static Syncer *gSyncer;
1.1 root 24:
25: // コンストラクタ
1.1.1.8 root 26: MPU680x0Device::MPU680x0Device()
1.1.1.15! root 27: : inherited()
1.1 root 28: {
1.1.1.15! root 29: SetName("MPU(68030)");
! 30:
! 31: // MMU.xRP.DT == 0 になってはいけない(MMU 構成例外)。
! 32: SetSRP(0x00000001, 0);
! 33: SetCRP(0x00000001, 0);
! 34: // FPU
! 35: fpu_init();
1.1 root 36:
1.1.1.13 root 37: // 割り込みイベントコールバック設定
38: intr_event.func = ToEventCallback(&MPU680x0Device::InterruptCallback);
1.1.1.15! root 39: intr_event.Regist(GetName() + " Interrupt");
1.1.1.4 root 40:
1.1.1.9 root 41: // レジスタモニター
1.1.1.13 root 42: reg_monitor.func = ToMonitorCallback(&MPU680x0Device::MonitorUpdateReg);
1.1.1.9 root 43: reg_monitor.SetSize(79, 16);
44: reg_monitor.Regist(ID_MONITOR_MPUREG);
45:
46: // ATC モニター
1.1.1.13 root 47: atc_monitor.func = ToMonitorCallback(&MPU680x0Device::MonitorUpdateATC);
1.1.1.9 root 48: #if !defined(ATC_SINGLE)
49: atc_monitor.SetSize(131, m68030ATCTable::LINES + 3);
50: #else
51: atc_monitor.SetSize(31, 24);
52: #endif
53: atc_monitor.Regist(ID_MONITOR_MPUATC);
1.1.1.13 root 54:
55: SetTrace(false);
1.1 root 56: }
57:
58: // デストラクタ
59: MPU680x0Device::~MPU680x0Device()
60: {
61: }
62:
1.1.1.15! root 63: // 初期化
1.1 root 64: bool
65: MPU680x0Device::Init()
66: {
1.1.1.5 root 67: if (inherited::Init() == false) {
68: return false;
69: }
1.1.1.13 root 70:
1.1.1.15! root 71: debugger = GetDebugger();
! 72: interruptdev = GetInterruptDevice();
! 73: // static 関数から参照するため。うーん…。
! 74: gSyncer = syncer;
! 75:
1.1.1.13 root 76: // 割り込み時のディレイ。
77: //
78: // 外部デバイスが 68030 の IPL0-2 をアサートしてから、68030 がそれを
79: // 認識(確定)するのに 1 ~ 2 クロックかかる。
80: // その後レベルを比較するのは次のアップエッジ。
81: // その後 /IPEND をアサートするのはその次のアップエッジ。
82: // 合わせると、IPL0-2 が変化してから /IPEND がアサートされるまでは
83: // 2 ~ 3 クロックかかる。(Figure 8-4)
84: intr_event.time = 3 * clock_nsec;
1.1.1.5 root 85:
1.1 root 86: // FPU (6888x)
1.1.1.15! root 87: if (gMainApp.IsX68030()) {
1.1 root 88: // X68k は設定による
1.1.1.14 root 89: const ConfigItem& item = gConfig->Find("fpu-type");
90: std::string type = item.AsString();
91: if (type == "none" || type == "0") {
1.1.1.15! root 92: has_fpu = 0;
1.1.1.14 root 93: } else if (type == "68881" || type == "1") {
1.1.1.15! root 94: has_fpu = 1;
1.1.1.14 root 95: } else {
1.1 root 96: item.Err();
97: return false;
98: }
1.1.1.15! root 99: } else {
! 100: // LUNA は 68881 固定 (設定は見ない)
! 101: has_fpu = 1;
1.1 root 102: }
103:
1.1.1.15! root 104: vectortable = GetVectorTable();
1.1.1.10 root 105:
1.1 root 106: return true;
107: }
108:
1.1.1.13 root 109: // リセット
110: void
111: MPU680x0Device::ResetHard(bool poweron)
1.1.1.8 root 112: {
1.1.1.13 root 113: if (poweron) {
1.1.1.15! root 114: // サイクルを初期化。
! 115: used_cycle = 0;
! 116:
! 117: // 履歴を初期化。電源(再)投入時のみ行う。
! 118: exhist.Clear();
! 119: brhist.Clear();
! 120:
! 121: for (int i = 0; i < 16; i++) {
! 122: reg.R[i] = 0xcccccccc;
! 123: }
! 124: // XXX ただし X68030 IPLROM には A6 を初期化せずに書き込んでいるバグが
! 125: // あるため、A6 を概ね RAM のあるあたりに指定しておく…。
! 126: reg.A[6] = 0x000ccccc;
! 127:
! 128: // この後起きるリセット例外で PC を初期化する前に参照することになるので
! 129: // これだけここでも初期化しておく。
! 130: reg.pc = 0;
1.1.1.13 root 131: }
1.1.1.14 root 132: resetting = true;
1.1.1.13 root 133:
134: // リセット例外を 520 クロック後に起こす。
135: // 520 は、電源オン時、Vcc が最小動作規定値に達してから最低 520 クロック
136: // の間アサートしなければならないという値であって、実際これだけかかる
137: // とかいう値ではない。が、こちら側の都合で、この後実行される RAM の
1.1.1.15! root 138: // ResetHard() がブートページを用意した後で ExceptionReset() で
1.1.1.13 root 139: // SP, PC をフェッチするという順序にしないといけないため、リセット例外が
140: // 起動するまでに時間がかかるというところを都合よく真似ておく。
141:
142: exec_event.func = ToEventCallback(&MPU680x0Device::ResetCallback);
143: exec_event.time = 520 * clock_nsec;
1.1.1.15! root 144: exec_event.SetName(GetName() + " Reset");
! 145: scheduler->RestartEvent(exec_event);
1.1.1.8 root 146: }
147:
1.1.1.13 root 148: // リセット例外コールバック。
149: // MPU リセットから規定時間後に呼ばれる。
1.1.1.8 root 150: void
1.1.1.13 root 151: MPU680x0Device::ResetCallback(Event& ev)
1.1.1.8 root 152: {
1.1.1.14 root 153: resetting = false;
1.1.1.13 root 154: intr_pending = false;
155:
156: // リセット例外を実行
1.1.1.15! root 157: int cycle = ExceptionReset();
1.1.1.13 root 158:
159: // コールバックを ResetCallback から Exec* に差し替える前のここで
160: // トレース状態を初期化する。
1.1.1.15! root 161: SetTrace(debugger->IsTrace());
1.1.1.13 root 162:
163: // 以降は通常走行。
1.1.1.15! root 164: if (cpu_state == CPU_STATE_NORMAL) {
1.1.1.13 root 165: exec_event.func = exec_short;
166: exec_event.time = cycle * clock_nsec;
1.1.1.15! root 167: exec_event.SetName(GetName() + " Execute");
! 168: scheduler->RestartEvent(exec_event);
1.1.1.13 root 169: } else {
170: // リセット例外でダブルバスフォールトならここで停止。
171: }
1.1.1.8 root 172: }
173:
1.1.1.15! root 174: // 割り込みチェック、STOP/HALT 処理を含んだ完全な実行サイクルのコールバック。
1.1.1.13 root 175: // SR の I2:I0 が変わった直後などに呼ばれる。
176: // ここでは割り込みをチェックし、必要なら割り込み処理を起動、
177: // そうでなければ通常の命令実行を行う。
178: void
179: MPU680x0Device::ExecFull(Event& ev)
180: {
1.1.1.15! root 181: if (cpu_state == CPU_STATE_HALT) {
! 182: // HALT なら停止。
! 183: return;
! 184: }
! 185:
! 186: // マスクよりも割り込みレベルのほうが高ければ割り込み処理。
! 187: // この時点で intr_pending が true の可能性もあることに留意。
! 188: // 割り込みレベルは変化してないので Level 7 の特別処理は不要
! 189: // (割り込みレベルの変化はここではなく InterruptCallback() で処理)。
! 190: if (intr_level > reg.intr_mask) {
! 191: intr_pending = true;
! 192: }
1.1.1.14 root 193:
1.1.1.13 root 194: if (intr_pending) {
195: // 割り込み処理を起動。
196:
197: intr_pending = false;
198:
199: // 前の命令実行後となるここで割り込みを起動
1.1.1.15! root 200: int cycle = ExceptionInterrupt();
1.1.1.13 root 201:
202: ev.time = cycle * clock_nsec;
1.1.1.15! root 203: scheduler->StartEvent(ev);
1.1.1.13 root 204: } else {
1.1.1.15! root 205: if (cpu_state == CPU_STATE_NORMAL) {
1.1.1.13 root 206: // コールバックを通常処理に戻してから...
207: exec_event.func = exec_short;
208:
209: // この場で次の命令を実行。
210: ExecShort(ev);
211: } else {
1.1.1.15! root 212: // STOP ならここで停止。
1.1.1.13 root 213: }
214: }
1.1 root 215: }
216:
1.1.1.13 root 217: // トレース実行
218: void
219: MPU680x0Device::ExecTrace(Event& ev)
1.1 root 220: {
1.1.1.15! root 221: debugger->ExecMain();
1.1.1.13 root 222: ExecFull(ev);
1.1 root 223: }
224:
1.1.1.13 root 225: // トレース状態を設定する
226: void
227: MPU680x0Device::SetTrace(bool enable)
1.1 root 228: {
1.1.1.13 root 229: if (enable) {
230: exec_short = ToEventCallback(&MPU680x0Device::ExecTrace);
231: exec_full = ToEventCallback(&MPU680x0Device::ExecTrace);
232: } else {
233: exec_short = ToEventCallback(&MPU680x0Device::ExecShort);
234: exec_full = ToEventCallback(&MPU680x0Device::ExecFull);
235: }
236: exec_event.func = exec_full;
237:
238: // STOP 命令中にオンにするには、停止しているイベントを起こす。
239: // STOP 命令中にオフにする場合は、何もしなくていい。
240: if (enable && exec_event.IsRunning() == false) {
1.1.1.15! root 241: scheduler->StartEvent(exec_event);
1.1.1.13 root 242: }
1.1 root 243: }
244:
1.1.1.5 root 245: // 割り込みレベルが変わったことを MPU に通知。
1.1 root 246: void
1.1.1.5 root 247: MPU680x0Device::Interrupt(int level)
1.1 root 248: {
1.1.1.5 root 249: assertmsg(0 <= level && level <= 7, "level=%d", level);
250:
1.1.1.13 root 251: intr_event.code = level;
252: if (intr_event.IsRunning() == false) {
1.1.1.15! root 253: scheduler->StartEvent(intr_event);
1.1.1.5 root 254: }
1.1.1.13 root 255: }
256:
257: // 外部割り込みイベントコールバック。
258: // ev.code 0-7 は新しい割り込みレベル。
1.1.1.15! root 259: //
! 260: // 割り込みは本来命令境界ごとにチェックするが、実際に割り込みが起きうるのは
! 261: // 1) 割り込み信号線が変化した時
! 262: // 2) 割り込みマスクを変更した時
! 263: // しかないはずなので、この時だけ命令境界でチェックする。
! 264: // ここはその 1) のほう。その2のほうは core.cpp:SetSR() 参照。
! 265: //
! 266: // 外部割り込みが来たので、レベル比較などをして、必要なら次の命令境界で
! 267: // 割り込みチェックを行うようにするところ。
1.1.1.13 root 268: void
269: MPU680x0Device::InterruptCallback(Event& ev)
270: {
271: int level = ev.code;
1.1.1.15! root 272: int mask = reg.intr_mask;
! 273:
! 274: // 信号線が 2 クロック期間安定していること云々は省略
1.1.1.13 root 275:
1.1.1.15! root 276: // ホールトなら何もしない
! 277: if (cpu_state == CPU_STATE_HALT) {
! 278: return;
! 279: }
! 280:
! 281: // 割り込みを起こすかどうかに関わらずここで更新
! 282: intr_level = level;
! 283:
! 284: // 割り込み例外を起動するのは
! 285: // o レベルがマスクより高い
! 286: // o マスクに関係なく、レベルが下位から 7 に変化した場合
! 287: // (ここは変化した時だけ呼ばれるのと 7 より上はないので
! 288: // level==7 なら必ず「下位から 7 に変化」のはず)
! 289: if (level > mask || level == 7) {
1.1.1.13 root 290: // 通知した結果割り込みが受け付けられたので、
291: // この命令終了時のコールバックを割り込み処理起動に差し替える。
292: MakeNextFull();
293: intr_pending = true;
294:
295: // STOP 状態だったらここでイベントを開始する
1.1.1.15! root 296: if (cpu_state == CPU_STATE_STOP) {
1.1.1.13 root 297: // STOP から抜ける時間?
298: exec_event.time = 1 * clock_nsec;
1.1.1.15! root 299: scheduler->StartEvent(exec_event);
1.1.1.13 root 300: }
301: }
302: }
303:
304: // 次の命令境界は Full にする。
305: void
306: MPU680x0Device::MakeNextFull()
307: {
308: exec_event.func = exec_full;
309: }
310:
1.1.1.15! root 311: void
! 312: MPU680x0Device::ChangeState(uint32 new_state)
1.1.1.13 root 313: {
1.1.1.15! root 314: // new_mode (CPU_STATE_*) と
! 315: // RequestCPUMode() の引数 Syncer::SCHED_CPU_* は
1.1.1.13 root 316: // 実は同じ値なのでそのまま渡してよいことにする。
1.1.1.15! root 317: static_assert(CPU_STATE_NORMAL == Syncer::SCHED_CPU_NORMAL, "");
! 318: static_assert(CPU_STATE_STOP == Syncer::SCHED_CPU_STOP, "");
! 319: static_assert(CPU_STATE_HALT == Syncer::SCHED_CPU_HALT, "");
! 320:
! 321: if (cpu_state != new_state) {
! 322: cpu_state = new_state;
! 323: // スケジューラに通知
! 324: syncer->RequestCPUMode(cpu_state);
! 325: }
1.1 root 326: }
327:
328: // A-Line 命令エミュレーションのコールバックを指定。
329: void
1.1.1.15! root 330: MPU680x0Device::SetALineCallback(bool (*callback)(MPU680x0Device *, void*),
! 331: void *arg)
1.1 root 332: {
1.1.1.15! root 333: aline_callback = callback;
! 334: aline_arg = arg;
1.1 root 335: }
336:
337: // F-Line 命令エミュレーションのコールバックを指定。
338: void
1.1.1.15! root 339: MPU680x0Device::SetFLineCallback(bool (*callback)(MPU680x0Device *, void*),
! 340: void *arg)
1.1 root 341: {
1.1.1.15! root 342: fline_callback = callback;
! 343: fline_arg = arg;
1.1 root 344: }
345:
1.1.1.15! root 346: // RESET 命令
! 347: void
! 348: MPU680x0Device::ops_reset()
1.1.1.10 root 349: {
1.1.1.13 root 350: gVM->ResetByMPU();
351: }
352:
353: // ホールト状態
354: void
355: MPU680x0Device::Halt()
356: {
1.1.1.15! root 357: ChangeState(CPU_STATE_HALT);
! 358:
! 359: // この命令完了後に停止するため exec を差し替える。
! 360: MakeNextFull();
1.1.1.13 root 361:
362: // UI に通知 (メッセージボックス表示とか)
1.1.1.10 root 363: UIMessage::Post(UIMessage::HALT);
364: }
365:
366: // MPU からのアクセスをエミュレート
367: uint64
368: MPU680x0Device::Read8(uint32 addr)
369: {
370: try {
1.1.1.15! root 371: return read_8(addr);
1.1.1.10 root 372: } catch (...) {
373: return (uint64)-1;
374: }
375: }
376:
377: uint64
378: MPU680x0Device::Read16(uint32 addr)
379: {
380: try {
1.1.1.15! root 381: return read_16(addr);
1.1.1.10 root 382: } catch (...) {
383: return (uint64)-1;
384: }
385: }
386:
387: uint64
388: MPU680x0Device::Read32(uint32 addr)
1.1.1.2 root 389: {
1.1.1.10 root 390: try {
1.1.1.15! root 391: return read_32(addr);
1.1.1.10 root 392: } catch (...) {
393: return (uint64)-1;
394: }
395: }
396:
397: uint64
398: MPU680x0Device::Write8(uint32 addr, uint32 data)
399: {
400: try {
1.1.1.15! root 401: write_8(addr, data);
1.1.1.10 root 402: return 0;
403: } catch (...) {
404: return (uint64)-1;
405: }
406: }
407:
408: uint64
409: MPU680x0Device::Write16(uint32 addr, uint32 data)
410: {
411: try {
1.1.1.15! root 412: write_16(addr, data);
1.1.1.10 root 413: return 0;
414: } catch (...) {
415: return (uint64)-1;
416: }
417: }
418:
419: uint64
420: MPU680x0Device::Write32(uint32 addr, uint32 data)
421: {
422: try {
1.1.1.15! root 423: write_32(addr, data);
1.1.1.10 root 424: return 0;
425: } catch (...) {
426: return (uint64)-1;
427: }
1.1.1.2 root 428: }
429:
1.1 root 430: // モニター更新 (レジスタウィンドウ)
1.1.1.3 root 431: void
1.1.1.9 root 432: MPU680x0Device::MonitorUpdateReg(Monitor *, TextScreen& screen)
1.1 root 433: {
1.1.1.15! root 434: m68kreg tmp;
! 435: uint32 tmp_ppc;
! 436: uint32 tmp_state;
1.1 root 437: int x;
438: int y;
439:
1.1.1.9 root 440: screen.Clear();
1.1 root 441:
442: // ローカルにコピー
1.1.1.13 root 443: // ただし一部は reg ではなく cpu クラス本体にある
1.1.1.15! root 444: tmp = reg;
! 445: tmp_ppc = ppc;
! 446: tmp_state = cpu_state;
1.1 root 447:
448: // データレジスタ、アドレスレジスタ
449: for (int i = 0; i < 4; i++) {
1.1.1.9 root 450: screen.Print(0, i, "D%d:%08x D%d:%08x A%d:%08x A%d:%08x",
1.1.1.15! root 451: i + 0, tmp.R[i],
! 452: i + 4, tmp.R[i + 4],
! 453: i + 0, tmp.R[i + 8],
! 454: i + 4, tmp.R[i + 12]);
1.1 root 455: }
456:
457: // 5列目
458: x = 50;
459: // SR
1.1.1.9 root 460: screen.Print(x, 0, "SR:%04x(%c%c%c%c%c)",
1.1.1.15! root 461: tmp.GetSR(),
! 462: (tmp.ccr.IsX()) ? 'X' : '-',
! 463: (tmp.ccr.IsN()) ? 'N' : '-',
! 464: (tmp.ccr.IsZ()) ? 'Z' : '-',
! 465: (tmp.ccr.IsV()) ? 'V' : '-',
! 466: (tmp.ccr.IsC()) ? 'C' : '-');
1.1 root 467:
468: // VBR
1.1.1.15! root 469: screen.Print(x, 1, "VBR:%08x", tmp.vbr);
1.1 root 470:
471: // *SP
1.1.1.15! root 472: uint ms = (tmp.s ? 2 : 0) | (tmp.m ? 1 : 0); // T1 T0 S M
1.1 root 473: if (ms == 3) {
474: // Supervisor (Master) mode (A7=MSP, USP/ISP)
1.1.1.15! root 475: screen.Print(x, 2, TA::Disable, "USP:%08x", tmp.usp);
! 476: screen.Print(x, 3, TA::Disable, "ISP:%08x", tmp.isp);
1.1 root 477: } else if (ms == 2) {
478: // Supervisor (Interrupt) mode (A7=ISP, USP/MSP)
1.1.1.15! root 479: screen.Print(x, 2, TA::Disable, "USP:%08x", tmp.usp);
! 480: screen.Print(x, 3, TA::Disable, "MSP:%08x", tmp.msp);
1.1 root 481: } else {
482: // User mode (A7=USP, ISP/MSP)
1.1.1.15! root 483: screen.Print(x, 2, TA::Disable, "ISP:%08x", tmp.isp);
! 484: screen.Print(x, 3, TA::Disable, "MSP:%08x", tmp.msp);
1.1 root 485: }
486:
487: // 6列目
488: // PC: 01234567
489: // SFC:1 DFC:1
490: // CACR:01234567
491: // CAAR:01234567
492: x = 66;
1.1.1.15! root 493: screen.Print(x, 0, "PC: %08x", tmp_ppc);
! 494: screen.Print(x, 1, "SFC:%d DFC:%d", tmp.sfc, tmp.dfc);
! 495: screen.Print(x, 2, "CACR:%08x", tmp.cacr);
! 496: screen.Print(x, 3, "CAAR:%08x", tmp.caar);
1.1 root 497:
498: // XXX どこに置くのがいいか
1.1.1.15! root 499: switch (tmp_state) {
! 500: case CPU_STATE_NORMAL:
1.1.1.9 root 501: screen.Puts(25, 4, "State: Running");
1.1.1.13 root 502: break;
1.1.1.15! root 503: case CPU_STATE_STOP:
1.1.1.9 root 504: screen.Puts(25, 4, "State: STOP instruction");
1.1.1.13 root 505: break;
1.1.1.15! root 506: case CPU_STATE_HALT:
1.1.1.9 root 507: screen.Puts(25, 4, TA::On, "State: Double Bus Fault");
1.1.1.13 root 508: break;
509: default:
1.1.1.15! root 510: screen.Print(25, 4, TA::On, "corrupted cpu_state=%d", tmp_state);
! 511: break;
1.1 root 512: }
513:
514: // MMU
515: x = 0;
516: y = 4;
1.1.1.9 root 517: screen.Puts(x, y++, "<MMU>");
1.1.1.15! root 518: screen.Print(x, y, "SRP:%08x_%08x", tmp.srp.h, tmp.srp.l);
! 519: screen.Print(x, y + 1, "CRP:%08x_%08x", tmp.crp.h, tmp.crp.l);
1.1 root 520: x += 23;
521: for (int i = 0; i < 2; i++) {
1.1.1.15! root 522: uint32 tt = tmp.tt[i];
1.1.1.9 root 523: screen.Print(x, y + i, "TT%d:%08x(%c%c%c)",
1.1 root 524: i, tt,
525: (tt & m68030TT::E) ? 'E' : '-',
526: (tt & m68030TT::CI) ? 'C' : '-',
527: (tt & m68030TT::RWM) ? '-' : ((tt & m68030TT::RW) ? 'R' : 'W'));
528: }
529: x += 19;
1.1.1.9 root 530: screen.Print(x, y, "TC:%08x(%c%c%c)",
1.1.1.15! root 531: tmp.tc,
! 532: (tmp.tc & m68030TC::TC_E) ? 'E' : '-',
! 533: (tmp.tc & m68030TC::TC_SRE) ? 'S' : '-',
! 534: (tmp.tc & m68030TC::TC_FCL) ? 'F' : '-');
! 535: y++;
! 536: screen.Print(x, y, "MMUSR: %04x(", tmp.mmusr);
! 537: x += 12;
! 538: screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::B) | 'B');
! 539: screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::L) | 'L');
! 540: screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::S) | 'S');
! 541: screen.Putc(x++, y, '-');
! 542: screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::W) | 'W');
! 543: screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::I) | 'I');
! 544: screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::M) | 'M');
! 545: screen.Puts(x, y, "- -");
! 546: x += 3;
! 547: screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::T) | 'T');
! 548: screen.Print(x, y, "---N=%d)", tmp.mmusr & m68030MMUSR::N);
1.1 root 549:
550: // FPU
551: x = 0;
552: y = 7;
1.1.1.9 root 553: screen.Puts(x, y++, "<FPU>");
1.1 root 554: for (int i = 0; i < 8; i++) {
1.1.1.9 root 555: screen.Print(x, y + i, "FP%d:%04x_%08x_%08x (%-20s)",
1.1 root 556: i,
1.1.1.15! root 557: tmp.fpframe.fpf_regs[i * 3 + 0] >> 16,
! 558: tmp.fpframe.fpf_regs[i * 3 + 1],
! 559: tmp.fpframe.fpf_regs[i * 3 + 2],
1.1 root 560: "not yet");
561: }
562:
563: x = 51;
564:
565: // FPCR
566: static const char * const rpstr[] = {
567: ".EXT",
568: ".SGL",
569: ".DBL",
570: ".???",
571: };
1.1.1.15! root 572: uint32 fpcr = tmp.fpframe.fpf_fpcr;
1.1.1.9 root 573: screen.Print(x, y++, "FPCR:%04x", fpcr);
1.1.1.11 root 574: screen.Puts(x + 2, y, TA::OnOff(fpcr & 0x8000), "BS");
575: screen.Puts(x + 5, y, TA::OnOff(fpcr & 0x4000), "SN");
576: screen.Puts(x + 8, y, TA::OnOff(fpcr & 0x2000), "OP");
577: screen.Puts(x + 11, y, TA::OnOff(fpcr & 0x1000), "OV");
578: screen.Puts(x + 14, y, TA::OnOff(fpcr & 0x0800), "UF");
579: screen.Puts(x + 17, y, TA::OnOff(fpcr & 0x0400), "DZ");
580: screen.Puts(x + 20, y, TA::OnOff(fpcr & 0x0200), "I2");
581: screen.Puts(x + 23, y, TA::OnOff(fpcr & 0x0100), "I1");
582: y++;
1.1.1.9 root 583: screen.Print(x + 2, y++, "RP=%s RM=%s",
1.1 root 584: rpstr[(fpcr >> 6) & 3],
585: rmstr[(fpcr >> 4) & 3]);
586:
587: // FPSR
1.1.1.15! root 588: uint32 fpsr = tmp.fpframe.fpf_fpsr;
1.1 root 589: uint32 cc = fpsr >> 24;
1.1.1.9 root 590: screen.Print(x, y++, "FPSR:%08x", fpsr);
1.1.1.11 root 591: screen.Puts(x + 2, y, TA::OnOff(cc & 0x08), "N");
592: screen.Puts(x + 4, y, TA::OnOff(cc & 0x04), "Z");
593: screen.Puts(x + 6, y, TA::OnOff(cc & 0x02), "Inf");
594: screen.Puts(x + 10, y, TA::OnOff(cc & 0x01), "NAN");
595: screen.Print(x + 14, y, "Q=$%02x", (fpsr >> 16) & 0xff);
596: y++;
597: screen.Puts(x + 2, y, TA::OnOff(fpsr & 0x8000), "BS");
598: screen.Puts(x + 5, y, TA::OnOff(fpsr & 0x4000), "SN");
599: screen.Puts(x + 8, y, TA::OnOff(fpsr & 0x2000), "OP");
600: screen.Puts(x + 11, y, TA::OnOff(fpsr & 0x1000), "OV");
601: screen.Puts(x + 14, y, TA::OnOff(fpsr & 0x0800), "UF");
602: screen.Puts(x + 17, y, TA::OnOff(fpsr & 0x0400), "DZ");
603: screen.Puts(x + 20, y, TA::OnOff(fpsr & 0x0200), "I2");
604: screen.Puts(x + 23, y, TA::OnOff(fpsr & 0x0100), "I1");
605: y++;
606: screen.Puts(x + 2, y, TA::OnOff(fpsr & 0x80), "IOP");
607: screen.Puts(x + 6, y, TA::OnOff(fpsr & 0x80), "OVFL");
608: screen.Puts(x + 11, y, TA::OnOff(fpsr & 0x80), "UNFL");
609: screen.Puts(x + 16, y, TA::OnOff(fpsr & 0x80), "DZ");
610: screen.Puts(x + 19, y, TA::OnOff(fpsr & 0x80), "INEX");
611: y++;
1.1 root 612:
613: // FPIAR
1.1.1.15! root 614: screen.Print(x, y++, "FPIAR:%08x", tmp.fpframe.fpf_fpiar);
1.1 root 615: }
616:
1.1.1.9 root 617: // モニター更新 (ATC)
1.1.1.3 root 618: void
1.1.1.9 root 619: MPU680x0Device::MonitorUpdateATC(Monitor *, TextScreen& screen)
1.1 root 620: {
621: m68030ATCTable *table;
622: #if !defined(ATC_SINGLE)
623: struct {
624: int fc;
625: const char *name;
626: } fclist[] = {
627: { 1, "User/Data" },
628: { 2, "User/Program" },
629: { 5, "Super/Data" },
630: { 6, "Super/Program" },
631: };
632:
1.1.1.9 root 633: screen.Clear();
1.1 root 634:
635: // 行数は左端にだけ
636: for (int i = 0; i < m68030ATCTable::LINES; i++) {
1.1.1.9 root 637: screen.Print(0, i + 2, "%02d:", i);
1.1 root 638: }
639:
640: for (int t = 0; t < countof(fclist); t++) {
641: int x = t * 32 + 4;
642: int fc = fclist[t].fc;
643: const char *name = fclist[t].name;
644: double r;
645: int i;
646:
1.1.1.15! root 647: table = &atc.tables[fc];
1.1.1.9 root 648: screen.Print(x, 0, "FC=%d %s", fc, name);
649: screen.Puts(x, 1, "LAddr PAddr Flag Hit% Age");
1.1 root 650: // 先に統計用の母数を計算
651: uint64 total = 0;
652: for (i = 0; i < countof(table->hit); i++) {
653: total += table->hit[i];
654: }
655: total += table->hit_head;
656:
657: // エントリ表示
658: for (i = 0; i < countof(table->line); i++) {
659: m68030ATCLine& a = table->line[i];
1.1.1.2 root 660: TA attr;
1.1 root 661: if (a.IsInvalid()) {
662: attr = TA::Disable;
663: } else if (table->head == &a) {
664: attr = TA::Em;
665: } else {
666: attr = TA::Normal;
667: }
668:
1.1.1.9 root 669: screen.Print(x, i + 2, attr, "%08x %08x %c%c%c",
1.1 root 670: a.GetLAddr(),
671: a.GetPAddr(),
672: a.IsBusError() ? 'B' : '-',
673: a.IsWProtect() ? 'P' : '-',
674: a.IsModified() ? 'M' : '-');
675: }
676: // ヘッドのヒット率
1.1.1.9 root 677: screen.Puts(x + 0, i + 2, "head");
1.1 root 678: r = (double)table->hit_head * 100 / total;
1.1.1.13 root 679: if (!std::isnan(r)) {
1.1.1.9 root 680: screen.Print(x + 5, i + 2, "%4.1f%%", r);
1.1 root 681: }
682: // 配列のヒット率とミス率
1.1.1.9 root 683: screen.Puts(x + 17, i + 2, "miss");
1.1 root 684: for (i = 0; i < countof(table->hit); i++) {
685: r = (double)(table->hit[i] * 100) / total;
1.1.1.13 root 686: if (!std::isnan(r)) {
1.1.1.9 root 687: screen.Print(x + 22, i + 2, "%4.1f%%", r);
1.1 root 688: }
689: }
690: // カウンタ
691: // 分かりやすさのため最大値からの差(距離)を表示
692: for (i = 0; i < countof(table->line); i++) {
693: m68030ATCLine& a = table->line[i];
694: if (!a.IsInvalid()) {
1.1.1.9 root 695: screen.Print(x + 27, i + 2, "%4d", table->head->age - a.age);
1.1 root 696: }
697: }
698: }
699: #else
700: m68030ATCLine *a;
701: int i;
702:
1.1.1.15! root 703: table = &atc.tables[0];
1.1.1.9 root 704: screen.Clear();
1.1 root 705:
1.1.1.9 root 706: screen.Puts(0, 0, "No. FC LAddr PAddr Flag");
1.1 root 707: for (a = table->head, i = 0; a; a = a->next, i++) {
1.1.1.9 root 708: screen.Print(0, i + 1, "%02d:", i);
1.1 root 709: if (!a->IsInvalid()) {
710: char dp;
711: switch (a->fc & 3) {
712: case 1: dp = 'D'; break;
713: case 2: dp = 'P'; break;
714: default: dp = '?'; break;
715: }
1.1.1.9 root 716: screen.Print(4, i + 1, "%d(%c%c):%08x %08x %c%c%c",
1.1 root 717: a->fc,
718: (a->fc & 4) ? 'S' : 'U',
719: dp,
720: a->GetLAddr(), a->GetPAddr(),
721: a->IsBusError() ? 'B' : '-',
722: a->IsWProtect() ? 'W' : '-',
723: a->IsModified() ? 'M' : '-');
724: }
725: }
726: #endif
727: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.