mirror of
https://github.com/ctm/syn68k.git
synced 2024-12-01 00:51:19 +00:00
284 lines
7.8 KiB
C
284 lines
7.8 KiB
C
|
#include "syn68k_private.h"
|
||
|
#include "block.h"
|
||
|
#include "mapping.h"
|
||
|
#include "rangetree.h"
|
||
|
#include "blockinfo.h"
|
||
|
#include "alloc.h"
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <assert.h>
|
||
|
|
||
|
static void determine_next_block_addresses (const uint16 *code,
|
||
|
TempBlockInfo *temp,
|
||
|
const OpcodeMappingInfo *map);
|
||
|
|
||
|
|
||
|
/* This function takes a block and a pointer to some m68k code and computes
|
||
|
* the length of the block and various cc bit information about the block.
|
||
|
* The info is stored in the block struct.
|
||
|
*/
|
||
|
void
|
||
|
compute_block_info (Block *b, const uint16 *code, TempBlockInfo *temp)
|
||
|
{
|
||
|
const uint16 *start_code = code, *old_code;
|
||
|
int clobbered = 0, may_set = 0, may_not_set = ALL_CCS, needed = 0;
|
||
|
int next_array_size;
|
||
|
const OpcodeMappingInfo *map = NULL;
|
||
|
|
||
|
/* Initialize the next offset array. This lets us step through this
|
||
|
* code forwards when we actually get around to compiling it.
|
||
|
*/
|
||
|
temp->num_68k_instrs = 0;
|
||
|
next_array_size = 16;
|
||
|
temp->next_instr_offset = (int8 *) xmalloc (next_array_size * sizeof (int8));
|
||
|
|
||
|
/* Loop over all instructions in the block and determine information
|
||
|
* about how this block deals with CC bits.
|
||
|
*/
|
||
|
old_code = code;
|
||
|
do
|
||
|
{
|
||
|
int insn_size;
|
||
|
unsigned m68k_op;
|
||
|
|
||
|
m68k_op = READUW (US_TO_SYN68K (code));
|
||
|
map = &opcode_map_info[opcode_map_index[m68k_op]];
|
||
|
|
||
|
#if 0
|
||
|
if (opcode_map_index[m68k_op] == 0)
|
||
|
{
|
||
|
fprintf (stderr, "m68kop 0x%04X unimplemented!\n", m68k_op);
|
||
|
abort ();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
insn_size = instruction_size (code, map);
|
||
|
if (insn_size <= 0)
|
||
|
{
|
||
|
map = &opcode_map_info[opcode_map_index[0x4AFC]]; /* illegal */
|
||
|
insn_size = 1;
|
||
|
}
|
||
|
|
||
|
/* Update cc bit info for this block. */
|
||
|
clobbered |= (map->cc_may_set
|
||
|
& ~(map->cc_may_not_set | map->cc_needed | needed));
|
||
|
needed |= map->cc_needed & may_not_set;
|
||
|
may_set |= map->cc_may_set;
|
||
|
may_not_set &= map->cc_may_not_set;
|
||
|
|
||
|
/* Grow next instruction offset array iff necessary. We'll
|
||
|
* leave one extra element for after this loop ends.
|
||
|
*/
|
||
|
if (temp->num_68k_instrs >= next_array_size - 1)
|
||
|
{
|
||
|
next_array_size *= 2;
|
||
|
temp->next_instr_offset = (int8 *) xrealloc (temp->next_instr_offset,
|
||
|
next_array_size
|
||
|
* sizeof (int8));
|
||
|
}
|
||
|
|
||
|
/* Remember offset to next instruction. */
|
||
|
temp->next_instr_offset[temp->num_68k_instrs++] = insn_size;
|
||
|
|
||
|
/* Move on to the next instruction. */
|
||
|
old_code = code;
|
||
|
code += insn_size;
|
||
|
}
|
||
|
while (!map->ends_block);
|
||
|
|
||
|
/* Terminate the array with a 0 offset. */
|
||
|
temp->next_instr_offset[temp->num_68k_instrs] = 0;
|
||
|
|
||
|
/* Figure out where this block goes (if possible). */
|
||
|
determine_next_block_addresses (old_code, temp, map);
|
||
|
|
||
|
/* Record the block information we've computed. */
|
||
|
b->cc_clobbered = clobbered;
|
||
|
b->cc_may_not_set = may_not_set;
|
||
|
b->cc_needed = needed;
|
||
|
b->m68k_start_address = US_TO_SYN68K (start_code);
|
||
|
b->m68k_code_length = (code - start_code) * sizeof (uint16);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void
|
||
|
determine_next_block_addresses (const uint16 *code, TempBlockInfo *temp,
|
||
|
const OpcodeMappingInfo *map)
|
||
|
{
|
||
|
uint16 m68kop = READUW (US_TO_SYN68K (code));
|
||
|
BOOL is_bsr = ((m68kop >> 8) == 0x61);
|
||
|
BOOL is_fixed_jsr = ((m68kop & 0xFFFE) == 0x4EB8 /* jsr abs{w,l}? */
|
||
|
|| m68kop == 0x4EBA); /* jsr pc@d16? */
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
temp->child[0] = temp->child[1] = US_TO_SYN68K (NULL);
|
||
|
#endif
|
||
|
|
||
|
/* First see if we can even tell where the child block is. */
|
||
|
if (map->next_block_dynamic && !is_bsr && !is_fixed_jsr)
|
||
|
{
|
||
|
temp->num_child_blocks = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Is it a Bcc? */
|
||
|
if ((m68kop >> 12) == 6)
|
||
|
{
|
||
|
uint32 t1, t2;
|
||
|
|
||
|
t1 = (uint32) (code + 1);
|
||
|
/* Compute branch target. */
|
||
|
if ((m68kop & 0xFF) == 0)
|
||
|
{
|
||
|
t1 += READSW (US_TO_SYN68K (code + 1));
|
||
|
t2 = (uint32) (code + 2);
|
||
|
}
|
||
|
else if ((m68kop & 0xFF) == 0xFF)
|
||
|
{
|
||
|
t1 += READSL (US_TO_SYN68K (code + 1));
|
||
|
t2 = (uint32) (code + 3);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
t1 += ((int8 *)code)[1];
|
||
|
t2 = (uint32) (code + 1);
|
||
|
}
|
||
|
|
||
|
/* Is it a bsr or bra? If so, only one destination address. In the
|
||
|
* case of bsr, we compute the target address but officially pretend
|
||
|
* we don't know where it's going.
|
||
|
*/
|
||
|
if ((m68kop & 0xFE00) == 0x6000)
|
||
|
{
|
||
|
temp->child[0] = t1;
|
||
|
temp->num_child_blocks = !is_bsr;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
temp->child[0] = t2;
|
||
|
temp->child[1] = t1;
|
||
|
temp->num_child_blocks = 2;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Is it a dbcc? */
|
||
|
if ((m68kop >> 12) == 5)
|
||
|
{
|
||
|
temp->child[0] = (uint32) (code + 2);
|
||
|
|
||
|
if ((m68kop >> 8) == 0x50) /* dbt? */
|
||
|
{
|
||
|
temp->num_child_blocks = 1;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
temp->child[1] = ((int32 ) (code + 1)
|
||
|
+ READSW (US_TO_SYN68K (code + 1)));
|
||
|
temp->num_child_blocks = 2;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
switch (m68kop)
|
||
|
{
|
||
|
case 0x4EF8: /* Is it a jmp _abs.w? */
|
||
|
temp->child[0] =
|
||
|
(uint32) SYN68K_TO_US (READSW (US_TO_SYN68K (code + 1)));
|
||
|
temp->num_child_blocks = 1;
|
||
|
return;
|
||
|
case 0x4EB8: /* Is it a jsr _abs.w? */
|
||
|
temp->child[0] =
|
||
|
(uint32) SYN68K_TO_US (READSW (US_TO_SYN68K (code + 1)));
|
||
|
temp->num_child_blocks = 0; /* Pretend we don't know the dest. */
|
||
|
return;
|
||
|
case 0x4EF9: /* Is it a jmp _abs.l? */
|
||
|
temp->child[0] =
|
||
|
(uint32) SYN68K_TO_US (READUL (US_TO_SYN68K (code + 1)));
|
||
|
temp->num_child_blocks = 1;
|
||
|
return;
|
||
|
case 0x4EB9: /* Is it a jsr _abs.l? */
|
||
|
temp->child[0] =
|
||
|
(uint32) SYN68K_TO_US (READUL (US_TO_SYN68K (code + 1)));
|
||
|
temp->num_child_blocks = 0;
|
||
|
return;
|
||
|
case 0x4EBA: /* Is it a pc-relative jsr? */
|
||
|
temp->child[0] = (uint32) ((READSW (US_TO_SYN68K (code + 1))
|
||
|
+ code + 1));
|
||
|
temp->num_child_blocks = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Strange, unknown block ender. Probably something capable of trapping.
|
||
|
* Assume that the subsequent instruction is the target.
|
||
|
*/
|
||
|
temp->child[0] = (uint32) (code + map->instruction_words);
|
||
|
temp->num_child_blocks = 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
int
|
||
|
amode_size (int amode, const uint16 *code, int ref_size)
|
||
|
{
|
||
|
uint16 c = READUW (US_TO_SYN68K (code));
|
||
|
|
||
|
switch (amode) {
|
||
|
case 0x28: case 0x29: case 0x2A: case 0x2B: /* Addressing mode 5 */
|
||
|
case 0x2C: case 0x2D: case 0x2E: case 0x2F:
|
||
|
return 1;
|
||
|
case 0x38: /* 111/000 (xxx).W */
|
||
|
return 1;
|
||
|
case 0x39: /* 111/001 (xxx).L */
|
||
|
return 2;
|
||
|
case 0x3C: /* 111/100 #<data> */
|
||
|
return ref_size == 4 ? 2 : 1; /* Bytes still take 1 full word. */
|
||
|
case 0x30: case 0x31: case 0x32: case 0x33: /* Addressing mode 6. */
|
||
|
case 0x34: case 0x35: case 0x36: case 0x37:
|
||
|
case 0x3B: /* Addressing mode 111/011 */
|
||
|
if (!(c & 0x100)) /* 8 bit displacement. */
|
||
|
return 1;
|
||
|
else if ((c & 0xF) == 0x0)
|
||
|
return ((c >> 4) & 3);
|
||
|
return ((c >> 4) & 3) + (c & 3) - 1;
|
||
|
case 0x3A: /* 111/010 (d16,PC) */
|
||
|
return 1;
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Given a pointer to an m68k opcode, returns the total number of words
|
||
|
* that instruction occupies, including operands and memory taken by
|
||
|
* complex addressing modes.
|
||
|
*/
|
||
|
int
|
||
|
instruction_size (const uint16 *code, const OpcodeMappingInfo *map)
|
||
|
{
|
||
|
int size;
|
||
|
int m68kop = READUW (US_TO_SYN68K (code));
|
||
|
|
||
|
/* See if we have a conditional branch format operand instruction. */
|
||
|
if ((m68kop >> 12) == 6)
|
||
|
{
|
||
|
if ((m68kop & 0xFF) == 0)
|
||
|
size = 2;
|
||
|
else if ((m68kop & 0xFF) == 0xFF)
|
||
|
size = 3;
|
||
|
else
|
||
|
size = 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
size = map->instruction_words;
|
||
|
/* If we have an addressing mode, compute how many words it takes. */
|
||
|
if (map->amode_size != 0)
|
||
|
size += amode_size (m68kop & 0x3F, code + size, map->amode_size);
|
||
|
|
||
|
/* If we have a rev. addressing mode, compute how many words it takes. */
|
||
|
if (map->reversed_amode_size != 0) /* Is there a rev. addressing mode? */
|
||
|
size += amode_size (((m68kop >> 9) & 0x7) | ((m68kop >> 3) & 0x38),
|
||
|
code + size, map->reversed_amode_size);
|
||
|
}
|
||
|
|
||
|
return size;
|
||
|
}
|