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

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: 
1.1.1.12  root        7: //
                      8: // CLI/GUI 共通のメイン部分
                      9: //
                     10: 
1.1       root       11: #include "mainapp.h"
1.1.1.12  root       12: #include "config.h"
                     13: #include "hostnet.h"
                     14: #include "logger.h"
                     15: #include "monitor.h"
1.1.1.2   root       16: #include "mystring.h"
1.1.1.12  root       17: #include "mythread.h"
1.1.1.14  root       18: #include "sram.h"
1.1.1.12  root       19: #include "vm_luna.h"
1.1.1.14  root       20: #include "vm_news.h"
1.1.1.12  root       21: #include "vm_x68k.h"
1.1.1.14  root       22: #include <fcntl.h>
1.1.1.2   root       23: #include <getopt.h>
1.1.1.14  root       24: #include <pwd.h>
1.1.1.2   root       25: #include <sys/stat.h>
1.1.1.8   root       26: #include <algorithm>
1.1.1.12  root       27: #if defined(__linux__)
                     28: #include <sys/prctl.h>
                     29: #endif
1.1.1.2   root       30: 
                     31: // グローバルインスタンス
                     32: MainApp gMainApp;
                     33: 
1.1.1.12  root       34: // コンストラクタ
                     35: MainApp::MainApp()
                     36: {
                     37: }
                     38: 
                     39: // デストラクタ
                     40: MainApp::~MainApp()
                     41: {
                     42:        // 逆順に解放
                     43: 
                     44:        pVM.reset();
1.1.1.14  root       45: 
                     46:        pConfig.reset();
                     47:        gConfig = NULL;
                     48: 
                     49:        pMonitorManager.reset();
                     50:        gMonitorManager = NULL;
                     51: 
1.1.1.12  root       52: #if 0  // 確認用
                     53:        if (objects.empty() == false) {
                     54:                printf("~MainApp: undead objects are:");
                     55:                for (const auto *obj : objects) {
1.1.1.14  root       56:                        printf(" %s", obj->GetIdStr());
1.1.1.12  root       57:                }
                     58:                printf("\n");
                     59:        }
                     60: #endif
                     61: 
                     62:        logger.reset();
                     63: }
                     64: 
1.1.1.2   root       65: // ヘルプメッセージ
                     66: void
                     67: MainApp::ShowHelp(bool all) const
                     68: {
                     69:        ShowVersion();
                     70: 
                     71: #define p(msg) printf(" " msg "\n")
                     72: 
                     73:        printf("usage: %s [<options>]\n", getprogname());
1.1.1.6   root       74:        p("-c <path>           vm directory or configuration file");
1.1.1.14  root       75:        p("-f                  fast mode (same as '-V fast-mode=1')");
1.1.1.2   root       76:        if (IsGUI())
1.1.1.14  root       77:                p("--fontsize <height> fontsize (same as '-V monitor-fontsize=<height>')");
1.1.1.2   root       78:        p("-h                  show brief help message");
                     79:        p("--help              show all help message including for developers");
                     80:        if (IsGUI())
1.1.1.14  root       81:                p("-s <scale>          mainview scale (same as '-V mainview-scale=<scale>')");
1.1.1.2   root       82:        p("--show-config       show configuration variables");
1.1.1.8   root       83:        p("--show-hostnet      show list of hostnet drivers");
1.1.1.2   root       84:        p("-v                  show version");
                     85:        p("-V <key>=<val>      overwrite config option");
1.1.1.9   root       86:        p("-X <file> [arg..]   load and execute host binary (a.out or ELF)");
1.1.1.2   root       87: 
                     88:        if (all) {
                     89:                printf("\n(options for developers)\n");
1.1.1.14  root       90:                p("-b [<cpu>,]<addr>[,<skip>]");
                     91:                p("                    set breakpoint. <cpu> is either 'main'(default) or 'xp'");
1.1.1.2   root       92:                p("-B <rom#>           benchmark mode");
                     93:                p("-C                  output log to console");
                     94:                p("-d                  debugger prompt on startup");
1.1.1.12  root       95:                p("-D                  same as '-V debugger-driver=stdio'");
1.1.1.10  root       96:                p("-H                  human68k console emulation");
1.1.1.2   root       97:                p("-L <name>=<lv>[,..] set loglevel (-Lhelp displays names)");
1.1.1.11  root       98:                p("--load-only <file>  load host binary (a.out or ELF) but not execute");
1.1.1.2   root       99:                p("-M <name>[,..]      monitors to display at startup (-Mhelp displays names)");
1.1.1.14  root      100:                p("   memdump<N>[=<hex-addr>[.<fmt>]]  fmt := B/W/L/M(MMU)/I(Disasm)/Z(XPDisasm)");
                    101:                p("-S                  MSX-DOS console emulation");
1.1.1.12  root      102:                p("--perf              performance measure mode");
1.1.1.2   root      103:        }
                    104: }
                    105: 
                    106: // long option は enum と struct option をアルファベット順に並べる。
                    107: // enum は getopt() の1文字のオプションと衝突しなければいいので適当に
                    108: // 0x80 から始めておく。
                    109: enum {
1.1.1.14  root      110:        OPTstart = 0x80 - 1,
                    111:        OPT_create_sram,
                    112:        OPT_fontsize,
1.1.1.11  root      113:        OPT_load_only,
1.1.1.2   root      114:        OPT_help,
1.1.1.12  root      115:        OPT_perf,
1.1.1.2   root      116:        OPT_show_config,
1.1.1.3   root      117:        OPT_show_config_all,
1.1.1.8   root      118:        OPT_show_hostnet,
1.1.1.2   root      119: };
                    120: static struct option longopts[] = {
1.1.1.14  root      121:        { "create-sram",        no_argument,            NULL,   OPT_create_sram },
1.1.1.2   root      122:        { "fontsize",           required_argument,      NULL,   OPT_fontsize },
1.1.1.11  root      123:        { "load-only",          required_argument,      NULL,   OPT_load_only },
1.1.1.2   root      124:        { "help",                       no_argument,            NULL,   OPT_help },
1.1.1.12  root      125:        { "perf",                       no_argument,            NULL,   OPT_perf },
1.1.1.2   root      126:        { "show-config",        no_argument,            NULL,   OPT_show_config },
1.1.1.3   root      127:        // --show-config-all は開発用なのでヘルプには載せない
                    128:        { "show-config-all",no_argument,                NULL,   OPT_show_config_all },
1.1.1.8   root      129:        { "show-hostnet",       no_argument,            NULL,   OPT_show_hostnet },
1.1.1.2   root      130:        { NULL,                         0,                                      NULL,   0 },
                    131: };
                    132: 
