--- nono/vm/pio.cpp 2026/04/29 17:04:59 1.1.1.9 +++ nono/vm/pio.cpp 2026/04/29 17:05:14 1.1.1.11 @@ -4,26 +4,41 @@ // Licensed under nono-license.txt // +// +// PIO/PPI (i8255) +// + +// IODevice +// | +// | +-------------+ +// +--| i8255Device | (8255 としての共通部分) +// +-------------+ +// | +// | +-----------+ +// +--| PPIDevice | (X68030) +// | +-----------+ +// | +// | +------------+ +// +--| PIO0Device | (LUNA) +// | +------------+ +// | +// | +------------+ +// +--| PIO1Device | (LUNA) +// +------------+ + #include "pio.h" #include "bitops.h" #include "config.h" #include "lcd.h" #include "mainapp.h" +#include "mpu.h" +#include "power.h" #include "scheduler.h" -// IODevice -// | -// v -// i8255Device (8255 としての共通部分) -// | -// +----------+----------+ -// v v v -// PPIDevice PIO0Device PIO1Device -// (X68030) (LUNA) (LUNA) - -std::unique_ptr gPPI; -std::unique_ptr gPIO0; -std::unique_ptr gPIO1; +// グローバル参照用 +PPIDevice *gPPI; +PIO0Device *gPIO0; +PIO1Device *gPIO1; // // i8255 @@ -42,7 +57,7 @@ i8255Device::~i8255Device() // リセット void -i8255Device::ResetHard() +i8255Device::ResetHard(bool poweron) { // clears the control register // and place ports A, B, and C in input mode. @@ -59,7 +74,7 @@ i8255Device::WriteCtrl(uint32 data) // 最上位が立っていればモード設定 ctrl = data; putlog(1, - "モード設定 groupA=%d groupB=%d portA=%s portB=%s portC=%s/%s", + "Set mode: groupA=%d groupB=%d portA=%s portB=%s portC=%s/%s", GetGroupAMode(), GetGroupBMode(), PortADir() == DIR_IN ? "IN" : "OUT", PortBDir() == DIR_IN ? "IN" : "OUT", @@ -83,13 +98,6 @@ i8255Device::WriteCtrl(uint32 data) } } -void -i8255Device::SetPC(int pc, int data) -{ - // 継承側で用意すること。ここには来ない - PANIC("not impl"); -} - // // X68k PPI // @@ -105,6 +113,7 @@ PPIDevice::PPIDevice() // デストラクタ PPIDevice::~PPIDevice() { + gPPI = NULL; } uint64 @@ -117,15 +126,14 @@ PPIDevice::Read(uint32 offset) case 1: return 0xff; case 2: - putlog(0, "$e9a005 PortC 未実装読み込み"); + putlog(0, "Read $e9a005 PortC (NOT IMPLEMENTED)"); return 0xff; case 3: - PANIC("PPI $e9a007 未サポート読み込み"); - break; + putlog(0, "Read $e9a007 Ctrl (NOT IMPLEMENTED)"); + return 0xff; default: __unreachable(); } - PANIC("not impl"); } uint64 @@ -137,15 +145,14 @@ PPIDevice::Write(uint32 offset, uint32 d case 0: case 1: break; - case 2: - putlog(0, "$e9a005 未実装書き込み $%02X (無視)", data); + case 2: // PPI PortC + for (int i = 0; i < 8; i++) { + SetPC(i, data & 1); + data >>= 1; + } break; case 3: - if (data != 0x92) { - PANIC("PPI $e9a007 未実装書き込み $%02X", data); - } else { - putlog(0, "$e9a007 未実装書き込み $%02x (無視)", data); - } + WriteCtrl(data); break; default: __unreachable(); @@ -160,6 +167,35 @@ PPIDevice::Peek(uint32 offset) return 0xff; } +// PortC 書き込み +void +PPIDevice::SetPC(int pc, int val) +{ + switch (pc) { + case 0: // ADPCM PAN Left Off + pan_left = !val; + break; + case 1: // ADPCM PAN Right Off + pan_right = !val; + break; + case 2: // ADPCM サンプリングレート + adpcm_rate = (adpcm_rate & 2) | val; + break; + case 3: // ADPCM サンプリングレート + adpcm_rate = (val << 1) | (adpcm_rate & 1); + break; + + case 4: // ジョイスティック #1 コントロール + case 5: // ジョイスティック #2 コントロール + case 6: // ジョイスティック #1 ボタン A + case 7: // ジョイスティック #1 ボタン B + putlog(1, "SetPC(%d) <- %d (NOT IMPLEMENTED)", pc, val); + break; + default: + __unreachable(); + } +} + // // LUNA PIO0 // @@ -248,11 +284,11 @@ PIO0Device::PIO0Device() // LUNA-1 と LUNA88K で使いやすいように初期値を変えておく。 switch (gMainApp.GetVMType()) { - case VMTYPE_LUNA1: + case VMType::LUNA1: gConfig->SetDefault("luna-dipsw1", "11110111"); msb_first = false; break; - case VMTYPE_LUNA88K: + case VMType::LUNA88K: gConfig->SetDefault("luna-dipsw1", "11111111"); msb_first = true; break; @@ -262,7 +298,7 @@ PIO0Device::PIO0Device() // DIP-SW#2 はユーザ定義(?)なので(今の所?)どちらも共通。 gConfig->SetDefault("luna-dipsw2", "11111111"); - monitor.func = (MonitorCallback_t)&PIO0Device::MonitorUpdate; + monitor.func = ToMonitorCallback(&PIO0Device::MonitorUpdate); monitor.SetSize(22, 11); monitor.Regist(ID_MONITOR_PIO0); } @@ -270,6 +306,7 @@ PIO0Device::PIO0Device() // デストラクタ PIO0Device::~PIO0Device() { + gPIO0 = NULL; } bool @@ -297,7 +334,7 @@ PIO0Device::Init() return true; } -// XXX PowerOn か ResetHard 未調査 +// XXX ResetHard 未調査 uint64 PIO0Device::Read(uint32 offset) @@ -307,23 +344,25 @@ PIO0Device::Read(uint32 offset) switch (offset) { case 0: // PortA - putlog(1, "DIP-SW 1 読み込み"); - return GetPA(); + data = GetPA(); + putlog(1, "DIP-SW1 -> $%02x", data); + return data; case 1: // PortB - putlog(1, "DIP-SW 2 読み込み"); - return GetPB(); + data = GetPB(); + putlog(1, "DIP-SW2 -> $%02x", data); + return data; case 2: // PortC data = GetPC(); - putlog(1, "PC 読み込み -> $%02x", data); + putlog(1, "PortC -> $%02x", data); return data; case 3: data = ReadCtrl(); - putlog(1, "コントロール読み込み -> $%02x", data); + putlog(1, "Ctrl -> $%02x", data); return data; default: @@ -465,7 +504,7 @@ PIO0Device::SetPC(int pc, int val) case 0: if ((val ^ int1rq)) { int1rq = val; - putlog(0, "INT1割り込み要求 %d (未実装)", int1rq); + putlog(0, "INT1 Interrupt Request %d (NOT IMPLEMENTED)", int1rq); } return; case 1: @@ -474,19 +513,19 @@ PIO0Device::SetPC(int pc, int val) case 2: if ((val ^ int1en)) { int1en = val; - putlog(0, "INT1割り込み許可 %d (未実装)", int1en); + putlog(0, "INT1 Interrupt Enable %d (NOT IMPLEMENTED)", int1en); } return; case 3: if ((val ^ int5rq)) { int5rq = val; - putlog(0, "INT5割り込み要求 %d (未実装)", int5rq); + putlog(0, "INT5 Interrupt Request %d (NOT IMPLEMENTED)", int5rq); } return; case 4: if ((val ^ int5en)) { int5en = val; - putlog(0, "INT5割り込み許可 %d (未実装)", int5en); + putlog(0, "INT5 Interrupt Enable %d (NOT IMPLEMENTED)", int5en); } return; case 5: @@ -501,11 +540,11 @@ PIO0Device::SetPC(int pc, int val) // XP リセット if ((val ^ xp_reset)) { xp_reset = val; - putlog(0, "XPリセット %d (未実装)", xp_reset); + putlog(0, "XP Reset %d (NOT IMPLEMENTED)", xp_reset); } return; } - PANIC("invalid pc=%d", pc); + VMPANIC("invalid pc=%d", pc); } @@ -522,22 +561,24 @@ PIO1Device::PIO1Device() devaddr = 0x4d000000; devlen = 4; - poffevent.func = (DeviceCallback_t)&PIO1Device::PowerOffCallback; - poffevent.SetName("PIO1 Power Off"); } // デストラクタ PIO1Device::~PIO1Device() { + gPIO1 = NULL; } // リセット void -PIO1Device::ResetHard() +PIO1Device::ResetHard(bool poweron) { - inherited::ResetHard(); + inherited::ResetHard(poweron); - poffevent.Stop(); + // デフォルトで PIO 出力は「1msec 後に電源オフ」という状態なので、 + // 電源オンから 1msec 以内にソフトウェアがこれをキャンセルしないと + // いけないらしい。どうしてこうなった…。 + gPower->SetSystemPowerOn(false); } uint64 @@ -560,12 +601,12 @@ PIO1Device::Read(uint32 offset) data |= xp_intreq ? 0 : 0x02; // 残りのビットは %1 だろうか? data |= 0x0d; - putlog(1, "PC 読み込み -> $%02x", data); + putlog(1, "PortC -> $%02x", data); return data; case 3: data = ReadCtrl(); - putlog(1, "コントロール読み込み -> $%02x", data); + putlog(1, "Ctrl -> $%02x", data); return data; default: @@ -598,7 +639,7 @@ PIO1Device::Write(uint32 offset, uint32 default: __unreachable(); } - PANIC("$%08X <- $%02X 未サポート書き込み", gMPU->GetPaddr(), data); + VMPANIC("$%08X <- $%02X 未サポート書き込み", gMPU->GetPaddr(), data); return 0; } @@ -645,13 +686,12 @@ PIO1Device::SetPC(int pc, int val) power = val; if (power) { // 電源オフ取り消し - putlog(1, "電源 OFF 取消"); - poffevent.Stop(); + putlog(1, "Power-off canceled"); + gPower->SetSystemPowerOn(true); } else { // 1msec 後に電源オフ - putlog(1, "電源 OFF 指示"); - poffevent.time = 1_msec; - poffevent.Start(); + putlog(1, "Power-off scheduled"); + gPower->SetSystemPowerOn(false); } return; case 5: @@ -667,13 +707,5 @@ PIO1Device::SetPC(int pc, int val) default: break; } - PANIC("PC%d 未実装", pc); -} - -// 電源 OFF イベントのコールバック -void -PIO1Device::PowerOffCallback(Event& ev) -{ - putlog(0, "電源 OFF"); - gScheduler->RequestPowerOff(); + VMPANIC("PC%d 未実装", pc); }