--- nono/vm/scc.cpp 2026/04/29 17:04:42 1.1.1.4 +++ nono/vm/scc.cpp 2026/04/29 17:05:50 1.1.1.17 @@ -4,248 +4,1103 @@ // Licensed under nono-license.txt // -#include "scc.h" -#include "mpu680x0.h" - -// IODevice -// | -// v -// upd7201Device (uPD7201 と Z8530 の共通部分) -// | -// +---------+----------+ -// v v -// SIODevice SCCDevice -// (LUNA, uPD7201) (X68030, Z8530) +// +// SCC (Z8530) +// -std::unique_ptr gSCC; +#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() { - logname = "scc"; - devname = "SCC"; - devaddr = baseaddr; - devlen = 0x2000; + 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_ns = 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_ns = 250_nsec; + } - cr.chan[0].desc = "Serial Port"; - cr.chan[1].desc = "Mouse"; + monitor = gMonitorManager->Regist(ID_MONITOR_SCC, this); + monitor->func = ToMonitorCallback(&SCCDevice::MonitorUpdate); + monitor->SetSize(70, 35); } +// デストラクタ SCCDevice::~SCCDevice() { } -// Z8530 は RD と WR を同時に Low にするとリセットがかかる。 -// 回路図からは PEDEC から繋がってるという以上のことは分からないけど -// 普通に考えればハードリセットでリセットされるよな。 +// 初期化 +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() +SCCDevice::ResetHard(bool poweron) { - Reset(); + // 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; + } } -uint64 -SCCDevice::Read(uint32 addr) +// チャンネルリセット +void +SCCDevice::ResetChannel(MPSCCChan& chan) { - gMPU->AddCycle(41); // InsideOut p.135 + uint8 val; - switch (addr) { - case 0: // $E98001 - return ReadCtrl(&cr.chan[1]); + // 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; - case 1: // $E98003 - // マウス非接続は 0x00 ? - putlog(0, "ChB データレジスタ読み込み (未実装)"); - return 0x00; + // RR3 + chan.intpend = 0; - case 2: // $E98005 - return ReadCtrl(&cr.chan[0]); + // RR10 b0-5,7 = %0 + chan.rr10 &= 0x40; - case 3: // $E98007 - putlog(0, "ChA データレジスタ読み込み (未実装)"); - return ReadData(&cr.chan[0]); - } - __unreachable(); + ResetChannelCommon(chan); } -uint64 -SCCDevice::Write(uint32 addr, uint32 data) +busdata +SCCDevice::ReadPort(uint32 offset) { - gMPU->AddCycle(49); // InsideOut p.135 + busdata data; - switch (addr) { + switch (offset) { case 0: // $E98001 - WriteCtrl(&cr.chan[1], data); - return 0; - + data = ReadCtrl(mpscc.chan[1]); + break; case 1: // $E98003 - WriteData(&cr.chan[1], data); - return 0; - + data = ReadData(mpscc.chan[1]); + break; case 2: // $E98005 - WriteCtrl(&cr.chan[0], data); - return 0; - + data = ReadCtrl(mpscc.chan[0]); + break; case 3: // $E98007 - WriteData(&cr.chan[0], data); - return 0; + data = ReadData(mpscc.chan[0]); + break; + default: + VMPANIC("corrupted offset=%u", offset); } - __unreachable(); + + data |= read_wait; + data |= BusData::Size1; + return data; } -uint64 -SCCDevice::Peek(uint32 addr) +busdata +SCCDevice::WritePort(uint32 offset, uint32 data) { - switch (addr) { + switch (offset) { case 0: // $E98001 - return PeekCtrl(&cr.chan[1]); - + WriteCtrl(mpscc.chan[1], data); + break; case 1: // $E98003 - // マウス非接続は 0x00 ? - return 0x00; - + WriteData(mpscc.chan[1], data); + break; case 2: // $E98005 - return PeekCtrl(&cr.chan[0]); + 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 - return PeekData(&cr.chan[0]); + data = PeekData(mpscc.chan[0]); + break; + default: + data = 0xff; + break; } - __unreachable(); + 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(struct SIO::channel *chan) +SCCDevice::ReadCtrl(MPSCCChan& chan) { - return inherited::ReadCtrl(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(struct SIO::channel *chan, uint32 data) +SCCDevice::WriteCtrl(MPSCCChan& chan, uint32 data) { - switch (chan->ptr) { - case 0: // WR0 - chan->cr0 = data; + // WR0 はコマンドかまたは次にアクセスするレジスタを指定する。 + // WR0 以外のレジスタをアクセスすると、次のアクセスは WR0 に戻る。 - switch ((chan->cr0 & SIO::WR0_CMD) >> 3) { - case 0: + if (chan.ptr == 0) { + WriteWR0(chan, data); + } else { + switch (chan.ptr) { case 1: - // コマンド 0、1 がレジスタ切り替え - chan->ptr = chan->cr0 & SIO::WR0_PTR; - putlog(3, "WR0%s <- $%02x (ptr=%d)", chan->name, data, chan->ptr); + 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 2: // Reset Ext/Status Interrupts - putlog(0, "WR0 Reset Ext/Status Interrupts 未実装"); + case 8: + WriteData(chan, data); break; - case 3: // Send Abort - putlog(0, "WR0 Send Abort 未実装"); + case 9: + WriteWR9(data); break; - case 4: // Enable Int on Next Rx Character - putlog(0, "WR0 Enable Int on Next Rx Character 未実装"); + case 10: + WriteWR10(chan, data); break; - case 5: // Reset Tx Int Pending - putlog(0, "WR0 Reset Tx Int Pending 未実装"); + case 11: + WriteWR11(chan, data); break; - case 6: // Error Reset - putlog(0, "WR0 Error Reset 未実装"); + case 12: + WriteWR12(chan, data); break; - case 7: // Reset Highest IUS - putlog(0, "WR0 Reset Highest IUS"); + 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); } - break; - - case 2: // WR2 - // Z8530 の WR2 は A/B 共通でベクタ設定 - putlog(2, "WR2 <- $%02x ベクタ設定", data); - cr.vector = data; // アクセス後は WR0 へ戻す - chan->ptr = 0; - break; + chan.ptr = 0; + } +} - case 1: // WR1 - case 3: +// 制御ポートの 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: - inherited::WriteCtrl(chan, data); + if (chan.id == 0) { // RR3A + data = GetRR3A(); + } else { // RR3B + data = 0; + } break; - case 8: // WR8 - WriteData(chan, data); - // アクセス後は WR0 へ戻す - chan->ptr = 0; + 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; - case 9: // WR9 - putlog(2, "WR%d%s <- $%02x", chan->ptr, chan->name, data); - switch ((data & 0xc0) >> 6) { - case 0: // No Reset + 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 1: // Channel Reset A - ResetChannel(&cr.chan[0]); + case 4: // Enable Int on Next Rx Character + EnableIntrOnNextRx(chan); + break; + case 5: // Reset Tx Int Pending + ResetTxIntrPending(chan); break; - case 2: // Channel Reset B - ResetChannel(&cr.chan[1]); + case 6: // Error Reset + ErrorReset(chan); break; - case 3: // Force Hardware Reset - Reset(); + case 7: // Reset Highest IUS + ResetHighestIUS(chan); break; default: - __unreachable(); + VMPANIC("corrupted chan.cmd=%u", chan.cmd); } - // アクセス後は WR0 へ戻す - chan->ptr = 0; - break; + } +} - case 11: // WR11 - putlog(2, "WR%d%s <- $%02x (未実装)", chan->ptr, chan->name, data); - chan->wr11 = data; - // アクセス後は WR0 へ戻す - chan->ptr = 0; - break; +// WR1[AB] への書き込み +void +SCCDevice::WriteWR1(MPSCCChan& chan, uint32 data) +{ + putlog(2, "%s <- $%02x", WRName(chan), data); - case 12: // WR12 - chan->tc = (chan->tc & 0xff00) | data; - putlog(2, "WR%d%s TC(L)=%04x", chan->ptr, chan->name, chan->tc); - // アクセス後は WR0 へ戻す - chan->ptr = 0; - break; + 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); +} - case 13: // WR13 - chan->tc = (chan->tc & 0x00ff) | (data << 8); - putlog(2, "WR%d%s TC(H)=%04x", chan->ptr, chan->name, chan->tc); - // アクセス後は WR0 へ戻す - chan->ptr = 0; - break; +// WR2 への書き込み +void +SCCDevice::WriteWR2(uint32 data) +{ + // Z8530 の WR2 は A/B 共通でベクタ設定 + putlog(2, "WR2 <- $%02x (Set Vector)", data); + mpscc.vector = data; +} - case 14: // WR14 - putlog(2, "WR%d%s <- $%02x (未実装)", chan->ptr, chan->name, data); - chan->wr14 = data; - // アクセス後は WR0 へ戻す - chan->ptr = 0; - break; +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(); + } + } +} - case 15: // WR15 - putlog(2, "WR%d%s <- $%02x (未実装)", chan->ptr, chan->name, data); - chan->wr15 = data; - // アクセス後は WR0 へ戻す - chan->ptr = 0; - break; +void +SCCDevice::WriteWR9(uint32 data) +{ + putlog(2, "WR9 <- $%02x", data); - default: - PANIC("未実装レジスタ書き込み WR%d%s <- $%02x", - chan->ptr, chan->name, 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::Interrupt() +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) { - putlog(0, "割り込み(未実装)"); - gMPU680x0->Interrupt(this, 5, 0x50/*XXX*/); + 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::MonitorUpdate(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; + MonitorUpdateCh(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::MonitorUpdateCh(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 = MonitorUpdateCR345(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 + MonitorUpdateSR1(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_ns = (2 * (chan.tc + 2)) * pclk_ns * 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", +};