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