Annotation of nono/lib/mainapp.cpp, revision 1.1.1.5

1.1       root        1: //
                      2: // nono
1.1.1.3   root        3: // Copyright (C) 2020 nono project
                      4: // Licensed under nono-license.txt
1.1       root        5: //
                      6: 
                      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())
1.1.1.4   root       28:                p("--fontsize <height> fontsize in monitors {12,16} (default:12)");
1.1.1.2   root       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");
1.1.1.4   root       39:                p("-b <addr>[,<skip>]  set breakpoint");
1.1.1.2   root       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,
1.1.1.3   root       57:        OPT_show_config_all,
1.1.1.2   root       58: };
                     59: static struct option longopts[] = {
                     60:        { "fontsize",           required_argument,      NULL,   OPT_fontsize },
                     61:        { "help",                       no_argument,            NULL,   OPT_help },
                     62:        { "show-config",        no_argument,            NULL,   OPT_show_config },
1.1.1.3   root       63:        // --show-config-all は開発用なのでヘルプには載せない
                     64:        { "show-config-all",no_argument,                NULL,   OPT_show_config_all },
1.1.1.2   root       65:        { NULL,                         0,                                      NULL,   0 },
                     66: };
                     67: 
                     68: // 起動時の処理、VM の実行開始前まで。
                     69: // 所々 CLI と GUI で処理が違う。
1.1.1.5 ! root       70: // 戻り値は以下のいずれか。
        !            71: //  MainApp::PASS .. 実行を継続(通常パス)
        !            72: //  EXIT_SUCCESS / EXIT_FAILURE .. この終了コードでアプリケーションを終了
        !            73: int
