summaryrefslogtreecommitdiff
path: root/emulator.c
diff options
context:
space:
mode:
authorOxore <oxore@protonmail.com>2022-08-11 23:16:38 +0300
committerOxore <oxore@protonmail.com>2022-08-25 22:33:54 +0300
commitbfec175173f4e1a350729588526c4ff0be9ac949 (patch)
tree3bd10776c8c370f05bf4a81eb2e8fc07b1288569 /emulator.c
Initial commit
Diffstat (limited to 'emulator.c')
-rw-r--r--emulator.c237
1 files changed, 237 insertions, 0 deletions
diff --git a/emulator.c b/emulator.c
new file mode 100644
index 0000000..7eb30a8
--- /dev/null
+++ b/emulator.c
@@ -0,0 +1,237 @@
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <time.h>
+#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)
+
+
+/* Read/write macros */
+#define READ_8(BASE, ADDR) ((BASE)[ADDR])
+#define READ_16(BASE, ADDR) (((BASE)[ADDR]<<8) | (BASE)[(ADDR)+1])
+#define READ_32(BASE, ADDR) (((BASE)[ADDR]<<24) | \
+ ((BASE)[(ADDR)+1]<<16) | \
+ ((BASE)[(ADDR)+2]<<8) | \
+ (BASE)[(ADDR)+3])
+
+#define WRITE_8(BASE, ADDR, VAL) ((BASE)[ADDR] = (VAL)&0xff)
+#define WRITE_16(BASE, ADDR, VAL) (((BASE)[ADDR] = ((VAL)>>8) & 0xff), \
+ ((BASE)[(ADDR)+1] = (VAL)&0xff))
+#define WRITE_32(BASE, ADDR, VAL) (((BASE)[ADDR] = ((VAL)>>24) & 0xff), \
+ ((BASE)[(ADDR)+1] = ((VAL)>>16)&0xff), \
+ ((BASE)[(ADDR)+2] = ((VAL)>>8)&0xff), \
+ ((BASE)[(ADDR)+3] = (VAL)&0xff))
+
+/* 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;
+}
+
+#define MEMORY_READ(BITNESS, ADDR) \
+ do { \
+ if (is_in_range(ADDR, ROM_START, ROM_SIZE)) \
+ return READ_##BITNESS(g_rom, ADDR - ROM_START); \
+ else if (is_in_range(ADDR, RAM_START, RAM_SIZE)) \
+ return READ_##BITNESS(g_ram, ADDR - RAM_START); \
+ else if (is_in_range(ADDR, IO1_START, IO1_SIZE)) \
+ return READ_##BITNESS(g_io1, ADDR - IO1_START); \
+ else if (is_in_range(ADDR, IO2_START, IO2_SIZE)) \
+ return READ_##BITNESS(g_io2, ADDR - IO2_START); \
+ } while (0)
+
+#define MEMORY_WRITE(BITNESS, ADDR, VAL) \
+ do { \
+ if (is_in_range(ADDR, ROM_START, ROM_SIZE)) { \
+ WRITE_##BITNESS(g_rom, ADDR - ROM_START, VAL); return; \
+ } else if (is_in_range(ADDR, RAM_START, RAM_SIZE)) { \
+ WRITE_##BITNESS(g_ram, ADDR - RAM_START, VAL); return; \
+ } else if (is_in_range(ADDR, IO1_START, IO1_SIZE)) { \
+ WRITE_##BITNESS(g_io1, ADDR - IO1_START, VAL); return; \
+ } else if (is_in_range(ADDR, IO2_START, IO2_SIZE)) { \
+ WRITE_##BITNESS(g_io2, ADDR - IO2_START, VAL); return; \
+ } \
+ } while (0)
+
+#define MASK_24(X) ((X) & (0xFF << 24))
+
+unsigned int m68k_read_memory_8(unsigned int address)
+{
+ assert(MASK_24(address) == 0); // Just curious
+ MEMORY_READ(8, address);
+ exit_error("Attempted to read u8 from address %08x", address);
+ return 0;
+}
+
+unsigned int m68k_read_memory_16(unsigned int address)
+{
+ assert(MASK_24(address) == 0); // Just curious
+ MEMORY_READ(16, address);
+ exit_error("Attempted to read u16 from address %08x", address);
+ return 0;
+}
+
+unsigned int m68k_read_memory_32(unsigned int address)
+{
+ assert(MASK_24(address) == 0); // Just curious
+ MEMORY_READ(32, address);
+ exit_error("Attempted to read u32 from address %08x", address);
+ return 0;
+}
+
+unsigned int m68k_read_disassembler_16(unsigned int address)
+{
+ assert(MASK_24(address) == 0); // Just curious
+ MEMORY_READ(16, address);
+ exit_error("Disassembler attempted to read u16 from address %08x", address);
+ return 0;
+}
+
+unsigned int m68k_read_disassembler_32(unsigned int address)
+{
+ assert(MASK_24(address) == 0); // Just curious
+ MEMORY_READ(32, address);
+ exit_error("Disassembler attempted to read u32 from address %08x", address);
+ return 0;
+}
+
+/* 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
+ MEMORY_WRITE(8, address, value);
+ 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
+ MEMORY_WRITE(16, address, value);
+ 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
+ MEMORY_WRITE(16, address, value);
+ 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)
+{
+}
+
+/* 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 <program file>\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;
+}