1.1.1.7   root      133: // VM の初期化、ステージ1。
                    134: // VM 作成と設定確定あたりまで。スレッド生成を伴わないもの。
1.1.1.2   root      135: // 所々 CLI と GUI で処理が違う。
1.1.1.5   root      136: // 戻り値は以下のいずれか。
                    137: //  MainApp::PASS .. 実行を継続(通常パス)
                    138: //  EXIT_SUCCESS / EXIT_FAILURE .. この終了コードでアプリケーションを終了
                    139: int
1.1.1.7   root      140: MainApp::Init1(bool is_cli_, int ac, char *av[])
1.1.1.2   root      141: {
1.1.1.14  root      142:        int rv;
                    143: 
1.1.1.2   root      144:        is_cli = is_cli_;
                    145: 
1.1.1.12  root      146: #if defined(__linux__)
                    147:        // Linux ではケーパビリティが設定されていると coredump しないので
                    148:        // 明示的に許可する必要がある
                    149:        prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
                    150: #endif
                    151: 
1.1.1.14  root      152:        rv = ParseOpt(ac, av);
                    153:        if (rv != MainApp::PASS) {
                    154:                return rv;
1.1.1.2   root      155:        }
1.1       root      156: 
1.1.1.2   root      157:        // ログ機構は引数処理後なるはや
                    158:        // CLI ならログは常に標準出力へ(も)出力。
                    159:        // GUI なら -C で指定。
1.1.1.12  root      160:        logger.reset(new Logger());
                    161:        logger->UseStdout(IsCLI() ? true : log_to_console);
                    162: 
                    163:        // モニタマネージャ (VM 作成より前、もしかしたら Config より前?)
                    164:        pMonitorManager.reset(new MonitorManager());
                    165:        gMonitorManager = pMonitorManager.get();
1.1.1.2   root      166: 
1.1.1.14  root      167:        // 設定を作成。
                    168:        // c0. コンストラクタで初期値を用意。
1.1.1.12  root      169:        pConfig.reset(new Config());
                    170:        gConfig = pConfig.get();
1.1.1.2   root      171: 
1.1.1.14  root      172:        // c1. ホームディレクトリに設定ファイルがあれば読み込んで設定を更新。
                    173:        struct passwd *passwd = getpwuid(getuid());
                    174:        // 見付からないことはないはずだが、一応。
                    175:        if (passwd != NULL) {
                    176:                std::string filename = std::string(passwd->pw_dir) + "/.nono.cfg";
                    177:                ConfigFile dotfile(filename);
                    178:                if (dotfile.Load()) {
                    179:                        if (gConfig->Update(dotfile) == false) {
                    180:                                return EXIT_FAILURE;
                    181:                        }
                    182:                }
1.1.1.2   root      183:        }
1.1.1.14  root      184: 
                    185:        // c2. VM ディレクトリに設定ファイルがあれば読み込んで設定を更新。
                    186:        // vmfile が存在するかどうかはここで調べるまで分からない。
                    187:        ConfigFile cfgfile(vmfile);
                    188:        if (cfgfile.Load()) {
                    189:                if (gConfig->Update(cfgfile) == false) {
                    190:                        return EXIT_FAILURE;
                    191:                }
1.1.1.2   root      192:        }
1.1.1.14  root      193: 
                    194:        // c3. 最後にコマンドライン引数で更新
                    195:        for (const auto& pair : config_options) {
                    196:                if (gConfig->Update(pair.first, pair.second) == false) {
                    197:                        return EXIT_FAILURE;
                    198:                }
                    199:        }
                    200: 
                    201:        // MSX-DOS モードならここでいくつかパラメータを強制的に変更。
                    202:        if (msxdos_mode) {
                    203:                // LUNA-I 最小構成でいい
                    204:                gConfig->Update("vmtype=luna", "-S");
                    205:                gConfig->Update("ram-size=16", "-S");
                    206:                gConfig->Update("luna-video-plane=1", "-S");
                    207:                // ホスト側には干渉しない。
                    208:                gConfig->Update("hostcom-driver=none", "-S");
                    209:                gConfig->Update("hostnet-driver=none", "-S");
1.1.1.2   root      210:        }
1.1       root      211: 
1.1.1.12  root      212:        // 0. VM 種別を決定
1.1.1.9   root      213:        // VM 種別文字列から vmtype を決定
                    214:        const ConfigItem& item = gConfig->Find("vmtype");
1.1.1.14  root      215:        vmstr = string_tolower(item.AsString());
1.1.1.9   root      216:        if (vmstr == "x68030") {
1.1.1.13  root      217:                vmtype = VMType::X68030;
1.1.1.9   root      218:        } else if (vmstr == "luna") {
1.1.1.13  root      219:                vmtype = VMType::LUNA1;
1.1.1.9   root      220:        } else if (vmstr == "luna88k") {
1.1.1.13  root      221:                vmtype = VMType::LUNA88K;
1.1.1.14  root      222:        } else if (vmstr == "news") {
                    223:                vmtype = VMType::NEWS;
1.1.1.2   root      224:        } else {
1.1.1.9   root      225:                if (item.GetFrom() == ConfigItem::FromInitial) {
                    226:                        // 未指定の時
                    227:                        warnx("vmtype must be specified");
1.1.1.2   root      228:                } else {
1.1.1.9   root      229:                        // ユーザ由来の時
1.1.1.14  root      230:                        item.Err("Invalid vmtype");
1.1.1.2   root      231:                }
1.1.1.9   root      232:                return EXIT_FAILURE;
1.1.1.2   root      233:        }
1.1       root      234: 
1.1.1.14  root      235:        // 0.5. VM が確定したところで SRAM 作成。
                    236:        if (create_sram) {
                    237:                if (vmtype == VMType::X68030) {
                    238:                        return CreateSRAM();
                    239:                } else {
                    240:                        warnx("--create-sram is only for X68030 mode");
                    241:                        return EXIT_FAILURE;
                    242:                }
                    243:        }
                    244: 
1.1.1.12  root      245:        // 1. VM 作成
1.1.1.2   root      246:        switch (vmtype) {
1.1.1.13  root      247:         case VMType::X68030:
1.1.1.12  root      248:                pVM.reset(new VM_X68030());
1.1.1.2   root      249:                break;
                    250: 
1.1.1.13  root      251:         case VMType::LUNA1:
1.1.1.12  root      252:                pVM.reset(new VM_LUNA1());
1.1.1.2   root      253:                break;
                    254: 
1.1.1.13  root      255:         case VMType::LUNA88K:
1.1.1.12  root      256:                pVM.reset(new VM_LUNA88K());
1.1.1.2   root      257:                break;
1.1       root      258: 
1.1.1.14  root      259:         case VMType::NEWS:
                    260:                pVM.reset(new VM_NEWS());
                    261:                break;
                    262: 
1.1.1.2   root      263:         default:
1.1.1.14  root      264:                PANIC("corrupted vmtype=%s", vmstr.c_str());
1.1.1.2   root      265:        }
1.1.1.12  root      266:        gVM = pVM.get();
1.1.1.2   root      267: 
1.1.1.12  root      268:        // 2. 動的なコンストラクション
1.1.1.5   root      269:        if (!gVM->Create()) {
1.1.1.12  root      270:                pVM.reset();
1.1.1.5   root      271:                return EXIT_FAILURE;
                    272:        }
                    273: 
1.1.1.12  root      274:        // 3. ログの処理
1.1.1.8   root      275: 
1.1.1.5   root      276:        // 設定内容を表示して終了
                    277:        // (VM コンストラクタで変数の増減があるのでそれより後、
                    278:        // Create() でも SCSI パラメータを減らすので、それより後)
1.1.1.14  root      279:        if (gConfig->Fix() == false) {
                    280:                pVM.reset();
                    281:                return EXIT_SUCCESS;
                    282:        }
1.1.1.2   root      283:        if (show_config) {
1.1.1.3   root      284:                gConfig->Show(show_config - 1);
1.1.1.12  root      285:                pVM.reset();
1.1.1.5   root      286:                return EXIT_SUCCESS;
1.1.1.2   root      287:        }
                    288: 
1.1.1.12  root      289:        // ログレベルを設定。コンストラクト後すぐに行う。
1.1.1.2   root      290:        // -L help もここで処理。
                    291:        if (!ParseLogopt()) {
1.1.1.12  root      292:                pVM.reset();
1.1.1.5   root      293:                return EXIT_FAILURE;
1.1.1.2   root      294:        }
                    295: 
1.1.1.5   root      296:        return PASS;
1.1.1.2   root      297: }
                    298: 
