File:  [Isaki's NoNo m68k/m88k emulator] / nono / m680x0 / m68030subr.cpp
Revision 1.1.1.9 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:08 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v016, v015, HEAD
nono 0.3.0

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

#include "m68030core.h"
#include "m68030bus.h"
#include "m68030fpu.h"
#include "debugger.h"
#include "mpu680x0.h"

static void m68030_double_bus_fault(m68kcpu *, const char *);

// CPU コアの領域を確保して初期化
m68kcpu *
m68030_init(MPU680x0Device *dev_)
{
	m68kcpu *cpu;

	cpu = new m68kcpu();
	fpu_init(cpu);
	cpu->dev = dev_;

	for (int i = 0; i < 16; i++) {
		RegR(i) = 0xcccccccc;
	}
	// XXX ただし X68030 IPLROM には A6 を初期化せずに書き込んでいるバグが
	// あるため、A6 を概ね RAM のあるあたりに指定しておく…。
	RegA(6) = 0x000ccccc;

	return cpu;
}

// CPU の状態 (通常/STOP/HALT) を変更する
static void
m68030_change_state(m68kcpu *cpu, uint32 new_state)
{
	if (cpu->state != new_state) {
		cpu->state = new_state;
		// スケジューラに通知
		cpu->dev->NotifyCPUMode(cpu->state);
	}
}

// 二重バスフォールト
static void
m68030_double_bus_fault(m68kcpu *cpu, const char *where)
{
	m68030_change_state(cpu, m68kcpu::CPU_STATE_HALT);

	// コールバックがあれば呼び出す
	if (cpu->halt_callback) {
		cpu->halt_callback(cpu->halt_arg);
	}
}

// 電源オン
void
m68030_power_on(m68kcpu *cpu)
{
	// サイクルを初期化。
	cpu->used_cycle = 0;

	// 履歴を初期化。電源(再)投入時のみ行う。
	cpu->exhist.Clear();
	cpu->brhist.Clear();

	// この後起きるリセット例外で PC を初期化する前に参照することになるので
	// これだけここでも初期化しておく。
	RegPC = 0;
}

// SR (と CCR) をセットする。
// リセット例外以外から使う (リセット例外はやや異なるため)。
void
m68030_set_sr(m68kcpu *cpu, uint16 data)
{
	bool oldS = cpu->reg.s;
	bool oldM = cpu->reg.m;

	// 現在の SP を保存
	if (oldS == false) {
		RegUSP = RegA(7);
	} else if (oldM == false) {
		RegISP = RegA(7);
	} else {
		RegMSP = RegA(7);
	}

	CCR.Set(data);
	cpu->reg.s = ((data & M68K_SR_S) != 0);
	cpu->reg.m = ((data & M68K_SR_M) != 0);

	// 割り込みマスクレジスタを更新して..
	uint new_mask = (data >> 8) & 7;
	if (cpu->reg.intr_mask != new_mask) {
		cpu->reg.intr_mask = new_mask;

		// 次の命令境界で割り込みをチェックさせる
		cpu->dev->CheckIntr();
	}

	// 新しい SP を用意
	if (cpu->reg.s == false) {
		RegA(7) = RegUSP;
	} else if (cpu->reg.m == false) {
		RegA(7) = RegISP;
	} else {
		RegA(7) = RegMSP;
	}

	// バスの FC2 を変更
	cpu->bus.SetFC2(cpu->reg.s);

	// XXX 他に何か状態変えないといけないことあったっけか。
}

// 命令中に (An)+ で変化したアドレスレジスタを命令実行前の状態に巻き戻す。
// flag_pi は bit7..0 が save_pi[7..0] の A7..A0 に対応している。
void
m68030_restore_reg_pi(m68kcpu *cpu)
{
	for (int i = 7; cpu->flag_pi; i--) {
		if ((int8)cpu->flag_pi < 0) {
			RegA(i) = cpu->save_pi[i];
		}
		cpu->flag_pi <<= 1;
	}
}

// 命令中に -(An) で変化したアドレスレジスタを命令実行前の状態に巻き戻す。
// flag_pd は bit7..0 が save_pd[7..0] の A7..A0 に対応している。
void
m68030_restore_reg_pd(m68kcpu *cpu)
{
	for (int i = 7; cpu->flag_pd; i--) {
		if ((int8)cpu->flag_pd < 0) {
			RegA(i) = cpu->save_pd[i];
		}
		cpu->flag_pd <<= 1;
	}
}

// vector にジャンプする。vector はベクタ番号(アドレスではない)。
static inline void
m68030_jump_vector(m68kcpu *cpu, int vector)
{
	uint32 vecaddr;
	uint32 newpc;

	vecaddr = RegVBR + vector * 4;
	RegPC = vecaddr;
	newpc = m68030_fetch_32(cpu);
	// VBR 込みのジャンプ先が確定したところで今度はブランチ履歴に記録。
	// この時のブランチ元はベクタアドレス
	// (RegPC は fetch によって進んでしまうので先に保存)
	cpu->brhist.AddEntry(vecaddr | (cpu->reg.s ? 1 : 0), newpc, 0x8000);

	CHECK_ADDRESS_ERROR(newpc);
	RegPC = newpc;

	// 実際はここでパイプライン充填のために3ワードフェッチするので、
	// ベクタテーブルはフェッチ出来たけどそのジャンプ先がバスエラーに
	// なるケースはここでバスエラーが出なければいけないが、まだ
	// キャッシュを実装していないので、ここで1ワードフェッチしてみて
	// PC を戻すあほっぽい方法を取る。
	m68030_fetch_16(cpu);
	RegPC -= 2;
}

// 例外スタックフレーム $0 を生成する。
// vector はベクタ番号。
// pc はスタックに積むプログラムカウンタの値 (例外によって、この命令位置か
// 次の命令位置かが異なる)。
// sr は例外発生時の SR。
static inline void
m68030_generate_exframe_0(m68kcpu *cpu, int vector, uint32 pc, uint16 sr)
{
	// 例外スタックフレーム $0
	// SP+$00.W STATUS REGISTER
	//   +$02.L PROGRAM COUNTER		; 例外ごとに異なる
	//   +$06.W %0000 | VECTOR

	m68030_push_16(cpu, (0 << 12) | (vector * 4));
	m68030_push_32(cpu, pc);
	m68030_push_16(cpu, sr);
}

// 例外スタックフレーム $2 を生成する。
// vector はベクタ番号。
// スタックに積む PC は常に次の命令。
static inline void
m68030_generate_exframe_2(m68kcpu *cpu, int vector, uint16 sr)
{
	// 例外スタックフレーム $2
	// SP+$00.W STATUS REGISTER
	//   +$02.L PROGRAM COUNTER		; 次の命令位置
	//   +$06.W %0010 | VECTOR
	//   +$08.L INSTRUCTION ADDRESS	; 例外を起こした命令のアドレス

	m68030_push_32(cpu, RegPPC);	// 例外を起こした命令のアドレス
	m68030_push_16(cpu, (2 << 12) | (vector * 4));
	m68030_push_32(cpu, RegPC);		// 次の命令位置
	m68030_push_16(cpu, sr);
}

// 例外スタックフレーム $B を生成する。
static inline void
m68030_generate_exframe_b(m68kcpu *cpu, int vector, uint16 sr)
{
	uint16 ssw;
	uint32 fault_addr;
	uint32 stageBaddr;

	stageBaddr = RegPC + 2;
	// この後のスタック操作で bus が変化する前に保存
	fault_addr = cpu->bus.laddr;
	// DF ビットを立てておくと RTE した後この命令を再実行。
	ssw = cpu->bus.ssw | SSW_DF;

	// 例外スタックフレーム $B
	// SP+$00.W   STATUS REGISTER
	//   +$02.L   PROGRAM COUNTER
	//   +$06.W   %1011 | VECTOR
	//   +$08.W   (internal register)
	//   +$0a.W   SPECIAL STATUS REGISTER		; SSW
	//   +$0c.W   INSTRUCTION PIPE STAGE C
	//   +$0e.W   INSTRUCTION PIPE STAGE B
	//   +$10.L   DATA CYCLE FAULT ADDRESS
	//   +$14.W*2 (internal register)
	//   +$18.L   DATA OUTPUT BUFFER
	//   +$1c.W*4 (internal register, 4 words)
	//   +$24.L   STAGE B ADDRESS
	//   +$28.W*2 (internal register, 2 words)
	//   +$2c.L   DATA INPUT BUFFER
	//   +$30.W*3 (internal register, 3 words)
	//   +$36.W   VERSION# | internal information
	//   +$38.W*18 (internal register, 18 words)
	//   +$5a
	//	こっちから上に向かって PUSH

	// internal registers, 18 words
	// 内部レジスタ18ワードのうち、ここでは17ワード分を save_pd[8] (16ワード)、
	// flag_pd (1ワード) として使うことにする。
	// -(An) でバスエラーが起きると An は減算された状態になるが、DF ビットが
	// 立っていて再実行する時には現行構造では命令先頭から再実行せざるをえず、
	// そうするともう一度プリデクリメントが動いてしまう。そのため、RTE の時点
	// で DF=1 ならプリデクリメント発生前の An に戻すという処理をする。
	// RTE 時点で DF=0 なら命令を再実行しないため An も巻き戻してはならず、
	// そのためプリデクリメント分の巻き戻しは RTE 時点まで行えない。
	// 一方、ポストインクリメントのケースはこの少し下の
	// m68030_exception_buserr() 内のコメント参照。
	for (int i = 7; i >= 0; i--) {
		m68030_push_32(cpu, cpu->save_pd[i]);
	}
	m68030_push_16(cpu, cpu->flag_pd);
	RegA(7) -= 2;

	RegA(7) -= 2;						// VERSION#
	RegA(7) -= 6;
	m68030_push_32(cpu, 0);				// DATA INPUT BUFFER
	RegA(7) -= 4;
	m68030_push_32(cpu, stageBaddr);	// STAGE B ADDRESS
	RegA(7) -= 8;
	m68030_push_32(cpu, 0);				// DATA OUTPUT BUFFER
	RegA(7) -= 4;
	m68030_push_32(cpu, fault_addr);	// DATA CYCLE FAULT ADDRESS
	m68030_push_16(cpu, 0);				// INSTRUCTION PIPE STAGE B
	m68030_push_16(cpu, 0);				// INSTRUCTION PIPE STAGE C
	m68030_push_16(cpu, ssw);
	RegA(7) -= 2;
	m68030_push_16(cpu, 0xb000 | (vector << 2));
	// 本当は現在の PC だが、再実行のため PPC を積む。
	m68030_push_32(cpu, RegPPC);		// PC
	m68030_push_16(cpu, sr);			// 例外入り口で保存した SR
}

// 例外処理について。
//
// 0. リセット例外
//	リセット例外は外部から m68030_exception_reset() を呼び出す。
//
// 1. バスエラー例外
//	MMU による論理アドレスでのバスエラーは (m68030_mmu_translate_*() が
//	false を返した後) m68030bus.cpp ルーチンが (C++の) 例外をスローする。
//	物理アドレス空間アクセス後のバスエラーは (VM側の read*()/write*() が
//	(int64)-1 を返した後) m68030bus.cpp ルーチンが (C++) の例外をスローする。
//	どちらのケースも m68030core.cpp のメインループで catch し、その catch
//	ブロック内で m68030_exception() を呼び出す。
//
// 2. アドレスエラー例外
//	アドレスエラーは m68030_jump() 内でのみ発生するはずで、その場で
//	m68030_exception() を呼んで処理する。
//
// 3. 命令中で起きる例外
//	命令途中で起きる(確定的な)例外は m68030_exception() を呼び出す。
//	これに該当するのは
//	  o 特権違反
//	  o A系列/F系列命令
//	  o TRAP* 系命令、CHK/CHK2 命令
//	  o トレース例外 (未実装)
//	  o ゼロ除算?
//	  o MMU 構成例外
//	  o 不当命令 (*)
//	である。
//	ただし、不当命令についてはどこで不当命令と判定されるかによって
//	フローが複数存在することに注意。
//	  o 1ワード目が不当命令
//		-> 命令がそれぞれ m68030_exception() で処理。
//	  o 1ワード目の EA が不当命令
//		-> (C++の)例外をスローして catch 内で m68030_exception() を実行。
//	  o 2ワード目が不当命令
//		-> 命令がそれぞれ m68030_exception() で処理。
//
// 4. 割り込み例外
//	別コメント(下のほう)参照。
//

// リセット例外処理。
// 各レジスタを所定の状態にして、リセットベクタを読み込む。
// リセットベクタが読み込めなければ HALT 状態に移行。
// 戻り値は消費したサイクル数。
int
m68030_exception_reset(m68kcpu *cpu)
{
	uint64 cycle_start = cpu->used_cycle;

	// 例外履歴に記録 (例外発生はブランチ履歴にも記録)
	// m68k では今の所リセット例外発生時の PC にはあまり意味がないけど、
	// m88k と動作を揃えておく。
	// 履歴のリセットベクタは 0 の代わりに 1 を使う仕様になっている。
	const uint vector = 0;
	cpu->exhist.AddEntry(RegPC | 1, 0, 0x8000 | (vector + 1));
	cpu->brhist.AddEntry(RegPC | 1, 0, 0x8000 | (vector + 1));
	// デバッガに通知 (例外ブレーク用)。こっちのベクタは 0 のままでよい。
	debugger_notify_exception(vector);

	// RESET 信号線をネゲートしてから SP の読み込み開始まで 4クロック
	// (Section 7.8, Figure 7-64)
	cpu->used_cycle += 4;

	// STOP 状態とホールト状態を解除。
	m68030_change_state(cpu, m68kcpu::CPU_STATE_NORMAL);

	// 1. T0, T1 クリア
	// 2. S をセット、M をクリア
	// 3. 割り込みレベルを 7

	// ここは set_sr を使わずに設定する。
	cpu->reg.s = true;
	cpu->reg.m = false;
	cpu->bus.SetFC2(cpu->reg.s);
	// ここは割り込みレベル変化のチェックは不要でセットだけでいい。
	cpu->reg.intr_mask = 7;

	// 4. VBR をクリア
	RegVBR = 0;
	// 5. キャッシュ
	RegCACR = 0;	// XXX
	// 6. キャッシュエントリクリア
	// 7. TC:E、TTn:E をクリア
	cpu->SetTC(cpu->GetTC() & ~m68030TC::TC_E);
	cpu->SetTT(0, cpu->GetTT(0) & ~m68030TT::E);
	cpu->SetTT(1, cpu->GetTT(1) & ~m68030TT::E);
	// 8. リセットベクタ生成

	// 9. SP をロード
	// 10. PC をロード
	try {
		RegPC = 0;
		RegA(7) = m68030_fetch_32(cpu);
		uint32 pc = m68030_fetch_32(cpu);
		// 大丈夫のはずだけど m68030_fetch_32() が RegPC を書き換えるので一応
		RegPC = pc;
		// リセットベクタは常に 0, 4 番地から取得したことになっている
		cpu->brhist.AddEntry(0x00000004 | 1, RegPC, 0x8000);
	} catch (int) {
		m68030_double_bus_fault(cpu, "reset exception");
	}

	// SP のアドレスエラーはリセット例外ではチェックしないようだ。
	// PC のアドレスエラーは本来ならここにあるはずの3ワードのプリフェッチで
	// 行われるのだが、それがないので、とりあえずここで。
	if ((RegPC & 1) != 0) {
		m68030_double_bus_fault(cpu, "reset exception");
	}

	return cpu->used_cycle - cycle_start;
}

// 例外処理 (リセット・割り込み以外) を行う。
// vector はベクタ番号。
void
m68030_exception(m68kcpu *cpu, int vector)
{
	// バスエラー・アドレスエラーは別で処理。
	if (vector <= M68K_EXCEP_ADDRERR) {
		m68030_exception_buserr(cpu, vector);
		return;
	}

	// 例外履歴に記録 (例外発生はブランチ履歴にも記録)
	uint32 from = RegPPC | (cpu->reg.s ? 1 : 0);
	cpu->exhist.AddEntry(from, 0, 0x8000 | vector);
	cpu->brhist.AddEntry(from, 0, 0x8000 | vector);
	// デバッガに通知 (例外ブレーク用)
	debugger_notify_exception(vector);

	// 状態を保存
	uint16 sr = RegSR;

	// S を立てて T1,T0 を落とす
	m68030_set_sr(cpu, (RegSR | M68K_SR_S) & ~M68K_SR_T);

	try {
		switch (vector) {
		 case M68K_EXCEP_ILLEGAL:		// 不当命令
		 case M68K_EXCEP_PRIV:			// 特権違反例外
		 case M68K_EXCEP_ALINE:			// Aライン命令例外
		 case M68K_EXCEP_FLINE:			// Fライン命令例外
			// スタックに積む PC はこの命令のアドレス
			m68030_generate_exframe_0(cpu, vector, RegPPC, sr);
			break;

		 case M68K_EXCEP_TRAP0			// TRAP#0
		  ... M68K_EXCEP_TRAP15:		// TRAP#15
			// スタックに積む PC は次の命令
			m68030_generate_exframe_0(cpu, vector, RegPC, sr);
			break;

		 case M68K_EXCEP_ZERODIV:		// ゼロ除算
		 case M68K_EXCEP_CHK:			// CHK
		 case M68K_EXCEP_TRAPV:			// TRAPV
		 case M68K_EXCEP_MMU_CONFIG:	// MMU 構成例外
			m68030_generate_exframe_2(cpu, vector, sr);
			break;

		 default:
			PANIC("$%08x exception vector %d ?", RegPPC, vector);
			break;
		}

		// ベクタにジャンプ
		m68030_jump_vector(cpu, vector);
	} catch (int vector2) {
		assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector=%d", vector);

		// バスエラー処理へ
		m68030_exception_buserr(cpu, vector2);
	}
}

// バスエラー・アドレスエラー例外処理を行う。
// 他の例外とは違いが多いので分けた。
void
m68030_exception_buserr(m68kcpu *cpu, int vector)
{
	assertmsg((vector == M68K_EXCEP_BUSERR || vector == M68K_EXCEP_ADDRERR),
		"vector=%d", vector);

	// 例外履歴に記録 (例外発生はブランチ履歴にも記録)
	uint32 from = RegPPC | (cpu->reg.s ? 1 : 0);
	cpu->exhist.AddEntry(from, 0, 0x8000 | vector);
	cpu->brhist.AddEntry(from, 0, 0x8000 | vector);
	// デバッガに通知 (例外ブレーク用)
	debugger_notify_exception(vector);

	// 状態を保存
	uint16 sr = RegSR;

	// ここで (An)+ で変化したレジスタを命令実行前の状態に巻き戻す。
	// EA 処理の時点で先にインクリメントしているのだが、バスエラーが
	// 起きたということは実際にはポストインクリメントは発生しないので、
	// ここで無条件に巻き戻してよい。SR を書き換えると A7 が切り替わって
	// しまうので、この巻き戻しは SR を書き換える前のここで行うこと。
	// 一方、プリデクリメントのケースはこの少し上の exframe_b の中の
	// コメント参照。
	m68030_restore_reg_pi(cpu);

	// S を立てて T1,T0 を落とす
	m68030_set_sr(cpu, (RegSR | M68K_SR_S) & ~M68K_SR_T);

	try {
		// フレームを作成。
		// 実際はショート($A)、ロング($B)の2種類あって、本来ならショートが
		// 使えればショートを使うのだが、ここでは全部ロングを使う。
		m68030_generate_exframe_b(cpu, vector, sr);
		// ベクタにジャンプ
		m68030_jump_vector(cpu, vector);
	} catch (int vector2) {
		// ここに来るのはバスエラーだけのはず
		assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector=%d", vector);

		// バスエラー・アドレスエラー処理中のバスエラーは
		// ダブルバスフォールト。
		m68030_double_bus_fault(cpu, "bus/address error handling");
	}
}

// 割り込み処理。
//
// 割り込みは、本来命令境界ごとにチェックして条件を満たせば割り込み例外が
// 起動するのだが、割り込み例外が起きうる要因は
// o 割り込み信号線が変化した時
// o 割り込みマスクを変更した時
// しかないはずなので、この時だけチェックすればよいはず。
//
// 外部割り込み信号線が変化した時 m68030_interrupt(level) が VM から呼ばれる。
// m68030_interrupt() は割り込みレベルがマスクより高いか、レベルが下位から 7
// に変化すれば true を返す。
// これにより VM から割り込み処理 m68030_exception_interrupt() が呼ばれる。
//
// 命令によって割り込みマスクを変更する場合、命令は m68030_change_intrmask()
// を呼ぶ。これが VM 側で次の命令実行コールバックを差し替えるため、
// 次命令境界で割り込みのチェックが呼ばれる。これが m68030_check_interrupt()。
// こちらは割り込みレベルがマスクより高いかどうかだけをチェックする (レベルは
// 変化していないので下位から 7 に変化したかは調べない)。レベルのほうが高く
// なっていれば true を返す。
// これにより VM から割り込み処理 m68030_exception_interrupt() が呼ばれる。

// 割り込みレベル変更の受け付け。
// 発生している最も高い割り込みレベルが変化した時にここに通知される。
// 全員が下ろしたことによって 0 に遷移したことも通知されることに留意。
// 割り込み例外を起動するなら true を返す。
bool
m68030_interrupt(m68kcpu *cpu, int level)
{
	int mask = cpu->reg.intr_mask;

	// 信号線が 2 クロック期間安定していること云々は省略

	// ホールトなら何もしない
	if (cpu->state == m68kcpu::CPU_STATE_HALT) {
		return false;
	}

	// 割り込みを起こすかどうかに関わらずここで更新
	cpu->intr_level = level;

	// 割り込み例外を起動するのは
	// o レベルがマスクより高い
	// o マスクに関係なく、レベルが下位から 7 に変化した場合
	//   (ここは変化した時だけ呼ばれるのと 7 より上はないので
	//   level==7 なら必ず「下位から 7 に変化」のはず)
	if (level > mask || level == 7) {
		return true;
	}

	return false;
}

// 割り込みをチェックする。
// 割り込みマスク変更後の命令境界で VM 側から呼ばれる。
// 割り込みを起こすなら true を返す。
bool
m68030_check_interrupt(m68kcpu *cpu)
{
	int mask = cpu->reg.intr_mask;
	int level = cpu->intr_level;

	// こちらは HALT では呼び出されない

	// 割り込みレベルは変化していないので、ここでは Level 7 の特別処理は不要
	if (level > mask) {
		return true;
	}
	return false;
}

// 割り込み例外処理を行う。
// 割り込みの受け付けとレベルのチェック等の後、次命令境界で呼ばれるやつ。
int
m68030_exception_interrupt(m68kcpu *cpu)
{
	int cycle_start = cpu->used_cycle;
	int level = cpu->intr_level;

	// ユーザーズマニュアル(日本語) p.399 図8-5 の中ほど
	// 「/IPEND をネゲート、割り込みアクノリッジサイクルを実行」
	// 以降に相当。

	// STOP なら解除
	m68030_change_state(cpu, m68kcpu::CPU_STATE_NORMAL);

	// 割り込みアクノリッジサイクルを実行
	int vector = m68030_interrupt_acknowledge(level);
	if (vector == -1) {
		vector = 24 + level;
	}

	// 例外履歴に記録 (例外発生はブランチ履歴にも記録)
	uint32 from = RegPPC | (cpu->reg.s ? 1 : 0);
	uint32 inst = 0x8000 | (level << 8) | vector;
	cpu->exhist.AddEntry(from, 0, inst);
	cpu->brhist.AddEntry(from, 0, inst);
	// デバッガに通知 (例外ブレーク用)
	debugger_notify_exception(vector);

	// 状態を保存
	uint16 oldsr = RegSR;

	// S を立てて T1,T0 を落とす。割り込みレベルも変更。
	uint16 newsr = RegSR & ~(M68K_SR_T | M68K_SR_IMASK);
	newsr |= M68K_SR_S;
	newsr |= level << 8;
	m68030_set_sr(cpu, newsr);

	try {
		// 例外スタックフレーム $0 を生成。
		// スタックに積む PC は次の命令位置。
		// M ビットは未実装なのでスローアウェイとか無視
		m68030_generate_exframe_0(cpu, vector, RegPC, oldsr);

		// ベクタにジャンプ
		m68030_jump_vector(cpu, vector);
	} catch (int vector2) {
		assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector=%d", vector);

		// バスエラー処理へ
		m68030_exception_buserr(cpu, vector2);
	}

	return cpu->used_cycle - cycle_start;
}

// RTE 命令。
// 例外処理の近くに置いておくほうが便利なので。
void
m68030_rte(m68kcpu *cpu)
{
	uint32 pc;
	uint16 sr;
	uint16 frame;

	try {
		sr = m68030_pop_16(cpu);
		pc = m68030_pop_32(cpu);
		frame = m68030_pop_16(cpu);
		switch (frame >> 12) {
		 case 0:
			CYCLE2(18, 20);
			m68030_set_sr(cpu, sr);
			m68030_jump(cpu, pc);
			break;

		 case 2:
			CYCLE2(18, 20);
			// +08.L INSTRUCTION ADDRESS を読み捨てる
			RegA(7) += 4;
			m68030_set_sr(cpu, sr);
			m68030_jump(cpu, pc);
			break;

		 case 0x0b:
		 {
			uint32 stage_b;
			uint16 ssw;

			CYCLE(76);
			RegA(7) += 2;						// $08.W
			ssw = m68030_pop_16(cpu);			// $0a.W SSW
			RegA(7) += 4 * 6;					// $0c.L*6
			stage_b = m68030_pop_32(cpu);		// $24.L Stage B
			RegA(7) += 2 * 9;					// $28.W*9
			// $3a..$58 ここが save_pd の取り出し
			cpu->flag_pd = m68030_pop_16(cpu);
			for (int i = 0; i < 8; i++) {
				cpu->save_pd[i] = m68030_pop_32(cpu);
			}
			// A7 レジスタをリストアするかもしれない前に Super/User を
			// 復帰しておかないといけない
			m68030_set_sr(cpu, sr);

			// DFビットセットなら命令を再実行するため、-(An) で変化
			// したレジスタを巻き戻す。
			// DFビットクリアなら例外要因の命令はハンドラで処理された
			// とみなして、次の命令に進む。
			if ((ssw & SSW_DF)) {
				m68030_restore_reg_pd(cpu);
			} else {
				pc = stage_b - 2;
			}
			m68030_jump(cpu, pc);
			break;
		 }

		 default:
			PANIC("RTE frame=$%x not supported", frame >> 12);
			break;
		}
	} catch (int vector2) {
		assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector2=%d", vector2);

		// RTE 処理中のバスエラーはダブルバスフォールト。
		m68030_double_bus_fault(cpu, "bus/address error handling");
	}
}

// STOP 命令。
// 動作制御が必要なのでこっちに置いておくほうが便利。
void
m68030_stop(m68kcpu *cpu)
{
	uint32 data = m68030_fetch_16(cpu);
	m68030_set_sr(cpu, data);
	m68030_change_state(cpu, m68kcpu::CPU_STATE_STOP);

	cpu->dev->MakeNextFull();
}

unix.superglobalmegacorp.com

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