File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / busio.h
Revision 1.1.1.10 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:42 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, v026, v025, v024, v023, HEAD
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);
			}
		}
	}
};

unix.superglobalmegacorp.com

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