--- nono/vm/device.h 2026/04/29 17:04:28 1.1 +++ nono/vm/device.h 2026/04/29 17:05:51 1.1.1.18 @@ -1,12 +1,17 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt +// + +// +// デバイスの基本クラスなど // #pragma once #include "object.h" -#include +#include "bus.h" #if BYTE_ORDER == LITTLE_ENDIAN #define HB(addr) ((addr) ^ 1) @@ -14,173 +19,217 @@ #define HLW(addr) ((addr) ^ 2) #else #define HB(addr) (addr) +#define HLB(addr) (addr) +#define HLW(addr) (addr) #endif +// デバイスを作成して代入するマクロ。 +// …だったが今となってはあまり意味がなくなっている。 +#define NEWDV(NAME, NEW_CTOR) do { \ + __CONCAT(p,NAME).reset(NEW_CTOR); \ + assert((bool)__CONCAT(p,NAME)); \ +} while (0) + +// ユーザ定義リテラルを使ったサフィックス + +// ナノ秒 +static inline constexpr uint64 operator"" _nsec(unsigned long long ns) +{ + return ns; +} +static inline constexpr uint64 operator"" _nsec(long double ns) +{ + return ns; +} + +// マイクロ秒 +static inline constexpr uint64 operator"" _usec(unsigned long long us) +{ + return us * 1000_nsec; +} +static inline constexpr uint64 operator"" _usec(long double us) +{ + return us * 1000_nsec; +} + +// ミリ秒 +static inline constexpr uint64 operator"" _msec(unsigned long long ms) +{ + return ms * 1000_usec; +} +static inline constexpr uint64 operator"" _msec(long double ms) +{ + return ms * 1000_usec; +} + +// 秒 +static inline constexpr uint64 operator"" _sec(unsigned long long sec) +{ + return sec * 1000_msec; +} +static inline constexpr uint64 operator"" _sec(long double sec) +{ + return sec * 1000_msec; +} + +// 前方参照 +class Device; +class Event; +class MainMPUDevice; +class Scheduler; + +using EventCallback_t = void (Device::*)(Event *); + class Device : public Object { - typedef Object inherited; + using inherited = Object; protected: - Device(); + explicit Device(uint objid_); public: - virtual ~Device(); + ~Device() override; public: // 初期化は以下の手順で行う。 // 1. コンストラクタ // メンバ変数の初期値代入などを行う。失敗するケースは考慮していない。 - // この時点からすでに gConfig で設定ファイルにはアクセスできるが、 - // 設定に左右される事象は極力コンストラクタでは扱わないこと。 + // gConfig の内部変数の追加はここ(か2.の動的コンストラクション)で + // 行うこと。この時点ですでに gConfig の設定内容の参照は可能だが、 + // エラーが出るものはここではなく Create() か Init() で処理すること。 + // putmsg() は使えない。 // // 2. Create() ... 動的コンストラクション // 必要なら設定ファイルによって可変なオブジェクトのコンストラクトを行う。 - // 例えば SCSI ドライブなどが該当する。 - // この時点から gVM->GetType() による VM 判定は使える - // (正確にはコンストラクタの時点でもう使えるが使う必要はないはず)。 - // - // 3. parse_logopt() ... -L オプションの処理 - // この時点で Object 由来のオブジェクトはすべて出揃っている必要がある。 - // - // 4. Init() ... 初期化 - // 設定ファイルのうち起動時のみ適用するパラメータの設定、ROM などの - // ファイルの読み込み、またスレッドの起動などを行う。 - // Object 由来のオブジェクトはすべて揃っているため、相互にアクセス可能。 - // ただしその際の依存関係については、vm.cpp (や xiospace.cpp) 内の - // コンストラクタの順序で調整すること (循環参照不可)。 - // またここから putlog() が使える (Scheduler::Init() のコメントも参照)。 + // 例えば SCSI ドライブなどが該当する。Create() で作成したインスタンスの + // Create() もこのフェーズで呼ばれる。 + // ここでも gConfig の参照および内部変数の登録は可能、エラー終了する + // 可能性のあるものも処理できる。 + // 継承クラスの Create() は Device::Create() を呼ぶ必要はない。 + // putmsg() は使えない。 + // + // 3. EarlyInit() ... ログ出力の初期化 + // ここですべての Device に対してログ出力のための変数を初期化する。 + // ただし putmsg() が使えるのは 4. 以降。 + // これは override しない。 + // + // 4. 設定とログの締め切り。 + // これ以降 gConfig の変更は出来ない。 + // またログの割り当てを受ける Object 由来のオブジェクトはすべて出揃って + // いる必要がある。 + // 4.1 この時点で gConfig の内容は確定したので、--show-config を行う。 + // 4.2 ParseLogopt() ... -L オプションの処理。 + // + // 5. Create2() ... 動的コンストラクションその2 + // 作成にあたってログ出力をしたいホストドライバのためのフェーズ。 + // ログは既存のものから従属するしか出来ない。 + // putmsg() は使える。 + // EarlyInit() は Create2() のループ後に改めて呼ばれる。 + // + // 6. ParseMonitors() ... -M オプションの処理 + // (GUI 版のみ) この時点で Monitor はすべて登録されている必要がある。 + // + // 7. Init() ... 初期化 + // この時点で Object 由来のオブジェクトはすべて出揃っているため、他の + // Object はすべてアクセス可能。 + // 継承クラスは Device::Init() を呼ぶ必要はないが、子クラスが親クラスの + // Init() を呼ぶことを要求することはあるかもしれない。 + // gConfig の設定項目のうち内容によってエラー終了する可能性があるものは + // ここまでに処理する。 + // 複数のオブジェクトが同じ設定項目を処理しないようにすること (複数で + // 別々に読んでその際の範囲チェックに齟齬が発生するとかを避けるため)。 + // Object 同士は完全に相互アクセス可能だが、Init() 自身の依存関係 + // (A::Init() で設定した値を B::Init() が参照する必要があるケース) に + // ついては vm.cpp (などコンテナデバイス) 内のコンストラクタの順序で + // 手動調整すること (循環参照不可)。 // - // 5. Apply() ... 動的設定の適用 - // 実行中に変更可能なパラメータもここで一度適用する。 + // 8. StartThread() ... スレッド起動 // - // 6. PowerOn(), ResetHard() ... VM 起動 + // 9. ResetHard(true) ... 電源オン + + // ログの初期化。 + void EarlyInit(); // 動的コンストラクション。 - // この時点で設定ファイルは読める。putlog() は使えない。 - // 成功すれば true を返す、 - // 失敗なら warn()/warnx() でエラーメッセージを表示して false を返すこと。 + // 成功すれば true を返す。失敗なら warn() 系、あるいは gConfig->Err() + // 系でエラーメッセージを表示して false を返すこと。 virtual bool Create(); + // 動的コンストラクションその2。 + // 成功すれば true を返す。失敗なら warn() 系、あるいは gConfig->Err() + // 系でエラーメッセージを表示して false を返すこと。 + // putmsg() は使える。 + virtual bool Create2(); + // 初期化。 - // 起動時設定の適用、ファイルの読み込み、スレッド作成を行う。 - // Object 由来のオブジェクトはすべて揃っており、設定ファイルは読めるが - // putlog() は使えない。 - // 成功すれば true を返すこと。 - // 失敗なら warn()/warnx()、あるいは gConfig->Err() 系 (実態は warnx) で - // エラーメッセージを表示して false を返すこと。 + // 成功すれば true を返す。失敗なら warn() 系、あるいは gConfig->Err() + // 系でエラーメッセージを表示して false を返すこと。 virtual bool Init(); - // 現在の設定を VM に反映させる。 - // ここで適用するのは実行中に変更可能なパラメータのみ。 - // 適用内容を表示したい時は putlog(1) が妥当か。 - // 成功すれば true を返すこと。 - // 失敗なら warn()/warnx()、あるいは gConfig->Err() 系 (実態は warnx) で - // エラーメッセージを表示して false を返すこと。 - // といっても false が返されたらどうするんだろうか。 - virtual bool Apply(); - - virtual bool PowerOn(); - virtual bool PowerOff(); - - // ResetHard() は本体リセットスイッチによるリセット。 - // ResetSoft() は MPU の RESET 命令によるリセット。 - // - // X680x0 では、 - // o PowerOn でリセット : VIPS, VICON, OSCIAN2 - // o ResetHard でリセット: YUKI, MPU, FPU および ResetSoft の全員 - // o ResetSoft でリセット: PPI, PEDEC, SPC, ADPCM, FDC, MFP, SAKI, - // CYNTHIA, DMAC らしい - // o いずれでもリセットされないもの: CACHY, YM2151, RTC - // o 分からないけどたぶん ResetHard でリセット: SCC - // - // LUNA では、詳細分からないので全員 ResetHard にしとくか。 - virtual void ResetHard(); - virtual void ResetSoft(); - -}; - -class IODevice : public Device -{ - typedef Device inherited; - protected: - IODevice(); - public: - virtual ~IODevice(); + // 電源オンもしくはリセット。 + // true なら電源オン、false ならリセット。 + // このデバイスがどのタイミングでリセットされるかは VM による。 + // X68030 なら vm_x68k.cpp 内のコメント参照。 + // LUNA は詳細不明。 + virtual void ResetHard(bool poweron); - public: - // アクセス関数。 - // Read*() は読み込んだ値を (uint32) で返す。 - // Write*() は書き込めたら 0 を返す。 - // Peek8() はデバッガアクセスで読み込んだ値を (uint32) で返す。 - // いずれもバスエラーなら (uint64)-1 を返す。 - // (signed) int64 を使ってないのはノーマルパスで signed/unsigned の - // キャスト事故を防ぐため。 - virtual uint64 Read8(uint32 addr); - virtual uint64 Read16(uint32 addr); - virtual uint64 Read32(uint32 addr); - virtual uint64 Write8(uint32 addr, uint32 data); - virtual uint64 Write16(uint32 addr, uint32 data); - virtual uint64 Write32(uint32 addr, uint32 data); - virtual uint64 Peek8(uint32 addr); - - uint32 devaddr = 0; // このデバイスの先頭アドレス - - // (必要なら)このデバイスの占めるバイト数。 - // 今のところ XIOSpace、MuxDevice では必要。それ以外では不要。 - uint32 devlen = 0; -}; + // 電源オフ + virtual void PowerOff(); -class BusErrDevice : public IODevice -{ - typedef IODevice inherited; - public: - BusErrDevice(); - virtual ~BusErrDevice(); + // ログ出力 + void putlogn(const char *fmt, ...) const override __printflike(2, 3); - public: - virtual uint64 Read8(uint32 addr); - virtual uint64 Read16(uint32 addr); - virtual uint64 Read32(uint32 addr); - virtual uint64 Write8(uint32 addr, uint32 data); - virtual uint64 Write16(uint32 addr, uint32 data); - virtual uint64 Write32(uint32 addr, uint32 data); - virtual uint64 Peek8(uint32 addr); + protected: + // ログに時刻と PPC を出すために必要 + MainMPUDevice *mpu {}; + Scheduler *scheduler {}; }; -// 何もしないデバイス。 -// 読み込むと全域で 0xff が読めて、書き込んでも何も起きない。 -class NullDevice : public IODevice +class IODevice : public Device { - typedef IODevice inherited; + using inherited = Device; + protected: + explicit IODevice(uint objid_); public: - NullDevice(); - virtual ~NullDevice(); + ~IODevice() override; public: - virtual uint64 Read8(uint32 addr); - virtual uint64 Write8(uint32 addr, uint32 data); - virtual uint64 Peek8(uint32 addr); + // メイン CPU からのデバイスの読み込み。 + // busaddr.Addr にアドレス、busaddr.Size に要求バイト数がセットされている。 + // busdata.Size にはポートサイズ (応答バイト数) を返す。 + // 戻り値 busdata.Data は下詰めし、不要な上位バイトはクリアして返すこと。 + virtual busdata Read(busaddr addr); + + // メイン CPU からのデバイスへの書き込み。 + // busaddr.Addr にアドレス、busaddr.Size に要求バイト数がセットされている。 + // data は Size バイト分が下詰めされており、上位の不要バイトはクリア済み。 + // 戻り値の busdata.Size にはポートサイズを返すこと。 + virtual busdata Write(busaddr addr, uint32 data); + + // メイン CPU からのバーストアクセス。 + // addr (16バイト境界) からの 16 バイトを転送してウェイトと OK を返す。 + // dst/src の uint32 はホストエンディアン。 + // バースト転送をサポートしてなければ BusData::BusErr を返す。 + virtual busdata ReadBurst16(busaddr addr, uint32 *dst); + virtual busdata WriteBurst16(busaddr addr, const uint32 *src); + + // XP からのデバイスの読み込み。 + // 戻り値は busdata.Data の1バイトと必要なら Wait。他は使用しない。 + virtual busdata Read1(uint32 addr); + + // XP からのデバイスへの書き込み。 + // data は1バイト。 + // 戻り値は busdata.Wait のみ。他は使用しない。 + virtual busdata Write1(uint32 addr, uint32 data); + + // Peek1() はデバッガアクセスで読み込んだ値を (uint32) で返す。 + // バイトアクセスした時に読める値ではなく、現在このアドレスに見えている + // であろう値。バスエラーなら BusData::BusErr を返す。 + virtual busdata Peek1(uint32 addr); + + // Poke1() はデバッガからこのアドレス(メモリ)へ書き込む。 + // メモリでない領域 (レジスタなど) は書き込み不可。 + // data が (int32 で) 非負なら data の下位8ビットを書き込む。 + // 書き込めれば true、書き込めなければ false を返す。 + // data が (int32 で) 負なら addr への書き込み可否の問い合わせで、 + // 書き込み可能なら true、書き込み不可能なら false を返す。 + virtual bool Poke1(uint32 addr, uint32 data); }; - -// 前方参照 -class CRTC2Device; -class BitmapDevice; -class Human68k; -class IODevice; -class LanceDevice; -class MaskROMDevice; -class MK48T02Device; -class MPUDevice; -class PIO0Device; -class PIO1Device; -class PROMDevice; -class RAMDevice; -class SCSIHD; -class SCSIHostDevice; -class SCSITarget; -class SIODevice; -class SPCDevice; -class SysClkDevice; -class VM; -class XIOSpaceDevice; -class ZRAMDevice; - -extern std::vector gDevices;