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