|
|
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();
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.