Annotation of nono/m680x0/m680x0core.cpp, revision 1.1.1.3

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.