|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2022 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // MPU (HD647180)
9: //
10:
11: #include "mpu64180.h"
12: #include "debugger.h"
13: #include "hd647180.h"
14: #include "pio.h"
15: #include "scheduler.h"
16: #include "xpbus.h"
17:
18: // コンストラクタ
19: MPU64180Device::MPU64180Device()
20: : inherited(OBJ_MPUXP)
21: {
22: ClearAlias();
23: AddAlias("XP");
24: AddAlias("HD647180");
25:
26: // 6.144MHz = 162.7604… [nsec]
27: clock_nsec = 163;
28:
29: // 命令実行イベント。func, time は都度設定する。
30: exec_event.Regist("XP(HD647180) Execute");
31:
32: // タイマーイベント。time は都度設定する。
33: timer_event.func = ToEventCallback(&MPU64180Device::TimerCallback);
34: timer_event.Regist("XP(HD647180) Timer");
35:
36: // レジスタモニター
37: reg_monitor.func = ToMonitorCallback(&MPU64180Device::MonitorUpdateReg);
38: reg_monitor.SetSize(38, 6);
39: reg_monitor.Regist(ID_MONITOR_XPREG);
40:
41: // I/O モニター
42: io_monitor.func = ToMonitorCallback(&MPU64180Device::MonitorUpdateIO);
43: io_monitor.SetSize(55, 20);
44: io_monitor.Regist(ID_MONITOR_XPIO);
45:
46: // 割り込みモニター
47: intr_monitor.func = ToMonitorCallback(&MPU64180Device::MonitorUpdateIntr);
48: intr_monitor.SetSize(64, 18);
49: intr_monitor.Regist(ID_MONITOR_XPINTR);
50: }
51:
52: // デストラクタ
53: MPU64180Device::~MPU64180Device()
54: {
55: }
56:
57: // ログ表示を差し替える。
58: // こっちでは XP の PC を表示したい。
59: void
60: MPU64180Device::putlogn(const char *fmt, ...) const
61: {
62: char buf[1024];
63: va_list ap;
64: uint64 vt;
65: int len;
66:
67: vt = scheduler->GetVirtTime();
68: len = snprintf(buf, sizeof(buf), "%4d.%03d'%03d'%03d %04x %s ",
69: (int)(vt / 1000 / 1000 / 1000),
70: (int)((vt / 1000 / 1000) % 1000),
71: (int)((vt / 1000) % 1000),
72: (int)(vt % 1000),
73: GetPPC(),
74: GetName().c_str());
75:
76: va_start(ap, fmt);
77: vsnprintf(buf + len, sizeof(buf) - len, fmt, ap);
78: va_end(ap);
79:
80: WriteLog(buf);
81: }
82:
83: // 初期化
84: bool
85: MPU64180Device::Init()
86: {
87: if (inherited::Init() == false) {
88: return false;
89: }
90:
91: debugger = GetDebugger();
92: pio0 = GetPIO0Device();
93: xpbus = GetXPbusDevice();
94:
95: scheduler->ConnectMessage(MessageID::MPU_TRACE_XP, this,
96: ToMessageCallback(&MPU64180Device::TraceMessage));
97:
98: return true;
99: }
100:
101: // こっちは本体リセットと電源オン
102: void
103: MPU64180Device::ResetHard(bool poweron)
104: {
105: if (poweron) {
106: used_cycle = 0;
107:
108: // 履歴は電源オン時だけ初期化。
109: exhist.Clear();
110: brhist.Clear();
111: }
112:
113: // リセット信号でさすがにリセットされるよな?
114: Reset();
115: }
116:
117: // RESET 信号を変化させる。
118: // new_reset が true ならアサート、false ならネゲート。
119: void
120: MPU64180Device::ChangeRESET(bool new_reset)
121: {
122: if (new_reset) {
123: AssertRESET();
124: } else {
125: NegateRESET();
126: }
127: }
128:
129: // RESET 信号をアサートする。
130: void
131: MPU64180Device::AssertRESET()
132: {
133: if (opmode != OpMode::Reset) {
134: Reset();
135: }
136: }
137:
138: // RESET 信号をネゲートする。
139: void
140: MPU64180Device::NegateRESET()
141: {
142: if (opmode == OpMode::Reset) {
143: // リセット解除がタイマーの開始時刻
144: timer_epoch = scheduler->GetVirtTime();
145:
146: EnterNormal();
147: }
148: }
149:
150: // プロセッサをリセット
151: // (どうすべ)
152: void
153: MPU64180Device::Reset()
154: {
155: putlog(1, "RESET");
156: opmode = OpMode::Reset;
157:
158: // 命令実行サイクルを停止
159: exec_event.SetName("XP(HD647180) Reset");
160: scheduler->StopEvent(exec_event);
161:
162: // 汎用レジスタの値は不定。目印として $cc で埋めておく。
163: memset((void *)®, 0xcc, sizeof(reg));
164: reg.f.Set(0xcc);
165: reg.ex.f.Set(0xcc);
166:
167: ppc = 0;
168: reg.pc = 0;
169: reg.sp = 0;
170: reg_i = 0;
171: reg_r = 0;
172: ief1 = false;
173: ief2 = false;
174: int0mode = 0;
175:
176: for (int i = 0; i < timer.size(); i++) {
177: timer[i].reload = 0xffff;
178: timer[i].count = 0xffff;
179: timer[i].running = false;
180: timer[i].intr_enable = false;
181: timer[i].tif = 0;
182: // 一時レジスタの初期値は定義されてないがとりあえず
183: timer[i].tmpcount = 0xffff;
184: }
185: MakeActiveTimer();
186: timer_toc = 0;
187: scheduler->StopEvent(timer_event);
188:
189: memwait = 3;
190: iowait = 4;
191: dcntl = 0;
192: itc_trap = false;
193: itc_ufo = false;
194: itc_ite0 = true; // ITE0 は初期値 1
195: itc_ite1 = false;
196: itc_ite2 = false;
197: rcr = 0xfc;
198: commbase = 0;
199: bankbase = 0;
200: commarea = 0xf000;
201: bankarea = 0x0000;
202: io_address = 0x00;
203:
204: memset(&int_counter, 0, sizeof(int_counter));
205:
206: xpbus->SetRMCR(0xf0);
207: }
208:
209: // ノーマルモードに入る。
210: void
211: MPU64180Device::EnterNormal()
212: {
213: putlog(1, "Normal mode");
214: opmode = OpMode::Normal;
215:
216: SetExec(ToEventCallback(&MPU64180Device::ExecNormal));
217: exec_event.SetName("XP(HD647180) Execute");
218:
1.1.1.2 root 219: SetTrace(debugger->IsTrace());
220:
1.1 root 221: // 3 クロック後から通常サイクル開始? (HD647180.pdf, p23)
222: exec_event.time = 3 * clock_nsec;
223: scheduler->RestartEvent(exec_event);
224: }
225:
226: // スリープモードに入る。(SLP 命令から呼ばれる)
227: void
228: MPU64180Device::EnterSleep()
229: {
230: // ExecNormal() 内で StartEvent() をしないに分岐するのは無駄が多いので、
231: // ここでは一旦コールバックだけ差し替えて、イベントを停止する。
232: SetExec(ToEventCallback(&MPU64180Device::ExecSleep));
233: exec_event.SetName("XP(HD647180) Sleep");
234: exec_event.time = 0;
235:
236: // 内蔵デバイスのイベントも停止する。
237: scheduler->StopEvent(timer_event);
238: }
239:
240: // スリープモードから抜ける。
241: void
242: MPU64180Device::EnterWakeup()
243: {
244: // 復帰処理のため一旦 Wakeup へ。
245: SetExec(ToEventCallback(&MPU64180Device::ExecWakeup));
246: exec_event.time = 3 * clock_nsec;
247:
248: scheduler->StartEvent(exec_event);
249: }
250:
251: // スリープに入る時のイベント
252: void
253: MPU64180Device::ExecSleep(Event& ev)
254: {
255: // 実行イベントを停止する。(StartEvent() を呼ばない)
256: }
257:
258: // スリープから復帰するイベント
259: void
260: MPU64180Device::ExecWakeup(Event& ev)
261: {
262: // EI なら、割り込み処理から再開。
263: // DI なら、次の命令から再開。
264: if (GetIEF1()) {
265: DoInterrupt();
266: }
267:
268: EnterNormal();
269: }
270:
271: // MPU トレース状態設定要求メッセージ
272: void
273: MPU64180Device::TraceMessage(MessageID msgid, uint32 arg)
274: {
275: // リセット中は SetTrace しない
276: if (opmode == OpMode::Reset) {
277: return;
278: }
279:
280: // デバッガから MPU のトレース状態を設定してくれと言われた
281: SetTrace((bool)arg);
282: }
283:
284: // トレース実行
285: void
286: MPU64180Device::ExecTrace(Event& ev)
287: {
288: debugger->ExecXP();
289: (this->*exec_func)(ev);
290: }
291:
292: // 実行コールバックを設定する。
293: void
294: MPU64180Device::SetExec(EventCallback_t new_exec)
295: {
296: exec_func = new_exec;
297:
298: if (exec_event.func != ToEventCallback(&MPU64180Device::ExecTrace)) {
299: exec_event.func = exec_func;
300: }
301: }
302:
303: // トレース状態を設定する。
304: void
305: MPU64180Device::SetTrace(bool enable)
306: {
307: EventCallback_t func;
308:
309: if (enable) {
310: func = ToEventCallback(&MPU64180Device::ExecTrace);
311: } else {
312: func = exec_func;
313: }
314: exec_event.func = func;
315: }
316:
317: // IX/IY 分を考慮したサイクル数
318: void
319: MPU64180Device::CYCLE_IX(int cycle_hl, int cycle_ix)
320: {
321: if (__predict_true(ixiy == USE_HL)) {
322: used_cycle += cycle_hl;
323: } else {
324: used_cycle += cycle_ix;
325: }
326: }
327:
328: uint32
329: MPU64180Device::Fetch8()
330: {
331: uint8 data = Read8(reg.pc++);
332: ops.push_back(data);
333: if (__predict_false(reg.pc > 0xffff)) {
334: reg.pc = 0;
335: }
336: return data;
337: }
338:
339: uint32
340: MPU64180Device::Read8(uint32 laddr)
341: {
342: uint32 paddr = Translate(laddr);
343: // XXX 内蔵 RAM はノーウェイト
344: CYCLE(memwait);
345: return xpbus->Read8(paddr);
346: }
347:
348: uint32
349: MPU64180Device::Write8(uint32 laddr, uint32 data)
350: {
351: uint32 paddr = Translate(laddr);
352: // XXX 内蔵 RAM はノーウェイト
353: CYCLE(memwait);
354: return xpbus->Write8(paddr, data);
355: }
356:
357: uint32
358: MPU64180Device::Peek8(uint32 laddr) const
359: {
360: uint32 addr = TranslatePeek(laddr);
361: return xpbus->Peek8(addr);
362: }
363:
364: uint32
365: MPU64180Device::Peek16(uint32 laddr) const
366: {
367: uint32 data;
368: data = Peek8(laddr);
369: data |= Peek8(laddr + 1) << 8;
370: return data;
371: }
372:
373: // アドレス変換
374: uint32
375: MPU64180Device::Translate(uint32 laddr) const
376: {
377: uint32 paddr;
378:
379: paddr = TranslatePeek(laddr);
380: putlog(3, "Translate: $%04x -> $%05x", laddr, paddr);
381: return paddr;
382: }
383:
384: // アドレス変換 (デバッガ用)
1.1.1.3 ! root 385: busaddr
1.1 root 386: MPU64180Device::TranslatePeek(uint32 laddr) const
387: {
388: uint32 paddr;
389:
390: // 論理アドレスから物理アドレスへの変換
391: if (laddr >= commarea) {
392: // Common Area 1
393: paddr = commbase + laddr;
394: } else if (laddr >= bankarea) {
395: // Bank Area
396: paddr = bankbase + laddr;
397: } else {
398: // Common Area 0
399: paddr = laddr;
400: }
401:
402: return paddr;
403: }
404:
405: // MSXDOS エミュレーションのコールバックを指定
406: void
407: MPU64180Device::SetSYSCALLCallback(void (*callback)(void *), void *arg)
408: {
409: syscall_callback = callback;
410: syscall_arg = arg;
411: }
412:
413: void
414: MPU64180Device::MonitorUpdateReg(Monitor *, TextScreen& screen)
415: {
416: screen.Clear();
417:
418: // 0 1 2 3
419: // 012345678901234567890123456789012345678
420: // AF:00 00 (------) PC:8000 AF':00 00
421: // BC:00 00 SP:8000 BC':00 00
422: // DE:00 00 IX:0000 I:00 DE':00 00
423: // HL:00 00 IY:0000 R:00 HL':00 00
424: //
425: // Operation Mode: Normal
426:
427: hd64180reg tmp = reg;
428:
429: screen.Print(0, 0, "AF:%02x %02x", tmp.a, tmp.f.Get());
430: screen.Print(0, 1, "BC:%02x %02x", tmp.b, tmp.c);
431: screen.Print(0, 2, "DE:%02x %02x", tmp.d, tmp.e);
432: screen.Print(0, 3, "HL:%02x %02x", tmp.h, tmp.l);
433:
434: screen.Print(9, 0, "(%c%c%c%c%c%c)",
435: tmp.f.IsS() ? 'S' : '-',
436: tmp.f.IsZ() ? 'Z' : '-',
437: tmp.f.IsH() ? 'H' : '-',
438: tmp.f.IsV() ? 'V' : (tmp.f.IsP() ? 'P' : '-'),
439: tmp.f.IsN() ? 'N' : '-',
440: tmp.f.IsC() ? 'C' : '-');
441: screen.Print(10, 2, "IX:%04x", tmp.ix);
442: screen.Print(10, 3, "IY:%04x", tmp.iy);
443:
444: screen.Print(19, 0, "PC:%04x", tmp.pc);
445: screen.Print(19, 1, "SP:%04x", tmp.sp);
446: screen.Print(20, 2, "I:%02x", reg_i);
447: screen.Print(20, 3, "R:%02x", GetR());
448:
449: screen.Print(29, 0, "AF':%02x %02x", tmp.ex.a, tmp.ex.f.Get());
450: screen.Print(29, 1, "BC':%02x %02x", tmp.ex.b, tmp.ex.c);
451: screen.Print(29, 2, "DE':%02x %02x", tmp.ex.d, tmp.ex.e);
452: screen.Print(29, 3, "HL':%02x %02x", tmp.ex.h, tmp.ex.l);
453:
454: screen.Puts(0, 5, "Operation Mode:");
455: screen.Puts(16, 5, (opmode == OpMode::Reset) ? TA::On : TA::Normal,
456: opmode_names[(int)opmode]);
457: }
458:
459: /*static*/ const char * const
460: MPU64180Device::opmode_names[] = {
461: "Reset",
462: "Normal",
463: "Halt",
464: "Sleep",
465: };
466:
467: void
468: MPU64180Device::MonitorUpdateIO(Monitor *, TextScreen& screen)
469: {
470: int y;
471: uint32 val;
472:
473: screen.Clear();
474:
475: /*
476: 0 1 2 3 4 5 6 7
477: 01234567890123456789012345678901234567890123456789012345678901234567890123456789
478: <MMU>
479: 38H CBR = $20: Common Base Addr = $20000
480: 39H BBR = $00: Bank Base Addr = $00000
481: 3AH CBAR = $83: Common Area = $8000
482: Bank Area = $3000
483: 51H RMCR = $00: RAM Address = $00000
484: */
485: y = 0;
486: screen.Puts(0, y++, "<DMA and wait>");
487: val = PeekDCNTL();
488: screen.Print(0, y, "32H DCNTL= $%02x:", val);
489: screen.Print(16, y, "MemWait=%d", memwait);
490: screen.Print(26, y, "IOWait=%d", iowait);
491: y++;
492:
493: screen.Puts(0, y++, "<Interrupt Control>");
494: screen.Print(0, y++, "33H IL = $%02x", intvec_low);
495:
496: val = PeekITC();
497: screen.Print(0, y, "34H ITC = $%02x:", val);
498: static const char * const itc_str[] = {
499: "TRAP", "UFO", "----", "----", "----", "ITE2", "ITE1", "ITE0"
500: };
501: for (int i = 0; i < 8; i++) {
502: screen.Puts(16 + 5 * i, y,
503: TA::OnOff(val & (1U << (7 - i))), itc_str[i]);
504: }
505: y++;
506: val = PeekRCR();
507: screen.Print(0, y, "36H RCR = $%02x:", val);
508: static const char * const rcr_str[] = {
509: "REFE", "REFW", "----", "----", "----", "----"
510: };
511: for (int i = 0; i < countof(rcr_str); i++) {
512: screen.Puts(16 + 5 * i, y,
513: TA::OnOff(val & (1U << (7 - i))), rcr_str[i]);
514: }
515: screen.Print(46, y, "RefCyc=%d", (1 << (val & 3)) * 10);
516: y++;
517:
518: y++;
519: screen.Puts(0, y++, "<Timer>");
520: val = PeekTCR();
521: screen.Print(0, y, "10H TCR = $%02x:", val);
522: static const char * const tcr_str[] = {
523: "TIF1", "TIF0", "TIE1", "TIE0", "", "", "TDE1", "TDE0"
524: };
525: for (int i = 0; i < 8; i++) {
526: screen.Puts(16 + 5 * i, y,
527: TA::OnOff(val & (1U << (7 - i))), tcr_str[i]);
528: }
529: screen.Print(16 + 5 * 4, y, "TOC=$%d", timer_toc);
530: y++;
531: for (int ch = 0; ch < timer.size(); ch++) {
532: auto& t = timer[ch];
533: uint64 reload_nsec = t.reload * clock_nsec * 20;
534: screen.Print(4, y++, "Timer%d: Count=$%04x Reload=$%04x(%6u.%03u usec)",
535: ch, t.count, t.reload,
536: (uint)(reload_nsec / 1000),
537: (uint)(reload_nsec % 1000));
538: }
539: screen.Print(0, y++, "18H FRC = $%02x", PeekFRC());
540:
541: y++;
542: screen.Puts(0, y++, "<Memory Control>");
543: screen.Print(0, y++, "38H CBR = $%02x: Common Base Addr = $%05x",
544: PeekCBR(), commbase);
545: screen.Print(0, y++, "39H BBR = $%02x: Bank Base Addr = $%05x",
546: PeekBBR(), bankbase);
547: screen.Print(0, y++, "3AH CBAR = $%02x: Common Area = $%04x",
548: PeekCBAR(), commarea);
549: screen.Print(18, y++, "Bank Area = $%04x", bankarea);
550:
551: screen.Print(0, y++, "3FH ICR = $%02x: I/O Address = $%02x",
552: PeekICR(), io_address);
553: screen.Print(0, y++, "51H RMCR = $%02x: RAM Address = $%05x",
554: PeekRMCR(), xpbus->GetXPRAMAddr());
555: }
556:
557: void
558: MPU64180Device::MonitorUpdateIntr(Monitor *, TextScreen& screen)
559: {
560: static const struct {
561: uint offset;
562: const char *flagname;
563: } table[] = {
564: { 0x00, "" }, // TRAP
565: { 0x66, "" }, // NMI
566: { 0, "ITC:ITE0" }, // INT0
567: { 0x00, "ITC:ITE1" }, // INT1
568: { 0x02, "ITC:ITE2" }, // INT2
569: { 0x12, "" }, // Input Capture
570: { 0x14, "" }, // Output Compare
571: { 0x16, "" }, // Timer Overflow
572: { 0x04, "TCR:TIE0" }, // Timer0
573: { 0x06, "TCR:TIE1" }, // Timer1
574: { 0x08, "" }, // DMA0
575: { 0x0a, "" }, // DMA1
576: { 0x0c, "" }, // CSIO
577: { 0x0e, "" }, // ASCI0
578: { 0x10, "" }, // ASCI1
579: };
580: struct {
581: bool enable; // この割り込みが有効か
582: uint32 vecaddr; // ベクタが格納されているアドレス
583: uint16 handler; // ハンドラのアドレス
584: } data[countof(table)];
585: int x, y;
586:
587: // 割り込み有効制御ビットは各地に散らばってるのでここで集める。
588: uint32 itc = PeekITC();
589: uint32 tcr = PeekTCR();
590: data[HD647180::IntmapINT0].enable = (itc & 0x01);
591: data[HD647180::IntmapINT1].enable = (itc & 0x02);
592: data[HD647180::IntmapINT2].enable = (itc & 0x04);
593: data[HD647180::IntmapTimer0].enable = (tcr & 0x10);
594: data[HD647180::IntmapTimer1].enable = (tcr & 0x20);
595:
596: // ベクタを集める。
597: // INT1 (Pri=3) 以降が I + IL + ベクタ方式。
598: // 0(TRAP), 1(NMI), 2(INT0) は個別処理。
599: uint32 intvec = (reg_i << 8) + intvec_low;
600: for (int i = 3; i < countof(data); i++) {
601: data[i].vecaddr = intvec + table[i].offset;
602: data[i].handler = Peek16(data[i].vecaddr);
603: }
604:
605: screen.Clear();
606:
607: screen.Puts(0, 0, "Mask:");
608: screen.Puts(6, 0, TA::OnOff(GetIEF1()), "IEF1");
609: screen.Putc(11, 0, '(');
610: screen.Puts(12, 0, TA::OnOff(GetIEF2()), "IEF2");
611: screen.Putc(16, 0, ')');
612: screen.Print(26, 0, "INT0 Mode: %d", int0mode);
613:
614: // 0 1 2 3 4 5 6
615: // 0123456789012345678901234567890123456789012345678901234567890123
616: // Pri Name Enable Vec Count
617: // 00 InputCapture ITC:ITE0 0000H(0000H)01234567890123456789012345
618:
619: y = 2; // 012345678901234567890123456789012345
620: screen.Puts(0, y, "Pri Name Enable Vector");
621: screen.Puts(59, y, "Count");
622: y++;
623:
624: // TRAP, NMI だけ別処理
625: screen.Puts(3, y, InterruptName[0]);
626: screen.Puts(3, y + 1, TA::Disable, "NMI (NotConn)");
627:
628: // INT0 以降はレベルトリガー & マスク可能なので現在の状態を表示
629: for (int i = 2; i < 15; i++) {
630: screen.Puts(3, y + i, TA::OnOff(intmap & (1U << (31 - i))),
631: InterruptName[i]);
632: screen.Puts(17, y + i, TA::OnOff(data[i].enable), table[i].flagname);
633: }
634:
635: // ベクタ
636: x = 26;
637: screen.Puts(x, y + 0, "0000H");
638: screen.Puts(x, y + 1, "0066H");
639: if (int0mode == 1) {
640: screen.Puts(x, y + 2, "0038H");
641: } else {
642: // Mode0 はデータバスから命令自体を読み込む方式。
643: // Mode1 はデータバスからベクタの下位アドレスを読み込む方式。
644: // どちらもサポートしていない。
645: screen.Print(x, y + 2, "(IM%d)", int0mode);
646: }
647: for (int i = 3; i < 15; i++) {
648: screen.Print(x, y + i, "%04XH(%04XH)",
649: data[i].handler, data[i].vecaddr);
650: }
651:
652: // カウンタ
653: x = 38;
654: for (int i = 0; i < 15; i++) {
655: screen.Print(0, y + i, "%2d", i);
656: screen.Print(x, y + i, "%26s", format_number(int_counter[i]).c_str());
657: }
658: }
659:
660: /*static*/ std::vector<const char *>
661: MPU64180Device::InterruptName = {
662: "TRAP",
663: "NMI",
664: "INT0(PIO1)",
665: "INT1",
666: "INT2",
667: "InputCapture",
668: "OutputCompare",
669: "TimerOverflow",
670: "Timer 0",
671: "Timer 1",
672: "DMA 0",
673: "DMA 1",
674: "CSIO",
675: "ASCI 0",
676: "ASCI 1",
677: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.