--- nono/vm/busio.h 2026/04/29 17:04:31 1.1 +++ nono/vm/busio.h 2026/04/29 17:05:29 1.1.1.9 @@ -1,313 +1,463 @@ // // nono -// Copyright (C) 2020 isaki@NetBSD.org +// 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 BusIO_B_Base: public T +class BusIO_B_Base : public T { private: - inline uint32 Decoder(uint32 addr) { + inline uint32 Decoder(busaddr addr) const { + return Decoder(addr.Addr()); + } + inline uint32 Decoder(uint32 addr) const { return (addr >> rshift) & (T::NPORT - 1); } public: - virtual uint64 Read8(uint32 addr) { - return T::Read(Decoder(addr)); + 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); } - virtual uint64 Write8(uint32 addr, uint32 data) { - return T::Write(Decoder(addr), data); + busdata Peek1(uint32 addr) override { + return T::PeekPort(Decoder(addr)); } - virtual uint64 Peek8(uint32 addr) { - return T::Peek(Decoder(addr)); + bool Poke1(uint32 addr, uint32 data) override { + return T::PokePort(Decoder(addr), data); } }; // バイトデバイスをバイトのまま配置する。 // NPORT によるミラーを行う以外はこれを使わないのと何も変わらない。 template -class BusIO_B: public BusIO_B_Base { }; +class BusIO_B : public BusIO_B_Base { }; // バイトデバイスをワードにミラー配置する。 template -class BusIO_BB: public BusIO_B_Base { }; +class BusIO_BB : public BusIO_B_Base { }; // バイトデバイスをロングワードにミラー配置する。 template -class BusIO_BBBB: public BusIO_B_Base { }; +class BusIO_BBBB : public BusIO_B_Base { }; // ワードデバイスをワードのまま配置する。 // NPORT によるミラーを行う以外はこれを使わないのと何も変わらない。 template -class BusIO_W: public T +class BusIO_W : public T { private: - inline uint32 Decoder(uint32 addr) { + 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: - virtual uint64 Read8(uint32 addr) { - if ((addr & 1) == 0) { - return T::Read(Decoder(addr)) >> 8; - } else { - return T::Read(Decoder(addr)) & 0xff; - } - } - virtual uint64 Read16(uint32 addr) { - return T::Read(Decoder(addr)); + busdata Read(busaddr addr) override { + return T::ReadPort(Decoder(addr)); } - virtual uint64 Write8(uint32 addr, uint32 data) { - PANIC("not implemented\n"); - return (int64)-1; // XXX 未確認 - } - virtual uint64 Write16(uint32 addr, uint32 data) { - return T::Write(Decoder(addr), data); + 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); } - - virtual uint64 Peek8(uint32 addr) { + busdata Peek1(uint32 addr) override { if ((addr & 1) == 0) { - return T::Peek(Decoder(addr)) >> 8; + return T::PeekPort(Decoder(addr)) >> 8; } else { - return T::Peek(Decoder(addr)) & 0xff; + return T::PeekPort(Decoder(addr)) & 0xff; } } }; -/* XXX: stub -class BusIO_L: public T -*/ - -// バイトデバイスを偶数アドレスに配置し、奇数アドレスはバスエラー。 +// ロングワードデバイスをロングワードのまま m68k バスに配置する。 template -class BusIO_BE: public T +class BusIO_L : public T { private: - inline uint32 Decoder(uint32 addr) { - return (addr >> 1) & (T::NPORT - 1); + inline uint32 Decoder(busaddr addr) const { + return Decoder(addr.Addr()); + } + inline uint32 Decoder(uint32 addr) const { + return (addr >> 2) & (T::NPORT - 1); } public: - virtual uint64 Read8(uint32 addr) { - if ((addr & 1) == 0) { - return T::Read(Decoder(addr)); - } else { - return (int64)-1; - } + BusIO_L() + : T() + { } - virtual uint64 Read16(uint32 addr) { - return (T::Read(Decoder(addr)) << 8) | 0xff; + BusIO_L(uint arg1) + : T(arg1) + { + } + BusIO_L(uint arg1, uint arg2) + : T(arg1, arg2) + { } - virtual uint64 Write8(uint32 addr, uint32 data) { - if ((addr & 1) == 0) { - return T::Write(Decoder(addr), data); - } else { - return (int64)-1; - } + busdata Read(busaddr addr) override { + return T::ReadPort(Decoder(addr)); } - virtual uint64 Write16(uint32 addr, uint32 data) { - return T::Write(Decoder(addr), data >> 8); + + busdata Write(busaddr addr, uint32 data) override { + data = BusIO::DataMultiplexer_Write_L(addr, data); + return T::WritePort(Decoder(addr), data); } - virtual uint64 Peek8(uint32 addr) { - if ((addr & 1) == 0) { - return T::Peek(Decoder(addr)); - } else { - return (int64)-1; + 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 BusIO_EB: public T +class BusIO_EB : public T { private: - inline uint32 Decoder(uint32 addr) { + inline uint32 Decoder(busaddr addr) const { + return Decoder(addr.Addr()); + } + inline uint32 Decoder(uint32 addr) const { return (addr >> 1) & (T::NPORT - 1); } public: - virtual uint64 Read8(uint32 addr) { - if ((addr & 1) == 0) { - return (int64)-1; - } else { - return T::Read(Decoder(addr)); - } - } - virtual uint64 Read16(uint32 addr) { - return T::Read(Decoder(addr)) | 0xff00; - } - - virtual uint64 Write8(uint32 addr, uint32 data) { - if ((addr & 1) == 0) { - return (int64)-1; + 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 { - return T::Write(Decoder(addr), data); + if (offset == 0) { + data = op1; + } else { + data = op0; + } } - } - virtual uint64 Write16(uint32 addr, uint32 data) { - return T::Write(Decoder(addr), data & 0xff); + r = T::WritePort(Decoder(addr), data); + r.ChangeSize(2); + return r; } - virtual uint64 Peek8(uint32 addr) { + busdata Peek1(uint32 addr) override { if ((addr & 1) == 0) { - return (int64)-1; + return BusData::BusErr; } else { - return T::Peek(Decoder(addr)); + return T::PeekPort(Decoder(addr)); } } }; -// バイトデバイスをロングワードの先頭に配置し、他は非接続。 +// バイトデバイスを奇数アドレスに配置し、偶数アドレスは非接続。 template -class BusIO_BFFF: public T +class BusIO_FB : public T { private: - inline uint32 Decoder(uint32 addr) { - return (addr >> 2) & (T::NPORT - 1); - } - - public: - virtual uint64 Read8(uint32 addr) { - if ((addr & 3) == 0) { - return T::Read(Decoder(addr)); - } else { - return 0xff; - } + inline uint32 Decoder(busaddr addr) const { + return Decoder(addr.Addr()); } - virtual uint64 Read16(uint32 addr) { - if ((addr & 2) == 0) { - return (T::Read(Decoder(addr)) << 8) | 0xff; - } else { - return 0xffff; - } - } - virtual uint64 Read32(uint32 addr) { - return (T::Read(Decoder(addr)) << 24) | 0x00ffffff; + inline uint32 Decoder(uint32 addr) const { + return (addr >> 1) & (T::NPORT - 1); } - virtual uint64 Write8(uint32 addr, uint32 data) { - if ((addr & 3) == 0) { - return T::Write(Decoder(addr), data); - } else { - return 0; - } - } - virtual uint64 Write16(uint32 addr, uint32 data) { - if ((addr & 2) == 0) { - return T::Write(Decoder(addr), data >> 8); + 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 { - return 0; + if (offset == 0) { + data = op1; + } else { + data = op0; + } } - } - virtual uint64 Write32(uint32 addr, uint32 data) { - return T::Write(Decoder(addr), data >> 24); + r = T::WritePort(Decoder(addr), data); + r.ChangeSize(2); + return r; } - virtual uint64 Peek8(uint32 addr) { - if ((addr & 3) == 0) { - return T::Peek(Decoder(addr)); - } else { + busdata Peek1(uint32 addr) override { + if ((addr & 1) == 0) { return 0xff; + } else { + return T::PeekPort(Decoder(addr)); } } }; -// ワードデバイスをロングワードにミラー配置する。 +// バイトデバイスをロングワードの先頭に配置し、他は非接続。 template -class BusIO_WW: public T +class BusIO_BFFF : public T { private: - inline uint32 Decoder(uint32 addr) { + inline uint32 Decoder(busaddr addr) const { + return Decoder(addr.Addr()); + } + inline uint32 Decoder(uint32 addr) const { return (addr >> 2) & (T::NPORT - 1); } + public: - virtual uint64 Read8(uint32 addr) { - if ((addr & 1) == 0) { - return T::Read(Decoder(addr)) >> 8; - } else { - return T::Read(Decoder(addr)) & 0xff; - } - } - virtual uint64 Read16(uint32 addr) { - return T::Read(Decoder(addr)); + 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; } - virtual uint64 Write8(uint32 addr, uint32 data) { - PANIC("not implemented\n"); - return (int64)-1; // XXX 未確認 - } - virtual uint64 Write16(uint32 addr, uint32 data) { - return T::Write(Decoder(addr), data); + busdata Peek1(uint32 addr) override { + if ((addr & 3) == 0) { + return T::PeekPort(Decoder(addr)); + } else { + return 0xff; + } } - virtual uint64 Peek8(uint32 addr) { - if ((addr & 1) == 0) { - return T::Peek(Decoder(addr)) >> 8; + bool Poke1(uint32 addr, uint32 data) override { + if ((addr & 3) == 0) { + return T::PokePort(Decoder(addr), data); } else { - return T::Peek(Decoder(addr)) & 0xff; + // 非接続なので、書き換えようとしてもエラーも起きず何も起きない。 + return true; } } }; // ワードデバイスをロングワード先頭に配置し、他は非接続。 template -class BusIO_WF: public T +class BusIO_WF : public T { private: - inline uint32 Decoder(uint32 addr) { + inline uint32 Decoder(busaddr addr) const { + return Decoder(addr.Addr()); + } + inline uint32 Decoder(uint32 addr) const { return (addr >> 2) & (T::NPORT - 1); } public: - virtual uint64 Read8(uint32 addr) { - switch (addr & 3) { - case 0: - return T::Read(Decoder(addr)) >> 8; - case 1: - return T::Read(Decoder(addr)) & 0xff; - case 2: - case 3: - return 0xff; - } - __unreachable(); - } - virtual uint64 Read16(uint32 addr) { - if ((addr & 2) == 0) { - return T::Read(Decoder(addr)); - } else { - return 0xffff; - } + busdata Read(busaddr addr) override { + busdata bd = T::ReadPort(Decoder(addr)); + bd <<= 16; + bd |= 0xffff; + bd.ChangeSize(4); + return bd; } - virtual uint64 Write8(uint32 addr, uint32 data) { - PANIC("not implemented\n"); - return 0; // XXX 未確認 - } - virtual uint64 Write16(uint32 addr, uint32 data) { - if ((addr & 2) == 0) { - return T::Write(Decoder(addr), data); + 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 { - return 0; + // バイト書き込みなら全バイトがコピー。 + data |= data << 8; } + busdata r = T::WritePort(Decoder(addr), data); + r.ChangeSize(4); + return r; } - virtual uint64 Peek8(uint32 addr) { + + busdata Peek1(uint32 addr) override { switch (addr & 3) { case 0: - return T::Peek(Decoder(addr)) >> 8; + return T::PeekPort(Decoder(addr)) >> 8; case 1: - return T::Peek(Decoder(addr)) & 0xff; + return T::PeekPort(Decoder(addr)) & 0xff; case 2: case 3: return 0xff; @@ -325,66 +475,81 @@ template class BusIO_X68kSPC : public T { private: - inline uint64 Decoder(uint32 addr) { + 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: - virtual uint64 Read8(uint32 addr) { - if ((addr & 1) == 0) { - return (uint64)-1; + 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 { - addr = Decoder(addr); - if (addr < 0x10) { - // 前半16ポートは SASI 跡地。 - return 0xff; + // 後半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 { - // 後半16ポートは SCSI。 - return T::Read(addr - 0x10); + 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; } - } - } - virtual uint64 Read16(uint32 addr) { - addr = Decoder(addr); - if (addr < 0x10) { - // 前半16ポートは SASI 跡地 - return 0xffff; - } else { - return T::Read(addr - 0x10) | 0xff00; - } - } - - virtual uint64 Write8(uint32 addr, uint32 data) { - if ((addr & 1) == 0) { - return (uint64)-1; } else { - addr = Decoder(addr); - if (addr < 0x10) { - // 前半16ポートは SASI 跡地。 - // XXX 書き込みはどうなる? - return 0; + if (offset == 0) { + data = op1; } else { - // 後半16ポートは SCSI。 - return T::Write(addr - 0x10, data); + data = op0; } } - } - virtual uint64 Write16(uint32 addr, uint32 data) { - addr = Decoder(addr); - if (addr < 0x10) { + uint32 pa = Decoder(addr); + if (pa < 0x10) { // 前半16ポートは SASI 跡地。 // XXX 書き込みはどうなる? - return 0; } else { - // 後半16ポートは SCSI。 - return T::Write(addr - 0x10, data & 0xff); + r = T::WritePort(pa - 0x10, data); } + r.ChangeSize(2); + return r; } - virtual uint64 Peek8(uint32 addr) { + busdata Peek1(uint32 addr) override { if ((addr & 1) == 0) { - return (uint64)-1; + return BusData::BusErr; } else { addr = Decoder(addr); if (addr < 0x10) { @@ -392,7 +557,7 @@ class BusIO_X68kSPC : public T return 0xff; } else { // 後半16ポートは SCSI。 - return T::Peek(addr - 0x10); + return T::PeekPort(addr - 0x10); } } }