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

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

//
// SCC (Z8530)
//

#include "scc.h"
#include "bitops.h"
#include "event.h"
#include "hostcom.h"
#include "keyboard.h"

// InsideOut p.135
static const busdata read_wait  = busdata::Wait(41 * 40_nsec);
static const busdata write_wait = busdata::Wait(49 * 40_nsec);

// コンストラクタ
SCCDevice::SCCDevice()
	: inherited()
{
	SetName("SCC");
	ClearAlias();
	AddAlias("SCC");
	AddAlias("MPSCC");

	// チャンネル名とポートアドレスは機種ごとに異なる。
	//
	// デバイス自身は自分が配置されるアドレスを知らなくてよい構造になって
	// いるが、直接アクセスしたりする場合などに毎回アドレスを確認するのが
	// 面倒なので(全機種で向きと間隔が異なる)、モニタで表示されてほしい。
	if (gMainApp.IsX68030()) {
		mpscc.chan[0].desc = "Serial Port";
		mpscc.chan[1].desc = "Mouse";
		mpscc.chan[1].ctrladdr = 0xe98001;
		mpscc.chan[1].dataaddr = 0xe98003;
		mpscc.chan[0].ctrladdr = 0xe98005;
		mpscc.chan[0].dataaddr = 0xe98007;

		// 5MHz
		pclk_tsec = 200_nsec;
	} else {
		// NWS-1750
		mpscc.chan[0].desc = "Serial Port #0";
		mpscc.chan[1].desc = "Serial Port #1 (NotConnected)";
		mpscc.chan[1].ctrladdr = 0xe0d40000;
		mpscc.chan[1].dataaddr = 0xe0d40001;
		mpscc.chan[0].ctrladdr = 0xe0d40002;
		mpscc.chan[0].dataaddr = 0xe0d40003;

		// たぶん 4MHz
		// http://www.ceres.dti.ne.jp/~tsutsui/netbsd/port-news68k.html#19990727
		pclk_tsec = 250_nsec;
	}

	monitor = gMonitorManager->Regist(ID_MONITOR_SCC, this);
	monitor->SetCallback(&SCCDevice::MonitorScreen);
	monitor->SetSize(70, 35);
}

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

// 初期化
bool
SCCDevice::Init()
{
	if (inherited::Init() == false) {
		return false;
	}

	hostcom->SetPortName("SCC Ch.A");

	if (gMainApp.IsX68030()) {
		keyboard = GetKeyboard();
	}

	// X680x0 では SCC と呼ぶほうが通りがいい。
	// イベントの登録は親クラスで行ってある。
	for (int ch = 0; ch < txevent.size(); ch++) {
		auto& chan = mpscc.chan[ch];
		txevent[ch]->SetName(string_format("SCC Ch%c TX", chan.name));
		rxevent[ch]->SetName(string_format("SCC Ch%c RX", chan.name));
	}

	return true;
}

// リセット
void
SCCDevice::ResetHard(bool poweron)
{
	// Z8530 は RD と WR を同時に Low にするとリセットがかかる。
	// 回路図からは PEDEC から繋がってるという以上のことは分からないけど
	// 普通に考えればハードリセットでリセットされるよな。

	// WR9 b2,3,4 = %0、b6,7 = %1
	mpscc.disable_lower_chain = false;
	mpscc.master_int_enable = false;
	mpscc.status_high_low = false;
	// XXX Reset

	for (auto& chan : mpscc.chan) {
		// XC 設定は ResetChannel() の前に必要。
		SetXC(chan);
		ResetChannel(chan);

		// WR10 b5,6 = %0
		chan.wr10 &= ~0x40;

		// WR11
		chan.wr11 = 0x08;

		// WR14 b0-1 = %0
		//
		// b4 (Local Loopback) は p.2-15 ではチャンネルリセット時は %0、
		// ハードリセット時は %1 に初期化されると書いてあるが、おそらく
		// そんなことはないだろうし、p.5-20 の WR14 の説明には
		// "This bit is reset by a channel or hardware reset." とあるので
		// たぶんこっちのほうが正しい。
		chan.wr14 &= ~0x03;
	}
}

