--- nono/vm/bus.h 2026/04/29 17:04:35 1.1.1.3 +++ nono/vm/bus.h 2026/04/29 17:05:43 1.1.1.9 @@ -1,30 +1,400 @@ // // nono -// Copyright (C) 2020 nono project +// Copyright (C) 2024 nono project // Licensed under nono-license.txt // +// +// バス +// + #pragma once -#include "device.h" +#include "nono.h" + +// busaddr はアドレスバスにいくつかの情報を追加した構造。 +// +// : 6 : 5 : 4: 3:3 0 +// 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-- ... --+ +// |B| |T|P| |Size | |R| |S|I|D|S| Address | +// |E| |S|A| | | |W| |U| | |U| (32bit) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-- ... --+ +// +// 32ビット目と35ビット目はどちらも S/U で同じ値を入れる。m68k の FC2 に相当。 +// 33ビット目は D (データ空間)。m68k の FC0 に相当。 +// 34ビット目は I (命令/プログラム空間)。m68k の FC1 に相当。 +// これにより、 +// o 下位 33 ビットで S/U と32ビットアドレスが表現できる。 +// o 下位 35 ビットで FC(順不同) と32ビットアドレスが表現できる。 +// o 上位ワードを >>1 すると下位3ビットが FC2-0 (同順) になる。 +// +// 39ビット目は R/W (Read なら 1)。これは SSW で使う。 +// 上位ワードを >>1 して下位 7 ビットを取り出すと SSW (の FC と R/W) になる。 +// +// 48,49,50ビット目は要求バイトサイズ (1 - 4)。 +// +// 63ビット目 (BE) は (アドレス変換時の) バスエラーを示す。 +// +// 以下はデバッガなどで使用する追加のビットで、実行系では使わない。 +// 56ビット目 (PA) はこのアドレスを物理アドレスとする場合は %1 を指定する。 +// 論理アドレスまたは区別が不要な場合は %0 にする。 +// 論理アドレスと物理アドレスの両方を受け取るメモリダンプとかで使う。 +// 57ビット目 (TS: Table Search Result) は、このアドレスを求めるのに +// テーブルサーチを行った場合は %1。 + +// 定数 +enum class BusAddr : uint64 +{ + D = 0x0000'0002'0000'0000ULL, // データ空間 + I = 0x0000'0004'0000'0000ULL, // 命令空間 + S = 0x0000'0009'0000'0000ULL, // スーパーバイザ空間 + U = 0x0000'0000'0000'0000ULL, // ユーザ空間 + R = 0x0000'0080'0000'0000ULL, // 読み込み + W = 0x0000'0000'0000'0000ULL, // 書き込み + + Fetch = I | R, + Read = D | R, + Write = D | W, + SRead = S | Read, + SWrite = S | Write, + FCMask = 0x0000'000f'0000'0000ULL, + + Size1 = 0x0001'0000'0000'0000ULL, + Size2 = 0x0002'0000'0000'0000ULL, + Size3 = 0x0003'0000'0000'0000ULL, + Size4 = 0x0004'0000'0000'0000ULL, + SizeMask = 0x0007'0000'0000'0000ULL, + + // デバッガ用 + Physical = 0x0100'0000'0000'0000ULL, + Logical = 0x0000'0000'0000'0000ULL, + TableSearched = 0x0200'0000'0000'0000ULL, + + BusErr = 0x8000'0000'0000'0000ULL, +}; + +// BusAddr に対する OR 演算子 (まだ busaddr は出せない) +static __unused BusAddr operator|(const BusAddr& lhs, const BusAddr& rhs) { + return (BusAddr)((uint64)lhs | (uint64)rhs); +} +static __unused BusAddr operator|(const BusAddr& lhs, const uint64& rhs) { + return (BusAddr)((uint64)lhs | rhs); +} +static __unused BusAddr operator|(const uint64& lhs, const BusAddr& rhs) { + return (BusAddr)(lhs | (uint64)rhs); +} + +class busaddr +{ + private: + uint64 value {}; + + public: + // ファンクションコードから busaddr を作成。fc_ は 0-7 にすること。 + static constexpr BusAddr FC(uint32 fc_) noexcept { + return (BusAddr)(((uint64)fc_ << 33) | ((uint64)(fc_ & 4) << 30)); + } + // アクセスサイズ + static constexpr BusAddr Size(uint n) noexcept { + return (BusAddr)((uint64)n << 48); + } + // S/U + static constexpr BusAddr SU(bool super_) noexcept { + return super_ ? BusAddr::S : BusAddr::U; + } + + public: + busaddr() noexcept { + } + + explicit busaddr(uint64 value_) noexcept { + value = value_; + } + + // enum からの自動変換のため explicit つけない。 + busaddr(BusAddr value_) noexcept { + value = (uint64)value_; + } + + // コピーコンストラクタを自前で定義すると、 + // このクラスがレジスタ渡しから参照渡しになるようなので、 + // コピーコンストラクタはデフォルトのまま使う。 + + uint64 Get() const noexcept { return value; } + uint32 GetH() const noexcept { return (uint32)(value >> 32); } + uint32 GetL() const noexcept { return (uint32)value; } + uint32 Addr() const noexcept { return (uint32)value; } + // S/U ビットを足した計33ビット + uint64 GetSAddr() const noexcept + { return value & 0x1'ffff'ffffULL; } + uint32 GetFC() const noexcept { return (value >> 33) & 7; } + uint32 GetSSW() const noexcept { return (value >> 33) & 0x47; } + uint32 GetSize() const noexcept { return (value >> 48) & 7; } + bool IsSuper() const noexcept { return (value & (uint64)BusAddr::S); } + bool IsData() const noexcept { return (value & (uint64)BusAddr::D); } + bool IsWrite() const noexcept + { return (value & (uint64)BusAddr::R) == 0; } + bool IsPhysical() const noexcept + { return (value & (uint64)BusAddr::Physical); } + bool IsTableSearched() const noexcept + { return (value & (uint64)BusAddr::TableSearched); } + bool IsBusErr() const noexcept + { return (value & (uint64)BusAddr::BusErr); } + + // Change*() は特定のフィールドだけ差し替える + + void ChangeAddr(uint32 addr_) noexcept { + value &= 0xffffffff'00000000; + value |= addr_; + } + void ChangeSuper(bool super_) noexcept { + if (super_) { + value |= (uint64)BusAddr::S; + } else { + value &= ~(uint64)BusAddr::S; + } + } + void ChangeFC(uint32 fc_) noexcept { + value &= ~(uint64)BusAddr::FCMask; + value |= (uint64)busaddr::FC(fc_); + } + void ChangeData(bool data_) noexcept { + value &= ~(uint64)(BusAddr::I | BusAddr::D); + if (data_) { + value |= (uint64)BusAddr::D; + } else { + value |= (uint64)BusAddr::I; + } + } + void ChangeWrite(bool write_) noexcept { + if (write_) { + value &= ~(uint64)BusAddr::R; + } else { + value |= (uint64)BusAddr::R; + } + } + void ChangeSize(uint32 n) noexcept { + value &= ~(uint64)BusAddr::SizeMask; + value |= (uint64)Size(n); + } + + public: + // addr |= (busaddr) で FC とかのビットが足せると楽。 + busaddr& operator|=(const busaddr& rhs) { + value |= rhs.value; + return *this; + } + busaddr& operator|=(const uint32& mask_) { + ChangeAddr(Addr() | mask_); + return *this; + } + // addr &= mask でマスクが適用できると楽。 + busaddr& operator&=(const uint32& mask_) { + ChangeAddr(Addr() & mask_); + return *this; + } + busaddr& operator|=(const BusAddr& rhs) { + value |= (uint64)rhs; + return *this; + } + busaddr& operator&=(const BusAddr& rhs) { + value &= (uint64)rhs; + return *this; + } + + // addr += n, -= n でアドレス進められると楽。 + busaddr& operator+=(const uint32& val) { + ChangeAddr(Addr() + val); + return *this; + } + busaddr& operator-=(const uint32& val) { + ChangeAddr(Addr() - val); + return *this; + } + // addr++ でアドレス進められると楽。 + busaddr operator++(int n) { + busaddr old(value); + ChangeAddr(Addr() + 1); + return old; + } + // ++addr でアドレス進められると楽。 + busaddr& operator++() { + ChangeAddr(Addr() + 1); + return *this; + } +}; + +// busaddr の等価演算子 +static __unused bool operator==(const busaddr& lhs, const busaddr& rhs) { + return (lhs.Get() == rhs.Get()); +} +static __unused bool operator!=(const busaddr& lhs, const busaddr& rhs) { + return !(lhs == rhs); +} + +// busaddr に対する OR 演算子 +static __unused busaddr operator|(const busaddr& lhs, const uint64& rhs) { + return busaddr(lhs.Get() | rhs); +} +static __unused busaddr operator|(const busaddr& lhs, const busaddr& rhs) { + return lhs | rhs.Get(); +} +static __unused busaddr operator|(const busaddr& lhs, const BusAddr& rhs) { + return lhs | (busaddr)rhs; +} +static __unused busaddr operator|(const BusAddr& lhs, const busaddr& rhs) { + return (busaddr)lhs | rhs; +} -extern uint64 vm_phys_read_8(uint32 addr); -extern uint64 vm_phys_read_16(uint32 addr); -extern uint64 vm_phys_read_32(uint32 addr); -extern uint64 vm_phys_write_8(uint32 addr, uint32 data); -extern uint64 vm_phys_write_16(uint32 addr, uint32 data); -extern uint64 vm_phys_write_32(uint32 addr, uint32 data); -extern uint64 vm_phys_peek_8(uint32 addr); -static inline uint64 vm_phys_peek_32(uint32 addr) +// busdata はデータバスにいくつかの応答情報を追加した構造。 +// +// : 6 : 5 : 4: 3:3 0 +// 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-- ... --+ +// |B|R| |Size | Wait [nsec] | Data | +// |E|T| | | | (32bit) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-- ... --+ +// +// BE (63ビット目) はバスエラー。 +// RT (62ビット目) はリトライ。SPC で使う。 +// 48,49,50ビット目は応答サイズ。デバイスが処理したバイト数を 1 - 4 で返す。 + +// 定数 +enum class BusData : uint64 { - uint64 data; + OK = 0, + // BusErr は Data() 部分を $ffffffff に見せておく。Peek*() でも使う。 + BusErr = 0x8000'0000'ffff'ffffULL, + Retry = 0x4000'0000'0000'0000ULL, + + Size1 = (uint64)BusAddr::Size1, + Size2 = (uint64)BusAddr::Size2, + Size3 = (uint64)BusAddr::Size3, + Size4 = (uint64)BusAddr::Size4, + SizeMask = (uint64)BusAddr::SizeMask, +}; - data = vm_phys_peek_8(addr + 0) << 24; - data |= vm_phys_peek_8(addr + 1) << 16; - data |= vm_phys_peek_8(addr + 2) << 8; - data |= vm_phys_peek_8(addr + 3); - return data; +// BusData に対する OR 演算子 (まだ busdata は出せない) +static __unused BusData operator|(const BusData& lhs, const BusData& rhs) { + return (BusData)((uint64)lhs | (uint64)rhs); +} +static __unused BusData operator|(const BusData& lhs, const uint64& rhs) { + return (BusData)((uint64)lhs | rhs); +} +static __unused BusData operator|(const uint64& lhs, const BusData& rhs) { + return (BusData)(lhs | (uint64)rhs); } -extern IODevice *devtable[256]; +class busdata +{ + private: + uint64 value {}; + + public: + // ウェイト + static constexpr BusData Wait(uint32 nsec) noexcept { + return (BusData)((uint64)nsec << 32); + } + // データサイズ + static constexpr BusData Size(uint n) noexcept { + return (BusData)((uint64)n << 48); + } + + private: + // バスエラーかどうか判定するのに使うのは BusErrBit のほう。 + enum : uint64 { + BusErrBit = 0x8000'0000'0000'0000ULL, + RetryBit = (uint64)BusData::Retry, + Mask = BusErrBit | RetryBit, + }; + + public: + busdata() noexcept { + } + + // uint32 の値をそのまま busdata 型に代入したいので explicit はつけない。 + busdata(uint64 value_) noexcept { + value = value_; + } + + // enum からの自動変換のため explicit つけない。 + busdata(BusData value_) noexcept { + value = (uint64)value_; + } + + // コピーコンストラクタを自前で定義すると、 + // このクラスがレジスタ渡しから参照渡しになるようなので、 + // コピーコンストラクタはデフォルトのまま使う。 + + // uint64 にキャストできる + operator uint64() const noexcept { return Get(); } + + uint64 Get() const noexcept { return value; } + uint32 GetH() const noexcept { return (uint32)(value >> 32); } + uint32 GetL() const noexcept { return (uint32)value; } + uint32 Data() const noexcept { return (uint32)value; } + uint32 GetWait() const noexcept { return (uint32)((value >> 32) & 0xffff); } + uint32 GetSize() const noexcept { return (uint32)((value >> 48) & 7); } + bool IsOK() const noexcept { return (value & Mask) == 0; } + bool IsBusErr() const noexcept { return (value & BusErrBit); } + bool IsRetry() const noexcept { return (value & RetryBit); } + + void ChangeData(uint32 data_) noexcept { + value &= 0xffffffff'00000000; + value |= data_; + } + void ChangeWait(uint32 wait) noexcept { + value &= 0xffff0000'ffffffff; + value |= (uint64)Wait(wait); + } + void ChangeSize(uint32 n) noexcept { + value &= ~(uint64)BusData::SizeMask; + value |= (uint64)Size(n); + } + void SetBusErr() noexcept { value |= (uint64)BusData::BusErr; } + void SetRetry() noexcept { value |= (uint64)BusData::Retry; } + + public: + // data |= (busdata) で別途用意してあるウェイトが足せると楽。 + busdata& operator|=(const busdata& rhs) { + value |= rhs.value; + return *this; + } + + // data &= (uint32) でデータ部分のみ演算できると楽。 + busdata& operator&=(const uint32& rhs) { + ChangeData(Data() & rhs); + return *this; + } + + // data <<= (uint32) でデータ部分のみシフトできると楽。 + busdata& operator<<=(const uint32& rhs) { + ChangeData(Data() << rhs); + return *this; + } + + // data >>= (uint32) でデータ部分のみシフトできると楽。 + busdata& operator>>=(const uint32& rhs) { + ChangeData(Data() >> rhs); + return *this; + } +}; + +// busdata に対する OR 演算子 +static __unused busdata operator|(const busdata& lhs, const uint64& rhs) { + return busdata(lhs.Get() | rhs); +} +static __unused busdata operator|(const busdata& lhs, const busdata& rhs) { + return lhs | rhs.Get(); +} +static __unused busdata operator|(const busdata& lhs, const BusData& rhs) { + return lhs | (busdata)rhs; +} +static __unused busdata operator|(const BusData& lhs, const busdata& rhs) { + return (busdata)lhs | rhs; +} +static __unused busdata operator|(const uint64& lhs, const busdata& rhs) { + return busdata(lhs | rhs.Get()); +}