--- nono/vm/pio.cpp 2026/04/29 17:04:55 1.1.1.8 +++ nono/vm/pio.cpp 2026/04/29 17:05:14 1.1.1.11 @@ -4,25 +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 @@ -41,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. @@ -58,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", @@ -82,13 +98,6 @@ i8255Device::WriteCtrl(uint32 data) } } -void -i8255Device::SetPC(int pc, int data) -{ - // 継承側で用意すること。ここには来ない - PANIC("not impl"); -} - // // X68k PPI // @@ -104,6 +113,7 @@ PPIDevice::PPIDevice() // デストラクタ PPIDevice::~PPIDevice() { + gPPI = NULL; } uint64 @@ -116,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 @@ -136,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(); @@ -159,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 // @@ -166,11 +203,49 @@ PPIDevice::Peek(uint32 offset) // 0x48000000 から 0x4c000000 の手前まで 4バイト単位でミラーが見える。 // // LUNA の DIP-SW は #1, #2 にそれぞれ8個、計16個ある。 -// DIP-SW #1, #2 が設定ファイルの luna_dipsw_{1,2} に対応する。 -// luna_dipsw_{1,2} はいずれも "0"/"1" を8つ並べた文字列であり、 +// DIP-SW #1, #2 が設定ファイルの luna-dipsw{1,2} に対応する。 +// luna-dipsw{1,2} はいずれも "0"/"1" を8つ並べた文字列であり、 // 先頭が本体表記でいう 1番、末尾側が本体表記でいう 8番に対応する。 -// 変数は dipsw{1,2}[0..7] に対応。 -// 設定ファイルと変数の値は %0 が down を表し、%1 が up を表す。 +// 値は "0" が down、"1" が up を表す。 +// +// PIO0Device 内ではこの設定を読み込んで bool dipsw1[8], dipsw2[8] の配列で +// 値を保持している。dipsw1[0] が本体表記1番、dipsw1[7] が本体表記 8番に対応 +// する。値は false が down("0")、true が up("1") に対応する。 +// +// PIO から読み出せる値のビットの並び順が機種によって異なる。 +// LUNA-1 では 1番が LSB、8番が MSB の並び順、 +// LUNA88K では 1番が MSB、8番が LSB の並び順。 +// 値は、並び順に関わらずどちらも +// %0 が false = down = "0"、%1 が true = up = "1" である。 +// +// #1 #2 #3 #4 #5 #6 #7 #8 +// +-------------------------+ +// | [] [] [] [] [] [] [] | 本体外観 +// | [] | +// +-------------------------+ +// +// luna-dipsw1 = "01111111" ← 設定ファイル +// |||||||| ↓ このクラスのメンバ変数 +// |||||||+- dipsw1[7] = true; +// ||||||+-- dipsw1[6] = true; +// |||||+--- dipsw1[5] = true; +// ||||+---- dipsw1[4] = true; +// |||+----- dipsw1[3] = true; +// ||+------ dipsw1[2] = true; +// |+------- dipsw1[1] = true; +// +-------- dipsw1[0] = false; +// +// b7 b6 b5 b4 b3 b2 b1 b0 +// +----+----+----+----+----+----+----+----+ +// LUNA-1 | #8 | #7 | #6 | #5 | #4 | #3 | #2 | #1 | +// PortA | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | %1=UP, %0=DOWN +// +----+----+----+----+----+----+----+----+ +// +// b7 b6 b5 b4 b3 b2 b1 b0 +// +----+----+----+----+----+----+----+----+ +// LUNA88K | #1 | #2 | #3 | #4 | #5 | #6 | #7 | #8 | +// PortA | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | %1=UP, %0=DOWN +// +----+----+----+----+----+----+----+----+ // // LUNA-1: // #1-1 up なら UNIX をロードして移行。down なら ROM モニタで起動。 @@ -194,7 +269,8 @@ PPIDevice::Peek(uint32 offset) // #1-1 up なら ROM モニタで停止、down ならマルチユーザブート。 // OpenBSD カーネルも up なら UserKernelConfig で止まる模様?。 // #1-2 up なら外付けシリアルコンソール、down なら内蔵ビットマップ。 -// #1-3..#1-8 は予約。 +// #1-3..#1-7 は不明? +// #1-8 down なら自己診断後電源オフするようだ。 // #2-x はユーザ(?) // コンストラクタ @@ -208,11 +284,13 @@ 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; default: __unreachable(); @@ -220,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); } @@ -228,6 +306,7 @@ PIO0Device::PIO0Device() // デストラクタ PIO0Device::~PIO0Device() { + gPIO0 = NULL; } bool @@ -255,7 +334,7 @@ PIO0Device::Init() return true; } -// XXX PowerOn か ResetHard 未調査 +// XXX ResetHard 未調査 uint64 PIO0Device::Read(uint32 offset) @@ -265,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: @@ -363,11 +444,11 @@ PIO0Device::MonitorUpdate(Monitor *, Tex screen.Puts(0, y++, "PortC"); screen.Puts(1, y++, TA::OnOff(xp_reset), "b7: XP Reset"); screen.Puts(1, y++, TA::OnOff(parity), "b6: Parity"); - screen.Puts(1, y++, "b5: ---"); + screen.Puts(1, y++, TA::Disable, "b5: ---"); screen.Puts(1, y++, TA::OnOff(int5en), "b4: Int5 Enable"); screen.Puts(1, y++, TA::OnOff(int5rq), "b3: Int5 Request"); screen.Puts(1, y++, TA::OnOff(int1en), "b2: Int1 Enable"); - screen.Puts(1, y++, "b1: ---"); + screen.Puts(1, y++, TA::Disable, "b1: ---"); screen.Puts(1, y++, TA::OnOff(int1rq), "b0: Intq Request"); } @@ -379,6 +460,9 @@ PIO0Device::GetPA() const for (int i = 0; i < 8; i++) { data |= ((int)dipsw1[i]) << i; } + if (msb_first) { + data = bitrev(data); + } return data & 0xff; } @@ -390,6 +474,9 @@ PIO0Device::GetPB() const for (int i = 0; i < 8; i++) { data |= ((int)dipsw2[i]) << i; } + if (msb_first) { + data = bitrev(data); + } return data & 0xff; } @@ -417,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: @@ -426,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: @@ -453,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); } @@ -474,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 @@ -512,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: @@ -550,7 +639,7 @@ PIO1Device::Write(uint32 offset, uint32 default: __unreachable(); } - PANIC("$%08X <- $%02X 未サポート書き込み", gMPU->GetPaddr(), data); + VMPANIC("$%08X <- $%02X 未サポート書き込み", gMPU->GetPaddr(), data); return 0; } @@ -597,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: @@ -619,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); }