Annotation of nono/vm/sysctlr.cpp, revision 1.1.1.17

1.1       root        1: //
                      2: // nono
                      3: // Copyright (C) 2020 nono project
                      4: // Licensed under nono-license.txt
                      5: //
                      6: 
1.1.1.8   root        7: //
1.1.1.10  root        8: // LUNA-88K のシステムコントローラのような何か
1.1.1.8   root        9: //
1.1       root       10: 
1.1.1.8   root       11: // IODevice
                     12: //    |
                     13: //    +-- InterruptDevice (割り込みコントローラの共通部)
                     14: //          |
                     15: //          +-- M680x0Interrupt (m68k 割り込みコントローラの共通部)
                     16: //          |     |
                     17: //          |     +-- X68030Interrupt  (X68030 の割り込みコントローラ)
                     18: //          |     +-- LunaInterrupt    (LUNA-I の割り込みコントローラ)
                     19: //          |
                     20: //          +-- PEDECDevice (X68030 の Lv1 担当コントローラ)
                     21: //          |
                     22: //          |  +---------------+
1.1.1.10  root       23: //          +--| SysCtlrDevice | (LUNA-88K の割り込みコントローラ)
1.1.1.8   root       24: //             +---------------+
1.1       root       25: 
1.1.1.16  root       26: // この辺を全部このデバイスで受け付けてある。
                     27: //
                     28: // 公称アドレス  実態範囲
                     29: // $6500'000n  $64'〜$67'     割り込み制御
                     30: // $6900'000n  $68'〜$6b'     ソフトウェア割り込み設定
                     31: // $6d00'00nn  $6c'〜$6d'     CPUリセット(?)
                     32: // ($6e'〜$6f' は不明、$ffffffff が読める)
                     33: 
                     34: // $6500'000n: 割り込み制御レジスタ(?)
                     35: //     アドレスは
                     36: //       $6500'0000: CPU0
                     37: //       $6500'0004: CPU1
                     38: //       $6500'0008: CPU2
                     39: //       $6500'000c: CPU3
                     40: //     $64..$67 から始まるこの 64MB は 16バイトでミラーされている模様。
                     41: //
                     42: //     書き込み時は
                     43: //          3                   2                   1
                     44: //        1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
                     45: //       +-+-+-+-+-+-+---------------------------------------------------+
                     46: //       |6|5|4|3|2|1|                                                   |
                     47: //       +-+-+-+-+-+-+---------------------------------------------------+
                     48: //       bit31: %1 = 割り込み 6 イネーブル
                     49: //       bit30: %1 = 割り込み 5 イネーブル
                     50: //       bit29: %1 = 割り込み 4 イネーブル
                     51: //       bit28: %1 = 割り込み 3 イネーブル
                     52: //       bit27: %1 = 割り込み 2 イネーブル
                     53: //       bit26: %1 = 割り込み 1 イネーブル
                     54: //
                     55: //     m68k と異なり、個別の割り込みごとに許可/禁止できる。
                     56: //     該当ビットに 1 を書き込むと許可。空欄部分は無視される模様。
                     57: //
                     58: //     読み込み時は
                     59: //          3                   2                   1
                     60: //        1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
                     61: //       +-----+---------+-+-+-+-+-+-+-----------------------------------+
                     62: //       |Level|         |6|5|4|3|2|1|                                   |
                     63: //       +-----+---------+-+-+-+-+-+-+-----------------------------------+
                     64: //       bit31-29: 発生している最上位レベルの割り込み。範囲は7-0。
                     65: //       bit23-18: 書き込んだ割り込みマスク。
                     66: //
                     67: //     空欄部分は「不定」の模様。
                     68: //
                     69: //     bit31-29 のレベルは、割り込みコントローラに入力されている割り込み
                     70: //     信号線6本をマスク処理した後の、プライオリティバイナリエンコーダの値が
                     71: //     そのまま出力されている模様。ラッチ機構は無いようだ。
                     72: //     従ってマスクされている割り込み(ビットが 0) の値が出力されることはない。
                     73: //     同時に入力があった場合は、数字の大きいほうが出力される。
                     74: //     レベル7 はマスクなしでプライオリティエンコーダに入力されているようだ。
                     75: //     割り込み入力がないときは 0 になる模様。
                     76: //
                     77: //     本体 ABRT スイッチを押している間、すべての CPU の Level が %111 (=7)
                     78: //     になる。離すと戻る。
                     79: //     m88100 には NMI が無いので、通常の割り込みと同じように処理している?
                     80: //     (なので IND 状態だとABRT できない。いいのかそれで)
                     81: //     (OpenBSD6.7 の割り込みハンドラはABRTには対応していないように読める)
                     82: //
                     83: //     想定される回路
                     84: //
                     85: //       [割り込みソース]
                     86: //        7:ABRT スイッチ
                     87: //        |   6:システムクロック
                     88: //        |   |   5:(シリアル | XP 高レベル | 拡張 PC-98?)
                     89: //        |   |   |   4:(LANCE | 拡張スロット?)
                     90: //        |   |   |   |   3:SPC
                     91: //        |   |   |   |   |   2:(未使用?)
                     92: //        |   |   |   |   |   |   1:(XP 低レベル | SOFTINT レジスタ $6900'000n)
                     93: //        |   |   |   |   |   |   |
                     94: //        |   V   V   V   V   V   V
                     95: //        |  +---------------------+
                     96: //        |  |   割り込みマスク    | (R:bit23-18, W:bit31-26)
                     97: //        |  +---------------------+
                     98: //        |   |   |   |   |   |   |
                     99: //        V   V   V   V   V   V   V
                    100: //       +-------------------------+
                    101: //       |プライオリティエンコーダ |-> (R:bit31-29)
                    102: //       +-------------------------+
                    103: //                | | |
                    104: //                V V V
                    105: //              (Wired-OR)
                    106: //                  |
                    107: //              [M88K INT]
                    108: //
                    109: //     割り込みソース調査状況。(LUNA-I とはレベル配置が違う)
                    110: //
                    111: //     1:SOFTINT レジスタ $6900'000n
                    112: //             ROM のメモリコマンドで確認。OpenBSD machdep.c の記述の通り。
                    113: //     1:XP 低レベル
                    114: //             OpenBSD mainbus.c および Issue#131 より。
                    115: //     3:SPC
                    116: //             PROM 1.20, OpenBSD ともに使用。
                    117: //     4:LANCE
                    118: //             ROM はまだ不明。OpenBSD は使用。
                    119: //     4:拡張スロット
                    120: //             LUNA-I からの類推のみ。不明。
                    121: //     5:拡張PC98
                    122: //             LUNA-I からの類推のみ。
                    123: //             LUNA-88K にはスロットがないようなので LUNA-88K2 が必要。
                    124: //             OpenBSD mainbus.c に記述あり。
                    125: //     5:シリアル(uPD7201)
                    126: //             OpenBSD mainbus.c に記述。
                    127: //     5:XP 高レベル
                    128: //             Issue#131 より。
                    129: //     6:システムクロック
                    130: //             ROM のメモリコマンドではレベル6割り込みが常時上がっているように
                    131: //             観測できるので、おそらくシステムクロック割り込みが上がっている。
                    132: //             OpenBSD maibus.c にも記述。
                    133: //     7:ABRTスイッチ
                    134: //             ROM のメモリコマンドで確認。
                    135: //
                    136: // $6900'000n: ソフトウェア割り込み設定レジスタ(?)
                    137: //     アドレスは
                    138: //       $6900'0000: CPU0
                    139: //       $6900'0004: CPU1
                    140: //       $6900'0008: CPU2
                    141: //       $6900'000c: CPU3
                    142: //
                    143: //     この32ビットのどこかにライトアクセスすると、INT1 をアサートする。
                    144: //     この32ビットのどこかをリードアクセスすると、INT1 をネゲートする。
                    145: //     この時のデータバスは無視される。0x00 を書いてもアサートされることに注意。
                    146: //     リードアクセスで読み込める値も不定のようだ。
                    147: //     要はアドレスバスと R/W 信号線しか見ていない。
                    148: //
                    149: // $6d00'000n: CPU 個別リセットレジスタ(?)
                    150: //     このへん触ると CPU がリセットされるというかだんまりになるので調査困難。
                    151: //
                    152: // $6d00'0010: 全 CPU リセットレジスタ(?)
