--- nono/lib/config.cpp 2026/04/29 17:05:08 1.1.1.10 +++ nono/lib/config.cpp 2026/04/29 17:05:15 1.1.1.12 @@ -9,6 +9,7 @@ // #include "config.h" +#include "fdc.h" #include "mystring.h" // グローバル参照用 @@ -26,6 +27,9 @@ Config::Config() // 以下概ねアルファベット順に並べる + // X68030 CGROM ファイルパス。なければ内蔵 CGROM。 + Add("cgrom-image", "", VMCap::X68030); + // VM 内の時計を同期する時間軸。 // [ real, virtual ] Add("clock-sync", "real"); @@ -36,17 +40,39 @@ Config::Config() // デバッガコンソールの fallback は常に不可とする。hostcom.cpp 参照。 // "debugger-fallback" + // DIPSW エイリアス + Add("dipsw-autoboot", "", VMCap::LUNA | VMCap::NEWS); + Add("dipsw-serial", "", VMCap::LUNA | VMCap::NEWS); + // MAC アドレス - Add("ethernet-macaddr", "auto"); + Add("ethernet-macaddr", "auto", VMCap::LUNA | VMCap::NEWS); - // キーボードの入力モード - Add("hostkbd-input", "char"); + // X68030 の拡張メモリサイズ + Add("extram-size", "0", VMCap::X68030); + + // フロッピーディスク + Add("fd-drive", 2, VMCap::X68030); + for (int id = 0; id < FDCDevice::MAX_DRIVE; id++) { + // 種別とイメージパス + Add(string_format("fd%d-image", id), VMCap::X68030); + // エミュレータ的に書き込みを禁止するか + Add(string_format("fd%d-writeignore", id), 0, VMCap::X68030); + } + + // X68030 時の FPU の有無。 + Add("fpu-type", "68881", VMCap::X68030); + + // 高速モード。 + Add("fast-mode", "0"); // シリアルポートのホスト側設定 Add("hostcom-driver", "none"); Add("hostcom-fallback", 0); Add("hostcom-tcp-port", 9998); + // キーボードの入力モード + Add("hostkbd-input", "char"); + // ネットワークのホスト側設定。 Add("hostnet-driver", "auto"); Add("hostnet-fallback", 0); @@ -54,23 +80,33 @@ Config::Config() Add("hostnet-bpf-ifname", "auto"); Add("hostnet-tap-devpath", "auto"); + // IPLROM1/2 ファイルパス。なければ内蔵 ROM。 + Add("iplrom1-image", "", VMCap::X68030); + Add("iplrom2-image", "", VMCap::X68030); + // キーボードの接続 Add("keyboard-connect", 1); - // LUNA の DIPSW (1 が UP、0 が DOWN) - // 初期値は機種によって異なるため pio.cpp で設定している。 - Add("luna-dipsw1"); - Add("luna-dipsw2"); - // LUNA の RTC(MK48T02) がうるう年でない 1970 をエポックとして // 設定しているので、そちらに合わせてうるう年を補正する - Add("luna-adjust-misused-epoch", 1); + Add("luna-adjust-misused-epoch", 1, VMCap::LUNA); + + // LUNA の DIPSW (1 が UP、0 が DOWN)。 + // 初期値は機種によって異なるため pio.cpp で設定している。 + Add("luna-dipsw1", VMCap::LUNA); + Add("luna-dipsw2", VMCap::LUNA); // LUNA ビデオボードプレーン数 - Add("luna-video-plane", 1); + Add("luna-video-plane", 4, VMCap::LUNA); // m88100 の別名ニーモニックを併記する。 - Add(".m88k-altname", 0); + Add(".m88k-altname", 0, VMCap::M88K); + + // 画面サイズ。 + Add("mainview-scale", "1"); + + // モニタウィンドウ(等)のフォントサイズ + Add("monitor-fontsize", 12); // モニタウィンドウの更新頻度 [Hz] Add("monitor-rate", 20); @@ -79,19 +115,22 @@ Config::Config() // 初期値は各 VM クラスで設定している。 Add("mpu-clock"); - // X68030 時の FPU の有無。 - // LUNA-I では VM 初期化時に削除する。 - Add("mpu-has-fpu", 0); - // M88100 で疑似 STOP 状態をサポートする - Add("mpu-pseudo-stop", 1); + Add("mpu-pseudo-stop", 1, VMCap::M88K); + + // NEWS の DIPSW (1 がオン、0 がオフ)。 + Add("news-dipsw", "00001000", VMCap::NEWS); + + // NEWS の SIC を無視する。(暫定) + Add("xxx-news-sic-ignore", "0", VMCap::NEWS); + Add("xxx-news-sci-ignore", "", VMCap::NEWS); // PROM ファイルパス。なければ内蔵 ROM 起動。 - Add("prom-image", ""); + Add("prom-image", "", VMCap::LUNA); // PROM のデバッグオプション。 // PROM 中の SCSI デバッグビットを立てる。 - Add(".prom-scsi-debug", 0); + Add(".prom-scsi-debug", 0, VMCap::LUNA1); // RAM サイズ (MB 単位の整数)。 // 初期値は機種ごとの VM クラスで設定している。 @@ -103,7 +142,7 @@ Config::Config() Add(".rtc-time"); // RTC の epoch。初期値は各 VM クラスで設定している。 - Add("rtc-epoch-year"); + Add("rtc-epoch-year", VMCap::LUNA | VMCap::NEWS); // ステータスパネルを表示する。 Add("show-statuspanel", 1); @@ -120,6 +159,9 @@ Config::Config() // シークタイム [msec] Add(string_format("spc0-id%d-seektime", id), 0); } + + // SRAM の RAM 容量を同期するか。 + Add("sram-sync-ramsize", 1, VMCap::X68030); } // デストラクタ @@ -130,26 +172,26 @@ Config::~Config() // 項目を追加 (初期状態用)。値は空文字列。 bool -Config::Add(const std::string& key) +Config::Add(const std::string& key, VMCap vmcap) { - return Add(key, ""); + return Add(key, "", vmcap); } // 項目を追加 (初期状態用)。値は整数値を文字列にしたもの。 bool -Config::Add(const std::string& key, int value) +Config::Add(const std::string& key, int value, VMCap vmcap) { - return Add(key, std::to_string(value)); + return Add(key, std::to_string(value), vmcap); } // 項目を追加 (初期状態用) bool -Config::Add(const std::string& key, const std::string& value) +Config::Add(const std::string& key, const std::string& value, VMCap vmcap) { assert(FindItem(key) == NULL); assert(fixed == false); - list.emplace_back(key, value, ConfigItem::FromInitial, ""); + list.emplace_back(key, value, vmcap, ConfigItem::FromInitial, ""); return true; } @@ -189,17 +231,11 @@ Config::Update(const ConfigFile& file) // コマンドラインオプションで更新 bool -Config::Update(const std::vector& options) +Config::Update(const std::string& line, const std::string& optname) { assert(fixed == false); - for (const auto& line : options) { - if (UpdateLine(line, ConfigItem::FromOption, "") == false) { - return false; - } - } - - return true; + return UpdateLine(line, ConfigItem::FromOption, optname); } // 更新1行分の共通処理。 @@ -215,7 +251,7 @@ Config::UpdateLine(const std::string& li // キーと値に分解 if (SplitKeyVal(&key, &val, line) == false) { - ConfigItem::PrintErr(from, where, "", line, "syntax error"); + ConfigItem::PrintErr(from, where, "", line, "Syntax error"); return false; } @@ -224,11 +260,11 @@ Config::UpdateLine(const std::string& li if (itemptr == NULL) { // コマンドラインからの場合はエラーにする if (from == ConfigItem::FromOption) { - ConfigItem::PrintErr(from, where, key, val, "unknown key"); + ConfigItem::PrintErr(from, where, key, val, "Unknown key"); return false; } // コマンドライン以外の場合は警告のみで無視する - ConfigItem::PrintErr(from, where, key, val, "unknown key, ignored"); + ConfigItem::PrintErr(from, where, key, val, "Unknown key, ignored"); return true; } @@ -301,10 +337,11 @@ Config::Show(bool all) const fromstr = item.where; break; case ConfigItem::FromOption: - fromstr = "-V option"; + fromstr = "option " + item.where; break; default: - __unreachable(); + fromstr = string_format("corrupted item.from=%d", (int)item.from); + break; } printf("%s(%s)\n", buf.c_str(), fromstr.c_str()); @@ -359,13 +396,34 @@ Config::FindItem(const std::string& key) return NULL; } -// これ以降は書き込み禁止 -void +// これ以降は書き込み禁止。 +bool Config::Fix() { assert(fixed == false); + // ここで、この機種に不要な変数を削除する。 + // が、その前にその変数がユーザから指定されていればエラーにする。 + for (auto it = list.begin(); it != list.end(); ) { + ConfigItem& item = *it; + VMCap vmcap = item.GetVMCap(); + if (gMainApp.Has(vmcap) == false) { + // この機種用ではない + + if (item.from != ConfigItem::FromInitial) { + // 更新されていればエラー + item.Err("Not supported in this vmtype"); + return false; + } + + it = list.erase(it); + } else { + ++it; + } + } + fixed = true; + return true; } @@ -392,6 +450,25 @@ ConfigItem::TryInt(int *val) const return true; } +// 値を double 値として取得してみる。 +bool +ConfigItem::TryDouble(double *val) const +{ + char *endp; + + if (value.empty()) { + return false; + } + + errno = 0; + double res = strtod(value.c_str(), &endp); + if (errno) { + return false; + } + *val = res; + return true; +} + // デフォルト値を差し替える void ConfigItem::SetDefault(const std::string& val) @@ -405,7 +482,7 @@ void ConfigItem::Err() const { // 定型文 - Err("invalid argument"); + Err("Invalid argument"); } void @@ -428,34 +505,52 @@ ConfigItem::Err(const std::string& msg) PrintErr(from, where, key, value, msg); } +// この item についてエラーを表示する。 +// key=val の '=' がない場合のみ key を空、value を行全体として呼ぶ。 /*static*/ void ConfigItem::PrintErr(ConfigItem::from_t from, const std::string& where, const std::string& key, const std::string& value, const std::string& msg) { std::string fromstr; - std::string keyvalue; switch (from) { case FromInitial: - fromstr = "initial value:"; + // 初期値の場合 key が空なことは起きないはず。 + // というか初期値でエラーが起きることも普通はないはず。 + fromstr = "initial value: " + key + "=" + value; break; + case FromConfig: - fromstr = where + ":"; + // 設定ファイルでのエラー。where はファイル名と行番号。 + fromstr = where + ": "; + if (key.empty() == false) { + fromstr += key; + fromstr + "="; + } + fromstr += value; break; + case FromOption: - fromstr = "option -V"; + // コマンドラインオプションの場合は where は + // "--fontsize" とか "-V" なので、 + // "-V" なら (key =) value を追加し、 + // それ以外なら value のみ追加がいいか。 + fromstr = "option " + where + " "; + if (where == "-V") { + if (key.empty() == false) { + fromstr += key; + fromstr += "="; + } + } + fromstr += value; break; - default: - __unreachable(); - } - if (key.empty()) { - keyvalue = value; - } else { - keyvalue = key + "=" + value; + default: + fromstr = string_format("corrupted item.from=%d", (int)from); + break; } - warnx("%s %s: %s", fromstr.c_str(), keyvalue.c_str(), msg.c_str()); + warnx("%s: %s", fromstr.c_str(), msg.c_str()); } @@ -474,7 +569,10 @@ ConfigFile::~ConfigFile() { } -// ファイルの読み込み +// ファイルの読み込み。 +// 成功すれば true を、失敗すればメッセージを出力して false を返す。 +// ファイルがないのは正常とする (なければスルーするため)。 +// ファイルがあって読めなければ失敗 (エラー終了したいので)。 bool ConfigFile::Load() { @@ -483,6 +581,9 @@ ConfigFile::Load() fp = fopen(filepath.c_str(), "r"); if (fp == NULL) { + if (errno == ENOENT) { + return true; + } warn("%s", filepath.c_str()); return false; }