From c16b34f14a84e4556d5c25f1f59e5ae2cbe4b133 Mon Sep 17 00:00:00 2001 From: Stefan Arentz Date: Tue, 13 Jan 2015 19:42:14 -0500 Subject: [PATCH] Initial import --- Makefile | 18 + cpu.c | 340 +++++++++++++++++ cpu.h | 76 ++++ ewm.c | 80 ++++ ins.c | 1092 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ ins.h | 38 ++ mem.c | 162 ++++++++ mem.h | 62 ++++ pia.c | 119 ++++++ pia.h | 42 +++ 10 files changed, 2029 insertions(+) create mode 100644 Makefile create mode 100644 cpu.c create mode 100644 cpu.h create mode 100644 ewm.c create mode 100644 ins.c create mode 100644 ins.h create mode 100644 mem.c create mode 100644 mem.h create mode 100644 pia.c create mode 100644 pia.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..eca4483 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ + +CC=clang +CFLAGS=-O3 -std=c11 -Werror -Wall -Wshadow -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes +SOURCES=cpu.c ins.c pia.c mem.c ewm.c +OBJECTS=$(SOURCES:.c=.o) +LIBS=-lcurses +EXECUTABLE=ewm + +all: $(SOURCES) $(EXECUTABLE) + +clean: + rm -f $(OBJECTS) $(EXECUTABLE) + +$(EXECUTABLE): $(OBJECTS) + $(CC) $(LDFLAGS) $(OBJECTS) $(LIBS) -o $@ + +.c.o: + $(CC) $(CFLAGS) $< -c -o $@ diff --git a/cpu.c b/cpu.c new file mode 100644 index 0000000..daddf78 --- /dev/null +++ b/cpu.c @@ -0,0 +1,340 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cpu.h" +#include "ins.h" + +/* Private API */ + +typedef void (*cpu_instruction_handler_t)(struct cpu_t *cpu); +typedef void (*cpu_instruction_handler_byte_t)(struct cpu_t *cpu, uint8_t oper); +typedef void (*cpu_instruction_handler_word_t)(struct cpu_t *cpu, uint16_t oper); + +static uint8_t _mem_get_byte(struct cpu_t *cpu, uint16_t addr) { + return cpu->memory[addr]; +} + +static uint16_t _mem_get_word(struct cpu_t *cpu, uint16_t addr) { + return *((uint16_t*) &cpu->memory[addr]); +} + +#if 1 +static void cpu_format_instruction(struct cpu_t *cpu, char *buffer) { + *buffer = 0x00; + + cpu_instruction_t *i = &instructions[cpu->memory[cpu->state.pc]]; + uint8_t opcode = cpu->memory[cpu->state.pc]; + + /* Single byte instructions */ + if (i->bytes == 1) { + sprintf(buffer, "%s", i->name); + } + + /* JSR is the only exception */ + else if (opcode == 0x20) { + sprintf(buffer, "%s $%.4X", i->name, _mem_get_word(cpu, cpu->state.pc+1)); + } + + /* Branches */ + else if ((opcode & 0b00011111) == 0b00010000) { + int8_t offset = (int8_t) _mem_get_byte(cpu, cpu->state.pc+1); + uint16_t addr = cpu->state.pc + 2 + offset; + sprintf(buffer, "%s $%.4X", i->name, addr); + } + + else if ((opcode & 0b00000011) == 0b00000001) { + switch ((opcode & 0b00011100) >> 2) { + case 0b000: + sprintf(buffer, "%s ($%.2X,X)", i->name, cpu->memory[cpu->state.pc+1]); + break; + case 0b001: + sprintf(buffer, "%s $%.2X", i->name, cpu->memory[cpu->state.pc+1]); + break; + case 0b010: + sprintf(buffer, "%s #$%.2X", i->name, cpu->memory[cpu->state.pc+1]); + break; + case 0b011: + sprintf(buffer, "%s $%.2X%.2X", i->name, cpu->memory[cpu->state.pc+2], cpu->memory[cpu->state.pc+1]); + break; + case 0b100: + sprintf(buffer, "%s ($%.2X),Y", i->name, cpu->memory[cpu->state.pc+1]); + break; + case 0b101: + sprintf(buffer, "%s $%.2X,X", i->name, cpu->memory[cpu->state.pc+1]); + break; + case 0b110: + sprintf(buffer, "%s $%.2X%.2X,Y", i->name, cpu->memory[cpu->state.pc+2], cpu->memory[cpu->state.pc+1]); + break; + case 0b111: + sprintf(buffer, "%s $%.2X%.2X,X", i->name, cpu->memory[cpu->state.pc+2], cpu->memory[cpu->state.pc+1]); + break; + } + } + + else if ((opcode & 0b00000011) == 0b00000010) { + switch ((opcode & 0b00011100) >> 2) { + case 0b000: + sprintf(buffer, "%s #$%.2X", i->name, cpu->memory[cpu->state.pc+1]); + break; + case 0b001: + sprintf(buffer, "%s $%.2X", i->name, cpu->memory[cpu->state.pc+1]); + break; + case 0b010: + sprintf(buffer, "%s", i->name); + break; + case 0b011: + sprintf(buffer, "%s $%.2X%.2X", i->name, cpu->memory[cpu->state.pc+2], cpu->memory[cpu->state.pc+1]); + break; + case 0b101: + sprintf(buffer, "%s $%.2X,X", i->name, cpu->memory[cpu->state.pc+1]); + break; + case 0b111: + sprintf(buffer, "%s $%.2X%.2X,X", i->name, cpu->memory[cpu->state.pc+2], cpu->memory[cpu->state.pc+1]); + break; + } + } + + else if ((opcode & 0b00000011) == 0b00000000) { + switch ((opcode & 0b00011100) >> 2) { + case 0b000: + sprintf(buffer, "%s #$%.2X", i->name, cpu->memory[cpu->state.pc+1]); + break; + case 0b001: + sprintf(buffer, "%s $%.2X", i->name, cpu->memory[cpu->state.pc+1]); + break; + case 0b011: + sprintf(buffer, "%s $%.2X%.2X", i->name, cpu->memory[cpu->state.pc+2], cpu->memory[cpu->state.pc+1]); + break; + case 0b101: + sprintf(buffer, "%s $%.2X,X", i->name, cpu->memory[cpu->state.pc+1]); + break; + case 0b111: + sprintf(buffer, "%s $%.2X%.2X,X", i->name, cpu->memory[cpu->state.pc+2], cpu->memory[cpu->state.pc+1]); + break; + } + } +} + +static void cpu_format_state(struct cpu_t *cpu, char *buffer) { + sprintf(buffer, "A=%.2X X=%.2X Y=%.2X S=%.2X SP=%.4X %c%c%c%c%c%c%c%c", + cpu->state.a, cpu->state.x, cpu->state.y, cpu->state.s, cpu->state.sp, + + cpu->state.n ? 'N' : '-', + cpu->state.v ? 'V' : '-', + '-', + cpu->state.b ? 'B' : '-', + cpu->state.d ? 'D' : '-', + cpu->state.i ? 'I' : '-', + cpu->state.z ? 'Z' : '-', + cpu->state.c ? 'C' : '-'); +} + +static void cpu_format_stack(struct cpu_t *cpu, char *buffer) { + *buffer = 0x00; + for (uint16_t sp = cpu->state.sp; sp != 0x01ff; sp++) { + char tmp[8]; + sprintf(tmp, " %.2X", _mem_get_byte(cpu, sp)); + buffer = strcat(buffer, tmp); + } +} +#endif + +static mach_timebase_info_data_t timebase_info; + +static uint64_t abs_to_nanos(uint64_t abs) { + return abs * timebase_info.numer / timebase_info.denom; +} + +static uint64_t nanos_to_abs(uint64_t nanos) { + return nanos * timebase_info.denom / timebase_info.numer; +} + +static int cpu_execute_instruction(struct cpu_t *cpu) { + + + /* Trace code - Refactor into its own function or module */ + char trace_instruction[256]; + char trace_state[256]; + char trace_stack[256]; + + if (cpu->trace) { + cpu_format_instruction(cpu, trace_instruction); + } + + + uint64_t start_time = mach_absolute_time(); + + /* Fetch instruction */ + cpu_instruction_t *i = &instructions[cpu->memory[cpu->state.pc]]; + + /* Remember the PC since some instructions modify it */ + uint16_t pc = cpu->state.pc; + + /* Advance PC */ + if (pc == cpu->state.pc) { + cpu->state.pc += i->bytes; + } + + /* Execute instruction */ + switch (i->bytes) { + case 1: + ((cpu_instruction_handler_t) i->handler)(cpu); + break; + case 2: + ((cpu_instruction_handler_byte_t) i->handler)(cpu, _mem_get_byte(cpu, pc+1)); + break; + case 3: + ((cpu_instruction_handler_word_t) i->handler)(cpu, _mem_get_word(cpu, pc+1)); + break; + } + + + if (cpu->trace) { + cpu_format_state(cpu, trace_state); + cpu_format_stack(cpu, trace_stack); // TODO: This crashes on the hello world test + + switch (i->bytes) { + case 1: + fprintf(stderr, "CPU: %.4X %-20s | %.2X %-20s STACK: %s\n", + pc, trace_instruction, cpu->memory[pc], trace_state, trace_stack); + break; + case 2: + fprintf(stderr, "CPU: %.4X %-20s | %.2X %.2X %-20s STACK: %s\n", + pc, trace_instruction, cpu->memory[pc], cpu->memory[pc+1], trace_state, trace_stack); + break; + case 3: + fprintf(stderr, "CPU: %.4X %-20s | %.2X %.2X %.2X %-20s STACK: %s\n", + pc, trace_instruction, cpu->memory[pc], cpu->memory[pc+1], cpu->memory[pc+2], trace_state, trace_stack); + break; + } + } + + + + + + + /* Delay */ + + if (timebase_info.denom == 0) { + (void) mach_timebase_info(&timebase_info); + } + + uint64_t now = mach_absolute_time(); + + uint64_t elapsed_nano = abs_to_nanos(now - start_time); + uint64_t expected_duration = (i->cycles * (1000000000 / 960000)); + uint64_t delay_nano = expected_duration - elapsed_nano; + + //fprintf(stderr, "Expected: %" PRId64 " Elapsed: %" PRId64 " Delay: %" PRId64 "\n", expected_duration, elapsed_nano, delay_nano); + + mach_wait_until(now + nanos_to_abs(delay_nano)); + + return i->opcode; +} + +static void iom_init(struct iom_t *iom, uint16_t start, uint16_t length, void *obj, iom_read_handler_t read_handler, iom_write_handler_t write_handler) { + memset(iom, 0x00, sizeof(struct iom_t)); + iom->start = start; + iom->length = length; + iom->obj = obj; + iom->read_handler = read_handler; + iom->write_handler = write_handler; +} + +/* Public API */ + +void cpu_init(struct cpu_t *cpu) { + memset(cpu, 0x00, sizeof(struct cpu_t)); + cpu->memory = malloc(64 * 1024); +} + +void cpu_add_ram(struct cpu_t *cpu, uint16_t start, uint16_t length, uint8_t *data) { + if (data != NULL) { + memcpy(cpu->memory + start, data, length); + } + /* TODO: Mark pages as RAM */ +} + +void cpu_add_rom(struct cpu_t *cpu, uint16_t start, uint16_t length, uint8_t *data) { + if (data != NULL) { + memcpy(cpu->memory + start, data, length); + } + /* TODO: Mark pages as ROM */ +} + +void cpu_add_iom(struct cpu_t *cpu, uint16_t start, uint16_t length, void *obj, iom_read_handler_t read_handler, iom_write_handler_t write_handler) { + struct iom_t *iom = (struct iom_t*) malloc(sizeof(struct iom_t)); + iom_init(iom, start, length, obj, read_handler, write_handler); + iom->next = (struct iom_t*) cpu->iom; + cpu->iom = iom; + /* TODO: Mark pages as IO */ +} + +void cpu_trace(struct cpu_t *cpu, uint8_t trace) { + cpu->trace = trace; +} + +void cpu_reset(struct cpu_t *cpu) { + cpu->state.pc = _mem_get_word(cpu, 0xfffc); + cpu->state.sp = 0x01ff; +} + +void cpu_brk(struct cpu_t *cpu) { + +} + +void cpu_irq(struct cpu_t *cpu) { + /* TODO: Implement support for IRQ triggering */ +} + +void cpu_nmi(struct cpu_t *cpu) { + /* TODO: Implement support for interrupt triggering */ +} + +void cpu_run(struct cpu_t *cpu) { + uint64_t instruction_count = 0; + while (cpu_execute_instruction(cpu) != 0x00) { + /* TODO: Tick? */ + instruction_count++; + } + fprintf(stderr, "Executed %" PRId64 " instructions\n", instruction_count); +} + +void cpu_boot(struct cpu_t *cpu) { + cpu_reset(cpu); + cpu_run(cpu); +} + +void cpu_step(struct cpu_t *cpu) { + cpu_execute_instruction(cpu); +} diff --git a/cpu.h b/cpu.h new file mode 100644 index 0000000..67682fa --- /dev/null +++ b/cpu.h @@ -0,0 +1,76 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef CPU_H +#define CPU_H + +#include + +struct iom_t { + uint16_t start; + uint16_t length; + void *obj; + void *read_handler; + void *write_handler; + struct iom_t *next; +}; + +struct cpu_state_t { + uint8_t a, x, y, s; + uint16_t sp; + uint16_t pc; + uint8_t n, v, b, d, i, z, c; +}; + +struct cpu_t { + struct cpu_state_t state; + uint8_t *memory; + uint8_t trace; + struct iom_t *iom; +}; + +typedef uint8_t (*iom_read_handler_t)(struct cpu_t *cpu, void *obj, uint16_t addr); +typedef void (*iom_write_handler_t)(struct cpu_t *cpu, void *obj, uint16_t addr, uint8_t b); + +void cpu_init(struct cpu_t *cpu); +void cpu_add_ram(struct cpu_t *cpu, uint16_t start, uint16_t length, uint8_t *data); +void cpu_add_rom(struct cpu_t *cpu, uint16_t start, uint16_t length, uint8_t *data); +void cpu_add_iom(struct cpu_t *cpu, uint16_t start, uint16_t length, void *obj, iom_read_handler_t read_handler, iom_write_handler_t write_handler); + +void cpu_trace(struct cpu_t *cpu, uint8_t trace); + +void cpu_reset(struct cpu_t *cpu); +void cpu_brk(struct cpu_t *cpu); +void cpu_irq(struct cpu_t *cpu); +void cpu_nmi(struct cpu_t *cpu); + +void cpu_run(struct cpu_t *cpu); +void cpu_boot(struct cpu_t *cpu); +void cpu_step(struct cpu_t *cpu); + +uint16_t cpu_memory_get_word(struct cpu_t *cpu, uint16_t addr); +uint8_t cpu_memory_get_byte(struct cpu_t *cpu, uint16_t addr); + +void cpu_memory_set_word(struct cpu_t *cpu, uint16_t addr, uint16_t v); +void cpu_memory_set_byte(struct cpu_t *cpu, uint16_t addr, uint8_t v); + +#endif diff --git a/ewm.c b/ewm.c new file mode 100644 index 0000000..09c829f --- /dev/null +++ b/ewm.c @@ -0,0 +1,80 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include + +#include "cpu.h" +#include "mem.h" +#include "pia.h" + +static uint8_t monitor[256] = { + 0xD8, 0x58, 0xA0, 0x7F, 0x8C, 0x12, 0xD0, 0xA9, + 0xA7, 0x8D, 0x11, 0xD0, 0x8D, 0x13, 0xD0, 0xC9, + 0xDF, 0xF0, 0x13, 0xC9, 0x9B, 0xF0, 0x03, 0xC8, + 0x10, 0x0F, 0xA9, 0xDC, 0x20, 0xEF, 0xFF, 0xA9, + 0x8D, 0x20, 0xEF, 0xFF, 0xA0, 0x01, 0x88, 0x30, + 0xF6, 0xAD, 0x11, 0xD0, 0x10, 0xFB, 0xAD, 0x10, + 0xD0, 0x99, 0x00, 0x02, 0x20, 0xEF, 0xFF, 0xC9, + 0x8D, 0xD0, 0xD4, 0xA0, 0xFF, 0xA9, 0x00, 0xAA, + 0x0A, 0x85, 0x2B, 0xC8, 0xB9, 0x00, 0x02, 0xC9, + 0x8D, 0xF0, 0xD4, 0xC9, 0xAE, 0x90, 0xF4, 0xF0, + 0xF0, 0xC9, 0xBA, 0xF0, 0xEB, 0xC9, 0xD2, 0xF0, + 0x3B, 0x86, 0x28, 0x86, 0x29, 0x84, 0x2A, 0xB9, + 0x00, 0x02, 0x49, 0xB0, 0xC9, 0x0A, 0x90, 0x06, + 0x69, 0x88, 0xC9, 0xFA, 0x90, 0x11, 0x0A, 0x0A, + 0x0A, 0x0A, 0xA2, 0x04, 0x0A, 0x26, 0x28, 0x26, + 0x29, 0xCA, 0xD0, 0xF8, 0xC8, 0xD0, 0xE0, 0xC4, + 0x2A, 0xF0, 0x97, 0x24, 0x2B, 0x50, 0x10, 0xA5, + 0x28, 0x81, 0x26, 0xE6, 0x26, 0xD0, 0xB5, 0xE6, + 0x27, 0x4C, 0x44, 0xFF, 0x6C, 0x24, 0x00, 0x30, + 0x2B, 0xA2, 0x02, 0xB5, 0x27, 0x95, 0x25, 0x95, + 0x23, 0xCA, 0xD0, 0xF7, 0xD0, 0x14, 0xA9, 0x8D, + 0x20, 0xEF, 0xFF, 0xA5, 0x25, 0x20, 0xDC, 0xFF, + 0xA5, 0x24, 0x20, 0xDC, 0xFF, 0xA9, 0xBA, 0x20, + 0xEF, 0xFF, 0xA9, 0xA0, 0x20, 0xEF, 0xFF, 0xA1, + 0x24, 0x20, 0xDC, 0xFF, 0x86, 0x2B, 0xA5, 0x24, + 0xC5, 0x28, 0xA5, 0x25, 0xE5, 0x29, 0xB0, 0xC1, + 0xE6, 0x24, 0xD0, 0x02, 0xE6, 0x25, 0xA5, 0x24, + 0x29, 0x07, 0x10, 0xC8, 0x48, 0x4A, 0x4A, 0x4A, + 0x4A, 0x20, 0xE5, 0xFF, 0x68, 0x29, 0x0F, 0x09, + 0xB0, 0xC9, 0xBA, 0x90, 0x02, 0x69, 0x06, 0x2C, + 0x12, 0xD0, 0x30, 0xFB, 0x8D, 0x12, 0xD0, 0x60, + 0x00, 0x00, 0x00, 0x0F, 0x00, 0xFF, 0x00, 0x00 +}; + +int main(int argc, char **argv) { + struct pia_t pia; + pia_init(&pia); + pia_trace(&pia, 0); + + struct cpu_t cpu; + cpu_init(&cpu); + cpu_add_ram(&cpu, 0x0000, 0x4000, NULL); + cpu_add_iom(&cpu, 0xd000, 0x1000, &pia, pia_read, pia_write); + cpu_add_rom(&cpu, 0xff00, 0x0100, monitor); + cpu_trace(&cpu, 0); + cpu_boot(&cpu); + + return 0; +} diff --git a/ins.c b/ins.c new file mode 100644 index 0000000..ea617ac --- /dev/null +++ b/ins.c @@ -0,0 +1,1092 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +#include "ins.h" +#include "cpu.h" +#include "mem.h" + +static void update_zn(struct cpu_t *cpu, uint8_t v) { + cpu->state.z = (v == 0x00); + cpu->state.n = (v & 0x80); +} + +/* ADC */ + +static void adc(struct cpu_t *cpu, uint8_t m) { + uint16_t t = (uint16_t)cpu->state.a + (uint16_t)m + (uint16_t)(cpu->state.c ? 1 : 0); + uint8_t r = (uint8_t) (t & 0xff); + cpu->state.c = (t & 0x0100) != 0; + cpu->state.v = (cpu->state.a^r) & (m^r) & 0x80; + cpu->state.a = r; + update_zn(cpu, cpu->state.a); +} + +static void adc_imm(struct cpu_t *cpu, uint8_t oper) { + adc(cpu, oper); +} + +static void adc_zpg(struct cpu_t *cpu, uint8_t oper) { + adc(cpu, mem_get_byte_zpg(cpu, oper)); +} + +static void adc_zpgx(struct cpu_t *cpu, uint8_t oper) { + adc(cpu, mem_get_byte_zpgx(cpu, oper)); +} + +static void adc_abs(struct cpu_t *cpu, uint16_t oper) { + adc(cpu, mem_get_byte_abs(cpu, oper)); +} + +static void adc_absx(struct cpu_t *cpu, uint16_t oper) { + adc(cpu, mem_get_byte_absx(cpu, oper)); +} + +static void adc_absy(struct cpu_t *cpu, uint16_t oper) { + adc(cpu, mem_get_byte_absy(cpu, oper)); +} + +static void adc_indx(struct cpu_t *cpu, uint8_t oper) { + adc(cpu, mem_get_byte_indx(cpu, oper)); +} + +static void adc_indy(struct cpu_t *cpu, uint8_t oper) { + adc(cpu, mem_get_byte_indy(cpu, oper)); +} + +/* AND */ + +static void and(struct cpu_t *cpu, uint8_t m) { + cpu->state.a &= m; + update_zn(cpu, cpu->state.a); +} + +static void and_imm(struct cpu_t *cpu, uint8_t oper) { + and(cpu, oper); +} + +static void and_zpg(struct cpu_t *cpu, uint8_t oper) { + and(cpu, mem_get_byte_zpg(cpu, oper)); +} + +static void and_zpgx(struct cpu_t *cpu, uint8_t oper) { + and(cpu, mem_get_byte_zpgx(cpu, oper)); +} + +static void and_abs(struct cpu_t *cpu, uint16_t oper) { + and(cpu, mem_get_byte_abs(cpu, oper)); +} + +static void and_absx(struct cpu_t *cpu, uint16_t oper) { + and(cpu, mem_get_byte_absx(cpu, oper)); +} + +static void and_absy(struct cpu_t *cpu, uint16_t oper) { + and(cpu, mem_get_byte_absy(cpu, oper)); +} + +static void and_indx(struct cpu_t *cpu, uint8_t oper) { + and(cpu, mem_get_byte_indx(cpu, oper)); +} + +static void and_indy(struct cpu_t *cpu, uint8_t oper) { + and(cpu, mem_get_byte_indy(cpu, oper)); +} + +/* ASL */ + +static uint8_t asl(struct cpu_t *cpu, uint8_t b) { + cpu->state.c = (b & 0x80); + b <<= 1; + cpu->state.n = (b & 0x80); + cpu->state.z = (b == 0); + return b; +} + +static void asl_acc(struct cpu_t *cpu) { + cpu->state.a = asl(cpu, cpu->state.a); +} + +static void asl_zpg(struct cpu_t *cpu, uint8_t oper) { + mem_mod_byte_zpg(cpu, oper, asl); +} + +static void asl_zpgx(struct cpu_t *cpu, uint8_t oper) { + mem_mod_byte_zpgx(cpu, oper, asl); +} + +static void asl_abs(struct cpu_t *cpu, uint16_t oper) { + mem_mod_byte_abs(cpu, oper, asl); +} + +static void asl_absx(struct cpu_t *cpu, uint16_t oper) { + mem_mod_byte_absx(cpu, oper, asl); +} + +/* BIT */ + +static void bit(struct cpu_t *cpu, uint8_t m) { + uint8_t t = cpu->state.a & m; + cpu->state.n = (m & 0x80); + cpu->state.v = (m & 0x40); + cpu->state.z = (t == 0); +} + +static void bit_zpg(struct cpu_t *cpu, uint8_t oper) { + bit(cpu, mem_get_byte_zpg(cpu, oper)); +} + +static void bit_abs(struct cpu_t *cpu, uint16_t oper) { + bit(cpu, mem_get_byte_abs(cpu, oper)); +} + +/* Bxx Branches */ + +static void bcc(struct cpu_t *cpu, uint8_t oper) { + if (cpu->state.c == 0) { + cpu->state.pc += (int8_t) oper; + } +} + +static void bcs(struct cpu_t *cpu, uint8_t oper) { + if (cpu->state.c) { + cpu->state.pc += (int8_t) oper; + } +} + +static void beq(struct cpu_t *cpu, uint8_t oper) { + if (cpu->state.z) { + cpu->state.pc += (int8_t) oper; + } +} + +static void bmi(struct cpu_t *cpu, uint8_t oper) { + if (cpu->state.n) { + cpu->state.pc += (int8_t) oper; + } +} + +static void bne(struct cpu_t *cpu, uint8_t oper) { + if (!cpu->state.z) { + cpu->state.pc += (int8_t) oper; + } +} + +static void bpl(struct cpu_t *cpu, uint8_t oper) { + if (!cpu->state.n) { + cpu->state.pc += (int8_t) oper; + } +} + +static void bvc(struct cpu_t *cpu, uint8_t oper) { + if (!cpu->state.v) { + cpu->state.pc += (int8_t) oper; + } +} + +static void bvs(struct cpu_t *cpu, uint8_t oper) { + if (cpu->state.v) { + cpu->state.pc += (int8_t) oper; + } +} + +/* BRK */ + +static void brk(struct cpu_t *cpu) { + // TODO +} + +/* CLx */ + +static void clc(struct cpu_t *cpu) { + cpu->state.c = 0; +} + +static void cld(struct cpu_t *cpu) { + cpu->state.d = 0; +} + +static void cli(struct cpu_t *cpu) { + cpu->state.i = 0; +} + +static void clv(struct cpu_t *cpu) { + cpu->state.v = 0; +} + +/* CMP */ + +static void cmp(struct cpu_t *cpu, uint8_t m) { + uint8_t t = cpu->state.a - m; + cpu->state.c = (cpu->state.a >= m); + cpu->state.n = (t & 0x80); + cpu->state.z = (t == 0); +} + +static void cmp_imm(struct cpu_t *cpu, uint8_t oper) { + cmp(cpu, oper); +} + +static void cmp_zpg(struct cpu_t *cpu, uint8_t oper) { + cmp(cpu, mem_get_byte_zpg(cpu, oper)); +} + +static void cmp_zpgx(struct cpu_t *cpu, uint8_t oper) { + cmp(cpu, mem_get_byte_zpgx(cpu, oper)); +} + +static void cmp_abs(struct cpu_t *cpu, uint16_t oper) { + cmp(cpu, mem_get_byte_abs(cpu, oper)); +} + +static void cmp_absx(struct cpu_t *cpu, uint16_t oper) { + cmp(cpu, mem_get_byte_absx(cpu, oper)); +} + +static void cmp_absy(struct cpu_t *cpu, uint16_t oper) { + cmp(cpu, mem_get_byte_absy(cpu, oper)); +} + +static void cmp_indx(struct cpu_t *cpu, uint8_t oper) { + cmp(cpu, mem_get_byte_indx(cpu, oper)); +} + +static void cmp_indy(struct cpu_t *cpu, uint8_t oper) { + cmp(cpu, mem_get_byte_indy(cpu, oper)); +} + +/* CPX */ + +static void cpx(struct cpu_t *cpu, uint8_t m) { + uint8_t t = cpu->state.x - m; + cpu->state.c = (cpu->state.x >= m); + update_zn(cpu, t); +} + +static void cpx_imm(struct cpu_t *cpu, uint8_t oper) { + cpx(cpu, oper); +} + +static void cpx_zpg(struct cpu_t *cpu, uint8_t oper) { + cpx(cpu, mem_get_byte_zpg(cpu, oper)); +} + +static void cpx_abs(struct cpu_t *cpu, uint16_t oper) { + cpx(cpu, mem_get_byte_abs(cpu, oper)); +} + +/* CPY */ + +static void cpy(struct cpu_t *cpu, uint8_t m) { + uint8_t t = cpu->state.y - m; + cpu->state.c = (cpu->state.y >= m); + update_zn(cpu, t); +} + +static void cpy_imm(struct cpu_t *cpu, uint8_t oper) { + cpy(cpu, oper); +} + +static void cpy_zpg(struct cpu_t *cpu, uint8_t oper) { + cpy(cpu, mem_get_byte_zpg(cpu, oper)); +} + +static void cpy_abs(struct cpu_t *cpu, uint16_t oper) { + cpy(cpu, mem_get_byte_abs(cpu, oper)); +} + +/* DEx */ + +static uint8_t dec(struct cpu_t *cpu, uint8_t b) { + uint8_t t = b - 1; + update_zn(cpu, t); + return t; +} + +static void dec_zpg(struct cpu_t *cpu, uint8_t oper) { + mem_mod_byte_zpg(cpu, oper, dec); +} + +static void dec_zpgx(struct cpu_t *cpu, uint8_t oper) { + mem_mod_byte_zpgx(cpu, oper, dec); +} + +static void dec_abs(struct cpu_t *cpu, uint16_t oper) { + mem_mod_byte_abs(cpu, oper, dec); +} + +static void dec_absx(struct cpu_t *cpu, uint16_t oper) { + mem_mod_byte_absx(cpu, oper, dec); +} + +static void dex(struct cpu_t *cpu) { + cpu->state.x--; + update_zn(cpu, cpu->state.x); +} + +static void dey(struct cpu_t *cpu) { + cpu->state.y--; + update_zn(cpu, cpu->state.y); +} + +/* EOR */ + +static void eor(struct cpu_t *cpu, uint8_t m) { + cpu->state.a ^= m; + update_zn(cpu, cpu->state.a); +} + +static void eor_imm(struct cpu_t *cpu, uint8_t oper) { + eor(cpu, oper); +} + +static void eor_zpg(struct cpu_t *cpu, uint8_t oper) { + eor(cpu, mem_get_byte_zpg(cpu, oper)); +} + +static void eor_zpgx(struct cpu_t *cpu, uint8_t oper) { + eor(cpu, mem_get_byte_zpgx(cpu, oper)); +} + +static void eor_abs(struct cpu_t *cpu, uint16_t oper) { + eor(cpu, mem_get_byte_abs(cpu, oper)); +} + +static void eor_absx(struct cpu_t *cpu, uint16_t oper) { + eor(cpu, mem_get_byte_absx(cpu, oper)); +} + +static void eor_absy(struct cpu_t *cpu, uint16_t oper) { + eor(cpu, mem_get_byte_absy(cpu, oper)); +} + +static void eor_indx(struct cpu_t *cpu, uint8_t oper) { + eor(cpu, mem_get_byte_indx(cpu, oper)); +} + +static void eor_indy(struct cpu_t *cpu, uint8_t oper) { + eor(cpu, mem_get_byte_indy(cpu, oper)); +} + +/* INx */ + +static uint8_t inc(struct cpu_t *cpu, uint8_t b) { + uint8_t t = b + 1; + update_zn(cpu, t); + return t; +} + +static void inc_zpg(struct cpu_t *cpu, uint8_t oper) { + mem_mod_byte_zpg(cpu, oper, inc); +} + +static void inc_zpgx(struct cpu_t *cpu, uint8_t oper) { + mem_mod_byte_zpgx(cpu, oper, inc); +} + +static void inc_abs(struct cpu_t *cpu, uint16_t oper) { + mem_mod_byte_abs(cpu, oper, inc); +} + +static void inc_absx(struct cpu_t *cpu, uint16_t oper) { + mem_mod_byte_absx(cpu, oper, inc); +} + +static void inx(struct cpu_t *cpu) { + cpu->state.x++; + update_zn(cpu, cpu->state.x); +} + +static void iny(struct cpu_t *cpu) { + cpu->state.y++; + update_zn(cpu, cpu->state.y); +} + +/* JMP */ + +static void jmp_abs(struct cpu_t *cpu, uint16_t oper) { + cpu->state.pc = oper; +} + +static void jmp_ind(struct cpu_t *cpu, uint16_t oper) { + cpu->state.pc = mem_get_word(cpu, oper); +} + +/* JSR */ + +static void jsr_abs(struct cpu_t *cpu, uint16_t oper) { + cpu->state.sp -= 2; + mem_set_word(cpu, cpu->state.sp, cpu->state.pc-1); + cpu->state.pc = oper; +} + +/* LDA */ + +static void lda_imm(struct cpu_t *cpu, uint8_t oper) { + cpu->state.a = oper; + update_zn(cpu, cpu->state.a); +} + +static void lda_zpg(struct cpu_t *cpu, uint8_t oper) { + cpu->state.a = mem_get_byte_zpg(cpu, oper); + update_zn(cpu, cpu->state.a); +} + +static void lda_zpgx(struct cpu_t *cpu, uint8_t oper) { + cpu->state.a = mem_get_byte_zpgx(cpu, oper); + update_zn(cpu, cpu->state.a); +} + +static void lda_abs(struct cpu_t *cpu, uint16_t oper) { + cpu->state.a = mem_get_byte_abs(cpu, oper); + update_zn(cpu, cpu->state.a); +} + +static void lda_absx(struct cpu_t *cpu, uint16_t oper) { + cpu->state.a = mem_get_byte_absx(cpu, oper); + update_zn(cpu, cpu->state.a); +} + +static void lda_absy(struct cpu_t *cpu, uint16_t oper) { + cpu->state.a = mem_get_byte_absy(cpu, oper); + update_zn(cpu, cpu->state.a); +} + +static void lda_indx(struct cpu_t *cpu, uint8_t oper) { + cpu->state.a = mem_get_byte_indx(cpu, oper); + update_zn(cpu, cpu->state.a); +} + +static void lda_indy(struct cpu_t *cpu, uint8_t oper) { + cpu->state.a = mem_get_byte_indy(cpu, oper); + update_zn(cpu, cpu->state.a); +} + +/* LDX */ + +static void ldx_imm(struct cpu_t *cpu, uint8_t oper) { + cpu->state.x = oper; + update_zn(cpu, cpu->state.x); +} + +static void ldx_zpg(struct cpu_t *cpu, uint8_t oper) { + cpu->state.x = mem_get_byte_zpg(cpu, oper); + update_zn(cpu, cpu->state.x); +} + +static void ldx_zpgy(struct cpu_t *cpu, uint8_t oper) { + cpu->state.x = mem_get_byte_zpgy(cpu, oper); + update_zn(cpu, cpu->state.x); +} + +static void ldx_abs(struct cpu_t *cpu, uint16_t oper) { + cpu->state.x = mem_get_byte_abs(cpu, oper); + update_zn(cpu, cpu->state.x); +} + +static void ldx_absy(struct cpu_t *cpu, uint16_t oper) { + cpu->state.x = mem_get_byte_absy(cpu, oper); + update_zn(cpu, cpu->state.x); +} + +/* LDY */ + +static void ldy_imm(struct cpu_t *cpu, uint8_t oper) { + cpu->state.y = oper; + update_zn(cpu, cpu->state.y); +} + +static void ldy_zpg(struct cpu_t *cpu, uint8_t oper) { + cpu->state.y = mem_get_byte_zpg(cpu, oper); + update_zn(cpu, cpu->state.y); +} + +static void ldy_zpgx(struct cpu_t *cpu, uint8_t oper) { + cpu->state.y = mem_get_byte_zpgx(cpu, oper); + update_zn(cpu, cpu->state.y); +} + +static void ldy_abs(struct cpu_t *cpu, uint16_t oper) { + cpu->state.y = mem_get_byte_abs(cpu, oper); + update_zn(cpu, cpu->state.y); +} + +static void ldy_absx(struct cpu_t *cpu, uint16_t oper) { + cpu->state.y = mem_get_byte_absx(cpu, oper); + update_zn(cpu, cpu->state.y); +} + +/* LSR */ + +static uint8_t lsr(struct cpu_t *cpu, uint8_t b) { + cpu->state.c = (b & 1); + b >>= 1; + update_zn(cpu, b); + return b; +} + +static void lsr_acc(struct cpu_t *cpu) { + cpu->state.a = lsr(cpu, cpu->state.a); +} + +static void lsr_zpg(struct cpu_t *cpu, uint8_t oper) { + mem_mod_byte_zpg(cpu, oper, lsr); +} + +static void lsr_zpgx(struct cpu_t *cpu, uint8_t oper) { + mem_mod_byte_zpgx(cpu, oper, lsr); +} + +static void lsr_abs(struct cpu_t *cpu, uint16_t oper) { + mem_mod_byte_abs(cpu, oper, lsr); +} +static void lsr_absx(struct cpu_t *cpu, uint16_t oper) { + mem_mod_byte_absx(cpu, oper, lsr); +} + +/* NOP */ + +static void nop(struct cpu_t *cpu) { +} + +/* ORA */ + +static void ora(struct cpu_t *cpu, uint8_t m) { + cpu->state.a |= m; + update_zn(cpu, cpu->state.a); +} + +static void ora_imm(struct cpu_t *cpu, uint8_t oper) { + ora(cpu, oper); +} + +static void ora_zpg(struct cpu_t *cpu, uint8_t oper) { + ora(cpu, mem_get_byte_zpg(cpu, oper)); +} + +static void ora_zpgx(struct cpu_t *cpu, uint8_t oper) { + ora(cpu, mem_get_byte_zpgx(cpu, oper)); +} + +static void ora_abs(struct cpu_t *cpu, uint16_t oper) { + ora(cpu, mem_get_byte_abs(cpu, oper)); +} + +static void ora_absx(struct cpu_t *cpu, uint16_t oper) { + ora(cpu, mem_get_byte_absx(cpu, oper)); +} + +static void ora_absy(struct cpu_t *cpu, uint16_t oper) { + ora(cpu, mem_get_byte_absy(cpu, oper)); +} + +static void ora_indx(struct cpu_t *cpu, uint8_t oper) { + ora(cpu, mem_get_byte_indx(cpu, oper)); +} + +static void ora_indy(struct cpu_t *cpu, uint8_t oper) { + ora(cpu, mem_get_byte_indy(cpu, oper)); +} + + +/* P** */ + +static void pha(struct cpu_t *cpu) { + cpu->state.sp--; + mem_set_byte(cpu, cpu->state.sp, cpu->state.a); +} + +static void pla(struct cpu_t *cpu) { + cpu->state.a = mem_get_byte(cpu, cpu->state.sp); + cpu->state.sp++; + update_zn(cpu, cpu->state.a); +} + +/* ROL */ + +static uint8_t rol(struct cpu_t* cpu, uint8_t b) { + uint8_t carry = cpu->state.c ? 1 : 0; + cpu->state.c = (b & 0x80); + b = (b << 1) | carry; + update_zn(cpu, b); + return b; +} + +static void rol_acc(struct cpu_t *cpu) { + cpu->state.a = rol(cpu, cpu->state.a); +} + +static void rol_zpg(struct cpu_t *cpu, uint8_t oper) { + mem_mod_byte_zpg(cpu, oper, rol); +} + +static void rol_zpgx(struct cpu_t *cpu, uint8_t oper) { + mem_mod_byte_zpgx(cpu, oper, rol); +} + +static void rol_abs(struct cpu_t *cpu, uint16_t oper) { + mem_mod_byte_abs(cpu, oper, rol); +} + +static void rol_absx(struct cpu_t *cpu, uint16_t oper) { + mem_mod_byte_absx(cpu, oper, rol); +} + +/* ROR */ + +static uint8_t ror(struct cpu_t* cpu, uint8_t b) { + uint8_t carry = cpu->state.c ? 1 : 0; + cpu->state.c = (b & 0x01); + b = (b >> 1) | (carry << 7); + update_zn(cpu, b); + return b; +} + +static void ror_acc(struct cpu_t *cpu) { + cpu->state.a = ror(cpu, cpu->state.a); +} + +static void ror_zpg(struct cpu_t *cpu, uint8_t oper) { + mem_mod_byte_zpg(cpu, oper, ror); +} + +static void ror_zpgx(struct cpu_t *cpu, uint8_t oper) { + mem_mod_byte_zpgx(cpu, oper, ror); +} + +static void ror_abs(struct cpu_t *cpu, uint16_t oper) { + mem_mod_byte_abs(cpu, oper, ror); +} + +static void ror_absx(struct cpu_t *cpu, uint16_t oper) { + mem_mod_byte_absx(cpu, oper, ror); +} + +/* RTS */ + +static void rts(struct cpu_t *cpu) { + cpu->state.pc = mem_get_word(cpu, cpu->state.sp)+1; + cpu->state.sp += 2; +} + +/* SBC */ + +static void sbc(struct cpu_t *cpu, uint8_t m) { + adc(cpu, m ^ 0xff); +} + +static void sbc_imm(struct cpu_t *cpu, uint8_t oper) { + sbc(cpu, oper); +} + +static void sbc_zpg(struct cpu_t *cpu, uint8_t oper) { + sbc(cpu, mem_get_byte_zpg(cpu, oper)); +} + +static void sbc_zpgx(struct cpu_t *cpu, uint8_t oper) { + sbc(cpu, mem_get_byte_zpgx(cpu, oper)); +} + +static void sbc_abs(struct cpu_t *cpu, uint16_t oper) { + sbc(cpu, mem_get_byte_abs(cpu, oper)); +} + +static void sbc_absx(struct cpu_t *cpu, uint16_t oper) { + sbc(cpu, mem_get_byte_absx(cpu, oper)); +} + +static void sbc_absy(struct cpu_t *cpu, uint16_t oper) { + sbc(cpu, mem_get_byte_absy(cpu, oper)); +} + +static void sbc_indx(struct cpu_t *cpu, uint8_t oper) { + sbc(cpu, mem_get_byte_indx(cpu, oper)); +} + +static void sbc_indy(struct cpu_t *cpu, uint8_t oper) { + sbc(cpu, mem_get_byte_indy(cpu, oper)); +} + +/* STA */ + +static void sta_zpg(struct cpu_t *cpu, uint8_t oper) { + mem_set_byte_zpg(cpu, oper, cpu->state.a); +} + +static void sta_zpgx(struct cpu_t *cpu, uint8_t oper) { + mem_set_byte_zpgx(cpu, oper, cpu->state.a); +} + +static void sta_abs(struct cpu_t *cpu, uint16_t oper) { + mem_set_byte_abs(cpu, oper, cpu->state.a); +} + +static void sta_absx(struct cpu_t *cpu, uint16_t oper) { + mem_set_byte_absx(cpu, oper, cpu->state.a); +} + +static void sta_absy(struct cpu_t *cpu, uint16_t oper) { + mem_set_byte_absy(cpu, oper, cpu->state.a); +} + +static void sta_indx(struct cpu_t *cpu, uint8_t oper) { + mem_set_byte_indx(cpu, oper, cpu->state.a); +} + +static void sta_indy(struct cpu_t *cpu, uint8_t oper) { + mem_set_byte_indy(cpu, oper, cpu->state.a); +} + +/* STX */ + +static void stx_zpg(struct cpu_t *cpu, uint8_t oper) { + mem_set_byte_zpg(cpu, oper, cpu->state.x); +} + +static void stx_zpgy(struct cpu_t *cpu, uint8_t oper) { + mem_set_byte_zpgy(cpu, oper, cpu->state.x); +} + +static void stx_abs(struct cpu_t *cpu, uint16_t oper) { + mem_set_byte_abs(cpu, oper, cpu->state.x); +} + +/* STY */ + +static void sty_zpg(struct cpu_t *cpu, uint8_t oper) { + mem_set_byte_zpg(cpu, oper, cpu->state.y); +} + +static void sty_zpgx(struct cpu_t *cpu, uint8_t oper) { + mem_set_byte_zpgx(cpu, oper, cpu->state.y); +} + +static void sty_abs(struct cpu_t *cpu, uint16_t oper) { + mem_set_byte_abs(cpu, oper, cpu->state.y); +} + +/* Txx */ + +static void tax(struct cpu_t *cpu) { + cpu->state.x = cpu->state.a; + update_zn(cpu, cpu->state.x); +} + +static void tay(struct cpu_t *cpu) { + cpu->state.y = cpu->state.a; + update_zn(cpu, cpu->state.y); +} + +static void tsx(struct cpu_t *cpu) { + cpu->state.x = cpu->state.sp & 0x00ff; + update_zn(cpu, cpu->state.x); +} + +static void txa(struct cpu_t *cpu) { + cpu->state.a = cpu->state.x; + update_zn(cpu, cpu->state.a); +} + +static void txs(struct cpu_t *cpu) { + cpu->state.sp = 0x0100 + cpu->state.x; +} + +static void tya(struct cpu_t *cpu) { + cpu->state.a = cpu->state.y; + update_zn(cpu, cpu->state.a); +} + +/* Unknown instruction handler */ + +static void unknown_instruction(struct cpu_t *cpu) { + fprintf(stderr, "** ??? **\n"); +} + +/* Instruction dispatch table */ + +cpu_instruction_t instructions[256] = { + /* 0x00 */ { "BRK", 0x00, 1, 2, (void*) brk }, + /* 0x01 */ { "ORA", 0x01, 2, 6, (void*) ora_indx }, + /* 0x02 */ { "???", 0x02, 1, 2, (void*) unknown_instruction }, + /* 0x03 */ { "???", 0x03, 1, 2, (void*) unknown_instruction }, + /* 0x04 */ { "???", 0x04, 1, 2, (void*) unknown_instruction }, + /* 0x05 */ { "ORA", 0x05, 2, 2, (void*) ora_zpg }, + /* 0x06 */ { "ASL", 0x06, 2, 5, (void*) asl_zpg }, + /* 0x07 */ { "???", 0x07, 1, 2, (void*) unknown_instruction }, + /* 0x08 */ { "???", 0x08, 1, 2, (void*) unknown_instruction }, + /* 0x09 */ { "ORA", 0x09, 2, 2, (void*) ora_imm }, + /* 0x0a */ { "ASL", 0x0a, 1, 2, (void*) asl_acc }, + /* 0x0b */ { "???", 0x0b, 1, 2, (void*) unknown_instruction }, + /* 0x0c */ { "???", 0x0c, 1, 2, (void*) unknown_instruction }, + /* 0x0d */ { "ORA", 0x0d, 3, 4, (void*) ora_abs }, + /* 0x0e */ { "ASL", 0x0e, 3, 6, (void*) asl_abs }, + /* 0x0f */ { "???", 0x0f, 1, 2, (void*) unknown_instruction }, + /* 0x10 */ { "BPL", 0x10, 2, 2, (void*) bpl }, + /* 0x11 */ { "ORA", 0x11, 2, 5, (void*) ora_indy }, + /* 0x12 */ { "???", 0x12, 1, 2, (void*) unknown_instruction }, + /* 0x13 */ { "???", 0x13, 1, 2, (void*) unknown_instruction }, + /* 0x14 */ { "???", 0x14, 1, 2, (void*) unknown_instruction }, + /* 0x15 */ { "ORA", 0x15, 2, 3, (void*) ora_zpgx }, + /* 0x16 */ { "ASL", 0x16, 2, 6, (void*) asl_zpgx }, + /* 0x17 */ { "???", 0x17, 1, 2, (void*) unknown_instruction }, + /* 0x18 */ { "CLC", 0x18, 1, 2, (void*) clc }, + /* 0x19 */ { "ORA", 0x19, 3, 4, (void*) ora_absy }, + /* 0x1a */ { "???", 0x1a, 1, 2, (void*) unknown_instruction }, + /* 0x1b */ { "???", 0x1b, 1, 2, (void*) unknown_instruction }, + /* 0x1c */ { "???", 0x1c, 1, 2, (void*) unknown_instruction }, + /* 0x1d */ { "ORA", 0x1d, 3, 4, (void*) ora_absx }, + /* 0x1e */ { "ASL", 0x1e, 3, 7, (void*) asl_absx }, + /* 0x1f */ { "???", 0x1f, 1, 2, (void*) unknown_instruction }, + + /* 0x20 */ { "JSR", 0x20, 3, 6, (void*) jsr_abs }, + /* 0x21 */ { "AND", 0x21, 2, 6, (void*) and_indx }, + /* 0x22 */ { "???", 0x22, 1, 2, (void*) unknown_instruction }, + /* 0x23 */ { "???", 0x23, 1, 2, (void*) unknown_instruction }, + /* 0x24 */ { "BIT", 0x24, 2, 3, (void*) bit_zpg }, + /* 0x25 */ { "AND", 0x25, 2, 3, (void*) and_zpg }, + /* 0x26 */ { "ROL", 0x26, 2, 5, (void*) rol_zpg }, + /* 0x27 */ { "???", 0x27, 1, 2, (void*) unknown_instruction }, + /* 0x28 */ { "???", 0x28, 1, 2, (void*) unknown_instruction }, + /* 0x29 */ { "AND", 0x29, 2, 2, (void*) and_imm }, + /* 0x2a */ { "ROL", 0x2a, 1, 2, (void*) rol_acc }, + /* 0x2b */ { "???", 0x2b, 1, 2, (void*) unknown_instruction }, + /* 0x2c */ { "BIT", 0x2c, 3, 4, (void*) bit_abs }, + /* 0x2d */ { "AND", 0x2d, 3, 4, (void*) and_abs }, + /* 0x2e */ { "ROL", 0x2e, 3, 6, (void*) rol_abs }, + /* 0x2f */ { "???", 0x2f, 1, 2, (void*) unknown_instruction }, + /* 0x30 */ { "BMI", 0x30, 2, 2, (void*) bmi }, + /* 0x31 */ { "AND", 0x31, 2, 5, (void*) and_indy }, + /* 0x32 */ { "???", 0x32, 1, 2, (void*) unknown_instruction }, + /* 0x33 */ { "???", 0x33, 1, 2, (void*) unknown_instruction }, + /* 0x34 */ { "???", 0x34, 1, 2, (void*) unknown_instruction }, + /* 0x35 */ { "AND", 0x35, 2, 4, (void*) and_zpgx }, + /* 0x36 */ { "ROL", 0x36, 2, 6, (void*) rol_zpgx }, + /* 0x37 */ { "???", 0x37, 1, 2, (void*) unknown_instruction }, + /* 0x38 */ { "???", 0x38, 1, 2, (void*) unknown_instruction }, + /* 0x39 */ { "AND", 0x39, 3, 4, (void*) and_absy }, + /* 0x3a */ { "???", 0x3a, 1, 2, (void*) unknown_instruction }, + /* 0x3b */ { "???", 0x3b, 1, 2, (void*) unknown_instruction }, + /* 0x3c */ { "???", 0x3c, 1, 2, (void*) unknown_instruction }, + /* 0x3d */ { "AND", 0x3d, 3, 4, (void*) and_absx }, + /* 0x3e */ { "ROL", 0x3e, 3, 7, (void*) rol_absx }, + /* 0x3f */ { "???", 0x3f, 1, 2, (void*) unknown_instruction }, + + /* 0x40 */ { "???", 0x40, 1, 2, (void*) unknown_instruction }, + /* 0x41 */ { "EOR", 0x41, 2, 6, (void*) eor_indx }, + /* 0x42 */ { "???", 0x42, 1, 2, (void*) unknown_instruction }, + /* 0x43 */ { "???", 0x43, 1, 2, (void*) unknown_instruction }, + /* 0x44 */ { "???", 0x44, 1, 2, (void*) unknown_instruction }, + /* 0x45 */ { "EOR", 0x45, 2, 3, (void*) eor_zpg }, + /* 0x46 */ { "LSR", 0x46, 2, 5, (void*) lsr_zpg }, + /* 0x47 */ { "???", 0x47, 1, 2, (void*) unknown_instruction }, + /* 0x48 */ { "PHA", 0x48, 1, 3, (void*) pha }, + /* 0x49 */ { "EOR", 0x49, 2, 2, (void*) eor_imm }, + /* 0x4a */ { "LSR", 0x4a, 1, 2, (void*) lsr_acc }, + /* 0x4b */ { "???", 0x4b, 1, 2, (void*) unknown_instruction }, + /* 0x4c */ { "JMP", 0x4c, 3, 3, (void*) jmp_abs }, + /* 0x4d */ { "EOR", 0x4d, 3, 4, (void*) eor_abs }, + /* 0x4e */ { "LSR", 0x4e, 3, 6, (void*) lsr_abs }, + /* 0x4f */ { "???", 0x4f, 1, 2, (void*) unknown_instruction }, + /* 0x50 */ { "BVC", 0x50, 2, 2, (void*) bvc }, + /* 0x51 */ { "EOR", 0x51, 2, 5, (void*) eor_indy }, + /* 0x52 */ { "???", 0x52, 1, 2, (void*) unknown_instruction }, + /* 0x53 */ { "???", 0x53, 1, 2, (void*) unknown_instruction }, + /* 0x54 */ { "???", 0x54, 1, 2, (void*) unknown_instruction }, + /* 0x55 */ { "EOR", 0x55, 2, 4, (void*) eor_zpgx }, + /* 0x56 */ { "LSR", 0x56, 2, 6, (void*) lsr_zpgx }, + /* 0x57 */ { "???", 0x57, 1, 2, (void*) unknown_instruction }, + /* 0x58 */ { "CLI", 0x58, 1, 2, (void*) cli }, + /* 0x59 */ { "EOR", 0x59, 3, 4, (void*) eor_absy }, + /* 0x5a */ { "???", 0x5a, 1, 2, (void*) unknown_instruction }, + /* 0x5b */ { "???", 0x5b, 1, 2, (void*) unknown_instruction }, + /* 0x5c */ { "???", 0x5c, 1, 2, (void*) unknown_instruction }, + /* 0x5d */ { "EOR", 0x5d, 3, 4, (void*) eor_absx }, + /* 0x5e */ { "LSR", 0x5e, 3, 7, (void*) lsr_absx }, + /* 0x5f */ { "???", 0x5f, 1, 2, (void*) unknown_instruction }, + + /* 0x60 */ { "RTS", 0x60, 1, 6, (void*) rts }, + /* 0x61 */ { "ADC", 0x61, 2, 6, (void*) adc_indx }, + /* 0x62 */ { "???", 0x62, 1, 2, (void*) unknown_instruction }, + /* 0x63 */ { "???", 0x63, 1, 2, (void*) unknown_instruction }, + /* 0x64 */ { "???", 0x64, 1, 2, (void*) unknown_instruction }, + /* 0x65 */ { "ADC", 0x65, 2, 3, (void*) adc_zpg }, + /* 0x66 */ { "ROR", 0x66, 2, 5, (void*) ror_zpg }, + /* 0x67 */ { "???", 0x67, 1, 2, (void*) unknown_instruction }, + /* 0x68 */ { "PLA", 0x68, 1, 4, (void*) pla }, + /* 0x69 */ { "ADC", 0x69, 2, 2, (void*) adc_imm }, + /* 0x6a */ { "ROR", 0x6a, 1, 2, (void*) ror_acc }, + /* 0x6b */ { "???", 0x6b, 1, 2, (void*) unknown_instruction }, + /* 0x6c */ { "JMP", 0x6c, 3, 5, (void*) jmp_ind }, + /* 0x6d */ { "ADC", 0x6d, 3, 4, (void*) adc_abs }, + /* 0x6e */ { "ROR", 0x6e, 3, 6, (void*) ror_abs }, + /* 0x6f */ { "???", 0x6f, 1, 2, (void*) unknown_instruction }, + /* 0x70 */ { "BVS", 0x70, 2, 2, (void*) bvs }, + /* 0x71 */ { "ADC", 0x71, 2, 5, (void*) adc_indy }, + /* 0x72 */ { "???", 0x72, 1, 2, (void*) unknown_instruction }, + /* 0x73 */ { "???", 0x73, 1, 2, (void*) unknown_instruction }, + /* 0x74 */ { "???", 0x74, 1, 2, (void*) unknown_instruction }, + /* 0x75 */ { "ADC", 0x75, 2, 4, (void*) adc_zpgx }, + /* 0x76 */ { "ROR", 0x76, 2, 6, (void*) ror_zpgx }, + /* 0x77 */ { "???", 0x77, 1, 2, (void*) unknown_instruction }, + /* 0x78 */ { "???", 0x78, 1, 2, (void*) unknown_instruction }, + /* 0x79 */ { "ADC", 0x79, 3, 4, (void*) adc_absy }, + /* 0x7a */ { "???", 0x7a, 1, 2, (void*) unknown_instruction }, + /* 0x7b */ { "???", 0x7b, 1, 2, (void*) unknown_instruction }, + /* 0x7c */ { "???", 0x7c, 1, 2, (void*) unknown_instruction }, + /* 0x7d */ { "ADC", 0x7d, 3, 4, (void*) adc_absx }, + /* 0x7e */ { "ROR", 0x7e, 3, 7, (void*) ror_absx }, + /* 0x7f */ { "???", 0x7f, 1, 2, (void*) unknown_instruction }, + + /* 0x80 */ { "???", 0x80, 1, 2, (void*) unknown_instruction }, + /* 0x81 */ { "STA", 0x81, 2, 6, (void*) sta_indx }, + /* 0x82 */ { "???", 0x82, 1, 2, (void*) unknown_instruction }, + /* 0x83 */ { "???", 0x83, 1, 2, (void*) unknown_instruction }, + /* 0x84 */ { "STY", 0x84, 2, 3, (void*) sty_zpg }, + /* 0x85 */ { "STA", 0x85, 2, 3, (void*) sta_zpg }, + /* 0x86 */ { "STX", 0x86, 2, 3, (void*) stx_zpg }, + /* 0x87 */ { "???", 0x87, 1, 2, (void*) unknown_instruction }, + /* 0x88 */ { "DEY", 0x88, 1, 2, (void*) dey }, + /* 0x89 */ { "???", 0x89, 1, 2, (void*) unknown_instruction }, + /* 0x8a */ { "TXA", 0x8a, 1, 2, (void*) txa }, + /* 0x8b */ { "???", 0x8b, 1, 2, (void*) unknown_instruction }, + /* 0x8c */ { "STY", 0x8c, 3, 4, (void*) sty_abs }, + /* 0x8d */ { "STA", 0x8d, 3, 4, (void*) sta_abs }, + /* 0x8e */ { "STX", 0x8e, 3, 4, (void*) stx_abs }, + /* 0x8f */ { "???", 0x8f, 1, 2, (void*) unknown_instruction }, + /* 0x90 */ { "BCC", 0x90, 2, 2, (void*) bcc }, + /* 0x91 */ { "STA", 0x91, 2, 6, (void*) sta_indy }, + /* 0x92 */ { "???", 0x92, 1, 2, (void*) unknown_instruction }, + /* 0x93 */ { "???", 0x93, 1, 2, (void*) unknown_instruction }, + /* 0x94 */ { "STY", 0x94, 2, 4, (void*) sty_zpgx }, + /* 0x95 */ { "STA", 0x95, 2, 4, (void*) sta_zpgx }, + /* 0x96 */ { "STX", 0x96, 2, 4, (void*) stx_zpgy }, + /* 0x97 */ { "???", 0x97, 1, 2, (void*) unknown_instruction }, + /* 0x98 */ { "TYA", 0x98, 1, 2, (void*) tya }, + /* 0x99 */ { "STA", 0x99, 3, 5, (void*) sta_absy }, + /* 0x9a */ { "TXS", 0x9a, 1, 2, (void*) txs }, + /* 0x9b */ { "???", 0x9b, 1, 2, (void*) unknown_instruction }, + /* 0x9c */ { "???", 0x9c, 1, 2, (void*) unknown_instruction }, + /* 0x9d */ { "STA", 0x9d, 2, 5, (void*) sta_absx }, + /* 0x9e */ { "???", 0x9e, 1, 2, (void*) unknown_instruction }, + /* 0x9f */ { "???", 0x9f, 1, 2, (void*) unknown_instruction }, + + /* 0xa0 */ { "LDY", 0xa0, 2, 2, (void*) ldy_imm }, + /* 0xa1 */ { "LDA", 0xa1, 2, 6, (void*) lda_indx }, + /* 0xa2 */ { "LDX", 0xa2, 2, 2, (void*) ldx_imm }, + /* 0xa3 */ { "???", 0xa3, 1, 2, (void*) unknown_instruction }, + /* 0xa4 */ { "LDY", 0xa4, 2, 3, (void*) ldy_zpg }, + /* 0xa5 */ { "LDA", 0xa5, 2, 3, (void*) lda_zpg }, + /* 0xa6 */ { "LDX", 0xa6, 2, 3, (void*) ldx_zpg }, + /* 0xa7 */ { "???", 0xa7, 1, 2, (void*) unknown_instruction }, + /* 0xa8 */ { "TAY", 0xa8, 1, 2, (void*) tay }, + /* 0xa9 */ { "LDA", 0xa9, 2, 2, (void*) lda_imm }, + /* 0xaa */ { "TAX", 0xaa, 1, 2, (void*) tax }, + /* 0xab */ { "???", 0xab, 1, 2, (void*) unknown_instruction }, + /* 0xac */ { "LDY", 0xac, 3, 4, (void*) ldy_abs }, + /* 0xad */ { "LDA", 0xad, 3, 4, (void*) lda_abs }, + /* 0xae */ { "LDX", 0xae, 3, 4, (void*) ldx_abs }, + /* 0xaf */ { "???", 0xaf, 1, 2, (void*) unknown_instruction }, + /* 0xb0 */ { "BCS", 0xb0, 2, 2, (void*) bcs }, + /* 0xb1 */ { "LDA", 0xb1, 2, 5, (void*) lda_indy }, + /* 0xb2 */ { "???", 0xb2, 1, 2, (void*) unknown_instruction }, + /* 0xb3 */ { "???", 0xb3, 1, 2, (void*) unknown_instruction }, + /* 0xb4 */ { "LDY", 0xb4, 2, 4, (void*) ldy_zpgx }, + /* 0xb5 */ { "LDA", 0xb5, 2, 4, (void*) lda_zpgx }, + /* 0xb6 */ { "LDX", 0xb6, 2, 4, (void*) ldx_zpgy }, + /* 0xb7 */ { "???", 0xb7, 1, 2, (void*) unknown_instruction }, + /* 0xb8 */ { "CLV", 0xb8, 1, 2, (void*) clv }, + /* 0xb9 */ { "LDA", 0xb9, 3, 4, (void*) lda_absy }, + /* 0xba */ { "TSX", 0xba, 1, 2, (void*) tsx }, + /* 0xbb */ { "???", 0xbb, 1, 2, (void*) unknown_instruction }, + /* 0xbc */ { "LDY", 0xbc, 3, 4, (void*) ldy_absx }, + /* 0xbd */ { "LDA", 0xbd, 3, 4, (void*) lda_absx }, + /* 0xbe */ { "LDX", 0xbe, 3, 4, (void*) ldx_absy }, + /* 0xbf */ { "???", 0xbf, 1, 2, (void*) unknown_instruction }, + + /* 0xc0 */ { "CPY", 0xc0, 2, 2, (void*) cpy_imm }, + /* 0xc1 */ { "CMP", 0xc1, 2, 6, (void*) cmp_indx }, + /* 0xc2 */ { "???", 0xc2, 1, 2, (void*) unknown_instruction }, + /* 0xc3 */ { "???", 0xc3, 1, 2, (void*) unknown_instruction }, + /* 0xc4 */ { "CPY", 0xc4, 2, 3, (void*) cpy_zpg }, + /* 0xc5 */ { "CMP", 0xc5, 2, 3, (void*) cmp_zpg }, + /* 0xc6 */ { "DEC", 0xc6, 2, 5, (void*) dec_zpg }, + /* 0xc7 */ { "???", 0xc7, 1, 2, (void*) unknown_instruction }, + /* 0xc8 */ { "INY", 0xc8, 1, 2, (void*) iny }, + /* 0xc9 */ { "CMP", 0xc9, 2, 2, (void*) cmp_imm }, + /* 0xca */ { "DEX", 0xca, 1, 2, (void*) dex }, + /* 0xcb */ { "???", 0xcb, 1, 2, (void*) unknown_instruction }, + /* 0xcc */ { "CPY", 0xcc, 3, 4, (void*) cpy_abs }, + /* 0xcd */ { "CMP", 0xcd, 3, 4, (void*) cmp_abs }, + /* 0xce */ { "DEC", 0xce, 3, 3, (void*) dec_abs }, + /* 0xcf */ { "???", 0xcf, 1, 2, (void*) unknown_instruction }, + /* 0xd0 */ { "BNE", 0xd0, 2, 2, (void*) bne }, + /* 0xd1 */ { "CMP", 0xd1, 2, 5, (void*) cmp_indy }, + /* 0xd2 */ { "???", 0xd2, 1, 2, (void*) unknown_instruction }, + /* 0xd3 */ { "???", 0xd3, 1, 2, (void*) unknown_instruction }, + /* 0xd4 */ { "???", 0xd4, 1, 2, (void*) unknown_instruction }, + /* 0xd5 */ { "CMP", 0xd5, 2, 4, (void*) cmp_zpgx }, + /* 0xd6 */ { "DEC", 0xd6, 2, 6, (void*) dec_zpgx }, + /* 0xd7 */ { "???", 0xd7, 1, 2, (void*) unknown_instruction }, + /* 0xd8 */ { "CLD", 0xd8, 1, 2, (void*) cld }, + /* 0xd9 */ { "CMP", 0xd9, 3, 4, (void*) cmp_absy }, + /* 0xda */ { "???", 0xda, 1, 2, (void*) unknown_instruction }, + /* 0xdb */ { "???", 0xdb, 1, 2, (void*) unknown_instruction }, + /* 0xdc */ { "???", 0xdc, 1, 2, (void*) unknown_instruction }, + /* 0xdd */ { "CMP", 0xdd, 3, 4, (void*) cmp_absx }, + /* 0xde */ { "DEC", 0xde, 3, 7, (void*) dec_absx }, + /* 0xdf */ { "???", 0xdf, 1, 2, (void*) unknown_instruction }, + + /* 0xe0 */ { "CPX", 0xe0, 2, 2, (void*) cpx_imm }, + /* 0xe1 */ { "SBC", 0xe1, 2, 2, (void*) sbc_indx }, + /* 0xe2 */ { "???", 0xe2, 1, 2, (void*) unknown_instruction }, + /* 0xe3 */ { "???", 0xe3, 1, 2, (void*) unknown_instruction }, + /* 0xe4 */ { "CPX", 0xe4, 2, 3, (void*) cpx_zpg }, + /* 0xe5 */ { "SBC", 0xe5, 2, 2, (void*) sbc_zpg }, + /* 0xe6 */ { "INC", 0xe6, 2, 5, (void*) inc_zpg }, + /* 0xe7 */ { "???", 0xe7, 1, 2, (void*) unknown_instruction }, + /* 0xe8 */ { "INX", 0xe8, 1, 2, (void*) inx }, + /* 0xe9 */ { "SBC", 0xe9, 2, 2, (void*) sbc_imm }, + /* 0xea */ { "NOP", 0xea, 1, 2, (void*) nop }, + /* 0xeb */ { "???", 0xeb, 1, 2, (void*) unknown_instruction }, + /* 0xec */ { "CPX", 0xec, 3, 4, (void*) cpx_abs }, + /* 0xed */ { "SBC", 0xed, 3, 2, (void*) sbc_abs }, + /* 0xee */ { "INC", 0xee, 3, 6, (void*) inc_abs }, + /* 0xef */ { "???", 0xef, 1, 2, (void*) unknown_instruction }, + /* 0xf0 */ { "BEQ", 0xf0, 2, 2, (void*) beq }, + /* 0xf1 */ { "SBC", 0xf1, 1, 2, (void*) sbc_indy }, + /* 0xf2 */ { "???", 0xf2, 1, 2, (void*) unknown_instruction }, + /* 0xf3 */ { "???", 0xf3, 1, 2, (void*) unknown_instruction }, + /* 0xf4 */ { "???", 0xf4, 1, 2, (void*) unknown_instruction }, + /* 0xf5 */ { "SBC", 0xf5, 1, 2, (void*) sbc_zpgx }, + /* 0xf6 */ { "INC", 0xf6, 2, 6, (void*) inc_zpgx }, + /* 0xf7 */ { "???", 0xf7, 1, 2, (void*) unknown_instruction }, + /* 0xf8 */ { "???", 0xf8, 1, 2, (void*) unknown_instruction }, + /* 0xf9 */ { "SBC", 0xf9, 3, 2, (void*) sbc_absy }, + /* 0xfa */ { "???", 0xfa, 1, 2, (void*) unknown_instruction }, + /* 0xfb */ { "???", 0xfb, 1, 2, (void*) unknown_instruction }, + /* 0xfc */ { "???", 0xfc, 1, 2, (void*) unknown_instruction }, + /* 0xfd */ { "SBC", 0xfd, 3, 2, (void*) sbc_absx }, + /* 0xfe */ { "INC", 0xfe, 3, 7, (void*) inc_absx }, + /* 0xff */ { "???", 0xff, 1, 2, (void*) unknown_instruction } +}; diff --git a/ins.h b/ins.h new file mode 100644 index 0000000..b5b8d50 --- /dev/null +++ b/ins.h @@ -0,0 +1,38 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef INS_H +#define INS_H + +#include + +typedef struct { + char *name; + uint8_t opcode; + uint8_t bytes; + uint8_t cycles; + void *handler; +} cpu_instruction_t; + +extern cpu_instruction_t instructions[256]; + +#endif diff --git a/mem.c b/mem.c new file mode 100644 index 0000000..b9b777f --- /dev/null +++ b/mem.c @@ -0,0 +1,162 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "mem.h" + +/* Memory Access */ + +uint8_t mem_get_byte(struct cpu_t *cpu, uint16_t addr) { + if (cpu->iom) { + /* TODO: Assuming there is only one area of memory mapped io */ + if (addr >= cpu->iom->start && addr < (cpu->iom->start + cpu->iom->length)) { + uint8_t v = ((iom_read_handler_t) cpu->iom->read_handler)((struct cpu_t*) cpu, cpu->iom->obj, addr); + //fprintf(stderr, "MEM: GETTING BYTE AT %.4X -> %.2X\n", addr, v); + return v; + } + } + //fprintf(stderr, "MEM: GETTING BYTE AT %.4X -> %.2X\n", addr, cpu->memory[addr]); + return cpu->memory[addr]; +} + +uint8_t mem_get_byte_abs(struct cpu_t *cpu, uint16_t addr) { + return mem_get_byte(cpu, addr); +} + +uint8_t mem_get_byte_absx(struct cpu_t *cpu, uint16_t addr) { + return mem_get_byte(cpu, addr + cpu->state.x); /* TODO: Carry? */ +} + +uint8_t mem_get_byte_absy(struct cpu_t *cpu, uint16_t addr) { + return mem_get_byte(cpu, addr + cpu->state.y); /* TODO: Carry? */ +} + +uint8_t mem_get_byte_zpg(struct cpu_t *cpu, uint8_t addr) { + return mem_get_byte(cpu, addr); +} + +uint8_t mem_get_byte_zpgx(struct cpu_t *cpu, uint8_t addr) { + return mem_get_byte(cpu, ((uint16_t) addr + cpu->state.x) & 0x00ff); +} + +uint8_t mem_get_byte_zpgy(struct cpu_t *cpu, uint8_t addr) { + return mem_get_byte(cpu, ((uint16_t) addr + cpu->state.y) & 0x00ff); +} + +uint8_t mem_get_byte_indx(struct cpu_t *cpu, uint8_t addr) { + return mem_get_byte(cpu, mem_get_word(cpu, addr + cpu->state.x)); // TODO: Does this wrap? +} + +uint8_t mem_get_byte_indy(struct cpu_t *cpu, uint8_t addr) { + return mem_get_byte(cpu, mem_get_word(cpu, addr) + cpu->state.y); +} + + + +void mem_set_byte(struct cpu_t *cpu, uint16_t addr, uint8_t v) { + //fprintf(stderr, "MEM: SETTING BYTE AT %.4X -> %.2X (%c)\n", addr, v, v & 0x7f); + if (cpu->iom) { + /* TODO: Assuming there is only one area of memory mapped io */ + if (addr >= cpu->iom->start && addr < (cpu->iom->start + cpu->iom->length)) { + ((iom_write_handler_t) cpu->iom->write_handler)((struct cpu_t*) cpu, cpu->iom->obj, addr, v); + return; + } + } + cpu->memory[addr] = v; +} + + + +void mem_set_byte_zpg(struct cpu_t *cpu, uint8_t addr, uint8_t v) { + mem_set_byte(cpu, addr, v); +} + +void mem_set_byte_zpgx(struct cpu_t *cpu, uint8_t addr, uint8_t v) { + mem_set_byte(cpu, ((uint16_t) addr + cpu->state.x) & 0x00ff, v); +} + +void mem_set_byte_zpgy(struct cpu_t *cpu, uint8_t addr, uint8_t v) { + mem_set_byte(cpu, ((uint16_t) addr + cpu->state.y) & 0x00ff, v); +} + +void mem_set_byte_abs(struct cpu_t *cpu, uint16_t addr, uint8_t v) { + mem_set_byte(cpu, addr, v); +} + +void mem_set_byte_absx(struct cpu_t *cpu, uint16_t addr, uint8_t v) { + mem_set_byte(cpu, addr+cpu->state.x, v); +} + +void mem_set_byte_absy(struct cpu_t *cpu, uint16_t addr, uint8_t v) { + mem_set_byte(cpu, addr+cpu->state.y, v); +} + +void mem_set_byte_indx(struct cpu_t *cpu, uint8_t addr, uint8_t v) { + mem_set_byte(cpu, mem_get_word(cpu, addr+cpu->state.x), v); // TODO: Does this wrap? +} + +void mem_set_byte_indy(struct cpu_t *cpu, uint8_t addr, uint8_t v) { + mem_set_byte(cpu, mem_get_word(cpu, addr)+cpu->state.y, v); +} + +/* MOD */ + +void mem_mod_byte_zpg(struct cpu_t *cpu, uint8_t addr, mem_mod_t op) { + mem_set_byte_zpg(cpu, addr, op(cpu, mem_get_byte_zpg(cpu, addr))); +} + +void mem_mod_byte_zpgx(struct cpu_t *cpu, uint8_t addr, mem_mod_t op) { + mem_set_byte_zpgx(cpu, addr, op(cpu, mem_get_byte_zpgx(cpu, addr))); +} + +void mem_mod_byte_zpgy(struct cpu_t *cpu, uint8_t addr, mem_mod_t op) { + mem_set_byte_zpgy(cpu, addr, op(cpu, mem_get_byte_zpgy(cpu, addr))); +} + +void mem_mod_byte_abs(struct cpu_t *cpu, uint16_t addr, mem_mod_t op) { + mem_set_byte_abs(cpu, addr, op(cpu, mem_get_byte_abs(cpu, addr))); +} + +void mem_mod_byte_absx(struct cpu_t *cpu, uint16_t addr, mem_mod_t op) { + mem_set_byte_absx(cpu, addr, op(cpu, mem_get_byte_absx(cpu, addr))); +} + +void mem_mod_byte_absy(struct cpu_t *cpu, uint16_t addr, mem_mod_t op) { + mem_set_byte_absy(cpu, addr, op(cpu, mem_get_byte_absy(cpu, addr))); +} + +void mem_mod_byte_indx(struct cpu_t *cpu, uint8_t addr, mem_mod_t op) { + mem_set_byte_indx(cpu, addr, op(cpu, mem_get_byte_indx(cpu, addr))); +} + +void mem_mod_byte_indy(struct cpu_t *cpu, uint8_t addr, mem_mod_t op) { + mem_set_byte_indy(cpu, addr, op(cpu, mem_get_byte_indy(cpu, addr))); +} + +/* Words */ + +uint16_t mem_get_word(struct cpu_t *cpu, uint16_t addr) { + return *((uint16_t*) &cpu->memory[addr]); +} + +void mem_set_word(struct cpu_t *cpu, uint16_t addr, uint16_t v) { + *((uint16_t*) &cpu->memory[addr]) = v; +} diff --git a/mem.h b/mem.h new file mode 100644 index 0000000..737807d --- /dev/null +++ b/mem.h @@ -0,0 +1,62 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef MEM_H +#define MEM_H + +#include "cpu.h" + +typedef uint8_t (*mem_mod_t)(struct cpu_t *cpu, uint8_t b); + +uint8_t mem_get_byte(struct cpu_t *cpu, uint16_t addr); +uint8_t mem_get_byte_abs(struct cpu_t *cpu, uint16_t addr); +uint8_t mem_get_byte_absx(struct cpu_t *cpu, uint16_t addr); +uint8_t mem_get_byte_absy(struct cpu_t *cpu, uint16_t addr); +uint8_t mem_get_byte_zpg(struct cpu_t *cpu, uint8_t addr); +uint8_t mem_get_byte_zpgx(struct cpu_t *cpu, uint8_t addr); +uint8_t mem_get_byte_zpgy(struct cpu_t *cpu, uint8_t addr); +uint8_t mem_get_byte_indx(struct cpu_t *cpu, uint8_t addr); +uint8_t mem_get_byte_indy(struct cpu_t *cpu, uint8_t addr); + +void mem_set_byte(struct cpu_t *cpu, uint16_t addr, uint8_t v); +void mem_set_byte_zpg(struct cpu_t *cpu, uint8_t addr, uint8_t v); +void mem_set_byte_zpgx(struct cpu_t *cpu, uint8_t addr, uint8_t v); +void mem_set_byte_zpgy(struct cpu_t *cpu, uint8_t addr, uint8_t v); +void mem_set_byte_abs(struct cpu_t *cpu, uint16_t addr, uint8_t v); +void mem_set_byte_absx(struct cpu_t *cpu, uint16_t addr, uint8_t v); +void mem_set_byte_absy(struct cpu_t *cpu, uint16_t addr, uint8_t v); +void mem_set_byte_indx(struct cpu_t *cpu, uint8_t addr, uint8_t v); +void mem_set_byte_indy(struct cpu_t *cpu, uint8_t addr, uint8_t v); + +void mem_mod_byte_zpg(struct cpu_t *cpu, uint8_t addr, mem_mod_t op); +void mem_mod_byte_zpgx(struct cpu_t *cpu, uint8_t addr, mem_mod_t op); +void mem_mod_byte_zpgy(struct cpu_t *cpu, uint8_t addr, mem_mod_t op); +void mem_mod_byte_abs(struct cpu_t *cpu, uint16_t addr, mem_mod_t op); +void mem_mod_byte_absx(struct cpu_t *cpu, uint16_t addr, mem_mod_t op); +void mem_mod_byte_absy(struct cpu_t *cpu, uint16_t addr, mem_mod_t op); +void mem_mod_byte_indx(struct cpu_t *cpu, uint8_t addr, mem_mod_t op); +void mem_mod_byte_indy(struct cpu_t *cpu, uint8_t addr, mem_mod_t op); + +uint16_t mem_get_word(struct cpu_t *cpu, uint16_t addr); +void mem_set_word(struct cpu_t *cpu, uint16_t addr, uint16_t v); + +#endif diff --git a/pia.c b/pia.c new file mode 100644 index 0000000..c335a53 --- /dev/null +++ b/pia.c @@ -0,0 +1,119 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include + +#include "cpu.h" +#include "pia.h" + +static void pia_dsp_write(uint8_t b) { + //fprintf(stderr, "PIA: Sending to display: %.2x / %.2x\n", b, b & 0x7f); + b &= 0b01111111; + if (b == '\r') { + b = '\n'; + } + addch(b); + refresh(); +} + +/* static int pia_kbd_read() { */ +/* int c = getch(); */ +/* } */ + +/* static uint8_t pia_kbd_read() { */ +/* return getchar(); */ +/* } */ + +void pia_init(struct pia_t *pia) { + initscr(); + raw(); + noecho(); + pia->a = 0; + pia->cra = 0; + pia->b = 0; + pia->crb = 0; +} + +void pia_trace(struct pia_t *pia, uint8_t trace) { + pia->trace = trace; +} + +uint8_t pia_read(struct cpu_t *cpu, void *obj, uint16_t addr) { + struct pia_t *pia = (struct pia_t*) obj; + uint8_t result = 0; + switch (addr) { + case 0xd010: /* KBD */ + result = pia->a; + break; + case 0xd011: /* KBDCR */ { + int c = getch(); + if (c != ERR) { + /* TODO: Remove this, this is not how we want to stop the emulator */ + if (c == 3) { + exit(1); + } + if (c == '\n') { + c = '\r'; + } + pia->a = c | 0x80; + } + result = (c == ERR) ? 0x00 : 0x80; + break; + } + case 0xd012: /* DSP */ + result = 0; + break; + case 0xd013: /* DSPCR */ + result = 0; + break; + } + if (pia->trace) { + fprintf(stderr, "PIA: READ BYTE %.2X FROM %.4X\n", result, addr); + } + return result; +} + +void pia_write(struct cpu_t *cpu, void *obj, uint16_t addr, uint8_t b) { + struct pia_t *pia = (struct pia_t*) obj; + if (pia->trace) { + fprintf(stderr, "PIA: WRITING BYTE %.2X TO %.4X\n", b, addr); + } + switch (addr) { + case 0xd010: /* KBD */ + break; + case 0xd011: /* KBDCR */ + pia->cra = b; + break; + case 0xd012: /* DSP */ + if (pia->crb != 0x00) { /* TODO: Check the actual flag */ + pia_dsp_write(b); + } + break; + case 0xd013: /* DSPCR */ + pia->crb = b; + break; + } +} diff --git a/pia.h b/pia.h new file mode 100644 index 0000000..365429b --- /dev/null +++ b/pia.h @@ -0,0 +1,42 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef PIA_H +#define PIA_H + +#include +#include "cpu.h" + +struct pia_t { + uint8_t a; + uint8_t cra; + uint8_t b; + uint8_t crb; + uint8_t trace; +}; + +void pia_init(struct pia_t *pia); +void pia_trace(struct pia_t *pia, uint8_t trace); +uint8_t pia_read(struct cpu_t *cpu, void *obj, uint16_t addr); +void pia_write(struct cpu_t *cpu, void *obj, uint16_t addr, uint8_t b); + +#endif