mirror of
https://github.com/ctm/syn68k.git
synced 2024-11-28 12:51:40 +00:00
1280 lines
38 KiB
C
1280 lines
38 KiB
C
#include "syn68k_private.h"
|
|
#include "block.h"
|
|
#include "mapping.h"
|
|
#include "rangetree.h"
|
|
#include "translate.h"
|
|
#include "alloc.h"
|
|
#include "blockinfo.h"
|
|
#include "hash.h"
|
|
#include "diagnostics.h"
|
|
#include "destroyblock.h"
|
|
#include "callback.h"
|
|
#include "deathqueue.h"
|
|
#include "checksum.h"
|
|
#include "native.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include "safe_alloca.h"
|
|
|
|
/* If != NULL, we call this function periodically while we are busy. We
|
|
* pass it a 1 if we are busy, and a 0 when we are done.
|
|
*/
|
|
void (*call_while_busy_func)(int);
|
|
|
|
#ifdef GENERATE_NATIVE_CODE
|
|
/* Boolean: generate native code? */
|
|
int native_code_p;
|
|
#else
|
|
#define native_code_p FALSE
|
|
#endif
|
|
|
|
/* This keeps track of how nested our executions have gotten. We don't
|
|
* want to recompile code if we're nested, since that might involve
|
|
* destroying code being run by the interrupted task.
|
|
*/
|
|
int emulation_depth = 0;
|
|
|
|
|
|
static void compute_child_code_pointers (Block *b);
|
|
static void generate_code (Block *b, TempBlockInfo *tbi
|
|
/* #ifdef GENERATE_NATIVE_CODE */
|
|
, BOOL try_native_p
|
|
/* #endif */ /* GENERATE_NATIVE_CODE */
|
|
);
|
|
static int translate_instruction (const uint16 *m68k_code,
|
|
uint16 *synthetic_code,
|
|
const OpcodeMappingInfo *map,
|
|
int ccbits_live,
|
|
int ccbits_to_compute,
|
|
AmodeFetchInfo amf[2],
|
|
const TempBlockInfo *tbi,
|
|
Block *block,
|
|
int32 *backpatch_request_index
|
|
#ifdef GENERATE_NATIVE_CODE
|
|
, cache_info_t *cache_info,
|
|
BOOL *prev_native_p, BOOL try_native_p
|
|
#endif
|
|
);
|
|
static int generate_amode_fetch (uint16 *scode, const uint16 *m68koperand,
|
|
int amode, BOOL reversed, int size);
|
|
|
|
typedef struct
|
|
{
|
|
const OpcodeMappingInfo *map;
|
|
int live_cc;
|
|
} MapAndCC;
|
|
|
|
static void compute_maps_and_ccs (Block *b, MapAndCC *m,
|
|
const TempBlockInfo *tbi);
|
|
|
|
|
|
/* Compiles a block at a specified address and returns a mask indicating
|
|
* which cc bits must be valid on entry to this block. The block is placed
|
|
* in the hashtable and the rangetree. The block just created is
|
|
* returned by reference in *new. If a block already exists at the specified
|
|
* address, a new block is not created; cc bit information from the already
|
|
* existing block is returned.
|
|
*/
|
|
int
|
|
generate_block (Block *parent, uint32 m68k_address, Block **new
|
|
/* #ifdef GENERATE_NATIVE_CODE */
|
|
, BOOL try_native_p
|
|
/* #endif */ /* GENERATE_NATIVE_CODE */
|
|
)
|
|
{
|
|
Block *b, *old_block;
|
|
TempBlockInfo tbi;
|
|
int cc_needed_by_this_block, cc_needed_by_children;
|
|
int i;
|
|
|
|
/* Call a user-defined function periodically while doing stuff. */
|
|
if (call_while_busy_func != NULL)
|
|
call_while_busy_func (1);
|
|
|
|
/* If a block already exists there, just return its info. */
|
|
old_block = hash_lookup (m68k_address);
|
|
if (old_block != NULL)
|
|
{
|
|
*new = old_block;
|
|
if (parent != NULL)
|
|
block_add_parent (old_block, parent);
|
|
return old_block->cc_needed;
|
|
}
|
|
|
|
/* See if this is really a magical callback address; if so, compile
|
|
* it as such.
|
|
*/
|
|
if (IS_CALLBACK (m68k_address))
|
|
{
|
|
int tmp_cc = callback_compile (parent, m68k_address, new);
|
|
if (*new != NULL)
|
|
return tmp_cc;
|
|
}
|
|
|
|
/* Create an entirely new block & gather info about it. */
|
|
b = *new = block_new ();
|
|
|
|
compute_block_info (b, SYN68K_TO_US (m68k_address), &tbi);
|
|
if (parent != NULL)
|
|
block_add_parent (b, parent);
|
|
|
|
/* Temporarily, additionally demand that all cc bits we might not set
|
|
* be valid on entry into this block. We'll get a better idea what
|
|
* bits need to be set, but we need to assume the worst for this recursion.
|
|
*/
|
|
cc_needed_by_this_block = b->cc_needed;
|
|
b->cc_needed |= b->cc_may_not_set;
|
|
|
|
/* Add this block to the universe of blocks. */
|
|
hash_insert (b);
|
|
range_tree_insert (b);
|
|
|
|
/* Generate all child blocks & determine what cc bits they need. If this
|
|
* block has itself as a child, we ignore what cc bits it needs (since
|
|
* it needs exactly what we are now computing and can't add any new bits).
|
|
* Hopefully this should help tight loops compute fewer cc bits.
|
|
*/
|
|
b->num_children = tbi.num_child_blocks;
|
|
if (tbi.num_child_blocks == 0) /* No children -> next_block_dynamic. */
|
|
cc_needed_by_children = ALL_CCS;
|
|
else
|
|
for (i = 0, cc_needed_by_children = 0; i < b->num_children; i++)
|
|
{
|
|
if (tbi.child[i] != m68k_address)
|
|
cc_needed_by_children |= generate_block (b, US_TO_SYN68K (tbi.child[i]),
|
|
&b->child[i],
|
|
try_native_p);
|
|
else
|
|
{
|
|
block_add_parent (b, b);
|
|
b->child[i] = b;
|
|
}
|
|
}
|
|
|
|
/* Compute exactly what cc bits must be valid on block entry. */
|
|
b->cc_needed = (cc_needed_by_this_block
|
|
| (cc_needed_by_children & b->cc_may_not_set));
|
|
|
|
/* Generate code for this block. */
|
|
generate_code (b, &tbi,
|
|
native_code_p && (try_native_p || emulation_depth != 1));
|
|
|
|
/* Free up the scratch memory for tbi. */
|
|
free (tbi.next_instr_offset);
|
|
|
|
/* Finally, fill in all pointers, offsets, etc. to our children's code. */
|
|
compute_child_code_pointers (b);
|
|
|
|
/* Add this block to the end of the death queue. */
|
|
death_queue_enqueue (b);
|
|
|
|
return b->cc_needed;
|
|
}
|
|
|
|
|
|
/* This function fills in the synthetic operands that point to subsequent
|
|
* blocks with pointers to the compiled code in the child blocks. Because
|
|
* we have to compile loops, it may not always be possible to get the
|
|
* desired information about the child. However, we are guaranteed that
|
|
* when that child _does_ get filled in, it will recurse back to us
|
|
* and we'll get our child code pointers filled in then.
|
|
*/
|
|
static void
|
|
compute_child_code_pointers (Block *b)
|
|
{
|
|
int i;
|
|
backpatch_t *p, *next;
|
|
|
|
/* Loop over all of our backpatches and fill in what we can. */
|
|
for (p = b->backpatch; p != NULL; p = next)
|
|
{
|
|
next = p->next;
|
|
if (p->target == NULL || p->target->compiled_code != NULL)
|
|
backpatch_apply_and_free (b, p);
|
|
}
|
|
|
|
/* Only recurse on those parents interested in our new code location. */
|
|
for (i = b->num_parents - 1; i >= 0; i--)
|
|
{
|
|
for (p = b->parent[i]->backpatch; p != NULL; p = p->next)
|
|
if (p->target == b)
|
|
break;
|
|
|
|
/* Is this parent block still interested in where our code ended up? */
|
|
if (p != NULL)
|
|
compute_child_code_pointers (b->parent[i]);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef GENERATE_NATIVE_CODE
|
|
/* We use these to keep track of where we need to backpatch transitions
|
|
* from native to synthetic code.
|
|
*/
|
|
typedef struct
|
|
{
|
|
unsigned long stub_offset;
|
|
unsigned long synth_offset;
|
|
} ntos_cleanup_t;
|
|
#endif /* GENERATE_NATIVE_CODE */
|
|
|
|
|
|
/* The function generates the synthetic code for a given block. It does
|
|
* not fill in the pointers to the code in subsequent blocks (if they
|
|
* are even known at translation time).
|
|
*/
|
|
static void
|
|
generate_code (Block *b, TempBlockInfo *tbi, BOOL try_native_p)
|
|
{
|
|
const uint16 *m68k_code;
|
|
AmodeFetchInfo amf[2];
|
|
uint8 *code;
|
|
int i;
|
|
MapAndCC *map_and_cc;
|
|
unsigned long max_code_bytes, num_code_bytes;
|
|
uint32 instr_code[256]; /* Space for one instruction. */
|
|
#ifdef GENERATE_NATIVE_CODE
|
|
cache_info_t cache_info;
|
|
BOOL prev_native_p;
|
|
ntos_cleanup_t *ntos_cleanup;
|
|
int num_ntos_cleanup;
|
|
#ifdef SYNCHRONOUS_INTERRUPTS
|
|
int check_int_stub_offset = -1;
|
|
#endif
|
|
#endif /* GENERATE_NATIVE_CODE */
|
|
SAFE_DECL();
|
|
|
|
instr_code[(sizeof instr_code / sizeof instr_code[0]) - 1] = 0xFEEBFADE;
|
|
|
|
#ifdef GENERATE_NATIVE_CODE
|
|
/* Set up appropriate stuff for generating native code. */
|
|
cache_info = empty_cache_info;
|
|
|
|
ntos_cleanup = SAFE_alloca ((tbi->num_68k_instrs + 2)
|
|
* sizeof ntos_cleanup[0]);
|
|
num_ntos_cleanup = 0;
|
|
#endif
|
|
|
|
/* Allocate space for code. We'll skip over PTR_BYTES because that
|
|
* space is reserved.
|
|
*/
|
|
max_code_bytes = (tbi->num_68k_instrs * 32 + 512);
|
|
code = (uint8 *) xmalloc (PTR_BYTES + max_code_bytes) + PTR_BYTES;
|
|
num_code_bytes = 0;
|
|
|
|
/* Start with no backpatches. */
|
|
b->backpatch = NULL;
|
|
|
|
/* Compute exactly which cc bits and OpcodeMappingInfo *'s we should
|
|
* use for each m68k instruction in this block.
|
|
*/
|
|
map_and_cc = (MapAndCC *) SAFE_alloca ((tbi->num_68k_instrs + 1)
|
|
* sizeof map_and_cc[0]);
|
|
compute_maps_and_ccs (b, map_and_cc, tbi);
|
|
|
|
#ifdef GENERATE_NATIVE_CODE
|
|
/* Output the block preamble. We have separate entry points for
|
|
* incoming native code and incoming synthetic code. Why? Incoming
|
|
* native code will not have bothered to update the synthetic PC, and
|
|
* it shouldn't until we actually hit a synthetic opcode.
|
|
*
|
|
* The block preamble looks like this:
|
|
* [OPCODE_WORDS] synthetic opcode
|
|
* [<varies>] native code
|
|
*
|
|
* If the first m68k instruction in the block was translated to native
|
|
* code, the synthetic opcode will point to the native code (which
|
|
* follows it immediately). The native code will correspond to that
|
|
* m68k instruction.
|
|
*
|
|
* If the first m68k instruction was translated as synthetic code,
|
|
* the synthetic opcode will be a "skip n words" NOP which skips over
|
|
* the native code stub. The native code will properly set up the
|
|
* synthetic PC and then start executing the synthetic code, which
|
|
* will immediately follow the native code.
|
|
*/
|
|
|
|
/* Write out the default preamble. */
|
|
if (try_native_p)
|
|
{
|
|
*(const void **)&code[0] = direct_dispatch_table[0x1];
|
|
*(Block **)&code[sizeof (const void *)] = NULL;
|
|
}
|
|
else
|
|
{
|
|
*(const void **)&code[0] = direct_dispatch_table[0x3];
|
|
*(Block **)&code[sizeof (const void *)] = b;
|
|
}
|
|
num_code_bytes += NATIVE_START_BYTE_OFFSET;
|
|
|
|
/* No need to write out the native code preamble here; this will
|
|
* automatically happen if necessary since we'll pretend the previous
|
|
* instruction was native code
|
|
*/
|
|
#endif
|
|
|
|
/* Loop over all instructions, in forwards order, and compile them. */
|
|
m68k_code = SYN68K_TO_US (b->m68k_start_address);
|
|
#ifdef GENERATE_NATIVE_CODE
|
|
prev_native_p = TRUE; /* So n->s stub will be generated if necessary. */
|
|
#endif /* GENERATE_NATIVE_CODE */
|
|
for (i = 0; i < tbi->num_68k_instrs; i++)
|
|
{
|
|
int j, main_size;
|
|
int32 backpatch_request_index;
|
|
const OpcodeMappingInfo *map = map_and_cc[i].map;
|
|
#ifdef GENERATE_NATIVE_CODE
|
|
BOOL native_p = FALSE;
|
|
backpatch_t *old_backpatch, *native_backpatch;
|
|
|
|
old_backpatch = b->backpatch;
|
|
b->backpatch = NULL;
|
|
#endif /* GENERATE_NATIVE_CODE */
|
|
|
|
main_size = translate_instruction (m68k_code, (uint16 *)instr_code,
|
|
map, map_and_cc[i].live_cc,
|
|
(map_and_cc[i].live_cc
|
|
& map->cc_may_set),
|
|
amf, tbi, b,
|
|
&backpatch_request_index
|
|
#ifdef GENERATE_NATIVE_CODE
|
|
, &cache_info, &native_p,
|
|
try_native_p
|
|
#endif
|
|
);
|
|
|
|
/* Make sure we didn't overrun our temp array. */
|
|
assert (instr_code[sizeof instr_code / sizeof instr_code[0] - 1]
|
|
== 0xFEEBFADE);
|
|
|
|
#ifdef GENERATE_NATIVE_CODE
|
|
|
|
if (native_p)
|
|
{
|
|
native_backpatch = b->backpatch;
|
|
b->backpatch = old_backpatch;
|
|
|
|
if (num_code_bytes == NATIVE_START_BYTE_OFFSET)
|
|
{
|
|
/* Smash first synthetic opcode so that it now points
|
|
* to the native code.
|
|
*/
|
|
backpatch_add (b, 0, OPCODE_BYTES * 8, FALSE,
|
|
NATIVE_START_BYTE_OFFSET, b);
|
|
|
|
#ifdef SYNCHRONOUS_INTERRUPTS
|
|
memcpy (&code[num_code_bytes], check_interrupt_stub,
|
|
CHECK_INTERRUPT_STUB_BYTES);
|
|
check_int_stub_offset = num_code_bytes;
|
|
num_code_bytes += CHECK_INTERRUPT_STUB_BYTES;
|
|
#endif
|
|
}
|
|
else if (!prev_native_p)
|
|
{
|
|
/* Since the previous instruction wasn't native, and this one
|
|
* is, we need to throw in a synthetic opcode that will
|
|
* jump us to the native code.
|
|
*/
|
|
backpatch_add (b, num_code_bytes * 8,
|
|
OPCODE_BYTES * 8, FALSE,
|
|
OPCODE_BYTES + num_code_bytes, b);
|
|
num_code_bytes += OPCODE_BYTES;
|
|
}
|
|
}
|
|
else /* Can only generate amode fetches for non-native code. */
|
|
#endif /* GENERATE_NATIVE_CODE */
|
|
{
|
|
#ifdef GENERATE_NATIVE_CODE
|
|
assert (b->backpatch == NULL); /* They shouldn't have added any. */
|
|
native_backpatch = NULL;
|
|
b->backpatch = old_backpatch;
|
|
|
|
if (prev_native_p)
|
|
{
|
|
host_code_t *host_code;
|
|
BOOL first_p = (num_code_bytes == NATIVE_START_BYTE_OFFSET);
|
|
|
|
/* If the previous code was native, but this isn't,
|
|
* Generate a stub to pop us back into synthetic code.
|
|
*/
|
|
host_code = (host_code_t *)&code[num_code_bytes];
|
|
if (!first_p)
|
|
{
|
|
host_spill_cc_bits (&cache_info, &host_code,
|
|
cache_info.cached_cc);
|
|
host_spill_regs (&cache_info, &host_code, M68K_CC_NONE);
|
|
cache_info = empty_cache_info;
|
|
}
|
|
#ifdef SYNCHRONOUS_INTERRUPTS
|
|
else
|
|
{
|
|
/* Write out the check for interrupt stub since this is
|
|
* the initial native code entry point.
|
|
*/
|
|
memcpy (host_code, check_interrupt_stub,
|
|
CHECK_INTERRUPT_STUB_BYTES);
|
|
check_int_stub_offset = num_code_bytes;
|
|
host_code = (host_code_t *)((char *)host_code
|
|
+ CHECK_INTERRUPT_STUB_BYTES);
|
|
}
|
|
#endif /* SYNCHRONOUS_INTERRUPTS */
|
|
|
|
/* Write out the transition stub. */
|
|
memcpy (host_code, native_to_synth_stub,
|
|
NATIVE_TO_SYNTH_STUB_BYTES);
|
|
|
|
/* Compute the next synthetic opcode address. It has
|
|
* to follow the stub, but be aligned mod 4 bytes.
|
|
*/
|
|
num_code_bytes = (((char *)host_code + NATIVE_TO_SYNTH_STUB_BYTES
|
|
- (char *)code) + 3) & ~3;
|
|
|
|
/* Add a backpatch to clean up the stub. */
|
|
ntos_cleanup[num_ntos_cleanup].stub_offset
|
|
= (char *)host_code - (char *)code;
|
|
|
|
/* If we aren't allowed to use native code, and this is the
|
|
* first instruction, then jump back to the first synthetic
|
|
* opcode that counts how many times this block has been hit.
|
|
* Otherwise, gateway directly to the next instruction.
|
|
*/
|
|
if (!try_native_p && first_p)
|
|
ntos_cleanup[num_ntos_cleanup].synth_offset = 0;
|
|
else
|
|
ntos_cleanup[num_ntos_cleanup].synth_offset = num_code_bytes;
|
|
|
|
++num_ntos_cleanup;
|
|
}
|
|
#endif
|
|
/* Generate instructions to fetch pointer to amode, if necessary. */
|
|
for (j = 0; j < 2; j++)
|
|
if (amf[j].valid)
|
|
{
|
|
int afetch_size;
|
|
afetch_size = generate_amode_fetch (((uint16 *)
|
|
&code[num_code_bytes]),
|
|
amf[j].m68koperand,
|
|
amf[j].amode,
|
|
amf[j].reversed,
|
|
amf[j].size);
|
|
num_code_bytes += afetch_size * sizeof (uint16);
|
|
}
|
|
|
|
/* Set up backpatches for next block pointers, if necessary. */
|
|
if (backpatch_request_index != -1)
|
|
{
|
|
int bln;
|
|
for (bln = 0; bln < tbi->num_child_blocks; bln++)
|
|
{
|
|
backpatch_add (b,
|
|
(backpatch_request_index + num_code_bytes
|
|
+ bln * PTR_BYTES) * 8,
|
|
PTR_BYTES * 8, FALSE, 0, b->child[bln]);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef GENERATE_NATIVE_CODE
|
|
/* If the native code requested any backpatches, correct them now
|
|
* that we know exactly where the native code goes.
|
|
*/
|
|
if (native_backpatch != NULL)
|
|
{
|
|
backpatch_t *n, *next;
|
|
|
|
/* Correct each one and add it to the real backpatch list. */
|
|
for (n = native_backpatch; n != NULL; n = next)
|
|
{
|
|
next = n->next;
|
|
n->offset_location += 8 * num_code_bytes;
|
|
n->next = b->backpatch;
|
|
b->backpatch = n;
|
|
}
|
|
}
|
|
#endif /* GENERATE_NATIVE_CODE */
|
|
|
|
|
|
/* Now write out the real code, after any necessary amode fetches. */
|
|
memcpy (&code[num_code_bytes], instr_code, main_size);
|
|
num_code_bytes += main_size;
|
|
|
|
/* If we are dangerously close to the end of our allocated code space,
|
|
* xrealloc it to make it bigger.
|
|
*/
|
|
if (max_code_bytes - num_code_bytes < 512)
|
|
{
|
|
max_code_bytes *= 2;
|
|
code = (uint8 *) xrealloc (code - PTR_BYTES,
|
|
max_code_bytes + PTR_BYTES);
|
|
code += PTR_BYTES; /* Skip over reserved space again. */
|
|
}
|
|
|
|
/* Move on to the next instruction. */
|
|
m68k_code += tbi->next_instr_offset[i];
|
|
#ifdef GENERATE_NATIVE_CODE
|
|
prev_native_p = native_p;
|
|
#endif /* GENERATE_NATIVE_CODE */
|
|
}
|
|
|
|
/* Copy the code we just created over to the block. We allocate a little
|
|
* extra space because we prepend all compiled code with the big-endian
|
|
* 68k PC of the first instruction, in case we hit an interrupt when
|
|
* we are about to start the block. NOTE: to preserve alignment we
|
|
* allocate PTR_WORDS to hold the 68k PC even though we only need to
|
|
* use 2 (shorts).
|
|
*/
|
|
b->compiled_code = (((uint16 *) xrealloc (code - PTR_BYTES,
|
|
PTR_BYTES + num_code_bytes))
|
|
+ PTR_WORDS);
|
|
b->malloc_code_offset = PTR_WORDS;
|
|
|
|
WRITE_LONG (&b->compiled_code[-2], b->m68k_start_address);
|
|
|
|
#ifdef GENERATE_NATIVE_CODE
|
|
/* Now that the block's code is at a fixed address, patch up any
|
|
* native->synthetic stubs so they do the right thing.
|
|
*/
|
|
for (i = 0; i < num_ntos_cleanup; i++)
|
|
{
|
|
host_backpatch_native_to_synth_stub (b,
|
|
((host_code_t *)
|
|
((char *)b->compiled_code
|
|
+ ntos_cleanup[i].stub_offset)),
|
|
((uint32 *)
|
|
((char *)b->compiled_code
|
|
+ ntos_cleanup[i].synth_offset)));
|
|
}
|
|
|
|
#ifdef SYNCHRONOUS_INTERRUPTS
|
|
if (check_int_stub_offset >= 0)
|
|
{
|
|
host_backpatch_check_interrupt_stub (b, ((host_code_t *)
|
|
((char *)b->compiled_code
|
|
+ check_int_stub_offset)));
|
|
}
|
|
#endif
|
|
|
|
#endif /* GENERATE_NATIVE_CODE */
|
|
|
|
/* Checksum the code upon which this was based. */
|
|
#ifdef CHECKSUM_BLOCKS
|
|
b->checksum = compute_block_checksum (b);
|
|
#endif
|
|
|
|
ASSERT_SAFE (map_and_cc);
|
|
|
|
#ifdef GENERATE_NATIVE_CODE
|
|
ASSERT_SAFE (ntos_cleanup);
|
|
#endif
|
|
}
|
|
|
|
|
|
/* Helper function; writes out the magic bits for an opcode. */
|
|
static inline uint16 *
|
|
output_opcode (uint16 *code, uint32 opcode)
|
|
{
|
|
#ifdef USE_DIRECT_DISPATCH
|
|
*(const void **)code = direct_dispatch_table[opcode];
|
|
#else
|
|
*(const void **)code = (void *) opcode;
|
|
#endif
|
|
code += OPCODE_WORDS;
|
|
return code;
|
|
}
|
|
|
|
|
|
#define ROUND_UP(n) ((((n) + (PTR_WORDS - 1)) / PTR_WORDS) * PTR_WORDS)
|
|
|
|
#define IS_UNEXPANDABLE_AMODE(n) \
|
|
(((n) >> 3) == 6 || (n) == 0x3A || (n) == 0x3B)
|
|
|
|
/* Generates synthetic code for the m68k instruction pointed to by m68k_code,
|
|
* placing the synthetic code at the location pointed to by synthetic_code.
|
|
* On entry, ccbits_to_compute specifies a bitmask for the cc bits this
|
|
* instruction must compute (if it can), and ccbits_live specifies all
|
|
* CC bits known to be live at this point (which should be a superset of
|
|
* the bits to compute); the bits are specified CNVXZ, with Z being bit 0 of
|
|
* the mask and so on. Returns the number of _bytes_ of code generated.
|
|
*/
|
|
static int
|
|
translate_instruction (const uint16 *m68k_code, uint16 *synthetic_code,
|
|
const OpcodeMappingInfo *map,
|
|
int ccbits_live,
|
|
int ccbits_to_compute,
|
|
AmodeFetchInfo amf[2],
|
|
const TempBlockInfo *tbi,
|
|
Block *block,
|
|
int32 *backpatch_request_index
|
|
#ifdef GENERATE_NATIVE_CODE
|
|
, cache_info_t *cache_info, BOOL *prev_native_p,
|
|
BOOL try_native_p
|
|
#endif
|
|
)
|
|
{
|
|
uint16 *scode = synthetic_code;
|
|
const BitfieldInfo *bf;
|
|
const uint16 *opp;
|
|
uint16 m68kop = READUW (US_TO_SYN68K (m68k_code));
|
|
uint16 synop;
|
|
int amode = m68kop & 63;
|
|
int revmode = ((m68kop >> 9) & 7) | ((m68kop >> 3) & 0x38);
|
|
int32 operand[MAX_BITFIELDS];
|
|
int i;
|
|
|
|
*backpatch_request_index = -1; /* default */
|
|
|
|
/* Grab all of the operands and stick them in our operand array in
|
|
* native endian byte order. If we generate native code, this is
|
|
* what the native code routines expect. Otherwise, if we go to
|
|
* synthetic code, we'll later end up byte swapping and so on as
|
|
* necessary.
|
|
*/
|
|
for (bf = map->bitfield, i = 0;
|
|
i < MAX_BITFIELDS && !IS_TERMINATING_BITFIELD (bf);
|
|
i++, bf++)
|
|
{
|
|
int index = bf->index, length = bf->length + 1;
|
|
syn68k_addr_t p = (syn68k_addr_t) US_TO_SYN68K (&m68k_code[index >> 4]);
|
|
uint32 val;
|
|
|
|
/* A length of 16 or 32 bits implies that the operand is aligned on a
|
|
* word boundary.
|
|
*/
|
|
if (length == 16)
|
|
{
|
|
if (bf->sign_extend)
|
|
val = READSW (p); /* Sign extend. */
|
|
else
|
|
val = READUW (p); /* Zero extend. */
|
|
}
|
|
else if (length == 32)
|
|
{
|
|
val = READUL (p);
|
|
}
|
|
else /* It's not a nicely aligned word or long. */
|
|
{
|
|
val = READUW (p) >> (16 - ((index & 0xF) + length));
|
|
|
|
if (bf->rev_amode)
|
|
{
|
|
val = ((val >> 3) & 0x7) | ((val & 0x7) << 3);
|
|
}
|
|
else
|
|
{
|
|
if (bf->sign_extend && (val & (1 << (length - 1))))
|
|
val |= ~((1 << length) - 1); /* Ones extend. */
|
|
else
|
|
val &= ((1 << length) - 1); /* Zero extend. */
|
|
}
|
|
}
|
|
|
|
operand[i] = val;
|
|
}
|
|
|
|
#ifdef GENERATE_NATIVE_CODE
|
|
{
|
|
host_code_t *hc_scode;
|
|
|
|
hc_scode = (host_code_t *) scode;
|
|
if (map->guest_code_descriptor != NULL
|
|
&& try_native_p
|
|
&& generate_native_code (map->guest_code_descriptor, cache_info,
|
|
operand, &hc_scode,
|
|
map->cc_needed, ccbits_live, ccbits_to_compute,
|
|
map->ends_block, block, m68k_code))
|
|
{
|
|
*prev_native_p = TRUE;
|
|
return (char *)hc_scode - (char *)synthetic_code;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* If we have an unexpanded amode, insert an opcode to compute a pointer
|
|
* to the addressing mode.
|
|
*/
|
|
opp = m68k_code + map->instruction_words; /* Point to 1st amode operand */
|
|
|
|
if (map->amode_size != 0
|
|
&& (!map->amode_expanded || IS_UNEXPANDABLE_AMODE (amode)))
|
|
{
|
|
amf[0].valid = TRUE;
|
|
amf[0].reversed = FALSE;
|
|
amf[0].m68koperand = opp;
|
|
amf[0].amode = amode;
|
|
amf[0].size = 1 << (map->amode_size - 1);
|
|
opp += amode_size (amode, opp, map->amode_size); /* Skip to next oper */
|
|
}
|
|
else
|
|
amf[0].valid = FALSE;
|
|
|
|
/* If we have an unexpanded reversed amode, insert an opcode to compute a
|
|
* pointer to the addressing mode.
|
|
*/
|
|
if (map->reversed_amode_size != 0
|
|
&& (!map->reversed_amode_expanded || IS_UNEXPANDABLE_AMODE (revmode)))
|
|
{
|
|
amf[1].valid = TRUE;
|
|
amf[1].reversed = TRUE;
|
|
amf[1].m68koperand = opp;
|
|
amf[1].amode = revmode;
|
|
amf[1].size = 1 << (map->reversed_amode_size - 1);
|
|
}
|
|
else
|
|
amf[1].valid = FALSE;
|
|
|
|
/* Compute synthetic opcode and place it in synthetic instruction stream. */
|
|
synop = (((m68kop & map->opcode_and_bits) >> map->opcode_shift_count)
|
|
+ map->opcode_add_bits);
|
|
scode = output_opcode (scode, synop);
|
|
|
|
#ifdef DEBUG
|
|
if (synop == 0
|
|
#ifdef USE_DIRECT_DISPATCH
|
|
|| direct_dispatch_table[synop] == NULL
|
|
#endif
|
|
)
|
|
fprintf (stderr, "Unknown 68k opcode 0x%04X\n", (unsigned) m68kop);
|
|
#endif
|
|
|
|
/* Reserve space for pointers to next blocks and set up backpatches. */
|
|
if (map->ends_block && !map->next_block_dynamic)
|
|
{
|
|
*backpatch_request_index = (char *)scode - (char *)synthetic_code;
|
|
if (tbi->num_child_blocks > 0)
|
|
{
|
|
((uint32 *)scode)[0] = 0x56784321;
|
|
if (tbi->num_child_blocks > 1)
|
|
((uint32 *)scode)[1] = 0x57684321;
|
|
}
|
|
scode += tbi->num_child_blocks * PTR_WORDS;
|
|
}
|
|
|
|
/* If we are processing an instruction that requires the big-endian address
|
|
* of the next instruction in the instr stream, do it. Also insert computed
|
|
* subroutine target for bsr.
|
|
*/
|
|
if ((m68kop >> 6) == 0x13A /* jsr? */
|
|
|| (m68kop >> 8) == 0x61 /* bsr? */
|
|
)
|
|
{
|
|
uint32 addr;
|
|
|
|
/* Write out the big endian return address. */
|
|
addr = SWAPUL_IFLE (US_TO_SYN68K (m68k_code
|
|
+ instruction_size (m68k_code, map)));
|
|
WRITEUL_UNSWAPPED (scode, addr);
|
|
scode += 2;
|
|
|
|
/* If it's a bsr, compute the target address. */
|
|
if ((m68kop >> 8) == 0x61) /* bsr? */
|
|
{
|
|
/* Write out the target address. */
|
|
WRITEUL_UNSWAPPED (scode, tbi->child[0]);
|
|
scode += 2;
|
|
}
|
|
}
|
|
|
|
/* If we need to insert the address of the next instruction in native
|
|
* byte order, do it. FIXME - when looking for chk or div instructions,
|
|
* we are masking out the addressing mode field. It's possible that
|
|
* some of the impossible amode combinations actually correspond to
|
|
* entirely different instructions; this would cause the wrong results!
|
|
*/
|
|
else if ((m68kop & 0xF0C0) == 0x80C0 /* divs/divu ditto */
|
|
|| (m68kop & 0xFFC0) == 0x4C40 /* divsl/divul ditto */
|
|
|| (map->next_block_dynamic
|
|
&& (m68kop == 0x4E76 /* trapv? */
|
|
|| (m68kop >> 4) == 0x4E4 /* trap #n? */
|
|
|| ((m68kop & 0xF0FF) >= 0x50FA
|
|
&& (m68kop & 0xF0FF) <= 0x50FC) /* trapcc? */
|
|
|| (m68kop & 0xF140) == 0x4100 /* chk? Not all amodes ok*/
|
|
|| (m68kop & 0xF9C0) == 0x00C0 /* chk2? ditto */
|
|
)))
|
|
{
|
|
uint32 addr = US_TO_SYN68K (m68k_code + instruction_size (m68k_code,
|
|
map));
|
|
WRITEUL_UNSWAPPED (scode, addr);
|
|
scode += 2;
|
|
}
|
|
|
|
/* If we are processing something that might trap where we need a ptr to
|
|
* the trapping instruction, insert the PC into the instruction stream.
|
|
* See impossible amode warning in previous comment.
|
|
*/
|
|
if ((m68kop & 0xF0C0) == 0x80C0 /* divs/divu ditto */
|
|
|| (m68kop & 0xFFC0) == 0x4C40 /* divsl/divul ditto */
|
|
|| (m68kop & 0xFF28) == 0xF428
|
|
|| (map->next_block_dynamic
|
|
&& ((m68kop >> 12) == 0xA /* a-line trap? */
|
|
|| m68kop == 0x4E73 /* rte? */
|
|
|| m68kop == 0x4AFC /* explicit ILLEGAL? */
|
|
|| synop == 0 /* REAL illegal instruction? */
|
|
|| (m68kop >> 3) == (0x4848 >> 3) /* bkpt? */
|
|
|| (m68kop >> 12) == 0xF /* f-line trap? */
|
|
|| (m68kop & 0xF140) == 0x4100 /* chk? Not all amodes ok! */
|
|
|| (m68kop & 0xF9C0) == 0x00C0 /* chk2? ditto */
|
|
)))
|
|
{
|
|
WRITEUL_UNSWAPPED (scode, US_TO_SYN68K (m68k_code));
|
|
scode += 2;
|
|
}
|
|
|
|
|
|
/* Extract operands from m68k stream, process them, and place
|
|
* them in the synthetic stream. 16- and 32-bit bitfields must be
|
|
* word-aligned in the 68k stream. Only 32-bit bitfields are allowed to
|
|
* span multiple words.
|
|
*/
|
|
for (bf = map->bitfield, i = 0;
|
|
i < MAX_BITFIELDS && !IS_TERMINATING_BITFIELD (bf);
|
|
i++, bf++)
|
|
{
|
|
int words;
|
|
uint32 val;
|
|
|
|
/* Fetch the value (which we already extracted above). */
|
|
val = operand[i];
|
|
|
|
/* Put the value back to big endian ordering if we need to. */
|
|
words = bf->words + 1;
|
|
#ifdef LITTLEENDIAN
|
|
if (!bf->make_native_endian)
|
|
{
|
|
if (words == 1)
|
|
val = SWAPUW (val);
|
|
else
|
|
val = SWAPUL (val);
|
|
}
|
|
#endif
|
|
|
|
/* Write the operand out to the instruction stream. */
|
|
if (words == 1)
|
|
{
|
|
scode[0] = val; /* Ideally everything would be a long. */
|
|
scode[1] = 0; /* Avoid uninitialized memory complaints. */
|
|
}
|
|
else
|
|
WRITEUL_UNSWAPPED (scode, val);
|
|
scode += 2;
|
|
}
|
|
|
|
/* Round the size up to occupy an integral number of PTR_WORDS. */
|
|
return ROUND_UP (scode - synthetic_code) * sizeof (uint16);
|
|
}
|
|
|
|
|
|
/* Generates synthetic code to compute the value for an addressing mode
|
|
* and store it in cpu_state.amode_p or cpu_state.reversed_amode_p;
|
|
* Returns the number of 16-bit words generated (historical; should be
|
|
* bytes).
|
|
*/
|
|
static int
|
|
generate_amode_fetch (uint16 *code, const uint16 *m68koperand, int amode,
|
|
BOOL reversed, int size)
|
|
{
|
|
int mode = amode >> 3, reg = amode & 7;
|
|
uint16 *scode = code;
|
|
|
|
switch (mode) {
|
|
case 2:
|
|
case 3:
|
|
scode = output_opcode (scode, 0x4 + (reversed * 8) + reg);
|
|
return ROUND_UP (scode - code);
|
|
case 4:
|
|
{
|
|
static const unsigned char amode_4_base[] =
|
|
{ 0x00, 0x14, 0x24, 0x00, 0x34 };
|
|
scode = output_opcode (scode, amode_4_base[size] + (reversed * 8) + reg);
|
|
return ROUND_UP (scode - code);
|
|
}
|
|
case 5:
|
|
scode = output_opcode (scode, 0x44 + (reversed * 8) + reg);
|
|
*(int32 *)scode = READSW (US_TO_SYN68K (m68koperand));
|
|
scode += 2;
|
|
return ROUND_UP (scode - code);
|
|
case 6:
|
|
{
|
|
uint16 extword = READUW (US_TO_SYN68K (m68koperand));
|
|
if ((extword & 0x100) == 0)
|
|
{
|
|
scode = output_opcode (scode, (0x58 - 0x12 - 0x24
|
|
+ (0x12 << (extword >> 15))
|
|
+ (0x24 << ((extword >> 11) & 1))
|
|
+ (reversed * 9) + reg));
|
|
|
|
WRITE_PTR (scode, SYN68K_TO_US ((ptr_sized_uint)
|
|
(((int8 *) m68koperand)[1])));
|
|
scode += PTR_WORDS;
|
|
*(uint32 *)(scode ) = (extword >> 12) & 7;
|
|
*(uint32 *)(scode + 2) = (extword >> 9) & 3;
|
|
return ROUND_UP (scode + 4 - code);
|
|
}
|
|
else if ((extword & 0xF) == 0x0)
|
|
{
|
|
int32 disp;
|
|
uint16 synop;
|
|
|
|
/* Base suppress? Pretend they are using "a8" (== 0 here). */
|
|
if (extword & 0x80)
|
|
reg = 8;
|
|
|
|
/* Index suppress? */
|
|
if (extword & 0x40)
|
|
synop = 0xA0 + (reversed * 9) + reg;
|
|
else
|
|
synop = (0x58 + - 0x12 - 0x24 + (0x12 << (extword >> 15))
|
|
+ (0x24 << ((extword >> 11) & 1))
|
|
+ (reversed * 9) + reg);
|
|
|
|
scode = output_opcode (scode, synop);
|
|
|
|
switch ((extword >> 4) & 0x3) {
|
|
case 2:
|
|
disp = READSW (US_TO_SYN68K (m68koperand + 1));
|
|
break;
|
|
case 3:
|
|
disp = READSL (US_TO_SYN68K (m68koperand + 1));
|
|
break;
|
|
default:
|
|
disp = 0;
|
|
break;
|
|
}
|
|
WRITE_PTR (scode, SYN68K_TO_US (disp));
|
|
scode += PTR_WORDS;
|
|
|
|
if (!(extword & 0x40))
|
|
{
|
|
*(uint32 *)(scode ) = (extword >> 12) & 7;
|
|
*(uint32 *)(scode + 2) = (extword >> 9) & 3;
|
|
return ROUND_UP (scode + 4 - code);
|
|
}
|
|
return ROUND_UP (scode - code);
|
|
}
|
|
else /* Memory indirect pre-indexed or memory indirect post-indexed. */
|
|
{
|
|
int32 base_displacement, outer_displacement;
|
|
|
|
/* Memory indirect pre- or post-indexed. */
|
|
scode = output_opcode (scode, 0xB2);
|
|
|
|
/* Get base displacement size. */
|
|
switch ((extword >> 4) & 0x3) {
|
|
case 2:
|
|
base_displacement = READSW (US_TO_SYN68K (m68koperand + 1));
|
|
m68koperand += 1;
|
|
break;
|
|
case 3:
|
|
base_displacement = READSL (US_TO_SYN68K (m68koperand + 1));
|
|
m68koperand += 2;
|
|
break;
|
|
default:
|
|
base_displacement = 0;
|
|
break;
|
|
}
|
|
|
|
/* Get outer displacement size. */
|
|
switch (extword & 0x3) {
|
|
case 2:
|
|
outer_displacement = READSW (US_TO_SYN68K (m68koperand + 1));
|
|
break;
|
|
case 3:
|
|
outer_displacement = READSL (US_TO_SYN68K (m68koperand + 1));
|
|
break;
|
|
default:
|
|
outer_displacement = 0;
|
|
break;
|
|
}
|
|
|
|
WRITEUL_UNSWAPPED (scode, base_displacement);
|
|
WRITEUL_UNSWAPPED (scode + 2, outer_displacement);
|
|
|
|
/* Toss two magical flags into the flags word. If the low bit
|
|
* is set, we are computing reversed_amode_p instead of amode_p.
|
|
* If the next lowest bit is set, we are in memory indirect
|
|
* pre-indexed mode instead of memory indirect post-indexed mode.
|
|
*/
|
|
extword &= ~3;
|
|
extword |= reversed;
|
|
if (!(extword & 0x4)) /* Memory indirect pre-indexed? */
|
|
extword |= 2;
|
|
*(uint32 *)(scode + 4) = extword;
|
|
*(uint32 *)(scode + 6) = reg;
|
|
return ROUND_UP (scode + 8 - code);
|
|
}
|
|
}
|
|
break;
|
|
case 7:
|
|
switch (reg) {
|
|
case 0:
|
|
scode = output_opcode (scode, 0x54 + reversed);
|
|
*(uint32 *)scode = READUW (US_TO_SYN68K (m68koperand));
|
|
return ROUND_UP (scode + 2 - code);
|
|
case 1:
|
|
{
|
|
char *val = (char *) SYN68K_TO_US (CLEAN
|
|
(READUL
|
|
(US_TO_SYN68K (m68koperand))));
|
|
|
|
/* Specify absolute long address. Give a real pointer in our space. */
|
|
scode = output_opcode (scode, 0x56 + reversed);
|
|
WRITE_PTR (scode, val);
|
|
return ROUND_UP (scode + PTR_WORDS - code);
|
|
}
|
|
case 2:
|
|
{
|
|
char *val = (READSW (US_TO_SYN68K (m68koperand))
|
|
+ (char *) m68koperand);
|
|
|
|
/* Specify absolute long address. Give a real ptr in our space. */
|
|
scode = output_opcode (scode, 0x56 + reversed);
|
|
WRITE_PTR (scode, val);
|
|
return ROUND_UP (scode + PTR_WORDS - code);
|
|
}
|
|
case 3: /* 111/011 (d8,PC,Xn), (bd,PC,Xn), ([bd,PC,Xn],od) */
|
|
/* ([bd,PC],Xn,od) */
|
|
{
|
|
uint16 extword = READUW (US_TO_SYN68K (m68koperand));
|
|
reg = (extword >> 12) & 0x7;
|
|
if ((extword & 0x100) == 0)
|
|
{
|
|
scode = output_opcode (scode, (0x58 - 0x12 - 0x24
|
|
+ (0x12 << (extword >> 15))
|
|
+ (0x24 << ((extword >> 11) & 1))
|
|
+ (reversed * 9) + 8));
|
|
|
|
WRITE_PTR (scode,
|
|
(char *) ((long) m68koperand
|
|
+ (((int8 *) m68koperand)[1])));
|
|
scode += PTR_WORDS;
|
|
*(uint32 *)(scode ) = reg;
|
|
*(uint32 *)(scode + 2) = (extword >> 9) & 3;
|
|
return ROUND_UP (scode + 4 - code);
|
|
}
|
|
else if ((extword & 0xF) == 0x0)
|
|
{
|
|
int32 disp;
|
|
uint16 synop;
|
|
|
|
/* Index suppress? */
|
|
if (extword & 0x40)
|
|
synop = 0xA0 + (reversed * 9) + 8;
|
|
else
|
|
synop = (0x58 - 0x12 - 0x24 + (0x12 << (extword >> 15))
|
|
+ (0x24 << ((extword >> 11) & 1))
|
|
+ (reversed * 9) + 8);
|
|
|
|
scode = output_opcode (scode, synop);
|
|
|
|
switch ((extword >> 4) & 0x3) {
|
|
case 2:
|
|
disp = READSW (US_TO_SYN68K (m68koperand + 1));
|
|
break;
|
|
case 3:
|
|
disp = READSL (US_TO_SYN68K (m68koperand + 1));
|
|
break;
|
|
default:
|
|
disp = 0;
|
|
break;
|
|
}
|
|
disp += US_TO_SYN68K (m68koperand);/* Add in PC to displacement. */
|
|
WRITE_PTR (scode, SYN68K_TO_US (disp));
|
|
scode += PTR_WORDS;
|
|
|
|
if (!(extword & 0x40))
|
|
{
|
|
*(uint32 *)(scode ) = (extword >> 12) & 7;
|
|
*(uint32 *)(scode + 2) = (extword >> 9) & 3;
|
|
return ROUND_UP (scode + 4 - code);
|
|
}
|
|
return ROUND_UP (scode - code);
|
|
}
|
|
else /* PC relative mem indir pre-indexed or mem indir post-indexed. */
|
|
{
|
|
int32 base_displacement, outer_displacement;
|
|
|
|
/* Memory indirect pre- or post-indexed. */
|
|
scode = output_opcode (scode, 0xB2);
|
|
|
|
if (extword & 0x80)
|
|
base_displacement = 0; /* Suppress PC base */
|
|
else
|
|
base_displacement = US_TO_SYN68K (m68koperand); /* PC is base. */
|
|
|
|
/* Get base displacement size. */
|
|
switch ((extword >> 4) & 0x3) {
|
|
case 2:
|
|
base_displacement += READSW (US_TO_SYN68K (m68koperand + 1));
|
|
m68koperand += 1;
|
|
break;
|
|
case 3:
|
|
base_displacement += READSL (US_TO_SYN68K (m68koperand + 1));
|
|
m68koperand += 2;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Get outer displacement size. */
|
|
switch (extword & 0x3) {
|
|
case 2:
|
|
outer_displacement = READSW (US_TO_SYN68K (m68koperand + 1));
|
|
break;
|
|
case 3:
|
|
outer_displacement = READSL (US_TO_SYN68K (m68koperand + 1));
|
|
break;
|
|
default:
|
|
outer_displacement = 0;
|
|
break;
|
|
}
|
|
|
|
WRITEUL_UNSWAPPED (scode, base_displacement);
|
|
WRITEUL_UNSWAPPED (scode + 2, outer_displacement);
|
|
scode += 4;
|
|
|
|
/* Toss two magical flags into the flags word. If the low bit
|
|
* is set, we are computing reversed_amode_p instead of amode_p.
|
|
* If the next lowest bit is set, we are in memory indirect
|
|
* pre-indexed mode instead of memory indirect post-indexed mode.
|
|
*/
|
|
extword &= ~3;
|
|
extword |= reversed;
|
|
if (!(extword & 0x4)) /* Memory indirect pre-indexed? */
|
|
extword |= 2;
|
|
|
|
/* Pretend like base suppress is set. */
|
|
extword |= 0x80;
|
|
|
|
*(uint32 *)(scode ) = extword;
|
|
*(uint32 *)(scode + 2) = reg;
|
|
return ROUND_UP (scode + 4 - code);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void
|
|
compute_maps_and_ccs (Block *b, MapAndCC *m, const TempBlockInfo *tbi)
|
|
{
|
|
const uint16 *m68k_code;
|
|
int cc_needed;
|
|
int i;
|
|
|
|
/* Determine what cc bits must be valid after the last instruction. */
|
|
if (b->num_children == 0)
|
|
cc_needed = ALL_CCS;
|
|
else
|
|
{
|
|
cc_needed = b->child[0]->cc_needed;
|
|
if (b->num_children > 1)
|
|
cc_needed |= b->child[1]->cc_needed;
|
|
}
|
|
|
|
/* Loop over all instructions, in backwards order, and compute
|
|
* their live cc bits and optimal OcpodeMappingInfo *'s.
|
|
*/
|
|
m68k_code = (SYN68K_TO_US (b->m68k_start_address)
|
|
+ (b->m68k_code_length / sizeof (uint16)));
|
|
|
|
/* Save the cc bits live at the end at the end of the array. */
|
|
m[tbi->num_68k_instrs].map = NULL;
|
|
m[tbi->num_68k_instrs].live_cc = cc_needed;
|
|
|
|
for (i = tbi->num_68k_instrs - 1; i >= 0; i--)
|
|
{
|
|
const OpcodeMappingInfo *map;
|
|
int parity, best_cc;
|
|
|
|
m68k_code -= tbi->next_instr_offset[i];
|
|
|
|
/* Grab first struct in opcode mapping sequence. */
|
|
map = &opcode_map_info[opcode_map_index[READUW (US_TO_SYN68K (m68k_code))]];
|
|
|
|
/* Grab the parity of this sequence and max cc bits computable. */
|
|
parity = map->sequence_parity;
|
|
best_cc = cc_needed & map->cc_may_set;
|
|
|
|
/* Locate the mapping that computes as few cc bits as we legally can. */
|
|
while (map[1].sequence_parity == parity
|
|
&& (cc_needed & map[1].cc_may_set) == best_cc)
|
|
map++;
|
|
|
|
/* Record the best map and the cc bits we need here. */
|
|
m[i].map = map;
|
|
m[i].live_cc = cc_needed;
|
|
|
|
cc_needed = (cc_needed & map->cc_may_not_set) | map->cc_needed;
|
|
}
|
|
}
|
|
|
|
|
|
/* We use artificial blocks when we need to do something for which there
|
|
* is no m68k opcode, like exit the emulator or call a callback routine.
|
|
*/
|
|
Block *
|
|
make_artificial_block (Block *parent, syn68k_addr_t m68k_address,
|
|
int extra_words, uint16 **extra_start)
|
|
{
|
|
Block *b;
|
|
uint16 *code;
|
|
|
|
b = block_new ();
|
|
b->m68k_start_address = m68k_address;
|
|
b->m68k_code_length = 1;
|
|
b->cc_may_not_set = ALL_CCS;
|
|
b->cc_needed = ALL_CCS;
|
|
b->immortal = TRUE;
|
|
if (parent != NULL)
|
|
block_add_parent (b, parent);
|
|
|
|
b->malloc_code_offset = PTR_WORDS;
|
|
code = (((uint16 *) xcalloc (PTR_WORDS
|
|
#ifdef GENERATE_NATIVE_CODE
|
|
+ NATIVE_START_BYTE_OFFSET / sizeof (uint16)
|
|
+ NATIVE_PREAMBLE_WORDS
|
|
#endif
|
|
+ extra_words,
|
|
sizeof (uint16)))
|
|
+ PTR_WORDS); /* Skip over prepended m68k address. */
|
|
WRITE_LONG (&code[-2], m68k_address);
|
|
b->compiled_code = code;
|
|
b->checksum = compute_block_checksum (b);
|
|
|
|
#ifdef GENERATE_NATIVE_CODE
|
|
*(const void **)code = direct_dispatch_table[0x1];
|
|
*(Block **)(code + sizeof (const void *)) = NULL;
|
|
*extra_start = (uint16 *) ((char *)code
|
|
+ NATIVE_START_BYTE_OFFSET
|
|
+ NATIVE_PREAMBLE_WORDS * sizeof (uint16));
|
|
|
|
/* Write out the transition stub. It will just jump back to the
|
|
* synthetic NOP, which will skip over this. Fun, huh?
|
|
*/
|
|
#ifdef SYNCHRONOUS_INTERRUPTS
|
|
memcpy ((char *)code + NATIVE_START_BYTE_OFFSET, check_interrupt_stub,
|
|
CHECK_INTERRUPT_STUB_BYTES);
|
|
#endif
|
|
memcpy ((char *)code + NATIVE_START_BYTE_OFFSET + CHECK_INTERRUPT_STUB_BYTES,
|
|
native_to_synth_stub, NATIVE_TO_SYNTH_STUB_BYTES);
|
|
host_backpatch_check_interrupt_stub (b, ((host_code_t *)
|
|
((char *)code
|
|
+ NATIVE_START_BYTE_OFFSET)));
|
|
host_backpatch_native_to_synth_stub (b,
|
|
((host_code_t *)
|
|
((char *)code
|
|
+ NATIVE_START_BYTE_OFFSET
|
|
+ CHECK_INTERRUPT_STUB_BYTES)),
|
|
(uint32 *)code);
|
|
#else /* !GENERATE_NATIVE_CODE */
|
|
*extra_start = code;
|
|
#endif /* !GENERATE_NATIVE_CODE */
|
|
|
|
return b;
|
|
}
|