From cebee660765edbed367df9528a42ba89c544c2c1 Mon Sep 17 00:00:00 2001 From: william Date: Sat, 6 Jan 2024 14:27:09 -0500 Subject: [PATCH] Things --- .idea/workspace.xml | 237 ++- CMakeLists.txt | 9 +- conandata.yml | 1 + cpu/CMakeLists.txt | 1 - cpu/cpu.c | 152 +- cpu/cpu.h | 102 +- cpu/memory.c | 83 +- cpu/memory.h | 30 +- cpu/op.c | 1290 ++++++++++++----- cpu/op.h | 3 +- cpu/ram.c | 22 - cpu/ram.h | 19 - include/cpu.h | 10 +- include/mapper.h | 8 +- include/ppu.h | 96 ++ include/rom.h | 21 +- include/system.h | 66 + include/types.h | 18 + main.c | 22 +- mappers/simple_mapper.c | 20 +- ppu/CMakeLists.txt | 5 + ppu/ppu.c | 47 + rom/ines.c | 24 +- rom/rom.c | 57 +- system.c | 57 + .../cpu_exec_space/readme.txt | 0 .../cpu_exec_space/source/common/ascii_1.chr | Bin .../cpu_exec_space/source/common/ascii_2.chr | Bin .../cpu_exec_space/source/common/ascii_3.chr | Bin .../cpu_exec_space/source/common/build_rom.s | 0 .../cpu_exec_space/source/common/colors.inc | 0 .../cpu_exec_space/source/common/console.s | 0 .../cpu_exec_space/source/common/crc.s | 0 .../cpu_exec_space/source/common/delay.s | 0 .../cpu_exec_space/source/common/devcart.bin | Bin .../cpu_exec_space/source/common/macros.inc | 0 .../cpu_exec_space/source/common/neshw.inc | 0 .../cpu_exec_space/source/common/ppu.s | 0 .../cpu_exec_space/source/common/print.s | 0 .../cpu_exec_space/source/common/shell.inc | 0 .../cpu_exec_space/source/common/shell.s | 0 .../cpu_exec_space/source/common/testing.s | 0 .../cpu_exec_space/source/common/text_out.s | 0 .../cpu_exec_space/source/readme.txt | 0 .../source/test_cpu_exec_space_apu.s | 0 .../source/test_cpu_exec_space_ppuio.s | 0 .../test_cpu_exec_space_apu.nes | Bin .../test_cpu_exec_space_ppuio.nes | Bin test_roms/nestest.fdb | 3 + test_roms/nestest.nes | Bin 0 -> 24592 bytes test_roms/smb.fdb | 1 + {tests => test_roms}/smb.nes | Bin 52 files changed, 1784 insertions(+), 620 deletions(-) delete mode 100644 cpu/ram.c delete mode 100644 cpu/ram.h create mode 100644 include/ppu.h create mode 100644 include/system.h create mode 100644 include/types.h create mode 100644 ppu/CMakeLists.txt create mode 100644 ppu/ppu.c create mode 100644 system.c rename {tests => test_roms}/cpu_exec_space/readme.txt (100%) rename {tests => test_roms}/cpu_exec_space/source/common/ascii_1.chr (100%) rename {tests => test_roms}/cpu_exec_space/source/common/ascii_2.chr (100%) rename {tests => test_roms}/cpu_exec_space/source/common/ascii_3.chr (100%) rename {tests => test_roms}/cpu_exec_space/source/common/build_rom.s (100%) rename {tests => test_roms}/cpu_exec_space/source/common/colors.inc (100%) rename {tests => test_roms}/cpu_exec_space/source/common/console.s (100%) rename {tests => test_roms}/cpu_exec_space/source/common/crc.s (100%) rename {tests => test_roms}/cpu_exec_space/source/common/delay.s (100%) rename {tests => test_roms}/cpu_exec_space/source/common/devcart.bin (100%) rename {tests => test_roms}/cpu_exec_space/source/common/macros.inc (100%) rename {tests => test_roms}/cpu_exec_space/source/common/neshw.inc (100%) rename {tests => test_roms}/cpu_exec_space/source/common/ppu.s (100%) rename {tests => test_roms}/cpu_exec_space/source/common/print.s (100%) rename {tests => test_roms}/cpu_exec_space/source/common/shell.inc (100%) rename {tests => test_roms}/cpu_exec_space/source/common/shell.s (100%) rename {tests => test_roms}/cpu_exec_space/source/common/testing.s (100%) rename {tests => test_roms}/cpu_exec_space/source/common/text_out.s (100%) rename {tests => test_roms}/cpu_exec_space/source/readme.txt (100%) rename {tests => test_roms}/cpu_exec_space/source/test_cpu_exec_space_apu.s (100%) rename {tests => test_roms}/cpu_exec_space/source/test_cpu_exec_space_ppuio.s (100%) rename {tests => test_roms}/cpu_exec_space/test_cpu_exec_space_apu.nes (100%) rename {tests => test_roms}/cpu_exec_space/test_cpu_exec_space_ppuio.nes (100%) create mode 100644 test_roms/nestest.fdb create mode 100644 test_roms/nestest.nes create mode 100644 test_roms/smb.fdb rename {tests => test_roms}/smb.nes (100%) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 35ead56..eeaa3f2 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -14,6 +14,7 @@ + @@ -23,12 +24,59 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -283,6 +486,12 @@ + + + + + + - @@ -316,6 +533,18 @@ + + + + + file://$PROJECT_DIR$/system.c + 51 + + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 2da751d..0a746a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,19 +2,24 @@ cmake_minimum_required(VERSION 3.10) project(NESEmulator VERSION 0.1) add_subdirectory(cpu) +add_subdirectory(ppu) add_subdirectory(mappers) add_subdirectory(rom) list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/cpu" + "${PROJECT_SOURCE_DIR}/ppu" "${PROJECT_SOURCE_DIR}/mappers" "${PROJECT_SOURCE_DIR}/rom") -add_executable(NESEmulator main.c) +add_executable(NESEmulator main.c + system.c + include/system.h + include/types.h) find_package(log.c) -target_link_libraries(NESEmulator CPU Mappers ROM log.c::log.c) +target_link_libraries(NESEmulator CPU PPU Mappers ROM log.c::log.c) target_include_directories(NESEmulator PUBLIC "${PROJECT_BINARY_DIR}" ${EXTRA_INCLUDES}) diff --git a/conandata.yml b/conandata.yml index 5385094..72d8f52 100644 --- a/conandata.yml +++ b/conandata.yml @@ -2,4 +2,5 @@ # To keep your changes, remove these comment lines, but the plugin won't be able to modify your requirements requirements: + - "libcheck/0.15.2" - "log.c/cci.20200620" \ No newline at end of file diff --git a/cpu/CMakeLists.txt b/cpu/CMakeLists.txt index 79a7e5a..80e79ab 100644 --- a/cpu/CMakeLists.txt +++ b/cpu/CMakeLists.txt @@ -1,7 +1,6 @@ add_library(CPU cpu.c op.c - ram.c memory.c cpu.h) diff --git a/cpu/cpu.c b/cpu/cpu.c index f562b14..62c0997 100644 --- a/cpu/cpu.c +++ b/cpu/cpu.c @@ -1,4 +1,6 @@ #include +#include +#include #include "../include/cpu.h" #include "cpu.h" #include "memory.h" @@ -22,104 +24,120 @@ * ===================================================================================== */ -CpuRegisters registers; -Mapper mapper; -unsigned int wait_cycle_count = 0; - -void cpu_init() { - registers.program_counter = 0xc000; - registers.stack_pointer = 0xff; - registers.accumulator = 0x00; - registers.x = 0x00; - registers.y = 0x00; - registers.status = 0x00; - - mapper = get_mapper(MAPPER_TYPE_SIMPLE); +void cpu_init(CPU *cpu) { + cpu->program_counter = 0x8000; + cpu->stack_pointer = 0xfd; + cpu->accumulator = 0x00; + cpu->x = 0x00; + cpu->y = 0x00; + cpu->status = 0x04; + cpu->oam_dma_triggered = false; } -void cpu_step() { - int i = 0; - while (i < 10) { - byte op = cpu_get_next_byte(); - process_op_code(op); - i += 1; +void print_registers(CPU cpu, byte op, unsigned long cycle_count) { + log_debug("%#02x %#02x %s \t A:%#02x X:%#02x Y:%#02x F:%#02x SP:%#02x \t [%d]", + cpu.program_counter, + op, + get_op_code_name(op), + cpu.accumulator, + cpu.x, + cpu.y, + cpu.status, + cpu.stack_pointer, + cycle_count); +} + +void oam_dma_upload(System *system) { + byte page_high_addr = *system->ppu.oam_dma_register; + address page_addr = ((address) page_high_addr) << 8; + byte n = 0xff; + + byte *ram_source = &system->ram[page_addr]; + byte *oam_destination = system->ppu.oam; + + memcpy(oam_destination, ram_source, n); + + log_debug("OAM DMA %#04x", page_addr); + cpu_add_cycles(system, 513); // TODO +} + +void cpu_cycle(System *system) { + if (system->cpu.oam_dma_triggered) { + oam_dma_upload(system); + system->cpu.oam_dma_triggered = false; + return; } + + CPU registers = system->cpu; + byte op = cpu_get_next_byte(system); + + print_registers(registers, op, system->cycle_count); + + process_op_code(system, op); } -void cpu_add_cycles(unsigned int cycle_count) { - wait_cycle_count += cycle_count; - log_trace("Waiting for %d cycles", cycle_count); +void cpu_add_cycles(System *system, unsigned int cycle_count) { + system->cycle_count += cycle_count; } // === Registers === -CpuRegisters *cpu_get_registers() { - return ®isters; +bool cpu_get_flag(System *system, byte mask) { + return system->cpu.status & mask; } -byte cpu_get_flag(byte mask) { - return registers.status & mask; -} - -void cpu_set_flag(bool set, byte mask) { +void cpu_set_flag(System *system, byte mask, bool set) { if (set) { - registers.status |= mask; + system->cpu.status |= mask; } else { - registers.status &= ~mask; + system->cpu.status &= ~mask; } } -// === Memory === -byte cpu_get_next_byte() { - byte next_byte = mem_get_byte(&mapper, registers.program_counter); - registers.program_counter++; +byte cpu_get_next_byte(System *system) { + byte next_byte = mem_get_byte(system, system->cpu.program_counter); + system->cpu.program_counter++; return next_byte; } -word cpu_get_next_word() { - word next_word = mem_get_word(&mapper, registers.program_counter); - registers.program_counter += 2; +word cpu_get_next_word(System *system) { + word next_word = mem_get_word(system, system->cpu.program_counter); + system->cpu.program_counter += 2; return next_word; } -byte cpu_peek_byte(address addr) { - return mem_get_byte(&mapper, addr); +void cpu_stack_push(System *system, byte value) { + assert(system->cpu.stack_pointer > 0); + + address mem_addr = CPU_STACK_ADDR | system->cpu.stack_pointer; + mem_set_byte(system, mem_addr, value); + system->cpu.stack_pointer--; } -word cpu_peek_word(address addr) { - return mem_get_word(&mapper, addr); -} +byte cpu_stack_pop(System *system) { + assert(system->cpu.stack_pointer < 0xff); -void cpu_push_byte(byte value, address addr) { - mem_set_byte(&mapper, addr, value); -} - -// === Stack === -void cpu_stack_push(byte value) { - address mem_addr = CPU_STACK_ADDR | registers.stack_pointer; - cpu_push_byte(value, mem_addr); - registers.stack_pointer--; -} - -byte cpu_stack_pop() { - address mem_addr = CPU_STACK_ADDR | registers.stack_pointer; - byte value = cpu_peek_byte(mem_addr); - registers.stack_pointer++; + system->cpu.stack_pointer++; + address mem_addr = CPU_STACK_ADDR | system->cpu.stack_pointer; + byte value = mem_get_byte(system, mem_addr); return value; } -void cpu_stack_push_context() { - cpu_stack_push(registers.program_counter >> 8); - cpu_stack_push(registers.program_counter & 0xff); - cpu_stack_push(registers.status); +void cpu_stack_push_context(System *system) { + cpu_stack_push(system, system->cpu.program_counter >> 8); + cpu_stack_push(system, system->cpu.program_counter & 0xff); + cpu_stack_push(system, system->cpu.status); } -void cpu_stack_pop_context() { - registers.status = cpu_stack_pop(); +void cpu_stack_pop_context(System *system) { + byte value = cpu_stack_pop(system); + value &= 0xef; // The B mask cannot be set as it is a CPU signal + value |= 0x20; // This value is always set + system->cpu.status = value; - byte lo = cpu_stack_pop(); - address pc = cpu_stack_pop() << 8; + byte lo = cpu_stack_pop(system); + address pc = cpu_stack_pop(system) << 8; pc += lo; - registers.program_counter = pc; + system->cpu.program_counter = pc; } char *operand_name(Operand *operand) { diff --git a/cpu/cpu.h b/cpu/cpu.h index 2bcbb4f..8301bae 100644 --- a/cpu/cpu.h +++ b/cpu/cpu.h @@ -14,22 +14,11 @@ #define CPU_STATUS_INTERRUPT_DISABLE_MASK 0x04 #define CPU_STATUS_DECIMAL_MASK 0x08 #define CPU_STATUS_B_MASK 0x10 -#define CPU_STATUS_I_MASK 0x20 #define CPU_STATUS_OVERFLOW_MASK 0x40 #define CPU_STATUS_NEGATIVE_MASK 0x80 #define CPU_STACK_ADDR 0x0100 -// Reference: https://www.nesdev.org/obelisk-6502-guide/registers.html -typedef struct { - address program_counter; - byte stack_pointer; - byte accumulator; - byte x; - byte y; - byte status; -} CpuRegisters; - enum OperandType { OPERAND_TYPE_ACCUMULATOR, OPERAND_TYPE_IMMEDIATE, @@ -42,24 +31,87 @@ typedef struct { bool is_page_crossing; } Operand; -char* operand_name(Operand *operand); +/** + * Gets the name of the type of an operand, for logging. + * + * @param operand The operand + * @return The name of the operand's type. + */ +char *operand_name(Operand *operand); -CpuRegisters* cpu_get_registers(); -byte cpu_get_flag(byte mask); -void cpu_set_flag(bool set, byte mask); +/** + * Gets a flag from the CPU registers. + * + * @param system The system + * @param mask The flag mask + * @return The value of the flag. + */ +bool cpu_get_flag(System *system, byte mask); -byte cpu_get_next_byte(); -word cpu_get_next_word(); +/** + * Sets a flag in the CPU registers. + * + * @param system The system + * @param mask The flag mask + * @param set If the flag is set or not + */ +void cpu_set_flag(System *system, byte mask, bool set); -byte cpu_peek_byte(address addr); -word cpu_peek_word(address addr); -void cpu_push_byte(byte value, address addr); +/** + * Gets the next byte in the program. + * Increases the system program counter. + * + * @param system The system + * @return The value of the next byte. + */ +byte cpu_get_next_byte(System *system); -void cpu_stack_push(byte value); -void cpu_stack_push_context(); -byte cpu_stack_pop(); -void cpu_stack_pop_context(); +/** + * Gets the next word in the program. + * Increases the system program counter by 2. + * + * @param system The system + * @return The value of the next word. + */ +word cpu_get_next_word(System *system); -void cpu_add_cycles(unsigned int cycle_count); +/** + * Pushes a byte in to the stack. + * + * @param system The system + * @param value The value to push to the stack + */ +void cpu_stack_push(System *system, byte value); + +/** + * Pushes the execution context to the stack. + * This includes the program counter and the CPU status. + * + * @param system The system + */ +void cpu_stack_push_context(System *system); + +/** + * Pops a byte from the stack. + * + * @param system The system + * @return The value of the byte + */ +byte cpu_stack_pop(System *system); + +/** + * Pops an execution context from the stack and overwrite the current context. + * This includes the program counter and the CPU status. + * + * @param system The system + */ +void cpu_stack_pop_context(System *system); + +/** + * Adds wait cycles to the CPU. + * + * @param cycle_count The number of cycle to wait + */ +void cpu_add_cycles(System *system, unsigned int cycle_count); #endif //CPU_CPU_H diff --git a/cpu/memory.c b/cpu/memory.c index 57d4337..798fb0f 100644 --- a/cpu/memory.c +++ b/cpu/memory.c @@ -2,35 +2,76 @@ // Created by william on 10/15/23. // +#include +#include #include "memory.h" -#include "ram.h" #include "../include/rom.h" -byte mem_get_byte(Mapper *mapper, address addr) { - address redirected_addr = mapper->redirect_addr(addr); +#define RAM_MAX_ADDR 0x2000 +#define RAM_BANK_SIZE 0x800 +#define PPU_MAX_ADDR 0x4000 +#define PPU_BANK_SIZE 0x8 +#define APU_MAX_ADDR 0x4020 +#define MAX_ADDR 0xffff - if (redirected_addr < 0x0800) { - return ram_get_byte(redirected_addr); - } else if (redirected_addr >= 0x4020) { - return rom_prg_get_byte(redirected_addr - 0x4020); +byte mem_get_byte(System *system, address addr) { + assert(addr <= MAX_ADDR); + + if (addr >= RAM_MAX_ADDR && addr < PPU_MAX_ADDR) { + byte reg = (addr - RAM_MAX_ADDR) % PPU_BANK_SIZE; + ppu_read_register(&system->ppu, reg); + return system->ppu.registers[reg]; } - return 0; -} - -word mem_get_word(Mapper *mapper, address addr) { - address redirected_addr = mapper->redirect_addr(addr); - - if (redirected_addr < 0x0800) { - return ram_get_word(redirected_addr); - } else if (redirected_addr >= 0x4020) { - return rom_prg_get_word(redirected_addr - 0x4020); + if (addr >= PPU_MAX_ADDR && addr < APU_MAX_ADDR) { + byte apu_addr = addr - PPU_MAX_ADDR; + return system->apu_registers[apu_addr]; } - return 0; + return system->ram[addr]; } -void mem_set_byte(Mapper *mapper, address addr, byte byte) { - address redirected_addr = mapper->redirect_addr(addr); - ram_set_byte(redirected_addr, byte); +word mem_get_word(System *system, address addr) { + assert(addr < MAX_ADDR); + + if (addr >= RAM_MAX_ADDR && addr < APU_MAX_ADDR) { + assert(false); + } + + word word = system->ram[addr]; + word += system->ram[addr + 1] << 8; // Little endian + return word; +} + +void mem_set_byte(System *system, address addr, byte byte) { + assert(addr < MAX_ADDR); + + log_trace("Writing '%02x' to address 0x%04x", byte, addr); + + if (addr < RAM_MAX_ADDR) { + address init_ram_addr = addr % RAM_BANK_SIZE; + + // The value must also be cloned in the three mirrors + for (int i = 0; i < 4; i++) { + address ram_addr = init_ram_addr + RAM_BANK_SIZE * i; + system->ram[ram_addr] = byte; + } + } else if (addr < PPU_MAX_ADDR) { + address reg_addr = (addr - RAM_MAX_ADDR) % PPU_BANK_SIZE; + + int bank_count = (PPU_MAX_ADDR - RAM_MAX_ADDR) / PPU_BANK_SIZE; + for (int i = 0; i < bank_count; i++) { + address ram_addr = reg_addr + PPU_BANK_SIZE * i; + system->ppu.registers[ram_addr] = byte; + } + + ppu_write_register(&system->ppu, reg_addr); + } else { + system->ram[addr] = byte; + + if (addr == PPU_REGISTER_OAM_DMA_ADDR) { + // Writing to this address triggers an upload to the PPU memory + system->cpu.oam_dma_triggered = true; + } + } } \ No newline at end of file diff --git a/cpu/memory.h b/cpu/memory.h index c440d43..073e6a4 100644 --- a/cpu/memory.h +++ b/cpu/memory.h @@ -3,12 +3,36 @@ // #include "../include/mapper.h" +#include "../include/system.h" #ifndef NESEMULATOR_MEMORY_H #define NESEMULATOR_MEMORY_H -byte mem_get_byte(Mapper *mapper, address addr); -word mem_get_word(Mapper *mapper, address addr); -void mem_set_byte(Mapper *mapper, address addr, byte byte); +/** + * Gets a byte from a system's memory. + * + * @param system A reference to the system + * @param addr The address to get + * @return The value of the byte at the given address. + */ +byte mem_get_byte(System *system, address addr); + +/** + * Gets a word from a system's memory. + * + * @param system A reference to the system + * @param addr The address to get + * @return The value of the word at the given address. + */ +word mem_get_word(System *system, address addr); + +/** + * Sets a byte in a system's memory. + * + * @param system A reference to the system + * @param addr The address to set + * @param value The value to set + */ +void mem_set_byte(System *system, address addr, byte value); #endif //NESEMULATOR_MEMORY_H diff --git a/cpu/op.c b/cpu/op.c index 651f79e..b56b359 100644 --- a/cpu/op.c +++ b/cpu/op.c @@ -10,8 +10,7 @@ #define IS_OP_CODE_MODE(op, op_code, addr_mode) \ case op_code: \ - log_debug("OP: %s", #op); \ - op_ ## op(ADDR_MODE_ ## addr_mode); \ + op_ ## op(system, ADDR_MODE_ ## addr_mode); \ break; #define IS_OP_CODE(op, op_code) \ @@ -44,7 +43,7 @@ #define IS_RMW_OP_CODE(op, line) \ IS_RMW_OP_CODE_(op, line, 0x06, ZERO_PAGE) \ - IS_RMW_OP_CODE_(op, line, 0x0a, IMPLICIT) \ + IS_RMW_OP_CODE_(op, line, 0x0a, ACCUMULATOR) \ IS_RMW_OP_CODE_(op, line, 0x0e, ABSOLUTE) \ IS_RMW_OP_CODE_(op, line, 0x16, ZERO_PAGE_INDEXED_X) \ IS_RMW_OP_CODE_(op, line, 0x1e, ABSOLUTE_INDEXED_X) @@ -61,7 +60,7 @@ IS_UNOFFICIAL_OP_CODE_(op, line, 0x1b, ABSOLUTE_INDEXED_Y) \ IS_UNOFFICIAL_OP_CODE_(op, line, 0x1f, ABSOLUTE_INDEXED_X) -char* addr_mode_name(AddressingMode addr_mode) { +char *addr_mode_name(AddressingMode addr_mode) { switch (addr_mode) { case ADDR_MODE_ABSOLUTE: return "Absolute"; @@ -94,50 +93,52 @@ char* addr_mode_name(AddressingMode addr_mode) { } } -address decode_operand_addr(AddressingMode addr_mode, bool *page_crossing) { - CpuRegisters *registers = cpu_get_registers(); +address decode_operand_addr(System *system, AddressingMode addr_mode, bool *page_crossing) { + CPU registers = system->cpu; address operand_addr; if (addr_mode == ADDR_MODE_ZERO_PAGE) { - operand_addr = cpu_get_next_byte(); + operand_addr = cpu_get_next_byte(system); } else if (addr_mode == ADDR_MODE_ZERO_PAGE_INDEXED_X) { - operand_addr = (cpu_get_next_byte() + registers->x) & 0xff; + operand_addr = (cpu_get_next_byte(system) + registers.x) & 0xff; } else if (addr_mode == ADDR_MODE_ZERO_PAGE_INDEXED_Y) { - operand_addr = (cpu_get_next_byte() + registers->y) & 0xff; + operand_addr = (cpu_get_next_byte(system) + registers.y) & 0xff; } else if (addr_mode == ADDR_MODE_ABSOLUTE || addr_mode == ADDR_MODE_ABSOLUTE_JUMP) { - operand_addr = cpu_get_next_word(); + operand_addr = cpu_get_next_word(system); } else if (addr_mode == ADDR_MODE_ABSOLUTE_INDEXED_X) { - word addr = cpu_get_next_word(); - word new_addr = addr + registers->x; + word addr = cpu_get_next_word(system); + word new_addr = addr + registers.x; *page_crossing = (addr & 0xff00) != (new_addr & 0xff00); operand_addr = new_addr; } else if (addr_mode == ADDR_MODE_ABSOLUTE_INDEXED_Y) { - word addr = cpu_get_next_word(); - word new_addr = addr + registers->y; + word addr = cpu_get_next_word(system); + word new_addr = addr + registers.y; *page_crossing = (addr & 0xff00) != (new_addr & 0xff00); operand_addr = new_addr; } else if (addr_mode == ADDR_MODE_INDIRECT_JUMP) { - word addr = cpu_get_next_word(); + word addr = cpu_get_next_word(system); if ((addr & 0xff) == 0xff) { // Error in NES CPU for JMP op - word result = cpu_peek_byte(addr); - result += cpu_peek_byte(addr & 0xff00) << 8; + word result = mem_get_byte(system, addr); + result += mem_get_byte(system, addr & 0xff00) << 8; operand_addr = result; + } else { + operand_addr = mem_get_word(system, addr); } - operand_addr = cpu_peek_word(addr); } else if (addr_mode == ADDR_MODE_INDIRECT_X) { - byte addr = cpu_get_next_byte(); - word result = cpu_peek_byte((addr + registers->x) & 0xff); - result += cpu_peek_byte((addr + registers->x + 1) & 0xff) << 8; - operand_addr = result; + byte arg_addr = cpu_get_next_byte(system); + + word addr = mem_get_byte(system, (arg_addr + system->cpu.x) & 0xff); + addr += mem_get_byte(system, (arg_addr + system->cpu.x + 1) & 0xff) << 8; + operand_addr = addr; } else if (addr_mode == ADDR_MODE_INDIRECT_Y) { - byte arg_addr = cpu_get_next_byte(); - word addr = cpu_peek_byte(arg_addr) + (cpu_peek_byte((arg_addr + 1) & 0xff) << 8); - word new_addr = addr + registers->y; + byte arg_addr = cpu_get_next_byte(system); + word addr = mem_get_byte(system, arg_addr) + (mem_get_byte(system, (arg_addr + 1) & 0xff) << 8); + word new_addr = addr + registers.y; *page_crossing = (addr & 0xff00) != (new_addr & 0xff00); @@ -146,11 +147,11 @@ address decode_operand_addr(AddressingMode addr_mode, bool *page_crossing) { assert(false); } - log_trace("Operand address: %#02x, PC: %d, Addressing mode: %s", operand_addr, *page_crossing, addr_mode_name(addr_mode)); + log_trace("Operand address: %#02x, Addressing mode: %s", operand_addr, addr_mode_name(addr_mode)); return operand_addr; } -Operand decode_operand(AddressingMode addr_mode) { +Operand decode_operand(System *system, AddressingMode addr_mode) { Operand operand; if (addr_mode == ADDR_MODE_ACCUMULATOR) { @@ -159,37 +160,37 @@ Operand decode_operand(AddressingMode addr_mode) { operand.is_page_crossing = false; } else if (addr_mode == ADDR_MODE_IMMEDIATE) { operand.type = OPERAND_TYPE_IMMEDIATE; - operand.value = cpu_get_next_byte(); + operand.value = cpu_get_next_byte(system); operand.is_page_crossing = false; } else { operand.type = OPERAND_TYPE_ADDRESS; - operand.value = decode_operand_addr(addr_mode, &operand.is_page_crossing); + operand.value = decode_operand_addr(system, addr_mode, &operand.is_page_crossing); } log_trace("Operand type: %s, value: %#02x", operand_name(&operand), operand.value); return operand; } -byte read_operand(Operand operand) { +byte read_operand(System *system, Operand operand) { switch (operand.type) { case OPERAND_TYPE_ACCUMULATOR: - return cpu_get_registers()->accumulator; + return system->cpu.accumulator; case OPERAND_TYPE_IMMEDIATE: return (byte) operand.value; case OPERAND_TYPE_ADDRESS: - return cpu_peek_byte(operand.value); + return mem_get_byte(system, operand.value); default: assert(false); } } -void write_operand(Operand operand, byte value) { +void write_operand(System *system, Operand operand, byte value) { switch (operand.type) { case OPERAND_TYPE_ACCUMULATOR: - cpu_get_registers()->accumulator = value; + system->cpu.accumulator = value; break; case OPERAND_TYPE_ADDRESS: - cpu_push_byte(operand.value, value); + mem_set_byte(system, operand.value, value); break; default: assert(false); @@ -204,7 +205,7 @@ bool is_sign_overflow(byte val1, byte val2, byte result) { byte get_cycle_count(Operand operand, AddressingMode addr_mode) { switch (addr_mode) { case ADDR_MODE_ACCUMULATOR: - case ADDR_MODE_IMPLICIT: + case ADDR_MODE_IMMEDIATE: return 2; case ADDR_MODE_ZERO_PAGE: return 3; @@ -234,19 +235,23 @@ byte get_shift_cycle_count(AddressingMode addr_mode) { case ADDR_MODE_ABSOLUTE: return 6; case ADDR_MODE_ABSOLUTE_INDEXED_X: + case ADDR_MODE_ABSOLUTE_INDEXED_Y: return 7; + case ADDR_MODE_INDIRECT_X: + case ADDR_MODE_INDIRECT_Y: + return 8; default: assert(false); } } -void set_acl_flags(byte result) { - cpu_set_flag(result == 0, CPU_STATUS_ZERO_MASK); - cpu_set_flag(result & 0x80, CPU_STATUS_NEGATIVE_MASK); +void set_acl_flags(System *system, byte result) { + cpu_set_flag(system, CPU_STATUS_ZERO_MASK, result == 0); + cpu_set_flag(system, CPU_STATUS_NEGATIVE_MASK, result & 0x80); } -byte get_branch_cycle_count(bool branching, char offset) { - address target = cpu_get_registers()->program_counter; +byte get_branch_cycle_count(System *system, bool branching, char offset) { + address target = system->cpu.program_counter; byte cycle_count = 2; if (branching) { @@ -260,20 +265,21 @@ byte get_branch_cycle_count(bool branching, char offset) { return cycle_count; } -void op_branch(bool branching) { - char offset = (char) cpu_get_next_byte(); +__attribute__((unused)) +void op_branch(System *system, bool branching) { + char offset = (char) cpu_get_next_byte(system); if (branching) { - address counter = cpu_get_registers()->program_counter; + address counter = system->cpu.program_counter; address target = counter + offset; - cpu_get_registers()->program_counter = target; + system->cpu.program_counter = target; } - cpu_add_cycles(get_branch_cycle_count(branching, offset)); + cpu_add_cycles(system, get_branch_cycle_count(system, branching, offset)); } -void add_with_carry(byte value) { - byte acc = cpu_get_registers()->accumulator; +void add_with_carry(System *system, byte value) { + byte acc = system->cpu.accumulator; byte addition = acc + value; bool overflow = false; @@ -284,572 +290,776 @@ void add_with_carry(byte value) { } // Add carry flag and check for overflow again - byte result = addition + cpu_get_flag(CPU_STATUS_CARRY_MASK); + byte result = addition + cpu_get_flag(system, CPU_STATUS_CARRY_MASK); if (result < addition) { overflow = true; } - cpu_get_registers()->accumulator = acc; + system->cpu.accumulator = result; - cpu_set_flag(overflow, CPU_STATUS_CARRY_MASK); - cpu_set_flag(is_sign_overflow(acc, value, result), CPU_STATUS_OVERFLOW_MASK); + cpu_set_flag(system, CPU_STATUS_CARRY_MASK, overflow); + cpu_set_flag(system, CPU_STATUS_OVERFLOW_MASK, is_sign_overflow(acc, value, result)); - set_acl_flags(result); + set_acl_flags(system, result); } -void op_ADC(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); +__attribute__((unused)) +void op_ADC(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); - byte value = read_operand(operand); - add_with_carry(value); + byte value = read_operand(system, operand); + add_with_carry(system, value); - cpu_add_cycles(get_cycle_count(operand, addr_mode)); + cpu_add_cycles(system, get_cycle_count(operand, addr_mode)); } -void op_AHX(AddressingMode addr_mode) { +__attribute__((unused)) +void op_AHX(System *system, AddressingMode addr_mode) { assert(false); } -void op_ALR(AddressingMode addr_mode) { +__attribute__((unused)) +void op_ALR(System *system, AddressingMode addr_mode) { assert(false); } -void op_ANC(AddressingMode addr_mode) { +__attribute__((unused)) +void op_ANC(System *system, AddressingMode addr_mode) { assert(false); } -void op_AND(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte value = read_operand(operand); - byte acc = cpu_get_registers()->accumulator; +__attribute__((unused)) +void op_AND(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); + byte acc = system->cpu.accumulator; byte result = acc & value; - cpu_get_registers()->accumulator = result; + system->cpu.accumulator = result; - set_acl_flags(result); - cpu_add_cycles(get_cycle_count(operand, addr_mode)); + set_acl_flags(system, result); + cpu_add_cycles(system, get_cycle_count(operand, addr_mode)); } -void op_ARR(AddressingMode addr_mode) { +__attribute__((unused)) +void op_ARR(System *system, AddressingMode addr_mode) { assert(false); } -void op_ASL(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte value = read_operand(operand); +__attribute__((unused)) +void op_ASL(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); byte result = value << 1; - write_operand(operand, result); + write_operand(system, operand, result); - cpu_set_flag(value & 0x80, CPU_STATUS_CARRY_MASK); - set_acl_flags(result); - cpu_add_cycles(get_shift_cycle_count(addr_mode)); + cpu_set_flag(system, CPU_STATUS_CARRY_MASK, value & 0x80); + set_acl_flags(system, result); + cpu_add_cycles(system, get_shift_cycle_count(addr_mode)); } -void op_AXS(AddressingMode addr_mode) { +__attribute__((unused)) +void op_AXS(System *system, AddressingMode addr_mode) { assert(false); } -void op_BCC(AddressingMode addr_mode) { - op_branch(!cpu_get_flag(CPU_STATUS_CARRY_MASK)); +__attribute__((unused)) +void op_BCC(System *system, AddressingMode addr_mode) { + op_branch(system, !cpu_get_flag(system, CPU_STATUS_CARRY_MASK)); } -void op_BCS(AddressingMode addr_mode) { - op_branch(cpu_get_flag(CPU_STATUS_CARRY_MASK)); +__attribute__((unused)) +void op_BCS(System *system, AddressingMode addr_mode) { + op_branch(system, cpu_get_flag(system, CPU_STATUS_CARRY_MASK)); } -void op_BEQ(AddressingMode addr_mode) { - op_branch(cpu_get_flag(CPU_STATUS_ZERO_MASK)); +__attribute__((unused)) +void op_BEQ(System *system, AddressingMode addr_mode) { + op_branch(system, cpu_get_flag(system, CPU_STATUS_ZERO_MASK)); } -void op_BIT(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte value = read_operand(operand); - byte acc = cpu_get_registers()->accumulator; +__attribute__((unused)) +void op_BIT(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); + byte acc = system->cpu.accumulator; byte result = value & acc; - cpu_set_flag(result == 0, CPU_STATUS_ZERO_MASK); - cpu_set_flag(result & 0x40, CPU_STATUS_OVERFLOW_MASK); - cpu_set_flag(result & 0x80, CPU_STATUS_NEGATIVE_MASK); + cpu_set_flag(system, CPU_STATUS_ZERO_MASK, result == 0); + cpu_set_flag(system, CPU_STATUS_OVERFLOW_MASK, value & 0x40); + cpu_set_flag(system, CPU_STATUS_NEGATIVE_MASK, value & 0x80); } -void op_BMI(AddressingMode addr_mode) { - op_branch(cpu_get_flag(CPU_STATUS_NEGATIVE_MASK)); +__attribute__((unused)) +void op_BMI(System *system, AddressingMode addr_mode) { + op_branch(system, cpu_get_flag(system, CPU_STATUS_NEGATIVE_MASK)); } -void op_BNE(AddressingMode addr_mode) { - op_branch(!cpu_get_flag(CPU_STATUS_ZERO_MASK)); +__attribute__((unused)) +void op_BNE(System *system, AddressingMode addr_mode) { + op_branch(system, !cpu_get_flag(system, CPU_STATUS_ZERO_MASK)); } -void op_BPL(AddressingMode addr_mode) { - op_branch(!cpu_get_flag(CPU_STATUS_NEGATIVE_MASK)); +__attribute__((unused)) +void op_BPL(System *system, AddressingMode addr_mode) { + op_branch(system, !cpu_get_flag(system, CPU_STATUS_NEGATIVE_MASK)); } // Stops program execution, useful for debugging -void op_BRK(AddressingMode addr_mode) { - cpu_stack_push_context(); +__attribute__((unused)) +void op_BRK(System *system, AddressingMode addr_mode) { + cpu_stack_push_context(system); // TODO Load IRQ interrupt vector in PC at $FFFE/F - cpu_set_flag(true, CPU_STATUS_B_MASK); - cpu_add_cycles(7); + assert(false); + + cpu_set_flag(system, CPU_STATUS_B_MASK, true); + cpu_add_cycles(system, 7); } -void op_BVC(AddressingMode addr_mode) { - op_branch(!cpu_get_flag(CPU_STATUS_OVERFLOW_MASK)); +__attribute__((unused)) +void op_BVC(System *system, AddressingMode addr_mode) { + op_branch(system, !cpu_get_flag(system, CPU_STATUS_OVERFLOW_MASK)); } -void op_BVS(AddressingMode addr_mode) { - op_branch(cpu_get_flag(CPU_STATUS_OVERFLOW_MASK)); +__attribute__((unused)) +void op_BVS(System *system, AddressingMode addr_mode) { + op_branch(system, cpu_get_flag(system, CPU_STATUS_OVERFLOW_MASK)); } -void op_CLC(AddressingMode addr_mode) { - cpu_set_flag(false, CPU_STATUS_CARRY_MASK); - cpu_add_cycles(2); +__attribute__((unused)) +void op_CLC(System *system, AddressingMode addr_mode) { + cpu_set_flag(system, CPU_STATUS_CARRY_MASK, false); + cpu_add_cycles(system, 2); } -void op_CLD(AddressingMode addr_mode) { - cpu_set_flag(false, CPU_STATUS_DECIMAL_MASK); - cpu_add_cycles(2); +__attribute__((unused)) +void op_CLD(System *system, AddressingMode addr_mode) { + cpu_set_flag(system, CPU_STATUS_DECIMAL_MASK, false); + cpu_add_cycles(system, 2); } -void op_CLI(AddressingMode addr_mode) { - cpu_set_flag(false, CPU_STATUS_INTERRUPT_DISABLE_MASK); - cpu_add_cycles(2); +__attribute__((unused)) +void op_CLI(System *system, AddressingMode addr_mode) { + cpu_set_flag(system, CPU_STATUS_INTERRUPT_DISABLE_MASK, false); + cpu_add_cycles(system, 2); } -void op_CLV(AddressingMode addr_mode) { - cpu_set_flag(false, CPU_STATUS_OVERFLOW_MASK); - cpu_add_cycles(2); +__attribute__((unused)) +void op_CLV(System *system, AddressingMode addr_mode) { + cpu_set_flag(system, CPU_STATUS_OVERFLOW_MASK, false); + cpu_add_cycles(system, 2); } -void op_CMP(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte value = read_operand(operand); - byte acc = cpu_get_registers()->accumulator; +__attribute__((unused)) +void op_CMP(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); + byte acc = system->cpu.accumulator; byte result = acc - value; - cpu_set_flag(acc >= value, CPU_STATUS_CARRY_MASK); - cpu_set_flag(result == 0, CPU_STATUS_ZERO_MASK); - cpu_set_flag(result & 0x80, CPU_STATUS_NEGATIVE_MASK); + cpu_set_flag(system, CPU_STATUS_CARRY_MASK, acc >= value); + cpu_set_flag(system, CPU_STATUS_ZERO_MASK, result == 0); + cpu_set_flag(system, CPU_STATUS_NEGATIVE_MASK, result & 0x80); - cpu_add_cycles(get_cycle_count(operand, addr_mode)); + cpu_add_cycles(system, get_cycle_count(operand, addr_mode)); } -void op_CPX(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte value = read_operand(operand); - byte x = cpu_get_registers()->x; +__attribute__((unused)) +void op_CPX(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); + byte x = system->cpu.x; byte result = x - value; - cpu_set_flag(x >= value, CPU_STATUS_CARRY_MASK); - cpu_set_flag(result == 0, CPU_STATUS_ZERO_MASK); - cpu_set_flag(result & 0x80, CPU_STATUS_NEGATIVE_MASK); + cpu_set_flag(system, CPU_STATUS_CARRY_MASK, x >= value); + cpu_set_flag(system, CPU_STATUS_ZERO_MASK, result == 0); + cpu_set_flag(system, CPU_STATUS_NEGATIVE_MASK, result & 0x80); - cpu_add_cycles(get_cycle_count(operand, addr_mode)); + cpu_add_cycles(system, get_cycle_count(operand, addr_mode)); } -void op_CPY(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte value = read_operand(operand); - byte y = cpu_get_registers()->y; +__attribute__((unused)) +void op_CPY(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); + byte y = system->cpu.y; byte result = y - value; - cpu_set_flag(y >= value, CPU_STATUS_CARRY_MASK); - cpu_set_flag(result == 0, CPU_STATUS_ZERO_MASK); - cpu_set_flag(result & 0x80, CPU_STATUS_NEGATIVE_MASK); + cpu_set_flag(system, CPU_STATUS_CARRY_MASK, y >= value); + cpu_set_flag(system, CPU_STATUS_ZERO_MASK, result == 0); + cpu_set_flag(system, CPU_STATUS_NEGATIVE_MASK, result & 0x80); - cpu_add_cycles(get_cycle_count(operand, addr_mode)); + cpu_add_cycles(system, get_cycle_count(operand, addr_mode)); } -void op_DCP(AddressingMode addr_mode) { - assert(false); +__attribute__((unused)) +void op_DCP(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); + byte acc = system->cpu.accumulator; + + byte result = value - 1; + byte cmp_result = acc - result; + + cpu_set_flag(system, CPU_STATUS_CARRY_MASK, acc >= value); + cpu_set_flag(system, CPU_STATUS_ZERO_MASK, cmp_result == 0); + cpu_set_flag(system, CPU_STATUS_NEGATIVE_MASK, cmp_result & 0x80); + + write_operand(system, operand, result); + + byte cycle_count; + switch (addr_mode) { + case ADDR_MODE_ZERO_PAGE: + cycle_count = 5; + break; + case ADDR_MODE_ZERO_PAGE_INDEXED_X: + case ADDR_MODE_ABSOLUTE: + cycle_count = 6; + break; + case ADDR_MODE_ABSOLUTE_INDEXED_X: + case ADDR_MODE_ABSOLUTE_INDEXED_Y: + cycle_count = 7; + break; + case ADDR_MODE_INDIRECT_X: + case ADDR_MODE_INDIRECT_Y: + cycle_count = 8; + break; + default: + assert(false); + } + cpu_add_cycles(system, cycle_count); } -void op_DEC(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte value = read_operand(operand); +__attribute__((unused)) +void op_DEC(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); byte result = value - 1; - set_acl_flags(result); - write_operand(operand, result); - cpu_add_cycles(get_cycle_count(operand, addr_mode)); + set_acl_flags(system, result); + write_operand(system, operand, result); + cpu_add_cycles(system, get_cycle_count(operand, addr_mode)); } -void op_DEX(AddressingMode addr_mode) { - byte x = cpu_get_registers()->x; +__attribute__((unused)) +void op_DEX(System *system, AddressingMode addr_mode) { + byte x = system->cpu.x; byte result = x - 1; - cpu_get_registers()->x = result; + system->cpu.x = result; - set_acl_flags(result); - cpu_add_cycles(2); + set_acl_flags(system, result); + cpu_add_cycles(system, 2); } -void op_DEY(AddressingMode addr_mode) { - byte y = cpu_get_registers()->y; +__attribute__((unused)) +void op_DEY(System *system, AddressingMode addr_mode) { + byte y = system->cpu.y; byte result = y - 1; - cpu_get_registers()->y = result; + system->cpu.y = result; - set_acl_flags(result); - cpu_add_cycles(2); + set_acl_flags(system, result); + cpu_add_cycles(system, 2); } -void op_EOR(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte value = read_operand(operand); - byte acc = cpu_get_registers()->accumulator; +__attribute__((unused)) +void op_EOR(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); + byte acc = system->cpu.accumulator; acc ^= value; - cpu_get_registers()->accumulator = acc; + system->cpu.accumulator = acc; - set_acl_flags(acc); - cpu_add_cycles(get_cycle_count(operand, addr_mode)); + set_acl_flags(system, acc); + cpu_add_cycles(system, get_cycle_count(operand, addr_mode)); } -void op_INC(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte value = read_operand(operand); +__attribute__((unused)) +void op_INC(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); value += 1; - write_operand(operand, value); + write_operand(system, operand, value); - set_acl_flags(value); - cpu_add_cycles(get_shift_cycle_count(addr_mode)); + set_acl_flags(system, value); + cpu_add_cycles(system, get_shift_cycle_count(addr_mode)); } -void op_INX(AddressingMode addr_mode) { - byte x = cpu_get_registers()->x; +__attribute__((unused)) +void op_INX(System *system, AddressingMode addr_mode) { + byte x = system->cpu.x; x += 1; - cpu_get_registers()->x = x; + system->cpu.x = x; - set_acl_flags(x); - cpu_add_cycles(2); + set_acl_flags(system, x); + cpu_add_cycles(system, 2); } -void op_INY(AddressingMode addr_mode) { - byte y = cpu_get_registers()->y; +__attribute__((unused)) +void op_INY(System *system, AddressingMode addr_mode) { + byte y = system->cpu.y; y += 1; - cpu_get_registers()->y = y; + system->cpu.y = y; - set_acl_flags(y); - cpu_add_cycles(2); + set_acl_flags(system, y); + cpu_add_cycles(system, 2); } -void op_ISC(AddressingMode addr_mode) { - assert(false); +__attribute__((unused)) +void op_ISC(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); + + value += 1; + write_operand(system, operand, value); + add_with_carry(system, ~value); + + cpu_add_cycles(system, get_shift_cycle_count(addr_mode)); } -void op_JMP(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte addr = read_operand(operand); +__attribute__((unused)) +void op_JMP(System *system, AddressingMode addr_mode) { + word addr = decode_operand_addr(system, addr_mode, NULL); - cpu_get_registers()->program_counter = addr; + system->cpu.program_counter = addr; // TODO WN: Handle CPU bug? // > An original 6502 has does not correctly fetch the target address if the indirect vector falls on a page boundary (e.g. $xxFF where xx is any value from $00 to $FF). // > In this case fetches the LSB from $xxFF as expected but takes the MSB from $xx00. // > This is fixed in some later chips like the 65SC02 so for compatibility always ensure the indirect vector is not at the end of the page. + + int cycle_count = 3; + if (addr_mode == ADDR_MODE_INDIRECT_JUMP) { + cycle_count = 5; + } + + cpu_add_cycles(system, cycle_count); } -void op_JSR(AddressingMode addr_mode) { +__attribute__((unused)) +void op_JSR(System *system, AddressingMode addr_mode) { // Push the program counter on the stack - address program_counter = cpu_get_registers()->program_counter - 1; - cpu_stack_push(program_counter >> 8); - cpu_stack_push(program_counter & 0xff); + address program_counter = system->cpu.program_counter + 1; + cpu_stack_push(system, program_counter >> 8); + cpu_stack_push(system, program_counter & 0xff); // Updates the program counter to the address in the operand - address addr = decode_operand_addr(addr_mode, NULL); - cpu_get_registers()->program_counter = addr; + address addr = decode_operand_addr(system, addr_mode, NULL); + system->cpu.program_counter = addr; - cpu_add_cycles(6); + cpu_add_cycles(system, 6); } -void op_LAX(AddressingMode addr_mode) { +__attribute__((unused)) +void op_LAS(System *system, AddressingMode addr_mode) { assert(false); } -void op_LDA(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte value = read_operand(operand); +__attribute__((unused)) +void op_LAX(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); - cpu_get_registers()->accumulator = value; + system->cpu.accumulator = value; + system->cpu.x = value; - set_acl_flags(value); - cpu_add_cycles(get_cycle_count(operand, addr_mode)); + set_acl_flags(system, value); + cpu_add_cycles(system, get_cycle_count(operand, addr_mode)); } -void op_LDX(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte value = read_operand(operand); +__attribute__((unused)) +void op_LDA(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); - cpu_get_registers()->x = value; + system->cpu.accumulator = value; - set_acl_flags(value); - cpu_add_cycles(get_cycle_count(operand, addr_mode)); + set_acl_flags(system, value); + cpu_add_cycles(system, get_cycle_count(operand, addr_mode)); } -void op_LDY(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte value = read_operand(operand); +__attribute__((unused)) +void op_LDX(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); - cpu_get_registers()->y = value; + system->cpu.x = value; - set_acl_flags(value); - cpu_add_cycles(get_cycle_count(operand, addr_mode)); + set_acl_flags(system, value); + cpu_add_cycles(system, get_cycle_count(operand, addr_mode)); } -void op_LSR(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte value = read_operand(operand); +__attribute__((unused)) +void op_LDY(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); + + system->cpu.y = value; + + set_acl_flags(system, value); + cpu_add_cycles(system, get_cycle_count(operand, addr_mode)); +} + +__attribute__((unused)) +void op_LSR(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); // Put bit 0 in the carry flag - cpu_set_flag(value & 0x01, CPU_STATUS_CARRY_MASK); + cpu_set_flag(system, CPU_STATUS_CARRY_MASK, value & 0x01); value >>= 1; - write_operand(operand, value); + write_operand(system, operand, value); - set_acl_flags(value); - cpu_add_cycles(get_shift_cycle_count(addr_mode)); + set_acl_flags(system, value); + cpu_add_cycles(system, get_shift_cycle_count(addr_mode)); } -void op_NOP(AddressingMode addr_mode) { - cpu_add_cycles(2); +__attribute__((unused)) +void op_NOP(System *system, AddressingMode addr_mode) { + if (addr_mode != ADDR_MODE_IMPLICIT) { + Operand operand = decode_operand(system, addr_mode); + cpu_add_cycles(system, get_cycle_count(operand, addr_mode)); + } else { + cpu_add_cycles(system, 2); + } } -void op_ORA(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte value = read_operand(operand); - byte acc = cpu_get_registers()->accumulator; +__attribute__((unused)) +void op_ORA(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); + byte acc = system->cpu.accumulator; acc |= value; - cpu_get_registers()->accumulator = acc; + system->cpu.accumulator = acc; - set_acl_flags(acc); - cpu_add_cycles(get_cycle_count(operand, addr_mode)); + set_acl_flags(system, acc); + cpu_add_cycles(system, get_cycle_count(operand, addr_mode)); } -void op_PHA(AddressingMode addr_mode) { - byte acc = cpu_get_registers()->accumulator; - cpu_stack_push(acc); +__attribute__((unused)) +void op_PHA(System *system, AddressingMode addr_mode) { + byte acc = system->cpu.accumulator; + cpu_stack_push(system, acc); - cpu_add_cycles(3); + cpu_add_cycles(system, 3); } -void op_PHP(AddressingMode addr_mode) { - byte status = cpu_get_registers()->status; - cpu_stack_push(status); +__attribute__((unused)) +void op_PHP(System *system, AddressingMode addr_mode) { + byte status = system->cpu.status; + cpu_stack_push(system, status); - cpu_add_cycles(3); +// cpu_set_flag(system, CPU_STATUS_B_MASK, true); + + cpu_add_cycles(system, 3); } -void op_PLA(AddressingMode addr_mode) { - byte value = cpu_stack_pop(); - cpu_get_registers()->accumulator = value; +__attribute__((unused)) +void op_PLA(System *system, AddressingMode addr_mode) { + byte value = cpu_stack_pop(system); + system->cpu.accumulator = value; - cpu_add_cycles(4); + set_acl_flags(system, value); + + cpu_add_cycles(system, 4); } -void op_PLP(AddressingMode addr_mode) { - byte value = cpu_stack_pop(); - cpu_get_registers()->status = value; +__attribute__((unused)) +void op_PLP(System *system, AddressingMode addr_mode) { + byte value = cpu_stack_pop(system); - cpu_add_cycles(4); + value &= 0xef; // The B mask cannot be set as it is a CPU signal + value |= 0x20; // This value is always set + + system->cpu.status = value; + + cpu_add_cycles(system, 4); } -void op_RLA(AddressingMode addr_mode) { - assert(false); -} +__attribute__((unused)) +void op_RLA(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); + byte carry = cpu_get_flag(system, CPU_STATUS_CARRY_MASK); + byte acc = system->cpu.accumulator; -void op_ROL(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte value = read_operand(operand); - byte carry = cpu_get_flag(CPU_STATUS_CARRY_MASK); + cpu_set_flag(system, CPU_STATUS_CARRY_MASK, value & 0x80); - cpu_set_flag(value & 0x80, CPU_STATUS_CARRY_MASK); value = (value << 1) | carry; - write_operand(operand, value); + byte and_result = acc & value; + system->cpu.accumulator = and_result; - set_acl_flags(value); - cpu_add_cycles(get_shift_cycle_count(addr_mode)); + write_operand(system, operand, value); + + set_acl_flags(system, value); + cpu_add_cycles(system, get_shift_cycle_count(addr_mode)); } -void op_ROR(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte value = read_operand(operand); - byte carry = cpu_get_flag(CPU_STATUS_CARRY_MASK); +__attribute__((unused)) +void op_ROL(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); + byte carry = cpu_get_flag(system, CPU_STATUS_CARRY_MASK); - cpu_set_flag(value & 0x01, CPU_STATUS_CARRY_MASK); + cpu_set_flag(system, CPU_STATUS_CARRY_MASK, value & 0x80); + value = (value << 1) | carry; + write_operand(system, operand, value); + + set_acl_flags(system, value); + cpu_add_cycles(system, get_shift_cycle_count(addr_mode)); +} + +__attribute__((unused)) +void op_ROR(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); + byte carry = cpu_get_flag(system, CPU_STATUS_CARRY_MASK); + + cpu_set_flag(system, CPU_STATUS_CARRY_MASK, value & 0x01); value = (value >> 1) | (carry << 7); - write_operand(operand, value); + write_operand(system, operand, value); - set_acl_flags(value); - cpu_add_cycles(get_shift_cycle_count(addr_mode)); + set_acl_flags(system, value); + cpu_add_cycles(system, get_shift_cycle_count(addr_mode)); } -void op_RRA(AddressingMode addr_mode) { - assert(false); +__attribute__((unused)) +void op_RRA(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); + byte carry = cpu_get_flag(system, CPU_STATUS_CARRY_MASK); + + cpu_set_flag(system, CPU_STATUS_CARRY_MASK, value & 0x01); + value = (value >> 1) | (carry << 7); + add_with_carry(system, value); + write_operand(system, operand, value); + +// set_acl_flags(system, value); + cpu_add_cycles(system, get_shift_cycle_count(addr_mode)); } -void op_RTI(AddressingMode addr_mode) { - cpu_stack_pop_context(); - cpu_add_cycles(6); +__attribute__((unused)) +void op_RTI(System *system, AddressingMode addr_mode) { + cpu_stack_pop_context(system); + cpu_add_cycles(system, 6); } -void op_RTS(AddressingMode addr_mode) { - byte lo = cpu_stack_pop(); - address pc = cpu_stack_pop() << 8; +__attribute__((unused)) +void op_RTS(System *system, AddressingMode addr_mode) { + byte lo = cpu_stack_pop(system); + address pc = cpu_stack_pop(system) << 8; pc += lo; - cpu_get_registers()->program_counter = pc - 1; - cpu_add_cycles(6); + system->cpu.program_counter = pc + 1; + cpu_add_cycles(system, 6); } -void op_SAX(AddressingMode addr_mode) { +__attribute__((unused)) +void op_SAX(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + + byte x = system->cpu.x; + byte acc = system->cpu.accumulator; + + byte result = acc & x; + write_operand(system, operand, result); + + cpu_add_cycles(system, get_cycle_count(operand, addr_mode)); +} + +__attribute__((unused)) +void op_SBC(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); + + add_with_carry(system, ~value); + + cpu_add_cycles(system, get_cycle_count(operand, addr_mode)); +} + +__attribute__((unused)) +void op_SEC(System *system, AddressingMode addr_mode) { + cpu_set_flag(system, CPU_STATUS_CARRY_MASK, true); + cpu_add_cycles(system, 2); +} + +__attribute__((unused)) +void op_SED(System *system, AddressingMode addr_mode) { + cpu_set_flag(system, CPU_STATUS_DECIMAL_MASK, true); + cpu_add_cycles(system, 2); +} + +__attribute__((unused)) +void op_SEI(System *system, AddressingMode addr_mode) { + cpu_set_flag(system, CPU_STATUS_INTERRUPT_DISABLE_MASK, true); + cpu_add_cycles(system, 2); +} + +__attribute__((unused)) +void op_SHX(System *system, AddressingMode addr_mode) { assert(false); } -void op_SBC(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte value = read_operand(operand); - - add_with_carry(~value); - - cpu_add_cycles(get_cycle_count(operand, addr_mode)); -} - -void op_SEC(AddressingMode addr_mode) { - cpu_set_flag(1, CPU_STATUS_CARRY_MASK); - cpu_add_cycles(2); -} - -void op_SED(AddressingMode addr_mode) { - cpu_set_flag(1, CPU_STATUS_DECIMAL_MASK); - cpu_add_cycles(2); -} - -void op_SEI(AddressingMode addr_mode) { - cpu_set_flag(1, CPU_STATUS_INTERRUPT_DISABLE_MASK); - cpu_add_cycles(2); -} - -void op_SHX(AddressingMode addr_mode) { +__attribute__((unused)) +void op_SHY(System *system, AddressingMode addr_mode) { assert(false); } -void op_SHY(AddressingMode addr_mode) { - assert(false); +__attribute__((unused)) +void op_SLO(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); + byte acc = system->cpu.accumulator; + + byte result = value << 1; + acc |= result; + system->cpu.accumulator = acc; + + write_operand(system, operand, result); + + cpu_set_flag(system, CPU_STATUS_CARRY_MASK, value & 0x80); + set_acl_flags(system, acc); + cpu_add_cycles(system, get_shift_cycle_count(addr_mode)); } -void op_SLO(AddressingMode addr_mode) { - assert(false); +__attribute__((unused)) +void op_SRE(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte value = read_operand(system, operand); + byte acc = system->cpu.accumulator; + + // Put bit 0 in the carry flag + cpu_set_flag(system, CPU_STATUS_CARRY_MASK, value & 0x01); + + value >>= 1; + acc ^= value; + system->cpu.accumulator = acc; + write_operand(system, operand, value); + + set_acl_flags(system, acc); + cpu_add_cycles(system, get_shift_cycle_count(addr_mode)); } -void op_SRE(AddressingMode addr_mode) { - assert(false); -} - -void op_STA(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte acc = cpu_get_registers()->accumulator; +__attribute__((unused)) +void op_STA(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte acc = system->cpu.accumulator; assert(operand.type == OPERAND_TYPE_ADDRESS); - cpu_push_byte(acc, operand.value); + mem_set_byte(system, operand.value, acc); operand.is_page_crossing = true; - cpu_add_cycles(get_cycle_count(operand, addr_mode)); + cpu_add_cycles(system, get_cycle_count(operand, addr_mode)); } -void op_STP(AddressingMode addr_mode) { +__attribute__((unused)) +void op_STP(System *system, AddressingMode addr_mode) { assert(false); } -void op_STX(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte x = cpu_get_registers()->x; +__attribute__((unused)) +void op_STX(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte x = system->cpu.x; assert(operand.type == OPERAND_TYPE_ADDRESS); - cpu_push_byte(x, operand.value); + mem_set_byte(system, operand.value, x); - cpu_add_cycles(get_cycle_count(operand, addr_mode)); + cpu_add_cycles(system, get_cycle_count(operand, addr_mode)); } -void op_STY(AddressingMode addr_mode) { - Operand operand = decode_operand(addr_mode); - byte y = cpu_get_registers()->y; +__attribute__((unused)) +void op_STY(System *system, AddressingMode addr_mode) { + Operand operand = decode_operand(system, addr_mode); + byte y = system->cpu.y; assert(operand.type == OPERAND_TYPE_ADDRESS); - cpu_push_byte(y, operand.value); + mem_set_byte(system, operand.value, y); - cpu_add_cycles(get_cycle_count(operand, addr_mode)); + cpu_add_cycles(system, get_cycle_count(operand, addr_mode)); } -void op_TAS(AddressingMode addr_mode) { +__attribute__((unused)) +void op_TAS(System *system, AddressingMode addr_mode) { assert(false); } -void op_TAX(AddressingMode addr_mode) { - byte acc = cpu_get_registers()->accumulator; - cpu_get_registers()->x = acc; +__attribute__((unused)) +void op_TAX(System *system, AddressingMode addr_mode) { + byte acc = system->cpu.accumulator; + system->cpu.x = acc; - set_acl_flags(acc); - cpu_add_cycles(2); + set_acl_flags(system, acc); + cpu_add_cycles(system, 2); } -void op_TAY(AddressingMode addr_mode) { - byte acc = cpu_get_registers()->accumulator; - cpu_get_registers()->y = acc; +__attribute__((unused)) +void op_TAY(System *system, AddressingMode addr_mode) { + byte acc = system->cpu.accumulator; + system->cpu.y = acc; - set_acl_flags(acc); - cpu_add_cycles(2); + set_acl_flags(system, acc); + cpu_add_cycles(system, 2); } -void op_TSX(AddressingMode addr_mode) { - byte value = cpu_stack_pop(); - cpu_get_registers()->x = value; +__attribute__((unused)) +void op_TSX(System *system, AddressingMode addr_mode) { + byte value = system->cpu.stack_pointer; + system->cpu.x = value; - set_acl_flags(value); - cpu_add_cycles(2); + set_acl_flags(system, value); + cpu_add_cycles(system, 2); } -void op_TXA(AddressingMode addr_mode) { - byte x = cpu_get_registers()->x; - cpu_get_registers()->accumulator = x; +__attribute__((unused)) +void op_TXA(System *system, AddressingMode addr_mode) { + byte x = system->cpu.x; + system->cpu.accumulator = x; - set_acl_flags(x); - cpu_add_cycles(2); + set_acl_flags(system, x); + cpu_add_cycles(system, 2); } -void op_TXS(AddressingMode addr_mode) { - byte x = cpu_get_registers()->x; - cpu_stack_push(x); +__attribute__((unused)) +void op_TXS(System *system, AddressingMode addr_mode) { + byte x = system->cpu.x; + system->cpu.stack_pointer = x; - cpu_add_cycles(2); + cpu_add_cycles(system, 2); } -void op_TYA(AddressingMode addr_mode) { - byte y = cpu_get_registers()->y; - cpu_get_registers()->accumulator = y; +__attribute__((unused)) +void op_TYA(System *system, AddressingMode addr_mode) { + byte y = system->cpu.y; + system->cpu.accumulator = y; - set_acl_flags(y); - cpu_add_cycles(2); + set_acl_flags(system, y); + cpu_add_cycles(system, 2); } -void op_XAA(AddressingMode addr_mode) { +__attribute__((unused)) +void op_XAA(System *system, AddressingMode addr_mode) { assert(false); } -void process_op_code(byte op) { +void process_op_code(System *system, byte op) { switch (op) { - // CTRL +// CTRL IS_OP_CODE(BRK, 0x00) IS_OP_CODE(PHP, 0x08) IS_OP_CODE(CLC, 0x18) @@ -873,8 +1083,8 @@ void process_op_code(byte op) { IS_OP_CODE_MODE(JSR, 0x20, ABSOLUTE) IS_OP_CODE_MODE(BIT, 0x24, ZERO_PAGE) IS_OP_CODE_MODE(BIT, 0x2c, ABSOLUTE) - IS_OP_CODE_MODE(JMP, 0x4c, ABSOLUTE) - IS_OP_CODE_MODE(JMP, 0x6c, ABSOLUTE_JUMP) + IS_OP_CODE_MODE(JMP, 0x4c, ABSOLUTE_JUMP) + IS_OP_CODE_MODE(JMP, 0x6c, INDIRECT_JUMP) IS_OP_CODE_MODE(STY, 0x84, ZERO_PAGE) IS_OP_CODE_MODE(STY, 0x8c, ABSOLUTE) IS_OP_CODE_MODE(STY, 0x94, ZERO_PAGE_INDEXED_X) @@ -911,14 +1121,14 @@ void process_op_code(byte op) { IS_OP_CODE_MODE(NOP, 0x5c, ABSOLUTE_INDEXED_X) IS_OP_CODE_MODE(NOP, 0x64, ZERO_PAGE) IS_OP_CODE_MODE(NOP, 0x74, ZERO_PAGE_INDEXED_X) - IS_OP_CODE_MODE(NOP, 0x7c, ZERO_PAGE_INDEXED_X) + IS_OP_CODE_MODE(NOP, 0x7c, ABSOLUTE_INDEXED_X) IS_OP_CODE_MODE(NOP, 0x80, IMMEDIATE) IS_OP_CODE_MODE(NOP, 0xd4, ZERO_PAGE_INDEXED_X) IS_OP_CODE_MODE(NOP, 0xdc, ABSOLUTE_INDEXED_X) IS_OP_CODE_MODE(NOP, 0xf4, ZERO_PAGE_INDEXED_X) IS_OP_CODE_MODE(NOP, 0xfc, ABSOLUTE_INDEXED_X) - // ALU +// ALU IS_ALU_OP_CODE(ORA) IS_ALU_OP_CODE(AND) IS_ALU_OP_CODE(EOR) @@ -928,7 +1138,7 @@ void process_op_code(byte op) { IS_ALU_OP_CODE(CMP) IS_ALU_OP_CODE(SBC) - // RMW +// RMW IS_RMW_OP_CODE(ASL, ORA) IS_RMW_OP_CODE(ROL, AND) IS_RMW_OP_CODE(LSR, EOR) @@ -953,7 +1163,7 @@ void process_op_code(byte op) { IS_OP_CODE_MODE(STX, 0x8e, ABSOLUTE) IS_OP_CODE(STP, 0x92) IS_OP_CODE_MODE(STX, 0x96, ZERO_PAGE_INDEXED_Y) - IS_OP_CODE(TSX, 0x9a) + IS_OP_CODE(TXS, 0x9a) IS_OP_CODE_MODE(SHX, 0x9e, ABSOLUTE_INDEXED_Y) IS_OP_CODE_MODE(LDX, 0xa2, IMMEDIATE) @@ -970,20 +1180,20 @@ void process_op_code(byte op) { IS_OP_CODE(DEX, 0xca) IS_OP_CODE_MODE(DEC, 0xce, ABSOLUTE) IS_OP_CODE(STP, 0xd2) - IS_OP_CODE_MODE(DEC, 0xd6, ZERO_PAGE_INDEXED_Y) + IS_OP_CODE_MODE(DEC, 0xd6, ZERO_PAGE_INDEXED_X) IS_OP_CODE(NOP, 0xda) - IS_OP_CODE_MODE(DEC, 0xde, ABSOLUTE_INDEXED_Y) + IS_OP_CODE_MODE(DEC, 0xde, ABSOLUTE_INDEXED_X) IS_OP_CODE_MODE(NOP, 0xe2, IMMEDIATE) IS_OP_CODE_MODE(INC, 0xe6, ZERO_PAGE) IS_OP_CODE(NOP, 0xea) // The official NOP IS_OP_CODE_MODE(INC, 0xee, ABSOLUTE) IS_OP_CODE(STP, 0xf2) - IS_OP_CODE_MODE(INC, 0xf6, ZERO_PAGE_INDEXED_Y) + IS_OP_CODE_MODE(INC, 0xf6, ZERO_PAGE_INDEXED_X) IS_OP_CODE(NOP, 0xfa) IS_OP_CODE_MODE(INC, 0xfe, ABSOLUTE_INDEXED_X) - // Unofficial +// Unofficial IS_UNOFFICIAL_OP_CODE(SLO, ORA) IS_UNOFFICIAL_OP_CODE(RLA, AND) IS_UNOFFICIAL_OP_CODE(SRE, EOR) @@ -1013,7 +1223,351 @@ void process_op_code(byte op) { IS_OP_CODE_MODE(LAX, 0xaf, ABSOLUTE) IS_OP_CODE_MODE(LAX, 0xb3, INDIRECT_Y) IS_OP_CODE_MODE(LAX, 0xb7, ZERO_PAGE_INDEXED_Y) - IS_OP_CODE_MODE(LAX, 0xbb, ABSOLUTE_INDEXED_Y) + IS_OP_CODE_MODE(LAS, 0xbb, ABSOLUTE_INDEXED_Y) IS_OP_CODE_MODE(LAX, 0xbf, ABSOLUTE_INDEXED_Y) + + default: + assert(false); + } +} + +char *get_op_code_name(byte op) { + switch (op) { + case 0x00: + return "BRK"; + case 0x08: + return "PHP"; + case 0x0b: + case 0x2b: + return "ANC"; + case 0x10: + return "BPL"; + case 0x18: + return "CLC"; + case 0x20: + return "JSR"; + case 0x24: + case 0x2c: + return "BIT"; + case 0x28: + return "PLP"; + case 0x30: + return "BMI"; + case 0x38: + return "SEC"; + case 0x40: + return "RTI"; + case 0x48: + return "PHA"; + case 0x4c: + case 0x6c: + return "JMP"; + case 0x50: + return "BVC"; + case 0x58: + return "CLI"; + case 0x60: + return "RTS"; + case 0x68: + return "PLA"; + case 0x70: + return "BVS"; + case 0x78: + return "SEI"; + case 0x84: + case 0x8c: + case 0x94: + return "STY"; + case 0x88: + return "DEY"; + case 0x90: + return "BCC"; + case 0x98: + return "TYA"; + case 0x9c: + return "SHY"; + case 0xa0: + case 0xa4: + case 0xac: + case 0xb4: + case 0xbc: + return "LDY"; + case 0xa8: + return "TAY"; + case 0xb0: + return "BCS"; + case 0xb8: + return "CLV"; + case 0xc0: + case 0xc4: + case 0xcc: + return "CPY"; + case 0xc8: + return "INY"; + case 0xd0: + return "BNE"; + case 0xd8: + return "CLD"; + case 0xe0: + case 0xe4: + case 0xec: + return "CPX"; + case 0xe8: + return "INX"; + case 0xf0: + return "BEQ"; + case 0xf8: + return "SED"; + + case 0x01: + case 0x05: + case 0x09: + case 0x0d: + case 0x11: + case 0x15: + case 0x19: + case 0x1d: + return "ORA"; + case 0x21: + case 0x25: + case 0x29: + case 0x2d: + case 0x31: + case 0x35: + case 0x39: + case 0x3d: + return "AND"; + case 0x41: + case 0x45: + case 0x49: + case 0x4d: + case 0x51: + case 0x55: + case 0x59: + case 0x5d: + return "EOR"; + case 0x61: + case 0x65: + case 0x69: + case 0x6d: + case 0x71: + case 0x75: + case 0x79: + case 0x7d: + return "ADC"; + case 0x81: + case 0x85: + case 0x8d: + case 0x91: + case 0x95: + case 0x99: + case 0x9d: + return "STA"; + case 0xa1: + case 0xa5: + case 0xa9: + case 0xad: + case 0xb1: + case 0xb5: + case 0xb9: + case 0xbd: + return "LDA"; + case 0xc1: + case 0xc5: + case 0xc9: + case 0xcd: + case 0xd1: + case 0xd5: + case 0xd9: + case 0xdd: + return "CMP"; + case 0xe1: + case 0xe5: + case 0xe9: + case 0xed: + case 0xf1: + case 0xf5: + case 0xf9: + case 0xfd: + return "SBC"; + + case 0x03: + case 0x07: + case 0x0f: + case 0x13: + case 0x17: + case 0x1b: + case 0x1f: + return "SLO"; + case 0x23: + case 0x27: + case 0x2f: + case 0x33: + case 0x37: + case 0x3b: + case 0x3f: + return "RLA"; + case 0x43: + case 0x47: + case 0x4f: + case 0x53: + case 0x57: + case 0x5b: + case 0x5f: + return "SRE"; + case 0x4b: + return "ALR"; + case 0x63: + case 0x67: + case 0x6f: + case 0x73: + case 0x77: + case 0x7b: + case 0x7f: + return "RRA"; + case 0x6b: + return "ARR"; + case 0x83: + case 0x87: + case 0x8f: + case 0x97: + return "SAX"; + case 0x8b: + return "XAA"; + case 0x93: + case 0x9f: + return "AHX"; + case 0x9b: + return "TAS"; + case 0xa3: + case 0xa7: + case 0xab: + case 0xaf: + case 0xb3: + case 0xb7: + case 0xbb: + case 0xbf: + return "LAX"; + case 0xc3: + case 0xc7: + case 0xcf: + case 0xd3: + case 0xd7: + case 0xdb: + case 0xdf: + return "DCP"; + case 0xcb: + return "AXS"; + case 0xe3: + case 0xe7: + case 0xef: + case 0xf3: + case 0xf7: + case 0xfb: + case 0xff: + return "ISC"; + case 0xeb: + return "SBC"; + + case 0x06: + case 0x0a: + case 0x0e: + case 0x16: + case 0x1e: + return "ASL"; + case 0x26: + case 0x2a: + case 0x2e: + case 0x36: + case 0x3e: + return "ROL"; + case 0x46: + case 0x4a: + case 0x4e: + case 0x56: + case 0x5e: + return "LSR"; + case 0x66: + case 0x6a: + case 0x6e: + case 0x76: + case 0x7e: + return "ROR"; + case 0x86: + case 0x8e: + case 0x96: + return "STX"; + case 0x8a: + return "TXA"; + case 0x9a: + case 0xba: + return "TSX"; + case 0x9e: + return "SHX"; + case 0xa2: + case 0xa6: + case 0xae: + case 0xb6: + case 0xbe: + return "LDX"; + case 0xaa: + return "TAX"; + case 0xc6: + case 0xca: + case 0xce: + case 0xd6: + case 0xde: + return "DEC"; + case 0xe6: + case 0xee: + case 0xf6: + case 0xfe: + return "INC"; + + case 0x02: + case 0x12: + case 0x22: + case 0x32: + case 0x42: + case 0x52: + case 0x62: + case 0x72: + case 0x92: + case 0xb2: + case 0xd2: + case 0xf2: + return "STP"; + case 0x04: + case 0x0c: + case 0x14: + case 0x1c: + case 0x1a: + case 0x34: + case 0x3a: + case 0x3c: + case 0x44: + case 0x54: + case 0x5a: + case 0x5c: + case 0x64: + case 0x74: + case 0x7a: + case 0x7c: + case 0x80: + case 0x82: + case 0x89: + case 0xc2: + case 0xd4: + case 0xda: + case 0xdc: + case 0xe2: + case 0xea: + case 0xf4: + case 0xfa: + case 0xfc: + return "NOP"; + + default: + assert(false); } } \ No newline at end of file diff --git a/cpu/op.h b/cpu/op.h index 9ffe03c..6d4412f 100644 --- a/cpu/op.h +++ b/cpu/op.h @@ -33,6 +33,7 @@ typedef enum { ADDR_MODE_ZERO_PAGE_INDEXED_Y, // d,y } AddressingMode; -void process_op_code(byte op); +void process_op_code(System *system, byte op); +char* get_op_code_name(byte op); #endif \ No newline at end of file diff --git a/cpu/ram.c b/cpu/ram.c deleted file mode 100644 index 24b619d..0000000 --- a/cpu/ram.c +++ /dev/null @@ -1,22 +0,0 @@ -// -// Created by william on 30/09/23. -// - -#include "ram.h" -#include "../include/cpu.h" - -byte ram[MEM_RAM_AMOUNT]; - -void ram_set_byte(address addr, byte byte) { - ram[addr] = byte; -} - -byte ram_get_byte(address addr) { - return ram[addr]; -} - -word ram_get_word(address addr) { - word word = ram_get_byte(addr); - word += ram_get_byte(addr + 1) << 8; // Little endian - return word; -} \ No newline at end of file diff --git a/cpu/ram.h b/cpu/ram.h deleted file mode 100644 index 169e999..0000000 --- a/cpu/ram.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Created by william on 30/09/23. -// - -#include "cpu.h" - -#ifndef NESEMULATOR_RAM_H -#define NESEMULATOR_RAM_H - -// The 6502 CPU has 2 KiB of RAM -#define MEM_RAM_AMOUNT 2048 - -typedef unsigned short address; - -void ram_set_byte(address addr, byte byte); -byte ram_get_byte(address addr); -word ram_get_word(address addr); - -#endif //NESEMULATOR_RAM_H diff --git a/include/cpu.h b/include/cpu.h index 7236bae..9b0464d 100644 --- a/include/cpu.h +++ b/include/cpu.h @@ -16,14 +16,14 @@ * ===================================================================================== */ +#include "types.h" +#include "system.h" + #ifndef NESEMULATOR_CPU_H #define NESEMULATOR_CPU_H -typedef unsigned char byte; -typedef unsigned short address; -typedef unsigned short word; +void cpu_init(CPU *cpu); -void cpu_init(); -void cpu_step(); +void cpu_cycle(System *system); #endif diff --git a/include/mapper.h b/include/mapper.h index 67b48ff..b7c4293 100644 --- a/include/mapper.h +++ b/include/mapper.h @@ -2,13 +2,15 @@ // Created by william on 10/15/23. // +#include "types.h" + #ifndef NESEMULATOR_MAPPER_H #define NESEMULATOR_MAPPER_H -#include "../include/cpu.h" +typedef struct mapper { + address prg_rom_start_addr; -typedef struct { - address (*redirect_addr)(unsigned short); + void (*post_prg_load)(ram, unsigned int); } Mapper; enum MapperType { diff --git a/include/ppu.h b/include/ppu.h new file mode 100644 index 0000000..655b48f --- /dev/null +++ b/include/ppu.h @@ -0,0 +1,96 @@ +// +// Created by william on 12/30/23. +// + +#include +#include +#include "types.h" + +#ifndef NESEMULATOR_PPU_H +#define NESEMULATOR_PPU_H + +#define PPU_REGISTER_SIZE 0x8 +#define PPU_VRAM_SIZE 0x4000 +#define PPU_OAM_SIZE 0xff + +#define PPU_REGISTER_CTRL 0x00 +#define PPU_REGISTER_MASK 0x01 +#define PPU_REGISTER_STATUS 0x02 +#define PPU_REGISTER_OAM_ADDR 0x03 +#define PPU_REGISTER_OAM_DATA 0x04 +#define PPU_REGISTER_SCROLL 0x05 +#define PPU_REGISTER_ADDR 0x06 +#define PPU_REGISTER_DATA 0x07 + +#define PPU_CTRL_BASE_NAMETABLE_ADDR 0x3 +#define PPU_CTRL_VRAM_ADDR_INCREMENT 0x4 +#define PPU_CTRL_SP_PATTERN_TABLE_ADDR 0x8 +#define PPU_CTRL_BG_PATTERN_TABLE_ADDR 0x10 +#define PPU_CTRL_SP_SIZE 0x20 +#define PPU_CTRL_MODE_SELECT 0x40 +#define PPU_CTRL_GEN_VBLANK_NMI 0x80 + +#define PPU_MASK_GREYSCALE 0x1 +#define PPU_MASK_SHOW_BG_LEFT 0x2 +#define PPU_MASK_SHOW_SP_LEFT 0x4 +#define PPU_MASK_SHOW_BG 0x8 +#define PPU_MASK_SHOW_SP 0x10 +#define PPU_MASK_EMP_RED 0x20 +#define PPU_MASK_EMP_GREEN 0x40 +#define PPU_MASK_EMP_BLUE 0x80 + +#define PPU_STATUS_OPEN_BUS 0x1f +#define PPU_STATUS_SP_OVERFLOW 0x20 +#define PPU_STATUS_SP_0_HIT 0x40 +#define PPU_STATUS_VBLANK 0x80 + +#define PPU_MASK_NONE 0xff + +typedef struct ppu { + byte* registers; + byte* oam_dma_register; + byte vram[PPU_VRAM_SIZE]; + byte oam[PPU_OAM_SIZE]; + bool odd_frame; + address v; + address t; + byte x; + bool w; +} PPU; + +/** + * Initializes the PPU, according to the power up state. + * https://www.nesdev.org/wiki/PPU_power_up_state + * + * @param ppu + */ +void ppu_init(PPU *ppu, byte *registers_ram, byte *oam_dma_register); + +/** + * Cycles the PPU. + * + * @param ppu + * @param ram + */ +void ppu_cycle(PPU *ppu); + +/** + * Read a flag from the PPU registers. + * + * @param reg The register index + * @param mask The flag mask + */ +bool ppu_read_flag(PPU *ppu, size_t reg, byte mask); + +/** + * Read a value from the PPU registers. Does not apply any offset to the value, a mask of 0x20 will either result in 0x20 (true) or 0x0 (false). + * Read a value from the PPU registers. Does not apply any offset to the value, a mask of 0x20 will either result in 0x20 (true) or 0x0 (false). + * + * @param reg The register index + * @param mask The value mask + */ +void ppu_read_register(PPU *ppu, byte reg); + +void ppu_write_register(PPU *ppu, byte reg); + +#endif //NESEMULATOR_PPU_H diff --git a/include/rom.h b/include/rom.h index af5afca..fb85395 100644 --- a/include/rom.h +++ b/include/rom.h @@ -2,12 +2,14 @@ // Created by william on 12/2/23. // +#include +#include "types.h" +#include "system.h" + #ifndef NESEMULATOR_ROM_H #define NESEMULATOR_ROM_H // The size of the header in a ROM file, in bytes -#include "cpu.h" - #define ROM_HEADER_SIZE 16 // The size of the trainer in a ROM file, in bytes #define ROM_TRAINER_SIZE 512 @@ -18,12 +20,13 @@ typedef struct { void *header; } Rom; -int rom_load(char *path); - -void rom_uninit(); - -byte rom_prg_get_byte(address addr); - -word rom_prg_get_word(address addr); +/** + * Loads a ROM from a specified file path. + * + * @param path The file path + * @param rom ROM + * @return A boolean indicating a success (true) or an error. + */ +bool rom_load(char *path, System *system); #endif //NESEMULATOR_ROM_H \ No newline at end of file diff --git a/include/system.h b/include/system.h new file mode 100644 index 0000000..4dc6e05 --- /dev/null +++ b/include/system.h @@ -0,0 +1,66 @@ +// +// Created by william on 12/23/23. +// + +#include "types.h" +#include "mapper.h" +#include "ppu.h" + +#ifndef NESEMULATOR_SYSTEM_H +#define NESEMULATOR_SYSTEM_H + +// NTSC NES Master Clock (~21.47 MHz) +#define MASTER_CLOCK 21477272 +#define CPU_CLOCK_DIVISOR 12 +#define PPU_CLOCK_DIVISOR 4 +#define FRAME_RATE 60 + +#define PPU_REGISTERS_BASE_ADDR 0x2000 +#define PPU_REGISTER_OAM_DMA_ADDR 0x4014 +#define APU_REGISTERS_COUNT 24 + +// Reference: https://www.nesdev.org/obelisk-6502-guide/registers.html +typedef struct cpu { + address program_counter; + byte stack_pointer; + byte accumulator; + byte x; + byte y; + byte status; + bool oam_dma_triggered; +} CPU; + +typedef struct system { + void *rom_header; + CPU cpu; + PPU ppu; + Mapper mapper; + ram ram; + byte apu_registers[APU_REGISTERS_COUNT]; + unsigned long cycle_count; +} System; + +/** + * Initialize all components of a system. + * + * @param system The system to initialize + */ +void system_init(System *system); + +void system_start(System *system); + +/** + * Starts the main loop of a system. + * + * @param system The system + */ +void system_loop(System *system); + +/** + * De-initialize the components of a system. + * + * @param system The system to de-initialize + */ +void system_uninit(System *system); + +#endif //NESEMULATOR_SYSTEM_H diff --git a/include/types.h b/include/types.h new file mode 100644 index 0000000..0c31ed3 --- /dev/null +++ b/include/types.h @@ -0,0 +1,18 @@ +// +// Created by william on 12/26/23. +// + +#ifndef NESEMULATOR_TYPES_H +#define NESEMULATOR_TYPES_H + +#define RAM_SIZE 0xffff +#define VRAM_SIZE 0x4000 + +typedef unsigned char byte; +typedef unsigned short address; +typedef unsigned short word; + +typedef byte ram[RAM_SIZE]; +typedef byte vram[VRAM_SIZE]; + +#endif //NESEMULATOR_TYPES_H diff --git a/main.c b/main.c index 00a111c..6a795f9 100644 --- a/main.c +++ b/main.c @@ -16,19 +16,27 @@ * ===================================================================================== */ #include +#include #include "include/rom.h" +#include "include/system.h" int main() { - char *rom_path = "../tests/smb.nes"; + log_set_level(LOG_INFO); - cpu_init(); - rom_load(rom_path); + char *rom_path = "../test_roms/nestest.nes"; + System system; - cpu_step(); + system_init(&system); - rom_uninit(); + if (!rom_load(rom_path, &system)) { + system_uninit(&system); + return EXIT_FAILURE; + } + + system_start(&system); + system_loop(&system); + system_uninit(&system); return EXIT_SUCCESS; -} - +} \ No newline at end of file diff --git a/mappers/simple_mapper.c b/mappers/simple_mapper.c index 11538a5..cee62e6 100644 --- a/mappers/simple_mapper.c +++ b/mappers/simple_mapper.c @@ -1,11 +1,25 @@ #include "../include/mapper.h" +#include "../include/rom.h" +#include -address redirect_addr(address addr) { - return addr; +#define SIMPLE_MAPPER_PRG_START_ADDR 0x8000 +#define PRG_PART_SIZE 0x4000 // 16Kb + +void post_prg_load(ram ram, unsigned int prg_size) { + if (prg_size == 2) { + // The whole space is occupied, nothing to do + return; + } + + // We need to mirror the data in the upper ram + byte *source = (byte *) &ram[SIMPLE_MAPPER_PRG_START_ADDR]; + byte *destination = (byte *) &ram[SIMPLE_MAPPER_PRG_START_ADDR + PRG_PART_SIZE]; + memcpy(destination, source, PRG_PART_SIZE); } Mapper get_simple_mapper() { Mapper mapper; - mapper.redirect_addr = &redirect_addr; + mapper.prg_rom_start_addr = SIMPLE_MAPPER_PRG_START_ADDR; + mapper.post_prg_load = &post_prg_load; return mapper; } \ No newline at end of file diff --git a/ppu/CMakeLists.txt b/ppu/CMakeLists.txt new file mode 100644 index 0000000..b15a099 --- /dev/null +++ b/ppu/CMakeLists.txt @@ -0,0 +1,5 @@ +add_library(PPU + ppu.c) + +find_package(log.c) +target_link_libraries(PPU log.c::log.c) \ No newline at end of file diff --git a/ppu/ppu.c b/ppu/ppu.c new file mode 100644 index 0000000..ab51564 --- /dev/null +++ b/ppu/ppu.c @@ -0,0 +1,47 @@ +// +// Created by william on 12/30/23. +// + +#include +#include "../include/ppu.h" + +void ppu_init(PPU *ppu, byte *registers_ram, byte *oam_dma_register) { + ppu->registers = registers_ram; + ppu->registers[PPU_REGISTER_CTRL] = 0x00; + ppu->registers[PPU_REGISTER_MASK] = 0x00; + ppu->registers[PPU_REGISTER_STATUS] = 0x00; + ppu->registers[PPU_REGISTER_OAM_ADDR] = 0x00; + ppu->registers[PPU_REGISTER_OAM_DATA] = 0x00; + ppu->registers[PPU_REGISTER_SCROLL] = 0x00; + ppu->registers[PPU_REGISTER_ADDR] = 0x00; + ppu->registers[PPU_REGISTER_DATA] = 0x00; + ppu->oam_dma_register = oam_dma_register; + ppu->odd_frame = false; +} + +void ppu_cycle(PPU *ppu) { +} + +bool ppu_read_flag(PPU *ppu, size_t reg, byte mask) { + return ppu->registers[reg] & mask; +} + +//byte ppu_read_register(PPU *ppu, size_t reg, byte mask) { +// return ppu->registers[reg] & mask; +//} + +void ppu_read_register(PPU *ppu, byte reg) { + if (reg == PPU_REGISTER_STATUS) { + ppu->w = false; + } +} + +void ppu_write_register(PPU *ppu, byte reg) { + if (reg == PPU_REGISTER_SCROLL || reg == PPU_REGISTER_ADDR) { + ppu->w = !ppu->w; + } + + if (reg == PPU_REGISTER_OAM_DATA) { + ppu->registers[PPU_REGISTER_OAM_ADDR]++; + } +} \ No newline at end of file diff --git a/rom/ines.c b/rom/ines.c index 1d58921..e85bb21 100644 --- a/rom/ines.c +++ b/rom/ines.c @@ -4,9 +4,9 @@ #include #include -#include #include #include "../include/rom.h" +#include "../include/system.h" // Flag 6 #define NES_HEADER_FLAG_MIRRORING 0x01 @@ -126,32 +126,30 @@ bool rom_ines_read_trainer(FILE *file, INesHeader *header) { return false; } -bool rom_ines_read_prg_rom(FILE *file, INesHeader *header, Rom *rom) { +bool rom_ines_read_prg_rom(FILE *file, INesHeader *header, System *system) { unsigned int prg_rom_size = header->prg_rom_size * 16384; - rom->prg_rom = (byte *) malloc(prg_rom_size * sizeof(byte)); - log_debug("Reading %d bytes PRG ROM", prg_rom_size); - if (fread(rom->prg_rom, sizeof(byte), prg_rom_size, file) < prg_rom_size) { + if (fread(&system->ram[system->mapper.prg_rom_start_addr], sizeof(byte), prg_rom_size, file) < prg_rom_size) { log_error("Failed to read PRG ROM"); return false; } + system->mapper.post_prg_load(&system->ram[0], header->prg_rom_size); + return true; } -bool rom_ines_read_chr_rom(FILE *file, INesHeader *header, Rom *rom) { +bool rom_ines_read_chr_rom(FILE *file, INesHeader *header, System *system) { if (header->chr_rom_size <= 0) { log_debug("No CHR ROM to read"); return true; } unsigned int chr_rom_size = header->chr_rom_size * 8192; - rom->chr_rom = (byte *) malloc(chr_rom_size * sizeof(byte)); - log_debug("Reading %d bytes CHR ROM", chr_rom_size); - if (fread(rom->chr_rom, sizeof(byte), chr_rom_size, file) < chr_rom_size) { + if (fread(system->ppu.vram, sizeof(byte), chr_rom_size, file) < chr_rom_size) { log_error("Failed to read CHR ROM"); return false; } @@ -159,11 +157,11 @@ bool rom_ines_read_chr_rom(FILE *file, INesHeader *header, Rom *rom) { return true; } -bool rom_ines_read(const char header_buf[ROM_HEADER_SIZE], FILE *file, Rom *rom) { +bool rom_ines_read(const char header_buf[ROM_HEADER_SIZE], FILE *file, System *system) { INesHeader header = read_header(header_buf); - rom->header = &header; + system->rom_header = &header; return rom_ines_read_trainer(file, &header) && - rom_ines_read_prg_rom(file, &header, rom) && - rom_ines_read_chr_rom(file, &header, rom); + rom_ines_read_prg_rom(file, &header, system) && + rom_ines_read_chr_rom(file, &header, system); } \ No newline at end of file diff --git a/rom/rom.c b/rom/rom.c index 50eab4f..291f270 100644 --- a/rom/rom.c +++ b/rom/rom.c @@ -3,74 +3,37 @@ // #include -#include -#include #include "../include/rom.h" #include "ines.c" +#include "../include/system.h" -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) - -Rom rom; - -void rom_init() { - rom.header = NULL; - rom.prg_rom = NULL; - rom.chr_rom = NULL; -} - -int rom_load(char *path) { - rom_init(); - +bool rom_load(char *path, System *system) { FILE *file = fopen(path, "r"); if (!file) { log_error("Failed to open ROM"); - return EXIT_FAILURE; + return false; } char header_buffer[ROM_HEADER_SIZE] = {0}; - size_t read_size = fread(header_buffer, sizeof(char), ARRAY_SIZE(header_buffer), file); - if (read_size < ARRAY_SIZE(header_buffer)) { + size_t read_size = fread(header_buffer, sizeof(char), ROM_HEADER_SIZE, file); + if (read_size < ROM_HEADER_SIZE) { log_error("Failed to read ROM"); - return EXIT_FAILURE; + return false; } if (!rom_is_ines(header_buffer)) { log_error("Only iNes ROMs are supported"); - return EXIT_FAILURE; + return false; } log_info("Reading iNes 1.0 ROM at %s", path); - rom_ines_read(header_buffer, file, &rom); + rom_ines_read(header_buffer, file, system); if (fclose(file) != 0) { log_error("Failed to close ROM file"); - return EXIT_FAILURE; + return false; } - return 0; -} - -void rom_uninit() { - assert(rom.prg_rom != NULL); - assert(rom.chr_rom != NULL); - - free(rom.prg_rom); - free(rom.chr_rom); - - log_info("Cleared ROM data"); -} - -byte rom_prg_get_byte(address addr) { - assert(rom.prg_rom != NULL); - - return rom.prg_rom[addr]; -} - -word rom_prg_get_word(address addr) { - assert(rom.prg_rom != NULL); - - word word = rom.prg_rom[addr]; - word += rom.prg_rom[addr + 1] << 8; // Little endian - return word; + return true; } \ No newline at end of file diff --git a/system.c b/system.c new file mode 100644 index 0000000..4e0e5a4 --- /dev/null +++ b/system.c @@ -0,0 +1,57 @@ +// +// Created by william on 12/23/23. +// + +#include "include/cpu.h" +#include "include/system.h" +#include "memory.h" +#include +#include +#include + +void system_init(System *system) { + byte *registers_base_addr = &system->ram[PPU_REGISTERS_BASE_ADDR]; + byte *oam_dma_register = &system->ram[PPU_REGISTER_OAM_DMA_ADDR]; + + cpu_init(&system->cpu); + ppu_init(&system->ppu, registers_base_addr, oam_dma_register); + + system->mapper = get_mapper(MAPPER_TYPE_SIMPLE); + system->cycle_count = 7; +} + +void system_start(System *system) { + address pc = mem_get_word(system, 0xfffc); + system->cpu.program_counter = pc; +} + +void system_loop(System *system) { + assert(CPU_CLOCK_DIVISOR > PPU_CLOCK_DIVISOR); + + unsigned int master_cycle_per_frame = MASTER_CLOCK / FRAME_RATE; + unsigned int cpu_cycle_per_frame = master_cycle_per_frame / CPU_CLOCK_DIVISOR; + unsigned int ppu_cycle_per_cpu_cycle = CPU_CLOCK_DIVISOR / PPU_CLOCK_DIVISOR; + + long frame = 1; + long cpu_cycle_count = 0; + while (true) { + log_info("Frame %d", frame); + + while (system->cycle_count < cpu_cycle_per_frame * frame) { + if (cpu_cycle_count == system->cycle_count) { + cpu_cycle(system); + } + cpu_cycle_count++; + + for (int ppu_c = 0; ppu_c < ppu_cycle_per_cpu_cycle; ppu_c++) { + ppu_cycle(&system->ppu); + } + } + + frame++; + usleep(17000); // Wait 16.6666ms + } +} + +void system_uninit(System *system) { +} \ No newline at end of file diff --git a/tests/cpu_exec_space/readme.txt b/test_roms/cpu_exec_space/readme.txt similarity index 100% rename from tests/cpu_exec_space/readme.txt rename to test_roms/cpu_exec_space/readme.txt diff --git a/tests/cpu_exec_space/source/common/ascii_1.chr b/test_roms/cpu_exec_space/source/common/ascii_1.chr similarity index 100% rename from tests/cpu_exec_space/source/common/ascii_1.chr rename to test_roms/cpu_exec_space/source/common/ascii_1.chr diff --git a/tests/cpu_exec_space/source/common/ascii_2.chr b/test_roms/cpu_exec_space/source/common/ascii_2.chr similarity index 100% rename from tests/cpu_exec_space/source/common/ascii_2.chr rename to test_roms/cpu_exec_space/source/common/ascii_2.chr diff --git a/tests/cpu_exec_space/source/common/ascii_3.chr b/test_roms/cpu_exec_space/source/common/ascii_3.chr similarity index 100% rename from tests/cpu_exec_space/source/common/ascii_3.chr rename to test_roms/cpu_exec_space/source/common/ascii_3.chr diff --git a/tests/cpu_exec_space/source/common/build_rom.s b/test_roms/cpu_exec_space/source/common/build_rom.s similarity index 100% rename from tests/cpu_exec_space/source/common/build_rom.s rename to test_roms/cpu_exec_space/source/common/build_rom.s diff --git a/tests/cpu_exec_space/source/common/colors.inc b/test_roms/cpu_exec_space/source/common/colors.inc similarity index 100% rename from tests/cpu_exec_space/source/common/colors.inc rename to test_roms/cpu_exec_space/source/common/colors.inc diff --git a/tests/cpu_exec_space/source/common/console.s b/test_roms/cpu_exec_space/source/common/console.s similarity index 100% rename from tests/cpu_exec_space/source/common/console.s rename to test_roms/cpu_exec_space/source/common/console.s diff --git a/tests/cpu_exec_space/source/common/crc.s b/test_roms/cpu_exec_space/source/common/crc.s similarity index 100% rename from tests/cpu_exec_space/source/common/crc.s rename to test_roms/cpu_exec_space/source/common/crc.s diff --git a/tests/cpu_exec_space/source/common/delay.s b/test_roms/cpu_exec_space/source/common/delay.s similarity index 100% rename from tests/cpu_exec_space/source/common/delay.s rename to test_roms/cpu_exec_space/source/common/delay.s diff --git a/tests/cpu_exec_space/source/common/devcart.bin b/test_roms/cpu_exec_space/source/common/devcart.bin similarity index 100% rename from tests/cpu_exec_space/source/common/devcart.bin rename to test_roms/cpu_exec_space/source/common/devcart.bin diff --git a/tests/cpu_exec_space/source/common/macros.inc b/test_roms/cpu_exec_space/source/common/macros.inc similarity index 100% rename from tests/cpu_exec_space/source/common/macros.inc rename to test_roms/cpu_exec_space/source/common/macros.inc diff --git a/tests/cpu_exec_space/source/common/neshw.inc b/test_roms/cpu_exec_space/source/common/neshw.inc similarity index 100% rename from tests/cpu_exec_space/source/common/neshw.inc rename to test_roms/cpu_exec_space/source/common/neshw.inc diff --git a/tests/cpu_exec_space/source/common/ppu.s b/test_roms/cpu_exec_space/source/common/ppu.s similarity index 100% rename from tests/cpu_exec_space/source/common/ppu.s rename to test_roms/cpu_exec_space/source/common/ppu.s diff --git a/tests/cpu_exec_space/source/common/print.s b/test_roms/cpu_exec_space/source/common/print.s similarity index 100% rename from tests/cpu_exec_space/source/common/print.s rename to test_roms/cpu_exec_space/source/common/print.s diff --git a/tests/cpu_exec_space/source/common/shell.inc b/test_roms/cpu_exec_space/source/common/shell.inc similarity index 100% rename from tests/cpu_exec_space/source/common/shell.inc rename to test_roms/cpu_exec_space/source/common/shell.inc diff --git a/tests/cpu_exec_space/source/common/shell.s b/test_roms/cpu_exec_space/source/common/shell.s similarity index 100% rename from tests/cpu_exec_space/source/common/shell.s rename to test_roms/cpu_exec_space/source/common/shell.s diff --git a/tests/cpu_exec_space/source/common/testing.s b/test_roms/cpu_exec_space/source/common/testing.s similarity index 100% rename from tests/cpu_exec_space/source/common/testing.s rename to test_roms/cpu_exec_space/source/common/testing.s diff --git a/tests/cpu_exec_space/source/common/text_out.s b/test_roms/cpu_exec_space/source/common/text_out.s similarity index 100% rename from tests/cpu_exec_space/source/common/text_out.s rename to test_roms/cpu_exec_space/source/common/text_out.s diff --git a/tests/cpu_exec_space/source/readme.txt b/test_roms/cpu_exec_space/source/readme.txt similarity index 100% rename from tests/cpu_exec_space/source/readme.txt rename to test_roms/cpu_exec_space/source/readme.txt diff --git a/tests/cpu_exec_space/source/test_cpu_exec_space_apu.s b/test_roms/cpu_exec_space/source/test_cpu_exec_space_apu.s similarity index 100% rename from tests/cpu_exec_space/source/test_cpu_exec_space_apu.s rename to test_roms/cpu_exec_space/source/test_cpu_exec_space_apu.s diff --git a/tests/cpu_exec_space/source/test_cpu_exec_space_ppuio.s b/test_roms/cpu_exec_space/source/test_cpu_exec_space_ppuio.s similarity index 100% rename from tests/cpu_exec_space/source/test_cpu_exec_space_ppuio.s rename to test_roms/cpu_exec_space/source/test_cpu_exec_space_ppuio.s diff --git a/tests/cpu_exec_space/test_cpu_exec_space_apu.nes b/test_roms/cpu_exec_space/test_cpu_exec_space_apu.nes similarity index 100% rename from tests/cpu_exec_space/test_cpu_exec_space_apu.nes rename to test_roms/cpu_exec_space/test_cpu_exec_space_apu.nes diff --git a/tests/cpu_exec_space/test_cpu_exec_space_ppuio.nes b/test_roms/cpu_exec_space/test_cpu_exec_space_ppuio.nes similarity index 100% rename from tests/cpu_exec_space/test_cpu_exec_space_ppuio.nes rename to test_roms/cpu_exec_space/test_cpu_exec_space_ppuio.nes diff --git a/test_roms/nestest.fdb b/test_roms/nestest.fdb new file mode 100644 index 0000000..46c2c6d --- /dev/null +++ b/test_roms/nestest.fdb @@ -0,0 +1,3 @@ +BreakPoint: startAddr=00000014 endAddr=00000000 flags=ER--X- condition="" desc="" +BreakPoint: startAddr=00000023 endAddr=00000000 flags=ER--X- condition="" desc="" +BreakPoint: startAddr=00000000 endAddr=00000000 flags=EC--X- condition="" desc="" diff --git a/test_roms/nestest.nes b/test_roms/nestest.nes new file mode 100644 index 0000000000000000000000000000000000000000..fc2a88c36db9c5c2de9cca33be0e446e12007f17 GIT binary patch literal 24592 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 O2L?Sb=z;&AJn-*m;2p96 literal 0 HcmV?d00001 diff --git a/test_roms/smb.fdb b/test_roms/smb.fdb new file mode 100644 index 0000000..4168ad2 --- /dev/null +++ b/test_roms/smb.fdb @@ -0,0 +1 @@ +BreakPoint: startAddr=00000022 endAddr=00000000 flags=ER--X- condition="" desc="" diff --git a/tests/smb.nes b/test_roms/smb.nes similarity index 100% rename from tests/smb.nes rename to test_roms/smb.nes