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