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