1.1       root      153: 
1.1.1.8   root      154: #include "sysctlr.h"
1.1.1.10  root      155: #include "lance.h"
1.1.1.8   root      156: #include "mpu88xx0.h"
                    157: #include "nmi.h"
                    158: #include "prom.h"
                    159: #include "sio.h"
                    160: #include "spc.h"
                    161: #include "sysclk.h"
1.1       root      162: 
                    163: // コンストラクタ
                    164: SysCtlrDevice::SysCtlrDevice()
1.1.1.10  root      165:        : inherited(OBJ_INTERRUPT)
1.1       root      166: {
1.1.1.11  root      167:        SetName("SysCtlr");
1.1.1.5   root      168:        AddAlias("SysCtlr");
1.1.1.11  root      169:        AddAlias("SystemCtlr");
1.1       root      170: 
1.1.1.12  root      171:        intmap_status = IntmapSentinel;
                    172: 
1.1.1.14  root      173:        monitor = gMonitorManager->Regist(ID_MONITOR_INTERRUPT, this);
1.1.1.17! root      174:        monitor->SetCallback(&SysCtlrDevice::MonitorScreen);
1.1       root      175: }
                    176: 
                    177: // デストラクタ
                    178: SysCtlrDevice::~SysCtlrDevice()
                    179: {
                    180: }
                    181: 
