Annotation of nono/vm/pio.cpp, revision 1.1.1.7

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 "pio.h"
1.1.1.2   root        8: #include "config.h"
1.1       root        9: #include "lcd.h"
1.1.1.3   root       10: #include "mainapp.h"
1.1       root       11: #include "scheduler.h"
                     12: 
                     13: //           IODevice
                     14: //               |
                     15: //               v
                     16: //          i8255Device  (8255 としての共通部分)
                     17: //               |
                     18: //    +----------+----------+
                     19: //    v          v          v
                     20: // PPIDevice  PIO0Device PIO1Device
                     21: // (X68030)   (LUNA)     (LUNA)
                     22: 
1.1.1.6   root       23: std::unique_ptr<PPIDevice> gPPI;
1.1       root       24: std::unique_ptr<PIO0Device> gPIO0;
                     25: std::unique_ptr<PIO1Device> gPIO1;
                     26: 
                     27: //
                     28: // i8255
                     29: //
                     30: 
                     31: // コンストラクタ
                     32: i8255Device::i8255Device()
                     33: {
                     34: }
                     35: 
                     36: // デストラクタ
                     37: i8255Device::~i8255Device()
                     38: {
                     39: }
                     40: 
1.1.1.4   root       41: // リセット
                     42: void
                     43: i8255Device::ResetHard()
                     44: {
                     45:        // clears the control register
                     46:        // and place ports A, B, and C in input mode.
                     47:        // The input latches in ports A, B, and C are not cleared.
                     48: 
                     49:        ctrl = 0b10011011;
                     50: }
                     51: 
1.1       root       52: // コントロールポートへの書き込み。
                     53: void
                     54: i8255Device::WriteCtrl(uint32 data)
                     55: {
                     56:        if ((data & 0x80)) {
                     57:                // 最上位が立っていればモード設定
                     58:                ctrl = data;
                     59:                putlog(1,
                     60:                        "モード設定 groupA=%d groupB=%d portA=%s portB=%s portC=%s/%s",
                     61:                        GetGroupAMode(), GetGroupBMode(),
                     62:                        PortADir() == DIR_IN ? "IN" : "OUT",
                     63:                        PortBDir() == DIR_IN ? "IN" : "OUT",
                     64:                        PortCHDir() == DIR_IN ? "IN" : "OUT",
                     65:                        PortCLDir() == DIR_IN ? "IN" : "OUT");
                     66: 
                     67:                // モード設定すると PC はゼロクリアされる
                     68:                for (int pc = 0; pc < 8; pc++) {
                     69:                        SetPC(pc, 0);
                     70:                }
                     71:        } else {
                     72:                // portC 出力
                     73:                //
                     74:                // 0  x  x  x  P  P  P  V
                     75:                //             |  |  |  +-- 書き込むビットデータ
                     76:                //             +--+--+----- 書き込むビット位置
                     77: 
                     78:                int pc = (data >> 1) & 7;
                     79:                int val = data & 1;
                     80:                SetPC(pc, val);
                     81:        }
                     82: }
                     83: 
                     84: void
                     85: i8255Device::SetPC(int pc, int data)
                     86: {
                     87:        // 継承側で用意すること。ここには来ない
                     88:        PANIC("not impl");
                     89: }
                     90: 
                     91: //
                     92: // X68k PPI
                     93: //
                     94: 
                     95: // コンストラクタ
                     96: PPIDevice::PPIDevice()
                     97:        : inherited()
                     98: {
                     99:        logname = "pio";
                    100:        devname = "PPI";
                    101:        devaddr = baseaddr;
                    102:        devlen  = 0x2000;
                    103: }
                    104: 
                    105: // デストラクタ
                    106: PPIDevice::~PPIDevice()
                    107: {
                    108: }
                    109: 
                    110: uint64
