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