|
|
nono 1.2.0
// vi:set ts=4:
//
// nono
// Copyright (C) 2024 nono project
// Licensed under nono-license.txt
//
#include "runx.h"
#define CCR_X (0x0010)
#define CCR_N (0x0008)
#define CCR_Z (0x0004)
#define CCR_V (0x0002)
#define CCR_C (0x0001)
#define SUPERVISOR_OP() do { \
if ((emul.sr & 0x2000) == 0) \
goto unpriv; \
} while (0)
#define push2(data) do { \
RegA(7) -= 2; \
writemem2(RegA(7), (data)); \
} while (0)
#define push4(data) do { \
RegA(7) -= 4; \
writemem4(RegA(7), (data)); \
} while (0)
static uint32 exception_common(struct reg *, uint32, uint32, uint32);
#define pop2() ({ \
uint32 data_ = readmem2(RegA(7)); \
RegA(7) += 2; \
data_; \
})
#define pop4() ({ \
uint32 data_ = readmem4(RegA(7)); \
RegA(7) += 4; \
data_; \
})
// MOVE.* 系の CCR をセットする共通部分。
static void
acc_move(struct reg *reg, bool z, bool n)
{
RegCCR &= ~(CCR_N | CCR_Z | CCR_V | CCR_C);
if (z)
RegCCR |= CCR_Z;
if (n)
RegCCR |= CCR_N;
}
// MOVE.B 系の CCR をセットする。
static void
acc_move_1(struct reg *reg, uint32 data)
{
bool z = ((data & 0xff) == 0);
bool n = (data & 0x80);
acc_move(reg, z, n);
}
// MOVE.L 系の CCR をセットする。
static void
acc_move_4(struct reg *reg, uint32 data)
{
bool z = (data == 0);
bool n = (data & 0x8000'0000);
acc_move(reg, z, n);
}
// スタックフレームフォーマット0 の例外を起こす。
// pc はスタックに積む PC (例外ごとにこの命令か次の命令かが違う)。
uint32
exception_format0(struct reg *reg, uint32 vector, uint32 pc)
{
return exception_common(reg, 0, vector, pc);
}
// スタックフレームフォーマット2 の例外を起こす。
uint32
exception_format2(struct reg *reg, uint32 vector)
{
// XXX ここに積む INSTRUCTION ADDRESS は今の所取り出す方法がない。
// たぶん見てないのでへーきへーき。
push4(RegPC - 2);
return exception_common(reg, 2, vector, RegPC);
}
static uint32
exception_common(struct reg *reg, uint32 frame, uint32 vector, uint32 pc)
{
// 本当はスタックに積む前だが、ここでは影響ない。
SetSR(GetSR | 0x2000);
push2((frame << 12) | (vector << 2));
push4(pc);
push2(GetSR);
uint32 addr = readmem4(emul.vbr + (vector << 2));
return addr;
}
// inst は命令の1ワード目 (16bit 下詰め)。
// 戻り値は更新した nextpc。
uint32
exec_op(pid_t pid, struct reg *reg, int signo, uint32 inst)
{
uint32 inst2;
uint32 addr;
uint32 data;
uint n;
uint m;
uint oplen;
oplen = 2;
switch (inst) {
case 0x007c: // ori.w #imm,sr
SUPERVISOR_OP();
data = readmem2(RegPC + 2);
TRACE(RegPC, "ori.w #$%04x,sr", data);
data |= GetSR;
SetSR(data);
oplen = 4;
break;
case 0x06fa: // callm
TRACE(RegPC, "callm");
return exception_format0(reg, 4, RegPC);
case 0x21c9: // move.l an,abs.w
n = inst & 7;
addr = (int32)(int16)readmem2(RegPC + 2);
TRACE(RegPC, "move.l a%u,$%04x.w", n, addr);
data = RegA(n);
acc_move_4(reg, data);
writemem4(addr, data);
oplen = 4;
break;
case 0x21df: // move.l (an)+,abs.w
n = inst & 7;
addr = (int32)(int16)readmem2(RegPC + 2);
TRACE(RegPC, "move.l (a%u)+,$%04x.w", n, addr);
data = readmem4(RegA(n));
acc_move_4(reg, data);
writemem4(addr, data);
RegA(n) += 4;
oplen = 4;
break;
case 0x2855: // movea.l (an),am
n = inst & 7;
m = (inst >> 9) & 7;
TRACE(RegPC, "move.l (a%u),a%u", n, m);
data = readmem4(RegA(n));
RegA(m) = data;
break;
case 0x2a88: // move.l an,(am)
case 0x2a89:
case 0x2a8a:
case 0x2a8b:
case 0x2a8c:
case 0x2a8d:
case 0x2a8e:
case 0x2a8f:
n = inst & 7;
m = (inst >> 9) & 7;
TRACE(RegPC, "move.l a%u,(a%u)", n, m);
data = RegA(n);
acc_move_4(reg, data);
writemem4(RegA(m), data);
break;
case 0x2f38: // move.l abs.w,-(an)
n = (inst >> 9) & 7;
addr = (int32)(int16)readmem2(RegPC + 2);
TRACE(RegPC, "move.l $%04x.w,-(a%u)", addr, n);
data = readmem4(addr);
acc_move_4(reg, data);
RegA(n) -= 4;
writemem4(RegA(n), data);
oplen = 4;
break;
case 0x40c0: // move.w sr,dn
n = inst & 7;
if ((GetSR & 0x2000)) {
TRACE(RegPC, "move.w sr,d%u", n);
data = GetSR;
RegD(n) = (RegD(n) & 0xffff0000) | data;
} else {
// Human68k が move from sr を move from ccr に書き換えて
// この命令から再実行。
TRACE(RegPC, "Patch move.w sr,d%u -> move.w ccr,d%u", n, n);
writemem2(RegPC, inst | 0x0200);
oplen = 0;
}
break;
case 0x40e0:
case 0x40e1:
case 0x40e2:
case 0x40e3:
case 0x40e4:
case 0x40e5:
case 0x40e6:
case 0x40e7: // move.w sr,(a7)
SUPERVISOR_OP();
n = inst & 7;
TRACE(RegPC, "move.w sr,(a%u)", n);
addr = RegA(n);
data = GetSR;
writemem2(addr, data);
break;
case 0x46df: // move.w (a7)+,sr
SUPERVISOR_OP();
n = inst & 7;
TRACE(RegPC, "move.w (a%u)+,sr", n);
addr = RegA(n);
RegA(n) += 2;
data = readmem2(addr);
SetSR(data);
break;
case 0x4e73: // rte
{
uint32 sr = pop2();
uint32 pc = pop4();
uint32 frame = pop2();
TRACE(RegPC, "rte (frame=$%x, retpc=$%06x)", frame, pc);
switch (frame >> 12) {
case 0:
SetSR(sr);
return pc;
case 2:
// +08.L INSTRUCTION ADDRESS を読み捨てる
RegA(7) += 4;
SetSR(sr);
return pc;
default:
errx(1, "%06x: rte: Unimplemented format $%x", RegPC, frame);
break;
}
break;
}
case 0x4e7a:
inst2 = readmem2(RegPC + 2);
n = inst2 >> 12;
switch (inst2 & 0xfff) {
case 0x0801:
// movec.l vbr,d0
TRACE(RegPC, "movec.l vbr,%c%u", (n < 8) ? 'd' : 'a', n & 7);
RegR(n) = emul.vbr;
break;
case 0x0802:
// movec.l caar,d0
TRACE(RegPC, "movec.l caar,%c%u", (n < 8) ? 'd' : 'a', n & 7);
RegR(n) = emul.caar;
break;
default:
warnx("%06x: Illegal instruction %04x %04x", RegPC, inst, inst2);
return -1;
}
oplen = 4;
break;
case 0xf200 ... 0xf3ff: // FPU
if (signo == SIGILL) {
inst2 = readmem2(RegPC + 2);
TRACE(RegPC, "Illegal FP instruction %04x %04x", inst, inst2);
return exception_format0(reg, 11, RegPC);
} else {
// default へ
}
// FALLTHROUGH
default:
if (signo == SIGILL) {
warnx("%06x: Illegal instruction %04x", RegPC, inst);
} else {
// SIGSEGV なら fault address を取り出して表示。
struct ptrace_siginfo psi;
if (ptrace(PT_GET_SIGINFO, pid, &psi, sizeof(psi)) < 0) {
warn("ptrace(PT_GET_SIGINFO)");
}
warnx("%06x: SIGSEGV caught (instruction %04x, address $%06x)",
RegPC, inst, (uint)(uintptr_t)psi.psi_siginfo.si_addr);
}
return -1;
}
return RegPC + oplen;
unpriv:
warnx("%06x: Unprivledged instruction %04x", RegPC, inst);
return -1;
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.