--- nono/vm/upd7201.cpp 2026/04/29 17:04:28 1.1 +++ nono/vm/upd7201.cpp 2026/04/29 17:05:07 1.1.1.9 @@ -1,14 +1,16 @@ // // nono -// Copyright (C) 2018 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // #include "upd7201.h" +#include "interrupt.h" // コンストラクタ -uPD7201Device::uPD7201Device() +uPD7201Device::uPD7201Device(const std::string& objname_) + : inherited(objname_) { - monitor.Init(48, 19); cr.chan[0].id = 0; cr.chan[0].name = "A"; cr.chan[1].id = 1; @@ -16,6 +18,18 @@ uPD7201Device::uPD7201Device() cr.chan[0].desc = NULL; cr.chan[1].desc = NULL; + + for (int ch = 0; ch < txevent.size(); ch++) { + // time は SetTXPeriod() でセット + txevent[ch].dev = this; + txevent[ch].func = (DeviceCallback_t)&uPD7201Device::TXCallback; + txevent[ch].code = ch; + // 名前は継承先でセットする。機種ごとに名前を使い分けているため。 + } + + monitor.func = (MonitorCallback_t)&uPD7201Device::MonitorUpdate; + monitor.SetSize(48, 22); + // 登録は継承先で行う。機種ごとに通りのいい名前を使い分けているため。 } // デストラクタ @@ -24,9 +38,8 @@ uPD7201Device::~uPD7201Device() } // リセット -// どのタイミングで呼ばれるかは上位による void -uPD7201Device::Reset() +uPD7201Device::ResetHard() { putlog(2, "リセット"); @@ -40,17 +53,17 @@ uPD7201Device::Reset() // チャンネルリセット void -uPD7201Device::ResetChannel(struct SIO::channel *chan) +uPD7201Device::ResetChannel(SIO::channel *chan) { - putlog(2, "チャンネル %s リセット", chan->name); + putlog(2, "Channel %s Reset", chan->name); - // CR0 b2-0 - chan->cr0 &= ~SIO::CR0_PTR; + // CR0 b2-0 (PTR) chan->ptr = 0; // CR1 b0,1,3,4,7 chan->cr1 &= ~(SIO::CR1_WAIT_EN | SIO::CR1_RXINT | SIO::CR1_TXINT_EN | SIO::CR1_ESINT_EN); + chan->all_or_first = false; // CR3 b0-7 chan->cr3 = 0; @@ -58,6 +71,7 @@ uPD7201Device::ResetChannel(struct SIO:: // CR5 b1,2,3,4,7 chan->cr5 &= ~(SIO::CR5_DTR | SIO::CR5_SENDBREAK | SIO::CR5_TX_EN | SIO::CR5_CRC16 | SIO::CR5_RTS); + SetTXPeriod(chan); // SR0 は b0,1 が '0'、b2,6 が '1'、残りは不定 chan->sr0 &= ~(SIO::SR0_INTPEND | SIO::SR0_RXAVAIL); @@ -67,37 +81,61 @@ uPD7201Device::ResetChannel(struct SIO:: chan->sr1 &= ~(SIO::SR1_ENDOFFRAME | SIO::SR1_CRCERROR | SIO::SR1_RXOVERRUN | SIO::SR1_PARITYERROR); - // E/S ラッチ解除 + // 内部状態をリセット chan->es_ratched = false; + chan->txint = false; // 受信バッファをクリア memset(chan->rxbuf, 0, sizeof(chan->rxbuf)); chan->rxlen = 0; + + chan->txbuf = 0; + chan->txlen = 0; + chan->tx_shiftreg = 0; + // 送信の停止 + // XXX 送信途中のデータをぶちきることはサポートしない + txevent[chan->id].Stop(); + + ChangeInterrupt(); +} + +// CR(WR) レジスタ名を返す。uPD7201 では CRn のほうが通りがいい。 +const char * +uPD7201Device::CRName(const SIO::channel *chan, int ptr_) const +{ + assert(ptr_ < 8); + int idx = ptr_ * 2 + chan->id; + assert(idx < countof(crnames)); + return crnames[idx]; +} + +// SR(RR) レジスタ名を返す。uPD7201 では SRn のほうが通りがいい。 +const char * +uPD7201Device::SRName(const SIO::channel *chan, int ptr_) const +{ + assert(ptr_ < 8); + int idx = ptr_ * 2 + chan->id; + assert(idx < countof(srnames)); + return srnames[idx]; } // 制御ポートの読み込み uint8 -uPD7201Device::ReadCtrl(struct SIO::channel *chan) +uPD7201Device::ReadCtrl(SIO::channel *chan) { uint8 data; switch (chan->ptr) { case 0: // SR0 - // 送信バッファは常に空 - data = SIO::SR0_TXEMPTY; - // 受信データがあるか - if (chan->rxlen > 0) { - data |= SIO::SR0_RXAVAIL; - } - putlog(2, "SR%d%s -> $%02x", chan->ptr, chan->name, data); + data = chan->sr0; + putlog(2, "%s -> $%02x", SRName(chan), data); // アクセス後は SR0 へ戻す chan->ptr = 0; break; case 1: // SR1 - // エラーとか起きてないという状態だけ再現。 - data = SIO::SR1_ALL_SENT; - putlog(2, "SR%d%s -> $%02x", chan->ptr, chan->name, data); + data = chan->sr1; + putlog(2, "%s -> $%02x", SRName(chan), data); // アクセス後は SR0 へ戻す chan->ptr = 0; break; @@ -106,7 +144,7 @@ uPD7201Device::ReadCtrl(struct SIO::chan // SR2B はベクタの読み出し if (chan->id == 1) { data = cr.vector; - putlog(2, "SR%d%s -> $%02x", chan->ptr, chan->name, data); + putlog(2, "%s -> $%02x", SRName(chan), data); // アクセス後は SR0 へ戻す chan->ptr = 0; break; @@ -116,8 +154,7 @@ uPD7201Device::ReadCtrl(struct SIO::chan FALLTHROUGH; default: - putlog(0, "未実装レジスタ読み込み SR%d%s", - chan->ptr, chan->name); + putlog(0, "未実装レジスタ読み込み %s", SRName(chan)); // SR0 へ戻る? chan->ptr = 0; return 0xff; @@ -127,177 +164,326 @@ uPD7201Device::ReadCtrl(struct SIO::chan // 制御ポートの書き込み void -uPD7201Device::WriteCtrl(struct SIO::channel *chan, uint32 data) +uPD7201Device::WriteCtrl(SIO::channel *chan, uint32 data) { - uint cmd; + // CR0 はコマンドかまたは次にアクセスするレジスタを指定する。 + // CR0 以外のレジスタをアクセスすると、次のアクセスは CR0 に戻る。 - switch (chan->ptr) { - case 0: // CR0 - // XXX b7,b6 は未対応。 - // b5,b4,b3 がコマンド。 - // b2,b1,b0 が次にアクセスするレジスタ(chan->ptr)の切り替え。 - chan->cr0 = data; - chan->ptr = (chan->cr0 & SIO::CR0_PTR); - - cmd = (chan->cr0 & SIO::CR0_CMD) >> 3; - if (cmd == 0) { - // コマンド0 ならレジスタ切り替え - putlog(3, "CR0%s <- $%02x (ptr=%d)", chan->name, data, chan->ptr); - } else { - // 0 以外ならコマンド発行 - putlog(2, "CR0%s <- $%02x", chan->name, data); - } - switch (cmd) { - case 0: // No operation + if (chan->ptr == 0) { + WriteCR0(chan, data); + } else { + switch (chan->ptr) { + case 1: + WriteCR1(chan, data); break; - case 1: // Send Abort - if (chan->mode == SIO::Mode::HDLC) { - putlog(0, "CR0: Send Abort 未実装"); - } else { - // XXX Send Abort コマンドは「HDLC モードのみ有効」と書いて - // あるがそれ以外の時にどうなるか不明。 - putlog(0, "CR0: 未定義コマンド %d", cmd); - } + case 2: + WriteCR2(chan, data); break; - case 2: // Reset External/Status Interrupt - putlog(3, "E/S ラッチ解除"); - chan->es_ratched = false; + case 3: + WriteCR3(chan, data); break; - case 3: // Channel Reset - putlog(1, "チャンネル%sリセット", chan->name); - ResetChannel(chan); + case 4: + WriteCR4(chan, data); break; - case 4: // Enable Interrupt On Next Receive Character - putlog(0, "CR0: 未実装コマンド %d", cmd); + case 5: + WriteCR5(chan, data); break; - case 5: // Reset Transmitter Interrupt/DMA Pending - // 本来これは上がっている Tx 割り込みを落とすためのようだが、 - // SIO から MPU への割り込み通知はもう終わっていて下げる必要は - // ないので、何もすることはない。 + case 6: + WriteCR6(chan, data); break; - case 6: // Error Reset - case 7: // End of Interrupt - putlog(0, "CR0: 未実装コマンド %d", cmd); + case 7: + WriteCR7(chan, data); break; default: __unreachable(); } - break; - - case 1: // CR1 - chan->cr1 = data; - putlog(2, "CR%d%s <- $%02x", chan->ptr, chan->name, data); // アクセス後は CR0 へ戻す chan->ptr = 0; - break; + } +} - case 2: - if (chan->id == 0) { // CR2A - putlog(2, "CR2A <- $%02x 書き込み(b7-2未実装)", data); - cr.cr2a = data; - } else { // CR2B - putlog(2, "CR2B <- $%02x ベクタ設定", data); - cr.vector = data; +// 制御ポートの Peek +uint8 +uPD7201Device::PeekCtrl(const SIO::channel *chan) const +{ + switch (chan->ptr) { + case 0: // SR0 + return chan->sr0; + + case 1: // SR1 + return chan->sr1; + + case 2: // SR2 + // SR2A はない。SR2B はベクタの読み出し + if (chan->id == 1) { + return cr.vector; } - // アクセス後は CR0 へ戻す - chan->ptr = 0; + FALLTHROUGH; + + default: + // 未調査 + return 0xff; + } +} + +// CR0 への書き込み +// Z8530 の WR0 とは微妙に互換性がなく、scc.cpp に似た別物があるので注意。 +void +uPD7201Device::WriteCR0(SIO::channel *chan, uint32 data) +{ + uint cmd; + + // XXX b7,b6 は未対応。 + // b5,b4,b3 がコマンド。 + // b2,b1,b0 が次にアクセスするレジスタ(chan->ptr)の切り替え。 + chan->cr0 = data; + chan->ptr = (chan->cr0 & SIO::CR0_PTR); + + cmd = (chan->cr0 & SIO::CR0_CMD) >> 3; + if (cmd == 0) { + // コマンド0 ならレジスタ切り替え + putlog(3, "%s <- $%02x (ptr=%d)", CRName(chan, 0), data, chan->ptr); + } else { + // 0 以外ならコマンド発行 + putlog(2, "%s <- $%02x", CRName(chan, 0), data); + } + switch (cmd) { + case 0: // No operation + break; + case 1: // Send Abort + SendAbort(chan); + break; + case 2: // Reset External/Status Interrupt + ResetESIntr(chan); + break; + case 3: // Channel Reset + ResetChannel(chan); break; + case 4: // Enable Interrupt On Next Receive Character + EnableIntrOnNextRx(chan); + break; + case 5: // Reset Transmitter Interrupt/DMA Pending + ResetTxIntrPending(chan); + break; + case 6: // Error Reset + ErrorReset(chan); + break; + case 7: // End of Interrupt + // uPD7201 ではチャンネル A のみ有効。 + // (チャンネル B でどうなるかは書いてない) + if (chan->id == 0) { + ResetHighestIUS(chan); + } else { + putlog(0, "%s: End of Intterupt 未定義", CRName(chan, 0)); + } + break; + default: + __unreachable(); + } +} + +// CR0(WR0) Send Abort コマンド +void +uPD7201Device::SendAbort(SIO::channel *chan) +{ + if (chan->mode == SIO::Mode::HDLC) { + putlog(0, "%s: Send Abort 未実装", CRName(chan, 0)); + } else { + // XXX Send Abort コマンドは「HDLC モードのみ有効」と書いて + // あるがそれ以外の時にどうなるか不明。 + uint cmd = (chan->cr0 & SIO::CR0_CMD) >> 3; + putlog(0, "%s: 未定義コマンド %d", CRName(chan, 0), cmd); + } +} + +// CR0(WR0) Reset External/Status Interrupt コマンド +void +uPD7201Device::ResetESIntr(SIO::channel *chan) +{ + putlog(3, "%s: Reset E/S Interrupt", CRName(chan, 0)); + chan->es_ratched = false; +} + +// CR0(WR0) Enable Interrupt on Next Rx Character コマンド +void +uPD7201Device::EnableIntrOnNextRx(SIO::channel *chan) +{ + putlog(0, "%s: Enable Intr on Next Rx Character 未実装", CRName(chan, 0)); +} + +// CR0(WR0) Reset Tx Interrupt/DMA Pending コマンド +void +uPD7201Device::ResetTxIntrPending(SIO::channel *chan) +{ + putlog(1, "%s: Reset Tx Intr/DMA Pending", CRName(chan, 0)); + chan->txint = false; + ChangeInterrupt(); +} + +// CR0(WR0) Error Reset コマンド +void +uPD7201Device::ErrorReset(SIO::channel *chan) +{ + putlog(1, "%s: Error Reset", CRName(chan, 0)); + chan->sr1 &= ~(SIO::SR1_ENDOFFRAME | SIO::SR1_CRCERROR | + SIO::SR1_RXOVERRUN | SIO::SR1_PARITYERROR); + // XXX Special Rx Condition は未実装 +} + +// CR0 End of Interrupt コマンド +// WR0 Reset Highest IUS コマンド +// たぶん名前が違うだけで同じ。後者のほうが分かりやすいのでこっちを使う +void +uPD7201Device::ResetHighestIUS(SIO::channel *chan) +{ + putlog(0, "%s: Reset Highest IUS 未実装", CRName(chan, 0)); +} + +// CR1 への書き込み +void +uPD7201Device::WriteCR1(SIO::channel *chan, uint32 data) +{ + putlog(2, "%s <- $%02x", CRName(chan), data); + + chan->cr1 = data; - case 3: // CR3 - chan->cr3 = data; - putlog(2, "CR%d%s <- $%02x", chan->ptr, chan->name, data); - putlog(1, "チャンネル%s RX %s, %dbits", + // RXINT モードで all_or_first の値(初期値)を用意。 + // DISABLE なら(使わないのでどっちでもいいはずだけど一応) false にしとく。 + // FIRSTCHAR なら初期状態が true。ALL_* なら常時 true。 + chan->all_or_first = ((chan->cr1 & SIO::CR1_RXINT) != SIO::RXINT_DISABLE); +} + +// CR2 への書き込み +void +uPD7201Device::WriteCR2(SIO::channel *chan, uint32 data) +{ + if (chan->id == 0) { // CR2A + putlog(2, "%s <- $%02x 書き込み(b7,b2-0未実装)", CRName(chan), data); + cr.cr2a = data; + } else { // CR2B + putlog(2, "%s <- $%02x (Set vector)", CRName(chan), data); + cr.vector = data; + } +} + +// CR3 への書き込み +void +uPD7201Device::WriteCR3(SIO::channel *chan, uint32 data) +{ + uint8 oldcr3; + + oldcr3 = chan->cr3; + chan->cr3 = data; + + putlog(2, "%s <- $%02x", CRName(chan), data); + + // Disable は Disable に変化した時だけ表示 + if ((oldcr3 & SIO::CR3_RX_EN) != 0 && (chan->cr3 & SIO::CR3_RX_EN) == 0) { + putlog(1, "Channel %s RX Disable", chan->name); + } + // Enable はパラメータ変更があるかもなので常に表示 + if ((chan->cr3 & SIO::CR3_RX_EN)) { + putlog(1, "Channel %s RX Enable, %d bits/frame", chan->name, - (chan->cr3 & SIO::CR3_RX_EN) ? "Enable" : "Disable", bits_per_char[(chan->cr3 & SIO::CR3_RXBITS) >> 6]); - // アクセス後は CR0 へ戻す - chan->ptr = 0; - break; + } +} - case 4: // CR4 - { - chan->cr4 = data; - putlog(2, "CR%d%s <- $%02x", chan->ptr, chan->name, data); - uint8 stopbits = (chan->cr4 & SIO::CR4_STOPBITS); - uint8 syncmode = (chan->cr4 & SIO::CR4_SYNCMODE); - - // モード分け、優先順位は分からない - if (stopbits == SIO::SYNC_MODE) { - if (syncmode == SIO::SYNC_HDLC) { - chan->mode = SIO::Mode::HDLC; - putlog(2, "チャンネル%s HDLC モード", chan->name); - } else { - chan->mode = SIO::Mode::SYNC; - const char *sm[] = { "8bit", "16bit", "HDLC?", "Ext" }; - putlog(2, "チャンネル%s Sync (%s) モード", - chan->name, sm[syncmode >> 4]); - } +// CR4 への書き込み +void +uPD7201Device::WriteCR4(SIO::channel *chan, uint32 data) +{ + chan->cr4 = data; + putlog(2, "%s <- $%02x", CRName(chan), data); + uint8 stopbits = (chan->cr4 & SIO::CR4_STOPBITS); + uint8 syncmode = (chan->cr4 & SIO::CR4_SYNCMODE); + + // TODO: 優先順位逆じゃない? + + // モード分け、優先順位は分からない + if (stopbits == SIO::SYNC_MODE) { + if (syncmode == SIO::SYNC_HDLC) { + chan->mode = SIO::Mode::HDLC; + putlog(2, "Channel %s HDLC Mode", chan->name); } else { - chan->mode = SIO::Mode::ASYNC; - const char *cm[] = { "x1", "x16", "x32", "x64" }; - const char *sm[] = { "?", "1", "1.5", "2" }; - putlog(2, "チャンネル%s Async (%s clock, stopbit %s) モード", - chan->name, - cm[(chan->cr4 & SIO::CR4_CLKRATE) >> 6], - sm[stopbits >> 2]); + chan->mode = SIO::Mode::SYNC; + const char *sm[] = { "8bit", "16bit", "HDLC?", "Ext" }; + putlog(2, "Channel %s Sync (%s) Mode", + chan->name, sm[syncmode >> 4]); } + } else { + chan->mode = SIO::Mode::ASYNC; + const char *sm[] = { "?", "1", "1.5", "2" }; + putlog(2, "Channel %s Async (x%d clock, stopbit %s) Mode", + chan->name, + clkrate[(chan->cr4 & SIO::CR4_CLKRATE) >> 6], + sm[stopbits >> 2]); + } - // アクセス後は CR0 へ戻す - chan->ptr = 0; - break; - } + SetTXPeriod(chan); +} - case 5: // CR5 - { - chan->cr5 = data; - putlog(2, "CR%d%s <- $%02x", chan->ptr, chan->name, data); - putlog(1, "チャンネル%s TX %s, %dbits", - chan->name, - (chan->cr5 & SIO::CR5_TX_EN) ? "Enable" : "Disable", - bits_per_char[(chan->cr5 & SIO::CR5_TXBITS) >> 5]); - // アクセス後は CR0 へ戻す - chan->ptr = 0; - break; - } +// CR5 への書き込み +void +uPD7201Device::WriteCR5(SIO::channel *chan, uint32 data) +{ + uint8 oldcr5; - case 6: // CR6 - putlog(0, "CR6%s <- $%02x 書き込み(未実装)", chan->name, data); - chan->cr6 = data; - // アクセス後は CR0 へ戻す - chan->ptr = 0; - break; + oldcr5 = chan->cr5; + chan->cr5 = data; - case 7: // CR7 - putlog(0, "CR7%s <- $%02x 書き込み(未実装)", chan->name, data); - chan->cr7 = data; - // アクセス後は CR0 へ戻す - chan->ptr = 0; - break; + putlog(2, "%s <- $%02x", CRName(chan), data); - default: - putlog(0, "未実装レジスタ書き込み CR%d%s <- $%02x", - chan->ptr, chan->name, data); - break; + // Disable は Disable に変化した時だけ表示 + // (Enable は SetTXPeriod() 内で表示) + if ((oldcr5 & SIO::CR5_TX_EN) != 0 && (chan->cr5 & SIO::CR5_TX_EN) == 0) { + putlog(1, "Channel %s TX Disable", chan->name); } + + SetTXPeriod(chan); + TrySend(chan); +} + +// CR6 への書き込み +void +uPD7201Device::WriteCR6(SIO::channel *chan, uint32 data) +{ + chan->cr6 = data; + + putlog(0, "%s <- $%02x 書き込み(未実装)", CRName(chan), data); +} + +// CR7 への書き込み +void +uPD7201Device::WriteCR7(SIO::channel *chan, uint32 data) +{ + chan->cr7 = data; + + putlog(0, "%s <- $%02x 書き込み(未実装)", CRName(chan), data); } // データポートの読み込み uint8 -uPD7201Device::ReadData(int ch) +uPD7201Device::ReadData(SIO::channel *chan) { - struct SIO::channel *chan = &cr.chan[ch]; uint8 data; if (chan->rxlen > 0) { data = chan->rxbuf[0]; + putlog(3, "Ch%s Data -> $%02x", chan->name, data); chan->rxlen--; - // 少ないので毎回前詰めしちゃえ - if (chan->rxlen > 0) { - memmove(chan->rxbuf + 1, chan->rxbuf, chan->rxlen); + if (chan->rxlen == 0) { + // 取り出して空になった + chan->sr0 &= ~SIO::SR0_RXAVAIL; + ChangeInterrupt(); + } else { + chan->rxbuf[0] = chan->rxbuf[1]; + chan->rxbuf[1] = chan->rxbuf[2]; } } else { // XXX 何が読めるか data = 0xff; + putlog(3, "Ch%s Data -> $%02x (Empty)", chan->name, data); } return data; @@ -305,7 +491,7 @@ uPD7201Device::ReadData(int ch) // データポートの書き込み void -uPD7201Device::WriteData(struct SIO::channel *chan, uint32 data) +uPD7201Device::WriteData(SIO::channel *chan, uint32 data) { char charbuf[4]; @@ -314,83 +500,275 @@ uPD7201Device::WriteData(struct SIO::cha } else { charbuf[0] = '\0'; } - putlog(1, "Ch%s データレジスタ書き込み $%02x %s", - chan->name, data, charbuf); + putlog(3, "Ch%s Data <- $%02x %s", chan->name, data, charbuf); + + // XXX: EMPTY でないときにデータを書いたらどうなるかは未定義 + + // 1byte の TX Buffer + chan->txlen = 1; + chan->txbuf = data; + chan->sr0 &= ~SIO::SR0_TXEMPTY; + + // TX 割り込みのクリア + chan->txint = false; + ChangeInterrupt(); + + TrySend(chan); +} + +// 送信できるか調べて送信する +void +uPD7201Device::TrySend(SIO::channel *chan) +{ + if ((chan->cr5 & SIO::CR5_TX_EN) == false) { + // 送信可でなければなにもしない + return; + } + if (chan->txlen == 0) { + // 送信バッファが空なら何もしない + return; + } + if (txevent[chan->id].IsRunning()) { + // 送信中なので何もしない + return; + } + + // 送信する + chan->tx_shiftreg = chan->txbuf; + txevent[chan->id].Start(); + putlog(2, "Ch%s sending $%02x", chan->name, chan->tx_shiftreg); + chan->txlen = 0; - // 1文字送信 + // XXX たぶん送信してから割り込みを上げるまでにいくらか遅れがあるはず - // 今のところ内部バッファがなく常に送信できるので - // 必ず Tx Buffer Empty が発生する。 + // 送信割り込み chan->sr0 |= SIO::SR0_TXEMPTY; + chan->txint = true; + ChangeInterrupt(); +} + +// 送信のイベントコールバック +void +uPD7201Device::TXCallback(Event& event) +{ + int ch = event.code; + SIO::channel *chan = &cr.chan[ch]; + + // 接続デバイスの呼び出し + Tx(ch, chan->tx_shiftreg); - // 割り込みをあげる - if ((chan->cr1 & SIO::CR1_TXINT_EN)) { - putlog(2, "割り込み (TX Empty)"); - Interrupt(); + // 次の送信データが来てたら送信 + TrySend(chan); +} + +// データポートの Peek +uint8 +uPD7201Device::PeekData(const SIO::channel *chan) const +{ + if (chan->rxlen > 0) { + // 受信バッファにデータがあれば先頭バイトが見える + return chan->rxbuf[0]; + } else { + // 空の時は? + return 0xff; } } -// TX/RX の bits/char のレジスタ値を実際の bits/char の値に変換します。 -/* static */ -const int +// TX/RX の bits/char のレジスタ値を実際の bits/char の値に変換する。 +/*static*/ const int uPD7201Device::bits_per_char[] = { 5, 7, 6, 8 }; +// CR4 CLKRATE のレジスタ値を実際の倍率に変換する。 +/*static*/ const int +uPD7201Device::clkrate[] = { 1, 16, 32, 64 }; + // 1バイト受信する。 // 受理されれば true を返す。 bool uPD7201Device::Rx(int ch, uint32 data) { - struct SIO::channel *chan = &cr.chan[ch]; + SIO::channel *chan = &cr.chan[ch]; // Rx Enable でなければ受け取らない? if ((chan->cr3 & SIO::CR3_RX_EN) == 0) { return false; } + // 1バイトでもあれば有効 + chan->sr0 |= SIO::SR0_RXAVAIL; + // バッファが一杯なら Rx Overrun Error if (chan->rxlen == sizeof(chan->rxbuf)) { putlog(2, "Rx Overrun"); chan->sr1 |= SIO::SR1_RXOVERRUN; + // XXX special Rx condition interrupt occurs return false; } // 受信 chan->rxbuf[chan->rxlen++] = data; - // 必要なら割り込みを上げる - if ((chan->cr1 & SIO::CR1_RXINT) == SIO::RXINT_FIRSTCHAR) { - // First character interrupt モードなら最初の1バイトでだけ割り込み - if (chan->rxrecved == false) { - Interrupt(); - } - chan->rxrecved = true; - } else if ((chan->cr1 & SIO::CR1_RXINT) >= SIO::RXINT_ALL_INC_SPECIAL) { - // All character interrupt モード (Special のことはちょっと置いとく) - chan->rxrecved = true; - Interrupt(); - } + // 割り込み状態を変更 + ChangeInterrupt(); return true; } +static double +to_freq(uint64 nsec) +{ + return (double)1.0 / nsec * 1e9; +} + +// 送信イベントの時間を設定する (CR4, CR5 の変更で呼ぶ) +void +uPD7201Device::SetTXPeriod(SIO::channel *chan) +{ + uint64 bit12_ns; + int bit; + + // 誤差を減らすため 12bit 単位が基準 + // 12bit 時間 + bit12_ns = txc12_ns * clkrate[(chan->cr4 & SIO::CR4_CLKRATE) >> 6]; + + // start bit + bit = 1; + + // data bit + bit += bits_per_char[(chan->cr5 & SIO::CR5_TXBITS) >> 5]; + + // parity bit + bit += (chan->cr4 & SIO::CR4_PARITY_EN) ? 1 : 0; + + switch (chan->cr4 & SIO::CR4_STOPBITS) { + case 0: + // 非同期モードでの STOPBITS 0 は未定義 (Enable ならログ表示) + if ((chan->cr5 & SIO::CR5_TX_EN)) { + putlog(0, "%s 未定義ストップビット長 0", CRName(chan, 4)); + } + break; + case SIO::STOP_BITS_1: + bit += 1; + break; + case SIO::STOP_BITS_1_5: + // サポートしない (Enable ならログ表示) + if ((chan->cr5 & SIO::CR5_TX_EN)) { + putlog(0, "%s 未実装ストップビット長 1.5", CRName(chan, 4)); + } + break; + case SIO::STOP_BITS_2: + bit += 2; + break; + } + + txevent[chan->id].time = bit * bit12_ns / 12; + + // TX Enable (CR5) ならパラメータを含めて表示 + if ((chan->cr5 & SIO::CR5_TX_EN)) { + std::string nsstr; + if (loglevel >= 2) { + nsstr = string_format(" (%" PRIu64 ")", bit12_ns); + } + putlog(1, "Channel %s TX Enable, %.1f baud%s, %d bits/frame", + chan->name, + (double)to_freq(bit12_ns) * 12, + nsstr.c_str(), + bit); + } +} + +// 割り込み状態を変更 +void +uPD7201Device::ChangeInterrupt() +{ + uint int_asserted = 0; + + for (int ch = 0; ch < 2; ch++) { + SIO::channel *chan = &cr.chan[ch]; + + // Rx + bool rx_avail = (chan->rxlen > 0) && chan->all_or_first; + // Special Rx + bool special = false; // XXX not yet + + bool rx_enable = (chan->cr3 & SIO::CR3_RX_EN); + bool rx_int = (rx_enable && (rx_avail || special)); + + // FirstChar モードなら1文字受信割り込み後にフラグを下ろす + // (Rx 割り込み用の変数 rx_int が確定したここで行う) + if ((chan->cr1 & SIO::CR1_RXINT) == SIO::RXINT_FIRSTCHAR) { + chan->all_or_first = false; + } + + // XXX E/S not yet + + // Tx + bool tx_enable = (chan->cr1 & SIO::CR1_TXINT_EN); + bool tx_int = tx_enable && chan->txint; + + // 4つの要因の合成 + chan->int_asserted = 0; + chan->int_asserted |= tx_int ? SIO::INTR_TX : 0; + chan->int_asserted |= rx_int ? SIO::INTR_RX : 0; + + // 両方のチャンネル分を合成 + int_asserted |= chan->int_asserted; + } + + gInterrupt->ChangeINT(this, int_asserted); +} + // モニター -bool -uPD7201Device::MonitorUpdate() +void +uPD7201Device::MonitorUpdate(Monitor *, TextScreen& screen) { - monitor.Clear(); + int y; + + screen.Clear(); for (int i = 0; i < 2; i++) { int x = 0; - int y = i * 10; - MonitorUpdateCh(i, x, y); + y = i * 10; + MonitorUpdateCh(screen, i, x, y); } - return true; + + // CR2A + uint cr2a = cr.cr2a; + y = 20; + screen.Print(0, y, "CR2A:$%02x", cr2a); + if ((cr2a & 0x80)) { + screen.Print(9, y, TA::On, "SYNC"); + } else { + screen.Print(9, y, "RTSB"); + } + screen.Print(14, y, TA::Disable, "----"); + screen.Print(19, y, TA::OnOff((cr2a & 0x20)), "Vect"); + if ((cr2a & 0x18) == 0x10) { + screen.Print(24, y, "IM=V4-V2"); + } else { + screen.Print(24, y, "IM=V2-V0"); + } + // bit2 (Priority Select) は bit0 が 0 の時だけ意味があるようだ + if ((cr2a & 0x01) == 0) { + if ((cr2a & 0x04)) { + screen.Print(34, y, "RxTx"); + } else { + screen.Print(34, y, "A->B"); + } + } + // bit1,0 はよく分からん + screen.Print(39, y, "INT/DMA=%d", (cr2a & 0x3)); + y++; + + // CR2B + screen.Print(0, y, "CR2B:$%02x", cr.vector); } // モニター (1チャンネル) void -uPD7201Device::MonitorUpdateCh(int ch, int xbase, int ybase) +uPD7201Device::MonitorUpdateCh(TextScreen& screen, + int ch, int xbase, int ybase) { - struct SIO::channel *chan = &cr.chan[ch]; + SIO::channel *chan = &cr.chan[ch]; uint cr0; uint cr1; uint cr3; @@ -402,7 +780,7 @@ uPD7201Device::MonitorUpdateCh(int ch, i int x; int y; - cr0 = chan->cr0; + cr0 = (chan->cr0 & ~SIO::CR0_PTR) | chan->ptr; cr1 = chan->cr1; cr3 = chan->cr3; cr4 = chan->cr4; @@ -414,32 +792,39 @@ uPD7201Device::MonitorUpdateCh(int ch, i // 0 1 2 3 4 5 6 // 012345678901234567890123456789012345678901234567890123456789012345 // 0123 0123 0123 0123 0123 0123 0123 0123 - // CR0:$xx CRC=x CMD=x PTR=x - // CR1:$xx WE 0 WR RXInt=x 0 TXEn ESEn + // CR0: $xx CRC=x CMD=x PTR=x + // CR1: $xx WE 0 WR RXInt=x 0 TXEn ESEn + // RXBuf: 0 --- --- --- Intr: Rx Sp Tx ES x = xbase; y = ybase; - monitor.Print(x, y++, "Channel %s: %s", chan->name, chan->desc); - monitor.Print(x, y++, "CR0:$%02x", cr0); - monitor.Print(x, y++, "CR1:$%02x", cr1); - monitor.Print(x, y++, "CR3:$%02x", cr3); - monitor.Print(x, y++, "CR4:$%02x", cr4); - monitor.Print(x, y++, "CR5:$%02x", cr5); - monitor.Print(x, y++, "SR0:$%02x", sr0); - monitor.Print(x, y++, "SR1:$%02x", sr1); - monitor.Print(x, y, "RXbuf:%d", rxlen); + screen.Print(x, y++, "Channel %s: %s", chan->name, chan->desc); + screen.Print(x, y++, "CR0: $%02x", cr0); + screen.Print(x, y++, "CR1: $%02x", cr1); + screen.Print(x, y++, "CR3: $%02x", cr3); + screen.Print(x, y++, "CR4: $%02x", cr4); + screen.Print(x, y++, "CR5: $%02x", cr5); + screen.Print(x, y++, "SR0: $%02x", sr0); + screen.Print(x, y++, "SR1: $%02x", sr1); + screen.Print(x, y, "RXbuf: %d", rxlen); int i; for (i = 0; i < rxlen; i++) { - monitor.Print(x + 8 + i * 4, y, "$%02x", chan->rxbuf[i]); + screen.Print(x + 9 + i * 4, y, "$%02x", chan->rxbuf[i]); } for (; i < sizeof(chan->rxbuf); i++) { - monitor.Print(x + 8 + i * 4, y, "---"); + screen.Print(x + 9 + i * 4, y, "---"); } + // 内部割り込み状態 + screen.Print(31, y, "Intr:"); + screen.Print(37, y, TA::OnOff(chan->int_asserted & SIO::INTR_RX), "Rx"); + screen.Print(40, y, TA::OnOff(chan->int_asserted & SIO::INTR_SP), "SP"); + screen.Print(43, y, TA::OnOff(chan->int_asserted & SIO::INTR_TX), "Tx"); + screen.Print(46, y, TA::OnOff(chan->int_asserted & SIO::INTR_ES), "ES"); y = ybase + 1; x = xbase; // CR0 - monitor.Print(x + 9, y, "CRC=%d", (cr0 >> 6)); + screen.Print(x + 9, y, "CRC=%d", (cr0 >> 6)); static const char * const cmd_str[] = { // 0123456789012345 "No operation", @@ -452,81 +837,123 @@ uPD7201Device::MonitorUpdateCh(int ch, i "End of Interrupt", }; uint cmd = (cr0 >> 3) & 7; - monitor.Print(x + 19, y, "CMD=%d(%s)", cmd, cmd_str[cmd]); - monitor.Print(x + 43, y, "PTR=%d", (cr0 & 7)); + screen.Print(x + 19, y, "CMD=%d(%s)", cmd, cmd_str[cmd]); + screen.Print(x + 43, y, "PTR=%d", (cr0 & 7)); y++; // CR1 static const char * const cr1_str[] = { - "WaEn", "----", "WaRx", "", "", "----", "TxIE", "ESEn" + "Wait", "-", "", "", "", "VMod", "TxIE", "ESIE" }; - MonitorReg(x + 9, y, cr1, cr1_str); - monitor.Print(x + 24, y, "RXInt=%d", (cr1 >> 3) & 3); + MonitorReg(screen, x + 9, y, cr1, cr1_str); + if ((cr1 & SIO::CR1_WAIT_EN)) { + screen.Puts(x + 19, y, ((cr1 & SIO::CR1_WAIT_RX) ? "OnRx" : "OnTx")); + } else { + screen.Puts(x + 19, y, TA::Disable, "TxRx"); + } + screen.Print(x + 24, y, "RxIntMod%d", (cr1 >> 3) & 3); + // CR1 の bit2 (Modified Vector) は CR1B のみ。 + if (ch == 0) { + screen.Puts(x + 34, y, TA::Disable, "----"); + } y++; // CR3 static const char * const cr3_str[] = { - "", "", "AuEn", "----", "----", "----", "----", "RXEn" + "", "", "Auto", "-", "-", "-", "-", "RxEn" }; - MonitorReg(x + 9, y, cr3, cr3_str); - monitor.Print(x + 9, y, "RXbits=%d", 5 + ((cr3 >> 6) & 3)); + MonitorReg(screen, x + 9, y, cr3, cr3_str); + screen.Print(x + 9, y, "RxBits=%d", 5 + ((cr3 >> 6) & 3)); y++; // CR4 static const char * const cr4_str[] = { "", "", "", "", "", "", "PaEv", "PaEn" }; - MonitorReg(x + 9, y, cr4, cr4_str); + MonitorReg(screen, x + 9, y, cr4, cr4_str); static const char * const clkrate_str[] = { "1", "16", "32", "64" }; - monitor.Print(x + 9, y, "Rate=x%s", clkrate_str[(cr4 >> 6) & 3]); + screen.Print(x + 9, y, "Rate=x%s", clkrate_str[(cr4 >> 6) & 3]); static const char * const syncmode_str[] = { "Mono", "Bi", "HDLC", "Ext" }; - monitor.Print(x + 19, y, "Sync=%s", syncmode_str[(cr4 >> 4) & 3]); + uint stopbit = (cr4 >> 2) & 3; + screen.Print(x + 19, y, (stopbit == 0 ? TA::Normal : TA::Disable), + "Sync=%s", syncmode_str[(cr4 >> 4) & 3]); static const char * const stopbit_str[] = { - "Sync", "1", "1.5", "2" + "SyncMode", + "StopBit=1", + "Stop=1.5", + "StopBit=2" }; - monitor.Print(x + 29, y, "Stop=%s", stopbit_str[(cr4 >> 2) & 3]); + screen.Puts(x + 29, y, stopbit_str[stopbit]); y++; // CR5 static const char * const cr5_str[] = { - "DTR", "", "", "SBrk", "TXEn", "CRC", "!RTS", "TCEn" + "!DTR", "", "", "SBrk", "TxEn", "CRC", "!RTS", "TCEn" }; - MonitorReg(x + 9, y, cr5, cr5_str); - monitor.Print(x + 14, y, "TXbits=%d", 5 + ((cr5 >> 5) & 3)); + MonitorReg(screen, x + 9, y, cr5, cr5_str); + screen.Print(x + 14, y, "TxBits=%d", 5 + ((cr5 >> 5) & 3)); y++; // SR0 static const char * const sr0_str[] = { - "BrAb", "TxUn", "CTS", "SYNC", "DCD", "TxEm", "IntP", "RxAv" + "BrAb", "TxUn", "!CTS", "!SYN", "!DCD", "TxEm", "IntP", "RxAv" }; - MonitorReg(x + 9, y, sr0, sr0_str); + MonitorReg(screen, x + 9, y, sr0, sr0_str); + // SR0 の bit1 (Interrupt Pending) は SR0A のみ。SR0B では 0。 + if (ch == 1) { + screen.Print(39, y, TA::Disable, "----"); + } y++; // SR1 static const char * const sr1_str[] = { - "EOF", "CRCE", "RxOv", "PaEr", "", "", "", "Sent" + "EOF", "FrEr", "RxOv", "PaEr", "", "", "", "Sent" }; - MonitorReg(x + 9, y, sr1, sr1_str); - monitor.Print(x + 29, y, "ResidueCode=%d", (sr1 >> 1) & 7); + MonitorReg(screen, x + 9, y, sr1, sr1_str); + screen.Print(x + 29, y, "ResidueCode=%d", (sr1 >> 1) & 7); y++; } // レジスタをビットごとに表示する。MonitorUpdate の下請け。 void -uPD7201Device::MonitorReg(int x, int y, uint32 reg, const char * const *names) +uPD7201Device::MonitorReg(TextScreen& screen, + int x, int y, uint32 reg, const char * const *names) { for (int i = 0; i < 8; i++) { - if (strcmp(names[i], "----") == 0) { - monitor.Print(x + i * 5, y, TA::Disable, "%s", names[i]); + if (names[i][0] == '-') { + screen.Puts(x + i * 5, y, TA::Disable, "----"); } else { bool b = reg & (1 << (7 - i)); - uint attr = b ? TA::On : TA::Off; - monitor.Print(x + i * 5, y, attr, "%s", names[i]); + screen.Puts(x + i * 5, y, TA::OnOff(b), names[i]); } } } + +// ログ表示用のレジスタ名 +/*static*/ const char * const +uPD7201Device::crnames[16] = { + "CR0A", "CR0B", + "CR1A", "CR1B", + "CR2A", "CR2B", + "CR3A", "CR3B", + "CR4A", "CR4B", + "CR5A", "CR5B", + "CR6A", "CR6B", + "CR7A", "CR7B", +}; +/*static*/ const char * const +uPD7201Device::srnames[16] = { + "SR0A", "SR0B", + "SR1A", "SR1B", + "SR2A?", "SR2B", + "SR3A?", "SR3B?", + "SR4A?", "SR4B?", + "SR5A?", "SR5B?", + "SR6A?", "SR6B?", + "SR7A?", "SR7B?", +};