|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2017 Y.Sugahara (moveccr)
4: //
5: // human68k .r .x .z console emulator
6: //
7:
8: #include "human68k.h"
9: #include "mpu.h"
10: #include "m68030bus.h"
11: #include "m68030core.h"
12: #include "bus.h"
13: #include "iodevstream.h"
14: #include "scheduler.h"
15: #include "vm.h"
16: #include <err.h>
17: #include <fcntl.h>
18: #include <sys/stat.h>
19: #include <sys/mman.h>
20:
21: // メモリマップ
22: //
23: // 0000'0000 ベクタ
24: // 0000'0200 Humanエントリポイント
25: // 0000'8000 FCBエリア
26: // 0000'c000 コマンドライン引数
27: // 0000'e000 環境変数
28: // 0001'ff00 SSP 初期値
29: // 0001'ff00 PSP(プロセスエントリ)
30: // 0002'0000 ロードアドレス
31: // 00bf'ffff RAM_END
32:
33: static bool human68k_fline_callback(m68kcpu *cpu, void *arg);
34:
35: // コンストラクタ
36: Human68k::Human68k(const char *file, const char *arg)
37: {
38: logname = "human68k";
39: devname = "Human68k";
40:
41: assert(file);
42: human68k_file = file;
43: human68k_arg = arg;
44:
45: Files[0].fd = 0;
46: Files[0].filename = strdup("|stdin");
47: Files[1].fd = 1;
48: Files[1].filename = strdup("|stdout");
49: Files[2].fd = 2;
50: Files[2].filename = strdup("|stderr");
51: }
52:
53: // デストラクタ
54: Human68k::~Human68k()
55: {
56: }
57:
58: bool
59: Human68k::Init()
60: {
61: bool r;
62: uint32 psp_base;
63: uint8 *file;
64:
65: // ショートカット用
66: ram = gRAM;
67: // Fライン命令をこちらで処理する
68: gMPU->SetFLineCallback(human68k_fline_callback, this);
69:
70: // ワークを用意 (どこでやるか)
71: // $CBC.B MPU 種別 (3:68030)
72: gRAM->Write8(0x0cbc, 3);
73: // $CBD.B FPU 有無 (0xff:あり)
74: if (gMPU->HaveFPU()) {
75: gRAM->Write8(0x0cbd, 0xff);
76: }
77:
78: putmsg(1, "arg=%s", human68k_arg);
79:
80: // 実行ファイルオープン
81: int file_fd = open(human68k_file, O_RDONLY);
82: if (file_fd == -1) {
83: warn("open %s", human68k_file);
84: return false;
85: }
86:
87: struct stat st;
88: if (fstat(file_fd, &st) != 0) {
89: warn("fstat %s", human68k_file);
90: return false;
91: }
92: uint32 file_size;
93: file_size = (uint32)st.st_size;
94: putmsg(1, "file_size=%08x", file_size);
95:
96: // mmap
97: file = (uint8 *)mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, file_fd, 0);
98: if (file == MAP_FAILED) {
99: warn("mmap");
100: return false;
101: }
102:
103: // 拡張子判別
104: if (isext(human68k_file, ".r")) {
105: r = LoadR(file, file_size);
106: } else if (isext(human68k_file, ".x")) {
107: r = LoadX(file, file_size);
108: } else if (isext(human68k_file, ".z")) {
109: r = LoadZ(file, file_size);
110: } else {
111: if (file[0] == 0x48 && file[1] == 0x55) {
112: r = LoadX(file, file_size);
113: } else if (file[0] == 0x60 && file[1] == 0x1a) {
114: r = LoadZ(file, file_size);
115: } else {
116: warnx("Invalid file extension");
117: return false;
118: }
119: }
120: if (!r) {
121: warnx("Invalid file format");
122: return false;
123: }
124:
125: munmap(file, file_size);
126: close(file_fd);
127:
128: uint32 last_addr = load_addr + load_size + bss_size;
129:
130: IODeviceStream ds(ram);
131: boot_addr = 0x200;
132: ds.Write32(0, ram_size - 4); // 初期 SSP
133: ds.Write32(4, boot_addr);
134:
135: // TRAP #15 ベクタを書き込む。
136: ds.Write32(0xbc, 0x1000);
137: // TRAP #15 ハンドラを書き込む。
138: // エミュレータ内部命令 $f300 で処理して RTE するだけ。
139: ds.Write16(0x1000, 0xf600); // .dw $f300
140: ds.Write16(0x1002, 0x4e73); // rte
141:
142: // コマンドライン文字列を書き込む。LASCII 形式
143: ds.SetAddr(0xc001);
144: for (int i = 0; i < 255; i++) {
145: uint8 c = human68k_arg[i];
146: ds.Write8(c);
147: if (c == '\0') {
148: ds.Write8(0xc000, i);
149: break;
150: }
151: }
152:
153: // PSP (process entry) を書き込む。
154: // instructiontest.x はコマンドラインを指定するだけで動くようだ
155: // PSP はロードアドレス-256 の位置でなければならないようだ。
156: psp_base = 0x1ff00;
157: ds.SetAddr(psp_base);
158:
159: ds.Write32(-1); // 1つ前のメモリ管理ポインタ
160: ds.Write32(-1); // このメモリを確保したプロセスのメモリ管理ポインタ
161: ds.Write32(ram_size - 4 + 1); // このメモリブロックの終わり+1 のアドレス
162: ds.Write32(-1); // 次のメモリ管理ポインタ
163:
164: ds.Write32(psp_base + 0x20, 0xc000); // コマンドライン
165:
166: ds.SetAddr(boot_addr);
167: ds.Write16(0x2c4f); // move.l a7,a6
168: ds.Write16(0x2e7c); // move.l psp_base,a7
169: ds.Write32(psp_base);
170: ds.Write16(0x4e66); // move.l a6,usp
171: ds.Write16(0x207c); // move.l psp_base,a0
172: ds.Write32(psp_base);
173: ds.Write16(0x227c); // move.l last_addr,a1
174: ds.Write32(last_addr);
175: ds.Write16(0x247c); // move.l #$c000,a2
176: ds.Write32(0xc000);
177: ds.Write16(0x267c); // move.l #$e000,a3
178: ds.Write32(0xe000);
179: ds.Write16(0x287c); // move.l exec_addr,a4
180: ds.Write32(exec_addr);
181: ds.Write16(0x2c49); // move.l a1,a6
182: ds.Write16(0x46fc); // move.w #$0700,sr
183: ds.Write16(0x0700);
184: ds.Write16(0x4eb9); // jsr.l exec_addr
185: ds.Write32(exec_addr);
186: ds.Write16(0xff00); // DOS _EXIT
187:
188: putmsg(1, "LoadFile complete");
189: return true;
190: }
191:
192: bool
193: Human68k::LoadR(uint8 *file, uint size)
194: {
195: // リロケータブルなのでどこでもいい
196: load_addr = default_load_addr;
197: load_size = size;
198: LoadMem(load_addr, &file[0], load_size);
199:
200: // R 形式はファイル先頭が実行開始位置
201: exec_addr = load_addr;
202: text_size = load_size;
203: putmsg(1, "r format");
204: return true;
205: }
206:
207: bool
208: Human68k::LoadX(uint8 *file, uint size)
209: {
210: XFileHeader *hdr = (XFileHeader *)file;
211: uint hdr_size = sizeof(*hdr);
212:
213: if (!(hdr->magic[0] == 'H' && hdr->magic[1] == 'U')) {
214: errx(EXIT_FAILURE, "invalid magic");
215: }
216: base_addr = be32toh(hdr->base_addr);
217: exec_addr = be32toh(hdr->exec_addr);
218: text_size = be32toh(hdr->text_size);
219: data_size = be32toh(hdr->data_size);
220: bss_size = be32toh(hdr->bss_size);
221:
222: uint32 reloc_size = be32toh(hdr->reloc_size);
223:
224: // 最適配置位置も可能だが今回見送り
225: load_addr = default_load_addr;
226: load_size = text_size + data_size;
227: LoadMem(load_addr, &file[hdr_size], load_size);
228:
229: // 再配置テーブルの file での位置
230: uint32 reloc_pos = hdr_size + load_size;
231:
232: // 再配置
233: IODeviceStream ds(ram);
234: uint32 offset = load_addr - base_addr;
235: uint32 reloc_end = reloc_pos + reloc_size;
236: uint32 A = load_addr;
237: uint32 B = base_addr;
238: uint32 C = A - B;
239: while (reloc_pos < reloc_end) {
240: putlog(1, "reloc_pos=%08x reloc_end=%08x", reloc_pos, reloc_end);
241: uint32 D;
242: D = be16toh(*(uint16 *)&file[reloc_pos]);
243: putlog(1, "D=%x", D);
244: reloc_pos += 2;
245: if (D == 1) {
246: D = be32toh(*(uint32 *)&file[reloc_pos]);
247: putlog(1, " odd, D=%x", D);
248: reloc_pos += 4;
249: }
250: if ((D & 1) == 0) {
251: A += D;
252: uint32 old = ds.Read32(A);
253: ds.Write32(A, old + C);
254: putlog(1, " Write_L A=%x, old=%x new=%x", A, old, old + C);
255: } else {
256: A += D - 1;
257: uint32 old = ds.Read16(A);
258: ds.Write16(A, old + C);
259: putlog(1, " Write_W A=%x, old=%x new=%x", A, old, old + C);
260: }
261: }
262:
263: exec_addr += offset;
264:
265: putmsg(1, "x format, base_addr=%08x, exec_addr=%08x", base_addr, exec_addr);
266:
267: return true;
268: }
269:
270:
271: bool
272: Human68k::LoadZ(uint8 *file, uint size)
273: {
274: ZFileHeader *hdr = (ZFileHeader *)file;
275: uint32 hdr_size = sizeof(*hdr);
276:
277: if (be16toh(hdr->magic1) != 0x601a) {
278: errx(EXIT_FAILURE, "invalid magic");
279: }
280: text_size = be32toh(hdr->text_size);
281: data_size = be32toh(hdr->data_size);
282: bss_size = be32toh(hdr->bss_size);
283: base_addr = be32toh(hdr->base_addr);
284: exec_addr = base_addr;
285:
286: if (base_addr < 0x20000) {
287: errx(EXIT_FAILURE, "base, unsupported");
288: }
289:
290: load_addr = base_addr;
291: load_size = text_size + data_size;
292: LoadMem(load_addr, &file[hdr_size], load_size);
293:
294: putmsg(1, "z format, base_addr=%08x, exec_addr=%08x", base_addr, exec_addr);
295: return true;
296: }
297:
298: bool
299: Human68k::LoadMem(uint32 addr, uint8 *src, uint size)
300: {
301: if (addr + size > ram_size) {
302: errx(EXIT_FAILURE, "file too large");
303: }
304:
305: IODeviceStream ds(ram, addr);
306: for (uint32 i = 0; i < size; i++) {
307: ds.Write8(*src++);
308: }
309: return true;
310: }
311:
312: // file の拡張子が ext なら true を返す。ext は '.' を含む。
313: bool
314: Human68k::isext(const char *file, const char *ext)
315: {
316: size_t len_file = strlen(file);
317: size_t len_ext = strlen(ext);
318: if (len_ext <= 0) {
319: return false;
320: }
321: if (len_file < len_ext) {
322: return false;
323: }
324:
325: return strcasecmp(&file[len_file - len_ext], ext) == 0;
326: }
327:
328:
329: // fileaddr: Human68k ファイル名のゲストVA
330: // atr: Human68k atr
331: // mode: unix open mode
332: // return: Human68k fileno
333: int32
334: Human68k::OpenFile(uint32 fileaddr, uint16 atr, int mode)
335: {
336: // XXX: unix host only
337:
338: int32 fileno;
339: Human68k::File *f = NULL;
340:
341: // 開いているエントリを検索して Human fileno を取得
342: for (int i = 0; i < FilesCount; i++) {
343: if (Files[i].fd == -1) {
344: fileno = i;
345: f = &Files[fileno];
346: break;
347: }
348: }
349: if (f == NULL) {
350: return -1;
351: }
352:
353: char filename[256];
354: char *p = filename;
355:
356: // ファイル名変換
357: IODeviceStream ds(ram, fileaddr);
358: for (int i = 0; i < countof(filename) - 3; i++) {
359: uint8 c = ds.Read8();
360:
361: if (c == 0) break;
362: if (c < 32 || c >= 127 || c == 0x5c) {
363: p += sprintf(p, "%02X", c);
364: } else {
365: *p++ = c;
366: }
367: }
368: *p = '\0';
369:
370: putmsg(1, "OpenFile: %d %s", fileno, filename);
371: int fd = open(filename, mode, 0666);
372: if (fd == -1) {
373: warn("OpenFile.open");
374: return -1;
375: }
376:
377: f->fd = fd;
378: f->filename = strdup(filename);
379:
380: return fileno;
381: }
382:
383: // fileno: Human68k fileno
384: int32
385: Human68k::CloseFile(int32 fileno)
386: {
387: Human68k::File *f;
388:
389: if (fileno <= 0 || fileno >= FilesCount) {
390: warnx("CloseFile: unopened fileno=%d", fileno);
391: return -1;
392: }
393: f = &Files[fileno];
394: putmsg(1, "CloseFile: %d %s", fileno, f->filename);
395: if (f->fd > 2) {
396: // stdin/out/err は閉じない
397: close(f->fd);
398: }
399: f->fd = -1;
400: free(f->filename);
401: return 0;
402: }
403:
404: // fileno: Human68k fileno
405: // dataaddr: data addr (guest VA)
406: // size: data length
407: int32
408: Human68k::WriteFile(int32 fileno, uint32 dataaddr, uint32 size)
409: {
410: Human68k::File *f;
411:
412: if (fileno <= 0 || fileno >= FilesCount) {
413: warnx("WriteFile: unopened fileno=%d", fileno);
414: return -1;
415: }
416:
417: f = &Files[fileno];
418: if (f->fd < 0) {
419: return -1;
420: }
421:
422: IODeviceStream ds(ram, dataaddr);
423: uint8 *buf = new uint8[size];
424: for (int i = 0; i < size; i++) {
425: buf[i] = ds.Read8();
426: }
427:
428: int32 rv = write(f->fd, buf, size);
429:
430: delete[] buf;
431: return rv;
432: }
433:
434: // fileno: Human68k fileno
435: // dataaddr: data addr (guest VA)
436: void
437: Human68k::FputsFile(int32 fileno, uint32 dataaddr)
438: {
439: Human68k::File *f;
440:
441: if (fileno <= 0 || fileno >= FilesCount) {
442: return;
443: }
444:
445: f = &Files[fileno];
446: if (f->fd < 0) {
447: return;
448: }
449:
450: IODeviceStream ds(ram, dataaddr);
451: int size = 1024;
452: uint8 buf[size];
453: bool eof = false;
454:
455: do {
456: int len = 0;
457: for (int i = 0; i < size; i++) {
458: buf[i] = ds.Read8();
459: if (buf[i] == 0) {
460: eof = true;
461: break;
462: }
463: len++;
464: }
465: if (len > 0) {
466: write(f->fd, buf, len);
467: }
468: } while (!eof);
469: }
470:
471:
472: // Human68k DOSCALL Host Emulation
473:
474: void
475: Human68k::IOCS(m68kcpu *cpu)
476: {
477: switch (RegD(0) & 0xff) {
478: case 0x7f: // _ONTIME
479: {
480: putmsg(1, "IOCS ONTIME");
481: uint64 t = gScheduler->GetVirtTime();
482: // nanosec to 10msec, in day
483: RegD(0) = (t / 10_msec) % 8640000;
484: // nanosec to day
485: RegD(1) = t / 86400_sec;
486: break;
487: }
488: case 0x82: // _B_BPEEK
489: {
490: uint32 data = m68030_read_8(cpu, RegA(1));
491: RegD(0) = (RegD(0) & 0xffffff00) | data;
492: RegA(1)++;
493: break;
494: }
495: case 0xac: // _SYS_STAT (ROM1.3)
496: switch (RegD(1)) {
497: case 0:
498: // MPU 状態の取得
499: RegD(0) =
500: ((250) << 16) // 25.0MHz
501: | ((gMPU->HaveFPU() ? 1 : 0) << 15) // FPU
502: | ((0) << 14) // MMU
503: | ((3) << 0); // MPU Type
504: break;
505: case 1:
506: // キャッシュ状態の取得
507: RegD(0) = 0;
508: break;
509: case 2:
510: // キャッシュを SRAM の設定値に設定
511: RegD(0) = 0;
512: break;
513: case 3:
514: // キャッシュの消去
515: RegD(0) = 0;
516: break;
517: case 4:
518: // キャッシュの設定
519: RegD(0) = 0;
520: break;
521: default:
522: break;
523: }
524: break;
525:
526: default:
527: printf("Unimplemented IOCS $%02x\n", RegD(0) & 0xff);
528: exit(1);
529: }
530: }
531:
532: // F-Line 命令をこちらで処理する
533: bool
534: human68k_fline_callback(m68kcpu *cpu, void *arg)
535: {
536: auto *human68k = (Human68k *)arg;
537: return human68k->FLineOp(cpu);
538: }
539:
540: // F-Line 命令
541: bool
542: Human68k::FLineOp(m68kcpu *cpu)
543: {
544: switch (RegIR) {
545: case 0xf600: // IOCS call emulation
546: IOCS(cpu);
547: break;
548:
549: case 0xff00: // EXIT
550: putmsg(1, "DOS EXIT");
551: exit(0);
552:
553: case 0xff09: // PRINT
554: {
555: // STDOUT
556: uint32 dataptr = m68030_read_32(cpu, RegA(7));
557: do {
558: int c = m68030_read_8(cpu, dataptr++);
559: if (c == 0) break;
560: printf("%c", c);
561: } while (1);
562: RegD(0) = 0;
563: break;
564: }
565:
566: case 0xff1e: // FPUTS
567: {
568: uint32 mesptr = m68030_read_32(cpu, RegA(7));
569: uint16 fileno = m68030_read_16(cpu, RegA(7) + 4);
570: FputsFile(fileno, mesptr);
571: break;
572: }
573:
574: case 0xff20: // SUPER
575: {
576: uint32 data = m68030_read_32(cpu, RegA(7));
577:
578: if (data == 0) {
579: m68030_set_sr(cpu, RegSR | 0x2000);
580: data = RegUSP;
581: RegD(0) = RegA(7);
582: RegA(7) = data;
583: putmsg(1, "SUPERVISOR MODE");
584: } else {
585: m68030_set_sr(cpu, RegSR | 0x2000);
586: RegA(7) = data;
587: m68030_set_sr(cpu, RegSR & ~0x2000);
588: putmsg(1, "USER MODE");
589: }
590: break;
591: }
592:
593: case 0xff25: // INTVCS
594: {
595: uint16 intno = m68030_read_16(cpu, RegA(7));
596: uint32 addr = m68030_read_32(cpu, RegA(7) + 2);
597:
598: uint32 vecaddr = (intno & 0xff) * 4;
599:
600: if (intno <= 0xff) {
601: RegD(0) = m68030_read_32(cpu, vecaddr);
602: m68030_write_32(cpu, vecaddr, addr);
603: } else {
604: vecaddr += 0xd000;
605: RegD(0) = m68030_read_32(cpu, vecaddr);
606: m68030_write_32(cpu, vecaddr, addr);
607: }
608: break;
609: }
610:
611: case 0xff27: // GETTIM2
612: {
613: // DUMMY
614: RegD(0) = 0;
615: break;
616: }
617:
618: case 0xff2a: // GETDATE
619: {
620: // DUMMY
621: RegD(0) = 0;
622: break;
623: }
624:
625: case 0xff30: // VERNUM
626: {
627: RegD(0) = 0x36380302; // ver3.02
628: break;
629: }
630:
631: case 0xff35: // INTVCG
632: {
633: uint16 intno = m68030_read_16(cpu, RegA(7));
634:
635: uint32 vecaddr = (intno & 0xff) * 4;
636:
637: if (intno <= 0xff) {
638: RegD(0) = m68030_read_32(cpu, vecaddr);
639: } else {
640: vecaddr += 0xd000;
641: RegD(0) = m68030_read_32(cpu, vecaddr);
642: }
643: break;
644: }
645:
646: case 0xff3c: // CREATE
647: {
648: uint32 file = m68030_read_32(cpu, RegA(7));
649: uint16 atr = m68030_read_32(cpu, RegA(7) + 4);
650:
651: int32 fileno = OpenFile(file, atr,
652: O_CREAT | O_TRUNC | O_RDWR | O_SYNC);
653: RegD(0) = fileno;
654: break;
655: }
656:
657: case 0xff3e: // CLOSE
658: {
659: uint16 fileno = m68030_read_16(cpu, RegA(7));
660:
661: CloseFile(fileno);
662: RegD(0) = 0;
663: break;
664: }
665:
666: case 0xff40: // WRITE
667: {
668: // putmsg(1, "DOS _WRITE at %08x", RegPPC);
669: uint16 fileno = m68030_read_16(cpu, RegA(7));
670: uint32 dataptr = m68030_read_32(cpu, RegA(7) + 2);
671: uint32 size = m68030_read_32(cpu, RegA(7) + 6);
672: // putmsg(1, "WRITE(%d, 0x%08x, 0x%08x)\n", fileno, dataptr, size);
673: RegD(0) = WriteFile(fileno, dataptr, size);
674: break;
675: }
676:
677: case 0xff44: // IOCTRL
678: {
679: uint32 mode = m68030_read_16(cpu, RegA(7));
680: uint32 fileno;
681: switch (mode) {
682: case 0:
683: fileno = m68030_read_16(cpu, RegA(7) + 2);
684: putmsg(1, "DOS IOCTRL(mode=%d, fileno=%d)", mode, fileno);
685: if (fileno == 0) { // STDIN
686: RegD(0) = 0x8081; // 100u'uuuu'100u'0001;
687: break;
688: } else if (fileno == 1) { // STDOUT
689: RegD(0) = 0x8082; // 100u'uuuu'100u'0010;
690: break;
691: } else if (fileno == 2) { // STDERR
692: RegD(0) = 0x8081; // 100u'uuuu'100u'0001;
693: break;
694: } else {
695: RegD(0) = -1;
696: }
697: break;
698: default:
699: errx(EXIT_FAILURE, "DOS IOCTRL(mode=%d) not impl.", mode);
700: }
701: break;
702: }
703:
704: case 0xff4a: // SETBLOCK
705: {
706: uint32 memptr = m68030_read_32(cpu, RegA(7));
707: uint32 len = m68030_read_32(cpu, RegA(7) + 4);
708: putmsg(1, "DOS SETBLOCK(memptr=$%x len=$%x)", memptr, len);
709: // なにもせずに、できたという
710: RegD(0) = m68030_read_32(cpu, RegA(7) + 4);
711: break;
712: }
713:
714: case 0xff4c: // EXIT2
715: {
716: uint32 data = m68030_read_16(cpu, RegA(7));
717: putmsg(1, "DOS EXIT2(%d)", data);
718: exit(data);
719: }
720:
721: case 0xffac: // GETFCB
722: {
723: uint32 fileno = m68030_read_16(cpu, RegA(7));
724: putmsg(1, "DOS GETFCB(fileno=%d)", fileno);
725: if (fileno <= 4) {
726: RegD(0) = 0x8000 + fileno * 0x60;
727: } else {
728: RegD(0) = -1;
729: }
730: break;
731: }
732:
733: case 0xfff7: // BUS_ERR
734: {
735: uint32 p1 = m68030_read_32(cpu, RegA(7));
736: uint32 p2 = m68030_read_32(cpu, RegA(7) + 4);
737: uint16 size = m68030_read_16(cpu, RegA(7) + 8);
738: putmsg(1, "DOS BUS_ERR(size=%d p1=$%x p2=$%x)", size, p1, p2);
739:
740: RegD(0) = -1;
741: uint32 data;
742: try {
743: if (size == 1) {
744: data = m68030_read_8(cpu, p1);
745: } else if (size == 2 && ((p1 & 1) == 0)) {
746: data = m68030_read_16(cpu, p1);
747: } else if (size == 4 && ((p1 & 1) == 0)) {
748: data = m68030_read_32(cpu, p1);
749: } else {
750: break;
751: }
752: } catch (int cause) {
753: if (cause == M68K_EXCEP_BUSERR) {
754: RegD(0) = 2;
755: break;
756: } else {
757: throw;
758: }
759: }
760:
761: try {
762: if (size == 1) {
763: m68030_write_8(cpu, p2, data);
764: } else if (size == 2 && ((p2 & 1) == 0)) {
765: m68030_write_16(cpu, p2, data);
766: } else if (size == 4 && ((p2 & 1) == 0)) {
767: m68030_write_32(cpu, p2, data);
768: } else {
769: break;
770: }
771: } catch (int cause) {
772: if (cause == M68K_EXCEP_BUSERR) {
773: RegD(0) = 1;
774: break;
775: } else {
776: throw;
777: }
778: }
779: RegD(0) = 0;
780: break;
781: }
782:
783: default:
784: printf("Unimplemented DOSCALL $%02x at $%08X\n", RegIR, RegPPC);
785: exit(1);
786: }
787: return true;
788: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.