File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / ppi.cpp
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:04:29 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v002, HEAD
nono 0.0.2

//
// nono
// Copyright (C) 2017 [email protected]
//

#include "ppi.h"
#include "lcd.h"
#include "mystring.h"
#include "scheduler.h"
#include "configfile.h"

//           IODevice
//               |
//               v
//          i8255Device  (8255 としての共通部分)
//               |
//    +----------+----------+
//    v          v          v
// PPIDevice  PIO0Device PIO1Device
// (X68030)   (LUNA)     (LUNA)

//
// i8255
//

// コンストラクタ
i8255Device::i8255Device()
{
	logname = "ppi";
}

// デストラクタ
i8255Device::~i8255Device()
{
}

// コントロールポートへの書き込み。
void
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");

		// モード設定すると PC はゼロクリアされる
		for (int pc = 0; pc < 8; pc++) {
			SetPC(pc, 0);
		}
	} else {
		// portC 出力
		//
		// 0  x  x  x  P  P  P  V
		//             |  |  |  +-- 書き込むビットデータ
		//             +--+--+----- 書き込むビット位置

		int pc = (data >> 1) & 7;
		int val = data & 1;
		SetPC(pc, val);
	}
}

void
i8255Device::SetPC(int pc, int data)
{
	// 継承側で用意すること。ここには来ない
	PANIC("not impl");
}

//
// X68k PPI
//

// コンストラクタ
PPIDevice::PPIDevice()
	: inherited()
{
	devname = "PPI";
	devaddr = baseaddr;
	devlen  = 0x2000;
}

// デストラクタ
PPIDevice::~PPIDevice()
{
}

uint64
PPIDevice::Read8(uint32 addr)
{
	if (addr % 2 == 0)
		return (uint64)-1;

	int n = (addr - baseaddr) / 2;
	switch (n) {
	 case 0:
	 case 1:
		return 0xff;
	 case 2:
		PANIC("PPI $E9A005 未サポート読み込み");
		break;
	 case 3:
		PANIC("PPI $E9A007 未サポート読み込み");
		break;
	}
	PANIC("not impl");
}

uint64
PPIDevice::Read16(uint32 addr)
{
	return 0xff00 | Read8(addr + 1);
}

uint64
PPIDevice::Write8(uint32 addr, uint32 data)
{
	if (addr % 2 == 0)
		return (uint64)-1;

	int n = (addr - baseaddr) / 2;
	switch (n) {
	 case 0:
	 case 1:
		break;
	 case 2:
		putlog(0, "$E9A005 未実装書き込み $%02X (無視)", data);
		break;
	 case 3:
		if (data != 0x92) {
			PANIC("PPI $E9A007 未実装書き込み $%02X", data);
		} else {
			putlog(0, "$E9A007 未実装書き込み $%02x (無視)", data);
		}
		break;
	 default:
		// XXX
		return (uint64)-1;
	}
	return 0;
}

uint64
PPIDevice::Write16(uint32 addr, uint32 data)
{
	return Write8(addr + 1, data);
}

uint64
PPIDevice::Peek8(uint32 addr)
{
	// XXX
	return 0xff;
}

//
// 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 を表す。
//
// #1-1 up なら UNIX をロードして移行。down なら ROM モニタで起動。
// #1-2 up ならディスプレイコンソール。down ならシリアルコンソール。
// #1-3 up - color display, down - force to have monochrome display
// #1-4 up - no write verification,
//      down - verification on every harddisk write operation
// #1-5 up - OS is UniOS-U (SystemV/COFF),
//      down - OS is UniOS (4.3BSD/a.out OMAGIC)
// #1-6 down - force monochrome display?
// #1-7 up - boot from local device, down - boot from network
// #1-8 up - normal boot, down - start diagnostics と書いてあるが
//      down にすると画面真っ暗で詳細不明。
//
// #2-x すべて up にすると /boot が自動的にカーネルを起動。一つでも down が
//      あれば /boot がプロンプトで停止。これは ROM ではなく NetBSD のブート
//      ローダの話。
//      どこか由来のソースコードらしいがたぶんビット演算を間違えている。

