--- nono/vm/pio.cpp 2026/04/29 17:04:32 1.1.1.1 +++ nono/vm/pio.cpp 2026/04/29 17:05:10 1.1.1.10 @@ -1,33 +1,52 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// 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 "mystring.h" +#include "mainapp.h" +#include "mpu.h" +#include "power.h" #include "scheduler.h" -#include "config.h" - -// IODevice -// | -// v -// i8255Device (8255 としての共通部分) -// | -// +----------+----------+ -// v v v -// PPIDevice PIO0Device PIO1Device -// (X68030) (LUNA) (LUNA) -std::unique_ptr gPIO0; -std::unique_ptr gPIO1; +// グローバル参照用 +PPIDevice *gPPI; +PIO0Device *gPIO0; +PIO1Device *gPIO1; // // i8255 // // コンストラクタ -i8255Device::i8255Device() +i8255Device::i8255Device(const std::string& objname_) + : inherited(objname_) { } @@ -36,6 +55,17 @@ i8255Device::~i8255Device() { } +// リセット +void +i8255Device::ResetHard(bool poweron) +{ + // clears the control register + // and place ports A, B, and C in input mode. + // The input latches in ports A, B, and C are not cleared. + + ctrl = 0b10011011; +} + // コントロールポートへの書き込み。 void i8255Device::WriteCtrl(uint32 data) @@ -68,23 +98,14 @@ i8255Device::WriteCtrl(uint32 data) } } -void -i8255Device::SetPC(int pc, int data) -{ - // 継承側で用意すること。ここには来ない - PANIC("not impl"); -} - // // X68k PPI // // コンストラクタ PPIDevice::PPIDevice() - : inherited() + : inherited("PPI") { - logname = "pio"; - devname = "PPI"; devaddr = baseaddr; devlen = 0x2000; } @@ -92,42 +113,45 @@ PPIDevice::PPIDevice() // デストラクタ PPIDevice::~PPIDevice() { + gPPI = NULL; } uint64 -PPIDevice::Read(uint32 addr) +PPIDevice::Read(uint32 offset) { - switch (addr) { + gMPU->AddCycle(19); // InsideOut p.135 + + switch (offset) { case 0: case 1: return 0xff; case 2: - PANIC("PPI $E9A005 未サポート読み込み"); - break; + putlog(0, "$e9a005 PortC 未実装読み込み"); + return 0xff; case 3: - PANIC("PPI $E9A007 未サポート読み込み"); - break; + VMPANIC("PPI $e9a007 未サポート読み込み"); default: __unreachable(); } - PANIC("not impl"); } uint64 -PPIDevice::Write(uint32 addr, uint32 data) +PPIDevice::Write(uint32 offset, uint32 data) { - switch (addr) { + gMPU->AddCycle(19); // InsideOut p.135 + + switch (offset) { case 0: case 1: break; case 2: - putlog(0, "$E9A005 未実装書き込み $%02X (無視)", data); + putlog(0, "$e9a005 未実装書き込み $%02X (無視)", data); break; case 3: if (data != 0x92) { - PANIC("PPI $E9A007 未実装書き込み $%02X", data); + VMPANIC("PPI $e9a007 未実装書き込み $%02X", data); } else { - putlog(0, "$E9A007 未実装書き込み $%02x (無視)", data); + putlog(0, "$e9a007 未実装書き込み $%02x (無視)", data); } break; default: @@ -137,12 +161,19 @@ PPIDevice::Write(uint32 addr, uint32 dat } uint64 -PPIDevice::Peek(uint32 addr) +PPIDevice::Peek(uint32 offset) { // XXX return 0xff; } +// PortC 書き込み +void +PPIDevice::SetPC(int pc, int data) +{ + VMPANIC("not impl"); +} + // // LUNA PIO0 // @@ -150,12 +181,51 @@ PPIDevice::Peek(uint32 addr) // 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 モニタで起動。 // #1-2 up ならディスプレイコンソール。down ならシリアルコンソール。 // #1-3 up - color display, down - force to have monochrome display @@ -172,20 +242,49 @@ PPIDevice::Peek(uint32 addr) // あれば /boot がプロンプトで停止。これは ROM ではなく NetBSD のブート // ローダの話。 // どこか由来のソースコードらしいがたぶんビット演算を間違えている。 +// +// LUNA88K: +// #1-1 up なら ROM モニタで停止、down ならマルチユーザブート。 +// OpenBSD カーネルも up なら UserKernelConfig で止まる模様?。 +// #1-2 up なら外付けシリアルコンソール、down なら内蔵ビットマップ。 +// #1-3..#1-7 は不明? +// #1-8 down なら自己診断後電源オフするようだ。 +// #2-x はユーザ(?) // コンストラクタ PIO0Device::PIO0Device() - : inherited() + : inherited("PIO0") { - logname = "pio0"; - devname = "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"); + msb_first = true; + break; + default: + __unreachable(); + } + // DIP-SW#2 はユーザ定義(?)なので(今の所?)どちらも共通。 + gConfig->SetDefault("luna-dipsw2", "11111111"); + + monitor.func = ToMonitorCallback(&PIO0Device::MonitorUpdate); + monitor.SetSize(22, 11); + monitor.Regist(ID_MONITOR_PIO0); } // デストラクタ PIO0Device::~PIO0Device() { + gPIO0 = NULL; } bool @@ -193,14 +292,14 @@ PIO0Device::Init() { // XXX 本当はラッチのタイミングが違う気もするけど、とりあえずね - const ConfigItem& item1 = gConfig->Get("luna-dipsw1"); + 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; } - const ConfigItem& item2 = gConfig->Get("luna-dipsw2"); + const ConfigItem& item2 = gConfig->Find("luna-dipsw2"); const std::string& str2 = item2.AsString(); if (str2.size() != 8) { item2.Err("must be 8 digits"); @@ -213,12 +312,14 @@ PIO0Device::Init() return true; } +// XXX ResetHard 未調査 + uint64 -PIO0Device::Read(uint32 addr) +PIO0Device::Read(uint32 offset) { uint32 data; - switch (addr) { + switch (offset) { case 0: // PortA putlog(1, "DIP-SW 1 読み込み"); @@ -232,7 +333,7 @@ PIO0Device::Read(uint32 addr) case 2: // PortC data = GetPC(); - putlog(1, "PC 読み込み -> $%02x", data); + putlog(1, "PC 読み込み -> $%02x", data); return data; case 3: @@ -246,9 +347,9 @@ PIO0Device::Read(uint32 addr) } uint64 -PIO0Device::Write(uint32 addr, uint32 data) +PIO0Device::Write(uint32 offset, uint32 data) { - switch (addr) { + switch (offset) { case 0: // PIO0 PortA: DIPSW1 (入力のみ) case 1: // PIO0 PortB: DIPSW2 (入力のみ) return 0; @@ -270,9 +371,9 @@ PIO0Device::Write(uint32 addr, uint32 da } uint64 -PIO0Device::Peek(uint32 addr) +PIO0Device::Peek(uint32 offset) { - switch (addr) { + switch (offset) { case 0: return GetPA(); @@ -290,6 +391,43 @@ PIO0Device::Peek(uint32 addr) } } +void +PIO0Device::MonitorUpdate(Monitor *, TextScreen& screen) +{ + int y; + + 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"); +} + // PortA (DIP-SW 1) 読み込み uint32 PIO0Device::GetPA() const @@ -298,6 +436,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; } @@ -309,6 +450,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; } @@ -376,7 +520,7 @@ PIO0Device::SetPC(int pc, int val) } return; } - PANIC("invalid pc=%d", pc); + VMPANIC("invalid pc=%d", pc); } @@ -388,29 +532,37 @@ PIO0Device::SetPC(int pc, int val) // コンストラクタ PIO1Device::PIO1Device() - : inherited() + : inherited("PIO1") { - logname = "pio1"; - devname = "PIO1"; devaddr = 0x4d000000; devlen = 4; - poffevent.dev = this; - poffevent.func = (DeviceCallback_t)&PIO1Device::PowerOffCallback; - poffevent.SetName("PIO1 Power Off"); + poffevent.func = ToEventCallback(&PIO1Device::PowerOffCallback); + poffevent.time = 1_msec; + poffevent.Regist("PIO1 Power Off"); } // デストラクタ PIO1Device::~PIO1Device() { + gPIO1 = NULL; +} + +// リセット +void +PIO1Device::ResetHard(bool poweron) +{ + inherited::ResetHard(poweron); + + gScheduler->StopEvent(poffevent); } uint64 -PIO1Device::Read(uint32 addr) +PIO1Device::Read(uint32 offset) { uint32 data; - switch (addr) { + switch (offset) { case 0: // PIO1 PortA: LCD データ return gLCD->Read(); @@ -439,9 +591,9 @@ PIO1Device::Read(uint32 addr) } uint64 -PIO1Device::Write(uint32 addr, uint32 data) +PIO1Device::Write(uint32 offset, uint32 data) { - switch (addr) { + switch (offset) { case 0: // PIO1 PortA: LCD データ gLCD->Write(data); return 0; @@ -463,16 +615,16 @@ PIO1Device::Write(uint32 addr, uint32 da default: __unreachable(); } - PANIC("$%08X <- $%02X 未サポート書き込み", addr, data); + VMPANIC("$%08X <- $%02X 未サポート書き込み", gMPU->GetPaddr(), data); return 0; } uint64 -PIO1Device::Peek(uint32 addr) +PIO1Device::Peek(uint32 offset) { uint32 data; - switch (addr) { + switch (offset) { case 0: // PIO1 PortA LCD データ return gLCD->Peek(); @@ -511,12 +663,11 @@ PIO1Device::SetPC(int pc, int val) if (power) { // 電源オフ取り消し putlog(1, "電源 OFF 取消"); - poffevent.Stop(); + gScheduler->StopEvent(poffevent); } else { // 1msec 後に電源オフ putlog(1, "電源 OFF 指示"); - poffevent.time = 1_msec; - poffevent.Start(); + gScheduler->RestartEvent(poffevent); } return; case 5: @@ -532,13 +683,13 @@ PIO1Device::SetPC(int pc, int val) default: break; } - PANIC("PC%d 未実装", pc); + VMPANIC("PC%d 未実装", pc); } -// 電源 OFF イベントのコールバック +// 電源オフイベントのコールバック void -PIO1Device::PowerOffCallback(int arg) +PIO1Device::PowerOffCallback(Event& ev) { putlog(0, "電源 OFF"); - gScheduler->RequestPowerOff(); + gPower->MakePowerOffExit(); }