// チャンネルリセット
void
SCCDevice::ResetChannel(MPSCCChan& chan)
{
	uint8 val;

	// WR0 b0-7 = %0
	chan.crc_cmd = 0;
	chan.cmd = 0;
	chan.ptr = 0;

	// WR1 b0,1,3,4,6,7 = %0
	chan.esint_enable = false;
	chan.txint_enable = false;
	chan.rxint_mode   = MPSCCChan::RXINT_DISABLE;
	chan.wait_func    = false;
	chan.wait_enable  = false;

	// WR3 b0 = %0
	chan.rx_enable = false;

	// WR4 b2 = %1
	// b2,3 が StopBits 設定だが b2 だけ %1 にするらしい…。
	// 少なくとも非同期モードにしときたいということだろうか。
	val = GetCR4(chan);
	SetCR4(chan, val | 0x04);

	// WR5 b1,2,3,4,7 = %0
	val = GetCR5(chan);
	SetCR5(chan, val & 0x61);

	// WR9 b5 = %0 (未実装ビット)

	// WR10 b0-4,7 = %0
	chan.wr10 &= 0x60;

	// WR14 b2-4 = %0、b5 = %1
	chan.wr14 &= 0xe3;
	chan.wr14 |= 0x20;

	// WR15
	chan.wr15 = 0xf8;

	// RR0 b0,1=%0、b2,6=%1
	chan.rxlen = 0;
	chan.txlen = 0;
	chan.tx_underrun = true;

	// RR1 b1,2=%1、b3-7=%0
	chan.sr1 &= ~(MPSCC::SR1_ENDOFFRAME |
	              MPSCC::SR1_FRAMEERROR |
	              MPSCC::SR1_RXOVERRUN  |
	              MPSCC::SR1_PARITYERROR);
	chan.sr1 &= 0x08;
	chan.sr1 |= 0x60;

	// RR3
	chan.intpend = 0;

	// RR10 b0-5,7 = %0
	chan.rr10 &= 0x40;

	ResetChannelCommon(chan);
}

busdata
SCCDevice::ReadPort(uint32 offset)
{
	busdata data;

	switch (offset) {
	 case 0:	// $E98001
		data = ReadCtrl(mpscc.chan[1]);
		break;
	 case 1:	// $E98003
		data = ReadData(mpscc.chan[1]);
		break;
	 case 2:	// $E98005
		data = ReadCtrl(mpscc.chan[0]);
		break;
	 case 3:	// $E98007
		data = ReadData(mpscc.chan[0]);
		break;
	 default:
		VMPANIC("corrupted offset=%u", offset);
	}

	data |= read_wait;
	data |= BusData::Size1;
	return data;
}

busdata
SCCDevice::WritePort(uint32 offset, uint32 data)
{
	switch (offset) {
	 case 0:	// $E98001
		WriteCtrl(mpscc.chan[1], data);
		break;
	 case 1:	// $E98003
		WriteData(mpscc.chan[1], data);
		break;
	 case 2:	// $E98005
		WriteCtrl(mpscc.chan[0], data);
		break;
	 case 3:	// $E98007
		WriteData(mpscc.chan[0], data);
		break;
	 default:
		VMPANIC("corrupted offset=%u", offset);
	}

	busdata r = write_wait;
	r |= BusData::Size1;
	return r;
}

busdata
SCCDevice::PeekPort(uint32 offset)
{
	uint64 data;

	switch (offset) {
	 case 0:	// $E98001
		data = PeekCtrl(mpscc.chan[1]);
		break;
	 case 1:	// $E98003
		data = PeekData(mpscc.chan[1]);
		break;
	 case 2:	// $E98005
		data = PeekCtrl(mpscc.chan[0]);
		break;
	 case 3:	// $E98007
		data = PeekData(mpscc.chan[0]);
		break;
	 default:
		data = 0xff;
		break;
	}
	return data;
}

// 書き込みレジスタ名を返す。
const char *
SCCDevice::CRName(const MPSCCChan& chan, uint ptr_) const
{
	assert(ptr_ < 16);
	uint idx = ptr_ * 2 + chan.id;
	assert(idx < countof(wrnames));
	return wrnames[idx];
}

// 読み込みレジスタ名を返す。
const char *
SCCDevice::SRName(const MPSCCChan& chan, uint ptr_) const
{
	assert(ptr_ < 16);
	uint idx = ptr_ * 2 + chan.id;
	assert(idx < countof(rrnames));
	return rrnames[idx];
}

