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 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -49,6 +97,9 @@
+
+
+
@@ -92,17 +143,148 @@
}
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -113,11 +295,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -250,6 +452,7 @@
+
@@ -283,6 +486,12 @@
+
+
+
+
+
+
@@ -300,7 +509,15 @@
1701463073548
-
+
+
+ 1703369431911
+
+
+
+ 1703369431911
+
+
@@ -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 0000000..fc2a88c
Binary files /dev/null and b/test_roms/nestest.nes differ
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