--- nono/vm/scc.cpp 2026/04/29 17:04:42 1.1.1.4 +++ nono/vm/scc.cpp 2026/04/29 17:05:06 1.1.1.9 @@ -5,7 +5,10 @@ // #include "scc.h" -#include "mpu680x0.h" +#include "bitops.h" +#include "interrupt.h" +#include "keyboard.h" +#include "mpu.h" // IODevice // | @@ -20,14 +23,19 @@ std::unique_ptr gSCC; SCCDevice::SCCDevice() + : inherited("SCC") { - logname = "scc"; - devname = "SCC"; devaddr = baseaddr; devlen = 0x2000; cr.chan[0].desc = "Serial Port"; cr.chan[1].desc = "Mouse"; + + // X680x0 では SCC と呼ぶほうが通りがいい。 + for (int ch = 0; ch < txevent.size(); ch++) { + txevent[ch].SetName(string_format("SCC Ch%s TX", cr.chan[ch].name)); + } + monitor.Regist(ID_MONITOR_SCC); } SCCDevice::~SCCDevice() @@ -37,25 +45,18 @@ SCCDevice::~SCCDevice() // Z8530 は RD と WR を同時に Low にするとリセットがかかる。 // 回路図からは PEDEC から繋がってるという以上のことは分からないけど // 普通に考えればハードリセットでリセットされるよな。 -void -SCCDevice::ResetHard() -{ - Reset(); -} uint64 -SCCDevice::Read(uint32 addr) +SCCDevice::Read(uint32 offset) { gMPU->AddCycle(41); // InsideOut p.135 - switch (addr) { + switch (offset) { case 0: // $E98001 return ReadCtrl(&cr.chan[1]); case 1: // $E98003 - // マウス非接続は 0x00 ? - putlog(0, "ChB データレジスタ読み込み (未実装)"); - return 0x00; + return ReadData(&cr.chan[1]); case 2: // $E98005 return ReadCtrl(&cr.chan[0]); @@ -68,11 +69,11 @@ SCCDevice::Read(uint32 addr) } uint64 -SCCDevice::Write(uint32 addr, uint32 data) +SCCDevice::Write(uint32 offset, uint32 data) { gMPU->AddCycle(49); // InsideOut p.135 - switch (addr) { + switch (offset) { case 0: // $E98001 WriteCtrl(&cr.chan[1], data); return 0; @@ -93,15 +94,14 @@ SCCDevice::Write(uint32 addr, uint32 dat } uint64 -SCCDevice::Peek(uint32 addr) +SCCDevice::Peek(uint32 offset) { - switch (addr) { + switch (offset) { case 0: // $E98001 return PeekCtrl(&cr.chan[1]); case 1: // $E98003 - // マウス非接続は 0x00 ? - return 0x00; + return PeekData(&cr.chan[1]); case 2: // $E98005 return PeekCtrl(&cr.chan[0]); @@ -112,48 +112,40 @@ SCCDevice::Peek(uint32 addr) __unreachable(); } +// CR(WR) レジスタ名を返す。Z8530 では WRn のほうが通りがいい。 +const char * +SCCDevice::CRName(const SIO::channel *chan, int ptr_) const +{ + assert(ptr_ < 16); + int idx = ptr_ * 2 + chan->id; + assert(idx < countof(wrnames)); + return wrnames[idx]; +} + +// SR(RR) レジスタ名を返す。Z8530 では RRn のほうが通りがいい。 +const char * +SCCDevice::SRName(const SIO::channel *chan, int ptr_) const +{ + assert(ptr_ < 16); + int idx = ptr_ * 2 + chan->id; + assert(idx < countof(rrnames)); + return rrnames[idx]; +} + // Z8530 制御ポート読み込み uint8 -SCCDevice::ReadCtrl(struct SIO::channel *chan) +SCCDevice::ReadCtrl(SIO::channel *chan) { return inherited::ReadCtrl(chan); } // Z8530 制御ポート書き込み void -SCCDevice::WriteCtrl(struct SIO::channel *chan, uint32 data) +SCCDevice::WriteCtrl(SIO::channel *chan, uint32 data) { switch (chan->ptr) { case 0: // WR0 - chan->cr0 = data; - - switch ((chan->cr0 & SIO::WR0_CMD) >> 3) { - case 0: - case 1: - // コマンド 0、1 がレジスタ切り替え - chan->ptr = chan->cr0 & SIO::WR0_PTR; - putlog(3, "WR0%s <- $%02x (ptr=%d)", chan->name, data, chan->ptr); - break; - - case 2: // Reset Ext/Status Interrupts - putlog(0, "WR0 Reset Ext/Status Interrupts 未実装"); - break; - case 3: // Send Abort - putlog(0, "WR0 Send Abort 未実装"); - break; - case 4: // Enable Int on Next Rx Character - putlog(0, "WR0 Enable Int on Next Rx Character 未実装"); - break; - case 5: // Reset Tx Int Pending - putlog(0, "WR0 Reset Tx Int Pending 未実装"); - break; - case 6: // Error Reset - putlog(0, "WR0 Error Reset 未実装"); - break; - case 7: // Reset Highest IUS - putlog(0, "WR0 Reset Highest IUS"); - break; - } + WriteCR0(chan, data); break; case 2: // WR2 @@ -180,7 +172,7 @@ SCCDevice::WriteCtrl(struct SIO::channel break; case 9: // WR9 - putlog(2, "WR%d%s <- $%02x", chan->ptr, chan->name, data); + putlog(2, "%s <- $%02x", WRName(chan), data); switch ((data & 0xc0) >> 6) { case 0: // No Reset break; @@ -191,7 +183,7 @@ SCCDevice::WriteCtrl(struct SIO::channel ResetChannel(&cr.chan[1]); break; case 3: // Force Hardware Reset - Reset(); + ResetHard(); break; default: __unreachable(); @@ -200,8 +192,15 @@ SCCDevice::WriteCtrl(struct SIO::channel chan->ptr = 0; break; + case 10: // WR10 + putlog(2, "%s <- $%02x (未実装)", WRName(chan), data); + chan->wr10 = data; + // アクセス後は WR0 へ戻す + chan->ptr = 0; + break; + case 11: // WR11 - putlog(2, "WR%d%s <- $%02x (未実装)", chan->ptr, chan->name, data); + putlog(2, "%s <- $%02x (未実装)", WRName(chan), data); chan->wr11 = data; // アクセス後は WR0 へ戻す chan->ptr = 0; @@ -209,36 +208,105 @@ SCCDevice::WriteCtrl(struct SIO::channel case 12: // WR12 chan->tc = (chan->tc & 0xff00) | data; - putlog(2, "WR%d%s TC(L)=%04x", chan->ptr, chan->name, chan->tc); + putlog(2, "%s TC(L)=%04x", WRName(chan), chan->tc); + SetTXC(chan); // アクセス後は WR0 へ戻す chan->ptr = 0; break; case 13: // WR13 chan->tc = (chan->tc & 0x00ff) | (data << 8); - putlog(2, "WR%d%s TC(H)=%04x", chan->ptr, chan->name, chan->tc); + putlog(2, "%s TC(H)=%04x", WRName(chan), chan->tc); + SetTXC(chan); // アクセス後は WR0 へ戻す chan->ptr = 0; break; case 14: // WR14 - putlog(2, "WR%d%s <- $%02x (未実装)", chan->ptr, chan->name, data); + putlog(2, "%s <- $%02x (未実装)", WRName(chan), data); chan->wr14 = data; // アクセス後は WR0 へ戻す chan->ptr = 0; break; case 15: // WR15 - putlog(2, "WR%d%s <- $%02x (未実装)", chan->ptr, chan->name, data); + putlog(2, "%s <- $%02x (未実装)", WRName(chan), data); chan->wr15 = data; // アクセス後は WR0 へ戻す chan->ptr = 0; break; default: - PANIC("未実装レジスタ書き込み WR%d%s <- $%02x", - chan->ptr, chan->name, data); + PANIC("未実装レジスタ書き込み %s <- $%02x", WRName(chan), data); + break; + } +} + +// 内部の送信データクロックの設定 +void +SCCDevice::SetTXC(SIO::channel *chan) +{ + // 5MHz = 200nsec + // 12bit = 2.4usec + txc12_ns = (2 * (chan->tc + 2)) * 2.4_usec; +} + +// CR0(WR0) 書き込み。 +// uPD7201 の CR0 とは微妙に互換性がないので似てるけど違うので注意。 +void +SCCDevice::WriteCR0(SIO::channel *chan, uint32 data) +{ + uint cmd; + + // XXX b7,b6 (CRC Reset Command) は未対応。 + // XXX コマンドが %001、%000 以外の時に PTR(レジスタセレクト) が同時に + // 指定されたらどうなるか。 + chan->cr0 = data; + + if ((chan->cr0 & SIO::WR0_CRC)) { + putlog(0, "%s CRC リセット未実装", WRName(chan, 0)); + } + + cmd = (chan->cr0 & SIO::WR0_CMD) >> 3; + switch (cmd) { + case 0: + case 1: + // コマンド 0、1 がレジスタ切り替え + chan->ptr = chan->cr0 & SIO::WR0_PTR; + putlog(3, "%s <- $%02x (ptr=%d)", WRName(chan, 0), data, chan->ptr); + break; + 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: + __unreachable(); + } +} + +void +SCCDevice::WriteCR5(SIO::channel *chan, uint32 data) +{ + bool rts = bit_falling(chan->cr5, data, SIO::CR5_RTS); + + inherited::WriteCR5(chan, data); + + if (chan->id == 1 && rts) { + gKeyboard->MouseSendStart(); } } @@ -247,5 +315,60 @@ void SCCDevice::Interrupt() { putlog(0, "割り込み(未実装)"); - gMPU680x0->Interrupt(this, 5, 0x50/*XXX*/); +// gInterrupt->Raise(this); +} + +// 割り込みアクノリッジ +int +SCCDevice::InterruptAcknowledge() +{ + return 0x50; /* XXX 実際は WR2 とか要因で変わる */ } + +void +SCCDevice::Tx(int ch, uint32 data) +{ + // TODO: シリアル送信 + // ChA はシリアルポート + // ChB はマウスだが TxD は接続されていない +} + +// ログ表示用のレジスタ名 +/*static*/ const char * const +SCCDevice::wrnames[32] = { + "WR0A", "WR0B", + "WR1A", "WR1B", + "WR2A", "WR2B", + "WR3A", "WR3B", + "WR4A", "WR4B", + "WR5A", "WR5B", + "WR6A", "WR6B", + "WR7A", "WR7B", + "WR8A", "WR8B", + "WR9A", "WR9B", + "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", + "RR4A?", "RR4A?", + "RR5A?", "RR5A?", + "RR6A?", "RR6A?", + "RR7A?", "RR7A?", + "RR8A", "RR8B", + "RR9A?", "RR9A?", + "RR10A", "RR10B", + "RR11A?", "RR11A?", + "RR12A", "RR12B", + "RR13A", "RR13B", + "RR14A?", "RR14A?", + "RR15A", "RR15B", +};