// Z8530 制御ポート読み込み
uint8
SCCDevice::ReadCtrl(MPSCCChan& chan)
{
	uint8 data;

	// PTR とレジスタの対応 (Z8530 p.2-13)
	//
	// PTR Reg		PTR Reg		PTR Reg			PTR Reg
	// 0 = RR0		4 = (RR0)	 8 = RR8		12 = RR12
	// 1 = RR1		5 = (RR1)	 9 = (RR13)		13 = RR13
	// 2 = RR2		6 = (RR2)	10 = RR10		14 = RR14
	// 3 = RR3		7 = (RR3)	11 = (RR15)		15 = RR15

	switch (chan.ptr) {
	 case 0:	// RR0
	 case 4:
		data = GetRR0(chan);
		break;

	 case 1:	// RR1
	 case 5:
		data = chan.sr1;
		break;

	 case 2:
	 case 6:
		if (chan.id == 0) {	// RR2A
			data = mpscc.vector;
		} else {			// RR2B
			// 割り込み要因で変化したベクタ
			data = GetSR2B();
		}
		break;

	 case 3:
	 case 7:
		if (chan.id == 0) {	// RR3A
			data = GetRR3A();
		} else {			// RR3B
			data = 0;
		}
		break;

	 case 8:
	 case 10:
	 case 12:
	 case 13:
	 case 9:
	 case 14:
	 case 15:
	 case 11:
		putlog(0, "ReadCtrl %u (NOT IMPLEMENTED)", chan.ptr);
		data = 0xff;
		break;

	 default:
		VMPANIC("corrupted chan.ptr=%u", chan.ptr);
	}
	chan.ptr = 0;
	return data;
}

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

	if (chan.ptr == 0) {
		WriteWR0(chan, data);
	} else {
		switch (chan.ptr) {
		 case 1:
			WriteWR1(chan, data);
			break;
		 case 2:
			WriteWR2(data);
			break;
		 case 3:
			WriteCR3(chan, data);
			break;
		 case 4:
			WriteCR4(chan, data);
			break;
		 case 5:
			WriteWR5(chan, data);
			break;
		 case 6:
			WriteCR6(chan, data);
			break;
		 case 7:
			WriteCR7(chan, data);
			break;

		 case 8:
			WriteData(chan, data);
			break;
		 case 9:
			WriteWR9(data);
			break;
		 case 10:
			WriteWR10(chan, data);
			break;
		 case 11:
			WriteWR11(chan, data);
			break;
		 case 12:
			WriteWR12(chan, data);
			break;
		 case 13:
			WriteWR13(chan, data);
			break;
		 case 14:
			WriteWR14(chan, data);
			break;
		 case 15:
			WriteWR15(chan, data);
			break;
		 default:
			VMPANIC("corrupted chan.ptr=%u", chan.ptr);
		}
		// アクセス後は WR0 へ戻す
		chan.ptr = 0;
	}
}

// 制御ポートの Peek
uint8
SCCDevice::PeekCtrl(const MPSCCChan& chan) const
{
	uint8 data;

	switch (chan.ptr) {
	 case 0:	// RR0
	 case 4:
		data = GetRR0(chan);
		break;

	 case 1:	// RR1
	 case 5:
		data = chan.sr1;
		break;

	 case 2:
	 case 6:
		if (chan.id == 0) {	// RR2A
			data = mpscc.vector;
		} else {
			data = GetSR2B();
		}
		break;

	 case 3:
	 case 7:
		if (chan.id == 0) {	// RR3A
			data = GetRR3A();
		} else {			// RR3B
			data = 0;
		}
		break;

	 case 8:
		data = PeekData(chan);
		break;

	 case 10:
	 case 12:
	 case 13:
	 case 9:
	 case 14:
	 case 15:
	 case 11:
		data = 0xff;
		break;

	 default:
		VMPANIC("corrupted chan.ptr=%u", chan.ptr);
	}
	return data;
}

// WR1 レジスタの内容を取得
uint8
SCCDevice::GetWR1(const MPSCCChan& chan) const
{
	uint8 data = 0;

	if (chan.wait_enable)
		data |= MPSCC::WR1_WAIT_EN;
	if (chan.wait_func)
		data |= MPSCC::WR1_WAIT_FUNC;
	if (chan.wait_on_rx)
		data |= MPSCC::WR1_WAIT_RX;
	switch (chan.rxint_mode) {
	 case MPSCCChan::RXINT_DISABLE:
		break;
	 case MPSCCChan::RXINT_1STCHAR:
		data |= MPSCC::WR1_RXINT_1STCHAR;
		break;
	 case MPSCCChan::RXINT_ALLCHAR:
		data |= MPSCC::WR1_RXINT_ALLCHAR;
		break;
	 case MPSCCChan::RXINT_SPONLY:
		data |= MPSCC::WR1_RXINT_SPONLY;
		break;
	 default:
		VMPANIC("corrupted rxint_mode=%u", (uint)chan.rxint_mode);
	}
	if (chan.sp_parity)
		data |= MPSCC::WR1_SP_INC_PE;
	if (chan.txint_enable)
		data |= MPSCC::WR1_TXINT_EN;
	if (chan.esint_enable)
		data |= MPSCC::WR1_ESINT_EN;

	return data;
}

