--- nono/lib/mainapp.cpp 2026/04/29 17:04:30 1.1.1.2 +++ nono/lib/mainapp.cpp 2026/04/29 17:04:51 1.1.1.7 @@ -1,9 +1,9 @@ // // nono -// Copyright (C) 2018 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // -#include "header.h" #include "mainapp.h" #include "mystring.h" #include @@ -22,10 +22,10 @@ MainApp::ShowHelp(bool all) const printf("usage: %s []\n", getprogname()); p("-A load and execute host binary (a.out or ELF)"); - p("-c vm directory"); + p("-c vm directory or configuration file"); p("-f fast mode"); if (IsGUI()) - p("--fontsize fontsize in monitors {12,16,24} (default:12)"); + p("--fontsize fontsize in monitors {12,16} (default:12)"); p("-h show brief help message"); p("--help show all help message including for developers"); if (IsGUI()) @@ -36,7 +36,7 @@ MainApp::ShowHelp(bool all) const if (all) { printf("\n(options for developers)\n"); - p("-b breakpoint"); + p("-b [,] set breakpoint"); p("-B benchmark mode"); p("-C output log to console"); p("-d debugger prompt on startup"); @@ -54,44 +54,51 @@ enum { OPT_fontsize = 0x80, OPT_help, OPT_show_config, + OPT_show_config_all, }; static struct option longopts[] = { { "fontsize", required_argument, NULL, OPT_fontsize }, { "help", no_argument, NULL, OPT_help }, { "show-config", no_argument, NULL, OPT_show_config }, + // --show-config-all は開発用なのでヘルプには載せない + { "show-config-all",no_argument, NULL, OPT_show_config_all }, { NULL, 0, NULL, 0 }, }; -// 起動時の処理、VM の実行開始前まで。 +// VM の初期化、ステージ1。 +// VM 作成と設定確定あたりまで。スレッド生成を伴わないもの。 // 所々 CLI と GUI で処理が違う。 -bool -MainApp::Init(bool is_cli_, int ac, char *av[]) +// 戻り値は以下のいずれか。 +// MainApp::PASS .. 実行を継続(通常パス) +// EXIT_SUCCESS / EXIT_FAILURE .. この終了コードでアプリケーションを終了 +int +MainApp::Init1(bool is_cli_, int ac, char *av[]) { is_cli = is_cli_; if (!ParseOpt(ac, av)) { - return false; + return EXIT_FAILURE; } // ログ機構は引数処理後なるはや // CLI ならログは常に標準出力へ(も)出力。 // GUI なら -C で指定。 - gLogger->UseStdout(IsCLI() ? true : log_to_console); + gLogger.UseStdout(IsCLI() ? true : log_to_console); // 設定を作成。コンストラクタで初期値を用意 gConfig.reset(new Config()); - // 引数で指定されたディレクトリの設定ファイルを読み込んで設定を更新 - ConfigFile file(vmdir + "nono.cfg"); + // 引数で指定されたディレクトリの設定ファイルがあれば読み込んで設定を更新 + ConfigFile file(vmfile); if (file.Load() == false) { - return false; + return EXIT_FAILURE; } if (gConfig->Update(file) == false) { - return false; + return EXIT_FAILURE; } // 最後にコマンドライン引数で更新 if (gConfig->Update(config_options) == false) { - return false; + return EXIT_FAILURE; } // VM 種別を決定 @@ -99,12 +106,12 @@ MainApp::Init(bool is_cli_, int ac, char vmtype = VMTYPE_RXZ; } else { // VM 種別文字列から vmtype を決定 - const ConfigItem& item = gConfig->Get("vmtype"); + const ConfigItem& item = gConfig->Find("vmtype"); std::string vmstr = string_tolower(item.AsString()); if (vmstr == "x68030") { vmtype = VMTYPE_X68030; } else if (vmstr == "luna") { - vmtype = VMTYPE_LUNA; + vmtype = VMTYPE_LUNA1; } else if (vmstr == "luna88k") { vmtype = VMTYPE_LUNA88K; } else { @@ -115,7 +122,7 @@ MainApp::Init(bool is_cli_, int ac, char // ユーザ由来の時 item.Err("invalid vmtype"); } - return false; + return EXIT_FAILURE; } } @@ -125,8 +132,8 @@ MainApp::Init(bool is_cli_, int ac, char gVM.reset(new VM_X68030()); break; - case VMTYPE_LUNA: - gVM.reset(new VM_LUNA()); + case VMTYPE_LUNA1: + gVM.reset(new VM_LUNA1()); break; case VMTYPE_RXZ: @@ -141,32 +148,38 @@ MainApp::Init(bool is_cli_, int ac, char __unreachable(); } - // 設定内容表示 - // (MPU コンストラクタでクロック数の初期値を決めるため、その後で表示) - if (show_config) { - gConfig->Show(); - } - // 動的なコンストラクション if (!gVM->Create()) { - return false; + gVM.reset(); + return EXIT_FAILURE; + } + + // 設定内容を表示して終了 + // (VM コンストラクタで変数の増減があるのでそれより後、 + // Create() でも SCSI パラメータを減らすので、それより後) + gConfig->Fix(); + if (show_config) { + gConfig->Show(show_config - 1); + gVM.reset(); + return EXIT_SUCCESS; } // ログレベルを設定。コンストラクト後すぐに行う。 // -L help もここで処理。 if (!ParseLogopt()) { - return false; + gVM.reset(); + return EXIT_FAILURE; } - return true; + return PASS; } -// VM の実行部分。 -// Init() で引数を受け付けてから Start() でデバッガスレッドなどを起動するが、 +// VM の初期化、ステージ2。スレッド生成を伴う。 +// Init1() で引数を受け付けてから Init2() でデバッガスレッドなどを起動するが、 // -Mhelp とかが指定された場合はここでスレッド開始する前にプロセスを終了する -// 必要があるので、分けてある。 +// 必要があるので分けてある。wxapp.cpp も参照。 bool -MainApp::Start() +MainApp::Init2() { // VM 初期化。 // これ以降はスレッドを開始したかもしれないので false を返す際には @@ -192,12 +205,14 @@ MainApp::Start() bool MainApp::ParseOpt(int ac, char *av[]) { + struct stat st; + const char *cpath; int b; int c; fontsize = 12; screen_scale = 1.0; - debug_breakaddr = 0xffffffff; + cpath = "."; while ((c = getopt_long(ac, av, "A:b:B:c:CdDfhL:M:s:vV:X:", longopts, NULL)) != -1) { @@ -207,7 +222,7 @@ MainApp::ParseOpt(int ac, char *av[]) break; case 'b': - debug_breakaddr = (uint32)strtoul(optarg, NULL, 16); + debug_breakaddr.push_back(optarg); break; case 'B': @@ -220,7 +235,11 @@ MainApp::ParseOpt(int ac, char *av[]) break; case 'c': - vmdir = std::string(optarg); + cpath = optarg; + // 空文字列なら再び初期値に + if (cpath[0] == '\0') { + cpath = "."; + } break; case 'C': @@ -298,7 +317,11 @@ MainApp::ParseOpt(int ac, char *av[]) return false; case OPT_show_config: - show_config = true; + show_config = 1; + break; + + case OPT_show_config_all: + show_config = 2; break; case 'h': @@ -308,12 +331,33 @@ MainApp::ParseOpt(int ac, char *av[]) } } - // vmdir に '/' を付けておく - if (vmdir.empty()) { - vmdir = "."; + // 引数がディレクトリなら、それを VM ディレクトリとし、その中の + // nono.cfg を設定ファイルとする。 + // 引数がファイルなら、それを設定ファイルとし、そのファイルがある + // ディレクトリを VM ディレクトリとする。 + // -c DIR => vmdir = DIR, vmfile = DIR/nono.cfg + // -c DIR/FILE => vmdir = DIR, vmfile = FILE + if (stat(cpath, &st) < 0) { + warn("stat %s", cpath); + return false; } - if (vmdir.back() != '/') { - vmdir += '/'; + if (S_ISDIR(st.st_mode)) { + vmdir = std::string(cpath); + if (vmdir.back() != '/') { + vmdir += '/'; + } + vmfile = vmdir + "nono.cfg"; + } else if (S_ISREG(st.st_mode)) { + vmfile = std::string(cpath); + auto pos = vmfile.rfind('/'); + if (pos != std::string::npos) { + vmdir = vmfile.substr(0, pos + 1); + } else { + vmdir = "./"; + } + } else { + warnx("-c %s: path must be file or directory", cpath); + return false; } // CLI 版で -Mhelp つけても黙って起動するのはさすがにどうかと思う。 @@ -364,7 +408,7 @@ MainApp::ParseLogopt() } // エイリアスとか printf("sch -> scheduler\n"); - if (vmtype == VMTYPE_LUNA) { + if (gMainApp.HasVMFeature(VMF_LUNA)) { printf("scc -> sio\n"); } printf("all\n"); @@ -400,7 +444,7 @@ MainApp::ParseLogopt() // 短縮形とかエイリアスとか if (strcmp(name, "sch") == 0) name = "scheduler"; - if (vmtype == VMTYPE_LUNA) { + if (gMainApp.HasVMFeature(VMF_LUNA)) { if (strcmp(name, "scc") == 0) name = "sio"; } @@ -412,14 +456,14 @@ MainApp::ParseLogopt() obj->loglevel = val; } } else { - // それ以外は一致するキーを探してセット + // それ以外は一致するキーを探してセット。 + // 複数のオブジェクトが同じ logname を持ってもよい (CMMUとか)。 std::string sname = name; bool found = false; for (auto& obj : gObjects) { if (obj->logname == sname) { obj->loglevel = val; found = true; - break; } } // 見つからない場合はエラー @@ -427,7 +471,7 @@ MainApp::ParseLogopt() printf("invalid logname '%s'\n", name); goto abort; } - } + } } rv = true; @@ -437,27 +481,59 @@ MainApp::ParseLogopt() } // 関連するファイルのパスを取得。 +// 1. name がファイル名のみなら、VM ディレクトリとその親ディレクトリを検索。 +// ファイルが存在すればその時点で戻り値とする。ファイルが存在したけど +// 何らか不都合があったとしても (例えばファイルサイズが 0 だとか +// パーミッションが足りないとか) 次を試すとかはしない。 +// 2. name が '~' から始まっていればホームディレクトリに展開。 +// 3. name が相対パスなら VM ディレクトリからの相対パスとする。 +// 4. name は絶対パスのはずなので、そのまま使用する。 std::string MainApp::SearchFile(const std::string& name) const { std::string path; struct stat st; - // ファイルがあるかどうかを調べるだけで、存在したけどそれに何らか - // 不都合があったら次を試すとかはしない。 + // 1. パス区切りを含んでなければ、ファイル名のみ + if (name.find('/') == std::string::npos) { + // 1a. VM ディレクトリ + path = GetVMDir() + name; + if (stat(path.c_str(), &st) == 0) { + return path; + } - // 1. 設定ファイルディレクトリ - path = GetVMDir() + name; - if (stat(path.c_str(), &st) == 0) { - return path; + // 2. 親ディレクトリ + path = GetVMDir() + "../" + name; + if (stat(path.c_str(), &st) == 0) { + return path; + } + + // どちらもなければエラー + return ""; + } + + // 2. 先頭の '~' を $HOME に展開 + path = name; + if (path[0] == '~' && path[1] == '/') { + const char *home = getenv("HOME"); + if (home == NULL) { + home = ""; + } + path = string_format("%s%s", home, path.c_str() + 1); } - // 2. 共通ディレクトリ(?) - // XXX 共通ディレクトリを返す関数を用意したほうがいいか - path = GetVMDir() + "../" + name; - if (stat(path.c_str(), &st) == 0) { - return path; + // 3. 相対パスなら、VM ディレクトリからの相対 + if (path[0] != '/') { + path = GetVMDir() + path; } - return ""; + return path; +} + +// 現在の VM が指定の feature を持っているか? +bool +MainApp::HasVMFeature(vmf_t feature) const +{ + uint32 vmf = 1 << GetVMType(); + return (vmf & feature) != 0; }