|
|
nono 1.3.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
// バスとデバイスとのインタフェース。
// バスに対し、デバイスがどのように接続されているかを記述する。
// Poke1() はここで定義しなければ基底クラスの Device::Poke1() が呼ばれて
// 全域編集不可になる。BusIO を経由する必要があるデバイスはたいていは全域が
// 編集不可なので、通常この基底の動作でよく、Poke1() を再定義する必要はない。
// MK48T02Device のみ BusIO 経由で編集可能な領域を持っているため、これを
// 受け取る BusIO_B と BusIO_BFFF だけ Poke1() を持っている。
// 従って BusIO_B、BusIO_BFFF の親になるデバイスは全員 PokePort() も実装する
// 必要がある。うーん。
#pragma once
#if !defined(SELFTEST)
#include "device.h"
#endif
// テンプレート外のヘルパー関数。
class BusIO
{
static inline uint32 M4(uint32 a, uint32 b, uint32 c, uint32 d) {
return ((a << 24) | (b << 16) | (c << 8) | d);
}
public:
// アドレス(とサイズ情報からなる) addr と書き込みデータ data から
// m68030 のバスに出るはずのデータを構成する。68030UM.pdf p7-11。
// m88200 ではこのうち7つ(reqsize==2 なら offset==0 か 2 のみ、等)
// しか使わないが、そのケースに限れば m68030 と同じ動作になる。
static inline uint32
DataMultiplexer_Write_L(const busaddr addr, uint32 data)
{
uint32 reqsize = addr.GetSize();
uint32 offset = addr.Addr() & 3U;
uint32 op0 = (data >> 24) & 0xff;
uint32 op1 = (data >> 16) & 0xff;
uint32 op2 = (data >> 8) & 0xff;
uint32 op3 = data & 0xff;
if (reqsize == 4) {
switch (offset) {
case 0: data = M4(op0, op1, op2, op3); break;
case 1: data = M4(op0, op0, op1, op2); break;
case 2: data = M4(op0, op1, op0, op1); break;
case 3: data = M4(op0, op0, op1, op0); break;
default:
__unreachable();
}
} else if (reqsize == 3) {
switch (offset) {
case 0: data = M4(op1, op2, op3, op0); break;
case 1: data = M4(op1, op1, op2, op3); break;
case 2: data = M4(op1, op2, op1, op2); break;
case 3: data = M4(op1, op1, op2, op1); break;
default:
__unreachable();
}
} else if (reqsize == 2) {
if ((offset & 1U) == 0) {
data = M4(op2, op3, op2, op3);
} else {
data = M4(op2, op2, op3, op2);
}
} else {
data = M4(op3, op3, op3, op3);
}
return data;
}
};
// バイトデバイスをバイト、ワード、ロングワードポートにミラー配置する共通部
template <class T, int rshift>
class BusIO_B_Base : public T
{
private:
inline uint32 Decoder(busaddr addr) const {
return Decoder(addr.Addr());
}
inline uint32 Decoder(uint32 addr) const {
return (addr >> rshift) & (T::NPORT - 1);
}
public:
busdata Read(busaddr addr) override {
return T::ReadPort(Decoder(addr));
}
busdata Write(busaddr addr, uint32 data) override {
data >>= (addr.GetSize() - 1) * 8;
return T::WritePort(Decoder(addr), data);
}
busdata Peek1(uint32 addr) override {
return T::PeekPort(Decoder(addr));
}
bool Poke1(uint32 addr, uint32 data) override {
return T::PokePort(Decoder(addr), data);
}
};
// バイトデバイスをバイトのまま配置する。
// NPORT によるミラーを行う以外はこれを使わないのと何も変わらない。
template <class T>
class BusIO_B : public BusIO_B_Base<T, 0> { };
// バイトデバイスをワードにミラー配置する。
template <class T>
class BusIO_BB : public BusIO_B_Base<T, 1> { };
// バイトデバイスをロングワードにミラー配置する。
template <class T>
class BusIO_BBBB : public BusIO_B_Base<T, 2> { };
// ワードデバイスをワードのまま配置する。
// NPORT によるミラーを行う以外はこれを使わないのと何も変わらない。
template <class T>
class BusIO_W : public T
{
private:
inline uint32 Decoder(busaddr addr) const {
return Decoder(addr.Addr());
}
inline uint32 Decoder(uint32 addr) const {
return (addr >> 1) & (T::NPORT - 1);
}
static inline uint32 M2(uint32 a, uint32 b) {
return ((a) << 8) | b;
}
public:
busdata Read(busaddr addr) override {
return T::ReadPort(Decoder(addr));
}
busdata Write(busaddr addr, uint32 data) override {
uint32 reqsize = addr.GetSize();
uint32 offset = addr.Addr() & 1;
uint32 op0 = (data >> 24) & 0xff;
uint32 op1 = (data >> 16) & 0xff;
uint32 op2 = (data >> 8) & 0xff;
uint32 op3 = data & 0xff;
if (reqsize == 1) {
data = M2(op3, op3);
} else if (reqsize == 2) {
if (offset == 0) {
data = M2(op2, op3);
} else {
data = M2(op2, op2);
}
} else if (reqsize == 3) {
if (offset == 0) {
data = M2(op1, op2);
} else {
data = M2(op1, op1);
}
} else {
if (offset == 0) {
data = M2(op0, op1);
} else {
data = M2(op0, op0);
}
}
return T::WritePort(Decoder(addr), data);
}
busdata Peek1(uint32 addr) override {
if ((addr & 1) == 0) {
return T::PeekPort(Decoder(addr)) >> 8;
} else {
return T::PeekPort(Decoder(addr)) & 0xff;
}
}
};
// ロングワードデバイスをロングワードのまま m68k バスに配置する。
template <class T>
class BusIO_L : public T
{
private:
inline uint32 Decoder(busaddr addr) const {
return Decoder(addr.Addr());
}
inline uint32 Decoder(uint32 addr) const {
return (addr >> 2) & (T::NPORT - 1);
}
public:
BusIO_L<T>()
: T()
{
}
explicit BusIO_L<T>(uint arg1)
: T(arg1)
{
}
BusIO_L<T>(uint arg1, uint arg2)
: T(arg1, arg2)
{
}
busdata Read(busaddr addr) override {
return T::ReadPort(Decoder(addr));
}
busdata Write(busaddr addr, uint32 data) override {
data = BusIO::DataMultiplexer_Write_L(addr, data);
return T::WritePort(Decoder(addr), data);
}
busdata Peek1(uint32 addr) override {
busdata data = T::PeekPort(Decoder(addr));
if (data.IsBusErr()) {
return data;
}
data >>= (3 - (addr & 3U)) * 8;
data &= 0xff;
return data;
}
};
// バイトデバイスを偶数アドレスに配置し、奇数アドレスはバスエラー。
//class BusIO_BE
// バイトデバイスを奇数アドレスに配置し、偶数アドレスはバスエラー。
template <class T>
class BusIO_EB : public T
{
private:
inline uint32 Decoder(busaddr addr) const {
return Decoder(addr.Addr());
}
inline uint32 Decoder(uint32 addr) const {
return (addr >> 1) & (T::NPORT - 1);
}
public:
busdata Read(busaddr addr) override {
if (addr.GetSize() == 1 && (addr.Addr() & 1) == 0) {
return BusData::BusErr;
}
busdata bd = T::ReadPort(Decoder(addr));
bd |= 0xff00;
bd.ChangeSize(2);
return bd;
}
busdata Write(busaddr addr, uint32 data) override {
busdata r;
uint32 reqsize = addr.GetSize();
uint32 offset = addr.Addr() & 1;
uint32 op0 = (data >> 24) & 0xff;
uint32 op1 = (data >> 16) & 0xff;
uint32 op2 = (data >> 8) & 0xff;
uint32 op3 = data & 0xff;
if (reqsize == 1) {
if (offset == 0) {
return BusData::BusErr;
} else {
data = op3;
}
} else if (reqsize == 2) {
if (offset == 0) {
data = op3;
} else {
data = op2;
}
} else if (reqsize == 3) {
if (offset == 0) {
data = op2;
} else {
data = op1;
}
} else {
if (offset == 0) {
data = op1;
} else {
data = op0;
}
}
r = T::WritePort(Decoder(addr), data);
r.ChangeSize(2);
return r;
}
busdata Peek1(uint32 addr) override {
if ((addr & 1) == 0) {
return BusData::BusErr;
} else {
return T::PeekPort(Decoder(addr));
}
}
};
// バイトデバイスを奇数アドレスに配置し、偶数アドレスは非接続。
template <class T>
class BusIO_FB : public T
{
private:
inline uint32 Decoder(busaddr addr) const {
return Decoder(addr.Addr());
}
inline uint32 Decoder(uint32 addr) const {
return (addr >> 1) & (T::NPORT - 1);
}
public:
busdata Read(busaddr addr) override {
busdata bd;
if (addr.GetSize() == 1 && (addr.Addr() & 1) == 0) {
bd = 0xff;
} else {
bd = T::ReadPort(Decoder(addr));
}
bd |= 0xff00;
bd.ChangeSize(2);
return bd;
}
busdata Write(busaddr addr, uint32 data) override {
busdata r;
uint32 reqsize = addr.GetSize();
uint32 offset = addr.Addr() & 1;
uint32 op0 = (data >> 24) & 0xff;
uint32 op1 = (data >> 16) & 0xff;
uint32 op2 = (data >> 8) & 0xff;
uint32 op3 = data & 0xff;
if (reqsize == 1) {
if (offset == 0) {
r.ChangeSize(2);
return r;
} else {
data = op3;
}
} else if (reqsize == 2) {
if (offset == 0) {
data = op3;
} else {
data = op2;
}
} else if (reqsize == 3) {
if (offset == 0) {
data = op2;
} else {
data = op1;
}
} else {
if (offset == 0) {
data = op1;
} else {
data = op0;
}
}
r = T::WritePort(Decoder(addr), data);
r.ChangeSize(2);
return r;
}
busdata Peek1(uint32 addr) override {
if ((addr & 1) == 0) {
return 0xff;
} else {
return T::PeekPort(Decoder(addr));
}
}
};
// バイトデバイスをロングワードの先頭に配置し、他は非接続。
template <class T>
class BusIO_BFFF : public T
{
private:
inline uint32 Decoder(busaddr addr) const {
return Decoder(addr.Addr());
}
inline uint32 Decoder(uint32 addr) const {
return (addr >> 2) & (T::NPORT - 1);
}
public:
busdata Read(busaddr addr) override {
busdata bd = T::ReadPort(Decoder(addr));
bd <<= 24;
bd |= 0x00ffffff;
bd.ChangeSize(4);
return bd;
}
busdata Write(busaddr addr, uint32 data) override {
data >>= (addr.GetSize() - 1) * 8;
busdata r = T::WritePort(Decoder(addr), data);
r.ChangeSize(4);
return r;
}
busdata Peek1(uint32 addr) override {
if ((addr & 3) == 0) {
return T::PeekPort(Decoder(addr));
} else {
return 0xff;
}
}
bool Poke1(uint32 addr, uint32 data) override {
if ((addr & 3) == 0) {
return T::PokePort(Decoder(addr), data);
} else {
// 非接続なので、書き換えようとしてもエラーも起きず何も起きない。
return true;
}
}
};
// ワードデバイスをロングワード先頭に配置し、他は非接続。
template <class T>
class BusIO_WF : public T
{
private:
inline uint32 Decoder(busaddr addr) const {
return Decoder(addr.Addr());
}
inline uint32 Decoder(uint32 addr) const {
return (addr >> 2) & (T::NPORT - 1);
}
public:
busdata Read(busaddr addr) override {
busdata bd = T::ReadPort(Decoder(addr));
bd <<= 16;
bd |= 0xffff;
bd.ChangeSize(4);
return bd;
}
busdata Write(busaddr addr, uint32 data) override {
if (addr.GetSize() == 4) {
data >>= 16;
} else if (addr.GetSize() == 2) {
// ワード書き込みならデータバスは MSB LSB MSB LSB。(88200 p5-27)
} else {
// バイト書き込みなら全バイトがコピー。
data |= data << 8;
}
busdata r = T::WritePort(Decoder(addr), data);
r.ChangeSize(4);
return r;
}
busdata Peek1(uint32 addr) override {
switch (addr & 3) {
case 0:
return T::PeekPort(Decoder(addr)) >> 8;
case 1:
return T::PeekPort(Decoder(addr)) & 0xff;
case 2:
case 3:
return 0xff;
}
__unreachable();
}
};
// X680x0 の SPC を収容するための専用バスアダプタ。
// ここは SASI 跡地 32バイト分と SCSI 用地 32バイト分を合わせた 64 バイト
// (0x40 バイト) でのミラーになっているため、BusIO_EB では表現できない。
// SASI 跡地は実際には読み出しパターンがあるようだが、本題ではないので
// とりあえず全部 0xff を返しておく。
template <class T>
class BusIO_X68kSPC : public T
{
private:
inline uint32 Decoder(busaddr addr) const {
return Decoder(addr.Addr());
}
inline uint32 Decoder(uint32 addr) const {
// SASI 跡地と SCSI を合わせて 0x40 バイトでミラーしている
return (addr >> 1) & (0x20 - 1);
}
public:
busdata Read(busaddr addr) override {
if (addr.GetSize() == 1 && (addr.Addr() & 1) == 0) {
return BusData::BusErr;
}
uint32 offset = Decoder(addr);
busdata bd;
if (offset < 0x10) {
// 前半16ポートは SASI 跡地。
bd = 0xff;
} else {
// 後半16ポートは SCSI。
bd = T::ReadPort(offset - 0x10);
}
bd |= 0xff00;
bd.ChangeSize(2);
return bd;
}
busdata Write(busaddr addr, uint32 data) override {
busdata r;
uint32 reqsize = addr.GetSize();
uint32 offset = addr.Addr() & 1;
uint32 op0 = (data >> 24) & 0xff;
uint32 op1 = (data >> 16) & 0xff;
uint32 op2 = (data >> 8) & 0xff;
uint32 op3 = data & 0xff;
if (reqsize == 1) {
if (offset == 0) {
return BusData::BusErr;
} else {
data = op3;
}
} else if (reqsize == 2) {
if (offset == 0) {
data = op3;
} else {
data = op2;
}
} else if (reqsize == 3) {
if (offset == 0) {
data = op2;
} else {
data = op1;
}
} else {
if (offset == 0) {
data = op1;
} else {
data = op0;
}
}
uint32 pa = Decoder(addr);
if (pa < 0x10) {
// 前半16ポートは SASI 跡地。
// XXX 書き込みはどうなる?
} else {
r = T::WritePort(pa - 0x10, data);
}
r.ChangeSize(2);
return r;
}
busdata Peek1(uint32 addr) override {
if ((addr & 1) == 0) {
return BusData::BusErr;
} else {
addr = Decoder(addr);
if (addr < 0x10) {
// 前半16ポートは SASI 跡地。
return 0xff;
} else {
// 後半16ポートは SCSI。
return T::PeekPort(addr - 0x10);
}
}
}
};
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.