File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / human68k.cpp
Revision 1.1.1.18 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:55 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, v026, HEAD
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;
}

unix.superglobalmegacorp.com

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