// WR9 レジスタの内容を取得
uint8
SCCDevice::GetWR9() const
{
	uint8 data = 0;

	data |= mpscc.wr9_cmd << 6;
	if (mpscc.status_high_low)     data |= MPSCC::WR9_SHSL;
	if (mpscc.master_int_enable)   data |= MPSCC::WR9_MIE;
	if (mpscc.disable_lower_chain) data |= MPSCC::WR9_DLC;
	if (!mpscc.vectored_mode)      data |= MPSCC::WR9_NV;
	if (mpscc.sav_vis)             data |= MPSCC::WR9_VIS;

	return data;
}

// RR0 レジスタの内容を取得
uint8
SCCDevice::GetRR0(const MPSCCChan& chan) const
{
	uint8 data = 0;

	if (chan.break_detected)data |= MPSCC::RR0_BREAK;
	if (chan.tx_underrun)	data |= MPSCC::RR0_TXUNDER;
	if (chan.nCTS)			data |= MPSCC::RR0_nCTS;
	if (chan.nSYNC)			data |= MPSCC::RR0_nSYNC;
	if (chan.nDCD)			data |= MPSCC::RR0_nDCD;
	if (chan.txlen == 0)	data |= MPSCC::RR0_TXEMPTY;
	if (false)				data |= MPSCC::RR0_ZEROCNT;	// XXX TODO
	if (chan.rxlen != 0)	data |= MPSCC::RR0_RXAVAIL;

	return data;
}

// RR3A の内容を取得
uint8
SCCDevice::GetRR3A() const
{
	uint8 data = 0;

	if ((mpscc.chan[0].intpend & INTPEND_RX))
		data |= MPSCC::RR3_RXA;
	if ((mpscc.chan[0].intpend & INTPEND_TX))
		data |= MPSCC::RR3_TXA;
	if ((mpscc.chan[0].intpend & INTPEND_ES))
		data |= MPSCC::RR3_ESA;

	if ((mpscc.chan[1].intpend & INTPEND_RX))
		data |= MPSCC::RR3_RXB;
	if ((mpscc.chan[1].intpend & INTPEND_TX))
		data |= MPSCC::RR3_TXB;
	if ((mpscc.chan[1].intpend & INTPEND_ES))
		data |= MPSCC::RR3_ESB;

	return data;
}

// WR0 への書き込み
void
SCCDevice::WriteWR0(MPSCCChan& chan, uint32 data)
{
	// XXX b7,b6 (CRC Reset Command) は未対応。
	// XXX コマンドが %001、%000 以外の時に PTR(レジスタセレクト) が同時に
	//     指定されたらどうなるか。
	chan.crc_cmd = (data & MPSCC::CR0_CRC) >> 6;;
	chan.cmd     = (data & MPSCC::CR0_CMD) >> 3;

	if (chan.crc_cmd != 0) {
		// CRC は非同期モードでは不要のはず
		putlog(3, "%s CRC Reset %u (NOT IMPLEMENTED)",
			WRName(chan, 0), chan.crc_cmd);
	}

	if (chan.cmd == 0 || chan.cmd == 1) {
		// コマンド 0、1 がレジスタ切り替え
		chan.ptr = data & MPSCC::WR0_PTR;
		putlog(3, "%s <- $%02x (ptr=%u)", WRName(chan, 0), data, chan.ptr);
	} else {
		// それ以外ならコマンド発行
		putlog(2, "%s <- $%02x", CRName(chan, 0), data);

		switch (chan.cmd) {
		 case 2:	// Reset Ext/Status Interrupts
			ResetESIntr(chan);
			break;
		 case 3:	// Send Abort
			SendAbort(chan);
			break;
		 case 4:	// Enable Int on Next Rx Character
			EnableIntrOnNextRx(chan);
			break;
		 case 5:	// Reset Tx Int Pending
			ResetTxIntrPending(chan);
			break;
		 case 6:	// Error Reset
			ErrorReset(chan);
			break;
		 case 7:	// Reset Highest IUS
			ResetHighestIUS(chan);
			break;
		 default:
			VMPANIC("corrupted chan.cmd=%u", chan.cmd);
		}
	}
}

