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