|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2018 [email protected]
4: //
5:
6: #include "header.h"
7: #include "mainapp.h"
1.1.1.2 ! root 8: #include "mystring.h"
! 9: #include <getopt.h>
! 10: #include <sys/stat.h>
! 11:
! 12: // グローバルインスタンス
! 13: MainApp gMainApp;
! 14:
! 15: // ヘルプメッセージ
! 16: void
! 17: MainApp::ShowHelp(bool all) const
! 18: {
! 19: ShowVersion();
! 20:
! 21: #define p(msg) printf(" " msg "\n")
! 22:
! 23: printf("usage: %s [<options>]\n", getprogname());
! 24: p("-A <file> load and execute host binary (a.out or ELF)");
! 25: p("-c <dir> vm directory");
! 26: p("-f fast mode");
! 27: if (IsGUI())
! 28: p("--fontsize <height> fontsize in monitors {12,16,24} (default:12)");
! 29: p("-h show brief help message");
! 30: p("--help show all help message including for developers");
! 31: if (IsGUI())
! 32: p("-s,--scale <scale> screen scale, default 1.0");
! 33: p("--show-config show configuration variables");
! 34: p("-v show version");
! 35: p("-V <key>=<val> overwrite config option");
! 36:
! 37: if (all) {
! 38: printf("\n(options for developers)\n");
! 39: p("-b <addr> breakpoint");
! 40: p("-B <rom#> benchmark mode");
! 41: p("-C output log to console");
! 42: p("-d debugger prompt on startup");
! 43: p("-D debugger on console");
! 44: p("-L <name>=<lv>[,..] set loglevel (-Lhelp displays names)");
! 45: p("-M <name>[,..] monitors to display at startup (-Mhelp displays names)");
! 46: p("-X <human68k executable> human68k console emulation");
! 47: }
! 48: }
! 49:
! 50: // long option は enum と struct option をアルファベット順に並べる。
! 51: // enum は getopt() の1文字のオプションと衝突しなければいいので適当に
! 52: // 0x80 から始めておく。
! 53: enum {
! 54: OPT_fontsize = 0x80,
! 55: OPT_help,
! 56: OPT_show_config,
! 57: };
! 58: static struct option longopts[] = {
! 59: { "fontsize", required_argument, NULL, OPT_fontsize },
! 60: { "help", no_argument, NULL, OPT_help },
! 61: { "show-config", no_argument, NULL, OPT_show_config },
! 62: { NULL, 0, NULL, 0 },
! 63: };
! 64:
! 65: // 起動時の処理、VM の実行開始前まで。
! 66: // 所々 CLI と GUI で処理が違う。
! 67: bool
! 68: MainApp::Init(bool is_cli_, int ac, char *av[])
! 69: {
! 70: is_cli = is_cli_;
! 71:
! 72: if (!ParseOpt(ac, av)) {
! 73: return false;
! 74: }
1.1 root 75:
1.1.1.2 ! root 76: // ログ機構は引数処理後なるはや
! 77: // CLI ならログは常に標準出力へ(も)出力。
! 78: // GUI なら -C で指定。
! 79: gLogger->UseStdout(IsCLI() ? true : log_to_console);
! 80:
! 81: // 設定を作成。コンストラクタで初期値を用意
! 82: gConfig.reset(new Config());
! 83:
! 84: // 引数で指定されたディレクトリの設定ファイルを読み込んで設定を更新
! 85: ConfigFile file(vmdir + "nono.cfg");
! 86: if (file.Load() == false) {
! 87: return false;
! 88: }
! 89: if (gConfig->Update(file) == false) {
! 90: return false;
! 91: }
! 92: // 最後にコマンドライン引数で更新
! 93: if (gConfig->Update(config_options) == false) {
! 94: return false;
! 95: }
1.1 root 96:
1.1.1.2 ! root 97: // VM 種別を決定
! 98: if (IsCLI() && human68k_file) {
! 99: vmtype = VMTYPE_RXZ;
! 100: } else {
! 101: // VM 種別文字列から vmtype を決定
! 102: const ConfigItem& item = gConfig->Get("vmtype");
! 103: std::string vmstr = string_tolower(item.AsString());
! 104: if (vmstr == "x68030") {
! 105: vmtype = VMTYPE_X68030;
! 106: } else if (vmstr == "luna") {
! 107: vmtype = VMTYPE_LUNA;
! 108: } else if (vmstr == "luna88k") {
! 109: vmtype = VMTYPE_LUNA88K;
! 110: } else {
! 111: if (item.GetFrom() == ConfigItem::FromInitial) {
! 112: // 未指定の時
! 113: warnx("vmtype must be specified");
! 114: } else {
! 115: // ユーザ由来の時
! 116: item.Err("invalid vmtype");
! 117: }
! 118: return false;
! 119: }
! 120: }
1.1 root 121:
1.1.1.2 ! root 122: // VM 作成
! 123: switch (vmtype) {
! 124: case VMTYPE_X68030:
! 125: gVM.reset(new VM_X68030());
! 126: break;
! 127:
! 128: case VMTYPE_LUNA:
! 129: gVM.reset(new VM_LUNA());
! 130: break;
! 131:
! 132: case VMTYPE_RXZ:
! 133: gVM.reset(new VM_RXZ());
! 134: break;
! 135:
! 136: case VMTYPE_LUNA88K:
! 137: gVM.reset(new VM_LUNA88K());
! 138: break;
1.1 root 139:
1.1.1.2 ! root 140: default:
! 141: __unreachable();
! 142: }
! 143:
! 144: // 設定内容表示
! 145: // (MPU コンストラクタでクロック数の初期値を決めるため、その後で表示)
! 146: if (show_config) {
! 147: gConfig->Show();
! 148: }
! 149:
! 150: // 動的なコンストラクション
! 151: if (!gVM->Create()) {
! 152: return false;
! 153: }
! 154:
! 155: // ログレベルを設定。コンストラクト後すぐに行う。
! 156: // -L help もここで処理。
! 157: if (!ParseLogopt()) {
! 158: return false;
! 159: }
! 160:
! 161: return true;
! 162: }
! 163:
! 164: // VM の実行部分。
! 165: // Init() で引数を受け付けてから Start() でデバッガスレッドなどを起動するが、
! 166: // -Mhelp とかが指定された場合はここでスレッド開始する前にプロセスを終了する
! 167: // 必要があるので、分けてある。
! 168: bool
! 169: MainApp::Start()
! 170: {
! 171: // VM 初期化。
! 172: // これ以降はスレッドを開始したかもしれないので false を返す際には
! 173: // VM をデストラクトすること。
! 174: if (!gVM->Init()) {
! 175: gVM.reset();
! 176: return false;
! 177: }
! 178: // デバッガは VM オブジェクトリストに入っていない
! 179: debugger_init();
! 180:
! 181: // 起動時設定の適用
! 182: if (!gVM->Apply()) {
! 183: gVM.reset();
! 184: return false;
! 185: }
! 186:
! 187: return true;
! 188: }
! 189:
! 190: // コマンドライン引数を処理する。
! 191: // 知らない引数とかがあればこちらで usage を表示して false を返す。
! 192: bool
! 193: MainApp::ParseOpt(int ac, char *av[])
! 194: {
! 195: int b;
! 196: int c;
! 197:
! 198: fontsize = 12;
! 199: screen_scale = 1.0;
! 200: debug_breakaddr = 0xffffffff;
! 201:
! 202: while ((c = getopt_long(ac, av, "A:b:B:c:CdDfhL:M:s:vV:X:",
! 203: longopts, NULL)) != -1) {
! 204: switch (c) {
! 205: case 'A':
! 206: host_file = optarg;
! 207: break;
! 208:
! 209: case 'b':
! 210: debug_breakaddr = (uint32)strtoul(optarg, NULL, 16);
! 211: break;
! 212:
! 213: case 'B':
! 214: b = atoi(optarg);
! 215: if (b < 2 || b > 6) {
! 216: fprintf(stderr, "-B <benchmark_mode>: 2..6\n");
! 217: return false;
! 218: }
! 219: benchmark_mode = b;
! 220: break;
! 221:
! 222: case 'c':
! 223: vmdir = std::string(optarg);
! 224: break;
! 225:
! 226: case 'C':
! 227: log_to_console = true;
! 228: break;
! 229:
! 230: case 'd':
! 231: debug_on_start = true;
! 232: break;
! 233:
! 234: case 'D':
! 235: debug_on_console = true;
! 236: break;
! 237:
! 238: case 'f':
! 239: fast_mode = true;
! 240: break;
! 241:
! 242: case 'L':
! 243: if (logopt[0] != '\0') {
! 244: strlcat(logopt, ",", sizeof(logopt));
! 245: }
! 246: strlcat(logopt, optarg, sizeof(logopt));
! 247: break;
! 248:
! 249: case 'M':
! 250: if (!monitor_opt.empty()) {
! 251: monitor_opt += ",";
! 252: }
! 253: monitor_opt += optarg;
! 254: break;
! 255:
! 256: case 's':
! 257: if (IsGUI()) {
! 258: char *end;
! 259: errno = 0;
! 260: screen_scale = strtod(optarg, &end);
! 261: if (end == optarg || end[0] != '\0' || errno == ERANGE) {
! 262: warnx("-s: invalid argument");
! 263: return false;
! 264: }
! 265: // 上限は適当
! 266: if (screen_scale <= 0.0 || screen_scale >= 10.0) {
! 267: warnx("-s: invalid scale");
! 268: return false;
! 269: }
! 270: }
! 271: break;
! 272:
! 273: case 'X':
! 274: human68k_file = optarg;
! 275: for (int i = optind; i < ac; i++) {
! 276: if (i != optind) {
! 277: strlcat(human68k_arg, " ", sizeof(human68k_arg));
! 278: }
! 279: strlcat(human68k_arg, av[i], sizeof(human68k_arg));
! 280: }
! 281: optind = ac;
! 282: break;
! 283:
! 284: case 'v':
! 285: ShowVersion();
! 286: exit(0);
! 287:
! 288: case 'V':
! 289: config_options.push_back(optarg);
! 290: break;
! 291:
! 292: case OPT_fontsize:
! 293: fontsize = atoi(optarg);
! 294: break;
! 295:
! 296: case OPT_help:
! 297: ShowHelp(true);
! 298: return false;
! 299:
! 300: case OPT_show_config:
! 301: show_config = true;
! 302: break;
! 303:
! 304: case 'h':
! 305: default:
! 306: ShowHelp(false);
! 307: return false;
! 308: }
! 309: }
! 310:
! 311: // vmdir に '/' を付けておく
! 312: if (vmdir.empty()) {
! 313: vmdir = ".";
! 314: }
! 315: if (vmdir.back() != '/') {
! 316: vmdir += '/';
! 317: }
! 318:
! 319: // CLI 版で -Mhelp つけても黙って起動するのはさすがにどうかと思う。
! 320: // とは言え GUI 版のコマンドラインを CLI 版用に書き換えた時に -M を
! 321: // 消して回らないと起動できないのも面倒なので -M オプション自体は
! 322: // 無意味だけど許容する。うーん。
! 323: if (IsCLI()) {
! 324: if (monitor_opt == "help") {
! 325: warnx("-Mhelp is not available on CLI");
! 326: return false;
! 327: }
! 328: if (!monitor_opt.empty()) {
! 329: warnx("-M option is ignored on CLI");
! 330: return true;
! 331: }
! 332: }
! 333:
! 334: return true;
! 335: }
! 336:
! 337: // バージョンを表示
! 338: void
! 339: MainApp::ShowVersion() const
! 340: {
! 341: // ここは実行ファイル名によらず nono にする
! 342: fprintf(stderr, "nono version %d.%d.%d (%s)\n",
! 343: NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER, NONO_DATE);
! 344: }
! 345:
! 346:
! 347: // 引数 logopt のログ指定文字列をパースする。
1.1 root 348: // arg は "foo=1,bar=2" 形式の文字列で、これを分解して
349: // それぞれ担当するオブジェクトのログレベルにセットする。
350: bool
1.1.1.2 ! root 351: MainApp::ParseLogopt()
1.1 root 352: {
353: char *buf;
354: char *last;
355: char *p;
356: bool rv;
357:
358: // "help" (完全一致) なら識別子一覧を表示。
1.1.1.2 ! root 359: if (strcmp(logopt, "help") == 0) {
1.1 root 360: for (auto& obj : gObjects) {
361: if (obj->logname != "?") {
362: printf("%s\n", obj->logname.c_str());
363: }
364: }
365: // エイリアスとか
366: printf("sch -> scheduler\n");
1.1.1.2 ! root 367: if (vmtype == VMTYPE_LUNA) {
1.1 root 368: printf("scc -> sio\n");
369: }
370: printf("all\n");
371: return false;
372: }
373:
374: rv = false;
1.1.1.2 ! root 375: buf = strdup(logopt);
1.1 root 376: for (p = strtok_r(buf, ",", &last);
377: p;
378: p = strtok_r(NULL, ",", &last))
379: {
380: const char *name;
381: char *v;
382: int val;
383:
384: name = p;
385:
386: v = strchr(p, '=');
387: if (v) {
388: *v++ = '\0';
389: val = atoi(v);
390: } else {
391: val = 1;
392: }
393: // ここで name は変数名、val は値(省略されたら1)
394:
395: if (strlen(name) < 1) {
396: printf("invalid logname '%s'\n", name);
397: goto abort;
398: }
399:
400: // 短縮形とかエイリアスとか
401: if (strcmp(name, "sch") == 0)
402: name = "scheduler";
1.1.1.2 ! root 403: if (vmtype == VMTYPE_LUNA) {
1.1 root 404: if (strcmp(name, "scc") == 0)
405: name = "sio";
406: }
407:
408: // 比較
409: if (strcmp(name, "all") == 0) {
410: // "all" なら none 以外の全部にセット
411: for (auto& obj : gObjects) {
412: obj->loglevel = val;
413: }
414: } else {
415: // それ以外は一致するキーを探してセット
416: std::string sname = name;
417: bool found = false;
418: for (auto& obj : gObjects) {
419: if (obj->logname == sname) {
420: obj->loglevel = val;
421: found = true;
422: break;
423: }
424: }
425: // 見つからない場合はエラー
426: if (!found) {
427: printf("invalid logname '%s'\n", name);
428: goto abort;
429: }
430: }
431: }
432:
433: rv = true;
434: abort:
435: free(buf);
436: return rv;
437: }
1.1.1.2 ! root 438:
! 439: // 関連するファイルのパスを取得。
! 440: std::string
! 441: MainApp::SearchFile(const std::string& name) const
! 442: {
! 443: std::string path;
! 444: struct stat st;
! 445:
! 446: // ファイルがあるかどうかを調べるだけで、存在したけどそれに何らか
! 447: // 不都合があったら次を試すとかはしない。
! 448:
! 449: // 1. 設定ファイルディレクトリ
! 450: path = GetVMDir() + name;
! 451: if (stat(path.c_str(), &st) == 0) {
! 452: return path;
! 453: }
! 454:
! 455: // 2. 共通ディレクトリ(?)
! 456: // XXX 共通ディレクトリを返す関数を用意したほうがいいか
! 457: path = GetVMDir() + "../" + name;
! 458: if (stat(path.c_str(), &st) == 0) {
! 459: return path;
! 460: }
! 461:
! 462: return "";
! 463: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.