1.1.1.10  root      182: // 初期化
                    183: bool
                    184: SysCtlrDevice::Init()
                    185: {
1.1.1.12  root      186:        mpu88xx0 = GetMPU88xx0Device(mpu);
                    187: 
                    188:        // 検索順
                    189:        RegistINT(IntmapSysClk, "Clock",        GetSysClkDevice());
                    190:        RegistINT(IntmapSIO,    "SIO",          GetSIODevice());
                    191:        RegistINT(IntmapLance,  "Lance",        GetLanceDevice());
                    192:        RegistINT(IntmapSPC,    "SPC",          GetSPCDevice());
                    193:        RegistINT(IntmapXPHigh, "XP(Hi)",       DEVICE_XPINT_HIGH);
                    194:        RegistINT(IntmapXPLow,  "XP(Lo)",       DEVICE_XPINT_LOW);
                    195:        RegistINT(IntmapSoft,   "SoftInt",      mpu88xx0);      // ?
                    196:        RegistINT(IntmapNMI,    "NMI",          GetNMIDevice());
                    197: 
                    198:        // 表示順
                    199:        disp_list = {
                    200:                IntmapNMI,
                    201:                IntmapSysClk,
                    202:                IntmapSIO,
                    203:                IntmapXPHigh,
                    204:                IntmapLance,
                    205:                IntmapSPC,
                    206:                IntmapXPLow,
                    207:                IntmapSoft,
                    208:        };
                    209: 
                    210:        // 行数が確定したのでサイズ設定
1.1.1.14  root      211:        monitor->SetSize(41, 1 + disp_list.size());
1.1.1.10  root      212: 
                    213:        return true;
                    214: }
                    215: 
1.1.1.8   root      216: // リセット
1.1.1.3   root      217: void
1.1.1.8   root      218: SysCtlrDevice::ResetHard(bool poweron)
1.1.1.3   root      219: {
                    220:        // さすがにリセットくらいはされるだろう…
1.1.1.8   root      221:        inherited::ResetHard(poweron);
1.1.1.7   root      222: 
1.1.1.3   root      223:        for (int i = 0; i < 4; i++) {
                    224:                // XXX 初期値は分からないがとりあえず割り込み禁止にしておく
1.1.1.16  root      225:                cpu[i].maskval = 0x00000000;
1.1.1.3   root      226:                cpu[i].intmask = MakeIntMask(cpu[i].maskval, i);
                    227:                cpu[i].ipl = 0;
1.1.1.12  root      228:                cpu[i].softint = 0;
                    229:                cpu[i].softint_count = 0;
1.1.1.3   root      230:        }
1.1.1.12  root      231:        ChangeInterrupt();
1.1.1.3   root      232: }
                    233: 