1.1.1.6   root      111: PPIDevice::Read(uint32 offset)
1.1       root      112: {
1.1.1.4   root      113:        gMPU->AddCycle(19);     // InsideOut p.135
                    114: 
1.1.1.6   root      115:        switch (offset) {
1.1       root      116:         case 0:
                    117:         case 1:
                    118:                return 0xff;
                    119:         case 2:
1.1.1.6   root      120:                putlog(0, "$e9a005 PortC 未実装読み込み");
1.1.1.5   root      121:                return 0xff;
1.1       root      122:         case 3:
1.1.1.6   root      123:                PANIC("PPI $e9a007 未サポート読み込み");
1.1       root      124:                break;
                    125:         default:
                    126:                __unreachable();
                    127:        }
                    128:        PANIC("not impl");
                    129: }
                    130: 
                    131: uint64
1.1.1.6   root      132: PPIDevice::Write(uint32 offset, uint32 data)
1.1       root      133: {
1.1.1.4   root      134:        gMPU->AddCycle(19);     // InsideOut p.135
                    135: 
1.1.1.6   root      136:        switch (offset) {
1.1       root      137:         case 0:
                    138:         case 1:
                    139:                break;
                    140:         case 2:
1.1.1.6   root      141:                putlog(0, "$e9a005 未実装書き込み $%02X (無視)", data);
1.1       root      142:                break;
                    143:         case 3:
                    144:                if (data != 0x92) {
1.1.1.6   root      145:                        PANIC("PPI $e9a007 未実装書き込み $%02X", data);
1.1       root      146:                } else {
1.1.1.6   root      147:                        putlog(0, "$e9a007 未実装書き込み $%02x (無視)", data);
1.1       root      148:                }
                    149:                break;
                    150:         default:
                    151:                __unreachable();
                    152:        }
                    153:        return 0;
                    154: }
                    155: 
                    156: uint64
1.1.1.6   root      157: PPIDevice::Peek(uint32 offset)
1.1       root      158: {
                    159:        // XXX
                    160:        return 0xff;
                    161: }
                    162: 
                    163: //
                    164: // LUNA PIO0
                    165: //
                    166: // 仕様は 0x49000000 から 4バイトだが、
                    167: // 0x48000000 から 0x4c000000 の手前まで 4バイト単位でミラーが見える。
                    168: //
                    169: // LUNA の DIP-SW は #1, #2 にそれぞれ8個、計16個ある。
                    170: // DIP-SW #1, #2 が設定ファイルの luna_dipsw_{1,2} に対応する。
                    171: // luna_dipsw_{1,2} はいずれも "0"/"1" を8つ並べた文字列であり、
                    172: // 先頭が本体表記でいう 1番、末尾側が本体表記でいう 8番に対応する。
                    173: // 変数は dipsw{1,2}[0..7] に対応。
                    174: // 設定ファイルと変数の値は %0 が down を表し、%1 が up を表す。
                    175: //
1.1.1.3   root      176: // LUNA-1:
1.1       root      177: // #1-1 up なら UNIX をロードして移行。down なら ROM モニタで起動。
                    178: // #1-2 up ならディスプレイコンソール。down ならシリアルコンソール。
                    179: // #1-3 up - color display, down - force to have monochrome display
                    180: // #1-4 up - no write verification,
                    181: //      down - verification on every harddisk write operation
                    182: // #1-5 up - OS is UniOS-U (SystemV/COFF),
                    183: //      down - OS is UniOS (4.3BSD/a.out OMAGIC)
                    184: // #1-6 down - force monochrome display?
                    185: // #1-7 up - boot from local device, down - boot from network
                    186: // #1-8 up - normal boot, down - start diagnostics と書いてあるが
                    187: //      down にすると画面真っ暗で詳細不明。
                    188: //
                    189: // #2-x すべて up にすると /boot が自動的にカーネルを起動。一つでも down が
                    190: //      あれば /boot がプロンプトで停止。これは ROM ではなく NetBSD のブート
                    191: //      ローダの話。
                    192: //      どこか由来のソースコードらしいがたぶんビット演算を間違えている。
1.1.1.3   root      193: //
                    194: // LUNA88K:
                    195: // #1-1 up なら ROM モニタで停止、down ならマルチユーザブート。
                    196: //      OpenBSD カーネルも up なら UserKernelConfig で止まる模様?。
                    197: // #1-2 up なら外付けシリアルコンソール、down なら内蔵ビットマップ。
                    198: // #1-3..#1-8 は予約。
                    199: // #2-x はユーザ(?)
