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