Annotation of nono/vm/mpscc.cpp, revision 1.1.1.1

1.1       root        1: //
                      2: // nono
                      3: // Copyright (C) 2020 nono project
                      4: // Licensed under nono-license.txt
                      5: //
                      6: 
                      7: //
                      8: // uPD7201(MPSC) と Z8530(SCC) の共通部分 (シリアルポート1個つき)
                      9: //
                     10: 
                     11: // IODevice
                     12: //    |
                     13: //    |  +-------------+
                     14: //    +--| MPSCCDevice | (uPD7201 と Z8530 の共通部分)
                     15: //       +-------------+
                     16: //          |
                     17: //          +-- SCCDevice (X68030、Z8530)
                     18: //          +-- SIODevice (LUNA、uPD7201)
                     19: 
                     20: #define MPSCC_SIO_LOG_HACK
                     21: #include "mpscc.h"
                     22: #include "bitops.h"
                     23: #include "hostcom.h"
                     24: #include "interrupt.h"
                     25: #include "scheduler.h"
                     26: #include "sync.h"
                     27: 
                     28: //
                     29: // MPSCC チャンネル
                     30: //
                     31: 
                     32: // コンストラクタ
                     33: MPSCCChan::MPSCCChan()
                     34: {
                     35: }
                     36: 
                     37: // デストラクタ
                     38: MPSCCChan::~MPSCCChan()
                     39: {
                     40: }
                     41: 
                     42: // 初期化
                     43: void
                     44: MPSCCChan::Init(int id_)
                     45: {
                     46:        id = id_;
                     47:        name = 'A' + id;
                     48: }
                     49: 
                     50: 
                     51: //
                     52: // MPSCC デバイス
                     53: //
                     54: 
                     55: // コンストラクタ
                     56: MPSCCDevice::MPSCCDevice(const std::string& objname_)
                     57:        : inherited(objname_)
                     58: {
                     59:        for (int ch = 0; ch < mpscc.chan.size(); ch++) {
                     60:                auto& chan = mpscc.chan[ch];
                     61:                chan.Init(ch);
                     62: 
                     63:                // time は SetTxPeriod() でセット。
                     64:                // 名前は継承先でセットする。機種ごとに名前を使い分けているため。
                     65:                txevent[ch].dev = this;
                     66:                txevent[ch].func = ToEventCallback(&MPSCCDevice::TxCallback);
                     67:                txevent[ch].code = ch;
                     68:                txevent[ch].Regist();
                     69: 
                     70:                rxevent[ch].dev = this;
                     71:                rxevent[ch].func = ToEventCallback(&MPSCCDevice::RxCallback);
                     72:                rxevent[ch].code = ch;
                     73:                rxevent[ch].Regist();
                     74:        }
                     75: 
                     76:        // モニタは継承先で設定する
                     77: }
                     78: 
                     79: // デストラクタ
                     80: MPSCCDevice::~MPSCCDevice()
                     81: {
                     82:        if ((bool)hostcom) {
                     83:                hostcom->SetCallbackDevice(NULL);
                     84:        }
                     85: }
                     86: 
                     87: bool
                     88: MPSCCDevice::Create()
                     89: {
                     90:        // 対応するホストドライバを作成
                     91:        try {
                     92:                hostcom.reset(new HostCOMDevice("HostCOM"));
                     93:        } catch (...) {
                     94:                return false;
                     95:        }
                     96: 
                     97:        hostcom->SetRxCallback(ToDeviceCallback(&MPSCCDevice::HostRxCallback));
                     98:        hostcom->SetCallbackDevice(this);
                     99: 
                    100:        return true;
                    101: }
                    102: 
                    103: void
                    104: MPSCCDevice::SetLogLevel(int loglevel_)
                    105: {
                    106:        inherited::SetLogLevel(loglevel_);
                    107: 
                    108:        assert((bool)hostcom);
                    109:        hostcom->SetLogLevel(loglevel_);
                    110: }
                    111: 
                    112: // 初期化
                    113: bool
                    114: MPSCCDevice::Init()
                    115: {
                    116:        gScheduler->ConnectMessage(MessageID::HOSTCOM_RX, this,
                    117:                ToMessageCallback(&MPSCCDevice::RxMessageCallback));
                    118:        return true;
                    119: }
                    120: 
                    121: // チャンネルリセットの共通部
                    122: void
                    123: MPSCCDevice::ResetChannelCommon(MPSCCChan& chan)
                    124: {
                    125:        // 通信速度を反映する
                    126:        SetTxPeriod(chan);
                    127:        SetRxPeriod(chan);
                    128: 
                    129:        // リセットで送信バッファは空になるが、リセット直後に
                    130:        // Tx 割り込みを許可しても Tx 割り込みは起こさない。
                    131:        chan.txint_ready = false;
                    132: 
                    133:        // 割り込みをネゲートするため
                    134:        ChangeInterrupt();
                    135: }
                    136: 
                    137: // CR0/WR0 レジスタの内容を取得
                    138: // (値の取り出しだけなら共通)
                    139: uint8
                    140: MPSCCDevice::GetCR0(const MPSCCChan& chan) const
                    141: {
                    142:        uint8 data = (chan.crc_cmd << 6) | (chan.cmd << 3) | chan.ptr;
                    143:        return data;
                    144: }
                    145: 
                    146: // CR3/WR3 レジスタの内容を取得
                    147: uint8
                    148: MPSCCDevice::GetCR3(const MPSCCChan& chan) const
                    149: {
                    150:        uint8 data;
                    151: 
                    152:        data = chan.cr3 & ~MPSCC::CR3_RX_EN;
                    153:        if (chan.rx_enable)
                    154:                data |= MPSCC::CR3_RX_EN;
                    155: 
                    156:        return data;
                    157: }
                    158: 
                    159: // CR5/WR5 レジスタの内容を取得
                    160: uint8
                    161: MPSCCDevice::GetCR5(const MPSCCChan& chan) const
                    162: {
                    163:        uint8 data;
                    164: 
                    165:        data = chan.cr5 & ~MPSCC::CR5_TX_EN;
                    166:        if (chan.tx_enable)
                    167:                data |= MPSCC::CR5_TX_EN;
                    168: 
                    169:        return data;
                    170: }
                    171: 
                    172: // SR2B/RR2B レジスタの内容を取得
                    173: uint8
                    174: MPSCCDevice::GetSR2B() const
                    175: {
                    176:        uint8 data = mpscc.vector;
                    177: 
                    178:        // 割り込み要因をベクタに載せない
                    179:        if (mpscc.vis == MPSCC::VIS_FIXED) {
                    180:                return data;
                    181:        }
                    182: 
                    183:        uint stat = GetHighestInt();
                    184: 
                    185:        // 8種類 (3bit) の割り込み要因をベクタに載せる。
                    186:        // 載せる場所 (と向き) はそれぞれ 2種類ずつある。
                    187:        switch (mpscc.vis) {
                    188:         case MPSCC::VIS_432:           // uPD7201: INTMode = INT85
                    189:                // 割り込み要求がない場合は %111
                    190:                if (stat == MPSCC::VS_none)
                    191:                        stat = 7;
                    192:                data &= ~0x1c;
                    193:                data |= stat << 2;
                    194:                break;
                    195: 
                    196:         case MPSCC::VIS_210:           // uPD7201: INTMode = INT86
                    197:                // 割り込み要求がない場合は %111
                    198:                if (stat == MPSCC::VS_none)
                    199:                        stat = 7;
                    200:                data &= ~0x07;
                    201:                data |= stat;
                    202:                break;
                    203: 
                    204:         case MPSCC::VIS_321:           // Z8530: StatusHigh/StatusLow = %0
                    205:                // 割り込み要求がない場合は %011
                    206:                if (stat == MPSCC::VS_none)
                    207:                        stat = 3;
                    208:                data &= ~0x0e;
                    209:                data |= stat << 1;
                    210:                break;
                    211: 
                    212:         case MPSCC::VIS_456:           // Z8530: StatusHigh/StatusLow = %1
                    213:                // 割り込み要求がない場合は %011
                    214:                if (stat == MPSCC::VS_none)
                    215:                        stat = 3;
                    216:                // VIS_321 とはビット順が逆
                    217:                data &= ~0x70;
                    218:                data |= bitrev(stat << 1);
                    219:                break;
                    220:         default:
                    221:                __unreachable();
                    222:        }
                    223: 
                    224:        return data;
                    225: }
                    226: 
                    227: // CR0/WR0 Send Abort コマンド
                    228: void
                    229: MPSCCDevice::SendAbort(MPSCCChan& chan)
                    230: {
                    231:        putlog(0, "%s: Command 'Send Abort' 未実装", CRName(chan, 0));
                    232: }
                    233: 
                    234: // CR0/WR0 Reset External/Status Interrupt コマンド
                    235: void
                    236: MPSCCDevice::ResetESIntr(MPSCCChan& chan)
                    237: {
                    238:        putlog(3, "%s: Command 'Reset E/S Interrupt'", CRName(chan, 0));
                    239:        chan.es_latched = false;
                    240: }
                    241: 
                    242: // CR0/WR0 Enable Interrupt on Next Rx Character コマンド
                    243: void
                    244: MPSCCDevice::EnableIntrOnNextRx(MPSCCChan& chan)
                    245: {
                    246:        putlog(1, "%s: Command 'Enable Intr on Next Rx Character'",
                    247:                CRName(chan, 0));
                    248:        chan.all_or_first = true;
                    249: }
                    250: 
                    251: // CR0/WR0 Reset Tx Interrupt/DMA Pending コマンド
                    252: void
                    253: MPSCCDevice::ResetTxIntrPending(MPSCCChan& chan)
                    254: {
                    255:        putlog(2, "%s: Command 'Reset Tx Intr/DMA Pending'", CRName(chan, 0));
                    256:        chan.txint_ready = false;
                    257:        ChangeInterrupt();
                    258: }
                    259: 
                    260: // CR0/WR0 Error Reset コマンド
                    261: void
                    262: MPSCCDevice::ErrorReset(MPSCCChan& chan)
                    263: {
                    264:        putlog(1, "%s: Command 'Error Reset'", CRName(chan, 0));
                    265:        chan.sr1 &= ~(MPSCC::SR1_ENDOFFRAME |
                    266:                      MPSCC::SR1_FRAMEERROR |
                    267:                      MPSCC::SR1_RXOVERRUN  |
                    268:                      MPSCC::SR1_PARITYERROR);
                    269:        // XXX Special Rx Condition は未実装
                    270: }
                    271: 
                    272: // CR0 End of Interrupt コマンド
                    273: // WR0 Reset Highest IUS コマンド
                    274: // たぶん名前が違うだけで同じ。後者のほうが分かりやすいのでこっちを使う
                    275: void
                    276: MPSCCDevice::ResetHighestIUS(MPSCCChan& chan)
                    277: {
                    278:        putlog(2, "%s: Command 'Reset Highest IUS'", CRName(chan, 0));
                    279: 
                    280:        // ペンディングのうち最も高い割り込み要因をクリア
                    281:        uint stat = GetHighestInt();
                    282:        switch (stat) {
                    283:         case MPSCC::VS_TxB:
                    284:                mpscc.chan[1].intpend &= ~INTPEND_TX;
                    285:                break;
                    286:         case MPSCC::VS_ESB:
                    287:                mpscc.chan[1].intpend &= ~INTPEND_ES;
                    288:                break;
                    289:         case MPSCC::VS_RxB:
                    290:                mpscc.chan[1].intpend &= ~INTPEND_RX;
                    291:                break;
                    292:         case MPSCC::VS_SPB:
                    293:                mpscc.chan[1].intpend &= ~INTPEND_SP;
                    294:                break;
                    295: 
                    296:         case MPSCC::VS_TxA:
                    297:                mpscc.chan[0].intpend &= ~INTPEND_TX;
                    298:                break;
                    299:         case MPSCC::VS_ESA:
                    300:                mpscc.chan[0].intpend &= ~INTPEND_ES;
                    301:                break;
                    302:         case MPSCC::VS_RxA:
                    303:                mpscc.chan[0].intpend &= ~INTPEND_RX;
                    304:                break;
                    305:         case MPSCC::VS_SPA:
                    306:                mpscc.chan[0].intpend &= ~INTPEND_SP;
                    307:                break;
                    308: 
                    309:         default:
                    310:                break;
                    311:        }
                    312:        ChangeInterrupt();
                    313: }
                    314: 
                    315: // CR3/WR3 への書き込み
                    316: void
                    317: MPSCCDevice::WriteCR3(MPSCCChan& chan, uint32 data)
                    318: {
                    319:        bool old_enable;
                    320: 
                    321:        old_enable = chan.rx_enable;
                    322:        chan.cr3 = data;
                    323:        chan.rx_enable = (data & MPSCC::CR3_RX_EN);
                    324: 
                    325:        putlog(2, "%s <- $%02x", CRName(chan), data);
                    326: 
                    327:        // Disable は、Enable から変化した時だけ表示したい
                    328:        // (Enable は SetRxPeriod() 内で表示)
                    329:        if (old_enable == true && chan.rx_enable == false) {
                    330:                putlog(1, "Channel %c RX Disable", chan.name);
                    331:        }
                    332: 
                    333:        SetRxPeriod(chan);
                    334: }
                    335: 
                    336: // CR4/WR4 への書き込み
                    337: void
                    338: MPSCCDevice::WriteCR4(MPSCCChan& chan, uint32 data)
                    339: {
                    340:        chan.cr4 = data;
                    341:        putlog(2, "%s <- $%02x", CRName(chan), data);
                    342:        uint8 stopbits = (chan.cr4 & MPSCC::CR4_STOPBITS);
                    343: 
                    344:        if (stopbits == MPSCC::STOPBITS_SYNC) {
                    345:                // 同期モードはサポートしない
                    346:                putlog(2, "Channel %c Synchronous mode not supported", chan.name);
                    347:        } else {
                    348:                const char *sm[] = { "?", "1", "1.5", "2" };
                    349:                putlog(2, "Channel %c Async Mode (x%d clock, stopbit %s)",
                    350:                        chan.name,
                    351:                        clkrates[(chan.cr4 & MPSCC::CR4_CLKRATE) >> 6],
                    352:                        sm[stopbits >> 2]);
                    353:        }
                    354: 
                    355:        SetTxPeriod(chan);
                    356:        SetRxPeriod(chan);
                    357: }
                    358: 
                    359: // CR5/WR5 への書き込み
                    360: void
                    361: MPSCCDevice::WriteCR5(MPSCCChan& chan, uint32 data)
                    362: {
                    363:        bool old_enable;
                    364: 
                    365:        old_enable = chan.tx_enable;
                    366:        chan.cr5 = data;
                    367:        chan.tx_enable = (data & MPSCC::CR5_TX_EN);
                    368: 
                    369:        putlog(2, "%s <- $%02x", CRName(chan), data);
                    370: 
                    371:        // Disable は、Enable から変化した時だけ表示したい
                    372:        // (Enable は SetTxPeriod() 内で表示)
                    373:        if (old_enable == true && chan.tx_enable == false) {
                    374:                putlog(1, "Channel %c TX Disable", chan.name);
                    375:        }
                    376: 
                    377:        SetTxPeriod(chan);
                    378:        TrySend(chan);
                    379: }
                    380: 
                    381: // CR6/WR6 への書き込み
                    382: void
                    383: MPSCCDevice::WriteCR6(MPSCCChan& chan, uint32 data)
                    384: {
                    385:        chan.cr6 = data;
                    386: 
                    387:        putlog(0, "%s <- $%02x 書き込み(未実装)", CRName(chan), data);
                    388: }
                    389: 
                    390: // CR7/WR7 への書き込み
                    391: void
                    392: MPSCCDevice::WriteCR7(MPSCCChan& chan, uint32 data)
                    393: {
                    394:        chan.cr7 = data;
                    395: 
                    396:        putlog(0, "%s <- $%02x 書き込み(未実装)", CRName(chan), data);
                    397: }
                    398: 
                    399: // データポートの読み込み
                    400: uint8
                    401: MPSCCDevice::ReadData(MPSCCChan& chan)
                    402: {
                    403:        uint8 data;
                    404: 
                    405:        // 受信バッファが空でも直近のデータが読めるので常に [0] でよい
                    406:        data = chan.rxbuf[0];
                    407:        if (chan.rxlen > 0) {
                    408:                putlog(3, "Ch%c Data -> $%02x", chan.name, data);
                    409:                chan.rxlen--;
                    410:                if (chan.rxlen == 0) {
                    411:                        // 取り出して空になった
                    412:                        ChangeInterrupt();
                    413:                } else {
                    414:                        chan.rxbuf[0] = chan.rxbuf[1];
                    415:                        chan.rxbuf[1] = chan.rxbuf[2];
                    416:                }
                    417:        } else {
                    418:                putlog(3, "Ch%c Data -> $%02x (Empty)", chan.name, data);
                    419:        }
                    420: 
                    421:        return data;
                    422: }
                    423: 
                    424: // データポートの書き込み
                    425: void
                    426: MPSCCDevice::WriteData(MPSCCChan& chan, uint32 data)
                    427: {
                    428:        char charbuf[4];
                    429: 
                    430:        if (isprint((int)data)) {
                    431:                snprintf(charbuf, sizeof(charbuf), "'%c'", data);
                    432:        } else {
                    433:                charbuf[0] = '\0';
                    434:        }
                    435:        putlog(3, "Ch%c Data <- $%02x %s", chan.name, data, charbuf);
                    436: 
                    437:        // XXX: EMPTY でないときにデータを書いたらどうなるかは未定義
                    438: 
                    439:        // 1byte の TX Buffer
                    440:        chan.txlen = 1;
                    441:        chan.txbuf = data;
                    442: 
                    443:        // データ書き込み後に Empty になったら割り込みを上げるため
                    444:        // ここで ready をセット
                    445:        chan.txint_ready = true;
                    446: 
                    447:        TrySend(chan);
                    448: }
                    449: 
                    450: // データポートの Peek
                    451: uint8
                    452: MPSCCDevice::PeekData(const MPSCCChan &chan) const
                    453: {
                    454:        // 受信バッファにデータがあれば先頭バイトが見える。
                    455:        // 受信バッファが空でも直近に取り出したバイトがそのまま見える。
                    456:        return chan.rxbuf[0];
                    457: }
                    458: 
                    459: // 送信できるか調べて送信する
                    460: void
                    461: MPSCCDevice::TrySend(MPSCCChan& chan)
                    462: {
                    463:        // 送信可で送信バッファにデータがあって送信中でなければ
                    464:        if (chan.tx_enable && chan.txlen && !txevent[chan.id].IsRunning()) {
                    465:                // 送信する
                    466:                chan.tx_shiftreg = chan.txbuf;
                    467:                chan.txlen = 0;
                    468:                gScheduler->StartEvent(txevent[chan.id]);
                    469:                putlog(2, "Ch%c send $%02x", chan.name, chan.tx_shiftreg);
                    470: 
                    471:                // XXX たぶん送信してから割り込みを上げるまでにいくらか遅れがあるはず
                    472:        }
                    473: 
                    474:        // ネゲートされることがあるので、送信しなくても割り込み状態を更新する
                    475:        ChangeInterrupt();
                    476: }
                    477: 
                    478: // 送信のイベントコールバック
                    479: void
                    480: MPSCCDevice::TxCallback(Event& event)
                    481: {
                    482:        int ch = event.code;
                    483:        MPSCCChan& chan = mpscc.chan[ch];
                    484: 
                    485:        // 接続デバイスの呼び出し
                    486:        Tx(ch, chan.tx_shiftreg);
                    487: 
                    488:        // 次の送信データが来てたら送信
                    489:        TrySend(chan);
                    490: }
                    491: 
                    492: // TX/RX の bits/char のレジスタ値を実際の bits/char の値に変換する。
                    493: /*static*/ const int
                    494: MPSCCDevice::bits_per_char[] = { 5, 7, 6, 8 };
                    495: 
                    496: // CR4 CLKRATE のレジスタ値を実際の倍率に変換する。
                    497: /*static*/ const int
                    498: MPSCCDevice::clkrates[] = { 1, 16, 32, 64 };
                    499: 
                    500: // ホストスレッドからの受信通知
                    501: // (HostCOM スレッドで呼ばれる)
                    502: void
                    503: MPSCCDevice::HostRxCallback()
                    504: {
                    505:        // スレッドを超えるためにメッセージを投げる
                    506:        gScheduler->SendMessage(MessageID::HOSTCOM_RX);
                    507: }
                    508: 
                    509: // 1バイト受信する。
                    510: // 受理されれば true を返す。
                    511: bool
                    512: MPSCCDevice::Rx(int ch, uint32 data)
                    513: {
                    514:        MPSCCChan& chan = mpscc.chan[ch];
                    515: 
                    516:        // Rx Enable でなければ受け取らない?
                    517:        if (chan.rx_enable == false) {
                    518:                return false;
                    519:        }
                    520: 
                    521:        // バッファが一杯なら Rx Overrun Error
                    522:        if (chan.rxlen == sizeof(chan.rxbuf)) {
                    523:                putlog(2, "Ch%c Rx Overrun", chan.name);
                    524:                chan.sr1 |= MPSCC::SR1_RXOVERRUN;
                    525:                // XXX special Rx condition interrupt occurs
                    526:                return false;
                    527:        }
                    528: 
                    529:        // 受信
                    530:        putlog(2, "Ch%c recv $%02x", chan.name, data);
                    531:        chan.rxbuf[chan.rxlen++] = data;
                    532: 
                    533:        // 割り込み状態を変更
                    534:        ChangeInterrupt();
                    535: 
                    536:        return true;
                    537: }
                    538: 
                    539: // ホストシリアルからの1バイト受信メッセージ
                    540: void
                    541: MPSCCDevice::RxMessageCallback(MessageID msgid, uint32 arg)
                    542: {
                    543:        Event& ev = rxevent[COM_CH];
                    544: 
                    545:        // 受信ループが止まっていれば開始
                    546:        if (ev.IsRunning() == false) {
                    547:                gScheduler->StartEvent(ev);
                    548:        }
                    549: }
                    550: 
                    551: // 受信イベントコールバック
                    552: void
                    553: MPSCCDevice::RxCallback(Event& ev)
                    554: {
                    555:        int ch = ev.code;
                    556:        int data;
                    557: 
                    558:        // ホストキューにあるだけ取り出す
                    559:        data = hostcom->Rx();
                    560:        if (data >= 0) {
                    561:                Rx(ch, data);
                    562: 
                    563:                gScheduler->StartEvent(ev);
                    564:        }
                    565: }
                    566: 
                    567: static double
                    568: to_freq(uint64 nsec)
                    569: {
                    570:        return (double)1.0 / nsec * 1e9;
                    571: }
                    572: 
                    573: // 送受信タイミングのうち CR4 から分かる共通部分を計算。
                    574: // enable は tx_enable or rx_enable、
                    575: // bitp は IN/OUT パラメータで、
                    576: // 呼び出し時は CR3 or CR5 から求まる送受信のデータビット数を渡し、
                    577: // パリティやストップビット等を付与したフレーム内のビット数を書き戻される。
                    578: // *evtime にはイベント間隔 [nsec] を書き戻す。
                    579: std::string
                    580: MPSCCDevice::SetPeriod(MPSCCChan& chan, bool enable, int *bitp, uint64 *evtime)
                    581: {
                    582:        uint64 bit12_ns;
                    583:        int bit;
                    584: 
                    585:        // 誤差を減らすため 12bit 単位が基準
                    586:        // 12bit 時間
                    587:        bit12_ns = xc12_ns * clkrates[(chan.cr4 & MPSCC::CR4_CLKRATE) >> 6];
                    588: 
                    589:        bit = *bitp;
                    590: 
                    591:        // start bit
                    592:        bit += 1;
                    593: 
                    594:        // parity bit
                    595:        bit += (chan.cr4 & MPSCC::CR4_PARITY_EN) ? 1 : 0;
                    596: 
                    597:        // stop bit
                    598:        switch (chan.cr4 & MPSCC::CR4_STOPBITS) {
                    599:         case MPSCC::STOPBITS_SYNC:
                    600:                // 非同期モードでの STOPBITS 0 は未定義 (Enable ならログ表示)
                    601:                if (enable) {
                    602:                        putlog(0, "%s 未定義ストップビット長 0", CRName(chan, 4));
                    603:                }
                    604:                break;
                    605:         case MPSCC::STOPBITS_1:
                    606:                bit += 1;
                    607:                break;
                    608:         case MPSCC::STOPBITS_1_5:
                    609:                // サポートしない (Enable ならログ表示)
                    610:                if (enable) {
                    611:                        putlog(0, "%s 未実装ストップビット長 1.5", CRName(chan, 4));
                    612:                }
                    613:                break;
                    614:         case MPSCC::STOPBITS_2:
                    615:                bit += 2;
                    616:                break;
                    617:        }
                    618:        *bitp = bit;
                    619: 
                    620:        *evtime = bit * bit12_ns / 12;
                    621: 
                    622:        // 表示用
                    623:        chan.baudrate = (double)to_freq(bit12_ns) * 12;
                    624: 
                    625:        // Enable なら表示用のログメッセージを作成して返す
                    626:        if (enable) {
                    627:                std::string nsstr;
                    628:                if (loglevel >= 2) {
                    629:                        nsstr = string_format(" (%" PRIu64 ")", bit12_ns);
                    630:                }
                    631:                return string_format("%.1f baud%s, %d bits/frame",
                    632:                        chan.baudrate,
                    633:                        nsstr.c_str(),
                    634:                        bit);
                    635:        }
                    636: 
                    637:        return "";
                    638: }
                    639: 
                    640: // 受信イベントの時間を設定する (CR3, CR4 の変更で呼ぶ)
                    641: void
                    642: MPSCCDevice::SetRxPeriod(MPSCCChan& chan)
                    643: {
                    644:        uint64 evtime;
                    645:        int bit;
                    646: 
                    647:        // 受信データビット数
                    648:        bit = bits_per_char[(chan.cr3 & MPSCC::CR3_RXBITS) >> 6];
                    649: 
                    650:        // CR4 などからイベント時間を求めてセット
                    651:        // XXX 速度などのパラメータは送受信端双方で一致していることにする。
                    652:        std::string msg = SetPeriod(chan, chan.rx_enable, &bit, &evtime);
                    653:        chan.rxbits = bit;
                    654:        rxevent[chan.id].time = evtime;
                    655: 
                    656:        // RX Enable (CR3) ならパラメータを含めて表示
                    657:        if (chan.rx_enable) {
                    658:                putlog(1, "Channel %c RX Enable, %s", chan.name, msg.c_str());
                    659:        }
                    660: 
                    661:        // パフォーマンス測定用のギミック。
                    662:        // シリアルポートを特定の設定 (本体 5bit、ストップビット 2bit) にすると、
                    663:        // VM 電源オン時から現在までの実時間をログに出力する。NetBSD/OpenBSD の
                    664:        // マルチユーザブートがあらかた終わってログインプロンプトが出る手前の
                    665:        // /etc/rc.local に
                    666:        //
                    667:        // stty -f /dev/ttya cs5 cstopb; stty -f /dev/ttya cs8 -cstopb
                    668:        //
                    669:        // と書いておくことで、ここまでにかかる時間を表示できる。
                    670:        // この値はホスト性能に依存するため、同一環境下での相対比較のみ可能。
                    671:        // シリアルポートの設定を (通常ではまず使わなそうなやつに) 変えてすぐ戻す
                    672:        // だけなので、実機でも副作用はないはず。
                    673:        if ((chan.cr3 & MPSCC::CR3_RXBITS) == MPSCC::RXBITS_5 &&
                    674:            (chan.cr4 & MPSCC::CR4_STOPBITS) == MPSCC::STOPBITS_2)
                    675:        {
                    676:                gSync->PutlogRealtime();
                    677:        }
                    678: }
                    679: 
                    680: // 送信イベントの時間を設定する (CR4, CR5 の変更で呼ぶ)
                    681: void
                    682: MPSCCDevice::SetTxPeriod(MPSCCChan& chan)
                    683: {
                    684:        uint64 evtime;
                    685:        int bit;
                    686: 
                    687:        // 送信データビット数
                    688:        bit = bits_per_char[(chan.cr5 & MPSCC::CR5_TXBITS) >> 5];
                    689: 
                    690:        // CR4 などからイベント時間を求めてセット
                    691:        std::string msg = SetPeriod(chan, chan.tx_enable, &bit, &evtime);
                    692:        chan.txbits = bit;
                    693:        txevent[chan.id].time = evtime;
                    694: 
                    695:        // TX Enable (CR5) ならパラメータを含めて表示
                    696:        if (chan.tx_enable) {
                    697:                putlog(1, "Channel %c TX Enable, %s", chan.name, msg.c_str());
                    698:        }
                    699: }
                    700: 
                    701: // 割り込み状態を変更
                    702: //
                    703: // uPD7201 の場合:
                    704: //
                    705: //     RxINT Disable (CR1 b4,3=%00) -->o--+ (注1)
                    706: //                                        |
                    707: //   o RxCharAvailable  ----|&            |
                    708: //                          |&---|OR      +--|&
                    709: //     All/First RxChar ----|&   |OR         |&--+
                    710: //                               |OR---------|&  |
                    711: //   o Special Rx Condition -----|OR             +---|OR
                    712: //                                                   |OR       ---
                    713: //   o External/Status ------------------------------|OR-->o-- INT
                    714: //                                                   |OR
                    715: //   o Tx Buffer Empty ----------|&              +---|OR
                    716: //                               |&-----|&       |
                    717: //     Tx INT/DMA Enable --------|&     |&       |
                    718: //     (CR1 b1)                         |&-------+
                    719: //                                      |&
                    720: //     INT/DMA Mode (CR2A b1,0) --------|& (注2)
                    721: //
                    722: // 注1: uPD7201/7201A Figure 3-2 では "Rx Disable (CR1:D1)" だがそこは
                    723: // Tx INT/DMA Enable ビットなので名称もビット位置も誤り。
                    724: // uPD7201 ユーザーズマニュアル p.23 の 図3-2 では "Rx Disable (CR1:D4-3=00)"
                    725: // とあるのでこれのビット位置が正しければ Rx Int Disable が正しいはず。
                    726: //
                    727: // 注2: uPD7201 のみ。Z8530 ではこの選択肢はないので常に true のはず。
                    728: //
                    729: void
                    730: MPSCCDevice::ChangeInterrupt()
                    731: {
                    732:        uint total_intpend = 0;
                    733:        bool int_enable[2];
                    734: 
                    735:        // uPD7201 の CR1 INT/DMA Mode による制御。
                    736:        // Z8530 では intdma_mode = INT_INT 固定。
                    737:        //
                    738:        // ついでにここで txint_enable も演算しておく。見た目は
                    739:        // 無駄っぽいけどコンパイラで最適化されるので気にしない。
                    740:        if (__predict_true(mpscc.intdma_mode == MPSCC::INTDMA_INT_INT)) {
                    741:                int_enable[0] = mpscc.chan[0].txint_enable && true;             // ChA INT
                    742:                int_enable[1] = mpscc.chan[1].txint_enable && true;             // ChB INT
                    743:        } else if (mpscc.intdma_mode == MPSCC::INTDMA_DMA_INT) {
                    744:                int_enable[0] = mpscc.chan[0].txint_enable && false;    // ChA DMA
                    745:                int_enable[1] = mpscc.chan[1].txint_enable && true;             // ChB INT
                    746:        } else {
                    747:                int_enable[0] = mpscc.chan[0].txint_enable && false;    // ChA DMA
                    748:                int_enable[1] = mpscc.chan[1].txint_enable && false;    // ChB DMA
                    749:        }
                    750: 
                    751:        for (auto& chan : mpscc.chan) {
                    752:                // Rx
                    753:                bool rx_avail = (chan.rxlen > 0) && chan.all_or_first;
                    754:                // Special Rx (XXX not yet)
                    755:                bool special = false;
                    756:                bool rx_int = (chan.rxint_mode != MPSCCChan::RXINT_DISABLE) &&
                    757:                              (rx_avail || special);
                    758: 
                    759:                // FirstChar モードなら1文字受信割り込み後にフラグを下ろす
                    760:                // (Rx 割り込み用の変数 rx_int が確定したここで行う)
                    761:                if (chan.rxint_mode == MPSCCChan::RXINT_1STCHAR) {
                    762:                        chan.all_or_first = false;
                    763:                }
                    764: 
                    765:                // XXX E/S not yet
                    766:                bool es_int = false;
                    767: 
                    768:                // Tx
                    769:                bool tx_int = int_enable[chan.id] &&
                    770:                              chan.txint_ready && (chan.txlen == 0);
                    771: 
                    772:                uint intpend = 0;
                    773:                if (rx_int)
                    774:                        intpend |= INTPEND_RX;
                    775:                if (tx_int)
                    776:                        intpend |= INTPEND_TX;
                    777:                if (es_int)
                    778:                        intpend |= INTPEND_ES;
                    779:                chan.intpend = intpend;
                    780: 
                    781:                total_intpend |= chan.intpend;
                    782:        }
                    783: 
                    784:        // いずれかでもあれば割り込みをアサート
                    785:        gInterrupt->ChangeINT(this, total_intpend);
                    786: }
                    787: 
                    788: // "9600,N,8,1" みたいなのを返す
                    789: std::string
                    790: MPSCCDevice::GetParamStr(const MPSCCChan& chan) const
                    791: {
                    792:        std::string rv;
                    793:        char parity;
                    794:        char bits;
                    795:        char stopbits;
                    796: 
                    797:        int parity_idx = 0;
                    798:        if ((chan.cr4 & MPSCC::CR4_PARITY_EN)) {
                    799:                parity_idx++;
                    800:                if ((chan.cr4 & MPSCC::CR4_PARITY_EVEN)) {
                    801:                        parity_idx++;
                    802:                }
                    803:        }
                    804:        parity = "NOE"[parity_idx];
                    805: 
                    806:        // bits は RX==TX のときのみ有効出力
                    807:        // 違うときは '?' にしておく。
                    808:        int rxbits = (chan.cr3 >> 6) & 3;
                    809:        int txbits = (chan.cr5 >> 5) & 3;
                    810:        if (rxbits == txbits) {
                    811:                bits = '0' + bits_per_char[rxbits];
                    812:        } else {
                    813:                bits = '?';
                    814:        }
                    815: 
                    816:        // stop bit は 1, 2 のみ有効出力。
                    817:        // SYNC とか 1.5 bit とかは '?' にしておく。
                    818:        switch ((chan.cr4 & MPSCC::CR4_STOPBITS)) {
                    819:         case MPSCC::STOPBITS_1:
                    820:                stopbits = '1';
                    821:                break;
                    822:         case MPSCC::STOPBITS_2:
                    823:                stopbits = '2';
                    824:                break;
                    825:         default:
                    826:                stopbits = '?';
                    827:                break;
                    828:        }
                    829: 
                    830:        return string_format("%d,%c,%c,%c",
                    831:                (int)chan.baudrate,
                    832:                parity,
                    833:                bits,
                    834:                stopbits);
                    835: }
                    836: 
                    837: // モニタに CR3/WR3、CR4/WR4、CR5/WR5 の内容を書き出す。
                    838: // 更新後の y を返す。
                    839: int
                    840: MPSCCDevice::MonitorUpdateCR345(TextScreen& screen, int x, int y,
                    841:        uint8 cr3, uint8 cr4, uint8 cr5)
                    842: {
                    843:        // CR3/WR3
                    844:        static const char * const cr3_str[] = {
                    845:                "", "", "Auto", "", "", "", "", "RxEn"
                    846:        };
                    847:        MonitorReg(screen, x + 9, y, cr3, cr3_str);
                    848:        screen.Print(x + 9, y, "RxBits=%d", bits_per_char[(cr3 >> 6) & 3]);
                    849:        // b1-4 は非同期モードでは使わないのでグレーにしておく
                    850:        screen.Puts(x + 24, y, TA::Disable, (cr3 & 0x10) ? "1" : "0");
                    851:        screen.Puts(x + 29, y, TA::Disable, (cr3 & 0x08) ? "1" : "0");
                    852:        screen.Puts(x + 34, y, TA::Disable, (cr3 & 0x04) ? "1" : "0");
                    853:        screen.Puts(x + 39, y, TA::Disable, (cr3 & 0x02) ? "1" : "0");
                    854:        y++;
                    855: 
                    856:        // CR4/WR4
                    857:        static const char * const cr4_str[] = {
                    858:                "", "", "", "", "", "", "PaEv", "PaEn"
                    859:        };
                    860:        MonitorReg(screen, x + 9, y, cr4, cr4_str);
                    861:        screen.Print(x + 9, y, "Rate=x%d", clkrates[(cr4 >> 6) & 3]);
                    862:        static const char * const syncmode_str[] = {
                    863:                "Mono", "Bi", "HDLC", "Ext"
                    864:        };
                    865:        uint stopbit = (cr4 >> 2) & 3;
                    866:        screen.Print(x + 19, y, (stopbit == 0 ? TA::Normal : TA::Disable),
                    867:                "Sync=%s", syncmode_str[(cr4 >> 4) & 3]);
                    868:        static const char * const stopbit_str[] = {
                    869:                "SyncMode",
                    870:                "StopBit=1",
                    871:                "Stop=1.5",
                    872:                "StopBit=2"
                    873:        };
                    874:        screen.Puts(x + 29, y, stopbit_str[stopbit]);
                    875:        y++;
                    876: 
                    877:        // CR5/WR5
                    878:        static const char * const cr5_str[] = {
                    879:                "!DTR", "", "", "SBrk", "TxEn", "CRC", "!RTS", "TCEn"
                    880:        };
                    881:        MonitorReg(screen, x + 9, y, cr5, cr5_str);
                    882:        screen.Print(x + 14, y, "TxBits=%d", bits_per_char[(cr5 >> 5) & 3]);
                    883:        y++;
                    884: 
                    885:        return y;
                    886: }
                    887: 
                    888: // モニタに SR1/RR1 の内容を書き出す。
                    889: void
                    890: MPSCCDevice::MonitorUpdateSR1(TextScreen& screen, int x, int y, uint8 sr1)
                    891: {
                    892:        static const char * const sr1_str[] = {
                    893:                "EOF", "FrEr", "RxOv", "PaEr", "", "", "", "Sent"
                    894:        };
                    895:        MonitorReg(screen, x + 9, y, sr1, sr1_str);
                    896:        screen.Print(x + 29, y, TA::Disable, "ResidueCode=%d", (sr1 >> 1) & 7);
                    897: }
                    898: 
                    899: // レジスタをビットごとに表示する。MonitorUpdate の下請け。
                    900: void
                    901: MPSCCDevice::MonitorReg(TextScreen& screen,
                    902:        int x, int y, uint32 reg, const char * const *names)
                    903: {
                    904:        for (int i = 0; i < 8; i++) {
                    905:                if (names[i][0] == '-') {
                    906:                        screen.Puts(x + i * 5, y, TA::Disable, "----");
                    907:                } else {
                    908:                        bool b = reg & (1 << (7 - i));
                    909:                        screen.Puts(x + i * 5, y, TA::OnOff(b), names[i]);
                    910:                }
                    911:        }
                    912: }

unix.superglobalmegacorp.com

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