Annotation of nono/m680x0/m68030subr.cpp, revision 1.1.1.9

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 "m68030core.h"
                      8: #include "m68030bus.h"
                      9: #include "m68030fpu.h"
1.1.1.3   root       10: #include "debugger.h"
1.1.1.9 ! root       11: #include "mpu680x0.h"
1.1       root       12: 
                     13: static void m68030_double_bus_fault(m68kcpu *, const char *);
                     14: 
                     15: // CPU コアの領域を確保して初期化
                     16: m68kcpu *
1.1.1.9 ! root       17: m68030_init(MPU680x0Device *dev_)
1.1       root       18: {
                     19:        m68kcpu *cpu;
                     20: 
                     21:        cpu = new m68kcpu();
                     22:        fpu_init(cpu);
1.1.1.9 ! root       23:        cpu->dev = dev_;
1.1       root       24: 
                     25:        for (int i = 0; i < 16; i++) {
                     26:                RegR(i) = 0xcccccccc;
                     27:        }
                     28:        // XXX ただし X68030 IPLROM には A6 を初期化せずに書き込んでいるバグが
                     29:        // あるため、A6 を概ね RAM のあるあたりに指定しておく…。
                     30:        RegA(6) = 0x000ccccc;
                     31: 
                     32:        return cpu;
                     33: }
                     34: 
1.1.1.9 ! root       35: // CPU の状態 (通常/STOP/HALT) を変更する
        !            36: static void
        !            37: m68030_change_state(m68kcpu *cpu, uint32 new_state)
1.1.1.7   root       38: {
1.1.1.9 ! root       39:        if (cpu->state != new_state) {
        !            40:                cpu->state = new_state;
        !            41:                // スケジューラに通知
        !            42:                cpu->dev->NotifyCPUMode(cpu->state);
1.1.1.7   root       43:        }
                     44: }
                     45: 
1.1       root       46: // 二重バスフォールト
                     47: static void
                     48: m68030_double_bus_fault(m68kcpu *cpu, const char *where)
                     49: {
1.1.1.9 ! root       50:        m68030_change_state(cpu, m68kcpu::CPU_STATE_HALT);
1.1.1.2   root       51: 
                     52:        // コールバックがあれば呼び出す
                     53:        if (cpu->halt_callback) {
                     54:                cpu->halt_callback(cpu->halt_arg);
                     55:        }
1.1       root       56: }
                     57: 
1.1.1.8   root       58: // 電源オン
                     59: void
                     60: m68030_power_on(m68kcpu *cpu)
                     61: {
1.1.1.9 ! root       62:        // サイクルを初期化。
        !            63:        cpu->used_cycle = 0;
        !            64: 
1.1.1.8   root       65:        // 履歴を初期化。電源(再)投入時のみ行う。
                     66:        cpu->exhist.Clear();
                     67:        cpu->brhist.Clear();
                     68: 
                     69:        // この後起きるリセット例外で PC を初期化する前に参照することになるので
                     70:        // これだけここでも初期化しておく。
                     71:        RegPC = 0;
                     72: }
                     73: 
1.1.1.9 ! root       74: // SR (と CCR) をセットする。
        !            75: // リセット例外以外から使う (リセット例外はやや異なるため)。
