|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2017 [email protected]
4: //
5:
6: #include "header.h"
7: #include "m68030core.h"
8: #include "m68030bus.h"
9: #include "m68030fpu.h"
10:
11: static void m68030_change_intr(m68kcpu *, int);
12: static void m68030_set_intr(m68kcpu *, int);
13: static void m68030_double_bus_fault(m68kcpu *, const char *);
14:
15: // CPU コアの領域を確保して初期化
16: // reset_vector はリセット例外時に 0 番地に見せる(通常ROMの)アドレス。
17: m68kcpu *
18: m68030_init(uint32 reset_vector)
19: {
20: m68kcpu *cpu;
21:
22: cpu = new m68kcpu();
23: fpu_init(cpu);
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: // 手抜きのためリセットベクタを知っている
33: cpu->reset_vector = reset_vector;
34:
35: return cpu;
36: }
37:
38: // 二重バスフォールト
39: // XXX どうするかね
40: static void
41: m68030_double_bus_fault(m68kcpu *cpu, const char *where)
42: {
43: PANIC("double bus fault in %s", where);
44: }
45:
46: // リセット例外を発生させる。
47: // 各レジスタを所定の状態にして、リセットベクタを読み込む。
48: // リセットベクタが読み込めなければ HALT 状態に移行。
49: void
50: m68030_exception_reset(m68kcpu *cpu)
51: {
52: // 1. T0, T1 クリア
53: // 2. S をセット、M をクリア
54: // 3. 割り込みレベルを 7
55:
56: // ここは set_sr を使わずに設定する。
57: cpu->reg.s = true;
58: cpu->reg.m = false;
59: cpu->bus.SetFC2(cpu->reg.s);
60: // ここは割り込みレベル変化のチェックは不要でセットだけでいい。
61: m68030_set_intr(cpu, 7);
62:
63: // 4. VBR をクリア
64: RegVBR = 0;
65: // 5. キャッシュ
66: RegCACR = 0; // XXX
67: // 6. キャッシュエントリクリア
68: // 7. TC:E、TTn:E をクリア
69: cpu->SetTC(cpu->GetTC() & ~m68030TC::TC_E);
70: cpu->SetTT(0, cpu->GetTT(0) & ~m68030TT::E);
71: cpu->SetTT(1, cpu->GetTT(1) & ~m68030TT::E);
72: // 8. リセットベクタ生成
73:
74: // 9. SP をロード
75: // 10. PC をロード
76:
77: // 手抜き。
78: // XXX 本当は ROM を 0番地に見せかけてから 0番地からロードするのだが
79: // ぶっちゃけそれを外部から観測する手段もないのでなー。
80: // XXX FC を気にしないといけない
81: // XXX 読めなければ二重バスフォールト
82: // 本当はアドレスエラーもチェックするのだが、起きないので省略。
83: try {
84: RegA(7) = m68030_read_32(cpu, cpu->reset_vector);
85: RegPC = m68030_read_32(cpu, cpu->reset_vector + 4);
86: } catch (int) {
87: m68030_double_bus_fault(cpu, "reset exception");
88: }
89: }
90:
91: // MPU 外部からの割り込み受付。
92: // オートベクタの場合は vector にオートベクタの例外ベクタを指定のこと。
93: void
94: m68030_interrupt(m68kcpu *cpu, int level, int vector)
95: {
96: std::unique_lock<std::mutex> lock(cpu->intrmtx);
97:
98: // この割り込みレベルがすでにペンディングなら何もしない
99: if ((int8)(cpu->intr_pending << level) < 0) {
100: return;
101: }
102:
103: // 割り込みを受理
104: cpu->intr_pending |= (0x80U >> level);
105: cpu->intr_vector[level] = vector;
106: cpu->atomic_reqflag |= CPU_REQ_INTR;
107: }
108:
109: // 割り込みレベルの設定のみを行う。
110: // 割り込みチェックは行わない。
111: static void
112: m68030_set_intr(m68kcpu *cpu, int level)
113: {
114: cpu->reg.intr_level = level;
115: cpu->intr_mask = 0x7f >> level;
116: }
117:
118: // 割り込みのチェックとレベルの変更。
119: // ステータスレジスタの割り込みレベルを変更し、必要な割り込みチェックを行う。
120: static void
121: m68030_change_intr(m68kcpu *cpu, int level)
122: {
123: int oldlevel = cpu->reg.intr_level;
124:
125: if (oldlevel < 7 && level == 7) {
126: // レベル 7 に引き上げられたら割り込み確認
127: cpu->atomic_reqflag |= CPU_REQ_INTR;
128: } else if (oldlevel == 7 && level < 7) {
129: // レベル 7 から引き下げられたら割り込み確認
130: cpu->atomic_reqflag |= CPU_REQ_INTR;
131: } else if (oldlevel != level) {
132: // それ以外でレベルが変わったら割り込み確認?
133: cpu->atomic_reqflag |= CPU_REQ_INTR;
134: }
135: m68030_set_intr(cpu, level);
136: }
137:
138: // SR (と CCR) に data を代入する。
139: // リセット例外時には呼ばれないのであっちで必要に応じて似た処理をする必要が
140: // ある。XXX なんとかしたほうがいい
141: void
142: m68030_set_sr(m68kcpu *cpu, uint16 data)
143: {
144: bool oldS = cpu->reg.s;
145: bool oldM = cpu->reg.m;
146:
147: // 現在の SP を保存
148: if (oldS == false) {
149: RegUSP = RegA(7);
150: } else if (oldM == false) {
151: RegISP = RegA(7);
152: } else {
153: RegMSP = RegA(7);
154: }
155:
156: CCR.Set(data);
157: cpu->reg.s = ((data & M68K_SR_S) != 0);
158: cpu->reg.m = ((data & M68K_SR_M) != 0);
159: m68030_change_intr(cpu, (data >> 8) & 7);
160:
161: // 新しい SP を用意
162: if (cpu->reg.s == false) {
163: RegA(7) = RegUSP;
164: } else if (cpu->reg.m == false) {
165: RegA(7) = RegISP;
166: } else {
167: RegA(7) = RegMSP;
168: }
169:
170: // バスの FC2 を変更
171: cpu->bus.SetFC2(cpu->reg.s);
172:
173: // XXX 他に何か状態変えないといけないことあったっけか。
174: }
175:
176: // 命令中に (An)+ で変化したアドレスレジスタを命令実行前の状態に巻き戻す。
177: // flag_pi は bit7..0 が save_pi[7..0] の A7..A0 に対応している。
178: void
179: m68030_restore_reg_pi(m68kcpu *cpu)
180: {
181: for (int i = 7; cpu->flag_pi; i--) {
182: if ((int8)cpu->flag_pi < 0) {
183: RegA(i) = cpu->save_pi[i];
184: }
185: cpu->flag_pi <<= 1;
186: }
187: }
188:
189: // 命令中に -(An) で変化したアドレスレジスタを命令実行前の状態に巻き戻す。
190: // flag_pd は bit7..0 が save_pd[7..0] の A7..A0 に対応している。
191: void
192: m68030_restore_reg_pd(m68kcpu *cpu)
193: {
194: for (int i = 7; cpu->flag_pd; i--) {
195: if ((int8)cpu->flag_pd < 0) {
196: RegA(i) = cpu->save_pd[i];
197: }
198: cpu->flag_pd <<= 1;
199: }
200: }
201:
202: // 例外処理について。
203: //
204: // 0. リセット例外
205: // リセット例外は外部から m68030_exception_reset() を呼び出す。
206: //
207: // 1. バスエラー例外
208: // 物理アドレス空間アクセス後のバスエラーは、物理空間側(VM側)から
209: // (C++の)例外がスローされるので、catch したところで処理する。
210: //
211: // MMU による論理アドレスでのバスエラーについては後で考える。
212: //
213: // 2. アドレスエラー例外
214: // アドレスエラーは m68030_jump() 内でのみ発生するはずで、その場で
215: // 例外処理をするはず。
216: //
217: // 3. 命令中で起きる例外
218: // 命令途中で起きる(確定的な)例外は m68030_exception() を呼び出す。
219: // これに該当するのは
220: // o 特権違反
221: // o A系列/F系列命令
222: // o TRAP* 系命令、CHK/CHK2 命令
223: // o トレース例外 (未実装)
224: // o ゼロ除算?
225: // o MMU 構成例外
226: // o 不当命令 (*)
227: // である。
228: // ただし、不当命令についてはどこで不当命令と判定されるかによって
229: // フローが複数存在することに注意。
230: // o 1ワード目が不当命令
231: // -> 命令がそれぞれ m68030_exception() で処理。
232: // o 1ワード目の EA が不当命令
233: // -> (C++の)例外スローして catch 内で処理。
234: // o 2ワード目が不当命令
235: // -> 命令がそれぞれ m68030_exception() で処理。
236: //
237: // 4. 割り込み例外
238: // zzz
239:
240: // vector にジャンプする。vector はベクタ番号(アドレスではない)。
241: static inline void
242: m68030_jump_vector(m68kcpu *cpu, int vector)
243: {
244: uint32 newpc;
245:
246: RegPC = RegVBR + vector * 4;
247: newpc = m68030_fetch_32(cpu);
248: m68030_jump(cpu, newpc);
249: }
250:
251: // 例外スタックフレーム $0 を生成する。
252: // vector はベクタ番号。
253: // pc はスタックに積むプログラムカウンタの値 (例外によって、この命令位置か
254: // 次の命令位置かが異なる)。
255: // sr は例外発生時の SR。
256: static inline void
257: m68030_generate_exframe_0(m68kcpu *cpu, int vector, uint32 pc, uint16 sr)
258: {
259: // 例外スタックフレーム $0
260: // SP+$00.W STATUS REGISTER
261: // +$02.L PROGRAM COUNTER ; 例外ごとに異なる
262: // +$06.W %0000 | VECTOR
263:
264: m68030_push_16(cpu, (0 << 12) | (vector * 4));
265: m68030_push_32(cpu, pc);
266: m68030_push_16(cpu, sr);
267: }
268:
269: // 例外スタックフレーム $2 を生成する。
270: // vector はベクタ番号。
271: // スタックに積む PC は常に次の命令。
272: static inline void
273: m68030_generate_exframe_2(m68kcpu *cpu, int vector, uint16 sr)
274: {
275: // 例外スタックフレーム $2
276: // SP+$00.W STATUS REGISTER
277: // +$02.L PROGRAM COUNTER ; 次の命令位置
278: // +$06.W %0010 | VECTOR
279: // +$08.L INSTRUCTION ADDRESS ; 例外を起こした命令のアドレス
280:
281: m68030_push_32(cpu, RegPPC); // 例外を起こした命令のアドレス
282: m68030_push_16(cpu, (2 << 12) | (vector * 4));
283: m68030_push_32(cpu, RegPC); // 次の命令位置
284: m68030_push_16(cpu, sr);
285: }
286:
287: // 例外スタックフレーム $B を生成する。
288: static inline void
289: m68030_generate_exframe_b(m68kcpu *cpu, int vector, uint16 sr)
290: {
291: uint16 ssw;
292: uint32 fault_addr;
293: uint32 stageBaddr;
294:
295: stageBaddr = RegPC + 2;
296: // この後のスタック操作で bus が変化する前に保存
297: fault_addr = cpu->bus.laddr;
298: // DF ビットを立てておくと RTE した後この命令を再実行。
299: ssw = cpu->bus.ssw | SSW_DF;
300:
301: // 例外スタックフレーム $B
302: // SP+$00.W STATUS REGISTER
303: // +$02.L PROGRAM COUNTER
304: // +$06.W %1011 | VECTOR
305: // +$08.W (internal register)
306: // +$0a.W SPECIAL STATUS REGISTER ; SSW
307: // +$0c.W INSTRUCTION PIPE STAGE C
308: // +$0e.W INSTRUCTION PIPE STAGE B
309: // +$10.L DATA CYCLE FAULT ADDRESS
310: // +$14.W*2 (internal register)
311: // +$18.L DATA OUTPUT BUFFER
312: // +$1c.W*4 (internal register, 4 words)
313: // +$24.L STAGE B ADDRESS
314: // +$28.W*2 (internal register, 2 words)
315: // +$2c.L DATA INPUT BUFFER
316: // +$30.W*3 (internal register, 3 words)
317: // +$36.W VERSION# | internal information
318: // +$38.W*18 (internal register, 18 words)
319: // +$5a
320: // こっちから上に向かって PUSH
321:
322: // internal registers, 18 words
323: // 内部レジスタ18ワードのうち、ここでは17ワード分を save_pd[8] (16ワード)、
324: // flag_pd (1ワード) として使うことにする。
325: // -(An) でバスエラーが起きると An は減算された状態になるが、DF ビットが
326: // 立っていて再実行する時には現行構造では命令先頭から再実行せざるをえず、
327: // そうするともう一度プリデクリメントが動いてしまう。そのため、RTE の時点
328: // で DF=1 ならプリデクリメント発生前の An に戻すという処理をする。
329: // RTE 時点で DF=0 なら命令を再実行しないため An も巻き戻してはならず、
330: // そのためプリデクリメント分の巻き戻しは RTE 時点まで行えない。
331: // 一方、ポストインクリメントのケースはこの少し下の m68030_exception()
332: // 内のコメント参照。
333: for (int i = 7; i >= 0; i--) {
334: m68030_push_32(cpu, cpu->save_pd[i]);
335: }
336: m68030_push_16(cpu, cpu->flag_pd);
337: RegA(7) -= 2;
338:
339: RegA(7) -= 2; // VERSION#
340: RegA(7) -= 6;
341: m68030_push_32(cpu, 0); // DATA INPUT BUFFER
342: RegA(7) -= 4;
343: m68030_push_32(cpu, stageBaddr); // STAGE B ADDRESS
344: RegA(7) -= 8;
345: m68030_push_32(cpu, 0); // DATA OUTPUT BUFFER
346: RegA(7) -= 4;
347: m68030_push_32(cpu, fault_addr); // DATA CYCLE FAULT ADDRESS
348: m68030_push_16(cpu, 0); // INSTRUCTION PIPE STAGE B
349: m68030_push_16(cpu, 0); // INSTRUCTION PIPE STAGE C
350: m68030_push_16(cpu, ssw);
351: RegA(7) -= 2;
352: m68030_push_16(cpu, 0xb000 | (vector << 2));
353: // 本当は現在の PC だが、再実行のため PPC を積む。
354: m68030_push_32(cpu, RegPPC); // PC
355: m68030_push_16(cpu, sr); // 例外入り口で保存した SR
356: }
357:
358: // 例外処理 (リセット・割り込み以外) を行う。
359: // vector はベクタ番号。
360: void
361: m68030_exception(m68kcpu *cpu, int vector)
362: {
363: // 状態を保存
364: uint16 sr = RegSR;
365:
366: switch (vector) {
367: case M68K_EXCEP_BUSERR: // バスエラー
368: case M68K_EXCEP_ADDRERR: // アドレスエラー
369: // SR を書き換える前に! A7 が切り替わってしまう。
370:
371: // ここで (An)+ で変化したレジスタを命令実行前の状態に巻き戻す。
372: // EA 処理の時点で先にインクリメントしているのだが、バスエラーが
373: // 起きたということは実際にはポストインクリメントは発生しないので、
374: // ここで無条件に巻き戻してよい。
375: // 一方、プリデクリメントのケースはこの少し上の exframe_b の中の
376: // コメント参照。
377: m68030_restore_reg_pi(cpu);
378: }
379:
380: // S を立てて T1,T0 を落とす
381: m68030_set_sr(cpu, (RegSR | M68K_SR_S) & ~M68K_SR_T);
382:
383: switch (vector) {
384: case M68K_EXCEP_BUSERR: // バスエラー
385: case M68K_EXCEP_ADDRERR: // アドレスエラー
386:
387: // 実際はショート($A)、ロング($B)の2種類あって、本来ならショートが
388: // 使えればショートを使うのだが、ここでは全部ロングを使う。
389: try {
390: m68030_generate_exframe_b(cpu, vector, sr);
391: } catch (...) {
392: m68030_double_bus_fault(cpu, "exception");
393: return;
394: }
395: break;
396:
397: case M68K_EXCEP_ILLEGAL: // 不当命令
398: case M68K_EXCEP_PRIV: // 特権違反例外
399: case M68K_EXCEP_ALINE: // Aライン命令例外
400: case M68K_EXCEP_FLINE: // Fライン命令例外
401: // スタックに積む PC はこの命令のアドレス
402: m68030_generate_exframe_0(cpu, vector, RegPPC, sr);
403: break;
404:
405: case M68K_EXCEP_TRAP0 // TRAP#0
406: ... M68K_EXCEP_TRAP15: // TRAP#15
407: // スタックに積む PC は次の命令
408: m68030_generate_exframe_0(cpu, vector, RegPC, sr);
409: break;
410:
411: case M68K_EXCEP_ZERODIV: // ゼロ除算
412: case M68K_EXCEP_CHK: // CHK
413: case M68K_EXCEP_TRAPV: // TRAPV
414: case M68K_EXCEP_MMU_CONFIG: // MMU 構成例外
415: m68030_generate_exframe_2(cpu, vector, sr);
416: break;
417:
418: default:
419: PANIC("$%08x exception vector %d ?", RegPPC, vector);
420: break;
421: }
422:
423: // ベクタにジャンプ
424: m68030_jump_vector(cpu, vector);
425: }
426:
427: // 割り込み例外処理を行う。
428: // intrmtx なしで呼び出すこと。
429: // 割り込みチェックは m68030core.cpp::m68030_flush_interrupt() で判定している。
430: void
431: m68030_exception_interrupt(m68kcpu *cpu, int level, int vector)
432: {
433: if (0) {
434: cpulog(2, "Interrupt lv=%d vec=%d occured: %s",
435: level, vector, m68030_get_exception_name(vector));
436: }
437:
438: // 状態を保存
439: uint16 sr = RegSR;
440:
441: // S を立てて T1,T0 を落とす
442: m68030_set_sr(cpu, (RegSR | M68K_SR_S) & ~M68K_SR_T);
443: // 割り込みレベルを変更
444: m68030_change_intr(cpu, level);
445:
446: // 例外スタックフレーム $0 を生成。
447: // スタックに積む PC は次の命令位置。
448: // M ビットは未実装なのでスローアウェイとか無視
449: m68030_generate_exframe_0(cpu, vector, RegPC, sr);
450:
451: // ベクタにジャンプ
452: m68030_jump_vector(cpu, vector);
453: }
454:
455: // RTE 命令。
456: // 例外処理の近くに置いておくほうが便利なので。
457: void
458: m68030_rte(m68kcpu *cpu)
459: {
460: uint32 pc;
461: uint16 sr;
462: uint16 frame;
463:
464: sr = m68030_pop_16(cpu);
465: pc = m68030_pop_32(cpu);
466: frame = m68030_pop_16(cpu);
467: switch (frame >> 12) {
468: case 0:
469: CYCLE2(18, 20);
470: m68030_set_sr(cpu, sr);
471: m68030_jump(cpu, pc);
472: return;
473:
474: case 2:
475: CYCLE2(18, 20);
476: // +08.L INSTRUCTION ADDRESS を読み捨てる
477: RegA(7) += 4;
478: m68030_set_sr(cpu, sr);
479: m68030_jump(cpu, pc);
480: return;
481:
482: case 0x0b:
483: {
484: uint32 stage_b;
485: uint16 ssw;
486:
487: CYCLE(76);
488: RegA(7) += 2; // $08.W
489: ssw = m68030_pop_16(cpu); // $0a.W SSW
490: RegA(7) += 4 * 6; // $0c.L*6
491: stage_b = m68030_pop_32(cpu); // $24.L Stage B
492: RegA(7) += 2 * 9; // $28.W*9
493: // $3a..$58 ここが save_pd の取り出し
494: cpu->flag_pd = m68030_pop_16(cpu);
495: for (int i = 0; i < 8; i++) {
496: cpu->save_pd[i] = m68030_pop_32(cpu);
497: }
498: // A7 レジスタをリストアするかもしれない前に Super/User を
499: // 復帰しておかないといけない
500: m68030_set_sr(cpu, sr);
501:
502: // DFビットセットなら命令を再実行するため、-(An) で変化
503: // したレジスタを巻き戻す。
504: // DFビットクリアなら例外要因の命令はハンドラで処理された
505: // とみなして、次の命令に進む。
506: if ((ssw & SSW_DF)) {
507: m68030_restore_reg_pd(cpu);
508: } else {
509: pc = stage_b - 2;
510: }
511: m68030_jump(cpu, pc);
512: return;
513: }
514:
515: default:
516: break;
517: }
518: PANIC("RTE frame=$%x not supported", frame >> 12);
519: }
520:
521: static const char * const exception_name_table[] = {
522: /* 0 */ "Reset",
523: /* 1 */ NULL,
524: /* 2 */ "Bus Error",
525: /* 3 */ "Address Error",
526: /* 4 */ "Illegal Instruction",
527: /* 5 */ "Div by Zero",
528: /* 6 */ "CHK/CHK2 Instruction",
529: /* 7 */ "TRAP Instruction",
530: /* 8 */ "Priviledge Violation",
531: /* 9 */ "Trace",
532: /* 10 */ "A line emulation",
533: /* 11 */ "F line emulation",
534: /* 12 */ NULL,
535: /* 13 */ "Co-processor",
536: /* 14 */ "Format",
537: /* 15 */ NULL,
538: /* 16 */ "Uninitialized Interrupt",
539: /* 17 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
540: /* 24 */ "Spurious Interrupt",
541: /* 25 */ "Level 1 Interrupt",
542: /* 26 */ "Level 2 Interrupt",
543: /* 27 */ "Level 3 Interrupt",
544: /* 28 */ "Level 4 Interrupt",
545: /* 29 */ "Level 5 Interrupt",
546: /* 30 */ "Level 6 Interrupt",
547: /* 31 */ "Level 7 Interrupt",
548: /* 32 */ "Trap #0",
549: /* 33 */ "Trap #1",
550: /* 34 */ "Trap #2",
551: /* 35 */ "Trap #3",
552: /* 36 */ "Trap #4",
553: /* 37 */ "Trap #5",
554: /* 38 */ "Trap #6",
555: /* 39 */ "Trap #7",
556: /* 40 */ "Trap #8",
557: /* 41 */ "Trap #9",
558: /* 42 */ "Trap #10",
559: /* 43 */ "Trap #11",
560: /* 44 */ "Trap #12",
561: /* 45 */ "Trap #13",
562: /* 46 */ "Trap #14",
563: /* 47 */ "Trap #15",
564: /* 48 */ "FPCP Branch",
565: /* 49 */ "FPCP Inexcact",
566: /* 50 */ "FPCP Div by Zero",
567: /* 51 */ "FPCP UnderFlow",
568: /* 52 */ "FPCP Operation Error",
569: /* 53 */ "FPCP OverFlow",
570: /* 54 */ "FPCP SNAN",
571: /* 55 */ NULL,
572: /* 56 */ "MMU Configuration Error",
573: /* 57 */ NULL,
574: /* 58 */ NULL,
575: /* 59 */ NULL,
576: /* 60 */ NULL,
577: /* 61 */ NULL,
578: /* 62 */ NULL,
579: /* 63 */ NULL,
580: /* 64 */ "MFP Alarm",
581: /* 65 */ "MFP EXPON",
582: /* 66 */ "MFP POWSW",
583: /* 67 */ "MFP FMIRQ",
584: /* 68 */ "MFP Timer-D",
585: /* 69 */ "MFP Timer-C",
586: /* 70 */ "MFP V-Disp",
587: /* 71 */ "MFP GPIP5",
588: /* 72 */ "MFP Timer-B",
589: /* 73 */ "MFP TXError",
590: /* 74 */ "MFP TXEmpty",
591: /* 75 */ "MFP RXError",
592: /* 76 */ "MFP RXFull",
593: /* 77 */ "MFP Timer-A",
594: /* 78 */ "MFP CIRQ",
595: /* 79 */ "MFP H-Sync",
596: };
597:
598: // ベクタ番号から表示名を返す
599: const char *
600: m68030_get_exception_name(int vector)
601: {
602: const char *name = NULL;
603: if (vector < countof(exception_name_table)) {
604: name = exception_name_table[vector];
605: }
606: if (name == NULL) {
607: name = "(reserved?)";
608: }
609: return name;
610: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.