1.1.1.12  root      234: busdata
1.1.1.13  root      235: SysCtlrDevice::Read(busaddr addr)
1.1       root      236: {
1.1.1.13  root      237:        uint32 paddr = addr.Addr();
                    238:        uint n = (paddr >> 2) & 3;
                    239:        busdata data;
1.1       root      240: 
1.1.1.13  root      241:        // XXX バイト/ワードアクセスは未対応
                    242:        if (addr.GetSize() < 4) {
                    243:                putlog(0, "Read $%08x size=%u (NOT IMPLEMENTED)",
                    244:                        paddr, addr.GetSize());
                    245:        }
                    246: 
1.1.1.16  root      247:        if (0x64000000 <= paddr && paddr < 0x68000000) {
                    248:                data = Peek4(paddr);
                    249:                putlog(2, "Read INT_CTRL(CPU#%u) -> $%08x", n, data.Data());
1.1       root      250: 
1.1.1.13  root      251:        } else if (0x69000000 <= paddr && paddr < 0x69000010) {
1.1.1.3   root      252:                // 読み出しで SOFTINT ネゲート (値は関係ない)
1.1.1.12  root      253:                if (cpu[n].softint) {
1.1.1.13  root      254:                        putlog(1, "Softint(CPU#%u) Negate", n);
1.1.1.12  root      255:                        cpu[n].softint = 0;
                    256:                        ChangeInterrupt();
                    257:                }
1.1.1.13  root      258:                data = 0xffffffff;
1.1       root      259: 
1.1.1.13  root      260:        } else if (0x6d000000 <= paddr && paddr < 0x6d000010) {
                    261:                putlog(0, "Read CPU%u reset (NOT IMPLEMENTED)", n);
                    262:                data = 0xffffffff;
                    263: 
                    264:        } else if (paddr == 0x6d000010) {
1.1.1.9   root      265:                putlog(0, "Read All CPU reset (NOT IMPLEMENTED)");
1.1.1.13  root      266:                data = 0xffffffff;
                    267: 
                    268:        } else {
                    269:                putlog(0, "Read $%08x (NOT IMPLEMENTED)", paddr);
                    270:                data.SetBusErr();
1.1       root      271:        }
                    272: 
1.1.1.13  root      273:        data |= BusData::Size4;
                    274:        return data;
1.1       root      275: }
                    276: 
1.1.1.12  root      277: busdata
1.1.1.13  root      278: SysCtlrDevice::Write(busaddr addr, uint32 data)
1.1       root      279: {
1.1.1.13  root      280:        uint32 paddr = addr.Addr();
                    281:        uint n = (paddr >> 2) & 3U;
1.1       root      282: 
1.1.1.13  root      283:        // XXX バイト/ワードアクセスは未対応
                    284:        if (addr.GetSize() < 4) {
                    285:                putlog(0, "Write $%08x size=%u (NOT IMPLEMENTED)",
                    286:                        paddr, addr.GetSize());
1.1       root      287:        }
1.1.1.13  root      288: 
1.1.1.16  root      289:        if (0x64000000 <= paddr && paddr < 0x68000000) {
1.1.1.13  root      290:                // 割り込みマスクを設定。
                    291:                WriteIntMask(n, data);
                    292: 
                    293:        } else if (0x69000000 <= paddr && paddr < 0x69000010) {
1.1.1.3   root      294:                // 書き込みで SOFTINT アサート (値は関係ない)
1.1.1.12  root      295:                if (cpu[n].softint == false) {
1.1.1.13  root      296:                        putlog(1, "Softint(CPU#%u) Assert", n);
1.1.1.12  root      297:                        cpu[n].softint = IntmapSoft;
                    298:                        cpu[n].softint_count++;
                    299:                        // 親クラスのカウンタも上げる (4つの和になる)
                    300:                        int clz = __builtin_clz(IntmapSoft);
                    301:                        counter[clz]++;
                    302:                        ChangeInterrupt();
                    303:                }
1.1.1.13  root      304: 
                    305:        } else if (0x6d000000 <= paddr && paddr < 0x6d000010) {
                    306:                putlog(0, "Write CPU%u reset (NOT IMPLEMENTED)", n);
                    307: 
                    308:        } else if (paddr == 0x6d000010) {
1.1.1.4   root      309:                putlog(1, "Reset All CPUs");
1.1.1.8   root      310:                // XXX このあたりもう少し整理する必要がある
                    311:                // 今は MPU.ResetHard() が func を ResetCallback に、time をリセット
                    312:                // 間隔に書き換えて Start() した後、この書き込み命令から戻ったところの
                    313:                // ExecCallback が time だけ所要サイクルに書き換えて再度 Start() する
                    314:                // ため、時間はともかく幸い ResetCallback() が呼ばれることで動いている。
1.1.1.10  root      315:                GetPROM0Device()->ResetHard(false);
                    316:                mpu->ResetHard(false);
1.1.1.8   root      317:                ResetHard(false);
1.1.1.13  root      318: 
                    319:        } else {
                    320:                putlog(0, "Write $%08x <- $%08x (NOT IMPLEMENTED)", paddr, data);
1.1       root      321:        }
                    322: 
1.1.1.13  root      323:        busdata r = BusData::Size4;
                    324:        return r;
1.1       root      325: }
                    326: 
