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