// コンストラクタ
PIO0Device::PIO0Device()
	: inherited()
{
	devname = "PIO0";
	devaddr = 0x49000000;
	devlen  = 4;
}

// デストラクタ
PIO0Device::~PIO0Device()
{
}

bool
PIO0Device::Init()
{
	// XXX 本当はラッチのタイミングが違う気もするけど、とりあえずね
	const std::string str1 = gConfig->ReadStr("luna_dipsw1");
	if (str1.size() != 8) {
		gConfig->Err("luna_dipsw1", "invalid length %d", (int)str1.size());
		return false;
	}
	const std::string str2 = gConfig->ReadStr("luna_dipsw2");
	if (str2.size() != 8) {
		gConfig->Err("luna_dipsw2", "invalid length %d", (int)str2.size());
		return false;
	}
	for (int i = 0; i < 8; i++) {
		dipsw1[i] = (str1[i] != '0');
		dipsw2[i] = (str2[i] != '0');
	}
	return true;
}

uint64
PIO0Device::Read8(uint32 addr)
{
	uint32 data;

	// アドレスは 4バイト単位でミラーになっている
	uint32 reg = addr & 3;
	switch (reg) {
	 case 0:
		// PortA: DIP-SW 1
		putlog(1, "DIP-SW 1 読み込み");
		data = 0;
		for (int i = 0; i < 8; i++) {
			data |= ((int)dipsw1[i]) << i;
		}
		return data & 0xff;

	 case 1:
		// PortB: DIP-SW 2
		putlog(1, "DIP-SW 2 読み込み");
		data = 0;
		for (int i = 0; i < 8; i++) {
			data |= ((int)dipsw2[i]) << i;
		}
		return data & 0xff;

	 case 2:
		// PortC
		data = 0x00	// XXX 空きビットは何が読めるか?
			 | (int1rq ? 0x01 : 0)
			 | (int1en ? 0x04 : 0)
			 | (int5rq ? 0x08 : 0)
			 | (int5en ? 0x10 : 0)
			 | (parity ? 0x40 : 0)
			 | (xp_reset?0x80 : 0);
		putlog(1, "PC 読み込み -> $%02x", data); 
		return data;

	 case 3:
		data = ReadCtrl();
		putlog(1, "コントロール読み込み -> $%02x", data);
		return data;

	 default:
		__unreachable();
	}
}

uint64
PIO0Device::Write8(uint32 addr, uint32 data)
{
	// アドレスは 4バイト単位でミラーになっている
	uint32 reg = addr & 3;
	switch (reg) {
	 case 0:	// PIO0 PortA: DIPSW1 (入力のみ)
	 case 1:	// PIO0 PortB: DIPSW2 (入力のみ)
		return 0;
	 case 2:	// PIO0 PortC: ホスト割り込み制御
		for (int i = 0; i < 8; i++) {
			SetPC(i, data & 1);
			data >>= 1;
		}
		return 0;
	 case 3:	// コントロール
		WriteCtrl(data);
		return 0;
	 default:
		__unreachable();
	}
}

void
PIO0Device::SetPC(int pc, int val)
{
	switch (pc) {
	 case 0:
		if ((val ^ int1rq)) {
			int1rq = val;
			putlog(0, "INT1割り込み要求 %d (未実装)", int1rq);
		}
		return;
	 case 1:
		// Not Connected.
		return;
	 case 2:
		if ((val ^ int1en)) {
			int1en = val;
			putlog(0, "INT1割り込み許可 %d (未実装)", int1en);
		}
		return;
	 case 3:
		if ((val ^ int5rq)) {
			int5rq = val;
			putlog(0, "INT5割り込み要求 %d (未実装)", int5rq);
		}
		return;
	 case 4:
		if ((val ^ int5en)) {
			int5en = val;
			putlog(0, "INT5割り込み許可 %d (未実装)", int5en);
		}
		return;
	 case 5:
		// Not Connected.
		return;
	 case 6:
		// パリティ有効/無効
		// 使わないので無視するが読み返しのために覚えておく
		parity = val;
		return;
	 case 7:
		// XP リセット
		if ((val ^ xp_reset)) {
			xp_reset = val;
			putlog(0, "XPリセット %d (未実装)", xp_reset);
		}
		return;
	}
	PANIC("invalid pc=%d", pc);
}