1.1.1.7   root      299: // VM の初期化、ステージ2。スレッド生成を伴う。
                    300: // Init1() で引数を受け付けてから Init2() でデバッガスレッドなどを起動するが、
1.1.1.2   root      301: // -Mhelp とかが指定された場合はここでスレッド開始する前にプロセスを終了する
1.1.1.7   root      302: // 必要があるので分けてある。wxapp.cpp も参照。
1.1.1.2   root      303: bool
1.1.1.7   root      304: MainApp::Init2()
1.1.1.2   root      305: {
1.1.1.12  root      306:        // 5. VM 初期化。
1.1.1.2   root      307:        if (!gVM->Init()) {
                    308:                return false;
                    309:        }
                    310: 
1.1.1.12  root      311:        // メインスレッド名を設定
                    312:        PTHREAD_SETNAME("Main");
                    313: 
                    314:        // 6. スレッド開始
                    315:        if (!gVM->StartThread()) {
                    316:                return false;
                    317:        }
                    318: 
                    319:        // 7. 起動時設定の適用
1.1.1.2   root      320:        if (!gVM->Apply()) {
                    321:                return false;
                    322:        }
                    323: 
                    324:        return true;
                    325: }
                    326: 
                    327: // コマンドライン引数を処理する。
                    328: // 知らない引数とかがあればこちらで usage を表示して false を返す。
1.1.1.14  root      329: // 戻り値は、MainApp::PASS なら実行を継続、
                    330: // EXIT_SUCCESS/EXIT_FAILURE ならこのコードで終了。
                    331: int
