#include #include #include #include #include #include #include #include "musashi-m68k/m68k.h" #define ROM_START (0) #define ROM_SIZE (0x400000) #define RAM_START (0xFF0000) #define RAM_SIZE (0x10000) #define IO1_START (0xA10000) #define IO1_SIZE (0x4004) #define IO2_START (0xC00000) #define IO2_SIZE (0x20) /* Data */ unsigned char g_rom[ROM_SIZE]; unsigned char g_ram[RAM_SIZE]; unsigned char g_io1[IO1_SIZE]; unsigned char g_io2[IO2_SIZE]; /* Exit with an error message. Use printf syntax. */ static void exit_error(char* fmt, ...) { static int guard_val = 0; char buff[100]; unsigned int pc; va_list args; if (guard_val) return; else guard_val = 1; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "\n"); pc = m68k_get_reg(NULL, M68K_REG_PPC); m68k_disassemble(buff, pc, M68K_CPU_TYPE_68000); fprintf(stderr, "%08x: %s\n", pc, buff); exit(EXIT_FAILURE); } static inline bool is_in_range(uint32_t value, uint32_t begin, uint32_t length) { return value >= begin && value <= begin + length; } enum bitness { BITNESS_8, BITNESS_16, BITNESS_32, }; struct read_result { unsigned int result; bool successful; }; static inline unsigned int memory_read_concrete( enum bitness bitness, unsigned char const * base, unsigned int address) { switch (bitness) { case BITNESS_8: return base[address]; case BITNESS_16: return (base[address] << 8) | base[address + 1]; case BITNESS_32: return (base[address] << 24) | (base[address + 1] << 16) | (base[address + 2] << 8) | base[address + 3]; } __builtin_unreachable(); } static inline struct read_result memory_read( enum bitness bitness, unsigned int address) { if (is_in_range(address, ROM_START, ROM_SIZE)) return (struct read_result){ memory_read_concrete(bitness, g_rom, address - ROM_START), true, }; else if (is_in_range(address, RAM_START, RAM_SIZE)) return (struct read_result){ memory_read_concrete(bitness, g_ram, address - RAM_START), true, }; else if (is_in_range(address, IO1_START, IO1_SIZE)) return (struct read_result){ memory_read_concrete(bitness, g_io1, address - IO1_START), true, }; else if (is_in_range(address, IO2_START, IO2_SIZE)) return (struct read_result){ memory_read_concrete(bitness, g_io2, address - IO2_START), true, }; return (struct read_result){0, false}; } static inline void memory_write_concrete( enum bitness bitness, unsigned char * base, unsigned int address, unsigned int value) { switch (bitness) { case BITNESS_8: base[address] = value & 0xff; return; case BITNESS_16: base[address] = (value >> 8) & 0xff; base[address + 1] = value & 0xff; return; case BITNESS_32: base[address + 0] = (value >> 24) & 0xff; base[address + 1] = (value >> 16) & 0xff; base[address + 2] = (value >> 8) & 0xff; base[address + 3] = value & 0xff; return; } __builtin_unreachable(); } static inline bool memory_write( enum bitness bitness, unsigned int address, unsigned int value) { if (is_in_range(address, ROM_START, ROM_SIZE)) { memory_write_concrete(bitness, g_rom, address - ROM_START, value); return true; } else if (is_in_range(address, RAM_START, RAM_SIZE)) { memory_write_concrete(bitness, g_ram, address - RAM_START, value); return true; } else if (is_in_range(address, IO1_START, IO1_SIZE)) { memory_write_concrete(bitness, g_io1, address - IO1_START, value); return true; } else if (is_in_range(address, IO2_START, IO2_SIZE)) { memory_write_concrete(bitness, g_io2, address - IO2_START, value); return true; } return false; } #define MASK_24(X) ((X) & (0xFF << 24)) unsigned int m68k_read_memory_8(unsigned int address) { assert(MASK_24(address) == 0); // Just curious const struct read_result ret = memory_read(BITNESS_8, address); if (!ret.successful) exit_error("Read error u8 @%08x", address); return ret.result; } unsigned int m68k_read_memory_16(unsigned int address) { assert(MASK_24(address) == 0); // Just curious const struct read_result ret = memory_read(BITNESS_16, address); if (!ret.successful) exit_error("Read error u16 @%08x", address); return ret.result; } unsigned int m68k_read_memory_32(unsigned int address) { assert(MASK_24(address) == 0); // Just curious const struct read_result ret = memory_read(BITNESS_32, address); if (!ret.successful) exit_error("Read error u32 @%08x", address); return ret.result; } unsigned int m68k_read_disassembler_16(unsigned int address) { assert(MASK_24(address) == 0); // Just curious const struct read_result ret = memory_read(BITNESS_16, address); if (!ret.successful) exit_error("Disasm read error u16 @0x%08x", address); return ret.result; } unsigned int m68k_read_disassembler_32(unsigned int address) { assert(MASK_24(address) == 0); // Just curious const struct read_result ret = memory_read(BITNESS_32, address); if (!ret.successful) exit_error("Disasm read error u32 @0x%08x", address); return ret.result; } /* Write data to RAM or a device */ void m68k_write_memory_8(unsigned int address, unsigned int value) { assert(MASK_24(address) == 0); // Just curious const bool successful = memory_write(BITNESS_8, address, value); if (!successful) exit_error("Attempted to write %02x (u8) to address %08x", value&0xff, address); } void m68k_write_memory_16(unsigned int address, unsigned int value) { assert(MASK_24(address) == 0); // Just curious const bool successful = memory_write(BITNESS_16, address, value); if (!successful) exit_error("Attempted to write %04x (u16) to address %08x", value&0xffff, address); } void m68k_write_memory_32(unsigned int address, unsigned int value) { assert(MASK_24(address) == 0); // Just curious const bool successful = memory_write(BITNESS_16, address, value); if (!successful) exit_error("Attempted to write %08x (u32) to address %08x", value, address); } /* Called when the CPU pulses the RESET line */ void m68k_reset_callback(void) { // TODO } /* Called when the CPU acknowledges an interrupt */ int m68k_irq_ack(int level) { (void) level; // TODO exit_error("IRQ ack"); return M68K_INT_ACK_SPURIOUS; } static void make_hex(char* buff, unsigned int pc, unsigned int length) { char* ptr = buff; for (;length>0;length -= 2) { sprintf(ptr, "%04x", m68k_read_disassembler_16(pc)); pc += 2; ptr += 4; if (length > 2) *ptr++ = ' '; } } void m68k_instr_callback(int pc) { if (0) return; static char buff[100]; static char buff2[100]; static unsigned int instr_size; pc = m68k_get_reg(NULL, M68K_REG_PC); instr_size = m68k_disassemble(buff, pc, M68K_CPU_TYPE_68000); make_hex(buff2, pc, instr_size); printf("E %08X: %-20s: %s\n", pc, buff2, buff); fflush(stdout); } int main(int argc, char* argv[]) { if (argc != 2) { printf("Usage: sim \n"); exit(-1); } FILE* const fhandle = fopen(argv[1], "rb"); if (fhandle == NULL) exit_error("Unable to open %s", argv[1]); const size_t fread_ret = fread(g_rom, 1, ROM_SIZE, fhandle); if (fread_ret <= 0) exit_error("Error reading %s", argv[1]); printf("Read into ROM %zu bytes\n", fread_ret); m68k_init(); m68k_set_cpu_type(M68K_CPU_TYPE_68000); m68k_pulse_reset(); while (1) { // Values to execute determine the interleave rate. // Smaller values allow for more accurate interleaving with multiple // devices/CPUs but is more processor intensive. // 100000 is usually a good value to start at, then work from there. // Note that I am not emulating the correct clock speed! m68k_execute(100000); } return 0; }