|
|
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.11 root 7: //
1.1.1.13! root 8: // LUNA-88K の ROM エミュレーション
1.1.1.8 root 9: //
1.1.1.11 root 10:
1.1.1.8 root 11: // IODevice
12: // |
13: // +-- ROMDevice (LoadROM()、ウェイト、マスク等を持つ)
14: // | |
15: // | +-- PROMDevice (LUNA* の PROM)
16: // | +-- IPLROM1Device (X680x0 の IPLROM 後半)
17: // | +-- IPLROM2Device (X680x0 の IPLROM 前半)
18: // | +-- CGROMDevice (X680x0 の CGROM)
19: // | +-- LunaPROMEmuDevice (LUNA PROM エミュレーションの共通部分)
20: // | |
1.1.1.13! root 21: // | +-- Luna1PROMEmuDevice (LUNA-I の PROM エミュレーション)
1.1.1.8 root 22: // | |
23: // | | +----------------------+
1.1.1.13! root 24: // | +--| Luna88kPROMEmuDevice |(LUNA-88K の PROM エミュレーション)
1.1.1.8 root 25: // | +----------------------+
26: // |
27: // +-- PROM0Device (LUNA* のブートページ切り替え用プロキシ)
28: // +-- IPLROM0Device (X680x0 のブートページ切り替え用プロキシ)
1.1.1.2 root 29:
30: #include "romemu_luna88k.h"
1.1.1.11 root 31: #include "interrupt.h"
1.1.1.5 root 32: #include "iodevstream.h"
1.1 root 33: #include "mainapp.h"
1.1.1.13! root 34: #include "mainram.h"
1.1 root 35: #include "memorystream.h"
1.1.1.7 root 36: #include "mk48t02.h"
1.1.1.8 root 37: #include "mpu88xx0.h"
1.1.1.5 root 38: #include "pio.h"
1.1 root 39:
1.1.1.13! root 40: static bool luna88k_fline_callback(MPU88xx0Device *cpu, void *arg);
1.1.1.8 root 41:
42: // m88k の命令コードを少し読みやすく書きたい。
43: // 必要なものは適宜足すこと。
44: enum class OPr {
45: r0, r1, r2, r3, r4, r5, r6, r7,
46: r8, r9, r10, r11, r12, r13, r14, r15,
47: r16, r17, r18, r19, r20, r21, r22, r23,
48: r24, r25, r26, r27, r28, r29, r30, r31,
49: };
50: #define r0 OPr::r0
51: #define r1 OPr::r1
52: #define r2 OPr::r2
53: #define r3 OPr::r3
54: #define r4 OPr::r4
55: #define r5 OPr::r5
56: #define r6 OPr::r6
57: #define r7 OPr::r7
58: #define r8 OPr::r8
59: #define r9 OPr::r9
60: #define r10 OPr::r10
61: enum OPm5 {
62: eq0 = 0x02,
1.1.1.13! root 63: ge0 = 0x03,
1.1.1.8 root 64: ne0 = 0x0d,
65: };
66: enum OPcr {
67: psr = 0x01,
68: };
69:
1.1.1.11 root 70: #define USC __unused static constexpr
71:
72: USC uint32 OP3(uint32 base, uint32 rd, uint32 rs1, uint32 rs2) {
1.1.1.8 root 73: return (base) | (rd << 21) | (rs1 << 16) | rs2;
74: }
1.1.1.11 root 75: USC uint32 OP4(uint32 base, uint32 rd, uint32 rs1,
1.1.1.8 root 76: uint32 w, uint32 o) {
77: return (base) | (rd << 21) | (rs1 << 16) | (w << 5) | o;
78: }
79:
1.1.1.11 root 80: USC uint32 OP_addu(OPr rd, OPr rs1, uint32 imm) {
1.1.1.8 root 81: return OP3(0x60000000, (uint32)rd, (uint32)rs1, imm);
82: }
1.1.1.11 root 83: USC uint32 OP_addu(OPr rd, OPr rs1, OPr rs2) {
84: return OP3(0xf4006000, (uint32)rd, (uint32)rs1, (uint32)rs2);
1.1.1.8 root 85: }
1.1.1.11 root 86: USC uint32 OP_bcnd(OPm5 m5, OPr rs1, uint32 d16) {
87: return OP3(0xe8000000, (uint32)m5, (uint32)rs1, d16 & 0xffff);
1.1.1.8 root 88: }
1.1.1.11 root 89: USC uint32 OP_bsr(uint32 d26) {
90: return (0xc8000000 | (d26 & 0x03ffffff));
91: }
92: USC uint32 OP_clr(OPr rd, OPr rs1, uint32 w, uint32 o) {
1.1.1.8 root 93: return OP4(0xf0008000, (uint32)rd, (uint32)rs1, w, o);
94: }
1.1.1.11 root 95: USC uint32 OP_jmp(OPr rs2) {
1.1.1.8 root 96: return OP3(0xf400c000, 0, 0, (uint32)rs2);
97: }
1.1.1.11 root 98: USC uint32 OP_ld(OPr rd, OPr rs1, uint32 imm) {
1.1.1.8 root 99: return OP3(0x14000000, (uint32)rd, (uint32)rs1, imm);
100: }
1.1.1.11 root 101: USC uint32 OP_ldcr(OPr rd, uint32 crs) {
1.1.1.8 root 102: return OP4(0x80004000, (uint32)rd, 0, crs, 0);
103: }
1.1.1.11 root 104: USC uint32 __unused OP_or(OPr rd, OPr rs1, OPr rs2) {
1.1.1.8 root 105: return OP3(0xf4005800, (uint32)rd, (uint32)rs1, (uint32)rs2);
106: }
1.1.1.11 root 107: USC uint32 OP_or(OPr rd, OPr rs1, uint32 imm) {
1.1.1.8 root 108: return OP3(0x58000000, (uint32)rd, (uint32)rs1, imm);
109: }
1.1.1.11 root 110: USC uint32 OP_or_u(OPr rd, OPr rs1, uint32 imm) {
1.1.1.8 root 111: return OP3(0x5c000000, (uint32)rd, (uint32)rs1, imm);
112: }
1.1.1.11 root 113: USC uint32 OP_rte() {
1.1.1.8 root 114: return 0xf400fc00;
115: }
1.1.1.11 root 116: USC uint32 OP_st(OPr rd, OPr rs1, OPr rs2) {
1.1.1.8 root 117: return OP3(0xf4002400, (uint32)rd, (uint32)rs1, (uint32)rs2);
118: }
1.1.1.11 root 119: USC uint32 OP_stcr(OPr rs1, uint32 crd) {
1.1.1.8 root 120: return OP4(0x80008000, 0, (uint32)rs1, crd, (uint32)rs1);
121: }
1.1.1.11 root 122: USC uint32 OP_doscall(OPr rd, OPr rs1, uint32 imm) {
1.1.1.8 root 123: return OP3(0xfc000400, (uint32)rd, (uint32)rs1, imm);
124: }
125: #define DOS_GETCHAR (1)
126: #define DOS_PUTCHAR (2)
127:
128:
1.1 root 129: // コンストラクタ
130: Luna88kPROMEmuDevice::Luna88kPROMEmuDevice()
131: {
1.1.1.5 root 132: // ROM の特定アドレスを読み出す人がいるかも知れないので、
133: // サイズは実 ROM と同じにしておく。LUNA-I からの類推。
1.1.1.13! root 134: uint devlen = 256 * 1024;
1.1.1.8 root 135: imagebuf.reset(new uint8 [devlen]);
136: memset(&imagebuf[0], 0, devlen);
137: mem = &imagebuf[0];
138:
139: mask = devlen - 1;
1.1.1.2 root 140:
1.1.1.5 root 141: // プロンプトを初期化。
1.1.1.7 root 142: prompt = default_prompt;
1.1 root 143: }
144:
145: // デストラクタ
146: Luna88kPROMEmuDevice::~Luna88kPROMEmuDevice()
147: {
148: }
149:
1.1.1.13! root 150: // 初期化
1.1 root 151: bool
152: Luna88kPROMEmuDevice::Init()
153: {
1.1.1.5 root 154: if (inherited::Init() == false) {
155: return false;
156: }
157:
1.1.1.13! root 158: interrupt = GetInterruptDevice();
! 159:
1.1.1.8 root 160: // DOS call エミュレーションのコールバックを登録
1.1.1.13! root 161: auto mpu88xx0 = GetMPU88xx0Device();
! 162: mpu88xx0->SetFLineCallback(luna88k_fline_callback, this);
1.1.1.8 root 163:
164: MemoryStreamBE ms(imagebuf.get());
1.1 root 165:
1.1.1.6 root 166: // リセット時のブートページには ROM (これ) が見えている。
167: // ROMIO_INIT へのアクセスでブートページを RAM に切り替えて、同時に RAM の
168: // 0番地付近のベクタも作り直す。そのため、ここのベクタ1 以降 ($41000008〜)
169: // をベクタとして参照することはないので、ベクタ0 の命令列がそのまま使い潰
170: // してよい。というか2ワードでは 0番地から ROM まで飛べないので使わないと
171: // 面倒くさい。一体どういう設計してんだ…。
172:
1.1.1.8 root 173: // ここからややアセンブラっぽいインデント
174:
175: // _START:
176: // ここは 0番地。ここから _ROM_START へジャンプ
177: ms.Write32(OP_or_u (r6, r0, 0x4100));
178: ms.Write32(OP_or (r6, r6, 0x000c));
179: ms.Write32(OP_jmp (r6));
180:
181: // _ROM_START: (ここは ROM)
182: // RAM に切り替え
183: ms.Write32(OP_st (r0, r6, r0));
184:
1.1.1.11 root 185: // 元の PSR を r10 に保存し、r9 に割り込み許可の値を作る
1.1.1.8 root 186: ms.Write32(OP_ldcr (r10, psr));
1.1.1.11 root 187: ms.Write32(OP_clr (r9, r10, 2, 0)); // clear IND,SFRZ
1.1.1.8 root 188:
189: ms.Write32(OP_or_u (r3, r0, ROMIO_BASE >> 16));
1.1.1.13! root 190: ms.Write32(OP_or (r3, r3, ROMIO_BASE & 0xffff));
1.1.1.8 root 191: // ROMIO_INIT を読み出す
192: ms.Write32(OP_ld (r2, r3, (ROMIO_INIT - ROMIO_BASE)));
193: // 起動先があれば GO
1.1.1.13! root 194: ms.Write32(OP_bcnd (ge0, r2, 7)); // 7 = _go
1.1.1.8 root 195:
196: // _prompt:
197: // 疑似 STOP でキー入力を待つ
1.1.1.11 root 198: ms.Write32(OP_stcr (r9, psr)); // 割り込み許可
1.1.1.8 root 199: ms.Write32(OP_bsr (1)); // 1 = _stop
200: // この時点で r1 = _stop
201:
202: // _stop:
203: ms.Write32(OP_jmp (r1));
204:
205: // _back:
1.1.1.13! root 206: uint32 back = baseaddr + ms.GetOffset();
1.1.1.11 root 207: // 割り込み禁止に戻す
208: ms.Write32(OP_stcr (r10, psr));
209:
1.1.1.8 root 210: ms.Write32(OP_ld (r2, r3, (ROMIO_ENTRY - ROMIO_BASE)));
211: // 起動先がなければループ
1.1.1.11 root 212: ms.Write32(OP_bcnd (eq0, r2, -5)); // -5 = _prompt
1.1.1.8 root 213:
214: // _go:
215: // 起動先があれば後始末して
216: ms.Write32(OP_ld (r0, r3, (ROMIO_CLOSE - ROMIO_BASE)));
217: // ジャンプ
218: ms.Write32(OP_jmp (r2));
1.1.1.5 root 219:
220: // ここから割り込みハンドラ。割り込みは SIO のみ許可にしてある。
221: // キーボード割り込みが上がったら KEYIN をコールして _back に戻る。
1.1.1.11 root 222: ms.SetOffset(0x200);
1.1.1.8 root 223: ms.Write32(OP_ld (r0, r3, (ROMIO_KEYIN - ROMIO_BASE)));
224: // 戻ったところの jmp を抜ける
1.1.1.13! root 225: ms.Write32(OP_or_u (r1, r0, back >> 16));
! 226: ms.Write32(OP_or (r1, r1, back & 0xffff));
1.1.1.8 root 227: ms.Write32(OP_rte ());
228:
229: // ROM GETC エミュレータ
1.1.1.11 root 230: ms.SetOffset(0x300);
1.1.1.8 root 231: ms.Write32(OP_doscall(r2, r0, DOS_GETCHAR));
232: ms.Write32(OP_jmp (r1));
233:
234: // ROM PUTC エミュレータ
1.1.1.13! root 235: // 表示する文字は r2 に置かれている。
1.1.1.11 root 236: ms.SetOffset(0x400);
1.1.1.13! root 237: ms.Write32(OP_doscall(r0, r2, DOS_PUTCHAR));
1.1.1.8 root 238: ms.Write32(OP_jmp (r1));
239:
240:
241: // 変なインデントここまで
1.1 root 242:
243: return true;
244: }
245:
1.1.1.5 root 246: // 起動時の処理を行う。
247: // 次段実行ファイルの読み込みに成功すればそのエントリポイントを返す。
248: // そうでなければ 0 を返すので、プロンプトに処理を移すこと。
249: uint32
250: Luna88kPROMEmuDevice::ROM_Init()
1.1 root 251: {
1.1.1.13! root 252: IODeviceStream rs(mainram);
1.1.1.5 root 253: uint32 entry;
1.1 root 254:
1.1.1.13! root 255: b_entrypoint = -1;
1.1.1.11 root 256:
1.1.1.5 root 257: // デバイスの初期化 (親クラス)
258: InitDevice();
1.1.1.6 root 259:
1.1.1.13! root 260: auto pio0 = GetPIO0Device();
! 261:
1.1.1.5 root 262: // 割り込みマスクで SIO だけ許可
1.1.1.13! root 263: interrupt->Write32(0x65000000, 0x40000000);
1.1 root 264:
1.1.1.5 root 265: rs.SetAddr(0);
266: // ベクタテーブルを rte で埋めておく
267: for (int i = 0; i < 512 * 2; i++) {
1.1.1.8 root 268: rs.Write32(OP_rte());
1.1.1.5 root 269: }
270: // +$08: 割り込みベクタ
271: rs.Write32(8, 0xc00003fe); // br $00001000
272:
273: // 割り込みトランポリン (0x1000)
274: // ベクタテーブルは各2ワードしかないのに、0 番地付近にあるベクタテーブル
275: // から2ワードでは ROM まで飛べない。一体どういう設計してんだ…。
276: rs.SetAddr(0x1000);
1.1.1.8 root 277: rs.Write32(OP_or_u (r6, r0, 0x4100));
278: rs.Write32(OP_or (r6, r6, 0x0200));
279: rs.Write32(OP_jmp (r6));
1.1.1.2 root 280:
1.1.1.5 root 281: // ROM の機能テーブル
1.1.1.8 root 282: // 0x00001100 ?
283: // 0x00001104 ?
284: // 0x00001108 ?
285: // 0x0000110c GETC() のアドレス
286: // 0x00001110 PUTC(c) のアドレス
1.1.1.5 root 287: // 0x00001114 ビットマッププレーンの有効状態が入っている。
288: // bit0: plane0 有効
289: // bit1..7: plane1..7 有効
290: rs.SetAddr(0x110c);
1.1.1.8 root 291: rs.Write32(0x41000300);
292: rs.Write32(0x41000400);
293: rs.Write32(0x00000001);
1.1.1.5 root 294:
295: // スクリーンを初期化
1.1.1.11 root 296: InitScreen();
1.1.1.5 root 297:
1.1.1.13! root 298: entry = -1;
1.1.1.5 root 299:
300: // 表示環境が整ったところで NVRAM を照合
301: if (VerifyNVRAM() == false) {
1.1.1.7 root 302: // PROM 1.20 は初期化するかどうか確認してくるけど、とりあえず。
1.1.1.5 root 303: InitNVRAM();
1.1.1.11 root 304: errmsg = "NVRAM Initialized.";
1.1.1.5 root 305: // NVRAM をクリアしたら DIPSW によらずプロンプトに降りる
306: goto done;
307: }
308:
1.1.1.10 root 309: // -X file
310: // DIPSW の状態に関わらず、ファイルをロードし実行する。
311: // --load-only file
312: // DIPSW の状態に関わらず、ファイルをロードし、プロンプトで止まる。
313: if (gMainApp.exec_file) {
314: // ホストのファイルを読み込んでエントリポイントアドレスを
315: // この Read アクセスの読み出し値として返す。
316: // エラーがあればエラーメッセージは呼び出し先側で表示してある。
317: b_entrypoint = LoadHostFile();
318: if (gMainApp.load_and_exec) {
319: entry = b_entrypoint;
320: }
321: } else {
322: // DIPSW#1-1 が UP なら自動起動、DOWN なら ROM モニタ起動なので、
323: // これを真似する。
1.1.1.13! root 324: if (pio0->IsDIPSW11Up()) {
1.1.1.5 root 325: // NVRAM の設定に従ってブート先を探す。
326: errmsg.clear();
327:
328: bootinfo_t bootinfo;
329: GetNVRAM(bootinfo);
330: entry = LoadGuestFile(bootinfo);
331: }
332: }
333:
334: done:
1.1.1.13! root 335: if (entry == -1) {
1.1.1.11 root 336: PrintTitle("LUNA88K");
337: if (errmsg.empty() == false) {
338: Print("** %s\n\n", errmsg.c_str());
339: }
1.1.1.7 root 340: ClearPrompt();
1.1.1.5 root 341: }
342:
343: return entry;
344: }
345:
346: // ROM 処理。エントリポイントを返す。
347: uint32
348: Luna88kPROMEmuDevice::ROM_Entry()
349: {
1.1.1.13! root 350: if (b_entrypoint != -1) {
1.1.1.5 root 351: return b_entrypoint;
1.1.1.2 root 352: } else {
1.1.1.5 root 353: return 0;
1.1.1.2 root 354: }
355: }
356:
1.1.1.7 root 357: void
1.1.1.13! root 358: Luna88kPROMEmuDevice::ProcessChar(char asciicode)
1.1.1.7 root 359: {
360: if (prompt_idx == 0) {
361: // 行入力モード (画面更新もしてある)
1.1.1.13! root 362: inherited::ProcessChar(asciicode);
1.1.1.7 root 363: } else {
364: // 0 以外ならノンブロッキングモード
365:
366: // 入力されたキーを表示
367: if (' ' <= asciicode && asciicode < 0x7f) {
1.1.1.11 root 368: Putc(asciicode);
1.1.1.7 root 369: }
370:
371: switch (prompt_idx) {
372: case PROMPT_NVRAM_UPDATE:
373: CommandNvramUpdate2(asciicode);
374: break;
375:
376: case PROMPT_NVRAM_NEW:
377: CommandNvramNew2(asciicode);
378: break;
379:
380: case PROMPT_NVRAM_INIT:
381: CommandNvramInit2(asciicode);
382: break;
383: }
384:
385: // プロンプトを戻す
386: prompt = default_prompt;
387: prompt_idx = 0;
1.1.1.11 root 388: prompt_y = cursor_y;
389: ClearPrompt();
1.1.1.7 root 390: }
391: }
392:
1.1.1.5 root 393: // 入力コマンド処理 (入力行が揃ったところで呼ばれる)。
394: //
395: // b 現在のパラメータで起動
396: // b ... パラメータを指定して起動
397: // nvram 全パラメータを表示
398: // nvram [<name>] name のみ表示?
399: // nvram <name> <val> 値を設定
400: // nvram - <name> 削除
401: // nvram-- NVRAM 初期化
402:
403: void
404: Luna88kPROMEmuDevice::Command()
1.1.1.2 root 405: {
1.1.1.5 root 406: std::vector<char> buf(inputbuf.length() + 1);
407: std::vector<std::string> arg;
1.1.1.2 root 408:
1.1.1.5 root 409: // 1ワードごとに分解
410: strcpy(&buf[0], inputbuf.c_str());
411: char *p = &buf[0];
1.1.1.6 root 412: for (char *ap; (ap = strsep(&p, " ")) != NULL; ) {
413: if (*ap != '\0') {
414: arg.push_back(std::string(ap));
1.1.1.5 root 415: }
416: }
417: if (loglevel >= 1) {
418: for (int i = 0; i < arg.size(); i++) {
419: putmsgn("arg[%d]=|%s|", i, arg[i].c_str());
420: }
421: }
1.1.1.2 root 422:
1.1.1.5 root 423: // 空行
424: if (arg.size() == 0) {
425: return;
426: }
427:
428: // 1ワード目がコマンド
429: if (arg[0] == "h") {
1.1.1.7 root 430: CommandH(arg);
1.1.1.5 root 431: return;
432: }
433: if (arg[0] == "b") {
434: CommandB(arg);
435: return;
436: }
437: if (arg[0] == "nvram") {
438: CommandNvram(arg);
439: return;
1.1.1.2 root 440: }
1.1.1.5 root 441:
1.1.1.11 root 442: Print("** Unknown command: %s\n", arg[0].c_str());
1.1.1.2 root 443: }
444:
1.1.1.7 root 445: // "h" コマンド
446: // 引数なしなら、全体のヘルプメッセージ。
447: // 引数ありなら、個別のヘルプメッセージ。
448: void
449: Luna88kPROMEmuDevice::CommandH(const std::vector<std::string>& arg)
450: {
451: if (arg.size() < 2) {
452: // ヘルプメッセージ
453: mainhelp:
1.1.1.11 root 454: Print(
1.1.1.7 root 455: " *** Help Message ***\n"
456: " b : load and execute program\n"
457: " h : show this message\n"
458: " nvram : show/set nvram parameters\n"
459: );
460: return;
461: }
462:
463: const std::string& key = arg[1];
464: if (key == "nvram") {
1.1.1.11 root 465: Print(
1.1.1.7 root 466: " *** Help Message ***\n"
467: " nvram [<symbol>] : show\n"
468: " nvram <symbol> <value> : set\n"
469: " nvram - <symbol> : delete\n"
470: " nvram -- : initialize\n"
471: );
472: return;
473: } else {
474: goto mainhelp;
475: }
476: }
477:
1.1.1.5 root 478: // "b" コマンド
479: // 引数なしなら NVRAM の情報に従ってプログラムをロードして実行。
480: // 引数ありのほうは未実装。
481: void
482: Luna88kPROMEmuDevice::CommandB(const std::vector<std::string>& arg)
1.1 root 483: {
1.1.1.5 root 484: errmsg.clear();
1.1 root 485:
1.1.1.5 root 486: // XXX とりあえず boot を起動するだけ
487: bootinfo_t bootinfo;
488: GetNVRAM(bootinfo);
489: b_entrypoint = LoadGuestFile(bootinfo);
1.1.1.13! root 490: if (b_entrypoint == -1) {
1.1.1.11 root 491: Print("** %s\n", errmsg.c_str());
1.1 root 492: }
1.1.1.5 root 493: }
1.1 root 494:
1.1.1.5 root 495: // "bh" コマンド (勝手に拡張)
1.1.1.8 root 496: // -X オプションで指定されたホストプログラムをロードして実行。
1.1.1.5 root 497: void
498: Luna88kPROMEmuDevice::CommandBH()
499: {
500: // 指定されてたら読み込む
1.1.1.8 root 501: if (gMainApp.exec_file == NULL) {
1.1.1.11 root 502: Print("** Cannot loaded; host program is not specified.\n");
1.1.1.5 root 503: return;
1.1.1.2 root 504: }
1.1.1.5 root 505: b_entrypoint = LoadHostFile();
1.1.1.2 root 506: }
507:
1.1.1.7 root 508: // "nvram" コマンド
509: // nvram .. 全表示
510: // nvram <key> .. 表示
511: // nvram <key> <val> .. 追加/更新
512: // nvram - <key> .. 削除
513: // nvram -- .. 初期化
1.1.1.5 root 514: void
515: Luna88kPROMEmuDevice::CommandNvram(const std::vector<std::string>& arg)
1.1.1.2 root 516: {
1.1.1.7 root 517: switch (arg.size()) {
518: case 1:
519: // 引数なし
520: CommandNvramShowAll();
521: break;
522: case 2:
523: // 引数1つ
524: if (arg[1] == "--") {
525: CommandNvramInit();
526: } else {
527: CommandNvramShow(arg);
528: }
529: break;
530: case 3:
531: // 引数2つ
532: if (arg[1] == "-") {
533: CommandNvramDelete(arg);
534: } else {
535: CommandNvramUpdate(arg);
536: }
537: break;
538: default:
1.1.1.11 root 539: Print("** Syntax error\n");
1.1.1.7 root 540: }
541: }
542:
543: // NVRAM は 32バイトを1エントリとする。
544: // [0] はヘッダに相当し、フォーマットは別記。
545: // [1] 以降が key (16バイト)、val(16バイト) の並び。こんな感じ。
546: //
547: // 00000020 62 6f 6f 74 5f 64 65 76 69 63 65 00 00 00 00 00 |boot_device.....|
548: // 00000030 73 64 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |sd..............|
549: //
550: // key, val とも '\0' で終端もしくは16文字で終端のようだ。
551: // val に17文字を設定しようとすると怒られる。
552: // key が "" ならこのエントリは空きで val は無視、
553: // 空きエントリは無視して次へ (FAT のディレクトリエントリみたいな感じ)。
554: // val が "" は有効。
555: //
556: // オフセット 0x540 から始まるエントリが最後で、これ以上追加しようとすると
557: // "nvram set error: no area" (PROM 1.20) と言われる。
1.1.1.13! root 558: // LUNA-88K にはないが LUNA-I では 0x560 にぽつんと ENADDR があったので、
1.1.1.7 root 559: // その辺りとの互換性だろうか。
560:
561: // NVRAM 全項目表示
562: void
563: Luna88kPROMEmuDevice::CommandNvramShowAll()
564: {
565: putmsg(1, "nvram ShowAll");
566: for (uint32 offset = 0x20; offset < 0x560; offset += 0x20) {
567: std::string key = nvram->PeekString(offset);
568: if (key.empty()) {
569: continue;
570: }
571: std::string val = nvram->PeekString(offset + 16);
1.1.1.11 root 572: Print("%-15s : %s\n", key.c_str(), val.c_str());
1.1.1.7 root 573: }
574: }
575:
576: // NVRAM 指定項目を表示
577: void
578: Luna88kPROMEmuDevice::CommandNvramShow(const std::vector<std::string>& arg)
579: {
580: assert(arg.size() >= 2);
581: const std::string& key = arg[1];
582:
583: putmsg(1, "nvram Show |%s|", key.c_str());
584: uint32 offset = FindNVRAM(key);
585: if (offset > 0) {
586: std::string val = nvram->PeekString(offset + 16);
1.1.1.11 root 587: Print("%-15s : %s\n", key.c_str(), val.c_str());
1.1.1.7 root 588: } else {
1.1.1.11 root 589: Print("** Not found\n");
1.1.1.7 root 590: }
591: }
592:
593: // NVRAM 項目を追加/更新
594: void
595: Luna88kPROMEmuDevice::CommandNvramUpdate(const std::vector<std::string>& arg)
596: {
597: // キーがあれば更新、なければ追加。
598: // いずれも事前にプロンプトで確認する。
599:
600: assert(arg.size() >= 3);
601: nvram_key = arg[1];
602: nvram_val = arg[2];
603: putmsg(1, "nvram Update |%s|%s|", nvram_key.c_str(), nvram_val.c_str());
604:
605: if (nvram_key.size() > 16) {
1.1.1.11 root 606: Print("** Key too long\n");
1.1.1.7 root 607: return;
608: }
609: if (nvram_val.size() > 16) {
1.1.1.11 root 610: Print("** Val too long\n");
1.1.1.7 root 611: return;
612: }
613:
614: nvram_offset = FindNVRAM(nvram_key);
615: if (nvram_offset > 0) {
616: // キーがすでにあれば更新。
617: // Update2() に申し送りするのは nvram_offset と nvram_val のみでいい。
618:
619: std::string oldval = nvram->PeekString(nvram_offset + 16);
620: prompt = string_format("Update %s : \"%s\" -> \"%s\" (Y/[N]):",
621: nvram_key.c_str(), oldval.c_str(), nvram_val.c_str());
622: prompt_idx = PROMPT_NVRAM_UPDATE;
623: } else {
624: // キーがなければ追加
625: // New2() に申し送りするのは nvram_offset, nvram_key, nvram_val の3つ。
626:
627: // 先に空き領域をチェックしている
628: nvram_offset = FindNVRAM("");
629: if (nvram_offset == 0) {
1.1.1.11 root 630: Print("** No area available\n");
1.1.1.7 root 631: return;
632: }
633:
634: prompt = string_format("Set new %s : \"%s\" (Y/[N]):",
635: nvram_key.c_str(), nvram_val.c_str());
636: prompt_idx = PROMPT_NVRAM_NEW;
637: }
638:
639: // 入力待ちの状態で戻る
640: // (prompt_idx != 0 なのでキー入力後に指定の関数へ飛んで続きを実行する)
641: }
642:
643: // NVRAM 項目を更新 (キー入力後)
644: void
645: Luna88kPROMEmuDevice::CommandNvramUpdate2(char asciicode)
646: {
647: assert(prompt_idx == PROMPT_NVRAM_UPDATE);
648: assert(nvram_offset > 0);
649: assert(nvram_val.size() <= 16);
650:
1.1.1.11 root 651: Putc('\n');
1.1.1.7 root 652:
653: if (asciicode != 'y' && asciicode != 'Y') {
1.1.1.11 root 654: Print("** Not updated\n");
1.1.1.7 root 655: return;
656: }
657:
658: if (nvram->WriteString(nvram_offset + 16, nvram_val) == false) {
1.1.1.11 root 659: Print("** Write error\n");
1.1.1.7 root 660: return;
661: }
662: WriteNVRAMCsum();
663:
1.1.1.11 root 664: Print("Updated\n");
1.1.1.7 root 665: }
666:
667: // NVRAM 項目を追加 (キー入力後)
668: void
669: Luna88kPROMEmuDevice::CommandNvramNew2(char asciicode)
670: {
671: assert(prompt_idx == PROMPT_NVRAM_NEW);
672: assert(nvram_offset > 0);
673: assert(!nvram_key.empty());
674: assert(nvram_key.size() <= 16);
675: assert(nvram_val.size() <= 16);
676:
1.1.1.11 root 677: Putc('\n');
1.1.1.7 root 678:
679: if (asciicode != 'y' && asciicode != 'Y') {
1.1.1.11 root 680: Print("** Not added\n");
1.1.1.7 root 681: return;
682: }
683:
684: if (nvram->WriteString(nvram_offset, nvram_key) == false
685: || nvram->WriteString(nvram_offset + 16, nvram_val) == false) {
1.1.1.11 root 686: Print("** Write error\n");
1.1.1.7 root 687: return;
688: }
689: WriteNVRAMCsum();
690:
1.1.1.11 root 691: Print("Added\n");
1.1.1.2 root 692: }
693:
1.1.1.7 root 694: // NVRAM 項目削除
695: //
696: // 削除する時は確認がないらしい… (PROM 1.20)
697: // 成功しても何も言わないらしい… (PROM 1.20)
698: // 追加削除に比べてなんか雑じゃない?
699: void
700: Luna88kPROMEmuDevice::CommandNvramDelete(const std::vector<std::string>& arg)
701: {
702: assert(arg.size() >= 3);
703: assert(arg[1] == "-");
704: const std::string& key = arg[2];
705: putmsg(1, "nvram Delete |%s|", key.c_str());
706:
707: if (key.size() > 16) {
1.1.1.11 root 708: Print("** key too long\n");
1.1.1.7 root 709: return;
710: }
711:
712: uint32 offset = FindNVRAM(key);
713: if (offset == 0) {
1.1.1.11 root 714: Print("** Not found\n");
1.1.1.7 root 715: return;
716: }
717:
718: // キーを消せば削除になる (値のほうは消してないようだ)
719: nvram->WriteString(offset, "");
720: WriteNVRAMCsum();
721: }
722:
723: // NVRAM から key を探してそのエントリのオフセットを返す。
724: // key == "" なら空きエントリを探す。
725: // 見付からなければ 0 を返す (オフセット 0 はパラメータには使わないため)
726: uint32
727: Luna88kPROMEmuDevice::FindNVRAM(const std::string& key) const
1.1.1.2 root 728: {
1.1.1.7 root 729: for (uint32 offset = 0x20; offset < 0x560; offset += 0x20) {
730: std::string k = nvram->PeekString(offset);
731: if (k == key) {
732: return offset;
733: }
734: // XXX 同じキーが複数あったらどうなるか
735: }
1.1.1.2 root 736: return 0;
737: }
738:
1.1.1.7 root 739: // NVRAM 初期化
1.1.1.5 root 740: void
1.1.1.7 root 741: Luna88kPROMEmuDevice::CommandNvramInit()
1.1.1.2 root 742: {
1.1.1.7 root 743: putmsg(1, "nvram Init");
744:
745: prompt = "Initialize (Y/[N]):";
746: prompt_idx = PROMPT_NVRAM_INIT;
747:
748: // 入力待ちの状態で戻る
1.1.1.2 root 749: }
750:
1.1.1.7 root 751: // NVRAM 初期化 (キー入力後)
752: void
753: Luna88kPROMEmuDevice::CommandNvramInit2(char asciicode)
1.1.1.2 root 754: {
1.1.1.7 root 755: assert(prompt_idx == PROMPT_NVRAM_INIT);
756:
1.1.1.11 root 757: Putc('\n');
1.1.1.7 root 758:
759: if (asciicode != 'y' && asciicode != 'Y') {
1.1.1.11 root 760: Print("** Not initialized\n");
1.1.1.7 root 761: return;
762: }
763:
764: InitNVRAM();
1.1.1.11 root 765: Print("** Initialized\n");
1.1 root 766: }
767:
1.1.1.5 root 768: // NVRAM を初期化する
769: bool
770: Luna88kPROMEmuDevice::InitNVRAM()
1.1 root 771: {
1.1.1.7 root 772: nvram->ClearAll();
773:
774: nvram->WriteString(0x0004, "<nv>");
775: nvram->WriteString(0x0020, "boot_device");
776: nvram->WriteString(0x0030, "sd");
777: nvram->WriteString(0x0040, "boot_spec");
778: nvram->WriteString(0x0050, "0");
779: nvram->WriteString(0x0060, "boot_unit");
780: nvram->WriteString(0x0070, "0");
781: nvram->WriteString(0x0080, "boot_partition");
782: nvram->WriteString(0x0090, "0");
783: nvram->WriteString(0x00a0, "boot_filename");
784: nvram->WriteString(0x00b0, "vmunix");
785: nvram->WriteString(0x00c0, "main_mem_check");
786: nvram->WriteString(0x00d0, "1");
787: nvram->WriteString(0x00e0, "3port_mem_check");
788: nvram->WriteString(0x00f0, "1");
789:
790: // チェックサム再計算
791: WriteNVRAMCsum();
792:
793: return true;
1.1.1.5 root 794: }
1.1 root 795:
1.1.1.5 root 796: // NVRAM のブート設定を bootinfo に取得する。
797: void
798: Luna88kPROMEmuDevice::GetNVRAM(bootinfo_t& bootinfo)
799: {
1.1.1.7 root 800: // dkunit (装置番号) はたぶん boot_unit (0..)
801: // dkpart (パーティション) は boot_partition (0..)
802: // dkfile (ファイル名) は boot_filename
803: // sd 以外のデバイスは未対応
804: bootinfo.dkunit = nvram->Peek(0x0070) - '0';
805: bootinfo.dkpart = nvram->Peek(0x0090) - '0';
806: bootinfo.dkfile = nvram->PeekString(0x00b0);
807: putmsg(1, "%s unit=%d", __func__, bootinfo.dkunit);
808: putmsg(1, "%s part=%c", __func__, bootinfo.dkpart + 'a');
809: putmsg(1, "%s file=|%s|", __func__, bootinfo.dkfile.c_str());
1.1 root 810: }
1.1.1.8 root 811:
812: // コールバック関数 (C のグローバル関数)
813: bool
1.1.1.13! root 814: luna88k_fline_callback(MPU88xx0Device *cpu, void *arg)
1.1.1.8 root 815: {
816: Luna88kPROMEmuDevice *dev = (Luna88kPROMEmuDevice *)arg;
817: return dev->FLineCallback(cpu);
818: }
819:
820: // コールバック関数 (こっちが実体)
821: bool
1.1.1.13! root 822: Luna88kPROMEmuDevice::FLineCallback(MPU88xx0Device *cpu)
1.1.1.8 root 823: {
824: // 111111_DDDDDSSSSS_000001_00nnnnnnnn
825: //
826: // 引数は rS1 から順番に格納。
827: // m68k 的なバイト値やワード値の場合は下位詰めしてあるが、不要な上位
828: // ビットの処置はゲストコードよりホストコードで行うほうが楽なので、
829: // すべてこちらで責任持って処理すること。
830: // 戻り値がある DOS call では戻り値は rD に格納。
831:
1.1.1.13! root 832: uint32 D = (cpu->reg.opX >> 21) & 0x1f;
! 833: uint32 S1 = (cpu->reg.opX >> 16) & 0x1f;
! 834: uint32 num = cpu->reg.opX & 0xff;
1.1.1.8 root 835:
836: switch (num) {
837: case 0x00: // _EXIT
838: break;
839:
840: case 0x01: // _GETCHAR
1.1.1.13! root 841: cpu->reg.r[D] = Getc();
1.1.1.8 root 842: break;
843:
844: case 0x02: // _PUTCHAR
1.1.1.13! root 845: Putc(cpu->reg.r[S1++] & 0xff);
1.1.1.8 root 846: break;
847:
848: default:
1.1.1.12 root 849: putlog(0, "DOS call #$%02x (NOT IMPLEMENTED)", num);
1.1.1.13! root 850: cpu->reg.r[D] = 0xffffffff;
1.1.1.8 root 851: break;
852: }
853:
854: // 存在しないコール番号、未実装コールも含めて処理済みとする。
855: return true;
856: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.