1.1.1.2   root       74: MainApp::Init(bool is_cli_, int ac, char *av[])
                     75: {
                     76:        is_cli = is_cli_;
                     77: 
                     78:        if (!ParseOpt(ac, av)) {
1.1.1.5 ! root       79:                return EXIT_FAILURE;
1.1.1.2   root       80:        }
1.1       root       81: 
1.1.1.2   root       82:        // ログ機構は引数処理後なるはや
                     83:        // CLI ならログは常に標準出力へ(も)出力。
                     84:        // GUI なら -C で指定。
1.1.1.3   root       85:        gLogger.UseStdout(IsCLI() ? true : log_to_console);
1.1.1.2   root       86: 
                     87:        // 設定を作成。コンストラクタで初期値を用意
                     88:        gConfig.reset(new Config());
                     89: 
                     90:        // 引数で指定されたディレクトリの設定ファイルを読み込んで設定を更新
                     91:        ConfigFile file(vmdir + "nono.cfg");
                     92:        if (file.Load() == false) {
1.1.1.5 ! root       93:                return EXIT_FAILURE;
1.1.1.2   root       94:        }
                     95:        if (gConfig->Update(file) == false) {
1.1.1.5 ! root       96:                return EXIT_FAILURE;
1.1.1.2   root       97:        }
                     98:        // 最後にコマンドライン引数で更新
                     99:        if (gConfig->Update(config_options) == false) {
1.1.1.5 ! root      100:                return EXIT_FAILURE;
1.1.1.2   root      101:        }
1.1       root      102: 
1.1.1.2   root      103:        // VM 種別を決定
                    104:        if (IsCLI() && human68k_file) {
                    105:                vmtype = VMTYPE_RXZ;
                    106:        } else {
                    107:                // VM 種別文字列から vmtype を決定
1.1.1.3   root      108:                const ConfigItem& item = gConfig->Find("vmtype");
1.1.1.2   root      109:                std::string vmstr = string_tolower(item.AsString());
                    110:                if (vmstr == "x68030") {
                    111:                        vmtype = VMTYPE_X68030;
                    112:                } else if (vmstr == "luna") {
1.1.1.3   root      113:                        vmtype = VMTYPE_LUNA1;
1.1.1.2   root      114:                } else if (vmstr == "luna88k") {
                    115:                        vmtype = VMTYPE_LUNA88K;
                    116:                } else {
                    117:                        if (item.GetFrom() == ConfigItem::FromInitial) {
                    118:                                // 未指定の時
                    119:                                warnx("vmtype must be specified");
                    120:                        } else {
                    121:                                // ユーザ由来の時
                    122:                                item.Err("invalid vmtype");
                    123:                        }
1.1.1.5 ! root      124:                        return EXIT_FAILURE;
1.1.1.2   root      125:                }
                    126:        }
1.1       root      127: 
1.1.1.2   root      128:        // VM 作成
                    129:        switch (vmtype) {
                    130:         case VMTYPE_X68030:
                    131:                gVM.reset(new VM_X68030());
                    132:                break;
                    133: 
1.1.1.3   root      134:         case VMTYPE_LUNA1:
1.1.1.2   root      135:                gVM.reset(new VM_LUNA());
                    136:                break;
                    137: 
                    138:         case VMTYPE_RXZ:
                    139:                gVM.reset(new VM_RXZ());
                    140:                break;
                    141: 
                    142:         case VMTYPE_LUNA88K:
                    143:                gVM.reset(new VM_LUNA88K());
                    144:                break;
1.1       root      145: 
1.1.1.2   root      146:         default:
                    147:                __unreachable();
                    148:        }
                    149: 
1.1.1.5 ! root      150:        // 動的なコンストラクション
        !           151:        if (!gVM->Create()) {
        !           152:                return EXIT_FAILURE;
        !           153:        }
        !           154: 
        !           155:        // 設定内容を表示して終了
        !           156:        // (VM コンストラクタで変数の増減があるのでそれより後、
        !           157:        // Create() でも SCSI パラメータを減らすので、それより後)
1.1.1.3   root      158:        gConfig->Fix();
1.1.1.2   root      159:        if (show_config) {
1.1.1.3   root      160:                gConfig->Show(show_config - 1);
1.1.1.5 ! root      161:                return EXIT_SUCCESS;
1.1.1.2   root      162:        }
                    163: 
                    164:        // ログレベルを設定。コンストラクト後すぐに行う。
                    165:        // -L help もここで処理。
                    166:        if (!ParseLogopt()) {
1.1.1.5 ! root      167:                return EXIT_FAILURE;
1.1.1.2   root      168:        }
                    169: 
1.1.1.5 ! root      170:        return PASS;
1.1.1.2   root      171: }
                    172: 
                    173: // VM の実行部分。
                    174: // Init() で引数を受け付けてから Start() でデバッガスレッドなどを起動するが、
                    175: // -Mhelp とかが指定された場合はここでスレッド開始する前にプロセスを終了する
                    176: // 必要があるので、分けてある。
                    177: bool
                    178: MainApp::Start()
                    179: {
                    180:        // VM 初期化。
                    181:        // これ以降はスレッドを開始したかもしれないので false を返す際には
                    182:        // VM をデストラクトすること。
                    183:        if (!gVM->Init()) {
                    184:                gVM.reset();
                    185:                return false;
                    186:        }
                    187:        // デバッガは VM オブジェクトリストに入っていない
                    188:        debugger_init();
                    189: 
                    190:        // 起動時設定の適用
                    191:        if (!gVM->Apply()) {
                    192:                gVM.reset();
                    193:                return false;
                    194:        }
                    195: 
                    196:        return true;
                    197: }
                    198: 
                    199: // コマンドライン引数を処理する。
                    200: // 知らない引数とかがあればこちらで usage を表示して false を返す。
                    201: bool
                    202: MainApp::ParseOpt(int ac, char *av[])
                    203: {
                    204:        int b;
                    205:        int c;
                    206: 
                    207:        fontsize = 12;
                    208:        screen_scale = 1.0;
                    209: 
                    210:        while ((c = getopt_long(ac, av, "A:b:B:c:CdDfhL:M:s:vV:X:",
                    211:                                longopts, NULL)) != -1) {
                    212:                switch (c) {
                    213:                 case 'A':
                    214:                        host_file = optarg;
                    215:                        break;
                    216: 
                    217:                 case 'b':
1.1.1.4   root      218:                        debug_breakaddr.push_back(optarg);
1.1.1.2   root      219:                        break;
                    220: 
                    221:                 case 'B':
                    222:                        b = atoi(optarg);
                    223:                        if (b < 2 || b > 6) {
                    224:                                fprintf(stderr, "-B <benchmark_mode>: 2..6\n");
                    225:                                return false;
                    226:                        }
                    227:                        benchmark_mode = b;
                    228:                        break;
                    229: 
                    230:                 case 'c':
                    231:                        vmdir = std::string(optarg);
                    232:                        break;
                    233: 
                    234:                 case 'C':
                    235:                        log_to_console = true;
                    236:                        break;
                    237: 
                    238:                 case 'd':
                    239:                        debug_on_start = true;
                    240:                        break;
                    241: 
                    242:                 case 'D':
                    243:                        debug_on_console = true;
                    244:                        break;
                    245: 
                    246:                 case 'f':
                    247:                        fast_mode = true;
                    248:                        break;
                    249: 
                    250:                 case 'L':
                    251:                        if (logopt[0] != '\0') {
                    252:                                strlcat(logopt, ",", sizeof(logopt));
                    253:                        }
                    254:                        strlcat(logopt, optarg, sizeof(logopt));
                    255:                        break;
                    256: 
                    257:                 case 'M':
                    258:                        if (!monitor_opt.empty()) {
                    259:                                monitor_opt += ",";
                    260:                        }
                    261:                        monitor_opt += optarg;
                    262:                        break;
                    263: 
                    264:                 case 's':
                    265:                        if (IsGUI()) {
                    266:                                char *end;
                    267:                                errno = 0;
                    268:                                screen_scale = strtod(optarg, &end);
                    269:                                if (end == optarg || end[0] != '\0' || errno == ERANGE) {
                    270:                                        warnx("-s: invalid argument");
                    271:                                        return false;
                    272:                                }
                    273:                                // 上限は適当
                    274:                                if (screen_scale <= 0.0 || screen_scale >= 10.0) {
                    275:                                        warnx("-s: invalid scale");
                    276:                                        return false;
                    277:                                }
                    278:                        }
                    279:                        break;
                    280: 
                    281:                 case 'X':
                    282:                        human68k_file = optarg;
                    283:                        for (int i = optind; i < ac; i++) {
                    284:                                if (i != optind) {
                    285:                                        strlcat(human68k_arg, " ", sizeof(human68k_arg));
                    286:                                }
                    287:                                strlcat(human68k_arg, av[i], sizeof(human68k_arg));
                    288:                        }
                    289:                        optind = ac;
                    290:                        break;
                    291: 
                    292:                 case 'v':
                    293:                        ShowVersion();
                    294:                        exit(0);
                    295: 
                    296:                 case 'V':
                    297:                        config_options.push_back(optarg);
                    298:                        break;
                    299: 
                    300:                 case OPT_fontsize:
                    301:                        fontsize = atoi(optarg);
                    302:                        break;
                    303: 
                    304:                 case OPT_help:
                    305:                        ShowHelp(true);
                    306:                        return false;
                    307: 
                    308:                 case OPT_show_config:
1.1.1.3   root      309:                        show_config = 1;
                    310:                        break;
                    311: 
                    312:                 case OPT_show_config_all:
                    313:                        show_config = 2;
1.1.1.2   root      314:                        break;
                    315: 
                    316:                 case 'h':
                    317:                 default:
                    318:                        ShowHelp(false);
                    319:                        return false;
                    320:                }
                    321:        }
                    322: 
                    323:        // vmdir に '/' を付けておく
                    324:        if (vmdir.empty()) {
                    325:                vmdir = ".";
                    326:        }
                    327:        if (vmdir.back() != '/') {
                    328:                vmdir += '/';
                    329:        }
                    330: 
                    331:        // CLI 版で -Mhelp つけても黙って起動するのはさすがにどうかと思う。
                    332:        // とは言え GUI 版のコマンドラインを CLI 版用に書き換えた時に -M を
                    333:        // 消して回らないと起動できないのも面倒なので -M オプション自体は
                    334:        // 無意味だけど許容する。うーん。
                    335:        if (IsCLI()) {
                    336:                if (monitor_opt == "help") {
                    337:                        warnx("-Mhelp is not available on CLI");
                    338:                        return false;
                    339:                }
                    340:                if (!monitor_opt.empty()) {
                    341:                        warnx("-M option is ignored on CLI");
                    342:                        return true;
                    343:                }
                    344:        }
                    345: 
                    346:        return true;
                    347: }
                    348: 
                    349: // バージョンを表示
                    350: void
                    351: MainApp::ShowVersion() const
                    352: {
                    353:        // ここは実行ファイル名によらず nono にする
                    354:        fprintf(stderr, "nono version %d.%d.%d (%s)\n",
                    355:                NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER, NONO_DATE);
                    356: }
                    357: 
                    358: 
                    359: // 引数 logopt のログ指定文字列をパースする。
