--- nono/vm/pio.cpp 2026/04/29 17:05:18 1.1.1.12 +++ nono/vm/pio.cpp 2026/04/29 17:05:55 1.1.1.18 @@ -27,12 +27,14 @@ // +------------+ #include "pio.h" +#include "adpcm.h" #include "bitops.h" #include "dipsw.h" +#include "event.h" #include "interrupt.h" #include "lcd.h" #include "mainapp.h" -#include "mpu.h" +#include "monitor.h" #include "mpu64180.h" #include "power.h" #include "scheduler.h" @@ -42,7 +44,7 @@ // // コンストラクタ -i8255Device::i8255Device(int objid_) +i8255Device::i8255Device(uint objid_) : inherited(objid_) { } @@ -72,7 +74,7 @@ i8255Device::WriteCtrl(uint32 data) ctrl = data; if (__predict_false(loglevel >= 1)) { if (loglevel >= 2) { - putlogn("Ctrl <- $%02x: GrA/Bmode=%d/%d PA=%s PB=%s PC=%s:%s", + putlogn("Ctrl <- $%02x: GrA/Bmode=%u/%u PA=%s PB=%s PC=%s:%s", data, GetGroupAMode(), GetGroupBMode(), PortADir() == DIR_IN ? "IN" : "OUT", @@ -95,13 +97,19 @@ i8255Device::WriteCtrl(uint32 data) // | | | +-- 書き込むビットデータ // +--+--+----- 書き込むビット位置 - int pc = (data >> 1) & 7; - int val = data & 1; - putlog(2, "Ctrl <- $%02x: SetPC%d=%d", data, pc, val); + uint pc = (data >> 1) & 7; + uint val = data & 1; + putlog(2, "Ctrl <- $%02x: SetPC%u=%u", data, pc, val); SetPC(pc, val); } } +bool +i8255Device::PokePort(uint32 offset, uint32 data) +{ + return false; +} + // 共通部分を表示。 // 戻り値は表示を終えた次の行。 int @@ -120,10 +128,10 @@ i8255Device::MonitorUpdateCommon(TextScr "BiDirection", }; const int x = 16; - screen.Print(x, y++, "GroupA (PortA,PortCH) Mode: %d(%s)", + screen.Print(x, y++, "GroupA (PortA,PortCH) Mode: %u(%s)", GetGroupAMode(), modestr[GetGroupAMode()]); - screen.Print(x, y++, "GroupB (PortB,PortCL) Mode: %d(%s)", + screen.Print(x, y++, "GroupB (PortB,PortCL) Mode: %u(%s)", GetGroupBMode(), modestr[GetGroupBMode()]); static const char *inoutstr[] = { @@ -143,6 +151,9 @@ i8255Device::MonitorUpdateCommon(TextScr // X68k PPI // +// InsideOut p.135 +static const busdata ppi_wait = busdata::Wait(19 * 40_nsec); + // コンストラクタ PPIDevice::PPIDevice() : inherited(OBJ_PPI) @@ -154,31 +165,45 @@ PPIDevice::~PPIDevice() { } -uint64 -PPIDevice::Read(uint32 offset) +// 初期化 +bool +PPIDevice::Init() { - mpu->AddCycle(19); // InsideOut p.135 + adpcm = GetADPCMDevice(); + + return true; +} + +busdata +PPIDevice::ReadPort(uint32 offset) +{ + busdata data; switch (offset) { case 0: case 1: - return 0xff; + data = 0xff; + break; case 2: - putlog(0, "Read $e9a005 PortC (NOT IMPLEMENTED)"); - return 0xff; + data = GetPC(); + putlog(1, "PortC -> $%02x", data.Data()); + break; case 3: putlog(0, "Read $e9a007 Ctrl (NOT IMPLEMENTED)"); - return 0xff; + data = 0xff; + break; default: - VMPANIC("corrupted offset=%d", offset); + VMPANIC("corrupted offset=%u", offset); } + + data |= ppi_wait; + data |= BusData::Size1; + return data; } -uint64 -PPIDevice::Write(uint32 offset, uint32 data) +busdata +PPIDevice::WritePort(uint32 offset, uint32 data) { - mpu->AddCycle(19); // InsideOut p.135 - switch (offset) { case 0: case 1: @@ -193,44 +218,75 @@ PPIDevice::Write(uint32 offset, uint32 d WriteCtrl(data); break; default: - VMPANIC("corrupted offset=%d", offset); + VMPANIC("corrupted offset=%u", offset); } - return 0; + busdata r = ppi_wait; + r |= BusData::Size1; + return r; } -uint64 -PPIDevice::Peek(uint32 offset) +busdata +PPIDevice::PeekPort(uint32 offset) { // XXX return 0xff; } +// PortC 読み込み +uint32 +PPIDevice::GetPC() const +{ + uint32 data = 0; + + // パンの状態はこちらで持っていない。 + if (adpcm->GetPanOutR() == false) { + data |= 0x01; + } + if (adpcm->GetPanOutL() == false) { + data |= 0x02; + } + + // サンプリングレートは書き込み値をこちらでも持っている(ADPCMにもある)。 + data |= adpcm_rate << 2; + + // bit4: PC4 (通常 0) + // bit5: PC5 (通常 0) + // bit6: IOA5 (1=ジョイスティック#1のAが押された状態にする, 0=通常) + // bit6: IOA6 (1=ジョイスティック#1のBが押された状態にする, 0=通常) + + return data; +} + // PortC 書き込み void -PPIDevice::SetPC(int pc, int val) +PPIDevice::SetPC(uint pc, uint val) { switch (pc) { case 0: // ADPCM PAN Left Off - pan_left = !val; - break; + adpcm->SetPanOutL(!val); + return; case 1: // ADPCM PAN Right Off - pan_right = !val; - break; + adpcm->SetPanOutR(!val); + return; case 2: // ADPCM サンプリングレート - adpcm_rate = (adpcm_rate & 2) | val; - break; + adpcm_rate &= ~1U; + adpcm_rate |= val ? 1U : 0; + adpcm->SetRate(adpcm_rate); + return; case 3: // ADPCM サンプリングレート - adpcm_rate = (val << 1) | (adpcm_rate & 1); - break; + adpcm_rate &= ~2U; + adpcm_rate |= val ? 2U : 0; + adpcm->SetRate(adpcm_rate); + return; case 4: // ジョイスティック #1 コントロール case 5: // ジョイスティック #2 コントロール case 6: // ジョイスティック #1 ボタン A case 7: // ジョイスティック #1 ボタン B - putlog(1, "SetPC(%d) <- %d (NOT IMPLEMENTED)", pc, val); - break; + putlog(1, "SetPC(%u) <- %u (NOT IMPLEMENTED)", pc, val); + return; default: - VMPANIC("corrupted pc=%d", pc); + VMPANIC("corrupted pc=%u", pc); } } @@ -315,9 +371,9 @@ PIO0Device::PIO0Device() } // PIO0/PIO1 - monitor.func = ToMonitorCallback(&PIO0Device::MonitorUpdate); - monitor.SetSize(76, 15); - monitor.Regist(ID_MONITOR_PIO); + monitor = gMonitorManager->Regist(ID_MONITOR_PIO, this); + monitor->func = ToMonitorCallback(&PIO0Device::MonitorUpdate); + monitor->SetSize(76, 15); } // デストラクタ @@ -329,10 +385,6 @@ PIO0Device::~PIO0Device() bool PIO0Device::Init() { - if (inherited::Init() == false) { - return false; - } - interrupt = GetInterruptDevice(); pio1 = GetPIO1Device(); @@ -359,10 +411,10 @@ PIO0Device::ResetHard(bool poweron) } } -uint64 -PIO0Device::Read(uint32 offset) +busdata +PIO0Device::ReadPort(uint32 offset) { - uint32 data; + busdata data; switch (offset) { case 0: @@ -373,8 +425,8 @@ PIO0Device::Read(uint32 offset) intreq[INT_H] = false; ChangeInterrupt(); - putlog(1, "DIP-SW1 -> $%02x", data); - return data; + putlog(1, "DIP-SW1 -> $%02x", data.Data()); + break; case 1: // PortB @@ -384,51 +436,59 @@ PIO0Device::Read(uint32 offset) intreq[INT_L] = false; ChangeInterrupt(); - putlog(1, "DIP-SW2 -> $%02x", data); - return data; + putlog(1, "DIP-SW2 -> $%02x", data.Data()); + break; case 2: // PortC data = GetPC(); - putlog(1, "PortC -> $%02x", data); - return data; + putlog(1, "PortC -> $%02x", data.Data()); + break; case 3: data = ReadCtrl(); - putlog(1, "Ctrl -> $%02x", data); - return data; + putlog(1, "Ctrl -> $%02x", data.Data()); + break; default: - VMPANIC("corrupted offset=%d", offset); + VMPANIC("corrupted offset=%u", offset); } + + // XXX wait? + data |= BusData::Size1; + return data; } -uint64 -PIO0Device::Write(uint32 offset, uint32 data) +busdata +PIO0Device::WritePort(uint32 offset, uint32 data) { switch (offset) { case 0: // PIO0 PortA: DIPSW1 (入力のみ) case 1: // PIO0 PortB: DIPSW2 (入力のみ) - return 0; + break; case 2: // PIO0 PortC: ホスト割り込み制御 for (int i = 0; i < 8; i++) { SetPC(i, data & 1); data >>= 1; } - return 0; + break; case 3: // コントロール WriteCtrl(data); - return 0; + break; default: - VMPANIC("corrupted offset=%d", offset); + VMPANIC("corrupted offset=%u", offset); } + + // XXX wait? + busdata r = BusData::Size1; + return r; } -uint64 -PIO0Device::Peek(uint32 offset) +busdata +PIO0Device::PeekPort(uint32 offset) { switch (offset) { case 0: @@ -474,8 +534,8 @@ PIO0Device::MonitorUpdate(Monitor *, Tex bool d1 = (pa & (1U << bitpos)); bool d2 = (pb & (1U << bitpos)); - screen.Print(21 + i * 7, y, TA::OnOff(d1), "DIP1-%d", label); - screen.Print(21 + i * 7, y + 1, TA::OnOff(d2), "DIP2-%d", label); + screen.Print(21 + i * 7, y, TA::OnOff(d1), "DIP1-%u", label); + screen.Print(21 + i * 7, y + 1, TA::OnOff(d2), "DIP2-%u", label); } y += 2; @@ -504,7 +564,7 @@ PIO0Device::GetPA() const data |= ((int)dipsw1[i]) << i; } if (msb_first) { - data = bitrev(data); + data = bitrev8(data); } return data & 0xff; } @@ -518,7 +578,7 @@ PIO0Device::GetPB() const data |= ((int)dipsw2[i]) << i; } if (msb_first) { - data = bitrev(data); + data = bitrev8(data); } return data & 0xff; } @@ -541,7 +601,7 @@ PIO0Device::GetPC() const // PortC 書き込み void -PIO0Device::SetPC(int pc, int val) +PIO0Device::SetPC(uint pc, uint val) { switch (pc) { case 0: @@ -573,13 +633,14 @@ PIO0Device::SetPC(int pc, int val) // XP リセット if ((val ^ xp_reset)) { xp_reset = val; - putlog(1, "XP Reset %d", xp_reset); + putlog(1, "XP Reset %u", xp_reset); auto mpu64180 = GetMPU64180Device(); mpu64180->ChangeRESET(xp_reset); } return; + default: + VMPANIC("corrupted pc=%u", pc); } - VMPANIC("invalid pc=%d", pc); } // とりあえず、XP からここまではエッジトリガーで、 @@ -640,10 +701,6 @@ PIO1Device::~PIO1Device() bool PIO1Device::Init() { - if (inherited::Init() == false) { - return false; - } - lcd = GetLCDDevice(); mpu64180 = GetMPU64180Device(); powerdev = GetPowerDevice(); @@ -657,26 +714,32 @@ PIO1Device::ResetHard(bool poweron) { inherited::ResetHard(poweron); - // デフォルトで PIO 出力は「1msec 後に電源オフ」という状態なので、 - // 電源オンから 1msec 以内にソフトウェアがこれをキャンセルしないと - // いけないらしい。どうしてこうなった…。 - power = false; - powerdev->SetSystemPowerOn(false); + // 電源ユニットは POWF* 信号が 'L' になると 1msec 後に電源オフになる。 + // 正確には「1msec 以内に 'H' にすれば回避できる」なので、実際に電源が + // オフになるのはそれよりもっと十分長いとは思われる。 + // 8255 リセット時、ポート C の各ピンは入力モードになっているので、 + // ハイインピーダンス。たぶん電源的にも 'H' になってると想像する。 + // なので電源オンで POWF* がハイインピーダンスなだけでは電源オフ + // タイマーは動作しないんじゃないかな。 + power = true; + powerdev->SetSystemPowerOn(power); } -uint64 -PIO1Device::Read(uint32 offset) +busdata +PIO1Device::ReadPort(uint32 offset) { - uint32 data; + busdata data; switch (offset) { case 0: // PIO1 PortA: LCD データ - return lcd->Read(); + data = lcd->Read(); + break; case 1: // PIO1 PortB: 無効 - return 0xff; + data = 0xff; + break; case 2: // PortC @@ -684,51 +747,59 @@ PIO1Device::Read(uint32 offset) data |= xp_intreq ? 0 : 0x02; // 残りのビットは %1 だろうか? data |= 0x0d; - putlog(1, "PortC -> $%02x", data); - return data; + putlog(1, "PortC -> $%02x", data.Data()); + break; case 3: data = ReadCtrl(); - putlog(1, "Ctrl -> $%02x", data); - return data; + putlog(1, "Ctrl -> $%02x", data.Data()); + break; default: - VMPANIC("corrupted offset=%d", offset); + VMPANIC("corrupted offset=%u", offset); } + + // XXX wait? + data |= BusData::Size1; + return data; } -uint64 -PIO1Device::Write(uint32 offset, uint32 data) +busdata +PIO1Device::WritePort(uint32 offset, uint32 data) { switch (offset) { case 0: // PIO1 PortA: LCD データ lcd->Write(data); - return 0; + break; case 1: // PIO1 PortB: XP の INT0 をアサート // データ不問でアサートする。 putlog(1, "Assert XP INT0"); mpu64180->AssertINT0(); - return 0; + break; case 2: // PIO1 PortC: LCD/パワーオフ制御 for (int i = 0; i < 8; i++) { SetPC(i, data & 1); data >>= 1; } - return 0; + break; case 3: // コントロール WriteCtrl(data); - return 0; + break; default: - VMPANIC("corrupted offset=%d", offset); + VMPANIC("corrupted offset=%u", offset); } + + // XXX wait? + busdata r = BusData::Size1; + return r; } -uint64 -PIO1Device::Peek(uint32 offset) +busdata +PIO1Device::PeekPort(uint32 offset) { switch (offset) { case 0: // PIO1 PortA LCD データ @@ -786,7 +857,7 @@ PIO1Device::GetPC() const } void -PIO1Device::SetPC(int pc, int val) +PIO1Device::SetPC(uint pc, uint val) { switch (pc) { case 0: @@ -798,6 +869,10 @@ PIO1Device::SetPC(int pc, int val) return; case 4: + // 8255 はモード変更 (WriteCtrl) をするとポート C を 'L' にするが、 + // PC4 (POWF*) が 'L' だと電源オフ指示なので、1msec 以内に + // ソフトウェアでこれをキャンセルしないといけない。 + // どうしてこうなった…。 power = val; if (power) { // 電源オフ取り消し @@ -820,7 +895,6 @@ PIO1Device::SetPC(int pc, int val) return; default: - break; + VMPANIC("corrupted pc=%u", pc); } - VMPANIC("PC%d 未実装", pc); }