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