1.1       root      360: // arg は "foo=1,bar=2" 形式の文字列で、これを分解して
                    361: // それぞれ担当するオブジェクトのログレベルにセットする。
                    362: bool
1.1.1.2   root      363: MainApp::ParseLogopt()
1.1       root      364: {
                    365:        char *buf;
                    366:        char *last;
                    367:        char *p;
                    368:        bool rv;
                    369: 
                    370:        // "help" (完全一致) なら識別子一覧を表示。
1.1.1.2   root      371:        if (strcmp(logopt, "help") == 0) {
1.1       root      372:                for (auto& obj : gObjects) {
                    373:                        if (obj->logname != "?") {
                    374:                                printf("%s\n", obj->logname.c_str());
                    375:                        }
                    376:                }
                    377:                // エイリアスとか
                    378:                printf("sch -> scheduler\n");
1.1.1.3   root      379:                if (gMainApp.HasVMFeature(VMF_LUNA)) {
1.1       root      380:                        printf("scc -> sio\n");
                    381:                }
                    382:                printf("all\n");
                    383:                return false;
                    384:        }
                    385: 
                    386:        rv = false;
1.1.1.2   root      387:        buf = strdup(logopt);
1.1       root      388:        for (p = strtok_r(buf, ",", &last);
                    389:                p;
                    390:                p = strtok_r(NULL, ",", &last))
                    391:        {
                    392:                const char *name;
                    393:                char *v;
                    394:                int val;
                    395: 
                    396:                name = p;
                    397: 
                    398:                v = strchr(p, '=');
                    399:                if (v) {
                    400:                        *v++ = '\0';
                    401:                        val = atoi(v);
                    402:                } else {
                    403:                        val = 1;
                    404:                }
                    405:                // ここで name は変数名、val は値(省略されたら1)
                    406: 
                    407:                if (strlen(name) < 1) {
                    408:                        printf("invalid logname '%s'\n", name);
                    409:                        goto abort;
                    410:                }
                    411: 
                    412:                // 短縮形とかエイリアスとか
                    413:                if (strcmp(name, "sch") == 0)
                    414:                        name = "scheduler";
1.1.1.3   root      415:                if (gMainApp.HasVMFeature(VMF_LUNA)) {
1.1       root      416:                        if (strcmp(name, "scc") == 0)
                    417:                                name = "sio";
                    418:                }
                    419: 
                    420:                // 比較
                    421:                if (strcmp(name, "all") == 0) {
                    422:                        // "all" なら none 以外の全部にセット
                    423:                        for (auto& obj : gObjects) {
                    424:                                obj->loglevel = val;
                    425:                        }
                    426:                } else {
1.1.1.3   root      427:                        // それ以外は一致するキーを探してセット。
                    428:                        // 複数のオブジェクトが同じ logname を持ってもよい (CMMUとか)。
1.1       root      429:                        std::string sname = name;
                    430:                        bool found = false;
                    431:                        for (auto& obj : gObjects) {
                    432:                                if (obj->logname == sname) {
                    433:                                        obj->loglevel = val;
                    434:                                        found = true;
                    435:                                }
                    436:                        }
                    437:                        // 見つからない場合はエラー
                    438:                        if (!found) {
                    439:                                printf("invalid logname '%s'\n", name);
                    440:                                goto abort;
                    441:                        }
                    442:                }       
                    443:        }
                    444: 
                    445:        rv = true;
                    446:  abort:
                    447:        free(buf);
                    448:        return rv;
                    449: }
