|
|
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:
1.1.1.8 root 7: #include "mpu680x0.h"
1.1.1.11! root 8: #include "m68030cache.h"
1.1.1.8 root 9: #include "debugger.h"
10: #include "interrupt.h"
1.1.1.11! root 11: #include "mainbus.h"
1.1.1.8 root 12: #include "scheduler.h"
1.1.1.6 root 13: #include "vectortable.h"
1.1 root 14:
1.1.1.8 root 15: #define OP_FUNC(name) __CONCAT(op_,name)()
1.1 root 16:
1.1.1.6 root 17: // 1命令実行する。
1.1.1.8 root 18: // 割り込みチェックなどを省いたショート版。
19: void
20: MPU680x0Device::ExecShort(Event& ev)
1.1 root 21: {
1.1.1.8 root 22: uint64 cycle_start = used_cycle;
1.1 root 23:
1.1.1.10 root 24: buswait = 0;
25:
1.1.1.6 root 26: try {
27: // 1命令版では switch-case を使うしかない。
1.1.1.8 root 28: ppc = reg.pc;
29: flag_pi = 0;
30: flag_pd = 0;
1.1.1.11! root 31: ir = fetch_2();
1.1.1.6 root 32:
33: // uint16 を 6ビット右シフトしたら 1024 以上になるはずは
34: // ないのだが、デフォルトでは gcc/clang どちらも律儀に
35: // 1024 以上かどうか比較して分岐しようとする。
36: // gcc は __assume() を入れることでこの比較・分岐を出力
37: // しないようにすることが出来た(がパフォーマンスの差は
38: // 見えない程度でしかなかった)。
39: // clang ではどうやったら効くか不明。
1.1.1.8 root 40: uint16 ir6 = ir >> 6;
41: __assume(ir6 < 1024);
42: switch (ir6) {
1.1 root 43: #include "m68030switch.h"
44: }
1.1.1.4 root 45:
1.1.1.11! root 46: if (__predict_false(icache->enable == false)) {
1.1.1.10 root 47: used_cycle += cycle_nocache;
48: cycle_nocache = 0;
49: }
1.1.1.11! root 50: } catch (uint vector) {
1.1.1.6 root 51: // ここで(C++の)例外スローをキャッチするのは
52: // - メモリ空間をアクセスした先でのバスエラー例外
53: // - EA パース中の不当命令例外
54: // のみ。それ以外の命令途中例外はその場で処理できる。
1.1.1.8 root 55: if (__predict_false(loglevel >= 2)) {
56: const char *name = vectortable->GetExceptionName(vector);
1.1.1.7 root 57: if (__predict_true(name)) {
1.1.1.8 root 58: putlogn("%s", name);
1.1.1.7 root 59: } else {
1.1.1.8 root 60: putlogn("vector 0x%x", vector);
1.1.1.7 root 61: }
62: }
1.1.1.9 root 63: if (vector == M68K::EXCEP_BUSERR) {
64: ExceptionBuserr(vector);
65: } else {
66: Exception(vector);
67: }
1.1.1.6 root 68: }
1.1 root 69:
1.1.1.6 root 70: // 外部割り込みの起動は VM 側で処理している。
1.1 root 71:
1.1.1.8 root 72: int cycle = used_cycle - cycle_start;
1.1.1.10 root 73: ev.time = cycle * clock_nsec + buswait;
1.1.1.8 root 74: scheduler->StartEvent(ev);
75: }
76:
77: // 二重バスフォールト
78: void
79: MPU680x0Device::DoubleBusFault(const char *where)
80: {
81: // where をログに出す?
82:
83: // ホールト状態へ移行
84: Halt();
85: }
86:
87: // SR (と CCR) をセットする。
88: // リセット例外以外から使う (リセット例外はやや異なるため)。
89: //
90: // 割り込みマスクが変わる場合は、ここで次の命令実行コールバックを差し替える。
91: void
92: MPU680x0Device::SetSR(uint16 data)
93: {
94: bool oldS = reg.s;
95: bool oldM = reg.m;
96:
97: // 現在の SP を保存
98: if (oldS == false) {
99: reg.usp = reg.A[7];
100: } else if (oldM == false) {
101: reg.isp = reg.A[7];
102: } else {
103: reg.msp = reg.A[7];
104: }
105:
106: reg.ccr.Set(data);
107: reg.s = ((data & M68K::SR_S) != 0);
108: reg.m = ((data & M68K::SR_M) != 0);
109:
110: // 割り込みマスクレジスタを更新して..
111: uint new_mask = (data >> 8) & 7;
112: if (reg.intr_mask != new_mask) {
113: reg.intr_mask = new_mask;
114:
115: // 次の命令境界で割り込みをチェックさせる
116: MakeNextFull();
117: }
118:
119: // 新しい SP を用意
120: if (reg.s == false) {
121: reg.A[7] = reg.usp;
122: } else if (reg.m == false) {
123: reg.A[7] = reg.isp;
124: } else {
125: reg.A[7] = reg.msp;
126: }
127:
1.1.1.10 root 128: // FC2-0
1.1.1.11! root 129: fc_inst = BusAddr::Fetch | busaddr::SU(reg.s);
! 130: fc_data = BusAddr::D | busaddr::SU(reg.s);
1.1.1.8 root 131:
132: // XXX 他に何か状態変えないといけないことあったっけか。
133: }
134:
1.1.1.11! root 135: // CACR レジスタ書き込み
! 136: void
! 137: MPU680x0Device::SetCACR(uint32 newval)
! 138: {
! 139: newval &= M68K::CACR_MASK;
! 140: uint32 oldval = reg.cacr;
! 141: uint32 chgval = oldval ^ newval;
! 142:
! 143: // EI (命令キャッシュ有効)
! 144: if ((chgval & M68K::CACR_EI) != 0) {
! 145: if ((newval & M68K::CACR_EI) != 0) {
! 146: // 命令キャッシュ有効
! 147: icache->enable = true;
! 148: icache->hist.Clear();
! 149: putlog(1, "I-Cache enabled");
! 150: } else {
! 151: // 命令キャッシュ無効
! 152: icache->enable = false;
! 153: putlog(1, "I-Cache disabled");
! 154: // 命令キャッシュが有効だった期間 nocache は使われずに
! 155: // 積算され続けているが、キャッシュ無効になるとこれを加算
! 156: // しようとするのでここでリセット。
! 157: cycle_nocache = 0;
! 158: }
! 159: }
! 160:
! 161: // FI (命令キャッシュ凍結)
! 162: if ((chgval & M68K::CACR_FI) != 0) {
! 163: putlog(0, "CACR:FI Not Implemented");
! 164: icache->freeze = (newval & M68K::CACR_FI);
! 165: }
! 166:
! 167: // CEI (命令キャッシュの指定エントリをクリア)
! 168: if ((chgval & M68K::CACR_CEI) != 0) {
! 169: // CAAR の b7-2 で示されるロングワードを無効にする。
! 170: uint32 idx = (reg.caar & 0x000000f0) >> 4;
! 171: uint32 sel = (reg.caar & 0x0000000c) >> 2;
! 172: auto& l = icache->line[idx];
! 173: l.valid[sel] = 0;
! 174:
! 175: newval &= ~M68K::CACR_CEI;
! 176: }
! 177:
! 178: // CI (命令キャッシュクリア)
! 179: if ((chgval & M68K::CACR_CI) != 0) {
! 180: for (auto& l : icache->line) {
! 181: l.valid_all = 0;
! 182: }
! 183:
! 184: newval &= ~M68K::CACR_CI;
! 185: }
! 186:
! 187: // IBE (命令バースト有効)
! 188: if ((chgval & M68K::CACR_IBE) != 0) {
! 189: icache->burst_enable = (newval & M68K::CACR_IBE) && has_burst_access;
! 190: }
! 191:
! 192: // ED (データキャッシュ有効)
! 193: if ((chgval & M68K::CACR_ED) != 0) {
! 194: if ((newval & M68K::CACR_ED) != 0) {
! 195: // データキャッシュ有効。
! 196: dcache->enable = true;
! 197: dcache->hist.Clear();
! 198: putlog(1, "D-Cache enabled");
! 199: } else {
! 200: // データキャッシュ無効。
! 201: dcache->enable = false;
! 202: putlog(1, "D-Cache disabled");
! 203: }
! 204: }
! 205:
! 206: // FD (データキャッシュ凍結)
! 207: if ((chgval & M68K::CACR_FD) != 0) {
! 208: dcache->freeze = (newval & M68K::CACR_FD);
! 209: putlog(1, "CACR:FD -> %u", dcache->freeze);
! 210: }
! 211:
! 212: // CED (データキャッシュの指定エントリをクリア)
! 213: if ((chgval & M68K::CACR_CED) != 0) {
! 214: // CAAR の b7-2 で示されるロングワードを無効にする。
! 215: uint32 idx = (reg.caar & 0x000000f0) >> 4;
! 216: uint32 sel = (reg.caar & 0x0000000c) >> 2;
! 217: auto& l = dcache->line[idx];
! 218: l.valid[sel] = 0;
! 219:
! 220: newval &= ~M68K::CACR_CED;
! 221: }
! 222:
! 223: // CD (データキャッシュクリア)
! 224: if ((chgval & M68K::CACR_CD) != 0) {
! 225: for (auto& l : dcache->line) {
! 226: l.valid_all = 0;
! 227: }
! 228:
! 229: newval &= ~M68K::CACR_CD;
! 230: }
! 231:
! 232: // DBE (データバースト有効)
! 233: if ((chgval & M68K::CACR_DBE) != 0) {
! 234: dcache->burst_enable = (newval & M68K::CACR_DBE) && has_burst_access;
! 235: }
! 236:
! 237: // WA (ライトアロケート)
! 238: if ((chgval & M68K::CACR_WA) != 0) {
! 239: dcache->wa = (newval & M68K::CACR_WA);
! 240: }
! 241:
! 242: // とりあえずレジスタイメージも保存。
! 243: reg.cacr = newval;
! 244: }
! 245:
1.1.1.8 root 246: // 命令中に (An)+ で変化したアドレスレジスタを命令実行前の状態に巻き戻す。
247: // flag_pi は bit7..0 が save_pi[7..0] の A7..A0 に対応している。
248: void
249: MPU680x0Device::restore_reg_pi()
250: {
251: for (int i = 7; flag_pi; i--) {
252: if ((int8)flag_pi < 0) {
253: reg.A[i] = save_pi[i];
254: }
255: flag_pi <<= 1;
256: }
257: }
258:
259: // 命令中に -(An) で変化したアドレスレジスタを命令実行前の状態に巻き戻す。
260: // flag_pd は bit7..0 が save_pd[7..0] の A7..A0 に対応している。
261: void
262: MPU680x0Device::restore_reg_pd()
263: {
264: for (int i = 7; flag_pd; i--) {
265: if ((int8)flag_pd < 0) {
266: reg.A[i] = save_pd[i];
267: }
268: flag_pd <<= 1;
269: }
270: }
271:
272: // vector にジャンプする。vector はベクタ番号(アドレスではない)。
273: void
1.1.1.11! root 274: MPU680x0Device::JumpVector(uint vector, bool from_buserr)
1.1.1.8 root 275: {
276: uint32 vecaddr;
277: uint32 newpc;
278:
279: vecaddr = reg.vbr + vector * 4;
1.1.1.11! root 280: newpc = read_4(vecaddr);
1.1.1.8 root 281: // VBR 込みのジャンプ先が確定したところで今度はブランチ履歴に記録。
282: // この時のブランチ元はベクタアドレス
283: // (reg.pc は fetch によって進んでしまうので先に保存)
1.1.1.9 root 284: uint32 inst = BranchHistory_m680x0::VectorJump;
285: brhist.AddEntry(vecaddr | (IsSuper() ? 1 : 0), newpc, inst);
1.1.1.8 root 286:
287: // 奇数番地ならアドレスエラー
288: if (__predict_false((newpc & 1) != 0)) {
1.1.1.10 root 289: if (from_buserr) {
290: // バスエラー処理中ならダブルバスフォールト。
291: throw M68K::EXCEP_ADDRERR;
292: } else {
293: ExceptionBuserr(M68K::EXCEP_ADDRERR);
294: }
1.1.1.8 root 295: return;
296: }
297: reg.pc = newpc;
298:
299: // 実際はここでパイプライン充填のために3ワードフェッチするので、
300: // ベクタテーブルはフェッチ出来たけどそのジャンプ先がバスエラーに
301: // なるケースはここでバスエラーが出なければいけないが、まだ
302: // キャッシュを実装していないので、ここで1ワードフェッチしてみて
303: // PC を戻すあほっぽい方法を取る。
1.1.1.11! root 304: fetch_2();
1.1.1.8 root 305: reg.pc -= 2;
306: }
307:
308: // 例外スタックフレーム $0 を生成する。
309: // vector はベクタ番号。
310: // pc はスタックに積むプログラムカウンタの値 (例外によって、この命令位置か
311: // 次の命令位置かが異なる)。
312: // sr は例外発生時の SR。
313: inline void
1.1.1.11! root 314: MPU680x0Device::GenerateExframe0(uint vector, uint32 pc, uint16 sr)
1.1.1.8 root 315: {
316: // 例外スタックフレーム $0
317: // SP+$00.W STATUS REGISTER
318: // +$02.L PROGRAM COUNTER ; 例外ごとに異なる
319: // +$06.W %0000 | VECTOR
320:
1.1.1.11! root 321: push_2((0 << 12) | (vector << 2));
! 322: push_4(pc);
! 323: push_2(sr);
1.1.1.8 root 324: }
325:
326: // 例外スタックフレーム $2 を生成する。
327: // vector はベクタ番号。
328: // スタックに積む PC は常に次の命令。
329: inline void
1.1.1.11! root 330: MPU680x0Device::GenerateExframe2(uint vector, uint16 sr)
1.1.1.8 root 331: {
332: // 例外スタックフレーム $2
333: // SP+$00.W STATUS REGISTER
334: // +$02.L PROGRAM COUNTER ; 次の命令位置
335: // +$06.W %0010 | VECTOR
336: // +$08.L INSTRUCTION ADDRESS ; 例外を起こした命令のアドレス
337:
1.1.1.11! root 338: push_4(ppc); // 例外を起こした命令のアドレス
! 339: push_2((2 << 12) | (vector << 2));
! 340: push_4(reg.pc); // 次の命令位置
! 341: push_2(sr);
1.1.1.8 root 342: }
343:
344: // 例外スタックフレーム $B を生成する。
345: inline void
1.1.1.11! root 346: MPU680x0Device::GenerateExframeB(uint vector, uint16 sr)
1.1.1.8 root 347: {
348: uint16 ssw;
349: uint32 fault_addr;
350: uint32 stageBaddr;
351:
352: stageBaddr = reg.pc + 2;
353: // この後のスタック操作で bus が変化する前に保存
1.1.1.11! root 354: fault_addr = bus.laddr.Addr();
1.1.1.10 root 355:
356: // DF は常に立てる必要がある??
357: ssw = bus.GetSSW() | M68K::SSW_DF;
358: if (mmu_enable && (ssw & M68K::FC_DATA) != 0) {
359: // 命令アクセスなら RB をセット。
360: ssw |= M68K::SSW_RB;
361: }
1.1.1.8 root 362:
363: // 例外スタックフレーム $B
364: // SP+$00.W STATUS REGISTER
365: // +$02.L PROGRAM COUNTER
366: // +$06.W %1011 | VECTOR
367: // +$08.W (internal register)
368: // +$0a.W SPECIAL STATUS REGISTER ; SSW
369: // +$0c.W INSTRUCTION PIPE STAGE C
370: // +$0e.W INSTRUCTION PIPE STAGE B
371: // +$10.L DATA CYCLE FAULT ADDRESS
372: // +$14.W*2 (internal register)
373: // +$18.L DATA OUTPUT BUFFER
374: // +$1c.W*4 (internal register, 4 words)
375: // +$24.L STAGE B ADDRESS
376: // +$28.W*2 (internal register, 2 words)
377: // +$2c.L DATA INPUT BUFFER
378: // +$30.W*3 (internal register, 3 words)
379: // +$36.W VERSION# | internal information
380: // +$38.W*18 (internal register, 18 words)
381: // +$5a
382: // こっちから上に向かって PUSH
383:
384: // internal registers, 18 words
385: // 内部レジスタ18ワードのうち、ここでは17ワード分を save_pd[8] (16ワード)、
386: // flag_pd (1ワード) として使うことにする。
387: // -(An) でバスエラーが起きると An は減算された状態になるが、DF ビットが
388: // 立っていて再実行する時には現行構造では命令先頭から再実行せざるをえず、
389: // そうするともう一度プリデクリメントが動いてしまう。そのため、RTE の時点
390: // で DF=1 ならプリデクリメント発生前の An に戻すという処理をする。
391: // RTE 時点で DF=0 なら命令を再実行しないため An も巻き戻してはならず、
392: // そのためプリデクリメント分の巻き戻しは RTE 時点まで行えない。
393: // 一方、ポストインクリメントのケースはこの少し下の
394: // ExceptionBuserr() 内のコメント参照。
395: for (int i = 7; i >= 0; i--) {
1.1.1.11! root 396: push_4(save_pd[i]);
1.1.1.8 root 397: }
1.1.1.11! root 398: push_2(flag_pd);
1.1.1.8 root 399: reg.A[7] -= 2;
400:
401: reg.A[7] -= 2; // VERSION#
402: reg.A[7] -= 6;
1.1.1.11! root 403: push_4(0); // DATA INPUT BUFFER
1.1.1.8 root 404: reg.A[7] -= 4;
1.1.1.11! root 405: push_4(stageBaddr); // STAGE B ADDRESS
1.1.1.8 root 406: reg.A[7] -= 8;
1.1.1.11! root 407: push_4(0); // DATA OUTPUT BUFFER
1.1.1.8 root 408: reg.A[7] -= 4;
1.1.1.11! root 409: push_4(fault_addr); // DATA CYCLE FAULT ADDRESS
! 410: push_2(0); // INSTRUCTION PIPE STAGE B
! 411: push_2(0); // INSTRUCTION PIPE STAGE C
! 412: push_2(ssw);
1.1.1.8 root 413: reg.A[7] -= 2;
1.1.1.11! root 414: push_2(0xb000 | (vector << 2));
1.1.1.8 root 415: // 本当は現在の PC だが、再実行のため PPC を積む。
1.1.1.11! root 416: push_4(ppc); // PC
! 417: push_2(sr); // 例外入り口で保存した SR
1.1.1.8 root 418: }
419:
420: // 例外処理について。
421: //
422: // 0. リセット例外
423: // リセット例外は外部から ExceptionReset() を呼び出す。
424: //
425: // 1. バスエラー例外
426: // MMU による論理アドレスでのバスエラーは (Translate*() が false を返した後)
427: // m68030bus.cpp ルーチンが (C++の) 例外をスローする。
1.1.1.10 root 428: // 物理アドレス空間アクセス後のバスエラーは (VM側の Read*()/Write*() が
429: // BusErr を返した後) m68030bus.cpp ルーチンが (C++) の例外をスローする。
1.1.1.8 root 430: // どちらのケースも m68030core.cpp のメインループで catch し、その catch
1.1.1.9 root 431: // ブロック内で ExceptionBuserr() を呼び出す。
1.1.1.8 root 432: //
433: // 2. アドレスエラー例外
434: // アドレスエラーは Jump() 内でのみ発生するはずで、その場で
1.1.1.9 root 435: // ExceptionBuserr() を呼んで処理する。
1.1.1.8 root 436: //
437: // 3. 命令中で起きる例外
438: // 命令途中で起きる(確定的な)例外は Exception() を呼び出す。
439: // これに該当するのは
440: // o 特権違反
441: // o A系列/F系列命令
442: // o TRAP* 系命令、CHK/CHK2 命令
443: // o トレース例外 (未実装)
444: // o ゼロ除算?
445: // o MMU 構成例外
446: // o 不当命令 (*)
447: // である。
448: // ただし、不当命令についてはどこで不当命令と判定されるかによって
449: // フローが複数存在することに注意。
450: // o 1ワード目が不当命令
451: // -> 命令がそれぞれ Exception() で処理。
452: // o 1ワード目の EA が不当命令
453: // -> (C++の)例外をスローして catch 内で Exception() を実行。
454: // o 2ワード目が不当命令
455: // -> 命令がそれぞれ Exception() で処理。
456: //
457: // 4. 割り込み例外
458: // 別コメント(下のほう)参照。
459: //
460:
461: // リセット例外処理。
462: // 各レジスタを所定の状態にして、リセットベクタを読み込む。
463: // リセットベクタが読み込めなければ HALT 状態に移行。
464: // 戻り値は消費したサイクル数。
465: int
466: MPU680x0Device::ExceptionReset()
467: {
468: uint64 cycle_start = used_cycle;
469:
470: // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
471: // m68k では今の所リセット例外発生時の PC にはあまり意味がないけど、
472: // m88k と動作を揃えておく。
473: const uint vector = 0;
1.1.1.11! root 474: excep_counter[0]++;
1.1.1.9 root 475: exhist.AddEntry(reg.pc | 1, 0, BranchHistory_m680x0::Exception | 0);
476: brhist.AddEntry(reg.pc | 1, 0, BranchHistory_m680x0::Exception | 0);
1.1.1.8 root 477: // デバッガに通知 (例外ブレーク用)。こっちのベクタは 0 のままでよい。
478: debugger->NotifyExceptionMain(vector);
479:
480: // RESET 信号線をネゲートしてから SP の読み込み開始まで 4クロック
481: // (Section 7.8, Figure 7-64)
482: used_cycle += 4;
483:
484: // STOP 状態とホールト状態を解除。
485: ChangeState(CPU_STATE_NORMAL);
486:
487: // 1. T0, T1 クリア
488: // 2. S をセット、M をクリア
489: // 3. 割り込みレベルを 7
490:
491: // ここは SetSR を使わずに設定する。
492: reg.s = true;
493: reg.m = false;
1.1.1.11! root 494: fc_inst = BusAddr::S | BusAddr::Fetch;
! 495: fc_data = BusAddr::S | BusAddr::D;
1.1.1.8 root 496: // ここは割り込みレベル変化のチェックは不要でセットだけでいい。
497: reg.intr_mask = 7;
498:
499: // 4. VBR をクリア
500: reg.vbr = 0;
1.1.1.11! root 501:
! 502: // 5. キャッシュの EI|ED, FI|FD, IBE|DBE, WA をクリア
! 503: // 6. キャッシュエントリをすべてクリア (CI|CD)
! 504: SetCACR(M68K::CACR_CD | M68K::CACR_CI);
! 505: cycle_nocache = 0;
! 506: icache->hist.Clear();
! 507: dcache->hist.Clear();
! 508:
1.1.1.8 root 509: // 7. TC:E、TTn:E をクリア
510: SetTC(GetTC() & ~m68030TC::TC_E);
511: SetTT(0, GetTT(0) & ~m68030TT::E);
512: SetTT(1, GetTT(1) & ~m68030TT::E);
513: // 8. リセットベクタ生成
514:
515: // 9. SP をロード
516: // 10. PC をロード
517: try {
518: reg.pc = 0;
1.1.1.11! root 519: reg.A[7] = fetch_4();
! 520: uint32 pc = fetch_4();
! 521: // 大丈夫のはずだけど fetch_4() が reg.pc を書き換えるので一応
1.1.1.8 root 522: reg.pc = pc;
523: // リセットベクタは常に 0, 4 番地から取得したことになっている
1.1.1.9 root 524: uint32 inst = BranchHistory_m680x0::VectorJump;
525: brhist.AddEntry(0x00000004 | 1, reg.pc, inst);
1.1.1.11! root 526: } catch (uint) {
1.1.1.8 root 527: DoubleBusFault("reset exception");
528: }
529:
530: // SP のアドレスエラーはリセット例外ではチェックしないようだ。
531: // PC のアドレスエラーは本来ならここにあるはずの3ワードのプリフェッチで
532: // 行われるのだが、それがないので、とりあえずここで。
533: if ((reg.pc & 1) != 0) {
534: DoubleBusFault("reset exception");
535: }
536:
537: return used_cycle - cycle_start;
538: }
539:
1.1.1.9 root 540: // 汎用の例外処理を行う。vector はベクタ番号。
541: // ただし以下の例外はここではなくそれぞれ固有のを呼ぶこと。
542: // o リセット例外は ExceptionReset()
543: // o 割り込み例外は ExceptionInterrupt()
544: // o TRAP#15 例外は ExceptionTrap15()
545: // o バスエラー・アドレスエラーは ExceptionBuserr()
1.1.1.8 root 546: void
1.1.1.11! root 547: MPU680x0Device::Exception(uint vector)
1.1.1.8 root 548: {
1.1.1.9 root 549: uint32 inst = BranchHistory_m680x0::Exception | vector;
550: ExceptionGeneric(vector, inst);
551: }
552:
553: // TRAP #15 の例外処理を行う。
554: // 処理自体は汎用の Exception() とまったく同じだが、
555: // IOCS コールの時には分岐履歴の出力が変わるため入口を分けてある。
556: void
557: MPU680x0Device::ExceptionTrap15()
558: {
559: // 今発生した TRAP #15 が IOCS コールっぽいかどうか調べる。
560: //
561: // Human68k 方面をやってる時は TRAP #15 は IOCS コールで表示してほしい。
562: // 一方、NetBSD のカーネル内では TRAP #15 は IOCS コールではない別用途
563: // なので IOCS コールかのように表示されるのは紛らわしい。さらに言えば
564: // NetBSD でもブートローダの時点はまだ IOCS コールを使うので、出来れば
565: // 今呼ばれたこれが IOCS コールなのか、そうでない TRAP #15 なのかは
566: // 区別したい。
567: //
568: // IOCS コールかどうかは厳密には分からないが、これが呼ばれた時点で
569: // ワークエリアの $cbc == 0x03、$cbe == 0xff であれば IOCS コールと
570: // 判定することにする。
571: // $cbc は MPU 種別で 3:68030。
572: // $cbe は MMU 有無で 0xff:有り。
573: // 今の所 68030/MMU有り しかサポートしていないので。
574: // だめならまた考える。
575:
1.1.1.11! root 576: const uint vector = M68K::EXCEP_TRAP15;
1.1.1.9 root 577: uint32 inst;
1.1.1.11! root 578: if (mainbus->Peek1(0xcbc) == 0x03 && mainbus->Peek1(0xcbe) == 0xff) {
1.1.1.9 root 579: // IOCS コールっぽい
580: inst = BranchHistory_m680x0::IOCSCall | (reg.D[0] & 0xff);
581: } else {
582: // TRAP#15 っぽい
583: inst = BranchHistory_m680x0::Exception | vector;
1.1.1.8 root 584: }
1.1.1.9 root 585: ExceptionGeneric(vector, inst);
586: }
1.1.1.8 root 587:
1.1.1.9 root 588: // 汎用の例外処理本体。
589: void
1.1.1.11! root 590: MPU680x0Device::ExceptionGeneric(uint vector, uint32 inst)
1.1.1.9 root 591: {
1.1.1.8 root 592: // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
1.1.1.11! root 593: excep_counter[vector]++;
1.1.1.8 root 594: uint32 from = ppc | (IsSuper() ? 1 : 0);
1.1.1.9 root 595: exhist.AddEntry(from, 0, inst);
596: brhist.AddEntry(from, 0, inst);
1.1.1.8 root 597: // デバッガに通知 (例外ブレーク用)
598: debugger->NotifyExceptionMain(vector);
599:
600: // 状態を保存
601: uint16 sr = GetSR();
602:
603: // S を立てて T1,T0 を落とす
604: SetSR((GetSR() | M68K::SR_S) & ~M68K::SR_T);
605:
606: try {
607: switch (vector) {
608: case M68K::EXCEP_ILLEGAL: // 不当命令
609: case M68K::EXCEP_PRIV: // 特権違反例外
610: case M68K::EXCEP_ALINE: // Aライン命令例外
611: case M68K::EXCEP_FLINE: // Fライン命令例外
612: // スタックに積む PC はこの命令のアドレス
613: GenerateExframe0(vector, ppc, sr);
614: break;
615:
616: case M68K::EXCEP_TRAP0 // TRAP#0
617: ... M68K::EXCEP_TRAP15: // TRAP#15
618: // スタックに積む PC は次の命令
619: GenerateExframe0(vector, reg.pc, sr);
620: break;
621:
622: case M68K::EXCEP_ZERODIV: // ゼロ除算
623: case M68K::EXCEP_CHK: // CHK
624: case M68K::EXCEP_TRAPV: // TRAPV
625: case M68K::EXCEP_MMU_CONFIG: // MMU 構成例外
626: GenerateExframe2(vector, sr);
627: break;
628:
629: default:
1.1.1.11! root 630: PANIC("$%08x exception vector %u ?", ppc, vector);
1.1.1.8 root 631: break;
632: }
633:
634: // ベクタにジャンプ
1.1.1.10 root 635: JumpVector(vector, false);
1.1.1.11! root 636: } catch (uint vector2) {
! 637: assertmsg(vector2 == M68K::EXCEP_BUSERR, "vector=%u", vector);
1.1.1.8 root 638:
639: // バスエラー処理へ
640: ExceptionBuserr(vector2);
641: }
642: }
643:
644: // バスエラー・アドレスエラー例外処理を行う。
645: // 他の例外とは違いが多いので分けた。
646: void
1.1.1.11! root 647: MPU680x0Device::ExceptionBuserr(uint vector)
1.1.1.8 root 648: {
649: assertmsg((vector == M68K::EXCEP_BUSERR || vector == M68K::EXCEP_ADDRERR),
1.1.1.11! root 650: "vector=%u", vector);
1.1.1.8 root 651:
652: // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
1.1.1.11! root 653: excep_counter[vector]++;
1.1.1.8 root 654: uint32 from = ppc | (IsSuper() ? 1 : 0);
1.1.1.9 root 655: uint32 inst = BranchHistory_m680x0::Exception | vector;
656: exhist.AddEntry(from, 0, inst);
657: brhist.AddEntry(from, 0, inst);
1.1.1.8 root 658: // デバッガに通知 (例外ブレーク用)
659: debugger->NotifyExceptionMain(vector);
660:
661: // 状態を保存
662: uint16 sr = GetSR();
663:
664: // ここで (An)+ で変化したレジスタを命令実行前の状態に巻き戻す。
665: // EA 処理の時点で先にインクリメントしているのだが、バスエラーが
666: // 起きたということは実際にはポストインクリメントは発生しないので、
667: // ここで無条件に巻き戻してよい。SR を書き換えると A7 が切り替わって
668: // しまうので、この巻き戻しは SR を書き換える前のここで行うこと。
669: // 一方、プリデクリメントのケースはこの少し上の ExframeB の中の
670: // コメント参照。
671: restore_reg_pi();
672:
673: // S を立てて T1,T0 を落とす
674: SetSR((GetSR() | M68K::SR_S) & ~M68K::SR_T);
675:
676: try {
677: // フレームを作成。
678: // 実際はショート($A)、ロング($B)の2種類あって、本来ならショートが
679: // 使えればショートを使うのだが、ここでは全部ロングを使う。
680: GenerateExframeB(vector, sr);
681: // ベクタにジャンプ
1.1.1.10 root 682: JumpVector(vector, true);
1.1.1.11! root 683: } catch (uint vector2) {
1.1.1.10 root 684: // ここに来るのはバスエラーかアドレスエラーだけのはず
685: assertmsg(vector2 == M68K::EXCEP_BUSERR ||
1.1.1.11! root 686: vector2 == M68K::EXCEP_ADDRERR, "vector=%u", vector);
1.1.1.8 root 687:
688: // バスエラー・アドレスエラー処理中のバスエラーは
689: // ダブルバスフォールト。
690: DoubleBusFault("bus/address error handling");
691: }
692: }
693:
694: // 割り込み例外処理を行う。
695: // 割り込みの受け付けとレベルのチェック等の後、次命令境界で呼ばれるやつ。
1.1.1.10 root 696: // この処理にかかる時間 [nsec] を返す。
697: uint64
1.1.1.8 root 698: MPU680x0Device::ExceptionInterrupt()
699: {
1.1.1.10 root 700: uint64 cycle_start = used_cycle;
1.1.1.11! root 701: uint level = intr_level;
1.1.1.8 root 702:
703: // ユーザーズマニュアル(日本語) p.399 図8-5 の中ほど
704: // 「/IPEND をネゲート、割り込みアクノリッジサイクルを実行」
705: // 以降に相当。
706:
707: // STOP なら解除
708: ChangeState(CPU_STATE_NORMAL);
709:
710: // 割り込みアクノリッジサイクルを実行
1.1.1.10 root 711: uint32 vector;
712: busdata bd = interruptdev->InterruptAcknowledge(level);
713: if (__predict_false(bd.IsBusErr())) {
714: // 外部回路がバスエラーを返すと Spurious を発行する。(7.4.1.3)
715: vector = M68K::EXCEP_SPURIOUS;
716: } else {
717: vector = bd.Data();
718: if ((int32)vector < 0) {
719: // オートベクタ要求 (7.4.1.2)
720: // (負数(-1)ならオートベクタということにしている)
721: vector = M68K::EXCEP_LEVEL_BASE + level;
722: }
1.1.1.8 root 723: }
724:
725: // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
1.1.1.11! root 726: excep_counter[vector]++;
1.1.1.8 root 727: uint32 from = ppc | (IsSuper() ? 1 : 0);
1.1.1.9 root 728: uint32 inst = BranchHistory_m680x0::Exception | vector;
1.1.1.8 root 729: exhist.AddEntry(from, 0, inst);
730: brhist.AddEntry(from, 0, inst);
731: // デバッガに通知 (例外ブレーク用)
732: debugger->NotifyExceptionMain(vector);
733:
734: // 状態を保存
735: uint16 oldsr = GetSR();
736:
737: // S を立てて T1,T0 を落とす。割り込みレベルも変更。
738: uint16 newsr = GetSR() & ~(M68K::SR_T | M68K::SR_IMASK);
739: newsr |= M68K::SR_S;
740: newsr |= level << 8;
741: SetSR(newsr);
742:
743: try {
744: // 例外スタックフレーム $0 を生成。
745: // スタックに積む PC は次の命令位置。
746: // M ビットは未実装なのでスローアウェイとか無視
747: GenerateExframe0(vector, reg.pc, oldsr);
748:
749: // ベクタにジャンプ
1.1.1.10 root 750: JumpVector(vector, false);
1.1.1.11! root 751: } catch (uint vector2) {
! 752: assertmsg(vector2 == M68K::EXCEP_BUSERR, "vector=%u", vector);
1.1.1.8 root 753:
754: // バスエラー処理へ
755: ExceptionBuserr(vector2);
756: }
757:
1.1.1.10 root 758: return (used_cycle - cycle_start) * clock_nsec + bd.GetWait();
1.1.1.8 root 759: }
760:
761: // RTE 命令。
762: // 例外処理の近くに置いておくほうが便利なので。
763: void
764: MPU680x0Device::ops_rte()
765: {
766: uint32 pc;
767: uint16 sr;
768: uint16 frame;
769:
770: try {
1.1.1.11! root 771: sr = pop_2();
! 772: pc = pop_4();
! 773: frame = pop_2();
1.1.1.8 root 774: switch (frame >> 12) {
775: case 0:
776: CYCLE2(18, 20);
777: SetSR(sr);
778: Jump(pc);
779: break;
780:
781: case 2:
782: CYCLE2(18, 20);
783: // +08.L INSTRUCTION ADDRESS を読み捨てる
784: reg.A[7] += 4;
785: SetSR(sr);
786: Jump(pc);
787: break;
788:
789: case 0x0b:
790: {
791: uint32 stage_b;
792: uint16 ssw;
793:
794: CYCLE(76);
795: reg.A[7] += 2; // $08.W
1.1.1.11! root 796: ssw = pop_2(); // $0a.W SSW
1.1.1.8 root 797: reg.A[7] += 4 * 6; // $0c.L*6
1.1.1.11! root 798: stage_b = pop_4(); // $24.L Stage B
1.1.1.8 root 799: reg.A[7] += 2 * 9; // $28.W*9
800: // $3a..$58 ここが save_pd の取り出し
1.1.1.11! root 801: flag_pd = pop_2();
1.1.1.8 root 802: for (int i = 0; i < 8; i++) {
1.1.1.11! root 803: save_pd[i] = pop_4();
1.1.1.8 root 804: }
805: // A7 レジスタをリストアするかもしれない前に Super/User を
806: // 復帰しておかないといけない
807: SetSR(sr);
808:
809: // DFビットセットなら命令を再実行するため、-(An) で変化
810: // したレジスタを巻き戻す。
811: // DFビットクリアなら例外要因の命令はハンドラで処理された
812: // とみなして、次の命令に進む。
813: if ((ssw & M68K::SSW_DF)) {
814: restore_reg_pd();
815: } else {
816: pc = stage_b - 2;
817: }
818: Jump(pc);
819: break;
820: }
821:
822: default:
823: PANIC("RTE frame=$%x not supported", frame >> 12);
824: break;
825: }
1.1.1.11! root 826: } catch (uint vector2) {
! 827: assertmsg(vector2 == M68K::EXCEP_BUSERR, "vector2=%u", vector2);
1.1.1.8 root 828:
829: // RTE 処理中のバスエラーはダブルバスフォールト。
830: DoubleBusFault("bus/address error handling");
831: }
832: }
833:
834: // STOP 命令。
835: void
836: MPU680x0Device::ops_stop()
837: {
1.1.1.11! root 838: uint32 data = fetch_2();
1.1.1.8 root 839: SetSR(data);
840: ChangeState(CPU_STATE_STOP);
841:
842: MakeNextFull();
1.1 root 843: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.