1.1       root      200: 
                    201: // コンストラクタ
                    202: PIO0Device::PIO0Device()
                    203:        : inherited()
                    204: {
                    205:        logname = "pio0";
                    206:        devname = "PIO0";
                    207:        devaddr = 0x49000000;
                    208:        devlen  = 4;
1.1.1.2   root      209: 
1.1.1.3   root      210:        // 機種固有パラメータ
                    211: 
                    212:        // LUNA-1 と LUNA88K で使いやすいように初期値を変えておく。
                    213:        switch (gMainApp.GetVMType()) {
                    214:         case VMTYPE_LUNA1:
                    215:                gConfig->SetDefault("luna-dipsw1", "11110111");
                    216:                break;
                    217:         case VMTYPE_LUNA88K:
1.1.1.4   root      218:                gConfig->SetDefault("luna-dipsw1", "11111111");
1.1.1.3   root      219:                break;
                    220:         default:
                    221:                __unreachable();
                    222:        }
                    223:        // DIP-SW#2 はユーザ定義(?)なので(今の所?)どちらも共通。
                    224:        gConfig->SetDefault("luna-dipsw2", "11111111");
                    225: 
                    226:        monitor_size = nnSize(22, 11);
1.1       root      227: }
                    228: 
                    229: // デストラクタ
                    230: PIO0Device::~PIO0Device()
                    231: {
                    232: }
                    233: 
                    234: bool
                    235: PIO0Device::Init()
                    236: {
                    237:        // XXX 本当はラッチのタイミングが違う気もするけど、とりあえずね
                    238: 
1.1.1.2   root      239:        const ConfigItem& item1 = gConfig->Find("luna-dipsw1");
1.1       root      240:        const std::string& str1 = item1.AsString();
                    241:        if (str1.size() != 8) {
                    242:                item1.Err("must be 8 digits");
                    243:                return false;
                    244:        }
                    245: 
1.1.1.2   root      246:        const ConfigItem& item2 = gConfig->Find("luna-dipsw2");
1.1       root      247:        const std::string& str2 = item2.AsString();
                    248:        if (str2.size() != 8) {
                    249:                item2.Err("must be 8 digits");
                    250:                return false;
                    251:        }
                    252:        for (int i = 0; i < 8; i++) {
                    253:                dipsw1[i] = (str1[i] != '0');
                    254:                dipsw2[i] = (str2[i] != '0');
                    255:        }
                    256:        return true;
                    257: }
                    258: 
1.1.1.7 ! root      259: // XXX PowerOn か ResetHard 未調査
        !           260: 
1.1       root      261: uint64
1.1.1.6   root      262: PIO0Device::Read(uint32 offset)
1.1       root      263: {
                    264:        uint32 data;
                    265: 
1.1.1.6   root      266:        switch (offset) {
1.1       root      267:         case 0:
                    268:                // PortA
                    269:                putlog(1, "DIP-SW 1 読み込み");
                    270:                return GetPA();
                    271: 
                    272:         case 1:
                    273:                // PortB
                    274:                putlog(1, "DIP-SW 2 読み込み");
                    275:                return GetPB();
                    276: 
                    277:         case 2:
                    278:                // PortC
                    279:                data = GetPC();
1.1.1.6   root      280:                putlog(1, "PC 読み込み -> $%02x", data);
1.1       root      281:                return data;
                    282: 
                    283:         case 3:
                    284:                data = ReadCtrl();
                    285:                putlog(1, "コントロール読み込み -> $%02x", data);
                    286:                return data;
                    287: 
                    288:         default:
                    289:                __unreachable();
                    290:        }
                    291: }
                    292: 
                    293: uint64
1.1.1.6   root      294: PIO0Device::Write(uint32 offset, uint32 data)
1.1       root      295: {
1.1.1.6   root      296:        switch (offset) {
1.1       root      297:         case 0:        // PIO0 PortA: DIPSW1 (入力のみ)
                    298:         case 1:        // PIO0 PortB: DIPSW2 (入力のみ)
                    299:                return 0;
                    300: 
                    301:         case 2:        // PIO0 PortC: ホスト割り込み制御
                    302:                for (int i = 0; i < 8; i++) {
                    303:                        SetPC(i, data & 1);
                    304:                        data >>= 1;
                    305:                }
                    306:                return 0;
                    307: 
                    308:         case 3:        // コントロール
                    309:                WriteCtrl(data);
                    310:                return 0;
                    311: 
                    312:         default:
                    313:                __unreachable();
                    314:        }
                    315: }
                    316: 
                    317: uint64
