Annotation of nono/vm/mpscc.cpp, revision 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.