1.1       root       76: void
                     77: m68030_set_sr(m68kcpu *cpu, uint16 data)
                     78: {
                     79:        bool oldS = cpu->reg.s;
                     80:        bool oldM = cpu->reg.m;
                     81: 
                     82:        // 現在の SP を保存
                     83:        if (oldS == false) {
                     84:                RegUSP = RegA(7);
                     85:        } else if (oldM == false) {
                     86:                RegISP = RegA(7);
                     87:        } else {
                     88:                RegMSP = RegA(7);
                     89:        }
                     90: 
                     91:        CCR.Set(data);
                     92:        cpu->reg.s = ((data & M68K_SR_S) != 0);
                     93:        cpu->reg.m = ((data & M68K_SR_M) != 0);
1.1.1.9 ! root       94: 
        !            95:        // 割り込みマスクレジスタを更新して..
        !            96:        uint new_mask = (data >> 8) & 7;
        !            97:        if (cpu->reg.intr_mask != new_mask) {
        !            98:                cpu->reg.intr_mask = new_mask;
        !            99: 
        !           100:                // 次の命令境界で割り込みをチェックさせる
        !           101:                cpu->dev->CheckIntr();
        !           102:        }
1.1       root      103: 
                    104:        // 新しい SP を用意
                    105:        if (cpu->reg.s == false) {
                    106:                RegA(7) = RegUSP;
                    107:        } else if (cpu->reg.m == false) {
                    108:                RegA(7) = RegISP;
                    109:        } else {
                    110:                RegA(7) = RegMSP;
                    111:        }
                    112: 
                    113:        // バスの FC2 を変更
                    114:        cpu->bus.SetFC2(cpu->reg.s);
                    115: 
                    116:        // XXX 他に何か状態変えないといけないことあったっけか。
                    117: }
                    118: 
                    119: // 命令中に (An)+ で変化したアドレスレジスタを命令実行前の状態に巻き戻す。
                    120: // flag_pi は bit7..0 が save_pi[7..0] の A7..A0 に対応している。
                    121: void
                    122: m68030_restore_reg_pi(m68kcpu *cpu)
                    123: {
                    124:        for (int i = 7; cpu->flag_pi; i--) {
                    125:                if ((int8)cpu->flag_pi < 0) {
                    126:                        RegA(i) = cpu->save_pi[i];
                    127:                }
                    128:                cpu->flag_pi <<= 1;
                    129:        }
                    130: }
                    131: 
                    132: // 命令中に -(An) で変化したアドレスレジスタを命令実行前の状態に巻き戻す。
                    133: // flag_pd は bit7..0 が save_pd[7..0] の A7..A0 に対応している。
                    134: void
                    135: m68030_restore_reg_pd(m68kcpu *cpu)
                    136: {
                    137:        for (int i = 7; cpu->flag_pd; i--) {
                    138:                if ((int8)cpu->flag_pd < 0) {
                    139:                        RegA(i) = cpu->save_pd[i];
                    140:                }
                    141:                cpu->flag_pd <<= 1;
                    142:        }
                    143: }
                    144: 
                    145: // vector にジャンプする。vector はベクタ番号(アドレスではない)。
                    146: static inline void
                    147: m68030_jump_vector(m68kcpu *cpu, int vector)
                    148: {
1.1.1.3   root      149:        uint32 vecaddr;
1.1       root      150:        uint32 newpc;
                    151: 
1.1.1.3   root      152:        vecaddr = RegVBR + vector * 4;
                    153:        RegPC = vecaddr;
1.1       root      154:        newpc = m68030_fetch_32(cpu);
1.1.1.3   root      155:        // VBR 込みのジャンプ先が確定したところで今度はブランチ履歴に記録。
                    156:        // この時のブランチ元はベクタアドレス
                    157:        // (RegPC は fetch によって進んでしまうので先に保存)
1.1.1.5   root      158:        cpu->brhist.AddEntry(vecaddr | (cpu->reg.s ? 1 : 0), newpc, 0x8000);
1.1.1.3   root      159: 
                    160:        CHECK_ADDRESS_ERROR(newpc);
                    161:        RegPC = newpc;
1.1.1.2   root      162: 
                    163:        // 実際はここでパイプライン充填のために3ワードフェッチするので、
                    164:        // ベクタテーブルはフェッチ出来たけどそのジャンプ先がバスエラーに
                    165:        // なるケースはここでバスエラーが出なければいけないが、まだ
                    166:        // キャッシュを実装していないので、ここで1ワードフェッチしてみて
                    167:        // PC を戻すあほっぽい方法を取る。
                    168:        m68030_fetch_16(cpu);
                    169:        RegPC -= 2;
1.1       root      170: }
                    171: 
                    172: // 例外スタックフレーム $0 を生成する。
                    173: // vector はベクタ番号。
                    174: // pc はスタックに積むプログラムカウンタの値 (例外によって、この命令位置か
                    175: // 次の命令位置かが異なる)。
                    176: // sr は例外発生時の SR。
                    177: static inline void
                    178: m68030_generate_exframe_0(m68kcpu *cpu, int vector, uint32 pc, uint16 sr)
                    179: {
                    180:        // 例外スタックフレーム $0
                    181:        // SP+$00.W STATUS REGISTER
                    182:        //   +$02.L PROGRAM COUNTER             ; 例外ごとに異なる
                    183:        //   +$06.W %0000 | VECTOR
                    184: 
                    185:        m68030_push_16(cpu, (0 << 12) | (vector * 4));
                    186:        m68030_push_32(cpu, pc);
                    187:        m68030_push_16(cpu, sr);
                    188: }
                    189: 
                    190: // 例外スタックフレーム $2 を生成する。
                    191: // vector はベクタ番号。
                    192: // スタックに積む PC は常に次の命令。
                    193: static inline void
                    194: m68030_generate_exframe_2(m68kcpu *cpu, int vector, uint16 sr)
                    195: {
                    196:        // 例外スタックフレーム $2
                    197:        // SP+$00.W STATUS REGISTER
                    198:        //   +$02.L PROGRAM COUNTER             ; 次の命令位置
                    199:        //   +$06.W %0010 | VECTOR
                    200:        //   +$08.L INSTRUCTION ADDRESS ; 例外を起こした命令のアドレス
                    201: 
                    202:        m68030_push_32(cpu, RegPPC);    // 例外を起こした命令のアドレス
                    203:        m68030_push_16(cpu, (2 << 12) | (vector * 4));
                    204:        m68030_push_32(cpu, RegPC);             // 次の命令位置
                    205:        m68030_push_16(cpu, sr);
                    206: }
                    207: 
                    208: // 例外スタックフレーム $B を生成する。
                    209: static inline void
                    210: m68030_generate_exframe_b(m68kcpu *cpu, int vector, uint16 sr)
                    211: {
                    212:        uint16 ssw;
                    213:        uint32 fault_addr;
                    214:        uint32 stageBaddr;
                    215: 
                    216:        stageBaddr = RegPC + 2;
                    217:        // この後のスタック操作で bus が変化する前に保存
                    218:        fault_addr = cpu->bus.laddr;
                    219:        // DF ビットを立てておくと RTE した後この命令を再実行。
                    220:        ssw = cpu->bus.ssw | SSW_DF;
                    221: 
                    222:        // 例外スタックフレーム $B
                    223:        // SP+$00.W   STATUS REGISTER
                    224:        //   +$02.L   PROGRAM COUNTER
                    225:        //   +$06.W   %1011 | VECTOR
                    226:        //   +$08.W   (internal register)
                    227:        //   +$0a.W   SPECIAL STATUS REGISTER           ; SSW
                    228:        //   +$0c.W   INSTRUCTION PIPE STAGE C
                    229:        //   +$0e.W   INSTRUCTION PIPE STAGE B
                    230:        //   +$10.L   DATA CYCLE FAULT ADDRESS
                    231:        //   +$14.W*2 (internal register)
                    232:        //   +$18.L   DATA OUTPUT BUFFER
                    233:        //   +$1c.W*4 (internal register, 4 words)
                    234:        //   +$24.L   STAGE B ADDRESS
                    235:        //   +$28.W*2 (internal register, 2 words)
                    236:        //   +$2c.L   DATA INPUT BUFFER
                    237:        //   +$30.W*3 (internal register, 3 words)
                    238:        //   +$36.W   VERSION# | internal information
                    239:        //   +$38.W*18 (internal register, 18 words)
                    240:        //   +$5a
                    241:        //      こっちから上に向かって PUSH
                    242: 
                    243:        // internal registers, 18 words
                    244:        // 内部レジスタ18ワードのうち、ここでは17ワード分を save_pd[8] (16ワード)、
                    245:        // flag_pd (1ワード) として使うことにする。
                    246:        // -(An) でバスエラーが起きると An は減算された状態になるが、DF ビットが
                    247:        // 立っていて再実行する時には現行構造では命令先頭から再実行せざるをえず、
                    248:        // そうするともう一度プリデクリメントが動いてしまう。そのため、RTE の時点
                    249:        // で DF=1 ならプリデクリメント発生前の An に戻すという処理をする。
                    250:        // RTE 時点で DF=0 なら命令を再実行しないため An も巻き戻してはならず、
                    251:        // そのためプリデクリメント分の巻き戻しは RTE 時点まで行えない。
1.1.1.2   root      252:        // 一方、ポストインクリメントのケースはこの少し下の
                    253:        // m68030_exception_buserr() 内のコメント参照。
1.1       root      254:        for (int i = 7; i >= 0; i--) {
                    255:                m68030_push_32(cpu, cpu->save_pd[i]);
                    256:        }
                    257:        m68030_push_16(cpu, cpu->flag_pd);
                    258:        RegA(7) -= 2;
                    259: 
                    260:        RegA(7) -= 2;                                           // VERSION#
                    261:        RegA(7) -= 6;
                    262:        m68030_push_32(cpu, 0);                         // DATA INPUT BUFFER
                    263:        RegA(7) -= 4;
                    264:        m68030_push_32(cpu, stageBaddr);        // STAGE B ADDRESS
                    265:        RegA(7) -= 8;
                    266:        m68030_push_32(cpu, 0);                         // DATA OUTPUT BUFFER
                    267:        RegA(7) -= 4;
                    268:        m68030_push_32(cpu, fault_addr);        // DATA CYCLE FAULT ADDRESS
                    269:        m68030_push_16(cpu, 0);                         // INSTRUCTION PIPE STAGE B
                    270:        m68030_push_16(cpu, 0);                         // INSTRUCTION PIPE STAGE C
                    271:        m68030_push_16(cpu, ssw);
                    272:        RegA(7) -= 2;
                    273:        m68030_push_16(cpu, 0xb000 | (vector << 2));
                    274:        // 本当は現在の PC だが、再実行のため PPC を積む。
                    275:        m68030_push_32(cpu, RegPPC);            // PC
                    276:        m68030_push_16(cpu, sr);                        // 例外入り口で保存した SR
                    277: }
                    278: 
