--- nono/vm/mpscc.cpp 2026/04/29 17:05:21 1.1.1.4 +++ nono/vm/mpscc.cpp 2026/04/29 17:06:00 1.1.1.12 @@ -20,8 +20,10 @@ #define MPSCC_SIO_LOG_HACK #include "mpscc.h" #include "bitops.h" +#include "event.h" #include "hostcom.h" #include "interrupt.h" +#include "monitor.h" #include "scheduler.h" #include "syncer.h" #include @@ -65,18 +67,6 @@ MPSCCDevice::MPSCCDevice() 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(); } // モニタは継承先で設定する @@ -86,19 +76,23 @@ MPSCCDevice::MPSCCDevice() MPSCCDevice::~MPSCCDevice() { if ((bool)hostcom) { - hostcom->SetRxCallback(NULL); + hostcom->ResetRxCallback(); } } bool MPSCCDevice::Create() { - // 対応するホストドライバを作成 - hostcom.reset(new HostCOMDevice(this, "HostCOM")); + // 対応するホストドライバを作成。 + // ポート名は継承先ごとに Init() で設定している。 + try { + 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->SetRxCallback(ToDeviceCallback(&MPSCCDevice::HostRxCallback), 0); return true; } @@ -108,22 +102,30 @@ MPSCCDevice::SetLogLevel(int loglevel_) { inherited::SetLogLevel(loglevel_); - assert((bool)hostcom); - hostcom->SetLogLevel(loglevel_); + if ((bool)hostcom) { + hostcom->SetLogLevel(loglevel_); + } } // 初期化 bool MPSCCDevice::Init() { - if (inherited::Init() == false) { - return false; - } - interrupt = GetInterruptDevice(); - scheduler->ConnectMessage(MessageID::HOSTCOM_RX, this, - ToMessageCallback(&MPSCCDevice::RxMessageCallback)); + scheduler->ConnectMessage(MessageID::HOSTCOM0_RX, this, + ToMessageCallback(&MPSCCDevice::RxMessage)); + + auto evman = GetEventManager(); + for (int ch = 0; ch < mpscc.chan.size(); ch++) { + // 名前は継承先でセットする。機種ごとに名前を使い分けているため。 + txevent[ch] = evman->Regist(this, ch, + ToEventCallback(&MPSCCDevice::TxCallback)); + + rxevent[ch] = evman->Regist(this, ch, + ToEventCallback(&MPSCCDevice::RxCallback)); + } + return true; } @@ -149,6 +151,12 @@ MPSCCDevice::ResetChannelCommon(MPSCCCha ChangeInterrupt(); } +bool +MPSCCDevice::PokePort(uint32 offset, uint32 data) +{ + return false; +} + // CR0/WR0 レジスタの内容を取得 // (値の取り出しだけなら共通) /*static*/ uint8 @@ -206,7 +214,7 @@ MPSCCDevice::GetCR3(const MPSCCChan& cha data |= MPSCC::RXBITS_8; break; default: - VMPANIC("corrupted chan.rxbits=%d", chan.rxbits); + VMPANIC("corrupted chan.rxbits=%u", chan.rxbits); } if (chan.auto_enable) { data |= MPSCC::CR3_AUTO_EN; @@ -264,7 +272,7 @@ MPSCCDevice::GetCR4(const MPSCCChan& cha data |= MPSCC::CLKRATE_64; break; default: - VMPANIC("corrupted chan.clkrate=%d", chan.clkrate); + VMPANIC("corrupted chan.clkrate=%u", chan.clkrate); } data |= chan.syncmode; data |= chan.stopbits << 2; @@ -326,7 +334,7 @@ MPSCCDevice::GetCR5(const MPSCCChan& cha data |= MPSCC::TXBITS_8; break; default: - VMPANIC("corrupted chan.txbits=%d", chan.txbits); + VMPANIC("corrupted chan.txbits=%u", chan.txbits); } if (chan.nDTR) { data |= MPSCC::CR5_nDTR; @@ -395,10 +403,10 @@ MPSCCDevice::GetSR2B() const stat = 3; // VIS_321 とはビット順が逆 data &= ~0x70; - data |= bitrev(stat << 1); + data |= bitrev8(stat << 1); break; default: - VMPANIC("corrupted mpscc.vis=%d", (int)mpscc.vis); + VMPANIC("corrupted mpscc.vis=%u", (uint)mpscc.vis); } return data; @@ -517,7 +525,7 @@ MPSCCDevice::WriteCR4(MPSCCChan& chan, u 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, chan.clkrate, sm[chan.stopbits]); @@ -637,7 +645,7 @@ void MPSCCDevice::TrySend(MPSCCChan& chan) { // 送信可で送信バッファにデータがあって送信中でなければ - if (chan.tx_enable && chan.txlen && !txevent[chan.id].IsRunning()) { + if (chan.tx_enable && chan.txlen && !txevent[chan.id]->IsRunning()) { // 送信する chan.tx_shiftreg = chan.txbuf; chan.txlen = 0; @@ -653,9 +661,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]; // 接続デバイスの呼び出し @@ -668,7 +676,7 @@ MPSCCDevice::TxCallback(Event& event) // ホストスレッドからの受信通知 // (HostCOM スレッドで呼ばれる) void -MPSCCDevice::HostRxCallback() +MPSCCDevice::HostRxCallback(uint32 dummy) { // XXX 暫定 // HostCom からメッセージが大量に送られると、スケジューラの @@ -678,11 +686,39 @@ MPSCCDevice::HostRxCallback() // またはスケジューラにメッセージではないなにか別の方法で // 通知を出すようにする必要がある。 - Event& ev = rxevent[COM_CH]; + const Event *ev = rxevent[COM_CH]; - if (ev.IsRunning() == false) { + if (ev->IsRunning() == false) { // スレッドを超えるためにメッセージを投げる - scheduler->SendMessage(MessageID::HOSTCOM_RX); + scheduler->SendMessage(MessageID::HOSTCOM0_RX); + } +} + +// ホストシリアルからの1バイト受信メッセージ +void +MPSCCDevice::RxMessage(MessageID msgid, uint32 arg) +{ + Event *ev = rxevent[COM_CH]; + + // 受信ループが止まっていれば開始 + if (ev->IsRunning() == false) { + scheduler->StartEvent(ev); + } +} + +// 受信イベントコールバック +void +MPSCCDevice::RxCallback(Event *ev) +{ + int ch = ev->code; + int data; + + // ホストキューにあるだけ取り出す + data = hostcom->Rx(); + if (data >= 0) { + Rx(ch, data); + + scheduler->StartEvent(ev); } } @@ -716,50 +752,22 @@ MPSCCDevice::Rx(int ch, uint32 data) return true; } -// ホストシリアルからの1バイト受信メッセージ -void -MPSCCDevice::RxMessageCallback(MessageID msgid, uint32 arg) -{ - Event& ev = rxevent[COM_CH]; - - // 受信ループが止まっていれば開始 - if (ev.IsRunning() == false) { - scheduler->StartEvent(ev); - } -} - -// 受信イベントコールバック -void -MPSCCDevice::RxCallback(Event& ev) -{ - int ch = ev.code; - int data; - - // ホストキューにあるだけ取り出す - data = hostcom->Rx(); - if (data >= 0) { - Rx(ch, data); - - scheduler->StartEvent(ev); - } -} - static double -to_freq(uint64 nsec) +tsec_to_freq(uint64 tsec) { - return (double)1.0 / nsec * 1e9; + return (double)1_sec / tsec; } // ボーレートを再計算する。 -// CR4(ClkRate) と xc12_ns の変更で呼ぶこと。 +// CR4(ClkRate) と xc12_tsec の変更で呼ぶこと。 void MPSCCDevice::ChangeBaudrate(MPSCCChan& chan) { double old_baudrate = chan.baudrate; // 誤差を減らすため 12bit 分の時間を基準とする。 - chan.bit12_ns = chan.xc12_ns * chan.clkrate; - chan.baudrate = (double)to_freq(chan.bit12_ns) * 12; + chan.bit12_tsec = chan.xc12_tsec * chan.clkrate; + chan.baudrate = tsec_to_freq(chan.bit12_tsec) * 12; if (loglevel >= 3) { if (chan.baudrate != old_baudrate) { @@ -822,7 +830,7 @@ MPSCCDevice::GetStopBits(MPSCCChan& chan void MPSCCDevice::SetRxPeriod(MPSCCChan& chan) { - int bit; + uint bit; // 受信データビット数 bit = chan.rxbits; @@ -830,7 +838,7 @@ MPSCCDevice::SetRxPeriod(MPSCCChan& chan bit += AdditionalBits(chan, chan.rx_enable); chan.rxframebits = bit; - rxevent[chan.id].time = bit * chan.bit12_ns / 12; + rxevent[chan.id]->time = bit * chan.bit12_tsec / 12; if (loglevel >= 1) { bool old_enable = chan.old_cr3 & MPSCC::CR3_RX_EN; @@ -851,9 +859,9 @@ MPSCCDevice::SetRxPeriod(MPSCCChan& chan // そうでなければ(有効→有効なら)、パラメータが変化すれば表示。 std::string nsstr; if (loglevel >= 2) { - nsstr = string_format(" (%" PRIu64 ")", chan.bit12_ns); + nsstr = string_format(" (%" PRIu64 ")", chan.bit12_tsec); } - putlogn("Channel %c RX Enable, %.1f bps%s, %d bits/frame", + putlogn("Channel %c RX Enable, %.1f bps%s, %u bits/frame", chan.name, chan.baudrate, nsstr.c_str(), @@ -862,7 +870,7 @@ MPSCCDevice::SetRxPeriod(MPSCCChan& chan } } - // パフォーマンス測定用のギミック。 + // --perf によるパフォーマンス測定用のギミック。 // シリアルポートを特定の設定 (本体 5bit、ストップビット 2bit) にすると、 // VM 電源オン時から現在までの実時間をログに出力する。NetBSD/OpenBSD の // マルチユーザブートがあらかた終わってログインプロンプトが出る手前の @@ -887,7 +895,7 @@ MPSCCDevice::SetRxPeriod(MPSCCChan& chan void MPSCCDevice::SetTxPeriod(MPSCCChan& chan) { - int bit; + uint bit; // 送信データビット数 bit = chan.txbits; @@ -895,7 +903,7 @@ MPSCCDevice::SetTxPeriod(MPSCCChan& chan bit += AdditionalBits(chan, chan.tx_enable); chan.txframebits = bit; - txevent[chan.id].time = bit * chan.bit12_ns / 12; + txevent[chan.id]->time = bit * chan.bit12_tsec / 12; if (loglevel >= 1) { bool old_enable = chan.old_cr5 & MPSCC::CR5_TX_EN; @@ -916,9 +924,9 @@ MPSCCDevice::SetTxPeriod(MPSCCChan& chan // そうでなければ(有効→有効なら)、パラメータが変化すれば表示。 std::string nsstr; if (loglevel >= 2) { - nsstr = string_format(" (%" PRIu64 ")", chan.bit12_ns); + nsstr = string_format(" (%" PRIu64 ")", chan.bit12_tsec); } - putlogn("Channel %c TX Enable, %.1f bps%s, %d bits/frame", + putlogn("Channel %c TX Enable, %.1f bps%s, %u bits/frame", chan.name, chan.baudrate, nsstr.c_str(), @@ -939,15 +947,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 ビットなので名称もビット位置も誤り。 @@ -956,6 +969,8 @@ MPSCCDevice::SetTxPeriod(MPSCCChan& chan // // 注2: uPD7201 のみ。Z8530 ではこの選択肢はないので常に true のはず。 // +// 注3: Z8530 のみ。uPD7201 ではこの選択肢はないので常に true にしてある。 +// void MPSCCDevice::ChangeInterrupt() { @@ -1011,6 +1026,11 @@ MPSCCDevice::ChangeInterrupt() total_intpend |= chan.intpend; } + // Z8530 の MIE。uPD7201 では true 固定にしてある。 + if (mpscc.master_int_enable == false) { + total_intpend = 0; + } + // いずれかでもあれば割り込みをアサート interrupt->ChangeINT(this, total_intpend); } @@ -1064,7 +1084,7 @@ MPSCCDevice::GetParamStr(const MPSCCChan // 75 * 2 ^ (e-1) を求める double a = (chan.baudrate / 75) * 1.5; std::frexp(a, &e); - int b = 75 * (1 << (e - 1)); + int b = 75 * (1U << (e - 1)); if (b * 19 / 20 <= chan.baudrate && chan.baudrate <= b * 21 / 20) { baud = b; @@ -1083,7 +1103,7 @@ MPSCCDevice::GetParamStr(const MPSCCChan // モニタに CR3/WR3、CR4/WR4、CR5/WR5 の内容を書き出す。 // 更新後の y を返す。 int -MPSCCDevice::MonitorUpdateCR345(TextScreen& screen, int x, int y, +MPSCCDevice::MonitorScreenCR345(TextScreen& screen, int x, int y, uint8 cr3, uint8 cr4, uint8 cr5) { const std::array bits_per_char { '5', '7', '6', '8' }; @@ -1138,7 +1158,7 @@ MPSCCDevice::MonitorUpdateCR345(TextScre // モニタに SR1/RR1 の内容を書き出す。 void -MPSCCDevice::MonitorUpdateSR1(TextScreen& screen, int x, int y, uint8 sr1) +MPSCCDevice::MonitorScreenSR1(TextScreen& screen, int x, int y, uint8 sr1) { static const char * const sr1_str[] = { "EOF", "FrEr", "RxOv", "PaEr", "", "", "", "Sent" @@ -1147,7 +1167,7 @@ MPSCCDevice::MonitorUpdateSR1(TextScreen screen.Print(x + 29, y, TA::Disable, "ResidueCode=%d", (sr1 >> 1) & 7); } -// レジスタをビットごとに表示する。MonitorUpdate の下請け。 +// レジスタをビットごとに表示する。MonitorScreen の下請け。 void MPSCCDevice::MonitorReg(TextScreen& screen, int x, int y, uint32 reg, const char * const *names) @@ -1156,7 +1176,7 @@ MPSCCDevice::MonitorReg(TextScreen& scre if (names[i][0] == '-') { screen.Puts(x + i * 5, y, TA::Disable, "----"); } else { - bool b = reg & (1 << (7 - i)); + bool b = reg & (1U << (7 - i)); screen.Puts(x + i * 5, y, TA::OnOff(b), names[i]); } }