|
|
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.7 root 7: // LUNA シリーズの ROM エミュレーション共通部分
1.1.1.2 root 8:
9: // IODevice
10: // |
1.1.1.7 root 11: // +----------+---------------------+
12: // v v
13: // ROMDevice LunaPROMEmuDeivce (ROM エミュレーションの共通部分)
14: // | |
15: // | +------------+----------------+
16: // v v v
17: // PROMDevice Luna1PROMEmuDevice Luna88kPROMEmuDevice
18: // (実ROM) (LUNA-I エミュレーション) (LUNA88K エミュレーション)
1.1.1.2 root 19:
20: #include "romemu_luna.h"
1.1 root 21: #include "bt454.h"
1.1.1.9 ! root 22: #include "lunafb.h"
1.1 root 23: #include "mainapp.h"
24: #include "mk48t02.h"
25: #include "ram.h"
26: #include "sio.h"
27: #include "spc.h"
1.1.1.7 root 28: #include "ufs.h"
1.1 root 29:
30: // コンストラクタ
31: LunaPROMEmuDevice::LunaPROMEmuDevice()
1.1.1.9 ! root 32: : inherited("PROM")
1.1 root 33: {
34: }
35:
36: // デストラクタ
37: LunaPROMEmuDevice::~LunaPROMEmuDevice()
38: {
39: }
40:
41: bool
42: LunaPROMEmuDevice::Init()
43: {
44: // 都度都度 dynamic_cast は面倒なので、ここで一度代入しとく
45: nvram = dynamic_cast<MK48T02Device*>(gRTC.get());
46:
47: return true;
48: }
49:
50: void
51: LunaPROMEmuDevice::ResetHard()
52: {
1.1.1.8 root 53: // ブートページを ROM に切り替える。
54: isrom = true;
55: gVM->SwitchBootPage(this, true);
1.1 root 56: }
57:
58: // アドレスデコーダ
59: inline uint64
60: LunaPROMEmuDevice::Decoder(uint32 addr) const
61: {
1.1.1.2 root 62: // 折り返しなし
1.1 root 63: return addr - baseaddr;
64: }
65:
66: uint64
1.1.1.2 root 67: LunaPROMEmuDevice::Read8(uint32 addr)
68: {
69: uint64 offset = Decoder(addr);
70:
71: // 謎の IO 空間はロングワードアクセスのみ
72: return romdata[offset & (devlen - 1)];
73: }
74:
75: uint64
76: LunaPROMEmuDevice::Read16(uint32 addr)
77: {
78: uint64 offset = Decoder(addr);
79:
80: // 謎の IO 空間はロングワードアクセスのみ
81: return be16toh(*(uint16 *)&romdata[offset & (devlen - 1)]);
82: }
83:
84: uint64
1.1 root 85: LunaPROMEmuDevice::Read32(uint32 addr)
86: {
87: uint64 offset = Decoder(addr);
88:
1.1.1.9 ! root 89: // ROM からのロングワードアクセスでだけ謎の I/O 空間が見える
! 90: if ((gMPU->GetPaddr() & 0xff000000) == baseaddr) {
! 91: switch (addr) {
! 92: case ROMIO_INIT:
1.1.1.2 root 93: return ROM_Init();
1.1 root 94:
1.1.1.9 ! root 95: case ROMIO_KEYIN:
1.1.1.7 root 96: ROM_Keyin();
97: return 0;
1.1 root 98:
1.1.1.9 ! root 99: case ROMIO_CLOSE:
1.1.1.7 root 100: ROM_Close();
101: return 0;
1.1 root 102:
1.1.1.9 ! root 103: case ROMIO_ENTRY:
1.1.1.7 root 104: return ROM_Entry();
1.1.1.6 root 105:
1.1 root 106: default:
107: break;
108: }
109: }
1.1.1.2 root 110: return be32toh(*(uint32 *)&romdata[offset & (devlen - 1)]);
111: }
112:
113: uint64
114: LunaPROMEmuDevice::Write8(uint32 addr, uint32 data)
115: {
116: return 0;
117: }
118:
119: uint64
120: LunaPROMEmuDevice::Write16(uint32 addr, uint32 data)
121: {
122: return 0;
123: }
124:
125: uint64
126: LunaPROMEmuDevice::Write32(uint32 addr, uint32 data)
127: {
128: return 0;
129: }
130:
131: uint64
132: LunaPROMEmuDevice::Peek8(uint32 addr)
133: {
134: uint64 offset = Decoder(addr);
135:
136: return romdata[offset & (devlen - 1)];
1.1 root 137: }
138:
1.1.1.7 root 139: // デバイスの初期化の共通部分。(継承クラスの ROM_Init() から呼ぶ)
140: void
141: LunaPROMEmuDevice::InitDevice()
1.1 root 142: {
1.1.1.8 root 143: // ブートページを RAM に切り替える。
144: if (isrom) {
145: isrom = false;
146: gVM->SwitchBootPage(this, false);
147: }
148:
1.1 root 149: // 初期パレット
1.1.1.4 root 150: // 偶数番は白(R/G/B=15/15/15)、奇数番は黒(0/0/0)。
151: uint8 val = 0xf0;
1.1.1.7 root 152: gBT454->Write(0, 0); // select palette
1.1.1.4 root 153: for (int i = 0; i < 16; i++) {
1.1.1.7 root 154: gBT454->Write(1, val); // R
155: gBT454->Write(1, val); // G
156: gBT454->Write(1, val); // B
1.1.1.4 root 157: val ^= 0xf0;
158: }
1.1 root 159:
160: // SCSI
161: // BDID の書き込みによってホストデバイスがバスにアタッチされる構造なので。
1.1.1.7 root 162: gSPC->Write(SPC::BDID, 7);
1.1 root 163:
1.1.1.7 root 164: // キーボード(chB)を初期化
165: gSIO->Write(3, 0x01); // CR1
166: gSIO->Write(3, 0x10); // RX Int(all char)
167: gSIO->Write(3, 0x03); // CR3
168: gSIO->Write(3, 0xc1); // RX Enable, 8bit
169: gSIO->Write(3, 0x04); // CR4
170: gSIO->Write(3, 0x44); // Stop 1bit
171: gSIO->Write(3, 0x05); // CR5
172: gSIO->Write(3, 0x68); // TX Enable, 8bit
173: }
1.1.1.2 root 174:
1.1.1.7 root 175: // スクリーンを初期化
176: void
177: LunaPROMEmuDevice::InitScreen(const char *machine_name)
178: {
1.1.1.9 ! root 179: text.Init(80, 32, TextScreen::Console);
! 180: gLunafb->EmuConsoleOpen(&text);
1.1.1.2 root 181: inputbuf.clear();
182: inputpos = 0;
183: is_shift = false;
1.1.1.3 root 184: mousecnt = 0;
1.1 root 185:
1.1.1.7 root 186: text.Print(0, 0, "NONO %d.%d.%d Emulated ROM Monitor for %s",
187: NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER, machine_name);
1.1 root 188: text.Locate(0, 2);
189: }
190:
1.1.1.7 root 191: // ROM 処理のクローズ
192: void
1.1.1.2 root 193: LunaPROMEmuDevice::ROM_Close()
1.1 root 194: {
1.1.1.7 root 195: // SIO からキーの Break 入力を取り除いておく。
196: // コマンド入力によってここへ来る時は、キーの Make で呼ばれるため
197: // Break はまだ取り出されておらず SIO のバッファに残っている。
198: // コマンド入力によらずここに来た場合でも空読みして問題ないだろう。
199: (void)gSIO->Read(2);
200:
1.1 root 201: // コンソールクローズ
1.1.1.9 ! root 202: gLunafb->EmuConsoleClose();
1.1 root 203: }
204:
1.1.1.2 root 205: // ROM プロンプトのキー入力。
1.1.1.7 root 206: // ゲストコードの割り込みハンドラから呼ばれて、キー入力で行を構成し、
207: // あるいは行が完成すればコマンドを実行する。
208: // 実行結果により画面を更新したりメンバ変数を更新する。
209: void
1.1.1.2 root 210: LunaPROMEmuDevice::ROM_Keyin()
1.1 root 211: {
1.1.1.7 root 212: uint32 lunakey = gSIO->Read(2);
1.1 root 213:
1.1.1.3 root 214: // マウス入力の 2, 3バイト目は無視
215: if (mousecnt > 0) {
216: mousecnt--;
1.1.1.7 root 217: return;
1.1.1.3 root 218: }
219:
1.1.1.2 root 220: // SHIFT キーだけ状態を持つので先に処理
221: if (lunakey == 0x0c || lunakey == 0x0d) { // 左右SHIFT押下
222: is_shift = true;
1.1.1.7 root 223: return;
1.1.1.2 root 224: }
225: if (lunakey == 0x8c || lunakey == 0x8d) { // 左右SHIFT開放
1.1 root 226: is_shift = false;
1.1.1.7 root 227: return;
1.1.1.2 root 228: }
1.1.1.3 root 229: // マウスデータの1バイト目なら、後続2バイトをスキップ
230: if ((lunakey & 0xf8) == 0x80) {
231: mousecnt = 2;
1.1.1.7 root 232: return;
1.1.1.3 root 233: }
234: // キー開放は無視してよい
1.1.1.2 root 235: if ((lunakey & 0x80)) {
1.1.1.7 root 236: return;
1.1.1.2 root 237: }
1.1 root 238:
1.1.1.9 ! root 239: // キーコードから文字を取得
! 240: char asciicode;
! 241: if (is_shift) {
! 242: asciicode = lunakey2shifttable[lunakey];
! 243: } else {
! 244: asciicode = lunakey2asciitable[lunakey];
! 245: }
! 246: if (asciicode != 0) {
! 247: GetChar(asciicode);
! 248: }
! 249: }
! 250:
! 251: // キー入力ハンドラ (キー入力割り込みによって呼ばれる)。
! 252: // 引数 asciicode は概ね ASCII コード (ただし 0x1c..0x1f が上下左右)。
! 253: void
! 254: LunaPROMEmuDevice::GetChar(char asciicode)
! 255: {
! 256: // LUNA1 の場合、
! 257: // 実機 PROM にはカーソル左右移動の機能はなく、カーソルは常に行末にいる。
! 258: // そのためかどうか Delete も BackSpace と同じく1文字前を消す動作をして
! 259: // いる。カーソル位置に文字はないので、Delete をそうするのはまあ分からん
! 260: // でもない。
! 261: // 一方、エミュレーション ROM では、カーソルの左右移動をサポートしてみた
! 262: // ため Delete を「カーソル位置の文字を消す」にすることは出来るが、それは
! 263: // それで実機と乖離が広がるので、どうしたもんか。
! 264:
! 265: switch (asciicode) {
! 266: case BS:
! 267: if (inputbuf.empty() == false && inputpos != 0) {
! 268: inputbuf.erase(--inputpos, 1);
1.1 root 269: }
1.1.1.2 root 270: break;
1.1.1.9 ! root 271:
! 272: case LF:
! 273: // 改行を起こす前にカーソル(ただの反転)を戻しておく必要がある
1.1.1.8 root 274: text.SetAttr(TA::Normal);
1.1.1.2 root 275: // 改行
1.1.1.9 ! root 276: text.Putc(asciicode);
! 277: prompt_y = text.GetY();
! 278: // コマンド実行
1.1.1.7 root 279: Command();
1.1.1.9 ! root 280: // プロンプトを更新
! 281: ClearPrompt();
1.1.1.2 root 282: break;
1.1.1.9 ! root 283:
! 284: case LEFT:
1.1.1.2 root 285: if (inputpos > 0) {
286: inputpos--;
1.1 root 287: }
1.1.1.2 root 288: break;
1.1.1.9 ! root 289: case RIGHT:
1.1.1.2 root 290: if (inputpos < inputbuf.length()) {
1.1 root 291: inputpos++;
292: }
293: break;
1.1.1.9 ! root 294:
! 295: case UP:
! 296: case DOWN:
! 297: case 0: // キー割り当てなし、これは来ないはずだが一応
! 298: return;
! 299:
! 300: default: // 通常文字のはず
1.1.1.2 root 301: inputbuf.insert(inputbuf.begin() + inputpos, asciicode);
302: inputpos++;
303: break;
1.1 root 304: }
1.1.1.2 root 305: ConsoleUpdate();
306: }
1.1 root 307:
1.1.1.9 ! root 308: // 入力バッファを初期化する。
! 309: void
! 310: LunaPROMEmuDevice::ClearPrompt()
! 311: {
! 312: prompt_y = text.GetY();
! 313: // 入力行をクリア
! 314: inputbuf.clear();
! 315: inputpos = 0;
! 316: }
! 317:
1.1.1.2 root 318: // 画面を更新
319: void
320: LunaPROMEmuDevice::ConsoleUpdate()
321: {
1.1.1.9 ! root 322: int x;
! 323: int y;
! 324:
! 325: // カーソルを描画する予定の行が最下行を越えていたらスクロール
! 326: int len = prompt.length() + inputpos;
! 327: y = prompt_y + (len / text.GetCol());
! 328: if (y >= text.GetRow()) {
! 329: text.ScrollUp();
! 330: prompt_y--;
! 331: }
! 332:
! 333: // プロンプト行から下を一旦全部クリア
! 334: // (手抜きだが、見た目問題ないはず)
! 335: text.Mode = TextScreen::Fixed;
! 336: for (y = prompt_y; y < text.GetRow(); y++) {
! 337: text.Locate(0, y);
! 338: for (x = 0; x < text.GetCol(); x++) {
! 339: text.Putc(' ');
! 340: }
! 341: }
! 342: text.Locate(0, prompt_y);
! 343: text.Mode = TextScreen::Console;
! 344:
! 345: // プロンプトと現在の入力行を出力
! 346: text.Puts(prompt.c_str());
! 347: text.Puts(inputbuf.c_str());
! 348:
1.1.1.8 root 349: // カーソルを描画
1.1.1.9 ! root 350: y = prompt_y + (len / text.GetCol());
! 351: x = len % text.GetCol();
! 352: putmsg(2, "|%s|%s| pos=%d prompt_y=%d cur=(%d,%d)",
! 353: prompt.c_str(), inputbuf.c_str(), inputpos, prompt_y, x, y);
! 354: text.Locate(x, y);
1.1.1.8 root 355: text.SetAttr(TA::On);
1.1.1.9 ! root 356:
1.1 root 357: // 画面更新
1.1.1.9 ! root 358: gLunafb->EmuConsoleUpdate();
! 359: }
! 360:
! 361: // NVRAM のチェックサムを返す
! 362: uint8
! 363: LunaPROMEmuDevice::CalcNVRAMCsum() const
! 364: {
! 365: uint8 eor = 0;
! 366: for (uint32 addr = 0x20; addr < 0x560; addr++) {
! 367: eor ^= nvram->Peek(addr);
! 368: }
! 369: return eor;
! 370: }
! 371:
! 372: // NVRAM のチェックサムを計算して書き込む
! 373: void
! 374: LunaPROMEmuDevice::WriteNVRAMCsum()
! 375: {
! 376: uint8 eor = CalcNVRAMCsum();
! 377:
! 378: nvram->Write(0x001c, eor);
! 379: nvram->Write(0x001d, eor);
! 380: nvram->Write(0x001e, eor);
! 381: }
! 382:
! 383: // NVRAM のマジックとチェックサムを照合する
! 384: bool
! 385: LunaPROMEmuDevice::VerifyNVRAM() const
! 386: {
! 387: if (nvram->PeekString(0x0004) != "<nv>") {
! 388: return false;
! 389: }
! 390:
! 391: uint8 eor = CalcNVRAMCsum();
! 392: for (uint32 addr = 0x1c; addr < 0x1f; addr++) {
! 393: if (nvram->Peek(addr) != eor) {
! 394: return false;
! 395: }
! 396: }
! 397: return true;
1.1 root 398: }
399:
400: // -A オプションで指定されたホストファイルをロードする。
401: // ロードできればエントリポイントを返す。
402: // host_file が指定されている時に呼ぶこと。
403: uint32
404: LunaPROMEmuDevice::LoadHostFile()
405: {
406: assert(gMainApp.host_file);
407:
1.1.1.7 root 408: uint32 entry = gRAM->LoadFile(gMainApp.host_file);
1.1 root 409: if (entry) {
410: text.Print("Host program loaded. Entry point = $%08x\n",
411: entry);
412: } else {
413: text.Print("** Couldn't load specified host program.\n");
414: }
415: return entry;
416: }
417:
1.1.1.7 root 418: // bootinfo に従ってゲストから起動先をロードする。
1.1 root 419: // 成功すればエントリポイントを返す。
420: // 失敗ならをエラーメッセージを errmsg にセットして 0 を返す。
421: uint32
1.1.1.7 root 422: LunaPROMEmuDevice::LoadGuestFile(const bootinfo_t& bootinfo)
1.1 root 423: {
1.1.1.7 root 424: const int& dkpart = bootinfo.dkpart;
425: const int& dkunit = bootinfo.dkunit;
426: const std::string& dkfile = bootinfo.dkfile;
1.1 root 427:
428: // ユニット (今は SCSI ID=6 決め打ち)
429: int id = 6;
430: SCSITarget *target = gSPC->GetTarget(id);
431: if (target == NULL) {
432: errmsg = "No bootable disks";
433: putmsg(0, "%s", errmsg.c_str());
434: return 0;
435: }
1.1.1.3 root 436: if (target->GetDevType() != SCSI::DevType::HD) {
1.1 root 437: errmsg = "No bootable disks";
438: putmsg(0, "%s", errmsg.c_str());
439: return 0;
440: }
441:
442: SCSIHD *hd = dynamic_cast<SCSIHD*>(target);
443: putmsg(2, "%s SCSIHD[%d] found", __func__, id);
444:
445: // +0 セクタ目(512byte)に Sun disklabel
446: scd_dk_label dk;
447: if (hd->PeekImage(&dk, 0, sizeof(dk)) == false) {
448: putmsg(0, "SCSIHD[%d] ReadSector failed", id);
449: errmsg = string_format("dk%d Read disklabel failed.", dkunit);
450: return 0;
451: }
452:
453: // +$1fc の MAGIC をチェック
454: uint32 magic = be16toh(dk.magic);
455: if (magic != scd_dk_label::DKL_MAGIC) {
456: putmsg(0, "SCSIHD[%d] Bad magic 0x%04x (!= 0x%x)",
457: id, magic, scd_dk_label::DKL_MAGIC);
458: errmsg = string_format("dk%d Bad disklabel magic.", dkunit);
459: return 0;
460: }
461:
462: // チェックサム照合は手抜きで省略
463:
464: // 指定パーティションの開始位置とサイズ
465: if (loglevel >= 2) {
466: // デバッグ用に全部表示
467: for (int i = 0; i < 8; i++) {
1.1.1.7 root 468: putmsgn("%s partition %c: { start=%8u size=%8u }", __func__,
1.1 root 469: 'a' + i,
470: be32toh(dk.map[i].blkno),
471: be32toh(dk.map[i].nblk));
472: }
473: }
474: uint32 part_start = be32toh(dk.map[dkpart].blkno);
475: uint32 part_size = be32toh(dk.map[dkpart].nblk);
476: putmsg(1, "%s partition=%c, start=%u size=%u", __func__,
477: dkpart + 'a', part_start, part_size);
478:
479: // ファイルシステムをオープン (マウントっぽいイメージ)
480: Filesys fsys(this);
1.1.1.7 root 481: if (fsys.Mount(hd, part_start, part_size) == false) {
482: errmsg = string_format("dk%d,%c Open ffs failed: %s",
483: dkunit, dkpart + 'a', fsys.errstr.c_str());
484: putmsg(0, "%s: %s", __func__, errmsg.c_str());
1.1 root 485: return 0;
486: }
487:
1.1.1.7 root 488: // ファイルを探してオープンする
489: inodefile f(this);
490: if (fsys.OpenFile(f, dkfile) == false) {
491: errmsg = string_format("dk%d,%c,%s open failed: %s",
492: dkunit, dkpart + 'a', dkfile.c_str(), fsys.errstr.c_str());
493: putmsg(0, "%s: %s", __func__, errmsg.c_str());
1.1 root 494: return 0;
495: }
1.1.1.7 root 496:
1.1 root 497: // ファイル本体を読み込み
1.1.1.7 root 498: fsys.ReadData(f);
1.1 root 499:
500: // ロード
501: uint32 entry;
502: std::string name = string_format("SCSIHD %d,%c:%s",
1.1.1.7 root 503: id, dkpart + 'a', dkfile.c_str());
504: entry = gRAM->LoadFile(name.c_str(), f.data.data(), f.data.size());
505: if (entry == 0) {
506: errmsg = string_format("dk%d,%c,%s not executable",
507: dkunit, dkpart + 'a', dkfile.c_str());
1.1 root 508: }
509:
1.1.1.7 root 510: return entry;
1.1 root 511: }
512:
1.1.1.7 root 513: // キーコードから文字への変換
514: /*static*/ const uint8
515: LunaPROMEmuDevice::lunakey2asciitable[] = {
1.1.1.9 ! root 516: 0, 0, 0, 0, 0, 0, 0, 0,
! 517: 0, 0, 0, 0, 0, 0, 0, 0,
! 518: 0, BS, LF, 0, ' ', 0, 0, 0,
! 519: 0, 0, 0, 0, UP,LEFT,RIGHT,DOWN,
1.1.1.7 root 520: 0, 0, '1', '2', '3', '4', '5', '6',
521: '7', '8', '9', '0', '-', '^', '\\', 0,
522: 0, 0, 'q', 'w', 'e', 'r', 't', 'y',
523: 'u', 'i', 'o', 'p', '@', '[', 0, 0,
524: 0, 0, 'a', 's', 'd', 'f', 'g', 'h',
525: 'j', 'k', 'l', ';', ':', ']', 0, 0,
526: 0, 0, 'z', 'x', 'c', 'v', 'b', 'n',
527: 'm', ',', '.', '/', 0, 0, 0, 0,
528: 0, 0, 0, 0, 0, 0, 0, 0, // XXX テンキー
529: 0, 0, 0, 0, 0, 0, 0, 0,
530: 0, 0, 0, 0, 0, 0, 0, 0,
531: 0, 0, 0, 0, 0, 0, 0, 0,
532: };
533: /*static*/ const uint8
534: LunaPROMEmuDevice::lunakey2shifttable[] = {
1.1.1.9 ! root 535: 0, 0, 0, 0, 0, 0, 0, 0,
! 536: 0, 0, 0, 0, 0, 0, 0, 0,
! 537: 0, BS, LF, 0, ' ', 0, 0, 0,
! 538: 0, 0, 0, 0, UP,LEFT,RIGHT,DOWN,
! 539: 0, 0, '!', '\"','#', '$', '%', '&',
! 540: '\'','(', ')', ' ', '=', '~', '|', 0, // XXX SHIFT+'0'は?
1.1.1.7 root 541: 0, 0, 'Q', 'W', 'E', 'R', 'T', 'Y',
542: 'U', 'I', 'O', 'P', '`', '{', 0, 0,
543: 0, 0, 'A', 'S', 'D', 'F', 'G', 'H',
544: 'J', 'K', 'L', '+', '*', '}', 0, 0,
545: 0, 0, 'Z', 'X', 'C', 'V', 'B', 'N',
546: 'M', '<', '>', '?', '_', 0, 0, 0,
547: 0, 0, 0, 0, 0, 0, 0, 0, // XXX テンキー
548: 0, 0, 0, 0, 0, 0, 0, 0,
549: 0, 0, 0, 0, 0, 0, 0, 0,
550: 0, 0, 0, 0, 0, 0, 0, 0,
551: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.