|
|
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: #include "m88100.h"
8: #include "m88100excep.h"
9: #include "debugger.h"
10: #include "cvprompt.h"
11: #include "mpu.h"
12:
13: #define OP_DEF(name) void __CONCAT(m88kcpu::op_,name)()
14: #define OP_FUNC(name) __CONCAT(op_,name)()
15: #include "m88100ops.cpp"
16:
17: // リセット例外のようなもの?
18: void
1.1.1.5 ! root 19: m88kcpu::Reset()
1.1 root 20: {
1.1.1.4 root 21: const uint32 Undefined = 0xcccccccc;
22:
1.1.1.5 ! root 23: // 内部の割り込み状態をクリア
! 24: intr_pending = 0;
! 25:
1.1.1.4 root 26: // 履歴を初期化。リセット前の履歴いらんよな?
27: exhist.Clear();
28: brhist.Clear();
29:
30: // Table 6-8 (p6-47)
1.1 root 31: psr = 0x800003ff;
1.1.1.4 root 32: // XXX ただし SFU1 は未実装なので下げておく
1.1 root 33: psr &= ~PSR_SFD1;
34:
1.1.1.2 root 35: #if defined(TEST_INTERRUPT)
1.1.1.4 root 36: psr &= ~PSR_IND;
37: psr &= ~PSR_SFRZ;
1.1 root 38: #endif
39:
1.1.1.4 root 40: SetPSR();
1.1.1.2 root 41:
1.1.1.4 root 42: epsr = Undefined;
43: // scoreboard: cleared
44: ssbr = Undefined;
1.1.1.2 root 45:
1.1 root 46: // 実際には m88k MPU はリセット直後は 0 番地から実行を開始するので、
47: // 大抵の場合システムでリセット直後は 0 番地付近に ROM を見せるように
48: // してあるのだと思うが、ここではその代わりに ROM のアドレスを指定して、
49: // 直接そこから実行を開始することにする。
50: fip = reset_vector;
51: nip = 0;
52: xip = 0;
1.1.1.4 root 53: sfip = Undefined;
54: snip = Undefined;
55: sxip = Undefined;
56:
57: vbr = 0;
58: // DMTx は bit0(Valid) をクリア。他は不定。
59: dmt0 &= ~DM_VALID;
60: dmt1 &= ~DM_VALID;
61: dmt2 &= ~DM_VALID;
62: dma0 = Undefined;
63: dma1 = Undefined;
64: dma2 = Undefined;
65: dmd0 = Undefined;
66: dmd1 = Undefined;
67: dmd2 = Undefined;
68: for (int i = 1; i < 32; i++) {
69: r[i] = Undefined;
70: }
71:
72: fpecr = 0;
73: for (int i = 1; i < 9; i++) {
74: fcr[i] = Undefined;
75: }
76: fpcr = 0;
77: fpsr = 0;
78:
79: cmmu[0].Reset();
80: cmmu[1].Reset();
81:
82: // 履歴を初期化
83: exhist.Clear();
84: brhist.Clear();
85:
86: // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
87: // リセット例外発生時の from にはあまり意味がないので 0 にするか。
1.1.1.5 ! root 88: exhist.AddEntry(0 | 1, 0, 0xfc000000 | 0);
! 89: brhist.AddEntry(0 | 1, 0, 0xfc000000 | 0);
1.1.1.4 root 90: // デバッガに通知 (例外ブレーク用)
91: debugger_notify_exception(0);
1.1 root 92:
93: fetch();
94: }
95:
1.1.1.5 ! root 96: // 仮想時間 delta [nsec] だけ CPU を実行する。実際には大抵行き過ぎる。
! 97: // 戻り値はフラグ。
! 98: //
! 99: // 戻る時に仮想時刻を確定させなくても構わない。スケジューラはこれを呼び出した
! 100: // 後に必然的に仮想時刻を取得することになるため、そこで確定させられる。
1.1 root 101: uint32
1.1.1.5 ! root 102: m88kcpu::Run(uint32 delta)
1.1 root 103: {
104: bool is_release = false;
105: bool is_intr = false;
106:
1.1.1.5 ! root 107: assert(used_cycle == 0);
! 108:
! 109: goal_cycle = Vtime2Cycle(delta);
! 110: if (__predict_false(goal_cycle == 0)) {
! 111: goal_cycle = 1;
! 112: }
! 113:
1.1.1.2 root 114: if ((atomic_reqflag & CPU_REQ_STOP)) {
1.1.1.5 ! root 115: if ((atomic_reqflag & (CPU_REQ_INTR | CPU_REQ_PROMPT | CPU_REQ_RESET))) {
! 116: // 割り込み、デバッガ、リセットが来たら STOP 解除。
! 117: atomic_reqflag &= ~CPU_REQ_STOP;
! 118: } else {
1.1.1.2 root 119: // STOP 継続として戻る。
1.1.1.5 ! root 120: used_cycle = goal_cycle;
1.1.1.2 root 121: return atomic_reqflag;
122: }
123: }
124:
1.1 root 125: while (!is_release) {
126: // shift pipeline
127: opX = opF;
128: xip = nip;
129:
130: // prefetch next inst
131: fetch();
132:
133: // この時点で実行しようとする命令が opX に入っている
134:
135: if (atomic_reqflag != 0) {
136: // 何かが起きた
137:
1.1.1.5 ! root 138: if ((atomic_reqflag & CPU_REQ_RESET)) {
! 139: // リセット例外。
! 140: atomic_reqflag &= ~CPU_REQ_RESET;
! 141: Reset();
! 142: // XIP がリセットベクタを指すためにはもう一回フェッチが必要
! 143: // なので、ここは continue が必要。
! 144: continue;
! 145: }
! 146:
1.1 root 147: // 割り込みの認識
148: // 割り込みの実行は命令実行完了後、次のプリフェッチの前
149: if ((atomic_reqflag & CPU_REQ_INTR)) {
150: atomic_reqflag &= ~CPU_REQ_INTR;
1.1.1.5 ! root 151: if (intr_pending && IsIntrEnable()) {
! 152: // この命令のあとに割り込み処理
! 153: is_intr = true;
! 154: }
1.1.1.2 root 155: #if defined(TEST_INTERRUPT)
1.1.1.4 root 156: atomic_reqflag |= CPU_REQ_PROMPT;
1.1 root 157: #endif
158: }
159:
160: if ((atomic_reqflag & CPU_REQ_TRACE)) {
161: // デバッガプロンプトを出すかどうかを判断する
162: //inst_count++;
163: if (debugger_check()) {
164: atomic_reqflag |= CPU_REQ_PROMPT;
165: }
166: }
167:
168: if ((atomic_reqflag & CPU_REQ_PROMPT)) {
169: // デバッガプロンプトのための実行停止
170:
171: // プロンプト獲得を通知
172: gCVPrompt->NotifyAcquire();
173:
174: // プロンプト解放を待機
175: gCVPrompt->WaitRelease();
176:
177: // プロンプトを抜けたのでここでフラグを落とす
178: atomic_reqflag &= ~CPU_REQ_PROMPT;
1.1.1.4 root 179:
180: // プロンプトから戻ってきたら一旦スケジューラまで戻る(?)。
181: // reset コマンドのようにデバッガからスケジューラに出した
182: // リクエストを(なるはやで?)回収するため。
183: atomic_reqflag |= CPU_REQ_RELEASE;
1.1 root 184: }
185:
186: if ((atomic_reqflag & CPU_REQ_RELEASE)) {
187: // 中断リクエスト受信
188: // この命令を実行し終わったら抜ける
189: atomic_reqflag &= ~CPU_REQ_RELEASE;
190: is_release = true;
191: }
192: }
193:
194: if (OpIsBusErr(opX)) {
195: // プリフェッチしていた命令がバスエラー
196: Exception(M88K_EXCEP_INST);
197: continue;
198: }
199:
200: uint32 op12 = op32_to_12(opX);
201:
202: switch (op12) {
203: #include "m88100switch.inc"
204: default:
1.1.1.2 root 205: OP_FUNC(illegal);
206: break;
1.1 root 207: }
208:
1.1.1.5 ! root 209: if (is_intr && IsIntrEnable()) {
! 210: // 割り込み処理要求が来ていて、直前の命令が割り込みを禁止していなければ割り込み
! 211: // XXX 実際は違う気がする
! 212: is_intr = false;
! 213: gMPU->putlog(4, "INTR take");
! 214: ExceptionCore(M88K_EXCEP_INTERRUPT, ExceptionKind::INTR);
! 215: continue;
1.1 root 216: }
217:
1.1.1.5 ! root 218: used_cycle++;
1.1.1.2 root 219:
220: if ((atomic_reqflag & CPU_REQ_STOP)) {
221: // STOP 状態は、残りサイクルを消費したことにして戻る
222: gMPU->putlog(2, "STOP 状態検出");
1.1.1.5 ! root 223: used_cycle = goal_cycle;
1.1.1.2 root 224: is_release = true;
225: }
226:
1.1.1.5 ! root 227: if (used_cycle >= goal_cycle)
1.1 root 228: break;
229: }
230:
231: return atomic_reqflag;
232: }
233:
1.1.1.5 ! root 234: // 仕掛中のサイクルを仮想時刻に反映させる
! 235: void
! 236: m88kcpu::UpdateVTime()
! 237: {
! 238: goal_cycle -= used_cycle;
! 239: total_vtime += Cycle2Vtime(used_cycle);
! 240: used_cycle = 0;
! 241: }
! 242:
! 243: // 仮想時刻 [nsec] を取得する
! 244: uint64
! 245: m88kcpu::GetVTime()
! 246: {
! 247: if (used_cycle != 0) {
! 248: UpdateVTime();
! 249: }
! 250: return total_vtime;
! 251: }
! 252:
1.1 root 253: // この命令後に実行を中断して VM に戻る
254: void
255: m88kcpu::Release()
256: {
257: atomic_reqflag |= CPU_REQ_RELEASE;
258: }
259:
1.1.1.5 ! root 260: // リセット例外を発行する
! 261: void
! 262: m88kcpu::RequestReset()
! 263: {
! 264: atomic_reqflag |= CPU_REQ_RESET;
! 265: }
! 266:
1.1 root 267: // 通常の例外。
1.1.1.2 root 268: // DataAccessException, エラー、割り込み、TRAP からは使わない。
1.1 root 269: // vec はベクタ番号。
270: void
271: m88kcpu::Exception(int vec)
272: {
273: ExceptionCore(vec, ExceptionKind::NORMAL);
274: }
275:
276: // 例外処理コア部分
277: // vec はベクタ番号。
278: void
279: m88kcpu::ExceptionCore(int vec, ExceptionKind cause)
280: {
1.1.1.4 root 281: uint syscall = 0;
282:
1.1.1.3 root 283: // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
1.1.1.4 root 284: // OpenBSD システムコールなら番号もついでに記録。
285: // システムコール番号は tb0 発行時点で r13 にセットされている。
286: // ただし 0 番は syscall(2) (間接システムコール) で、実際の番号は r2。
287: // 198番 __syscall(2) だと r2:r3 (なので実質 r3)。
288: if (vec == 450) {
289: syscall = r[13];
290: if (syscall == 0) {
291: syscall = r[2];
292: } else if (syscall == 198) {
293: syscall = r[3];
294: }
295: syscall &= 0xfff;
296: }
1.1.1.5 ! root 297: uint32 from = xip | (IsSuper() ? 1 : 0);
! 298: uint32 inst = 0xfc000000 | (syscall << 12) | vec;
! 299: exhist.AddEntry(from, 0, inst);
! 300: brhist.AddEntry(from, 0, inst);
1.1.1.3 root 301: // デバッガに通知 (例外ブレーク用)
1.1.1.4 root 302: debugger_notify_exception(vec);
303:
304: // 命令中で起きる例外はとりあえず全部2クロック追加? (Table.7-5)
305: if (cause != ExceptionKind::INTR) {
1.1.1.5 ! root 306: AddCycle(2);
1.1.1.4 root 307: }
1.1.1.3 root 308:
1.1 root 309: if (psr & PSR_SFRZ) {
310: if (cause != ExceptionKind::TRAP) {
311: gMPU->putlog(2, "SFRZ Error Exception");
312: vec = M88K_EXCEP_ERROR;
313: cause = ExceptionKind::ERROR;
314: }
315: } else {
316: gMPU->putlog(2, "Exception %x", vec);
1.1.1.2 root 317: if (vec != 1) {
318: gMPU->putlog(1, "Exception %x x=%x n=%x f=%x", vec, xip, nip, fip);
319: }
1.1 root 320: sxip = xip;
321: snip = nip;
322: sfip = fip;
323:
324: sxip |= SIP_V;
325: snip |= SIP_V;
1.1.1.4 root 326: sfip |= SIP_V;
1.1 root 327:
328: if (OpIsBusErr(opX)) {
329: sxip |= SIP_E; // E bit
330: }
331: if (OpIsBusErr(opF)) {
332: snip |= SIP_E; // E bit
333: }
334:
335: if (vec == M88K_EXCEP_UNIMPL_OP) {
336: sxip &= ~SIP_V;
337: }
338: }
339:
1.1.1.2 root 340: if (cause != ExceptionKind::DATA) {
341: // データアクセス例外以外は、
342: // データパイプラインはフラッシュ状態とする。
343: // ほんとうは違う。かも。
344: dmt0 = 0;
345: } else {
346: // xip: ld/st/xmem
347: // nip: next
348: // fip: fetch
349:
350: // rom1.2 は DAE が起きると、
351:
352: // ROM挙動 CPU の RTE 挙動
353: // sfip = sxip
354: // sxip -> ~V (INVALID) CPU はそもそも sxip を評価しない
355: // snip = 0 (INVALID) CPU はこれを nop 相当に処理する
356: // rte
357:
358: // してくる。そのまま読むと命令を再実行しようとしているように
359: // 読めるが、再実行すると無限ループになってしまう。
360: // DAE の時点で xip が進んでいることを期待して利用している?
361:
362: // openbsd は
363: // xmem の write バスエラーを検知すると命令を再実行するために
364: // fip = nip, nip = xip して rte する。ということは xip = xmem に
365: // なっていることを期待している。
366:
367: // というわけで
368: // ld で DataAccessException のときは、実CPUはすでに次の命令を
369: // 実行している?
370: // もう少し考えると、スコアボードでレジスタの干渉がなければ、
371: // 実行している?
372:
373: // 仕方がないのでハックでごまかす。
374: // rte のほうも参照のこと。
375:
376: if (IsSuper() && xip >= 0x41000000 && xip < 0x42000000) {
377: // 遅延例外機構がないので、ROM 合わせのハック。
378: sxip = snip;
379: }
380: }
381:
1.1 root 382: epsr = psr;
1.1.1.2 root 383: SetPSR(psr | PSR_SUPER | PSR_SFD1 | PSR_IND | PSR_SFRZ);
1.1 root 384:
385: fip = vbr + (vec << 3);
386: fetch();
1.1.1.3 root 387: // ここでベクタに飛ぶのだが、ベクタ内の2命令でおそらく必ずもう一度
388: // 分岐してそっちでも履歴が残るので、ここでブランチ履歴を残すのは
389: // ちょっと冗長という気もする。
390:
1.1 root 391: if (OpIsBusErr(opF)) {
392: if (cause == ExceptionKind::ERROR) {
393: // double bus fault
394: // 実機は無限ループ状態
395: PANIC("Infinite ERROR (double bus fault)");
396: } else {
397: ExceptionCore(M88K_EXCEP_ERROR, ExceptionKind::ERROR);
398: }
399: }
400: }
401:
1.1.1.5 ! root 402: inline
! 403: uint32
! 404: m88kcpu::Calcdmt(uint32 flag)
! 405: {
! 406: return
! 407: ((psr & PSR_BO_LE) ? DM_BO : 0)
! 408: | ((IsSuper() && !(flag & DM_USR)) ? DM_DAS : 0)
! 409: | DM_VALID;
! 410: }
! 411:
1.1 root 412: // データアクセス例外(Read)
1.1.1.5 ! root 413: // ld.b, ld.h, ld.hu, ld 用
1.1 root 414: void
1.1.1.5 ! root 415: m88kcpu::ReadDataException32(uint32 addr, uint32 flag)
1.1 root 416: {
417: // ミスアラインドはこの例外より前にチェックされていて、
418: // ここにはアラインドしかこない。
419: dma0 = addr & ~0x3;
1.1.1.5 ! root 420: dmt0 = Calcdmt(flag)
1.1 root 421: | (FLD_D << 7)
422: | (flag & DM_SIGNED)
423: | ((flag & DM_EN_MASK) >> (addr & 0x03))
1.1.1.5 ! root 424: ;
! 425: dmt1 &= ~DM_VALID;
! 426: dmt2 &= ~DM_VALID;
1.1.1.2 root 427: gMPU->putlog(1, "ReadDAE xip=%x addr=%x", xip, addr);
428: ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA);
1.1 root 429: }
430:
1.1.1.5 ! root 431: // データアクセス例外(Read)
! 432: // ld.d 用
! 433: void
! 434: m88kcpu::ReadDataException64(uint32 addr, uint32 flag)
! 435: {
! 436: // ミスアラインドはこの例外より前にチェックされていて、
! 437: // ここにはアラインドしかこない。
! 438: dma0 = addr;
! 439: dmt0 = Calcdmt(flag)
! 440: | DM_DOUB1 // ダブルワードの第1アクセスのときセット
! 441: | (FLD_D << 7)
! 442: | DM_EN_MASK // 32ビット有効
! 443: ;
! 444:
! 445: dma1 = dma0 + 4;
! 446: dmt1 = Calcdmt(flag)
! 447: | (FLD_D2 << 7)
! 448: | DM_EN_MASK // 32ビット有効
! 449: ;
! 450:
! 451: dmt2 &= ~DM_VALID;
! 452:
! 453: if ((flag & DM_DOUB1) == 0) {
! 454: // .d の2ワード目がバスエラー。第1ワードのアクセスは成功した。
! 455: dmt0 &= ~DM_VALID;
! 456: }
! 457:
! 458: gMPU->putlog(1, "ReadDAE64 xip=%x addr=%x", xip, addr);
! 459: ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA);
! 460: }
! 461:
1.1 root 462: // データアクセス例外(Write)
1.1.1.5 ! root 463: // st.b, st.h, st.hu, st 用
1.1 root 464: void
1.1.1.5 ! root 465: m88kcpu::WriteDataException32(uint32 addr, uint32 flag)
1.1 root 466: {
467: // ミスアラインドはこの例外より前にチェックされていて、
468: // ここにはアラインドしかこない。
469: dma0 = addr & ~0x3;
1.1.1.5 ! root 470: dmt0 = Calcdmt(flag)
1.1 root 471: | (FLD_D << 7)
472: | ((flag & DM_EN_MASK) >> (addr & 0x03))
473: | DM_WRITE
1.1.1.5 ! root 474: ;
! 475: dmd0 = rD;
1.1 root 476:
1.1.1.5 ! root 477: dmt1 &= ~DM_VALID;
! 478: dmt2 &= ~DM_VALID;
1.1.1.2 root 479: gMPU->putlog(1, "WriteDAE xip=%x addr=%x", xip, addr);
480: ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA);
481: }
482:
1.1.1.5 ! root 483: // データアクセス例外(Write)
! 484: // st.d 用
! 485: void
! 486: m88kcpu::WriteDataException64(uint32 addr, uint32 flag)
! 487: {
! 488: // ミスアラインドはこの例外より前にチェックされていて、
! 489: // ここにはアラインドしかこない。
! 490: dma0 = addr & ~0x3;
! 491: dmt0 = Calcdmt(flag)
! 492: | DM_DOUB1 // ダブルワードの第1アクセスのときセット
! 493: | (FLD_D << 7)
! 494: | DM_EN_MASK
! 495: | DM_WRITE
! 496: ;
! 497: dmd0 = rD;
! 498:
! 499: dma1 = dma0 + 4;
! 500: dmt1 = Calcdmt(flag)
! 501: | (FLD_D2 << 7)
! 502: | DM_EN_MASK
! 503: | DM_WRITE
! 504: ;
! 505: dmd1 = rD2;
! 506:
! 507: dmt2 &= ~DM_VALID;
! 508:
! 509: if ((flag & DM_DOUB1) == 0) {
! 510: // .d の2ワード目がバスエラー。第1ワードのアクセスは成功した。
! 511: dmt0 &= ~DM_VALID;
! 512: }
! 513:
! 514: gMPU->putlog(1, "WriteDAE64 xip=%x addr=%x", xip, addr);
! 515: ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA);
! 516: }
! 517:
1.1.1.2 root 518: // データアクセス例外(xmem)
1.1.1.5 ! root 519: // xmem には .d は無い
1.1.1.2 root 520: void
1.1.1.5 ! root 521: m88kcpu::XmemDataException(uint32 addr, uint32 flag)
1.1.1.2 root 522: {
523: dma0 = addr & ~0x3;
1.1.1.5 ! root 524: dmt0 = Calcdmt(flag)
! 525: | DM_LOCK // xmem でセット
1.1.1.2 root 526: | (FLD_D << 7)
527: | (flag & DM_SIGNED)
528: | ((flag & DM_EN_MASK) >> (addr & 0x03))
1.1.1.5 ! root 529: ;
1.1.1.2 root 530:
1.1.1.5 ! root 531: dma1 = dma0;
! 532: dmt1 = Calcdmt(flag)
! 533: | DM_LOCK // xmem でセット
1.1.1.2 root 534: | (FLD_D << 7)
535: | (flag & DM_SIGNED)
536: | ((flag & DM_EN_MASK) >> (addr & 0x03))
537: | DM_WRITE
1.1.1.5 ! root 538: ;
! 539: dmd1 = rD;
1.1.1.2 root 540:
1.1.1.5 ! root 541: dmt2 &= ~DM_VALID;
1.1.1.2 root 542:
543: // ミスアラインドはこの例外より前にチェックされていて、
544: // ここにはアラインドしかこない。
545: if (cmmu[1].fault_code != m88200::FAULT_CODE_BUSERR
546: || cmmu[1].acc_read) {
547: // アドレス変換フォルトまたは read 時点
548: // nop
1.1 root 549: } else {
1.1.1.2 root 550: // xmem の write 時点
1.1.1.5 ! root 551: dmt0 &= ~DM_VALID;
1.1 root 552: }
1.1.1.2 root 553:
554: gMPU->putlog(1, "xmemDAE xip=%x addr=%x", xip, addr);
555: ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA);
1.1 root 556: }
557:
1.1.1.5 ! root 558: // FP 例外
! 559: // fpex: FP例外コード M88K_FPEXCEP
! 560: void
! 561: m88kcpu::FPException(int fpex)
! 562: {
! 563: // FDVZ 以上の例外が PRECISE。
! 564: // 未満の FUNF, FOVF, FINX は IMPRECISE。
! 565: int vec = (fpex >= M88K_FPEXCEP_FDVZ) ? M88K_EXCEP_SFU1_PRECISE : M88K_EXCEP_SFU1_IMPRECISE;
! 566: fpecr = fpex;
! 567:
! 568: Exception(vec);
! 569: }
! 570:
1.1 root 571: void
1.1.1.5 ! root 572: m88kcpu::Interrupt(int level)
1.1 root 573: {
1.1.1.5 ! root 574: // level は 0 か 1。
! 575: intr_pending = level;
1.1 root 576: atomic_reqflag |= CPU_REQ_INTR;
577: }
578:
579: void
580: m88kcpu::fpu_unimpl()
581: {
582: printf("%s\n", __func__);
1.1.1.5 ! root 583: FPException(M88K_FPEXCEP_FUNIMP);
1.1 root 584: }
1.1.1.2 root 585:
586: OP_DEF(illegal)
587: {
588: // 本来は不当命令例外?
589: gMPU->putlog(0, "Illegal instruction %08x", (uint32)opX);
590: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.