1.1.1.2   root      332: MainApp::ParseOpt(int ac, char *av[])
                    333: {
1.1.1.6   root      334:        struct stat st;
                    335:        const char *cpath;
1.1.1.2   root      336:        int b;
                    337:        int c;
                    338: 
1.1.1.6   root      339:        cpath = ".";
1.1.1.2   root      340: 
1.1.1.14  root      341:        while ((c = getopt_long(ac, av, "b:B:c:CdDfhHL:M:s:SvV:X:",
1.1.1.2   root      342:                                longopts, NULL)) != -1) {
                    343:                switch (c) {
                    344:                 case 'b':
1.1.1.4   root      345:                        debug_breakaddr.push_back(optarg);
1.1.1.2   root      346:                        break;
                    347: 
                    348:                 case 'B':
                    349:                        b = atoi(optarg);
                    350:                        if (b < 2 || b > 6) {
                    351:                                fprintf(stderr, "-B <benchmark_mode>: 2..6\n");
1.1.1.14  root      352:                                return EXIT_FAILURE;
1.1.1.2   root      353:                        }
                    354:                        benchmark_mode = b;
                    355:                        break;
                    356: 
                    357:                 case 'c':
1.1.1.6   root      358:                        cpath = optarg;
                    359:                        // 空文字列なら再び初期値に
                    360:                        if (cpath[0] == '\0') {
                    361:                                cpath = ".";
                    362:                        }
1.1.1.2   root      363:                        break;
                    364: 
                    365:                 case 'C':
                    366:                        log_to_console = true;
                    367:                        break;
                    368: 
                    369:                 case 'd':
                    370:                        debug_on_start = true;
                    371:                        break;
                    372: 
                    373:                 case 'D':
1.1.1.12  root      374:                        // -D は -V debugger-driver=stdio と等価。
1.1.1.14  root      375:                        config_options.emplace_back("debugger-driver=stdio", "-D");
1.1.1.2   root      376:                        break;
                    377: 
                    378:                 case 'f':
1.1.1.14  root      379:                        // -f は -V fast-mode=1 と等価。
                    380:                        config_options.emplace_back("fast-mode=1", "-f");
1.1.1.2   root      381:                        break;
                    382: 
1.1.1.9   root      383:                 case 'H':
                    384:                        human_mode = true;
                    385:                        break;
                    386: 
1.1.1.2   root      387:                 case 'L':
1.1.1.12  root      388:                        AddLogopt(optarg);
1.1.1.2   root      389:                        break;
                    390: 
                    391:                 case 'M':
                    392:                        if (!monitor_opt.empty()) {
                    393:                                monitor_opt += ",";
                    394:                        }
                    395:                        monitor_opt += optarg;
                    396:                        break;
                    397: 
                    398:                 case 's':
1.1.1.14  root      399:                 {
                    400:                        auto line = string_format("mainview-scale=%s", optarg);
                    401:                        config_options.emplace_back(line, "-s");
                    402:                        break;
                    403:                 }
                    404: 
                    405:                 case 'S':
                    406:                        msxdos_mode = true;
1.1.1.2   root      407:                        break;
                    408: 
                    409:                 case 'X':
1.1.1.11  root      410:                        load_and_exec = true;
                    411:                        FALLTHROUGH;
                    412:                 case OPT_load_only:
1.1.1.9   root      413:                        exec_file = optarg;
1.1.1.2   root      414:                        for (int i = optind; i < ac; i++) {
                    415:                                if (i != optind) {
1.1.1.9   root      416:                                        exec_arg += " ";
1.1.1.2   root      417:                                }
1.1.1.9   root      418:                                exec_arg += av[i];
1.1.1.2   root      419:                        }
                    420:                        optind = ac;
                    421:                        break;
                    422: 
                    423:                 case 'v':
                    424:                        ShowVersion();
                    425:                        exit(0);
                    426: 
                    427:                 case 'V':
1.1.1.14  root      428:                        config_options.emplace_back(optarg, "-V");
                    429:                        break;
                    430: 
                    431:                 case OPT_create_sram:
                    432:                        create_sram = true;
1.1.1.2   root      433:                        break;
                    434: 
                    435:                 case OPT_fontsize:
1.1.1.14  root      436:                 {
                    437:                        auto line = string_format("monitor-fontsize=%s", optarg);
                    438:                        config_options.emplace_back(line, "--fontsize");
1.1.1.2   root      439:                        break;
1.1.1.14  root      440:                 }
1.1.1.2   root      441: 
                    442:                 case OPT_help:
                    443:                        ShowHelp(true);
1.1.1.14  root      444:                        return EXIT_SUCCESS;
1.1.1.2   root      445: 
1.1.1.12  root      446:                 case OPT_perf:
                    447:                        // パフォーマンス確認用。
                    448:                        // -Vprom-image=PROM.DAT はパスの問題があるので各自で追加指定
                    449:                        // する必要がある。
1.1.1.14  root      450:                        config_options.emplace_back(".rtc-force-fixed=1", "--perf");
                    451:                        config_options.emplace_back("clock-sync=virtual", "--perf");
                    452:                        config_options.emplace_back("ethernet-macaddr=02:00:00:00:00:01",
                    453:                                "--perf");
                    454:                        config_options.emplace_back("hostcom-driver=none", "--perf");
                    455:                        config_options.emplace_back("hostnet-driver=none", "--perf");
                    456:                        config_options.emplace_back("luna-dipsw1=11110111", "--perf");
                    457:                        config_options.emplace_back("spc0-id0-writeignore=1", "--perf");
                    458:                        config_options.emplace_back("spc0-id1-writeignore=1", "--perf");
                    459:                        config_options.emplace_back("spc0-id2-writeignore=1", "--perf");
                    460:                        config_options.emplace_back("spc0-id3-writeignore=1", "--perf");
                    461:                        config_options.emplace_back("spc0-id4-writeignore=1", "--perf");
                    462:                        config_options.emplace_back("spc0-id5-writeignore=1", "--perf");
                    463:                        config_options.emplace_back("spc0-id6-writeignore=1", "--perf");
                    464:                        config_options.emplace_back("fast-mode=1", "--perf");
                    465:                        // -C
1.1.1.12  root      466:                        log_to_console = true;
                    467:                        break;
                    468: 
1.1.1.2   root      469:                 case OPT_show_config:
1.1.1.3   root      470:                        show_config = 1;
                    471:                        break;
                    472: 
                    473:                 case OPT_show_config_all:
                    474:                        show_config = 2;
1.1.1.2   root      475:                        break;
                    476: 
1.1.1.8   root      477:                 case OPT_show_hostnet:
                    478:                        ShowHostnet();
1.1.1.14  root      479:                        return EXIT_SUCCESS;
1.1.1.8   root      480: 
1.1.1.2   root      481:                 case 'h':
1.1.1.14  root      482:                        ShowHelp(false);
                    483:                        return EXIT_SUCCESS;
                    484: 
1.1.1.2   root      485:                 default:
                    486:                        ShowHelp(false);
1.1.1.14  root      487:                        return EXIT_FAILURE;
1.1.1.2   root      488:                }
                    489:        }
                    490: 
1.1.1.6   root      491:        // 引数がディレクトリなら、それを VM ディレクトリとし、その中の
                    492:        // nono.cfg を設定ファイルとする。
                    493:        // 引数がファイルなら、それを設定ファイルとし、そのファイルがある
                    494:        // ディレクトリを VM ディレクトリとする。
                    495:        // -c DIR       => vmdir = DIR, vmfile = DIR/nono.cfg
                    496:        // -c DIR/FILE  => vmdir = DIR, vmfile = FILE
                    497:        if (stat(cpath, &st) < 0) {
                    498:                warn("stat %s", cpath);
1.1.1.14  root      499:                return EXIT_FAILURE;
1.1.1.2   root      500:        }
1.1.1.6   root      501:        if (S_ISDIR(st.st_mode)) {
                    502:                vmdir = std::string(cpath);
                    503:                if (vmdir.back() != '/') {
                    504:                        vmdir += '/';
                    505:                }
                    506:                vmfile = vmdir + "nono.cfg";
                    507:        } else if (S_ISREG(st.st_mode)) {
                    508:                vmfile = std::string(cpath);
                    509:                auto pos = vmfile.rfind('/');
                    510:                if (pos != std::string::npos) {
                    511:                        vmdir = vmfile.substr(0, pos + 1);
                    512:                } else {
                    513:                        vmdir = "./";
                    514:                }
                    515:        } else {
                    516:                warnx("-c %s: path must be file or directory", cpath);
1.1.1.14  root      517:                return EXIT_FAILURE;
1.1.1.2   root      518:        }
                    519: 
                    520:        // CLI 版で -Mhelp つけても黙って起動するのはさすがにどうかと思う。
                    521:        // とは言え GUI 版のコマンドラインを CLI 版用に書き換えた時に -M を
                    522:        // 消して回らないと起動できないのも面倒なので -M オプション自体は
                    523:        // 無意味だけど許容する。うーん。
                    524:        if (IsCLI()) {
                    525:                if (monitor_opt == "help") {
                    526:                        warnx("-Mhelp is not available on CLI");
1.1.1.14  root      527:                        return EXIT_FAILURE;
1.1.1.2   root      528:                }
                    529:                if (!monitor_opt.empty()) {
                    530:                        warnx("-M option is ignored on CLI");
1.1.1.14  root      531:                        return EXIT_FAILURE;
1.1.1.2   root      532:                }
                    533:        }
                    534: 
1.1.1.14  root      535:        // Human モード、MSX-DOS モードでは実行ファイル名が必要
1.1.1.9   root      536:        if (human_mode) {
                    537:                if (exec_file == NULL) {
                    538:                        warnx("-H option needs -X");
1.1.1.14  root      539:                        return EXIT_FAILURE;
                    540:                }
                    541:        }
                    542:        if (msxdos_mode) {
                    543:                if (exec_file == NULL) {
                    544:                        warnx("-S option needs -X");
                    545:                        return EXIT_FAILURE;
1.1.1.9   root      546:                }
                    547:        }
                    548: 
1.1.1.14  root      549:        // 実行ファイルは、ファイル名を間違えたくらいならここでエラーに出来る。
                    550:        if (exec_file) {
                    551:                int r = access(exec_file, R_OK);
                    552:                if (r != 0) {
                    553:                        warn("-X %s", exec_file);
                    554:                        return EXIT_FAILURE;
                    555:                }
                    556:        }
                    557: 
                    558:        return MainApp::PASS;
1.1.1.2   root      559: }
                    560: 
                    561: // バージョンを表示
                    562: void
                    563: MainApp::ShowVersion() const
                    564: {
                    565:        // ここは実行ファイル名によらず nono にする
                    566:        fprintf(stderr, "nono version %d.%d.%d (%s)\n",
                    567:                NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER, NONO_DATE);
                    568: }
                    569: 
