--- nono/vm/mpscc.cpp 2026/04/29 17:05:10 1.1 +++ nono/vm/mpscc.cpp 2026/04/29 17:05:46 1.1.1.10 @@ -22,8 +22,10 @@ #include "bitops.h" #include "hostcom.h" #include "interrupt.h" +#include "monitor.h" #include "scheduler.h" -#include "sync.h" +#include "syncer.h" +#include // // MPSCC チャンネル @@ -32,6 +34,11 @@ // コンストラクタ MPSCCChan::MPSCCChan() { + // これらのレジスタの値の初期値は実際は不定だがそれは実際難しいのと + // 有効範囲内の値にしておく必要があるので、適当に初期値を固定しておく。 + rxbits = 5; + txbits = 5; + clkrate = 1; } // デストラクタ @@ -53,24 +60,12 @@ MPSCCChan::Init(int id_) // // コンストラクタ -MPSCCDevice::MPSCCDevice(const std::string& objname_) - : inherited(objname_) +MPSCCDevice::MPSCCDevice() + : inherited(OBJ_MPSCC) { for (int ch = 0; ch < mpscc.chan.size(); ch++) { auto& chan = mpscc.chan[ch]; chan.Init(ch); - - // time は SetTxPeriod() でセット。 - // 名前は継承先でセットする。機種ごとに名前を使い分けているため。 - txevent[ch].dev = this; - txevent[ch].func = ToEventCallback(&MPSCCDevice::TxCallback); - txevent[ch].code = ch; - txevent[ch].Regist(); - - rxevent[ch].dev = this; - rxevent[ch].func = ToEventCallback(&MPSCCDevice::RxCallback); - rxevent[ch].code = ch; - rxevent[ch].Regist(); } // モニタは継承先で設定する @@ -80,22 +75,23 @@ MPSCCDevice::MPSCCDevice(const std::stri MPSCCDevice::~MPSCCDevice() { if ((bool)hostcom) { - hostcom->SetCallbackDevice(NULL); + hostcom->ResetRxCallback(); } } bool MPSCCDevice::Create() { - // 対応するホストドライバを作成 + // 対応するホストドライバを作成。 + // ポート名は継承先ごとに Init() で設定している。 try { - hostcom.reset(new HostCOMDevice("HostCOM")); - } catch (...) { + hostcom.reset(new HostCOMDevice(this, 0, "")); + } catch (...) { } + if ((bool)hostcom == false) { + warnx("Failed to initialize HostCOMDevice at %s", __method__); return false; } - - hostcom->SetRxCallback(ToDeviceCallback(&MPSCCDevice::HostRxCallback)); - hostcom->SetCallbackDevice(this); + hostcom->SetRxCallback(ToDeviceCallback(&MPSCCDevice::HostRxCallback), 0); return true; } @@ -105,16 +101,34 @@ MPSCCDevice::SetLogLevel(int loglevel_) { inherited::SetLogLevel(loglevel_); - assert((bool)hostcom); - hostcom->SetLogLevel(loglevel_); + if ((bool)hostcom) { + hostcom->SetLogLevel(loglevel_); + } } // 初期化 bool MPSCCDevice::Init() { - gScheduler->ConnectMessage(MessageID::HOSTCOM_RX, this, - ToMessageCallback(&MPSCCDevice::RxMessageCallback)); + interrupt = GetInterruptDevice(); + + scheduler->ConnectMessage(MessageID::HOSTCOM0_RX, this, + ToMessageCallback(&MPSCCDevice::RxMessage)); + + for (int ch = 0; ch < mpscc.chan.size(); ch++) { + // time は SetTxPeriod() でセット。 + // 名前は継承先でセットする。機種ごとに名前を使い分けているため。 + txevent[ch].dev = this; + txevent[ch].func = ToEventCallback(&MPSCCDevice::TxCallback); + txevent[ch].code = ch; + scheduler->RegistEvent(txevent[ch]); + + rxevent[ch].dev = this; + rxevent[ch].func = ToEventCallback(&MPSCCDevice::RxCallback); + rxevent[ch].code = ch; + scheduler->RegistEvent(rxevent[ch]); + } + return true; } @@ -123,9 +137,15 @@ void MPSCCDevice::ResetChannelCommon(MPSCCChan& chan) { // 通信速度を反映する + ChangeBaudrate(chan); SetTxPeriod(chan); SetRxPeriod(chan); + // Set(Tx|Rx)Period() 呼び出し後は old_cr[345] を更新。 + chan.old_cr3 = GetCR3(chan); + chan.old_cr4 = GetCR4(chan); + chan.old_cr5 = GetCR5(chan); + // リセットで送信バッファは空になるが、リセット直後に // Tx 割り込みを許可しても Tx 割り込みは起こさない。 chan.txint_ready = false; @@ -134,38 +154,209 @@ MPSCCDevice::ResetChannelCommon(MPSCCCha ChangeInterrupt(); } +bool +MPSCCDevice::PokePort(uint32 offset, uint32 data) +{ + return false; +} + // CR0/WR0 レジスタの内容を取得 // (値の取り出しだけなら共通) -uint8 -MPSCCDevice::GetCR0(const MPSCCChan& chan) const +/*static*/ uint8 +MPSCCDevice::GetCR0(const MPSCCChan& chan) { uint8 data = (chan.crc_cmd << 6) | (chan.cmd << 3) | chan.ptr; return data; } +// CR3/WR3 レジスタを設定 +/*static*/ void +MPSCCDevice::SetCR3(MPSCCChan& chan, uint32 data) +{ + uint cr3_rxbits = data & MPSCC::CR3_RXBITS; + switch (cr3_rxbits) { + case MPSCC::RXBITS_5: + chan.rxbits = 5; + break; + case MPSCC::RXBITS_6: + chan.rxbits = 6; + break; + case MPSCC::RXBITS_7: + chan.rxbits = 7; + break; + case MPSCC::RXBITS_8: + chan.rxbits = 8; + break; + default: + VMPANIC("corrupted cr3_rxbits=$%x", cr3_rxbits); + } + + chan.auto_enable = (data & MPSCC::CR3_AUTO_EN); + chan.cr3_sync = (data & MPSCC::CR3_SYNC_MASK); + chan.rx_enable = (data & MPSCC::CR3_RX_EN); +} + // CR3/WR3 レジスタの内容を取得 -uint8 -MPSCCDevice::GetCR3(const MPSCCChan& chan) const +/*static*/ uint8 +MPSCCDevice::GetCR3(const MPSCCChan& chan) { uint8 data; - data = chan.cr3 & ~MPSCC::CR3_RX_EN; - if (chan.rx_enable) + data = chan.cr3_sync; + switch (chan.rxbits) { + case 5: + data |= MPSCC::RXBITS_5; + break; + case 6: + data |= MPSCC::RXBITS_6; + break; + case 7: + data |= MPSCC::RXBITS_7; + break; + case 8: + data |= MPSCC::RXBITS_8; + break; + default: + VMPANIC("corrupted chan.rxbits=%u", chan.rxbits); + } + if (chan.auto_enable) { + data |= MPSCC::CR3_AUTO_EN; + } + if (chan.rx_enable) { data |= MPSCC::CR3_RX_EN; + } + return data; +} + +// CR4/WR4 レジスタを設定 +/*static*/void +MPSCCDevice::SetCR4(MPSCCChan& chan, uint32 data) +{ + uint cr4_clkrate = data & MPSCC::CR4_CLKRATE; + switch (cr4_clkrate) { + case MPSCC::CLKRATE_1: + chan.clkrate = 1; + break; + case MPSCC::CLKRATE_16: + chan.clkrate = 16; + break; + case MPSCC::CLKRATE_32: + chan.clkrate = 32; + break; + case MPSCC::CLKRATE_64: + chan.clkrate = 64; + break; + default: + VMPANIC("corrupted cr4_clkrate=$%x", cr4_clkrate); + } + chan.syncmode = (data & MPSCC::CR4_SYNCMODE); + chan.stopbits = (data & MPSCC::CR4_STOPBITS) >> 2; + chan.parity_even = (data & MPSCC::CR4_PARITY_EVEN); + chan.parity_enable = (data & MPSCC::CR4_PARITY_EN) ? 1 : 0; +} +// CR4/WR4 レジスタの内容を取得 +/*static*/ uint8 +MPSCCDevice::GetCR4(const MPSCCChan& chan) +{ + uint8 data = 0; + + switch (chan.clkrate) { + case 1: + data |= MPSCC::CLKRATE_1; + break; + case 16: + data |= MPSCC::CLKRATE_16; + break; + case 32: + data |= MPSCC::CLKRATE_32; + break; + case 64: + data |= MPSCC::CLKRATE_64; + break; + default: + VMPANIC("corrupted chan.clkrate=%u", chan.clkrate); + } + data |= chan.syncmode; + data |= chan.stopbits << 2; + if (chan.parity_even) { + data |= MPSCC::CR4_PARITY_EVEN; + } + if (chan.parity_enable) { + data |= MPSCC::CR4_PARITY_EN; + } return data; } +// CR5/WR5 レジスタを設定 +/*static*/ void +MPSCCDevice::SetCR5(MPSCCChan& chan, uint32 data) +{ + uint cr5_txbits = data & MPSCC::CR5_TXBITS; + switch (cr5_txbits) { + case MPSCC::TXBITS_5: + chan.txbits = 5; + break; + case MPSCC::TXBITS_6: + chan.txbits = 6; + break; + case MPSCC::TXBITS_7: + chan.txbits = 7; + break; + case MPSCC::TXBITS_8: + chan.txbits = 8; + break; + default: + VMPANIC("corrupted cr5_txbits=$%x", cr5_txbits); + } + chan.nDTR = (data & MPSCC::CR5_nDTR); + chan.sendbreak = (data & MPSCC::CR5_SENDBREAK); + chan.tx_enable = (data & MPSCC::CR5_TX_EN); + chan.crc16 = (data & MPSCC::CR5_CRC16); + chan.nRTS = (data & MPSCC::CR5_nRTS); + chan.txcrc_en = (data & MPSCC::CR5_TXCRC_EN); +} + // CR5/WR5 レジスタの内容を取得 -uint8 -MPSCCDevice::GetCR5(const MPSCCChan& chan) const +/*static*/ uint8 +MPSCCDevice::GetCR5(const MPSCCChan& chan) { - uint8 data; + uint8 data = 0; - data = chan.cr5 & ~MPSCC::CR5_TX_EN; - if (chan.tx_enable) + switch (chan.txbits) { + case 5: + data |= MPSCC::TXBITS_5; + break; + case 6: + data |= MPSCC::TXBITS_6; + break; + case 7: + data |= MPSCC::TXBITS_7; + break; + case 8: + data |= MPSCC::TXBITS_8; + break; + default: + VMPANIC("corrupted chan.txbits=%u", chan.txbits); + } + if (chan.nDTR) { + data |= MPSCC::CR5_nDTR; + } + if (chan.sendbreak) { + data |= MPSCC::CR5_SENDBREAK; + } + if (chan.tx_enable) { data |= MPSCC::CR5_TX_EN; - + } + if (chan.crc16) { + data |= MPSCC::CR5_CRC16; + } + if (chan.nRTS) { + data |= MPSCC::CR5_nRTS; + } + if (chan.txcrc_en) { + data |= MPSCC::CR5_TXCRC_EN; + } return data; } @@ -215,10 +406,10 @@ MPSCCDevice::GetSR2B() const stat = 3; // VIS_321 とはビット順が逆 data &= ~0x70; - data |= bitrev(stat << 1); + data |= bitrev8(stat << 1); break; default: - __unreachable(); + VMPANIC("corrupted mpscc.vis=%u", (uint)mpscc.vis); } return data; @@ -228,7 +419,7 @@ MPSCCDevice::GetSR2B() const void MPSCCDevice::SendAbort(MPSCCChan& chan) { - putlog(0, "%s: Command 'Send Abort' 未実装", CRName(chan, 0)); + putlog(0, "%s: Command 'Send Abort' (NOT IMPLEMENTED)", CRName(chan, 0)); } // CR0/WR0 Reset External/Status Interrupt コマンド @@ -316,65 +507,53 @@ MPSCCDevice::ResetHighestIUS(MPSCCChan& void MPSCCDevice::WriteCR3(MPSCCChan& chan, uint32 data) { - bool old_enable; - - old_enable = chan.rx_enable; - chan.cr3 = data; - chan.rx_enable = (data & MPSCC::CR3_RX_EN); - + SetCR3(chan, data); putlog(2, "%s <- $%02x", CRName(chan), data); - // Disable は、Enable から変化した時だけ表示したい - // (Enable は SetRxPeriod() 内で表示) - if (old_enable == true && chan.rx_enable == false) { - putlog(1, "Channel %c RX Disable", chan.name); - } - SetRxPeriod(chan); + + // Set(Tx|Rx)Period() 呼び出し後は old_cr[345] を更新。 + chan.old_cr3 = GetCR3(chan); } // CR4/WR4 への書き込み void MPSCCDevice::WriteCR4(MPSCCChan& chan, uint32 data) { - chan.cr4 = data; + SetCR4(chan, data); putlog(2, "%s <- $%02x", CRName(chan), data); - uint8 stopbits = (chan.cr4 & MPSCC::CR4_STOPBITS); - if (stopbits == MPSCC::STOPBITS_SYNC) { + if (chan.stopbits == MPSCC::STOPBITS_SYNC) { // 同期モードはサポートしない putlog(2, "Channel %c Synchronous mode not supported", chan.name); } else { const char *sm[] = { "?", "1", "1.5", "2" }; - putlog(2, "Channel %c Async Mode (x%d clock, stopbit %s)", + putlog(2, "Channel %c Async Mode (x%u clock, stopbit %s)", chan.name, - clkrates[(chan.cr4 & MPSCC::CR4_CLKRATE) >> 6], - sm[stopbits >> 2]); + chan.clkrate, + sm[chan.stopbits]); + // なんとなく順序的に何倍と表示した後に速度を表示 + ChangeBaudrate(chan); } SetTxPeriod(chan); SetRxPeriod(chan); + + // Set(Tx|Rx)Period() 呼び出し後は old_cr[345] を更新。 + chan.old_cr4 = GetCR4(chan); } // CR5/WR5 への書き込み void MPSCCDevice::WriteCR5(MPSCCChan& chan, uint32 data) { - bool old_enable; - - old_enable = chan.tx_enable; - chan.cr5 = data; - chan.tx_enable = (data & MPSCC::CR5_TX_EN); - + SetCR5(chan, data); putlog(2, "%s <- $%02x", CRName(chan), data); - // Disable は、Enable から変化した時だけ表示したい - // (Enable は SetTxPeriod() 内で表示) - if (old_enable == true && chan.tx_enable == false) { - putlog(1, "Channel %c TX Disable", chan.name); - } - SetTxPeriod(chan); + // Set(Tx|Rx)Period() 呼び出し後は old_cr[345] を更新。 + chan.old_cr5 = GetCR5(chan); + TrySend(chan); } @@ -384,7 +563,11 @@ MPSCCDevice::WriteCR6(MPSCCChan& chan, u { chan.cr6 = data; - putlog(0, "%s <- $%02x 書き込み(未実装)", CRName(chan), data); + // どのみちサポートしてないけど初期化のために 0x00 を書き込むようなので + // それ以外ならログを出す。 + if (data != 0) { + putlog(0, "Write %s <- $%02x (NOT IMPLEMENTED)", CRName(chan), data); + } } // CR7/WR7 への書き込み @@ -393,7 +576,11 @@ MPSCCDevice::WriteCR7(MPSCCChan& chan, u { chan.cr7 = data; - putlog(0, "%s <- $%02x 書き込み(未実装)", CRName(chan), data); + // どのみちサポートしてないけど初期化のために 0x00 を書き込むようなので + // それ以外ならログを出す。 + if (data != 0) { + putlog(0, "Write %s <- $%02x (NOT IMPLEMENTED)", CRName(chan), data); + } } // データポートの読み込み @@ -465,7 +652,7 @@ MPSCCDevice::TrySend(MPSCCChan& chan) // 送信する chan.tx_shiftreg = chan.txbuf; chan.txlen = 0; - gScheduler->StartEvent(txevent[chan.id]); + scheduler->StartEvent(txevent[chan.id]); putlog(2, "Ch%c send $%02x", chan.name, chan.tx_shiftreg); // XXX たぶん送信してから割り込みを上げるまでにいくらか遅れがあるはず @@ -477,9 +664,9 @@ MPSCCDevice::TrySend(MPSCCChan& chan) // 送信のイベントコールバック void -MPSCCDevice::TxCallback(Event& event) +MPSCCDevice::TxCallback(Event& ev) { - int ch = event.code; + int ch = ev.code; MPSCCChan& chan = mpscc.chan[ch]; // 接続デバイスの呼び出し @@ -489,21 +676,25 @@ MPSCCDevice::TxCallback(Event& event) TrySend(chan); } -// TX/RX の bits/char のレジスタ値を実際の bits/char の値に変換する。 -/*static*/ const int -MPSCCDevice::bits_per_char[] = { 5, 7, 6, 8 }; - -// CR4 CLKRATE のレジスタ値を実際の倍率に変換する。 -/*static*/ const int -MPSCCDevice::clkrates[] = { 1, 16, 32, 64 }; - // ホストスレッドからの受信通知 // (HostCOM スレッドで呼ばれる) void -MPSCCDevice::HostRxCallback() +MPSCCDevice::HostRxCallback(uint32 dummy) { - // スレッドを超えるためにメッセージを投げる - gScheduler->SendMessage(MessageID::HOSTCOM_RX); + // XXX 暫定 + // HostCom からメッセージが大量に送られると、スケジューラの + // メッセージキューが溢れてしまうことがあったので、とりあえず + // ここでメッセージを送るかどうかを見ているが、 + // 本当はたぶんこうではなくて、送る側が速度調節するか、 + // またはスケジューラにメッセージではないなにか別の方法で + // 通知を出すようにする必要がある。 + + Event& ev = rxevent[COM_CH]; + + if (ev.IsRunning() == false) { + // スレッドを超えるためにメッセージを投げる + scheduler->SendMessage(MessageID::HOSTCOM0_RX); + } } // 1バイト受信する。 @@ -538,13 +729,13 @@ MPSCCDevice::Rx(int ch, uint32 data) // ホストシリアルからの1バイト受信メッセージ void -MPSCCDevice::RxMessageCallback(MessageID msgid, uint32 arg) +MPSCCDevice::RxMessage(MessageID msgid, uint32 arg) { Event& ev = rxevent[COM_CH]; // 受信ループが止まっていれば開始 if (ev.IsRunning() == false) { - gScheduler->StartEvent(ev); + scheduler->StartEvent(ev); } } @@ -560,7 +751,7 @@ MPSCCDevice::RxCallback(Event& ev) if (data >= 0) { Rx(ch, data); - gScheduler->StartEvent(ev); + scheduler->StartEvent(ev); } } @@ -570,92 +761,116 @@ to_freq(uint64 nsec) return (double)1.0 / nsec * 1e9; } -// 送受信タイミングのうち CR4 から分かる共通部分を計算。 -// enable は tx_enable or rx_enable、 -// bitp は IN/OUT パラメータで、 -// 呼び出し時は CR3 or CR5 から求まる送受信のデータビット数を渡し、 -// パリティやストップビット等を付与したフレーム内のビット数を書き戻される。 -// *evtime にはイベント間隔 [nsec] を書き戻す。 -std::string -MPSCCDevice::SetPeriod(MPSCCChan& chan, bool enable, int *bitp, uint64 *evtime) +// ボーレートを再計算する。 +// CR4(ClkRate) と xc12_ns の変更で呼ぶこと。 +void +MPSCCDevice::ChangeBaudrate(MPSCCChan& chan) { - uint64 bit12_ns; - int bit; + double old_baudrate = chan.baudrate; - // 誤差を減らすため 12bit 単位が基準 - // 12bit 時間 - bit12_ns = xc12_ns * clkrates[(chan.cr4 & MPSCC::CR4_CLKRATE) >> 6]; + // 誤差を減らすため 12bit 分の時間を基準とする。 + chan.bit12_ns = chan.xc12_ns * chan.clkrate; + chan.baudrate = (double)to_freq(chan.bit12_ns) * 12; - bit = *bitp; + if (loglevel >= 3) { + if (chan.baudrate != old_baudrate) { + putlogn("Channel %c Baudrate %.1f", chan.name, chan.baudrate); + } + } +} + +// スタートビット、ストップビット、パリティビットの総数を返す。 +// enable はサポートしていないモードの時にログを出すため。 +int +MPSCCDevice::AdditionalBits(MPSCCChan& chan, bool enable) +{ + int bit; // start bit - bit += 1; + bit = 1; // parity bit - bit += (chan.cr4 & MPSCC::CR4_PARITY_EN) ? 1 : 0; + bit += chan.parity_enable; // stop bit - switch (chan.cr4 & MPSCC::CR4_STOPBITS) { + bit += GetStopBits(chan, enable); + + return bit; +} + +// ストップビット数を返す。 +// サポートしていないモードなどの場合は enable ならログを出力する。 +// enable は tx_enable か rx_enable。 +int +MPSCCDevice::GetStopBits(MPSCCChan& chan, bool enable) +{ + switch (chan.stopbits) { case MPSCC::STOPBITS_SYNC: // 非同期モードでの STOPBITS 0 は未定義 (Enable ならログ表示) if (enable) { - putlog(0, "%s 未定義ストップビット長 0", CRName(chan, 4)); + putlog(0, "%s Undefined stopbit 0", CRName(chan, 4)); } - break; + return 0; case MPSCC::STOPBITS_1: - bit += 1; - break; + return 1; case MPSCC::STOPBITS_1_5: // サポートしない (Enable ならログ表示) if (enable) { - putlog(0, "%s 未実装ストップビット長 1.5", CRName(chan, 4)); + putlog(0, "%s Stopbit 1.5 not supported", CRName(chan, 4)); } - break; + return 0; case MPSCC::STOPBITS_2: - bit += 2; - break; - } - *bitp = bit; + return 2; - *evtime = bit * bit12_ns / 12; - - // 表示用 - chan.baudrate = (double)to_freq(bit12_ns) * 12; - - // Enable なら表示用のログメッセージを作成して返す - if (enable) { - std::string nsstr; - if (loglevel >= 2) { - nsstr = string_format(" (%" PRIu64 ")", bit12_ns); - } - return string_format("%.1f baud%s, %d bits/frame", - chan.baudrate, - nsstr.c_str(), - bit); + default: + VMPANIC("corrupted chan.stopbits=$%x", chan.stopbits); } - - return ""; } -// 受信イベントの時間を設定する (CR3, CR4 の変更で呼ぶ) +// 受信イベントの時間を設定する。 +// CR3 または CR4 の変更で呼ぶこと。また呼び出し後は old_cr3、old_cr4 を +// 更新すること。 void MPSCCDevice::SetRxPeriod(MPSCCChan& chan) { - uint64 evtime; - int bit; + uint bit; // 受信データビット数 - bit = bits_per_char[(chan.cr3 & MPSCC::CR3_RXBITS) >> 6]; - - // CR4 などからイベント時間を求めてセット - // XXX 速度などのパラメータは送受信端双方で一致していることにする。 - std::string msg = SetPeriod(chan, chan.rx_enable, &bit, &evtime); - chan.rxbits = bit; - rxevent[chan.id].time = evtime; - - // RX Enable (CR3) ならパラメータを含めて表示 - if (chan.rx_enable) { - putlog(1, "Channel %c RX Enable, %s", chan.name, msg.c_str()); + bit = chan.rxbits; + // スタート/ストップ等を足す + bit += AdditionalBits(chan, chan.rx_enable); + chan.rxframebits = bit; + + rxevent[chan.id].time = bit * chan.bit12_ns / 12; + + if (loglevel >= 1) { + bool old_enable = chan.old_cr3 & MPSCC::CR3_RX_EN; + bool new_enable = chan.rx_enable; + if (old_enable == false && new_enable == false) { + // 無効→無効ならパラメータが変化しても表示しない + + } else if (old_enable == true && new_enable == false) { + // 有効→無効なら無効にしたことだけ表示 + putlogn("Channel %c RX Disable", chan.name); + sr0_logphase = 0; + + } else if ((old_enable == false && new_enable == true) || + (chan.old_cr4 ^ GetCR4(chan)) != 0 || + ((chan.old_cr3 ^ GetCR3(chan)) & MPSCC::CR3_RXBITS) != 0) + { + // 無効→有効なら表示。 + // そうでなければ(有効→有効なら)、パラメータが変化すれば表示。 + std::string nsstr; + if (loglevel >= 2) { + nsstr = string_format(" (%" PRIu64 ")", chan.bit12_ns); + } + putlogn("Channel %c RX Enable, %.1f bps%s, %u bits/frame", + chan.name, + chan.baudrate, + nsstr.c_str(), + bit); + sr0_logphase = 0; + } } // パフォーマンス測定用のギミック。 @@ -670,31 +885,57 @@ MPSCCDevice::SetRxPeriod(MPSCCChan& chan // この値はホスト性能に依存するため、同一環境下での相対比較のみ可能。 // シリアルポートの設定を (通常ではまず使わなそうなやつに) 変えてすぐ戻す // だけなので、実機でも副作用はないはず。 - if ((chan.cr3 & MPSCC::CR3_RXBITS) == MPSCC::RXBITS_5 && - (chan.cr4 & MPSCC::CR4_STOPBITS) == MPSCC::STOPBITS_2) + if (chan.id == 0 && + chan.rxbits == 5 && chan.stopbits == MPSCC::STOPBITS_2) { - gSync->PutlogRealtime(); + GetSyncer()->PutlogRealtime(); } } -// 送信イベントの時間を設定する (CR4, CR5 の変更で呼ぶ) +// 送信イベントの時間を設定する。 +// CR4 または CR5 の変更で呼ぶこと。また呼び出し後は old_cr4、old_cr5 を +// 更新すること。 void MPSCCDevice::SetTxPeriod(MPSCCChan& chan) { - uint64 evtime; - int bit; + uint bit; // 送信データビット数 - bit = bits_per_char[(chan.cr5 & MPSCC::CR5_TXBITS) >> 5]; - - // CR4 などからイベント時間を求めてセット - std::string msg = SetPeriod(chan, chan.tx_enable, &bit, &evtime); - chan.txbits = bit; - txevent[chan.id].time = evtime; - - // TX Enable (CR5) ならパラメータを含めて表示 - if (chan.tx_enable) { - putlog(1, "Channel %c TX Enable, %s", chan.name, msg.c_str()); + bit = chan.txbits; + // スタート/ストップ等を足す + bit += AdditionalBits(chan, chan.tx_enable); + chan.txframebits = bit; + + txevent[chan.id].time = bit * chan.bit12_ns / 12; + + if (loglevel >= 1) { + bool old_enable = chan.old_cr5 & MPSCC::CR5_TX_EN; + bool new_enable = chan.tx_enable; + if (old_enable == false && new_enable == false) { + // 無効→無効ならパラメータが変化しても表示しない + + } else if (old_enable == true && new_enable == false) { + // 有効→無効なら無効にしたことだけ表示 + putlogn("Channel %c TX Disable", chan.name); + sr0_logphase = 0; + + } else if ((old_enable == false && new_enable == true) || + (chan.old_cr4 ^ GetCR4(chan)) != 0 || + ((chan.old_cr5 ^ GetCR5(chan)) & MPSCC::CR5_TXBITS) != 0) + { + // 無効→有効なら表示。 + // そうでなければ(有効→有効なら)、パラメータが変化すれば表示。 + std::string nsstr; + if (loglevel >= 2) { + nsstr = string_format(" (%" PRIu64 ")", chan.bit12_ns); + } + putlogn("Channel %c TX Enable, %.1f bps%s, %u bits/frame", + chan.name, + chan.baudrate, + nsstr.c_str(), + bit); + sr0_logphase = 0; + } } } @@ -709,15 +950,20 @@ MPSCCDevice::SetTxPeriod(MPSCCChan& chan // All/First RxChar ----|& |OR |&--+ // |OR---------|& | // o Special Rx Condition -----|OR +---|OR -// |OR --- -// o External/Status ------------------------------|OR-->o-- INT // |OR -// o Tx Buffer Empty ----------|& +---|OR -// |&-----|& | -// Tx INT/DMA Enable --------|& |& | -// (CR1 b1) |&-------+ -// |& -// INT/DMA Mode (CR2A b1,0) --------|& (注2) +// o External/Status ------------------------------|OR----|& ___ +// |OR |&-->o-- INT +// o Tx Buffer Empty ----------|& +---|OR +-|& +// |&-----|& | | +// Tx INT/DMA Enable --------|& |& | | +// (CR1 b1) |&-------+ | +// |& | +// INT/DMA Mode (CR2A b1,0) --------|& (注2) | +// | +// Master Interrupt Enable ---------------------------+ (注3) +// (Z8530 Only, WR9) +// +// 左端の o 印は割り込み要因(チャンネルあたり4種類)。他は制御フラグ。 // // 注1: uPD7201/7201A Figure 3-2 では "Rx Disable (CR1:D1)" だがそこは // Tx INT/DMA Enable ビットなので名称もビット位置も誤り。 @@ -726,6 +972,8 @@ MPSCCDevice::SetTxPeriod(MPSCCChan& chan // // 注2: uPD7201 のみ。Z8530 ではこの選択肢はないので常に true のはず。 // +// 注3: Z8530 のみ。uPD7201 ではこの選択肢はないので常に true にしてある。 +// void MPSCCDevice::ChangeInterrupt() { @@ -781,11 +1029,16 @@ MPSCCDevice::ChangeInterrupt() total_intpend |= chan.intpend; } + // Z8530 の MIE。uPD7201 では true 固定にしてある。 + if (mpscc.master_int_enable == false) { + total_intpend = 0; + } + // いずれかでもあれば割り込みをアサート - gInterrupt->ChangeINT(this, total_intpend); + interrupt->ChangeINT(this, total_intpend); } -// "9600,N,8,1" みたいなのを返す +// "9600,N,8,1" みたいなのを返す。最大13桁? std::string MPSCCDevice::GetParamStr(const MPSCCChan& chan) const { @@ -795,9 +1048,9 @@ MPSCCDevice::GetParamStr(const MPSCCChan char stopbits; int parity_idx = 0; - if ((chan.cr4 & MPSCC::CR4_PARITY_EN)) { + if (chan.parity_enable) { parity_idx++; - if ((chan.cr4 & MPSCC::CR4_PARITY_EVEN)) { + if (chan.parity_even) { parity_idx++; } } @@ -805,17 +1058,15 @@ MPSCCDevice::GetParamStr(const MPSCCChan // bits は RX==TX のときのみ有効出力 // 違うときは '?' にしておく。 - int rxbits = (chan.cr3 >> 6) & 3; - int txbits = (chan.cr5 >> 5) & 3; - if (rxbits == txbits) { - bits = '0' + bits_per_char[rxbits]; + if (chan.rxbits == chan.txbits) { + bits = '0' + chan.rxbits; } else { bits = '?'; } // stop bit は 1, 2 のみ有効出力。 // SYNC とか 1.5 bit とかは '?' にしておく。 - switch ((chan.cr4 & MPSCC::CR4_STOPBITS)) { + switch (chan.stopbits) { case MPSCC::STOPBITS_1: stopbits = '1'; break; @@ -827,8 +1078,26 @@ MPSCCDevice::GetParamStr(const MPSCCChan break; } + // ここは、このポートに接続する時に相手方で指定する設定値を + // 表示したいので、ボーレートをよく知られている値に丸めたい。 + // 75 * 2^n の +-5% に入っていたら採用。 + int baud; + int e; + + // 75 * 2 ^ (e-1) を求める + double a = (chan.baudrate / 75) * 1.5; + std::frexp(a, &e); + int b = 75 * (1 << (e - 1)); + + if (b * 19 / 20 <= chan.baudrate && chan.baudrate <= b * 21 / 20) { + baud = b; + } else { + // +-5% 範囲外なら仕方ないのでそのままの値 + baud = (int)chan.baudrate; + } + return string_format("%d,%c,%c,%c", - (int)chan.baudrate, + baud, parity, bits, stopbits); @@ -840,12 +1109,14 @@ int MPSCCDevice::MonitorUpdateCR345(TextScreen& screen, int x, int y, uint8 cr3, uint8 cr4, uint8 cr5) { + const std::array bits_per_char { '5', '7', '6', '8' }; + // CR3/WR3 static const char * const cr3_str[] = { "", "", "Auto", "", "", "", "", "RxEn" }; MonitorReg(screen, x + 9, y, cr3, cr3_str); - screen.Print(x + 9, y, "RxBits=%d", bits_per_char[(cr3 >> 6) & 3]); + screen.Print(x + 9, y, "RxBits=%c", bits_per_char[(cr3 >> 6) & 3]); // b1-4 は非同期モードでは使わないのでグレーにしておく screen.Puts(x + 24, y, TA::Disable, (cr3 & 0x10) ? "1" : "0"); screen.Puts(x + 29, y, TA::Disable, (cr3 & 0x08) ? "1" : "0"); @@ -858,7 +1129,10 @@ MPSCCDevice::MonitorUpdateCR345(TextScre "", "", "", "", "", "", "PaEv", "PaEn" }; MonitorReg(screen, x + 9, y, cr4, cr4_str); - screen.Print(x + 9, y, "Rate=x%d", clkrates[(cr4 >> 6) & 3]); + static const char * const clkrate_str[] = { + "1", "16", "32", "64" + }; + screen.Print(x + 9, y, "Rate=x%s", clkrate_str[cr4 >> 6]); static const char * const syncmode_str[] = { "Mono", "Bi", "HDLC", "Ext" }; @@ -879,7 +1153,7 @@ MPSCCDevice::MonitorUpdateCR345(TextScre "!DTR", "", "", "SBrk", "TxEn", "CRC", "!RTS", "TCEn" }; MonitorReg(screen, x + 9, y, cr5, cr5_str); - screen.Print(x + 14, y, "TxBits=%d", bits_per_char[(cr5 >> 5) & 3]); + screen.Print(x + 14, y, "TxBits=%c", bits_per_char[(cr5 >> 5) & 3]); y++; return y;