1.1.1.6   root      318: PIO0Device::Peek(uint32 offset)
1.1       root      319: {
1.1.1.6   root      320:        switch (offset) {
1.1       root      321:         case 0:
                    322:                return GetPA();
                    323: 
                    324:         case 1:
                    325:                return GetPB();
                    326: 
                    327:         case 2:
                    328:                return GetPC();
                    329: 
                    330:         case 3:
                    331:                return ReadCtrl();
                    332: 
                    333:         default:
                    334:                __unreachable();
                    335:        }
                    336: }
                    337: 
1.1.1.3   root      338: void
                    339: PIO0Device::MonitorUpdate(TextScreen& monitor)
1.1.1.2   root      340: {
                    341:        int y;
                    342: 
                    343:        monitor.Clear();
                    344: 
                    345:        y = 0;
                    346:        monitor.Print(0, y++, "PortA(DIPSW1):%c%c%c%c%c%c%c%c",
                    347:                (dipsw1[0] ? '1' : '0'),
                    348:                (dipsw1[1] ? '1' : '0'),
                    349:                (dipsw1[2] ? '1' : '0'),
                    350:                (dipsw1[3] ? '1' : '0'),
                    351:                (dipsw1[4] ? '1' : '0'),
                    352:                (dipsw1[5] ? '1' : '0'),
                    353:                (dipsw1[6] ? '1' : '0'),
                    354:                (dipsw1[7] ? '1' : '0'));
                    355:        monitor.Print(0, y++, "PortB(DIPSW2):%c%c%c%c%c%c%c%c",
                    356:                (dipsw2[0] ? '1' : '0'),
                    357:                (dipsw2[1] ? '1' : '0'),
                    358:                (dipsw2[2] ? '1' : '0'),
                    359:                (dipsw2[3] ? '1' : '0'),
                    360:                (dipsw2[4] ? '1' : '0'),
                    361:                (dipsw2[5] ? '1' : '0'),
                    362:                (dipsw2[6] ? '1' : '0'),
                    363:                (dipsw2[7] ? '1' : '0'));
                    364:        monitor.Puts(0, y++, "PortC");
                    365:        monitor.Puts(1, y++, TA::OnOff(xp_reset), "b7: XP Reset");
                    366:        monitor.Puts(1, y++, TA::OnOff(parity),   "b6: Parity");
                    367:        monitor.Puts(1, y++,                      "b5: ---");
                    368:        monitor.Puts(1, y++, TA::OnOff(int5en),   "b4: Int5 Enable");
                    369:        monitor.Puts(1, y++, TA::OnOff(int5rq),   "b3: Int5 Request");
                    370:        monitor.Puts(1, y++, TA::OnOff(int1en),   "b2: Int1 Enable");
                    371:        monitor.Puts(1, y++,                      "b1: ---");
                    372:        monitor.Puts(1, y++, TA::OnOff(int1rq),   "b0: Intq Request");
                    373: }
                    374: 
