|
|
nono 1.7.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
//
// メインバス (共通部分)
//
// メインバス付近に必要な機能は次のようになっている。
//
// Luna News X68k X68kIO XPbus
// InitMainbus o o o o x
// FC access o o o o x
// SwitchBoot o o o o x
// ResetByMPU o o o *1 x (*1:どっちでもいい)
//
// Access Map o o o x x
// HV access o o o x ?
// Peek4 o o o x x
// Singleton o o o x x
//
// もともと FC 付きアクセス機能を Mainbus からX68kIO に伸ばすために
// MainbusDevice を導入したが、一方モニタの登録などでは Mainbus は
// システムに複数存在してはならないため、これらは同時に成立しない。
// そのため、表の上半分の機能を MainbusBaseDevice として独立させ、
// X68kIO はここから派生。表の下半分の機能を MainbusDevice とする。
//
// XPbus はどちらも不要なので (XP プロセッサから見ればメインバスだが、
// ここの Mainbus はそういう意味ではないので) ただの IODevice。
// IODevice
// |
// | +-------------------+
// +--| MainbusBaseDevice | (InitMainbus() と FC アクセスを持つ)
// | +-------------------+
// | |
// | | +---------------+
// | +--| MainbusDevice | (これがメインバス、システムに1つ)
// | | +---------------+
// | | |
// | | | +-----------------+
// | | +--| Mainbus24Device | (上位8ビットがテーブルで表せるメインバス)
// | | | +-----------------+
// | | | |
// | | | +-- LunaMainbus
// | | | | +-- Luna1Mainbus
// | | | | +-- Luna88kMainbus
// | | | +-- NewsMainbus
// | | | +-- Virt68kMainbus
// | | +-- X68kMainbus (上位8ビットが IODevice で表せないため)
// | |
// | +-- X68kIODevice
// |
// +-- XPbusDevice (これはただの IODevice)
#include "mainbus.h"
#include "monitor.h"
//
// メインバスの素質を持つ基本クラス
//
// コンストラクタ
MainbusBaseDevice::MainbusBaseDevice(uint objid_)
: inherited(objid_)
{
}
// デストラクタ
MainbusBaseDevice::~MainbusBaseDevice()
{
}
// MPU の RESET 命令によるリセット
void
MainbusBaseDevice::ResetByMPU()
{
}
// ブートページを切り替える際の共通処理。
// 派生クラスから呼ぶこと。
void
MainbusBaseDevice::SwitchBootPage(bool isrom)
{
// ログ表示
if (isrom) {
putlog(1, "SwitchBootPage ROM (Boot)");
} else {
putlog(1, "SwitchBootPage RAM (Normal)");
}
}
// モニタ表示用にデバイス名を整形。
// 7文字に切り詰めるのと、括弧があれば取り除いて Disable 色にする。
/*static*/ std::pair<std::string, TA>
MainbusBaseDevice::FormatDevName(const Device *dev)
{
std::string name = dev->GetName();
TA attr;
if (name[0] == '(') {
// 括弧付きなら、括弧を取り除いて Disable 色で表示
int len = std::min(7, (int)name.size() - 2);
name = name.substr(1, len);
attr = TA::Disable;
} else {
name = name.substr(0, 7);
attr = TA::Normal;
}
return std::make_pair(name, attr);
}
//
// メインバス (基本クラス)
//
// コンストラクタ
MainbusDevice::MainbusDevice(uint objid_)
: inherited(objid_)
{
accstat_monitor = gMonitorManager->Regist(ID_MONITOR_ACCSTAT, this);
accstat_monitor->SetCallback(&MainbusDevice::MonitorScreenAccStat);
// 表示行数が機種ごとに異なるので継承側でセットしている。
// 通常は 32bit 空間全体 (そうでない機種はコンストラクタで上書きする)
accstat_baseaddr = 0;
accstat_bitlen = 32;
}
// デストラクタ
MainbusDevice::~MainbusDevice()
{
}
// 初期化
bool
MainbusDevice::Init()
{
// アクセス状況の表示用配列。
accstat_rw.resize(AccStat::MAINLEN >> (32 - accstat_bitlen));
return true;
}
// リセット
void
MainbusDevice::ResetHard(bool poweron)
{
inherited::ResetHard(poweron);
std::fill(accstat_read.begin(), accstat_read.end(), 0);
std::fill(accstat_write.begin(), accstat_write.end(), 0);
}
// ハイパーバイザ的読み込み (ミスアライン可)。
// addr はアドレスとサイズを指定すること。
// バスエラーが起きればその時点で BusData::BusErr を返す。
busdata
MainbusDevice::HVReadN(busaddr addr)
{
addr |= BusAddr::SRead;
uint32 reqsize = addr.GetSize();
busdata data;
do {
busdata bd = Read(addr);
if (__predict_false(bd.IsBusErr())) {
return bd;
}
data |= DYNAMIC_BUS_SIZING_R(addr, bd);
} while (addr.GetSize() != 0);
data |= busdata::Size(reqsize);
return data;
}
// ハイパーバイザ的読み込み
busdata
MainbusDevice::HVRead1(uint32 paddr)
{
busaddr addr = busaddr(paddr) | BusAddr::Size1;
return HVReadN(addr);
}
// ハイパーバイザ的読み込み (ミスアライン可)
busdata
MainbusDevice::HVRead2(uint32 paddr)
{
busaddr addr = busaddr(paddr) | BusAddr::Size2;
return HVReadN(addr);
}
// ハイパーバイザ的読み込み (ミスアライン可)
busdata
MainbusDevice::HVRead4(uint32 paddr)
{
busaddr addr = busaddr(paddr) | BusAddr::Size4;
return HVReadN(addr);
}
// ハイパーバイザ的書き込み (ミスアライン可)。
// addr はアドレスとサイズを指定すること。属性は SRead 固定。
// バスエラーが起きればその時点で busdata::BusErr を返す。
busdata
MainbusDevice::HVWriteN(busaddr addr, uint32 data)
{
addr |= BusAddr::SWrite;
do {
busdata r = Write(addr, data);
if (__predict_false(r.IsBusErr())) {
return r;
}
DYNAMIC_BUS_SIZING_W(addr, data, r);
} while (addr.GetSize() != 0);
return 0;
}
// ハイパーバイザ的書き込み
busdata
MainbusDevice::HVWrite1(uint32 paddr, uint32 data)
{
busaddr addr = busaddr(paddr) | BusAddr::Size1;
return HVWriteN(addr, data);
}
// ハイパーバイザ的書き込み (ミスアライン可)
busdata
MainbusDevice::HVWrite2(uint32 paddr, uint32 data)
{
busaddr addr = busaddr(paddr) | BusAddr::Size2;
return HVWriteN(addr, data);
}
// ハイパーバイザ的書き込み (ミスアライン可)
busdata
MainbusDevice::HVWrite4(uint32 paddr, uint32 data)
{
busaddr addr = busaddr(paddr) | BusAddr::Size4;
return HVWriteN(addr, data);
}
// 連続する4バイトを Peek する。(ミスアライン可)
uint64
MainbusDevice::Peek4(uint32 addr)
{
uint64 data;
data = Peek1(addr++) << 24;
data |= Peek1(addr++) << 16;
data |= Peek1(addr++) << 8;
data |= Peek1(addr);
return data;
}
void
MainbusDevice::MonitorScreenAccStat(Monitor *, TextScreen& screen)
{
std::array<char, 64 + 5> buf;
#define UPDATE_RW(rw_ptr) do { \
uint64 dst = *(uint64 *)(rw_ptr); \
dst <<= 2; \
dst &= 0xfcfcfcfc'fcfcfcfc; \
dst |= src; \
*(uint64 *)(rw_ptr) = dst; \
} while (0)
// R|W をマージしながらコピー。
if (__predict_true(accstat_rw.size() == AccStat::MAINLEN)) {
// 全域。
for (int i = 0; i < AccStat::MAINLEN; i += 8) {
uint64 src = *(uint64 *)&accstat_read[i]
| *(uint64 *)&accstat_write[i];
UPDATE_RW(&accstat_rw[i]);
}
} else {
// NEWS なら表示に使うのは前 1/4 のみで、後ろはミラー。
// ただしアドレス表記は $c000'0000 以降(後ろの 1/4)。
uint offset = accstat_rw.size();
uint ndiv = accstat_read.size() / offset;
for (int i = 0, iend = accstat_rw.size(); i < iend; i += 8) {
uint64 src = 0;
for (int j = 0; j < ndiv; j++) {
src |= *(uint64 *)&accstat_read[i + j * offset];
src |= *(uint64 *)&accstat_write[i + j * offset];
}
UPDATE_RW(&accstat_rw[i]);
}
}
// 読んだのでリセット。
std::fill(accstat_read.begin(), accstat_read.end(), 0);
std::fill(accstat_write.begin(), accstat_write.end(), 0);
screen.Clear();
static_assert(AccStat::SHIFT >= 20, "");
screen.Print(0, 0,
"Shows %ubit space. %uMB/char. 'R':Read, 'W':Write, 'A':Read+Write",
accstat_bitlen, 1U << (AccStat::SHIFT - 20));
screen.Print(12, 1,
"+$00 +$01 +$02 +$03 +$04 +$05 +$06 +$07");
uint32 baseaddr = accstat_baseaddr;
for (int y = 0, yend = accstat_rw.size() / 64; y < yend; y++) {
const uint8 *a = &accstat_rw[y * 64];
uint8 avail = accstat_avail[y];
char *d = &buf[0];
std::fill(buf.begin(), buf.end() - 1, ' ');
for (int i = 0; i < 8; i++) {
// 先頭に空白を入れといて、参照時は buf+1 から参照する。
if ((i & 1) == 0) {
d++;
}
if ((int8)avail < 0) {
// 何かしらデバイスがある。16MB、8文字分。
for (int j = 0; j < 8; j++) {
uint op = *a & 3;
*d++ = ".RWA"[op];
a++;
}
} else {
// この 16MB には何のデバイスもない。
d += 8;
a += 8;
}
avail <<= 1;
}
*d = '\0';
screen.Print(0, y + 2, "$%02x00'0000: %s",
baseaddr + y * 0x08, &buf[1]);
}
}
//
// 上位8ビットがテーブルで表せる構造のメインバス。
//
// コンストラクタ (オブジェクト名指定なし)
Mainbus24Device::Mainbus24Device()
: MainbusDevice(OBJ_MAINBUS)
{
monitor = gMonitorManager->Regist(ID_MONITOR_MAINBUS, this);
monitor->SetCallback(&Mainbus24Device::MonitorScreen);
monitor->SetSize(75, 33);
}
Mainbus24Device::~Mainbus24Device()
{
}
// アクセス状況のエリア情報初期化。
// 継承側の InitMainbus() の終わりで呼ぶこと。
void
Mainbus24Device::InitAccStat()
{
for (int i = 0; i < accstat_avail.size(); i++) {
uint bits = 0;
for (int j = 0; j < 8; j++) {
int n = i * 8 + j;
int id = devtable[n]->GetId();
bits <<= 1;
if (id != OBJ_BUSERR && id != OBJ_NOPIO) {
bits |= 1;
}
}
accstat_avail[i] = bits;
}
// デバッグ用
if (0) {
for (int i = 0; i < accstat_avail.size(); i++) {
printf("%02x: ", i);
uint8 a = accstat_avail[i];
for (int j = 0; j < 8; j++) {
if ((int8)a < 0) {
printf("1");
} else {
printf("0");
}
a <<= 1;
}
printf("\n");
}
}
}
inline IODevice *
Mainbus24Device::Decoder(uint32 addr) const
{
return devtable[addr >> 24];
}
busdata
Mainbus24Device::Read(busaddr addr)
{
uint32 pa = addr.Addr();
lastaddr = pa;
accstat_read[pa >> AccStat::SHIFT] = AccStat::READ;
IODevice *dev = Decoder(pa);
return dev->Read(addr);
}
busdata
Mainbus24Device::Write(busaddr addr, uint32 data)
{
uint32 pa = addr.Addr();
lastaddr = pa;
accstat_write[pa >> AccStat::SHIFT] = AccStat::WRITE;
IODevice *dev = Decoder(pa);
return dev->Write(addr, data);
}
busdata
Mainbus24Device::ReadBurst16(busaddr addr, uint32 *dst)
{
uint32 pa = addr.Addr();
lastaddr = pa; // 開始アドレスしか残らない…
IODevice *dev = Decoder(pa);
busdata r = dev->ReadBurst16(addr, dst);
if (__predict_true(r.IsBusErr() == false)) {
accstat_read[pa >> AccStat::SHIFT] = AccStat::READ;
}
return r;
}
busdata
Mainbus24Device::WriteBurst16(busaddr addr, const uint32 *src)
{
uint32 pa = addr.Addr();
lastaddr = pa; // 開始アドレスしか残らない…
IODevice *dev = Decoder(pa);
busdata r = dev->WriteBurst16(addr, src);
if (__predict_true(r.IsBusErr() == false)) {
accstat_write[pa >> AccStat::SHIFT] = AccStat::WRITE;
}
return r;
}
busdata
Mainbus24Device::Peek1(uint32 addr)
{
IODevice *dev = Decoder(addr);
return dev->Peek1(addr);
}
bool
Mainbus24Device::Poke1(uint32 addr, uint32 data)
{
IODevice *dev = Decoder(addr);
return dev->Poke1(addr, data);
}
void
Mainbus24Device::MonitorScreen(Monitor *, TextScreen& screen)
{
screen.Clear();
// 012345678901234567890
// $0000'0000: 1234567
for (int i = 0; i < 8; i++) {
screen.Print(12 + i * 8, 0, "+$0%x", i);
}
int idx = 0;
for (int i = 0; i < devtable.size() / 8; i++) {
screen.Print(0, i + 1, "$%02x00'0000:", idx);
for (int j = 0; j < 8; j++) {
auto pair = FormatDevName(devtable[idx]);
const std::string& name = pair.first;
TA attr = pair.second;
screen.Print(12 + j * 8, i + 1, attr, "%s", name.c_str());
idx++;
}
}
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.