--- nono/vm/sio.cpp 2026/04/29 17:04:36 1.1.1.4 +++ nono/vm/sio.cpp 2026/04/29 17:05:37 1.1.1.15 @@ -4,113 +4,854 @@ // Licensed under nono-license.txt // -#include "sio.h" -#include "mpu.h" - -// IODevice -// | -// v -// upd7201Device (uPD7201 と Z8530 の共通部分) -// | -// +---------+----------+ -// v v -// SIODevice SCCDevice -// (LUNA, uPD7201) (X68030, Z8530) +// +// SIO (uPD7201) +// -std::unique_ptr gSIO; +#define MPSCC_SIO_LOG_HACK +#include "sio.h" +#include "hostcom.h" +#include "keyboard.h" +#include "mainapp.h" +// コンストラクタ SIODevice::SIODevice() + : inherited() { - logname = "sio"; - devname = "SIO"; - devaddr = 0x51000000; + SetName("SIO"); + ClearAlias(); + AddAlias("SIO"); + AddAlias("MPSCC"); + + mpscc.chan[0].desc = "Serial Console"; + mpscc.chan[1].desc = "Keyboard"; + + // ポートアドレス。 + // デバイス自身は自分が配置されるアドレスを知らなくてよい構造になって + // いるが、直接アクセスしたりする場合などに毎回アドレスを確認するのが + // 面倒なので(全機種で向きと間隔が異なる)、モニタで表示されてほしい。 + if (gMainApp.IsLUNA1()) { + mpscc.chan[0].dataaddr = 0x51000000; + mpscc.chan[0].ctrladdr = 0x51000002; + mpscc.chan[1].dataaddr = 0x51000004; + mpscc.chan[1].ctrladdr = 0x51000006; + } else { + mpscc.chan[0].dataaddr = 0x51000000; + mpscc.chan[0].ctrladdr = 0x51000004; + mpscc.chan[1].dataaddr = 0x51000008; + mpscc.chan[1].ctrladdr = 0x5100000c; + } - cr.chan[0].desc = "Serial Console"; - cr.chan[1].desc = "Keyboard"; + // 153.6kHz = 6.510416...usec / bit; + // 12bit = 78.125usec + mpscc.chan[0].xc12_ns = 78.125_usec; + mpscc.chan[1].xc12_ns = 78.125_usec; + + // LUNA では SIO と呼ぶほうが通りがいい。 + // イベントの登録は親クラスで行ってある。 + for (int ch = 0; ch < txevent.size(); ch++) { + auto& chan = mpscc.chan[ch]; + txevent[ch].SetName(string_format("SIO Ch%c TX", chan.name)); + rxevent[ch].SetName(string_format("SIO Ch%c RX", chan.name)); + } + + monitor = gMonitorManager->Regist(ID_MONITOR_SIO, this); + monitor->func = ToMonitorCallback(&SIODevice::MonitorUpdate); + monitor->SetSize(70, 24); } +// デストラクタ SIODevice::~SIODevice() { } -// 本体リセットスイッチによるリセット +// 初期化 +bool +SIODevice::Init() +{ + if (inherited::Init() == false) { + return false; + } + + keyboard = GetKeyboard(); + + return true; +} + +// リセット void -SIODevice::ResetHard() +SIODevice::ResetHard(bool poweron) { - // チップリセット - Reset(); + // master_int_enable は uPD7201 にはなく常に true と等価 + mpscc.master_int_enable = true; + + // CR2 b0-5,7 = %0 + mpscc.intdma_mode = 0; + mpscc.priority_select = false; + mpscc.int_mode = 0; + mpscc.vectored_mode = false; + mpscc.syncb = false; + + for (auto& chan : mpscc.chan) { + ResetChannel(chan); + } + + // ログ省略ステートをリセット + sr0_logphase = 0; } -uint64 -SIODevice::Read(uint32 addr) +// チャンネルリセット +void +SIODevice::ResetChannel(MPSCCChan& chan) { - uint32 data; + // CR0 b0-2 = %0 + chan.ptr = 0; + + // CR1 b0,1,3,4,7 = %0 + chan.esint_enable = false; + chan.txint_enable = false; + chan.rxint_mode = 0; + chan.wait_enable = false; + + // CR3 b0-7 = %0 + SetCR3(chan, 0x00); + + // CR5 b1,2,3,4,7 = %0 + uint8 val = GetCR5(chan); + SetCR5(chan, val & 0x61); + + // SR0 b0,1 = %0、b2,6 = %1 + chan.rxlen = 0; + chan.intpend = 0; + chan.txlen = 0; + chan.tx_underrun = true; + + // SR1 b4-7 = %0 + chan.sr1 &= ~(MPSCC::SR1_ENDOFFRAME | + MPSCC::SR1_FRAMEERROR | + MPSCC::SR1_RXOVERRUN | + MPSCC::SR1_PARITYERROR); - switch (addr) { + ResetChannelCommon(chan); +} + +busdata +SIODevice::ReadPort(uint32 offset) +{ + busdata data; + + switch (offset) { case 0: - data = ReadData(&cr.chan[0]); + data = ReadData(mpscc.chan[0]); break; case 1: - data = ReadCtrl(&cr.chan[0]); + data = ReadCtrl(mpscc.chan[0]); break; case 2: - data = ReadData(&cr.chan[1]); + data = ReadData(mpscc.chan[1]); break; case 3: - data = ReadCtrl(&cr.chan[1]); + data = ReadCtrl(mpscc.chan[1]); break; default: - __unreachable(); + VMPANIC("corrupted offset=%u", offset); } + // XXX wait? + data |= BusData::Size1; return data; } -uint64 -SIODevice::Write(uint32 addr, uint32 data) +busdata +SIODevice::WritePort(uint32 offset, uint32 data) { - switch (addr) { + switch (offset) { case 0: - WriteData(&cr.chan[0], data); - return 0; - + WriteData(mpscc.chan[0], data); + break; case 1: - WriteCtrl(&cr.chan[0], data); - return 0; - + WriteCtrl(mpscc.chan[0], data); + break; case 2: - WriteData(&cr.chan[1], data); - return 0; - + WriteData(mpscc.chan[1], data); + break; case 3: - WriteCtrl(&cr.chan[1], data); - return 0; + WriteCtrl(mpscc.chan[1], data); + break; + default: + VMPANIC("corrupted offset=%u", offset); } - __unreachable(); + // XXX wait? + busdata r = BusData::Size1; + return r; } -uint64 -SIODevice::Peek(uint32 addr) +busdata +SIODevice::PeekPort(uint32 offset) { - switch (addr) { - case 0: - return PeekData(&cr.chan[0]); + uint64 data; + switch (offset) { + case 0: + data = PeekData(mpscc.chan[0]); + break; case 1: - return PeekCtrl(&cr.chan[0]); + data = PeekCtrl(mpscc.chan[0]); + break; + case 2: + data = PeekData(mpscc.chan[1]); + break; + case 3: + data = PeekCtrl(mpscc.chan[1]); + break; + default: + data = 0xff; + break; + } + return data; +} + +// 書き込みレジスタ名を返す +const char * +SIODevice::CRName(const MPSCCChan& chan, uint ptr_) const +{ + assert(ptr_ < 8); + uint idx = ptr_ * 2 + chan.id; + assert(idx < countof(crnames)); + return crnames[idx]; +} + +// 読み込みレジスタ名を返す +const char * +SIODevice::SRName(const MPSCCChan& chan, uint ptr_) const +{ + assert(ptr_ < 8); + uint idx = ptr_ * 2 + chan.id; + assert(idx < countof(srnames)); + return srnames[idx]; +} + +// 制御ポートの読み込み +uint8 +SIODevice::ReadCtrl(MPSCCChan& chan) +{ + uint8 data; + + switch (chan.ptr) { + case 0: // SR0 + data = GetSR0(chan); + break; + + case 1: // SR1 + data = chan.sr1; + break; case 2: - return PeekData(&cr.chan[1]); + // SR2B はベクタの読み出し + if (chan.id == 1) { + data = GetSR2B(); + break; + } + + // SR2A はない + // XXX どうなる? + FALLTHROUGH; + + default: + // XXX どうなる? + putlog(0, "Read %s (NOT IMPLEMENTED)", SRName(chan)); + // SR0 へ戻る? + chan.ptr = 0; + return 0xff; + } + + // PROM が SR0 を高速でポーリングするのでログを細工する。 + // + // SR0 SRn + // loglevel<=1 非表示 非表示 + // loglevel 2 * 常に表示 + // loglevel 3 常に表示 常に表示 + // + // *: 同じ値の読み込みログが連続したら省略。 + if (__predict_true(loglevel <= 1)) { + // nop + } else if (__predict_false(loglevel >= 3 || chan.ptr > 0)) { + putlog(2, "%s -> $%02x", SRName(chan), data); + } else { + // ここは loglevel==2 && SR0 + + switch (sr0_logphase) { + case 0: // 表示する + first: + sr0_logphase = 1; + sr0_lastread = data; + disp: + putlogn("%s -> $%02x", SRName(chan, 0), data); + break; + case 1: // 連続(1回目) + if (__predict_true(data == sr0_lastread)) { + sr0_logphase++; + goto disp; + } else { + goto first; + } + break; + case 2: // 連続(2回目) + if (__predict_true(data == sr0_lastread)) { + sr0_logphase++; + putlogn("%s -> $%02x (omit the following)", + SRName(chan, 0), data); + } else { + goto first; + } + break; + default: // 連続(3回目以降) + if (__predict_true(data == sr0_lastread)) { + // 連続したので表示省略 + } else { + goto first; + } + break; + } + } + // アクセス後は SR0 へ戻す + chan.ptr = 0; + + return data; +} + +// 制御ポートの書き込み +void +SIODevice::WriteCtrl(MPSCCChan& chan, uint32 data) +{ + // CR0 はコマンドかまたは次にアクセスするレジスタを指定する。 + // CR0 以外のレジスタをアクセスすると、次のアクセスは CR0 に戻る。 + + if (chan.ptr == 0) { + WriteCR0(chan, data); + } else { + switch (chan.ptr) { + case 1: + WriteCR1(chan, data); + break; + case 2: + WriteCR2(chan, data); + break; + case 3: + WriteCR3(chan, data); + break; + case 4: + WriteCR4(chan, data); + break; + case 5: + WriteCR5(chan, data); + break; + case 6: + WriteCR6(chan, data); + break; + case 7: + WriteCR7(chan, data); + break; + default: + VMPANIC("corrupted chan.ptr=%u", chan.ptr); + } + // アクセス後は CR0 へ戻す + chan.ptr = 0; + } +} + +// 制御ポートの Peek +uint8 +SIODevice::PeekCtrl(const MPSCCChan& chan) const +{ + switch (chan.ptr) { + case 0: // SR0 + return GetSR0(chan); + + case 1: // SR1 + return chan.sr1; + + case 2: // SR2 + // SR2A はない。SR2B は割り込み要因込みのベクタ番号。 + if (chan.id == 1) { + return GetSR2B(); + } + FALLTHROUGH; + + default: + // 未調査 + return 0xff; + } +} + +// CR1 レジスタの内容を取得 +uint8 +SIODevice::GetCR1(const MPSCCChan& chan) const +{ + uint8 data = 0; + + if (chan.wait_enable) + data |= MPSCC::CR1_WAIT_EN; + if (chan.wait_on_rx) + data |= MPSCC::CR1_WAIT_RX; + // 内部フォーマットから元のビットを求める + switch (chan.rxint_mode) { + case MPSCCChan::RXINT_DISABLE: + break; + case MPSCCChan::RXINT_1STCHAR: + data |= MPSCC::CR1_RXINT_1STCHAR; + break; + case MPSCCChan::RXINT_ALLCHAR: + if (chan.sp_parity) { + data |= MPSCC::CR1_RXINT_ALLCHAR; + } else { + data |= MPSCC::CR1_RXINT_ALL_BUT_PE; + } + break; + default: + assert(false); + } + if (chan.txint_enable) + data |= MPSCC::CR1_TXINT_EN; + if (chan.esint_enable) + data |= MPSCC::CR1_ESINT_EN; + + return data; +} +// CR2A レジスタの内容を取得 +uint8 +SIODevice::GetCR2A() const +{ + uint8 data = 0; + + data |= mpscc.syncb ? MPSCC::CR2_SYNCB : 0; + data |= mpscc.vectored_mode ? MPSCC::CR2_VM : 0; + data |= mpscc.int_mode << 3; + data |= mpscc.priority_select ? MPSCC::CR2_PRIOSEL : 0; + data |= mpscc.intdma_mode; + + return data; +} + +// CR2B レジスタの内容を取得 +inline uint8 +SIODevice::GetCR2B() const +{ + return mpscc.vector; +} + +// SR0 レジスタの内容を取得 +uint8 +SIODevice::GetSR0(const MPSCCChan& chan) const +{ + uint8 data = 0; + + if (chan.break_detected)data |= MPSCC::SR0_BREAK; + if (chan.tx_underrun) data |= MPSCC::SR0_TXUNDER; + if (chan.nCTS) data |= MPSCC::SR0_nCTS; + if (chan.nSYNC) data |= MPSCC::SR0_nSYNC; + if (chan.nDCD) data |= MPSCC::SR0_nDCD; + if (chan.txlen == 0) data |= MPSCC::SR0_TXEMPTY; + if (chan.intpend != 0) data |= MPSCC::SR0_INTPEND; + if (chan.rxlen != 0) data |= MPSCC::SR0_RXAVAIL; + + return data; +} + +// CR0 への書き込み +void +SIODevice::WriteCR0(MPSCCChan& chan, uint32 data) +{ + // XXX b7,b6 は未対応。 + // b5,b4,b3 がコマンド。 + // b2,b1,b0 が次にアクセスするレジスタ(chan->ptr)の切り替え。 + chan.crc_cmd = (data & MPSCC::CR0_CRC) >> 6; + chan.cmd = (data & MPSCC::CR0_CMD) >> 3; + chan.ptr = data & MPSCC::CR0_PTR; + + if (chan.crc_cmd != 0) { + putlog(0, "%s crc_cmd %u (NOT IMPLEMENTED)", + CRName(chan, 0), chan.crc_cmd); + } + + if (chan.cmd == 0) { + // コマンド0 ならレジスタ切り替え + putlog(3, "%s <- $%02x (ptr=%u)", CRName(chan, 0), data, chan.ptr); + } else { + // 0 以外ならコマンド発行 + putlog(2, "%s <- $%02x", CRName(chan, 0), data); + } + switch (chan.cmd) { + case 0: // No operation + break; + case 1: // Send Abort + SendAbort(chan); + break; + case 2: // Reset External/Status Interrupt + ResetESIntr(chan); + break; + case 3: // Channel Reset + putlog(1, "Channel %c Reset", chan.name); + ResetChannel(chan); + break; + case 4: // Enable Interrupt On Next Rx Character + EnableIntrOnNextRx(chan); + break; + case 5: // Reset Transmitter Interrupt/DMA Pending + ResetTxIntrPending(chan); + break; + case 6: // Error Reset + ErrorReset(chan); + break; + case 7: // End of Interrupt + // uPD7201 ではチャンネル A のみ有効。 + // (チャンネル B でどうなるかは書いてない) + if (chan.id == 0) { + ResetHighestIUS(chan); + } else { + putlog(0, "%s: Command 'End of Interrupt' undefined", + CRName(chan, 0)); + } + break; + default: + VMPANIC("corrupted chan.cmd=%u", chan.cmd); + } +} + +// CR1 への書き込み +void +SIODevice::WriteCR1(MPSCCChan& chan, uint32 data) +{ + putlog(2, "%s <- $%02x", CRName(chan), data); + + chan.wait_enable = (data & MPSCC::CR1_WAIT_EN); + chan.wait_on_rx = (data & MPSCC::CR1_WAIT_RX); + // RXINT は内部フォーマットで覚えておく。 + // all_or_first の初期値もここでセット。 + // o RXINT_DISABLE なら (使わないけど一応) false にしとく。 + // o RXINT_1STCHAR なら初期状態が true。 + // o RXINT_ALLCHAR なら常時 true。 + switch ((data >> 3) & 3) { + case 0: + chan.rxint_mode = MPSCCChan::RXINT_DISABLE; + chan.all_or_first = false; + break; + case 1: + chan.rxint_mode = MPSCCChan::RXINT_1STCHAR; + chan.sp_parity = false; + chan.all_or_first = true; + break; + case 2: + chan.rxint_mode = MPSCCChan::RXINT_ALLCHAR; + chan.sp_parity = false; + chan.all_or_first = true; + break; case 3: - return PeekCtrl(&cr.chan[1]); + chan.rxint_mode = MPSCCChan::RXINT_ALLCHAR; + chan.sp_parity = true; + chan.all_or_first = true; + default: + __unreachable(); + } + + if (chan.id == 0) { + mpscc.sav_vis = (data & MPSCC::CR1_SAV); + // sav を変更したので vis モードを更新 + SetVIS(); } - __unreachable(); + + chan.txint_enable = (data & MPSCC::CR1_TXINT_EN); + chan.esint_enable = (data & MPSCC::CR1_ESINT_EN); +} + +// CR2 への書き込み +void +SIODevice::WriteCR2(MPSCCChan& chan, uint32 data) +{ + if (chan.id == 0) { // CR2A + putlog(2, "%s <- $%02x", CRName(chan), data); + mpscc.syncb = (data & MPSCC::CR2_SYNCB); + mpscc.vectored_mode = (data & MPSCC::CR2_VM); + mpscc.int_mode = (data & MPSCC::CR2_INT); + mpscc.priority_select = (data & MPSCC::CR2_PRIOSEL); + mpscc.intdma_mode = (data & MPSCC::CR2_INTDMA); + // int_mode を変更したので vis モードを更新 + SetVIS(); + } else { // CR2B + putlog(2, "%s <- $%02x (Set vector)", CRName(chan), data); + mpscc.vector = data; + } +} + +// CR1 の SAV と CR2 の INT_MODE に従って VIS を更新 +void +SIODevice::SetVIS() +{ + if (mpscc.sav_vis) { + if (mpscc.int_mode == MPSCC::INTMODE_86) { + mpscc.vis = MPSCC::VIS_210; + } else { + mpscc.vis = MPSCC::VIS_432; + } + } else { + mpscc.vis = MPSCC::VIS_FIXED; + } +} + +// モニター +void +SIODevice::MonitorUpdate(Monitor *, TextScreen& screen) +{ + uint cr2a; + uint cr2b; + uint sr2b; + int y; + + cr2a = GetCR2A(); + cr2b = mpscc.vector; + sr2b = GetSR2B(); + + screen.Clear(); + for (int i = 0; i < 2; i++) { + int x = 0; + y = i * 11; + MonitorUpdateCh(screen, i, x, y); + } + + // CR2A + y = 22; + screen.Print(0, y, "CR2A:$%02x", cr2a); + if ((cr2a & MPSCC::CR2_SYNCB)) { + screen.Print(9, y, TA::On, "SYNC"); + } else { + screen.Print(9, y, "RTSB"); + } + screen.Print(14, y, TA::Disable, "----"); + screen.Print(19, y, TA::OnOff((cr2a & MPSCC::CR2_VM)), "Vect"); + if ((cr2a & MPSCC::CR2_INT) == 0x10) { + screen.Print(24, y, "IM=V4-V2"); + } else { + screen.Print(24, y, "IM=V2-V0"); + } + screen.Print(34, y, TA::OnOff((cr2a & MPSCC::CR2_PRIOSEL)), "PRIO"); + // bit1,0 はよく分からん + screen.Print(39, y, "INT/DMA=%u", (cr2a & MPSCC::CR2_INTDMA)); + y++; + + // CR2B + screen.Print(0, y, "CR2B:$%02x / SR2B:$%02x", cr2b, sr2b); +} + +// モニター (1チャンネル) +void +SIODevice::MonitorUpdateCh(TextScreen& screen, int ch, int xbase, int ybase) +{ + const MPSCCChan& chan = mpscc.chan[ch]; + uint cr0; + uint cr1; + uint cr3; + uint cr4; + uint cr5; + uint cr6; + uint cr7; + uint sr0; + uint sr1; + uint rxlen; + int x; + int y; + + cr0 = GetCR0(chan); + cr1 = GetCR1(chan); + cr3 = GetCR3(chan); + cr4 = GetCR4(chan); + cr5 = GetCR5(chan); + cr6 = chan.cr6; + cr7 = chan.cr7; + sr0 = GetSR0(chan); + sr1 = chan.sr1; + rxlen = chan.rxlen; + + // 0 1 2 3 4 5 6 + // 0123456789012345678901234567890123456789012345678901234567890123456789 + // 0123 0123 0123 0123 0123 0123 0123 0123 + // Channel A: Serial Console DataAddr $00000000 + // CR0: $xx CRC=x CMD=x PTR=x CtrlAddr $00000000 + // CR1: $xx WE 0 WR RXInt=x 0 TXEn ESEn + // RXBuf: 0 --- --- --- Intr: SP Rx ES Tx + + x = xbase; + y = ybase; + screen.Print(x, y++, "Channel %c: %s", chan.name, chan.desc); + screen.Print(x, y++, "CR0: $%02x", cr0); + screen.Print(x, y++, "CR1: $%02x", cr1); + screen.Print(x, y++, "CR3: $%02x", cr3); + screen.Print(x, y++, "CR4: $%02x", cr4); + screen.Print(x, y++, "CR5: $%02x", cr5); + screen.Print(x, y++, "CR7/CR6: $%02x%02x", cr7, cr6); + screen.Print(x, y++, "SR0: $%02x", sr0); + screen.Print(x, y++, "SR1: $%02x", sr1); + 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, "---"); + } + // 内部割り込み状態 + screen.Print(x + 31, y, "Intr:"); + screen.Print(x + 37, y, TA::OnOff(chan.intpend & INTPEND_SP), "SP"); + screen.Print(x + 40, y, TA::OnOff(chan.intpend & INTPEND_RX), "Rx"); + screen.Print(x + 43, y, TA::OnOff(chan.intpend & INTPEND_ES), "ES"); + screen.Print(x + 46, y, TA::OnOff(chan.intpend & INTPEND_TX), "Tx"); + + y = ybase + 1; + x = xbase; + // CR0 + screen.Print(x + 9, y, "CRC=%u", (cr0 >> 6)); + static const char * const cmd_str[] = { + // 0123456789012345 + "No operation", + "Send Abort", + "Reset E/S Int", + "Channel Reset", + "Enable IntNextCh", + "Reset TxIntPend", + "Error Reset", + "End of Interrupt", + }; + uint cmd = (cr0 >> 3) & 7; + screen.Print(x + 19, y, "CMD=%u(%s)", cmd, cmd_str[cmd]); + screen.Print(x + 43, y, "PTR=%u", (cr0 & 7)); + y++; + + // CR1 + static const char * const cr1_str[] = { + "Wait", "-", "OnRx", "", "", "SAV", "TxIE", "ESIE" + }; + MonitorReg(screen, x + 9, y, cr1, cr1_str); + // XXX All Char 割り込みのうちパリティエラーの扱いは表示省略。 + // パリティエラーの扱いが必要になったら考える。 + static const char * const rxint_str[] = { + // 012345678 + "RxInt=Dis", + "RI=1stChr", + "RI=AllChr", // Parity Error を Special Condition に含む + "RI=AllChr", // Parity Error を Special Condition に含まない + }; + screen.Puts(x + 24, y, rxint_str[(cr1 >> 3) & 3]); + // CR1 の bit2 (Modified Vector) は CR1B のみ。 + if (ch == 0) { + screen.Puts(x + 34, y, TA::Disable, "----"); + } + y++; + + // CR3,CR4,CR5 + y = MonitorUpdateCR345(screen, x, y, cr3, cr4, cr5); + + // CR6,CR7 + y++; + + // SR0 + static const char * const sr0_str[] = { + "BrAb", "TxUn", "!CTS", "!SYN", "!DCD", "TxEm", "IntP", "RxAv" + }; + MonitorReg(screen, x + 9, y, sr0, sr0_str); + // SR0 の bit1 (Interrupt Pending) は SR0A のみ。SR0B では 0。 + if (ch == 1) { + screen.Print(39, y, TA::Disable, "----"); + } + y++; + + // SR1 + MonitorUpdateSR1(screen, x, y, sr1); + y++; + + // 右列 + y = ybase + 1; + x = xbase + 51; + screen.Print(x, y++, "DataAddr: $%08x", chan.dataaddr); + screen.Print(x, y++, "CtrlAddr: $%08x", 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); +} + +// ペンディングのうち最も優先度の高い割り込み要因を返す +uint +SIODevice::GetHighestInt() const +{ + // 優先度は CR2A b2 (priority_select) で決まるが、 + // Int/DMA が %01 なら priority_select によらず 0 と同じほうになるという + // ことのようだ。 + // + // CR2 b210 High -> Low + // uPD7201 0 0 0 RxA TxA RxB TxB ESA ESB + // uPD7201 x 0 1 RxA TxA RxB TxB ESA ESB + // uPD7201 0 1 0 RxA TxA RxB TxB ESA ESB + // + // uPD7201 1 0 0 RxA RxB TxA TxB ESA ESB + // uPD7201 1 1 0 RxA RxB TxA TxB ESA ESB + + if (mpscc.intdma_mode == 1 || mpscc.priority_select == false) { + 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[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[0].intpend & INTPEND_ES)) return MPSCC::VS_ESA; + if ((mpscc.chan[1].intpend & INTPEND_ES)) return MPSCC::VS_ESB; + } else { + 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[1].intpend & INTPEND_SP)) return MPSCC::VS_SPB; + if ((mpscc.chan[1].intpend & INTPEND_RX)) return MPSCC::VS_RxB; + if ((mpscc.chan[0].intpend & INTPEND_TX)) return MPSCC::VS_TxA; + if ((mpscc.chan[1].intpend & INTPEND_TX)) return MPSCC::VS_TxB; + if ((mpscc.chan[0].intpend & INTPEND_ES)) return MPSCC::VS_ESA; + if ((mpscc.chan[1].intpend & INTPEND_ES)) return MPSCC::VS_ESB; + } + + return MPSCC::VS_none; } -// 割り込みを上げる void -SIODevice::Interrupt() +SIODevice::Tx(int ch, uint32 data) { - putlog(2, "Interrupt"); - gMPU->Interrupt(this, 6); + switch (ch) { + case 0: + hostcom->Tx(data); + break; + case 1: + keyboard->Command(data); + break; + default: + VMPANIC("corrupted ch=%u", ch); + } } + +// ログ表示用のレジスタ名 +/*static*/ const char * const +SIODevice::crnames[16] = { + "CR0A", "CR0B", + "CR1A", "CR1B", + "CR2A", "CR2B", + "CR3A", "CR3B", + "CR4A", "CR4B", + "CR5A", "CR5B", + "CR6A", "CR6B", + "CR7A", "CR7B", +}; +/*static*/ const char * const +SIODevice::srnames[16] = { + "SR0A", "SR0B", + "SR1A", "SR1B", + "SR2A?", "SR2B", + "SR3A?", "SR3B?", + "SR4A?", "SR4B?", + "SR5A?", "SR5B?", + "SR6A?", "SR6B?", + "SR7A?", "SR7B?", +};