File:  [Isaki's NoNo m68k/m88k emulator] / nono / util / runx / doscall.cpp
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:27 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, v026, v025, v024, v023, v022, v021, v020, HEAD
nono 1.0.0

// vi:set ts=4:

//
// nono
// Copyright (C) 2024 nono project
// Licensed under nono-license.txt
//

#include "runx.h"
#include <fcntl.h>
#include <string.h>
#include <time.h>

#define MAXFILES	(10)	// 同時にオープン出来るファイルハンドル数。適当

struct filedesc {
	int fd;
	char *filename;
};

static int32  open_file(uint32, uint32, int);
static uint32 write_file(uint32, uint32, uint32);
static int  get_file_mode(uint32);
static uint32 close_file(uint32);

static struct filedesc files[MAXFILES];

// 戻り値は 1 なら EXIT 処理、0 なら継続。
int
doscall(pid_t pid, uint32 callno, struct reg *reg)
{
	switch (callno) {
	 case 0x00:	// _EXIT
		TRACE(RegPC, "DOS _EXIT");
		// 関数 p() からの戻り値を 0 にする。
		RegD(0) = 0;
		return 1;

	 case 0x09:	// _PRINT
	 {
		uint32 msg;
		msg = ptrace(PT_READ_D, pid, (void *)RegA(7), 0);
		TRACE(RegPC, "DOS _PRINT($%06x)", msg);
		if (msg == (uint32)-1) {
			err(1, "doscall(%x): PT_READ_D", callno);
		}
		for (;;) {
			uint32 ch32 = ptrace(PT_READ_D, pid, (void *)msg, 0);
			for (int i = 0; i < 4; i++) {
				if ((ch32 & 0xff000000) == 0) {
					return 0;
				}
				fputc(ch32 >> 24, stdout);
				ch32 <<= 8;
			}
			msg += 4;
		}
		break;
	 }

	 case 0x1e:	// _FPUTS
	 {
		uint32 mesptr = readmem4(RegA(7));
		uint32 fileno = readmem2(RegA(7) + 4);
		TRACE(RegPC, "DOS _FPUTS($%06x, fileno=%u)", mesptr, fileno);

		uint32 m = mesptr;
		for (uint32 c; (c = readmem1(m)) != '\0'; m++)
			;
		uint32 size = m - mesptr;

		write_file(fileno, mesptr, size);
		break;
	 }

	 case 0x20:	// _SUPER
	 {
		uint32 data = readmem4(RegA(7));

		if (data == 0 && (GetSR & 0x2000) == 0) {
			TRACE(RegPC, "DOS _SUPER(0)");
			emul.usp = RegA(7);
			SetSR(GetSR | 0x2000);
			data = emul.usp;
			RegD(0) = emul.ssp;
			RegA(7) = data;
		} else if ((GetSR & 0x2000) != 0) {
			TRACE(RegPC, "DOS _SUPER($%08x)", data);
			emul.ssp = data;
			SetSR(GetSR & ~0x2000);
			RegA(7) = emul.usp;
		} else {
			TRACE(RegPC, "DOS _SUPER($%08x): XXX", data);
			RegD(0) = -1;
		}
		break;
	 }

	 case 0x25:	// _INTVCS
	 {
		uint16 intno = readmem2(RegA(7));
		uint32 addr  = readmem4(RegA(7) + 2);
		TRACE(RegPC, "DOS _INTVCS($%04x, $%06x)", intno, addr);

		uint32 vecaddr = (intno & 0xff) * 4;
		if (intno <= 0xff) {
			RegD(0) = readmem4(vecaddr);
			writemem4(vecaddr, addr);
		} else {
			vecaddr += 0xd000;
			RegD(0) = readmem4(vecaddr);
			writemem4(vecaddr, addr);
		}
		break;
	 }

	 case 0x27:	// _GETTIM2
	 {
		struct tm tm;
		time_t now = time(NULL);
		localtime_r(&now, &tm);
		TRACE(RegPC, "DOS _GETTIM2");
		RegD(0) = (tm.tm_hour << 16)
				| (tm.tm_min  << 8)
				|  tm.tm_sec;
		break;
	 }

	 case 0x2a:	// _GETDATE
	 {
		struct tm tm;
		time_t now = time(NULL);
		localtime_r(&now, &tm);
		TRACE(RegPC, "DOS _GETDATE");
		RegD(0) = (tm.tm_wday << 16)
				| ((tm.tm_year + 1900 - 1980) << 9)
				| ((tm.tm_mon + 1) << 5)
				|   tm.tm_mday;
		break;
	 }

	 case 0x35:	// _INTVCG
	 {
		uint16 intno = readmem2(RegA(7));
		TRACE(RegPC, "DOS _INTVCG($%04x)", intno);
		uint32 vecaddr = (intno & 0xff) * 4;
		if (intno <= 0xff) {
			RegD(0) = readmem4(vecaddr);
		} else {
			vecaddr += 0xd000;
			RegD(0) = readmem4(vecaddr);
		}
		break;
	 }

	 case 0x3c:	// _CREATE
	 {
		uint32 filename = readmem4(RegA(7));
		uint32 attr     = readmem2(RegA(7) + 4);

		TRACE(RegPC, "DOS _CREATE($%06x, attr=$%04x)", filename, attr);
		int32 fileno = open_file(filename, attr,
			O_CREAT | O_TRUNC | O_RDWR | O_SYNC);
		RegD(0) = fileno;
		break;
	 }

	 case 0x3d:	// _OPEN
	 {
		uint32 filename = readmem4(RegA(7));
		uint32 mode     = readmem2(RegA(7) + 4);
		TRACE(RegPC, "DOS _OPEN($%06x, mode=$%04x)", filename, mode);

		int unix_mode = 0;
		bool ok = true;

		switch (mode & 0x03) {
		 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 = open_file(filename, 0, unix_mode | O_SYNC);
			RegD(0) = fileno;
		}
		break;
	 }

	 case 0x3e:	// _CLOSE
	 {
		uint16 fileno  = readmem2(RegA(7));

		TRACE(RegPC, "DOS _CLOSE(%u)", fileno);
		RegD(0) = close_file(fileno);
		break;
	 }

	 case 0x40:	// _WRITE
	 {
		uint16 fileno  = readmem2(RegA(7));
		uint32 dataptr = readmem4(RegA(7) + 2);
		uint32 size    = readmem4(RegA(7) + 6);

		TRACE(RegPC, "DOS _WRITE(%u, $%06x, len=$%06x)", fileno, dataptr, size);
		RegD(0) = write_file(fileno, dataptr, size);
		break;
	 }

	 case 0x44:	// _IOCTRL
	 {
		uint32 mode = readmem2(RegA(7));
		uint32 fileno;
		switch (mode) {
		 case 0:	// IOCTRLGT 装置情報取得
			fileno = readmem2(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;
			}
			TRACE(RegPC, "DOS _IOCTRL(mode=0(IOCTRLGT), fileno=%u) -> $%x",
				fileno, RegD(0));
			break;

		 case 7:	// IOCTRLOS 出力ステータス取得
			fileno = readmem2(RegA(7) + 2);
			if (fileno == 0) {
				RegD(0) = 0;
			} else if (fileno == 1 || fileno == 2) {
				RegD(0) = -1;
			} else {
				int filemode = get_file_mode(fileno);
				if (filemode < 0) {
					RegD(0) = filemode;
				} else if (filemode == O_WRONLY || filemode == O_RDWR) {
					RegD(0) = -1;
				} else {
					RegD(0) = 0;
				}
			}
			TRACE(RegPC, "DOS _IOCTRL(mode=7(IOCTRLOS), fileno=%u) -> $%x",
				fileno, RegD(0));
			break;

		 default:
			errx(1, "%06x: DOS _IOCTRL(mode=%u) not implemented", RegPC, mode);
		}
		break;
	 }

	 case 0x4a:	// _SETBLOCK
	 {
		uint32 memptr = readmem4(RegA(7));
		uint32 len    = readmem4(RegA(7) + 4);

		TRACE(RegPC, "DOS _SETBLOCK(ptr=$%06x, len=$%06x)", memptr, len);
		// 何もせずに出来たという。
		RegD(0) = len;
		break;
	 }

	 case 0xff:	// _CHANGE_PR
		TRACE(RegPC, "DOS _CHANGE_PR");
		// 何もしない。
		break;

	 default:
		errx(1, "%06x: Unsupported DOS call $%x", RegPC, callno);
	}
	return 0;
}

// DOS CALL 関連の初期化。
int
init_doscall()
{
	emul.ssp = 0x10000;

	files[0].fd = 0;
	files[1].fd = 1;
	files[2].fd = 2;
	for (int i = 3; i < countof(files); i++) {
		files[i].fd = -1;
	}

	return 0;
}

// ファイルオープン。
// fileaddr: Human68k ファイル名の先頭ゲストアドレス
// attr:     Human68k 属性
// 戻り値はオープンしたファイルハンドル。負数ならエラーコード。
static int32
open_file(uint32 fileaddr, uint32 attr, int mode)
{
	int32 fileno;
	struct filedesc *f = NULL;

	// 空きエントリを探す。
	for (int i = 0; i < countof(files); i++) {
		if (files[i].fd == -1) {
			fileno = i;
			f = &files[fileno];
			break;
		}
	}
	if (f == NULL) {
		errno = EMFILE;
		warn("%s", __func__);
		return -4;
	}

	char filename[256];
	char *p = filename;
	for (int i = 0; i < sizeof(filename) - 3; i++) {
		uint c = readmem1(fileaddr++);
		if (c == 0) {
			break;
		}
		if (c < 32 || c >= 127 || c == 0x5c) {
			int n = snprintf(p, sizeof(filename) - 3 - (p - filename),
				"%02x", c);
			p += n;
		} else {
			*p++ = c;
		}
	}
	*p = '\0';

	DEBUG(2, "open_file: \"%s\" -> %u", filename, fileno);
	int fd = open(filename, mode, 0666);
	if (fd < 0) {
		if (errno != ENOENT) {
			warn("open_file: %s", filename);
		}
		return -1;
	}

	f->fd = fd;
	f->filename = strdup(filename);

	return fileno;
}

// ファイルハンドルへの書き出し。
static uint32
write_file(uint32 fileno, uint32 addr, uint32 size)
{
	if (fileno >= countof(files)) {
		errno = ERANGE;
		warn("%s: fileno=%d", __func__, fileno);
		return -14;
	}

	struct filedesc *f = &files[fileno];
	if (f->fd < 0) {
		errno = EINVAL;
		warn("%s: fileno=%d", __func__, fileno);
		return -6;
	}

	char buf[size];
	for (int i = 0; i < size; i++) {
		buf[i] = readmem1(addr + i);
	}
	int n = write(f->fd, buf, size);
	if (n < 0) {
		warn("%s: fileno=%d", __func__, fileno);
		return -1;
	}
	return size;
}

static int
get_file_mode(uint32 fileno)
{
	struct filedesc *f;

	if (fileno >= countof(files)) {
		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;
}

// ファイルハンドルのクローズ。
static uint32
close_file(uint32 fileno)
{
	if (fileno >= countof(files)) {
		errno = ERANGE;
		warn("%s: fileno=%d", __func__, fileno);
		return -14;
	}

	struct filedesc *f = &files[fileno];
	if (f->fd < 0) {
		errno = EINVAL;
		warn("%s: fileno=%d", __func__, fileno);
		return -6;
	}

	if (f->fd >= 3) {
		close(f->fd);
	}
	return  0;
}

unix.superglobalmegacorp.com

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