|
|
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.5 root 215: if (used_cycle >= goal_cycle)
1.1 root 216: break;
217: }
218:
1.1.1.6 root 219: exit:
1.1.1.5 root 220: total_vtime += Cycle2Vtime(used_cycle);
221: used_cycle = 0;
222:
1.1.1.7 root 223: // HALT 状態がないので NORMAL か STOP かだけでいい
224: return (atomic_reqflag & CPU_REQ_STOP)
225: ? Scheduler::SCHED_CPU_STOP
226: : Scheduler::SCHED_CPU_NORMAL;
1.1.1.5 root 227: }
228:
1.1 root 229: // この命令後に実行を中断して VM に戻る
230: void
231: m88kcpu::Release()
232: {
233: atomic_reqflag |= CPU_REQ_RELEASE;
234: }
235:
1.1.1.5 root 236: // リセット例外を発行する
237: void
238: m88kcpu::RequestReset()
239: {
240: atomic_reqflag |= CPU_REQ_RESET;
241: }
242:
1.1 root 243: // 通常の例外。
1.1.1.2 root 244: // DataAccessException, エラー、割り込み、TRAP からは使わない。
1.1 root 245: // vec はベクタ番号。
246: void
247: m88kcpu::Exception(int vec)
248: {
249: ExceptionCore(vec, ExceptionKind::NORMAL);
250: }
251:
252: // 例外処理コア部分
253: // vec はベクタ番号。
254: void
255: m88kcpu::ExceptionCore(int vec, ExceptionKind cause)
256: {
1.1.1.4 root 257: uint syscall = 0;
258:
1.1.1.3 root 259: // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
1.1.1.4 root 260: // OpenBSD システムコールなら番号もついでに記録。
261: // システムコール番号は tb0 発行時点で r13 にセットされている。
262: // ただし 0 番は syscall(2) (間接システムコール) で、実際の番号は r2。
263: // 198番 __syscall(2) だと r2:r3 (なので実質 r3)。
264: if (vec == 450) {
265: syscall = r[13];
266: if (syscall == 0) {
267: syscall = r[2];
268: } else if (syscall == 198) {
269: syscall = r[3];
270: }
271: syscall &= 0xfff;
272: }
1.1.1.5 root 273: uint32 from = xip | (IsSuper() ? 1 : 0);
274: uint32 inst = 0xfc000000 | (syscall << 12) | vec;
275: exhist.AddEntry(from, 0, inst);
276: brhist.AddEntry(from, 0, inst);
1.1.1.3 root 277: // デバッガに通知 (例外ブレーク用)
1.1.1.4 root 278: debugger_notify_exception(vec);
279:
280: // 命令中で起きる例外はとりあえず全部2クロック追加? (Table.7-5)
281: if (cause != ExceptionKind::INTR) {
1.1.1.5 root 282: AddCycle(2);
1.1.1.4 root 283: }
1.1.1.3 root 284:
1.1 root 285: if (psr & PSR_SFRZ) {
286: if (cause != ExceptionKind::TRAP) {
1.1.1.7 root 287: cpulog(2, "SFRZ Error Exception");
1.1 root 288: vec = M88K_EXCEP_ERROR;
289: cause = ExceptionKind::ERROR;
290: }
291: } else {
1.1.1.7 root 292: cpulog(2, "Exception %x", vec);
1.1.1.2 root 293: if (vec != 1) {
1.1.1.7 root 294: cpulog(1, "Exception %x x=%x n=%x f=%x", vec, xip, nip, fip);
1.1.1.2 root 295: }
1.1 root 296: sxip = xip;
297: snip = nip;
1.1.1.7 root 298: sfip = fip;
1.1 root 299:
300: sxip |= SIP_V;
301: snip |= SIP_V;
1.1.1.4 root 302: sfip |= SIP_V;
1.1 root 303:
304: if (OpIsBusErr(opX)) {
305: sxip |= SIP_E; // E bit
306: }
307: if (OpIsBusErr(opF)) {
308: snip |= SIP_E; // E bit
309: }
310:
311: if (vec == M88K_EXCEP_UNIMPL_OP) {
312: sxip &= ~SIP_V;
313: }
314: }
315:
1.1.1.2 root 316: if (cause != ExceptionKind::DATA) {
317: // データアクセス例外以外は、
318: // データパイプラインはフラッシュ状態とする。
319: // ほんとうは違う。かも。
320: dmt0 = 0;
321: } else {
322: // xip: ld/st/xmem
323: // nip: next
324: // fip: fetch
325:
326: // rom1.2 は DAE が起きると、
327:
328: // ROM挙動 CPU の RTE 挙動
329: // sfip = sxip
330: // sxip -> ~V (INVALID) CPU はそもそも sxip を評価しない
331: // snip = 0 (INVALID) CPU はこれを nop 相当に処理する
332: // rte
333:
334: // してくる。そのまま読むと命令を再実行しようとしているように
335: // 読めるが、再実行すると無限ループになってしまう。
336: // DAE の時点で xip が進んでいることを期待して利用している?
337:
338: // openbsd は
339: // xmem の write バスエラーを検知すると命令を再実行するために
340: // fip = nip, nip = xip して rte する。ということは xip = xmem に
341: // なっていることを期待している。
342:
343: // というわけで
344: // ld で DataAccessException のときは、実CPUはすでに次の命令を
345: // 実行している?
346: // もう少し考えると、スコアボードでレジスタの干渉がなければ、
347: // 実行している?
348:
349: // 仕方がないのでハックでごまかす。
350: // rte のほうも参照のこと。
351:
352: if (IsSuper() && xip >= 0x41000000 && xip < 0x42000000) {
353: // 遅延例外機構がないので、ROM 合わせのハック。
354: sxip = snip;
355: }
356: }
357:
1.1 root 358: epsr = psr;
1.1.1.2 root 359: SetPSR(psr | PSR_SUPER | PSR_SFD1 | PSR_IND | PSR_SFRZ);
1.1 root 360:
361: fip = vbr + (vec << 3);
362: fetch();
1.1.1.3 root 363: // ここでベクタに飛ぶのだが、ベクタ内の2命令でおそらく必ずもう一度
364: // 分岐してそっちでも履歴が残るので、ここでブランチ履歴を残すのは
365: // ちょっと冗長という気もする。
366:
1.1 root 367: if (OpIsBusErr(opF)) {
368: if (cause == ExceptionKind::ERROR) {
369: // double bus fault
370: // 実機は無限ループ状態
371: PANIC("Infinite ERROR (double bus fault)");
372: } else {
373: ExceptionCore(M88K_EXCEP_ERROR, ExceptionKind::ERROR);
374: }
375: }
376: }
377:
1.1.1.5 root 378: inline
379: uint32
380: m88kcpu::Calcdmt(uint32 flag)
381: {
382: return
383: ((psr & PSR_BO_LE) ? DM_BO : 0)
384: | ((IsSuper() && !(flag & DM_USR)) ? DM_DAS : 0)
385: | DM_VALID;
386: }
387:
1.1 root 388: // データアクセス例外(Read)
1.1.1.5 root 389: // ld.b, ld.h, ld.hu, ld 用
1.1 root 390: void
1.1.1.5 root 391: m88kcpu::ReadDataException32(uint32 addr, uint32 flag)
1.1 root 392: {
393: // ミスアラインドはこの例外より前にチェックされていて、
394: // ここにはアラインドしかこない。
395: dma0 = addr & ~0x3;
1.1.1.5 root 396: dmt0 = Calcdmt(flag)
1.1 root 397: | (FLD_D << 7)
398: | (flag & DM_SIGNED)
399: | ((flag & DM_EN_MASK) >> (addr & 0x03))
1.1.1.5 root 400: ;
401: dmt1 &= ~DM_VALID;
402: dmt2 &= ~DM_VALID;
1.1.1.7 root 403: cpulog(1, "ReadDAE xip=%x addr=%x", xip, addr);
1.1.1.2 root 404: ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA);
1.1 root 405: }
406:
1.1.1.5 root 407: // データアクセス例外(Read)
408: // ld.d 用
409: void
410: m88kcpu::ReadDataException64(uint32 addr, uint32 flag)
411: {
412: // ミスアラインドはこの例外より前にチェックされていて、
413: // ここにはアラインドしかこない。
414: dma0 = addr;
415: dmt0 = Calcdmt(flag)
416: | DM_DOUB1 // ダブルワードの第1アクセスのときセット
417: | (FLD_D << 7)
418: | DM_EN_MASK // 32ビット有効
419: ;
420:
421: dma1 = dma0 + 4;
422: dmt1 = Calcdmt(flag)
423: | (FLD_D2 << 7)
424: | DM_EN_MASK // 32ビット有効
425: ;
426:
427: dmt2 &= ~DM_VALID;
428:
429: if ((flag & DM_DOUB1) == 0) {
430: // .d の2ワード目がバスエラー。第1ワードのアクセスは成功した。
431: dmt0 &= ~DM_VALID;
432: }
433:
1.1.1.7 root 434: cpulog(1, "ReadDAE64 xip=%x addr=%x", xip, addr);
1.1.1.5 root 435: ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA);
436: }
437:
1.1 root 438: // データアクセス例外(Write)
1.1.1.5 root 439: // st.b, st.h, st.hu, st 用
1.1 root 440: void
1.1.1.5 root 441: m88kcpu::WriteDataException32(uint32 addr, uint32 flag)
1.1 root 442: {
443: // ミスアラインドはこの例外より前にチェックされていて、
444: // ここにはアラインドしかこない。
445: dma0 = addr & ~0x3;
1.1.1.5 root 446: dmt0 = Calcdmt(flag)
1.1 root 447: | (FLD_D << 7)
448: | ((flag & DM_EN_MASK) >> (addr & 0x03))
449: | DM_WRITE
1.1.1.5 root 450: ;
451: dmd0 = rD;
1.1 root 452:
1.1.1.5 root 453: dmt1 &= ~DM_VALID;
454: dmt2 &= ~DM_VALID;
1.1.1.7 root 455: cpulog(1, "WriteDAE xip=%x addr=%x", xip, addr);
1.1.1.2 root 456: ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA);
457: }
458:
1.1.1.5 root 459: // データアクセス例外(Write)
460: // st.d 用
461: void
462: m88kcpu::WriteDataException64(uint32 addr, uint32 flag)
463: {
464: // ミスアラインドはこの例外より前にチェックされていて、
465: // ここにはアラインドしかこない。
466: dma0 = addr & ~0x3;
467: dmt0 = Calcdmt(flag)
468: | DM_DOUB1 // ダブルワードの第1アクセスのときセット
469: | (FLD_D << 7)
470: | DM_EN_MASK
471: | DM_WRITE
472: ;
473: dmd0 = rD;
474:
475: dma1 = dma0 + 4;
476: dmt1 = Calcdmt(flag)
477: | (FLD_D2 << 7)
478: | DM_EN_MASK
479: | DM_WRITE
480: ;
481: dmd1 = rD2;
482:
483: dmt2 &= ~DM_VALID;
484:
485: if ((flag & DM_DOUB1) == 0) {
486: // .d の2ワード目がバスエラー。第1ワードのアクセスは成功した。
487: dmt0 &= ~DM_VALID;
488: }
489:
1.1.1.7 root 490: cpulog(1, "WriteDAE64 xip=%x addr=%x", xip, addr);
1.1.1.5 root 491: ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA);
492: }
493:
1.1.1.2 root 494: // データアクセス例外(xmem)
1.1.1.5 root 495: // xmem には .d は無い
1.1.1.2 root 496: void
1.1.1.5 root 497: m88kcpu::XmemDataException(uint32 addr, uint32 flag)
1.1.1.2 root 498: {
499: dma0 = addr & ~0x3;
1.1.1.5 root 500: dmt0 = Calcdmt(flag)
501: | DM_LOCK // xmem でセット
1.1.1.2 root 502: | (FLD_D << 7)
503: | (flag & DM_SIGNED)
504: | ((flag & DM_EN_MASK) >> (addr & 0x03))
1.1.1.5 root 505: ;
1.1.1.2 root 506:
1.1.1.5 root 507: dma1 = dma0;
508: dmt1 = Calcdmt(flag)
509: | DM_LOCK // xmem でセット
1.1.1.2 root 510: | (FLD_D << 7)
511: | (flag & DM_SIGNED)
512: | ((flag & DM_EN_MASK) >> (addr & 0x03))
513: | DM_WRITE
1.1.1.5 root 514: ;
515: dmd1 = rD;
1.1.1.2 root 516:
1.1.1.5 root 517: dmt2 &= ~DM_VALID;
1.1.1.2 root 518:
519: // ミスアラインドはこの例外より前にチェックされていて、
520: // ここにはアラインドしかこない。
521: if (cmmu[1].fault_code != m88200::FAULT_CODE_BUSERR
522: || cmmu[1].acc_read) {
523: // アドレス変換フォルトまたは read 時点
524: // nop
1.1 root 525: } else {
1.1.1.2 root 526: // xmem の write 時点
1.1.1.5 root 527: dmt0 &= ~DM_VALID;
1.1 root 528: }
1.1.1.2 root 529:
1.1.1.7 root 530: cpulog(1, "xmemDAE xip=%x addr=%x", xip, addr);
1.1.1.2 root 531: ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA);
1.1 root 532: }
533:
1.1.1.5 root 534: // FP 例外
535: // fpex: FP例外コード M88K_FPEXCEP
536: void
537: m88kcpu::FPException(int fpex)
538: {
539: // FDVZ 以上の例外が PRECISE。
540: // 未満の FUNF, FOVF, FINX は IMPRECISE。
541: int vec = (fpex >= M88K_FPEXCEP_FDVZ) ? M88K_EXCEP_SFU1_PRECISE : M88K_EXCEP_SFU1_IMPRECISE;
542: fpecr = fpex;
543:
544: Exception(vec);
545: }
546:
1.1 root 547: void
1.1.1.5 root 548: m88kcpu::Interrupt(int level)
1.1 root 549: {
1.1.1.5 root 550: // level は 0 か 1。
551: intr_pending = level;
1.1 root 552: atomic_reqflag |= CPU_REQ_INTR;
553: }
554:
555: void
556: m88kcpu::fpu_unimpl()
557: {
558: printf("%s\n", __func__);
1.1.1.5 root 559: FPException(M88K_FPEXCEP_FUNIMP);
1.1 root 560: }
1.1.1.2 root 561:
562: OP_DEF(illegal)
563: {
564: // 本来は不当命令例外?
1.1.1.7 root 565: cpulog(0, "Illegal instruction %08x", (uint32)opX);
1.1.1.2 root 566: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.