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