// WR1[AB] への書き込み
void
SCCDevice::WriteWR1(MPSCCChan& chan, uint32 data)
{
	putlog(2, "%s <- $%02x", WRName(chan), data);

	chan.wait_enable  = (data & MPSCC::WR1_WAIT_EN);
	chan.wait_func    = (data & MPSCC::WR1_WAIT_FUNC);
	chan.wait_on_rx   = (data & MPSCC::WR1_WAIT_RX);
	// SCC の RXINT は内部フォーマットと同じ値にしてあるのでそのまま代入可。
	chan.rxint_mode   = (data >> 3) & 3;
	chan.all_or_first = (chan.rxint_mode != 0);
	chan.sp_parity    = (data & MPSCC::WR1_SP_INC_PE);
	chan.txint_enable = (data & MPSCC::WR1_TXINT_EN);
	chan.esint_enable = (data & MPSCC::WR1_ESINT_EN);
}

// WR2 への書き込み
void
SCCDevice::WriteWR2(uint32 data)
{
	// Z8530 の WR2 は A/B 共通でベクタ設定
	putlog(2, "WR2 <- $%02x (Set Vector)", data);
	mpscc.vector = data;
}

void
SCCDevice::WriteWR5(MPSCCChan& chan, uint32 data)
{
	// ~RTS 端子の立ち下がりでマウスに送信要求。
	// ~RTS 端子の立ち下がりは nRTS ビットでは立ち上がりになる。
	bool old_nRTS = chan.nRTS;
	bool new_nRTS = (data & MPSCC::WR5_nRTS);
	bool rts = (old_nRTS == false && new_nRTS);

	inherited::WriteCR5(chan, data);

	if (chan.id == 1 && rts) {
		if (keyboard) {
			keyboard->MouseSendStart();
		}
	}
}

void
SCCDevice::WriteWR9(uint32 data)
{
	putlog(2, "WR9  <- $%02x", data);

	mpscc.wr9_cmd = (data >> 6);
	switch (mpscc.wr9_cmd) {
	 case 0:	// No Reset
		break;
	 case 1:	// Channel Reset B
		putlog(1, "Channel B Reset");
		ResetChannel(mpscc.chan[1]);
		break;
	 case 2:	// Channel Reset A
		putlog(1, "Channel A Reset");
		ResetChannel(mpscc.chan[0]);
		break;
	 case 3:	// Force Hardware Reset
		ResetHard(false);
		break;
	 default:
		VMPANIC("corrupted mpscc.wr9_cmd=%u", mpscc.wr9_cmd);
	}

	// これらはハードウェアリセットと同時に指定されたら、
	// リセット後に処理のはず?
	mpscc.status_high_low		= (data & MPSCC::WR9_SHSL);
	mpscc.master_int_enable		= (data & MPSCC::WR9_MIE);
	mpscc.disable_lower_chain	= (data & MPSCC::WR9_DLC);
	mpscc.vectored_mode			= !(data & MPSCC::WR9_NV);
	mpscc.sav_vis				= (data & MPSCC::WR9_VIS);

	// SHSL と VIS によって vis モードを再設定
	if (mpscc.sav_vis) {
		if (mpscc.status_high_low) {
			mpscc.vis = MPSCC::VIS_456;
		} else {
			mpscc.vis = MPSCC::VIS_321;
		}
	} else {
		mpscc.vis = MPSCC::VIS_FIXED;
	}
}

void
SCCDevice::WriteWR10(MPSCCChan& chan, uint32 data)
{
	putlog(2, "%s <- $%02x (NOT IMPLEMENTED)", WRName(chan), data);
	chan.wr10 = data;
}

void
SCCDevice::WriteWR11(MPSCCChan& chan, uint32 data)
{
	putlog(2, "%s <- $%02x (NOT IMPLEMENTED)", WRName(chan), data);
	chan.wr11 = data;
}

void
SCCDevice::WriteWR12(MPSCCChan& chan, uint32 data)
{
	chan.tc = (chan.tc & 0xff00) | data;
	putlog(2, "%s TC(L)=%04x", WRName(chan), chan.tc);
	SetXC(chan);
}

void
SCCDevice::WriteWR13(MPSCCChan& chan, uint32 data)
{
	chan.tc = (chan.tc & 0x00ff) | (data << 8);
	putlog(2, "%s TC(H)=%04x", WRName(chan), chan.tc);
	SetXC(chan);
}

void
SCCDevice::WriteWR14(MPSCCChan& chan, uint32 data)
{
	putlog(2, "%s <- $%02x (NOT IMPLEMENTED)", WRName(chan), data);
	chan.wr14 = data;
}