1.1.1.9 ! root      279: // 例外処理について。
        !           280: //
        !           281: // 0. リセット例外
        !           282: //     リセット例外は外部から m68030_exception_reset() を呼び出す。
        !           283: //
        !           284: // 1. バスエラー例外
        !           285: //     MMU による論理アドレスでのバスエラーは (m68030_mmu_translate_*() が
        !           286: //     false を返した後) m68030bus.cpp ルーチンが (C++の) 例外をスローする。
        !           287: //     物理アドレス空間アクセス後のバスエラーは (VM側の read*()/write*() が
        !           288: //     (int64)-1 を返した後) m68030bus.cpp ルーチンが (C++) の例外をスローする。
        !           289: //     どちらのケースも m68030core.cpp のメインループで catch し、その catch
        !           290: //     ブロック内で m68030_exception() を呼び出す。
        !           291: //
        !           292: // 2. アドレスエラー例外
        !           293: //     アドレスエラーは m68030_jump() 内でのみ発生するはずで、その場で
        !           294: //     m68030_exception() を呼んで処理する。
        !           295: //
        !           296: // 3. 命令中で起きる例外
        !           297: //     命令途中で起きる(確定的な)例外は m68030_exception() を呼び出す。
        !           298: //     これに該当するのは
        !           299: //       o 特権違反
        !           300: //       o A系列/F系列命令
        !           301: //       o TRAP* 系命令、CHK/CHK2 命令
        !           302: //       o トレース例外 (未実装)
        !           303: //       o ゼロ除算?
        !           304: //       o MMU 構成例外
        !           305: //       o 不当命令 (*)
        !           306: //     である。
        !           307: //     ただし、不当命令についてはどこで不当命令と判定されるかによって
        !           308: //     フローが複数存在することに注意。
        !           309: //       o 1ワード目が不当命令
        !           310: //             -> 命令がそれぞれ m68030_exception() で処理。
        !           311: //       o 1ワード目の EA が不当命令
        !           312: //             -> (C++の)例外をスローして catch 内で m68030_exception() を実行。
        !           313: //       o 2ワード目が不当命令
        !           314: //             -> 命令がそれぞれ m68030_exception() で処理。
        !           315: //
        !           316: // 4. 割り込み例外
        !           317: //     別コメント(下のほう)参照。
        !           318: //
        !           319: 
        !           320: // リセット例外処理。
        !           321: // 各レジスタを所定の状態にして、リセットベクタを読み込む。
        !           322: // リセットベクタが読み込めなければ HALT 状態に移行。
        !           323: // 戻り値は消費したサイクル数。
        !           324: int
        !           325: m68030_exception_reset(m68kcpu *cpu)
        !           326: {
        !           327:        uint64 cycle_start = cpu->used_cycle;
        !           328: 
        !           329:        // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
        !           330:        // m68k では今の所リセット例外発生時の PC にはあまり意味がないけど、
        !           331:        // m88k と動作を揃えておく。
        !           332:        // 履歴のリセットベクタは 0 の代わりに 1 を使う仕様になっている。
        !           333:        const uint vector = 0;
        !           334:        cpu->exhist.AddEntry(RegPC | 1, 0, 0x8000 | (vector + 1));
        !           335:        cpu->brhist.AddEntry(RegPC | 1, 0, 0x8000 | (vector + 1));
        !           336:        // デバッガに通知 (例外ブレーク用)。こっちのベクタは 0 のままでよい。
        !           337:        debugger_notify_exception(vector);
        !           338: 
        !           339:        // RESET 信号線をネゲートしてから SP の読み込み開始まで 4クロック
        !           340:        // (Section 7.8, Figure 7-64)
        !           341:        cpu->used_cycle += 4;
        !           342: 
        !           343:        // STOP 状態とホールト状態を解除。
        !           344:        m68030_change_state(cpu, m68kcpu::CPU_STATE_NORMAL);
        !           345: 
        !           346:        // 1. T0, T1 クリア
        !           347:        // 2. S をセット、M をクリア
        !           348:        // 3. 割り込みレベルを 7
        !           349: 
        !           350:        // ここは set_sr を使わずに設定する。
        !           351:        cpu->reg.s = true;
        !           352:        cpu->reg.m = false;
        !           353:        cpu->bus.SetFC2(cpu->reg.s);
        !           354:        // ここは割り込みレベル変化のチェックは不要でセットだけでいい。
        !           355:        cpu->reg.intr_mask = 7;
        !           356: 
        !           357:        // 4. VBR をクリア
        !           358:        RegVBR = 0;
        !           359:        // 5. キャッシュ
        !           360:        RegCACR = 0;    // XXX
        !           361:        // 6. キャッシュエントリクリア
        !           362:        // 7. TC:E、TTn:E をクリア
        !           363:        cpu->SetTC(cpu->GetTC() & ~m68030TC::TC_E);
        !           364:        cpu->SetTT(0, cpu->GetTT(0) & ~m68030TT::E);
        !           365:        cpu->SetTT(1, cpu->GetTT(1) & ~m68030TT::E);
        !           366:        // 8. リセットベクタ生成
        !           367: 
        !           368:        // 9. SP をロード
        !           369:        // 10. PC をロード
        !           370:        try {
        !           371:                RegPC = 0;
        !           372:                RegA(7) = m68030_fetch_32(cpu);
        !           373:                uint32 pc = m68030_fetch_32(cpu);
        !           374:                // 大丈夫のはずだけど m68030_fetch_32() が RegPC を書き換えるので一応
        !           375:                RegPC = pc;
        !           376:                // リセットベクタは常に 0, 4 番地から取得したことになっている
        !           377:                cpu->brhist.AddEntry(0x00000004 | 1, RegPC, 0x8000);
        !           378:        } catch (int) {
        !           379:                m68030_double_bus_fault(cpu, "reset exception");
        !           380:        }
        !           381: 
        !           382:        // SP のアドレスエラーはリセット例外ではチェックしないようだ。
        !           383:        // PC のアドレスエラーは本来ならここにあるはずの3ワードのプリフェッチで
        !           384:        // 行われるのだが、それがないので、とりあえずここで。
        !           385:        if ((RegPC & 1) != 0) {
        !           386:                m68030_double_bus_fault(cpu, "reset exception");
        !           387:        }
        !           388: 
        !           389:        return cpu->used_cycle - cycle_start;
        !           390: }
        !           391: 
