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