|
|
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.11! root 139: // SIO をリセットしておく。
! 140: // SIO のバッファにデータが残っていると、NetBSD のブートローダが
! 141: // 起動してこないようだ。リセットして RXEN を落としておけば大丈夫ぽい。
! 142: gSIO->ResetHard();
1.1.1.7 root 143:
1.1 root 144: // コンソールクローズ
1.1.1.9 root 145: gLunafb->EmuConsoleClose();
1.1 root 146: }
147:
1.1.1.2 root 148: // ROM プロンプトのキー入力。
1.1.1.7 root 149: // ゲストコードの割り込みハンドラから呼ばれて、キー入力で行を構成し、
150: // あるいは行が完成すればコマンドを実行する。
151: // 実行結果により画面を更新したりメンバ変数を更新する。
152: void
1.1.1.2 root 153: LunaPROMEmuDevice::ROM_Keyin()
1.1 root 154: {
1.1.1.7 root 155: uint32 lunakey = gSIO->Read(2);
1.1 root 156:
1.1.1.3 root 157: // マウス入力の 2, 3バイト目は無視
158: if (mousecnt > 0) {
159: mousecnt--;
1.1.1.7 root 160: return;
1.1.1.3 root 161: }
162:
1.1.1.2 root 163: // SHIFT キーだけ状態を持つので先に処理
164: if (lunakey == 0x0c || lunakey == 0x0d) { // 左右SHIFT押下
165: is_shift = true;
1.1.1.7 root 166: return;
1.1.1.2 root 167: }
168: if (lunakey == 0x8c || lunakey == 0x8d) { // 左右SHIFT開放
1.1 root 169: is_shift = false;
1.1.1.7 root 170: return;
1.1.1.2 root 171: }
1.1.1.3 root 172: // マウスデータの1バイト目なら、後続2バイトをスキップ
173: if ((lunakey & 0xf8) == 0x80) {
174: mousecnt = 2;
1.1.1.7 root 175: return;
1.1.1.3 root 176: }
177: // キー開放は無視してよい
1.1.1.2 root 178: if ((lunakey & 0x80)) {
1.1.1.7 root 179: return;
1.1.1.2 root 180: }
1.1 root 181:
1.1.1.9 root 182: // キーコードから文字を取得
183: char asciicode;
184: if (is_shift) {
185: asciicode = lunakey2shifttable[lunakey];
186: } else {
187: asciicode = lunakey2asciitable[lunakey];
188: }
189: if (asciicode != 0) {
190: GetChar(asciicode);
191: }
192: }
193:
194: // キー入力ハンドラ (キー入力割り込みによって呼ばれる)。
195: // 引数 asciicode は概ね ASCII コード (ただし 0x1c..0x1f が上下左右)。
196: void
197: LunaPROMEmuDevice::GetChar(char asciicode)
198: {
199: // LUNA1 の場合、
200: // 実機 PROM にはカーソル左右移動の機能はなく、カーソルは常に行末にいる。
201: // そのためかどうか Delete も BackSpace と同じく1文字前を消す動作をして
202: // いる。カーソル位置に文字はないので、Delete をそうするのはまあ分からん
203: // でもない。
204: // 一方、エミュレーション ROM では、カーソルの左右移動をサポートしてみた
205: // ため Delete を「カーソル位置の文字を消す」にすることは出来るが、それは
206: // それで実機と乖離が広がるので、どうしたもんか。
207:
208: switch (asciicode) {
209: case BS:
210: if (inputbuf.empty() == false && inputpos != 0) {
211: inputbuf.erase(--inputpos, 1);
1.1 root 212: }
1.1.1.2 root 213: break;
1.1.1.9 root 214:
215: case LF:
216: // 改行を起こす前にカーソル(ただの反転)を戻しておく必要がある
1.1.1.8 root 217: text.SetAttr(TA::Normal);
1.1.1.2 root 218: // 改行
1.1.1.9 root 219: text.Putc(asciicode);
220: prompt_y = text.GetY();
221: // コマンド実行
1.1.1.7 root 222: Command();
1.1.1.9 root 223: // プロンプトを更新
224: ClearPrompt();
1.1.1.2 root 225: break;
1.1.1.9 root 226:
227: case LEFT:
1.1.1.2 root 228: if (inputpos > 0) {
229: inputpos--;
1.1 root 230: }
1.1.1.2 root 231: break;
1.1.1.9 root 232: case RIGHT:
1.1.1.2 root 233: if (inputpos < inputbuf.length()) {
1.1 root 234: inputpos++;
235: }
236: break;
1.1.1.9 root 237:
238: case UP:
239: case DOWN:
240: case 0: // キー割り当てなし、これは来ないはずだが一応
241: return;
242:
243: default: // 通常文字のはず
1.1.1.2 root 244: inputbuf.insert(inputbuf.begin() + inputpos, asciicode);
245: inputpos++;
246: break;
1.1 root 247: }
1.1.1.2 root 248: ConsoleUpdate();
249: }
1.1 root 250:
1.1.1.9 root 251: // 入力バッファを初期化する。
252: void
253: LunaPROMEmuDevice::ClearPrompt()
254: {
255: prompt_y = text.GetY();
256: // 入力行をクリア
257: inputbuf.clear();
258: inputpos = 0;
259: }
260:
1.1.1.2 root 261: // 画面を更新
262: void
263: LunaPROMEmuDevice::ConsoleUpdate()
264: {
1.1.1.9 root 265: int x;
266: int y;
267:
268: // カーソルを描画する予定の行が最下行を越えていたらスクロール
269: int len = prompt.length() + inputpos;
270: y = prompt_y + (len / text.GetCol());
271: if (y >= text.GetRow()) {
272: text.ScrollUp();
273: prompt_y--;
274: }
275:
276: // プロンプト行から下を一旦全部クリア
277: // (手抜きだが、見た目問題ないはず)
278: text.Mode = TextScreen::Fixed;
279: for (y = prompt_y; y < text.GetRow(); y++) {
280: text.Locate(0, y);
281: for (x = 0; x < text.GetCol(); x++) {
282: text.Putc(' ');
283: }
284: }
285: text.Locate(0, prompt_y);
286: text.Mode = TextScreen::Console;
287:
288: // プロンプトと現在の入力行を出力
289: text.Puts(prompt.c_str());
290: text.Puts(inputbuf.c_str());
291:
1.1.1.8 root 292: // カーソルを描画
1.1.1.9 root 293: y = prompt_y + (len / text.GetCol());
294: x = len % text.GetCol();
295: putmsg(2, "|%s|%s| pos=%d prompt_y=%d cur=(%d,%d)",
296: prompt.c_str(), inputbuf.c_str(), inputpos, prompt_y, x, y);
297: text.Locate(x, y);
1.1.1.8 root 298: text.SetAttr(TA::On);
1.1.1.9 root 299:
1.1 root 300: // 画面更新
1.1.1.9 root 301: gLunafb->EmuConsoleUpdate();
302: }
303:
304: // NVRAM のチェックサムを返す
305: uint8
306: LunaPROMEmuDevice::CalcNVRAMCsum() const
307: {
308: uint8 eor = 0;
309: for (uint32 addr = 0x20; addr < 0x560; addr++) {
310: eor ^= nvram->Peek(addr);
311: }
312: return eor;
313: }
314:
315: // NVRAM のチェックサムを計算して書き込む
316: void
317: LunaPROMEmuDevice::WriteNVRAMCsum()
318: {
319: uint8 eor = CalcNVRAMCsum();
320:
321: nvram->Write(0x001c, eor);
322: nvram->Write(0x001d, eor);
323: nvram->Write(0x001e, eor);
324: }
325:
326: // NVRAM のマジックとチェックサムを照合する
327: bool
328: LunaPROMEmuDevice::VerifyNVRAM() const
329: {
330: if (nvram->PeekString(0x0004) != "<nv>") {
331: return false;
332: }
333:
334: uint8 eor = CalcNVRAMCsum();
335: for (uint32 addr = 0x1c; addr < 0x1f; addr++) {
336: if (nvram->Peek(addr) != eor) {
337: return false;
338: }
339: }
340: return true;
1.1 root 341: }
342:
1.1.1.10 root 343: // -X オプションで指定されたホストファイルをロードする。
1.1 root 344: // ロードできればエントリポイントを返す。
1.1.1.10 root 345: // exec_file が指定されている時に呼ぶこと。
1.1 root 346: uint32
347: LunaPROMEmuDevice::LoadHostFile()
348: {
1.1.1.10 root 349: assert(gMainApp.exec_file);
1.1 root 350:
1.1.1.10 root 351: uint32 entry = gRAM->LoadFile(gMainApp.exec_file);
1.1 root 352: if (entry) {
353: text.Print("Host program loaded. Entry point = $%08x\n",
354: entry);
355: } else {
356: text.Print("** Couldn't load specified host program.\n");
357: }
358: return entry;
359: }
360:
1.1.1.7 root 361: // bootinfo に従ってゲストから起動先をロードする。
1.1 root 362: // 成功すればエントリポイントを返す。
363: // 失敗ならをエラーメッセージを errmsg にセットして 0 を返す。
364: uint32
1.1.1.7 root 365: LunaPROMEmuDevice::LoadGuestFile(const bootinfo_t& bootinfo)
1.1 root 366: {
1.1.1.7 root 367: const int& dkpart = bootinfo.dkpart;
368: const int& dkunit = bootinfo.dkunit;
369: const std::string& dkfile = bootinfo.dkfile;
1.1 root 370:
371: // ユニット (今は SCSI ID=6 決め打ち)
372: int id = 6;
373: SCSITarget *target = gSPC->GetTarget(id);
374: if (target == NULL) {
375: errmsg = "No bootable disks";
376: putmsg(0, "%s", errmsg.c_str());
377: return 0;
378: }
1.1.1.3 root 379: if (target->GetDevType() != SCSI::DevType::HD) {
1.1 root 380: errmsg = "No bootable disks";
381: putmsg(0, "%s", errmsg.c_str());
382: return 0;
383: }
384:
1.1.1.10 root 385: SCSIDisk *hd = dynamic_cast<SCSIDisk*>(target);
1.1 root 386: putmsg(2, "%s SCSIHD[%d] found", __func__, id);
387:
388: // +0 セクタ目(512byte)に Sun disklabel
389: scd_dk_label dk;
1.1.1.10 root 390: if (hd->Peek(&dk, 0, sizeof(dk)) == false) {
1.1 root 391: putmsg(0, "SCSIHD[%d] ReadSector failed", id);
392: errmsg = string_format("dk%d Read disklabel failed.", dkunit);
393: return 0;
394: }
395:
396: // +$1fc の MAGIC をチェック
397: uint32 magic = be16toh(dk.magic);
398: if (magic != scd_dk_label::DKL_MAGIC) {
399: putmsg(0, "SCSIHD[%d] Bad magic 0x%04x (!= 0x%x)",
400: id, magic, scd_dk_label::DKL_MAGIC);
401: errmsg = string_format("dk%d Bad disklabel magic.", dkunit);
402: return 0;
403: }
404:
405: // チェックサム照合は手抜きで省略
406:
407: // 指定パーティションの開始位置とサイズ
408: if (loglevel >= 2) {
409: // デバッグ用に全部表示
410: for (int i = 0; i < 8; i++) {
1.1.1.7 root 411: putmsgn("%s partition %c: { start=%8u size=%8u }", __func__,
1.1 root 412: 'a' + i,
413: be32toh(dk.map[i].blkno),
414: be32toh(dk.map[i].nblk));
415: }
416: }
417: uint32 part_start = be32toh(dk.map[dkpart].blkno);
418: uint32 part_size = be32toh(dk.map[dkpart].nblk);
419: putmsg(1, "%s partition=%c, start=%u size=%u", __func__,
420: dkpart + 'a', part_start, part_size);
421:
422: // ファイルシステムをオープン (マウントっぽいイメージ)
423: Filesys fsys(this);
1.1.1.7 root 424: if (fsys.Mount(hd, part_start, part_size) == false) {
425: errmsg = string_format("dk%d,%c Open ffs failed: %s",
426: dkunit, dkpart + 'a', fsys.errstr.c_str());
427: putmsg(0, "%s: %s", __func__, errmsg.c_str());
1.1 root 428: return 0;
429: }
430:
1.1.1.7 root 431: // ファイルを探してオープンする
432: inodefile f(this);
433: if (fsys.OpenFile(f, dkfile) == false) {
434: errmsg = string_format("dk%d,%c,%s open failed: %s",
435: dkunit, dkpart + 'a', dkfile.c_str(), fsys.errstr.c_str());
436: putmsg(0, "%s: %s", __func__, errmsg.c_str());
1.1 root 437: return 0;
438: }
1.1.1.7 root 439:
1.1 root 440: // ファイル本体を読み込み
1.1.1.7 root 441: fsys.ReadData(f);
1.1 root 442:
443: // ロード
444: uint32 entry;
445: std::string name = string_format("SCSIHD %d,%c:%s",
1.1.1.7 root 446: id, dkpart + 'a', dkfile.c_str());
447: entry = gRAM->LoadFile(name.c_str(), f.data.data(), f.data.size());
448: if (entry == 0) {
449: errmsg = string_format("dk%d,%c,%s not executable",
450: dkunit, dkpart + 'a', dkfile.c_str());
1.1 root 451: }
452:
1.1.1.7 root 453: return entry;
1.1 root 454: }
455:
1.1.1.7 root 456: // キーコードから文字への変換
457: /*static*/ const uint8
458: LunaPROMEmuDevice::lunakey2asciitable[] = {
1.1.1.9 root 459: 0, 0, 0, 0, 0, 0, 0, 0,
460: 0, 0, 0, 0, 0, 0, 0, 0,
461: 0, BS, LF, 0, ' ', 0, 0, 0,
462: 0, 0, 0, 0, UP,LEFT,RIGHT,DOWN,
1.1.1.7 root 463: 0, 0, '1', '2', '3', '4', '5', '6',
464: '7', '8', '9', '0', '-', '^', '\\', 0,
465: 0, 0, 'q', 'w', 'e', 'r', 't', 'y',
466: 'u', 'i', 'o', 'p', '@', '[', 0, 0,
467: 0, 0, 'a', 's', 'd', 'f', 'g', 'h',
468: 'j', 'k', 'l', ';', ':', ']', 0, 0,
469: 0, 0, 'z', 'x', 'c', 'v', 'b', 'n',
470: 'm', ',', '.', '/', 0, 0, 0, 0,
471: 0, 0, 0, 0, 0, 0, 0, 0, // XXX テンキー
472: 0, 0, 0, 0, 0, 0, 0, 0,
473: 0, 0, 0, 0, 0, 0, 0, 0,
474: 0, 0, 0, 0, 0, 0, 0, 0,
475: };
476: /*static*/ const uint8
477: LunaPROMEmuDevice::lunakey2shifttable[] = {
1.1.1.9 root 478: 0, 0, 0, 0, 0, 0, 0, 0,
479: 0, 0, 0, 0, 0, 0, 0, 0,
480: 0, BS, LF, 0, ' ', 0, 0, 0,
481: 0, 0, 0, 0, UP,LEFT,RIGHT,DOWN,
482: 0, 0, '!', '\"','#', '$', '%', '&',
483: '\'','(', ')', ' ', '=', '~', '|', 0, // XXX SHIFT+'0'は?
1.1.1.7 root 484: 0, 0, 'Q', 'W', 'E', 'R', 'T', 'Y',
485: 'U', 'I', 'O', 'P', '`', '{', 0, 0,
486: 0, 0, 'A', 'S', 'D', 'F', 'G', 'H',
487: 'J', 'K', 'L', '+', '*', '}', 0, 0,
488: 0, 0, 'Z', 'X', 'C', 'V', 'B', 'N',
489: 'M', '<', '>', '?', '_', 0, 0, 0,
490: 0, 0, 0, 0, 0, 0, 0, 0, // XXX テンキー
491: 0, 0, 0, 0, 0, 0, 0, 0,
492: 0, 0, 0, 0, 0, 0, 0, 0,
493: 0, 0, 0, 0, 0, 0, 0, 0,
494: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.