#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 #include #include #include #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 * [] 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; }