1.1.1.16  root      327: // Peek1() の下請け。ロングワードで処理して後でバイトに分割するほうが楽。
                    328: uint32
                    329: SysCtlrDevice::Peek4(uint32 addr) const
1.1       root      330: {
1.1.1.13  root      331:        uint n = (addr >> 2) & 3;
1.1       root      332:        uint32 data;
                    333: 
1.1.1.16  root      334:        if (0x64000000 <= addr && addr < 0x68000000) {
                    335:                // bit31-29 に発生している最上位の割り込みレベル。
                    336:                // bit23-18 に設定したレベル 6-1 の割り込みマスク。
1.1.1.3   root      337:                data = (cpu[n].ipl << 29) | (cpu[n].maskval >> 8);
1.1       root      338:        } else {
                    339:                data = 0xffffffff;
                    340:        }
1.1.1.16  root      341:        return data;
                    342: }
                    343: 
                    344: busdata
                    345: SysCtlrDevice::Peek1(uint32 addr)
                    346: {
                    347:        uint32 data = Peek4(addr & ~3U);
1.1       root      348:        switch (addr & 3) {
                    349:         case 0:
                    350:                return data >> 24;
                    351:         case 1:
                    352:                return (data >> 16) & 0xff;
                    353:         case 2:
                    354:                return (data >>  8) & 0xff;
                    355:         case 3:
                    356:                return data & 0xff;
                    357:         default:
1.1.1.10  root      358:                __unreachable();
1.1       root      359:        }
                    360: }
                    361: 
1.1.1.2   root      362: void
1.1.1.17! root      363: SysCtlrDevice::MonitorScreen(Monitor *, TextScreen& screen)
1.1       root      364: {
1.1.1.3   root      365:        int y;
                    366: 
                    367:        // CPU の割り込み可否は本当は関係ないけど、便宜的に一緒に表示したい
1.1.1.10  root      368:        bool ei = mpu88xx0->IsIntrEnable();
1.1.1.3   root      369: 
1.1.1.5   root      370:        screen.Clear();
1.1.1.10  root      371: 
                    372:        // 0         1         2         3         4
                    373:        // 01234567890123456789012345678901234567890
                    374:        // Lv Device CPU0                     Count
                    375: 
1.1.1.3   root      376:        y = 0;
1.1.1.10  root      377:        // XXX CPU の実装数によってウィンドウ幅を変えること。
1.1.1.12  root      378:        screen.Print(0, y, "Lv Device  CPU0");
                    379:        screen.Puts(36, y, "Count");
1.1.1.10  root      380:        y++;
1.1.1.12  root      381:        for (uint32 map : disp_list) {
1.1.1.13  root      382:                uint clz = __builtin_clz(map);
                    383:                uint lv = (31 - clz) / 4;
                    384:                screen.Print(0, y, "%u", lv);
1.1.1.12  root      385:                screen.Puts(3, y, TA::OnOff(intmap_status & map), GetIntName(map));
                    386:                screen.Print(15, y, "%26s", format_number(counter[clz]).c_str());
1.1.1.3   root      387: 
                    388:                // CPU ごとのマスク
                    389:                // XXX 今は CPU は1つしかない
                    390:                for (int n = 0; n < 1; n++) {
                    391:                        // Softint の他 CPU 分は
                    392:                        // 動作としては常にマスクされてて通過しないから問題ないが、
                    393:                        // 見せる分にはマスクされているではなく無効と見せたほうがいい。
1.1.1.12  root      394:                        if ((cpu[n].intmask & map) == 0) {
                    395:                                screen.Puts(11 + n * 5, y, "Mask");
1.1.1.3   root      396:                        } else if (!ei) {
1.1.1.12  root      397:                                screen.Puts(11 + n * 5, y, "IND");
1.1.1.3   root      398:                        }
                    399:                }
                    400: 
                    401:                y++;
                    402:        }
                    403: 
1.1.1.12  root      404:        // ソフトウェア割り込みの CPU ごとのカウンタいる?
1.1.1.3   root      405: }
                    406: 
