|
|
1.1 root 1: //
2: // nono
1.1.1.4 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
5: //
6:
1.1 root 7: //
1.1.1.9 root 8: // Human68k .r .x .z console emulator
1.1 root 9: //
10:
11: // メモリマップ
12: //
13: // 0000'0000 ベクタ
14: // 0000'0200 Humanエントリポイント
15: // 0000'8000 FCBエリア
16: // 0000'c000 コマンドライン引数
17: // 0000'e000 環境変数
18: // 0001'ff00 SSP 初期値
19: // 0001'ff00 PSP(プロセスエントリ)
20: // 0002'0000 ロードアドレス
21: // 00bf'ffff RAM_END
22:
1.1.1.9 root 23: #include "human68k.h"
24: #include "autofd.h"
25: #include "iodevstream.h"
26: #include "mainapp.h"
1.1.1.11 root 27: #include "mainbus.h"
1.1.1.12 root 28: #include "mainram.h"
1.1.1.9 root 29: #include "mpu680x0.h"
1.1.1.11 root 30: #include "power.h"
1.1.1.9 root 31: #include "scheduler.h"
32: #include <fcntl.h>
1.1.1.13 root 33: #include <time.h>
1.1.1.9 root 34: #include <sys/stat.h>
35: #include <sys/mman.h>
36:
1.1.1.11 root 37: static bool human68k_fline_callback(MPU680x0Device *, void *arg);
1.1 root 38:
1.1.1.12 root 39: #define RegD(n) mpu680x0->reg.D[n]
40: #define RegA(n) mpu680x0->reg.A[n]
1.1.1.9 root 41:
1.1 root 42: // コンストラクタ
1.1.1.12 root 43: Human68kDevice::Human68kDevice()
1.1.1.11 root 44: : inherited(OBJ_HUMAN68K)
1.1 root 45: {
46: Files[0].fd = 0;
1.1.1.4 root 47: Files[0].filename = "|stdin";
1.1 root 48: Files[1].fd = 1;
1.1.1.4 root 49: Files[1].filename = "|stdout";
1.1 root 50: Files[2].fd = 2;
1.1.1.4 root 51: Files[2].filename = "|stderr";
1.1 root 52: }
53:
54: // デストラクタ
1.1.1.12 root 55: Human68kDevice::~Human68kDevice()
1.1 root 56: {
57: }
58:
1.1.1.11 root 59: // 初期化
1.1 root 60: bool
1.1.1.12 root 61: Human68kDevice::Init()
1.1 root 62: {
63: uint32 psp_base;
64: uint8 *file;
1.1.1.13 root 65: uint32 last_addr;
66: int ram_size;
1.1 root 67:
1.1.1.11 root 68: if (inherited::Init() == false) {
69: return false;
70: }
71:
1.1.1.12 root 72: assert(gMainApp.exec_file);
73: human68k_file = gMainApp.exec_file;
74: human68k_arg = gMainApp.exec_arg;
75:
76: mainbus = GetMainbusDevice();
1.1.1.13 root 77: mainram = GetMainRAMDevice();
1.1.1.12 root 78: mpu680x0 = GetMPU680x0Device(mpu);
1.1.1.11 root 79:
1.1.1.13 root 80: // Fライン命令をこちらで処理する。
1.1.1.12 root 81: mpu680x0->SetFLineCallback(human68k_fline_callback, this);
1.1 root 82:
1.1.1.13 root 83: IODeviceStream ds(mainram);
84:
85: // ワークを用意 (どこでやるか)。
1.1 root 86: // $CBC.B MPU 種別 (3:68030)
1.1.1.13 root 87: ds.Write1(0x0cbc, 3);
88: // $CBD.B FPU 有無 (0xff:あり)。
1.1.1.14! root 89: if (mpu680x0->HasFPU()) {
1.1.1.13 root 90: ds.Write1(0x0cbd, 0xff);
1.1 root 91: }
92:
1.1.1.8 root 93: putmsg(1, "arg=%s", human68k_arg.c_str());
1.1 root 94:
1.1.1.13 root 95: // 実行ファイルオープン。
1.1.1.6 root 96: autofd file_fd = open(human68k_file, O_RDONLY);
1.1 root 97: if (file_fd == -1) {
1.1.1.2 root 98: warn("Human68k executable \"%s\" open failed", human68k_file);
1.1 root 99: return false;
100: }
101:
102: struct stat st;
103: if (fstat(file_fd, &st) != 0) {
1.1.1.2 root 104: warn("Human68k executable \"%s\" fstat failed", human68k_file);
1.1 root 105: return false;
106: }
107: uint32 file_size;
108: file_size = (uint32)st.st_size;
109: putmsg(1, "file_size=%08x", file_size);
110:
111: // mmap
112: file = (uint8 *)mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, file_fd, 0);
113: if (file == MAP_FAILED) {
1.1.1.2 root 114: warn("Human68k executable \"%s\" mmap failed", human68k_file);
1.1 root 115: return false;
116: }
117:
1.1.1.13 root 118: // 拡張子判別。
1.1 root 119: if (isext(human68k_file, ".r")) {
1.1.1.13 root 120: if (LoadR(file, file_size) == false) {
121: goto abort;
122: }
1.1 root 123: } else if (isext(human68k_file, ".x")) {
1.1.1.13 root 124: if (LoadX(file, file_size) == false) {
125: goto abort;
126: }
1.1 root 127: } else if (isext(human68k_file, ".z")) {
1.1.1.13 root 128: if (LoadZ(file, file_size) == false) {
129: goto abort;
130: }
1.1 root 131: } else {
1.1.1.13 root 132: // ELF .o のロードを試行する。
133: if (LoadELF(file, file_size) == false) {
1.1.1.7 root 134: if (file[0] == 0x48 && file[1] == 0x55) {
1.1.1.13 root 135: if (LoadX(file, file_size) == false) {
136: goto abort;
137: }
1.1.1.7 root 138: } else if (file[0] == 0x60 && file[1] == 0x1a) {
1.1.1.13 root 139: if (LoadZ(file, file_size) == false) {
140: goto abort;
141: }
1.1.1.7 root 142: } else {
1.1.1.13 root 143: errno = ENOEXEC;
144: warn("Human68k executable \"%s\"", human68k_file);
145: goto abort;
1.1.1.7 root 146: }
1.1 root 147: }
148: }
149: munmap(file, file_size);
150:
1.1.1.13 root 151: last_addr = load_addr + load_size + bss_size;
152: ram_size = mainram->GetSize();
1.1 root 153:
154: boot_addr = 0x200;
1.1.1.13 root 155: ds.Write4(0, ram_size - 4); // 初期 SSP
156: ds.Write4(4, boot_addr);
1.1 root 157:
158: // TRAP #15 ベクタを書き込む。
1.1.1.13 root 159: ds.Write4(0xbc, 0x1000);
1.1.1.14! root 160: // TRAP #15 (IOCS コール) ハンドラを書き込む。
! 161: // オレオレ F ライン命令 (エミュレータ内部命令) で処理して RTE するだけ。
! 162: ds.Write2(0x1000, 0xfa00);
1.1.1.13 root 163: ds.Write2(0x1002, 0x4e73); // rte
1.1 root 164:
1.1.1.11 root 165: // STOP による停止。終了時に CPU を止めておくため。
1.1.1.13 root 166: ds.Write4(0x1010, 0x4e722700); // stop #$2700
1.1.1.11 root 167:
1.1 root 168: // コマンドライン文字列を書き込む。LASCII 形式
1.1.1.13 root 169: ds.SetOffset(0xc001);
1.1 root 170: for (int i = 0; i < 255; i++) {
171: uint8 c = human68k_arg[i];
1.1.1.13 root 172: ds.Write1(c);
1.1 root 173: if (c == '\0') {
1.1.1.13 root 174: ds.Write1(0xc000, i);
1.1 root 175: break;
176: }
177: }
1.1.1.8 root 178: // XXX: 255文字以上のときどうなるか調べること
1.1 root 179:
180: // PSP (process entry) を書き込む。
1.1.1.13 root 181: // instructiontest.x はコマンドラインを指定するだけで動くようだ。
1.1 root 182: // PSP はロードアドレス-256 の位置でなければならないようだ。
183: psp_base = 0x1ff00;
1.1.1.13 root 184: ds.SetOffset(psp_base);
1.1 root 185:
1.1.1.13 root 186: ds.Write4(-1); // 1つ前のメモリ管理ポインタ
187: ds.Write4(-1); // このメモリを確保したプロセスのメモリ管理ポインタ
188: ds.Write4(ram_size - 4 + 1); // このメモリブロックの終わり+1 のアドレス
189: ds.Write4(-1); // 次のメモリ管理ポインタ
190:
191: ds.Write4(psp_base + 0x20, 0xc000); // コマンドライン
192:
193: ds.SetOffset(boot_addr);
194: ds.Write2(0x2c4f); // move.l a7,a6
195: ds.Write2(0x2e7c); // move.l psp_base,a7
196: ds.Write4(psp_base);
197: ds.Write2(0x4e66); // move.l a6,usp
198: ds.Write2(0x207c); // move.l psp_base,a0
199: ds.Write4(psp_base);
200: ds.Write2(0x227c); // move.l last_addr,a1
201: ds.Write4(last_addr);
202: ds.Write2(0x247c); // move.l #$c000,a2
203: ds.Write4(0xc000);
204: ds.Write2(0x267c); // move.l #$e000,a3
205: ds.Write4(0xe000);
206: ds.Write2(0x287c); // move.l exec_addr,a4
207: ds.Write4(exec_addr);
208: ds.Write2(0x2c49); // move.l a1,a6
209: ds.Write2(0x46fc); // move.w #$0700,sr
210: ds.Write2(0x0700);
211: ds.Write2(0x4eb9); // jsr.l exec_addr
212: ds.Write4(exec_addr);
213: ds.Write2(0xff00); // DOS _EXIT
1.1 root 214:
215: putmsg(1, "LoadFile complete");
216: return true;
1.1.1.13 root 217:
218: abort:
219: munmap(file, file_size);
220: return false;
1.1 root 221: }
222:
1.1.1.13 root 223: // R 形式ファイルをロードする。
224: // 成功すれば true を返す。失敗すればエラーメッセージを表示して false を返す。
1.1 root 225: bool
1.1.1.13 root 226: Human68kDevice::LoadR(const uint8 *file, uint size)
1.1 root 227: {
1.1.1.13 root 228: // リロケータブルなのでどこでもいい。
1.1 root 229: load_addr = default_load_addr;
230: load_size = size;
1.1.1.13 root 231: if (LoadMem(load_addr, &file[0], load_size) == false) {
232: return false;
233: }
1.1 root 234:
1.1.1.13 root 235: // R 形式はファイル先頭が実行開始位置。
1.1 root 236: exec_addr = load_addr;
237: text_size = load_size;
238: putmsg(1, "r format");
239: return true;
240: }
241:
1.1.1.13 root 242: // X 形式ファイルをロードする。
243: // 成功すれば true を返す。失敗すればエラーメッセージを表示して false を返す。
1.1 root 244: bool
1.1.1.13 root 245: Human68kDevice::LoadX(const uint8 *file, uint size)
1.1 root 246: {
1.1.1.13 root 247: auto *hdr = reinterpret_cast<const XFileHeader *>(file);
1.1 root 248: uint hdr_size = sizeof(*hdr);
249:
250: if (!(hdr->magic[0] == 'H' && hdr->magic[1] == 'U')) {
1.1.1.13 root 251: warnx("Human68k executable \"%s\" invalid magic", human68k_file);
252: return false;
1.1 root 253: }
254: base_addr = be32toh(hdr->base_addr);
255: exec_addr = be32toh(hdr->exec_addr);
256: text_size = be32toh(hdr->text_size);
257: data_size = be32toh(hdr->data_size);
258: bss_size = be32toh(hdr->bss_size);
259:
260: uint32 reloc_size = be32toh(hdr->reloc_size);
261:
1.1.1.13 root 262: // 最適配置位置も可能だが今回見送り。
1.1 root 263: load_addr = default_load_addr;
264: load_size = text_size + data_size;
1.1.1.13 root 265: if (LoadMem(load_addr, &file[hdr_size], load_size) == false) {
266: return false;
267: }
1.1 root 268:
1.1.1.13 root 269: // 再配置テーブルの file での位置。
1.1 root 270: uint32 reloc_pos = hdr_size + load_size;
271:
1.1.1.13 root 272: // 再配置。
273: IODeviceStream ds(mainram);
1.1 root 274: uint32 offset = load_addr - base_addr;
275: uint32 reloc_end = reloc_pos + reloc_size;
276: uint32 A = load_addr;
277: uint32 B = base_addr;
278: uint32 C = A - B;
279: while (reloc_pos < reloc_end) {
1.1.1.13 root 280: putmsg(3, "reloc_pos=%08x reloc_end=%08x", reloc_pos, reloc_end);
1.1 root 281: uint32 D;
1.1.1.13 root 282: D = be16toh(*(const uint16 *)&file[reloc_pos]);
283: putmsg(3, "D=%x", D);
1.1 root 284: reloc_pos += 2;
285: if (D == 1) {
1.1.1.13 root 286: D = be32toh(*(const uint32 *)&file[reloc_pos]);
287: putmsg(3, " odd, D=%x", D);
1.1 root 288: reloc_pos += 4;
289: }
290: if ((D & 1) == 0) {
291: A += D;
1.1.1.13 root 292: uint32 old = ds.Read4(A);
293: ds.Write4(A, old + C);
294: putmsg(3, " Write_L A=%x, old=%x new=%x", A, old, old + C);
1.1 root 295: } else {
296: A += D - 1;
1.1.1.13 root 297: uint32 old = ds.Read2(A);
298: ds.Write2(A, old + C);
299: putmsg(3, " Write_W A=%x, old=%x new=%x", A, old, old + C);
1.1 root 300: }
301: }
302:
303: exec_addr += offset;
304:
305: putmsg(1, "x format, base_addr=%08x, exec_addr=%08x", base_addr, exec_addr);
306:
307: return true;
308: }
309:
1.1.1.13 root 310: // Z 形式ファイルをロードする。
311: // 成功すれば true を返す。失敗すればエラーメッセージを表示して false を返す。
1.1 root 312: bool
1.1.1.13 root 313: Human68kDevice::LoadZ(const uint8 *file, uint size)
1.1 root 314: {
1.1.1.13 root 315: auto *hdr = reinterpret_cast<const ZFileHeader *>(file);
1.1 root 316: uint32 hdr_size = sizeof(*hdr);
317:
318: if (be16toh(hdr->magic1) != 0x601a) {
1.1.1.13 root 319: warnx("Human68k executable \"%s\" invalid magic", human68k_file);
320: return false;
1.1 root 321: }
322: text_size = be32toh(hdr->text_size);
323: data_size = be32toh(hdr->data_size);
324: bss_size = be32toh(hdr->bss_size);
325: base_addr = be32toh(hdr->base_addr);
326: exec_addr = base_addr;
327:
328: if (base_addr < 0x20000) {
1.1.1.13 root 329: warnx("Human68k executable \"%s\" unsupported base specified",
330: human68k_file);
331: return false;
1.1 root 332: }
333:
334: load_addr = base_addr;
335: load_size = text_size + data_size;
1.1.1.13 root 336: if (LoadMem(load_addr, &file[hdr_size], load_size) == false) {
337: return false;
338: }
1.1 root 339:
340: putmsg(1, "z format, base_addr=%08x, exec_addr=%08x", base_addr, exec_addr);
341: return true;
342: }
343:
1.1.1.13 root 344: // なぜか m68k ELF .o が読める。
345: // 成功すれば true を返す。失敗すればエラーメッセージを表示して false を返す。
1.1.1.7 root 346: bool
1.1.1.13 root 347: Human68kDevice::LoadELF(const uint8 *file, uint size)
1.1.1.7 root 348: {
1.1.1.11 root 349: LoadInfo info(human68k_file, file, size);
1.1.1.13 root 350: if (mainram->LoadExec(&info) == false) {
1.1.1.7 root 351: return false;
352: }
353:
1.1.1.11 root 354: // XXX load_size は esym のような気がする。
355: // XXX text_size も今はやろうと思えば返せる構造になったが未対応。
356: load_addr = info.entry;
1.1.1.7 root 357: load_size = size;
1.1.1.11 root 358: exec_addr = info.entry;
1.1.1.7 root 359: text_size = size;
360: return true;
361: }
362:
1.1.1.13 root 363: // src から size バイトを RAM の addr 以降にロードする。
364: // 成功すれば true を返す。
365: // ファイルが RAM に収まらない場合はエラーメッセージを表示して false を返す。
1.1 root 366: bool
1.1.1.13 root 367: Human68kDevice::LoadMem(uint32 addr, const uint8 *src, uint size)
1.1 root 368: {
1.1.1.13 root 369: if (addr + size >= mainram->GetSize()) {
370: warnx("Human68k executable \"%s\": file too large", human68k_file);
371: return false;
1.1 root 372: }
373:
1.1.1.13 root 374: mainram->WriteMem(addr, src, size);
1.1 root 375: return true;
376: }
377:
378: // file の拡張子が ext なら true を返す。ext は '.' を含む。
379: bool
1.1.1.12 root 380: Human68kDevice::isext(const char *file, const char *ext)
1.1 root 381: {
382: size_t len_file = strlen(file);
383: size_t len_ext = strlen(ext);
1.1.1.13 root 384: if (len_ext == 0) {
1.1 root 385: return false;
386: }
387: if (len_file < len_ext) {
388: return false;
389: }
390:
391: return strcasecmp(&file[len_file - len_ext], ext) == 0;
392: }
393:
394:
1.1.1.7 root 395: // 仮想マシンの中から終了させる
1.1.1.13 root 396: // (MSXDOSDevice からも終了時に呼ぶ)。
1.1.1.11 root 397: // XXX code は未対応。
398: /*static*/ void
1.1.1.12 root 399: Human68kDevice::RequestExit(int code)
1.1.1.7 root 400: {
1.1.1.11 root 401: // 電源を即切る
402: auto power = GetPowerDevice();
1.1.1.12 root 403: power->PushPowerButton();
1.1.1.11 root 404: power->SetSystemPowerOn(false);
1.1.1.7 root 405: }
406:
1.1.1.13 root 407: // F-Line 命令をこちらで処理する。
1.1 root 408: bool
1.1.1.11 root 409: human68k_fline_callback(MPU680x0Device *, void *arg)
1.1 root 410: {
1.1.1.13 root 411: auto *human68k = reinterpret_cast<Human68kDevice *>(arg);
1.1.1.11 root 412: return human68k->FLineOp();
1.1 root 413: }
414:
1.1.1.13 root 415: // F-Line 命令。
1.1 root 416: bool
1.1.1.12 root 417: Human68kDevice::FLineOp()
1.1 root 418: {
1.1.1.12 root 419: switch (mpu680x0->GetIR()) {
1.1.1.14! root 420: case 0xfa00: // IOCS call emulation
1.1.1.11 root 421: IOCS();
1.1 root 422: break;
423:
1.1.1.13 root 424: case 0xff00: // _EXIT
425: putlog(1, "DOS _EXIT");
1.1.1.11 root 426: // XXX CPU の実行を止める。方式自体を I/O 方式に見直したときには
427: // ちゃんとした方法を取る予定。
1.1.1.12 root 428: mpu680x0->SetSR(mpu680x0->GetSR() | 0x2000);
429: mpu680x0->reg.pc = 0x1010;
1.1.1.7 root 430: RequestExit(0);
431: break;
1.1 root 432:
1.1.1.13 root 433: case 0xff09: // _PRINT
1.1 root 434: {
1.1.1.13 root 435: uint32 dataptr = mainbus->HVRead4(RegA(7));
436: putlog(2, "DOS _PRINT($%06x)", dataptr);
1.1.1.11 root 437: DOS_PRINT(dataptr);
1.1 root 438: break;
439: }
440:
1.1.1.13 root 441: case 0xff1e: // _FPUTS
1.1 root 442: {
1.1.1.13 root 443: uint32 mesptr = mainbus->HVRead4(RegA(7));
444: uint16 fileno = mainbus->HVRead2(RegA(7) + 4);
445: putlog(2, "DOS _FPUTS(fileno=%d, mesptr=$%06x)", fileno, mesptr);
1.1 root 446: FputsFile(fileno, mesptr);
447: break;
448: }
449:
1.1.1.13 root 450: case 0xff20: // _SUPER
1.1 root 451: {
1.1.1.13 root 452: uint32 data = mainbus->HVRead4(RegA(7));
1.1 root 453:
1.1.1.13 root 454: putlog(1, "DOS _SUPER($%x) to %s", data,
455: (data == 0) ? "SUPERVISOR" : "USER");
1.1 root 456: if (data == 0) {
1.1.1.12 root 457: mpu680x0->SetSR(mpu680x0->GetSR() | 0x2000);
458: data = mpu680x0->reg.usp;
1.1 root 459: RegD(0) = RegA(7);
460: RegA(7) = data;
461: } else {
1.1.1.12 root 462: mpu680x0->SetSR(mpu680x0->GetSR() | 0x2000);
1.1 root 463: RegA(7) = data;
1.1.1.12 root 464: mpu680x0->SetSR(mpu680x0->GetSR() & ~0x2000);
1.1 root 465: }
466: break;
467: }
468:
1.1.1.13 root 469: case 0xff25: // _INTVCS
1.1 root 470: {
1.1.1.13 root 471: uint16 intno = mainbus->HVRead2(RegA(7));
472: uint32 addr = mainbus->HVRead4(RegA(7) + 2);
473: putlog(1, "DOS _INTVCS(intno=$%02x, addr=$%06x)", intno, addr);
1.1 root 474:
475: uint32 vecaddr = (intno & 0xff) * 4;
476:
477: if (intno <= 0xff) {
1.1.1.13 root 478: RegD(0) = mainbus->HVRead4(vecaddr);
479: mainbus->HVWrite4(vecaddr, addr);
1.1 root 480: } else {
481: vecaddr += 0xd000;
1.1.1.13 root 482: RegD(0) = mainbus->HVRead4(vecaddr);
483: mainbus->HVWrite4(vecaddr, addr);
1.1.1.6 root 484: }
1.1 root 485: break;
486: }
487:
1.1.1.13 root 488: case 0xff27: // _GETTIM2
1.1 root 489: {
1.1.1.13 root 490: struct tm tm;
491: time_t now = time(NULL);
492: localtime_r(&now, &tm);
493: RegD(0) = (tm.tm_hour << 16)
494: | (tm.tm_min << 8)
495: | tm.tm_sec;
496: putlog(1, "DOS _GETTIM2 -> $%08x", RegD(0));
1.1 root 497: break;
498: }
499:
1.1.1.13 root 500: case 0xff2a: // _GETDATE
1.1 root 501: {
1.1.1.13 root 502: struct tm tm;
503: time_t now = time(NULL);
504: localtime_r(&now, &tm);
505: RegD(0) = (tm.tm_wday << 16)
506: | ((tm.tm_year + 1900 - 1980) << 9)
507: | ((tm.tm_mon + 1) << 5)
508: | tm.tm_mday;
509: putlog(1, "DOS _GETDATE -> $%08x", RegD(0));
1.1 root 510: break;
511: }
512:
1.1.1.13 root 513: case 0xff30: // _VERNUM
1.1 root 514: {
515: RegD(0) = 0x36380302; // ver3.02
1.1.1.13 root 516: putlog(1, "DOS _VERNUM -> $%08x", RegD(0));
1.1 root 517: break;
518: }
519:
1.1.1.13 root 520: case 0xff35: // _INTVCG
1.1 root 521: {
1.1.1.13 root 522: uint16 intno = mainbus->HVRead2(RegA(7));
1.1 root 523: uint32 vecaddr = (intno & 0xff) * 4;
524:
525: if (intno <= 0xff) {
1.1.1.13 root 526: RegD(0) = mainbus->HVRead4(vecaddr);
1.1 root 527: } else {
528: vecaddr += 0xd000;
1.1.1.13 root 529: RegD(0) = mainbus->HVRead4(vecaddr);
1.1 root 530: }
1.1.1.13 root 531: putlog(1, "DOS _INTVCG(intno=%d) -> $%08x", intno, RegD(0));
1.1 root 532: break;
533: }
534:
1.1.1.13 root 535: case 0xff3c: // _CREATE
1.1 root 536: {
1.1.1.13 root 537: uint32 file = mainbus->HVRead4(RegA(7));
538: uint16 atr = mainbus->HVRead2(RegA(7) + 4);
539: putlog(1, "DOS _CREATE(file=$%x, atr=$%x)", file, atr);
1.1 root 540:
541: int32 fileno = OpenFile(file, atr,
542: O_CREAT | O_TRUNC | O_RDWR | O_SYNC);
543: RegD(0) = fileno;
544: break;
545: }
546:
1.1.1.13 root 547: case 0xff3d: // _OPEN
1.1 root 548: {
1.1.1.13 root 549: uint32 file = mainbus->HVRead4(RegA(7));
550: uint16 mode = mainbus->HVRead2(RegA(7) + 4);
551: int unix_mode = 0;
552: bool ok = true;
553: putlog(1, "DOS _OPEN(file=$%x, mode=$%x)", file, mode);
1.1 root 554:
1.1.1.13 root 555: // mode の辞書モード、シェアリングモードは無視している。
556: switch (mode & 0x03) { // read/write
557: case 2:
558: unix_mode |= O_RDWR;
559: break;
560: case 1:
561: unix_mode |= O_WRONLY;
562: break;
563: case 0:
564: unix_mode |= O_RDONLY;
565: break;
566: default:
567: RegD(0) = -12; // アクセスモード異常
568: ok = false;
569: break;
570: }
571:
572: if (ok) {
573: int32 fileno = OpenFile(file, 0, unix_mode | O_SYNC);
574: RegD(0) = fileno;
575: }
576: break;
577: }
578:
579: case 0xff3e: // _CLOSE
580: {
581: uint16 fileno = mainbus->HVRead2(RegA(7));
582:
583: putlog(1, "DOS _CLOSE(fileno=%d)", fileno);
1.1 root 584: CloseFile(fileno);
585: RegD(0) = 0;
586: break;
587: }
588:
1.1.1.13 root 589: case 0xff40: // _WRITE
1.1 root 590: {
1.1.1.13 root 591: uint16 fileno = mainbus->HVRead2(RegA(7));
592: uint32 dataptr = mainbus->HVRead4(RegA(7) + 2);
593: uint32 size = mainbus->HVRead4(RegA(7) + 6);
594: putlog(2, "DOS _WRITE(fileno=%u, dataptr=$%06x, size=$%08x)",
595: fileno, dataptr, size);
1.1 root 596: RegD(0) = WriteFile(fileno, dataptr, size);
597: break;
598: }
599:
1.1.1.13 root 600: case 0xff44: // _IOCTRL
1.1 root 601: {
1.1.1.13 root 602: uint32 mode = mainbus->HVRead2(RegA(7));
1.1 root 603: uint32 fileno;
604: switch (mode) {
1.1.1.13 root 605: case 0: // IOCTRLGT 装置情報取得
606: fileno = mainbus->HVRead2(RegA(7) + 2);
1.1 root 607: if (fileno == 0) { // STDIN
608: RegD(0) = 0x8081; // 100u'uuuu'100u'0001;
609: } else if (fileno == 1) { // STDOUT
610: RegD(0) = 0x8082; // 100u'uuuu'100u'0010;
611: } else if (fileno == 2) { // STDERR
612: RegD(0) = 0x8081; // 100u'uuuu'100u'0001;
613: } else {
614: RegD(0) = -1;
615: }
1.1.1.13 root 616: putlog(1, "DOS _IOCTRL(mode=0(IOCTRLGT), fileno=%d) -> $%x",
617: fileno, RegD(0));
618: break;
619:
620: case 7: // IOCTRLOS 出力ステータス取得
621: fileno = mainbus->HVRead2(RegA(7) + 2);
622: if (fileno == 0) {
623: RegD(0) = 0;
624: } else if (fileno == 1 || fileno == 2) {
625: RegD(0) = -1;
626: } else {
627: int filemode = GetFileMode(fileno);
628: if (filemode < 0) {
629: RegD(0) = filemode;
630: } else if (filemode == O_WRONLY || filemode == O_RDWR) {
631: RegD(0) = -1;
632: } else {
633: RegD(0) = 0;
634: }
635: }
636: putlog(1, "DOS _IOCTRL(mode=7(IOCTRLOS), fileno=%d) -> %d",
637: fileno, RegD(0));
1.1 root 638: break;
1.1.1.13 root 639:
1.1 root 640: default:
1.1.1.13 root 641: errx(EXIT_FAILURE, "DOS _IOCTRL(mode=%d) not implemented", mode);
1.1 root 642: }
643: break;
644: }
645:
1.1.1.13 root 646: case 0xff4a: // _SETBLOCK
1.1 root 647: {
1.1.1.13 root 648: uint32 memptr = mainbus->HVRead4(RegA(7));
649: uint32 len = mainbus->HVRead4(RegA(7) + 4);
650: putlog(1, "DOS _SETBLOCK(memptr=$%x, len=$%x)", memptr, len);
1.1 root 651: // なにもせずに、できたという
1.1.1.13 root 652: RegD(0) = mainbus->HVRead4(RegA(7) + 4);
1.1 root 653: break;
654: }
655:
1.1.1.13 root 656: case 0xff4c: // _EXIT2
1.1 root 657: {
1.1.1.13 root 658: uint32 data = mainbus->HVRead2(RegA(7));
659: putlog(1, "DOS _EXIT2(%d)", data);
1.1.1.7 root 660: RequestExit(data);
661: break;
1.1 root 662: }
663:
1.1.1.13 root 664: case 0xffac: // _GETFCB
1.1 root 665: {
1.1.1.13 root 666: uint32 fileno = mainbus->HVRead2(RegA(7));
667: putlog(1, "DOS _GETFCB(fileno=%d)", fileno);
1.1 root 668: if (fileno <= 4) {
669: RegD(0) = 0x8000 + fileno * 0x60;
670: } else {
671: RegD(0) = -1;
672: }
673: break;
674: }
675:
1.1.1.13 root 676: case 0xfff7: // _BUS_ERR
1.1 root 677: {
1.1.1.13 root 678: uint32 p1 = mainbus->HVRead4(RegA(7));
679: uint32 p2 = mainbus->HVRead4(RegA(7) + 4);
680: uint16 size = mainbus->HVRead2(RegA(7) + 8);
681: putlog(1, "DOS _BUS_ERR(size=%d p1=$%x p2=$%x)", size, p1, p2);
1.1 root 682:
683: RegD(0) = -1;
684: uint32 data;
685: try {
686: if (size == 1) {
1.1.1.13 root 687: data = mainbus->HVRead1(p1);
1.1 root 688: } else if (size == 2 && ((p1 & 1) == 0)) {
1.1.1.13 root 689: data = mainbus->HVRead2(p1);
1.1 root 690: } else if (size == 4 && ((p1 & 1) == 0)) {
1.1.1.13 root 691: data = mainbus->HVRead4(p1);
1.1 root 692: } else {
693: break;
694: }
695: } catch (int cause) {
1.1.1.11 root 696: if (cause == M68K::EXCEP_BUSERR) {
1.1 root 697: RegD(0) = 2;
698: break;
699: } else {
700: throw;
701: }
702: }
703:
704: try {
705: if (size == 1) {
1.1.1.13 root 706: mainbus->HVWrite1(p2, data);
1.1 root 707: } else if (size == 2 && ((p2 & 1) == 0)) {
1.1.1.13 root 708: mainbus->HVWrite2(p2, data);
1.1 root 709: } else if (size == 4 && ((p2 & 1) == 0)) {
1.1.1.13 root 710: mainbus->HVWrite4(p2, data);
1.1 root 711: } else {
712: break;
713: }
714: } catch (int cause) {
1.1.1.11 root 715: if (cause == M68K::EXCEP_BUSERR) {
1.1 root 716: RegD(0) = 1;
717: break;
718: } else {
719: throw;
720: }
721: }
722: RegD(0) = 0;
723: break;
724: }
725:
1.1.1.13 root 726: case 0xffff: // _CHANGE_PR
727: {
728: putlog(1, "DOS _CHANGE_PR");
729: // なにもしない。
730: break;
731: }
732:
1.1 root 733: default:
1.1.1.11 root 734: printf("DOSCALL $%02x at $%08X (NOT IMPLEMENTED)\n",
1.1.1.12 root 735: mpu680x0->GetIR(), mpu680x0->GetPPC());
1.1.1.7 root 736: RequestExit(1);
737: break;
1.1 root 738: }
739: return true;
740: }
1.1.1.11 root 741:
742: void
1.1.1.13 root 743: Human68kDevice::IOCS()
744: {
745: // XXX 本当は HVRead*() の IsBusErr() を調べないといけない。
746:
747: // 呼び出し元っぽい PC を表示したい。
748: uint32 pc = mainbus->HVRead4(RegA(7) + 2) - 2;
749: if ((mainbus->HVRead2(pc - 2) & 0xff00) == 0x7000) {
750: // moveq #imm, d0
751: pc -= 2;
752: }
753:
754: switch (RegD(0) & 0xff) {
755: case 0x21: // _B_PRINT
756: {
757: uint32 dataptr = RegA(1);
758: putlog(2, "PC=$%06x IOCS _B_PRINT($%06x)", pc, dataptr);
759: DOS_PRINT(dataptr);
760: // 戻り値 d0 は表示後の位置だが無視。
761: break;
762: }
763: case 0x7f: // _ONTIME
764: {
765: putlog(1, "PC=$%06x IOCS _ONTIME", pc);
766: uint64 t = scheduler->GetVirtTime();
767: // nanosec to 10msec, in day
768: RegD(0) = (t / 10_msec) % 8640000;
769: // nanosec to day
770: RegD(1) = t / 86400_sec;
771: break;
772: }
773: case 0x82: // _B_BPEEK
774: {
775: uint32 data = mainbus->HVRead1(RegA(1));
776: putlog(1, "PC=$%06x IOCS _B_BPEEK($%06x) -> $%02x", pc, RegA(1), data);
777: RegD(0) = (RegD(0) & 0xffffff00) | data;
778: RegA(1)++;
779: break;
780: }
781: case 0xac: // _SYS_STAT (ROM1.3)
782: switch (RegD(1)) {
783: case 0:
784: // MPU 状態の取得
785: RegD(0) =
786: ((250) << 16) // 25.0MHz
1.1.1.14! root 787: | ((mpu680x0->HasFPU() ? 1 : 0) << 15) // FPU
1.1.1.13 root 788: | ((0) << 14) // MMU
789: | ((3) << 0); // MPU Type
790: break;
791: case 1:
792: // キャッシュ状態の取得
793: RegD(0) = 0;
794: break;
795: case 2:
796: // キャッシュを SRAM の設定値に設定
797: RegD(0) = 0;
798: break;
799: case 3:
800: // キャッシュの消去
801: RegD(0) = 0;
802: break;
803: case 4:
804: // キャッシュの設定
805: RegD(0) = 0;
806: break;
807: default:
808: break;
809: }
810: putlog(1, "PC=$%06x IOCS _SYS_STAT(%x) -> $%x", pc, RegD(1), RegD(0));
811: break;
812:
813: default:
1.1.1.14! root 814: printf("IOCS $%02x at $%06x (NOT IMPLEMENTED)\n",
! 815: RegD(0) & 0xff, pc);
1.1.1.13 root 816: RequestExit(1);
817: break;
818: }
819: }
820:
821: void
1.1.1.12 root 822: Human68kDevice::DOS_PRINT(uint32 dataptr)
1.1.1.11 root 823: {
824: int c;
1.1.1.13 root 825: while ((c = mainbus->HVRead1(dataptr++)) != 0) {
826: putchar(c);
1.1.1.11 root 827: }
828: RegD(0) = 0;
829: }
1.1.1.13 root 830:
831: // ファイルをオープンする。
832: // fileaddr: Human68k ファイル名のゲストアドレス
833: // atr: Human68k の属性
834: // mode: unix open mode
835: // 戻り値は Human68k fileno。
836: int32
837: Human68kDevice::OpenFile(uint32 fileaddr, uint16 atr, int mode)
838: {
839: int32 fileno {};
840: Human68kDevice::File *f = NULL;
841:
842: // 空いてるエントリを検索して Human fileno を取得。
843: for (int i = 0; i < FilesCount; i++) {
844: if (Files[i].fd == -1) {
845: fileno = i;
846: f = &Files[fileno];
847: break;
848: }
849: }
850: if (f == NULL) {
851: return -1;
852: }
853:
854: // ファイル名変換。
855: std::string filename;
856: for (uint8 c; (c = mainbus->HVRead1(fileaddr++)) != '\0'; ) {
857: if (c < 32 || c >= 127 || c == 0x5c) {
858: char hex[16];
859: snprintf(hex, sizeof(hex), "%02X", c);
860: filename += std::string(hex);
861: } else {
862: filename += c;
863: }
864: }
865:
866: int fd = open(filename.c_str(), mode, 0666);
867: if (fd == -1) {
868: putmsg(1, "OpenFile: %s: %s", filename.c_str(), strerror(errno));
869: return -1;
870: }
871:
872: f->fd = fd;
873: f->filename = filename;
874:
875: putmsg(1, "OpenFile: name=\"%s\" mode=$%x -> fileno=%u",
876: filename.c_str(), mode, fileno);
877: return fileno;
878: }
879:
880: // ファイルをクローズする。
881: // fileno: Human68k fileno
882: int32
883: Human68kDevice::CloseFile(int32 fileno)
884: {
885: Human68kDevice::File *f;
886:
887: if (fileno <= 0 || fileno >= FilesCount) {
888: putmsg(1, "CloseFile: fileno=%d is not opened", fileno);
889: return -1;
890: }
891: f = &Files[fileno];
892: putmsg(1, "CloseFile: %d \"%s\"", fileno, f->filename.c_str());
893: if (f->fd > 2) {
894: close(f->fd);
895: }
896: f->fd = -1;
897: f->filename.clear();
898: return 0;
899: }
900:
901: // ファイルに書き出す。
902: // fileno: Human68k fileno
903: // dataaddr: データのゲストアドレス
904: // size: データ長
905: int32
906: Human68kDevice::WriteFile(int32 fileno, uint32 dataaddr, uint32 size)
907: {
908: Human68kDevice::File *f;
909:
910: if (fileno <= 0 || fileno >= FilesCount) {
911: putmsg(1, "WriteFile: fileno=%d is not opened", fileno);
912: return -1;
913: }
914:
915: f = &Files[fileno];
916: if (f->fd < 0) {
917: return -1;
918: }
919:
920: std::vector<uint8> buf(size);
921: mainram->ReadMem(dataaddr, buf.data(), size);
922:
923: int32 rv = write(f->fd, buf.data(), size);
924: return rv;
925: }
926:
927: // 文字列をファイルに書き出す。
928: // fileno: Human68k fileno
929: // dataaddr: data addr (guest VA)
930: void
931: Human68kDevice::FputsFile(int32 fileno, uint32 dataaddr)
932: {
933: Human68kDevice::File *f;
934:
935: if (fileno <= 0 || fileno >= FilesCount) {
936: putmsg(1, "FputsFile: fileno=%d is not opened", fileno);
937: return;
938: }
939:
940: f = &Files[fileno];
941: if (f->fd < 0) {
942: return;
943: }
944:
945: std::vector<uint8> buf;
946: for (uint8 c; (c = mainbus->HVRead1(dataaddr++)) != '\0'; ) {
947: buf.push_back(c);
948: }
949: if (buf.empty() == false) {
950: write(f->fd, buf.data(), buf.size());
951: }
952: }
953:
954: // ファイルモードを取得する。
955: // fileno: Human68k fileno
956: // 戻り値: <0 Humak68k error code
957: // : >=0 unix file mode
958: int
959: Human68kDevice::GetFileMode(int32 fileno)
960: {
961: Human68kDevice::File *f;
962:
963: if (fileno <= 0 || fileno >= FilesCount) {
964: return -2 /* FILE_NOT_FOUND */;
965: }
966:
967: f = &Files[fileno];
968: if (f->fd < 0) {
969: return -2 /* FILE_NOT_FOUND */;
970: }
971:
972: int fl = fcntl(f->fd, F_GETFL);
973: int accmode = fl & O_ACCMODE;
974:
975: return accmode;
976: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.