Annotation of nono/vm/human68k.cpp, revision 1.1.1.13

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.