1.1.1.12  root      570: // ログレベル指定文字列を logopt に追加する。
                    571: void
                    572: MainApp::AddLogopt(const char *opt)
                    573: {
                    574:        if (logopt.empty() == false) {
                    575:                logopt += ',';
                    576:        }
                    577:        logopt += opt;
                    578: }
                    579: 
1.1.1.8   root      580: // ログレベル指定文字列を処理する。
                    581: // str はログレベル指定文字列を ',' で連結した "foo=1,bar=2" 形式の文字列で、
                    582: // これを分解してそれぞれ担当するオブジェクトのログレベルにセットする。
                    583: // "help" があれば設定は行わず一覧を表示。
1.1       root      584: bool
1.1.1.2   root      585: MainApp::ParseLogopt()
1.1       root      586: {
1.1.1.8   root      587:        std::vector<std::string> items;
                    588: 
                    589:        // 分解して..
1.1.1.12  root      590:        items = string_split(logopt.c_str(), ',');
1.1.1.8   root      591: 
                    592:        // "help" があればヘルプを表示して終了
                    593:        for (const auto& item : items) {
                    594:                if (item == "help") {
                    595:                        std::vector<std::string> list = GetLogNames();
                    596:                        // less したいだろうから stderr ではなく stdout に出力する
                    597:                        for (const auto& name : list) {
                    598:                                printf(" %s\n", name.c_str());
                    599:                        }
                    600:                        return false;
1.1       root      601:                }
1.1.1.8   root      602:        }
                    603: 
                    604:        // ログレベルを設定
                    605:        std::string errmsg;
                    606:        if (SetLogopt(items, &errmsg) == false) {
                    607:                warnx("%s", errmsg.c_str());
1.1       root      608:                return false;
                    609:        }
                    610: 
1.1.1.8   root      611:        return true;
                    612: }
