|
|
1.1 root 1: //
2: // nono
1.1.1.2 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
7: #include "m88100.h"
8: #include "m88100excep.h"
9: #include "debugger.h"
10: #include "cvprompt.h"
11: #include "mpu.h"
12:
13: #define OP_DEF(name) void __CONCAT(m88kcpu::op_,name)()
14: #define OP_FUNC(name) __CONCAT(op_,name)()
15: #include "m88100ops.cpp"
16:
17: // リセット例外のようなもの?
18: void
19: m88kcpu::Reset(uint32 reset_vector)
20: {
1.1.1.4 ! root 21: const uint32 Undefined = 0xcccccccc;
! 22:
! 23: // 履歴を初期化。リセット前の履歴いらんよな?
! 24: exhist.Clear();
! 25: brhist.Clear();
! 26:
! 27: // Table 6-8 (p6-47)
1.1 root 28: psr = 0x800003ff;
1.1.1.4 ! root 29: // XXX ただし SFU1 は未実装なので下げておく
1.1 root 30: psr &= ~PSR_SFD1;
31:
1.1.1.2 root 32: #if defined(TEST_INTERRUPT)
1.1.1.4 ! root 33: psr &= ~PSR_IND;
! 34: psr &= ~PSR_SFRZ;
1.1 root 35: #endif
36:
1.1.1.4 ! root 37: SetPSR();
1.1.1.2 root 38:
1.1.1.4 ! root 39: epsr = Undefined;
! 40: // scoreboard: cleared
! 41: ssbr = Undefined;
1.1.1.2 root 42:
1.1 root 43: // 実際には m88k MPU はリセット直後は 0 番地から実行を開始するので、
44: // 大抵の場合システムでリセット直後は 0 番地付近に ROM を見せるように
45: // してあるのだと思うが、ここではその代わりに ROM のアドレスを指定して、
46: // 直接そこから実行を開始することにする。
47: fip = reset_vector;
48: nip = 0;
49: xip = 0;
1.1.1.4 ! root 50: sfip = Undefined;
! 51: snip = Undefined;
! 52: sxip = Undefined;
! 53:
! 54: vbr = 0;
! 55: // DMTx は bit0(Valid) をクリア。他は不定。
! 56: dmt0 &= ~DM_VALID;
! 57: dmt1 &= ~DM_VALID;
! 58: dmt2 &= ~DM_VALID;
! 59: dma0 = Undefined;
! 60: dma1 = Undefined;
! 61: dma2 = Undefined;
! 62: dmd0 = Undefined;
! 63: dmd1 = Undefined;
! 64: dmd2 = Undefined;
! 65: for (int i = 1; i < 32; i++) {
! 66: r[i] = Undefined;
! 67: }
! 68:
! 69: fpecr = 0;
! 70: for (int i = 1; i < 9; i++) {
! 71: fcr[i] = Undefined;
! 72: }
! 73: fpcr = 0;
! 74: fpsr = 0;
! 75:
! 76: cmmu[0].Reset();
! 77: cmmu[1].Reset();
! 78:
! 79: // 履歴を初期化
! 80: exhist.Clear();
! 81: brhist.Clear();
! 82:
! 83: // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
! 84: // リセット例外発生時の from にはあまり意味がないので 0 にするか。
! 85: exhist.AddEntry(0, 0, 0xfc000000 | 0);
! 86: brhist.AddEntry(0, 0, 0xfc000000 | 0);
! 87: // デバッガに通知 (例外ブレーク用)
! 88: debugger_notify_exception(0);
1.1 root 89:
90: fetch();
91: }
92:
93: // request サイクル分実行する。
94: uint32
95: m88kcpu::Run(uint64 request)
96: {
97: uint64 goal_cycle = total_cycle + request;
98: bool is_release = false;
99: bool is_intr = false;
100:
1.1.1.2 root 101: if ((atomic_reqflag & CPU_REQ_STOP)) {
102: if ((atomic_reqflag & CPU_REQ_INTR) == 0) {
103: // STOP 状態のままで割り込みが無ければ、
104: // STOP 継続として戻る。
105: total_cycle = goal_cycle;
106: return atomic_reqflag;
107: }
108: // 状態が変わったら STOP 解除。
109: atomic_reqflag &= ~CPU_REQ_STOP;
110: }
111:
1.1 root 112: while (!is_release) {
113: // shift pipeline
114: opX = opF;
115: xip = nip;
116:
117: // prefetch next inst
118: fetch();
119:
120: // この時点で実行しようとする命令が opX に入っている
121:
122: if (atomic_reqflag != 0) {
123: // 何かが起きた
124:
125: // 割り込みの認識
126: // 割り込みの実行は命令実行完了後、次のプリフェッチの前
127: if ((atomic_reqflag & CPU_REQ_INTR)) {
128: atomic_reqflag &= ~CPU_REQ_INTR;
129: is_intr = true;
1.1.1.2 root 130: #if defined(TEST_INTERRUPT)
1.1.1.4 ! root 131: atomic_reqflag |= CPU_REQ_PROMPT;
1.1 root 132: #endif
133: }
134:
135: if ((atomic_reqflag & CPU_REQ_TRACE)) {
136: // デバッガプロンプトを出すかどうかを判断する
137: //inst_count++;
138: if (debugger_check()) {
139: atomic_reqflag |= CPU_REQ_PROMPT;
140: }
141: }
142:
143: if ((atomic_reqflag & CPU_REQ_PROMPT)) {
144: // デバッガプロンプトのための実行停止
145:
146: // プロンプト獲得を通知
147: gCVPrompt->NotifyAcquire();
148:
149: // プロンプト解放を待機
150: gCVPrompt->WaitRelease();
151:
152: // プロンプトを抜けたのでここでフラグを落とす
153: atomic_reqflag &= ~CPU_REQ_PROMPT;
1.1.1.4 ! root 154:
! 155: // プロンプトから戻ってきたら一旦スケジューラまで戻る(?)。
! 156: // reset コマンドのようにデバッガからスケジューラに出した
! 157: // リクエストを(なるはやで?)回収するため。
! 158: atomic_reqflag |= CPU_REQ_RELEASE;
1.1 root 159: }
160:
161: if ((atomic_reqflag & CPU_REQ_RELEASE)) {
162: // 中断リクエスト受信
163: // この命令を実行し終わったら抜ける
164: atomic_reqflag &= ~CPU_REQ_RELEASE;
165: is_release = true;
166: }
167: }
168:
169: if (OpIsBusErr(opX)) {
170: // プリフェッチしていた命令がバスエラー
171: Exception(M88K_EXCEP_INST);
172: continue;
173: }
174:
175: uint32 op12 = op32_to_12(opX);
176:
177: switch (op12) {
178: #include "m88100switch.inc"
179: default:
1.1.1.2 root 180: OP_FUNC(illegal);
181: break;
1.1 root 182: }
183:
184: if (is_intr) {
185: if (!(psr & PSR_IND)) {
1.1.1.2 root 186: // XXX: 割り込み信号線はレベル割り込みなのだが
187: // まだ処理できないのでとりあえずラッチ割り込みにしておく。
188: is_intr = false;
1.1 root 189: gMPU->putlog(4, "INTR take");
190: ExceptionCore(M88K_EXCEP_INTERRUPT, ExceptionKind::INTR);
191: continue;
192: } else {
193: gMPU->putlog(4, "INTR DISABLE");
194: }
195: }
196:
197: total_cycle++;
1.1.1.2 root 198:
199: if ((atomic_reqflag & CPU_REQ_STOP)) {
200: // STOP 状態は、残りサイクルを消費したことにして戻る
201: gMPU->putlog(2, "STOP 状態検出");
202: total_cycle = goal_cycle;
203: is_release = true;
204: }
205:
1.1 root 206: if (total_cycle >= goal_cycle)
207: break;
208: }
209:
210: return atomic_reqflag;
211: }
212:
213: // この命令後に実行を中断して VM に戻る
214: void
215: m88kcpu::Release()
216: {
217: atomic_reqflag |= CPU_REQ_RELEASE;
218: }
219:
220: // 通常の例外。
1.1.1.2 root 221: // DataAccessException, エラー、割り込み、TRAP からは使わない。
1.1 root 222: // vec はベクタ番号。
223: void
224: m88kcpu::Exception(int vec)
225: {
226: ExceptionCore(vec, ExceptionKind::NORMAL);
227: }
228:
229: // 例外処理コア部分
230: // vec はベクタ番号。
231: void
232: m88kcpu::ExceptionCore(int vec, ExceptionKind cause)
233: {
1.1.1.4 ! root 234: uint syscall = 0;
! 235:
1.1.1.3 root 236: // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
1.1.1.4 ! root 237: // OpenBSD システムコールなら番号もついでに記録。
! 238: // システムコール番号は tb0 発行時点で r13 にセットされている。
! 239: // ただし 0 番は syscall(2) (間接システムコール) で、実際の番号は r2。
! 240: // 198番 __syscall(2) だと r2:r3 (なので実質 r3)。
! 241: if (vec == 450) {
! 242: syscall = r[13];
! 243: if (syscall == 0) {
! 244: syscall = r[2];
! 245: } else if (syscall == 198) {
! 246: syscall = r[3];
! 247: }
! 248: syscall &= 0xfff;
! 249: }
! 250: exhist.AddEntry(xip, 0, 0xfc000000 | (syscall << 12) | vec);
! 251: brhist.AddEntry(xip, 0, 0xfc000000 | (syscall << 12) | vec);
1.1.1.3 root 252: // デバッガに通知 (例外ブレーク用)
1.1.1.4 ! root 253: debugger_notify_exception(vec);
! 254:
! 255: // 命令中で起きる例外はとりあえず全部2クロック追加? (Table.7-5)
! 256: if (cause != ExceptionKind::INTR) {
! 257: total_cycle += 2;
! 258: }
1.1.1.3 root 259:
1.1 root 260: if (psr & PSR_SFRZ) {
261: if (cause != ExceptionKind::TRAP) {
262: gMPU->putlog(2, "SFRZ Error Exception");
263: vec = M88K_EXCEP_ERROR;
264: cause = ExceptionKind::ERROR;
265: }
266: } else {
267: gMPU->putlog(2, "Exception %x", vec);
1.1.1.2 root 268: if (vec != 1) {
269: gMPU->putlog(1, "Exception %x x=%x n=%x f=%x", vec, xip, nip, fip);
270: }
1.1 root 271: sxip = xip;
272: snip = nip;
273: sfip = fip;
274:
275: sxip |= SIP_V;
276: snip |= SIP_V;
1.1.1.4 ! root 277: sfip |= SIP_V;
1.1 root 278:
279: if (OpIsBusErr(opX)) {
280: sxip |= SIP_E; // E bit
281: }
282: if (OpIsBusErr(opF)) {
283: snip |= SIP_E; // E bit
284: }
285:
286: if (vec == M88K_EXCEP_UNIMPL_OP) {
287: sxip &= ~SIP_V;
288: }
289: }
290:
1.1.1.2 root 291: if (cause != ExceptionKind::DATA) {
292: // データアクセス例外以外は、
293: // データパイプラインはフラッシュ状態とする。
294: // ほんとうは違う。かも。
295: dmt0 = 0;
296: } else {
297: // xip: ld/st/xmem
298: // nip: next
299: // fip: fetch
300:
301: // rom1.2 は DAE が起きると、
302:
303: // ROM挙動 CPU の RTE 挙動
304: // sfip = sxip
305: // sxip -> ~V (INVALID) CPU はそもそも sxip を評価しない
306: // snip = 0 (INVALID) CPU はこれを nop 相当に処理する
307: // rte
308:
309: // してくる。そのまま読むと命令を再実行しようとしているように
310: // 読めるが、再実行すると無限ループになってしまう。
311: // DAE の時点で xip が進んでいることを期待して利用している?
312:
313: // openbsd は
314: // xmem の write バスエラーを検知すると命令を再実行するために
315: // fip = nip, nip = xip して rte する。ということは xip = xmem に
316: // なっていることを期待している。
317:
318: // というわけで
319: // ld で DataAccessException のときは、実CPUはすでに次の命令を
320: // 実行している?
321: // もう少し考えると、スコアボードでレジスタの干渉がなければ、
322: // 実行している?
323:
324: // 仕方がないのでハックでごまかす。
325: // rte のほうも参照のこと。
326:
327: if (IsSuper() && xip >= 0x41000000 && xip < 0x42000000) {
328: // 遅延例外機構がないので、ROM 合わせのハック。
329: sxip = snip;
330: }
331: }
332:
1.1 root 333: epsr = psr;
1.1.1.2 root 334: SetPSR(psr | PSR_SUPER | PSR_SFD1 | PSR_IND | PSR_SFRZ);
1.1 root 335:
336: fip = vbr + (vec << 3);
337: fetch();
1.1.1.3 root 338: // ここでベクタに飛ぶのだが、ベクタ内の2命令でおそらく必ずもう一度
339: // 分岐してそっちでも履歴が残るので、ここでブランチ履歴を残すのは
340: // ちょっと冗長という気もする。
341:
1.1 root 342: if (OpIsBusErr(opF)) {
343: if (cause == ExceptionKind::ERROR) {
344: // double bus fault
345: // 実機は無限ループ状態
346: PANIC("Infinite ERROR (double bus fault)");
347: } else {
348: ExceptionCore(M88K_EXCEP_ERROR, ExceptionKind::ERROR);
349: }
350: }
351: }
352:
353: // データアクセス例外(Read)
354: void
355: m88kcpu::ReadDataException(uint32 addr, uint32 flag)
356: {
357: // ミスアラインドはこの例外より前にチェックされていて、
358: // ここにはアラインドしかこない。
359: dma0 = addr & ~0x3;
360: dmt0 = ((psr & PSR_BO_LE) ? DM_BO : 0)
361: | (((psr & PSR_SUPER) && !(flag & DM_USR)) ? DM_DAS : 0)
362: | (flag & DM_DOUB1) // ダブルワードの第1アクセスのときセット
363: | (flag & DM_LOCK) // xmem でセット
364: | (FLD_D << 7)
365: | (flag & DM_SIGNED)
366: | ((flag & DM_EN_MASK) >> (addr & 0x03))
367: | 0 // DM_WRITE = 0
368: | DM_VALID;
1.1.1.2 root 369: gMPU->putlog(1, "ReadDAE xip=%x addr=%x", xip, addr);
370: ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA);
1.1 root 371: }
372:
373: // データアクセス例外(Write)
374: void
375: m88kcpu::WriteDataException(uint32 addr, uint32 data, uint32 flag)
376: {
377: // ミスアラインドはこの例外より前にチェックされていて、
378: // ここにはアラインドしかこない。
379: dma0 = addr & ~0x3;
380: dmt0 = ((psr & PSR_BO_LE) ? DM_BO : 0)
381: | (((psr & PSR_SUPER) && !(flag & DM_USR)) ? DM_DAS : 0)
382: | (flag & DM_DOUB1) // ダブルワードの第1アクセスのときセット
383: | (flag & DM_LOCK) // xmem でセット
384: | (FLD_D << 7)
385: | (flag & DM_SIGNED)
386: | ((flag & DM_EN_MASK) >> (addr & 0x03))
387: | DM_WRITE
388: | DM_VALID;
389:
1.1.1.2 root 390: dmd0 = data;
391: gMPU->putlog(1, "WriteDAE xip=%x addr=%x", xip, addr);
392: ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA);
393: }
394:
395: // データアクセス例外(xmem)
396: void
397: m88kcpu::XmemDataException(uint32 addr, uint32 data, uint32 flag)
398: {
399: dma0 = addr & ~0x3;
400: dmt0 = ((psr & PSR_BO_LE) ? DM_BO : 0)
401: | (((psr & PSR_SUPER) && !(flag & DM_USR)) ? DM_DAS : 0)
402: | (flag & DM_DOUB1) // ダブルワードの第1アクセスのときセット
403: | (flag & DM_LOCK) // xmem でセット
404: | (FLD_D << 7)
405: | (flag & DM_SIGNED)
406: | ((flag & DM_EN_MASK) >> (addr & 0x03))
407: | 0 // DM_WRITE = 0
408: | DM_VALID;
409:
410: dma1 = addr & ~0x3;
411: dmt1 = ((psr & PSR_BO_LE) ? DM_BO : 0)
412: | (((psr & PSR_SUPER) && !(flag & DM_USR)) ? DM_DAS : 0)
413: | (flag & DM_DOUB1) // ダブルワードの第1アクセスのときセット
414: | (flag & DM_LOCK) // xmem でセット
415: | (FLD_D << 7)
416: | (flag & DM_SIGNED)
417: | ((flag & DM_EN_MASK) >> (addr & 0x03))
418: | DM_WRITE
419: | DM_VALID;
420:
421: dmd1 = data;
422:
423: // ミスアラインドはこの例外より前にチェックされていて、
424: // ここにはアラインドしかこない。
425: if (cmmu[1].fault_code != m88200::FAULT_CODE_BUSERR
426: || cmmu[1].acc_read) {
427: // アドレス変換フォルトまたは read 時点
428: // nop
1.1 root 429: } else {
1.1.1.2 root 430: // xmem の write 時点
431: dma0 = dma1;
432: dmt0 = dmt1;
433: dmd0 = dmd1;
434: dmt1 &= ~DM_VALID;
1.1 root 435: }
1.1.1.2 root 436:
437: gMPU->putlog(1, "xmemDAE xip=%x addr=%x", xip, addr);
438: ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA);
1.1 root 439: }
440:
441: void
442: m88kcpu::Interrupt()
443: {
444: atomic_reqflag |= CPU_REQ_INTR;
1.1.1.2 root 445: // gMPU->putlog(1, "INTERRUPT IGNORED");
1.1 root 446: gMPU->putlog(4, "INTERRUPT ACK (アドレスは不正確)");
447: }
448:
449: void
450: m88kcpu::fpu_unimpl()
451: {
452: printf("%s\n", __func__);
453: }
1.1.1.2 root 454:
455: OP_DEF(illegal)
456: {
457: // 本来は不当命令例外?
458: gMPU->putlog(0, "Illegal instruction %08x", (uint32)opX);
459: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.