File:  [Isaki's NoNo m68k/m88k emulator] / nono / hd64180 / hd64180core.cpp
Revision 1.1.1.7 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:06:01 2026 UTC (3 months ago) by root
Branches: MAIN, Isaki
CVS tags: v027, HEAD
nono 1.7.0

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

//
// HD647180 コア
//

#include "mpu64180.h"
#include "debugger.h"
#include "event.h"
#include "hd64180.h"
#include "hd647180.h"
#include "scheduler.h"

#define OP_DEF(name)	void __CONCAT(MPU64180Device::op_,name)()
#define OP_FUNC(name)	__CONCAT(op_,name)()
#include "hd64180ops.cpp"

void
MPU64180Device::ExecNormal(Event *ev)
{
	uint64 cycle_start = used_cycle;

	ppc = reg.pc;
	ixiy = USE_HL;
	ixd = -1;
	ops.clear();
	intcheck = true;

	// R レジスタは命令ごとにカウント。7bit マスクは読み出し時に行う。
	// 命令の前か後かは分からないが。
	reg_r++;

	// 1バイト目処理
	op = Fetch1();
	if (__predict_false(opmode == OpMode::Halt)) {
		// ホールトモードなら PC を戻し続けるだけ
		reg.pc--;
		CYCLE(3);
	} else {
		switch (op) {
#include "hd64180switch_00.inc"
		 default:
			__unreachable();
		}
	}

	// 割り込みチェック。
	if (__predict_true(intcheck)) {
		// intmap が非ゼロなら割り込み発生中。
		if (GetIEF1() == true && __predict_false(intmap != 0)) {
			DoInterrupt();
		}
	} else {
		// この命令境界での割り込みチェックを飛ばす。次からは行う。
		intcheck = true;
	}

	int cycle = used_cycle - cycle_start;
	ev->time = cycle * clock_tsec;
	scheduler->StartEvent(ev);
}

// 不当命令。
// pos は不当命令になったバイト位置。2バイト目なら2。
void
MPU64180Device::op_illegal(int pos)
{
	// ログ表示
	std::string str;
	for (auto o : ops) {
		str += string_format(" %02x", o);
	}
	putlog(1, "Undefined opcode%s", str.c_str());

	itc_trap = true;
	// 3バイト目なら UFO ビットをセット。
	itc_ufo = (pos == 3);

	// XXX CYCLE

	// TRAP 割り込みはマスク不可能。
	// IEF[12] not affected

	// (1) ITC の TRAP ビットを 1 にする
	// (2) 未定義 opcode 位置の PC をスタックに保存する
	// (3) ベクタを 0000H にする

	// PC は未定義命令の先頭番地ではなく、未定義 opcode だった位置?
	// nono の実装ではフェッチと同時にインクリメントしているので、
	// 1つ前でいい? HD64180.pdf p38-39
	if (pos == 2) {
		Push2(reg.pc - 1);
	} else {
		// DD CB d OP xx
		//            ^ PC
		// d の位置をスタックに積む
		Push2(reg.pc - 2);
	}
	ExceptionDirect(HD647180::IntmapTRAP, 0x0000);
}

// 分岐
void
MPU64180Device::Jump(uint16 addr)
{
	reg.pc = addr;

	// 命令本体(オペランドを含まない)を16ビット左詰めにする。
	// 分岐命令は1バイト命令か ED XX か DD/FD XX の2バイト命令しかないはず。
	// 上位ワードは $0000。
	uint32 info;
	if (ops[0] == 0xed || ops[0] == 0xdd || ops[0] == 0xfd) {
		info = (ops[0] << 8) | ops[1];
	} else {
		info = (ops[0] << 8);
	}

	brhist.AddEntry(GetPPC(), addr, info);
}

//
// 割り込み。
//
// TRAP         0000H
// NMI          0066H
// INT0 mode0   命令をデータバスからフェッチ
// INT0 mode1   0038H
// INT0 mode2   Vectored (上位8bit が I レジスタ、下位8bit はデータバス)
// INT1         Vectored (I + IL + 固定コード)
// INT2         Vectored (〃)
// 内蔵割り込み Vectored (〃)

// 内部の割り込みマップ (Intmap) をアサートする。
void
MPU64180Device::AssertIntmap(int source)
{
	uint32 oldmap;
	uint32 srcmap;

	oldmap = intmap;
	srcmap = 1U << (31 - source);
	intmap |= srcmap;

	if ((oldmap ^ intmap) != 0) {
		// 立ち上がりエッジでカウント
		int_counter[source]++;

		// ホールトモード、スリープモードなら解除
		if (__predict_false((int)opmode >= (int)OpMode::Halt)) {
			if (opmode == OpMode::Halt) {
				// ホールトモードなら解除するだけ。
				// XXX 割り込みは一つ次の命令境界で処理されるけど、いいか?
				opmode = OpMode::Normal;
			} else {
				// スリープモードなら通常モードに戻す。
				EnterWakeup();
			}
		}
	}
}

// 内部の割り込みマップ (Intmap) をネゲートする。
void
MPU64180Device::NegateIntmap(int source)
{
	uint32 oldmap;
	uint32 srcmap;

	oldmap = intmap;
	srcmap = 1U << (31 - source);
	intmap &= ~srcmap;

	if ((oldmap ^ intmap) != 0) {
		// 今は立ち下がりで何もすることはない。
	}
}

// INT0 信号線をアサートする。(PIO1 から呼ばれる)
void
MPU64180Device::AssertINT0()
{
	AssertIntmap(HD647180::IntmapINT0);
}

// INT0 信号線をネゲートする。
void
MPU64180Device::NegateINT0()
{
	NegateIntmap(HD647180::IntmapINT0);
}

// ASCIn 割り込みをアサート/ネゲートする。
// IEF1 はこちらで加味する。
void
MPU64180Device::ChangeASCIInt(uint n, bool asci_req)
{
	uint32 map = HD647180::IntmapASCI(n);

	if ((asci_req && GetIEF1())) {
		AssertIntmap(map);
	} else {
		NegateIntmap(map);
	}
}

// マスカブル割り込みを処理する。(命令境界で呼ばれる)
void
MPU64180Device::DoInterrupt()
{
	// まず割り込み禁止。
	ief1 = false;
	ief2 = false;

	// 優先順位の最も高い割り込みを一つ選択。
	assert(intmap != 0);
	uint source = __builtin_clz(intmap);

	// TRAP はここに来ない。NMI は LUNA では未接続のため未実装。
	assert(source != HD647180::IntmapTRAP);
	assert(source != HD647180::IntmapNMI);

	if (source == HD647180::IntmapINT0) {
		// INT0 はモードによって分岐方法が異なる。

		// ただし mode0 と mode2 は使われてないので未対応。
		if (int0mode != 1) {
			PANIC("INT0 on mode%u (NOT IMPLEMENTED)", int0mode);
		}

		// mode1 は固定番地へ
		Push2(reg.pc);
		ExceptionDirect(source, 0x0038);
	} else {
		// 残りはすべてベクタ方式。

		Push2(reg.pc);
		ExceptionVector(source);
	}

	// XXX あとで見る
	// HD64180.pdf, p45. Figure 2.7.7
	CYCLE(11);
}

// 例外によって new_addr に直接ジャンプする。
// (HD64180 系の用語では例外ではなくて割り込みだが)
// source は Intmap*。
void
MPU64180Device::ExceptionDirect(int source, uint32 new_addr)
{
	// 例外発生とジャンプを記録。
	last_vector = source;
	brhist.AddEntry(GetPPC(), new_addr, 0x80000000 | source);
	exhist.AddEntry(GetPPC(), new_addr, 0x80000000 | source);

	reg.pc = new_addr;
}

// 例外ごとに固有のベクタにジャンプする。
// (HD64180 系の用語では例外ではなくて割り込みだが)
// source は Intmap*。
void
MPU64180Device::ExceptionVector(int source)
{
	// 固定コード
	static uint16 vector_offset[] = {
		0,		// 0:TRAP
		0,		// 1:NMI
		0,		// 2:INT0

		0x00,	// 3:INT1
		0x02,	// 4:INT2
		0x12,	// 5:Input Capture
		0x14,	// 6:Output Compare
		0x16,	// 7:Timer Overflow
		0x04,	// 8:Timer0 (PRT0)
		0x06,	// 9:Timer1 (PRT1)
		0x08,	// 10:DMA0
		0x0a,	// 11:DMA1
		0x0c,	// 12:CSIO
		0x0e,	// 13:ASCI0
		0x10,	// 14:ASCI1
	};

	// 例外からベクタアドレスを求める。ベクタアドレスは以下の構成。
	//
	//   F  E  D  C  B  A  9  8  7  6  5  4  3  2  1  0
	// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
	// |      I レジスタ       |  IL    |  固定コード  |
	// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
	assert(source < countof(vector_offset));
	uint32 vec_addr = (reg_i << 8) | intvec_low | vector_offset[source];

	// 例外発生を記録。
	last_vector = source;
	brhist.AddEntry(GetPPC(), -1, 0x80000000 | source);
	exhist.AddEntry(GetPPC(), -1, 0x80000000 | source);

	// ジャンプ先を読み込む
	uint32 new_addr = Read2(vec_addr);

	// ベクタジャンプを記録。
	brhist.AddEntry(vec_addr, new_addr, -1);

	reg.pc = new_addr;
}


//
// hd64180flag
//

// F レジスタの値を取得する
uint8
hd64180flag::Get() const
{
	uint8 f = 0;

	if (IsS())	f |= HD64180::FlagS;
	if (IsZ())	f |= HD64180::FlagZ;
	if (b5)		f |= 0x20;
	if (IsH())	f |= HD64180::FlagH;
	if (b3)		f |= 0x08;
	if (IsPV())	f |= HD64180::FlagPV;
	if (IsN())	f |= HD64180::FlagN;
	if (IsC())	f |= HD64180::FlagC;

	return f;
}

// F レジスタに代入する
void
hd64180flag::Set(uint8 val)
{
	SetS(val & HD64180::FlagS);
	SetZ(val & HD64180::FlagZ);
	b5 = (val & 0x20);
	SetH(val & HD64180::FlagH);
	b3 = (val & 0x08);
	SetPV(val & HD64180::FlagPV);
	SetN(val & HD64180::FlagN);
	SetC(val & HD64180::FlagC);
}

// fff (0..7) の条件が成立すれば true を返す。
bool
hd64180flag::IsCond(uint fff) const
{
	switch (fff) {
	 case 0:	return !IsZ();	// NZ
	 case 1:	return IsZ();	// Z
	 case 2:	return !IsC();	// NC
	 case 3:	return IsC();	// C
	 case 4:	return !IsPV();	// PO
	 case 5:	return IsPV();	// PE
	 case 6:	return !IsS();	// P
	 case 7:	return IsS();	// M
	 default:
		PANIC("invalid cond = %u", fff);
	}
}

unix.superglobalmegacorp.com

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