1.1       root      613: 
1.1.1.8   root      614: // ログレベルを設定する。
                    615: // ログレベル指定文字列を 1つずつに分解したリスト items を処理する。
                    616: // "help" があるケースはここに来るまでに処理してあるので、ここには来ない。
                    617: // 成功なら何も表示せず true を返す。失敗なら *errmsg にエラーメッセージを
                    618: // 格納して false を返す。
                    619: // MainApp 内と Debugger からも呼ばれる。
                    620: /*static*/ bool
                    621: MainApp::SetLogopt(const std::vector<std::string>& items, std::string *errmsg)
                    622: {
                    623:        for (const auto& item : items) {
                    624:                if (SetLogopt1(item.c_str(), errmsg) == false) {
                    625:                        return false;
                    626:                }
                    627:        }
                    628:        if (0) {        // デバッグ用
1.1.1.12  root      629:                for (const auto o : gMainApp.GetObjects()) {
1.1.1.8   root      630:                        printf("%-16s %d\n", o->GetName().c_str(), o->loglevel);
1.1       root      631:                }
1.1.1.8   root      632:        }
                    633:        return true;
                    634: }
                    635: 
                    636: // "logname[=loglevel]" 形式をパースしてオブジェクトにログレベルを設定する。
                    637: // loglevel は省略なら 1 とする。
                    638: // logname が "all" なら全オブジェクトにセットする。
                    639: // そうでない場合は case ignore で完全一致するか前方一致で1つに確定すれば、
                    640: // そのオブジェクトにログレベルを設定して true を返す。
                    641: // 見付からないか候補が複数ある場合はエラーメッセージを *errmsg に出力して
                    642: // false を返す。
                    643: // この関数は (このすぐ上の SetLogopt() を経由して)
                    644: // MainApp と Debugger から呼ばれることに注意。
                    645: /*static*/ bool
                    646: MainApp::SetLogopt1(const std::string& item, std::string *errmsg)
                    647: {
                    648:        std::string name;
                    649:        const char *v;
                    650:        int level;
                    651: 
                    652:        v = strchr(item.c_str(), '=');
                    653:        if (v) {
                    654:                name = std::string(item.c_str(), v - item.c_str());
                    655:                level = atoi(++v);
                    656:        } else {
                    657:                name = item;
                    658:                level = 1;
                    659:        }
                    660:        // ここで name は変数名、level は値(省略されたら1)
                    661: 
                    662:        if (name.empty()) {
                    663:                *errmsg = "logname must be specified";
                    664:                return false;
                    665:        }
1.1       root      666: 
1.1.1.12  root      667:        // 今の所、値域は -1 〜 9 ということにしておく。
                    668:        if (level < -1) {
                    669:                level = -1;
                    670:        } else if (level > 9) {
                    671:                level = 9;
                    672:        }
                    673: 
1.1.1.8   root      674:        // "all" なら全部にセット
                    675:        if (name == "all") {
1.1.1.12  root      676:                for (auto obj : gMainApp.GetObjects()) {
1.1.1.8   root      677:                        if (obj->GetName().empty() == false) {
1.1.1.12  root      678:                                obj->SetLogLevel(level);
1.1.1.8   root      679:                        }
1.1       root      680:                }
1.1.1.8   root      681:                return true;
                    682:        }
1.1.1.13  root      683:        // "fdd" なら "fdd*" 全部にする
                    684:        if (name == "fdd") {
                    685:                for (auto obj : gMainApp.GetObjects()) {
                    686:                        if (strncasecmp(obj->GetName().c_str(), "fdd", 3) == 0) {
                    687:                                obj->SetLogLevel(level);
                    688:                        }
                    689:                }
                    690:                return true;
                    691:        }
1.1.1.15! root      692:        // "bankram" も "bankram[01]" 両方にする
        !           693:        if (name == "bankram") {
        !           694:                for (auto obj : gMainApp.GetObjects()) {
        !           695:                        if (strncasecmp(obj->GetName().c_str(), "bankram", 7) == 0) {
        !           696:                                obj->SetLogLevel(level);
        !           697:                        }
        !           698:                }
        !           699:                return true;
        !           700:        }
1.1.1.8   root      701: 
1.1.1.12  root      702:        // エイリアスリストを作る
                    703:        using aliaslist_t = std::vector<std::pair<std::string, Object *>>;
                    704:        aliaslist_t alias_list;
                    705:        for (const auto& obj : gMainApp.GetObjects()) {
1.1.1.8   root      706:                const std::vector<std::string>& aliases = obj->GetAliases();
                    707: 
                    708:                for (const auto& a : aliases) {
1.1.1.12  root      709:                        alias_list.emplace_back(a, obj);
                    710:                }
                    711:        }
1.1.1.8   root      712: 
1.1.1.12  root      713:        // エイリアスを完全一致のみのものと部分一致も許容するものに分ける
                    714:        aliaslist_t exact_alias;
                    715:        aliaslist_t partial_alias;
                    716:        for (const auto& a0 : alias_list) {
                    717:                bool exact = false;
                    718:                for (const auto& a1 : alias_list) {
                    719:                        if (a0.first == a1.first) {
                    720:                                continue;
                    721:                        }
                    722:                        if (starts_with_ignorecase(a0.first, a1.first)) {
                    723:                                exact = true;
                    724:                                break;
1.1       root      725:                        }
1.1.1.8   root      726:                }
1.1.1.12  root      727:                if (exact) {
                    728:                        exact_alias.push_back(a0);
                    729:                } else {
                    730:                        partial_alias.push_back(a0);
                    731:                }
                    732:        }
                    733: 
                    734:        // 完全一致をまず調べる
                    735:        for (const auto& a : exact_alias) {
                    736:                if (strcasecmp(a.first.c_str(), name.c_str()) == 0) {
                    737:                        a.second->SetLogLevel(level);
                    738:                        return true;
                    739:                }
                    740:        }
                    741: 
                    742:        aliaslist_t found;
                    743:        for (const auto& a : partial_alias) {
                    744:                // 前方一致したら覚えておく
                    745:                if (starts_with_ignorecase(a.first, name)) {
                    746:                        found.push_back(a);
                    747:                }
1.1.1.8   root      748:        }
                    749: 
                    750:        // 見付からない場合はエラー
                    751:        if (found.empty()) {
                    752:                *errmsg = string_format("Unknown logname \"%s\"", name.c_str());
                    753:                return false;
                    754:        }
                    755: 
                    756:        // 1つだけなら確定
                    757:        if (found.size() == 1) {
1.1.1.12  root      758:                found[0].second->SetLogLevel(level);
1.1.1.8   root      759:                return true;
                    760:        }
                    761: 
                    762:        // 複数あれば候補文字列を作成
                    763:        *errmsg = string_format("Ambiguous logname \"%s\": candidates are",
                    764:                name.c_str());
1.1.1.12  root      765:        for (const auto& cand : found) {
                    766:                *errmsg += string_format(" \"%s\"", cand.first.c_str());
1.1.1.8   root      767:        }
                    768:        return false;
                    769: }
                    770: 
                    771: // ログ名の一覧を取得する。
                    772: // MainApp と Debugger から呼ばれる。
                    773: /*static*/ std::vector<std::string>
                    774: MainApp::GetLogNames()
                    775: {
                    776:        std::vector<Object *> sortobj;
                    777: 
                    778:        // エイリアスを持つオブジェクトだけ抜き出す
1.1.1.12  root      779:        for (const auto& obj : gMainApp.GetObjects()) {
1.1.1.8   root      780:                if (obj->GetAliases().empty() == false) {
                    781:                        sortobj.emplace_back(obj);
                    782:                }
                    783:        }
                    784: 
                    785:        // aliases の一語目で sortobj をソートする
                    786:        std::sort(sortobj.begin(), sortobj.end(),
1.1.1.12  root      787:                [](const auto a, const auto b) {
1.1.1.8   root      788:                        const auto& sa = a->GetAliases()[0];
                    789:                        const auto& sb = b->GetAliases()[0];
1.1.1.12  root      790:                        return sa < sb;
1.1.1.8   root      791:                }
                    792:        );
                    793: 
                    794:        std::vector<std::string> list;
                    795:        for (const auto *obj : sortobj) {
                    796:                const auto& aliases = obj->GetAliases();
                    797:                std::string str;
                    798: 
1.1.1.12  root      799:                str = aliases[0];
1.1.1.8   root      800:                if (aliases.size() > 1) {
                    801:                        str += " (alias:";
                    802:                        for (int i = 1, sz = aliases.size(); i < sz; i++) {
                    803:                                str += ' ';
                    804:                                str += aliases[i];
1.1       root      805:                        }
1.1.1.8   root      806:                        str += ')';
1.1.1.6   root      807:                }
1.1.1.8   root      808:                list.emplace_back(str);
1.1       root      809:        }
1.1.1.8   root      810:        list.emplace_back("all");
1.1       root      811: 
1.1.1.8   root      812:        return list;
1.1       root      813: }
1.1.1.2   root      814: 
                    815: // 関連するファイルのパスを取得。