void
SCCDevice::WriteWR15(MPSCCChan& chan, uint32 data)
{
	putlog(2, "%s <- $%02x (NOT IMPLEMENTED)", WRName(chan), data);
	chan.wr15 = data;
}

// モニター
void
SCCDevice::MonitorScreen(Monitor *, TextScreen& screen)
{
	uint wr9;
	uint rr3a;
	int y;

	wr9 = GetWR9();
	rr3a = GetRR3A();

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

	y = 32;
	// WR9
	screen.Print(0, y, "WR9 :$%02x", wr9);
	static const char * const wr9_str[] = {
		"", "", "-", "SHSL", "MIE", "DLC", "NV", "VIS"
	};
	MonitorReg(screen, 9, y, wr9, wr9_str);
	static const char * const resetcmd_str[] = {
	//   012345678
		"(NoReset)",
		"(RstChB)",
		"(RstChA)",
		"(HardRst)",
	};
	screen.Puts(9, y, resetcmd_str[wr9 >> 6]);
	y++;

	// RR2B / RR2B
	screen.Print(0, y, "RR2A:$%02x / RR2B:$%02x", mpscc.vector, GetSR2B());
	y++;

	// RR3A
	static const char * const rr3a_str[] = {
		"-", "-", "RxA", "TxA", "ESA", "RxB", "TxB", "ESB"
	};
	screen.Print(0, y, "RR3A:$%02x", rr3a);
	MonitorReg(screen, 9, y, rr3a, rr3a_str);
	y++;
}

