|
|
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 "ethernet.h"
1.1.1.3 ! root 22: #include "lunafb.h"
1.1 root 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)に
1.1.1.3 ! root 124: int nplane = gLunafb->GetPlaneCount();
1.1 root 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) {
1.1.1.3 ! root 176: ClearPrompt();
1.1 root 177: ConsoleUpdate();
178: }
179:
180: return entry;
181: }
182:
183: // ROM 処理。エントリポイントを返す。
184: uint32
185: Luna1PROMEmuDevice::ROM_Entry()
186: {
187: if (x_executed && x_entrypoint != 0) {
188: return x_entrypoint;
189: } else {
190: return 0;
191: }
192: }
193:
194: // ROM 処理のクローズ。
195: // 次段プログラムにジャンプするための処理も含む。
196: void
197: Luna1PROMEmuDevice::ROM_Close()
198: {
199: inherited::ROM_Close();
200:
201: // NetBSD のブートローダがカーネルを読み込む時にはいくつかレジスタ
202: // 渡しのパラメータがあるので、ここで用意する。本当はターゲットが
203: // NetBSD カーネルの時に限定すべきのような気もするけど、とりあえず。
204: // ROM がブートローダを読み込む時にはそのような取り決め事は一切ない
205: // ので、ブートローダを読む時にも同じ値が入ってしまうが気にしない
206: // ことにする。
207: // 必要なパラメータは
208: // %d6 に bootdev、NetBSD/luna68k の場合は
209: // bootdev = $MACUPP00
210: // ||||++-- パーティション番号
211: // |||+---- SCSI ID (XXX ただしここでは 6 決め打ち)
212: // ||+----- unit (spc0 なら 0 のやつのはず)
213: // |+------ アダプタ (spc = 0、lance = 1)
214: // +------- magic ($a)
215: //
216: // %d7 に howto (0 でよい)
217: // (と %a0 にジャンプ先を入れるがこれは Init() が書き出した ROM
218: // アセンブラコード側で行っている)
219: m68kcpu *cpu = gMPU680x0->GetCPU();
220: RegD(6) = 0xa0060000;
221: RegD(7) = 0;
222: }
223:
224: // 入力コマンド処理 (入力行が揃ったところで呼ばれる)。
225: void
226: Luna1PROMEmuDevice::Command()
227: {
228: std::vector<char> buf(inputbuf.length() + 1);
229: std::vector<std::string> arg;
230:
231: // 1ワードごとに分解
232: strcpy(&buf[0], inputbuf.c_str());
233: char *p = &buf[0];
1.1.1.2 root 234: for (char *ap; (ap = strsep(&p, " ")) != NULL; ) {
235: if (*ap != '\0') {
236: arg.push_back(std::string(ap));
1.1 root 237: }
238: }
239: if (loglevel >= 1) {
240: for (int i = 0; i < arg.size(); i++) {
241: putmsgn("arg[%d]=|%s|", i, arg[i].c_str());
242: }
243: }
244:
245: // "k" コマンド中ならここで先に処理する
246: if (prompt_idx != 0) {
247: CommandK(arg);
248: return;
249: }
250:
251: // 空行
252: if (arg.size() == 0) {
253: return;
254: }
255:
256: // 1ワード目がコマンド
257: if (arg[0] == "h") {
258: // ヘルプメッセージ
259: text.Print("Commands\n");
260: text.Print(
261: " g [<filename>] : load program\n"
262: " gh : load host program (specified by -A option)\n"
263: " h : show this message\n"
264: " k : show/set boot constants\n"
265: " x : execute loaded program\n"
266: );
267: return;
268: }
269: if (arg[0] == "g") {
270: CommandG(arg);
271: return;
272: }
273: if (arg[0] == "gh") {
274: CommandGH();
275: return;
276: }
277: if (arg[0] == "k") {
278: CommandK(arg);
279: return;
280: }
281: if (arg[0] == "x") {
282: CommandX();
283: return;
284: }
285:
286: text.Print("** Unknown command: %s\n", arg[0].c_str());
287: }
288:
289: // "g" コマンド
290: // g なら NVRAM の情報に従ってプログラムをロード。
291: // g <fname> なら NVRAM の fname だけ指定の名前を使ってロード。NVRAM には
292: // 書き込まない。
293: void
294: Luna1PROMEmuDevice::CommandG(const std::vector<std::string>& arg)
295: {
296: std::string ctlr = nvram->PeekString(0x0030);
297: if (ctlr == "dk") { // harddisk
298: std::string fname {};
299:
300: switch (arg.size()) {
301: case 1:
302: break;
303: case 2:
304: fname = arg[1];
305: break;
306: default:
307: text.Print("** Invalid argument.\n");
308: return;
309: }
310: errmsg.clear();
311:
312: bootinfo_t bootinfo;
313: GetNVRAM(bootinfo);
314: // 引数で指定されたらファイル名だけ上書き
315: if (!fname.empty()) {
316: bootinfo.dkfile = fname;
317: }
318: x_entrypoint = LoadGuestFile(bootinfo);
319: if (x_entrypoint == 0) {
320: text.Print("** %s\n", errmsg.c_str());
321: }
322:
323: } else if (ctlr == "et" || // netboot
324: ctlr == "sd" || // cassette tape
325: ctlr == "fl" ) // floppy
326: {
327: text.Print("** %s boot not supported.\n", ctlr.c_str());
328: return;
329: } else {
330: // 起きないはず
331: text.Print("** Invalid boot controller name.\n");
332: return;
333: }
334:
335: if (x_entrypoint) {
336: text.Print("Loaded. Entry point = $%08x\n", x_entrypoint);
337: }
338: }
339:
340: // "gh" コマンド (勝手に拡張)
341: // -A オプションで指定されたホストプログラムをロードする。
342: void
343: Luna1PROMEmuDevice::CommandGH()
344: {
345: // 指定されてたら読み込む
346: if (gMainApp.host_file == NULL) {
347: text.Print("** Cannot loaded; host program is not specified.\n");
348: return;
349: }
350: x_entrypoint = LoadHostFile();
351: }
352:
353:
354: // k コマンドのパラメータ表
355: struct kinfo {
356: uint32 offset; // NVRAM 上のオフセット
357: const char * const name; // 表示名
358: int next; // 次インデックス
359: };
360: static const struct kinfo kinfo[] = {
361: { 0 }, // 0 は通常プロンプトを指すのでここでは使わない
362: { 0x0030, "controller", 0 }, // [1] BOOT (next は動的判定する)
363: { 0x0090, "drive unit", 3 }, // [2] DKUNIT
364: { 0x00b0, "partition ", 4 }, // [3] DKPART
365: { 0x00d0, "filename ", 0 }, // [4] FLNAME
366: { 0x0050, "hostname ", 6 }, // [5] HOST
367: { 0x0070, "servername", 7 }, // [6] SERVER
368: { 0x0190, "filename ", 8 }, // [7] ETBOOT
369: { 0x01b0, "", 0 }, // [8] ETFILE (ETBOOT と1行で処理する)
370: };
371:
372: // "k" コマンド
373: void
374: Luna1PROMEmuDevice::CommandK(const std::vector<std::string>& arg)
375: {
376: uint32 offset;
377: const char *name;
378: int next;
379:
380: // 入力を処理
381: if (prompt_idx == 0) {
382: // ここは1行目表示前なので前行の入力はない。
383: next = 1;
384: } else {
385: std::string val;
386: bool rv;
387:
388: // 入力があれば更新
389: if (arg.size() != 0) {
390: rv = CommandKInput(arg[0]);
391: } else {
392: // [7] の時に入力があれば CommandKInput() の中で更新されるが、
393: // 入力がなければそこを通らないので、こっちでやらないといけない。
394: // うーん。
395: if (prompt_idx == 7) {
396: prompt_idx = kinfo[prompt_idx].next;
397: }
398: rv = true;
399: }
400:
401: // これ以降は更新後の prompt_idx を使う
402:
403: // 入力エラーがあった時はこの行を再入力するため
404: next = prompt_idx;
405:
406: if (rv == true) {
407: // 入力があっても空でも次インデックスへ。
408: if (prompt_idx == 1) {
409: // BOOT なら現在値によって次のインデックスが変わる
410: // "dk" なら [2]、"et" なら [5]。他は未対応
411: offset = kinfo[prompt_idx].offset;
412: val = nvram->PeekString(offset);
413: if (val == "dk") {
414: next = 2;
415: } else if (val == "et") {
416: next = 5;
417: } else {
418: // 起きることはないはず
419: text.Print("** Inconsistent boot parameter: %s\n",
420: val.c_str());
421: }
422: } else {
423: // それ以外はテーブルにかかれている通り
424: next = kinfo[prompt_idx].next;
425: }
426: }
427: }
428:
429: // この行のプロンプト文字列を作成
430: prompt_idx = next;
431: if (prompt_idx == 0) {
432: // 最終行なら元に戻す
433: prompt = default_prompt;
434: } else {
435: // 次のプロンプトに変更
436: name = kinfo[prompt_idx].name;
437: offset = kinfo[prompt_idx].offset;
438: std::string val = nvram->PeekString(offset);
439:
440: // [7] ETBOOT だけ1行で ETBOOT:ETFILE 形式
441: if (prompt_idx == 7) {
442: next = kinfo[prompt_idx].next; // ETFILE
443: offset = kinfo[next].offset;
444: val += ":";
445: val += nvram->PeekString(offset);
446: }
447:
448: prompt = string_format("%s: %s ?", name, val.c_str());
449: }
450: }
451:
452: // k コマンドの入力値を処理する部分。
453: // 成功すれば true を返す。
454: // 失敗すれば false を返す。
455: // 実機 ROM は入力値にエラーがあると黙って再入力を促す。
456: // エラーメッセージくらい表示してもいいかもだがとりあえず真似しておく。
457: bool
458: Luna1PROMEmuDevice::CommandKInput(const std::string& val)
459: {
460: int offset;
461: bool rv = true;
462:
463: offset = kinfo[prompt_idx].offset;
464:
465: switch (prompt_idx) {
466: case 1: // BOOT
467: // 正常な入力値は "dk", "et", "sd", "fl" のみ。
468: if (val != "dk" && val != "et" && val != "sd" && val != "fl") {
469: return false;
470: }
471: rv = nvram->WriteString(offset, val);
472: if (rv == false) {
473: return false;
474: }
475: break;
476:
477: case 7: // ETBOOT
478: {
479: // こいつだけ ETBOOT:ETFILE 形式で2つのパラメータを同時に処理する
480:
481: // ':' で前後に分ける
482: int sep = val.find(':');
483: if (sep == std::string::npos) {
484: return false;
485: }
486: std::string etboot = val.substr(0, sep);
487: std::string etfile = val.substr(sep + 1, val.size() - sep + 1);
488: if (etboot.empty() || etfile.empty()) {
489: return false;
490: }
491:
492: // ETBOOT
493: rv = nvram->WriteString(offset, etboot);
494: if (rv == false) {
495: return false;
496: }
497:
498: // 次へ
499: prompt_idx = kinfo[prompt_idx].next;
500:
501: // ETFILE
502: offset = kinfo[prompt_idx].offset;
503: rv = nvram->WriteString(offset, etfile);
504: if (rv == false) {
505: return false;
506: }
507: break;
508: }
509:
510: default:
511: rv = nvram->WriteString(offset, val);
512: if (rv == false) {
513: return false;
514: }
515: break;
516: }
517:
518: WriteNVRAMCsum();
519: return rv;
520: }
521:
522: // "x" コマンド
523: // 読み込まれていたらフラグを立てるだけ。
524: void
525: Luna1PROMEmuDevice::CommandX()
526: {
527: if (x_entrypoint == 0) {
528: text.Print("** Program is not loaded.\n");
529: } else {
530: x_executed = true;
531: }
532: }
533:
534: // NVRAM を初期化する
535: bool
536: Luna1PROMEmuDevice::InitNVRAM()
537: {
1.1.1.3 ! root 538: nvram->ClearAll();
! 539:
1.1 root 540: // 初期値どうしたもんか
541: nvram->WriteString(0x0004, "<nv>");
542: nvram->WriteString(0x0020, "BOOT");
543: nvram->WriteString(0x0030, "dk");
544: nvram->WriteString(0x0040, "HOST");
545: nvram->WriteString(0x0050, "nono"); // 偽札的なアレ
546: nvram->WriteString(0x0060, "SERVER");
547: nvram->WriteString(0x0070, "servername");
548: nvram->WriteString(0x0080, "DKUNIT");
549: nvram->WriteString(0x0090, "0");
550: nvram->WriteString(0x00a0, "DKPART");
551: nvram->WriteString(0x00b0, "c");
552: nvram->WriteString(0x00c0, "DKFILE");
553: nvram->WriteString(0x00d0, "vmunix");
554: nvram->WriteString(0x00e0, "FLUNIT");
555: nvram->WriteString(0x00f0, "2");
556: nvram->WriteString(0x0100, "FLFILE");
557: nvram->WriteString(0x0110, "vmunix");
558: nvram->WriteString(0x0120, "STUNIT");
559: nvram->WriteString(0x0130, "0");
560: nvram->WriteString(0x0140, "STFLNO");
561: nvram->WriteString(0x0150, "1");
562: nvram->WriteString(0x0160, "STFILE");
563: nvram->WriteString(0x0170, "vmunix");
564: nvram->WriteString(0x0180, "ETBOOT");
565: nvram->WriteString(0x0190, "server");
566: nvram->WriteString(0x01a0, "ETFILE");
567: nvram->WriteString(0x01b0, "/vmunix");
568: nvram->WriteString(0x0560, "ENADDR");
569: nvram->WriteString(0x0570, ""); // どうするか
570:
571: // チェックサム再計算
572: WriteNVRAMCsum();
573:
574: return true;
575: }
576:
577: // NVRAM のブート設定を bootinfo に取得する。
578: void
579: Luna1PROMEmuDevice::GetNVRAM(bootinfo_t& bootinfo)
580: {
581: // 起動先情報を NVRAM (mk48t02) に問い合わせる
582: // DKUNIT が装置番号(今はまだ SCSI ID=6 固定)。
583: // DKPART が 'a'..'h' でパーティション。
584: // DKFILE が読み込む次段プログラム (NetBSD では通常 /boot のこと)。
585: bootinfo.dkunit = nvram->Peek(0x0090) - '0';
586: bootinfo.dkpart = nvram->Peek(0x00b0) - 'a';
587: bootinfo.dkfile = nvram->PeekString(0x00d0);
588: putmsg(1, "%s DKUNIT=%d", __func__, bootinfo.dkunit);
589: putmsg(1, "%s DKPART=%c", __func__, bootinfo.dkpart + 'a');
590: putmsg(1, "%s DKFILE=|%s|", __func__, bootinfo.dkfile.c_str());
591: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.