--- nono/vm/bus.h 2026/04/29 17:04:35 1.1.1.3 +++ nono/vm/bus.h 2026/04/29 17:05:25 1.1.1.6 @@ -1,30 +1,305 @@ // // nono -// Copyright (C) 2020 nono project +// Copyright (C) 2024 nono project // Licensed under nono-license.txt // +// +// バス +// + #pragma once -#include "device.h" +#include "header.h" -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); +// 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| |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) になる。 +// +// 以下はデバッガなどで使用する追加のビットで、実行系では使わない。 +// 55ビット目 (PA) はこのアドレスを物理アドレスとする場合は %1 を指定する。 +// 論理アドレスまたは区別が不要な場合は %0 にする。 +// 論理アドレスと物理アドレスの両方を受け取るメモリダンプとかで使う。 +// 62ビット目 (TS: Table Search Result) は、このアドレスを求めるのに +// テーブルサーチを行った場合は %1。 +// 63ビット目 (BE) は (アドレス変換時の) バスエラーを示す。 -static inline uint64 vm_phys_peek_32(uint32 addr) +class busaddr { - uint64 data; + private: + uint64 value {}; + + public: + static const uint64 D = 0x0000'0002'0000'0000ULL; // データ空間 + static const uint64 I = 0x0000'0004'0000'0000ULL; // 命令空間 + static const uint64 S = 0x0000'0009'0000'0000ULL; // スーパーバイザ + static const uint64 U = 0x0000'0000'0000'0000ULL; // ユーザ + static const uint64 R = 0x0000'0080'0000'0000ULL; // 読み込み + static const uint64 W = 0x0000'0000'0000'0000ULL; // 書き込み + + static const uint64 Fetch = I | R; + static const uint64 Read = D | R; + static const uint64 Write = D | W; + + // 以下デバッガ用のビット + static const uint64 Physical = 0x0080'0000'0000'0000ULL; + static const uint64 Logical = 0x0000'0000'0000'0000ULL; + static const uint64 TableSearched = 0x4000'0000'0000'0000ULL; + static const uint64 BusErr = 0x8000'0000'0000'0000ULL; + + // ファンクションコードから busaddr を作成。fc_ は 0-7 にすること。 + static constexpr uint64 FC(uint32 fc_) { + return ((uint64)fc_ << 33) | ((uint64)(fc_ & 4) << 30); + } + + public: + busaddr() { + } + busaddr(uint64 value_) { + Set(value_); + } + + // 32ビットアドレスと属性を代入したいことはよくある。 + busaddr(uint32 addr_, const busaddr& busaddr_) { + Set(addr_, busaddr_); + } + // 32ビットアドレス、アクセス方法、権限が別々なこともよくある。 + busaddr(uint32 addr_, const busaddr& busaddr_, bool super_) { + Set(addr_, busaddr_, super_); + } + + // コピーコンストラクタを自前で定義すると、 + // このクラスがレジスタ渡しから参照渡しになるようなので、 + // コピーコンストラクタはデフォルトのまま使う。 + + // 代入演算子 (全体のみ) + busaddr& operator=(const busaddr& rhs) & { + Set(rhs.value); + return *this; + } + busaddr& operator=(const uint64& rhs) & { + Set(rhs); + return *this; + } + + uint64 Get() const { return value; } + uint32 Addr() const { return (uint32)value; } + // S/U ビットを足した計33ビット + uint64 GetSAddr() const { return value & 0x1'ffff'ffffULL; } + uint32 GetFC() const { return (value >> 33) & 7; } + uint32 GetSSW() const { return (value >> 33) & 0x47; } + bool IsSuper() const { return (value & S); } + bool IsData() const { return (value & D); } + bool IsWrite() const { return (value & R) == 0; } + bool IsPhysical() const { return (value & Physical); } + bool IsTableSearched() const { return (value & TableSearched); } + bool IsBusErr() const { return (value & BusErr); } + + void Clear() { value = 0; } + void Set(uint64 value_) { value = value_; } + // 32ビットアドレスと属性を代入したいことはよくある。 + void Set(uint32 addr_, const busaddr& busaddr_) { + Set((uint64)addr_ | busaddr_); + } + void Set(uint32 addr_, const busaddr& busaddr_, bool super_) { + Set((uint64)addr_ | busaddr_ | (super_ ? busaddr::S : busaddr::U)); + } + // FC 等は変えずにアドレスだけセットする。 + void SetAddr(uint32 addr_) { + value &= 0xffffffff'00000000; + value |= addr_; + } + void SetSuper(bool super_) { + if (super_) { + value |= S; + } else { + value &= ~S; + } + } + void SetData(bool data_) { + value &= ~(I|D); + if (data_) { + value |= D; + } else { + value |= I; + } + } + void SetWrite(bool write_) { + if (write_) { + value &= ~R; + } else { + value |= R; + } + } + void SetPhysical(bool physical_) { + if (physical_) { + value |= Physical; + } else { + value &= ~Physical; + } + } + + operator uint64() const { return Get(); } + + // addr |= (busaddr) で FC とかのビットが足せると楽。 + busaddr& operator|=(const busaddr& rhs) { + value |= rhs.value; + return *this; + } + // addr &= mask でマスクが適用できると楽。 + busaddr& operator&=(const uint32 mask_) { + SetAddr(Addr() & mask_); + return *this; + } - 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; + // addr += n, -= n でアドレス進められると楽。 + busaddr& operator+=(uint32 val) { + SetAddr(Addr() + val); + return *this; + } + busaddr& operator-=(uint32 val) { + SetAddr(Addr() - val); + return *this; + } + // addr++ でアドレス進められると楽。 + busaddr operator++(int n) { + busaddr old(value); + SetAddr(Addr() + 1); + return old; + } + // ++addr でアドレス進められると楽。 + busaddr& operator++() { + SetAddr(Addr() + 1); + return *this; + } +}; + +// busaddr と uint32 の AND 演算子。 +// busaddr & val だけでアドレスがマスクして取り出せると楽。 +static __unused uint32 operator&(const busaddr& addr_, const uint32& mask_) { + return addr_.Addr() & mask_; +} +static __unused uint32 operator&(const busaddr& addr_, int mask_) { + return addr_.Addr() & (uint32)mask_; } -extern IODevice *devtable[256]; + +// 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| | Wait [nsec] | Data | +// |E|T| | | (32bit) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-- ... --+ +// +// BE (63ビット目) はバスエラー。 +// RT (62ビット目) はリトライ。SPC で使う。 +class busdata +{ + private: + uint64 value {}; + + public: + // BusErr は Data() 部分を $ffffffff に見せておく。Peek*() でも使う。 + static const uint64 OK = 0; + static const uint64 BusErr = 0x8000'0000'ffff'ffffULL; + static const uint64 Retry = 0x4000'0000'0000'0000ULL; + + static constexpr uint64 Wait(uint32 nsec) { + return (uint64)nsec << 32; + } + + private: + // バスエラーかどうか判定するのに使うのは BusErrBit のほう。 + static const uint64 BusErrBit = 0x8000'0000'0000'0000ULL; + static const uint64 RetryBit = Retry; + static const uint64 Mask = BusErrBit | RetryBit; + + public: + busdata() { + Clear(); + } + busdata(uint64 value_) { + Set(value_); + } + + // コピーコンストラクタを自前で定義すると、 + // このクラスがレジスタ渡しから参照渡しになるようなので、 + // コピーコンストラクタはデフォルトのまま使う。 + + // 代入演算子 (全体のみ) + busdata& operator=(const busdata& rhs) & { + Set(rhs.value); + return *this; + } + busdata& operator=(const uint64& rhs) & { + Set(rhs); + return *this; + } + + uint64 Get() const { return value; } + uint32 Data() const { return (uint32)value; } + uint32 GetWait() const { + uint32 wait = (value >> 32) & 0xffff; + return wait; + } + bool IsOK() const { return (value & Mask) == 0; } + bool IsBusErr() const { return (value & BusErrBit); } + bool IsRetry() const { return (value & RetryBit); } + + void Clear() { value = 0; } + void Set(uint64 value_) { value = value_; } + void SetData(uint32 data_) { + value &= 0xffffffff'00000000; + value |= data_; + } + void SetWait(uint32 wait) { + value &= 0xffff0000'ffffffff; + value |= (uint64)wait << 32; + } + void SetBusErr() { value |= BusErr; } + void SetRetry() { value |= Retry; } + + operator uint64() const { return Get(); } + + // data |= (busdata) で別途用意してあるウェイトが足せると楽。 + busdata& operator|=(const busdata& rhs) { + value |= rhs.value; + return *this; + } + + // data &= (uint32) でデータ部分のみ演算できると楽。 + busdata& operator&=(const uint32& rhs) { + SetData(Data() & rhs); + return *this; + } + + // data <<= (uint32) でデータ部分のみシフトできると楽。 + busdata& operator<<=(const uint32& rhs) { + SetData(Data() << rhs); + return *this; + } + + // data >>= (uint32) でデータ部分のみシフトできると楽。 + busdata& operator>>=(const uint32& rhs) { + SetData(Data() >> rhs); + return *this; + } +};