File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / upd7201.cpp
Revision 1.1.1.9 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:07 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v014, HEAD
nono 0.2.3

//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//

#include "upd7201.h"
#include "interrupt.h"

// コンストラクタ
uPD7201Device::uPD7201Device(const std::string& objname_)
	: inherited(objname_)
{
	cr.chan[0].id = 0;
	cr.chan[0].name = "A";
	cr.chan[1].id = 1;
	cr.chan[1].name = "B";

	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);
	// 登録は継承先で行う。機種ごとに通りのいい名前を使い分けているため。
}

// デストラクタ
uPD7201Device::~uPD7201Device()
{
}

// リセット
void
uPD7201Device::ResetHard()
{
	putlog(2, "リセット");

	// CR2A はチップリセットで bit6 以外を '0' に
	cr.cr2a &= ~0xbf;

	// チャンネルリセット
	ResetChannel(&cr.chan[0]);
	ResetChannel(&cr.chan[1]);
}

// チャンネルリセット
void
uPD7201Device::ResetChannel(SIO::channel *chan)
{
	putlog(2, "Channel %s Reset", chan->name);

	// 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;

	// 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);
	chan->sr0 |= (SIO::SR0_TXUNDER | SIO::SR0_TXEMPTY);

	// SR1 は b4-7 が '0'
	chan->sr1 &= ~(SIO::SR1_ENDOFFRAME | SIO::SR1_CRCERROR |
	               SIO::SR1_RXOVERRUN | SIO::SR1_PARITYERROR);

	// 内部状態をリセット
	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(SIO::channel *chan)
{
	uint8 data;

	switch (chan->ptr) {
	 case 0:	// SR0
		data = chan->sr0;
		putlog(2, "%s -> $%02x", SRName(chan), data);
		// アクセス後は SR0 へ戻す
		chan->ptr = 0;
		break;

	 case 1:	// SR1
		data = chan->sr1;
		putlog(2, "%s -> $%02x", SRName(chan), data);
		// アクセス後は SR0 へ戻す
		chan->ptr = 0;
		break;

	 case 2:
		// SR2B はベクタの読み出し
		if (chan->id == 1) {
			data = cr.vector;
			putlog(2, "%s -> $%02x", SRName(chan), data);
			// アクセス後は SR0 へ戻す
			chan->ptr = 0;
			break;
		}

		// SR2A はない
		FALLTHROUGH;

	 default:
		putlog(0, "未実装レジスタ読み込み %s", SRName(chan));
		// SR0 へ戻る?
		chan->ptr = 0;
		return 0xff;
	}
	return data;
}

// 制御ポートの書き込み
void
uPD7201Device::WriteCtrl(SIO::channel *chan, uint32 data)
{
	// CR0 はコマンドかまたは次にアクセスするレジスタを指定する。
	// CR0 以外のレジスタをアクセスすると、次のアクセスは CR0 に戻る。

	if (chan->ptr == 0) {
		WriteCR0(chan, data);
	} else {
		switch (chan->ptr) {
		 case 1:
			WriteCR1(chan, data);
			break;
		 case 2:
			WriteCR2(chan, data);
			break;
		 case 3:
			WriteCR3(chan, data);
			break;
		 case 4:
			WriteCR4(chan, data);
			break;
		 case 5:
			WriteCR5(chan, data);
			break;
		 case 6:
			WriteCR6(chan, data);
			break;
		 case 7:
			WriteCR7(chan, data);
			break;
		 default:
			__unreachable();
		}
		// アクセス後は CR0 へ戻す
		chan->ptr = 0;
	}
}

// 制御ポートの 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;
		}
		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;

	// 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,
			bits_per_char[(chan->cr3 & SIO::CR3_RXBITS) >> 6]);
	}
}

// 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::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]);
	}

	SetTXPeriod(chan);
}

