|
|
1.1 root 1: //
2: // nono
1.1.1.2 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.10 root 7: //
8: // 設定
9: //
10:
1.1 root 11: #include "config.h"
1.1.1.11 root 12: #include "fdc.h"
1.1 root 13: #include "mystring.h"
14:
1.1.1.10 root 15: // グローバル参照用
16: Config *gConfig;
1.1 root 17:
18: // コンストラクタ
19: Config::Config()
20: {
21: // 設定項目はすべてここで用意すること。
22: // ここで追加した順に表示されるので基本的にはアルファベット順に並べる
23: // (先頭のドットは考慮しなくていい)。
24: // ただし vmtype だけは特別なので最初に表示されたほうがいいだろう。
25:
26: Add("vmtype", "");
27:
28: // 以下概ねアルファベット順に並べる
29:
1.1.1.11 root 30: // X68030 CGROM ファイルパス。なければ内蔵 CGROM。
31: Add("cgrom-image", "", VMCap::X68030);
32:
1.1.1.7 root 33: // VM 内の時計を同期する時間軸。
34: // [ real, virtual ]
35: Add("clock-sync", "real");
36:
1.1.1.10 root 37: // デバッガコンソール
38: Add("debugger-driver", "none");
39: Add("debugger-tcp-port", 0);
40: // デバッガコンソールの fallback は常に不可とする。hostcom.cpp 参照。
41: // "debugger-fallback"
1.1.1.7 root 42:
1.1.1.12! root 43: // DIPSW エイリアス
! 44: Add("dipsw-autoboot", "", VMCap::LUNA | VMCap::NEWS);
! 45: Add("dipsw-serial", "", VMCap::LUNA | VMCap::NEWS);
! 46:
1.1.1.7 root 47: // MAC アドレス
1.1.1.12! root 48: Add("ethernet-macaddr", "auto", VMCap::LUNA | VMCap::NEWS);
! 49:
! 50: // X68030 の拡張メモリサイズ
! 51: Add("extram-size", "0", VMCap::X68030);
1.1.1.7 root 52:
1.1.1.11 root 53: // フロッピーディスク
54: Add("fd-drive", 2, VMCap::X68030);
55: for (int id = 0; id < FDCDevice::MAX_DRIVE; id++) {
56: // 種別とイメージパス
57: Add(string_format("fd%d-image", id), VMCap::X68030);
58: // エミュレータ的に書き込みを禁止するか
59: Add(string_format("fd%d-writeignore", id), 0, VMCap::X68030);
60: }
61:
62: // X68030 時の FPU の有無。
63: Add("fpu-type", "68881", VMCap::X68030);
1.1.1.9 root 64:
1.1.1.12! root 65: // 高速モード。
! 66: Add("fast-mode", "0");
! 67:
1.1.1.10 root 68: // シリアルポートのホスト側設定
69: Add("hostcom-driver", "none");
70: Add("hostcom-fallback", 0);
71: Add("hostcom-tcp-port", 9998);
72:
1.1.1.11 root 73: // キーボードの入力モード
74: Add("hostkbd-input", "char");
75:
1.1.1.7 root 76: // ネットワークのホスト側設定。
77: Add("hostnet-driver", "auto");
78: Add("hostnet-fallback", 0);
79: Add("hostnet-afpacket-ifname", "auto");
80: Add("hostnet-bpf-ifname", "auto");
81: Add("hostnet-tap-devpath", "auto");
82:
1.1.1.11 root 83: // IPLROM1/2 ファイルパス。なければ内蔵 ROM。
84: Add("iplrom1-image", "", VMCap::X68030);
85: Add("iplrom2-image", "", VMCap::X68030);
86:
1.1.1.10 root 87: // キーボードの接続
88: Add("keyboard-connect", 1);
89:
1.1.1.8 root 90: // LUNA の RTC(MK48T02) がうるう年でない 1970 をエポックとして
91: // 設定しているので、そちらに合わせてうるう年を補正する
1.1.1.11 root 92: Add("luna-adjust-misused-epoch", 1, VMCap::LUNA);
93:
94: // LUNA の DIPSW (1 が UP、0 が DOWN)。
95: // 初期値は機種によって異なるため pio.cpp で設定している。
96: Add("luna-dipsw1", VMCap::LUNA);
97: Add("luna-dipsw2", VMCap::LUNA);
1.1.1.8 root 98:
1.1.1.10 root 99: // LUNA ビデオボードプレーン数
1.1.1.12! root 100: Add("luna-video-plane", 4, VMCap::LUNA);
1.1.1.10 root 101:
1.1.1.8 root 102: // m88100 の別名ニーモニックを併記する。
1.1.1.11 root 103: Add(".m88k-altname", 0, VMCap::M88K);
1.1.1.8 root 104:
1.1.1.12! root 105: // 画面サイズ。
! 106: Add("mainview-scale", "1");
! 107:
! 108: // モニタウィンドウ(等)のフォントサイズ
! 109: Add("monitor-fontsize", 12);
! 110:
1.1.1.7 root 111: // モニタウィンドウの更新頻度 [Hz]
1.1.1.3 root 112: Add("monitor-rate", 20);
1.1.1.7 root 113:
114: // MPU クロック (MHz 単位の実数、小数点以下は3桁まで)。
115: // 初期値は各 VM クラスで設定している。
1.1 root 116: Add("mpu-clock");
1.1.1.7 root 117:
118: // M88100 で疑似 STOP 状態をサポートする
1.1.1.11 root 119: Add("mpu-pseudo-stop", 1, VMCap::M88K);
1.1.1.7 root 120:
1.1.1.12! root 121: // NEWS の DIPSW (1 がオン、0 がオフ)。
! 122: Add("news-dipsw", "00001000", VMCap::NEWS);
! 123:
! 124: // NEWS の SIC を無視する。(暫定)
! 125: Add("xxx-news-sic-ignore", "0", VMCap::NEWS);
! 126: Add("xxx-news-sci-ignore", "", VMCap::NEWS);
! 127:
1.1.1.7 root 128: // PROM ファイルパス。なければ内蔵 ROM 起動。
1.1.1.11 root 129: Add("prom-image", "", VMCap::LUNA);
1.1.1.7 root 130:
131: // PROM のデバッグオプション。
132: // PROM 中の SCSI デバッグビットを立てる。
1.1.1.11 root 133: Add(".prom-scsi-debug", 0, VMCap::LUNA1);
1.1.1.7 root 134:
135: // RAM サイズ (MB 単位の整数)。
136: // 初期値は機種ごとの VM クラスで設定している。
1.1.1.2 root 137: Add("ram-size", 0);
1.1.1.7 root 138:
139: // RTC のデバッグ用オプション。
1.1 root 140: Add(".rtc-force-fixed", 0);
1.1.1.8 root 141: Add(".rtc-date");
142: Add(".rtc-time");
1.1.1.7 root 143:
1.1.1.10 root 144: // RTC の epoch。初期値は各 VM クラスで設定している。
1.1.1.12! root 145: Add("rtc-epoch-year", VMCap::LUNA | VMCap::NEWS);
1.1.1.10 root 146:
1.1.1.7 root 147: // ステータスパネルを表示する。
1.1.1.6 root 148: Add("show-statuspanel", 1);
1.1.1.7 root 149:
150: // SCSI 設定。
1.1 root 151: for (int id = 0; id < 8; id++) {
1.1.1.7 root 152: // 種別とイメージパス
1.1 root 153: Add(string_format("spc0-id%d-image", id));
1.1.1.7 root 154: // エミュレータ的に書き込みを禁止するか
1.1.1.8 root 155: Add(string_format("spc0-id%d-writeignore", id), 0);
156: // エミュレータ的に書き込みを禁止するか
157: // (後方互換、十分不要になった頃に削除する)
1.1 root 158: Add(string_format("spc0-id%d-writeprotect", id), 0);
1.1.1.7 root 159: // シークタイム [msec]
1.1.1.4 root 160: Add(string_format("spc0-id%d-seektime", id), 0);
1.1 root 161: }
1.1.1.11 root 162:
163: // SRAM の RAM 容量を同期するか。
164: Add("sram-sync-ramsize", 1, VMCap::X68030);
1.1 root 165: }
166:
167: // デストラクタ
168: Config::~Config()
169: {
1.1.1.10 root 170: gConfig = NULL;
1.1 root 171: }
172:
173: // 項目を追加 (初期状態用)。値は空文字列。
174: bool
1.1.1.11 root 175: Config::Add(const std::string& key, VMCap vmcap)
1.1 root 176: {
1.1.1.11 root 177: return Add(key, "", vmcap);
1.1 root 178: }
179:
180: // 項目を追加 (初期状態用)。値は整数値を文字列にしたもの。
181: bool
1.1.1.11 root 182: Config::Add(const std::string& key, int value, VMCap vmcap)
1.1 root 183: {
1.1.1.11 root 184: return Add(key, std::to_string(value), vmcap);
1.1 root 185: }
186:
187: // 項目を追加 (初期状態用)
188: bool
1.1.1.11 root 189: Config::Add(const std::string& key, const std::string& value, VMCap vmcap)
1.1 root 190: {
191: assert(FindItem(key) == NULL);
1.1.1.2 root 192: assert(fixed == false);
1.1 root 193:
1.1.1.11 root 194: list.emplace_back(key, value, vmcap, ConfigItem::FromInitial, "");
1.1 root 195: return true;
196: }
197:
1.1.1.4 root 198: // 項目を削除
199: // key が見付からなくても何もしない。key は複数登録されていることはないはず。
200: void
201: Config::Delete(const std::string& key)
202: {
203: for (auto it = list.begin(); it != list.end(); ++it) {
204: auto item = *it;
205: if (key == item.key) {
206: list.erase(it);
207: return;
208: }
209: }
210: assert(FindItem(key) == NULL);
211: }
212:
1.1 root 213: // 設定ファイルの内容で上書きする
214: bool
215: Config::Update(const ConfigFile& file)
216: {
1.1.1.2 root 217: assert(fixed == false);
218:
1.1 root 219: for (const auto& pair : file.lines) {
220: const std::string& line = pair.first;
221: int lineno = pair.second;
222:
223: auto where = string_format("%s:%d", file.filepath.c_str(), lineno);
224: if (UpdateLine(line, ConfigItem::FromConfig, where) == false) {
225: return false;
226: }
227: }
228:
229: return true;
230: }
231:
232: // コマンドラインオプションで更新
233: bool
1.1.1.12! root 234: Config::Update(const std::string& line, const std::string& optname)
1.1 root 235: {
1.1.1.2 root 236: assert(fixed == false);
237:
1.1.1.12! root 238: return UpdateLine(line, ConfigItem::FromOption, optname);
1.1 root 239: }
240:
241: // 更新1行分の共通処理。
1.1.1.10 root 242: // キーが見付からない場合、コマンドラインからならエラーとし、
243: // それ以外からの場合は警告のみで無視する。
1.1 root 244: // エラーが起きたらエラーメッセージを表示して false を返す。
245: bool
246: Config::UpdateLine(const std::string& line, ConfigItem::from_t from,
247: const std::string& where)
248: {
249: std::string key;
250: std::string val;
251:
252: // キーと値に分解
253: if (SplitKeyVal(&key, &val, line) == false) {
1.1.1.12! root 254: ConfigItem::PrintErr(from, where, "", line, "Syntax error");
1.1 root 255: return false;
256: }
257:
1.1.1.10 root 258: // キーで検索
1.1 root 259: ConfigItem *itemptr = FindItem(key);
260: if (itemptr == NULL) {
1.1.1.10 root 261: // コマンドラインからの場合はエラーにする
262: if (from == ConfigItem::FromOption) {
1.1.1.12! root 263: ConfigItem::PrintErr(from, where, key, val, "Unknown key");
1.1.1.10 root 264: return false;
265: }
266: // コマンドライン以外の場合は警告のみで無視する
1.1.1.12! root 267: ConfigItem::PrintErr(from, where, key, val, "Unknown key, ignored");
1.1 root 268: return true;
269: }
270:
271: // 見付かったので更新
272: ConfigItem& item = *itemptr;
273: item.value = val;
274: item.from = from;
275: item.where = where;
276:
277: return true;
278: }
279:
280: // "key=val" 形式の行 line を分解するして *keyp, *valp に代入する。
281: // その際 key, val 前後の空白は取り除く。
282: // 失敗すれば false を返す。
283: /*static*/ bool
284: Config::SplitKeyVal(std::string *keyp, std::string *valp,
285: const std::string& line)
286: {
287: assert(keyp);
288: assert(valp);
289:
290: // キーと値に分解
291: auto pos = line.find('=');
292: if (pos == std::string::npos) {
293: return false;
294: }
295: auto key = line.substr(0, pos++);
296: auto val = line.substr(pos, line.size() - pos);
297:
1.1.1.8 root 298: *keyp = string_trim(key);
299: *valp = string_trim(val);
1.1 root 300:
301: return true;
302: }
303:
1.1.1.2 root 304: // 内容を表示する。
305: // all なら隠し設定も含めて表示。
1.1 root 306: void
1.1.1.2 root 307: Config::Show(bool all) const
1.1 root 308: {
1.1.1.2 root 309: assert(fixed);
310:
1.1 root 311: for (const auto& item : list) {
1.1.1.2 root 312: // '!' から始まるのは内部用変数なので隠す
313: if (!all && item.key[0] == '!') {
314: continue;
315: }
1.1 root 316: // ドットから始まるのは隠し設定で、初期値のままなら隠す
1.1.1.2 root 317: if (!all &&
318: item.key[0] == '.' && item.from == ConfigItem::FromInitial) {
1.1 root 319: continue;
320: }
321:
322: std::string buf = string_format("%s=\"%s\"",
323: item.key.c_str(), item.value.c_str());
324: if (buf.size() < 40) {
325: int n = ((40 - buf.size()) + 7) / 8;
326: buf.append(n, '\t');
327: } else {
328: buf.push_back('\t');
329: }
330:
331: std::string fromstr;
332: switch (item.from) {
333: case ConfigItem::FromInitial:
334: fromstr = "initial value";
335: break;
336: case ConfigItem::FromConfig:
337: fromstr = item.where;
338: break;
339: case ConfigItem::FromOption:
1.1.1.12! root 340: fromstr = "option " + item.where;
1.1 root 341: break;
342: default:
1.1.1.12! root 343: fromstr = string_format("corrupted item.from=%d", (int)item.from);
! 344: break;
1.1 root 345: }
346:
347: printf("%s(%s)\n", buf.c_str(), fromstr.c_str());
348: }
349: }
350:
351: // key から ConfigItem を取得する。見付からなければ assert する。
352: const ConfigItem&
1.1.1.2 root 353: Config::Find(const std::string& key) const
1.1 root 354: {
355: for (const auto& item : list) {
356: if (key == item.key) {
357: return item;
358: }
359: }
1.1.1.10 root 360: PANIC("Config::Find() \"%s\" not found", key.c_str());
1.1.1.2 root 361: }
362:
363: // key のデフォルト値を差し替える。key が見付からなければ assert する。
364: void
365: Config::SetDefault(const std::string& key, int val)
366: {
367: std::string valstr = std::to_string(val);
368: SetDefault(key, valstr);
1.1 root 369: }
370:
1.1.1.2 root 371: // key のデフォルト値を差し替える。key が見付からなければ assert する。
372: void
373: Config::SetDefault(const std::string& key, const std::string& val)
1.1 root 374: {
1.1.1.2 root 375: assert(fixed == false);
376:
1.1 root 377: for (auto& item : list) {
378: if (key == item.key) {
1.1.1.2 root 379: item.SetDefault(val);
380: return;
1.1 root 381: }
382: }
1.1.1.2 root 383: assertmsg(0, "Config::SetDefault() \"%s\" not found\n", key.c_str());
1.1 root 384: }
385:
386: // key から ConfigItem を返す。見付からなければ NULL を返す。
387: // (こっちは内部で変更するために使う)
388: ConfigItem *
389: Config::FindItem(const std::string& key)
390: {
391: for (auto& item : list) {
392: if (key == item.key) {
393: return &item;
394: }
395: }
396: return NULL;
397: }
398:
1.1.1.12! root 399: // これ以降は書き込み禁止。
! 400: bool
1.1.1.2 root 401: Config::Fix()
402: {
403: assert(fixed == false);
404:
1.1.1.11 root 405: // ここで、この機種に不要な変数を削除する。
1.1.1.12! root 406: // が、その前にその変数がユーザから指定されていればエラーにする。
1.1.1.11 root 407: for (auto it = list.begin(); it != list.end(); ) {
408: ConfigItem& item = *it;
409: VMCap vmcap = item.GetVMCap();
410: if (gMainApp.Has(vmcap) == false) {
1.1.1.12! root 411: // この機種用ではない
! 412:
! 413: if (item.from != ConfigItem::FromInitial) {
! 414: // 更新されていればエラー
! 415: item.Err("Not supported in this vmtype");
! 416: return false;
! 417: }
! 418:
1.1.1.11 root 419: it = list.erase(it);
420: } else {
421: ++it;
422: }
423: }
424:
1.1.1.2 root 425: fixed = true;
1.1.1.12! root 426: return true;
1.1.1.2 root 427: }
428:
1.1 root 429:
430: //
431: // 設定項目
432: //
433:
1.1.1.10 root 434: // 値を整数値として取得してみる。
435: bool
436: ConfigItem::TryInt(int *val) const
437: {
438: char *endp;
439:
440: if (value.empty()) {
441: return false;
442: }
443:
444: errno = 0;
445: int r = strtol(value.c_str(), &endp, 10);
446: if (errno == ERANGE || *endp != '\0') {
447: return false;
448: }
449: *val = r;
450: return true;
451: }
452:
1.1.1.12! root 453: // 値を double 値として取得してみる。
! 454: bool
! 455: ConfigItem::TryDouble(double *val) const
! 456: {
! 457: char *endp;
! 458:
! 459: if (value.empty()) {
! 460: return false;
! 461: }
! 462:
! 463: errno = 0;
! 464: double res = strtod(value.c_str(), &endp);
! 465: if (errno) {
! 466: return false;
! 467: }
! 468: *val = res;
! 469: return true;
! 470: }
! 471:
1.1 root 472: // デフォルト値を差し替える
473: void
474: ConfigItem::SetDefault(const std::string& val)
475: {
476: if (from == FromInitial) {
477: value = val;
478: }
479: }
480:
481: void
482: ConfigItem::Err() const
483: {
484: // 定型文
1.1.1.12! root 485: Err("Invalid argument");
1.1 root 486: }
487:
488: void
489: ConfigItem::Err(const char *fmt, ...) const
490: {
491: char buf[1024];
492: va_list ap;
493:
494: va_start(ap, fmt);
495: vsnprintf(buf, sizeof(buf), fmt, ap);
496: va_end(ap);
497:
498: std::string msg(buf);
499: Err(msg);
500: }
501:
502: void
503: ConfigItem::Err(const std::string& msg) const
504: {
1.1.1.10 root 505: PrintErr(from, where, key, value, msg);
506: }
507:
1.1.1.12! root 508: // この item についてエラーを表示する。
! 509: // key=val の '=' がない場合のみ key を空、value を行全体として呼ぶ。
1.1.1.10 root 510: /*static*/ void
511: ConfigItem::PrintErr(ConfigItem::from_t from, const std::string& where,
512: const std::string& key, const std::string& value, const std::string& msg)
513: {
1.1 root 514: std::string fromstr;
515:
516: switch (from) {
517: case FromInitial:
1.1.1.12! root 518: // 初期値の場合 key が空なことは起きないはず。
! 519: // というか初期値でエラーが起きることも普通はないはず。
! 520: fromstr = "initial value: " + key + "=" + value;
1.1 root 521: break;
1.1.1.12! root 522:
1.1 root 523: case FromConfig:
1.1.1.12! root 524: // 設定ファイルでのエラー。where はファイル名と行番号。
! 525: fromstr = where + ": ";
! 526: if (key.empty() == false) {
! 527: fromstr += key;
! 528: fromstr + "=";
! 529: }
! 530: fromstr += value;
1.1 root 531: break;
1.1.1.12! root 532:
1.1 root 533: case FromOption:
1.1.1.12! root 534: // コマンドラインオプションの場合は where は
! 535: // "--fontsize" とか "-V" なので、
! 536: // "-V" なら (key =) value を追加し、
! 537: // それ以外なら value のみ追加がいいか。
! 538: fromstr = "option " + where + " ";
! 539: if (where == "-V") {
! 540: if (key.empty() == false) {
! 541: fromstr += key;
! 542: fromstr += "=";
! 543: }
! 544: }
! 545: fromstr += value;
1.1 root 546: break;
1.1.1.10 root 547:
1.1.1.12! root 548: default:
! 549: fromstr = string_format("corrupted item.from=%d", (int)from);
! 550: break;
1.1.1.10 root 551: }
552:
1.1.1.12! root 553: warnx("%s: %s", fromstr.c_str(), msg.c_str());
1.1 root 554: }
555:
556:
557: //
558: // 設定ファイル
559: //
560:
561: // コンストラクタ
562: ConfigFile::ConfigFile(const std::string& filepath_)
563: {
564: filepath = filepath_;
565: }
566:
567: // デストラクタ
568: ConfigFile::~ConfigFile()
569: {
570: }
571:
1.1.1.12! root 572: // ファイルの読み込み。
! 573: // 成功すれば true を、失敗すればメッセージを出力して false を返す。
! 574: // ファイルがないのは正常とする (なければスルーするため)。
! 575: // ファイルがあって読めなければ失敗 (エラー終了したいので)。
1.1 root 576: bool
577: ConfigFile::Load()
578: {
579: char buf[1024];
580: FILE *fp;
581:
582: fp = fopen(filepath.c_str(), "r");
583: if (fp == NULL) {
1.1.1.12! root 584: if (errno == ENOENT) {
! 585: return true;
! 586: }
1.1 root 587: warn("%s", filepath.c_str());
588: return false;
589: }
590:
591: for (int line = 1; fgets(buf, sizeof(buf), fp) != NULL; line++) {
592: // 空行とコメント行を無視
593: rtrim(buf);
594: if (buf[0] == '#') {
595: buf[0] = '\0';
596: }
597: if (buf[0] == '\0') {
598: continue;
599: }
600:
601: // ここでは記憶するだけ
602: lines.emplace_back(buf, line);
603: }
604:
605: fclose(fp);
606: return true;
607: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.