Mercurial > repos > blastem
view m68k_core.c @ 2069:8e51c0c3f2e3 segacd
Initial attempt at implementing the Sega CD graphics hardware
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sun, 30 Jan 2022 19:55:33 -0800 |
parents | d59ace2d7a6a |
children | 8554751f17b5 |
line wrap: on
line source
/* Copyright 2014 Michael Pavone This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #include "m68k_core.h" #include "m68k_internal.h" #include "68kinst.h" #include "backend.h" #include "gen.h" #include "util.h" #include "serialize.h" #include <stdio.h> #include <stddef.h> #include <stdlib.h> #include <string.h> char disasm_buf[1024]; int8_t native_reg(m68k_op_info * op, m68k_options * opts) { if (op->addr_mode == MODE_REG) { return opts->dregs[op->params.regs.pri]; } if (op->addr_mode == MODE_AREG) { return opts->aregs[op->params.regs.pri]; } return -1; } size_t dreg_offset(uint8_t reg) { return offsetof(m68k_context, dregs) + sizeof(uint32_t) * reg; } size_t areg_offset(uint8_t reg) { return offsetof(m68k_context, aregs) + sizeof(uint32_t) * reg; } //must be called with an m68k_op_info that uses a register size_t reg_offset(m68k_op_info *op) { return op->addr_mode == MODE_REG ? dreg_offset(op->params.regs.pri) : areg_offset(op->params.regs.pri); } void m68k_print_regs(m68k_context * context) { printf("XNZVC\n%d%d%d%d%d\n", context->flags[0], context->flags[1], context->flags[2], context->flags[3], context->flags[4]); for (int i = 0; i < 8; i++) { printf("d%d: %X\n", i, context->dregs[i]); } for (int i = 0; i < 8; i++) { printf("a%d: %X\n", i, context->aregs[i]); } } void m68k_read_size(m68k_options *opts, uint8_t size) { switch (size) { case OPSIZE_BYTE: call(&opts->gen.code, opts->read_8); break; case OPSIZE_WORD: call(&opts->gen.code, opts->read_16); break; case OPSIZE_LONG: call(&opts->gen.code, opts->read_32); break; } } void m68k_write_size(m68k_options *opts, uint8_t size, uint8_t lowfirst) { switch (size) { case OPSIZE_BYTE: call(&opts->gen.code, opts->write_8); break; case OPSIZE_WORD: call(&opts->gen.code, opts->write_16); break; case OPSIZE_LONG: if (lowfirst) { call(&opts->gen.code, opts->write_32_lowfirst); } else { call(&opts->gen.code, opts->write_32_highfirst); } break; } } void m68k_save_result(m68kinst * inst, m68k_options * opts) { if (inst->dst.addr_mode != MODE_REG && inst->dst.addr_mode != MODE_AREG && inst->dst.addr_mode != MODE_UNUSED) { if (inst->dst.addr_mode == MODE_AREG_PREDEC && ((inst->src.addr_mode == MODE_AREG_PREDEC && inst->op != M68K_MOVE) || (inst->op == M68K_NBCD)) ) { areg_to_native(opts, inst->dst.params.regs.pri, opts->gen.scratch2); } m68k_write_size(opts, inst->extra.size, 1); } } static void translate_m68k_lea_pea(m68k_options * opts, m68kinst * inst) { code_info *code = &opts->gen.code; int8_t dst_reg = inst->op == M68K_PEA ? opts->gen.scratch1 : native_reg(&(inst->dst), opts); switch(inst->src.addr_mode) { case MODE_AREG_INDIRECT: cycles(&opts->gen, BUS); if (dst_reg >= 0) { areg_to_native(opts, inst->src.params.regs.pri, dst_reg); } else { if (opts->aregs[inst->src.params.regs.pri] >= 0) { native_to_areg(opts, opts->aregs[inst->src.params.regs.pri], inst->dst.params.regs.pri); } else { areg_to_native(opts, inst->src.params.regs.pri, opts->gen.scratch1); native_to_areg(opts, opts->gen.scratch1, inst->dst.params.regs.pri); } } break; case MODE_AREG_DISPLACE: cycles(&opts->gen, 8); calc_areg_displace(opts, &inst->src, dst_reg >= 0 ? dst_reg : opts->gen.scratch1); if (dst_reg < 0) { native_to_areg(opts, opts->gen.scratch1, inst->dst.params.regs.pri); } break; case MODE_AREG_INDEX_DISP8: cycles(&opts->gen, 12); if (dst_reg < 0 || inst->dst.params.regs.pri == inst->src.params.regs.pri || inst->dst.params.regs.pri == (inst->src.params.regs.sec >> 1 & 0x7)) { dst_reg = opts->gen.scratch1; } calc_areg_index_disp8(opts, &inst->src, dst_reg); if (dst_reg == opts->gen.scratch1 && inst->op != M68K_PEA) { native_to_areg(opts, opts->gen.scratch1, inst->dst.params.regs.pri); } break; case MODE_PC_DISPLACE: cycles(&opts->gen, 8); if (inst->op == M68K_PEA) { ldi_native(opts, inst->src.params.regs.displacement + inst->address+2, dst_reg); } else { ldi_areg(opts, inst->src.params.regs.displacement + inst->address+2, inst->dst.params.regs.pri); } break; case MODE_PC_INDEX_DISP8: cycles(&opts->gen, BUS*3); if (dst_reg < 0 || inst->dst.params.regs.pri == (inst->src.params.regs.sec >> 1 & 0x7)) { dst_reg = opts->gen.scratch1; } ldi_native(opts, inst->address+2, dst_reg); calc_index_disp8(opts, &inst->src, dst_reg); if (dst_reg == opts->gen.scratch1 && inst->op != M68K_PEA) { native_to_areg(opts, opts->gen.scratch1, inst->dst.params.regs.pri); } break; case MODE_ABSOLUTE: case MODE_ABSOLUTE_SHORT: cycles(&opts->gen, (inst->src.addr_mode == MODE_ABSOLUTE) ? BUS * 3 : BUS * 2); if (inst->op == M68K_PEA) { ldi_native(opts, inst->src.params.immed, dst_reg); } else { ldi_areg(opts, inst->src.params.immed, inst->dst.params.regs.pri); } break; default: m68k_disasm(inst, disasm_buf); fatal_error("%X: %s\naddress mode %d not implemented (lea src)\n", inst->address, disasm_buf, inst->src.addr_mode); } if (inst->op == M68K_PEA) { subi_areg(opts, 4, 7); areg_to_native(opts, 7, opts->gen.scratch2); call(code, opts->write_32_lowfirst); } } static void push_const(m68k_options *opts, int32_t value) { ldi_native(opts, value, opts->gen.scratch1); subi_areg(opts, 4, 7); areg_to_native(opts, 7, opts->gen.scratch2); call(&opts->gen.code, opts->write_32_highfirst); } void jump_m68k_abs(m68k_options * opts, uint32_t address) { code_info *code = &opts->gen.code; code_ptr dest_addr = get_native_address(opts, address); if (!dest_addr) { opts->gen.deferred = defer_address(opts->gen.deferred, address, code->cur + 1); //dummy address to be replaced later, make sure it generates a 4-byte displacement dest_addr = code->cur + 256; } jmp(code, dest_addr); //this used to call opts->native_addr for destinations in RAM, but that shouldn't be needed //since instruction retranslation patches the original native instruction location } static void translate_m68k_bsr(m68k_options * opts, m68kinst * inst) { code_info *code = &opts->gen.code; int32_t disp = inst->src.params.immed; uint32_t after = inst->address + (inst->variant == VAR_BYTE ? 2 : 4); //TODO: Add cycles in the right place relative to pushing the return address on the stack cycles(&opts->gen, 10); push_const(opts, after); jump_m68k_abs(opts, inst->address + 2 + disp); } static void translate_m68k_jmp_jsr(m68k_options * opts, m68kinst * inst) { uint8_t is_jsr = inst->op == M68K_JSR; code_info *code = &opts->gen.code; code_ptr dest_addr; uint8_t sec_reg; uint32_t after; uint32_t m68k_addr; switch(inst->src.addr_mode) { case MODE_AREG_INDIRECT: cycles(&opts->gen, BUS*2); if (is_jsr) { push_const(opts, inst->address+2); } areg_to_native(opts, inst->src.params.regs.pri, opts->gen.scratch1); call(code, opts->native_addr); jmp_r(code, opts->gen.scratch1); break; case MODE_AREG_DISPLACE: cycles(&opts->gen, BUS*2); if (is_jsr) { push_const(opts, inst->address+4); } calc_areg_displace(opts, &inst->src, opts->gen.scratch1); call(code, opts->native_addr); jmp_r(code, opts->gen.scratch1); break; case MODE_AREG_INDEX_DISP8: cycles(&opts->gen, BUS*3);//TODO: CHeck that this is correct if (is_jsr) { push_const(opts, inst->address+4); } calc_areg_index_disp8(opts, &inst->src, opts->gen.scratch1); call(code, opts->native_addr); jmp_r(code, opts->gen.scratch1); break; case MODE_PC_DISPLACE: //TODO: Add cycles in the right place relative to pushing the return address on the stack cycles(&opts->gen, 10); if (is_jsr) { push_const(opts, inst->address+4); } jump_m68k_abs(opts, inst->src.params.regs.displacement + inst->address + 2); break; case MODE_PC_INDEX_DISP8: cycles(&opts->gen, BUS*3);//TODO: CHeck that this is correct if (is_jsr) { push_const(opts, inst->address+4); } ldi_native(opts, inst->address+2, opts->gen.scratch1); calc_index_disp8(opts, &inst->src, opts->gen.scratch1); call(code, opts->native_addr); jmp_r(code, opts->gen.scratch1); break; case MODE_ABSOLUTE: case MODE_ABSOLUTE_SHORT: //TODO: Add cycles in the right place relative to pushing the return address on the stack cycles(&opts->gen, inst->src.addr_mode == MODE_ABSOLUTE ? 12 : 10); if (is_jsr) { push_const(opts, inst->address + (inst->src.addr_mode == MODE_ABSOLUTE ? 6 : 4)); } jump_m68k_abs(opts, inst->src.params.immed); break; default: m68k_disasm(inst, disasm_buf); fatal_error("%s\naddress mode %d not yet supported (%s)\n", disasm_buf, inst->src.addr_mode, is_jsr ? "jsr" : "jmp"); } } static void translate_m68k_unlk(m68k_options * opts, m68kinst * inst) { cycles(&opts->gen, BUS); if (inst->dst.params.regs.pri != 7) { areg_to_native(opts, inst->dst.params.regs.pri, opts->aregs[7]); } areg_to_native(opts, 7, opts->gen.scratch1); call(&opts->gen.code, opts->read_32); native_to_areg(opts, opts->gen.scratch1, inst->dst.params.regs.pri); if (inst->dst.params.regs.pri != 7) { addi_areg(opts, 4, 7); } } static void translate_m68k_link(m68k_options * opts, m68kinst * inst) { //compensate for displacement word cycles(&opts->gen, BUS); subi_areg(opts, 4, 7); areg_to_native(opts, 7, opts->gen.scratch2); areg_to_native(opts, inst->src.params.regs.pri, opts->gen.scratch1); call(&opts->gen.code, opts->write_32_highfirst); native_to_areg(opts, opts->aregs[7], inst->src.params.regs.pri); addi_areg(opts, inst->dst.params.immed, 7); //prefetch cycles(&opts->gen, BUS); } static void translate_m68k_rts(m68k_options * opts, m68kinst * inst) { code_info *code = &opts->gen.code; areg_to_native(opts, 7, opts->gen.scratch1); addi_areg(opts, 4, 7); call(code, opts->read_32); cycles(&opts->gen, 2*BUS); call(code, opts->native_addr); jmp_r(code, opts->gen.scratch1); } static void translate_m68k_rtr(m68k_options *opts, m68kinst * inst) { code_info *code = &opts->gen.code; //Read saved CCR areg_to_native(opts, 7, opts->gen.scratch1); call(code, opts->read_16); addi_areg(opts, 2, 7); call(code, opts->set_ccr); //Read saved PC areg_to_native(opts, 7, opts->gen.scratch1); call(code, opts->read_32); addi_areg(opts, 4, 7); //Get native address and jump to it call(code, opts->native_addr); jmp_r(code, opts->gen.scratch1); } static void translate_m68k_trap(m68k_options *opts, m68kinst *inst) { code_info *code = &opts->gen.code; uint32_t vector, pc = inst->address; switch (inst->op) { case M68K_TRAP: vector = inst->src.params.immed + VECTOR_TRAP_0; pc += 2; break; case M68K_A_LINE_TRAP: vector = VECTOR_LINE_1010; break; case M68K_F_LINE_TRAP: vector = VECTOR_LINE_1111; break; } ldi_native(opts, vector, opts->gen.scratch2); ldi_native(opts, pc, opts->gen.scratch1); jmp(code, opts->trap); } static void translate_m68k_illegal(m68k_options *opts, m68kinst *inst) { code_info *code = &opts->gen.code; cycles(&opts->gen, BUS); ldi_native(opts, VECTOR_ILLEGAL_INST, opts->gen.scratch2); ldi_native(opts, inst->address, opts->gen.scratch1); jmp(code, opts->trap); } static void translate_m68k_move_usp(m68k_options *opts, m68kinst *inst) { m68k_trap_if_not_supervisor(opts, inst); cycles(&opts->gen, BUS); int8_t reg; if (inst->src.addr_mode == MODE_UNUSED) { reg = native_reg(&inst->dst, opts); if (reg < 0) { reg = opts->gen.scratch1; } areg_to_native(opts, 8, reg); if (reg == opts->gen.scratch1) { native_to_areg(opts, opts->gen.scratch1, inst->dst.params.regs.pri); } } else { reg = native_reg(&inst->src, opts); if (reg < 0) { reg = opts->gen.scratch1; areg_to_native(opts, inst->src.params.regs.pri, reg); } native_to_areg(opts, reg, 8); } } static void translate_movem_regtomem_reglist(m68k_options * opts, m68kinst *inst) { code_info *code = &opts->gen.code; int8_t bit,reg,dir; if (inst->dst.addr_mode == MODE_AREG_PREDEC) { reg = 15; dir = -1; } else { reg = 0; dir = 1; } for(bit=0; reg < 16 && reg >= 0; reg += dir, bit++) { if (inst->src.params.immed & (1 << bit)) { if (inst->dst.addr_mode == MODE_AREG_PREDEC) { subi_native(opts, (inst->extra.size == OPSIZE_LONG) ? 4 : 2, opts->gen.scratch2); } push_native(opts, opts->gen.scratch2); if (reg > 7) { areg_to_native(opts, reg-8, opts->gen.scratch1); } else { dreg_to_native(opts, reg, opts->gen.scratch1); } if (inst->extra.size == OPSIZE_LONG) { call(code, opts->write_32_lowfirst); } else { call(code, opts->write_16); } pop_native(opts, opts->gen.scratch2); if (inst->dst.addr_mode != MODE_AREG_PREDEC) { addi_native(opts, (inst->extra.size == OPSIZE_LONG) ? 4 : 2, opts->gen.scratch2); } } } } static void translate_movem_memtoreg_reglist(m68k_options * opts, m68kinst *inst) { code_info *code = &opts->gen.code; for(uint8_t reg = 0; reg < 16; reg ++) { if (inst->dst.params.immed & (1 << reg)) { push_native(opts, opts->gen.scratch1); if (inst->extra.size == OPSIZE_LONG) { call(code, opts->read_32); } else { call(code, opts->read_16); } if (inst->extra.size == OPSIZE_WORD) { sign_extend16_native(opts, opts->gen.scratch1); } if (reg > 7) { native_to_areg(opts, opts->gen.scratch1, reg-8); } else { native_to_dreg(opts, opts->gen.scratch1, reg); } pop_native(opts, opts->gen.scratch1); addi_native(opts, (inst->extra.size == OPSIZE_LONG) ? 4 : 2, opts->gen.scratch1); } } } static code_ptr get_movem_impl(m68k_options *opts, m68kinst *inst) { uint8_t reg_to_mem = inst->src.addr_mode == MODE_REG; uint8_t size = inst->extra.size; int8_t dir = reg_to_mem && inst->dst.addr_mode == MODE_AREG_PREDEC ? -1 : 1; uint16_t reglist = reg_to_mem ? inst->src.params.immed : inst->dst.params.immed; for (uint32_t i = 0; i < opts->num_movem; i++) { if ( opts->big_movem[i].reglist == reglist && opts->big_movem[i].reg_to_mem == reg_to_mem && opts->big_movem[i].size == size && opts->big_movem[i].dir == dir ) { return opts->big_movem[i].impl; } } if (opts->num_movem == opts->movem_storage) { if (!opts->movem_storage) { opts->movem_storage = 4; } else { opts->movem_storage *= 2; } opts->big_movem = realloc(opts->big_movem, sizeof(movem_fun) * opts->movem_storage); } if (!opts->extra_code.cur) { init_code_info(&opts->extra_code); } check_alloc_code(&opts->extra_code, 512); code_ptr impl = opts->extra_code.cur; code_info tmp = opts->gen.code; opts->gen.code = opts->extra_code; if (reg_to_mem) { translate_movem_regtomem_reglist(opts, inst); } else { translate_movem_memtoreg_reglist(opts, inst); } opts->extra_code = opts->gen.code; opts->gen.code = tmp; rts(&opts->extra_code); return impl; } static void translate_m68k_movem(m68k_options * opts, m68kinst * inst) { code_info *code = &opts->gen.code; uint8_t early_cycles; uint16_t num_regs = inst->src.addr_mode == MODE_REG ? inst->src.params.immed : inst->dst.params.immed; { //TODO: Move this popcount alg to a utility function uint16_t a = (num_regs & 0b1010101010101010) >> 1; uint16_t b = num_regs & 0b0101010101010101; num_regs = a + b; a = (num_regs & 0b1100110011001100) >> 2; b = num_regs & 0b0011001100110011; num_regs = a + b; a = (num_regs & 0b1111000011110000) >> 4; b = num_regs & 0b0000111100001111; num_regs = a + b; a = (num_regs & 0b1111111100000000) >> 8; b = num_regs & 0b0000000011111111; num_regs = a + b; } if(inst->src.addr_mode == MODE_REG) { //reg to mem early_cycles = 8; switch (inst->dst.addr_mode) { case MODE_AREG_INDIRECT: case MODE_AREG_PREDEC: areg_to_native(opts, inst->dst.params.regs.pri, opts->gen.scratch2); break; case MODE_AREG_DISPLACE: early_cycles += BUS; calc_areg_displace(opts, &inst->dst, opts->gen.scratch2); break; case MODE_AREG_INDEX_DISP8: early_cycles += 6; calc_areg_index_disp8(opts, &inst->dst, opts->gen.scratch2); break; case MODE_PC_DISPLACE: early_cycles += BUS; ldi_native(opts, inst->dst.params.regs.displacement + inst->address+2, opts->gen.scratch2); break; case MODE_PC_INDEX_DISP8: early_cycles += 6; ldi_native(opts, inst->address+2, opts->gen.scratch2); calc_index_disp8(opts, &inst->dst, opts->gen.scratch2); case MODE_ABSOLUTE: early_cycles += 4; case MODE_ABSOLUTE_SHORT: early_cycles += 4; ldi_native(opts, inst->dst.params.immed, opts->gen.scratch2); break; default: m68k_disasm(inst, disasm_buf); fatal_error("%X: %s\naddress mode %d not implemented (movem dst)\n", inst->address, disasm_buf, inst->dst.addr_mode); } cycles(&opts->gen, early_cycles); if (num_regs <= 9) { translate_movem_regtomem_reglist(opts, inst); } else { call(code, get_movem_impl(opts, inst)); } if (inst->dst.addr_mode == MODE_AREG_PREDEC) { native_to_areg(opts, opts->gen.scratch2, inst->dst.params.regs.pri); } } else { //mem to reg early_cycles = 8; //includes prefetch switch (inst->src.addr_mode) { case MODE_AREG_INDIRECT: case MODE_AREG_POSTINC: areg_to_native(opts, inst->src.params.regs.pri, opts->gen.scratch1); break; case MODE_AREG_DISPLACE: early_cycles += BUS; calc_areg_displace(opts, &inst->src, opts->gen.scratch1); break; case MODE_AREG_INDEX_DISP8: early_cycles += 6; calc_areg_index_disp8(opts, &inst->src, opts->gen.scratch1); break; case MODE_PC_DISPLACE: early_cycles += BUS; ldi_native(opts, inst->src.params.regs.displacement + inst->address+2, opts->gen.scratch1); break; case MODE_PC_INDEX_DISP8: early_cycles += 6; ldi_native(opts, inst->address+2, opts->gen.scratch1); calc_index_disp8(opts, &inst->src, opts->gen.scratch1); break; case MODE_ABSOLUTE: early_cycles += 4; case MODE_ABSOLUTE_SHORT: early_cycles += 4; ldi_native(opts, inst->src.params.immed, opts->gen.scratch1); break; default: m68k_disasm(inst, disasm_buf); fatal_error("%X: %s\naddress mode %d not implemented (movem src)\n", inst->address, disasm_buf, inst->src.addr_mode); } cycles(&opts->gen, early_cycles); if (num_regs <= 9) { translate_movem_memtoreg_reglist(opts, inst); } else { call(code, get_movem_impl(opts, inst)); } if (inst->src.addr_mode == MODE_AREG_POSTINC) { native_to_areg(opts, opts->gen.scratch1, inst->src.params.regs.pri); } //Extra read call(code, opts->read_16); } } static void translate_m68k_nop(m68k_options *opts, m68kinst *inst) { cycles(&opts->gen, BUS); } void swap_ssp_usp(m68k_options * opts) { areg_to_native(opts, 7, opts->gen.scratch2); areg_to_native(opts, 8, opts->aregs[7]); native_to_areg(opts, opts->gen.scratch2, 8); } static void translate_m68k_rte(m68k_options *opts, m68kinst *inst) { m68k_trap_if_not_supervisor(opts, inst); code_info *code = &opts->gen.code; //Read saved SR areg_to_native(opts, 7, opts->gen.scratch1); call(code, opts->read_16); addi_areg(opts, 2, 7); call(code, opts->set_sr); //Read saved PC areg_to_native(opts, 7, opts->gen.scratch1); call(code, opts->read_32); addi_areg(opts, 4, 7); check_user_mode_swap_ssp_usp(opts); cycles(&opts->gen, 2*BUS); //Get native address, sync components, recalculate integer points and jump to returned address call(code, opts->native_addr_and_sync); jmp_r(code, opts->gen.scratch1); } code_ptr get_native_address(m68k_options *opts, uint32_t address) { native_map_slot * native_code_map = opts->gen.native_code_map; memmap_chunk const *mem_chunk = find_map_chunk(address, &opts->gen, 0, NULL); if (mem_chunk) { //calculate the lowest alias for this address address = mem_chunk->start + ((address - mem_chunk->start) & mem_chunk->mask); } else { address &= opts->gen.address_mask; } uint32_t chunk = address / NATIVE_CHUNK_SIZE; if (!native_code_map[chunk].base) { return NULL; } uint32_t offset = address % NATIVE_CHUNK_SIZE; if (native_code_map[chunk].offsets[offset] == INVALID_OFFSET || native_code_map[chunk].offsets[offset] == EXTENSION_WORD) { return NULL; } return native_code_map[chunk].base + native_code_map[chunk].offsets[offset]; } code_ptr get_native_from_context(m68k_context * context, uint32_t address) { return get_native_address(context->options, address); } uint32_t get_instruction_start(m68k_options *opts, uint32_t address) { native_map_slot * native_code_map = opts->gen.native_code_map; memmap_chunk const *mem_chunk = find_map_chunk(address, &opts->gen, 0, NULL); if (mem_chunk) { //calculate the lowest alias for this address address = mem_chunk->start + ((address - mem_chunk->start) & mem_chunk->mask); } else { address &= opts->gen.address_mask; } uint32_t chunk = address / NATIVE_CHUNK_SIZE; if (!native_code_map[chunk].base) { return 0; } uint32_t offset = address % NATIVE_CHUNK_SIZE; if (native_code_map[chunk].offsets[offset] == INVALID_OFFSET) { return 0; } while (native_code_map[chunk].offsets[offset] == EXTENSION_WORD) { --address; chunk = address / NATIVE_CHUNK_SIZE; offset = address % NATIVE_CHUNK_SIZE; } return address; } static void map_native_address(m68k_context * context, uint32_t address, code_ptr native_addr, uint8_t size, uint8_t native_size) { m68k_options * opts = context->options; native_map_slot * native_code_map = opts->gen.native_code_map; uint32_t meta_off; memmap_chunk const *mem_chunk = find_map_chunk(address, &opts->gen, MMAP_CODE, &meta_off); if (mem_chunk) { if (mem_chunk->flags & MMAP_CODE) { uint32_t masked = (address - mem_chunk->start) & mem_chunk->mask; uint32_t final_off = masked + meta_off; uint32_t ram_flags_off = final_off >> (opts->gen.ram_flags_shift + 3); context->ram_code_flags[ram_flags_off] |= 1 << ((final_off >> opts->gen.ram_flags_shift) & 7); uint32_t slot = final_off / 1024; if (!opts->gen.ram_inst_sizes[slot]) { opts->gen.ram_inst_sizes[slot] = malloc(sizeof(uint8_t) * 512); } opts->gen.ram_inst_sizes[slot][(final_off/2) & 511] = native_size; //TODO: Deal with case in which end of instruction is in a different memory chunk masked = (address + size - 1) & mem_chunk->mask; final_off = masked + meta_off; ram_flags_off = final_off >> (opts->gen.ram_flags_shift + 3); context->ram_code_flags[ram_flags_off] |= 1 << ((final_off >> opts->gen.ram_flags_shift) & 7); } //calculate the lowest alias for this address address = mem_chunk->start + ((address - mem_chunk->start) & mem_chunk->mask); } else { address &= opts->gen.address_mask; } uint32_t chunk = address / NATIVE_CHUNK_SIZE; if (!native_code_map[chunk].base) { native_code_map[chunk].base = native_addr; native_code_map[chunk].offsets = malloc(sizeof(int32_t) * NATIVE_CHUNK_SIZE); memset(native_code_map[chunk].offsets, 0xFF, sizeof(int32_t) * NATIVE_CHUNK_SIZE); } uint32_t offset = address % NATIVE_CHUNK_SIZE; native_code_map[chunk].offsets[offset] = native_addr-native_code_map[chunk].base; for(address++,size-=1; size; address++,size-=1) { address &= opts->gen.address_mask; chunk = address / NATIVE_CHUNK_SIZE; offset = address % NATIVE_CHUNK_SIZE; if (!native_code_map[chunk].base) { native_code_map[chunk].base = native_addr; native_code_map[chunk].offsets = malloc(sizeof(int32_t) * NATIVE_CHUNK_SIZE); memset(native_code_map[chunk].offsets, 0xFF, sizeof(int32_t) * NATIVE_CHUNK_SIZE); } if (native_code_map[chunk].offsets[offset] == INVALID_OFFSET) { //TODO: Better handling of overlapping instructions native_code_map[chunk].offsets[offset] = EXTENSION_WORD; } } } static uint8_t get_native_inst_size(m68k_options * opts, uint32_t address) { uint32_t meta_off; memmap_chunk const *chunk = find_map_chunk(address, &opts->gen, MMAP_CODE, &meta_off); if (chunk) { meta_off += (address - chunk->start) & chunk->mask; } uint32_t slot = meta_off/1024; return opts->gen.ram_inst_sizes[slot][(meta_off/2)%512]; } uint8_t m68k_is_terminal(m68kinst * inst) { return inst->op == M68K_RTS || inst->op == M68K_RTE || inst->op == M68K_RTR || inst->op == M68K_JMP || inst->op == M68K_TRAP || inst->op == M68K_ILLEGAL || inst->op == M68K_INVALID || (inst->op == M68K_BCC && inst->extra.cond == COND_TRUE); } static void m68k_handle_deferred(m68k_context * context) { m68k_options * opts = context->options; process_deferred(&opts->gen.deferred, context, (native_addr_func)get_native_from_context); if (opts->gen.deferred) { translate_m68k_stream(opts->gen.deferred->address, context); } } uint16_t m68k_get_ir(m68k_context *context) { uint32_t inst_addr = get_instruction_start(context->options, context->last_prefetch_address-2); uint16_t *native_addr = get_native_pointer(inst_addr, (void **)context->mem_pointers, &context->options->gen); if (native_addr) { return *native_addr; } fprintf(stderr, "M68K: Failed to calculate value of IR. Last prefetch address: %X\n", context->last_prefetch_address); return 0xFFFF; } static m68k_debug_handler find_breakpoint(m68k_context *context, uint32_t address) { for (uint32_t i = 0; i < context->num_breakpoints; i++) { if (context->breakpoints[i].address == address) { return context->breakpoints[i].handler; } } return NULL; } void insert_breakpoint(m68k_context * context, uint32_t address, m68k_debug_handler bp_handler) { if (!find_breakpoint(context, address)) { if (context->bp_storage == context->num_breakpoints) { context->bp_storage *= 2; if (context->bp_storage < 4) { context->bp_storage = 4; } context->breakpoints = realloc(context->breakpoints, context->bp_storage * sizeof(m68k_breakpoint)); } context->breakpoints[context->num_breakpoints++] = (m68k_breakpoint){ .handler = bp_handler, .address = address }; m68k_breakpoint_patch(context, address, bp_handler, NULL); } } m68k_context *m68k_bp_dispatcher(m68k_context *context, uint32_t address) { m68k_debug_handler handler = find_breakpoint(context, address); if (handler) { handler(context, address); } else { //spurious breakoint? warning("Spurious breakpoing at %X\n", address); remove_breakpoint(context, address); } return context; } typedef enum { RAW_FUNC = 1, BINARY_ARITH, UNARY_ARITH, OP_FUNC } impl_type; typedef void (*raw_fun)(m68k_options * opts, m68kinst *inst); typedef void (*op_fun)(m68k_options * opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op); typedef struct { union { raw_fun raw; uint32_t flag_mask; op_fun op; } impl; impl_type itype; } impl_info; #define RAW_IMPL(inst, fun) [inst] = { .impl = { .raw = fun }, .itype = RAW_FUNC } #define OP_IMPL(inst, fun) [inst] = { .impl = { .op = fun }, .itype = OP_FUNC } #define UNARY_IMPL(inst, mask) [inst] = { .impl = { .flag_mask = mask }, .itype = UNARY_ARITH } #define BINARY_IMPL(inst, mask) [inst] = { .impl = { .flag_mask = mask}, .itype = BINARY_ARITH } static impl_info m68k_impls[] = { //math BINARY_IMPL(M68K_ADD, X|N|Z|V|C), BINARY_IMPL(M68K_SUB, X|N|Z|V|C), //z flag is special cased for ADDX/SUBX BINARY_IMPL(M68K_ADDX, X|N|V|C), BINARY_IMPL(M68K_SUBX, X|N|V|C), OP_IMPL(M68K_ABCD, translate_m68k_abcd_sbcd), OP_IMPL(M68K_SBCD, translate_m68k_abcd_sbcd), OP_IMPL(M68K_NBCD, translate_m68k_abcd_sbcd), BINARY_IMPL(M68K_AND, N|Z|V0|C0), BINARY_IMPL(M68K_EOR, N|Z|V0|C0), BINARY_IMPL(M68K_OR, N|Z|V0|C0), RAW_IMPL(M68K_CMP, translate_m68k_cmp), OP_IMPL(M68K_DIVS, translate_m68k_div), OP_IMPL(M68K_DIVU, translate_m68k_div), OP_IMPL(M68K_MULS, translate_m68k_mul), OP_IMPL(M68K_MULU, translate_m68k_mul), RAW_IMPL(M68K_EXT, translate_m68k_ext), UNARY_IMPL(M68K_NEG, X|N|Z|V|C), OP_IMPL(M68K_NEGX, translate_m68k_negx), UNARY_IMPL(M68K_NOT, N|Z|V|C), UNARY_IMPL(M68K_TST, N|Z|V0|C0), //shift/rotate OP_IMPL(M68K_ASL, translate_m68k_sl), OP_IMPL(M68K_LSL, translate_m68k_sl), OP_IMPL(M68K_ASR, translate_m68k_asr), OP_IMPL(M68K_LSR, translate_m68k_lsr), OP_IMPL(M68K_ROL, translate_m68k_rot), OP_IMPL(M68K_ROR, translate_m68k_rot), OP_IMPL(M68K_ROXL, translate_m68k_rot), OP_IMPL(M68K_ROXR, translate_m68k_rot), UNARY_IMPL(M68K_SWAP, N|Z|V0|C0), //bit OP_IMPL(M68K_BCHG, translate_m68k_bit), OP_IMPL(M68K_BCLR, translate_m68k_bit), OP_IMPL(M68K_BSET, translate_m68k_bit), OP_IMPL(M68K_BTST, translate_m68k_bit), //data movement RAW_IMPL(M68K_MOVE, translate_m68k_move), RAW_IMPL(M68K_MOVEM, translate_m68k_movem), RAW_IMPL(M68K_MOVEP, translate_m68k_movep), RAW_IMPL(M68K_MOVE_USP, translate_m68k_move_usp), RAW_IMPL(M68K_LEA, translate_m68k_lea_pea), RAW_IMPL(M68K_PEA, translate_m68k_lea_pea), UNARY_IMPL(M68K_CLR, N0|V0|C0|Z1), OP_IMPL(M68K_EXG, translate_m68k_exg), RAW_IMPL(M68K_SCC, translate_m68k_scc), //function calls and branches RAW_IMPL(M68K_BCC, translate_m68k_bcc), RAW_IMPL(M68K_BSR, translate_m68k_bsr), RAW_IMPL(M68K_DBCC, translate_m68k_dbcc), RAW_IMPL(M68K_JMP, translate_m68k_jmp_jsr), RAW_IMPL(M68K_JSR, translate_m68k_jmp_jsr), RAW_IMPL(M68K_RTS, translate_m68k_rts), RAW_IMPL(M68K_RTE, translate_m68k_rte), RAW_IMPL(M68K_RTR, translate_m68k_rtr), RAW_IMPL(M68K_LINK, translate_m68k_link), RAW_IMPL(M68K_UNLK, translate_m68k_unlk), //SR/CCR stuff RAW_IMPL(M68K_ANDI_CCR, translate_m68k_andi_ori_ccr_sr), RAW_IMPL(M68K_ANDI_SR, translate_m68k_andi_ori_ccr_sr), RAW_IMPL(M68K_EORI_CCR, translate_m68k_eori_ccr_sr), RAW_IMPL(M68K_EORI_SR, translate_m68k_eori_ccr_sr), RAW_IMPL(M68K_ORI_CCR, translate_m68k_andi_ori_ccr_sr), RAW_IMPL(M68K_ORI_SR, translate_m68k_andi_ori_ccr_sr), OP_IMPL(M68K_MOVE_CCR, translate_m68k_move_ccr_sr), OP_IMPL(M68K_MOVE_SR, translate_m68k_move_ccr_sr), OP_IMPL(M68K_MOVE_FROM_SR, translate_m68k_move_from_sr), RAW_IMPL(M68K_STOP, translate_m68k_stop), //traps OP_IMPL(M68K_CHK, translate_m68k_chk), RAW_IMPL(M68K_TRAP, translate_m68k_trap), RAW_IMPL(M68K_A_LINE_TRAP, translate_m68k_trap), RAW_IMPL(M68K_F_LINE_TRAP, translate_m68k_trap), RAW_IMPL(M68K_TRAPV, translate_m68k_trapv), RAW_IMPL(M68K_ILLEGAL, translate_m68k_illegal), RAW_IMPL(M68K_INVALID, translate_m68k_illegal), //misc RAW_IMPL(M68K_NOP, translate_m68k_nop), RAW_IMPL(M68K_RESET, translate_m68k_reset), RAW_IMPL(M68K_TAS, translate_m68k_tas), }; static void translate_m68k(m68k_context *context, m68kinst * inst) { m68k_options * opts = context->options; if (inst->address & 1) { translate_m68k_odd(opts, inst); return; } code_ptr start = opts->gen.code.cur; check_cycles_int(&opts->gen, inst->address); m68k_debug_handler bp; if ((bp = find_breakpoint(context, inst->address))) { m68k_breakpoint_patch(context, inst->address, bp, start); } //log_address(&opts->gen, inst->address, opts->gen.clock_divider == 4 ? "Sub M68k: %X @ %d\n" : "Main M68K: %X @ %d\n"); if ( (inst->src.addr_mode > MODE_AREG && inst->src.addr_mode < MODE_IMMEDIATE) || (inst->dst.addr_mode > MODE_AREG && inst->dst.addr_mode < MODE_IMMEDIATE) || (inst->op == M68K_BCC && (inst->src.params.immed & 1)) ) { //Not accurate for all cases, but probably good enough for now m68k_set_last_prefetch(opts, inst->address + inst->bytes); } impl_info * info = m68k_impls + inst->op; if (info->itype == RAW_FUNC) { info->impl.raw(opts, inst); return; } host_ea src_op, dst_op; uint8_t needs_int_latch = 0; if (inst->src.addr_mode != MODE_UNUSED) { needs_int_latch |= translate_m68k_op(inst, &src_op, opts, 0); } if (inst->dst.addr_mode != MODE_UNUSED) { needs_int_latch |= translate_m68k_op(inst, &dst_op, opts, 1); } if (needs_int_latch) { m68k_check_cycles_int_latch(opts); } if (info->itype == OP_FUNC) { info->impl.op(opts, inst, &src_op, &dst_op); } else if (info->itype == BINARY_ARITH) { translate_m68k_arith(opts, inst, info->impl.flag_mask, &src_op, &dst_op); } else if (info->itype == UNARY_ARITH) { translate_m68k_unary(opts, inst, info->impl.flag_mask, inst->dst.addr_mode != MODE_UNUSED ? &dst_op : &src_op); } else { m68k_disasm(inst, disasm_buf); fatal_error("%X: %s\ninstruction %d not yet implemented\n", inst->address, disasm_buf, inst->op); } if (opts->gen.code.stack_off) { m68k_disasm(inst, disasm_buf); fatal_error("Stack offset is %X after %X: %s\n", opts->gen.code.stack_off, inst->address, disasm_buf); } } void translate_m68k_stream(uint32_t address, m68k_context * context) { m68kinst instbuf; m68k_options * opts = context->options; code_info *code = &opts->gen.code; if(get_native_address(opts, address)) { return; } uint16_t *encoded, *next; do { if (opts->address_log) { fprintf(opts->address_log, "%X\n", address); fflush(opts->address_log); } do { encoded = get_native_pointer(address, (void **)context->mem_pointers, &opts->gen); if (!encoded) { code_ptr start = code->cur; translate_out_of_bounds(opts, address); code_ptr after = code->cur; map_native_address(context, address, start, 2, after-start); break; } code_ptr existing = get_native_address(opts, address); if (existing) { jmp(code, existing); break; } next = m68k_decode(encoded, &instbuf, address); if (instbuf.op == M68K_INVALID) { instbuf.src.params.immed = *encoded; } uint16_t m68k_size = (next-encoded)*2; address += m68k_size; //char disbuf[1024]; //m68k_disasm(&instbuf, disbuf); //printf("%X: %s\n", instbuf.address, disbuf); //make sure the beginning of the code for an instruction is contiguous check_code_prologue(code); code_ptr start = code->cur; translate_m68k(context, &instbuf); code_ptr after = code->cur; map_native_address(context, instbuf.address, start, m68k_size, after-start); } while(!m68k_is_terminal(&instbuf) && !(address & 1)); process_deferred(&opts->gen.deferred, context, (native_addr_func)get_native_from_context); if (opts->gen.deferred) { address = opts->gen.deferred->address; } } while(opts->gen.deferred); } void * m68k_retranslate_inst(uint32_t address, m68k_context * context) { m68k_options * opts = context->options; code_info *code = &opts->gen.code; uint8_t orig_size = get_native_inst_size(opts, address); code_ptr orig_start = get_native_address(context->options, address); uint32_t orig = address; code_info orig_code = {orig_start, orig_start + orig_size + 5, 0}; uint16_t *after, *inst = get_native_pointer(address, (void **)context->mem_pointers, &opts->gen); m68kinst instbuf; after = m68k_decode(inst, &instbuf, orig); if (orig_size != MAX_NATIVE_SIZE) { deferred_addr * orig_deferred = opts->gen.deferred; //make sure we have enough code space for the max size instruction check_alloc_code(code, MAX_NATIVE_SIZE); code_ptr native_start = code->cur; translate_m68k(context, &instbuf); code_ptr native_end = code->cur; /*uint8_t is_terminal = m68k_is_terminal(&instbuf); if ((native_end - native_start) <= orig_size) { code_ptr native_next; if (!is_terminal) { native_next = get_native_address(context->native_code_map, orig + (after-inst)*2); } if (is_terminal || (native_next && ((native_next == orig_start + orig_size) || (orig_size - (native_end - native_start)) > 5))) { printf("Using original location: %p\n", orig_code.cur); remove_deferred_until(&opts->gen.deferred, orig_deferred); code_info tmp; tmp.cur = code->cur; tmp.last = code->last; code->cur = orig_code.cur; code->last = orig_code.last; translate_m68k(context, &instbuf); native_end = orig_code.cur = code->cur; code->cur = tmp.cur; code->last = tmp.last; if (!is_terminal) { nop_fill_or_jmp_next(&orig_code, orig_start + orig_size, native_next); } m68k_handle_deferred(context); return orig_start; } }*/ map_native_address(context, instbuf.address, native_start, (after-inst)*2, MAX_NATIVE_SIZE); jmp(&orig_code, native_start); if (!m68k_is_terminal(&instbuf)) { code_ptr native_end = code->cur; code->cur = native_start + MAX_NATIVE_SIZE; code_ptr rest = get_native_address_trans(context, orig + (after-inst)*2); code_info tmp_code = { .cur = native_end, .last = native_start + MAX_NATIVE_SIZE, .stack_off = code->stack_off }; jmp(&tmp_code, rest); } else { code->cur = native_start + MAX_NATIVE_SIZE; } m68k_handle_deferred(context); return native_start; } else { code_info tmp = *code; *code = orig_code; translate_m68k(context, &instbuf); orig_code = *code; *code = tmp; if (!m68k_is_terminal(&instbuf)) { jmp(&orig_code, get_native_address_trans(context, orig + (after-inst)*2)); } m68k_handle_deferred(context); return orig_start; } } code_ptr get_native_address_trans(m68k_context * context, uint32_t address) { code_ptr ret = get_native_address(context->options, address); if (!ret) { translate_m68k_stream(address, context); ret = get_native_address(context->options, address); } return ret; } void remove_breakpoint(m68k_context * context, uint32_t address) { for (uint32_t i = 0; i < context->num_breakpoints; i++) { if (context->breakpoints[i].address == address) { if (i != (context->num_breakpoints-1)) { context->breakpoints[i] = context->breakpoints[context->num_breakpoints-1]; } context->num_breakpoints--; break; } } code_ptr native = get_native_address(context->options, address); if (!native) { return; } code_info tmp = context->options->gen.code; context->options->gen.code.cur = native; context->options->gen.code.last = native + MAX_NATIVE_SIZE; check_cycles_int(&context->options->gen, address); context->options->gen.code = tmp; } void start_68k_context(m68k_context * context, uint32_t address) { code_ptr addr = get_native_address_trans(context, address); m68k_options * options = context->options; options->start_context(addr, context); } void resume_68k(m68k_context *context) { code_ptr addr = context->resume_pc; context->resume_pc = NULL; m68k_options * options = context->options; context->should_return = 0; options->start_context(addr, context); } void m68k_reset(m68k_context * context) { //TODO: Actually execute the M68K reset vector rather than simulating some of its behavior uint16_t *reset_vec = get_native_pointer(0, (void **)context->mem_pointers, &context->options->gen); if (!(context->status & 0x20)) { //switching from user to system mode so swap stack pointers context->aregs[8] = context->aregs[7]; } context->status = 0x27; context->aregs[7] = reset_vec[0] << 16 | reset_vec[1]; uint32_t address = reset_vec[2] << 16 | reset_vec[3]; //interrupt mask may have changed so force a sync context->options->sync_components(context, address); start_68k_context(context, address); } void m68k_options_free(m68k_options *opts) { for (uint32_t address = 0; address < opts->gen.address_mask; address += NATIVE_CHUNK_SIZE) { uint32_t chunk = address / NATIVE_CHUNK_SIZE; if (opts->gen.native_code_map[chunk].base) { free(opts->gen.native_code_map[chunk].offsets); } } free(opts->gen.native_code_map); uint32_t ram_inst_slots = ram_size(&opts->gen) / 1024; for (uint32_t i = 0; i < ram_inst_slots; i++) { free(opts->gen.ram_inst_sizes[i]); } free(opts->gen.ram_inst_sizes); free(opts->big_movem); free(opts); } m68k_context * init_68k_context(m68k_options * opts, m68k_reset_handler reset_handler) { m68k_context * context = calloc(1, sizeof(m68k_context) + ram_size(&opts->gen) / (1 << opts->gen.ram_flags_shift) / 8); context->options = opts; context->int_cycle = CYCLE_NEVER; context->status = 0x27; context->reset_handler = (code_ptr)reset_handler; return context; } void m68k_serialize(m68k_context *context, uint32_t pc, serialize_buffer *buf) { for (int i = 0; i < 8; i++) { save_int32(buf, context->dregs[i]); } for (int i = 0; i < 9; i++) { save_int32(buf, context->aregs[i]); } save_int32(buf, pc); uint16_t sr = context->status << 3; for (int flag = 4; flag >= 0; flag--) { sr <<= 1; sr |= context->flags[flag] != 0; } save_int16(buf, sr); save_int32(buf, context->current_cycle); save_int32(buf, context->int_cycle); save_int8(buf, context->int_num); save_int8(buf, context->int_pending); save_int8(buf, context->trace_pending); } void m68k_deserialize(deserialize_buffer *buf, void *vcontext) { m68k_context *context = vcontext; for (int i = 0; i < 8; i++) { context->dregs[i] = load_int32(buf); } for (int i = 0; i < 9; i++) { context->aregs[i] = load_int32(buf); } //hack until both PC and IR registers are represented properly context->last_prefetch_address = load_int32(buf); uint16_t sr = load_int16(buf); context->status = sr >> 8; for (int flag = 0; flag < 5; flag++) { context->flags[flag] = sr & 1; sr >>= 1; } context->current_cycle = load_int32(buf); context->int_cycle = load_int32(buf); context->int_num = load_int8(buf); context->int_pending = load_int8(buf); context->trace_pending = load_int8(buf); }