|
|
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.18! root 439: case 0xfe11: // FPACK __LTOS
! 440: {
! 441: putlog(1, "FPACK __LTOS");
! 442: uint32 data = RegD(0);
! 443: uint32 dst = RegA(0);
! 444: char buf[16];
! 445: snprintf(buf, sizeof(buf), "%d", data);
! 446: for (char *p = buf; ; p++, dst++) {
! 447: uint8 c = *p;
! 448: mainbus->HVWrite1(dst, c);
! 449: if (c == 0) {
! 450: break;
! 451: }
! 452: }
! 453: // '\0' の位置を A0 に返す。
! 454: RegA(0) = dst;
! 455: break;
! 456: }
! 457:
1.1.1.13 root 458: case 0xff00: // _EXIT
459: putlog(1, "DOS _EXIT");
1.1.1.11 root 460: // XXX CPU の実行を止める。方式自体を I/O 方式に見直したときには
461: // ちゃんとした方法を取る予定。
1.1.1.12 root 462: mpu680x0->SetSR(mpu680x0->GetSR() | 0x2000);
463: mpu680x0->reg.pc = 0x1010;
1.1.1.7 root 464: RequestExit(0);
465: break;
1.1 root 466:
1.1.1.13 root 467: case 0xff09: // _PRINT
1.1 root 468: {
1.1.1.13 root 469: uint32 dataptr = mainbus->HVRead4(RegA(7));
470: putlog(2, "DOS _PRINT($%06x)", dataptr);
1.1.1.11 root 471: DOS_PRINT(dataptr);
1.1 root 472: break;
473: }
474:
1.1.1.13 root 475: case 0xff1e: // _FPUTS
1.1 root 476: {
1.1.1.13 root 477: uint32 mesptr = mainbus->HVRead4(RegA(7));
478: uint16 fileno = mainbus->HVRead2(RegA(7) + 4);
479: putlog(2, "DOS _FPUTS(fileno=%d, mesptr=$%06x)", fileno, mesptr);
1.1 root 480: FputsFile(fileno, mesptr);
481: break;
482: }
483:
1.1.1.13 root 484: case 0xff20: // _SUPER
1.1 root 485: {
1.1.1.13 root 486: uint32 data = mainbus->HVRead4(RegA(7));
1.1 root 487:
1.1.1.13 root 488: putlog(1, "DOS _SUPER($%x) to %s", data,
489: (data == 0) ? "SUPERVISOR" : "USER");
1.1 root 490: if (data == 0) {
1.1.1.12 root 491: mpu680x0->SetSR(mpu680x0->GetSR() | 0x2000);
492: data = mpu680x0->reg.usp;
1.1 root 493: RegD(0) = RegA(7);
494: RegA(7) = data;
495: } else {
1.1.1.12 root 496: mpu680x0->SetSR(mpu680x0->GetSR() | 0x2000);
1.1 root 497: RegA(7) = data;
1.1.1.12 root 498: mpu680x0->SetSR(mpu680x0->GetSR() & ~0x2000);
1.1 root 499: }
500: break;
501: }
502:
1.1.1.13 root 503: case 0xff25: // _INTVCS
1.1 root 504: {
1.1.1.13 root 505: uint16 intno = mainbus->HVRead2(RegA(7));
506: uint32 addr = mainbus->HVRead4(RegA(7) + 2);
507: putlog(1, "DOS _INTVCS(intno=$%02x, addr=$%06x)", intno, addr);
1.1 root 508:
509: uint32 vecaddr = (intno & 0xff) * 4;
510:
511: if (intno <= 0xff) {
1.1.1.13 root 512: RegD(0) = mainbus->HVRead4(vecaddr);
513: mainbus->HVWrite4(vecaddr, addr);
1.1 root 514: } else {
515: vecaddr += 0xd000;
1.1.1.13 root 516: RegD(0) = mainbus->HVRead4(vecaddr);
517: mainbus->HVWrite4(vecaddr, addr);
1.1.1.6 root 518: }
1.1 root 519: break;
520: }
521:
1.1.1.13 root 522: case 0xff27: // _GETTIM2
1.1 root 523: {
1.1.1.13 root 524: struct tm tm;
525: time_t now = time(NULL);
526: localtime_r(&now, &tm);
527: RegD(0) = (tm.tm_hour << 16)
528: | (tm.tm_min << 8)
529: | tm.tm_sec;
530: putlog(1, "DOS _GETTIM2 -> $%08x", RegD(0));
1.1 root 531: break;
532: }
533:
1.1.1.13 root 534: case 0xff2a: // _GETDATE
1.1 root 535: {
1.1.1.13 root 536: struct tm tm;
537: time_t now = time(NULL);
538: localtime_r(&now, &tm);
539: RegD(0) = (tm.tm_wday << 16)
540: | ((tm.tm_year + 1900 - 1980) << 9)
541: | ((tm.tm_mon + 1) << 5)
542: | tm.tm_mday;
543: putlog(1, "DOS _GETDATE -> $%08x", RegD(0));
1.1 root 544: break;
545: }
546:
1.1.1.13 root 547: case 0xff30: // _VERNUM
1.1 root 548: {
549: RegD(0) = 0x36380302; // ver3.02
1.1.1.13 root 550: putlog(1, "DOS _VERNUM -> $%08x", RegD(0));
1.1 root 551: break;
552: }
553:
1.1.1.13 root 554: case 0xff35: // _INTVCG
1.1 root 555: {
1.1.1.13 root 556: uint16 intno = mainbus->HVRead2(RegA(7));
1.1 root 557: uint32 vecaddr = (intno & 0xff) * 4;
558:
559: if (intno <= 0xff) {
1.1.1.13 root 560: RegD(0) = mainbus->HVRead4(vecaddr);
1.1 root 561: } else {
562: vecaddr += 0xd000;
1.1.1.13 root 563: RegD(0) = mainbus->HVRead4(vecaddr);
1.1 root 564: }
1.1.1.13 root 565: putlog(1, "DOS _INTVCG(intno=%d) -> $%08x", intno, RegD(0));
1.1 root 566: break;
567: }
568:
1.1.1.13 root 569: case 0xff3c: // _CREATE
1.1 root 570: {
1.1.1.13 root 571: uint32 file = mainbus->HVRead4(RegA(7));
572: uint16 atr = mainbus->HVRead2(RegA(7) + 4);
573: putlog(1, "DOS _CREATE(file=$%x, atr=$%x)", file, atr);
1.1 root 574:
575: int32 fileno = OpenFile(file, atr,
576: O_CREAT | O_TRUNC | O_RDWR | O_SYNC);
577: RegD(0) = fileno;
578: break;
579: }
580:
1.1.1.13 root 581: case 0xff3d: // _OPEN
1.1 root 582: {
1.1.1.13 root 583: uint32 file = mainbus->HVRead4(RegA(7));
584: uint16 mode = mainbus->HVRead2(RegA(7) + 4);
585: int unix_mode = 0;
586: bool ok = true;
587: putlog(1, "DOS _OPEN(file=$%x, mode=$%x)", file, mode);
1.1 root 588:
1.1.1.13 root 589: // mode の辞書モード、シェアリングモードは無視している。
590: switch (mode & 0x03) { // read/write
591: case 2:
592: unix_mode |= O_RDWR;
593: break;
594: case 1:
595: unix_mode |= O_WRONLY;
596: break;
597: case 0:
598: unix_mode |= O_RDONLY;
599: break;
600: default:
601: RegD(0) = -12; // アクセスモード異常
602: ok = false;
603: break;
604: }
605:
606: if (ok) {
607: int32 fileno = OpenFile(file, 0, unix_mode | O_SYNC);
608: RegD(0) = fileno;
609: }
610: break;
611: }
612:
613: case 0xff3e: // _CLOSE
614: {
615: uint16 fileno = mainbus->HVRead2(RegA(7));
616:
617: putlog(1, "DOS _CLOSE(fileno=%d)", fileno);
1.1 root 618: CloseFile(fileno);
619: RegD(0) = 0;
620: break;
621: }
622:
1.1.1.13 root 623: case 0xff40: // _WRITE
1.1 root 624: {
1.1.1.13 root 625: uint16 fileno = mainbus->HVRead2(RegA(7));
626: uint32 dataptr = mainbus->HVRead4(RegA(7) + 2);
627: uint32 size = mainbus->HVRead4(RegA(7) + 6);
628: putlog(2, "DOS _WRITE(fileno=%u, dataptr=$%06x, size=$%08x)",
629: fileno, dataptr, size);
1.1 root 630: RegD(0) = WriteFile(fileno, dataptr, size);
631: break;
632: }
633:
1.1.1.13 root 634: case 0xff44: // _IOCTRL
1.1 root 635: {
1.1.1.13 root 636: uint32 mode = mainbus->HVRead2(RegA(7));
1.1 root 637: uint32 fileno;
638: switch (mode) {
1.1.1.13 root 639: case 0: // IOCTRLGT 装置情報取得
640: fileno = mainbus->HVRead2(RegA(7) + 2);
1.1 root 641: if (fileno == 0) { // STDIN
642: RegD(0) = 0x8081; // 100u'uuuu'100u'0001;
643: } else if (fileno == 1) { // STDOUT
644: RegD(0) = 0x8082; // 100u'uuuu'100u'0010;
645: } else if (fileno == 2) { // STDERR
646: RegD(0) = 0x8081; // 100u'uuuu'100u'0001;
647: } else {
648: RegD(0) = -1;
649: }
1.1.1.13 root 650: putlog(1, "DOS _IOCTRL(mode=0(IOCTRLGT), fileno=%d) -> $%x",
651: fileno, RegD(0));
652: break;
653:
654: case 7: // IOCTRLOS 出力ステータス取得
655: fileno = mainbus->HVRead2(RegA(7) + 2);
656: if (fileno == 0) {
657: RegD(0) = 0;
658: } else if (fileno == 1 || fileno == 2) {
659: RegD(0) = -1;
660: } else {
661: int filemode = GetFileMode(fileno);
662: if (filemode < 0) {
663: RegD(0) = filemode;
664: } else if (filemode == O_WRONLY || filemode == O_RDWR) {
665: RegD(0) = -1;
666: } else {
667: RegD(0) = 0;
668: }
669: }
670: putlog(1, "DOS _IOCTRL(mode=7(IOCTRLOS), fileno=%d) -> %d",
671: fileno, RegD(0));
1.1 root 672: break;
1.1.1.13 root 673:
1.1 root 674: default:
1.1.1.13 root 675: errx(EXIT_FAILURE, "DOS _IOCTRL(mode=%d) not implemented", mode);
1.1 root 676: }
677: break;
678: }
679:
1.1.1.13 root 680: case 0xff4a: // _SETBLOCK
1.1 root 681: {
1.1.1.13 root 682: uint32 memptr = mainbus->HVRead4(RegA(7));
683: uint32 len = mainbus->HVRead4(RegA(7) + 4);
684: putlog(1, "DOS _SETBLOCK(memptr=$%x, len=$%x)", memptr, len);
1.1 root 685: // なにもせずに、できたという
1.1.1.13 root 686: RegD(0) = mainbus->HVRead4(RegA(7) + 4);
1.1 root 687: break;
688: }
689:
1.1.1.13 root 690: case 0xff4c: // _EXIT2
1.1 root 691: {
1.1.1.13 root 692: uint32 data = mainbus->HVRead2(RegA(7));
693: putlog(1, "DOS _EXIT2(%d)", data);
1.1.1.7 root 694: RequestExit(data);
695: break;
1.1 root 696: }
697:
1.1.1.13 root 698: case 0xffac: // _GETFCB
1.1 root 699: {
1.1.1.13 root 700: uint32 fileno = mainbus->HVRead2(RegA(7));
701: putlog(1, "DOS _GETFCB(fileno=%d)", fileno);
1.1 root 702: if (fileno <= 4) {
703: RegD(0) = 0x8000 + fileno * 0x60;
704: } else {
705: RegD(0) = -1;
706: }
707: break;
708: }
709:
1.1.1.13 root 710: case 0xfff7: // _BUS_ERR
1.1 root 711: {
1.1.1.13 root 712: uint32 p1 = mainbus->HVRead4(RegA(7));
713: uint32 p2 = mainbus->HVRead4(RegA(7) + 4);
714: uint16 size = mainbus->HVRead2(RegA(7) + 8);
715: putlog(1, "DOS _BUS_ERR(size=%d p1=$%x p2=$%x)", size, p1, p2);
1.1 root 716:
717: RegD(0) = -1;
718: uint32 data;
719: try {
720: if (size == 1) {
1.1.1.13 root 721: data = mainbus->HVRead1(p1);
1.1 root 722: } else if (size == 2 && ((p1 & 1) == 0)) {
1.1.1.13 root 723: data = mainbus->HVRead2(p1);
1.1 root 724: } else if (size == 4 && ((p1 & 1) == 0)) {
1.1.1.13 root 725: data = mainbus->HVRead4(p1);
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) = 2;
732: break;
733: } else {
734: throw;
735: }
736: }
737:
738: try {
739: if (size == 1) {
1.1.1.13 root 740: mainbus->HVWrite1(p2, data);
1.1 root 741: } else if (size == 2 && ((p2 & 1) == 0)) {
1.1.1.13 root 742: mainbus->HVWrite2(p2, data);
1.1 root 743: } else if (size == 4 && ((p2 & 1) == 0)) {
1.1.1.13 root 744: mainbus->HVWrite4(p2, data);
1.1 root 745: } else {
746: break;
747: }
748: } catch (int cause) {
1.1.1.11 root 749: if (cause == M68K::EXCEP_BUSERR) {
1.1 root 750: RegD(0) = 1;
751: break;
752: } else {
753: throw;
754: }
755: }
756: RegD(0) = 0;
757: break;
758: }
759:
1.1.1.13 root 760: case 0xffff: // _CHANGE_PR
761: {
762: putlog(1, "DOS _CHANGE_PR");
763: // なにもしない。
764: break;
765: }
766:
1.1 root 767: default:
1.1.1.11 root 768: printf("DOSCALL $%02x at $%08X (NOT IMPLEMENTED)\n",
1.1.1.12 root 769: mpu680x0->GetIR(), mpu680x0->GetPPC());
1.1.1.7 root 770: RequestExit(1);
771: break;
1.1 root 772: }
773: return true;
774: }
1.1.1.11 root 775:
776: void
1.1.1.13 root 777: Human68kDevice::IOCS()
778: {
779: // XXX 本当は HVRead*() の IsBusErr() を調べないといけない。
780:
781: // 呼び出し元っぽい PC を表示したい。
782: uint32 pc = mainbus->HVRead4(RegA(7) + 2) - 2;
783: if ((mainbus->HVRead2(pc - 2) & 0xff00) == 0x7000) {
784: // moveq #imm, d0
785: pc -= 2;
786: }
787:
788: switch (RegD(0) & 0xff) {
789: case 0x21: // _B_PRINT
790: {
791: uint32 dataptr = RegA(1);
792: putlog(2, "PC=$%06x IOCS _B_PRINT($%06x)", pc, dataptr);
793: DOS_PRINT(dataptr);
794: // 戻り値 d0 は表示後の位置だが無視。
795: break;
796: }
797: case 0x7f: // _ONTIME
798: {
799: putlog(1, "PC=$%06x IOCS _ONTIME", pc);
800: uint64 t = scheduler->GetVirtTime();
801: // nanosec to 10msec, in day
802: RegD(0) = (t / 10_msec) % 8640000;
803: // nanosec to day
804: RegD(1) = t / 86400_sec;
805: break;
806: }
1.1.1.15 root 807: case 0x80: // _B_INTVCS
808: {
809: uint32 num = RegD(1);
810: uint32 newaddr = RegA(1);
811: uint32 oldaddr;
812: if (num < 0x100) {
813: oldaddr = mainbus->HVRead4(num * 4);
814: mainbus->HVWrite4(num * 4, newaddr);
815: RegD(0) = oldaddr;
816: } else {
817: printf("IOCS _B_INTVCS(#%04x) (NOT IMPLEMENTED)", num);
818: RequestExit(1);
819: }
820: break;
821: }
1.1.1.13 root 822: case 0x82: // _B_BPEEK
823: {
824: uint32 data = mainbus->HVRead1(RegA(1));
825: putlog(1, "PC=$%06x IOCS _B_BPEEK($%06x) -> $%02x", pc, RegA(1), data);
826: RegD(0) = (RegD(0) & 0xffffff00) | data;
827: RegA(1)++;
828: break;
829: }
830: case 0xac: // _SYS_STAT (ROM1.3)
831: switch (RegD(1)) {
832: case 0:
833: // MPU 状態の取得
834: RegD(0) =
835: ((250) << 16) // 25.0MHz
1.1.1.14 root 836: | ((mpu680x0->HasFPU() ? 1 : 0) << 15) // FPU
1.1.1.13 root 837: | ((0) << 14) // MMU
838: | ((3) << 0); // MPU Type
839: break;
840: case 1:
841: // キャッシュ状態の取得
842: RegD(0) = 0;
843: break;
844: case 2:
845: // キャッシュを SRAM の設定値に設定
846: RegD(0) = 0;
847: break;
848: case 3:
849: // キャッシュの消去
850: RegD(0) = 0;
851: break;
852: case 4:
853: // キャッシュの設定
854: RegD(0) = 0;
855: break;
856: default:
857: break;
858: }
859: putlog(1, "PC=$%06x IOCS _SYS_STAT(%x) -> $%x", pc, RegD(1), RegD(0));
860: break;
861:
862: default:
1.1.1.14 root 863: printf("IOCS $%02x at $%06x (NOT IMPLEMENTED)\n",
864: RegD(0) & 0xff, pc);
1.1.1.13 root 865: RequestExit(1);
866: break;
867: }
868: }
869:
870: void
1.1.1.12 root 871: Human68kDevice::DOS_PRINT(uint32 dataptr)
1.1.1.11 root 872: {
873: int c;
1.1.1.13 root 874: while ((c = mainbus->HVRead1(dataptr++)) != 0) {
875: putchar(c);
1.1.1.11 root 876: }
877: RegD(0) = 0;
878: }
1.1.1.13 root 879:
880: // ファイルをオープンする。
881: // fileaddr: Human68k ファイル名のゲストアドレス
882: // atr: Human68k の属性
883: // mode: unix open mode
884: // 戻り値は Human68k fileno。
885: int32
886: Human68kDevice::OpenFile(uint32 fileaddr, uint16 atr, int mode)
887: {
888: int32 fileno {};
889: Human68kDevice::File *f = NULL;
890:
891: // 空いてるエントリを検索して Human fileno を取得。
892: for (int i = 0; i < FilesCount; i++) {
893: if (Files[i].fd == -1) {
894: fileno = i;
895: f = &Files[fileno];
896: break;
897: }
898: }
899: if (f == NULL) {
900: return -1;
901: }
902:
903: // ファイル名変換。
904: std::string filename;
905: for (uint8 c; (c = mainbus->HVRead1(fileaddr++)) != '\0'; ) {
906: if (c < 32 || c >= 127 || c == 0x5c) {
907: char hex[16];
908: snprintf(hex, sizeof(hex), "%02X", c);
909: filename += std::string(hex);
910: } else {
911: filename += c;
912: }
913: }
914:
915: int fd = open(filename.c_str(), mode, 0666);
916: if (fd == -1) {
917: putmsg(1, "OpenFile: %s: %s", filename.c_str(), strerror(errno));
918: return -1;
919: }
920:
921: f->fd = fd;
922: f->filename = filename;
923:
924: putmsg(1, "OpenFile: name=\"%s\" mode=$%x -> fileno=%u",
925: filename.c_str(), mode, fileno);
926: return fileno;
927: }
928:
929: // ファイルをクローズする。
930: // fileno: Human68k fileno
931: int32
932: Human68kDevice::CloseFile(int32 fileno)
933: {
934: Human68kDevice::File *f;
935:
936: if (fileno <= 0 || fileno >= FilesCount) {
937: putmsg(1, "CloseFile: fileno=%d is not opened", fileno);
938: return -1;
939: }
940: f = &Files[fileno];
941: putmsg(1, "CloseFile: %d \"%s\"", fileno, f->filename.c_str());
942: if (f->fd > 2) {
943: close(f->fd);
944: }
945: f->fd = -1;
946: f->filename.clear();
947: return 0;
948: }
949:
950: // ファイルに書き出す。
951: // fileno: Human68k fileno
952: // dataaddr: データのゲストアドレス
953: // size: データ長
954: int32
955: Human68kDevice::WriteFile(int32 fileno, uint32 dataaddr, uint32 size)
956: {
957: Human68kDevice::File *f;
958:
959: if (fileno <= 0 || fileno >= FilesCount) {
960: putmsg(1, "WriteFile: fileno=%d is not opened", fileno);
961: return -1;
962: }
963:
964: f = &Files[fileno];
965: if (f->fd < 0) {
966: return -1;
967: }
968:
969: std::vector<uint8> buf(size);
970: mainram->ReadMem(dataaddr, buf.data(), size);
971:
972: int32 rv = write(f->fd, buf.data(), size);
973: return rv;
974: }
975:
976: // 文字列をファイルに書き出す。
977: // fileno: Human68k fileno
978: // dataaddr: data addr (guest VA)
979: void
980: Human68kDevice::FputsFile(int32 fileno, uint32 dataaddr)
981: {
982: Human68kDevice::File *f;
983:
984: if (fileno <= 0 || fileno >= FilesCount) {
985: putmsg(1, "FputsFile: fileno=%d is not opened", fileno);
986: return;
987: }
988:
989: f = &Files[fileno];
990: if (f->fd < 0) {
991: return;
992: }
993:
994: std::vector<uint8> buf;
995: for (uint8 c; (c = mainbus->HVRead1(dataaddr++)) != '\0'; ) {
996: buf.push_back(c);
997: }
998: if (buf.empty() == false) {
999: write(f->fd, buf.data(), buf.size());
1000: }
1001: }
1002:
1003: // ファイルモードを取得する。
1004: // fileno: Human68k fileno
1005: // 戻り値: <0 Humak68k error code
1006: // : >=0 unix file mode
1007: int
1008: Human68kDevice::GetFileMode(int32 fileno)
1009: {
1010: Human68kDevice::File *f;
1011:
1012: if (fileno <= 0 || fileno >= FilesCount) {
1013: return -2 /* FILE_NOT_FOUND */;
1014: }
1015:
1016: f = &Files[fileno];
1017: if (f->fd < 0) {
1018: return -2 /* FILE_NOT_FOUND */;
1019: }
1020:
1021: int fl = fcntl(f->fd, F_GETFL);
1022: int accmode = fl & O_ACCMODE;
1023:
1024: return accmode;
1025: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.