1.1       root      392: // 例外処理 (リセット・割り込み以外) を行う。
                    393: // vector はベクタ番号。
                    394: void
                    395: m68030_exception(m68kcpu *cpu, int vector)
                    396: {
1.1.1.2   root      397:        // バスエラー・アドレスエラーは別で処理。
                    398:        if (vector <= M68K_EXCEP_ADDRERR) {
                    399:                m68030_exception_buserr(cpu, vector);
                    400:                return;
                    401:        }
                    402: 
1.1.1.3   root      403:        // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
1.1.1.5   root      404:        uint32 from = RegPPC | (cpu->reg.s ? 1 : 0);
                    405:        cpu->exhist.AddEntry(from, 0, 0x8000 | vector);
                    406:        cpu->brhist.AddEntry(from, 0, 0x8000 | vector);
1.1.1.3   root      407:        // デバッガに通知 (例外ブレーク用)
1.1.1.4   root      408:        debugger_notify_exception(vector);
1.1.1.3   root      409: 
1.1       root      410:        // 状態を保存
                    411:        uint16 sr = RegSR;
                    412: 
                    413:        // S を立てて T1,T0 を落とす
                    414:        m68030_set_sr(cpu, (RegSR | M68K_SR_S) & ~M68K_SR_T);
                    415: 
1.1.1.2   root      416:        try {
                    417:                switch (vector) {
                    418:                 case M68K_EXCEP_ILLEGAL:               // 不当命令
                    419:                 case M68K_EXCEP_PRIV:                  // 特権違反例外
                    420:                 case M68K_EXCEP_ALINE:                 // Aライン命令例外
                    421:                 case M68K_EXCEP_FLINE:                 // Fライン命令例外
                    422:                        // スタックに積む PC はこの命令のアドレス
                    423:                        m68030_generate_exframe_0(cpu, vector, RegPPC, sr);
                    424:                        break;
                    425: 
                    426:                 case M68K_EXCEP_TRAP0                  // TRAP#0
                    427:                  ... M68K_EXCEP_TRAP15:                // TRAP#15
                    428:                        // スタックに積む PC は次の命令
                    429:                        m68030_generate_exframe_0(cpu, vector, RegPC, sr);
                    430:                        break;
                    431: 
                    432:                 case M68K_EXCEP_ZERODIV:               // ゼロ除算
                    433:                 case M68K_EXCEP_CHK:                   // CHK
                    434:                 case M68K_EXCEP_TRAPV:                 // TRAPV
                    435:                 case M68K_EXCEP_MMU_CONFIG:    // MMU 構成例外
                    436:                        m68030_generate_exframe_2(cpu, vector, sr);
                    437:                        break;
                    438: 
                    439:                 default:
                    440:                        PANIC("$%08x exception vector %d ?", RegPPC, vector);
                    441:                        break;
1.1       root      442:                }
                    443: 
1.1.1.2   root      444:                // ベクタにジャンプ
                    445:                m68030_jump_vector(cpu, vector);
                    446:        } catch (int vector2) {
                    447:                assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector=%d", vector);
1.1       root      448: 
1.1.1.2   root      449:                // バスエラー処理へ
                    450:                m68030_exception_buserr(cpu, vector2);
1.1       root      451:        }
1.1.1.2   root      452: }
                    453: 
                    454: // バスエラー・アドレスエラー例外処理を行う。
                    455: // 他の例外とは違いが多いので分けた。
                    456: void
                    457: m68030_exception_buserr(m68kcpu *cpu, int vector)
                    458: {
                    459:        assertmsg((vector == M68K_EXCEP_BUSERR || vector == M68K_EXCEP_ADDRERR),
                    460:                "vector=%d", vector);
                    461: 
1.1.1.3   root      462:        // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
1.1.1.5   root      463:        uint32 from = RegPPC | (cpu->reg.s ? 1 : 0);
                    464:        cpu->exhist.AddEntry(from, 0, 0x8000 | vector);
                    465:        cpu->brhist.AddEntry(from, 0, 0x8000 | vector);
1.1.1.3   root      466:        // デバッガに通知 (例外ブレーク用)
1.1.1.4   root      467:        debugger_notify_exception(vector);
1.1.1.3   root      468: 
1.1.1.2   root      469:        // 状態を保存
                    470:        uint16 sr = RegSR;
1.1       root      471: 
1.1.1.2   root      472:        // ここで (An)+ で変化したレジスタを命令実行前の状態に巻き戻す。
                    473:        // EA 処理の時点で先にインクリメントしているのだが、バスエラーが
                    474:        // 起きたということは実際にはポストインクリメントは発生しないので、
                    475:        // ここで無条件に巻き戻してよい。SR を書き換えると A7 が切り替わって
                    476:        // しまうので、この巻き戻しは SR を書き換える前のここで行うこと。
                    477:        // 一方、プリデクリメントのケースはこの少し上の exframe_b の中の
                    478:        // コメント参照。
                    479:        m68030_restore_reg_pi(cpu);
                    480: 
                    481:        // S を立てて T1,T0 を落とす
                    482:        m68030_set_sr(cpu, (RegSR | M68K_SR_S) & ~M68K_SR_T);
                    483: 
                    484:        try {
                    485:                // フレームを作成。
                    486:                // 実際はショート($A)、ロング($B)の2種類あって、本来ならショートが
                    487:                // 使えればショートを使うのだが、ここでは全部ロングを使う。
                    488:                m68030_generate_exframe_b(cpu, vector, sr);
                    489:                // ベクタにジャンプ
                    490:                m68030_jump_vector(cpu, vector);
                    491:        } catch (int vector2) {
                    492:                // ここに来るのはバスエラーだけのはず
                    493:                assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector=%d", vector);
                    494: 
                    495:                // バスエラー・アドレスエラー処理中のバスエラーは
                    496:                // ダブルバスフォールト。
                    497:                m68030_double_bus_fault(cpu, "bus/address error handling");
                    498:        }
1.1       root      499: }
                    500: 
