--- nono/vm/scc.cpp 2026/04/29 17:04:28 1.1 +++ nono/vm/scc.cpp 2026/04/29 17:05:06 1.1.1.9 @@ -1,9 +1,13 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // #include "scc.h" +#include "bitops.h" +#include "interrupt.h" +#include "keyboard.h" #include "mpu.h" // IODevice @@ -16,12 +20,22 @@ // SIODevice SCCDevice // (LUNA, uPD7201) (X68030, Z8530) +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() @@ -31,130 +45,107 @@ SCCDevice::~SCCDevice() // Z8530 は RD と WR を同時に Low にするとリセットがかかる。 // 回路図からは PEDEC から繋がってるという以上のことは分からないけど // 普通に考えればハードリセットでリセットされるよな。 -void -SCCDevice::ResetHard() -{ - Reset(); -} uint64 -SCCDevice::Read8(uint32 addr) +SCCDevice::Read(uint32 offset) { - uint32 an = addr - devaddr; + gMPU->AddCycle(41); // InsideOut p.135 - switch (an) { - case 1: // $E98001 + switch (offset) { + case 0: // $E98001 return ReadCtrl(&cr.chan[1]); - case 3: // $E98003 - // マウス非接続は 0x00 ? - putlog(0, "ChB データレジスタ読み込み (未実装)"); - return 0x00; + case 1: // $E98003 + return ReadData(&cr.chan[1]); - case 5: // $E98005 + case 2: // $E98005 return ReadCtrl(&cr.chan[0]); - case 7: // $E98007 + case 3: // $E98007 putlog(0, "ChA データレジスタ読み込み (未実装)"); - return ReadData(0); - - default: - break; + return ReadData(&cr.chan[0]); } - putlog(0, "未実装レジスタ読み込み"); - return (uint64)-1; -} - -uint64 -SCCDevice::Read16(uint32 addr) -{ - return 0xff00 | Read8(addr + 1); + __unreachable(); } uint64 -SCCDevice::Write8(uint32 addr, uint32 data) +SCCDevice::Write(uint32 offset, uint32 data) { - uint32 an = addr - devaddr; + gMPU->AddCycle(49); // InsideOut p.135 - switch (an) { - case 1: // $E98001 + switch (offset) { + case 0: // $E98001 WriteCtrl(&cr.chan[1], data); return 0; - case 3: // $E98003 + case 1: // $E98003 WriteData(&cr.chan[1], data); return 0; - case 5: // $E98005 + case 2: // $E98005 WriteCtrl(&cr.chan[0], data); return 0; - case 7: // $E98007 + case 3: // $E98007 WriteData(&cr.chan[0], data); return 0; - - default: - break; } - putlog(0, "未実装レジスタ書き込み"); - return (uint64)-1; + __unreachable(); } uint64 -SCCDevice::Write16(uint32 addr, uint32 data) +SCCDevice::Peek(uint32 offset) { - return Write8(addr + 1, data); + switch (offset) { + case 0: // $E98001 + return PeekCtrl(&cr.chan[1]); + + case 1: // $E98003 + return PeekData(&cr.chan[1]); + + case 2: // $E98005 + return PeekCtrl(&cr.chan[0]); + + case 3: // $E98007 + return PeekData(&cr.chan[0]); + } + __unreachable(); } -uint64 -SCCDevice::Peek8(uint32 addr) +// CR(WR) レジスタ名を返す。Z8530 では WRn のほうが通りがいい。 +const char * +SCCDevice::CRName(const SIO::channel *chan, int ptr_) const { - // XXX - return 0xff; + 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 @@ -181,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; @@ -192,7 +183,7 @@ SCCDevice::WriteCtrl(struct SIO::channel ResetChannel(&cr.chan[1]); break; case 3: // Force Hardware Reset - Reset(); + ResetHard(); break; default: __unreachable(); @@ -201,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; @@ -210,43 +208,167 @@ 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(); + } +} + // 割り込みを上げる void SCCDevice::Interrupt() { putlog(0, "割り込み(未実装)"); - gMPU->Interrupt(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", +};