1.1       root      375: // PortA (DIP-SW 1) 読み込み
                    376: uint32
                    377: PIO0Device::GetPA() const
                    378: {
                    379:        uint32 data = 0;
                    380:        for (int i = 0; i < 8; i++) {
                    381:                data |= ((int)dipsw1[i]) << i;
                    382:        }
                    383:        return data & 0xff;
                    384: }
                    385: 
                    386: // PortB (DIP-SW 2) 読み込み
                    387: uint32
                    388: PIO0Device::GetPB() const
                    389: {
                    390:        uint32 data = 0;
                    391:        for (int i = 0; i < 8; i++) {
                    392:                data |= ((int)dipsw2[i]) << i;
                    393:        }
                    394:        return data & 0xff;
                    395: }
                    396: 
                    397: // PortC 読み込み
                    398: uint32
                    399: PIO0Device::GetPC() const
                    400: {
                    401:        uint32 data;
                    402: 
                    403:        data = 0x00     // XXX 空きビットは何が読めるか?
                    404:                 | (int1rq ? 0x01 : 0)
                    405:                 | (int1en ? 0x04 : 0)
                    406:                 | (int5rq ? 0x08 : 0)
                    407:                 | (int5en ? 0x10 : 0)
                    408:                 | (parity ? 0x40 : 0)
                    409:                 | (xp_reset?0x80 : 0);
                    410:        return data;
                    411: }
                    412: 
                    413: // PortC 書き込み
                    414: void
                    415: PIO0Device::SetPC(int pc, int val)
                    416: {
                    417:        switch (pc) {
                    418:         case 0:
                    419:                if ((val ^ int1rq)) {
                    420:                        int1rq = val;
                    421:                        putlog(0, "INT1割り込み要求 %d (未実装)", int1rq);
                    422:                }
                    423:                return;
                    424:         case 1:
                    425:                // Not Connected.
                    426:                return;
                    427:         case 2:
                    428:                if ((val ^ int1en)) {
                    429:                        int1en = val;
                    430:                        putlog(0, "INT1割り込み許可 %d (未実装)", int1en);
                    431:                }
                    432:                return;
                    433:         case 3:
                    434:                if ((val ^ int5rq)) {
                    435:                        int5rq = val;
                    436:                        putlog(0, "INT5割り込み要求 %d (未実装)", int5rq);
                    437:                }
                    438:                return;
                    439:         case 4:
                    440:                if ((val ^ int5en)) {
                    441:                        int5en = val;
                    442:                        putlog(0, "INT5割り込み許可 %d (未実装)", int5en);
                    443:                }
                    444:                return;
                    445:         case 5:
                    446:                // Not Connected.
                    447:                return;
                    448:         case 6:
                    449:                // パリティ有効/無効
                    450:                // 使わないので無視するが読み返しのために覚えておく
                    451:                parity = val;
                    452:                return;
                    453:         case 7:
                    454:                // XP リセット
                    455:                if ((val ^ xp_reset)) {
                    456:                        xp_reset = val;
                    457:                        putlog(0, "XPリセット %d (未実装)", xp_reset);
                    458:                }
                    459:                return;
                    460:        }
                    461:        PANIC("invalid pc=%d", pc);
                    462: }
                    463: 
                    464: 
                    465: //
                    466: // LUNA PIO1
                    467: //
                    468: // 仕様は 0x4d000000 から 4バイトだが、
                    469: // 0x4c000000 から 0x50000000 の手前まで 4バイト単位でミラーが見える。
                    470: 
                    471: // コンストラクタ
                    472: PIO1Device::PIO1Device()
                    473:        : inherited()
                    474: {
                    475:        logname = "pio1";
                    476:        devname = "PIO1";
                    477:        devaddr = 0x4d000000;
                    478:        devlen  = 4;
                    479: 
                    480:        poffevent.dev = this;
                    481:        poffevent.func = (DeviceCallback_t)&PIO1Device::PowerOffCallback;
                    482:        poffevent.SetName("PIO1 Power Off");
                    483: }
                    484: 
                    485: // デストラクタ
                    486: PIO1Device::~PIO1Device()
                    487: {
                    488: }
                    489: 
1.1.1.4   root      490: // リセット
                    491: void
                    492: PIO1Device::ResetHard()
                    493: {
                    494:        inherited::ResetHard();
                    495: 
                    496:        poffevent.Stop();
                    497: }
                    498: 
1.1       root      499: uint64
1.1.1.6   root      500: PIO1Device::Read(uint32 offset)
1.1       root      501: {
                    502:        uint32 data;
                    503: 
1.1.1.6   root      504:        switch (offset) {
1.1       root      505:         case 0:
                    506:                // PIO1 PortA: LCD データ
                    507:                return gLCD->Read();
                    508: 
                    509:         case 1:
                    510:                // PIO1 PortB: 無効
                    511:                return 0xff;
                    512: 
                    513:         case 2:
                    514:                // PortC
                    515:                data = gLCD->Get();
                    516:                data |= xp_intreq ? 0 : 0x02;
                    517:                // 残りのビットは %1 だろうか?
                    518:                data |= 0x0d;
                    519:                putlog(1, "PC 読み込み -> $%02x", data);
                    520:                return data;
                    521: 
                    522:         case 3:
                    523:                data = ReadCtrl();
                    524:                putlog(1, "コントロール読み込み -> $%02x", data);
                    525:                return data;
                    526: 
                    527:         default:
                    528:                __unreachable();
                    529:        }
                    530: }
                    531: 
                    532: uint64
