|
|
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;
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.