|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2017 [email protected]
4: //
5: // MPU
6: //
7:
8: #include <math.h>
9: #include "mpu.h"
10: #include "m68030excep.h"
11: #include "debugger.h"
12: #include "scheduler.h"
13: #include "vm.h"
14: #include "configfile.h"
15:
16: MPUDevice *gMPU;
17: MPUATC *gMPUATC;
18: MPUBrHist *gMPUBrHist;
19:
20: // コンストラクタ
21: //
22: // reset_vector にはリセットベクタの番地を指定する。
23: // 本来の MPU の動作は、リセット例外時にメモリの $0, $4 番地 (もっと言えば
24: // FC がプログラム空間の) から SP, PC を読み込むのだが、そのためにメモリ空間
25: // を切り替える処理はリセット時にしか必要ないわりに結構手間である。そしてこの
26: // m68k 恒例のブートストラップ動作はソフトウェア的には一切観測する手段がない。
27: // であれば、リセット例外時にこのアドレスから SP, PC の値を直接読み込んだと
28: // しても動作にはまったく違いは出ない。そしてバス・メモリ空間の実装も簡略化
29: // できる。すごい楽。
30: MPUDevice::MPUDevice(uint32 reset_vector)
31: {
32: logname = "mpu";
33: devname = "MPU";
34: monitor.Init(78, 16);
35:
36: cpu = m68030_init(reset_vector);
37:
38: // モニタ用の別オブジェクト
39: gMPUATC = new MPUATC(cpu);
40: gMPUBrHist = new MPUBrHist(cpu);
41: }
42:
43: // デストラクタ
44: MPUDevice::~MPUDevice()
45: {
46: }
47:
48: bool
49: MPUDevice::Init()
50: {
51: if (gVM->GetType() == VM::TYPE_LUNA) {
52: // LUNA は 68881 固定
53: cpu->has_fpu = 1;
54: } else {
55: // X68k は設定ファイルで指定
56: cpu->has_fpu = gConfig->ReadInt("mpu_has_fpu");
57: }
58: return true;
59: }
60:
61: // リセット
62: // (MPU が動くのはこの後 Scheduler::Run() を発行した後)
63: void
64: MPUDevice::ResetHard()
65: {
66: m68030_exception_reset(cpu);
67: }
68:
69: // MPU を request で指定されたクロックサイクルだけ実行する。
70: // 今回の実行サイクル数が request を越えた命令境界まで実行するので
71: // 実際には少しはみ出る。
72: // また実行中にデバイスアクセスなどによりスケジューラにイベントが
73: // 追加されるとその次の命令境界で処理を打ち切って戻ってくる。
74: // 戻り値は CPU の outer フラグ。
75: uint32
76: MPUDevice::Run(uint64 request)
77: {
78: return m68030_run(cpu, request);
79: }
80:
81: // 割り込み発生を MPU に通知 (オートベクタ)
82: void
83: MPUDevice::Interrupt(int level)
84: {
85: int vector = M68K_EXCEP_LEVEL_BASE + level;
86: m68030_interrupt(cpu, level, vector);
87: }
88:
89: // 割り込み発生を MPU に通知 (ベクタ)
90: void
91: MPUDevice::Interrupt(int level, int vector)
92: {
93: m68030_interrupt(cpu, level, vector);
94: }
95:
96: // A-Line 命令エミュレーションのコールバックを指定。
97: void
98: MPUDevice::SetALineCallback(bool (*callback)(m68kcpu *, void*), void *arg)
99: {
100: cpu->aline_callback = callback;
101: cpu->aline_arg = arg;
102: }
103:
104: // F-Line 命令エミュレーションのコールバックを指定。
105: void
106: MPUDevice::SetFLineCallback(bool (*callback)(m68kcpu *, void*), void *arg)
107: {
108: cpu->fline_callback = callback;
109: cpu->fline_arg = arg;
110: }
111:
112: // モニター更新 (レジスタウィンドウ)
113: bool
114: MPUDevice::MonitorUpdate()
115: {
116: m68kreg reg;
117: uint32 ppc;
118: int x;
119: int y;
120:
121: monitor.Clear();
122:
123: // ローカルにコピー
124: // ただし PPC だけ reg 構造体ではなく cpu クラス側にある
125: reg = cpu->reg;
126: ppc = cpu->ppc;
127:
128: // データレジスタ、アドレスレジスタ
129: for (int i = 0; i < 4; i++) {
130: monitor.Print(0, i, "D%d:%08x D%d:%08x A%d:%08x A%d:%08x",
131: i + 0, reg.da[i],
132: i + 4, reg.da[i + 4],
133: i + 0, reg.da[i + 8],
134: i + 4, reg.da[i + 12]);
135: }
136:
137: // 5列目
138: x = 50;
139: // SR
140: monitor.Print(x, 0, "SR:%04x(%c%c%c%c%c)",
141: (reg.sr_h() | reg.ccr.Get()),
142: (reg.ccr.IsX()) ? 'X' : '-',
143: (reg.ccr.IsN()) ? 'N' : '-',
144: (reg.ccr.IsZ()) ? 'Z' : '-',
145: (reg.ccr.IsV()) ? 'V' : '-',
146: (reg.ccr.IsC()) ? 'C' : '-');
147:
148: // VBR
149: monitor.Print(x, 1, "VBR:%08x", reg.vbr);
150:
151: // *SP
152: uint ms = (reg.s ? 2 : 0) | (reg.m ? 1 : 0); // T1 T0 S M
153: if (ms == 3) {
154: // Supervisor (Master) mode (A7=MSP, USP/ISP)
155: monitor.Print(x, 2, TA::Disable, "USP:%08x", reg.usp);
156: monitor.Print(x, 3, TA::Disable, "ISP:%08x", reg.isp);
157: } else if (ms == 2) {
158: // Supervisor (Interrupt) mode (A7=ISP, USP/MSP)
159: monitor.Print(x, 2, TA::Disable, "USP:%08x", reg.usp);
160: monitor.Print(x, 3, TA::Disable, "MSP:%08x", reg.msp);
161: } else {
162: // User mode (A7=USP, ISP/MSP)
163: monitor.Print(x, 2, TA::Disable, "ISP:%08x", reg.isp);
164: monitor.Print(x, 3, TA::Disable, "MSP:%08x", reg.msp);
165: }
166:
167: // 6列目
168: // PC: 01234567
169: // SFC:1 DFC:1
170: // CACR:01234567
171: // CAAR:01234567
172: x = 66;
173: monitor.Print(x, 0, "PC: %08x", ppc);
174: monitor.Print(x, 1, "SFC:%d DFC:%d", reg.sfc, reg.dfc);
175: monitor.Print(x, 2, "CACR:%08x", reg.cacr);
176: monitor.Print(x, 3, "CAAR:%08x", reg.caar);
177:
178: // XXX どこに置くのがいいか
179: if (reg.state == 0) {
180: monitor.Print(25, 4, "State: Running");
181: } else if (reg.state == CPU_REQ_STOP) {
182: monitor.Print(25, 4, "State: STOP instruction");
183: } else if (reg.state == CPU_REQ_HALT) {
184: monitor.Print(25, 4, "State: Double Bus Fault");
185: }
186:
187: // MMU
188: x = 0;
189: y = 4;
190: monitor.Print(x, y++, "<MMU>");
191: monitor.Print(x, y, "SRP:%08x_%08x", reg.srp.h, reg.srp.l);
192: monitor.Print(x, y + 1, "CRP:%08x_%08x", reg.crp.h, reg.crp.l);
193: x += 23;
194: for (int i = 0; i < 2; i++) {
195: uint32 tt = reg.tt[i];
196: monitor.Print(x, y + i, "TT%d:%08x(%c%c%c)",
197: i, tt,
198: (tt & m68030TT::E) ? 'E' : '-',
199: (tt & m68030TT::CI) ? 'C' : '-',
200: (tt & m68030TT::RWM) ? '-' : ((tt & m68030TT::RW) ? 'R' : 'W'));
201: }
202: x += 19;
203: monitor.Print(x, y, "TC:%08x(%c%c%c)",
204: reg.tc,
205: (reg.tc & m68030TC::TC_E) ? 'E' : '-',
206: (reg.tc & m68030TC::TC_SRE) ? 'S' : '-',
207: (reg.tc & m68030TC::TC_FCL) ? 'F' : '-');
208: monitor.Print(x, y + 1, "MMUSR: %04x(%c%c%c%c%c%c%c N=%d)",
209: reg.mmusr,
210: (reg.mmusr & m68030MMUSR::B) ? 'B' : '-',
211: (reg.mmusr & m68030MMUSR::L) ? 'L' : '-',
212: (reg.mmusr & m68030MMUSR::S) ? 'S' : '-',
213: (reg.mmusr & m68030MMUSR::W) ? 'W' : '-',
214: (reg.mmusr & m68030MMUSR::I) ? 'I' : '-',
215: (reg.mmusr & m68030MMUSR::M) ? 'M' : '-',
216: (reg.mmusr & m68030MMUSR::T) ? 'T' : '-',
217: (reg.mmusr & m68030MMUSR::N));
218:
219: // FPU
220: x = 0;
221: y = 7;
222: monitor.Print(x, y++, "<FPU>");
223: for (int i = 0; i < 8; i++) {
224: monitor.Print(x, y + i, "FP%d:%04x_%08x_%08x (%-20s)",
225: i,
226: reg.fpframe.fpf_regs[i * 3 + 0] >> 16,
227: reg.fpframe.fpf_regs[i * 3 + 1],
228: reg.fpframe.fpf_regs[i * 3 + 2],
229: "not yet");
230: }
231:
232: x = 51;
233:
234: // FPCR
235: static const char * const rpstr[] = {
236: ".EXT",
237: ".SGL",
238: ".DBL",
239: ".???",
240: };
241: static const char * const rmstr[] = {
242: "Near",
243: "Zero",
244: "Minus",
245: "Plus",
246: };
247: uint32 fpcr = reg.fpframe.fpf_fpcr;
248: monitor.Print(x, y++, "FPCR:%04x", fpcr);
249: monitor.Print(x + 2, y++, "%s %s %s %s %s %s %s %s",
250: (fpcr & 0x8000) ? "BS" : "--",
251: (fpcr & 0x4000) ? "SN" : "--",
252: (fpcr & 0x2000) ? "OP" : "--",
253: (fpcr & 0x1000) ? "OV" : "--",
254: (fpcr & 0x0800) ? "UF" : "--",
255: (fpcr & 0x0400) ? "DZ" : "--",
256: (fpcr & 0x0200) ? "I2" : "--",
257: (fpcr & 0x0100) ? "I1" : "--");
258: monitor.Print(x + 2, y++, "RP=%s RM=%s",
259: rpstr[(fpcr >> 6) & 3],
260: rmstr[(fpcr >> 4) & 3]);
261:
262: // FPSR
263: uint32 fpsr = reg.fpframe.fpf_fpsr;
264: uint32 cc = fpsr >> 24;
265: monitor.Print(x, y++, "FPSR:%08x", fpsr);
266: monitor.Print(x + 2, y++, "%c %c %s %s Q=$%02x",
267: (cc & 0x08) ? 'N' : '-',
268: (cc & 0x04) ? 'Z' : '-',
269: (cc & 0x02) ? "Inf" : "---",
270: (cc & 0x01) ? "NAN" : "---",
271: (fpsr >> 16) & 0xff);
272: monitor.Print(x + 2, y++, "%s %s %s %s %s %s %s %s",
273: (fpsr & 0x8000) ? "BS" : "--",
274: (fpsr & 0x4000) ? "SN" : "--",
275: (fpsr & 0x2000) ? "OP" : "--",
276: (fpsr & 0x1000) ? "OV" : "--",
277: (fpsr & 0x0800) ? "UF" : "--",
278: (fpsr & 0x0400) ? "DZ" : "--",
279: (fpsr & 0x0200) ? "I2" : "--",
280: (fpsr & 0x0100) ? "I1" : "--");
281: monitor.Print(x + 2, y++, "%s %s %s %s %s",
282: (fpsr & 0x80) ? "IOP" : "---",
283: (fpsr & 0x40) ? "OVFL" : "----",
284: (fpsr & 0x20) ? "UNFL" : "----",
285: (fpsr & 0x10) ? "DZ" : "--",
286: (fpsr & 0x08) ? "INEX" : "----");
287:
288: // FPIAR
289: monitor.Print(x, y++, "FPIAR:%08x", reg.fpframe.fpf_fpiar);
290:
291: return true;
292: }
293:
294: // CPU 側からのコールバック。RESET 命令ハンドラ
295: void
296: m68030_reset_inst(m68kcpu *cpu)
297: {
298: gVM->ResetSoft();
299: }
300:
301: //
302: // ATC モニタ
303: //
304: MPUATC::MPUATC(m68kcpu *arg)
305: {
306: cpu = arg;
307: #if !defined(ATC_SINGLE)
308: monitor.Init(131, m68030ATCTable::LINES + 3);
309: #else
310: monitor.Init(31, 24);
311: #endif
312: }
313:
314: bool
315: MPUATC::MonitorUpdate()
316: {
317: m68030ATCTable *table;
318: #if !defined(ATC_SINGLE)
319: struct {
320: int fc;
321: const char *name;
322: } fclist[] = {
323: { 1, "User/Data" },
324: { 2, "User/Program" },
325: { 5, "Super/Data" },
326: { 6, "Super/Program" },
327: };
328:
329: monitor.Clear();
330:
331: // 行数は左端にだけ
332: for (int i = 0; i < m68030ATCTable::LINES; i++) {
333: monitor.Print(0, i + 2, "%02d:", i);
334: }
335:
336: for (int t = 0; t < countof(fclist); t++) {
337: int x = t * 32 + 4;
338: int fc = fclist[t].fc;
339: const char *name = fclist[t].name;
340: double r;
341: int i;
342:
343: table = &cpu->atc.tables[fc];
344: monitor.Print(x, 0, "FC=%d %s", fc, name);
345: monitor.Print(x, 1, "LAddr PAddr Flag Hit%% Age");
346: // 先に統計用の母数を計算
347: uint64 total = 0;
348: for (i = 0; i < countof(table->hit); i++) {
349: total += table->hit[i];
350: }
351: total += table->hit_head;
352:
353: // エントリ表示
354: for (i = 0; i < countof(table->line); i++) {
355: m68030ATCLine& a = table->line[i];
356: int attr;
357: if (a.IsInvalid()) {
358: attr = TA::Disable;
359: } else if (table->head == &a) {
360: attr = TA::Em;
361: } else {
362: attr = TA::Normal;
363: }
364:
365: monitor.Print(x, i + 2, attr, "%08x %08x %c%c%c",
366: a.GetLAddr(),
367: a.GetPAddr(),
368: a.IsBusError() ? 'B' : '-',
369: a.IsWProtect() ? 'P' : '-',
370: a.IsModified() ? 'M' : '-');
371: }
372: // ヘッドのヒット率
373: monitor.Print(x + 0, i + 2, "head");
374: r = (double)table->hit_head * 100 / total;
375: if (!isnan(r)) {
376: monitor.Print(x + 5, i + 2, "%4.1f%%", r);
377: }
378: // 配列のヒット率とミス率
379: monitor.Print(x + 17, i + 2, "miss");
380: for (i = 0; i < countof(table->hit); i++) {
381: r = (double)(table->hit[i] * 100) / total;
382: if (!isnan(r)) {
383: monitor.Print(x + 22, i + 2, "%4.1f%%", r);
384: }
385: }
386: // カウンタ
387: // 分かりやすさのため最大値からの差(距離)を表示
388: for (i = 0; i < countof(table->line); i++) {
389: m68030ATCLine& a = table->line[i];
390: if (!a.IsInvalid()) {
391: monitor.Print(x + 27, i + 2, "%4d", table->head->age - a.age);
392: }
393: }
394: }
395: #else
396: m68030ATCLine *a;
397: int i;
398:
399: table = &cpu->atc.tables[0];
400: monitor.Clear();
401:
402: monitor.Print(0, 0, "No. FC LAddr PAddr Flag");
403: for (a = table->head, i = 0; a; a = a->next, i++) {
404: monitor.Print(0, i + 1, "%02d:", i);
405: if (!a->IsInvalid()) {
406: char dp;
407: switch (a->fc & 3) {
408: case 1: dp = 'D'; break;
409: case 2: dp = 'P'; break;
410: default: dp = '?'; break;
411: }
412: monitor.Print(4, i + 1, "%d(%c%c):%08x %08x %c%c%c",
413: a->fc,
414: (a->fc & 4) ? 'S' : 'U',
415: dp,
416: a->GetLAddr(), a->GetPAddr(),
417: a->IsBusError() ? 'B' : '-',
418: a->IsWProtect() ? 'W' : '-',
419: a->IsModified() ? 'M' : '-');
420: }
421: }
422: #endif
423: return true;
424: }
425:
426: //
427: // ブランチ履歴モニタ
428: //
429: MPUBrHist::MPUBrHist(m68kcpu *arg)
430: {
431: cpu = arg;
432: monitor.Init(70, 16);
433: }
434:
435: bool
436: MPUBrHist::MonitorUpdate()
437: {
438: int i;
439: uint8 t;
440:
441: // 0 1 2 3 4 5 6
442: // 0123456789012345678901234567890123456789012345678901234567890123456789
443: // 01 $01234567->$01234567 x123456789 16 $01234567->$01234567 x123456789
444:
445: monitor.Clear();
446:
447: // top は現在の位置。
448: // brhist[] は古い順に書き込まれるが、表示は新しい順にしたい。
449: // ブランチ履歴は書き込み最適化のため256エントリあるが、そんなに
450: // 奥のほうが気になることはないだろう。
451: t = cpu->brhist_top;
452: for (i = 0; i < 32; i++) {
453: m68kbrhist& h = cpu->brhist[t--];
454: int x = (i / 16) * 36;
455: int y = i % 16;
456: if (h.count == 0) {
457: continue;
458: }
459: monitor.Print(x, y, "%02d:$%08x->$%08x", i,
460: h.from, h.to);
461: if (h.count > 1) {
462: monitor.Print(x + 24, y, "x%9u", h.count);
463: }
464: }
465: return true;
466: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.