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