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