mirror of
https://github.com/phabrics/Run-Sun3-SunOS-4.1.1.git
synced 2026-04-29 11:02:59 -04:00
968 lines
34 KiB
C
968 lines
34 KiB
C
/* $Id: m68k-execute.c,v 1.24 2009/08/29 19:25:48 fredette Exp $ */
|
|
|
|
/* ic/m68k/m68k-execute.c - executes Motorola 68k instructions: */
|
|
|
|
/*
|
|
* Copyright (c) 2002, 2003 Matt Fredette
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Matt Fredette.
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
_TME_RCSID("$Id: m68k-execute.c,v 1.24 2009/08/29 19:25:48 fredette Exp $");
|
|
|
|
/* includes: */
|
|
#include "m68k-auto.h"
|
|
|
|
/* the m68k instruction executor: */
|
|
static void
|
|
_TME_M68K_EXECUTE_NAME(struct tme_m68k *ic)
|
|
{
|
|
#undef _TME_M68K_SEQUENCE_RESTARTING
|
|
#undef _TME_M68K_INSN_FETCH_SAVE
|
|
#ifdef _TME_M68K_EXECUTE_FAST
|
|
tme_bus_context_t bus_context;
|
|
struct tme_m68k_tlb *tlb;
|
|
const tme_shared tme_uint8_t *fetch_fast_next;
|
|
#define _TME_M68K_INSN_FETCH_SAVE \
|
|
do { \
|
|
ic->_tme_m68k_insn_fetch_fast_next = fetch_fast_next; \
|
|
} while (/* CONSTCOND */ 0)
|
|
#define _TME_M68K_SEQUENCE_RESTARTING (FALSE)
|
|
#else /* !_TME_M68K_EXECUTE_FAST */
|
|
unsigned int exceptions;
|
|
tme_uint32_t linear_pc;
|
|
#define _TME_M68K_INSN_FETCH_SAVE \
|
|
do { \
|
|
} while (/* CONSTCOND */ 0)
|
|
#define _TME_M68K_SEQUENCE_RESTARTING TME_M68K_SEQUENCE_RESTARTING
|
|
#endif /* !_TME_M68K_EXECUTE_FAST */
|
|
#if (_TME_M68K_EXECUTE_CPU == TME_M68K_M68020) || (_TME_M68K_EXECUTE_CPU == TME_M68K_M68030)
|
|
unsigned int eai_function_code;
|
|
int ea_post_index;
|
|
unsigned int ea_i_is;
|
|
tme_uint32_t ea_od;
|
|
unsigned int src_specifier;
|
|
#else /* !TME_M68K_M68020 && !TME_M68K_M68030 */
|
|
#define eai_function_code ea_function_code
|
|
#endif /* !TME_M68K_M68020 && !TME_M68K_M68030 */
|
|
unsigned int function_code_program;
|
|
unsigned int function_code_data;
|
|
tme_uint16_t opw, extword;
|
|
void (*func) _TME_P((struct tme_m68k *, void *, void *));
|
|
tme_uint32_t params;
|
|
int ea_size;
|
|
int ea_reg, ea_pre_index;
|
|
unsigned int ea_index_long, ea_index_scale;
|
|
tme_uint32_t ea_address;
|
|
unsigned int ea_function_code;
|
|
tme_int32_t ea_bd;
|
|
tme_uint32_t imm32;
|
|
tme_uint16_t transfer_next_before;
|
|
int rc;
|
|
|
|
/* silence gcc -Wuninitialized: */
|
|
ea_size = 0;
|
|
|
|
/* get the function codes. if the privilege ever changes as a
|
|
result of any instruction, we must redispatch: */
|
|
if (TME_M68K_PRIV(ic)) {
|
|
function_code_program = TME_M68K_FC_SP;
|
|
function_code_data = TME_M68K_FC_SD;
|
|
}
|
|
else {
|
|
function_code_program = TME_M68K_FC_UP;
|
|
function_code_data = TME_M68K_FC_UD;
|
|
}
|
|
|
|
/* if we have used up our burst: */
|
|
if (ic->_tme_m68k_instruction_burst_remaining == 0) {
|
|
|
|
/* start a new burst: */
|
|
ic->_tme_m68k_instruction_burst_remaining
|
|
= ic->_tme_m68k_instruction_burst;
|
|
|
|
/* if this is a cooperative threading system, yield: */
|
|
#if TME_THREADS_COOPERATIVE
|
|
tme_thread_yield();
|
|
#endif /* TME_THREADS_COOPERATIVE */
|
|
}
|
|
|
|
if (ic->_tme_m68k_mode == TME_M68K_MODE_DIE)
|
|
return;
|
|
|
|
#ifdef _TME_M68K_EXECUTE_FAST
|
|
|
|
/* get our instruction TLB entry and reload it: */
|
|
bus_context = ic->_tme_m68k_bus_context;
|
|
tlb = &ic->_tme_m68k_itlb;
|
|
tme_m68k_tlb_busy(tlb);
|
|
if (__tme_predict_false(tme_m68k_tlb_is_invalid(tlb)
|
|
|| tlb->tme_m68k_tlb_bus_context != bus_context
|
|
|| (tlb->tme_m68k_tlb_function_codes_mask
|
|
& TME_BIT(function_code_program)) == 0
|
|
|| ic->tme_m68k_ireg_pc < (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first
|
|
|| ic->tme_m68k_ireg_pc > (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last
|
|
|| tlb->tme_m68k_tlb_emulator_off_read == TME_EMULATOR_OFF_UNDEF)) {
|
|
tme_m68k_tlb_fill(ic, tlb,
|
|
function_code_program,
|
|
ic->tme_m68k_ireg_pc,
|
|
TME_BUS_CYCLE_READ);
|
|
}
|
|
|
|
/* if we have to go slow, run the slow executor: */
|
|
if (TME_M68K_SEQUENCE_RESTARTING
|
|
|| tme_m68k_go_slow(ic)) {
|
|
tme_m68k_tlb_unbusy(tlb);
|
|
_TME_M68K_EXECUTE_SLOW(ic);
|
|
return;
|
|
}
|
|
|
|
/* set up to do fast reads from the instruction TLB entry: */
|
|
ic->_tme_m68k_insn_fetch_fast_last = tlb->tme_m68k_tlb_emulator_off_read + ((tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last) - (sizeof(tme_uint32_t) - 1);
|
|
ic->_tme_m68k_insn_fetch_fast_itlb = tlb;
|
|
ic->_tme_m68k_group0_hook = tme_m68k_group0_hook_fast;
|
|
#else /* !_TME_M68K_EXECUTE_FAST */
|
|
|
|
/* set up to do slow reads from the instruction TLB entry: */
|
|
ic->_tme_m68k_group0_hook = NULL;
|
|
#endif /* !_TME_M68K_EXECUTE_FAST */
|
|
|
|
/* the execution loop: */
|
|
for (;;) {
|
|
|
|
#if 0
|
|
extern int _m68k_dead;
|
|
if (_m68k_dead) {
|
|
extern void tme_m68k_kill_cpu(struct tme_m68k *ic);
|
|
tme_m68k_kill_cpu(ic);
|
|
}
|
|
#endif
|
|
|
|
/* reset for this instruction: */
|
|
#ifdef _TME_M68K_EXECUTE_FAST
|
|
fetch_fast_next = tlb->tme_m68k_tlb_emulator_off_read + ic->tme_m68k_ireg_pc;
|
|
ic->_tme_m68k_insn_fetch_fast_start = fetch_fast_next;
|
|
assert (ic->_tme_m68k_insn_fetch_fast_itlb == tlb
|
|
&& tlb->tme_m68k_tlb_emulator_off_read != TME_EMULATOR_OFF_UNDEF
|
|
&& (tlb->tme_m68k_tlb_function_codes_mask & TME_BIT(function_code_program)) != 0
|
|
&& (fetch_fast_next > ic->_tme_m68k_insn_fetch_fast_last
|
|
|| ((tme_m68k_tlb_is_valid(tlb)
|
|
|| !TME_THREADS_COOPERATIVE)
|
|
&& tlb->tme_m68k_tlb_bus_context == ic->_tme_m68k_bus_context
|
|
&& ic->tme_m68k_ireg_pc >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first
|
|
&& (ic->tme_m68k_ireg_pc + sizeof(tme_uint16_t) - 1) <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last)));
|
|
tme_m68k_verify_begin(ic, fetch_fast_next);
|
|
#else /* !_TME_M68K_EXECUTE_FAST */
|
|
linear_pc = ic->tme_m68k_ireg_pc;
|
|
ic->_tme_m68k_insn_fetch_slow_next = 0;
|
|
if (!_TME_M68K_SEQUENCE_RESTARTING) {
|
|
ic->_tme_m68k_insn_fetch_slow_count_fast = 0;
|
|
ic->_tme_m68k_insn_fetch_slow_count_total = 0;
|
|
}
|
|
exceptions = 0;
|
|
if (__tme_predict_false((ic->tme_m68k_ireg_sr & ic->_tme_m68k_sr_mask_t) == TME_M68K_FLAG_T1)) {
|
|
ic->tme_m68k_ireg_pc_last = ic->tme_m68k_ireg_pc;
|
|
exceptions |= TME_M68K_EXCEPTION_TRACE;
|
|
}
|
|
tme_m68k_verify_begin(ic, NULL);
|
|
#endif /* _TME_M68K_EXECUTE_FAST */
|
|
#ifdef _TME_M68K_VERIFY
|
|
if (ic->tme_m68k_ireg_pc == 0x6000) {
|
|
tme_m68k_verify_hook();
|
|
}
|
|
#endif
|
|
#ifdef _TME_M68K_STATS
|
|
ic->tme_m68k_stats.tme_m68k_stats_insns_total++;
|
|
#ifndef _TME_M68K_EXECUTE_FAST
|
|
ic->tme_m68k_stats.tme_m68k_stats_insns_slow++;
|
|
#endif /* !_TME_M68K_EXECUTE_FAST */
|
|
#endif /* _TME_M68K_STATS */
|
|
ic->_tme_m68k_instruction_burst_remaining--;
|
|
|
|
/* fetch and decode the first word of this instruction: */
|
|
_TME_M68K_EXECUTE_FETCH_U16_FIXED(opw, _tme_m68k_insn_opcode);
|
|
ic->_tme_m68k_insn_opcode = opw;
|
|
params = _TME_M68K_EXECUTE_OPMAP[opw];
|
|
func = tme_m68k_opcode_insns[TME_M68K_OPCODE_INSN_WHICH(params)];
|
|
|
|
/* now that we no longer need the insn index part of the params,
|
|
replace it with the least significant bits of the opcode, which
|
|
contain any EA mode and reg fields: */
|
|
TME_FIELD_MASK_DEPOSITU(params,
|
|
TME_M68K_OPCODE_INSN_MASK,
|
|
(opw & (TME_M68K_OPCODE_INSN_MASK / TME_M68K_OPCODE_INSN(1))));
|
|
|
|
/* if this is a special opcode: */
|
|
if (__tme_predict_false((params & TME_M68K_OPCODE_SPECOP) != 0)) {
|
|
|
|
#if (_TME_M68K_EXECUTE_CPU == TME_M68K_M68020) || (_TME_M68K_EXECUTE_CPU == TME_M68K_M68030)
|
|
|
|
/* a general floating-point instruction: */
|
|
if (__tme_predict_false((opw & 0xffc0) == 0xf200)) {
|
|
|
|
/* if there is no FPU present, or if it isn't enabled: */
|
|
if (__tme_predict_false(!ic->tme_m68k_fpu_enabled)) {
|
|
|
|
/* mark this instruction as illegal and use an FPgen command
|
|
word of zero: */
|
|
func = tme_m68k_illegal;
|
|
extword = 0;
|
|
}
|
|
|
|
/* otherwise, there is an FPU present and it is enabled: */
|
|
else {
|
|
|
|
/* fetch the FPgen command word: */
|
|
_TME_M68K_EXECUTE_FETCH_U16_FIXED(ic->_tme_m68k_insn_specop, _tme_m68k_insn_specop);
|
|
|
|
/* temporarily store the FPgen command word in extword: */
|
|
extword = ic->_tme_m68k_insn_specop;
|
|
}
|
|
|
|
/* the goal here is not to decide whether or not an FPgen
|
|
instruction is legal, although some illegal instructions
|
|
are caught. the goal is to only decide if this FPgen
|
|
instruction uses the EA field.
|
|
|
|
we need to know this here because only the executer can
|
|
calculate all memory EAs (i.e., absolute addresses,
|
|
indirect addresses, PC-relative addresses, etc.) and fetch
|
|
immediates.
|
|
|
|
in general, all other decisions about whether or not an
|
|
instruction is legal (including whether or not certain EAs
|
|
are legal, like data register direct or address register
|
|
direct) or how to dispatch it is done somewhere else.
|
|
|
|
all legal FPgen instructions that use the EA field are
|
|
handled by one of the following ifs. all illegal FPgen
|
|
instructions, and all FPgen instructions that do not use
|
|
the EA field are handled by the final unconditional else
|
|
clause that cancels memory EA calculation and immediate
|
|
fetching.
|
|
|
|
for those FPgen instructions that do use the EA field, they
|
|
either leave the EA cycles unchanged, as TME_M68K_OPCODE_EA_READ, to
|
|
indicate that they read the EA, or they change it to
|
|
TME_M68K_OPCODE_EA_WRITE to indicate that they write the EA.
|
|
additionally, they either flag any immediate operand as
|
|
illegal, fetch it themselves, or specify its size for the
|
|
normal immediate fetching code to use: */
|
|
|
|
/* m68k-iset.txt must assume that any EA is not written: */
|
|
assert (!TME_M68K_OPCODE_HAS_EA(params)
|
|
|| ((params
|
|
& (TME_M68K_OPCODE_EA_READ
|
|
| TME_M68K_OPCODE_EA_WRITE))
|
|
== TME_M68K_OPCODE_EA_READ));
|
|
|
|
/* m68k-iset.txt must assume that any immediate is 32 bits: */
|
|
assert (!TME_M68K_OPCODE_HAS_IMM(params)
|
|
|| (params & (TME_M68K_OPCODE_IMM_16 | TME_M68K_OPCODE_IMM_32)) == TME_M68K_OPCODE_IMM_32);
|
|
|
|
/* if this is an FMOVE or FMOVEM of floating-point control
|
|
registers (command word pattern 10dr rr00 0000 0000): */
|
|
if ((extword & 0xc3ff) == 0x8000) {
|
|
|
|
/* override the function: */
|
|
func = tme_m68k_fmovemctl;
|
|
|
|
/* if this is a register-to-memory operation: */
|
|
if (extword & TME_BIT(13)) {
|
|
|
|
/* any EA must be writable: */
|
|
params |= TME_M68K_OPCODE_EA_WRITE;
|
|
}
|
|
|
|
/* otherwise, this is a memory-to-register operation: */
|
|
else {
|
|
|
|
/* if this instruction has an immediate, and this
|
|
instruction is moving multiple control registers, this
|
|
is an illegal instruction: */
|
|
/* NB the trick we use to see if multiple bits are set in
|
|
the rrr field - we subtract one from the base of the
|
|
rrr field (0x0400), binary-AND the result with extword,
|
|
and mask off all bits except the rrr field. this
|
|
result will be nonzero iff the rrr field has multiple
|
|
bits set: */
|
|
if (__tme_predict_false(TME_M68K_OPCODE_HAS_IMM(params)
|
|
&& ((extword & (extword - 0x0400)) & 0x1c00))) {
|
|
func = tme_m68k_illegal;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if this is an FMOVEM
|
|
(command word pattern 11dm m000 rrrr rrrr): */
|
|
else if ((extword & 0xc700) == 0xc000) {
|
|
|
|
/* override the function: */
|
|
func = tme_m68k_fmovem;
|
|
|
|
/* if this instruction has an immediate, this is an
|
|
illegal instruction: */
|
|
if (__tme_predict_false(TME_M68K_OPCODE_HAS_IMM(params))) {
|
|
func = tme_m68k_illegal;
|
|
}
|
|
|
|
/* if this is a register-to-memory operation: */
|
|
if (extword & TME_BIT(13)) {
|
|
|
|
/* any EA must be writable: */
|
|
params |= TME_M68K_OPCODE_EA_WRITE;
|
|
}
|
|
}
|
|
|
|
/* if this is a register-to-memory FMOVE instruction
|
|
(command word pattern 011d ddss skkk kkkk): */
|
|
else if ((extword & 0xe000) == 0x6000) {
|
|
|
|
/* override the function: */
|
|
func = tme_m68k_fmove_rm;
|
|
|
|
/* any EA must be writable: */
|
|
params |= TME_M68K_OPCODE_EA_WRITE;
|
|
}
|
|
|
|
/* if this is a memory-to-register true FPgen instruction
|
|
(command word pattern 010s ssdd dooo oooo): */
|
|
else if ((extword & 0xe000) == 0x4000
|
|
&& (_tme_m6888x_fpgen_opmode_bitmap[TME_FIELD_EXTRACTU(extword, 0, 7) / 8]
|
|
& (1 << (TME_FIELD_EXTRACTU(extword, 0, 7) % 8)))
|
|
&& TME_FIELD_EXTRACTU(extword, 10, 3) != TME_M6888X_TYPE_INVALID) {
|
|
|
|
/* if this instruction has an immediate: */
|
|
if (TME_M68K_OPCODE_HAS_IMM(params)) {
|
|
|
|
/* if the source specifier is for a size that the normal
|
|
immediate fetching code can handle, let it handle it,
|
|
otherwise we have to fetch the immediate ourselves: */
|
|
|
|
/* m68k-iset.txt must have specified the EA operand to be
|
|
operand one: */
|
|
assert((void *) TME_M68K_OPCODE_OP1_WHICH(ic, params)
|
|
== (void *) &ic->tme_m68k_ireg_uint32(TME_M68K_IREG_IMM32 + 0));
|
|
|
|
/* dispatch on the source specifier: */
|
|
src_specifier = TME_FIELD_EXTRACTU(extword, 10, 3);
|
|
switch (src_specifier) {
|
|
|
|
/* we can let the normal immediate fetching code fetch
|
|
word integers, long-word integers, and
|
|
single-precision reals: */
|
|
default:
|
|
assert (func == tme_m68k_illegal);
|
|
/* FALLTHROUGH */
|
|
case TME_M6888X_TYPE_WORD:
|
|
/* we can simply flip TME_M68K_OPCODE_IMM_16 and
|
|
TME_M68K_OPCODE_IMM_32 to select a 16-bit immediate;
|
|
we know that only TME_M68K_OPCODE_IMM_32 is set,
|
|
thanks to the assert we did at the beginning of the
|
|
fpgen specop handling: */
|
|
params ^= (TME_M68K_OPCODE_IMM_16 | TME_M68K_OPCODE_IMM_32);
|
|
/* FALLTHROUGH */
|
|
case TME_M6888X_TYPE_LONG:
|
|
case TME_M6888X_TYPE_SINGLE:
|
|
break;
|
|
case TME_M6888X_TYPE_EXTENDED80:
|
|
case TME_M6888X_TYPE_PACKEDDEC:
|
|
case TME_M6888X_TYPE_DOUBLE:
|
|
_TME_M68K_EXECUTE_FETCH_U32(imm32);
|
|
ic->tme_m68k_ireg_uint32(TME_M68K_IREG_IMM32 + 0) = imm32;
|
|
_TME_M68K_EXECUTE_FETCH_U32(imm32);
|
|
ic->tme_m68k_ireg_uint32(TME_M68K_IREG_IMM32 + 1) = imm32;
|
|
if (src_specifier != TME_M6888X_TYPE_DOUBLE) {
|
|
_TME_M68K_EXECUTE_FETCH_U32(imm32);
|
|
ic->tme_m68k_ireg_uint32(TME_M68K_IREG_IMM32 + 2) = imm32;
|
|
}
|
|
/* we only need to clear TME_M68K_OPCODE_IMM_32 here to
|
|
cancel the later immediate fetching; we know that
|
|
only TME_M68K_OPCODE_IMM_32 is set, thanks to the
|
|
assert we did at the beginning of the fpgen specop
|
|
handling: */
|
|
params &= ~TME_M68K_OPCODE_IMM_32;
|
|
break;
|
|
case TME_M6888X_TYPE_BYTE:
|
|
_TME_M68K_EXECUTE_FETCH_U16(imm32);
|
|
ic->tme_m68k_ireg_uint32(TME_M68K_IREG_IMM32 + 0) = TME_EXT_S8_U32((tme_int8_t) imm32);
|
|
/* we only need to clear TME_M68K_OPCODE_IMM_32 here to
|
|
cancel the later immediate fetching; we know that
|
|
only TME_M68K_OPCODE_IMM_32 is set, thanks to the
|
|
assert we did at the beginning of the fpgen specop
|
|
handling: */
|
|
params &= ~TME_M68K_OPCODE_IMM_32;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* otherwise, this FPgen instruction does not need any memory
|
|
EA or immediate. we'll decide later if this instruction is
|
|
actually legal: */
|
|
else {
|
|
/* cancel immediate fetching and all EA work: */
|
|
params &= ~(TME_M68K_OPCODE_IMM_32
|
|
| TME_M68K_OPCODE_IMM_16
|
|
| TME_M68K_OPCODE_EA_SIZE_MASK
|
|
| TME_M68K_OPCODE_EA_READ
|
|
| TME_M68K_OPCODE_EA_WRITE);
|
|
}
|
|
|
|
/* if this instruction has already been marked as illegal, or
|
|
this EA must be writable, and this is a PC-relative or
|
|
immediate EA, this instruction is illegal: */
|
|
if (__tme_predict_false(func == tme_m68k_illegal
|
|
|| (params & TME_M68K_OPCODE_EA_WRITE
|
|
&& (TME_M68K_OPCODE_HAS_IMM(params)
|
|
|| (TME_M68K_OPCODE_EA_MODE_WHICH(params) == 7
|
|
&& (TME_M68K_OPCODE_EA_REG_WHICH(params) & 6) == 2))))) {
|
|
func = tme_m68k_illegal;
|
|
/* cancel immediate fetching and all EA work: */
|
|
params &= ~(TME_M68K_OPCODE_IMM_32
|
|
| TME_M68K_OPCODE_IMM_16
|
|
| TME_M68K_OPCODE_EA_SIZE_MASK
|
|
| TME_M68K_OPCODE_EA_READ
|
|
| TME_M68K_OPCODE_EA_WRITE);
|
|
}
|
|
|
|
/* otherwise, if this instruction has a memory EA: */
|
|
else if (params & TME_M68K_OPCODE_EA_READ) {
|
|
|
|
/* override any m68k-iset.txt guess about the operand
|
|
size, and cancel all memory cycles. the instruction
|
|
itself will do the actual operand reading and any address
|
|
register postincrement or predecrement: */
|
|
params =
|
|
((params
|
|
& ~(TME_M68K_OPCODE_EA_READ
|
|
| TME_M68K_OPCODE_EA_WRITE
|
|
| TME_M68K_OPCODE_EA_SIZE_MASK))
|
|
| TME_M68K_OPCODE_EA_UNSIZED);
|
|
}
|
|
|
|
/* otherwise, this instruction does not have a memory EA: */
|
|
else {
|
|
|
|
/* cancel all EA work: */
|
|
params &= ~(TME_M68K_OPCODE_EA_SIZE_MASK
|
|
| TME_M68K_OPCODE_EA_READ
|
|
| TME_M68K_OPCODE_EA_WRITE);
|
|
}
|
|
}
|
|
else
|
|
#endif /* TME_M68K_M68020 || TME_M68K_M68030 */
|
|
|
|
/* if this is not a memory-to-memory move instruction: */
|
|
if ((params & TME_M68K_OPCODE_EA_Y) == 0) {
|
|
|
|
/* many instructions have a single special extension word: */
|
|
_TME_M68K_EXECUTE_FETCH_U16_FIXED(ic->_tme_m68k_insn_specop, _tme_m68k_insn_specop);
|
|
}
|
|
}
|
|
|
|
/* get any immediate operand: */
|
|
if (__tme_predict_false(TME_M68K_OPCODE_HAS_IMM(params))) {
|
|
if (params & TME_M68K_OPCODE_IMM_16) {
|
|
_TME_M68K_EXECUTE_FETCH_S16(imm32);
|
|
}
|
|
else {
|
|
_TME_M68K_EXECUTE_FETCH_U32(imm32);
|
|
}
|
|
ic->tme_m68k_ireg_imm32 = imm32;
|
|
}
|
|
|
|
/* loop over up to two effective addresses calculations. this
|
|
initializes for the normal, single effective address: */
|
|
while (TME_M68K_OPCODE_HAS_EA(params)) {
|
|
|
|
/* if this EA is described by the alternate EA mode and reg
|
|
fields, copy them into the EA mode and reg fields in
|
|
params: */
|
|
if (__tme_predict_false((params
|
|
& (TME_M68K_OPCODE_EA_Y | TME_M68K_OPCODE_SPECOP))
|
|
== TME_M68K_OPCODE_EA_Y)) {
|
|
|
|
/* reload to write the other memory EA: */
|
|
params
|
|
= ((params
|
|
& ~(TME_M68K_OPCODE_EA_MODE_MASK
|
|
| TME_M68K_OPCODE_EA_REG_MASK
|
|
| TME_M68K_OPCODE_EA_READ
|
|
| TME_M68K_OPCODE_EA_Y))
|
|
| TME_M68K_OPCODE_EA_MODE(TME_FIELD_EXTRACTU(opw, 6, 3))
|
|
| TME_M68K_OPCODE_EA_REG(TME_FIELD_EXTRACTU(opw, 9, 3))
|
|
| TME_M68K_OPCODE_EA_WRITE);
|
|
}
|
|
|
|
/* get the reg, size, and function code of this EA: */
|
|
ea_reg = TME_M68K_IREG_A0 + TME_M68K_OPCODE_EA_REG_WHICH(params);
|
|
ea_size = TME_M68K_OPCODE_EA_SIZE_WHICH(params);
|
|
ea_function_code = function_code_data;
|
|
|
|
/* this EA must have either no size, or be exactly one, two, or
|
|
four bytes: */
|
|
assert(ea_size == TME_M68K_SIZE_UNSIZED
|
|
|| ea_size == TME_M68K_SIZE_8
|
|
|| ea_size == TME_M68K_SIZE_16
|
|
|| ea_size == TME_M68K_SIZE_32);
|
|
|
|
/* for the effective address predecrement and postincrement
|
|
modes, we require that these size macros correspond exactly
|
|
to the number of bytes, that the %a7 register number be 15,
|
|
and that the ea reg not be greater than %a7: */
|
|
#if TME_M68K_SIZE_UNSIZED != 0
|
|
#error "TME_M68K_SIZE_UNSIZED must be 0"
|
|
#endif
|
|
#if TME_M68K_SIZE_8 != 1
|
|
#error "TME_M68K_SIZE_8 must be 1"
|
|
#endif
|
|
#if TME_M68K_SIZE_16 != 2
|
|
#error "TME_M68K_SIZE_16 must be 2"
|
|
#endif
|
|
#if TME_M68K_SIZE_32 != 4
|
|
#error "TME_M68K_SIZE_32 must be 4"
|
|
#endif
|
|
#if TME_M68K_IREG_A7 != 15
|
|
#error "TME_M68K_IREG_A7 must be 15"
|
|
#endif
|
|
assert(ea_reg <= TME_M68K_IREG_A7);
|
|
#define TME_M68K_AREG_INCREMENT(areg, size) \
|
|
(((((areg) + 1) / (TME_M68K_IREG_A7 + 1)) & (size)) + (size))
|
|
|
|
/* initialize ea_address to silence -Wuninitialized: */
|
|
ea_address = 0;
|
|
|
|
/* set the EA inner function code: */
|
|
eai_function_code = ea_function_code;
|
|
|
|
/* dispatch on the mode: */
|
|
switch (TME_M68K_OPCODE_EA_MODE_WHICH(params)) {
|
|
|
|
/* address register indirect: */
|
|
case 2:
|
|
ea_address = ic->tme_m68k_ireg_uint32(ea_reg);
|
|
break;
|
|
|
|
/* address register indirect postincrement: */
|
|
case 3:
|
|
/* if we are not restarting, set the effective address: */
|
|
if (!_TME_M68K_SEQUENCE_RESTARTING) {
|
|
ea_address = ic->tme_m68k_ireg_uint32(ea_reg);
|
|
ic->tme_m68k_ireg_uint32(ea_reg) += TME_M68K_AREG_INCREMENT(ea_reg, ea_size);
|
|
}
|
|
break;
|
|
|
|
/* address register indirect predecrement: */
|
|
case 4:
|
|
/* if we are not restarting, set the effective address: */
|
|
if (!_TME_M68K_SEQUENCE_RESTARTING) {
|
|
ic->tme_m68k_ireg_uint32(ea_reg) -= TME_M68K_AREG_INCREMENT(ea_reg, ea_size);
|
|
ea_address = ic->tme_m68k_ireg_uint32(ea_reg);
|
|
}
|
|
break;
|
|
|
|
/* address register indirect with 16-bit displacement: */
|
|
case 5:
|
|
_TME_M68K_EXECUTE_FETCH_S16(ea_bd);
|
|
ea_address = ic->tme_m68k_ireg_uint32(ea_reg) + ea_bd;
|
|
break;
|
|
|
|
/* miscellaneous modes: */
|
|
case 7:
|
|
|
|
/* absolute short addressing: */
|
|
if (ea_reg == TME_M68K_IREG_A0) {
|
|
_TME_M68K_EXECUTE_FETCH_S16(ea_address);
|
|
break;
|
|
}
|
|
|
|
/* absolute long addressing: */
|
|
if (ea_reg == TME_M68K_IREG_A1) {
|
|
_TME_M68K_EXECUTE_FETCH_S32(ea_address);
|
|
break;
|
|
}
|
|
|
|
/* the remaining modes use the PC of the first extension word
|
|
as a base register: */
|
|
#ifdef _TME_M68K_EXECUTE_FAST
|
|
ic->tme_m68k_ireg_pc_next = ic->tme_m68k_ireg_pc + (fetch_fast_next - ic->_tme_m68k_insn_fetch_fast_start);
|
|
#else /* !_TME_M68K_EXECUTE_FAST */
|
|
ic->tme_m68k_ireg_pc_next = linear_pc;
|
|
#endif /* !_TME_M68K_EXECUTE_FAST */
|
|
|
|
/* program counter indirect with 16-bit displacement: */
|
|
if (ea_reg == TME_M68K_IREG_A2) {
|
|
_TME_M68K_EXECUTE_FETCH_S16(ea_bd);
|
|
ea_address = ic->tme_m68k_ireg_pc_next + ea_bd;
|
|
ea_function_code = function_code_program;
|
|
break;
|
|
}
|
|
|
|
/* everything else is just like mode 6 except with the PC of
|
|
the first extension word as the base register: */
|
|
assert (ea_reg == TME_M68K_IREG_A3);
|
|
ea_reg = TME_M68K_IREG_PC_NEXT;
|
|
eai_function_code = function_code_program;
|
|
/* FALLTHROUGH */
|
|
|
|
/* various indexed modes: */
|
|
case 6:
|
|
|
|
/* fetch the extension word and take it apart. the 68000 and
|
|
68010 ignore the scale field in the extension word and always
|
|
behave as if it is zero: */
|
|
_TME_M68K_EXECUTE_FETCH_U16(extword);
|
|
ea_pre_index = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(extword, 12, 4);
|
|
ea_index_long = (extword & TME_BIT(11));
|
|
#if (_TME_M68K_EXECUTE_CPU == TME_M68K_M68020) || (_TME_M68K_EXECUTE_CPU == TME_M68K_M68030)
|
|
ea_index_scale = TME_FIELD_EXTRACTU(extword, 9, 2);
|
|
#else /* !TME_M68K_M68020 && !TME_M68K_M68030 */
|
|
ea_index_scale = 0;
|
|
#endif /* !TME_M68K_M68020 && !TME_M68K_M68030 */
|
|
|
|
/* if this is a full extension word: */
|
|
if (__tme_predict_false(extword & TME_BIT(8))) {
|
|
#if (_TME_M68K_EXECUTE_CPU == TME_M68K_M68020) || (_TME_M68K_EXECUTE_CPU == TME_M68K_M68030)
|
|
|
|
ea_i_is = TME_FIELD_EXTRACTU(extword, 0, 3);
|
|
|
|
/* optionally suppress the base register: */
|
|
if (extword & TME_BIT(7)) {
|
|
ea_reg = TME_M68K_IREG_ZERO;
|
|
}
|
|
|
|
/* fetch any base displacement: */
|
|
ea_bd = 0;
|
|
switch (TME_FIELD_EXTRACTU(extword, 4, 2)) {
|
|
case 0: abort();
|
|
case 1: break;
|
|
case 2: _TME_M68K_EXECUTE_FETCH_S16(ea_bd); break;
|
|
case 3: _TME_M68K_EXECUTE_FETCH_S32(ea_bd); break;
|
|
}
|
|
|
|
/* optionally suppress the index register. this is also
|
|
where we check for combined IS-I/IS fields greater than
|
|
or equal to 0xc, which are reserved: */
|
|
if (extword & TME_BIT(6)) {
|
|
ea_pre_index = TME_M68K_IREG_ZERO;
|
|
if (ea_i_is >= 0x4) {
|
|
abort();
|
|
}
|
|
}
|
|
|
|
/* fetch any outer displacement: */
|
|
ea_od = 0;
|
|
switch (ea_i_is & 3) {
|
|
case 0: case 1: break;
|
|
case 2: _TME_M68K_EXECUTE_FETCH_S16(ea_od); break;
|
|
case 3: _TME_M68K_EXECUTE_FETCH_S32(ea_od); break;
|
|
}
|
|
|
|
/* dispatch on the I/IS fields: */
|
|
ea_post_index = TME_M68K_IREG_ZERO;
|
|
switch (ea_i_is) {
|
|
|
|
/* no memory indirect action: */
|
|
case 0x0:
|
|
ea_post_index = TME_M68K_IREG_UNDEF;
|
|
break;
|
|
|
|
/* indirect preindexed with null outer displacement: */
|
|
/* indirect preindexed with word outer displacement: */
|
|
/* indirect preindexed with long outer displacement: */
|
|
case 0x1: case 0x2: case 0x3:
|
|
break;
|
|
|
|
/* reserved: */
|
|
case 0x4: default: abort();
|
|
|
|
/* indirect postindexed with null outer displacement: */
|
|
/* indirect postindexed with word outer displacement: */
|
|
/* indirect postindexed with long outer displacement: */
|
|
case 0x5: case 0x6: case 0x7:
|
|
ea_post_index = ea_pre_index;
|
|
ea_pre_index = TME_M68K_IREG_ZERO;
|
|
break;
|
|
}
|
|
|
|
/* preindex and base-displace the original address register
|
|
to arrive at the indirect EA: */
|
|
ea_address =
|
|
(ic->tme_m68k_ireg_uint32(ea_reg)
|
|
+ ((ea_index_long
|
|
? ic->tme_m68k_ireg_int32(ea_pre_index)
|
|
: ((tme_int32_t) ic->tme_m68k_ireg_int16(ea_pre_index << 1)))
|
|
<< ea_index_scale)
|
|
+ ea_bd);
|
|
|
|
/* if this is a memory indirect, read the indirect EA.
|
|
don't disturb the EA in the IC state if we're restarting,
|
|
for two reasons:
|
|
|
|
first, the value in the IC state may belong to some later
|
|
part of the instruction handling, in which case we must
|
|
(continue to) preserve it, and
|
|
|
|
second, if the EA in the IC state *is* from this part of
|
|
the instruction handling, it's correct, while our EA may
|
|
*not* be correct, since it was generated from IC state
|
|
that may have changed since the instruction originally
|
|
started (i.e., address register changes by the user or by
|
|
our own postincrement/predecrement, or function code
|
|
register changes by the user): */
|
|
if (ea_post_index != TME_M68K_IREG_UNDEF) {
|
|
if (!_TME_M68K_SEQUENCE_RESTARTING) {
|
|
ic->_tme_m68k_ea_address = ea_address;
|
|
ic->_tme_m68k_ea_function_code = eai_function_code;
|
|
}
|
|
_TME_M68K_INSN_FETCH_SAVE;
|
|
tme_m68k_read_mem32(ic, TME_M68K_IREG_MEMY32);
|
|
ea_address =
|
|
(ic->tme_m68k_ireg_memy32
|
|
+ ((ea_index_long
|
|
? ic->tme_m68k_ireg_int32(ea_post_index)
|
|
: ((tme_int32_t) ic->tme_m68k_ireg_int16(ea_post_index << 1)))
|
|
<< ea_index_scale)
|
|
+ ea_od);
|
|
}
|
|
else {
|
|
ea_function_code = eai_function_code;
|
|
}
|
|
|
|
#else /* !TME_M68K_M68020 && !TME_M68K_M68030 */
|
|
/* XXX - illegal instruction */
|
|
abort();
|
|
#endif /* !TME_M68K_M68020 && !TME_M68K_M68030 */
|
|
}
|
|
|
|
/* otherwise, this is a brief extension word: */
|
|
else {
|
|
ea_address =
|
|
(ic->tme_m68k_ireg_uint32(ea_reg)
|
|
+ ((tme_int32_t) ((tme_int8_t) (extword & 0xff)))
|
|
+ ((ea_index_long
|
|
? ic->tme_m68k_ireg_int32(ea_pre_index)
|
|
: ((tme_int32_t) ic->tme_m68k_ireg_int16(ea_pre_index << 1)))
|
|
<< ea_index_scale));
|
|
ea_function_code = eai_function_code;
|
|
}
|
|
break;
|
|
|
|
default: assert(FALSE);
|
|
}
|
|
|
|
/* we have calculated the effective address. we don't store it
|
|
if we're restarting, because it may have been calculated
|
|
using user-visible registers (address registers and even
|
|
function code registers!) that the user may have changed (or,
|
|
in the case of pre/postdecrement EAs, that *we* may have
|
|
changed) between the bus fault and the instruction restart.
|
|
when we restart an instruction we *always* want to use the
|
|
same effective address as before: */
|
|
if (!_TME_M68K_SEQUENCE_RESTARTING) {
|
|
ic->_tme_m68k_ea_address = ea_address;
|
|
ic->_tme_m68k_ea_function_code = eai_function_code;
|
|
}
|
|
|
|
/* XXX XXX XXX - if we detect a store to program space, that's an illegal: */
|
|
/* XXX but maybe not for moves? */
|
|
if (__tme_predict_false(ea_function_code == function_code_program
|
|
&& (params & TME_M68K_OPCODE_EA_WRITE) != 0)) {
|
|
abort();
|
|
}
|
|
|
|
/* if we're loading this operand: */
|
|
if (params & TME_M68K_OPCODE_EA_READ) {
|
|
_TME_M68K_INSN_FETCH_SAVE;
|
|
(*_tme_m68k_read_memx[ea_size])(ic);
|
|
}
|
|
|
|
/* stop unless this is a memory-to-memory move: */
|
|
if (__tme_predict_true(!(params & TME_M68K_OPCODE_EA_Y)))
|
|
break;
|
|
|
|
/* loop to reload for the other memory EA at the same size: */
|
|
params &= ~TME_M68K_OPCODE_SPECOP;
|
|
}
|
|
|
|
/* we've fetched all of the instruction words: */
|
|
_TME_M68K_INSN_FETCH_SAVE;
|
|
|
|
/* set the next PC: */
|
|
#ifdef _TME_M68K_EXECUTE_FAST
|
|
ic->tme_m68k_ireg_pc_next = ic->tme_m68k_ireg_pc + (fetch_fast_next - ic->_tme_m68k_insn_fetch_fast_start);
|
|
#else /* !_TME_M68K_EXECUTE_FAST */
|
|
ic->tme_m68k_ireg_pc_next = linear_pc;
|
|
#endif /* !_TME_M68K_EXECUTE_FAST */
|
|
|
|
/* if we're not restarting, or if this instruction function can
|
|
fault, call the instruction function: */
|
|
if (!_TME_M68K_SEQUENCE_RESTARTING
|
|
|| (ic->_tme_m68k_mode_flags & TME_M68K_EXECUTION_INST_CANFAULT)) {
|
|
transfer_next_before = ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_next;
|
|
(*func)(ic, TME_M68K_OPCODE_OP0_WHICH(ic, params), TME_M68K_OPCODE_OP1_WHICH(ic, params));
|
|
assert(!(ic->_tme_m68k_mode_flags & TME_M68K_EXECUTION_INST_CANFAULT)
|
|
!= (ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_next
|
|
!= transfer_next_before));
|
|
ic->_tme_m68k_mode_flags &= ~TME_M68K_EXECUTION_INST_CANFAULT;
|
|
}
|
|
|
|
/* store up to one EA path: */
|
|
if ((params & TME_M68K_OPCODE_EA_WRITE) != 0) {
|
|
(*_tme_m68k_write_memx[ea_size])(ic);
|
|
}
|
|
|
|
/* an instruction has ended: */
|
|
tme_m68k_verify_end(ic, func);
|
|
|
|
#if 1
|
|
if ((int)ic->tme_m68k_ireg_pc < 0x80000)
|
|
{
|
|
extern int printf(const char *format, ...);
|
|
//printf("finished %08x\n", (int)ic->tme_m68k_ireg_pc);
|
|
|
|
if ((int)ic->tme_m68k_ireg_pc == 0x3c3c0) {
|
|
#ifdef _TME_M68K_EXECUTE_FAST
|
|
#else
|
|
printf("blammo; old %x\n", (int)exceptions);
|
|
exceptions |= TME_M68K_EXCEPTION_BERR;
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
/* update the PC: */
|
|
ic->tme_m68k_ireg_pc = ic->tme_m68k_ireg_pc_next;
|
|
TME_M68K_SEQUENCE_START;
|
|
|
|
#ifdef _TME_M68K_EXECUTE_FAST
|
|
/* if we haven't finished the instruction burst yet, continue: */
|
|
if (__tme_predict_true(ic->_tme_m68k_instruction_burst_remaining != 0)) {
|
|
continue;
|
|
}
|
|
#endif /* _TME_M68K_EXECUTE_FAST */
|
|
|
|
/* try to acquire the external mutex and check for external
|
|
resets, halts, or interrupts, and process them along
|
|
with any internal exceptions: */
|
|
rc = tme_mutex_trylock(&ic->tme_m68k_external_mutex);
|
|
if (TME_THREADS_ERRNO(rc) == TME_OK) {
|
|
tme_m68k_external_check(ic,
|
|
#ifdef _TME_M68K_EXECUTE_FAST
|
|
0
|
|
#else /* !_TME_M68K_EXECUTE_FAST */
|
|
exceptions
|
|
#endif /* !_TME_M68K_EXECUTE_FAST */
|
|
);
|
|
|
|
/* unlock the external mutex: */
|
|
tme_mutex_unlock(&ic->tme_m68k_external_mutex);
|
|
}
|
|
|
|
#ifndef _TME_M68K_EXECUTE_FAST
|
|
|
|
/* otherwise, if we have internal exceptions, process them: */
|
|
else if (exceptions) {
|
|
tme_m68k_exception(ic, exceptions);
|
|
}
|
|
|
|
/* if we can go fast now, go fast: */
|
|
if (!tme_m68k_go_slow(ic)) {
|
|
tme_m68k_redispatch(ic);
|
|
}
|
|
|
|
/* otherwise, unless we've used up our burst, continue: */
|
|
if (ic->_tme_m68k_instruction_burst_remaining != 0) {
|
|
continue;
|
|
}
|
|
|
|
#endif /* !_TME_M68K_EXECUTE_FAST */
|
|
|
|
/* start a new burst: */
|
|
ic->_tme_m68k_instruction_burst_remaining
|
|
= ic->_tme_m68k_instruction_burst;
|
|
|
|
/* if this is a cooperative threading system, yield: */
|
|
#if TME_THREADS_COOPERATIVE
|
|
#ifdef _TME_M68K_EXECUTE_FAST
|
|
/* unbusy and forget the fast instruction TLB entry: */
|
|
assert (ic->_tme_m68k_insn_fetch_fast_itlb == tlb);
|
|
tme_m68k_tlb_unbusy(tlb);
|
|
ic->_tme_m68k_insn_fetch_fast_itlb = NULL;
|
|
#endif /* _TME_M68K_EXECUTE_FAST */
|
|
tme_thread_yield();
|
|
#endif /* TME_THREADS_COOPERATIVE */
|
|
|
|
#ifdef _TME_M68K_EXECUTE_FAST
|
|
/* if this instruction TLB entry has been invalidated, redispatch.
|
|
this can only happen in a multiprocessing (preemptive or true
|
|
multiprocessor) environment, and it means that during the
|
|
previous burst, another thread invalidated this instruction TLB
|
|
entry, but we didn't make any callouts at all (for TLB fills,
|
|
slow bus cycles, etc.) where we would have noticed this
|
|
earlier: */
|
|
if (tme_m68k_tlb_is_invalid(tlb)) {
|
|
tme_m68k_redispatch(ic);
|
|
}
|
|
#endif /* _TME_M68K_EXECUTE_FAST */
|
|
|
|
}
|
|
/* NOTREACHED */
|
|
|
|
#ifdef _TME_M68K_EXECUTE_FAST
|
|
|
|
/* if we get here, we "faulted" trying to fetch an instruction word
|
|
from host memory. it's possibly not a "real" fault, since this
|
|
instruction may simply cross a page boundary, but since the fast
|
|
executor can't restart instructions we have to treat this like a
|
|
group 0 fault: */
|
|
_tme_m68k_fast_fetch_failed:
|
|
|
|
/* mimic a group 0 exception: */
|
|
_TME_M68K_INSN_FETCH_SAVE;
|
|
ic->_tme_m68k_group0_flags = TME_M68K_BUS_CYCLE_FETCH | TME_M68K_BUS_CYCLE_READ;
|
|
ic->_tme_m68k_group0_function_code = function_code_program;
|
|
ic->_tme_m68k_group0_address = ic->tme_m68k_ireg_pc + (fetch_fast_next - ic->_tme_m68k_insn_fetch_fast_start);
|
|
ic->_tme_m68k_group0_sequence = ic->_tme_m68k_sequence;
|
|
ic->_tme_m68k_group0_sequence._tme_m68k_sequence_transfer_faulted_after = 0;
|
|
ic->_tme_m68k_group0_buffer_read_size = 0;
|
|
ic->_tme_m68k_group0_buffer_read_softrr = 0;
|
|
tme_m68k_group0_hook_fast(ic);
|
|
ic->_tme_m68k_group0_sequence._tme_m68k_sequence_transfer_faulted =
|
|
ic->_tme_m68k_group0_sequence._tme_m68k_sequence_transfer_next;
|
|
|
|
/* mimic the rte: */
|
|
ic->_tme_m68k_sequence = ic->_tme_m68k_group0_sequence;
|
|
ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_next = 1;
|
|
TME_M68K_SEQUENCE_RESTART;
|
|
|
|
tme_m68k_redispatch(ic);
|
|
/* NOTREACHED */
|
|
#endif /* _TME_M68K_EXECUTE_FAST */
|
|
}
|