|
|
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];
};
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.