File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / upd7201.h
Revision 1.1.1.10 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:07 2026 UTC (3 months 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
//

#pragma once

#include "device.h"
#include "event.h"
#include "monitor.h"
#include <array>

// uPD7201/Z8530 のレジスタ
//
// uPD7201 は、書き込みが CR (Command Register) 8本、
// 読み込みが SR (Status Register) 2本。
// Z8530 は、書き込みが WR (Write Register) 16本、
// 読み込みが RR (Read Register) 8本。
//
// Z8530 の WR0..WR7、RR0..RR2 は (名前が異なるが)
// uPD7201 の CR0..CR7、SR0..SR2 とわりとよく似ている (所々違う)。
//
// そのためデバイスは uPD7201Device クラスを基底に、SIODevice
// (ほぼ uPD7201Device) と SCCDevice (Z8530) とに派生している。
// レジスタ構造体は C++ の制約的に両方のセットを持ったものを一つ
// 用意するほうが楽なので、こうなっている。
struct SIO
{
	//       b7   b6   b5   b4   b3   b2   b1   b0
	//     +---------+--------------+--------------+
	// CR0 |   CRC   |   Command    |   Pointer    |
	//     +---------+--------------+--------------+
	static const int CR0_CRC		= 0xc0;
	static const int CR0_CMD		= 0x38;
	static const int CR0_PTR		= 0x07;

	// Z8530 ではコマンド %000 と %001 がレジスタ切り替えなので、
	// CMD が 0x38、PTR が 0x0f とあえて被せてある。
	static const int WR0_CRC		= CR0_CRC;
	static const int WR0_CMD		= CR0_CMD;
	static const int WR0_PTR		= 0x0f;

	//       b7   b6   b5   b4   b3   b2   b1   b0
	//     +----+----+----+---------+----+----+----+
	// CR1 | WE | -- | WR |  RX Int | -- | TE | ES |
	//     +----+----+----+---------+----+----+----+
	//       |         |       |           |    +---- E/S Int Enable
	//       |         |       |           +--------- TX INT/DMA Enable
	//       |         |       +--------------------- RX Int Mode
	//       |         +----------------------------- Wait on RX(=%1),TX(=%0)
	//       +--------------------------------------- Wait Enable
	static const int CR1_WAIT_EN	= 0x80;
	static const int CR1_WAIT_RX	= 0x20;
	static const int CR1_RXINT		= 0x18;
	static const int CR1_TXINT_EN	= 0x02;
	static const int CR1_ESINT_EN	= 0x01;

	static const int WR1_WAIT_EN	= CR1_WAIT_EN;
	static const int WR1_WAIT_RX	= CR1_WAIT_RX;
	static const int WR1_RXINT		= CR1_RXINT;
	static const int WR1_TXINT_EN	= CR1_TXINT_EN;
	static const int WR1_ESINT_EN	= CR1_ESINT_EN;

	// CR1_RXINT
	static const int RXINT_DISABLE			= 0x00;	// disabled
	static const int RXINT_FIRSTCHAR		= 0x08;	// First charactoer intr.
	static const int RXINT_ALL_INC_SPECIAL	= 0x10;	// All char include special
	static const int RXINT_ALL_EXC_SPECIAL	= 0x18;	// All char exclude special

	//       b7   b6   b5   b4   b3   b2   b1   b0
	//     +---------+----+-------------------+----+
	// CR3 | RX bits | AE | --   --   --   -- | RE |
	//     +---------+----+-------------------+----+
	//          |      |                        +---- RX Enable
	//          |      +----------------------------- Auto Enable
	//          +------------------------------------ RX bits
	static const int CR3_RXBITS		= 0xc0;
	static const int CR3_AUTO_EN	= 0x40;
	static const int CR3_RX_EN		= 0x01;

	static const int WR3_RXBITS		= CR3_RXBITS;
	static const int WR3_AUTO_EN	= CR3_AUTO_EN;
	static const int WR3_RX_EN		= CR3_RX_EN;

	//       b7   b6   b5   b4   b3   b2   b1   b0
	//     +---------+---------+---------+----+----+
	// CR4 | ClkRate | SyncMode| StopBits| PV | PE |
	//     +---------+---------+---------+----+----+
	//          |         |         |      |    +---- Parity Enable(%1)
	//          |         |         |      +--------- Parity Even(%1)
	//          |         |         +---------------- Stop Bits
	//          |         +-------------------------- Sync Mode
	//          +------------------------------------ Clock Rate
	//
	static const int CR4_CLKRATE	= 0xc0;
	static const int CR4_SYNCMODE	= 0x30;
	static const int CR4_STOPBITS	= 0x0c;
	static const int CR4_PARITY_EVEN= 0x02;
	static const int CR4_PARITY_EN	= 0x01;

	static const int WR4_CLKRATE	= CR4_CLKRATE;
	static const int WR4_SYNCMODE	= CR4_SYNCMODE;
	static const int WR4_STOPBITS	= CR4_STOPBITS;
	static const int WR4_PARITY_EVEN= CR4_PARITY_EVEN;
	static const int WR4_PARITY_EN	= CR4_PARITY_EN;

	static const int SYNC_8BIT		= 0x00;
	static const int SYNC_16BIT		= 0x10;
	static const int SYNC_HDLC		= 0x20;
	static const int SYNC_EXT		= 0x30;

	static const int SYNC_MODE		= 0x00;
	static const int STOP_BITS_1	= 0x04;
	static const int STOP_BITS_1_5	= 0x08;
	static const int STOP_BITS_2	= 0x0c;

	enum Mode {
		SYNC = 0,
		ASYNC = 1,
		HDLC = 2,
	};

	//       b7   b6   b5   b4   b3   b2   b1   b0
	//     +----+---------+----+----+----+----+----+
	// CR5 | DTR| TX bits | SB | TE | CRC| RTS| TC |
	//     +----+---------+----+----+----+----+----+
	//       |       |      |    |    |    |    +---- TX CRC Enable
	//       |       |      |    |    |    +--------- ~RTS
	//       |       |      |    |    +-------------- CRC-16/CCITT
	//       |       |      |    +------------------- TX Enable
	//       |       |      +------------------------ Send Break
	//       |       +------------------------------- TX Bits
	//       +--------------------------------------- ~DTR
	static const int CR5_DTR		= 0x80;
	static const int CR5_TXBITS		= 0x60;
	static const int CR5_SENDBREAK	= 0x10;
	static const int CR5_TX_EN		= 0x08;
	static const int CR5_CRC16		= 0x04;
	static const int CR5_RTS		= 0x02;
	static const int CR5_TXCRC_EN	= 0x01;

	static const int WR5_DTR		= CR5_DTR;
	static const int WR5_TXBITS		= CR5_TXBITS;
	static const int WR5_SENDBREAK	= CR5_SENDBREAK;
	static const int WR5_TX_EN		= CR5_TX_EN;
	static const int WR5_CRC16		= CR5_CRC16;
	static const int WR5_RTS		= CR5_RTS;
	static const int WR5_TXCRC_EN	= CR5_TXCRC_EN;

	//       b7   b6   b5   b4   b3   b2   b1   b0
	//     +----+----+----+----+----+----+----+----+
	// SR0 | BA | TU | CTS| SY | DCD| TE | IP | RA |
	//     +----+----+----+----+----+----+----+----+
	static const int SR0_BREAK		= 0x80;	// Break/Abort
	static const int SR0_TXUNDER	= 0x40;	// Tx Underrun
	static const int SR0_CTS		= 0x20;	// CTS
	static const int SR0_SYNC		= 0x10;	// SYNC
	static const int SR0_DCD		= 0x08;	// DCD
	static const int SR0_TXEMPTY	= 0x04;	// Tx Buffer Empty
	static const int SR0_INTPEND	= 0x02;	// Interrupt Pending
	static const int SR0_RXAVAIL	= 0x01;	// Rx Character Available

	static const int RR0_BREAK		= SR0_BREAK;
	static const int RR0_TXUNDER	= SR0_TXUNDER;
	static const int RR0_CTS		= SR0_CTS;
	static const int RR0_SYNC		= SR0_SYNC;
	static const int RR0_DCD		= SR0_DCD;
	static const int RR0_TXEMPTY	= SR0_TXEMPTY;
	static const int RR0_INTPEND	= SR0_INTPEND;
	static const int RR0_RXAVAIL	= SR0_RXAVAIL;

	//       b7   b6   b5   b4   b3   b2   b1   b0
	//     +----+----+----+----+--------------+----+
	// SR1 | EF | CRC| RO | PE | ResidueCode  | AS |
	//     +----+----+----+----+--------------+----+
	static const int SR1_ENDOFFRAME	= 0x80;	// End of Frame
	static const int SR1_CRCERROR	= 0x40;	// CRC/Framing Error
	static const int SR1_RXOVERRUN	= 0x20;	// Rx Overrun
	static const int SR1_PARITYERROR= 0x10;	// Parity Error
	static const int SR1_RESIDUEMASK= 0x0e;	// Residue Code 2-0
	static const int SR1_ALL_SENT	= 0x01;	// All Sent

	static const int RR1_ENDOFFRAME	= SR1_ENDOFFRAME;
	static const int RR1_CRCERROR	= SR1_CRCERROR;
	static const int RR1_RXOVERRUN	= SR1_RXOVERRUN;
	static const int RR1_PARITYERROR= SR1_PARITYERROR;
	static const int RR1_RESIDUEMASK= SR1_RESIDUEMASK;
	static const int RR1_ALL_SENT	= SR1_ALL_SENT;

	// 割り込みベクタ          ChB   ChA
	// Tx Buffer Empty         %000  %100
	// External/Status Change  %001  %101
	// Rx Character Available  %010  %110
	// Special Rx Condition    %011  %111
	// (この 3bit が V4-V2 か V2-V0 のところに出力される)
	static const uint INTR_TX	= (1U << 0);
	static const uint INTR_ES	= (1U << 1);
	static const uint INTR_RX	= (1U << 2);
	static const uint INTR_SP	= (1U << 3);

	// 割り込みあたりのロジック (uPD7201/7201A Figure 3-2 より INT 分のみ)
	//
	//   Rx Disable (CR1:b1) *1 -----------------+    *1: Rx Enable (CR3:b0)
	//                                           |        じゃなくて?
	//                        +---+              |
	// * RxCharAvailable ---->|    |             |
	//                        | AND )--+         |  +---+
	//   All/First RxChar --->|    |   |  +---+  +->|    |
	//                        +---+    +->)    |    | AND )--+
	//                                    ) OR  )-->|    |   |
	// * Special Rx Condition ----------->)    |    +---+    |  +-+
	//                                    +---+              +->)  |
	//                                                          )   |     ---
	// * External/Status -------------------------------------->) OR |o-> INT
	//                                                          )   |
	//                                    +---+              +->)  |
	// * Tx Buffer Empty ---------------->|    |    +----+   |  +-+
	//                                    |    |    |    |   |
	//   (Reset TxIntPend Cmd) --------->o| AND )-->|    |   |
	//                                    |    |    |    |   |
	//   Tx INT/DMA Enable (CR1:b1) ----->|    |    | AND )--+
	//                                    +---+     |    |
	//                                              |    |
	//   INT DMA Mode (CR2A b1-0) ----------------->|    |
	//                                              +----+

	int cr2a;			// CR2A
	int vector;
	struct channel {
		// 二次変数など
		int ptr;			// CR0 で決まるレジスタ選択ポインタ
		int id;				// チャンネル番号。0 か 1
		const char *name;	// チャンネル名。"A" か "B"
		const char *desc;	// チャンネルの用途(モニタ表示用)
		Mode mode;			// CR4 で決まるモード

		// uPD7201 レジスタ
		// CR0 の下位 3bit (PTR 部分) は読み出し時には使わないこと (代わりに
		// ptr を使用する)。書き込み時は書き込んでよい(書き捨て)。
		uint8 cr0;
		uint8 cr1;
							// CR2 は特殊なのでここにはない
		uint8 cr3;
		uint8 cr4;
		uint8 cr5;
		uint8 cr6;			// CR6 (未実装)
		uint8 cr7;			// CR7 (未実装)

		uint8 sr0;
		uint8 sr1;

		// ここから Z8530 拡張
		uint8 wr10;			// WR10
		uint8 wr11;			// WR11
		uint16 tc;			// WR12, WR13
		uint8 wr14;			// WR14
		uint8 wr15;			// WR15

		// ここからその他の内部状態
		bool es_ratched;	// E/S ビットがラッチされているか

		// 受信バッファは短いので先頭からの FIFO とし、
		// 取り出したら前にずらす?
		uint8 rxbuf[3];		// 受信バッファ(FIFO)
		int   rxlen;		// 受信バッファの有効バイト数

		uint8 txbuf;		// 送信バッファ
		int   txlen;		// 送信バッファの有効バイト数
		uint8 tx_shiftreg;	// 送信シフトレジスタ相当品

		// First Char 用のフラグで、この受信で割り込みを起こすなら true。
		// ALL なら常に true。FIRSTCHAR の時は最初が true で1文字目の受信
		// 割り込みを出したところで false にする。
		bool all_or_first;

		// 送信割り込み
		bool txint;

		uint int_asserted;	// 割り込み要因を保持
	} chan[2];
};

class uPD7201Device : public IODevice
{
	using inherited = IODevice;
 public:
	uPD7201Device(const std::string& objname_);
	virtual ~uPD7201Device() override;

	void ResetHard() override;

	// 受信
	bool Rx(int ch, uint32 data);

 protected:
	void ResetChannel(SIO::channel *chan);
	virtual uint8 ReadCtrl(SIO::channel *chan);
	uint8 ReadData(SIO::channel *chan);
	virtual void WriteCtrl(SIO::channel *chan, uint32 data);
	void WriteData(SIO::channel *chan, uint32 data);
	uint8 PeekCtrl(const SIO::channel *chan) const;
	uint8 PeekData(const SIO::channel *chan) const;

	virtual void WriteCR0(SIO::channel *chan, uint32 data);
	void SendAbort(SIO::channel *chan);
	void ResetESIntr(SIO::channel *chan);
	void EnableIntrOnNextRx(SIO::channel *chan);
	void ResetTxIntrPending(SIO::channel *chan);
	void ErrorReset(SIO::channel *chan);
	void ResetHighestIUS(SIO::channel *chan);
	void WriteCR1(SIO::channel *chan, uint32 data);
	void WriteCR2(SIO::channel *chan, uint32 data);
	void WriteCR3(SIO::channel *chan, uint32 data);
	void WriteCR4(SIO::channel *chan, uint32 data);
	virtual void WriteCR5(SIO::channel *chan, uint32 data);
	void WriteCR6(SIO::channel *chan, uint32 data);
	void WriteCR7(SIO::channel *chan, uint32 data);

	void TrySend(SIO::channel *chan);
	void TXCallback(Event& event);
	virtual void Tx(int ch, uint32 data) = 0;

	void ChangeInterrupt();
	// 割り込みを上げる (上位デバイスで用意する)
	virtual void Interrupt() = 0;

	// 指定のレジスタ名を返す
	virtual const char *CRName(const SIO::channel *chan, int n) const;
	virtual const char *SRName(const SIO::channel *chan, int n) const;
	// 現在のレジスタ名を返す
	const char *CRName(const SIO::channel *chan) const {
		return CRName(chan, chan->ptr);
	}
	const char *SRName(const SIO::channel *chan) const {
		return SRName(chan, chan->ptr);
	}

	struct SIO cr {};

	// TxC ピンに供給されているクロックで 12ビットの通信にかかる
	// 時間を ns 単位で設定。12 ビットは誤差を減らすために選択。
	uint64 txc12_ns {};

	// 送信用イベント
	std::array<Event, 2> txevent {};

	Monitor monitor { this };

	static const int bits_per_char[4];
	static const int clkrate[4];

 private:
	void SetTXPeriod(SIO::channel *chan);

	DECLARE_MONITOR_CALLBACK(MonitorUpdate);
	void MonitorUpdateCh(TextScreen&, int ch, int xbase, int ybase);
	void MonitorReg(TextScreen&,
		int, int, uint32 reg, const char * const *names);

	static const char * const crnames[16];
	static const char * const srnames[16];
};

unix.superglobalmegacorp.com

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