emu_65c02 \
emu_65c02_alt \
6502_functional_test.ihx \
-65C02_extended_opcodes_test.ihx
+65C02_extended_opcodes_test.ihx \
+emu_z80_alt \
+zexall.ihx \
+zexdoc.ihx
+#emu_z80
emu_65c02: emu_65c02.o cpu_65c02.o
${CC} ${CFLAGS} -o $@ $^
${CC} ${CFLAGS} -o $@ $^
emu_65c02_alt.o: emu_65c02.c vrEmu6502/src/vrEmu6502.h
- ${CC} ${CFLAGS} -DVREMU6502=1 -DVR_6502_EMU_STATIC=1 -o $@ -c $<
+ ${CC} ${CFLAGS} -DALT_BACKEND=1 -DVR_6502_EMU_STATIC=1 -o $@ -c $<
vrEmu6502.o: vrEmu6502/src/vrEmu6502.c vrEmu6502/src/vrEmu6502.h
${CC} ${CFLAGS} -DVR_6502_EMU_STATIC=1 -c $<
./entry_point.py 0x400 __temp__.ihx $@
rm __temp__.ihx
+emu_z80: emu_z80.o cpu_z80.o
+ ${CC} ${CFLAGS} -o $@ $^
+
+emu_z80.o: cpu_z80.h
+
+cpu_z80.o: cpu_z80.h
+
+emu_z80_alt: emu_z80_alt.o z80.o
+ ${CC} ${CFLAGS} -o $@ $^
+
+emu_z80_alt.o: emu_z80.c z80/z80.h
+ ${CC} ${CFLAGS} -DALT_BACKEND=1 -o $@ -c $<
+
+z80.o: z80/z80.c z80/z80.h
+ ${CC} ${CFLAGS} -c $<
+
+zexall.ihx: ZEXALL/zexall.com
+ ${BIN2HEX} --offset=0x100 $< __temp__.ihx
+ ./entry_point.py 0x100 __temp__.ihx $@
+ rm __temp__.ihx
+
+zexdoc.ihx: ZEXALL/zexdoc.com
+ ${BIN2HEX} --offset=0x100 $< __temp__.ihx
+ ./entry_point.py 0x100 __temp__.ihx $@
+ rm __temp__.ihx
+
.PHONY: clean
clean:
- rm -f *.ihx *.o emu_65c02
+ rm -f *.ihx *.o emu_65c02 emu_65c02_alt emu_z80 emu_z80_alt
--- /dev/null
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if ALT_BACKEND
+#include "z80/z80.h"
+#else
+#include "cpu_z80.h"
+#endif
+
+#define REG_TRACE 0
+#define MEM_TRACE 0
+#define IO_TRACE 0
+
+#define MEM_SIZE 0x10000
+uint8_t mem[MEM_SIZE];
+
+// read and write are separated for I/O
+#define IO_SIZE 0x100
+uint8_t io_read[IO_SIZE];
+uint8_t io_write[IO_SIZE];
+
+int load_ihx(char *name) {
+ FILE *fp = fopen(name, "r");
+ if (fp == NULL) {
+ perror(name);
+ exit(EXIT_FAILURE);
+ }
+
+ int base = 0, entry_point = 0;
+ bool had_eof = false;
+ char line[0x100];
+ while (fgets(line, 0x100, fp)) {
+ for (char *p = line; *p; ++p)
+ if (*p == '\n') {
+ *p = 0;
+ break;
+ }
+
+ if (had_eof) {
+ fprintf(stderr, "garbage after EOF record: %s\n", line);
+ exit(EXIT_FAILURE);
+ }
+
+ if (line[0] != ':') {
+ fprintf(stderr, "require colon: %s\n", line);
+ exit(EXIT_FAILURE);
+ }
+
+ uint8_t buf[0x7f];
+ int len;
+ for (len = 0; len < 0x7f; ++len) {
+ char *p = line + 1 + 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 == 0) {
+ fprintf(stderr, "empty line: %s\n", line);
+ exit(EXIT_FAILURE);
+ }
+
+ uint8_t checksum = 0;
+ for (int i = 0; i < len; ++i)
+ checksum += buf[i];
+ if (checksum) {
+ checksum -= buf[len - 1];
+ fprintf(
+ stderr,
+ "checksum %02x, should be %02x\n",
+ checksum,
+ buf[len - 1]
+ );
+ exit(EXIT_FAILURE);
+ }
+
+ len -= 5;
+ if (len != buf[0]) {
+ fprintf(stderr, "incorrect length: %s\n", line);
+ exit(EXIT_FAILURE);
+ }
+
+ int addr = (buf[1] << 8) | buf[2], end_addr;
+ switch (buf[3]) {
+ case 0:
+ addr += base;
+ 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);
+ }
+ memcpy(mem + addr, buf + 4, len);
+ break;
+ case 1:
+ had_eof = true;
+ break;
+ case 4:
+ if (len < 2) {
+ fprintf(stderr, "invalid extended linear address record: %s\n", line);
+ exit(EXIT_FAILURE);
+ }
+ base = (buf[4] << 24) | (buf[5] << 16);
+ break;
+ case 5:
+ if (len < 4) {
+ fprintf(stderr, "invalid start linear address record: %s\n", line);
+ exit(EXIT_FAILURE);
+ }
+ entry_point = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+ break;
+ default:
+ fprintf(stderr, "unknown record type: 0x%x\n", buf[3]);
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (!had_eof) {
+ fprintf(stderr, "no EOF record\n");
+ exit(EXIT_FAILURE);
+ }
+
+ fclose(fp);
+ return entry_point;
+}
+
+int bdos(int c, int de) {
+ switch (c) {
+ case 2:
+ de &= 0xff;
+ if (de != '\r')
+ putchar(de);
+ break;
+ case 9:
+ while (mem[de] != '$') {
+ if (mem[de] != '\r')
+ putchar(mem[de]);
+ de = (de + 1) & 0xffff;
+ }
+ break;
+ default:
+ fprintf(stderr, "unimplemented BDOS call 0x%02x\n", c);
+ exit(EXIT_FAILURE);
+ }
+ return de;
+}
+
+int mem_read_byte(int addr) {
+#if MEM_TRACE
+ int data = mem[addr];
+ printf("mem_addr=%04x rd=%02x\n", addr, data);
+ return data;
+#else
+ return mem[addr];
+#endif
+}
+
+void mem_write_byte(int addr, int data) {
+#if MEM_TRACE
+ printf("mem_addr=%04x wr=%02x\n", addr, data);
+#endif
+ mem[addr] = data;
+}
+
+int io_read_byte(int addr) {
+#if IO_TRACE
+ int data = io_read[addr];
+ printf("io_addr=%04x rd=%02x\n", addr, data);
+ return data;
+#else
+ return io_read[addr];
+#endif
+}
+
+void io_write_byte(int addr, int data) {
+#if IO_TRACE
+ printf("io_addr=%04x wr=%02x\n", addr, data);
+#endif
+ io_write[addr] = data;
+}
+
+#if ALT_BACKEND
+uint8_t rb(void *userdata, uint16_t addr) {
+ return mem_read_byte(addr);
+}
+
+void wb(void *userdata, uint16_t addr, uint8_t val) {
+ mem_write_byte(addr, val);
+}
+
+uint8_t in(z80 *const z, uint8_t port) {
+ return io_read_byte(port);
+}
+
+void out(z80 *const z, uint8_t port, uint8_t val) {
+ io_write_byte(port, val);
+}
+#endif
+
+int main(int argc, char **argv) {
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s image.ihx\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ int entry_point = load_ihx(argv[1]);
+
+ mem[5] = 0xc9; // ret, for bdos emulation
+
+#if ALT_BACKEND
+ z80 cpu;
+ z80_init(&cpu);
+ cpu.read_byte = rb;
+ cpu.write_byte = wb;
+ cpu.port_in = in;
+ cpu.port_out = out;
+ cpu.pc = entry_point;
+
+ while (true) {
+#if REG_TRACE
+ printf(
+ "pc=%04x af=%04x bc=%04x de=%04x hl=%04x sp=%04x ix=%04x iy=%04x cf=%d nf=%d pf=%d hf=%d zf=%d sf=%d\n",
+ cpu.pc,
+ cpu.cf |
+ (cpu.nf << 1) |
+ (cpu.pf << 2) |
+ (cpu.hf << 4) |
+ (cpu.zf << 6) |
+ (cpu.sf << 7) |
+ (cpu.a << 8),
+ cpu.c | (cpu.b << 8),
+ cpu.e | (cpu.d << 8),
+ cpu.l | (cpu.h << 8),
+ cpu.sp,
+ cpu.ix,
+ cpu.iy,
+ cpu.cf,
+ cpu.nf,
+ cpu.pf,
+ cpu.hf,
+ cpu.zf,
+ cpu.sf
+ );
+#endif
+
+ if (cpu.pc == 0) {
+ putchar('\n');
+ break;
+ }
+ if (cpu.pc == 5) {
+ int de = cpu.e | (cpu.d << 8);
+ de = bdos(cpu.c, de);
+ cpu.e = de & 0xff;
+ cpu.d = de >> 8;
+ }
+ z80_step(&cpu, 1);
+ }
+#else
+ struct cpu_z80 cpu;
+ memset(&cpu, 0, sizeof(struct cpu_z80));
+ cpu.pc = entry_point;
+ cpu.regs[CPU_Z80_REG_S] = 0xff;
+ cpu.regs[CPU_Z80_REG_P] = 0x30;
+ cpu.mem_read_byte = mem_read_byte;
+ cpu.mem_write_byte = mem_write_byte;
+
+ while (true) {
+#if REG_TRACE
+ printf(
+ "pc=%04x a=%02x x=%02x y=%02x s=%02x p=%02x c=%d z=%d i=%d d=%d v=%d n=%d\n",
+ cpu.pc,
+ cpu.regs[CPU_Z80_REG_A],
+ cpu.regs[CPU_Z80_REG_X],
+ cpu.regs[CPU_Z80_REG_Y],
+ cpu.regs[CPU_Z80_REG_S],
+ cpu.regs[CPU_Z80_REG_P],
+ (cpu.regs[CPU_Z80_REG_P] >> CPU_Z80_BIT_C) & 1,
+ (cpu.regs[CPU_Z80_REG_P] >> CPU_Z80_BIT_Z) & 1,
+ (cpu.regs[CPU_Z80_REG_P] >> CPU_Z80_BIT_I) & 1,
+ (cpu.regs[CPU_Z80_REG_P] >> CPU_Z80_BIT_D) & 1,
+ (cpu.regs[CPU_Z80_REG_P] >> CPU_Z80_BIT_V) & 1,
+ (cpu.regs[CPU_Z80_REG_P] >> CPU_Z80_BIT_N) & 1
+ );
+#endif
+
+ int pc = cpu.pc;
+ cpu_z80_execute(&cpu);
+ if (pc == cpu.pc) {
+ printf("hung at %04x\n", pc);
+ break;
+ }
+ }
+#endif
+
+ return 0;
+}