|
|
nono 1.7.0
//
// nono
// Copyright (C) 2022 nono project
// Licensed under nono-license.txt
//
//
// NEWS の各種制御ポート(?)
//
// NWS-1750 の $e100'0000..$e1ff'ffff
//
// +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F'0000
// e100'0000 |TIC PE
// e110'0000 | I2D
// e120'0000 | ASD
// e130'0000 |L2E POW
// e140'0000 |
// e150'0000 |
// e160'0000 |
// e170'0000 |
// e180'0000 |
// e190'0000 |L2D
// e1a0'0000 |PD
// e1b0'0000 |
// e1c0'0000 |(*)
// e1d0'0000 |
// e1e0'0000 |
// e1f0'0000 |
//
// TIC: Timer Interrupt Control
// PE/PD: Parity Enable/Disable
// I2D: Lv2 Interrupt Disable
// ASD: AST Disable
// L2E/L2D: L2 Cache Enable/Disable
// POW: Power Control
// (*): IDROM, DIP-SW, Parity Vector, Interupt Status
//
// また、ここに書いてない $e000'0000 ブロックにあるデバイスも一部担当している。
// newsio.cpp 冒頭のメモリマップも参照。
#include "newsctlr.h"
#include "dipsw.h"
#include "ethernet.h"
#include "event.h"
#include "interrupt.h"
#include "scheduler.h"
#include "syncer.h"
#include "textscreen.h"
// コンストラクタ
NewsCtlrDevice::NewsCtlrDevice()
: inherited(OBJ_NEWSCTLR)
{
// ROM のうち固定のもの
oidrom[0] = 0x0d; // ModelID = NWS-1750
}
// デストラクタ
NewsCtlrDevice::~NewsCtlrDevice()
{
}
// 初期化
bool
NewsCtlrDevice::Init()
{
dipsw = GetDipswDevice();
// クロックの同期方法。
sync_rt = GetSyncer()->GetSyncRealtime();
// ステータス読み出しのためにキャストしたのを持っておく
interrupt = dynamic_cast<NewsInterrupt*>(GetInterruptDevice());
assert(interrupt);
// MAC アドレスを ROM にセット。
MacAddr macaddr;
if (EthernetDevice::GetConfigMacAddr(0, &macaddr, false) == false) {
// エラーメッセージは表示済み
return false;
}
putmsg(1, "macaddr=%s", macaddr.ToString(':').c_str());
memcpy(&oidrom[8], &macaddr, sizeof(macaddr));
auto evman = GetEventManager();
timer_event = evman->Regist(this,
ToEventCallback(&NewsCtlrDevice::TimerCallback),
"SystemTimer");
timer_event->time = TIMER_PERIOD;
return true;
}
void
NewsCtlrDevice::ResetHard(bool poweron)
{
// たぶん止まるだろう。知らんけど。
timer_intr = false;
timer_enable = false;
ChangeInterrupt();
// たぶん消すだろう。知らんけど。
led = 0;
// たぶん電源オンで即起動? 知らんけど。
if (poweron) {
if (sync_rt) {
stime = GetSyncer()->GetRealTime();
}
scheduler->StartEvent(timer_event);
}
}
busdata
NewsCtlrDevice::Read(busaddr addr)
{
uint32 paddr = addr.Addr() | 0xc0000000;
if ((paddr >> 16) == 0xe1c0) {
uint idx = (paddr >> 8) & 3;
busdata data;
switch (idx) {
case 0: // e1c0'00xx
data = GetIDROM(paddr);
putlog(2, "$%08x.B (IDROM) -> $%02x", paddr, data.Data());
break;
case 2: // e1c0'02xx
// 割り込みステータス
data = interrupt->PeekStatus();
putlog(1, "$%08x.B (IntStat) -> $%02x", paddr, data.Data());
break;
default: // e1c0'01xx
// DIP-SW
data = GetDIPSW();
putlog(1, "$%08x.B (DIPSW) -> $%02x", paddr, data.Data());
break;
}
data |= BusData::Size1;
return data;
}
VMPANIC("Read($%08x)", paddr);
}
busdata
NewsCtlrDevice::Write(busaddr addr, uint32 data)
{
uint32 paddr = addr.Addr() | 0xc0000000;
uint32 reqsize = addr.GetSize();
data >>= (reqsize - 1) * 8;
if (paddr == 0xe0c8'0000) {
// PWS-1560 FDC?
putlog(0, "FDC <- $%02x (NOT IMPLEMENTED)", data);
} else if (paddr == 0xe0dc'0000) {
WriteLED(data);
} else if (paddr == 0xe100'0000) {
WriteTimer(data);
} else if (paddr == 0xe108'0000) {
// Parity enable?
putlog(0, "Parity enable? <- $%02x (NOT IMPLEMENTED)", data);
} else if (paddr == 0xe118'0000) {
// Level2 Interrupt ?
putlog(0, "Lv2 Interrupt disable? <- $%02x (NOT IMPLEMENTED)", data);
} else if (paddr == 0xe128'0000) {
putlog(0, "AST disable? <- $%02x (NOT IMPLEMENTED)", data);
} else if (paddr == 0xe130'0000) {
putlog(0, "L2 cache enable? <- $%02x (NOT IMPLEMENTED)", data);
} else if (paddr == 0xe138'0000) {
putlog(0, "Power Control <- $%02x (NOT IMPLEMENTED)", data);
} else if (paddr == 0xe190'0000) {
putlog(0, "L2 cache disable? <- $%02x (NOT IMPLEMENTED)", data);
} else if (paddr == 0xe1a0'0000) {
putlog(0, "Parity disable? <- $%02x (NOT IMPLEMENTED)", data);
} else if (paddr == 0xe1c0'0200) {
// XXX たぶん 0xe1c0'02ff まで全部同じかも
putlog(0, "Parity Vector? <- $%02x (NOT IMPLEMENTED)", data);
} else {
VMPANIC("Write($%08x) <- $%02x", paddr, data);
}
busdata r = BusData::Size1;
return r;
}
busdata
NewsCtlrDevice::Peek1(uint32 addr)
{
addr |= 0xc0000000;
if ((addr >> 16) == 0xe1c0) {
uint idx = (addr >> 8) & 3;
switch (idx) {
case 0: // e1c0'00xx
return GetIDROM(addr);
case 2: // e1c0'02xx
return interrupt->PeekStatus();
default: // e1c0'01xx
return GetDIPSW();
}
}
return BusData::BusErr;
}
// NewsIO の下請けとして呼ばれる。
void
NewsCtlrDevice::MonitorScreenNewsCtlr(TextScreen& screen, int y)
{
// テーブル方式ではないので、Read/Write と手動で揃える…。
uint32 addr = 0xe100;
for (int i = 0; i < 32; i++) {
screen.Print(0, y, "$%04x'0000:", addr);
const char *name = NULL;
TA attr;
for (int j = 0; j < 8; j++) {
switch (addr) {
case 0xe100: name = "Timer"; break;
case 0xe108: name = "ParEn?"; break;
case 0xe118: name = "Lv2Int?"; break;
case 0xe128: name = "ASTDis?"; break;
case 0xe130: name = "L2CE?"; break;
case 0xe138: name = "Power"; break;
case 0xe190: name = "L2CD?"; break;
case 0xe1a0: name = "ParDis?"; break;
case 0xe1c0: name = "Misc"; break;
default:
name = NULL;
break;
}
if (name) {
attr = TA::Normal;
} else {
name = "NopIO";
attr = TA::Disable;
}
screen.Puts(12 + j * 8, y, attr, name);
addr++;
}
y++;
}
}
// IDROM の値を返す。この関数は副作用を起こしてはいけない。
uint8
NewsCtlrDevice::GetIDROM(uint32 addr) const
{
uint32 data;
// $e1c0'00xx はたぶん 256x4bit ROM。
//
// 生データ oidrom[] はバイトで持っておき(つまり128バイト)、
// アクセス時に分解する。
// 実際に使われているのはこのうち先頭の 16バイト(32ニブル)分のようだ。
int n = (addr & 0xff) / 2;
if ((addr & 1) == 0) {
data = oidrom[n] >> 4;
} else {
data = oidrom[n];
}
return data | 0xf0;
}
// DIPSW の状態を読み出す。この関数は副作用を起こしてはいけない。
// SW1 が LSB 側。
// オン(true)なら %0、オフ(false)なら %1。
uint8
NewsCtlrDevice::GetDIPSW() const
{
uint8 data = 0;
for (int i = 0; i < 8; i++) {
if (dipsw->Get(i) == false) {
data |= 1U << i;
}
}
return data;
}
// LED 制御ポート書き込み
void
NewsCtlrDevice::WriteLED(uint32 data)
{
// bit0 が緑 LED、bit1 がオレンジ LED のオンオフらしい。
led = data & 0x03;
putlog(1, "LED <- $%02x", led);
}
// システムタイマー
void
NewsCtlrDevice::WriteTimer(uint32 data)
{
switch (data) {
case 0:
timer_enable = false;
timer_intr = false;
break;
case 1:
timer_enable = true;
break;
default:
VMPANIC("Unknown Timer Control $%02x", data);
break;
}
ChangeInterrupt();
}
// システムタイマーイベント
void
NewsCtlrDevice::TimerCallback(Event *ev)
{
timer_intr = true;
ChangeInterrupt();
// 足す前は前回の割り込み発生時刻なので period を足すと現在時刻。
if (sync_rt) {
stime += TIMER_PERIOD;
scheduler->StartRealtimeEvent(ev, stime, TIMER_PERIOD);
} else {
scheduler->RestartEvent(ev);
}
}
// 割り込み信号線の状態を変える
void
NewsCtlrDevice::ChangeInterrupt()
{
interrupt->ChangeINT(this, (bool)(timer_enable && timer_intr));
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.