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

unix.superglobalmegacorp.com

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