|
|
nono 1.6.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
//
// Human68k .r .x .z console emulator
//
// メモリマップ
//
// 0000'0000 ベクタ
// 0000'0200 Humanエントリポイント
// 0000'8000 FCBエリア
// 0000'c000 コマンドライン引数
// 0000'e000 環境変数
// 0001'ff00 SSP 初期値
// 0001'ff00 PSP(プロセスエントリ)
// 0002'0000 ロードアドレス
// 00bf'ffff RAM_END
#include "human68k.h"
#include "autofd.h"
#include "event.h"
#include "iodevstream.h"
#include "mainapp.h"
#include "mainbus.h"
#include "mainram.h"
#include "mpu680x0.h"
#include "power.h"
#include "scheduler.h"
#include <fcntl.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/mman.h>
static bool human68k_fline_callback(MPU680x0Device *, void *arg);
#define RegD(n) mpu680x0->reg.D[n]
#define RegA(n) mpu680x0->reg.A[n]
// コンストラクタ
Human68kDevice::Human68kDevice()
: inherited(OBJ_HUMAN68K)
{
Files[0].fd = 0;
Files[0].filename = "|stdin";
Files[1].fd = 1;
Files[1].filename = "|stdout";
Files[2].fd = 2;
Files[2].filename = "|stderr";
}
// デストラクタ
Human68kDevice::~Human68kDevice()
{
}
// 初期化
bool
Human68kDevice::Init()
{
uint32 psp_base;
uint8 *file;
uint32 last_addr;
uint32 ram_size;
assert(gMainApp.exec_file);
human68k_file = gMainApp.exec_file;
human68k_arg = gMainApp.exec_arg;
mainbus = GetMainbusDevice();
mainram = GetMainRAMDevice();
mpu680x0 = GetMPU680x0Device(mpu);
// Fライン命令をこちらで処理する。
mpu680x0->SetFLineCallback(human68k_fline_callback, this);
IODeviceStream ds(mainram);
// ワークを用意 (どこでやるか)。
// $CBC.B MPU 種別 (3:68030)
ds.Write1(0x0cbc, 3);
// $CBD.B FPU 有無 (0xff:あり)。
if (mpu680x0->HasFPU()) {
ds.Write1(0x0cbd, 0xff);
}
putmsg(1, "arg=%s", human68k_arg.c_str());
// 実行ファイルオープン。
autofd file_fd = open(human68k_file, O_RDONLY);
if (file_fd == -1) {
warn("Human68k executable \"%s\" open failed", human68k_file);
return false;
}
struct stat st;
if (fstat(file_fd, &st) != 0) {
warn("Human68k executable \"%s\" fstat failed", human68k_file);
return false;
}
uint32 file_size;
file_size = (uint32)st.st_size;
putmsg(1, "file_size=%08x", file_size);
// mmap
file = (uint8 *)mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, file_fd, 0);
if (file == MAP_FAILED) {
warn("Human68k executable \"%s\" mmap failed", human68k_file);
return false;
}
// 拡張子判別。
if (isext(human68k_file, ".r")) {
if (LoadR(file, file_size) == false) {
goto abort;
}
} else if (isext(human68k_file, ".x")) {
if (LoadX(file, file_size) == false) {
goto abort;
}
} else if (isext(human68k_file, ".z")) {
if (LoadZ(file, file_size) == false) {
goto abort;
}
} else {
// ELF .o のロードを試行する。
if (LoadELF(file, file_size) == false) {
if (file[0] == 0x48 && file[1] == 0x55) {
if (LoadX(file, file_size) == false) {
goto abort;
}
} else if (file[0] == 0x60 && file[1] == 0x1a) {
if (LoadZ(file, file_size) == false) {
goto abort;
}
} else {
errno = ENOEXEC;
warn("Human68k executable \"%s\"", human68k_file);
goto abort;
}
}
}
munmap(file, file_size);
last_addr = load_addr + load_size + bss_size;
ram_size = (uint32)mainram->GetSize();
boot_addr = 0x200;
ds.Write4(0, ram_size - 4); // 初期 SSP
ds.Write4(4, boot_addr);
// TRAP #15 ベクタを書き込む。
ds.Write4(0xbc, 0x1000);
// TRAP #15 (IOCS コール) ハンドラを書き込む。
// オレオレ F ライン命令 (エミュレータ内部命令) で処理して RTE するだけ。
ds.Write2(0x1000, 0xfa00);
ds.Write2(0x1002, 0x4e73); // rte
// STOP による停止。終了時に CPU を止めておくため。
ds.Write4(0x1010, 0x4e722700); // stop #$2700
// MFP Timer-C ベクタを書き込む。
ds.Write4(0x0114, 0x1020);
// Timer-C で 10ミリ秒ごとに $9cc.w のワークを減算していく。
// 0 になったら $9d6.l の稼働時間(分) をインクリメントして、
// $9cc.w には $9ca.w の初期値 (6000 = 60秒 * 100) を再セット。
ds.SetAddr(0x1020);
ds.Write2(0x5378); // subq.w #1,($9cc)
ds.Write2(0x9cc);
ds.Write2(0x660a); // bne @f
ds.Write2(0x31f8); // move.w ($9ca),($9cc)
ds.Write2(0x09ca);
ds.Write2(0x09cc);
ds.Write2(0x52b8); // addq.l #1,($9d6)
ds.Write2(0x09d6);
ds.Write2(0x4e73); //@@: rte
ds.Write2(0x9ca, 6000); // $9ca: 初期値
ds.Write2(0x9cc, 6000); // いきなり初期化しておく。
// コマンドライン文字列を書き込む。LASCII 形式
ds.SetAddr(0xc001);
for (int i = 0; i < 255; i++) {
uint8 c = human68k_arg[i];
ds.Write1(c);
if (c == '\0') {
ds.Write1(0xc000, i);
break;
}
}
// XXX: 255文字以上のときどうなるか調べること
// PSP (process entry) を書き込む。
// instructiontest.x はコマンドラインを指定するだけで動くようだ。
// PSP はロードアドレス-256 の位置でなければならないようだ。
psp_base = 0x1ff00;
ds.SetAddr(psp_base);
ds.Write4(-1); // 1つ前のメモリ管理ポインタ
ds.Write4(-1); // このメモリを確保したプロセスのメモリ管理ポインタ
ds.Write4(ram_size - 4 + 1); // このメモリブロックの終わり+1 のアドレス
ds.Write4(-1); // 次のメモリ管理ポインタ
ds.Write4(psp_base + 0x20, 0xc000); // コマンドライン
ds.SetAddr(boot_addr);
ds.Write2(0x2c4f); // move.l a7,a6
ds.Write2(0x2e7c); // move.l psp_base,a7
ds.Write4(psp_base);
ds.Write2(0x4e66); // move.l a6,usp
ds.Write2(0x207c); // move.l psp_base,a0
ds.Write4(psp_base);
ds.Write2(0x227c); // move.l last_addr,a1
ds.Write4(last_addr);
ds.Write2(0x247c); // move.l #$c000,a2
ds.Write4(0xc000);
ds.Write2(0x267c); // move.l #$e000,a3
ds.Write4(0xe000);
ds.Write2(0x287c); // move.l exec_addr,a4
ds.Write4(exec_addr);
ds.Write2(0x2c49); // move.l a1,a6
ds.Write2(0x46fc); // move.w #$0500,sr
ds.Write2(0x0500);
ds.Write2(0x4eb9); // jsr.l exec_addr
ds.Write4(exec_addr);
ds.Write2(0xff00); // DOS _EXIT
putmsg(1, "LoadFile complete");
return true;
abort:
munmap(file, file_size);
return false;
}
// R 形式ファイルをロードする。
// 成功すれば true を返す。失敗すればエラーメッセージを表示して false を返す。
bool
Human68kDevice::LoadR(const uint8 *file, uint size)
{
// リロケータブルなのでどこでもいい。
load_addr = default_load_addr;
load_size = size;
if (LoadMem(load_addr, &file[0], load_size) == false) {
return false;
}
// R 形式はファイル先頭が実行開始位置。
exec_addr = load_addr;
text_size = load_size;
putmsg(1, "r format");
return true;
}
// X 形式ファイルをロードする。
// 成功すれば true を返す。失敗すればエラーメッセージを表示して false を返す。
bool
Human68kDevice::LoadX(const uint8 *file, uint size)
{
auto *hdr = reinterpret_cast<const XFileHeader *>(file);
uint hdr_size = sizeof(*hdr);
if (!(hdr->magic[0] == 'H' && hdr->magic[1] == 'U')) {
warnx("Human68k executable \"%s\" invalid magic", human68k_file);
return false;
}
base_addr = be32toh(hdr->base_addr);
exec_addr = be32toh(hdr->exec_addr);
text_size = be32toh(hdr->text_size);
data_size = be32toh(hdr->data_size);
bss_size = be32toh(hdr->bss_size);
uint32 reloc_size = be32toh(hdr->reloc_size);
// 最適配置位置も可能だが今回見送り。
load_addr = default_load_addr;
load_size = text_size + data_size;
if (LoadMem(load_addr, &file[hdr_size], load_size) == false) {
return false;
}
// 再配置テーブルの file での位置。
uint32 reloc_pos = hdr_size + load_size;
// 再配置。
IODeviceStream ds(mainram);
uint32 offset = load_addr - base_addr;
uint32 reloc_end = reloc_pos + reloc_size;
uint32 A = load_addr;
uint32 B = base_addr;
uint32 C = A - B;
while (reloc_pos < reloc_end) {
putmsg(3, "reloc_pos=%08x reloc_end=%08x", reloc_pos, reloc_end);
uint32 D;
D = be16toh(*(const uint16 *)&file[reloc_pos]);
putmsg(3, "D=%x", D);
reloc_pos += 2;
if (D == 1) {
D = be32toh(*(const uint32 *)&file[reloc_pos]);
putmsg(3, " odd, D=%x", D);
reloc_pos += 4;
}
if ((D & 1) == 0) {
A += D;
uint32 old = ds.Read4(A);
ds.Write4(A, old + C);
putmsg(3, " Write_L A=%x, old=%x new=%x", A, old, old + C);
} else {
A += D - 1;
uint32 old = ds.Read2(A);
ds.Write2(A, old + C);
putmsg(3, " Write_W A=%x, old=%x new=%x", A, old, old + C);
}
}
exec_addr += offset;
putmsg(1, "x format, base_addr=%08x, exec_addr=%08x", base_addr, exec_addr);
return true;
}
// Z 形式ファイルをロードする。
// 成功すれば true を返す。失敗すればエラーメッセージを表示して false を返す。
bool
Human68kDevice::LoadZ(const uint8 *file, uint size)
{
auto *hdr = reinterpret_cast<const ZFileHeader *>(file);
uint32 hdr_size = sizeof(*hdr);
if (be16toh(hdr->magic1) != 0x601a) {
warnx("Human68k executable \"%s\" invalid magic", human68k_file);
return false;
}
text_size = be32toh(hdr->text_size);
data_size = be32toh(hdr->data_size);
bss_size = be32toh(hdr->bss_size);
base_addr = be32toh(hdr->base_addr);
exec_addr = base_addr;
if (base_addr < 0x20000) {
warnx("Human68k executable \"%s\" unsupported base specified",
human68k_file);
return false;
}
load_addr = base_addr;
load_size = text_size + data_size;
if (LoadMem(load_addr, &file[hdr_size], load_size) == false) {
return false;
}
putmsg(1, "z format, base_addr=%08x, exec_addr=%08x", base_addr, exec_addr);
return true;
}
// なぜか m68k ELF .o が読める。
// 成功すれば true を返す。失敗すればエラーメッセージを表示して false を返す。
bool
Human68kDevice::LoadELF(const uint8 *file, uint size)
{
LoadInfo info(human68k_file, file, size);
if (BootLoader::LoadExec(mainram, &info) == false) {
return false;
}
// XXX load_size は esym のような気がする。
// XXX text_size も今はやろうと思えば返せる構造になったが未対応。
load_addr = info.entry;
load_size = size;
exec_addr = info.entry;
text_size = size;
return true;
}
// src から size バイトを RAM の addr 以降にロードする。
// 成功すれば true を返す。
// ファイルが RAM に収まらない場合はエラーメッセージを表示して false を返す。
bool
Human68kDevice::LoadMem(uint32 addr, const uint8 *src, uint size)
{
if (addr + size >= mainram->GetSize()) {
warnx("Human68k executable \"%s\": file too large", human68k_file);
return false;
}
mainram->WriteMem(addr, src, size);
return true;
}
// file の拡張子が ext なら true を返す。ext は '.' を含む。
bool
Human68kDevice::isext(const char *file, const char *ext)
{
size_t len_file = strlen(file);
size_t len_ext = strlen(ext);
if (len_ext == 0) {
return false;
}
if (len_file < len_ext) {
return false;
}
return strcasecmp(&file[len_file - len_ext], ext) == 0;
}
// 仮想マシンの中から終了させる
// (MSXDOSDevice からも終了時に呼ぶ)。
// XXX code は未対応。
/*static*/ void
Human68kDevice::RequestExit(int code)
{
// 電源を即切る
auto power = GetPowerDevice();
power->PushPowerButton();
power->SetSystemPowerOn(false);
}
// F-Line 命令をこちらで処理する。
bool
human68k_fline_callback(MPU680x0Device *, void *arg)
{
auto *human68k = reinterpret_cast<Human68kDevice *>(arg);
return human68k->FLineOp();
}
// F-Line 命令。
bool
Human68kDevice::FLineOp()
{
switch (mpu680x0->GetIR()) {
case 0xfa00: // IOCS call emulation
IOCS();
break;
case 0xfe11: // FPACK __LTOS
{
putlog(1, "FPACK __LTOS");
uint32 data = RegD(0);
uint32 dst = RegA(0);
char buf[16];
snprintf(buf, sizeof(buf), "%d", data);
for (char *p = buf; ; p++, dst++) {
uint8 c = *p;
mainbus->HVWrite1(dst, c);
if (c == 0) {
break;
}
}
// '\0' の位置を A0 に返す。
RegA(0) = dst;
break;
}
case 0xff00: // _EXIT
putlog(1, "DOS _EXIT");
// XXX CPU の実行を止める。方式自体を I/O 方式に見直したときには
// ちゃんとした方法を取る予定。
mpu680x0->SetSR(mpu680x0->GetSR() | 0x2000);
mpu680x0->reg.pc = 0x1010;
RequestExit(0);
break;
case 0xff09: // _PRINT
{
uint32 dataptr = mainbus->HVRead4(RegA(7));
putlog(2, "DOS _PRINT($%06x)", dataptr);
DOS_PRINT(dataptr);
break;
}
case 0xff1e: // _FPUTS
{
uint32 mesptr = mainbus->HVRead4(RegA(7));
uint16 fileno = mainbus->HVRead2(RegA(7) + 4);
putlog(2, "DOS _FPUTS(fileno=%d, mesptr=$%06x)", fileno, mesptr);
FputsFile(fileno, mesptr);
break;
}
case 0xff20: // _SUPER
{
uint32 data = mainbus->HVRead4(RegA(7));
putlog(1, "DOS _SUPER($%x) to %s", data,
(data == 0) ? "SUPERVISOR" : "USER");
if (data == 0) {
mpu680x0->SetSR(mpu680x0->GetSR() | 0x2000);
data = mpu680x0->reg.usp;
RegD(0) = RegA(7);
RegA(7) = data;
} else {
mpu680x0->SetSR(mpu680x0->GetSR() | 0x2000);
RegA(7) = data;
mpu680x0->SetSR(mpu680x0->GetSR() & ~0x2000);
}
break;
}
case 0xff25: // _INTVCS
{
uint16 intno = mainbus->HVRead2(RegA(7));
uint32 addr = mainbus->HVRead4(RegA(7) + 2);
putlog(1, "DOS _INTVCS(intno=$%02x, addr=$%06x)", intno, addr);
uint32 vecaddr = (intno & 0xff) * 4;
if (intno <= 0xff) {
RegD(0) = mainbus->HVRead4(vecaddr);
mainbus->HVWrite4(vecaddr, addr);
} else {
vecaddr += 0xd000;
RegD(0) = mainbus->HVRead4(vecaddr);
mainbus->HVWrite4(vecaddr, addr);
}
break;
}
case 0xff27: // _GETTIM2
{
struct tm tm;
time_t now = time(NULL);
localtime_r(&now, &tm);
RegD(0) = (tm.tm_hour << 16)
| (tm.tm_min << 8)
| tm.tm_sec;
putlog(1, "DOS _GETTIM2 -> $%08x", RegD(0));
break;
}
case 0xff2a: // _GETDATE
{
struct tm tm;
time_t now = time(NULL);
localtime_r(&now, &tm);
RegD(0) = (tm.tm_wday << 16)
| ((tm.tm_year + 1900 - 1980) << 9)
| ((tm.tm_mon + 1) << 5)
| tm.tm_mday;
putlog(1, "DOS _GETDATE -> $%08x", RegD(0));
break;
}
case 0xff30: // _VERNUM
{
RegD(0) = 0x36380302; // ver3.02
putlog(1, "DOS _VERNUM -> $%08x", RegD(0));
break;
}
case 0xff35: // _INTVCG
{
uint16 intno = mainbus->HVRead2(RegA(7));
uint32 vecaddr = (intno & 0xff) * 4;
if (intno <= 0xff) {
RegD(0) = mainbus->HVRead4(vecaddr);
} else {
vecaddr += 0xd000;
RegD(0) = mainbus->HVRead4(vecaddr);
}
putlog(1, "DOS _INTVCG(intno=%d) -> $%08x", intno, RegD(0));
break;
}
case 0xff3c: // _CREATE
{
uint32 file = mainbus->HVRead4(RegA(7));
uint16 atr = mainbus->HVRead2(RegA(7) + 4);
putlog(1, "DOS _CREATE(file=$%x, atr=$%x)", file, atr);
int32 fileno = OpenFile(file, atr,
O_CREAT | O_TRUNC | O_RDWR | O_SYNC);
RegD(0) = fileno;
break;
}
case 0xff3d: // _OPEN
{
uint32 file = mainbus->HVRead4(RegA(7));
uint16 mode = mainbus->HVRead2(RegA(7) + 4);
int unix_mode = 0;
bool ok = true;
putlog(1, "DOS _OPEN(file=$%x, mode=$%x)", file, mode);
// mode の辞書モード、シェアリングモードは無視している。
switch (mode & 0x03) { // read/write
case 2:
unix_mode |= O_RDWR;
break;
case 1:
unix_mode |= O_WRONLY;
break;
case 0:
unix_mode |= O_RDONLY;
break;
default:
RegD(0) = -12; // アクセスモード異常
ok = false;
break;
}
if (ok) {
int32 fileno = OpenFile(file, 0, unix_mode | O_SYNC);
RegD(0) = fileno;
}
break;
}
case 0xff3e: // _CLOSE
{
uint16 fileno = mainbus->HVRead2(RegA(7));
putlog(1, "DOS _CLOSE(fileno=%d)", fileno);
CloseFile(fileno);
RegD(0) = 0;
break;
}
case 0xff40: // _WRITE
{
uint16 fileno = mainbus->HVRead2(RegA(7));
uint32 dataptr = mainbus->HVRead4(RegA(7) + 2);
uint32 size = mainbus->HVRead4(RegA(7) + 6);
putlog(2, "DOS _WRITE(fileno=%u, dataptr=$%06x, size=$%08x)",
fileno, dataptr, size);
RegD(0) = WriteFile(fileno, dataptr, size);
break;
}
case 0xff44: // _IOCTRL
{
uint32 mode = mainbus->HVRead2(RegA(7));
uint32 fileno;
switch (mode) {
case 0: // IOCTRLGT 装置情報取得
fileno = mainbus->HVRead2(RegA(7) + 2);
if (fileno == 0) { // STDIN
RegD(0) = 0x8081; // 100u'uuuu'100u'0001;
} else if (fileno == 1) { // STDOUT
RegD(0) = 0x8082; // 100u'uuuu'100u'0010;
} else if (fileno == 2) { // STDERR
RegD(0) = 0x8081; // 100u'uuuu'100u'0001;
} else {
RegD(0) = -1;
}
putlog(1, "DOS _IOCTRL(mode=0(IOCTRLGT), fileno=%d) -> $%x",
fileno, RegD(0));
break;
case 7: // IOCTRLOS 出力ステータス取得
fileno = mainbus->HVRead2(RegA(7) + 2);
if (fileno == 0) {
RegD(0) = 0;
} else if (fileno == 1 || fileno == 2) {
RegD(0) = -1;
} else {
int filemode = GetFileMode(fileno);
if (filemode < 0) {
RegD(0) = filemode;
} else if (filemode == O_WRONLY || filemode == O_RDWR) {
RegD(0) = -1;
} else {
RegD(0) = 0;
}
}
putlog(1, "DOS _IOCTRL(mode=7(IOCTRLOS), fileno=%d) -> %d",
fileno, RegD(0));
break;
default:
errx(EXIT_FAILURE, "DOS _IOCTRL(mode=%d) not implemented", mode);
}
break;
}
case 0xff4a: // _SETBLOCK
{
uint32 memptr = mainbus->HVRead4(RegA(7));
uint32 len = mainbus->HVRead4(RegA(7) + 4);
putlog(1, "DOS _SETBLOCK(memptr=$%x, len=$%x)", memptr, len);
// なにもせずに、できたという
RegD(0) = mainbus->HVRead4(RegA(7) + 4);
break;
}
case 0xff4c: // _EXIT2
{
uint32 data = mainbus->HVRead2(RegA(7));
putlog(1, "DOS _EXIT2(%d)", data);
RequestExit(data);
break;
}
case 0xffac: // _GETFCB
{
uint32 fileno = mainbus->HVRead2(RegA(7));
putlog(1, "DOS _GETFCB(fileno=%d)", fileno);
if (fileno <= 4) {
RegD(0) = 0x8000 + fileno * 0x60;
} else {
RegD(0) = -1;
}
break;
}
case 0xfff7: // _BUS_ERR
{
uint32 p1 = mainbus->HVRead4(RegA(7));
uint32 p2 = mainbus->HVRead4(RegA(7) + 4);
uint16 size = mainbus->HVRead2(RegA(7) + 8);
putlog(1, "DOS _BUS_ERR(size=%d p1=$%x p2=$%x)", size, p1, p2);
RegD(0) = -1;
uint32 data;
try {
if (size == 1) {
data = mainbus->HVRead1(p1);
} else if (size == 2 && ((p1 & 1) == 0)) {
data = mainbus->HVRead2(p1);
} else if (size == 4 && ((p1 & 1) == 0)) {
data = mainbus->HVRead4(p1);
} else {
break;
}
} catch (int cause) {
if (cause == M68K::EXCEP_BUSERR) {
RegD(0) = 2;
break;
} else {
throw;
}
}
try {
if (size == 1) {
mainbus->HVWrite1(p2, data);
} else if (size == 2 && ((p2 & 1) == 0)) {
mainbus->HVWrite2(p2, data);
} else if (size == 4 && ((p2 & 1) == 0)) {
mainbus->HVWrite4(p2, data);
} else {
break;
}
} catch (int cause) {
if (cause == M68K::EXCEP_BUSERR) {
RegD(0) = 1;
break;
} else {
throw;
}
}
RegD(0) = 0;
break;
}
case 0xffff: // _CHANGE_PR
{
putlog(1, "DOS _CHANGE_PR");
// なにもしない。
break;
}
default:
printf("DOSCALL $%02x at $%08X (NOT IMPLEMENTED)\n",
mpu680x0->GetIR(), mpu680x0->GetPPC());
RequestExit(1);
break;
}
return true;
}
void
Human68kDevice::IOCS()
{
// XXX 本当は HVRead*() の IsBusErr() を調べないといけない。
// 呼び出し元っぽい PC を表示したい。
uint32 pc = mainbus->HVRead4(RegA(7) + 2) - 2;
if ((mainbus->HVRead2(pc - 2) & 0xff00) == 0x7000) {
// moveq #imm, d0
pc -= 2;
}
switch (RegD(0) & 0xff) {
case 0x21: // _B_PRINT
{
uint32 dataptr = RegA(1);
putlog(2, "PC=$%06x IOCS _B_PRINT($%06x)", pc, dataptr);
DOS_PRINT(dataptr);
// 戻り値 d0 は表示後の位置だが無視。
break;
}
case 0x7f: // _ONTIME
{
putlog(1, "PC=$%06x IOCS _ONTIME", pc);
uint64 t = scheduler->GetVirtTime();
// nanosec to 10msec, in day
RegD(0) = (t / 10_msec) % 8640000;
// nanosec to day
RegD(1) = t / 86400_sec;
break;
}
case 0x80: // _B_INTVCS
{
uint32 num = RegD(1);
uint32 newaddr = RegA(1);
uint32 oldaddr;
if (num < 0x100) {
oldaddr = mainbus->HVRead4(num * 4);
mainbus->HVWrite4(num * 4, newaddr);
RegD(0) = oldaddr;
} else {
printf("IOCS _B_INTVCS(#%04x) (NOT IMPLEMENTED)", num);
RequestExit(1);
}
break;
}
case 0x82: // _B_BPEEK
{
uint32 data = mainbus->HVRead1(RegA(1));
putlog(1, "PC=$%06x IOCS _B_BPEEK($%06x) -> $%02x", pc, RegA(1), data);
RegD(0) = (RegD(0) & 0xffffff00) | data;
RegA(1)++;
break;
}
case 0xac: // _SYS_STAT (ROM1.3)
switch (RegD(1)) {
case 0:
// MPU 状態の取得
RegD(0) =
((250) << 16) // 25.0MHz
| ((mpu680x0->HasFPU() ? 1 : 0) << 15) // FPU
| ((0) << 14) // MMU
| ((3) << 0); // MPU Type
break;
case 1:
// キャッシュ状態の取得
RegD(0) = 0;
break;
case 2:
// キャッシュを SRAM の設定値に設定
RegD(0) = 0;
break;
case 3:
// キャッシュの消去
RegD(0) = 0;
break;
case 4:
// キャッシュの設定
RegD(0) = 0;
break;
default:
break;
}
putlog(1, "PC=$%06x IOCS _SYS_STAT(%x) -> $%x", pc, RegD(1), RegD(0));
break;
default:
printf("IOCS $%02x at $%06x (NOT IMPLEMENTED)\n",
RegD(0) & 0xff, pc);
RequestExit(1);
break;
}
}
void
Human68kDevice::DOS_PRINT(uint32 dataptr)
{
int c;
while ((c = mainbus->HVRead1(dataptr++)) != 0) {
putchar(c);
}
RegD(0) = 0;
}
// ファイルをオープンする。
// fileaddr: Human68k ファイル名のゲストアドレス
// atr: Human68k の属性
// mode: unix open mode
// 戻り値は Human68k fileno。
int32
Human68kDevice::OpenFile(uint32 fileaddr, uint16 atr, int mode)
{
int32 fileno {};
Human68kDevice::File *f = NULL;
// 空いてるエントリを検索して Human fileno を取得。
for (int i = 0; i < FilesCount; i++) {
if (Files[i].fd == -1) {
fileno = i;
f = &Files[fileno];
break;
}
}
if (f == NULL) {
return -1;
}
// ファイル名変換。
std::string filename;
for (uint8 c; (c = mainbus->HVRead1(fileaddr++)) != '\0'; ) {
if (c < 32 || c >= 127 || c == 0x5c) {
char hex[16];
snprintf(hex, sizeof(hex), "%02X", c);
filename += std::string(hex);
} else {
filename += c;
}
}
int fd = open(filename.c_str(), mode, 0666);
if (fd == -1) {
putmsg(1, "OpenFile: %s: %s", filename.c_str(), strerror(errno));
return -1;
}
f->fd = fd;
f->filename = filename;
putmsg(1, "OpenFile: name=\"%s\" mode=$%x -> fileno=%u",
filename.c_str(), mode, fileno);
return fileno;
}
// ファイルをクローズする。
// fileno: Human68k fileno
int32
Human68kDevice::CloseFile(int32 fileno)
{
Human68kDevice::File *f;
if (fileno <= 0 || fileno >= FilesCount) {
putmsg(1, "CloseFile: fileno=%d is not opened", fileno);
return -1;
}
f = &Files[fileno];
putmsg(1, "CloseFile: %d \"%s\"", fileno, f->filename.c_str());
if (f->fd > 2) {
close(f->fd);
}
f->fd = -1;
f->filename.clear();
return 0;
}
// ファイルに書き出す。
// fileno: Human68k fileno
// dataaddr: データのゲストアドレス
// size: データ長
int32
Human68kDevice::WriteFile(int32 fileno, uint32 dataaddr, uint32 size)
{
Human68kDevice::File *f;
if (fileno <= 0 || fileno >= FilesCount) {
putmsg(1, "WriteFile: fileno=%d is not opened", fileno);
return -1;
}
f = &Files[fileno];
if (f->fd < 0) {
return -1;
}
std::vector<uint8> buf(size);
mainram->ReadMem(dataaddr, buf.data(), size);
int32 rv = write(f->fd, buf.data(), size);
return rv;
}
// 文字列をファイルに書き出す。
// fileno: Human68k fileno
// dataaddr: data addr (guest VA)
void
Human68kDevice::FputsFile(int32 fileno, uint32 dataaddr)
{
Human68kDevice::File *f;
if (fileno <= 0 || fileno >= FilesCount) {
putmsg(1, "FputsFile: fileno=%d is not opened", fileno);
return;
}
f = &Files[fileno];
if (f->fd < 0) {
return;
}
std::vector<uint8> buf;
for (uint8 c; (c = mainbus->HVRead1(dataaddr++)) != '\0'; ) {
buf.push_back(c);
}
if (buf.empty() == false) {
write(f->fd, buf.data(), buf.size());
}
}
// ファイルモードを取得する。
// fileno: Human68k fileno
// 戻り値: <0 Humak68k error code
// : >=0 unix file mode
int
Human68kDevice::GetFileMode(int32 fileno)
{
Human68kDevice::File *f;
if (fileno <= 0 || fileno >= FilesCount) {
return -2 /* FILE_NOT_FOUND */;
}
f = &Files[fileno];
if (f->fd < 0) {
return -2 /* FILE_NOT_FOUND */;
}
int fl = fcntl(f->fd, F_GETFL);
int accmode = fl & O_ACCMODE;
return accmode;
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.