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