1.1.1.9 ! root      501: // 割り込み処理。
        !           502: //
        !           503: // 割り込みは、本来命令境界ごとにチェックして条件を満たせば割り込み例外が
        !           504: // 起動するのだが、割り込み例外が起きうる要因は
        !           505: // o 割り込み信号線が変化した時
        !           506: // o 割り込みマスクを変更した時
        !           507: // しかないはずなので、この時だけチェックすればよいはず。
        !           508: //
        !           509: // 外部割り込み信号線が変化した時 m68030_interrupt(level) が VM から呼ばれる。
        !           510: // m68030_interrupt() は割り込みレベルがマスクより高いか、レベルが下位から 7
        !           511: // に変化すれば true を返す。
        !           512: // これにより VM から割り込み処理 m68030_exception_interrupt() が呼ばれる。
        !           513: //
        !           514: // 命令によって割り込みマスクを変更する場合、命令は m68030_change_intrmask()
        !           515: // を呼ぶ。これが VM 側で次の命令実行コールバックを差し替えるため、
        !           516: // 次命令境界で割り込みのチェックが呼ばれる。これが m68030_check_interrupt()。
        !           517: // こちらは割り込みレベルがマスクより高いかどうかだけをチェックする (レベルは
        !           518: // 変化していないので下位から 7 に変化したかは調べない)。レベルのほうが高く
        !           519: // なっていれば true を返す。
        !           520: // これにより VM から割り込み処理 m68030_exception_interrupt() が呼ばれる。
        !           521: 
        !           522: // 割り込みレベル変更の受け付け。
        !           523: // 発生している最も高い割り込みレベルが変化した時にここに通知される。
        !           524: // 全員が下ろしたことによって 0 に遷移したことも通知されることに留意。
        !           525: // 割り込み例外を起動するなら true を返す。
        !           526: bool
        !           527: m68030_interrupt(m68kcpu *cpu, int level)
        !           528: {
        !           529:        int mask = cpu->reg.intr_mask;
        !           530: 
        !           531:        // 信号線が 2 クロック期間安定していること云々は省略
        !           532: 
        !           533:        // ホールトなら何もしない
        !           534:        if (cpu->state == m68kcpu::CPU_STATE_HALT) {
        !           535:                return false;
        !           536:        }
        !           537: 
        !           538:        // 割り込みを起こすかどうかに関わらずここで更新
        !           539:        cpu->intr_level = level;
        !           540: 
        !           541:        // 割り込み例外を起動するのは
        !           542:        // o レベルがマスクより高い
        !           543:        // o マスクに関係なく、レベルが下位から 7 に変化した場合
        !           544:        //   (ここは変化した時だけ呼ばれるのと 7 より上はないので
        !           545:        //   level==7 なら必ず「下位から 7 に変化」のはず)
        !           546:        if (level > mask || level == 7) {
        !           547:                return true;
        !           548:        }
        !           549: 
        !           550:        return false;
        !           551: }
        !           552: 
        !           553: // 割り込みをチェックする。
        !           554: // 割り込みマスク変更後の命令境界で VM 側から呼ばれる。
        !           555: // 割り込みを起こすなら true を返す。
        !           556: bool
        !           557: m68030_check_interrupt(m68kcpu *cpu)
        !           558: {
        !           559:        int mask = cpu->reg.intr_mask;
        !           560:        int level = cpu->intr_level;
        !           561: 
        !           562:        // こちらは HALT では呼び出されない
        !           563: 
        !           564:        // 割り込みレベルは変化していないので、ここでは Level 7 の特別処理は不要
        !           565:        if (level > mask) {
        !           566:                return true;
        !           567:        }
        !           568:        return false;
        !           569: }
        !           570: 