1.1.1.2   root      450: 
                    451: // 関連するファイルのパスを取得。
1.1.1.3   root      452: // 1. name がファイル名のみなら、VM ディレクトリとその親ディレクトリを検索。
                    453: //    ファイルが存在すればその時点で戻り値とする。ファイルが存在したけど
                    454: //    何らか不都合があったとしても (例えばファイルサイズが 0 だとか
                    455: //    パーミッションが足りないとか) 次を試すとかはしない。
                    456: // 2. name が '~' から始まっていればホームディレクトリに展開。
                    457: // 3. name が相対パスなら VM ディレクトリからの相対パスとする。
                    458: // 4. name は絶対パスのはずなので、そのまま使用する。
1.1.1.2   root      459: std::string
                    460: MainApp::SearchFile(const std::string& name) const
                    461: {
                    462:        std::string path;
                    463:        struct stat st;
                    464: 
1.1.1.3   root      465:        // 1. パス区切りを含んでなければ、ファイル名のみ
                    466:        if (name.find('/') == std::string::npos) {
                    467:                // 1a. VM ディレクトリ
                    468:                path = GetVMDir() + name;
                    469:                if (stat(path.c_str(), &st) == 0) {
                    470:                        return path;
                    471:                }
                    472: 
                    473:                // 2. 親ディレクトリ
                    474:                path = GetVMDir() + "../" + name;
                    475:                if (stat(path.c_str(), &st) == 0) {
                    476:                        return path;
                    477:                }
                    478: 
                    479:                // どちらもなければエラー
                    480:                return "";
                    481:        }
1.1.1.2   root      482: 
1.1.1.3   root      483:        // 2. 先頭の '~' を $HOME に展開
                    484:        path = name;
                    485:        if (path[0] == '~' && path[1] == '/') {
                    486:                const char *home = getenv("HOME");
                    487:                if (home == NULL) {
                    488:                        home = "";
                    489:                }
                    490:                path = string_format("%s%s", home, path.c_str() + 1);
1.1.1.2   root      491:        }
                    492: 
1.1.1.3   root      493:        // 3. 相対パスなら、VM ディレクトリからの相対
                    494:        if (path[0] != '/') {
                    495:                path = GetVMDir() + path;
1.1.1.2   root      496:        }
                    497: 
1.1.1.3   root      498:        return path;
                    499: }
                    500: 
                    501: // 現在の VM が指定の feature を持っているか?
                    502: bool
                    503: MainApp::HasVMFeature(vmf_t feature) const
                    504: {
                    505:        uint32 vmf = 1 << GetVMType();
                    506:        return (vmf & feature) != 0;
1.1.1.2   root      507: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.