// モニター (1チャンネル)
void
SCCDevice::MonitorScreenCh(TextScreen& screen, int ch, int xbase, int ybase)
{
	const MPSCCChan& chan = mpscc.chan[ch];
	uint crc;
	uint cmd;
	uint ptr;
	uint wr0;
	uint wr1;
	uint wr3;
	uint wr4;
	uint wr5;
	uint wr6;
	uint wr7;
	uint wr10;
	uint wr11;
	uint wr14;
	uint wr15;
	uint rr0;
	uint rr1;
	uint rr10;
	uint rxlen;
	uint tc;
	int x;
	int y;

	wr0 = GetCR0(chan);
	crc = chan.crc_cmd;
	cmd = chan.cmd;
	ptr = chan.ptr;
	wr1 = GetWR1(chan);
	wr3 = GetCR3(chan);
	wr4 = GetCR4(chan);
	wr5 = GetCR5(chan);
	wr6 = chan.cr6;
	wr7 = chan.cr7;
	wr10 = chan.wr10;
	wr11 = chan.wr11;
	tc   = chan.tc;
	wr14 = chan.wr14;
	wr15 = chan.wr15;
	rr0 = GetRR0(chan);
	rr1 = chan.sr1;
	rr10 = chan.rr10;
	rxlen = chan.rxlen;

	// 0         1         2         3         4         5         6
	// 0123456789012345678901234567890123456789012345678901234567890123456789
	//          0123 0123 0123 0123 0123 0123 0123 0123
	// Channel A: Serial Console                          DataAddr    $000000
	// WR0: $xx CRC=x     CMD=x          PTR=x            CtrlAddr    $000000
	// WR1: $xx WE   0    WR   RXInt=x   0    TXEn ESEn
	// WR13/WR12: $001f (196000bps)       WR7/WR6:$1234

	x = xbase;
	y = ybase;
	screen.Print(x, y++, "Channel %c: %s", chan.name, chan.desc);
	screen.Print(x, y++, "WR0: $%02x", wr0);
	screen.Print(x, y++, "WR1: $%02x", wr1);
	screen.Print(x, y++, "WR3: $%02x", wr3);
	screen.Print(x, y++, "WR4: $%02x", wr4);
	screen.Print(x, y++, "WR5: $%02x", wr5);
	screen.Print(x, y++, "WR10:$%02x", wr10);
	screen.Print(x, y++, "WR11:$%02x", wr11);
	screen.Print(x, y++, "WR14:$%02x", wr14);
	screen.Print(x, y++, "WR15:$%02x", wr15);
	screen.Print(x, y,   "WR13/WR12:$%04x (%.1fbps)", tc, chan.baudrate);
	screen.Print(x + 35, y++, "WR7/WR6:$%02x%02x", wr7, wr6);
	screen.Print(x, y++, "RR0: $%02x", rr0);
	screen.Print(x, y++, "RR1: $%02x", rr1);
	screen.Print(x, y++, "RR10:$%02x", rr10);
	screen.Print(x, y,   "RXbuf: %u", 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, "---");
	}

	y = ybase + 1;
	x = xbase;
	// WR0
	screen.Print(x + 9, y, "CRC=%u", crc);
	static const char * const cmd_str[] = {
	//   012345678901234
		"Null command",
		"Point High",
		"Reset E/S Int",
		"Send Abort",
		"EnableIntNextCh",
		"Reset TxIntPend",
		"Error Reset",
		"ResetHighestIUS",
	};
	screen.Print(x + 19, y, "CMD=%u(%s)", cmd, cmd_str[cmd]);
	screen.Print(x + 43 - (ptr > 9 ? 1 : 0), y, "PTR=%u", ptr);
	y++;

	// WR1
	static const char * const wr1_str[] = {
		"Wait", "Func", "OnRx", "", "", "SpPE", "TxIE", "ESIE"
	};
	MonitorReg(screen, x + 9, y, wr1, wr1_str);
	static const char * const rxint_str[] = {
	//   012345678
		"RxInt=Dis",
		"RI=1stChr",
		"RI=AllChr",
		"RI=SPOnly",
	};
	screen.Puts(x + 24, y, rxint_str[(wr1 >> 3) & 3]);
	y++;

	// WR3,WR4,WR5
	y = MonitorScreenCR345(screen, x, y, wr3, wr4, wr5);

	// WR10
	screen.Puts(x +  9, y, TA::Disable, (wr10 & 0x80) ? "CRC1" : "CRC0");
	static const char * const de_str[] = {
	//	 012345678
		"Enc=NRZ",
		"Enc=NRZI",
		"Enc=FM(1)",
		"Enc=FM(0)",
	};
	screen.Puts(x + 14, y, de_str[(wr10 >> 5) & 3]);
	screen.Puts(x + 24, y, TA::Disable, (wr10 & 0x10) ? "1" : "0");
	screen.Puts(x + 29, y, TA::Disable, (wr10 & 0x08) ? "1" : "0");
	screen.Puts(x + 34, y, TA::Disable, (wr10 & 0x04) ? "1" : "0");
	screen.Puts(x + 39, y, TA::Disable, (wr10 & 0x02) ? "1" : "0");
	screen.Puts(x + 44, y, TA::Disable, (wr10 & 0x01) ? "1" : "0");
	y++;

	// WR11
	screen.Puts(x + 9, y, TA::OnOff(wr11 & 0x80), "XTAL");
	static const char * const clksrc_str[] = {
	//	 45678
		"!RTxC",
		"!TRxC",
		"BRG",
		"DPLL",
	};
	screen.Print(x + 14, y, "RxC=%s", clksrc_str[(wr11 >> 5) & 3]);
	screen.Print(x + 24, y, "TxC=%s", clksrc_str[(wr11 >> 3) & 3]);
	screen.Puts(x + 34, y, TA::OnOff(wr11 & 0x04), "TOUT");
	static const char * const clkout_str[] = {
	//	 012345678
		"TRxC=DPLL",
		"TRxC=BRG",
		"TRxC=Clk",
		"TRxC=Xtal",
	};
	screen.Puts(x + 39, y, clkout_str[(wr11 & 3)]);
	y++;

	// WR14
	static const char * const wr14_str[] = {
		"", "", "", "LLB", "Echo", "DTRL", "BRGS", "BRGE",
	};
	MonitorReg(screen, x + 9, y, wr14, wr14_str);
	static const char * const dpllcmd_str[] = {
	//	 01234567890123
		"(DPLL nullcmd)",
		"(EntSearchMod)",
		"(ResetMissClk)",
		"(Disable DPLL)",
		"(Set SRC=BRG)",
		"(Set SRC=RTxC)",
		"(Set FM Mode)",
		"(Set NRZIMode)",
	};
	screen.Puts(x + 9, y, dpllcmd_str[(wr14 >> 5)]);
	y++;

	// WR15
	static const char * const wr15_str[] = {
		"BrIE", "TUIE", "CTIE", "SHIE", "DCIE", "-", "ZCIE", "-"
	};
	MonitorReg(screen, x + 9, y, wr15, wr15_str);
	y++;

	// WR12/WR13
	y++;

	// RR0
	static const char * const rr0_str[] = {
		"BrAb", "TxUn", "!CTS", "!SYN", "!DCD", "TxEm", "ZCnt", "RxAv"
	};
	MonitorReg(screen, x + 9, y, rr0, rr0_str);
	y++;

	// RR1
	MonitorScreenSR1(screen, x, y, rr1);
	y++;

	// RR10
	static const char * const rr10_str[] = {
		"1CM", "2CM", "-", "", "-", "-", "", "-"
	};
	MonitorReg(screen, x + 9, y, rr10, rr10_str);
	screen.Puts(x + 24, y, TA::Disable, (rr10 & 0x10) ? "1" : "0");
	screen.Puts(x + 39, y, TA::Disable, (rr10 & 0x02) ? "1" : "0");
	y++;

	// 右列
	y = ybase + 1;
	x = xbase + 51;
	if (chan.dataaddr > 0x01000000) {
		screen.Print(x, y++, "DataAddr: $%08x", chan.dataaddr);
		screen.Print(x, y++, "CtrlAddr: $%08x", chan.ctrladdr);
	} else {
		screen.Print(x, y++, "DataAddr:   $%06x", chan.dataaddr);
		screen.Print(x, y++, "CtrlAddr:   $%06x", chan.ctrladdr);
	}
	y++;
	screen.Print(x, y++, "Param:%13s", GetParamStr(chan).c_str());
	screen.Print(x, y++, "Rx Bits/Frame:   %2u", chan.rxframebits);
	screen.Print(x, y++, "Tx Bits/Frame:   %2u", chan.txframebits);
}

