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