--- nono/vm/pio.cpp 2026/04/29 17:04:42 1.1.1.4 +++ nono/vm/pio.cpp 2026/04/29 17:05:33 1.1.1.15 @@ -4,32 +4,46 @@ // Licensed under nono-license.txt // +// +// PIO/PPI (i8255) +// + +// IODevice +// | +// | +-------------+ +// +--| i8255Device | (8255 としての共通部分) +// +-------------+ +// | +// | +-----------+ +// +--| PPIDevice | (X68030) +// | +-----------+ +// | +// | +------------+ +// +--| PIO0Device | (LUNA) +// | +------------+ +// | +// | +------------+ +// +--| PIO1Device | (LUNA) +// +------------+ + #include "pio.h" -#include "config.h" +#include "bitops.h" +#include "dipsw.h" +#include "interrupt.h" #include "lcd.h" #include "mainapp.h" -#include "mystring.h" +#include "monitor.h" +#include "mpu64180.h" +#include "power.h" #include "scheduler.h" -// IODevice -// | -// v -// i8255Device (8255 としての共通部分) -// | -// +----------+----------+ -// v v v -// PPIDevice PIO0Device PIO1Device -// (X68030) (LUNA) (LUNA) - -std::unique_ptr gPIO0; -std::unique_ptr gPIO1; - // // i8255 // // コンストラクタ -i8255Device::i8255Device() +i8255Device::i8255Device(uint objid_) + : inherited(objid_) { } @@ -40,7 +54,7 @@ i8255Device::~i8255Device() // リセット void -i8255Device::ResetHard() +i8255Device::ResetHard(bool poweron) { // clears the control register // and place ports A, B, and C in input mode. @@ -56,13 +70,19 @@ i8255Device::WriteCtrl(uint32 data) if ((data & 0x80)) { // 最上位が立っていればモード設定 ctrl = data; - putlog(1, - "モード設定 groupA=%d groupB=%d portA=%s portB=%s portC=%s/%s", - GetGroupAMode(), GetGroupBMode(), - PortADir() == DIR_IN ? "IN" : "OUT", - PortBDir() == DIR_IN ? "IN" : "OUT", - PortCHDir() == DIR_IN ? "IN" : "OUT", - PortCLDir() == DIR_IN ? "IN" : "OUT"); + if (__predict_false(loglevel >= 1)) { + if (loglevel >= 2) { + putlogn("Ctrl <- $%02x: GrA/Bmode=%u/%u PA=%s PB=%s PC=%s:%s", + data, + GetGroupAMode(), GetGroupBMode(), + PortADir() == DIR_IN ? "IN" : "OUT", + PortBDir() == DIR_IN ? "IN" : "OUT", + PortCHDir() == DIR_IN ? "IN" : "OUT", + PortCLDir() == DIR_IN ? "IN" : "OUT"); + } else { + putlogn("Ctrl <- $%02x: SetMode", data); + } + } // モード設定すると PC はゼロクリアされる for (int pc = 0; pc < 8; pc++) { @@ -75,31 +95,67 @@ i8255Device::WriteCtrl(uint32 data) // | | | +-- 書き込むビットデータ // +--+--+----- 書き込むビット位置 - int pc = (data >> 1) & 7; - int val = data & 1; + uint pc = (data >> 1) & 7; + uint val = data & 1; + putlog(2, "Ctrl <- $%02x: SetPC%u=%u", data, pc, val); SetPC(pc, val); } } -void -i8255Device::SetPC(int pc, int data) +bool +i8255Device::PokePort(uint32 offset, uint32 data) { - // 継承側で用意すること。ここには来ない - PANIC("not impl"); + return false; +} + +// 共通部分を表示。 +// 戻り値は表示を終えた次の行。 +int +i8255Device::MonitorUpdateCommon(TextScreen& screen, int y) +{ + // Control = $xx + // GroupA (PortA,PortCH) Mode: 0(Basic I/O) + // GroupB (PortB,PortCL) Mode: 1(Strobed I/O) + // Port Direction: PA=OUT PB=IN PC(H)=OUT PC(L)=OUT + + screen.Print(1, y, "Control = $%02x:", ctrl); + static const char * const modestr[] = { + "Basic I/O", + "Strobed I/O", + "BiDirection", + "BiDirection", + }; + const int x = 16; + screen.Print(x, y++, "GroupA (PortA,PortCH) Mode: %u(%s)", + GetGroupAMode(), + modestr[GetGroupAMode()]); + screen.Print(x, y++, "GroupB (PortB,PortCL) Mode: %u(%s)", + GetGroupBMode(), + modestr[GetGroupBMode()]); + static const char *inoutstr[] = { + "OUT", + "IN ", + }; + screen.Print(x, y++, "Port Direction: PA=%s PB=%s PC(H)=%s PC(L)=%s", + inoutstr[PortADir()], + inoutstr[PortBDir()], + inoutstr[PortCHDir()], + inoutstr[PortCLDir()]); + + return y; } // // X68k PPI // +// InsideOut p.135 +static const busdata ppi_wait = busdata::Wait(19 * 40_nsec); + // コンストラクタ PPIDevice::PPIDevice() - : inherited() + : inherited(OBJ_PPI) { - logname = "pio"; - devname = "PPI"; - devaddr = baseaddr; - devlen = 0x2000; } // デストラクタ @@ -107,73 +163,137 @@ PPIDevice::~PPIDevice() { } -uint64 -PPIDevice::Read(uint32 addr) +busdata +PPIDevice::ReadPort(uint32 offset) { - gMPU->AddCycle(19); // InsideOut p.135 + busdata data; - switch (addr) { + switch (offset) { case 0: case 1: - return 0xff; + data = 0xff; + break; case 2: - PANIC("PPI $E9A005 未サポート読み込み"); + putlog(0, "Read $e9a005 PortC (NOT IMPLEMENTED)"); + data = 0xff; break; case 3: - PANIC("PPI $E9A007 未サポート読み込み"); + putlog(0, "Read $e9a007 Ctrl (NOT IMPLEMENTED)"); + data = 0xff; break; default: - __unreachable(); + VMPANIC("corrupted offset=%u", offset); } - PANIC("not impl"); + + data |= ppi_wait; + data |= BusData::Size1; + return data; } -uint64 -PPIDevice::Write(uint32 addr, uint32 data) +busdata +PPIDevice::WritePort(uint32 offset, uint32 data) { - gMPU->AddCycle(19); // InsideOut p.135 - - switch (addr) { + switch (offset) { 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(); + VMPANIC("corrupted offset=%u", offset); } - return 0; + busdata r = ppi_wait; + r |= BusData::Size1; + return r; } -uint64 -PPIDevice::Peek(uint32 addr) +busdata +PPIDevice::PeekPort(uint32 offset) { // XXX return 0xff; } +// PortC 書き込み +void +PPIDevice::SetPC(uint pc, uint val) +{ + switch (pc) { + case 0: // ADPCM PAN Left Off + pan_left = !val; + return; + case 1: // ADPCM PAN Right Off + pan_right = !val; + return; + case 2: // ADPCM サンプリングレート + adpcm_rate = (adpcm_rate & 2) | val; + return; + case 3: // ADPCM サンプリングレート + adpcm_rate = (val << 1) | (adpcm_rate & 1); + return; + + case 4: // ジョイスティック #1 コントロール + case 5: // ジョイスティック #2 コントロール + case 6: // ジョイスティック #1 ボタン A + case 7: // ジョイスティック #1 ボタン B + putlog(1, "SetPC(%u) <- %u (NOT IMPLEMENTED)", pc, val); + return; + default: + VMPANIC("corrupted pc=%u", pc); + } +} + // // LUNA PIO0 // // 仕様は 0x49000000 から 4バイトだが、 -// 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つ並べた文字列であり、 -// 先頭が本体表記でいう 1番、末尾側が本体表記でいう 8番に対応する。 -// 変数は dipsw{1,2}[0..7] に対応。 -// 設定ファイルと変数の値は %0 が down を表し、%1 が up を表す。 +// LUNA-I では 0x48000000 から 0x4bffffff まで 4バイト単位のミラーが見える。 +// LUNA-88K では 0x48000000 から 0x49ffffff までが +// (こちらはロングワード配置なので) 16バイト単位のミラーに見える。 +// +// PIO から読み出せる値のビットの並び順が機種によって異なる。 +// LUNA-I では DIPSW 1番が LSB、8番が MSB の並び順、 +// LUNA-88K では DIPSW 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-I | #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 +// +----+----+----+----+----+----+----+----+ +// LUNA-88K | #1 | #2 | #3 | #4 | #5 | #6 | #7 | #8 | +// PortA | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | %1=UP, %0=DOWN +// +----+----+----+----+----+----+----+----+ // -// LUNA-1: +// LUNA-I: // #1-1 up なら UNIX をロードして移行。down なら ROM モニタで起動。 // #1-2 up ならディスプレイコンソール。down ならシリアルコンソール。 // #1-3 up - color display, down - force to have monochrome display @@ -191,39 +311,29 @@ PPIDevice::Peek(uint32 addr) // ローダの話。 // どこか由来のソースコードらしいがたぶんビット演算を間違えている。 // -// LUNA88K: +// LUNA-88K: // #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 はユーザ(?) // コンストラクタ PIO0Device::PIO0Device() - : inherited() + : inherited(OBJ_PIO0) { - logname = "pio0"; - devname = "PIO0"; - devaddr = 0x49000000; - devlen = 4; - - // 機種固有パラメータ - - // LUNA-1 と LUNA88K で使いやすいように初期値を変えておく。 - switch (gMainApp.GetVMType()) { - case VMTYPE_LUNA1: - gConfig->SetDefault("luna-dipsw1", "11110111"); - break; - case VMTYPE_LUNA88K: - gConfig->SetDefault("luna-dipsw1", "11111111"); - break; - default: - __unreachable(); + // LUNA-I と LUNA-88K とでポートのビット並びが逆。どうして… + if (gMainApp.IsLUNA88K()) { + msb_first = true; + } else { + msb_first = false; } - // DIP-SW#2 はユーザ定義(?)なので(今の所?)どちらも共通。 - gConfig->SetDefault("luna-dipsw2", "11111111"); - monitor_size = nnSize(22, 11); + // PIO0/PIO1 + monitor = gMonitorManager->Regist(ID_MONITOR_PIO, this); + monitor->func = ToMonitorCallback(&PIO0Device::MonitorUpdate); + monitor->SetSize(76, 15); } // デストラクタ @@ -231,91 +341,120 @@ PIO0Device::~PIO0Device() { } +// 初期化 bool PIO0Device::Init() { - // XXX 本当はラッチのタイミングが違う気もするけど、とりあえずね - - const ConfigItem& item1 = gConfig->Find("luna-dipsw1"); - const std::string& str1 = item1.AsString(); - if (str1.size() != 8) { - item1.Err("must be 8 digits"); + if (inherited::Init() == false) { return false; } - const ConfigItem& item2 = gConfig->Find("luna-dipsw2"); - const std::string& str2 = item2.AsString(); - if (str2.size() != 8) { - item2.Err("must be 8 digits"); - return false; - } + interrupt = GetInterruptDevice(); + pio1 = GetPIO1Device(); + + return true; +} + +// リセット +void +PIO0Device::ResetHard(bool poweron) +{ + auto dipswdev = GetDipswDevice(); + + // XXX 本当は XP から割り込みが上がったタイミングとからしいけど、 + // XP が出来るまでは当面リセットでラッチにしておく。 for (int i = 0; i < 8; i++) { - dipsw1[i] = (str1[i] != '0'); - dipsw2[i] = (str2[i] != '0'); + dipsw1[i] = dipswdev->Get(i); + dipsw2[i] = dipswdev->Get(i + 8); + } + + // XXX どうなる? + for (int i = 0; i < intreq.size(); i++) { + intreq[i] = false; + inten[i] = false; } - return true; } -uint64 -PIO0Device::Read(uint32 addr) +busdata +PIO0Device::ReadPort(uint32 offset) { - uint32 data; + busdata data; - switch (addr) { + switch (offset) { case 0: // PortA - putlog(1, "DIP-SW 1 読み込み"); - return GetPA(); + data = GetPA(); + + // 割り込み要求(High)をクリア + intreq[INT_H] = false; + ChangeInterrupt(); + + putlog(1, "DIP-SW1 -> $%02x", data.Data()); + break; case 1: // PortB - putlog(1, "DIP-SW 2 読み込み"); - return GetPB(); + data = GetPB(); + + // 割り込み要求(Low)をクリア + intreq[INT_L] = false; + ChangeInterrupt(); + + putlog(1, "DIP-SW2 -> $%02x", data.Data()); + break; case 2: // PortC data = GetPC(); - putlog(1, "PC 読み込み -> $%02x", data); - return data; + putlog(1, "PortC -> $%02x", data.Data()); + break; case 3: data = ReadCtrl(); - putlog(1, "コントロール読み込み -> $%02x", data); - return data; + putlog(1, "Ctrl -> $%02x", data.Data()); + break; default: - __unreachable(); + VMPANIC("corrupted offset=%u", offset); } + + // XXX wait? + data |= BusData::Size1; + return data; } -uint64 -PIO0Device::Write(uint32 addr, uint32 data) +busdata +PIO0Device::WritePort(uint32 offset, uint32 data) { - switch (addr) { + 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: - __unreachable(); + VMPANIC("corrupted offset=%u", offset); } + + // XXX wait? + busdata r = BusData::Size1; + return r; } -uint64 -PIO0Device::Peek(uint32 addr) +busdata +PIO0Device::PeekPort(uint32 offset) { - switch (addr) { + switch (offset) { case 0: return GetPA(); @@ -329,45 +468,55 @@ PIO0Device::Peek(uint32 addr) return ReadCtrl(); default: - __unreachable(); + return 0xff; } } +// PIO0 が PIO0/PIO1 のモニタ両方を担当する。 void -PIO0Device::MonitorUpdate(TextScreen& monitor) +PIO0Device::MonitorUpdate(Monitor *, TextScreen& screen) { int y; - monitor.Clear(); + screen.Clear(); + + // PIO0 + screen.Puts(0, 0, "PIO0 (BaseAddr: $49000000)"); + y = MonitorUpdateCommon(screen, 1); + + // PIO0 固有部 + uint32 pa = GetPA(); + uint32 pb = GetPB(); + screen.Print(1, y, "PortA(DIP-SW1)=$%02x:", pa); + screen.Print(1, y + 1, "PortB(DIP-SW2)=$%02x:", pb); + for (int i = 0; i < 8; i++) { + // この図は bit7..0 をこの順で表示するので bitpos の向きは固定。 + int bitpos = 7 - i; + // DIPSW の物理位置 #1 をバイトの下にするか上にするかが機種によって + // 違う。どうして…。 + int label = (msb_first ? i : bitpos) + 1; + + bool d1 = (pa & (1U << bitpos)); + bool d2 = (pb & (1U << bitpos)); + 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; + + uint32 pc = GetPC(); + screen.Print(1, y, "PortC(XP etc.)=$%02x:", pc); + static const char * const pcstr[] = { + "XP RST", "PARITY", "------", "INT5EN", + "INT5RQ", "INT1EN", "------", "INT1RQ", + }; + for (int i = 0; i < 8; i++) { + screen.Puts(21 + i * 7, y, TA::OnOff(pc & (1U << (7 - i))), pcstr[i]); + } + y++; - y = 0; - monitor.Print(0, y++, "PortA(DIPSW1):%c%c%c%c%c%c%c%c", - (dipsw1[0] ? '1' : '0'), - (dipsw1[1] ? '1' : '0'), - (dipsw1[2] ? '1' : '0'), - (dipsw1[3] ? '1' : '0'), - (dipsw1[4] ? '1' : '0'), - (dipsw1[5] ? '1' : '0'), - (dipsw1[6] ? '1' : '0'), - (dipsw1[7] ? '1' : '0')); - monitor.Print(0, y++, "PortB(DIPSW2):%c%c%c%c%c%c%c%c", - (dipsw2[0] ? '1' : '0'), - (dipsw2[1] ? '1' : '0'), - (dipsw2[2] ? '1' : '0'), - (dipsw2[3] ? '1' : '0'), - (dipsw2[4] ? '1' : '0'), - (dipsw2[5] ? '1' : '0'), - (dipsw2[6] ? '1' : '0'), - (dipsw2[7] ? '1' : '0')); - monitor.Puts(0, y++, "PortC"); - monitor.Puts(1, y++, TA::OnOff(xp_reset), "b7: XP Reset"); - monitor.Puts(1, y++, TA::OnOff(parity), "b6: Parity"); - monitor.Puts(1, y++, "b5: ---"); - monitor.Puts(1, y++, TA::OnOff(int5en), "b4: Int5 Enable"); - monitor.Puts(1, y++, TA::OnOff(int5rq), "b3: Int5 Request"); - monitor.Puts(1, y++, TA::OnOff(int1en), "b2: Int1 Enable"); - monitor.Puts(1, y++, "b1: ---"); - monitor.Puts(1, y++, TA::OnOff(int1rq), "b0: Intq Request"); + // PIO1 移譲 + y++; + pio1->MonitorUpdatePIO1(screen, y); } // PortA (DIP-SW 1) 読み込み @@ -378,6 +527,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; } @@ -389,6 +541,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; } @@ -399,46 +554,36 @@ PIO0Device::GetPC() const uint32 data; data = 0x00 // XXX 空きビットは何が読めるか? - | (int1rq ? 0x01 : 0) - | (int1en ? 0x04 : 0) - | (int5rq ? 0x08 : 0) - | (int5en ? 0x10 : 0) - | (parity ? 0x40 : 0) - | (xp_reset?0x80 : 0); + | (intreq[INT_L] ? 0x01 : 0) + | (inten[INT_L] ? 0x04 : 0) + | (intreq[INT_H] ? 0x08 : 0) + | (inten[INT_H] ? 0x10 : 0) + | (parity ? 0x40 : 0) + | (xp_reset ? 0x80 : 0); return data; } // PortC 書き込み void -PIO0Device::SetPC(int pc, int val) +PIO0Device::SetPC(uint pc, uint val) { switch (pc) { case 0: - if ((val ^ int1rq)) { - int1rq = val; - putlog(0, "INT1割り込み要求 %d (未実装)", int1rq); - } + // INT1 割り込み要求 (Read Only) return; case 1: // Not Connected. return; case 2: - if ((val ^ int1en)) { - int1en = val; - putlog(0, "INT1割り込み許可 %d (未実装)", int1en); - } + inten[INT_L] = val; + ChangeInterrupt(); return; case 3: - if ((val ^ int5rq)) { - int5rq = val; - putlog(0, "INT5割り込み要求 %d (未実装)", int5rq); - } + // INT5 割り込み要求 (Read Only) return; case 4: - if ((val ^ int5en)) { - int5en = val; - putlog(0, "INT5割り込み許可 %d (未実装)", int5en); - } + inten[INT_H] = val; + ChangeInterrupt(); return; case 5: // Not Connected. @@ -452,11 +597,48 @@ PIO0Device::SetPC(int pc, int val) // XP リセット if ((val ^ xp_reset)) { xp_reset = val; - putlog(0, "XPリセット %d (未実装)", xp_reset); + putlog(1, "XP Reset %u", xp_reset); + auto mpu64180 = GetMPU64180Device(); + mpu64180->ChangeRESET(xp_reset); } return; + default: + VMPANIC("corrupted pc=%u", pc); } - PANIC("invalid pc=%d", pc); +} + +// とりあえず、XP からここまではエッジトリガーで、 +// ここから割り込みコントローラへはレベルトリガーとしておく。 + +// XP からの割り込み (High) +void +PIO0Device::InterruptXPHigh() +{ + intreq[INT_H] = true; + ChangeInterrupt(); +} + + +// XP からの割り込み (Low) +void +PIO0Device::InterruptXPLow() +{ + intreq[INT_L] = true; + ChangeInterrupt(); +} + +// 割り込み状態を変更。 +// intreq[], inten[] の変更で呼ぶこと。 +void +PIO0Device::ChangeInterrupt() +{ + bool val; + + val = (inten[INT_H] && intreq[INT_H]); + interrupt->ChangeINT(DEVICE_XPINT_HIGH, val); + + val = (inten[INT_L] && intreq[INT_L]); + interrupt->ChangeINT(DEVICE_XPINT_LOW, val); } @@ -464,20 +646,14 @@ PIO0Device::SetPC(int pc, int val) // LUNA PIO1 // // 仕様は 0x4d000000 から 4バイトだが、 -// 0x4c000000 から 0x50000000 の手前まで 4バイト単位でミラーが見える。 +// LUNA-I では 0x4c000000 から 0x4fffffff まで 4バイト単位でミラーが見える。 +// LUNA-88K では 0x4c000000 から 0x4dffffff までが +// (こちらはロングワード配置なので) 16バイト単位のミラーに見える。 // コンストラクタ PIO1Device::PIO1Device() - : inherited() + : inherited(OBJ_PIO1) { - logname = "pio1"; - devname = "PIO1"; - devaddr = 0x4d000000; - devlen = 4; - - poffevent.dev = this; - poffevent.func = (DeviceCallback_t)&PIO1Device::PowerOffCallback; - poffevent.SetName("PIO1 Power Off"); } // デストラクタ @@ -485,57 +661,89 @@ PIO1Device::~PIO1Device() { } +// 初期化 +bool +PIO1Device::Init() +{ + if (inherited::Init() == false) { + return false; + } + + lcd = GetLCDDevice(); + mpu64180 = GetMPU64180Device(); + powerdev = GetPowerDevice(); + + return true; +} + // リセット void -PIO1Device::ResetHard() +PIO1Device::ResetHard(bool poweron) { - inherited::ResetHard(); + inherited::ResetHard(poweron); - poffevent.Stop(); + // 電源ユニットは POWF* 信号が 'L' になると 1msec 後に電源オフになる。 + // 正確には「1msec 以内に 'H' にすれば回避できる」なので、実際に電源が + // オフになるのはそれよりもっと十分長いとは思われる。 + // 8255 リセット時、ポート C の各ピンは入力モードになっているので、 + // ハイインピーダンス。たぶん電源的にも 'H' になってると想像する。 + // なので電源オンで POWF* がハイインピーダンスなだけでは電源オフ + // タイマーは動作しないんじゃないかな。 + power = true; + powerdev->SetSystemPowerOn(power); } -uint64 -PIO1Device::Read(uint32 addr) +busdata +PIO1Device::ReadPort(uint32 offset) { - uint32 data; + busdata data; - switch (addr) { + switch (offset) { case 0: // PIO1 PortA: LCD データ - return gLCD->Read(); + data = lcd->Read(); + break; case 1: // PIO1 PortB: 無効 - return 0xff; + data = 0xff; + break; case 2: // PortC - data = gLCD->Get(); + data = lcd->Get(); data |= xp_intreq ? 0 : 0x02; // 残りのビットは %1 だろうか? data |= 0x0d; - putlog(1, "PC 読み込み -> $%02x", data); - return data; + putlog(1, "PortC -> $%02x", data.Data()); + break; case 3: data = ReadCtrl(); - putlog(1, "コントロール読み込み -> $%02x", data); - return data; + putlog(1, "Ctrl -> $%02x", data.Data()); + break; default: - __unreachable(); + VMPANIC("corrupted offset=%u", offset); } + + // XXX wait? + data |= BusData::Size1; + return data; } -uint64 -PIO1Device::Write(uint32 addr, uint32 data) +busdata +PIO1Device::WritePort(uint32 offset, uint32 data) { - switch (addr) { + switch (offset) { case 0: // PIO1 PortA: LCD データ - gLCD->Write(data); - return 0; + lcd->Write(data); + break; - case 1: // PIO1 PortB: ローカル割り込み + case 1: // PIO1 PortB: XP の INT0 をアサート + // データ不問でアサートする。 + putlog(1, "Assert XP INT0"); + mpu64180->AssertINT0(); break; case 2: // PIO1 PortC: LCD/パワーオフ制御 @@ -543,48 +751,81 @@ PIO1Device::Write(uint32 addr, uint32 da SetPC(i, data & 1); data >>= 1; } - return 0; + break; case 3: // コントロール WriteCtrl(data); - return 0; + break; default: - __unreachable(); + VMPANIC("corrupted offset=%u", offset); } - PANIC("$%08X <- $%02X 未サポート書き込み", addr, data); - return 0; + + // XXX wait? + busdata r = BusData::Size1; + return r; } -uint64 -PIO1Device::Peek(uint32 addr) +busdata +PIO1Device::PeekPort(uint32 offset) { - uint32 data; - - switch (addr) { + switch (offset) { case 0: // PIO1 PortA LCD データ - return gLCD->Peek(); + return lcd->Peek(); case 1: // PIO1 PortB 無効 return 0xff; case 2: // PIO1 PortC - data = gLCD->Get(); - data |= xp_intreq ? 0 : 0x02; - // 残りのビットは %1 だろうか? - data |= 0x0d; - return data; + return GetPC(); case 3: return ReadCtrl(); default: - __unreachable(); + return 0xff; + } +} + +// PIO0 の下請け。 +void +PIO1Device::MonitorUpdatePIO1(TextScreen& screen, int y) +{ + screen.Puts(0, y++, "PIO1 (BaseAddr: $4d000000)"); + + // 共通部分 + y = MonitorUpdateCommon(screen, y++); + + // PIO1 固有部 + screen.Print(1, y++, "PortA(LCDDATA)=$%02x", lcd->Peek()); + screen.Print(1, y++, "PortB(NotConn)=$%02x", 0xff); + uint32 pc = GetPC(); + screen.Print(1, y, "PortC(LCD etc)=$%02x:", pc); + static const char * const pcstr[] = { + "LCD E", "LCD RS", "LCD RD", "!PWOFF", + "------", "------", "!XPINT", "------", + }; + for (int i = 0; i < 8; i++) { + screen.Puts(21 + i * 7, y, TA::OnOff(pc & (1U << (7 - i))), pcstr[i]); } } +uint32 +PIO1Device::GetPC() const +{ + uint32 data; + + data = lcd->Get(); + data |= xp_intreq ? 0 : 0x02; + data |= power ? 0x10 : 0; + // 残りのビットは %1 だろうか? + data |= 0x0d; + + return data; +} + void -PIO1Device::SetPC(int pc, int val) +PIO1Device::SetPC(uint pc, uint val) { switch (pc) { case 0: @@ -596,38 +837,32 @@ PIO1Device::SetPC(int pc, int val) return; case 4: + // 8255 はモード変更 (WriteCtrl) をするとポート C を 'L' にするが、 + // PC4 (POWF*) が 'L' だと電源オフ指示なので、1msec 以内に + // ソフトウェアでこれをキャンセルしないといけない。 + // どうしてこうなった…。 power = val; if (power) { // 電源オフ取り消し - putlog(1, "電源 OFF 取消"); - poffevent.Stop(); + putlog(1, "Power-off canceled"); + powerdev->SetSystemPowerOn(true); } else { // 1msec 後に電源オフ - putlog(1, "電源 OFF 指示"); - poffevent.time = 1_msec; - poffevent.Start(); + putlog(1, "Power-off scheduled"); + powerdev->SetSystemPowerOn(false); } return; case 5: - gLCD->SetRW(val); + lcd->SetRW(val); return; case 6: - gLCD->SetRS(val); + lcd->SetRS(val); return; case 7: - gLCD->SetE(val); + lcd->SetE(val); return; default: - break; + VMPANIC("corrupted pc=%u", pc); } - PANIC("PC%d 未実装", pc); -} - -// 電源 OFF イベントのコールバック -void -PIO1Device::PowerOffCallback(Event& ev) -{ - putlog(0, "電源 OFF"); - gScheduler->RequestPowerOff(); }