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