// 内部の送信データクロックの設定
void
SCCDevice::SetXC(MPSCCChan& chan)
{
	chan.xc12_tsec = (2 * (chan.tc + 2)) * pclk_tsec * 12;
	ChangeBaudrate(chan);
}

// 割り込みアクノリッジ
busdata
SCCDevice::InterruptAcknowledge()
{
	return GetSR2B();
}

// ペンディングのうち最も優先度の高い割り込み要因を返す
uint
SCCDevice::GetHighestInt() const
{
	// 上から RxA, TxA, ESA, RxB, TxB, ESB の順 (Z8530 p.2-16)。

	if ((mpscc.chan[0].intpend & INTPEND_SP)) return MPSCC::VS_SPA;
	if ((mpscc.chan[0].intpend & INTPEND_RX)) return MPSCC::VS_RxA;
	if ((mpscc.chan[0].intpend & INTPEND_TX)) return MPSCC::VS_TxA;
	if ((mpscc.chan[0].intpend & INTPEND_ES)) return MPSCC::VS_ESA;
	if ((mpscc.chan[1].intpend & INTPEND_SP)) return MPSCC::VS_SPB;
	if ((mpscc.chan[1].intpend & INTPEND_RX)) return MPSCC::VS_RxB;
	if ((mpscc.chan[1].intpend & INTPEND_TX)) return MPSCC::VS_TxB;
	if ((mpscc.chan[1].intpend & INTPEND_ES)) return MPSCC::VS_ESB;

	return MPSCC::VS_none;
}

void
SCCDevice::Tx(int ch, uint32 data)
{
	switch (ch) {
	 case 0:
		hostcom->Tx(data);
		break;
	 case 1:
		// ChB はマウスだが TxD は接続されていない
		break;
	 default:
		VMPANIC("corrupted ch=%u", ch);
	}
}

// ログ表示用のレジスタ名
/*static*/ const char * const
SCCDevice::wrnames[32] = {
	"WR0A",		"WR0B",
	"WR1A",		"WR1B",
	"WR2",		"WR2",
	"WR3A",		"WR3B",
	"WR4A",		"WR4B",
	"WR5A",		"WR5B",
	"WR6A",		"WR6B",
	"WR7A",		"WR7B",
	"WR8A",		"WR8B",
	"WR9",		"WR9",
	"WR10A",	"WR10B",
	"WR11A",	"WR11B",
	"WR12A",	"WR12B",
	"WR13A",	"WR13B",
	"WR14A",	"WR14B",
	"WR15A",	"WR15B",
};
/*static*/ const char * const
SCCDevice::rrnames[32] = {
	"RR0A",		"RR0B",
	"RR1A",		"RR1B",
	"RR2A",		"RR2B",
	"RR3A",		"RR3B",
	"(RR0A)",	"(RR0B)",
	"(RR1A)",	"(RR1B)",
	"(RR2A)",	"(RR2B)",
	"(RR3A)",	"(RR3B)",
	"RR8A",		"RR8B",
	"(RR13A)",	"(RR13B)",
	"RR10A",	"RR10B",
	"(RR15A)",	"(RR15B)",
	"RR12A",	"RR12B",
	"RR13A",	"RR13B",
	"RR14A",	"RR14B",
	"RR15A",	"RR15B",
};

unix.superglobalmegacorp.com

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