1.1.1.13  root      407: // 割り込みマスクを設定する。
                    408: // data はロングワードレジスタの書き込み値 (bit31-26 のみ有効)。
                    409: void
                    410: SysCtlrDevice::WriteIntMask(uint32 n, uint32 data)
                    411: {
                    412:        data &= 0xfc000000;
                    413: 
                    414:        // 割り込みレベル変えるたびに表示されるのですごく多い。
                    415:        // さらに同レベルが書き込まれることも多い…。
                    416:        if (cpu[n].maskval != data) {
                    417:                putlog(2, "Interrupt Mask(CPU#%u) <- $%08x", n, data);
                    418:        } else {
                    419:                putlog(3, "Interrupt Mask(CPU#%u) <- $%08x", n, data);
                    420:        }
                    421: 
                    422:        // 設定値を保存
                    423:        cpu[n].maskval = data;
                    424:        // 内部マスクを書き換えて割り込み状態を更新
                    425:        cpu[n].intmask = MakeIntMask(data, n);
                    426:        ChangeInterrupt();
                    427: }
                    428: 
1.1.1.3   root      429: // 割り込みアクノリッジはない
1.1.1.12  root      430: busdata
1.1.1.3   root      431: SysCtlrDevice::InterruptAcknowledge(int lv)
                    432: {
                    433:        return 0;
                    434: }
                    435: 
                    436: // 割り込み状態を更新
                    437: void
                    438: SysCtlrDevice::ChangeInterrupt()
                    439: {
                    440:        // XXX MPU 側がまだマルチプロセッサに対応してないので CPU#0 だけ
                    441:        for (int i = 0; i < 1; i++) {
1.1.1.12  root      442:                uint32 localmap = (intmap_status | cpu[i].softint) & cpu[i].intmask;
1.1.1.3   root      443: 
1.1.1.12  root      444:                // intmap_status の最下位ビットは常に立ててあり localmap は 0 にならない
1.1.1.13  root      445:                uint newipl = (uint)(31 - __builtin_clz(localmap)) / 4;
1.1.1.3   root      446: 
1.1.1.7   root      447:                // 割り込みレベルが変わったら、新しいレベルを通知
1.1.1.3   root      448:                if (newipl != cpu[i].ipl) {
                    449:                        cpu[i].ipl = newipl;
                    450:                        // MPU 側は 1 レベルしかない
1.1.1.10  root      451:                        mpu->Interrupt(cpu[i].ipl ? 1 : 0);
1.1.1.3   root      452:                }
1.1       root      453:        }
                    454: }
1.1.1.3   root      455: 
                    456: // 割り込みマスク設定レジスタの書き込み値から内部用のマスクを作成
                    457: // data は bit31-26 のみ使用。他のビットは不問。
                    458: // n は CPU# (Softint 用)。
                    459: /*static*/ uint32
1.1.1.13  root      460: SysCtlrDevice::MakeIntMask(uint32 data, uint n)
1.1.1.3   root      461: {
                    462:        uint32 val = 0xf0000001;        // Lv7 は NMI、最下位は番兵用
                    463: 
                    464:        uint32 mask = 0x0f000000;
                    465:        for (int i = 6; i >= 1; i--) {
                    466:                if ((int32)data < 0) {
                    467:                        val |= mask;
                    468:                }
                    469:                mask >>= 4;
                    470:                data <<= 1;
                    471:        }
                    472: 
                    473:        return val;
                    474: }

unix.superglobalmegacorp.com

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