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