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