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