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