1.1.1.3   root      816: // 1. name がファイル名のみなら、VM ディレクトリとその親ディレクトリを検索。
                    817: //    ファイルが存在すればその時点で戻り値とする。ファイルが存在したけど
                    818: //    何らか不都合があったとしても (例えばファイルサイズが 0 だとか
                    819: //    パーミッションが足りないとか) 次を試すとかはしない。
                    820: // 2. name が '~' から始まっていればホームディレクトリに展開。
                    821: // 3. name が相対パスなら VM ディレクトリからの相対パスとする。
                    822: // 4. name は絶対パスのはずなので、そのまま使用する。
1.1.1.2   root      823: std::string
                    824: MainApp::SearchFile(const std::string& name) const
                    825: {
                    826:        std::string path;
                    827:        struct stat st;
                    828: 
1.1.1.3   root      829:        // 1. パス区切りを含んでなければ、ファイル名のみ
                    830:        if (name.find('/') == std::string::npos) {
                    831:                // 1a. VM ディレクトリ
                    832:                path = GetVMDir() + name;
                    833:                if (stat(path.c_str(), &st) == 0) {
                    834:                        return path;
                    835:                }
                    836: 
                    837:                // 2. 親ディレクトリ
                    838:                path = GetVMDir() + "../" + name;
                    839:                if (stat(path.c_str(), &st) == 0) {
                    840:                        return path;
                    841:                }
                    842: 
                    843:                // どちらもなければエラー
                    844:                return "";
                    845:        }
1.1.1.2   root      846: 
1.1.1.3   root      847:        // 2. 先頭の '~' を $HOME に展開
                    848:        path = name;
                    849:        if (path[0] == '~' && path[1] == '/') {
                    850:                const char *home = getenv("HOME");
                    851:                if (home == NULL) {
                    852:                        home = "";
                    853:                }
                    854:                path = string_format("%s%s", home, path.c_str() + 1);
1.1.1.2   root      855:        }
                    856: 
1.1.1.3   root      857:        // 3. 相対パスなら、VM ディレクトリからの相対
                    858:        if (path[0] != '/') {
                    859:                path = GetVMDir() + path;
1.1.1.2   root      860:        }
                    861: 
1.1.1.3   root      862:        return path;
                    863: }
                    864: 