// CR5 への書き込み
void
uPD7201Device::WriteCR5(SIO::channel *chan, uint32 data)
{
	uint8 oldcr5;

	oldcr5 = chan->cr5;
	chan->cr5 = data;

	putlog(2, "%s <- $%02x", CRName(chan), data);

	// 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(SIO::channel *chan)
{
	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) {
			// 取り出して空になった
			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;
}

// データポートの書き込み
void
uPD7201Device::WriteData(SIO::channel *chan, uint32 data)
{
	char charbuf[4];

	if (isprint((int)data)) {
		snprintf(charbuf, sizeof(charbuf), "'%c'", data);
	} else {
		charbuf[0] = '\0';
	}
	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;

	// XXX たぶん送信してから割り込みを上げるまでにいくらか遅れがあるはず

	// 送信割り込み
	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);

	// 次の送信データが来てたら送信
	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
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)
{
	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;

	// 割り込み状態を変更
	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);
}

// モニター
void
uPD7201Device::MonitorUpdate(Monitor *, TextScreen& screen)
{
	int y;

	screen.Clear();
	for (int i = 0; i < 2; i++) {
		int x = 0;
		y = i * 10;
		MonitorUpdateCh(screen, i, x, y);
	}

	// 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(TextScreen& screen,
	int ch, int xbase, int ybase)
{
	SIO::channel *chan = &cr.chan[ch];
	uint cr0;
	uint cr1;
	uint cr3;
	uint cr4;
	uint cr5;
	uint sr0;
	uint sr1;
	uint rxlen;
	int x;
	int y;

	cr0 = (chan->cr0 & ~SIO::CR0_PTR) | chan->ptr;
	cr1 = chan->cr1;
	cr3 = chan->cr3;
	cr4 = chan->cr4;
	cr5 = chan->cr5;
	sr0 = chan->sr0;
	sr1 = chan->sr1;
	rxlen = chan->rxlen;

	// 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
	// RXBuf: 0 --- --- ---           Intr: Rx Sp Tx ES

	x = xbase;
	y = ybase;
	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++) {
		screen.Print(x + 9 + i * 4, y, "$%02x", chan->rxbuf[i]);
	}
	for (; i < sizeof(chan->rxbuf); i++) {
		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
	screen.Print(x + 9, y, "CRC=%d", (cr0 >> 6));
	static const char * const cmd_str[] = {
	//   0123456789012345
		"No operation",
		"Send Abort",
		"Reset E/S Int",
		"Channel Reset",
		"Enable IntNextCh",
		"Reset TxIntPend",
		"Error Reset",
		"End of Interrupt",
	};
	uint cmd = (cr0 >> 3) & 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[] = {
		"Wait", "-", "", "", "", "VMod", "TxIE", "ESIE"
	};
	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[] = {
		"", "", "Auto", "-", "-", "-", "-", "RxEn"
	};
	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(screen, x + 9, y, cr4, cr4_str);

	static const char * const clkrate_str[] = {
		"1", "16", "32", "64"
	};
	screen.Print(x + 9, y, "Rate=x%s", clkrate_str[(cr4 >> 6) & 3]);
	static const char * const syncmode_str[] = {
		"Mono", "Bi", "HDLC", "Ext"
	};
	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[] = {
		"SyncMode",
		"StopBit=1",
		"Stop=1.5",
		"StopBit=2"
	};
	screen.Puts(x + 29, y, stopbit_str[stopbit]);
	y++;

	// CR5
	static const char * const cr5_str[] = {
		"!DTR", "", "", "SBrk", "TxEn", "CRC", "!RTS", "TCEn"
	};
	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", "!SYN", "!DCD", "TxEm", "IntP", "RxAv"
	};
	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", "FrEr", "RxOv", "PaEr", "", "", "", "Sent"
	};
	MonitorReg(screen, x + 9, y, sr1, sr1_str);
	screen.Print(x + 29, y, "ResidueCode=%d", (sr1 >> 1) & 7);
	y++;
}

// レジスタをビットごとに表示する。MonitorUpdate の下請け。
void
uPD7201Device::MonitorReg(TextScreen& screen,
	int x, int y, uint32 reg, const char * const *names)
{
	for (int i = 0; i < 8; i++) {
		if (names[i][0] == '-') {
			screen.Puts(x + i * 5, y, TA::Disable, "----");
		} else {
			bool b = reg & (1 << (7 - 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?",
};

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.