From a4491261e507d8e99176e21403967aba9e88f41b Mon Sep 17 00:00:00 2001 From: Nick Downing Date: Sun, 11 Aug 2024 01:17:32 +1000 Subject: [PATCH] In /emu_65c02, add 65c02 emulator from multi_emu.git commit 900d9fa3 instead of vrEmu6502 --- .gitmodules | 3 - emu_65c02/Makefile | 4 +- emu_65c02/cpu_65c02.c | 879 ++++++++++++++++++++++++++++++++++++++++++ emu_65c02/cpu_65c02.h | 535 +++++++++++++++++++++++++ emu_65c02/emu_65c02.c | 153 +++----- 5 files changed, 1472 insertions(+), 102 deletions(-) create mode 100644 emu_65c02/cpu_65c02.c create mode 100644 emu_65c02/cpu_65c02.h diff --git a/.gitmodules b/.gitmodules index 6b0cb37..e026680 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "asxv5pxx"] path = asxv5pxx url = https://github.com/nickd4/asxv5pxx.git -[submodule "emu_65c02/vrEmu6502"] - path = emu_65c02/vrEmu6502 - url = https://github.com/nickd4/vrEmu6502.git [submodule "dos33fsprogs"] path = dos33fsprogs url = https://github.com/nickd4/dos33fsprogs.git diff --git a/emu_65c02/Makefile b/emu_65c02/Makefile index e2d6edb..1c2b957 100644 --- a/emu_65c02/Makefile +++ b/emu_65c02/Makefile @@ -1,4 +1,4 @@ -CFLAGS=-g -Wall -O3 -DVR_6502_EMU_STATIC=1 +CFLAGS=-g -Wall -Wno-attributes -Wno-unused-function -O3 LDFLAGS=-g .PHONY: all @@ -25,7 +25,7 @@ cg_default: #${MAKE} ${MAKEFLAGS} -C $@ ${MAKE} -C $@ -emu_65c02: emu_65c02.o stty_sane.o vrEmu6502/src/vrEmu6502.o +emu_65c02: emu_65c02.o cpu_65c02.o stty_sane.o ${CC} ${LDFLAGS} -o $@ $^ -lSDL2 -lm emu_65c02.o: cg_default diff --git a/emu_65c02/cpu_65c02.c b/emu_65c02/cpu_65c02.c new file mode 100644 index 0000000..8789d63 --- /dev/null +++ b/emu_65c02/cpu_65c02.c @@ -0,0 +1,879 @@ +#include +#include +#include +#include "cpu_65c02.h" + +// initialization +void cpu_65c02_init( + struct cpu_65c02 *self, + uint8_t (*read_byte)(void *context, uint16_t addr), + void *read_byte_context, + void (*write_byte)(void *context, uint16_t addr, uint8_t data), + void *write_byte_context +) { + memset(self, 0, sizeof(struct cpu_65c02)); + self->regs.byte.p = 0x30; // unused bits are hard coded to 1 + self->read_byte = read_byte; + self->read_byte_context = read_byte_context; + self->write_byte = write_byte; + self->write_byte_context = write_byte_context; +} + +void cpu_65c02_reset(struct cpu_65c02 *self) { + self->regs.word.pc = cpu_65c02_read_word(self, CPU_65C02_RESET_VECTOR); + self->regs.byte.iflags = 0; + self->regs.bit._if = true; + self->regs.bit.df = false; +} + +// instruction decode +void cpu_65c02_execute(struct cpu_65c02 *self) { + if (self->regs.bit.stp_flag) { + ++self->cycles; + return; + } + + if (self->regs.bit.nmi_pending) { + self->regs.bit.nmi_pending = false; + self->regs.bit.wai_flag = false; + cpu_65c02_irq(self, false, CPU_65C02_NMI_VECTOR); + return; + } + + if (self->regs.bit.irq_pending) { + if (self->regs.bit._if == 0) { + self->regs.bit.irq_pending = false; + self->regs.bit.wai_flag = false; + cpu_65c02_irq(self, false, CPU_65C02_IRQ_VECTOR); + return; + } + if (self->regs.bit.wai_flag) { + self->regs.bit.irq_pending = false; + self->regs.bit.wai_flag = false; + } + } + else if (self->regs.bit.wai_flag) { + ++self->cycles; + return; + } + + switch (cpu_65c02_fetch_byte(self)) { + case 0x00: + ++self->regs.word.pc; + cpu_65c02_irq(self, true, CPU_65C02_IRQ_VECTOR); + break; + case 0x01: + cpu_65c02_ora(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed_indirect(self, self->regs.byte.x))); + break; + case 0x02: + cpu_65c02_illegal_opcode22(self); + break; + case 0x03: + cpu_65c02_illegal_opcode11(self); + break; + case 0x04: + cpu_65c02_tsb(self, cpu_65c02_ea_zero_page(self)); + break; + case 0x05: + cpu_65c02_ora(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self))); + break; + case 0x06: + cpu_65c02_asl(self, cpu_65c02_ea_zero_page(self)); + break; + case 0x07: + cpu_65c02_rmb(self, 0, cpu_65c02_ea_zero_page(self)); + break; + case 0x08: + cpu_65c02_ph(self, self->regs.byte.p); + break; + case 0x09: + cpu_65c02_ora(self, cpu_65c02_fetch_byte(self)); + break; + case 0x0a: + cpu_65c02_asl(self, CPU_65C02_EA_A); + break; + case 0x0b: + cpu_65c02_illegal_opcode11(self); + break; + case 0x0c: + cpu_65c02_tsb(self, cpu_65c02_ea_absolute(self)); + break; + case 0x0d: + cpu_65c02_ora(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self))); + break; + case 0x0e: + cpu_65c02_asl(self, cpu_65c02_ea_absolute(self)); + break; + case 0x0f: + { + int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)); + cpu_65c02_bra(self, !(rvalue & 1), cpu_65c02_ea_relative(self)); + } + break; + case 0x10: + cpu_65c02_bra(self, !self->regs.bit.nf, cpu_65c02_ea_relative(self)); + break; + case 0x11: + cpu_65c02_ora(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect_indexed(self, self->regs.byte.y))); + break; + case 0x12: + cpu_65c02_ora(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect(self))); + break; + case 0x13: + cpu_65c02_illegal_opcode11(self); + break; + case 0x14: + cpu_65c02_trb(self, cpu_65c02_ea_zero_page(self)); + break; + case 0x15: + cpu_65c02_ora(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x))); + break; + case 0x16: + cpu_65c02_asl(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x)); + break; + case 0x17: + cpu_65c02_rmb(self, 1, cpu_65c02_ea_zero_page(self)); + break; + case 0x18: + cpu_65c02_cl(self, CPU_65C02_REG_P_BIT_C); + break; + case 0x19: + cpu_65c02_ora(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y))); + break; + case 0x1a: + cpu_65c02_inc(self, CPU_65C02_EA_A); + break; + case 0x1b: + cpu_65c02_illegal_opcode11(self); + break; + case 0x1c: + cpu_65c02_trb(self, cpu_65c02_ea_absolute(self)); + break; + case 0x1d: + cpu_65c02_ora(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x))); + break; + case 0x1e: + cpu_65c02_asl(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x)); + break; + case 0x1f: + { + int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)); + cpu_65c02_bra(self, !((rvalue >> 1) & 1), cpu_65c02_ea_relative(self)); + } + break; + case 0x20: + cpu_65c02_jsr(self, cpu_65c02_ea_absolute(self)); + break; + case 0x21: + cpu_65c02_and(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed_indirect(self, self->regs.byte.x))); + break; + case 0x22: + cpu_65c02_illegal_opcode22(self); + break; + case 0x23: + cpu_65c02_illegal_opcode11(self); + break; + case 0x24: + cpu_65c02_bit(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self))); + break; + case 0x25: + cpu_65c02_and(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self))); + break; + case 0x26: + cpu_65c02_rol(self, cpu_65c02_ea_zero_page(self)); + break; + case 0x27: + cpu_65c02_rmb(self, 2, cpu_65c02_ea_zero_page(self)); + break; + case 0x28: + cpu_65c02_plp(self); + break; + case 0x29: + cpu_65c02_and(self, cpu_65c02_fetch_byte(self)); + break; + case 0x2a: + cpu_65c02_rol(self, CPU_65C02_EA_A); + break; + case 0x2b: + cpu_65c02_illegal_opcode11(self); + break; + case 0x2c: + cpu_65c02_bit(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self))); + break; + case 0x2d: + cpu_65c02_and(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self))); + break; + case 0x2e: + cpu_65c02_rol(self, cpu_65c02_ea_absolute(self)); + break; + case 0x2f: + { + int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)); + cpu_65c02_bra(self, !((rvalue >> 2) & 1), cpu_65c02_ea_relative(self)); + } + break; + case 0x30: + cpu_65c02_bra(self, self->regs.bit.nf, cpu_65c02_ea_relative(self)); + break; + case 0x31: + cpu_65c02_and(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect_indexed(self, self->regs.byte.y))); + break; + case 0x32: + cpu_65c02_and(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect(self))); + break; + case 0x33: + cpu_65c02_illegal_opcode11(self); + break; + case 0x34: + cpu_65c02_bit(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x))); + break; + case 0x35: + cpu_65c02_and(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x))); + break; + case 0x36: + cpu_65c02_rol(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x)); + break; + case 0x37: + cpu_65c02_rmb(self, 3, cpu_65c02_ea_zero_page(self)); + break; + case 0x38: + cpu_65c02_se(self, CPU_65C02_REG_P_BIT_C); + break; + case 0x39: + cpu_65c02_and(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y))); + break; + case 0x3a: + cpu_65c02_dec(self, CPU_65C02_EA_A); + break; + case 0x3b: + cpu_65c02_illegal_opcode11(self); + break; + case 0x3c: + cpu_65c02_bit(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x))); + break; + case 0x3d: + cpu_65c02_and(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x))); + break; + case 0x3e: + cpu_65c02_rol(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x)); + break; + case 0x3f: + { + int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)); + cpu_65c02_bra(self, !((rvalue >> 3) & 1), cpu_65c02_ea_relative(self)); + } + break; + case 0x40: + cpu_65c02_rti(self); + break; + case 0x41: + cpu_65c02_eor(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed_indirect(self, self->regs.byte.x))); + break; + case 0x42: + cpu_65c02_illegal_opcode22(self); + break; + case 0x43: + cpu_65c02_illegal_opcode11(self); + break; + case 0x44: + cpu_65c02_illegal_opcode23(self); + break; + case 0x45: + cpu_65c02_eor(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self))); + break; + case 0x46: + cpu_65c02_lsr(self, cpu_65c02_ea_zero_page(self)); + break; + case 0x47: + cpu_65c02_rmb(self, 4, cpu_65c02_ea_zero_page(self)); + break; + case 0x48: + cpu_65c02_ph(self, self->regs.byte.a); + break; + case 0x49: + cpu_65c02_eor(self, cpu_65c02_fetch_byte(self)); + break; + case 0x4a: + cpu_65c02_lsr(self, CPU_65C02_EA_A); + break; + case 0x4b: + cpu_65c02_illegal_opcode11(self); + break; + case 0x4c: + cpu_65c02_jmp(self, cpu_65c02_ea_absolute(self)); + break; + case 0x4d: + cpu_65c02_eor(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self))); + break; + case 0x4e: + cpu_65c02_lsr(self, cpu_65c02_ea_absolute(self)); + break; + case 0x4f: + { + int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)); + cpu_65c02_bra(self, !((rvalue >> 4) & 1), cpu_65c02_ea_relative(self)); + } + break; + case 0x50: + cpu_65c02_bra(self, !self->regs.bit.vf, cpu_65c02_ea_relative(self)); + break; + case 0x51: + cpu_65c02_eor(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect_indexed(self, self->regs.byte.y))); + break; + case 0x52: + cpu_65c02_eor(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect(self))); + break; + case 0x53: + cpu_65c02_illegal_opcode11(self); + break; + case 0x54: + cpu_65c02_illegal_opcode24(self); + break; + case 0x55: + cpu_65c02_eor(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x))); + break; + case 0x56: + cpu_65c02_lsr(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x)); + break; + case 0x57: + cpu_65c02_rmb(self, 5, cpu_65c02_ea_zero_page(self)); + break; + case 0x58: + cpu_65c02_cl(self, CPU_65C02_REG_P_BIT_I); + break; + case 0x59: + cpu_65c02_eor(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y))); + break; + case 0x5a: + cpu_65c02_ph(self, self->regs.byte.y); + break; + case 0x5b: + cpu_65c02_illegal_opcode11(self); + break; + case 0x5c: + cpu_65c02_illegal_opcode38(self); + break; + case 0x5d: + cpu_65c02_eor(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x))); + break; + case 0x5e: + cpu_65c02_lsr(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x)); + break; + case 0x5f: + { + int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)); + cpu_65c02_bra(self, !((rvalue >> 5) & 1), cpu_65c02_ea_relative(self)); + } + break; + case 0x60: + cpu_65c02_rts(self); + break; + case 0x61: + cpu_65c02_adc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed_indirect(self, self->regs.byte.x))); + break; + case 0x62: + cpu_65c02_illegal_opcode22(self); + break; + case 0x63: + cpu_65c02_illegal_opcode11(self); + break; + case 0x64: + cpu_65c02_st(self, 0, cpu_65c02_ea_zero_page(self)); + break; + case 0x65: + cpu_65c02_adc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self))); + break; + case 0x66: + cpu_65c02_ror(self, cpu_65c02_ea_zero_page(self)); + break; + case 0x67: + cpu_65c02_rmb(self, 6, cpu_65c02_ea_zero_page(self)); + break; + case 0x68: + cpu_65c02_pl(self, CPU_65C02_EA_A); + break; + case 0x69: + cpu_65c02_adc(self, cpu_65c02_fetch_byte(self)); + break; + case 0x6a: + cpu_65c02_ror(self, CPU_65C02_EA_A); + break; + case 0x6b: + cpu_65c02_illegal_opcode11(self); + break; + case 0x6c: + cpu_65c02_jmp(self, cpu_65c02_ea_absolute_indirect(self)); + break; + case 0x6d: + cpu_65c02_adc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self))); + break; + case 0x6e: + cpu_65c02_ror(self, cpu_65c02_ea_absolute(self)); + break; + case 0x6f: + { + int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)); + cpu_65c02_bra(self, !((rvalue >> 6) & 1), cpu_65c02_ea_relative(self)); + } + break; + case 0x70: + cpu_65c02_bra(self, self->regs.bit.vf, cpu_65c02_ea_relative(self)); + break; + case 0x71: + cpu_65c02_adc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect_indexed(self, self->regs.byte.y))); + break; + case 0x72: + cpu_65c02_adc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect(self))); + break; + case 0x73: + cpu_65c02_illegal_opcode11(self); + break; + case 0x74: + cpu_65c02_st(self, 0, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x)); + break; + case 0x75: + cpu_65c02_adc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x))); + break; + case 0x76: + cpu_65c02_ror(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x)); + break; + case 0x77: + cpu_65c02_rmb(self, 7, cpu_65c02_ea_zero_page(self)); + break; + case 0x78: + cpu_65c02_se(self, CPU_65C02_REG_P_BIT_I); + break; + case 0x79: + cpu_65c02_adc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y))); + break; + case 0x7a: + cpu_65c02_pl(self, CPU_65C02_EA_Y); + break; + case 0x7b: + cpu_65c02_illegal_opcode11(self); + break; + case 0x7c: + cpu_65c02_jmp(self, cpu_65c02_ea_absolute_indexed_indirect(self, self->regs.byte.x)); + break; + case 0x7d: + cpu_65c02_adc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x))); + break; + case 0x7e: + cpu_65c02_ror(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x)); + break; + case 0x7f: + { + int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)); + cpu_65c02_bra(self, !((rvalue >> 7) & 1), cpu_65c02_ea_relative(self)); + } + break; + case 0x80: + cpu_65c02_bra(self, true, cpu_65c02_ea_relative(self)); + break; + case 0x81: + cpu_65c02_st(self, self->regs.byte.a, cpu_65c02_ea_zero_page_indexed_indirect(self, self->regs.byte.x)); + break; + case 0x82: + cpu_65c02_illegal_opcode22(self); + break; + case 0x83: + cpu_65c02_illegal_opcode11(self); + break; + case 0x84: + cpu_65c02_st(self, self->regs.byte.y, cpu_65c02_ea_zero_page(self)); + break; + case 0x85: + cpu_65c02_st(self, self->regs.byte.a, cpu_65c02_ea_zero_page(self)); + break; + case 0x86: + cpu_65c02_st(self, self->regs.byte.x, cpu_65c02_ea_zero_page(self)); + break; + case 0x87: + cpu_65c02_smb(self, 0, cpu_65c02_ea_zero_page(self)); + break; + case 0x88: + cpu_65c02_dec(self, CPU_65C02_EA_Y); + break; + case 0x89: + cpu_65c02_bit_imm(self, cpu_65c02_fetch_byte(self)); + break; + case 0x8a: + cpu_65c02_ld(self, CPU_65C02_EA_A, self->regs.byte.x); + break; + case 0x8b: + cpu_65c02_illegal_opcode11(self); + break; + case 0x8c: + cpu_65c02_st(self, self->regs.byte.y, cpu_65c02_ea_absolute(self)); + break; + case 0x8d: + cpu_65c02_st(self, self->regs.byte.a, cpu_65c02_ea_absolute(self)); + break; + case 0x8e: + cpu_65c02_st(self, self->regs.byte.x, cpu_65c02_ea_absolute(self)); + break; + case 0x8f: + { + int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)); + cpu_65c02_bra(self, rvalue & 1, cpu_65c02_ea_relative(self)); + } + break; + case 0x90: + cpu_65c02_bra(self, !self->regs.bit.cf, cpu_65c02_ea_relative(self)); + break; + case 0x91: + cpu_65c02_st(self, self->regs.byte.a, cpu_65c02_ea_zero_page_indirect_indexed(self, self->regs.byte.y)); + break; + case 0x92: + cpu_65c02_st(self, self->regs.byte.a, cpu_65c02_ea_zero_page_indirect(self)); + break; + case 0x93: + cpu_65c02_illegal_opcode11(self); + break; + case 0x94: + cpu_65c02_st(self, self->regs.byte.y, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x)); + break; + case 0x95: + cpu_65c02_st(self, self->regs.byte.a, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x)); + break; + case 0x96: + cpu_65c02_st(self, self->regs.byte.x, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.y)); + break; + case 0x97: + cpu_65c02_smb(self, 1, cpu_65c02_ea_zero_page(self)); + break; + case 0x98: + cpu_65c02_ld(self, CPU_65C02_EA_A, self->regs.byte.y); + break; + case 0x99: + cpu_65c02_st(self, self->regs.byte.a, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y)); + break; + case 0x9a: + cpu_65c02_st(self, self->regs.byte.x, CPU_65C02_EA_S); + break; + case 0x9b: + cpu_65c02_illegal_opcode11(self); + break; + case 0x9c: + cpu_65c02_st(self, 0, cpu_65c02_ea_absolute(self)); + break; + case 0x9d: + cpu_65c02_st(self, self->regs.byte.a, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x)); + break; + case 0x9e: + cpu_65c02_st(self, 0, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x)); + break; + case 0x9f: + { + int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)); + cpu_65c02_bra(self, (rvalue >> 1) & 1, cpu_65c02_ea_relative(self)); + } + break; + case 0xa0: + cpu_65c02_ld(self, CPU_65C02_EA_Y, cpu_65c02_fetch_byte(self)); + break; + case 0xa1: + cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed_indirect(self, self->regs.byte.x))); + break; + case 0xa2: + cpu_65c02_ld(self, CPU_65C02_EA_X, cpu_65c02_fetch_byte(self)); + break; + case 0xa3: + cpu_65c02_illegal_opcode11(self); + break; + case 0xa4: + cpu_65c02_ld(self, CPU_65C02_EA_Y, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self))); + break; + case 0xa5: + cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self))); + break; + case 0xa6: + cpu_65c02_ld(self, CPU_65C02_EA_X, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self))); + break; + case 0xa7: + cpu_65c02_smb(self, 2, cpu_65c02_ea_zero_page(self)); + break; + case 0xa8: + cpu_65c02_ld(self, CPU_65C02_EA_Y, self->regs.byte.a); + break; + case 0xa9: + cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_fetch_byte(self)); + break; + case 0xaa: + cpu_65c02_ld(self, CPU_65C02_EA_X, self->regs.byte.a); + break; + case 0xab: + cpu_65c02_illegal_opcode11(self); + break; + case 0xac: + cpu_65c02_ld(self, CPU_65C02_EA_Y, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self))); + break; + case 0xad: + cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self))); + break; + case 0xae: + cpu_65c02_ld(self, CPU_65C02_EA_X, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self))); + break; + case 0xaf: + { + int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)); + cpu_65c02_bra(self, (rvalue >> 2) & 1, cpu_65c02_ea_relative(self)); + } + break; + case 0xb0: + cpu_65c02_bra(self, self->regs.bit.cf, cpu_65c02_ea_relative(self)); + break; + case 0xb1: + cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect_indexed(self, self->regs.byte.y))); + break; + case 0xb2: + cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect(self))); + break; + case 0xb3: + cpu_65c02_illegal_opcode11(self); + break; + case 0xb4: + cpu_65c02_ld(self, CPU_65C02_EA_Y, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x))); + break; + case 0xb5: + cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x))); + break; + case 0xb6: + cpu_65c02_ld(self, CPU_65C02_EA_X, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.y))); + break; + case 0xb7: + cpu_65c02_smb(self, 3, cpu_65c02_ea_zero_page(self)); + break; + case 0xb8: + cpu_65c02_cl(self, CPU_65C02_REG_P_BIT_V); + break; + case 0xb9: + cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y))); + break; + case 0xba: + cpu_65c02_ld(self, CPU_65C02_EA_X, self->regs.byte.s); + break; + case 0xbb: + cpu_65c02_illegal_opcode11(self); + break; + case 0xbc: + cpu_65c02_ld(self, CPU_65C02_EA_Y, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x))); + break; + case 0xbd: + cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x))); + break; + case 0xbe: + cpu_65c02_ld(self, CPU_65C02_EA_X, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y))); + break; + case 0xbf: + { + int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)); + cpu_65c02_bra(self, (rvalue >> 3) & 1, cpu_65c02_ea_relative(self)); + } + break; + case 0xc0: + cpu_65c02_cmp(self, self->regs.byte.y, cpu_65c02_fetch_byte(self)); + break; + case 0xc1: + cpu_65c02_cmp(self, self->regs.byte.a, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed_indirect(self, self->regs.byte.x))); + break; + case 0xc2: + cpu_65c02_illegal_opcode22(self); + break; + case 0xc3: + cpu_65c02_illegal_opcode11(self); + break; + case 0xc4: + cpu_65c02_cmp(self, self->regs.byte.y, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self))); + break; + case 0xc5: + cpu_65c02_cmp(self, self->regs.byte.a, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self))); + break; + case 0xc6: + cpu_65c02_dec(self, cpu_65c02_ea_zero_page(self)); + break; + case 0xc7: + cpu_65c02_smb(self, 4, cpu_65c02_ea_zero_page(self)); + break; + case 0xc8: + cpu_65c02_inc(self, CPU_65C02_EA_Y); + break; + case 0xc9: + cpu_65c02_cmp(self, self->regs.byte.a, cpu_65c02_fetch_byte(self)); + break; + case 0xca: + cpu_65c02_dec(self, CPU_65C02_EA_X); + break; + case 0xcb: + cpu_65c02_wai(self); + break; + case 0xcc: + cpu_65c02_cmp(self, self->regs.byte.y, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self))); + break; + case 0xcd: + cpu_65c02_cmp(self, self->regs.byte.a, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self))); + break; + case 0xce: + cpu_65c02_dec(self, cpu_65c02_ea_absolute(self)); + break; + case 0xcf: + { + int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)); + cpu_65c02_bra(self, (rvalue >> 4) & 1, cpu_65c02_ea_relative(self)); + } + break; + case 0xd0: + cpu_65c02_bra(self, !self->regs.bit.zf, cpu_65c02_ea_relative(self)); + break; + case 0xd1: + cpu_65c02_cmp(self, self->regs.byte.a, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect_indexed(self, self->regs.byte.y))); + break; + case 0xd2: + cpu_65c02_cmp(self, self->regs.byte.a, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect(self))); + break; + case 0xd3: + cpu_65c02_illegal_opcode11(self); + break; + case 0xd4: + cpu_65c02_illegal_opcode24(self); + break; + case 0xd5: + cpu_65c02_cmp(self, self->regs.byte.a, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x))); + break; + case 0xd6: + cpu_65c02_dec(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x)); + break; + case 0xd7: + cpu_65c02_smb(self, 5, cpu_65c02_ea_zero_page(self)); + break; + case 0xd8: + cpu_65c02_cl(self, CPU_65C02_REG_P_BIT_D); + break; + case 0xd9: + cpu_65c02_cmp(self, self->regs.byte.a, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y))); + break; + case 0xda: + cpu_65c02_ph(self, self->regs.byte.x); + break; + case 0xdb: + cpu_65c02_illegal_opcode11(self); + break; + case 0xdc: + cpu_65c02_illegal_opcode34(self); + break; + case 0xdd: + cpu_65c02_cmp(self, self->regs.byte.a, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x))); + break; + case 0xde: + cpu_65c02_dec(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x)); + break; + case 0xdf: + { + int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)); + cpu_65c02_bra(self, (rvalue >> 5) & 1, cpu_65c02_ea_relative(self)); + } + break; + case 0xe0: + cpu_65c02_cmp(self, self->regs.byte.x, cpu_65c02_fetch_byte(self)); + break; + case 0xe1: + cpu_65c02_sbc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed_indirect(self, self->regs.byte.x))); + break; + case 0xe2: + cpu_65c02_illegal_opcode22(self); + break; + case 0xe3: + cpu_65c02_illegal_opcode11(self); + break; + case 0xe4: + cpu_65c02_cmp(self, self->regs.byte.x, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self))); + break; + case 0xe5: + cpu_65c02_sbc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self))); + break; + case 0xe6: + cpu_65c02_inc(self, cpu_65c02_ea_zero_page(self)); + break; + case 0xe7: + cpu_65c02_smb(self, 6, cpu_65c02_ea_zero_page(self)); + break; + case 0xe8: + cpu_65c02_inc(self, CPU_65C02_EA_X); + break; + case 0xe9: + cpu_65c02_sbc(self, cpu_65c02_fetch_byte(self)); + break; + case 0xea: + cpu_65c02_nop(self); + break; + case 0xeb: + cpu_65c02_illegal_opcode11(self); + break; + case 0xec: + cpu_65c02_cmp(self, self->regs.byte.x, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self))); + break; + case 0xed: + cpu_65c02_sbc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self))); + break; + case 0xee: + cpu_65c02_inc(self, cpu_65c02_ea_absolute(self)); + break; + case 0xef: + { + int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)); + cpu_65c02_bra(self, (rvalue >> 6) & 1, cpu_65c02_ea_relative(self)); + } + break; + case 0xf0: + cpu_65c02_bra(self, self->regs.bit.zf, cpu_65c02_ea_relative(self)); + break; + case 0xf1: + cpu_65c02_sbc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect_indexed(self, self->regs.byte.y))); + break; + case 0xf2: + cpu_65c02_sbc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect(self))); + break; + case 0xf3: + cpu_65c02_illegal_opcode11(self); + break; + case 0xf4: + cpu_65c02_illegal_opcode24(self); + break; + case 0xf5: + cpu_65c02_sbc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x))); + break; + case 0xf6: + cpu_65c02_inc(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x)); + break; + case 0xf7: + cpu_65c02_smb(self, 7, cpu_65c02_ea_zero_page(self)); + break; + case 0xf8: + cpu_65c02_se(self, CPU_65C02_REG_P_BIT_D); + break; + case 0xf9: + cpu_65c02_sbc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y))); + break; + case 0xfa: + cpu_65c02_pl(self, CPU_65C02_EA_X); + break; + case 0xfb: + cpu_65c02_illegal_opcode11(self); + break; + case 0xfc: + cpu_65c02_illegal_opcode34(self); + break; + case 0xfd: + cpu_65c02_sbc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x))); + break; + case 0xfe: + cpu_65c02_inc(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x)); + break; + case 0xff: + { + int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)); + cpu_65c02_bra(self, (rvalue >> 7) & 1, cpu_65c02_ea_relative(self)); + } + break; + } +} diff --git a/emu_65c02/cpu_65c02.h b/emu_65c02/cpu_65c02.h new file mode 100644 index 0000000..0a506db --- /dev/null +++ b/emu_65c02/cpu_65c02.h @@ -0,0 +1,535 @@ +#ifndef _CPU_65C02_H +#define _CPU_65C02_H + +#include +#include +#include + +// gcc specific +#ifndef ALWAYS_INLINE +#define ALWAYS_INLINE __attribute__((always_inline)) +#endif + +#define CPU_65C02_NMI_VECTOR 0xfffa +#define CPU_65C02_RESET_VECTOR 0xfffc +#define CPU_65C02_IRQ_VECTOR 0xfffe + +// bits within REG_P +#define CPU_65C02_REG_P_BIT_C 0 +#define CPU_65C02_REG_P_BIT_Z 1 +#define CPU_65C02_REG_P_BIT_I 2 +#define CPU_65C02_REG_P_BIT_D 3 +#define CPU_65C02_REG_P_BIT_B 4 +#define CPU_65C02_REG_P_BIT_V 6 +#define CPU_65C02_REG_P_BIT_N 7 + +// special memory locations (negative address) +#define CPU_65C02_EA_PC (-8) +#define CPU_65C02_EA_A (-6) +#define CPU_65C02_EA_X (-5) +#define CPU_65C02_EA_Y (-4) +#define CPU_65C02_EA_S (-3) +#define CPU_65C02_EA_P (-2) +#define CPU_65C02_EA_IFLAGS (-1) + +// registers, in same order as special memory locations, but reversed on +// big endian hardware where special memory address will be complemented +// (this allows special memory to always look like it is little endian) +union cpu_65c02_regs { +#if __BYTE_ORDER == __BIG_ENDIAN + struct { + uint8_t _fill_iflags : 4; + uint8_t stp_flag : 1; + uint8_t wai_flag : 1; + uint8_t irq_pending : 1; + uint8_t nmi_pending : 1; + uint8_t nf : 1; + uint8_t vf : 1; + uint8_t _fill_5f: 1; + uint8_t _fill_4f: 1; + uint8_t df : 1; + uint8_t _if : 1; + uint8_t zf : 1; + uint8_t cf : 1; + uint8_t _fill_s; + uint8_t _fill_y; + uint8_t _fill_x; + uint8_t _fill_a; + uint16_t _fill_pc; + } bit; + struct { + uint8_t iflags; + uint8_t p; + uint8_t s; + uint8_t y; + uint8_t x; + uint8_t a; + uint16_t _fill_pc; + } byte; + struct { + uint8_t _fill_iflags; + uint8_t _fill_p; + uint8_t _fill_s; + uint8_t _fill_y; + uint8_t _fill_x; + uint8_t _fill_a; + uint16_t pc; + } word; + uint8_t mem_be[8]; +#else + struct { + uint16_t _fill_pc; + uint8_t _fill_a; + uint8_t _fill_x; + uint8_t _fill_y; + uint8_t _fill_s; + uint8_t cf : 1; + uint8_t zf : 1; + uint8_t _if : 1; + uint8_t df : 1; + uint8_t _fill_4f: 1; + uint8_t _fill_5f: 1; + uint8_t vf : 1; + uint8_t nf : 1; + uint8_t nmi_pending : 1; + uint8_t irq_pending : 1; + uint8_t wai_flag : 1; + uint8_t stp_flag : 1; + uint8_t _fill_iflags : 4; + } bit; + struct { + uint16_t _fill_pc; + uint8_t a; + uint8_t x; + uint8_t y; + uint8_t s; + uint8_t p; + uint8_t iflags; + } byte; + struct { + uint16_t pc; + uint8_t _fill_a; + uint8_t _fill_x; + uint8_t _fill_y; + uint8_t _fill_s; + uint8_t _fill_p; + uint8_t _fill_iflags; + } word; + uint8_t mem_le[8]; +#endif +}; + +struct cpu_65c02 { + int cycles; + uint8_t (*read_byte)(void *context, uint16_t addr); + void *read_byte_context; + void (*write_byte)(void *context, uint16_t addr, uint8_t data); + void *write_byte_context; + union cpu_65c02_regs regs; +}; + +// memory or special memory access +static ALWAYS_INLINE uint8_t cpu_65c02_read_byte(struct cpu_65c02 *self, int32_t addr) { + if (addr < 0) +#if __BYTE_ORDER == __BIG_ENDIAN + return self->regs.mem_be[~addr]; +#else + return self->regs.mem_le[sizeof(union cpu_65c02_regs) + addr]; +#endif + self->cycles += 1; + return self->read_byte(self->read_byte_context, addr & 0xffff); +} + +static ALWAYS_INLINE uint16_t cpu_65c02_read_word(struct cpu_65c02 *self, int32_t addr) { + int data = cpu_65c02_read_byte(self, addr); + return data | (cpu_65c02_read_byte(self, addr + 1) << 8); +} + +static ALWAYS_INLINE uint16_t cpu_65c02_read_word_zero_page(struct cpu_65c02 *self, uint8_t addr) { + int data = cpu_65c02_read_byte(self, addr & 0xff); + return data | (cpu_65c02_read_byte(self, (addr + 1) & 0xff) << 8); +} + +static ALWAYS_INLINE void cpu_65c02_write_byte(struct cpu_65c02 *self, int32_t addr, uint8_t data) { + self->cycles += 1; + if (addr < 0) +#if __BYTE_ORDER == __BIG_ENDIAN + self->regs.mem_be[~addr] = data; +#else + self->regs.mem_le[sizeof(union cpu_65c02_regs) + addr] = data; +#endif + else + self->write_byte(self->write_byte_context, addr, data); +} + +static ALWAYS_INLINE void cpu_65c02_write_word(struct cpu_65c02 *self, int32_t addr, uint16_t data) { + cpu_65c02_write_byte(self, addr, data & 0xff); + cpu_65c02_write_byte(self, addr + 1, data >> 8); +} + +static ALWAYS_INLINE uint8_t cpu_65c02_fetch_byte(struct cpu_65c02 *self) { + int data = cpu_65c02_read_byte(self, self->regs.word.pc++); + return data; +} + +static ALWAYS_INLINE uint16_t cpu_65c02_fetch_word(struct cpu_65c02 *self) { + int data = cpu_65c02_fetch_byte(self); + return data | (cpu_65c02_fetch_byte(self) << 8); +} + +static ALWAYS_INLINE void cpu_65c02_push_byte(struct cpu_65c02 *self, uint8_t data) { + cpu_65c02_write_byte(self, self->regs.byte.s-- | 0x100, data); +} + +static ALWAYS_INLINE void cpu_65c02_push_word(struct cpu_65c02 *self, uint16_t data) { + cpu_65c02_push_byte(self, data >> 8); + cpu_65c02_push_byte(self, data & 0xff); +} + +static ALWAYS_INLINE uint8_t cpu_65c02_pop_byte(struct cpu_65c02 *self) { + return cpu_65c02_read_byte(self, ++self->regs.byte.s | 0x100); +} + +static ALWAYS_INLINE uint16_t cpu_65c02_pop_word(struct cpu_65c02 *self) { + int data = cpu_65c02_pop_byte(self); + return data | (cpu_65c02_pop_byte(self) << 8); +} + +// effective address calculation +static ALWAYS_INLINE uint16_t cpu_65c02_ea_absolute(struct cpu_65c02 *self) { + return cpu_65c02_fetch_word(self); +} + +static ALWAYS_INLINE uint16_t cpu_65c02_ea_absolute_indexed(struct cpu_65c02 *self, uint8_t rvalue) { + int addr = cpu_65c02_ea_absolute(self); + self->cycles += ((addr & 0xff) + (rvalue & 0xff)) >> 8; + return addr + rvalue; +} + +static ALWAYS_INLINE uint16_t cpu_65c02_ea_absolute_indexed_indirect(struct cpu_65c02 *self, uint8_t rvalue) { + return cpu_65c02_read_word(self, cpu_65c02_ea_absolute_indexed(self, rvalue)); +} + +static ALWAYS_INLINE uint16_t cpu_65c02_ea_absolute_indirect(struct cpu_65c02 *self) { + return cpu_65c02_read_word(self, cpu_65c02_ea_absolute(self)); +} + +static ALWAYS_INLINE int8_t cpu_65c02_ea_relative(struct cpu_65c02 *self) { + return (int8_t)cpu_65c02_fetch_byte(self); +} + +static ALWAYS_INLINE uint8_t cpu_65c02_ea_zero_page(struct cpu_65c02 *self) { + return cpu_65c02_fetch_byte(self); +} + +static ALWAYS_INLINE uint8_t cpu_65c02_ea_zero_page_indexed(struct cpu_65c02 *self, uint8_t rvalue) { + return (cpu_65c02_ea_zero_page(self) + rvalue) & 0xff; +} + +static ALWAYS_INLINE uint16_t cpu_65c02_ea_zero_page_indexed_indirect(struct cpu_65c02 *self, uint8_t rvalue) { + return cpu_65c02_read_word_zero_page(self, cpu_65c02_ea_zero_page_indexed(self, rvalue)); +} + +static ALWAYS_INLINE uint16_t cpu_65c02_ea_zero_page_indirect(struct cpu_65c02 *self) { + return cpu_65c02_read_word_zero_page(self, cpu_65c02_ea_zero_page(self)); +} + +static ALWAYS_INLINE uint16_t cpu_65c02_ea_zero_page_indirect_indexed(struct cpu_65c02 *self, uint8_t rvalue) { + int addr = cpu_65c02_read_word_zero_page(self, cpu_65c02_ea_zero_page(self)); + self->cycles += ((addr & 0xff) + (rvalue & 0xff)) >> 8; + return addr + rvalue; +} + +// instruction execute +static ALWAYS_INLINE void cpu_65c02_adc(struct cpu_65c02 *self, uint8_t rvalue) { + int result0, result1, result2; + if (self->regs.bit.df) { + result0 = self->regs.bit.cf + (self->regs.byte.a & 0xf) + (rvalue & 0xf); + if (result0 >= 0xa) + result0 = (result0 + 6) & 0x1f; + result0 += (self->regs.byte.a & 0x70) + (rvalue & 0x70); + result1 = result0 + (self->regs.byte.a & 0x80) + (rvalue & 0x80); + result2 = result1; + if (result2 >= 0xa0) + result2 = (result2 + 0x60) & 0x1ff; + } + else { + result0 = self->regs.bit.cf + (self->regs.byte.a & 0x7f) + (rvalue & 0x7f); + result1 = result0 + (self->regs.byte.a & 0x80) + (rvalue & 0x80); + result2 = result1; + } + self->regs.byte.a = result2 & 0xff; + self->regs.bit.cf = result2 >> 8; + self->regs.bit.zf = (result2 & 0xff) == 0; + self->regs.bit.vf = ((result0 >> 7) ^ (result1 >> 8)) & 1; + self->regs.bit.nf = (result2 >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_65c02_and(struct cpu_65c02 *self, uint8_t rvalue) { + int result = self->regs.byte.a & rvalue; + self->regs.byte.a = result; + self->regs.bit.zf = result == 0; + self->regs.bit.nf = result >> 7; +} + +static ALWAYS_INLINE void cpu_65c02_asl(struct cpu_65c02 *self, int32_t lvalue) { + int result = cpu_65c02_read_byte(self, lvalue) << 1; + ++self->cycles; + cpu_65c02_write_byte(self, lvalue, result & 0xff); + self->regs.bit.cf = result >> 8; + self->regs.bit.zf = (result & 0xff) == 0; + self->regs.bit.nf = (result >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_65c02_bit(struct cpu_65c02 *self, uint8_t rvalue) { + int result = self->regs.byte.a & rvalue; + self->regs.bit.zf = result == 0; + self->regs.bit.vf = (rvalue >> 6) & 1; + self->regs.bit.nf = (rvalue >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_65c02_bit_imm(struct cpu_65c02 *self, uint8_t rvalue) { + int result = self->regs.byte.a & rvalue; + self->regs.bit.zf = result == 0; +} + +static ALWAYS_INLINE void cpu_65c02_bra(struct cpu_65c02 *self, bool pred, int8_t rvalue) { + if (pred) { + self->cycles += ((self->regs.word.pc & 0xff) + (rvalue & 0xff)) >> 8; + self->regs.word.pc += rvalue; + } +} + +static ALWAYS_INLINE void cpu_65c02_cl(struct cpu_65c02 *self, int n) { + self->regs.byte.p &= ~(1 << n); +} + +static ALWAYS_INLINE void cpu_65c02_cmp(struct cpu_65c02 *self, uint8_t rvalue0, uint8_t rvalue1) { + rvalue1 ^= 0xff; + int result = 1 + rvalue0 + rvalue1; + self->regs.bit.cf = result >> 8; + self->regs.bit.zf = (result & 0xff) == 0; + self->regs.bit.nf = (result >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_65c02_dec(struct cpu_65c02 *self, int32_t lvalue) { + int result = (cpu_65c02_read_byte(self, lvalue) - 1) & 0xff; + ++self->cycles; + cpu_65c02_write_byte(self, lvalue, result); + self->regs.bit.zf = result == 0; + self->regs.bit.nf = result >> 7; +} + +static ALWAYS_INLINE void cpu_65c02_eor(struct cpu_65c02 *self, uint8_t rvalue) { + int result = self->regs.byte.a ^ rvalue; + self->regs.byte.a = result; + self->regs.bit.zf = result == 0; + self->regs.bit.nf = result >> 7; +} + +static ALWAYS_INLINE void cpu_65c02_illegal_opcode11(struct cpu_65c02 *self) { +} + +static ALWAYS_INLINE void cpu_65c02_illegal_opcode22(struct cpu_65c02 *self) { + ++self->cycles; + self->regs.word.pc += 1; +} + +static ALWAYS_INLINE void cpu_65c02_illegal_opcode23(struct cpu_65c02 *self) { + self->cycles += 2; + self->regs.word.pc += 1; +} + +static ALWAYS_INLINE void cpu_65c02_illegal_opcode24(struct cpu_65c02 *self) { + self->cycles += 3; + self->regs.word.pc += 1; +} + +static ALWAYS_INLINE void cpu_65c02_illegal_opcode34(struct cpu_65c02 *self) { + self->cycles += 3; + self->regs.word.pc += 2; +} + +static ALWAYS_INLINE void cpu_65c02_illegal_opcode38(struct cpu_65c02 *self) { + self->cycles += 7; + self->regs.word.pc += 2; +} + +static ALWAYS_INLINE void cpu_65c02_inc(struct cpu_65c02 *self, int32_t lvalue) { + int result = (cpu_65c02_read_byte(self, lvalue) + 1) & 0xff; + ++self->cycles; + cpu_65c02_write_byte(self, lvalue, result); + self->regs.bit.zf = result == 0; + self->regs.bit.nf = result >> 7; +} + +static ALWAYS_INLINE void cpu_65c02_irq(struct cpu_65c02 *self, bool brk, int32_t lvalue) { + ++self->cycles; + cpu_65c02_push_word(self, self->regs.word.pc); + cpu_65c02_push_byte( + self, + ((self->regs.byte.p) & ~(1 << CPU_65C02_REG_P_BIT_B)) | + (brk << CPU_65C02_REG_P_BIT_B) + ); + self->regs.word.pc = cpu_65c02_read_word(self, lvalue); + self->regs.bit._if = true; + self->regs.bit.df = false; +} + +static ALWAYS_INLINE void cpu_65c02_jmp(struct cpu_65c02 *self, int32_t lvalue) { + self->regs.word.pc = lvalue; +} + +static ALWAYS_INLINE void cpu_65c02_jsr(struct cpu_65c02 *self, int32_t lvalue) { + cpu_65c02_push_word(self, (self->regs.word.pc - 1) & 0xffff); + self->regs.word.pc = lvalue; +} + +static ALWAYS_INLINE void cpu_65c02_ld(struct cpu_65c02 *self, int32_t lvalue, uint8_t rvalue) { + cpu_65c02_write_byte(self, lvalue, rvalue); + self->regs.bit.zf = rvalue == 0; + self->regs.bit.nf = (rvalue >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_65c02_lsr(struct cpu_65c02 *self, int32_t lvalue) { + int result = cpu_65c02_read_byte(self, lvalue); + ++self->cycles; + cpu_65c02_write_byte(self, lvalue, result >> 1); + self->regs.bit.cf = result & 1; + self->regs.bit.zf = (result & 0xfe) == 0; + self->regs.bit.nf = false; +} + +static ALWAYS_INLINE void cpu_65c02_nop(struct cpu_65c02 *self) { +} + +static ALWAYS_INLINE void cpu_65c02_ora(struct cpu_65c02 *self, uint8_t rvalue) { + int result = self->regs.byte.a | rvalue; + self->regs.byte.a = result; + self->regs.bit.zf = result == 0; + self->regs.bit.nf = result >> 7; +} + +static ALWAYS_INLINE void cpu_65c02_ph(struct cpu_65c02 *self, uint8_t rvalue) { + cpu_65c02_push_byte(self, rvalue); +} + +static ALWAYS_INLINE void cpu_65c02_pl(struct cpu_65c02 *self, int32_t lvalue) { + int result = cpu_65c02_pop_byte(self); + cpu_65c02_write_byte(self, lvalue, result); + self->regs.bit.zf = result == 0; + self->regs.bit.nf = result >> 7; +} + +static ALWAYS_INLINE void cpu_65c02_plp(struct cpu_65c02 *self) { + self->regs.byte.p = cpu_65c02_pop_byte(self) | 0x30; +} + +static ALWAYS_INLINE void cpu_65c02_rmb(struct cpu_65c02 *self, int n, int32_t lvalue) { + int result = cpu_65c02_read_byte(self, lvalue) & ~(1 << n); + ++self->cycles; + cpu_65c02_write_byte(self, lvalue, result); +} + +static ALWAYS_INLINE void cpu_65c02_rol(struct cpu_65c02 *self, int32_t lvalue) { + int result = self->regs.bit.cf | (cpu_65c02_read_byte(self, lvalue) << 1); + ++self->cycles; + cpu_65c02_write_byte(self, lvalue, result & 0xff); + self->regs.bit.cf = result >> 8; + self->regs.bit.zf = (result & 0xff) == 0; + self->regs.bit.nf = (result >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_65c02_ror(struct cpu_65c02 *self, int32_t lvalue) { + int result = cpu_65c02_read_byte(self, lvalue) | (self->regs.bit.cf << 8); + ++self->cycles; + cpu_65c02_write_byte(self, lvalue, result >> 1); + self->regs.bit.cf = result & 1; + self->regs.bit.zf = (result & 0x1fe) == 0; + self->regs.bit.nf = (result >> 8) & 1; +} + +static ALWAYS_INLINE void cpu_65c02_rti(struct cpu_65c02 *self) { + self->regs.byte.p = cpu_65c02_pop_byte(self) | 0x30; + self->regs.word.pc = cpu_65c02_pop_word(self); +} + +static ALWAYS_INLINE void cpu_65c02_rts(struct cpu_65c02 *self) { + self->regs.word.pc = (cpu_65c02_pop_word(self) + 1) & 0xffff; +} + +static ALWAYS_INLINE void cpu_65c02_sbc(struct cpu_65c02 *self, uint8_t rvalue) { + rvalue ^= 0xff; + int result0, result1, result2; + if (self->regs.bit.df) { + result0 = self->regs.bit.cf + (self->regs.byte.a & 0xf) + (rvalue & 0xf); + if (result0 < 0x10) + result0 = (result0 - 6) & 0x1f; + result0 += (self->regs.byte.a & 0x70) + (rvalue & 0x70); + result1 = result0 + (self->regs.byte.a & 0x80) + (rvalue & 0x80); + result2 = result1; + if (result2 < 0x100) + result2 = (result2 - 0x60) & 0x1ff; + } + else { + result0 = self->regs.bit.cf + (self->regs.byte.a & 0x7f) + (rvalue & 0x7f); + result1 = result0 + (self->regs.byte.a & 0x80) + (rvalue & 0x80); + result2 = result1; + } + self->regs.byte.a = result2 & 0xff; + self->regs.bit.cf = result2 >> 8; + self->regs.bit.zf = (result2 & 0xff) == 0; + self->regs.bit.vf = ((result0 >> 7) ^ (result1 >> 8)) & 1; + self->regs.bit.nf = (result2 >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_65c02_se(struct cpu_65c02 *self, int n) { + self->regs.byte.p |= 1 << n; +} + +static ALWAYS_INLINE void cpu_65c02_smb(struct cpu_65c02 *self, int n, int32_t lvalue) { + int result = cpu_65c02_read_byte(self, lvalue) | (1 << n); + ++self->cycles; + cpu_65c02_write_byte(self, lvalue, result); +} + +static ALWAYS_INLINE void cpu_65c02_st(struct cpu_65c02 *self, uint8_t rvalue, int32_t lvalue) { + cpu_65c02_write_byte(self, lvalue, rvalue); +} + +static ALWAYS_INLINE void cpu_65c02_stp(struct cpu_65c02 *self) { + self->regs.bit.stp_flag = true; +} + +static ALWAYS_INLINE void cpu_65c02_trb(struct cpu_65c02 *self, int32_t lvalue) { + int result = cpu_65c02_read_byte(self, lvalue); + ++self->cycles; + cpu_65c02_write_byte(self, lvalue, result & ~self->regs.byte.a); + result &= self->regs.byte.a; + self->regs.bit.zf = result == 0; +} + +static ALWAYS_INLINE void cpu_65c02_tsb(struct cpu_65c02 *self, int32_t lvalue) { + int result = cpu_65c02_read_byte(self, lvalue); + ++self->cycles; + cpu_65c02_write_byte(self, lvalue, result | self->regs.byte.a); + result &= self->regs.byte.a; + self->regs.bit.zf = result == 0; +} + +static ALWAYS_INLINE void cpu_65c02_wai(struct cpu_65c02 *self) { + self->regs.bit.wai_flag = true; +} + +// prototypes +void cpu_65c02_init( + struct cpu_65c02 *self, + uint8_t (*read_byte)(void *context, uint16_t addr), + void *read_byte_context, + void (*write_byte)(void *context, uint16_t addr, uint8_t data), + void *write_byte_context +); +void cpu_65c02_reset(struct cpu_65c02 *self); +void cpu_65c02_execute(struct cpu_65c02 *self); + +#endif diff --git a/emu_65c02/emu_65c02.c b/emu_65c02/emu_65c02.c index 461936a..abcfddc 100644 --- a/emu_65c02/emu_65c02.c +++ b/emu_65c02/emu_65c02.c @@ -13,7 +13,7 @@ #include #include #include "stty_sane.h" -#include "vrEmu6502/src/vrEmu6502.h" +#include "cpu_65c02.h" #define APPLE_IIE 1 #define APPLE_WIDTH 560 @@ -153,7 +153,8 @@ uint32_t palette[0x10] = { // can make this green or amber to be more realistic uint32_t mono_palette[2] = {0xff000000, 0xffffffff}; -VrEmu6502 *cpu; +struct cpu_65c02 cpu; +bool jam_flag; #if APPLE_IIE #include "cg_default/cg_lowercase_20.inc" @@ -539,7 +540,7 @@ void dos(char *line) { } } -uint8_t mem_read(uint16_t addr0/*, bool isDbg*/) { +uint8_t read_byte(void *context, uint16_t addr0) { // for joystick, for now count memory accesses as proxy for cycles if (joystick_count < JOYSTICK_COUNT) { ++joystick_count; @@ -607,10 +608,10 @@ uint8_t mem_read(uint16_t addr0/*, bool isDbg*/) { //if (addr >= 0x8c08 && addr < 0x8e00) { // data fetch //if (addr >= 0x9580 && addr < 0x9600) { // data fetch if (addr >= 0x5c00 && addr < 0x5c1b) { // data fetch - int pc = vrEmu6502GetPC(cpu); + int pc = cpu.regs.word.pc; fprintf(stderr, "pc=%04x addr=%04x\n", pc, addr); exit_flag = 0x101; - vrEmu6502Jam(cpu); + jam_flag = true; } #endif @@ -632,8 +633,8 @@ uint8_t mem_read(uint16_t addr0/*, bool isDbg*/) { #if 0 // draw object if (addr == 0x1ab4) { // opcode fetch - int pc = vrEmu6502GetPC(cpu); - int object = vrEmu6502GetX(cpu); + int pc = cpu.regs.word.pc; + int object = cpu.regs.byte.x; int shape = mem[0xabd0 + object]; // object_shape const struct {int shape; const char *name;} shapes[138] = { @@ -800,12 +801,12 @@ uint8_t mem_read(uint16_t addr0/*, bool isDbg*/) { #if 0 // start at mission if (addr == 0x1726) // opcode fetch - vrEmu6502SetX(cpu, start_at_mission); + cpu.regs.byte.x = start_at_mission; #endif #if MEM_TRACE { - int pc = cpu ? vrEmu6502GetPC(cpu) : 0; + int pc = cpu ? cpu.regs.word.pc : 0; fprintf(stderr, "pc=%04x addr=%05x rd=%02x\n", pc, addr, mem[addr]); } #endif @@ -899,7 +900,7 @@ uint8_t mem_read(uint16_t addr0/*, bool isDbg*/) { } if (data == 'X' - 0x40) { // count == 0 or ctrl-x (unassigned by hrcg) exit_flag = 0x101; - vrEmu6502Jam(cpu); + jam_flag = true; } return data; } @@ -938,7 +939,7 @@ uint8_t mem_read(uint16_t addr0/*, bool isDbg*/) { return 0xff; } -void mem_write(uint16_t addr0, uint8_t val) { +void write_byte(void *context, uint16_t addr0, uint8_t data) { // for joystick, for now count memory accesses as proxy for cycles if (joystick_count < JOYSTICK_COUNT) { ++joystick_count; @@ -953,16 +954,6 @@ void mem_write(uint16_t addr0, uint8_t val) { } int addr = addr0; -#if 0 // vectors - if (addr >= 4 && addr < 0x80) { - int pc = vrEmu6502GetPC(cpu); - if (pc != 0x3f52) { - fprintf(stderr, "pc=%04x w=%04x v=%02x\n", pc, addr, val); - exit(EXIT_FAILURE); - } - } -#endif - if ((addr & 0xff00) != IO_PAGE) { #if APPLE_IIE if (addr < 0x200) { @@ -993,9 +984,9 @@ void mem_write(uint16_t addr0, uint8_t val) { (lc_state & LC_SELECT) == LC_SELECT_ROM_WP ) { #if MEM_TRACE - int pc = cpu ? vrEmu6502GetPC(cpu): 0; + int pc = cpu ? cpu.regs.word.pc: 0; addr += 0x20000 - 0xc000; - fprintf(stderr, "pc=%04x addr=%05x wr=%02x (nop)\n", pc, addr, val); + fprintf(stderr, "pc=%04x addr=%05x wr=%02x (nop)\n", pc, addr, data); #endif return; } @@ -1014,9 +1005,9 @@ void mem_write(uint16_t addr0, uint8_t val) { (lc_state & LC_SELECT) == LC_SELECT_ROM_WP ) { #if MEM_TRACE - int pc = cpu ? vrEmu6502GetPC(cpu) : 0; + int pc = cpu ? cpu.regs.word.pc : 0; addr += 0x10000 - 0xc000; - fprintf(stderr, "pc=%04x addr=%05x wr=%02x (nop)\n", pc, addr, val); + fprintf(stderr, "pc=%04x addr=%05x wr=%02x (nop)\n", pc, addr, data); #endif return; } @@ -1026,23 +1017,23 @@ void mem_write(uint16_t addr0, uint8_t val) { #if MEM_TRACE { - int pc = cpu ? vrEmu6502GetPC(cpu) : 0; - fprintf(stderr, "pc=%04x addr=%05x wr=%02x\n", pc, addr, val); + int pc = cpu ? cpu.regs.word.pc : 0; + fprintf(stderr, "pc=%04x addr=%05x wr=%02x\n", pc, addr, data); } #endif - mem[addr] = val; + mem[addr] = data; #if 0 // sound effects if (addr >= 0xab80 && addr < 0xab90) { - int pc = vrEmu6502GetPC(cpu); - fprintf(stderr, "pc=%04x w=%04x v=%02x\r\n", pc, addr, val); + int pc = cpu.regs.word.pc; + fprintf(stderr, "pc=%04x w=%04x v=%02x\r\n", pc, addr, data); } #endif #if 0 // decimals if (addr >= 0xb0 && addr < 0xc0) { - int pc = vrEmu6502GetPC(cpu); - fprintf(stderr, "pc=%04x w=%04x v=%02x", pc, addr, val); + int pc = cpu.regs.word.pc; + fprintf(stderr, "pc=%04x w=%04x v=%02x", pc, addr, data); for (int i = 0xb0; i < 0xc0; i += 2) { int v = mem[i] + (mem[i + 1] << 8); fprintf(stderr, " dec_%02x=%04x", i, v); @@ -1053,13 +1044,13 @@ void mem_write(uint16_t addr0, uint8_t val) { #if 0 // demo mode if (addr >= 0xf0 && addr < 0xf3) { - int pc = vrEmu6502GetPC(cpu); + int pc = cpu.regs.word.pc; fprintf( stderr, "pc=%04x w=%04x v=%02x demo_mode=%02x button_state=%02x key_state=%02x\n", pc, addr, - val, + data, mem[0xf0], mem[0xf1], mem[0xf2] @@ -1135,33 +1126,33 @@ void mem_write(uint16_t addr0, uint8_t val) { lc_state = addr & 7; // should check RWx2 for write enable break; case STDOUT_DATA: - if (write(fd_out, &val, 1) == -1) { + if (write(fd_out, &data, 1) == -1) { perror("write()"); exit(EXIT_FAILURE); } break; case STDERR_DATA: - if (write(STDERR_FILENO, &val, 1) == -1) { + if (write(STDERR_FILENO, &data, 1) == -1) { perror("write()"); exit(EXIT_FAILURE); } break; case USLEEP_LO: - usleep_lo = val; + usleep_lo = data; break; case USLEEP_HI: - usleep_count = usleep_lo | (val << 8); - vrEmu6502Jam(cpu); // force a screen update before going to sleep + usleep_count = usleep_lo | (data << 8); + jam_flag = true; // force a screen update before going to sleep break; case SYS_EXIT: - exit_flag = val | 0x100; - vrEmu6502Jam(cpu); + exit_flag = data | 0x100; + jam_flag = true; break; case DOS_LO: - dos_lo = val; + dos_lo = data; break; case DOS_HI: - dos((char *)(mem + (dos_lo | (val << 8)))); + dos((char *)(mem + (dos_lo | (data << 8)))); break; } } @@ -1208,10 +1199,9 @@ int main(int argc, char **argv) { char *f0_rom_file = NULL; char *f8_rom_file = NULL; #endif - bool timing = false; while (argn < argc) { if (strcmp(argv[argn], "--help") == 0) { - printf("usage: %s [--cg-rom=file.bin] [--video-rom=file.bin] [--NN-rom=file.bin ...] [--timing] [program.obj[,aNNNN] ...] [-- child-executable [child-argument ...]]\n", argv[0]); + printf("usage: %s [--cg-rom=file.bin] [--video-rom=file.bin] [--NN-rom=file.bin ...] [program.obj[,aNNNN] ...] [-- child-executable [child-argument ...]]\n", argv[0]); exit(EXIT_FAILURE); } if (memcmp(argv[argn], "--cg-rom=", 9) == 0) @@ -1237,8 +1227,6 @@ int main(int argc, char **argv) { else if (memcmp(argv[argn], "--f8-rom=", 9) == 0) f8_rom_file = argv[argn] + 9; #endif - else if (strcmp(argv[argn], "--timing") == 0) - timing = true; else break; ++argn; @@ -1312,17 +1300,6 @@ int main(int argc, char **argv) { entry_point = load(p); } - // do this before creating the CPU - if (entry_point != -1) { -#if APPLE_IIE - mem[RESET_VECTOR + 0x20000 - 0xc000] = (uint8_t)(entry_point & 0xff); - mem[RESET_VECTOR + 1 + 0x20000 - 0xc000] = (uint8_t)(entry_point >> 8); -#else - mem[RESET_VECTOR + 0x10000 - 0xc000] = (uint8_t)(entry_point & 0xff); - mem[RESET_VECTOR + 1 + 0x10000 - 0xc000] = (uint8_t)(entry_point >> 8); -#endif - } - // open pty and child process if requested if (argn < argc) { int fd_master = posix_openpt(O_RDWR); @@ -1567,11 +1544,10 @@ int main(int argc, char **argv) { joystick_axes[i] = JOYSTICK_COUNT >> 1; } - cpu = vrEmu6502New(CPU_65C02, mem_read, mem_write); - if (cpu == NULL) { - perror("malloc()"); - exit(EXIT_FAILURE); - } + cpu_65c02_init(&cpu, read_byte, NULL, write_byte, NULL); + cpu_65c02_reset(&cpu); + if (entry_point != -1) + cpu.regs.word.pc = entry_point; #if TRACE memset(pc_pairs, 0xff, N_PC_PAIRS * sizeof(struct pc_pair)); @@ -1588,7 +1564,6 @@ int main(int argc, char **argv) { #endif // main loop - long nb_instructions = 0, nb_cycles = 0; int update_count = 0; float spkr_rc = 0.f; while (true) { @@ -1954,17 +1929,18 @@ int main(int argc, char **argv) { #endif } else { - int i, j; #if TRACE - struct pc_pair pc_pair = {vrEmu6502GetPC(cpu), 0}; - for (int k = 0; k < CYCLES_PER_SAMPLE; k += j) { - i = vrEmu6502RunInstrs(cpu, 1, &j); - nb_instructions += i; - nb_cycles += j; - if (i == 0) - goto jam; //break; - - pc_pair.pc1 = vrEmu6502GetPC(cpu); + struct pc_pair pc_pair = {cpu.pc, 0}; +#endif + for (cpu.cycles = 0; cpu.cycles < CYCLES_PER_SAMPLE; ) { + cpu_65c02_execute(&cpu); + if (jam_flag) { + jam_flag = false; + break; + } + +#if TRACE + pc_pair.pc1 = cpu.regs.word.pc; bool show_trace = false; int hash = (19 * pc_pair.pc0 + 37 * pc_pair.pc1) % N_PC_PAIRS; @@ -1988,11 +1964,11 @@ int main(int argc, char **argv) { } uint8_t regs[N_TRACE_REGS] = { - vrEmu6502GetAcc(cpu), - vrEmu6502GetX(cpu), - vrEmu6502GetY(cpu), - vrEmu6502GetStackPointer(cpu), - vrEmu6502GetStatus(cpu) + cpu.regs.byte.a, + cpu.regs.byte.x, + cpu.regs.byte.y, + cpu.regs.byte.s, + cpu.regs.byte.p }; for (int i = 0; i < N_TRACE_REGS; ++i) { if (regs[i] < trace[pc_pair.pc1][i].min_unsigned) { @@ -2059,18 +2035,10 @@ int main(int argc, char **argv) { trace[pc_pair.pc1][TRACE_REG_P].max_bitwise ); pc_pair.pc0 = pc_pair.pc1; - } -jam: -#else - i = vrEmu6502RunCycles(cpu, CYCLES_PER_SAMPLE, &j); - nb_instructions += i; - nb_cycles += j; - //if (j < CYCLES_PER_UPDATE) - // break; #endif + } if (exit_flag) break; - vrEmu6502Unjam(cpu); // sample tape and speaker outputs at CYCLES_PER_SAMPLE rate #if 0 @@ -2090,8 +2058,6 @@ jam: } quit: - vrEmu6502Destroy(cpu); - if (joystick) SDL_JoystickClose(joystick); SDL_CloseAudioDevice(audio_device_id); @@ -2099,12 +2065,5 @@ quit: SDL_DestroyWindow(window); SDL_Quit(); - if (timing) - fprintf( - stderr, - "%lu instructions executed on %lu cycles\n", - nb_instructions, - nb_cycles - ); exit(exit_flag & 0xff); } -- 2.34.1