From 60c505b82cbbec4da360a81b959597b96c2a365a Mon Sep 17 00:00:00 2001 From: Nick Downing Date: Sun, 21 Aug 2022 18:19:21 +1000 Subject: [PATCH] Initial 68000 emulator, from multi_emu.git commit 07320529 --- .gitignore | 3 + .gitmodules | 3 + Makefile | 42 ++++++ Musashi | 1 + emu_68000.c | 369 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 418 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 Makefile create mode 160000 Musashi create mode 100644 emu_68000.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8d4b223 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +/emu_68000 +/emu_68000_alt diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..11cdffe --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Musashi"] + path = Musashi + url = https://github.com/nickd4/Musashi.git diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4985e36 --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +BIN2HEX=bin2hex.py + +CFLAGS=-g -Og -Wall -Wno-attributes -Wno-unused-function +ALT_68000_CFLAGS=-DALT_BACKEND=1 -IMusashi -IMusashi/softfloat + +.PHONY: all +all: \ +emu_68000_alt \ +#emu_68000 + +# 68000 +emu_68000: emu_68000.o cpu_68000.o + ${CC} ${CFLAGS} -o $@ $^ + +emu_68000.o: cpu_68000.h + +cpu_68000.o: cpu_68000.h + +emu_68000_alt: \ +emu_68000_alt.o \ +m68kcpu.o \ +m68kops.o \ +softfloat.o + ${CC} ${CFLAGS} -o $@ $^ + +emu_68000_alt.o: \ +emu_68000.c \ +Musashi/m68k.h + ${CC} ${CFLAGS} ${ALT_68000_CFLAGS} -o $@ -c $< + +m68kcpu.o: Musashi/m68kcpu.c Musashi/m68kfpu.c + ${CC} ${CFLAGS} ${ALT_68000_CFLAGS} -o $@ -c $< + +m68kops.o: Musashi/m68kops.c + ${CC} ${CFLAGS} ${ALT_68000_CFLAGS} -o $@ -c $< + +softfloat.o: Musashi/softfloat/softfloat.c + ${CC} ${CFLAGS} ${ALT_68000_CFLAGS} -o $@ -c $< + +.PHONY: clean +clean: + rm -f *.o emu_68000 emu_68000_alt diff --git a/Musashi b/Musashi new file mode 160000 index 0000000..5b2b80f --- /dev/null +++ b/Musashi @@ -0,0 +1 @@ +Subproject commit 5b2b80f4b42ea251093583aac1d19033bb37f35c diff --git a/emu_68000.c b/emu_68000.c new file mode 100644 index 0000000..762038d --- /dev/null +++ b/emu_68000.c @@ -0,0 +1,369 @@ +#include +#include +#include +#include +#include +#include +#if ALT_BACKEND +#include "Musashi/m68k.h" +#else +#include "cpu_68000.h" +#endif + +#define EXCEPTION_TRAP_BASE 0x20 + +#define REG_TRACE 0 +#define MEM_TRACE 0 + +#define MEM_SIZE 0x8000 +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define MEM_SIZE_M1 (MEM_SIZE - 1) +#define MEM_SIZE_M2 (MEM_SIZE - 2) +#define MEM_SIZE_M4 (MEM_SIZE - 4) +#else +#define MEM_SIZE_M1 0 +#define MEM_SIZE_M2 0 +#define MEM_SIZE_M4 0 +#endif +union { + uint8_t b[MEM_SIZE]; + uint16_t w[MEM_SIZE >> 1]; + uint32_t l[MEM_SIZE >> 2]; +} mem; + +void load_s19(char *name) { + FILE *fp = fopen(name, "r"); + if (fp == NULL) { + perror(name); + exit(EXIT_FAILURE); + } + + char line[0x100]; + while (fgets(line, 0x100, fp)) { + for (char *p = line; *p; ++p) + if (*p == '\n') { + *p = 0; + break; + } + + // allow comments and concatenation + if (line[0] != 'S' || line[1] != '1') + continue; + + uint8_t buf[0x7f]; + int len; + for (len = 0; len < 0x7f; ++len) { + char *p = line + 2 + len * 2; + if (*p == 0) + break; + if (*p == '\n') { + *p = 0; + break; + } + uint8_t c = p[2]; + p[2] = 0; + + char *q; + buf[len] = (uint8_t)strtol(p, &q, 16); + p[2] = c; + if (q != p + 2) { + fprintf(stderr, "not hex byte: %s\n", p); + exit(EXIT_FAILURE); + } + } + + if (len < 4) { + fprintf(stderr, "too short: %s\n", line); + exit(EXIT_FAILURE); + } + + uint8_t checksum = 0; + for (int i = 0; i < len; ++i) + checksum += buf[i]; + if (checksum != 0xff) { + checksum -= 0xff + buf[len - 1]; + fprintf( + stderr, + "checksum %02x, should be %02x\n", + checksum, + buf[len - 1] + ); + exit(EXIT_FAILURE); + } + + if (len != buf[0] + 1) { + fprintf(stderr, "incorrect length: %s\n", line); + exit(EXIT_FAILURE); + } + len -= 4; + + int addr = (buf[1] << 8) | buf[2], end_addr; + end_addr = addr + len; + if (end_addr < addr || end_addr > MEM_SIZE) { + fprintf(stderr, "invalid load range: [0x%x, 0x%x)\n", addr, end_addr); + exit(EXIT_FAILURE); + } + for (int i = 0; i < len; ++i) + mem.b[(addr + i) ^ MEM_SIZE_M1] = buf[3 + i]; + } + + fclose(fp); +} + +int read_byte(void *context, int addr) { + int data; + switch (addr) { + case 0x10040: + return 3; // bit 0 = rdrf, bit 1 = tdre + case 0x10042: + data = getchar(); + switch (data) { + case '\n': + data = '\r'; + break; + case 0x7f: + data = '\b'; + break; +#if 0 // just keep sending 0xff at EOF + case EOF: + exit(EXIT_SUCCESS); + break; +#endif + } + break; + default: + if (addr < 0 || addr >= MEM_SIZE) { + fprintf(stderr, "invalid read byte: %06x\n", addr); + exit(EXIT_FAILURE); + } + data = mem.b[addr ^ MEM_SIZE_M1]; + break; + } +#if MEM_TRACE + fprintf(stderr, "addr=%06x rd=%02x\n", addr, data); +#endif + return data; +} + +int read_word(void *context, int addr) { + if (addr < 0 || addr >= MEM_SIZE || (addr & 1)) { + fprintf(stderr, "invalid read word: %06x\n", addr); + exit(EXIT_FAILURE); + } + int data = mem.w[(addr ^ MEM_SIZE_M2) >> 1]; +#if MEM_TRACE + fprintf(stderr, "addr=%06x rd=%04x\n", addr, data); +#endif + return data; +} + +int read_long(void *context, int addr) { + if (addr < 0 || addr >= MEM_SIZE - 2 || (addr & 1)) { + fprintf(stderr, "invalid read long: %06x\n", addr); + exit(EXIT_FAILURE); + } + int data = + (mem.w[(addr ^ MEM_SIZE_M2) >> 1] << 16) | + mem.w[((addr + 2) ^ MEM_SIZE_M2) >> 1]; +#if MEM_TRACE + fprintf(stderr, "addr=%06x rd=%08x\n", addr, data); +#endif + return data; +} + +void write_byte(void *context, int addr, int data) { +#if MEM_TRACE + fprintf(stderr, "addr=%06x wr=%02x\n", addr, data); +#endif + switch (addr) { + case 0x10040: + break; + case 0x10042: + data &= 0x7f; + switch (data) { + case '\r': + putchar('\n'); + break; + case '\n': + break; + default: + putchar(data); + break; + } + break; + default: + if (addr < 0 || addr >= MEM_SIZE) { + fprintf(stderr, "invalid write byte: %06x\n", addr); + exit(EXIT_FAILURE); + } + mem.b[addr ^ MEM_SIZE_M1] = data; + } +} + +void write_word(void *context, int addr, int data) { +#if MEM_TRACE + fprintf(stderr, "addr=%06x wr=%04x\n", addr, data); +#endif + if (addr < 0 || addr >= MEM_SIZE || (addr & 1)) { + fprintf(stderr, "invalid write word: %06x\n", addr); + exit(EXIT_FAILURE); + } + mem.w[(addr ^ MEM_SIZE_M2) >> 1] = data; +} + +void write_long(void *context, int addr, int data) { +#if MEM_TRACE + fprintf(stderr, "addr=%06x wr=%08x\n", addr, data); +#endif + if (addr < 0 || addr >= MEM_SIZE - 2 || (addr & 1)) { + fprintf(stderr, "invalid write long: %06x\n", addr); + exit(EXIT_FAILURE); + } + mem.w[(addr ^ MEM_SIZE_M2) >> 1] = data >> 16; + mem.w[((addr + 2) ^ MEM_SIZE_M2) >> 1] = data; +} + +#if ALT_BACKEND +unsigned int m68k_read_memory_8(unsigned int address) { + return read_byte(NULL, address); +} + +unsigned int m68k_read_memory_16(unsigned int address) { + return read_word(NULL, address); +} + +unsigned int m68k_read_memory_32(unsigned int address) { + return read_long(NULL, address); +} + +void m68k_write_memory_8(unsigned int address, unsigned int value) { + write_byte(NULL, address, value); +} + +void m68k_write_memory_16(unsigned int address, unsigned int value) { + write_word(NULL, address, value); +} + +void m68k_write_memory_32(unsigned int address, unsigned int value) { + write_long(NULL, address, value); +} + +/* Called when the CPU pulses the RESET line */ +void cpu_pulse_reset(void) +{ +// nmi_device_reset(); +// output_device_reset(); +// input_device_reset(); +} + +/* Called when the CPU changes the function code pins */ +void cpu_set_fc(unsigned int fc) +{ +// g_fc = fc; +} + +/* Called when the CPU acknowledges an interrupt */ +int cpu_irq_ack(int level) +{ +// switch(level) +// { +// case IRQ_NMI_DEVICE: +// return nmi_device_ack(); +// case IRQ_INPUT_DEVICE: +// return input_device_ack(); +// case IRQ_OUTPUT_DEVICE: +// return output_device_ack(); +// } +// return M68K_INT_ACK_SPURIOUS; + return 0; +} + +int m68k_service_trap(unsigned int vector) { + //fprintf(stderr, "trap %x\n", vector); + if (vector == EXCEPTION_TRAP_BASE + 0xe) // TBI68K bye command + exit(EXIT_SUCCESS); + return 0; +} +#endif + +int main(int argc, char **argv) { + if (argc < 2) { + printf("usage: %s image.s19\n", argv[0]); + exit(EXIT_FAILURE); + } + load_s19(argv[1]); + + // emulate Tutor firmware for TBI68K + mem.l[(0 ^ MEM_SIZE_M4) >> 2] = 0x8000; // initial SP + mem.l[(4 ^ MEM_SIZE_M4) >> 2] = 0x900; // initial PC + +#if ALT_BACKEND + m68k_init(); + m68k_set_cpu_type(M68K_CPU_TYPE_68000); + m68k_pulse_reset(); + + while (true) { +#if REG_TRACE + int sr = m68k_get_reg(NULL, M68K_REG_SR); + fprintf( + stderr, + "pc=%08x d0=%08x d1=%08x d2=%08x d3=%08x d4=%08x d5=%08x d6=%08x d7=%08x a0=%08x a1=%08x a2=%08x a3=%08x a4=%08x a5=%08x a6=%08x a7=%08x sr=%04x xf=%d nf=%d zf=%d vf=%d cf=%d\n", + m68k_get_reg(NULL, M68K_REG_PC), + m68k_get_reg(NULL, M68K_REG_D0), + m68k_get_reg(NULL, M68K_REG_D1), + m68k_get_reg(NULL, M68K_REG_D2), + m68k_get_reg(NULL, M68K_REG_D3), + m68k_get_reg(NULL, M68K_REG_D4), + m68k_get_reg(NULL, M68K_REG_D5), + m68k_get_reg(NULL, M68K_REG_D6), + m68k_get_reg(NULL, M68K_REG_D7), + m68k_get_reg(NULL, M68K_REG_A0), + m68k_get_reg(NULL, M68K_REG_A1), + m68k_get_reg(NULL, M68K_REG_A2), + m68k_get_reg(NULL, M68K_REG_A3), + m68k_get_reg(NULL, M68K_REG_A4), + m68k_get_reg(NULL, M68K_REG_A5), + m68k_get_reg(NULL, M68K_REG_A6), + m68k_get_reg(NULL, M68K_REG_A7), + sr, + (sr >> 4) & 1, + (sr >> 3) & 1, + (sr >> 2) & 1, + (sr >> 1) & 1, + sr & 1 + ); +#endif + + m68k_execute(1); + } +#else + struct cpu_68000 cpu; + cpu_68000_init(&cpu, read_byte, NULL, write_byte, NULL); + cpu_68000_reset(&cpu); + + while (true) { +#if REG_TRACE + fprintf( + stderr, + "pc=%04x a=%02x b=%02x s=%04x x=%04x p=%02x hf=%d if=%d nf=%d zf=%d vf=%d cf=%d\n", + cpu.regs.word.pc, + cpu.regs.byte.a, + cpu.regs.byte.b, + cpu.regs.word.s, + cpu.regs.word.x, + cpu.regs.byte.p, + cpu.regs.bit.hf, + cpu.regs.bit._if, + cpu.regs.bit.nf, + cpu.regs.bit.zf, + cpu.regs.bit.vf, + cpu.regs.bit.cf + ); +#endif + + cpu_68000_execute(&cpu); + } +#endif + + return 0; +} -- 2.34.1