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

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

unix.superglobalmegacorp.com

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