syn68k/runtime/syn68k_header.c
Clifford T. Matthews fc75521fb6 Ancient hacks that I made as I was trying to get Executor on iPad.
Unfortunately I never documented exactly what I did and now months
have gone by, so I have to go back and try to figure out what I was
doing and what worked and what didn't.
2010-11-11 09:33:10 -07:00

1069 lines
35 KiB
C

#include "config.h"
/* Global register to hold the PC. We put this up here so that
* the global register will be defined before any of the inline functions
* in syn68k_public.h are hit, but after uint16 is typedef'd.
*/
#ifdef i386
# ifdef __CHECKER__
# define GLOBAL_REGISTER_DECLS \
register const uint16 *code asm ("%si");
# else /* !__CHECKER__ */
# define GLOBAL_REGISTER_DECLS \
register const uint16 *code asm ("%si"); \
register CPUState *cpu_state_ptr asm ("%bp");
# endif /* !__CHECKER__ */
#elif defined (mc68000)
# define GLOBAL_REGISTER_DECLS register const uint16 *code asm ("a4");
#elif defined (__alpha__)
# define GLOBAL_REGISTER_DECLS register const uint16 *code asm ("$9");
#elif defined (powerpc) || defined (__ppc__)
# define GLOBAL_REGISTER_DECLS register const uint16 *code asm ("%r13");
#elif defined(__x86_64)
# define GLOBAL_REGISTER_DECLS register const uint16 *code asm ("%r12");
#else
/* DO NOT COMMIT THIS. IT IS FOR ARM ONLY, but I DON'T KNOW THE PREPROCESSOR SYMBOL */
# define GLOBAL_REGISTER_DECLS register const uint16 *code asm ("%r11");
// # error "Choose a global register to hold the current synthetic PC. Make sure it is saved by the normal calling convention."
#endif
/* #define this value so headers can detect that syn68k.c is including them. */
#define SYN68K_C
#include "syn68k_private.h"
#include "interrupt.h"
#include "hash.h"
#include "trap.h"
#include "profile.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "rangetree.h"
#include "native.h"
#include "translate.h"
#include "recompile.h"
#include <stdlib.h>
#if defined (i386) && !defined (__CHECKER__)
#define cpu_state (*cpu_state_ptr) /* To provide more concise code. */
#endif
#include "ccfuncs.h"
#undef cpu_state
#ifdef DEBUG
# define IFDEBUG(x) x
#else
# define IFDEBUG(x)
#endif
#ifdef USE_BIOS_TIMER
# define RESTORE_FS() asm volatile ("movw %0,%%fs" \
: : "g" (dos_memory_selector))
#else
# define RESTORE_FS()
#endif
/* Do an efficient inline code lookup. Whenever we get a hit on a hash table
* entry, we move it to the head of the linked list. We can just check the
* head here; if that fails, we can do the slower check and possible compile.
*/
static __inline__ const uint16 *code_lookup (uint32 addr) __attribute__((always_inline));
static __inline__ const uint16 *
code_lookup (uint32 addr)
{
Block *b = block_hash_table[BLOCK_HASH (addr)];
const uint16 *c;
if (b != NULL && b->m68k_start_address == addr)
c = b->compiled_code;
else
{
c = hash_lookup_code_and_create_if_needed (addr);
RESTORE_FS ();
}
return c;
}
/* #define FREQUENCY(n) do { if (profile_p) ++frequency[n].freq; } while (0) */
#ifdef FREQUENCY
#warning "Frequency counting code in place; expect a performance hit."
int profile_p = 0;
struct _freq
{
unsigned long freq;
int opcode;
} frequency[65536];
static int
compare_freq (const void *p1, const void *p2)
{
int diff = (((const struct _freq *)p2)->freq
- ((const struct _freq *)p1)->freq);
if (diff != 0)
return diff;
/* Break ties with the opcode. */
return (((const struct _freq *)p1)->opcode
- ((const struct _freq *)p2)->opcode);
}
static int
compare_opcode (const void *p1, const void *p2)
{
return (((const struct _freq *)p1)->opcode
- ((const struct _freq *)p2)->opcode);
}
void
dump_frequency ()
{
int i;
double total_freq;
total_freq = 0.0;
for (i = 0; i < 65536; i++)
{
frequency[i].opcode = i;
total_freq += frequency[i].freq;
}
qsort (frequency, 65536, sizeof frequency[0], compare_freq);
for (i = 0; i < 65536 && frequency[i].freq != 0; i++)
printf ("0x%04X\t%lu\t%.2f%%\n", (unsigned)frequency[i].opcode,
frequency[i].freq, frequency[i].freq * 100.0 / total_freq);
qsort (frequency, 65536, sizeof frequency[0], compare_opcode);
}
void
reset_frequency ()
{
memset (frequency, 0, sizeof frequency);
}
#else
#define FREQUENCY(n)
#endif
#ifdef SYNCHRONOUS_INTERRUPTS
# define CHECK_FOR_INTERRUPT(pc) \
do \
{ \
if (INTERRUPT_PENDING ()) \
{ \
syn68k_addr_t __pc; \
syn68k_addr_t new_addr; \
\
__pc = (pc); \
new_addr = interrupt_process_any_pending (__pc); \
if (new_addr != (__pc)) \
{ \
code = code_lookup (new_addr); \
NEXT_INSTRUCTION (PTR_WORDS); \
} \
} \
} while (0)
#else /* !SYNCHRONOUS_INTERRUPTS */
# define CHECK_FOR_INTERRUPT(pc)
#endif /* !SYNCHRONOUS_INTERRUPTS */
#ifdef USE_DIRECT_DISPATCH
#define INSTR_DEBUG_HOLD_SIZE 0 /* DO NOT CHECK IN WITH NON-ZERO VALUE */
#if INSTR_DEBUG_HOLD_SIZE > 0
#warning "Instructing tracing code in place; expect a performance hit."
void *instr_debug_addr[INSTR_DEBUG_HOLD_SIZE];
void *instr_debug_hold[INSTR_DEBUG_HOLD_SIZE];
int instr_debug_index;
static void next_instruction_hook(const void *vp)
{
++instr_debug_index;
instr_debug_addr[instr_debug_index % INSTR_DEBUG_HOLD_SIZE] = vp;
instr_debug_hold[instr_debug_index % INSTR_DEBUG_HOLD_SIZE] =
* (void **) vp;
}
#define NEXT_INSTRUCTION_HOOK(n) next_instruction_hook(code + (n) - PTR_WORDS)
#else
#define NEXT_INSTRUCTION_HOOK(n)
#endif
# define CASE(n) \
void \
s68k_handle_opcode_ ## n () \
{ \
asm volatile ("\n_S68K_HANDLE_" #n ":"); \
FREQUENCY (n);
# define CASE_PREAMBLE(name,bits,ms,mns,n) {
extern void s68k_handle_opcode_dummy (void);
# ifdef i386
# define NEXT_INSTRUCTION(words_to_inc) \
{ \
register void *next_code asm ("%edi"); /* Little-used register. */ \
NEXT_INSTRUCTION_HOOK(words_to_inc); \
asm volatile ("movl %3,%0\n\t" \
"addl %4,%1\n\t" \
"jmp *%0\n" \
"/* _S68K_DONE_WITH_THIS: */" \
: "=r" (next_code), "=r" (code) : "1" (code), \
"g" (*(void **)(code + (words_to_inc) - PTR_WORDS)), \
"g" (words_to_inc * sizeof (uint16))); \
s68k_handle_opcode_dummy (); \
}
# else /* !i386 */
# define NEXT_INSTRUCTION(words_to_inc) \
{ \
void *next_code; \
NEXT_INSTRUCTION_HOOK(words_to_inc); \
next_code = *(void **)(code + (words_to_inc) - PTR_WORDS); \
INCREMENT_CODE (words_to_inc); \
goto *next_code; \
}
# endif /* !i386 */
# define CASE_POSTAMBLE(words_to_inc) } NEXT_INSTRUCTION (words_to_inc); }
#else
# define CASE(n) case n:
# define NEXT_INSTRUCTION(ignored) break
# define CASE_PREAMBLE(name,bits,ms,mns,n) {
# define CASE_POSTAMBLE(words_to_inc) } NEXT_INSTRUCTION (words_to_inc);
#endif
#if defined(__GNUC__) && !defined(__alpha) && __GNUC__ < 3
/* Work around poor gcc code generated when adding to a global reg var. */
/* NOTE: if we want to use this trick on the alpha, we'll need to cast
v up to 64 bits, first, but this trick may not be needed on
the alpha, so we don't hassle with changes yet */
/* NOTE: I hope we don't need this trick with GCC 3 or greater. I certainly
haven't timed the change though. I just put the test for __GNUC__
in above to get rid of some warnings */
# define INCREMENT_CODE(n) (++((typeof (*code) (*)[n])code))
/* It seems that gcc is generating poor code for ++'s to memory as well. */
# define INC_VAR(v, n) ((typeof (v)) ++((char (*)[n])(v)))
# define DEC_VAR(v, n) ((typeof (v)) --((char (*)[n])(v)))
#else
# define INCREMENT_CODE(n) (code += (n))
# define INC_VAR(v, n) ((v) += (n))
# define DEC_VAR(v, n) ((v) -= (n))
#endif
/* This macro rounds a size up to some integral multiple of PTR_WORDS. */
#define ROUND_UP(n) ((((n) + (PTR_WORDS - 1)) / PTR_WORDS) * PTR_WORDS)
#if 0 && !defined(GO32)
extern void abort (void);
#endif
#ifdef M68K_REGS_IN_ARRAY
# define LOAD_CPU_STATE()
#define SAVE_CPU_STATE()
#else /* !M68K_REGS_IN_ARRAY */
# define LOAD_CPU_STATE() \
d0 = cpu_state.regs[0], d1 = cpu_state.regs[1], \
d2 = cpu_state.regs[2], d3 = cpu_state.regs[3], \
d4 = cpu_state.regs[4], d5 = cpu_state.regs[5], \
d6 = cpu_state.regs[6], d7 = cpu_state.regs[7], \
a0 = cpu_state.regs[8], a1 = cpu_state.regs[9], \
a2 = cpu_state.regs[10], a3 = cpu_state.regs[11], \
a4 = cpu_state.regs[12], a5 = cpu_state.regs[13], \
a6 = cpu_state.regs[14], a7 = cpu_state.regs[15]
#define SAVE_CPU_STATE() \
cpu_state.regs[0] = d0, cpu_state.regs[1] = d1, \
cpu_state.regs[2] = d2, cpu_state.regs[3] = d3, \
cpu_state.regs[4] = d4, cpu_state.regs[5] = d5, \
cpu_state.regs[6] = d6, cpu_state.regs[7] = d7, \
cpu_state.regs[8] = a0, cpu_state.regs[9] = a1, \
cpu_state.regs[10] = a2, cpu_state.regs[11] = a3, \
cpu_state.regs[12] = a4, cpu_state.regs[13] = a5, \
cpu_state.regs[14] = a6, cpu_state.regs[15] = a7
#endif /* !M68K_REGS_IN_ARRAY */
#ifdef M68K_REGS_IN_ARRAY
#define GENERAL_REGISTER(n,TYPE) (cpu_state.regs[n] TYPE)
#define DATA_REGISTER(n,TYPE) (cpu_state.regs[n] TYPE)
#define ADDRESS_REGISTER(n,TYPE) (cpu_state.regs[8 + (n)] TYPE)
/* We use these macros to compensate for gcc brain damage when referencing
* arrays of structs on the i386.
*/
#ifndef offsetof
# define offsetof(s, t) ((int) &((s *) 0)->t)
#endif
#define GENERAL_REGISTER_SB(reg) \
(*((int8 *)((int32 *)&cpu_state.regs[0] + (reg)) + offsetof (M68kReg, ub.n)))
#define GENERAL_REGISTER_UB(reg) \
(*((uint8 *)((int32 *)&cpu_state.regs[0] + (reg)) + offsetof (M68kReg, sb.n)))
#define GENERAL_REGISTER_SW(reg) \
(*(int16 *)((int8 *)((int32 *)&cpu_state.regs[0] + (reg)) \
+ offsetof (M68kReg, sw.n)))
#define GENERAL_REGISTER_UW(reg) \
(*(uint16 *)((int8 *)((int32 *)&cpu_state.regs[0] + (reg)) \
+ offsetof (M68kReg, uw.n)))
#define GENERAL_REGISTER_SL(reg) (*((int32 *)&cpu_state.regs[0] + (reg)))
#define GENERAL_REGISTER_UL(reg) (*((uint32 *)&cpu_state.regs[0] + (reg)))
#define DATA_REGISTER_UB(n) GENERAL_REGISTER_UB (n)
#define DATA_REGISTER_SB(n) GENERAL_REGISTER_SB (n)
#define DATA_REGISTER_UW(n) GENERAL_REGISTER_UW (n)
#define DATA_REGISTER_SW(n) GENERAL_REGISTER_SW (n)
#define DATA_REGISTER_UL(n) GENERAL_REGISTER_UL (n)
#define DATA_REGISTER_SL(n) GENERAL_REGISTER_SL (n)
#define ADDRESS_REGISTER_UB(n) GENERAL_REGISTER_UB ((n) + 8)
#define ADDRESS_REGISTER_SB(n) GENERAL_REGISTER_SB ((n) + 8)
#define ADDRESS_REGISTER_UW(n) GENERAL_REGISTER_UW ((n) + 8)
#define ADDRESS_REGISTER_SW(n) GENERAL_REGISTER_SW ((n) + 8)
#define ADDRESS_REGISTER_UL(n) GENERAL_REGISTER_UL ((n) + 8)
#define ADDRESS_REGISTER_SL(n) GENERAL_REGISTER_SL ((n) + 8)
#else
# define GENERAL_REGISTER(n,TYPE) \
((n) >= 8 ? ADDRESS_REGISTER ((n) - 8, TYPE) : DATA_REGISTER (n, TYPE))
# ifdef __GNUC__
# define DATA_REGISTER(n,TYPE) \
({ int _tmp = (n); \
(((_tmp) > 3) \
? (((_tmp) > 5) \
? (((_tmp) > 6) ? d7 TYPE : d6 TYPE) \
: (((_tmp) < 5) ? d4 TYPE : d5 TYPE)) \
: (((_tmp) > 1) \
? (((_tmp) > 2) ? d3 TYPE : d2 TYPE) \
: (((_tmp) < 1) ? d0 TYPE : d1 TYPE))); })
# define ADDRESS_REGISTER(n,TYPE) \
({ int _tmp = (n); \
(((_tmp) > 3) \
? (((_tmp) > 5) \
? (((_tmp) > 6) ? a7 TYPE : a6 TYPE) \
: (((_tmp) < 5) ? a4 TYPE : a5 TYPE)) \
: (((_tmp) > 1) \
? (((_tmp) > 2) ? a3 TYPE : a2 TYPE) \
: (((_tmp) < 1) ? a0 TYPE : a1 TYPE))); })
# else /* Not M68K_REGS_IN_ARRAY and Not __GNUC__ */
# define DATA_REGISTER(n,TYPE) \
(((n) > 3) \
? (((n) > 5) \
? (((n) > 6) ? d7 TYPE : d6 TYPE) \
: (((n) < 5) ? d4 TYPE : d5 TYPE)) \
: (((n) > 1) \
? (((n) > 2) ? d3 TYPE : d2 TYPE) \
: (((n) < 1) ? d0 TYPE : d1 TYPE)))
# define ADDRESS_REGISTER(n,TYPE) \
(((n) > 3) \
? (((n) > 5) \
? (((n) > 6) ? a7 TYPE : a6 TYPE) \
: (((n) < 5) ? a4 TYPE : a5 TYPE)) \
: (((n) > 1) \
? (((n) > 2) ? a3 TYPE : a2 TYPE) \
: (((n) < 1) ? a0 TYPE : a1 TYPE)))
# endif /* Not __GNUC__ */
#endif /* Not M68K_REGS_IN_ARRAY */
#ifdef M68K_REGS_IN_ARRAY
typedef struct {
uint32 reg; /* Could make these uint8/int8, but that requires */
int32 delta; /* movzbl/movsbl, which are slow and non-pairable. */
} AmodeCleanupInfo;
#endif
static const uint8 neg_bcd_table[16] = {
0x9A, -6, -6, -6, -6, -6, -6, -6, -6, -6, 0xFA, -6, -6, -6, -6, -6
};
#define NEGBCD_TABLE(n) neg_bcd_table[n]
#ifdef M68K_REGS_IN_ARRAY
static const AmodeCleanupInfo amode_cleanup_info[3][64] = {
{ {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},
{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},
{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},
{8, 1}, {9, 1}, {10, 1},{11, 1},{12, 1},{13, 1},{14, 1},{15, 2},
{8,-1}, {9,-1}, {10,-1},{11,-1},{12,-1},{13,-1},{14,-1},{15,-2},
{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},
{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},
{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0} },
{ {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},
{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},
{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},
{8, 2}, {9, 2}, {10, 2},{11, 2},{12, 2},{13, 2},{14, 2},{15,2},
{8,-2}, {9,-2}, {10,-2},{11,-2},{12,-2},{13,-2},{14,-2},{15,-2},
{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},
{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},
{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0} },
{ {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},
{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},
{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},
{8, 4}, {9, 4}, {10, 4},{11, 4},{12, 4},{13, 4},{14, 4},{15, 4},
{8,-4}, {9,-4}, {10,-4},{11,-4},{12,-4},{13,-4},{14,-4},{15,-4},
{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},
{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},
{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0} }
};
#endif
#if defined (__GNUC__) && __GNUC__ > 2
# define NOINLINE __attribute__((noinline))
#else
# define NOINLINE
#endif
static void threaded_gateway (void) NOINLINE;
void
interpret_code (const uint16 *start_code)
{
jmp_buf setjmp_buf;
char *saved_amode_p, *saved_reversed_amode_p; /* Used in interpreter. */
jmp_buf *saved_setjmp_buf;
const uint16 *saved_code;
#if defined (i386) && !defined (__CHECKER__)
CPUState *saved_cpu_state_ptr;
#endif
#ifdef USE_BIOS_TIMER
volatile uint16 saved_fs;
#endif
/* #define CODE_HISTORY 10 */
#if defined(CODE_HISTORY)
uint16 lastcodes[CODE_HISTORY];
uint16 *lastcodeps[CODE_HISTORY];
#endif
#ifndef M68K_REGS_IN_ARRAY
#error "Regs need to be in an array; this is totally broken right now."
#endif
#define d0 cpu_state.regs[0]
#define d1 cpu_state.regs[1]
#define d2 cpu_state.regs[2]
#define d3 cpu_state.regs[3]
#define d4 cpu_state.regs[4]
#define d5 cpu_state.regs[5]
#define d6 cpu_state.regs[6]
#define d7 cpu_state.regs[7]
#define a0 cpu_state.regs[8]
#define a1 cpu_state.regs[9]
#define a2 cpu_state.regs[10]
#define a3 cpu_state.regs[11]
#define a4 cpu_state.regs[12]
#define a5 cpu_state.regs[13]
#define a6 cpu_state.regs[14]
#define a7 cpu_state.regs[15]
/* Save stuff so we are reentrant and don't smash registers illegally. */
#if defined (i386) && !defined (__CHECKER__)
saved_cpu_state_ptr = cpu_state_ptr;
cpu_state_ptr = &cpu_state;
#define cpu_state (*cpu_state_ptr) /* To provide more concise code. */
#endif
saved_amode_p = cpu_state.amode_p;
saved_reversed_amode_p = cpu_state.reversed_amode_p;
saved_setjmp_buf = cpu_state.setjmp_buf;
saved_code = code;
#ifdef USE_BIOS_TIMER
asm volatile ("movw %%fs,%0\n\t"
"movw %1,%%fs"
: "=m" (saved_fs)
: "g" (dos_memory_selector));
#endif /* USE_BIOS_TIMER */
/* Note that we are currently busy. */
++emulation_depth;
/* Grab all information from the CPUState. */
LOAD_CPU_STATE ();
code = start_code;
/* Skip over various hacks. */
goto main_loop;
#ifndef M68K_REGS_IN_ARRAY
cleanup_amode_for_size_1:
switch (amode) {
case 24: a0.ul.n += 1; break;
case 25: a1.ul.n += 1; break;
case 26: a2.ul.n += 1; break;
case 27: a3.ul.n += 1; break;
case 28: a4.ul.n += 1; break;
case 29: a5.ul.n += 1; break;
case 30: a6.ul.n += 1; break;
case 31: a7.ul.n += 2; break;
case 32: a0.ul.n -= 1; break;
case 33: a1.ul.n -= 1; break;
case 34: a2.ul.n -= 1; break;
case 35: a3.ul.n -= 1; break;
case 36: a4.ul.n -= 1; break;
case 37: a5.ul.n -= 1; break;
case 38: a6.ul.n -= 1; break;
case 39: a7.ul.n -= 2; break;
}
goto main_loop;
cleanup_amode_for_size_2:
switch (amode) {
case 24: a0.ul.n += 2; break;
case 25: a1.ul.n += 2; break;
case 26: a2.ul.n += 2; break;
case 27: a3.ul.n += 2; break;
case 28: a4.ul.n += 2; break;
case 29: a5.ul.n += 2; break;
case 30: a6.ul.n += 2; break;
case 31: a7.ul.n += 2; break;
case 32: a0.ul.n -= 2; break;
case 33: a1.ul.n -= 2; break;
case 34: a2.ul.n -= 2; break;
case 35: a3.ul.n -= 2; break;
case 36: a4.ul.n -= 2; break;
case 37: a5.ul.n -= 2; break;
case 38: a6.ul.n -= 2; break;
case 39: a7.ul.n -= 2; break;
}
goto main_loop;
cleanup_amode_for_size_4:
switch (amode) {
case 24: a0.ul.n += 4; break;
case 25: a1.ul.n += 4; break;
case 26: a2.ul.n += 4; break;
case 27: a3.ul.n += 4; break;
case 28: a4.ul.n += 4; break;
case 29: a5.ul.n += 4; break;
case 30: a6.ul.n += 4; break;
case 31: a7.ul.n += 4; break;
case 32: a0.ul.n -= 4; break;
case 33: a1.ul.n -= 4; break;
case 34: a2.ul.n -= 4; break;
case 35: a3.ul.n -= 4; break;
case 36: a4.ul.n -= 4; break;
case 37: a5.ul.n -= 4; break;
case 38: a6.ul.n -= 4; break;
case 39: a7.ul.n -= 4; break;
}
goto main_loop;
#define CLEANUP_AMODE(mode, size) goto cleanup_amode_for_size_ ## size
#else /* M68K_REGS_IN_ARRAY */
/* We use ugly, idiotic code here to compensate for gcc 2.6.0 brain damage
* when referencing arrays of structs on the i386.
*/
#define CLEANUP_REG(mode, ix) \
(*(uint32 *) ((int8 *) &amode_cleanup_info[ix][0] \
+ (sizeof amode_cleanup_info[0][0] * (mode)) \
+ offsetof (AmodeCleanupInfo, reg)))
#define CLEANUP_DELTA(mode, ix) \
(*(int32 *) ((int8 *) &amode_cleanup_info[ix][0] \
+ (sizeof amode_cleanup_info[0][0] * (mode)) \
+ offsetof (AmodeCleanupInfo, delta)))
#define CLEANUP_AMODE(mode, size) \
{ \
/* C compiler should do good things here since "size" is a constant. */ \
if ((size) == 1) \
GENERAL_REGISTER_SL (CLEANUP_REG (mode, 0)) \
+= CLEANUP_DELTA (mode, 0); \
else if ((size) == 2) \
GENERAL_REGISTER_SL (CLEANUP_REG (mode, 1)) \
+= CLEANUP_DELTA (mode, 1); \
else \
GENERAL_REGISTER_SL (CLEANUP_REG (mode, 2)) \
+= CLEANUP_DELTA (mode, 2); \
}
#endif
/* Extract out increment of code and put it before the loop; this
* should save some memory as arms of the switch that used to increment
* this and branch back to the top can now just branch back to the top.
* Will probably also help instruction cache hits. Falls through to
* the main loop...
*/
main_loop:
#ifdef USE_DIRECT_DISPATCH
cpu_state.setjmp_buf = &setjmp_buf;
if (!setjmp (setjmp_buf))
threaded_gateway ();
SAVE_CPU_STATE ();
/* Restore stuff so (for reentrancy). */
cpu_state.amode_p = saved_amode_p;
cpu_state.reversed_amode_p = saved_reversed_amode_p;
cpu_state.setjmp_buf = saved_setjmp_buf;
code = saved_code;
#ifdef USE_BIOS_TIMER
asm volatile ("movw %0,%%fs"
: : "m" (saved_fs));
#endif
#if defined (i386) && !defined (__CHECKER__)
cpu_state_ptr = saved_cpu_state_ptr;
#endif
}
#else
while (1)
{
/* This can't be used with USE_DIRECT_DISPATCH enabled. */
#if defined(CODE_HISTORY)
memmove(lastcodes +1, lastcodes ,
sizeof(lastcodes ) - sizeof(lastcodes [0]));
memmove(lastcodeps+1, lastcodeps,
sizeof(lastcodeps) - sizeof(lastcodeps[0]));
lastcodes [0] = (int) *(void **)code;
lastcodeps[0] = code;
#endif
switch ((int) *(((void **)code)++))
{
#endif
#ifndef USE_DIRECT_DISPATCH
/* Default to printing error message. */
default:
fprintf (stderr, "Internal error: unknown synthetic opcode 0x%04X; "
"code = %p\n", (unsigned) (((void **) code)[-1]),
(void *) code);
abort ();
break;
#endif /* !USE_DIRECT_DISPATCH */
/* Reserved - exit emulator. */
CASE (0x0000)
CASE_PREAMBLE ("Reserved - exit emulator", "", "", "", "")
#ifdef USE_DIRECT_DISPATCH
--emulation_depth;
assert (emulation_depth >= 0);
longjmp (*cpu_state.setjmp_buf, 1);
#else
SAVE_CPU_STATE ();
/* Restore stuff (for reentrancy). */
cpu_state.amode_p = saved_amode_p;
cpu_state.reversed_amode_p = saved_reversed_amode_p;
cpu_state.setjmp_buf = saved_setjmp_buf;
code = saved_code;
--emulation_depth;
assert (emulation_depth >= 0);
return;
#endif
CASE_POSTAMBLE (ROUND_UP (PTR_WORDS));
/* Reserved - one word NOP. */
CASE (0x0001)
#ifdef GENERATE_NATIVE_CODE
CASE_PREAMBLE ("Reserved: skip native preamble NOP", "", "", "", "")
#ifdef SYNCHRONOUS_INTERRUPTS
{
/* Each block's code is prefaced by its address in big
* endian order. Since we know we're at the beginning of
* a block, we can check for the interrupt here.
*/
#if SIZEOF_CHAR_P != 8
syn68k_addr_t addr = READUL (US_TO_SYN68K (code - PTR_WORDS - PTR_WORDS));
#else
syn68k_addr_t addr = READUL_US (code - PTR_WORDS - PTR_WORDS);
#endif
CHECK_FOR_INTERRUPT (addr);
}
#endif /* SYNCHRONOUS_INTERRUPTS */
CASE_POSTAMBLE (ROUND_UP (PTR_WORDS + PTR_WORDS
+ NATIVE_PREAMBLE_WORDS));
#else /* !GENERATE_NATIVE_CODE */
/* Historical cruft. */
CASE_PREAMBLE ("Reserved: 1 word NOP", "", "", "", "")
abort ();
CASE_POSTAMBLE (ROUND_UP (PTR_WORDS + 1));
#endif /* !GENERATE_NATIVE_CODE */
/* Reserved - two word NOP. */
CASE (0x0002)
/* Historical cruft. */
CASE_PREAMBLE ("Reserved: 2 word NOP", "", "", "", "")
CASE_POSTAMBLE (ROUND_UP (PTR_WORDS + 1));
/* Reserved - skip stub NOP. */
CASE (0x0003)
#ifdef GENERATE_NATIVE_CODE
CASE_PREAMBLE ("Reserved: count block freq NOP", "", "", "", "")
{
Block *b = *((Block **)code);
if (b != NULL)
{
syn68k_addr_t addr = b->m68k_start_address;
CHECK_FOR_INTERRUPT (addr);
if (native_code_p
&& ++b->num_times_called >= RECOMPILE_CUTOFF
&& emulation_depth == 1)
{
recompile_block_as_native (b);
code = (hash_lookup_code_and_create_if_needed (addr)
/* Compensate for the add we do below. */
- ROUND_UP (PTR_WORDS + PTR_WORDS
+ NATIVE_PREAMBLE_WORDS)
+ OPCODE_WORDS);
}
}
}
CASE_POSTAMBLE (ROUND_UP (PTR_WORDS + PTR_WORDS
+ NATIVE_PREAMBLE_WORDS));
#else /* !GENERATE_NATIVE_CODE */
/* Historical cruft. */
CASE_PREAMBLE ("Reserved: 3 word NOP", "", "", "", "")
CASE_POSTAMBLE (ROUND_UP (PTR_WORDS + 2));
#endif /* !GENERATE_NATIVE_CODE */
#define AMODE_2_3(casenum, reg, p) \
CASE (casenum) \
CASE_PREAMBLE ("Reserved - compute " #p " for mode == 2/3, reg == " \
#reg, "", "", "", "") \
p = (char *) SYN68K_TO_US (CLEAN (reg)); \
IFDEBUG (printf ("\t" #p " == %p\n", p)); \
CASE_POSTAMBLE (ROUND_UP (PTR_WORDS))
AMODE_2_3 (0x0004, a0.ul.n, cpu_state.amode_p);
AMODE_2_3 (0x0005, a1.ul.n, cpu_state.amode_p);
AMODE_2_3 (0x0006, a2.ul.n, cpu_state.amode_p);
AMODE_2_3 (0x0007, a3.ul.n, cpu_state.amode_p);
AMODE_2_3 (0x0008, a4.ul.n, cpu_state.amode_p);
AMODE_2_3 (0x0009, a5.ul.n, cpu_state.amode_p);
AMODE_2_3 (0x000A, a6.ul.n, cpu_state.amode_p);
AMODE_2_3 (0x000B, a7.ul.n, cpu_state.amode_p);
AMODE_2_3 (0x000C, a0.ul.n, cpu_state.reversed_amode_p);
AMODE_2_3 (0x000D, a1.ul.n, cpu_state.reversed_amode_p);
AMODE_2_3 (0x000E, a2.ul.n, cpu_state.reversed_amode_p);
AMODE_2_3 (0x000F, a3.ul.n, cpu_state.reversed_amode_p);
AMODE_2_3 (0x0010, a4.ul.n, cpu_state.reversed_amode_p);
AMODE_2_3 (0x0011, a5.ul.n, cpu_state.reversed_amode_p);
AMODE_2_3 (0x0012, a6.ul.n, cpu_state.reversed_amode_p);
AMODE_2_3 (0x0013, a7.ul.n, cpu_state.reversed_amode_p);
#undef AMODE_2_3
#define AMODE_4(casenum, reg, size, p) \
CASE (casenum) \
CASE_PREAMBLE ("Reserved - compute " #p " for mode == 4, reg == " \
#reg ", size == " #size, "", "", "", "") \
p = (char *) SYN68K_TO_US (CLEAN (reg - size)); \
IFDEBUG (printf ("\t" #p " == %p\n", p)); \
CASE_POSTAMBLE (ROUND_UP (PTR_WORDS))
AMODE_4 (0x0014, a0.ul.n, 1, cpu_state.amode_p);
AMODE_4 (0x0015, a1.ul.n, 1, cpu_state.amode_p);
AMODE_4 (0x0016, a2.ul.n, 1, cpu_state.amode_p);
AMODE_4 (0x0017, a3.ul.n, 1, cpu_state.amode_p);
AMODE_4 (0x0018, a4.ul.n, 1, cpu_state.amode_p);
AMODE_4 (0x0019, a5.ul.n, 1, cpu_state.amode_p);
AMODE_4 (0x001A, a6.ul.n, 1, cpu_state.amode_p);
AMODE_4 (0x001B, a7.ul.n, 2, cpu_state.amode_p);
AMODE_4 (0x001C, a0.ul.n, 1, cpu_state.reversed_amode_p);
AMODE_4 (0x001D, a1.ul.n, 1, cpu_state.reversed_amode_p);
AMODE_4 (0x001E, a2.ul.n, 1, cpu_state.reversed_amode_p);
AMODE_4 (0x001F, a3.ul.n, 1, cpu_state.reversed_amode_p);
AMODE_4 (0x0020, a4.ul.n, 1, cpu_state.reversed_amode_p);
AMODE_4 (0x0021, a5.ul.n, 1, cpu_state.reversed_amode_p);
AMODE_4 (0x0022, a6.ul.n, 1, cpu_state.reversed_amode_p);
AMODE_4 (0x0023, a7.ul.n, 2, cpu_state.reversed_amode_p);
AMODE_4 (0x0024, a0.ul.n, 2, cpu_state.amode_p);
AMODE_4 (0x0025, a1.ul.n, 2, cpu_state.amode_p);
AMODE_4 (0x0026, a2.ul.n, 2, cpu_state.amode_p);
AMODE_4 (0x0027, a3.ul.n, 2, cpu_state.amode_p);
AMODE_4 (0x0028, a4.ul.n, 2, cpu_state.amode_p);
AMODE_4 (0x0029, a5.ul.n, 2, cpu_state.amode_p);
AMODE_4 (0x002A, a6.ul.n, 2, cpu_state.amode_p);
AMODE_4 (0x002B, a7.ul.n, 2, cpu_state.amode_p);
AMODE_4 (0x002C, a0.ul.n, 2, cpu_state.reversed_amode_p);
AMODE_4 (0x002D, a1.ul.n, 2, cpu_state.reversed_amode_p);
AMODE_4 (0x002E, a2.ul.n, 2, cpu_state.reversed_amode_p);
AMODE_4 (0x002F, a3.ul.n, 2, cpu_state.reversed_amode_p);
AMODE_4 (0x0030, a4.ul.n, 2, cpu_state.reversed_amode_p);
AMODE_4 (0x0031, a5.ul.n, 2, cpu_state.reversed_amode_p);
AMODE_4 (0x0032, a6.ul.n, 2, cpu_state.reversed_amode_p);
AMODE_4 (0x0033, a7.ul.n, 2, cpu_state.reversed_amode_p);
AMODE_4 (0x0034, a0.ul.n, 4, cpu_state.amode_p);
AMODE_4 (0x0035, a1.ul.n, 4, cpu_state.amode_p);
AMODE_4 (0x0036, a2.ul.n, 4, cpu_state.amode_p);
AMODE_4 (0x0037, a3.ul.n, 4, cpu_state.amode_p);
AMODE_4 (0x0038, a4.ul.n, 4, cpu_state.amode_p);
AMODE_4 (0x0039, a5.ul.n, 4, cpu_state.amode_p);
AMODE_4 (0x003A, a6.ul.n, 4, cpu_state.amode_p);
AMODE_4 (0x003B, a7.ul.n, 4, cpu_state.amode_p);
AMODE_4 (0x003C, a0.ul.n, 4, cpu_state.reversed_amode_p);
AMODE_4 (0x003D, a1.ul.n, 4, cpu_state.reversed_amode_p);
AMODE_4 (0x003E, a2.ul.n, 4, cpu_state.reversed_amode_p);
AMODE_4 (0x003F, a3.ul.n, 4, cpu_state.reversed_amode_p);
AMODE_4 (0x0040, a4.ul.n, 4, cpu_state.reversed_amode_p);
AMODE_4 (0x0041, a5.ul.n, 4, cpu_state.reversed_amode_p);
AMODE_4 (0x0042, a6.ul.n, 4, cpu_state.reversed_amode_p);
AMODE_4 (0x0043, a7.ul.n, 4, cpu_state.reversed_amode_p);
#undef AMODE_4
#define AMODE_5(casenum, reg, p) \
CASE (casenum) \
CASE_PREAMBLE ("Reserved - compute " #p " for mode == 5, "\
"reg == " #reg, "", "", "", "") \
p = (char *) SYN68K_TO_US (CLEAN (reg + (*(int32 *)code))); \
IFDEBUG (printf ("\t" #p " == %p\n", p)); \
CASE_POSTAMBLE (ROUND_UP (PTR_WORDS + 2))
AMODE_5 (0x0044, a0.ul.n, cpu_state.amode_p);
AMODE_5 (0x0045, a1.ul.n, cpu_state.amode_p);
AMODE_5 (0x0046, a2.ul.n, cpu_state.amode_p);
AMODE_5 (0x0047, a3.ul.n, cpu_state.amode_p);
AMODE_5 (0x0048, a4.ul.n, cpu_state.amode_p);
AMODE_5 (0x0049, a5.ul.n, cpu_state.amode_p);
AMODE_5 (0x004A, a6.ul.n, cpu_state.amode_p);
AMODE_5 (0x004B, a7.ul.n, cpu_state.amode_p);
AMODE_5 (0x004C, a0.ul.n, cpu_state.reversed_amode_p);
AMODE_5 (0x004D, a1.ul.n, cpu_state.reversed_amode_p);
AMODE_5 (0x004E, a2.ul.n, cpu_state.reversed_amode_p);
AMODE_5 (0x004F, a3.ul.n, cpu_state.reversed_amode_p);
AMODE_5 (0x0050, a4.ul.n, cpu_state.reversed_amode_p);
AMODE_5 (0x0051, a5.ul.n, cpu_state.reversed_amode_p);
AMODE_5 (0x0052, a6.ul.n, cpu_state.reversed_amode_p);
AMODE_5 (0x0053, a7.ul.n, cpu_state.reversed_amode_p);
#undef AMODE_5
CASE (0x0054)
CASE_PREAMBLE ("Reserved - compute cpu_state.amode_p for (xxx).W",
"", "", "", "")
cpu_state.amode_p = (char *) SYN68K_TO_US (CLEAN (*(int32 *)code));
#ifdef DEBUG
printf ("\tcpu_state.amode_p = %p\n", (void *) cpu_state.amode_p);
#endif
CASE_POSTAMBLE (ROUND_UP (PTR_WORDS + 2));
CASE (0x0055)
CASE_PREAMBLE ("Reserved - compute cpu_state.reversed_amode_p for (xxx).W",
"", "", "", "")
cpu_state.reversed_amode_p = (char *) SYN68K_TO_US (CLEAN (*(int32 *)code));
#ifdef DEBUG
printf ("\tcpu_state.reversed_amode_p = %p\n", (void *) cpu_state.reversed_amode_p);
#endif
CASE_POSTAMBLE (ROUND_UP (PTR_WORDS + 2));
CASE (0x0056)
CASE_PREAMBLE ("Reserved - compute cpu_state.amode_p for (xxx).L",
"", "", "", "")
cpu_state.amode_p = (char *) *(signed char **)code;
#ifdef DEBUG
printf ("\tcpu_state.amode_p = %p\n", (void *) cpu_state.amode_p);
#endif
CASE_POSTAMBLE (ROUND_UP (PTR_WORDS + PTR_WORDS));
CASE (0x0057)
CASE_PREAMBLE ("Reserved - compute cpu_state.reversed_amode_p for (xxx).L",
"", "", "", "")
cpu_state.reversed_amode_p = (char *) *(signed char **)code;
#ifdef DEBUG
printf ("\tcpu_state.reversed_amode_p = %p\n",
(void *) cpu_state.reversed_amode_p);
#endif
CASE_POSTAMBLE (ROUND_UP (PTR_WORDS + PTR_WORDS));
#define AMODE_6_SIMPLE(casenum, areg, ixreg, size, p) \
CASE (casenum) \
CASE_PREAMBLE ("Reserved - compute " #p " for mode == 6, areg == " \
#areg ", ixreg == " #ixreg, "", "", "", "") \
p = (char *) (CLEAN ((areg) + (((int32) (ixreg)) << \
*(uint32 *)(code + PTR_WORDS + 2)) \
+ *(signed char **)code)); \
IFDEBUG (printf ("\t" #p " = %p\n", (void *) p)); \
CASE_POSTAMBLE (ROUND_UP (PTR_WORDS + (size)))
#define ALL_AREG_AMODE_6_SIMPLE(base0, base1, base2, base3, \
base4, base5, base6, base7, \
base8, base9, base10, base11, \
base12, base13, base14, base15, \
base16, base17, ixreg, size) \
AMODE_6_SIMPLE (base0, a0.ul.n, ixreg, size, cpu_state.amode_p); \
AMODE_6_SIMPLE (base1, a1.ul.n, ixreg, size, cpu_state.amode_p); \
AMODE_6_SIMPLE (base2, a2.ul.n, ixreg, size, cpu_state.amode_p); \
AMODE_6_SIMPLE (base3, a3.ul.n, ixreg, size, cpu_state.amode_p); \
AMODE_6_SIMPLE (base4, a4.ul.n, ixreg, size, cpu_state.amode_p); \
AMODE_6_SIMPLE (base5, a5.ul.n, ixreg, size, cpu_state.amode_p); \
AMODE_6_SIMPLE (base6, a6.ul.n, ixreg, size, cpu_state.amode_p); \
AMODE_6_SIMPLE (base7, a7.ul.n, ixreg, size, cpu_state.amode_p); \
AMODE_6_SIMPLE (base8, 0, ixreg, size, cpu_state.amode_p); \
AMODE_6_SIMPLE (base9, a0.ul.n, ixreg, size, \
cpu_state.reversed_amode_p); \
AMODE_6_SIMPLE (base10, a1.ul.n, ixreg, size, \
cpu_state.reversed_amode_p); \
AMODE_6_SIMPLE (base11, a2.ul.n, ixreg, size, \
cpu_state.reversed_amode_p); \
AMODE_6_SIMPLE (base12, a3.ul.n, ixreg, size, \
cpu_state.reversed_amode_p); \
AMODE_6_SIMPLE (base13, a4.ul.n, ixreg, size, \
cpu_state.reversed_amode_p); \
AMODE_6_SIMPLE (base14, a5.ul.n, ixreg, size, \
cpu_state.reversed_amode_p); \
AMODE_6_SIMPLE (base15, a6.ul.n, ixreg, size, \
cpu_state.reversed_amode_p); \
AMODE_6_SIMPLE (base16, a7.ul.n, ixreg, size, \
cpu_state.reversed_amode_p); \
AMODE_6_SIMPLE (base17, 0, ixreg, size, \
cpu_state.reversed_amode_p)
/* Actual case statements. */
ALL_AREG_AMODE_6_SIMPLE (0x0058, 0x0059, 0x005A, 0x005B, 0x005C,
0x005D, 0x005E, 0x005F, 0x0060, 0x0061,
0x0062, 0x0063, 0x0064, 0x0065, 0x0066,
0x0067, 0x0068, 0x0069,
DATA_REGISTER_SW (*(uint32 *)(code
+ PTR_WORDS)),
4 + PTR_WORDS);
ALL_AREG_AMODE_6_SIMPLE (0x006A, 0x006B, 0x006C, 0x006D, 0x006E,
0x006F, 0x0070, 0x0071, 0x0072, 0x0073,
0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
0x0079, 0x007A, 0x007B,
ADDRESS_REGISTER_SW (*(uint32 *)(code
+ PTR_WORDS)),
4 + PTR_WORDS);
ALL_AREG_AMODE_6_SIMPLE (0x007C, 0x007D, 0x007E, 0x007F, 0x0080,
0x0081, 0x0082, 0x0083, 0x0084, 0x0085,
0x0086, 0x0087, 0x0088, 0x0089, 0x008A,
0x008B, 0x008C, 0x008D,
DATA_REGISTER_UL (*(uint32 *)(code + PTR_WORDS)),
4 + PTR_WORDS);
ALL_AREG_AMODE_6_SIMPLE (0x008E, 0x008F, 0x0090, 0x0091, 0x0092,
0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
0x0098, 0x0099, 0x009A, 0x009B, 0x009C,
0x009D, 0x009E, 0x009F,
ADDRESS_REGISTER_UL (*(uint32 *)(code
+ PTR_WORDS)),
4 + PTR_WORDS);
ALL_AREG_AMODE_6_SIMPLE (0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4,
0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9,
0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE,
0x00AF, 0x00B0, 0x00B1,
0, PTR_WORDS);/* ixreg suppressed */
#undef ALL_AREG_AMODE_6_SIMPLE
#undef AMODE_6_SIMPLE
/* These addressing modes (memory indirect pre- and post-indexed)
* are ridiculous so I don't care about performance. There is only
* one case for all variants of this addressing mode, and only one
* variant for amode_p and reversed_amode_p. It wouldn't be difficult
* to speed this up by expanding out this case and doing more work at
* translation time.
*/
CASE (0x00B2)
CASE_PREAMBLE ("Reserved - compute [reversed_]amode_p for memory "
"indirect pre/post-indexed", "", "", "", "")
int32 base_displacement, outer_displacement;
uint32 flags = *(uint32 *)(code + 4);
uint32 areg = ((flags & 0x80) ? 0
: ADDRESS_REGISTER_UL (*(uint32 *)(code + 6)));
int32 index;
char *temp;
if (flags & 0x40) /* Index suppress? */
index = 0;
else
{
if (flags & (1 << 11))
index = GENERAL_REGISTER_SL (flags >> 12);
else
index = GENERAL_REGISTER_SW (flags >> 12);
index <<= (flags >> 9) & 3;
}
base_displacement = ((int32 *)code)[0];
outer_displacement = ((int32 *)code)[1];
if (flags & 2)
temp = (char *) SYN68K_TO_US (READSL (areg + base_displacement
+ index)
+ outer_displacement);
else
temp = (char *) SYN68K_TO_US (READSL (areg + base_displacement)
+ index + outer_displacement);
if (flags & 1)
{
cpu_state.reversed_amode_p = temp;
#ifdef DEBUG
printf ("\tcpu_state.reversed_amode_p = %p\n",
(void *) reversed_amode_p);
#endif
}
else
{
cpu_state.amode_p = temp;
#ifdef DEBUG
printf ("\tcpu_state.amode_p = %p\n", (void *) cpu_state.amode_p);
#endif
}
CASE_POSTAMBLE (ROUND_UP (PTR_WORDS + 8));
CASE (0x00B3)
CASE_PREAMBLE ("Reserved - callback", "", "", "", "")
SAVE_CPU_STATE ();
code = code_lookup ((*((uint32 (**)(uint32, void *)) code))
(*(uint32 *)(code + PTR_WORDS + PTR_WORDS),
*(void **)(code + PTR_WORDS)));
LOAD_CPU_STATE ();
RESTORE_FS ();
CASE_POSTAMBLE (ROUND_UP (PTR_WORDS));
CASE (0x00B4)
CASE_PREAMBLE ("Reserved - fast jsr", "", "", "", "")
unsigned ix;
jsr_stack_elt_t *j;
syn68k_addr_t retaddr;
ix = ((cpu_state.jsr_stack_byte_index - sizeof (jsr_stack_elt_t))
% sizeof (cpu_state.jsr_stack));
cpu_state.jsr_stack_byte_index = ix;
/* Note: retaddr is in big-endian byte order. */
retaddr = *(const uint32 *)(code + PTR_WORDS + PTR_WORDS);
j = (jsr_stack_elt_t *)((char *)&cpu_state.jsr_stack + ix);
j->tag = retaddr;
j->code = *(const uint16 **)(code + PTR_WORDS);
code = *(const uint16 **)code;
a7.ul.n -= 4;
WRITEUL_UNSWAPPED (SYN68K_TO_US (CLEAN (a7.ul.n)), retaddr);
CASE_POSTAMBLE (ROUND_UP (PTR_WORDS));