1.1.1.6   root      533: PIO1Device::Write(uint32 offset, uint32 data)
1.1       root      534: {
1.1.1.6   root      535:        switch (offset) {
1.1       root      536:         case 0:        // PIO1 PortA: LCD データ
                    537:                gLCD->Write(data);
                    538:                return 0;
                    539: 
                    540:         case 1:        // PIO1 PortB: ローカル割り込み
                    541:                break;
                    542: 
                    543:         case 2:        // PIO1 PortC: LCD/パワーオフ制御
                    544:                for (int i = 0; i < 8; i++) {
                    545:                        SetPC(i, data & 1);
                    546:                        data >>= 1;
                    547:                }
                    548:                return 0;
                    549: 
                    550:         case 3:        // コントロール
                    551:                WriteCtrl(data);
                    552:                return 0;
                    553: 
                    554:         default:
                    555:                __unreachable();
                    556:        }
1.1.1.6   root      557:        PANIC("$%08X <- $%02X 未サポート書き込み", gMPU->GetPaddr(), data);
1.1       root      558:        return 0;
                    559: }
                    560: 
                    561: uint64
1.1.1.6   root      562: PIO1Device::Peek(uint32 offset)
1.1       root      563: {
                    564:        uint32 data;
                    565: 
1.1.1.6   root      566:        switch (offset) {
1.1       root      567:         case 0:        // PIO1 PortA LCD データ
                    568:                return gLCD->Peek();
                    569: 
                    570:         case 1:        // PIO1 PortB 無効
                    571:                return 0xff;
                    572: 
                    573:         case 2:        // PIO1 PortC
                    574:                data = gLCD->Get();
                    575:                data |= xp_intreq ? 0 : 0x02;
                    576:                // 残りのビットは %1 だろうか?
                    577:                data |= 0x0d;
                    578:                return data;
                    579: 
                    580:         case 3:
                    581:                return ReadCtrl();
                    582: 
                    583:         default:
                    584:                __unreachable();
                    585:        }
                    586: }
                    587: 
                    588: void
                    589: PIO1Device::SetPC(int pc, int val)
                    590: {
                    591:        switch (pc) {
                    592:         case 0:
                    593:         case 1:
                    594:         case 2:
                    595:         case 3:
                    596:                // PortC の下半分は入力ポートとして使われることを想定して
                    597:                // いるので、ここへの書き込みは起きないだろうし、無視する。
                    598:                return;
                    599: 
                    600:         case 4:
                    601:                power = val;
                    602:                if (power) {
                    603:                        // 電源オフ取り消し
                    604:                        putlog(1, "電源 OFF 取消");
                    605:                        poffevent.Stop();
                    606:                } else {
                    607:                        // 1msec 後に電源オフ
                    608:                        putlog(1, "電源 OFF 指示");
                    609:                        poffevent.time = 1_msec;
                    610:                        poffevent.Start();
                    611:                }
                    612:                return;
                    613:         case 5:
                    614:                gLCD->SetRW(val);
                    615:                return;
                    616:         case 6:
                    617:                gLCD->SetRS(val);
                    618:                return;
                    619:         case 7:
                    620:                gLCD->SetE(val);
                    621:                return;
                    622: 
                    623:         default:
                    624:                break;
                    625:        }
                    626:        PANIC("PC%d 未実装", pc);
                    627: }
                    628: 
                    629: // 電源 OFF イベントのコールバック
                    630: void
1.1.1.4   root      631: PIO1Device::PowerOffCallback(Event& ev)
1.1       root      632: {
                    633:        putlog(0, "電源 OFF");
                    634:        gScheduler->RequestPowerOff();
                    635: }

unix.superglobalmegacorp.com

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