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