|
|
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.12 root 89: if (mpu680x0->HaveFPU()) {
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 root 160: // TRAP #15 ハンドラを書き込む。
161: // エミュレータ内部命令 $f300 で処理して RTE するだけ。
1.1.1.13! root 162: ds.Write2(0x1000, 0xf600); // .dw $f300
! 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 root 420: case 0xf600: // 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
! 787: | ((mpu680x0->HaveFPU() ? 1 : 0) << 15) // FPU
! 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:
! 814: putlog(0, "PC=$%06x IOCS $%02x (NOT IMPLEMENTED)", pc, RegD(0) & 0xff);
! 815: RequestExit(1);
! 816: break;
! 817: }
! 818: }
! 819:
! 820: void
1.1.1.12 root 821: Human68kDevice::DOS_PRINT(uint32 dataptr)
1.1.1.11 root 822: {
823: int c;
1.1.1.13! root 824: while ((c = mainbus->HVRead1(dataptr++)) != 0) {
! 825: putchar(c);
1.1.1.11 root 826: }
827: RegD(0) = 0;
828: }
1.1.1.13! root 829:
! 830: // ファイルをオープンする。
! 831: // fileaddr: Human68k ファイル名のゲストアドレス
! 832: // atr: Human68k の属性
! 833: // mode: unix open mode
! 834: // 戻り値は Human68k fileno。
! 835: int32
! 836: Human68kDevice::OpenFile(uint32 fileaddr, uint16 atr, int mode)
! 837: {
! 838: int32 fileno {};
! 839: Human68kDevice::File *f = NULL;
! 840:
! 841: // 空いてるエントリを検索して Human fileno を取得。
! 842: for (int i = 0; i < FilesCount; i++) {
! 843: if (Files[i].fd == -1) {
! 844: fileno = i;
! 845: f = &Files[fileno];
! 846: break;
! 847: }
! 848: }
! 849: if (f == NULL) {
! 850: return -1;
! 851: }
! 852:
! 853: // ファイル名変換。
! 854: std::string filename;
! 855: for (uint8 c; (c = mainbus->HVRead1(fileaddr++)) != '\0'; ) {
! 856: if (c < 32 || c >= 127 || c == 0x5c) {
! 857: char hex[16];
! 858: snprintf(hex, sizeof(hex), "%02X", c);
! 859: filename += std::string(hex);
! 860: } else {
! 861: filename += c;
! 862: }
! 863: }
! 864:
! 865: int fd = open(filename.c_str(), mode, 0666);
! 866: if (fd == -1) {
! 867: putmsg(1, "OpenFile: %s: %s", filename.c_str(), strerror(errno));
! 868: return -1;
! 869: }
! 870:
! 871: f->fd = fd;
! 872: f->filename = filename;
! 873:
! 874: putmsg(1, "OpenFile: name=\"%s\" mode=$%x -> fileno=%u",
! 875: filename.c_str(), mode, fileno);
! 876: return fileno;
! 877: }
! 878:
! 879: // ファイルをクローズする。
! 880: // fileno: Human68k fileno
! 881: int32
! 882: Human68kDevice::CloseFile(int32 fileno)
! 883: {
! 884: Human68kDevice::File *f;
! 885:
! 886: if (fileno <= 0 || fileno >= FilesCount) {
! 887: putmsg(1, "CloseFile: fileno=%d is not opened", fileno);
! 888: return -1;
! 889: }
! 890: f = &Files[fileno];
! 891: putmsg(1, "CloseFile: %d \"%s\"", fileno, f->filename.c_str());
! 892: if (f->fd > 2) {
! 893: close(f->fd);
! 894: }
! 895: f->fd = -1;
! 896: f->filename.clear();
! 897: return 0;
! 898: }
! 899:
! 900: // ファイルに書き出す。
! 901: // fileno: Human68k fileno
! 902: // dataaddr: データのゲストアドレス
! 903: // size: データ長
! 904: int32
! 905: Human68kDevice::WriteFile(int32 fileno, uint32 dataaddr, uint32 size)
! 906: {
! 907: Human68kDevice::File *f;
! 908:
! 909: if (fileno <= 0 || fileno >= FilesCount) {
! 910: putmsg(1, "WriteFile: fileno=%d is not opened", fileno);
! 911: return -1;
! 912: }
! 913:
! 914: f = &Files[fileno];
! 915: if (f->fd < 0) {
! 916: return -1;
! 917: }
! 918:
! 919: std::vector<uint8> buf(size);
! 920: mainram->ReadMem(dataaddr, buf.data(), size);
! 921:
! 922: int32 rv = write(f->fd, buf.data(), size);
! 923: return rv;
! 924: }
! 925:
! 926: // 文字列をファイルに書き出す。
! 927: // fileno: Human68k fileno
! 928: // dataaddr: data addr (guest VA)
! 929: void
! 930: Human68kDevice::FputsFile(int32 fileno, uint32 dataaddr)
! 931: {
! 932: Human68kDevice::File *f;
! 933:
! 934: if (fileno <= 0 || fileno >= FilesCount) {
! 935: putmsg(1, "FputsFile: fileno=%d is not opened", fileno);
! 936: return;
! 937: }
! 938:
! 939: f = &Files[fileno];
! 940: if (f->fd < 0) {
! 941: return;
! 942: }
! 943:
! 944: std::vector<uint8> buf;
! 945: for (uint8 c; (c = mainbus->HVRead1(dataaddr++)) != '\0'; ) {
! 946: buf.push_back(c);
! 947: }
! 948: if (buf.empty() == false) {
! 949: write(f->fd, buf.data(), buf.size());
! 950: }
! 951: }
! 952:
! 953: // ファイルモードを取得する。
! 954: // fileno: Human68k fileno
! 955: // 戻り値: <0 Humak68k error code
! 956: // : >=0 unix file mode
! 957: int
! 958: Human68kDevice::GetFileMode(int32 fileno)
! 959: {
! 960: Human68kDevice::File *f;
! 961:
! 962: if (fileno <= 0 || fileno >= FilesCount) {
! 963: return -2 /* FILE_NOT_FOUND */;
! 964: }
! 965:
! 966: f = &Files[fileno];
! 967: if (f->fd < 0) {
! 968: return -2 /* FILE_NOT_FOUND */;
! 969: }
! 970:
! 971: int fl = fcntl(f->fd, F_GETFL);
! 972: int accmode = fl & O_ACCMODE;
! 973:
! 974: return accmode;
! 975: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.