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

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

unix.superglobalmegacorp.com

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