1.1       root      571: // 割り込み例外処理を行う。
1.1.1.9 ! root      572: // 割り込みの受け付けとレベルのチェック等の後、次命令境界で呼ばれるやつ。
        !           573: int
        !           574: m68030_exception_interrupt(m68kcpu *cpu)
1.1       root      575: {
1.1.1.9 ! root      576:        int cycle_start = cpu->used_cycle;
        !           577:        int level = cpu->intr_level;
        !           578: 
1.1.1.5   root      579:        // ユーザーズマニュアル(日本語) p.399 図8-5 の中ほど
                    580:        // 「/IPEND をネゲート、割り込みアクノリッジサイクルを実行」
                    581:        // 以降に相当。
                    582: 
1.1.1.9 ! root      583:        // STOP なら解除
        !           584:        m68030_change_state(cpu, m68kcpu::CPU_STATE_NORMAL);
        !           585: 
1.1.1.5   root      586:        // 割り込みアクノリッジサイクルを実行
                    587:        int vector = m68030_interrupt_acknowledge(level);
                    588:        if (vector == -1) {
                    589:                vector = 24 + level;
                    590:        }
                    591: 
1.1.1.3   root      592:        // 例外履歴に記録 (例外発生はブランチ履歴にも記録)
1.1.1.5   root      593:        uint32 from = RegPPC | (cpu->reg.s ? 1 : 0);
                    594:        uint32 inst = 0x8000 | (level << 8) | vector;
                    595:        cpu->exhist.AddEntry(from, 0, inst);
                    596:        cpu->brhist.AddEntry(from, 0, inst);
1.1.1.3   root      597:        // デバッガに通知 (例外ブレーク用)
1.1.1.4   root      598:        debugger_notify_exception(vector);
1.1       root      599: 
                    600:        // 状態を保存
1.1.1.9 ! root      601:        uint16 oldsr = RegSR;
1.1       root      602: 
1.1.1.9 ! root      603:        // S を立てて T1,T0 を落とす。割り込みレベルも変更。
        !           604:        uint16 newsr = RegSR & ~(M68K_SR_T | M68K_SR_IMASK);
        !           605:        newsr |= M68K_SR_S;
        !           606:        newsr |= level << 8;
        !           607:        m68030_set_sr(cpu, newsr);
1.1       root      608: 
1.1.1.2   root      609:        try {
                    610:                // 例外スタックフレーム $0 を生成。
                    611:                // スタックに積む PC は次の命令位置。
                    612:                // M ビットは未実装なのでスローアウェイとか無視
1.1.1.9 ! root      613:                m68030_generate_exframe_0(cpu, vector, RegPC, oldsr);
1.1       root      614: 
1.1.1.2   root      615:                // ベクタにジャンプ
                    616:                m68030_jump_vector(cpu, vector);
                    617:        } catch (int vector2) {
                    618:                assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector=%d", vector);
                    619: 
                    620:                // バスエラー処理へ
                    621:                m68030_exception_buserr(cpu, vector2);
                    622:        }
1.1.1.9 ! root      623: 
        !           624:        return cpu->used_cycle - cycle_start;
1.1       root      625: }
                    626: 
                    627: // RTE 命令。
                    628: // 例外処理の近くに置いておくほうが便利なので。
                    629: void
                    630: m68030_rte(m68kcpu *cpu)
                    631: {
                    632:        uint32 pc;
                    633:        uint16 sr;
                    634:        uint16 frame;
                    635: 
1.1.1.2   root      636:        try {
                    637:                sr = m68030_pop_16(cpu);
                    638:                pc = m68030_pop_32(cpu);
                    639:                frame = m68030_pop_16(cpu);
                    640:                switch (frame >> 12) {
                    641:                 case 0:
                    642:                        CYCLE2(18, 20);
                    643:                        m68030_set_sr(cpu, sr);
                    644:                        m68030_jump(cpu, pc);
                    645:                        break;
                    646: 
                    647:                 case 2:
                    648:                        CYCLE2(18, 20);
                    649:                        // +08.L INSTRUCTION ADDRESS を読み捨てる
                    650:                        RegA(7) += 4;
                    651:                        m68030_set_sr(cpu, sr);
                    652:                        m68030_jump(cpu, pc);
                    653:                        break;
                    654: 
                    655:                 case 0x0b:
                    656:                 {
                    657:                        uint32 stage_b;
                    658:                        uint16 ssw;
                    659: 
                    660:                        CYCLE(76);
                    661:                        RegA(7) += 2;                                           // $08.W
                    662:                        ssw = m68030_pop_16(cpu);                       // $0a.W SSW
                    663:                        RegA(7) += 4 * 6;                                       // $0c.L*6
                    664:                        stage_b = m68030_pop_32(cpu);           // $24.L Stage B
                    665:                        RegA(7) += 2 * 9;                                       // $28.W*9
                    666:                        // $3a..$58 ここが save_pd の取り出し
                    667:                        cpu->flag_pd = m68030_pop_16(cpu);
                    668:                        for (int i = 0; i < 8; i++) {
                    669:                                cpu->save_pd[i] = m68030_pop_32(cpu);
                    670:                        }
                    671:                        // A7 レジスタをリストアするかもしれない前に Super/User を
                    672:                        // 復帰しておかないといけない
                    673:                        m68030_set_sr(cpu, sr);
                    674: 
                    675:                        // DFビットセットなら命令を再実行するため、-(An) で変化
                    676:                        // したレジスタを巻き戻す。
                    677:                        // DFビットクリアなら例外要因の命令はハンドラで処理された
                    678:                        // とみなして、次の命令に進む。
                    679:                        if ((ssw & SSW_DF)) {
                    680:                                m68030_restore_reg_pd(cpu);
                    681:                        } else {
                    682:                                pc = stage_b - 2;
                    683:                        }
                    684:                        m68030_jump(cpu, pc);
                    685:                        break;
                    686:                 }
                    687: 
                    688:                 default:
                    689:                        PANIC("RTE frame=$%x not supported", frame >> 12);
                    690:                        break;
1.1       root      691:                }
1.1.1.2   root      692:        } catch (int vector2) {
                    693:                assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector2=%d", vector2);
1.1       root      694: 
1.1.1.2   root      695:                // RTE 処理中のバスエラーはダブルバスフォールト。
                    696:                m68030_double_bus_fault(cpu, "bus/address error handling");
1.1       root      697:        }
                    698: }
                    699: 
1.1.1.9 ! root      700: // STOP 命令。
        !           701: // 動作制御が必要なのでこっちに置いておくほうが便利。
        !           702: void
        !           703: m68030_stop(m68kcpu *cpu)
        !           704: {
        !           705:        uint32 data = m68030_fetch_16(cpu);
        !           706:        m68030_set_sr(cpu, data);
        !           707:        m68030_change_state(cpu, m68kcpu::CPU_STATE_STOP);
        !           708: 
        !           709:        cpu->dev->MakeNextFull();
1.1       root      710: }

unix.superglobalmegacorp.com

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