--- nono/vm/upd7201.cpp 2026/04/29 17:04:28 1.1.1.1 +++ nono/vm/upd7201.cpp 2026/04/29 17:04:45 1.1.1.5 @@ -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() { - monitor.Init(48, 19); + monitor_size = nnSize(48, 22); cr.chan[0].id = 0; cr.chan[0].name = "A"; cr.chan[1].id = 1; @@ -42,15 +44,15 @@ uPD7201Device::Reset() void uPD7201Device::ResetChannel(struct 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; @@ -67,8 +69,9 @@ 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->reset_txint_pend = true; // 受信バッファをクリア memset(chan->rxbuf, 0, sizeof(chan->rxbuf)); @@ -83,20 +86,14 @@ uPD7201Device::ReadCtrl(struct SIO::chan switch (chan->ptr) { case 0: // SR0 - // 送信バッファは常に空 - data = SIO::SR0_TXEMPTY; - // 受信データがあるか - if (chan->rxlen > 0) { - data |= SIO::SR0_RXAVAIL; - } + data = chan->sr0; putlog(2, "SR%d%s -> $%02x", chan->ptr, chan->name, data); // アクセス後は SR0 へ戻す chan->ptr = 0; break; case 1: // SR1 - // エラーとか起きてないという状態だけ再現。 - data = SIO::SR1_ALL_SENT; + data = chan->sr1; putlog(2, "SR%d%s -> $%02x", chan->ptr, chan->name, data); // アクセス後は SR0 へ戻す chan->ptr = 0; @@ -129,175 +126,248 @@ uPD7201Device::ReadCtrl(struct SIO::chan void uPD7201Device::WriteCtrl(struct 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(); + putlog(0, "未実装レジスタ書き込み CR%d%s <- $%02x", + chan->ptr, chan->name, data); + break; } - 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 struct 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; - break; + FALLTHROUGH; - case 3: // CR3 - chan->cr3 = data; - putlog(2, "CR%d%s <- $%02x", chan->ptr, chan->name, data); - putlog(1, "チャンネル%s RX %s, %dbits", - chan->name, - (chan->cr3 & SIO::CR3_RX_EN) ? "Enable" : "Disable", - bits_per_char[(chan->cr3 & SIO::CR3_RXBITS) >> 6]); - // アクセス後は CR0 へ戻す - chan->ptr = 0; - break; + default: + // 未調査 + return 0xff; + } +} + +// CR0 への書き込み +void +uPD7201Device::WriteCR0(struct SIO::channel *chan, uint32 data) +{ + uint cmd; - 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]); - } + // 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 + break; + case 1: // Send Abort + if (chan->mode == SIO::Mode::HDLC) { + putlog(0, "CR0: Send Abort 未実装"); } 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]); + // XXX Send Abort コマンドは「HDLC モードのみ有効」と書いて + // あるがそれ以外の時にどうなるか不明。 + putlog(0, "CR0: 未定義コマンド %d", cmd); } - - // アクセス後は CR0 へ戻す - chan->ptr = 0; break; - } - - 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; + case 2: // Reset External/Status Interrupt + putlog(3, "CR0%s: Reset E/S Interrupt", chan->name); + chan->es_ratched = false; + break; + case 3: // Channel Reset + ResetChannel(chan); + break; + case 4: // Enable Interrupt On Next Receive Character + putlog(0, "CR0: 未実装コマンド %d", cmd); + break; + case 5: // Reset Transmitter Interrupt/DMA Pending + putlog(1, "CR0%s: Reset Tx Int/DMA Pending", chan->name); + chan->reset_txint_pend = false; + ChangeInterrupt(); + break; + case 6: // Error Reset + case 7: // End of Interrupt + putlog(0, "CR0: 未実装コマンド %d", cmd); break; - } + default: + __unreachable(); + } +} - case 6: // CR6 - putlog(0, "CR6%s <- $%02x 書き込み(未実装)", chan->name, data); - chan->cr6 = data; - // アクセス後は CR0 へ戻す - chan->ptr = 0; - break; +// CR1 への書き込み +void +uPD7201Device::WriteCR1(struct SIO::channel *chan, uint32 data) +{ + putlog(2, "CR%d%s <- $%02x", chan->ptr, chan->name, data); - case 7: // CR7 - putlog(0, "CR7%s <- $%02x 書き込み(未実装)", chan->name, data); - chan->cr7 = data; - // アクセス後は CR0 へ戻す - chan->ptr = 0; - break; + chan->cr1 = data; - default: - putlog(0, "未実装レジスタ書き込み CR%d%s <- $%02x", - chan->ptr, chan->name, data); - break; + // 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(struct SIO::channel *chan, uint32 data) +{ + if (chan->id == 0) { // CR2A + putlog(2, "CR2A <- $%02x 書き込み(b7,b2-0未実装)", data); + cr.cr2a = data; + } else { // CR2B + putlog(2, "CR2B <- $%02x (Set vector)", data); + cr.vector = data; } } +// CR3 への書き込み +void +uPD7201Device::WriteCR3(struct SIO::channel *chan, uint32 data) +{ + chan->cr3 = data; + + putlog(2, "CR%d%s <- $%02x", chan->ptr, chan->name, data); + putlog(1, "Channel %s RX %s, %dbits", + chan->name, + (chan->cr3 & SIO::CR3_RX_EN) ? "Enable" : "Disable", + bits_per_char[(chan->cr3 & SIO::CR3_RXBITS) >> 6]); +} + +// CR4 への書き込み +void +uPD7201Device::WriteCR4(struct SIO::channel *chan, uint32 data) +{ + 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, "Channel %s HDLC Mode", chan->name); + } else { + 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 *cm[] = { "x1", "x16", "x32", "x64" }; + const char *sm[] = { "?", "1", "1.5", "2" }; + putlog(2, "Channel %s Async (%s clock, stopbit %s) Mode", + chan->name, + cm[(chan->cr4 & SIO::CR4_CLKRATE) >> 6], + sm[stopbits >> 2]); + } +} + +// CR5 への書き込み +void +uPD7201Device::WriteCR5(struct SIO::channel *chan, uint32 data) +{ + chan->cr5 = data; + + putlog(2, "CR%d%s <- $%02x", chan->ptr, chan->name, data); + putlog(1, "Channel %s TX %s, %dbits", + chan->name, + (chan->cr5 & SIO::CR5_TX_EN) ? "Enable" : "Disable", + bits_per_char[(chan->cr5 & SIO::CR5_TXBITS) >> 5]); +} + +// CR6 への書き込み +void +uPD7201Device::WriteCR6(struct SIO::channel *chan, uint32 data) +{ + chan->cr6 = data; + + putlog(0, "CR6%s <- $%02x 書き込み(未実装)", chan->name, data); +} + +// CR7 への書き込み +void +uPD7201Device::WriteCR7(struct SIO::channel *chan, uint32 data) +{ + chan->cr7 = data; + + putlog(0, "CR7%s <- $%02x 書き込み(未実装)", chan->name, data); +} + // データポートの読み込み uint8 -uPD7201Device::ReadData(int ch) +uPD7201Device::ReadData(struct 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) { + if (chan->rxlen == 0) { + // 取り出して空になった + chan->sr0 &= ~SIO::SR0_RXAVAIL; + ChangeInterrupt(); + } else { + // 少ないので毎回前詰めしちゃえ memmove(chan->rxbuf + 1, chan->rxbuf, chan->rxlen); } } else { // XXX 何が読めるか data = 0xff; + putlog(3, "Ch%s Data -> $%02x (Empty)", chan->name, data); } return data; @@ -314,8 +384,7 @@ 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); // 1文字送信 @@ -323,14 +392,24 @@ uPD7201Device::WriteData(struct SIO::cha // 必ず Tx Buffer Empty が発生する。 chan->sr0 |= SIO::SR0_TXEMPTY; - // 割り込みをあげる - if ((chan->cr1 & SIO::CR1_TXINT_EN)) { - putlog(2, "割り込み (TX Empty)"); - Interrupt(); + // 割り込み状態を変える + ChangeInterrupt(); +} + +// データポートの Peek +uint8 +uPD7201Device::PeekData(const struct SIO::channel *chan) const +{ + if (chan->rxlen > 0) { + // 受信バッファにデータがあれば先頭バイトが見える + return chan->rxbuf[0]; + } else { + // 空の時は? + return 0xff; } } -// TX/RX の bits/char のレジスタ値を実際の bits/char の値に変換します。 +// TX/RX の bits/char のレジスタ値を実際の bits/char の値に変換する。 /* static */ const int uPD7201Device::bits_per_char[] = { 5, 7, 6, 8 }; @@ -347,48 +426,119 @@ uPD7201Device::Rx(int ch, uint32 data) 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(); + // 割り込み状態を変更 + ChangeInterrupt(); + + return true; +} + +// 割り込み状態を変更 +void +uPD7201Device::ChangeInterrupt() +{ + uint int_asserted = 0; + + for (int ch = 0; ch < 2; ch++) { + struct 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; } - chan->rxrecved = true; - } else if ((chan->cr1 & SIO::CR1_RXINT) >= SIO::RXINT_ALL_INC_SPECIAL) { - // All character interrupt モード (Special のことはちょっと置いとく) - chan->rxrecved = true; - Interrupt(); + + // XXX E/S not yet + + // Tx + bool tx_empty = (chan->sr0 & SIO::SR0_TXEMPTY); + bool tx_enable = (chan->cr1 & SIO::CR1_TXINT_EN); + // reset_txint_pend は通常 true、コマンド発行で false に落ちる。 + // XXX INT DMA Mode はよく分からんのでスルー + bool tx_int = (tx_empty && tx_enable && chan->reset_txint_pend); + + // 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; } - return true; + gInterrupt->ChangeINT(this, int_asserted); } // モニター -bool -uPD7201Device::MonitorUpdate() +void +uPD7201Device::MonitorUpdate(TextScreen& monitor) { + int y; + monitor.Clear(); for (int i = 0; i < 2; i++) { int x = 0; - int y = i * 10; - MonitorUpdateCh(i, x, y); + y = i * 10; + MonitorUpdateCh(monitor, i, x, y); } - return true; + + // CR2A + uint cr2a = cr.cr2a; + y = 20; + monitor.Print(0, y, "CR2A:$%02x", cr2a); + if ((cr2a & 0x80)) { + monitor.Print(9, y, TA::On, "SYNC"); + } else { + monitor.Print(9, y, "RTSB"); + } + monitor.Print(14, y, "----"); + monitor.Print(19, y, TA::OnOff((cr2a & 0x20)), "Vect"); + if ((cr2a & 0x18) == 0x10) { + monitor.Print(24, y, "IM=V4-V2"); + } else { + monitor.Print(24, y, "IM=V2-V0"); + } + // bit2 (Priority Select) は bit0 が 0 の時だけ意味があるようだ + if ((cr2a & 0x01) == 0) { + if ((cr2a & 0x04)) { + monitor.Print(34, y, "RxTx"); + } else { + monitor.Print(34, y, "A->B"); + } + } + // bit1,0 はよく分からん + monitor.Print(39, y, "INT/DMA=%d", (cr2a & 0x3)); + y++; + + // CR2B + monitor.Print(0, y, "CR2B:$%02x", cr.vector); } // モニター (1チャンネル) void -uPD7201Device::MonitorUpdateCh(int ch, int xbase, int ybase) +uPD7201Device::MonitorUpdateCh(TextScreen& monitor, + int ch, int xbase, int ybase) { struct SIO::channel *chan = &cr.chan[ch]; uint cr0; @@ -402,7 +552,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,27 +564,34 @@ 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); + 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); int i; for (i = 0; i < rxlen; i++) { - monitor.Print(x + 8 + i * 4, y, "$%02x", chan->rxbuf[i]); + monitor.Print(x + 9 + i * 4, y, "$%02x", chan->rxbuf[i]); } for (; i < sizeof(chan->rxbuf); i++) { - monitor.Print(x + 8 + i * 4, y, "---"); + monitor.Print(x + 9 + i * 4, y, "---"); } + // 内部割り込み状態 + monitor.Print(31, y, "Intr:"); + monitor.Print(37, y, TA::OnOff(chan->int_asserted & SIO::INTR_RX), "Rx"); + monitor.Print(40, y, TA::OnOff(chan->int_asserted & SIO::INTR_SP), "SP"); + monitor.Print(43, y, TA::OnOff(chan->int_asserted & SIO::INTR_TX), "Tx"); + monitor.Print(46, y, TA::OnOff(chan->int_asserted & SIO::INTR_ES), "ES"); y = ybase + 1; x = xbase; @@ -460,15 +617,22 @@ uPD7201Device::MonitorUpdateCh(int ch, i static const char * const cr1_str[] = { "WaEn", "----", "WaRx", "", "", "----", "TxIE", "ESEn" }; - MonitorReg(x + 9, y, cr1, cr1_str); - monitor.Print(x + 24, y, "RXInt=%d", (cr1 >> 3) & 3); + MonitorReg(monitor, x + 9, y, cr1, cr1_str); + static const char * const rxint_str[] = { + // 2345678 + "=SpOnly", + "=AllChr", + "=1stChr", + "Disable", + }; + monitor.Print(x + 24, y, "RI%s", rxint_str[(cr1 >> 3) & 3]); y++; // CR3 static const char * const cr3_str[] = { "", "", "AuEn", "----", "----", "----", "----", "RXEn" }; - MonitorReg(x + 9, y, cr3, cr3_str); + MonitorReg(monitor, x + 9, y, cr3, cr3_str); monitor.Print(x + 9, y, "RXbits=%d", 5 + ((cr3 >> 6) & 3)); y++; @@ -476,7 +640,7 @@ uPD7201Device::MonitorUpdateCh(int ch, i static const char * const cr4_str[] = { "", "", "", "", "", "", "PaEv", "PaEn" }; - MonitorReg(x + 9, y, cr4, cr4_str); + MonitorReg(monitor, x + 9, y, cr4, cr4_str); static const char * const clkrate_str[] = { "1", "16", "32", "64" @@ -496,7 +660,7 @@ uPD7201Device::MonitorUpdateCh(int ch, i static const char * const cr5_str[] = { "DTR", "", "", "SBrk", "TXEn", "CRC", "!RTS", "TCEn" }; - MonitorReg(x + 9, y, cr5, cr5_str); + MonitorReg(monitor, x + 9, y, cr5, cr5_str); monitor.Print(x + 14, y, "TXbits=%d", 5 + ((cr5 >> 5) & 3)); y++; @@ -504,29 +668,33 @@ uPD7201Device::MonitorUpdateCh(int ch, i static const char * const sr0_str[] = { "BrAb", "TxUn", "CTS", "SYNC", "DCD", "TxEm", "IntP", "RxAv" }; - MonitorReg(x + 9, y, sr0, sr0_str); + MonitorReg(monitor, x + 9, y, sr0, sr0_str); + // SR0 の bit1 (Interrupt Pending) は SR0A のみ。SR0B では 0。 + if (ch == 1) { + monitor.Print(39, y, "----"); + } y++; // SR1 static const char * const sr1_str[] = { "EOF", "CRCE", "RxOv", "PaEr", "", "", "", "Sent" }; - MonitorReg(x + 9, y, sr1, sr1_str); + MonitorReg(monitor, x + 9, y, sr1, sr1_str); monitor.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& monitor, + 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]); + monitor.Puts(x + i * 5, y, TA::Disable, names[i]); } else { bool b = reg & (1 << (7 - i)); - uint attr = b ? TA::On : TA::Off; - monitor.Print(x + i * 5, y, attr, "%s", names[i]); + monitor.Puts(x + i * 5, y, TA::OnOff(b), names[i]); } } }