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