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