1.1.1.14  root      865: // 初回起動時用に SRAM.DAT を作成する。
                    866: // 戻り値は EXIT_SUCCESS / EXIT_FAILURE。
                    867: int
                    868: MainApp::CreateSRAM()
                    869: {
                    870:        char buf[16 * 1024];
                    871:        std::string filename;
                    872:        autofd fd;
                    873:        int r;
                    874: 
                    875:        filename = GetVMDir() + "SRAM.DAT";
                    876: 
                    877:        // 存在確認のため一旦読み込み専用で開いてみる。
                    878:        // オープン出来たら何もしない。
                    879:        fd = open(filename.c_str(), O_RDONLY);
                    880:        if (fd >= 0) {
                    881:                warnx("%s: Already exists", filename.c_str());
                    882:                return EXIT_FAILURE;
                    883:        }
                    884: 
                    885:        fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
                    886:        if (fd < 0) {
                    887:                warn("%s: open failed", filename.c_str());
                    888:                return EXIT_FAILURE;
                    889:        }
                    890: 
                    891:        memset(&buf[0], 0, sizeof(buf));
                    892:        for (int i = 0; i < SRAMDevice::InitialData.size(); i++) {
                    893:                buf[i] = SRAMDevice::InitialData[i];
                    894:        }
                    895: 
                    896:        r = write(fd, buf, sizeof(buf));
                    897:        if (r < 0) {
                    898:                warn("%s: write failed", filename.c_str());
                    899:                return EXIT_FAILURE;
                    900:        }
                    901: 
                    902:        fd.Close();
                    903:        warnx("created %s", filename.c_str());
                    904:        return EXIT_SUCCESS;
                    905: }
                    906: 
1.1.1.13  root      907: // 現在の VM が指定のケーパビリティを持っているか?
1.1.1.3   root      908: bool
1.1.1.13  root      909: MainApp::Has(VMCap cap) const
1.1.1.3   root      910: {
1.1.1.13  root      911:        uint32 vmcap = 1U << (int)GetVMType();
                    912:        return (vmcap & (uint32)cap) != 0;
1.1.1.2   root      913: }
1.1.1.8   root      914: 
                    915: // コンパイルされている host netdriver の一覧を表示。
                    916: void
                    917: MainApp::ShowHostnet() const
                    918: {
1.1.1.12  root      919:        auto list = HostNetDevice::GetDrivers();
1.1.1.8   root      920:        for (const auto& name : list) {
                    921:                printf(" %s\n", name.c_str());
                    922:        }
                    923: }
1.1.1.12  root      924: 
1.1.1.14  root      925: // オブジェクト登録
1.1.1.12  root      926: void
1.1.1.14  root      927: MainApp::RegistObject(Object *obj)
1.1.1.12  root      928: {
1.1.1.14  root      929:        // ID が重複していないかチェック (NONE なら重複可)
                    930:        auto id = obj->GetId();
                    931:        if (id != OBJ_NONE) {
                    932:                if (FindObject(id)) {
                    933:                        PANIC("%s already exists", Object::GetIdStr(id));
                    934:                }
                    935:        }
                    936: 
                    937:        obj->logger = gMainApp.GetLogger();
1.1.1.12  root      938: 
                    939:        objects.push_back(obj);
                    940: }
                    941: 
1.1.1.14  root      942: // オブジェクト削除
1.1.1.12  root      943: void
1.1.1.14  root      944: MainApp::UnregistObject(Object *obj)
1.1.1.12  root      945: {
1.1.1.14  root      946:        // 最初に見付かった一つを削除するだけでいい
1.1.1.12  root      947:        for (auto it = objects.begin(); it != objects.end(); ++it) {
                    948:                if (*it == obj) {
                    949:                        objects.erase(it);
                    950:                        break;
                    951:                }
                    952:        }
                    953: }
1.1.1.14  root      954: 
                    955: // 指定された id を持つオブジェクトを返す。なければ NULL を返す。
                    956: Object *
                    957: MainApp::FindObject(int id) const
                    958: {
                    959:        // NONE はここで検索しない (SCSI デバイスなど、そっちで検索する)
                    960:        if (id == OBJ_NONE) {
                    961:                return NULL;
                    962:        }
                    963:        for (auto obj : objects) {
                    964:                if (obj->GetId() == id) {
                    965:                        return obj;
                    966:                }
                    967:        }
                    968:        return NULL;
                    969: }
                    970: 
                    971: // 指定された id を持つオブジェクトを探す。なければ assert する。
                    972: Object *
                    973: MainApp::GetObject(int id) const
                    974: {
                    975:        Object *obj = FindObject(id);
                    976:        if (__predict_false(obj == NULL)) {
                    977:                PANIC("objid=%s not found", Object::GetIdStr(id));
                    978:        }
                    979:        return obj;
                    980: }

unix.superglobalmegacorp.com

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