//
// LUNA PIO1
//
// 仕様は 0x4d000000 から 4バイトだが、
// 0x4c000000 から 0x50000000 の手前まで 4バイト単位でミラーが見える。

// コンストラクタ
PIO1Device::PIO1Device()
	: inherited()
{
	devname = "PIO1";
	devaddr = 0x4d000000;
	devlen  = 4;

	poffevent.dev = this;
	poffevent.func = (DeviceCallback_t)&PIO1Device::PowerOffCallback;
	poffevent.SetName("PIO1 Power Off");
}

// デストラクタ
PIO1Device::~PIO1Device()
{
}

uint64
PIO1Device::Read8(uint32 addr)
{
	// アドレスは 4バイト単位でミラーになっている
	uint32 reg = addr & 3;
	uint32 data;

	switch (reg) {
	 case 0:
		// PIO1 PortA: LCD データ
		return gLCD->Read();

	 case 1:
		// PIO1 PortB: 無効
		return 0xff;

	 case 2:
		// PortC
		data = gLCD->Get();
		data |= xp_intreq ? 0 : 0x02;
		// 残りのビットは %1 だろうか?
		data |= 0x0d;
		putlog(1, "PC 読み込み -> $%02x", data);
		return data;

	 case 3:
		data = ReadCtrl();
		putlog(1, "コントロール読み込み -> $%02x", data);
		return data;

	 default:
		__unreachable();
	}
}

uint64
PIO1Device::Write8(uint32 addr, uint32 data)
{
	// アドレスは 4バイト単位でミラーになっている
	uint32 reg = addr & 3;
	switch (reg) {
	 case 0:	// PIO1 PortA: LCD データ
		gLCD->Write(data);
		return 0;
	 case 1:	// PIO1 PortB: ローカル割り込み
		break;
	 case 2:	// PIO1 PortC: LCD/パワーオフ制御
		for (int i = 0; i < 8; i++) {
			SetPC(i, data & 1);
			data >>= 1;
		}
		return 0;
	 case 3:	// コントロール
		WriteCtrl(data);
		return 0;
	 default:
		break;
	}
	PANIC("$%08X <- $%02X 未サポート書き込み", addr, data);
	return 0;
}

void
PIO1Device::SetPC(int pc, int val)
{
	switch (pc) {
	 case 0:
	 case 1:
	 case 2:
	 case 3:
		// PortC の下半分は入力ポートとして使われることを想定して
		// いるので、ここへの書き込みは起きないだろうし、無視する。
		return;

	 case 4:
		power = val;
		if (power) {
			// 電源オフ取り消し
			putlog(1, "電源 OFF 取消");
			gScheduler->DelEvent(&poffevent);
		} else {
			// 1msec 後に電源オフ
			putlog(1, "電源 OFF 指示");
			poffevent.time = 1_msec;
			gScheduler->AddEvent(&poffevent);
		}
		return;
	 case 5:
		gLCD->SetRW(val);
		return;
	 case 6:
		gLCD->SetRS(val);
		return;
	 case 7:
		gLCD->SetE(val);
		return;

	 default:
		break;
	}
	PANIC("PC%d 未実装", pc);
}

// 電源 OFF イベントのコールバック
void
PIO1Device::PowerOffCallback(int arg)
{
	putlog(0, "電源 OFF");
	gScheduler->RequestPowerOff();
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.