--- nono/vm/pio.cpp 2026/04/29 17:05:10 1.1.1.10 +++ nono/vm/pio.cpp 2026/04/29 17:05:38 1.1.1.16 @@ -28,25 +28,22 @@ #include "pio.h" #include "bitops.h" -#include "config.h" +#include "dipsw.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" -// グローバル参照用 -PPIDevice *gPPI; -PIO0Device *gPIO0; -PIO1Device *gPIO1; - // // i8255 // // コンストラクタ -i8255Device::i8255Device(const std::string& objname_) - : inherited(objname_) +i8255Device::i8255Device(uint objid_) + : inherited(objid_) { } @@ -73,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++) { @@ -92,76 +95,127 @@ 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); } } +bool +i8255Device::PokePort(uint32 offset, uint32 data) +{ + 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("PPI") + : inherited(OBJ_PPI) { - devaddr = baseaddr; - devlen = 0x2000; } // デストラクタ PPIDevice::~PPIDevice() { - gPPI = NULL; } -uint64 -PPIDevice::Read(uint32 offset) +busdata +PPIDevice::ReadPort(uint32 offset) { - gMPU->AddCycle(19); // InsideOut p.135 + busdata data; switch (offset) { case 0: case 1: - return 0xff; + data = 0xff; + break; case 2: - putlog(0, "$e9a005 PortC 未実装読み込み"); - return 0xff; + putlog(0, "Read $e9a005 PortC (NOT IMPLEMENTED)"); + data = 0xff; + break; case 3: - VMPANIC("PPI $e9a007 未サポート読み込み"); + putlog(0, "Read $e9a007 Ctrl (NOT IMPLEMENTED)"); + data = 0xff; + break; default: - __unreachable(); + 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) { - gMPU->AddCycle(19); // InsideOut p.135 - 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) { - VMPANIC("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 offset) +busdata +PPIDevice::PeekPort(uint32 offset) { // XXX return 0xff; @@ -169,30 +223,44 @@ PPIDevice::Peek(uint32 offset) // PortC 書き込み void -PPIDevice::SetPC(int pc, int data) +PPIDevice::SetPC(uint pc, uint val) { - VMPANIC("not impl"); + 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番に対応する。 -// 値は "0" が down、"1" が up を表す。 -// -// PIO0Device 内ではこの設定を読み込んで bool dipsw1[8], dipsw2[8] の配列で -// 値を保持している。dipsw1[0] が本体表記1番、dipsw1[7] が本体表記 8番に対応 -// する。値は false が down("0")、true が up("1") に対応する。 +// LUNA-I では 0x48000000 から 0x4bffffff まで 4バイト単位のミラーが見える。 +// LUNA-88K では 0x48000000 から 0x49ffffff までが +// (こちらはロングワード配置なので) 16バイト単位のミラーに見える。 // // PIO から読み出せる値のビットの並び順が機種によって異なる。 -// LUNA-1 では 1番が LSB、8番が MSB の並び順、 -// LUNA88K では 1番が MSB、8番が LSB の並び順。 +// LUNA-I では DIPSW 1番が LSB、8番が MSB の並び順、 +// LUNA-88K では DIPSW 1番が MSB、8番が LSB の並び順。 // 値は、並び順に関わらずどちらも // %0 が false = down = "0"、%1 が true = up = "1" である。 // @@ -215,17 +283,17 @@ PPIDevice::SetPC(int pc, int data) // // b7 b6 b5 b4 b3 b2 b1 b0 // +----+----+----+----+----+----+----+----+ -// LUNA-1 | #8 | #7 | #6 | #5 | #4 | #3 | #2 | #1 | +// 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 // +----+----+----+----+----+----+----+----+ -// LUNA88K | #1 | #2 | #3 | #4 | #5 | #6 | #7 | #8 | +// 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 @@ -243,7 +311,7 @@ PPIDevice::SetPC(int pc, int data) // ローダの話。 // どこか由来のソースコードらしいがたぶんビット演算を間違えている。 // -// LUNA88K: +// LUNA-88K: // #1-1 up なら ROM モニタで停止、down ならマルチユーザブート。 // OpenBSD カーネルも up なら UserKernelConfig で止まる模様?。 // #1-2 up なら外付けシリアルコンソール、down なら内蔵ビットマップ。 @@ -253,125 +321,134 @@ PPIDevice::SetPC(int pc, int data) // コンストラクタ PIO0Device::PIO0Device() - : inherited("PIO0") + : inherited(OBJ_PIO0) { - devaddr = 0x49000000; - devlen = 4; - - // 機種固有パラメータ - - // LUNA-1 と LUNA88K で使いやすいように初期値を変えておく。 - switch (gMainApp.GetVMType()) { - case VMTYPE_LUNA1: - gConfig->SetDefault("luna-dipsw1", "11110111"); - msb_first = false; - break; - case VMTYPE_LUNA88K: - gConfig->SetDefault("luna-dipsw1", "11111111"); + // LUNA-I と LUNA-88K とでポートのビット並びが逆。どうして… + if (gMainApp.IsLUNA88K()) { msb_first = true; - break; - default: - __unreachable(); + } else { + msb_first = false; } - // DIP-SW#2 はユーザ定義(?)なので(今の所?)どちらも共通。 - gConfig->SetDefault("luna-dipsw2", "11111111"); - monitor.func = ToMonitorCallback(&PIO0Device::MonitorUpdate); - monitor.SetSize(22, 11); - monitor.Regist(ID_MONITOR_PIO0); + // PIO0/PIO1 + monitor = gMonitorManager->Regist(ID_MONITOR_PIO, this); + monitor->func = ToMonitorCallback(&PIO0Device::MonitorUpdate); + monitor->SetSize(76, 15); } // デストラクタ PIO0Device::~PIO0Device() { - gPIO0 = NULL; } +// 初期化 bool PIO0Device::Init() { - // XXX 本当はラッチのタイミングが違う気もするけど、とりあえずね + interrupt = GetInterruptDevice(); + pio1 = GetPIO1Device(); - const ConfigItem& item1 = gConfig->Find("luna-dipsw1"); - const std::string& str1 = item1.AsString(); - if (str1.size() != 8) { - item1.Err("must be 8 digits"); - return false; - } + return true; +} - 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; - } +// リセット +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); } - return true; -} -// XXX ResetHard 未調査 + // XXX どうなる? + for (int i = 0; i < intreq.size(); i++) { + intreq[i] = false; + inten[i] = false; + } +} -uint64 -PIO0Device::Read(uint32 offset) +busdata +PIO0Device::ReadPort(uint32 offset) { - uint32 data; + busdata data; 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 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: - __unreachable(); + 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: @@ -387,10 +464,11 @@ PIO0Device::Peek(uint32 offset) return ReadCtrl(); default: - __unreachable(); + return 0xff; } } +// PIO0 が PIO0/PIO1 のモニタ両方を担当する。 void PIO0Device::MonitorUpdate(Monitor *, TextScreen& screen) { @@ -398,34 +476,43 @@ PIO0Device::MonitorUpdate(Monitor *, Tex screen.Clear(); - y = 0; - screen.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')); - screen.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')); - 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++, 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++, TA::Disable, "b1: ---"); - screen.Puts(1, y++, TA::OnOff(int1rq), "b0: Intq Request"); + // 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++; + + // PIO1 移譲 + y++; + pio1->MonitorUpdatePIO1(screen, y); } // PortA (DIP-SW 1) 読み込み @@ -437,7 +524,7 @@ PIO0Device::GetPA() const data |= ((int)dipsw1[i]) << i; } if (msb_first) { - data = bitrev(data); + data = bitrev8(data); } return data & 0xff; } @@ -451,7 +538,7 @@ PIO0Device::GetPB() const data |= ((int)dipsw2[i]) << i; } if (msb_first) { - data = bitrev(data); + data = bitrev8(data); } return data & 0xff; } @@ -463,46 +550,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. @@ -516,11 +593,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); } - VMPANIC("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); } @@ -528,24 +642,30 @@ 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("PIO1") + : inherited(OBJ_PIO1) { - devaddr = 0x4d000000; - devlen = 4; - - poffevent.func = ToEventCallback(&PIO1Device::PowerOffCallback); - poffevent.time = 1_msec; - poffevent.Regist("PIO1 Power Off"); } // デストラクタ PIO1Device::~PIO1Device() { - gPIO1 = NULL; +} + +// 初期化 +bool +PIO1Device::Init() +{ + lcd = GetLCDDevice(); + mpu64180 = GetMPU64180Device(); + powerdev = GetPowerDevice(); + + return true; } // リセット @@ -554,51 +674,68 @@ PIO1Device::ResetHard(bool poweron) { inherited::ResetHard(poweron); - gScheduler->StopEvent(poffevent); + // 電源ユニットは 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 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 offset, uint32 data) +busdata +PIO1Device::WritePort(uint32 offset, uint32 data) { 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/パワーオフ制御 @@ -606,48 +743,81 @@ PIO1Device::Write(uint32 offset, uint32 SetPC(i, data & 1); data >>= 1; } - return 0; + break; case 3: // コントロール WriteCtrl(data); - return 0; + break; default: - __unreachable(); + VMPANIC("corrupted offset=%u", offset); } - VMPANIC("$%08X <- $%02X 未サポート書き込み", gMPU->GetPaddr(), data); - return 0; + + // XXX wait? + busdata r = BusData::Size1; + return r; } -uint64 -PIO1Device::Peek(uint32 offset) +busdata +PIO1Device::PeekPort(uint32 offset) { - uint32 data; - 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: @@ -659,37 +829,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 取消"); - gScheduler->StopEvent(poffevent); + putlog(1, "Power-off canceled"); + powerdev->SetSystemPowerOn(true); } else { // 1msec 後に電源オフ - putlog(1, "電源 OFF 指示"); - gScheduler->RestartEvent(poffevent); + 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); } - VMPANIC("PC%d 未実装", pc); -} - -// 電源オフイベントのコールバック -void -PIO1Device::PowerOffCallback(Event& ev) -{ - putlog(0, "電源 OFF"); - gPower->MakePowerOffExit(); }