|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: // LUNA-I の ROM エミュレーション
8:
9: // IODevice
10: // |
11: // +----------+---------------------+
12: // v v
13: // ROMDevice LunaPROMEmuDeivce (ROM エミュレーションの共通部分)
14: // | |
15: // | +------------+----------------+
16: // v v v
17: // PROMDevice Luna1PROMEmuDevice Luna88kPROMEmuDevice
18: // (実ROM) (LUNA-I エミュレーション) (LUNA88K エミュレーション)
19:
20: #include "romemu_luna1.h"
21: #include "bitmap.h"
22: #include "ethernet.h"
23: #include "mainapp.h"
24: #include "memorystream.h"
25: #include "mk48t02.h"
26: #include "mpu680x0.h"
27: #include "pio.h"
28: #include "ram.h"
29:
30: // コンストラクタ
31: Luna1PROMEmuDevice::Luna1PROMEmuDevice()
32: {
33: // LUNA-I の ROM の特定アドレスを読み出す人がいるので、
34: // サイズは実 ROM と同じにしておく必要がある。
35: devlen = 128 * 1024;
36: romdata.reset(new uint8 [devlen]);
37: }
38:
39: // デストラクタ
40: Luna1PROMEmuDevice::~Luna1PROMEmuDevice()
41: {
42: }
43:
44: bool
45: Luna1PROMEmuDevice::Init()
46: {
47: // 親クラス
48: if (inherited::Init() == false) {
49: return false;
50: }
51:
52: MemoryStreamBE ms(romdata.get());
53: ms.Write32(0x00002000); // +00: リセットベクタ SP
54: ms.Write32(0x41000400); // +04: リセットベクタ PC
55: ms.Write32(0); // +08
56: ms.Write32(0x41000500); // +0c: メモリサイズ格納先
57:
58: // リセット時に実行する命令
59: ms.SetPtr(&romdata[0x400]);
60: ms.Write16(0x2039); // move.l (ROMIO_INIT),d0
61: ms.Write32(ROMIO_INIT);
62: ms.Write16(0x660c); // bne _go ; 起動先あればGO
63: //_prompt:
64: ms.Write32(0x4e722500); // stop #0x2500 ; キー入力を待つ
65: ms.Write16(0x2039); // move.l (ROMIO_ENTRY),d0
66: ms.Write32(ROMIO_ENTRY); // ; キー入力処理
67: ms.Write16(0x67f4); // beq _prompt ; 起動先なければループ
68: //_go:
69: ms.Write16(0x2040); // move.l d0,a0 ; ジャンプ先を a0 にセット
70: ms.Write16(0x2039); // move.l (ROMIO_CLOSE),d0
71: ms.Write32(ROMIO_CLOSE); // ; 後始末
72: ms.Write16(0x4ed0); // jmp (a0) ; ジャンプ
73:
74: // Lv6 (SIO) 割り込みハンドラ
75: // キーボード割り込みが上がったら KEYIN をコールする
76: ms.SetPtr(&romdata[0x600]);
77: ms.Write16(0x4ab9); // tst.l (ROMIO_KEYIN)
78: ms.Write32(ROMIO_KEYIN); // ; キー入力処理
79: ms.Write16(0x4e73); // rte
80:
81: // MAC アドレス
82: const macaddr_t macaddr = gEthernet->GetMacAddr();
83: strcpy((char *)&romdata[0x1ffd8], "ENADDR");
84: memcpy(&romdata[0x1ffe0], macaddr.to_string().c_str(), 12);
85:
86: // RAM にベクタを書き込む
87: // XXX VBR を 41000000 に向けるのとどっちが楽かはある
88: gRAM->Write32(0x78, 0x41000600); // +78: Lv6 割り込み
89:
90: return true;
91: }
92:
93: void
94: Luna1PROMEmuDevice::ResetHard()
95: {
96: inherited::ResetHard();
97:
98: x_executed = false;
99: x_entrypoint = 0;
100: }
101:
102: // 起動時の処理を行う。
103: // 次段実行ファイルの読み込みに成功すればそのエントリポイントを返す。
104: // そうでなければ 0 を返すので、プロンプトに処理を移すこと。
105: uint32
106: Luna1PROMEmuDevice::ROM_Init()
107: {
108: MemoryStreamBE ms;
109: uint32 entry;
110:
111: // デバイスの初期化 (親クラス)
112: InitDevice();
113:
114: // ROM 0x41000000.L リセットベクタ(SP,PC)もカーネルが参照している
115: // (これは Init() が用意している)
116: // ROM 0x4100000c.L メモリサイズ(バイト数)格納先アドレス
117: // ROM 0x410000b8.L プレーンマスク格納先アドレス
118:
119: // メモリサイズはとりあえず 0x41000500 に格納
120: ms.SetPtr(&romdata[0x500]);
121: ms.Write32(gRAM->GetSize());
122:
123: // プレーン数(1..4)をプレーンマスク(0x01..0x0f)に
124: int nplane = gBitmap->GetPlaneCount();
125: int planemask = (1 << nplane) - 1;
126: // 実際は RAM 上を指しているのだが、とりあえずすぐ隣に置いておく。
127: ms.SetPtr(&romdata[0xb8]);
128: ms.Write32(0x410000bc);
129: ms.Write32(planemask);
130:
131: prompt_idx = 0;
132: prompt = default_prompt;
133:
134: // スクリーンを初期化
135: InitScreen("LUNA-I");
136:
137: entry = 0;
138:
139: // 表示環境が整ったところで NVRAM を照合
140: if (VerifyNVRAM() == false) {
141: InitNVRAM();
142: text.Print("** NVRAM Initialized.\n");
143: text.Print("\n");
144: // NVRAM をクリアしたら DIPSW によらずプロンプトに降りる
145: goto done;
146: }
147:
148: // DIPSW#1-1 が UP なら自動起動、DOWN なら ROM モニタ起動なので、
149: // これを真似する。
150: if ((gPIO0->GetDIPSW1() & 1)) {
151: if (gMainApp.host_file) {
152: // ホストのファイルを読み込んでエントリポイントアドレスを
153: // この Read アクセスの読み出し値として返す。
154: // エラーがあればエラーメッセージは呼び出し先側で表示してある。
155: entry = LoadHostFile();
156: } else {
157: // NVRAM の設定に従ってブート先を探す。
158: errmsg.clear();
159:
160: bootinfo_t bootinfo;
161: GetNVRAM(bootinfo);
162: entry = LoadGuestFile(bootinfo);
163: if (entry == 0) {
164: text.Print("** %s\n", errmsg.c_str());
165: }
166: }
167: }
168:
169: // ここまでタイトルなどを Print してはいるが、実際の画面更新は
170: // ConsoleUpdate() を呼ぶまで行われないことを利用(悪用?)して、
171: // ここでエラーなくブートプログラムに制御を渡せる場合には何も表示せずに
172: // ゲストに処理を移す。意図して或いは意図せずプロンプトに降りる場合だけ
173: // ConsoleUpdate を呼ぶことでタイトルを含めて画面を表示する。
174: done:
175: if (entry == 0) {
176: ConsoleUpdate();
177: }
178:
179: return entry;
180: }
181:
182: // ROM 処理。エントリポイントを返す。
183: uint32
184: Luna1PROMEmuDevice::ROM_Entry()
185: {
186: if (x_executed && x_entrypoint != 0) {
187: return x_entrypoint;
188: } else {
189: return 0;
190: }
191: }
192:
193: // ROM 処理のクローズ。
194: // 次段プログラムにジャンプするための処理も含む。
195: void
196: Luna1PROMEmuDevice::ROM_Close()
197: {
198: inherited::ROM_Close();
199:
200: // NetBSD のブートローダがカーネルを読み込む時にはいくつかレジスタ
201: // 渡しのパラメータがあるので、ここで用意する。本当はターゲットが
202: // NetBSD カーネルの時に限定すべきのような気もするけど、とりあえず。
203: // ROM がブートローダを読み込む時にはそのような取り決め事は一切ない
204: // ので、ブートローダを読む時にも同じ値が入ってしまうが気にしない
205: // ことにする。
206: // 必要なパラメータは
207: // %d6 に bootdev、NetBSD/luna68k の場合は
208: // bootdev = $MACUPP00
209: // ||||++-- パーティション番号
210: // |||+---- SCSI ID (XXX ただしここでは 6 決め打ち)
211: // ||+----- unit (spc0 なら 0 のやつのはず)
212: // |+------ アダプタ (spc = 0、lance = 1)
213: // +------- magic ($a)
214: //
215: // %d7 に howto (0 でよい)
216: // (と %a0 にジャンプ先を入れるがこれは Init() が書き出した ROM
217: // アセンブラコード側で行っている)
218: m68kcpu *cpu = gMPU680x0->GetCPU();
219: RegD(6) = 0xa0060000;
220: RegD(7) = 0;
221: }
222:
223: // 入力コマンド処理 (入力行が揃ったところで呼ばれる)。
224: void
225: Luna1PROMEmuDevice::Command()
226: {
227: std::vector<char> buf(inputbuf.length() + 1);
228: std::vector<std::string> arg;
229:
230: // 1ワードごとに分解
231: strcpy(&buf[0], inputbuf.c_str());
232: char *p = &buf[0];
233: char *s = p;
234: while (*p != '\0') {
235: if (*p == ' ') {
236: *p++ = '\0';
237: arg.push_back(std::string(s));
238: s = p;
239: } else {
240: p++;
241: }
242: }
243: if (p != s) {
244: arg.push_back(std::string(s));
245: }
246: if (loglevel >= 1) {
247: for (int i = 0; i < arg.size(); i++) {
248: putmsgn("arg[%d]=|%s|", i, arg[i].c_str());
249: }
250: }
251:
252: // "k" コマンド中ならここで先に処理する
253: if (prompt_idx != 0) {
254: CommandK(arg);
255: return;
256: }
257:
258: // 空行
259: if (arg.size() == 0) {
260: return;
261: }
262:
263: // 1ワード目がコマンド
264: if (arg[0] == "h") {
265: // ヘルプメッセージ
266: text.Print("Commands\n");
267: text.Print(
268: " g [<filename>] : load program\n"
269: " gh : load host program (specified by -A option)\n"
270: " h : show this message\n"
271: " k : show/set boot constants\n"
272: " x : execute loaded program\n"
273: );
274: return;
275: }
276: if (arg[0] == "g") {
277: CommandG(arg);
278: return;
279: }
280: if (arg[0] == "gh") {
281: CommandGH();
282: return;
283: }
284: if (arg[0] == "k") {
285: CommandK(arg);
286: return;
287: }
288: if (arg[0] == "x") {
289: CommandX();
290: return;
291: }
292:
293: text.Print("** Unknown command: %s\n", arg[0].c_str());
294: }
295:
296: // "g" コマンド
297: // g なら NVRAM の情報に従ってプログラムをロード。
298: // g <fname> なら NVRAM の fname だけ指定の名前を使ってロード。NVRAM には
299: // 書き込まない。
300: void
301: Luna1PROMEmuDevice::CommandG(const std::vector<std::string>& arg)
302: {
303: std::string ctlr = nvram->PeekString(0x0030);
304: if (ctlr == "dk") { // harddisk
305: std::string fname {};
306:
307: switch (arg.size()) {
308: case 1:
309: break;
310: case 2:
311: fname = arg[1];
312: break;
313: default:
314: text.Print("** Invalid argument.\n");
315: return;
316: }
317: errmsg.clear();
318:
319: bootinfo_t bootinfo;
320: GetNVRAM(bootinfo);
321: // 引数で指定されたらファイル名だけ上書き
322: if (!fname.empty()) {
323: bootinfo.dkfile = fname;
324: }
325: x_entrypoint = LoadGuestFile(bootinfo);
326: if (x_entrypoint == 0) {
327: text.Print("** %s\n", errmsg.c_str());
328: }
329:
330: } else if (ctlr == "et" || // netboot
331: ctlr == "sd" || // cassette tape
332: ctlr == "fl" ) // floppy
333: {
334: text.Print("** %s boot not supported.\n", ctlr.c_str());
335: return;
336: } else {
337: // 起きないはず
338: text.Print("** Invalid boot controller name.\n");
339: return;
340: }
341:
342: if (x_entrypoint) {
343: text.Print("Loaded. Entry point = $%08x\n", x_entrypoint);
344: }
345: }
346:
347: // "gh" コマンド (勝手に拡張)
348: // -A オプションで指定されたホストプログラムをロードする。
349: void
350: Luna1PROMEmuDevice::CommandGH()
351: {
352: // 指定されてたら読み込む
353: if (gMainApp.host_file == NULL) {
354: text.Print("** Cannot loaded; host program is not specified.\n");
355: return;
356: }
357: x_entrypoint = LoadHostFile();
358: }
359:
360:
361: // k コマンドのパラメータ表
362: struct kinfo {
363: uint32 offset; // NVRAM 上のオフセット
364: const char * const name; // 表示名
365: int next; // 次インデックス
366: };
367: static const struct kinfo kinfo[] = {
368: { 0 }, // 0 は通常プロンプトを指すのでここでは使わない
369: { 0x0030, "controller", 0 }, // [1] BOOT (next は動的判定する)
370: { 0x0090, "drive unit", 3 }, // [2] DKUNIT
371: { 0x00b0, "partition ", 4 }, // [3] DKPART
372: { 0x00d0, "filename ", 0 }, // [4] FLNAME
373: { 0x0050, "hostname ", 6 }, // [5] HOST
374: { 0x0070, "servername", 7 }, // [6] SERVER
375: { 0x0190, "filename ", 8 }, // [7] ETBOOT
376: { 0x01b0, "", 0 }, // [8] ETFILE (ETBOOT と1行で処理する)
377: };
378:
379: // "k" コマンド
380: void
381: Luna1PROMEmuDevice::CommandK(const std::vector<std::string>& arg)
382: {
383: uint32 offset;
384: const char *name;
385: int next;
386:
387: // 入力を処理
388: if (prompt_idx == 0) {
389: // ここは1行目表示前なので前行の入力はない。
390: next = 1;
391: } else {
392: std::string val;
393: bool rv;
394:
395: // 入力があれば更新
396: if (arg.size() != 0) {
397: rv = CommandKInput(arg[0]);
398: } else {
399: // [7] の時に入力があれば CommandKInput() の中で更新されるが、
400: // 入力がなければそこを通らないので、こっちでやらないといけない。
401: // うーん。
402: if (prompt_idx == 7) {
403: prompt_idx = kinfo[prompt_idx].next;
404: }
405: rv = true;
406: }
407:
408: // これ以降は更新後の prompt_idx を使う
409:
410: // 入力エラーがあった時はこの行を再入力するため
411: next = prompt_idx;
412:
413: if (rv == true) {
414: // 入力があっても空でも次インデックスへ。
415: if (prompt_idx == 1) {
416: // BOOT なら現在値によって次のインデックスが変わる
417: // "dk" なら [2]、"et" なら [5]。他は未対応
418: offset = kinfo[prompt_idx].offset;
419: val = nvram->PeekString(offset);
420: if (val == "dk") {
421: next = 2;
422: } else if (val == "et") {
423: next = 5;
424: } else {
425: // 起きることはないはず
426: text.Print("** Inconsistent boot parameter: %s\n",
427: val.c_str());
428: }
429: } else {
430: // それ以外はテーブルにかかれている通り
431: next = kinfo[prompt_idx].next;
432: }
433: }
434: }
435:
436: // この行のプロンプト文字列を作成
437: prompt_idx = next;
438: if (prompt_idx == 0) {
439: // 最終行なら元に戻す
440: prompt = default_prompt;
441: } else {
442: // 次のプロンプトに変更
443: name = kinfo[prompt_idx].name;
444: offset = kinfo[prompt_idx].offset;
445: std::string val = nvram->PeekString(offset);
446:
447: // [7] ETBOOT だけ1行で ETBOOT:ETFILE 形式
448: if (prompt_idx == 7) {
449: next = kinfo[prompt_idx].next; // ETFILE
450: offset = kinfo[next].offset;
451: val += ":";
452: val += nvram->PeekString(offset);
453: }
454:
455: prompt = string_format("%s: %s ?", name, val.c_str());
456: }
457: }
458:
459: // k コマンドの入力値を処理する部分。
460: // 成功すれば true を返す。
461: // 失敗すれば false を返す。
462: // 実機 ROM は入力値にエラーがあると黙って再入力を促す。
463: // エラーメッセージくらい表示してもいいかもだがとりあえず真似しておく。
464: bool
465: Luna1PROMEmuDevice::CommandKInput(const std::string& val)
466: {
467: int offset;
468: bool rv = true;
469:
470: offset = kinfo[prompt_idx].offset;
471:
472: switch (prompt_idx) {
473: case 1: // BOOT
474: // 正常な入力値は "dk", "et", "sd", "fl" のみ。
475: if (val != "dk" && val != "et" && val != "sd" && val != "fl") {
476: return false;
477: }
478: rv = nvram->WriteString(offset, val);
479: if (rv == false) {
480: return false;
481: }
482: break;
483:
484: case 7: // ETBOOT
485: {
486: // こいつだけ ETBOOT:ETFILE 形式で2つのパラメータを同時に処理する
487:
488: // ':' で前後に分ける
489: int sep = val.find(':');
490: if (sep == std::string::npos) {
491: return false;
492: }
493: std::string etboot = val.substr(0, sep);
494: std::string etfile = val.substr(sep + 1, val.size() - sep + 1);
495: if (etboot.empty() || etfile.empty()) {
496: return false;
497: }
498:
499: // ETBOOT
500: rv = nvram->WriteString(offset, etboot);
501: if (rv == false) {
502: return false;
503: }
504:
505: // 次へ
506: prompt_idx = kinfo[prompt_idx].next;
507:
508: // ETFILE
509: offset = kinfo[prompt_idx].offset;
510: rv = nvram->WriteString(offset, etfile);
511: if (rv == false) {
512: return false;
513: }
514: break;
515: }
516:
517: default:
518: rv = nvram->WriteString(offset, val);
519: if (rv == false) {
520: return false;
521: }
522: break;
523: }
524:
525: WriteNVRAMCsum();
526: return rv;
527: }
528:
529: // "x" コマンド
530: // 読み込まれていたらフラグを立てるだけ。
531: void
532: Luna1PROMEmuDevice::CommandX()
533: {
534: if (x_entrypoint == 0) {
535: text.Print("** Program is not loaded.\n");
536: } else {
537: x_executed = true;
538: }
539: }
540:
541: // NVRAM のチェックサムを返す
542: uint8
543: Luna1PROMEmuDevice::CalcNVRAMCsum()
544: {
545: uint8 eor = 0;
546: for (uint32 addr = 0x20; addr < 0x560; addr++) {
547: eor ^= nvram->Peek(addr);
548: }
549: return eor;
550: }
551:
552: // NVRAM のチェックサムを計算して書き込む
553: void
554: Luna1PROMEmuDevice::WriteNVRAMCsum()
555: {
556: uint8 eor = CalcNVRAMCsum();
557:
558: nvram->Write(0x001c, eor);
559: nvram->Write(0x001d, eor);
560: nvram->Write(0x001e, eor);
561: }
562:
563: // NVRAM のマジックとチェックサムを照合する
564: bool
565: Luna1PROMEmuDevice::VerifyNVRAM()
566: {
567: if (nvram->PeekString(0x0004) != "<nv>") {
568: return false;
569: }
570:
571: uint8 eor = CalcNVRAMCsum();
572: for (uint32 addr = 0x1c; addr < 0x1f; addr++) {
573: if (nvram->Peek(addr) != eor) {
574: return false;
575: }
576: }
577: return true;
578: }
579:
580: // NVRAM を初期化する
581: bool
582: Luna1PROMEmuDevice::InitNVRAM()
583: {
584: // 初期値どうしたもんか
585: nvram->WriteString(0x0004, "<nv>");
586: nvram->WriteString(0x0020, "BOOT");
587: nvram->WriteString(0x0030, "dk");
588: nvram->WriteString(0x0040, "HOST");
589: nvram->WriteString(0x0050, "nono"); // 偽札的なアレ
590: nvram->WriteString(0x0060, "SERVER");
591: nvram->WriteString(0x0070, "servername");
592: nvram->WriteString(0x0080, "DKUNIT");
593: nvram->WriteString(0x0090, "0");
594: nvram->WriteString(0x00a0, "DKPART");
595: nvram->WriteString(0x00b0, "c");
596: nvram->WriteString(0x00c0, "DKFILE");
597: nvram->WriteString(0x00d0, "vmunix");
598: nvram->WriteString(0x00e0, "FLUNIT");
599: nvram->WriteString(0x00f0, "2");
600: nvram->WriteString(0x0100, "FLFILE");
601: nvram->WriteString(0x0110, "vmunix");
602: nvram->WriteString(0x0120, "STUNIT");
603: nvram->WriteString(0x0130, "0");
604: nvram->WriteString(0x0140, "STFLNO");
605: nvram->WriteString(0x0150, "1");
606: nvram->WriteString(0x0160, "STFILE");
607: nvram->WriteString(0x0170, "vmunix");
608: nvram->WriteString(0x0180, "ETBOOT");
609: nvram->WriteString(0x0190, "server");
610: nvram->WriteString(0x01a0, "ETFILE");
611: nvram->WriteString(0x01b0, "/vmunix");
612: nvram->WriteString(0x0560, "ENADDR");
613: nvram->WriteString(0x0570, ""); // どうするか
614:
615: // チェックサム再計算
616: WriteNVRAMCsum();
617:
618: return true;
619: }
620:
621: // NVRAM のブート設定を bootinfo に取得する。
622: void
623: Luna1PROMEmuDevice::GetNVRAM(bootinfo_t& bootinfo)
624: {
625: // 起動先情報を NVRAM (mk48t02) に問い合わせる
626: // DKUNIT が装置番号(今はまだ SCSI ID=6 固定)。
627: // DKPART が 'a'..'h' でパーティション。
628: // DKFILE が読み込む次段プログラム (NetBSD では通常 /boot のこと)。
629: bootinfo.dkunit = nvram->Peek(0x0090) - '0';
630: bootinfo.dkpart = nvram->Peek(0x00b0) - 'a';
631: bootinfo.dkfile = nvram->PeekString(0x00d0);
632: putmsg(1, "%s DKUNIT=%d", __func__, bootinfo.dkunit);
633: putmsg(1, "%s DKPART=%c", __func__, bootinfo.dkpart + 'a');
634: putmsg(1, "%s DKFILE=|%s|", __func__, bootinfo.dkfile.c_str());
635: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.