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