|
|
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 "m68030core.h"
8: #include "m68030bus.h"
9: #include "m68030fpu.h"
1.1.1.3 root 10: #include "debugger.h"
1.1 root 11:
1.1.1.5 ! root 12: static void m68030_update_vtime(m68kcpu *cpu);
1.1 root 13: static void m68030_change_intr(m68kcpu *, int);
14: static void m68030_set_intr(m68kcpu *, int);
15: static void m68030_double_bus_fault(m68kcpu *, const char *);
16:
17: // CPU コアの領域を確保して初期化
18: // reset_vector はリセット例外時に 0 番地に見せる(通常ROMの)アドレス。
19: m68kcpu *
20: m68030_init(uint32 reset_vector)
21: {
22: m68kcpu *cpu;
23:
24: cpu = new m68kcpu();
25: fpu_init(cpu);
26:
27: for (int i = 0; i < 16; i++) {
28: RegR(i) = 0xcccccccc;
29: }
30: // XXX ただし X68030 IPLROM には A6 を初期化せずに書き込んでいるバグが
31: // あるため、A6 を概ね RAM のあるあたりに指定しておく…。
32: RegA(6) = 0x000ccccc;
33:
34: // 手抜きのためリセットベクタを知っている
35: cpu->reset_vector = reset_vector;
36:
37: return cpu;
38: }
39:
1.1.1.5 ! root 40: // 仕掛中のサイクルを仮想時刻に反映させる。
! 41: void
! 42: m68030_update_vtime(m68kcpu *cpu)
! 43: {
! 44: if (cpu->used_cycle > cpu->goal_cycle) {
! 45: cpu->goal_cycle = cpu->used_cycle;
! 46: } else {
! 47: cpu->goal_cycle -= cpu->used_cycle;
! 48: }
! 49: cpu->total_vtime += cpu->Cycle2Vtime(cpu->used_cycle);
! 50: cpu->used_cycle = 0;
! 51: }
! 52:
! 53: // 仮想時刻を返す
! 54: uint64
! 55: m68030_get_vtime(m68kcpu *cpu)
! 56: {
! 57: if (cpu->used_cycle != 0) {
! 58: m68030_update_vtime(cpu);
! 59: }
! 60: return cpu->total_vtime;
! 61: }
! 62:
1.1 root 63: // 二重バスフォールト
64: static void
65: m68030_double_bus_fault(m68kcpu *cpu, const char *where)
66: {
1.1.1.2 root 67: cpu->atomic_reqflag |= CPU_REQ_RELEASE | CPU_REQ_HALT;
68:
69: // コールバックがあれば呼び出す
70: if (cpu->halt_callback) {
71: cpu->halt_callback(cpu->halt_arg);
72: }
1.1 root 73: }
74:
1.1.1.5 ! root 75: // リセット例外を発生させる。(外部ルーチン)
! 76: void
! 77: m68030_request_reset_exception(m68kcpu *cpu)
! 78: {
! 79: cpu->atomic_reqflag |= CPU_REQ_RESET;
! 80: }
! 81:
! 82: // リセット例外処理。
1.1 root 83: // 各レジスタを所定の状態にして、リセットベクタを読み込む。
84: // リセットベクタが読み込めなければ HALT 状態に移行。
85: void
86: m68030_exception_reset(m68kcpu *cpu)
87: {
1.1.1.4 root 88: // 履歴を初期化。リセット前の履歴いらんよな?
89: cpu->exhist.Clear();
90: cpu->brhist.Clear();
91:
92: // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
93: // リセット例外発生時の PC にはあまり意味がないので 0 にするか。
94: // 履歴のリセットベクタは 0 の代わりに 1 を使う仕様になっている。
95: const uint vector = 0;
1.1.1.5 ! root 96: cpu->exhist.AddEntry(0 | 1, 0, 0x8000 | (vector + 1));
! 97: cpu->brhist.AddEntry(0 | 1, 0, 0x8000 | (vector + 1));
1.1.1.4 root 98: // デバッガに通知 (例外ブレーク用)。こっちのベクタは 0 のままでよい。
99: debugger_notify_exception(vector);
1.1.1.3 root 100:
1.1 root 101: // 1. T0, T1 クリア
102: // 2. S をセット、M をクリア
103: // 3. 割り込みレベルを 7
104:
1.1.1.4 root 105: // 受け付け中だった割り込みをクリア
1.1.1.5 ! root 106: cpu->intr_pending = 0;
! 107: cpu->atomic_reqflag &= ~CPU_REQ_INTR;
1.1.1.4 root 108:
1.1 root 109: // ここは set_sr を使わずに設定する。
110: cpu->reg.s = true;
111: cpu->reg.m = false;
112: cpu->bus.SetFC2(cpu->reg.s);
113: // ここは割り込みレベル変化のチェックは不要でセットだけでいい。
114: m68030_set_intr(cpu, 7);
115:
116: // 4. VBR をクリア
117: RegVBR = 0;
118: // 5. キャッシュ
119: RegCACR = 0; // XXX
120: // 6. キャッシュエントリクリア
121: // 7. TC:E、TTn:E をクリア
122: cpu->SetTC(cpu->GetTC() & ~m68030TC::TC_E);
123: cpu->SetTT(0, cpu->GetTT(0) & ~m68030TT::E);
124: cpu->SetTT(1, cpu->GetTT(1) & ~m68030TT::E);
125: // 8. リセットベクタ生成
126:
127: // 9. SP をロード
128: // 10. PC をロード
129:
130: // 手抜き。
131: // XXX 本当は ROM を 0番地に見せかけてから 0番地からロードするのだが
132: // ぶっちゃけそれを外部から観測する手段もないのでなー。
133: // XXX FC を気にしないといけない
134: // XXX 読めなければ二重バスフォールト
135: // 本当はアドレスエラーもチェックするのだが、起きないので省略。
136: try {
137: RegA(7) = m68030_read_32(cpu, cpu->reset_vector);
138: RegPC = m68030_read_32(cpu, cpu->reset_vector + 4);
1.1.1.4 root 139: // リセットベクタは常に 0, 4 番地から取得したことになっている
1.1.1.5 ! root 140: cpu->brhist.AddEntry(0x00000004 | 1, RegPC, 0x8000);
1.1 root 141: } catch (int) {
142: m68030_double_bus_fault(cpu, "reset exception");
143: }
144: }
145:
1.1.1.5 ! root 146: // 割り込みを受理
1.1 root 147: void
1.1.1.5 ! root 148: m68030_interrupt(m68kcpu *cpu, int level)
1.1 root 149: {
1.1.1.5 ! root 150: // レベル比較等は VM 側で行ってあるので、ここは割り込みの受理。
! 151: // といってもフラグを立てるだけで実際の処理はメインループから呼ばれる
! 152: // m68030_exception_interrupt() (少し下にある) で行っている。
! 153: cpu->intr_pending = level;
1.1 root 154: cpu->atomic_reqflag |= CPU_REQ_INTR;
155: }
156:
157: // 割り込みレベルの設定のみを行う。
158: // 割り込みチェックは行わない。
159: static void
160: m68030_set_intr(m68kcpu *cpu, int level)
161: {
162: cpu->reg.intr_level = level;
163: }
164:
165: // 割り込みのチェックとレベルの変更。
166: // ステータスレジスタの割り込みレベルを変更し、必要な割り込みチェックを行う。
167: static void
168: m68030_change_intr(m68kcpu *cpu, int level)
169: {
170: int oldlevel = cpu->reg.intr_level;
171:
172: if (oldlevel < 7 && level == 7) {
173: // レベル 7 に引き上げられたら割り込み確認
174: cpu->atomic_reqflag |= CPU_REQ_INTR;
175: } else if (oldlevel == 7 && level < 7) {
176: // レベル 7 から引き下げられたら割り込み確認
177: cpu->atomic_reqflag |= CPU_REQ_INTR;
178: } else if (oldlevel != level) {
179: // それ以外でレベルが変わったら割り込み確認?
180: cpu->atomic_reqflag |= CPU_REQ_INTR;
181: }
182: m68030_set_intr(cpu, level);
183: }
184:
185: // SR (と CCR) に data を代入する。
186: // リセット例外時には呼ばれないのであっちで必要に応じて似た処理をする必要が
187: // ある。XXX なんとかしたほうがいい
188: void
189: m68030_set_sr(m68kcpu *cpu, uint16 data)
190: {
191: bool oldS = cpu->reg.s;
192: bool oldM = cpu->reg.m;
193:
194: // 現在の SP を保存
195: if (oldS == false) {
196: RegUSP = RegA(7);
197: } else if (oldM == false) {
198: RegISP = RegA(7);
199: } else {
200: RegMSP = RegA(7);
201: }
202:
203: CCR.Set(data);
204: cpu->reg.s = ((data & M68K_SR_S) != 0);
205: cpu->reg.m = ((data & M68K_SR_M) != 0);
206: m68030_change_intr(cpu, (data >> 8) & 7);
207:
208: // 新しい SP を用意
209: if (cpu->reg.s == false) {
210: RegA(7) = RegUSP;
211: } else if (cpu->reg.m == false) {
212: RegA(7) = RegISP;
213: } else {
214: RegA(7) = RegMSP;
215: }
216:
217: // バスの FC2 を変更
218: cpu->bus.SetFC2(cpu->reg.s);
219:
220: // XXX 他に何か状態変えないといけないことあったっけか。
221: }
222:
223: // 命令中に (An)+ で変化したアドレスレジスタを命令実行前の状態に巻き戻す。
224: // flag_pi は bit7..0 が save_pi[7..0] の A7..A0 に対応している。
225: void
226: m68030_restore_reg_pi(m68kcpu *cpu)
227: {
228: for (int i = 7; cpu->flag_pi; i--) {
229: if ((int8)cpu->flag_pi < 0) {
230: RegA(i) = cpu->save_pi[i];
231: }
232: cpu->flag_pi <<= 1;
233: }
234: }
235:
236: // 命令中に -(An) で変化したアドレスレジスタを命令実行前の状態に巻き戻す。
237: // flag_pd は bit7..0 が save_pd[7..0] の A7..A0 に対応している。
238: void
239: m68030_restore_reg_pd(m68kcpu *cpu)
240: {
241: for (int i = 7; cpu->flag_pd; i--) {
242: if ((int8)cpu->flag_pd < 0) {
243: RegA(i) = cpu->save_pd[i];
244: }
245: cpu->flag_pd <<= 1;
246: }
247: }
248:
249: // 例外処理について。
250: //
251: // 0. リセット例外
252: // リセット例外は外部から m68030_exception_reset() を呼び出す。
253: //
254: // 1. バスエラー例外
1.1.1.3 root 255: // MMU による論理アドレスでのバスエラーは (m68030_mmu_translate_*() が
256: // false を返した後) m68030bus.cpp ルーチンが (C++の) 例外をスローする。
257: // 物理アドレス空間アクセス後のバスエラーは (VM側の read*()/write*() が
258: // (int64)-1 を返した後) m68030bus.cpp ルーチンが (C++) の例外をスローする。
259: // どちらのケースも m68030core.cpp のメインループで catch し、その catch
260: // ブロック内で m68030_exception() を呼び出す。
1.1 root 261: //
262: // 2. アドレスエラー例外
263: // アドレスエラーは m68030_jump() 内でのみ発生するはずで、その場で
1.1.1.3 root 264: // m68030_exception() を呼んで処理する。
1.1 root 265: //
266: // 3. 命令中で起きる例外
267: // 命令途中で起きる(確定的な)例外は m68030_exception() を呼び出す。
268: // これに該当するのは
269: // o 特権違反
270: // o A系列/F系列命令
271: // o TRAP* 系命令、CHK/CHK2 命令
272: // o トレース例外 (未実装)
273: // o ゼロ除算?
274: // o MMU 構成例外
275: // o 不当命令 (*)
276: // である。
277: // ただし、不当命令についてはどこで不当命令と判定されるかによって
278: // フローが複数存在することに注意。
279: // o 1ワード目が不当命令
280: // -> 命令がそれぞれ m68030_exception() で処理。
281: // o 1ワード目の EA が不当命令
1.1.1.3 root 282: // -> (C++の)例外をスローして catch 内で m68030_exception() を実行。
1.1 root 283: // o 2ワード目が不当命令
284: // -> 命令がそれぞれ m68030_exception() で処理。
285: //
286: // 4. 割り込み例外
1.1.1.3 root 287: // 外部デバイスは m68030_interrpt() を呼び出すことで MPU に割り込みを要求
288: // する。その結果 MPU は直後の命令境界で割り込みをチェックし、
289: // m68030_exception_interrupt() を呼び出す。
290: //
1.1 root 291:
292: // vector にジャンプする。vector はベクタ番号(アドレスではない)。
293: static inline void
294: m68030_jump_vector(m68kcpu *cpu, int vector)
295: {
1.1.1.3 root 296: uint32 vecaddr;
1.1 root 297: uint32 newpc;
298:
1.1.1.3 root 299: vecaddr = RegVBR + vector * 4;
300: RegPC = vecaddr;
1.1 root 301: newpc = m68030_fetch_32(cpu);
1.1.1.3 root 302: // VBR 込みのジャンプ先が確定したところで今度はブランチ履歴に記録。
303: // この時のブランチ元はベクタアドレス
304: // (RegPC は fetch によって進んでしまうので先に保存)
1.1.1.5 ! root 305: cpu->brhist.AddEntry(vecaddr | (cpu->reg.s ? 1 : 0), newpc, 0x8000);
1.1.1.3 root 306:
307: CHECK_ADDRESS_ERROR(newpc);
308: RegPC = newpc;
1.1.1.2 root 309:
310: // 実際はここでパイプライン充填のために3ワードフェッチするので、
311: // ベクタテーブルはフェッチ出来たけどそのジャンプ先がバスエラーに
312: // なるケースはここでバスエラーが出なければいけないが、まだ
313: // キャッシュを実装していないので、ここで1ワードフェッチしてみて
314: // PC を戻すあほっぽい方法を取る。
315: m68030_fetch_16(cpu);
316: RegPC -= 2;
1.1 root 317: }
318:
319: // 例外スタックフレーム $0 を生成する。
320: // vector はベクタ番号。
321: // pc はスタックに積むプログラムカウンタの値 (例外によって、この命令位置か
322: // 次の命令位置かが異なる)。
323: // sr は例外発生時の SR。
324: static inline void
325: m68030_generate_exframe_0(m68kcpu *cpu, int vector, uint32 pc, uint16 sr)
326: {
327: // 例外スタックフレーム $0
328: // SP+$00.W STATUS REGISTER
329: // +$02.L PROGRAM COUNTER ; 例外ごとに異なる
330: // +$06.W %0000 | VECTOR
331:
332: m68030_push_16(cpu, (0 << 12) | (vector * 4));
333: m68030_push_32(cpu, pc);
334: m68030_push_16(cpu, sr);
335: }
336:
337: // 例外スタックフレーム $2 を生成する。
338: // vector はベクタ番号。
339: // スタックに積む PC は常に次の命令。
340: static inline void
341: m68030_generate_exframe_2(m68kcpu *cpu, int vector, uint16 sr)
342: {
343: // 例外スタックフレーム $2
344: // SP+$00.W STATUS REGISTER
345: // +$02.L PROGRAM COUNTER ; 次の命令位置
346: // +$06.W %0010 | VECTOR
347: // +$08.L INSTRUCTION ADDRESS ; 例外を起こした命令のアドレス
348:
349: m68030_push_32(cpu, RegPPC); // 例外を起こした命令のアドレス
350: m68030_push_16(cpu, (2 << 12) | (vector * 4));
351: m68030_push_32(cpu, RegPC); // 次の命令位置
352: m68030_push_16(cpu, sr);
353: }
354:
355: // 例外スタックフレーム $B を生成する。
356: static inline void
357: m68030_generate_exframe_b(m68kcpu *cpu, int vector, uint16 sr)
358: {
359: uint16 ssw;
360: uint32 fault_addr;
361: uint32 stageBaddr;
362:
363: stageBaddr = RegPC + 2;
364: // この後のスタック操作で bus が変化する前に保存
365: fault_addr = cpu->bus.laddr;
366: // DF ビットを立てておくと RTE した後この命令を再実行。
367: ssw = cpu->bus.ssw | SSW_DF;
368:
369: // 例外スタックフレーム $B
370: // SP+$00.W STATUS REGISTER
371: // +$02.L PROGRAM COUNTER
372: // +$06.W %1011 | VECTOR
373: // +$08.W (internal register)
374: // +$0a.W SPECIAL STATUS REGISTER ; SSW
375: // +$0c.W INSTRUCTION PIPE STAGE C
376: // +$0e.W INSTRUCTION PIPE STAGE B
377: // +$10.L DATA CYCLE FAULT ADDRESS
378: // +$14.W*2 (internal register)
379: // +$18.L DATA OUTPUT BUFFER
380: // +$1c.W*4 (internal register, 4 words)
381: // +$24.L STAGE B ADDRESS
382: // +$28.W*2 (internal register, 2 words)
383: // +$2c.L DATA INPUT BUFFER
384: // +$30.W*3 (internal register, 3 words)
385: // +$36.W VERSION# | internal information
386: // +$38.W*18 (internal register, 18 words)
387: // +$5a
388: // こっちから上に向かって PUSH
389:
390: // internal registers, 18 words
391: // 内部レジスタ18ワードのうち、ここでは17ワード分を save_pd[8] (16ワード)、
392: // flag_pd (1ワード) として使うことにする。
393: // -(An) でバスエラーが起きると An は減算された状態になるが、DF ビットが
394: // 立っていて再実行する時には現行構造では命令先頭から再実行せざるをえず、
395: // そうするともう一度プリデクリメントが動いてしまう。そのため、RTE の時点
396: // で DF=1 ならプリデクリメント発生前の An に戻すという処理をする。
397: // RTE 時点で DF=0 なら命令を再実行しないため An も巻き戻してはならず、
398: // そのためプリデクリメント分の巻き戻しは RTE 時点まで行えない。
1.1.1.2 root 399: // 一方、ポストインクリメントのケースはこの少し下の
400: // m68030_exception_buserr() 内のコメント参照。
1.1 root 401: for (int i = 7; i >= 0; i--) {
402: m68030_push_32(cpu, cpu->save_pd[i]);
403: }
404: m68030_push_16(cpu, cpu->flag_pd);
405: RegA(7) -= 2;
406:
407: RegA(7) -= 2; // VERSION#
408: RegA(7) -= 6;
409: m68030_push_32(cpu, 0); // DATA INPUT BUFFER
410: RegA(7) -= 4;
411: m68030_push_32(cpu, stageBaddr); // STAGE B ADDRESS
412: RegA(7) -= 8;
413: m68030_push_32(cpu, 0); // DATA OUTPUT BUFFER
414: RegA(7) -= 4;
415: m68030_push_32(cpu, fault_addr); // DATA CYCLE FAULT ADDRESS
416: m68030_push_16(cpu, 0); // INSTRUCTION PIPE STAGE B
417: m68030_push_16(cpu, 0); // INSTRUCTION PIPE STAGE C
418: m68030_push_16(cpu, ssw);
419: RegA(7) -= 2;
420: m68030_push_16(cpu, 0xb000 | (vector << 2));
421: // 本当は現在の PC だが、再実行のため PPC を積む。
422: m68030_push_32(cpu, RegPPC); // PC
423: m68030_push_16(cpu, sr); // 例外入り口で保存した SR
424: }
425:
426: // 例外処理 (リセット・割り込み以外) を行う。
427: // vector はベクタ番号。
428: void
429: m68030_exception(m68kcpu *cpu, int vector)
430: {
1.1.1.2 root 431: // バスエラー・アドレスエラーは別で処理。
432: if (vector <= M68K_EXCEP_ADDRERR) {
433: m68030_exception_buserr(cpu, vector);
434: return;
435: }
436:
1.1.1.3 root 437: // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
1.1.1.5 ! root 438: uint32 from = RegPPC | (cpu->reg.s ? 1 : 0);
! 439: cpu->exhist.AddEntry(from, 0, 0x8000 | vector);
! 440: cpu->brhist.AddEntry(from, 0, 0x8000 | vector);
1.1.1.3 root 441: // デバッガに通知 (例外ブレーク用)
1.1.1.4 root 442: debugger_notify_exception(vector);
1.1.1.3 root 443:
1.1 root 444: // 状態を保存
445: uint16 sr = RegSR;
446:
447: // S を立てて T1,T0 を落とす
448: m68030_set_sr(cpu, (RegSR | M68K_SR_S) & ~M68K_SR_T);
449:
1.1.1.2 root 450: try {
451: switch (vector) {
452: case M68K_EXCEP_ILLEGAL: // 不当命令
453: case M68K_EXCEP_PRIV: // 特権違反例外
454: case M68K_EXCEP_ALINE: // Aライン命令例外
455: case M68K_EXCEP_FLINE: // Fライン命令例外
456: // スタックに積む PC はこの命令のアドレス
457: m68030_generate_exframe_0(cpu, vector, RegPPC, sr);
458: break;
459:
460: case M68K_EXCEP_TRAP0 // TRAP#0
461: ... M68K_EXCEP_TRAP15: // TRAP#15
462: // スタックに積む PC は次の命令
463: m68030_generate_exframe_0(cpu, vector, RegPC, sr);
464: break;
465:
466: case M68K_EXCEP_ZERODIV: // ゼロ除算
467: case M68K_EXCEP_CHK: // CHK
468: case M68K_EXCEP_TRAPV: // TRAPV
469: case M68K_EXCEP_MMU_CONFIG: // MMU 構成例外
470: m68030_generate_exframe_2(cpu, vector, sr);
471: break;
472:
473: default:
474: PANIC("$%08x exception vector %d ?", RegPPC, vector);
475: break;
1.1 root 476: }
477:
1.1.1.2 root 478: // ベクタにジャンプ
479: m68030_jump_vector(cpu, vector);
480: } catch (int vector2) {
481: assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector=%d", vector);
1.1 root 482:
1.1.1.2 root 483: // バスエラー処理へ
484: m68030_exception_buserr(cpu, vector2);
1.1 root 485: }
1.1.1.2 root 486: }
487:
488: // バスエラー・アドレスエラー例外処理を行う。
489: // 他の例外とは違いが多いので分けた。
490: void
491: m68030_exception_buserr(m68kcpu *cpu, int vector)
492: {
493: assertmsg((vector == M68K_EXCEP_BUSERR || vector == M68K_EXCEP_ADDRERR),
494: "vector=%d", vector);
495:
1.1.1.3 root 496: // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
1.1.1.5 ! root 497: uint32 from = RegPPC | (cpu->reg.s ? 1 : 0);
! 498: cpu->exhist.AddEntry(from, 0, 0x8000 | vector);
! 499: cpu->brhist.AddEntry(from, 0, 0x8000 | vector);
1.1.1.3 root 500: // デバッガに通知 (例外ブレーク用)
1.1.1.4 root 501: debugger_notify_exception(vector);
1.1.1.3 root 502:
1.1.1.2 root 503: // 状態を保存
504: uint16 sr = RegSR;
1.1 root 505:
1.1.1.2 root 506: // ここで (An)+ で変化したレジスタを命令実行前の状態に巻き戻す。
507: // EA 処理の時点で先にインクリメントしているのだが、バスエラーが
508: // 起きたということは実際にはポストインクリメントは発生しないので、
509: // ここで無条件に巻き戻してよい。SR を書き換えると A7 が切り替わって
510: // しまうので、この巻き戻しは SR を書き換える前のここで行うこと。
511: // 一方、プリデクリメントのケースはこの少し上の exframe_b の中の
512: // コメント参照。
513: m68030_restore_reg_pi(cpu);
514:
515: // S を立てて T1,T0 を落とす
516: m68030_set_sr(cpu, (RegSR | M68K_SR_S) & ~M68K_SR_T);
517:
518: try {
519: // フレームを作成。
520: // 実際はショート($A)、ロング($B)の2種類あって、本来ならショートが
521: // 使えればショートを使うのだが、ここでは全部ロングを使う。
522: m68030_generate_exframe_b(cpu, vector, sr);
523: // ベクタにジャンプ
524: m68030_jump_vector(cpu, vector);
525: } catch (int vector2) {
526: // ここに来るのはバスエラーだけのはず
527: assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector=%d", vector);
528:
529: // バスエラー・アドレスエラー処理中のバスエラーは
530: // ダブルバスフォールト。
531: m68030_double_bus_fault(cpu, "bus/address error handling");
532: }
1.1 root 533: }
534:
535: // 割り込み例外処理を行う。
536: // 割り込みチェックは m68030core.cpp::m68030_flush_interrupt() で判定している。
537: void
1.1.1.5 ! root 538: m68030_exception_interrupt(m68kcpu *cpu, int level)
1.1 root 539: {
1.1.1.5 ! root 540: // ユーザーズマニュアル(日本語) p.399 図8-5 の中ほど
! 541: // 「/IPEND をネゲート、割り込みアクノリッジサイクルを実行」
! 542: // 以降に相当。
! 543:
! 544: // 割り込みアクノリッジサイクルを実行
! 545: int vector = m68030_interrupt_acknowledge(level);
! 546: if (vector == -1) {
! 547: vector = 24 + level;
! 548: }
! 549:
1.1.1.3 root 550: // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
1.1.1.5 ! root 551: uint32 from = RegPPC | (cpu->reg.s ? 1 : 0);
! 552: uint32 inst = 0x8000 | (level << 8) | vector;
! 553: cpu->exhist.AddEntry(from, 0, inst);
! 554: cpu->brhist.AddEntry(from, 0, inst);
1.1.1.3 root 555: // デバッガに通知 (例外ブレーク用)
1.1.1.4 root 556: debugger_notify_exception(vector);
1.1 root 557:
558: // 状態を保存
559: uint16 sr = RegSR;
560:
561: // S を立てて T1,T0 を落とす
562: m68030_set_sr(cpu, (RegSR | M68K_SR_S) & ~M68K_SR_T);
563: // 割り込みレベルを変更
564: m68030_change_intr(cpu, level);
565:
1.1.1.2 root 566: try {
567: // 例外スタックフレーム $0 を生成。
568: // スタックに積む PC は次の命令位置。
569: // M ビットは未実装なのでスローアウェイとか無視
570: m68030_generate_exframe_0(cpu, vector, RegPC, sr);
1.1 root 571:
1.1.1.2 root 572: // ベクタにジャンプ
573: m68030_jump_vector(cpu, vector);
574: } catch (int vector2) {
575: assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector=%d", vector);
576:
577: // バスエラー処理へ
578: m68030_exception_buserr(cpu, vector2);
579: }
1.1 root 580: }
581:
582: // RTE 命令。
583: // 例外処理の近くに置いておくほうが便利なので。
584: void
585: m68030_rte(m68kcpu *cpu)
586: {
587: uint32 pc;
588: uint16 sr;
589: uint16 frame;
590:
1.1.1.2 root 591: try {
592: sr = m68030_pop_16(cpu);
593: pc = m68030_pop_32(cpu);
594: frame = m68030_pop_16(cpu);
595: switch (frame >> 12) {
596: case 0:
597: CYCLE2(18, 20);
598: m68030_set_sr(cpu, sr);
599: m68030_jump(cpu, pc);
600: break;
601:
602: case 2:
603: CYCLE2(18, 20);
604: // +08.L INSTRUCTION ADDRESS を読み捨てる
605: RegA(7) += 4;
606: m68030_set_sr(cpu, sr);
607: m68030_jump(cpu, pc);
608: break;
609:
610: case 0x0b:
611: {
612: uint32 stage_b;
613: uint16 ssw;
614:
615: CYCLE(76);
616: RegA(7) += 2; // $08.W
617: ssw = m68030_pop_16(cpu); // $0a.W SSW
618: RegA(7) += 4 * 6; // $0c.L*6
619: stage_b = m68030_pop_32(cpu); // $24.L Stage B
620: RegA(7) += 2 * 9; // $28.W*9
621: // $3a..$58 ここが save_pd の取り出し
622: cpu->flag_pd = m68030_pop_16(cpu);
623: for (int i = 0; i < 8; i++) {
624: cpu->save_pd[i] = m68030_pop_32(cpu);
625: }
626: // A7 レジスタをリストアするかもしれない前に Super/User を
627: // 復帰しておかないといけない
628: m68030_set_sr(cpu, sr);
629:
630: // DFビットセットなら命令を再実行するため、-(An) で変化
631: // したレジスタを巻き戻す。
632: // DFビットクリアなら例外要因の命令はハンドラで処理された
633: // とみなして、次の命令に進む。
634: if ((ssw & SSW_DF)) {
635: m68030_restore_reg_pd(cpu);
636: } else {
637: pc = stage_b - 2;
638: }
639: m68030_jump(cpu, pc);
640: break;
641: }
642:
643: default:
644: PANIC("RTE frame=$%x not supported", frame >> 12);
645: break;
1.1 root 646: }
1.1.1.2 root 647: } catch (int vector2) {
648: assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector2=%d", vector2);
1.1 root 649:
1.1.1.2 root 650: // RTE 処理中のバスエラーはダブルバスフォールト。
651: m68030_double_bus_fault(cpu, "bus/address error handling");
1.1 root 652: }
653: }
654:
655: static const char * const exception_name_table[] = {
1.1.1.3 root 656: // 0123456789012345678 <- 例外履歴欄の横幅
1.1 root 657: /* 0 */ "Reset",
658: /* 1 */ NULL,
659: /* 2 */ "Bus Error",
660: /* 3 */ "Address Error",
661: /* 4 */ "Illegal Instruction",
1.1.1.3 root 662: /* 5 */ "Zero Divide",
663: /* 6 */ "CHK/CHK2 Insn",
664: /* 7 */ "TRAPV, *TRAPcc Insn",
665: /* 8 */ "PriviledgeViolation",
1.1 root 666: /* 9 */ "Trace",
1.1.1.3 root 667: /* 10 */ "Line 1010 emulator",
668: /* 11 */ "Line 1111 emulator",
1.1 root 669: /* 12 */ NULL,
1.1.1.3 root 670: /* 13 */ "CoproProtoViolation",
671: /* 14 */ "Format Error",
672: /* 15 */ "Uninitialized Intr",
673: /* 16 */ NULL, NULL, NULL, NULL,
674: /* 20 */ NULL, NULL, NULL, NULL,
1.1 root 675: /* 24 */ "Spurious Interrupt",
676: /* 25 */ "Level 1 Interrupt",
677: /* 26 */ "Level 2 Interrupt",
678: /* 27 */ "Level 3 Interrupt",
679: /* 28 */ "Level 4 Interrupt",
680: /* 29 */ "Level 5 Interrupt",
681: /* 30 */ "Level 6 Interrupt",
682: /* 31 */ "Level 7 Interrupt",
683: /* 32 */ "Trap #0",
684: /* 33 */ "Trap #1",
685: /* 34 */ "Trap #2",
686: /* 35 */ "Trap #3",
687: /* 36 */ "Trap #4",
688: /* 37 */ "Trap #5",
689: /* 38 */ "Trap #6",
690: /* 39 */ "Trap #7",
691: /* 40 */ "Trap #8",
692: /* 41 */ "Trap #9",
693: /* 42 */ "Trap #10",
694: /* 43 */ "Trap #11",
695: /* 44 */ "Trap #12",
696: /* 45 */ "Trap #13",
697: /* 46 */ "Trap #14",
698: /* 47 */ "Trap #15",
1.1.1.3 root 699: // 0123456789012345678
1.1 root 700: /* 48 */ "FPCP Branch",
1.1.1.3 root 701: /* 49 */ "FPCP InexcactResult",
702: /* 50 */ "FPCP Divide by Zero",
1.1 root 703: /* 51 */ "FPCP UnderFlow",
1.1.1.3 root 704: /* 52 */ "FPCP Operand Error",
1.1 root 705: /* 53 */ "FPCP OverFlow",
1.1.1.3 root 706: /* 54 */ "FPCP Signaling NAN",
1.1 root 707: /* 55 */ NULL,
1.1.1.3 root 708: /* 56 */ "MMU Config Error",
1.1 root 709: /* 57 */ NULL,
710: /* 58 */ NULL,
711: /* 59 */ NULL,
712: /* 60 */ NULL,
713: /* 61 */ NULL,
714: /* 62 */ NULL,
715: /* 63 */ NULL,
1.1.1.3 root 716: // 0123456789012345678
717: /* $40 */ "MFP Alarm",
718: /* $41 */ "MFP EXPON",
719: /* $42 */ "MFP POWSW",
720: /* $43 */ "MFP FM IRQ",
721: /* $44 */ "MFP Timer-D",
722: /* $45 */ "MFP Timer-C",
723: /* $46 */ "MFP V-Disp",
724: /* $47 */ "MFP GPIP5",
725: /* $48 */ "MFP Timer-B",
726: /* $49 */ "MFP TX Error",
727: /* $4a */ "MFP TX Empty",
728: /* $4b */ "MFP RX Error",
729: /* $4c */ "MFP RX Full",
730: /* $4d */ "MFP Timer-A",
731: /* $4e */ "MFP CRTC IRQ",
732: /* $4f */ "MFP H-Sync",
733: /* $50 */ "SCC#B TX Empty",
734: /* $51 */ "SCC#B TX Empty",
735: /* $52 */ "SCC#B",
736: /* $53 */ "SCC#B",
737: /* $54 */ "SCC#B",
738: /* $55 */ "SCC#B",
739: /* $56 */ "SCC#B",
740: /* $57 */ "SCC#B",
741: /* $58 */ "SCC#A TX Empty",
742: /* $59 */ "SCC#A TX Empty",
743: /* $5a */ "SCC#A",
744: /* $5b */ "SCC#A",
745: /* $5c */ "SCC#A",
746: /* $5d */ "SCC#A",
747: /* $5e */ "SCC#A",
748: /* $5f */ "SCC#A",
749:
750: // 0123456789012345678
751: /* $60 */ "I/O FDC Intr",
752: /* $61 */ "I/O FDD Intr",
753: /* $62 */ "I/O HDC Intr",
754: /* $63 */ "I/O PRN Intr",
755: /* $64 */ "DMAC#0 Complete",
756: /* $65 */ "DMAC#0 Error",
757: /* $66 */ "DMAC#1 Complete",
758: /* $67 */ "DMAC#1 Error",
759: /* $68 */ "DMAC#2 Complete",
760: /* $69 */ "DMAC#2 Error",
761: /* $6a */ "DMAC#3 Complete",
762: /* $6b */ "DMAC#3 Error",
763: /* $6c */ "SPC Interrupt",
764: /* $6d */ NULL,
765: /* $6e */ NULL,
766: /* $6f */ NULL,
767:
768: /* $70 */ NULL, NULL, NULL, NULL,
769: /* $74 */ NULL, NULL, NULL, NULL,
770: /* $78 */ NULL, NULL, NULL, NULL,
771: /* $7c */ NULL, NULL, NULL, NULL,
772: /* $80 */ NULL, NULL, NULL, NULL, // MIDI
773: /* $84 */ NULL, NULL, NULL, NULL, // MIDI
774: /* $88 */ NULL, NULL, NULL, NULL, // MIDI
775: /* $8c */ NULL, NULL, NULL, NULL, // MIDI
776: /* $90 */ NULL, NULL, NULL, NULL,
777: /* $94 */ NULL, NULL, NULL, NULL,
778: /* $98 */ NULL, NULL, NULL, NULL,
779: /* $9c */ NULL, NULL, NULL, NULL,
780: /* $a0 */ NULL, NULL, NULL, NULL, // MIDI
781: /* $a4 */ NULL, NULL, NULL, NULL, // MIDI
782: /* $a8 */ NULL, NULL, NULL, NULL, // MIDI
783: /* $ac */ NULL, NULL, NULL, NULL, // MIDI
784: /* $b0 */ NULL, NULL, NULL, NULL, // RS-232C
785: /* $b4 */ NULL, NULL, NULL, NULL, // RS-232C
786: /* $b8 */ NULL, NULL, NULL, NULL, // RS-232C
787: /* $bc */ NULL, NULL, NULL, NULL, // RS-232C
788: /* $c0 */ NULL, NULL, NULL, NULL, // RS-232C
789: /* $c4 */ NULL, NULL, NULL, NULL, // RS-232C
790: /* $c8 */ NULL, NULL, NULL, NULL, // RS-232C
791: /* $cc */ NULL, NULL, NULL, NULL, // RS-232C
792: /* $d0 */ NULL, NULL, NULL, NULL,
793: /* $d4 */ NULL, NULL, NULL, NULL,
794: /* $d8 */ NULL, NULL, NULL, NULL,
795: /* $dc */ NULL, NULL, NULL, NULL,
796: /* $e0 */ NULL, NULL, NULL, NULL,
797: /* $e4 */ NULL, NULL, NULL, NULL,
798: /* $e8 */ NULL, NULL, NULL, NULL,
799: /* $ec */ NULL, NULL, NULL, NULL,
800:
801: // 0123456789012345678
802: /* $f0 */ "PSX16x50 Interrupt",
803: /* $f1 */ NULL,
804: /* $f2 */ NULL,
805: /* $f3 */ NULL,
806: /* $f4 */ NULL,
807: /* $f5 */ NULL,
808: /* $f6 */ "ExSPC Interrupt",
809: /* $f7 */ NULL, // TS-6BSI
810: /* $f8 */ "Nereid#1 Interrupt",
811: /* $f9 */ "Neptune-X Interrupt",
812: /* $fa */ "Nereid#1 USB",
813: /* $fb */ "Nereid#0 USB",
814: /* $fc */ NULL,
815: /* $fd */ NULL,
816: /* $fe */ NULL,
817: /* $ff */ NULL,
1.1 root 818: };
819:
820: // ベクタ番号から表示名を返す
821: const char *
822: m68030_get_exception_name(int vector)
823: {
824: const char *name = NULL;
825: if (vector < countof(exception_name_table)) {
826: name = exception_name_table[vector];
827: }
828: if (name == NULL) {
829: name = "(reserved?)";
830: }
831: return name;
832: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.