|
|
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.5 ! root 74: ev->time = cycle * clock_tsec + 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 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)
1.1.1.5 ! root 309: // +$3c
1.1 root 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 が変化する前に保存。
1.1.1.5 ! root 319: const busaddr fault = bus.laddr;
1.1 root 320:
1.1.1.5 ! root 321: ssw = fault.GetFC(); // TM(=FC)
! 322: ssw |= (fault.GetSize() & 3) << 5; // Size の下位2ビット
! 323: if (fault.IsRead()) {
1.1 root 324: ssw |= M68040::SSW_RW;
325: }
326: if (bus.atcfault) {
327: ssw |= M68040::SSW_ATC;
328: bus.atcfault = false;
329:
330: if (bus.atcfault2) {
331: ssw |= M68040::SSW_MA;
332: bus.atcfault2 = false;
333: }
334: }
335: // 再実行のため、例外を発生した命令に戻る。
336: pc = ppc;
337:
338: push_4(0); // PD3
339: push_4(0); // PD2
340: push_4(0); // PD1
341: push_4(wb1d); // WB1D
342: push_4(wb1a); // WB1A
343: push_4(wb2d); // WB2D
344: push_4(wb2a); // WB2A
345: push_4(0); // WB3D
346: push_4(0); // WB3A
1.1.1.5 ! root 347: push_4(fault.Addr());
1.1 root 348: push_2(wb1s); // WB1S
349: push_2(wb2s); // WB2S
350: push_2(0); // WB3S
351: push_2(ssw);
352: push_4(ea);
1.1.1.5 ! root 353: push_2(0x7000 | (vector << 2));
1.1 root 354: push_4(pc);
355: push_2(sr);
356: }
357:
358: // 例外スタックフレーム $B を生成する。(68030 only)
359: inline void
360: MPU680x0Device::GenerateExframeB(uint vector, uint16 sr)
361: {
362: uint16 ssw;
363: uint32 stageBaddr;
364:
365: stageBaddr = reg.pc + 2;
1.1.1.5 ! root 366:
1.1 root 367: // この後のスタック操作で bus が変化する前に保存
1.1.1.5 ! root 368: const busaddr fault = bus.laddr;
! 369:
! 370: // SSW
! 371: // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
! 372: // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
! 373: // |FC |FB |RC |RB | X | X | X |DF |RM |RW | SIZE | X | FC |
! 374: // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
1.1 root 375:
1.1.1.5 ! root 376: ssw = (fault.Get() >> 33) & 0x47; // R/W+FC
! 377: ssw |= (fault.GetSize() & 3) << 4; // Size の下位2ビット
! 378: ssw |= M68030::SSW_DF; // DF は常に立てる必要がある??
1.1 root 379: if (mmu_enable && (ssw & M68K::FC_DATA) != 0) {
380: // 命令アクセスなら RB をセット。
381: ssw |= M68030::SSW_RB;
382: }
383:
384: // 例外スタックフレーム $B
385: // SP+$00.W STATUS REGISTER
386: // +$02.L PROGRAM COUNTER
387: // +$06.W %1011 | VECTOR
388: // +$08.W (internal register)
389: // +$0a.W SPECIAL STATUS REGISTER ; SSW
390: // +$0c.W INSTRUCTION PIPE STAGE C
391: // +$0e.W INSTRUCTION PIPE STAGE B
392: // +$10.L DATA CYCLE FAULT ADDRESS
393: // +$14.W*2 (internal register)
394: // +$18.L DATA OUTPUT BUFFER
395: // +$1c.W*4 (internal register, 4 words)
396: // +$24.L STAGE B ADDRESS
397: // +$28.W*2 (internal register, 2 words)
398: // +$2c.L DATA INPUT BUFFER
399: // +$30.W*3 (internal register, 3 words)
400: // +$36.W VERSION# | internal information
401: // +$38.W*18 (internal register, 18 words)
402: // +$5a
403: // こっちから上に向かって PUSH
404:
405: // internal registers, 18 words
406: // 内部レジスタ18ワードのうち、ここでは17ワード分を save_pd[8] (16ワード)、
407: // flag_pd (1ワード) として使うことにする。
408: // -(An) でバスエラーが起きると An は減算された状態になるが、DF ビットが
409: // 立っていて再実行する時には現行構造では命令先頭から再実行せざるをえず、
410: // そうするともう一度プリデクリメントが動いてしまう。そのため、RTE の時点
411: // で DF=1 ならプリデクリメント発生前の An に戻すという処理をする。
412: // RTE 時点で DF=0 なら命令を再実行しないため An も巻き戻してはならず、
413: // そのためプリデクリメント分の巻き戻しは RTE 時点まで行えない。
414: // 一方、ポストインクリメントのケースはこの少し下の
415: // ExceptionBuserr() 内のコメント参照。
416: for (int i = 7; i >= 0; i--) {
417: push_4(save_pd[i]);
418: }
419: push_2(flag_pd);
420: reg.A[7] -= 2;
421:
422: reg.A[7] -= 2; // VERSION#
423: reg.A[7] -= 6;
424: push_4(0); // DATA INPUT BUFFER
425: reg.A[7] -= 4;
426: push_4(stageBaddr); // STAGE B ADDRESS
427: reg.A[7] -= 8;
428: push_4(0); // DATA OUTPUT BUFFER
429: reg.A[7] -= 4;
1.1.1.5 ! root 430: push_4(fault.Addr()); // DATA CYCLE FAULT ADDRESS
1.1 root 431: push_2(0); // INSTRUCTION PIPE STAGE B
432: push_2(0); // INSTRUCTION PIPE STAGE C
433: push_2(ssw);
434: reg.A[7] -= 2;
435: push_2(0xb000 | (vector << 2));
436: // 本当は現在の PC だが、再実行のため PPC を積む。
437: push_4(ppc); // PC
438: push_2(sr); // 例外入り口で保存した SR
439: }
440:
441: // 例外処理について。
442: //
443: // 0. リセット例外
444: // リセット例外は外部から ExceptionReset() を呼び出す。
445: //
446: // 1. バスエラー例外
447: // MMU による論理アドレスでのバスエラーは (Translate*() が false を返した後)
448: // m68030bus.cpp ルーチンが (C++の) 例外をスローする。
449: // 物理アドレス空間アクセス後のバスエラーは (VM側の Read*()/Write*() が
450: // BusErr を返した後) m68030bus.cpp ルーチンが (C++) の例外をスローする。
451: // どちらのケースも m68030core.cpp のメインループで catch し、その catch
452: // ブロック内で ExceptionBuserr() を呼び出す。
453: //
454: // 2. アドレスエラー例外
455: // アドレスエラーは Jump() 内でのみ発生するはずで、その場で
456: // ExceptionBuserr() を呼んで処理する。
457: //
458: // 3. 命令中で起きる例外
459: // 命令途中で起きる(確定的な)例外は基本的に Exception() を呼び出す。
460: // これに該当するのは
461: // o 特権違反
462: // o A系列/F系列命令
463: // o TRAP* 系命令、CHK/CHK2 命令
464: // o トレース例外 (未実装)
465: // o ゼロ除算?
466: // o MMU 構成例外
467: // o 不当命令 (*)
468: // である。
469: // ただし、不当命令についてはどこで不当命令と判定されるかによって
470: // フローが複数存在することに注意。
471: // o 1ワード目が不当命令
472: // -> 命令がそれぞれ Exception() で処理。
473: // o 1ワード目の EA が不当命令
474: // -> (C++の)例外をスローして catch 内で Exception() を実行。
475: // o 2ワード目が不当命令
476: // -> 命令がそれぞれ Exception() で処理。
477: //
478: // 4. 割り込み例外
479: // 別コメント(下のほう)参照。
480: //
481:
482: // リセット例外処理。
483: // 各レジスタを所定の状態にして、リセットベクタを読み込む。
484: // リセットベクタが読み込めなければ HALT 状態に移行。
485: // 戻り値は消費したサイクル数。
486: int
487: MPU680x0Device::ExceptionReset()
488: {
489: uint64 cycle_start = used_cycle;
490:
491: // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
492: // m68k では今の所リセット例外発生時の PC にはあまり意味がないけど、
493: // m88k と動作を揃えておく。
494: const uint vector = 0;
495: excep_counter[0]++;
1.1.1.3 root 496: constexpr uint32 info = BranchHistory_m680x0::InfoException(vector);
497: exhist.AddEntry(reg.pc | 1, 0, info);
498: brhist.AddEntry(reg.pc | 1, 0, info);
1.1.1.5 ! root 499: last_vector = vector;
1.1 root 500:
501: // RESET 信号線をネゲートしてから SP の読み込み開始まで 4クロック
502: // (Section 7.8, Figure 7-64)
503: used_cycle += 4;
504:
505: // STOP 状態とホールト状態を解除。
506: ChangeState(CPU_STATE_NORMAL);
507:
508: // 1. T0, T1 クリア
509: // 2. S をセット、M をクリア
510: // 3. 割り込みレベルを 7
511:
512: // ここは SetSR を使わずに設定する。
513: reg.s = true;
514: reg.m = false;
515: fc_inst = BusAddr::S | BusAddr::Fetch;
516: fc_data = BusAddr::S | BusAddr::D;
517: // ここは割り込みレベル変化のチェックは不要でセットだけでいい。
518: reg.intr_mask = 7;
519:
520: // 4. VBR をクリア
521: reg.vbr = 0;
522:
523: // 5. キャッシュの EI|ED, FI|FD, IBE|DBE, WA をクリア
524: // 6. キャッシュエントリをすべてクリア (CI|CD)
525: ResetCache();
526:
1.1.1.3 root 527: // キャッシュ状態が変わったので、サイクル表も更新。
528: if (GetMPUType() == m680x0MPUType::M68030) {
529: cycle_table = cycle_table_030ncc;
530: }
531:
1.1 root 532: // 7. TC:E、TTn:E をクリア
533: ResetMMU();
534:
535: // ところでたぶん FPU もリセットされるはず?
536: ResetFPU(true);
537:
538: // 8. リセットベクタ生成
539: // 9. SP をロード
540: // 10. PC をロード
541: try {
542: reg.pc = 0;
543: reg.A[7] = fetch_4();
544: uint32 pc = fetch_4();
545: // 大丈夫のはずだけど fetch_4() が reg.pc を書き換えるので一応
546: reg.pc = pc;
547: // リセットベクタは常に 0, 4 番地から取得したことになっている
1.1.1.3 root 548: uint32 from = 0x00000004 | 1;
549: brhist.AddEntry(from, reg.pc, BranchHistory_m680x0::InfoVectorJump());
1.1 root 550: } catch (uint) {
551: DoubleBusFault("reset exception");
552: }
553:
554: // SP のアドレスエラーはリセット例外ではチェックしないようだ。
555: // PC のアドレスエラーは本来ならここにあるはずの3ワードのプリフェッチで
556: // 行われるのだが、それがないので、とりあえずここで。
557: if ((reg.pc & 1) != 0) {
558: DoubleBusFault("reset exception");
559: }
560:
561: return used_cycle - cycle_start;
562: }
563:
564: // 汎用の例外処理を行う。vector はベクタ番号。
565: // ただし以下の例外はここではなくそれぞれ固有のを呼ぶこと。
566: // o リセット例外は ExceptionReset()
567: // o 割り込み例外は ExceptionInterrupt()
1.1.1.5 ! root 568: // o TRAP#0 例外は ExceptionTrap0() .. NetBSD システムコール用
! 569: // o TRAP#15 例外は ExceptionTrap15() .. IOCS コール用
1.1 root 570: // o バスエラー・アドレスエラーは ExceptionBuserr()
571: void
572: MPU680x0Device::Exception(uint vector)
573: {
1.1.1.3 root 574: uint32 info = BranchHistory_m680x0::InfoException(vector);
575: ExceptionGeneric(vector, info);
1.1 root 576: }
577:
1.1.1.5 ! root 578: // TRAP #0 の例外処理を行う。
! 579: // 処理自体は汎用の Exception() とまったく同じだが、
! 580: // NetBSD システムコールの時に分岐履歴の内容を変えるため入口を分けてある。
! 581: void
! 582: MPU680x0Device::ExceptionTrap0()
! 583: {
! 584: // 今発生した TRAP #0 が NetBSD/m68k システムコールっぽいか調べる。
! 585: //
! 586: // NetBSD/m68k 方面をやってる時は TRAP #0 はシステムコールを表示して
! 587: // ほしい。一方 Human68k などでは TRAP #0 は別用途なので、システムコール
! 588: // かのように表示されるのは紛らわしいので、
! 589: // 出来れば今呼ばれたこれが NetBSD/m68k システムコールなのか、そうでない
! 590: // TRAP #0 なのかは区別したい。
! 591: //
! 592: // NetBSD/m68k カーネルが実行中かどうかは厳密には分からないが、これが
! 593: // 呼ばれた時点で、
! 594: // 1) MMU がオン。
! 595: // 2) カーネルのエントリポイントの最初の命令が move.w #$2700,sr 命令。
! 596: // を満たせば NetBSD/m68k システムコールと判定することにする。
! 597: // カーネルエントリポイントは機種ごとに固有で、不変ではないものの
! 598: // ブートローダ側にも影響があるためおそらくそう簡単には変わらないだろう。
! 599: // だめならまた考える。
! 600:
! 601: const uint vector = M68K::EXCEP_TRAP0;
! 602: uint32 info;
! 603: if (__predict_true(mmu_enable)) {
! 604: // カーネルエントリポイントの最初の命令はどれも move.w #$2700,sr。
! 605: if (mainbus->Peek4(netbsd_entrypoint) == 0x46fc2700) {
! 606: // NetBSD/m68k カーネルっぽい。
! 607: info = BranchHistory_m680x0::InfoNetBSDSyscall(reg.D[0] & 0xffff);
! 608: ExceptionGeneric(vector, info);
! 609: return;
! 610: }
! 611: }
! 612: // その他の TRAP#0 っぽい
! 613: Exception(vector);
! 614: }
! 615:
1.1 root 616: // TRAP #15 の例外処理を行う。
617: // 処理自体は汎用の Exception() とまったく同じだが、
1.1.1.3 root 618: // IOCS コールの時に分岐履歴の内容を変えるため入口を分けてある。
1.1 root 619: void
620: MPU680x0Device::ExceptionTrap15()
621: {
622: // 今発生した TRAP #15 が IOCS コールっぽいかどうか調べる。
623: //
624: // Human68k 方面をやってる時は TRAP #15 は IOCS コールで表示してほしい。
625: // 一方、NetBSD のカーネル内では TRAP #15 は IOCS コールではない別用途
626: // なので IOCS コールかのように表示されるのは紛らわしい。さらに言えば
627: // NetBSD でもブートローダの時点はまだ IOCS コールを使うので、出来れば
628: // 今呼ばれたこれが IOCS コールなのか、そうでない TRAP #15 なのかは
629: // 区別したい。
630: //
631: // IOCS コールかどうかは厳密には分からないが、これが呼ばれた時点で
1.1.1.3 root 632: // ワークエリアの $cb6 == 0x02、$cb7 == 0x03 であれば IOCS コールと
1.1 root 633: // 判定することにする。
1.1.1.3 root 634: // $cb6、$cb7 はシステムポートの機種とクロック数 (を加工したもの)
635: // なので、本体が X68030 であれば常に 0x02、0x03 になるはず。
1.1 root 636: // だめならまた考える。
637:
638: const uint vector = M68K::EXCEP_TRAP15;
1.1.1.3 root 639: uint32 info;
640: if (mainbus->Peek1(0xcb6) == 0x02 && mainbus->Peek1(0xcb7) == 0x03) {
1.1 root 641: // IOCS コールっぽい
1.1.1.3 root 642: info = BranchHistory_m680x0::InfoIOCSCall(reg.D[0] & 0xff);
1.1.1.5 ! root 643: ExceptionGeneric(vector, info);
! 644: return;
1.1.1.3 root 645: }
1.1.1.5 ! root 646:
! 647: // その他の TRAP#15 っぽい
! 648: Exception(vector);
1.1.1.3 root 649: }
650:
651: // F ライン例外処理を行う。
652: // 処理自体は Exception() とまったく同じだが、
653: // DOS コールの時に分岐履歴の内容を変えるため入口を分けてある。
654: void
655: MPU680x0Device::ExceptionFLine()
656: {
657: // 今発生した F ライン例外が DOS コールっぽいかどうか調べる。
658: // といいつつ今のところ IOCS ワークで判定している。
659: // DOS コールは本来 Human68k が起動しているかどうかなので、
660: // IOCS ワークがあるかどうかとは違うが、とりあえず。
661: // このすぐ上の ExceptionTrap15() のコメントも参照。
662:
663: const uint vector = M68K::EXCEP_FLINE;
664: uint32 info;
665: if ((ir & 0xff00) == 0xff00 &&
666: mainbus->Peek1(0xcb6) == 0x02 && mainbus->Peek1(0xcb7) == 0x03)
667: {
668: // DOS コールっぽい。
669: info = BranchHistory_m680x0::InfoDOSCall(ir);
670: } else {
671: // F ライン例外っぽい。
672: info = BranchHistory_m680x0::InfoException(vector);
1.1 root 673: }
1.1.1.3 root 674: ExceptionGeneric(vector, info);
1.1 root 675: }
676:
677: // 汎用の例外処理本体。
678: // ext_vector は下位8ビットがベクタ番号、
679: // 8ビット目が %1 なら浮動小数点未実装命令例外 (vector は F ライン)。
1.1.1.3 root 680: // info は履歴に記録するやつ。
1.1 root 681: void
1.1.1.3 root 682: MPU680x0Device::ExceptionGeneric(uint ext_vector, uint32 info)
1.1 root 683: {
684: uint vector = ext_vector & 0xff;
685:
686: // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
1.1.1.5 ! root 687: last_vector = vector;
1.1 root 688: excep_counter[vector]++;
689: uint32 from = ppc | (IsSuper() ? 1 : 0);
1.1.1.3 root 690: exhist.AddEntry(from, 0, info);
691: brhist.AddEntry(from, 0, info);
1.1 root 692:
693: // 状態を保存
694: uint16 sr = GetSR();
695:
696: // S を立てて T1,T0 を落とす
697: SetSR((GetSR() | M68K::SR_S) & ~M68K::SR_T);
698:
699: try {
700: switch (ext_vector) {
701: case M68K::EXCEP_ILLEGAL: // 不当命令
702: case M68K::EXCEP_PRIV: // 特権違反例外
703: case M68K::EXCEP_ALINE: // Aライン命令例外
704: case M68K::EXCEP_FLINE: // Fライン命令例外
705: case M68K::EXCEP_FORMAT: // フォーマットエラー例外
706: // スタックに積む PC はこの命令のアドレス
707: GenerateExframe0(vector, sr, ppc);
708: break;
709:
710: case M68K::EXCEP_TRAP0 // TRAP#0
711: ... M68K::EXCEP_TRAP15: // TRAP#15
712: // スタックに積む PC は次の命令
713: GenerateExframe0(vector, sr, reg.pc);
714: break;
715:
716: case M68K::EXCEP_ZERODIV: // ゼロ除算
717: case M68K::EXCEP_CHK: // CHK
718: case M68K::EXCEP_TRAPV: // TRAPV
719: case M68K::EXCEP_MMU_CONFIG: // MMU 構成例外
720: GenerateExframe2(vector, sr, reg.pc, ppc);
721: break;
722:
723: case M68K::EXCEP_FP_UNIMPL: // 68040 の浮動小数点未実装命令例外
724: case M68K::EXCEP_FP_UNSUPP: // 68040 の FPU 未実装データ型例外
725: // スタックに積む PC は次の命令、ADDRESS は計算した EA。
726: GenerateExframe2(vector, sr, reg.pc, fpu40.ea);
727: break;
728:
729: default:
730: PANIC("$%08x exception vector %u ?", ppc, vector);
731: break;
732: }
733:
734: // ベクタにジャンプ
735: JumpVector(vector, false);
736: } catch (uint vector2) {
737: assertmsg(vector2 == M68K::EXCEP_BUSERR, "vector=%u", vector);
738:
739: // バスエラー処理へ
740: ExceptionBuserr(vector2);
741: }
742: }
743:
744: // バスエラー・アドレスエラー例外処理を行う。
745: // 他の例外とは違いが多いので分けた。
746: void
747: MPU680x0Device::ExceptionBuserr(uint vector)
748: {
749: assertmsg((vector == M68K::EXCEP_BUSERR || vector == M68K::EXCEP_ADDRERR),
750: "vector=%u", vector);
751:
752: // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
1.1.1.5 ! root 753: last_vector = vector;
1.1 root 754: excep_counter[vector]++;
755: uint32 from = ppc | (IsSuper() ? 1 : 0);
1.1.1.3 root 756: uint32 info = BranchHistory_m680x0::InfoException(vector);
757: exhist.AddEntry(from, 0, info);
758: brhist.AddEntry(from, 0, info);
1.1 root 759:
1.1.1.3 root 760: // 現状 68030 のバスエラーに short/long の区別はないので全部 long 相当。
761: CYCLE3(busfault);
762:
1.1 root 763: // 状態を保存
764: uint16 sr = GetSR();
765:
766: if (mpu_type == m680x0MPUType::M68030) {
767: // 68030 なら、
768: // ここで (An)+ で変化したレジスタを命令実行前の状態に巻き戻す。
769: // EA 処理の時点で先にインクリメントしているのだが、バスエラーが
770: // 起きたということは実際にはポストインクリメントは発生しないので、
771: // ここで無条件に巻き戻してよい。SR を書き換えると A7 が切り替わって
772: // しまうので、この巻き戻しは SR を書き換える前のここで行うこと。
773: // 一方、プリデクリメントのケースはこの少し上の ExframeB の中の
774: // コメント参照。
775: restore_reg_pi();
776: } else {
777: // 68040 なら、
778: // (An)+, -(An) どちらも命令実行前の状態に巻き戻す。
779: // 少し上の frame7 の中のコメントも参照。
780: restore_reg_pi();
781: restore_reg_pd();
782: }
783:
784: // S を立てて T1,T0 を落とす
785: SetSR((GetSR() | M68K::SR_S) & ~M68K::SR_T);
786:
787: try {
788: // フレームを作成。
789: if (mpu_type == m680x0MPUType::M68030) {
790: // 実際はショート($A)、ロング($B)の2種類あって、本来ならショートが
791: // 使えればショートを使うのだが、ここでは全部ロングを使う。
792: GenerateExframeB(vector, sr);
793: } else {
794: // 68040 ではバスエラーは $7、アドレスエラーは $2 を使う。
795: if (__predict_true(vector == M68K::EXCEP_BUSERR)) {
796: GenerateExframe7(vector, sr);
797: } else {
798: uint32 fault_addr = bus.laddr.Addr();
799: GenerateExframe2(vector, sr, ppc, fault_addr - 1);
800: }
801: }
802: // ベクタにジャンプ
803: JumpVector(vector, true);
804: } catch (uint vector2) {
805: // ここに来るのはバスエラーかアドレスエラーだけのはず
806: assertmsg(vector2 == M68K::EXCEP_BUSERR ||
807: vector2 == M68K::EXCEP_ADDRERR, "vector=%u", vector);
808:
809: // バスエラー・アドレスエラー処理中のバスエラーは
810: // ダブルバスフォールト。
811: DoubleBusFault("bus/address error handling");
812: }
813: }
814:
815: // 割り込み例外処理を行う。
816: // 割り込みの受け付けとレベルのチェック等の後、次命令境界で呼ばれるやつ。
817: // この処理にかかる時間 [nsec] を返す。
818: uint64
819: MPU680x0Device::ExceptionInterrupt()
820: {
821: uint64 cycle_start = used_cycle;
822: uint level = intr_level;
823:
824: // ユーザーズマニュアル(日本語) p.399 図8-5 の中ほど
825: // 「/IPEND をネゲート、割り込みアクノリッジサイクルを実行」
826: // 以降に相当。
827:
828: // STOP なら解除
829: ChangeState(CPU_STATE_NORMAL);
830:
831: // 割り込みアクノリッジサイクルを実行
832: uint32 vector;
833: busdata bd = interruptdev->InterruptAcknowledge(level);
834: if (__predict_false(bd.IsBusErr())) {
835: // 外部回路がバスエラーを返すと Spurious を発行する。(7.4.1.3)
836: vector = M68K::EXCEP_SPURIOUS;
837: } else {
838: vector = bd.Data();
839: if ((int32)vector < 0) {
840: // オートベクタ要求 (7.4.1.2)
841: // (負数(-1)ならオートベクタということにしている)
842: vector = M68K::EXCEP_LEVEL_BASE + level;
843: }
844: }
845:
846: // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
1.1.1.5 ! root 847: last_vector = vector;
1.1 root 848: excep_counter[vector]++;
849: uint32 from = ppc | (IsSuper() ? 1 : 0);
1.1.1.3 root 850: uint32 info = BranchHistory_m680x0::InfoException(vector);
851: exhist.AddEntry(from, 0, info);
852: brhist.AddEntry(from, 0, info);
1.1 root 853:
854: // 状態を保存
855: uint16 oldsr = GetSR();
856:
857: // S を立てて T1,T0 を落とす。割り込みレベルも変更。
858: uint16 newsr = GetSR() & ~(M68K::SR_T | M68K::SR_IMASK);
859: newsr |= M68K::SR_S;
860: newsr |= level << 8;
861: SetSR(newsr);
862:
863: try {
864: // 例外スタックフレーム $0 を生成。
865: // スタックに積む PC は次の命令位置。
866: // M ビットは未実装なのでスローアウェイとか無視
867: GenerateExframe0(vector, oldsr, reg.pc);
868:
869: // ベクタにジャンプ
870: JumpVector(vector, false);
871: } catch (uint vector2) {
872: assertmsg(vector2 == M68K::EXCEP_BUSERR, "vector=%u", vector);
873:
874: // バスエラー処理へ
875: ExceptionBuserr(vector2);
876: }
877:
1.1.1.5 ! root 878: return (used_cycle - cycle_start) * clock_tsec + bd.GetWait();
1.1 root 879: }
880:
1.1.1.2 root 881: // LC040 の未実装浮動小数点命令例外。
882: void
883: MPU680x0Device::ExceptionFPLC(uint32 ea)
884: {
885: const uint vector = M68K::EXCEP_FLINE;
886:
887: // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
1.1.1.5 ! root 888: last_vector = vector;
1.1.1.2 root 889: excep_counter[vector]++;
890: uint32 from = ppc | (IsSuper() ? 1 : 0);
1.1.1.3 root 891: constexpr uint32 info = BranchHistory_m680x0::InfoException(vector);
892: exhist.AddEntry(from, 0, info);
893: brhist.AddEntry(from, 0, info);
1.1.1.2 root 894:
895: // 状態を保存
896: uint16 sr = GetSR();
897:
898: // S を立てて T1,T0 を落とす
899: SetSR((GetSR() | M68K::SR_S) & ~M68K::SR_T);
900:
901: try {
902: GenerateExframe4(vector, sr, ea);
903:
904: // ベクタにジャンプ
905: JumpVector(vector, false);
906: } catch (uint vector2) {
907: assertmsg(vector2 == M68K::EXCEP_BUSERR, "vector=%u", vector);
908:
909: // バスエラー処理へ
910: ExceptionBuserr(vector2);
911: }
912: }
913:
1.1 root 914: // RTE 命令。
915: // 例外処理の近くに置いておくほうが便利なので。
916: void
917: MPU680x0Device::ops_rte()
918: {
919: uint32 pc;
920: uint16 sr;
921: uint16 frame;
922: uint32 type;
923:
924: try {
925: sr = pop_2();
926: pc = pop_4();
927: frame = pop_2();
928: type = frame >> 12;
929: switch (type) {
930: case 0:
1.1.1.3 root 931: CYCLE3(rte_frame0);
1.1 root 932: SetSR(sr);
933: Jump(pc);
934: break;
935:
936: case 2:
1.1.1.3 root 937: CYCLE3(rte_frame2);
1.1 root 938: // +08.L (INSTRUCTION) ADDRESS を読み捨てる。
939: reg.A[7] += 4;
940: SetSR(sr);
941: Jump(pc);
942: break;
943:
1.1.1.2 root 944: case 0x04:
945: {
946: // 68LC040 only
947: if (GetFPUType().Is4060LC() == false) {
948: Exception(M68K::EXCEP_FORMAT);
949: return;
950: }
1.1.1.3 root 951: CYCLE(15);
1.1.1.2 root 952: reg.A[7] += 8;
953: SetSR(sr);
954: Jump(pc);
955: break;
956: }
957:
1.1 root 958: case 0x07:
959: {
960: // 68040 only
961: if (mpu_type != m680x0MPUType::M68040) {
962: Exception(M68K::EXCEP_FORMAT);
963: return;
964: }
1.1.1.3 root 965: CYCLE(23);
1.1 root 966: uint16 ssw;
967: reg.A[7] += 4;
968: ssw = pop_2();
969: putlog(2, "RTE (Frame7): SSW=$%04x", ssw);
970: reg.A[7] += 46;
971: SetSR(sr);
972: Jump(pc);
973: break;
974: }
975:
976: case 0x0b:
977: {
978: // 68030 only
979: if (mpu_type != m680x0MPUType::M68030) {
980: Exception(M68K::EXCEP_FORMAT);
981: return;
982: }
983:
984: uint32 stage_b;
985: uint16 ssw;
986:
987: CYCLE(76);
988: reg.A[7] += 2; // $08.W
989: ssw = pop_2(); // $0a.W SSW
990: reg.A[7] += 4 * 6; // $0c.L*6
991: stage_b = pop_4(); // $24.L Stage B
992: reg.A[7] += 2 * 9; // $28.W*9
993: // $3a..$58 ここが save_pd の取り出し
994: flag_pd = pop_2();
995: for (int i = 0; i < 8; i++) {
996: save_pd[i] = pop_4();
997: }
998: // A7 レジスタをリストアするかもしれない前に Super/User を
999: // 復帰しておかないといけない。
1000: SetSR(sr);
1001:
1002: // DFビットセットなら命令を再実行するため、-(An) で変化
1003: // したレジスタを巻き戻す。
1004: // DFビットクリアなら例外要因の命令はハンドラで処理された
1005: // とみなして、次の命令に進む。
1006: if ((ssw & M68030::SSW_DF)) {
1007: restore_reg_pd();
1008: } else {
1009: pc = stage_b - 2;
1010: }
1011: Jump(pc);
1012: break;
1013: }
1014:
1015: default:
1016: if (mpu_type == m680x0MPUType::M68030) {
1017: switch (type) {
1018: case 0x01:
1019: case 0x08:
1020: case 0x09:
1021: case 0x0a:
1022: // CPU は本来対応しているが、
1023: // エミュレータが対応していない (遭遇しないはず)。
1024: VMPANIC("RTE frame=$%x not supported", type);
1025: default:
1026: break;
1027: }
1028: } else /* 68040 */ {
1029: switch (type) {
1030: case 0x01:
1031: case 0x03:
1032: // CPU は本来対応しているが、
1033: // エミュレータが対応していない (遭遇しないはず)。
1034: VMPANIC("RTE frame=$%x not supported", type);
1035: default:
1036: break;
1037: }
1038: }
1039: // CPU が対応していないフレームタイプはフォーマットエラー例外。
1040: Exception(M68K::EXCEP_FORMAT);
1041: break;
1042: }
1043: } catch (uint vector2) {
1044: assertmsg(vector2 == M68K::EXCEP_BUSERR, "vector2=%u", vector2);
1045:
1046: // RTE 処理中のバスエラーはダブルバスフォールト。
1047: DoubleBusFault("bus/address error handling");
1048: }
1049: }
1050:
1051: // STOP 命令。
1052: void
1053: MPU680x0Device::ops_stop()
1054: {
1055: uint32 data = fetch_2();
1056: SetSR(data);
1057: ChangeState(CPU_STATE_STOP);
1058:
1059: MakeNextFull();
1060: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.