Move states to global variables

This commit is contained in:
FyloZ 2024-05-06 20:23:44 -04:00
parent 7caf88171f
commit 22401f30ac
Signed by: william
GPG Key ID: 835378AE9AF4AE97
31 changed files with 710 additions and 656 deletions

1
.gitignore vendored
View File

@ -154,3 +154,4 @@ environment_run.ps1.env
environment_run.sh.env environment_run.sh.env
# End of https://www.toptal.com/developers/gitignore/api/cmake,clion,conan # End of https://www.toptal.com/developers/gitignore/api/cmake,clion,conan
/test_roms/

156
cpu/cpu.c
View File

@ -11,7 +11,7 @@
/* /*
* ===================================================================================== * =====================================================================================
* *
* Filename: cpu.c * Filename: cpu_state.c
* *
* Description: 6502 CPU emulator * Description: 6502 CPU emulator
* *
@ -26,133 +26,147 @@
* ===================================================================================== * =====================================================================================
*/ */
void cpu_init(CPU *cpu) { CPU cpu_state;
cpu->program_counter = 0x8000;
cpu->stack_pointer = 0xfd; void cpu_init() {
cpu->accumulator = 0x00; cpu_state.program_counter = 0x8000;
cpu->x = 0x00; cpu_state.stack_pointer = 0xfd;
cpu->y = 0x00; cpu_state.accumulator = 0x00;
cpu->status = 0x04; cpu_state.x = 0x00;
cpu->oam_dma_triggered = false; cpu_state.y = 0x00;
cpu->nmi_requested = false; cpu_state.status = 0x04;
cpu_state.oam_dma_triggered = false;
cpu_state.nmi_requested = false;
} }
void print_registers(CPU cpu, byte op, unsigned long cycle_count) { void print_registers(byte op, unsigned long cycle_count) {
log_debug("%#02x %#02x %s \t A:%#02x X:%#02x Y:%#02x F:%#02x SP:%#02x \t [%d]", log_info("%#02x %#02x %s \t A:%#02x X:%#02x Y:%#02x F:%#02x SP:%#02x \t [%d]",
cpu.program_counter, cpu_state.program_counter,
op, op,
get_op_code_name(op), get_op_code_name(op),
cpu.accumulator, cpu_state.accumulator,
cpu.x, cpu_state.x,
cpu.y, cpu_state.y,
cpu.status, cpu_state.status,
cpu.stack_pointer, cpu_state.stack_pointer,
cycle_count); cycle_count);
} }
void cpu_process_nmi(System *system) { void cpu_process_nmi() {
cpu_stack_push_context(system); cpu_stack_push_context();
address handler_addr = mem_get_word(system, 0xfffa); address handler_addr = mem_get_word(0xfffa);
log_debug("NMI %#04x", handler_addr); log_debug("NMI %#04x", handler_addr);
system->cpu.nmi_requested = false; cpu_state.nmi_requested = false;
system->cpu.program_counter = handler_addr; cpu_state.program_counter = handler_addr;
} }
void oam_dma_upload(System *system) { void oam_dma_upload() {
byte page_high_addr = *system->ppu.oam_dma_register; byte page_high_addr = *ppu_get_state()->oam_dma_register; // TODO
address page_addr = ((address) page_high_addr) << 8; address page_addr = ((address) page_high_addr) << 8;
byte n = 0xff; byte n = 0xff;
byte *ram_source = &system->ram[page_addr]; byte *ram_source = mem_get_ptr(page_addr);
byte *oam_destination = system->ppu.oam; byte *oam_destination = ppu_get_state()->oam;
memcpy(oam_destination, ram_source, n); memcpy(oam_destination, ram_source, n);
log_debug("OAM DMA %#04x", page_addr); log_debug("OAM DMA %#04x", page_addr);
cpu_add_cycles(system, 513); // TODO cpu_add_cycles(513); // TODO
} }
void cpu_cycle(System *system) { void cpu_cycle() {
if (system->cpu.nmi_requested) { if (cpu_state.nmi_requested) {
cpu_process_nmi(system); cpu_process_nmi();
} }
if (system->cpu.oam_dma_triggered) { if (cpu_state.oam_dma_triggered) {
oam_dma_upload(system); oam_dma_upload();
system->cpu.oam_dma_triggered = false; cpu_state.oam_dma_triggered = false;
return; return;
} }
CPU cpu = system->cpu; CPU cpu = cpu_state;
byte op = cpu_get_next_byte(system); byte op = cpu_get_next_byte();
print_registers(cpu, op, system->cycle_count); print_registers(op, system_get_cycles());
process_op_code(system, op); process_op_code(op);
} }
void cpu_add_cycles(System *system, unsigned int cycle_count) { void cpu_add_cycles(unsigned int cycle_count) {
system->cycle_count += cycle_count; system_add_cycles(cycle_count);
} }
// === Registers === // === Registers ===
bool system_get_flag(System *system, byte mask) { bool cpu_get_flag(byte mask) {
return cpu_get_flag(&system->cpu, mask); return cpu_state.status & mask;
} }
void system_set_flag(System *system, byte mask, bool set) { void cpu_set_flag(byte mask, bool set) {
if (set) { if (set) {
system->cpu.status |= mask; cpu_state.status |= mask;
} else { } else {
system->cpu.status &= ~mask; cpu_state.status &= ~mask;
} }
} }
byte cpu_get_next_byte(System *system) { byte cpu_get_next_byte() {
byte next_byte = mem_get_byte(system, system->cpu.program_counter); byte next_byte = mem_get_byte(cpu_state.program_counter);
system->cpu.program_counter++; cpu_state.program_counter++;
return next_byte; return next_byte;
} }
word cpu_get_next_word(System *system) { word cpu_get_next_word() {
word next_word = mem_get_word(system, system->cpu.program_counter); word next_word = mem_get_word(cpu_state.program_counter);
system->cpu.program_counter += 2; cpu_state.program_counter += 2;
return next_word; return next_word;
} }
void cpu_stack_push(System *system, byte value) { void cpu_stack_push(byte value) {
assert(system->cpu.stack_pointer > 0); assert(cpu_state.stack_pointer > 0);
address mem_addr = CPU_STACK_ADDR | system->cpu.stack_pointer; address mem_addr = CPU_STACK_ADDR | cpu_state.stack_pointer;
mem_set_byte(system, mem_addr, value); mem_set_byte(mem_addr, value);
system->cpu.stack_pointer--; cpu_state.stack_pointer--;
} }
byte cpu_stack_pop(System *system) { byte cpu_stack_pop() {
assert(system->cpu.stack_pointer < 0xff); assert(cpu_state.stack_pointer < 0xff);
system->cpu.stack_pointer++; cpu_state.stack_pointer++;
address mem_addr = CPU_STACK_ADDR | system->cpu.stack_pointer; address mem_addr = CPU_STACK_ADDR | cpu_state.stack_pointer;
byte value = mem_get_byte(system, mem_addr); byte value = mem_get_byte(mem_addr);
return value; return value;
} }
void cpu_stack_push_context(System *system) { void cpu_stack_push_context() {
cpu_stack_push(system, system->cpu.program_counter >> 8); cpu_stack_push(cpu_state.program_counter >> 8);
cpu_stack_push(system, system->cpu.program_counter & 0xff); cpu_stack_push(cpu_state.program_counter & 0xff);
cpu_stack_push(system, system->cpu.status); cpu_stack_push(cpu_state.status);
} }
void cpu_stack_pop_context(System *system) { void cpu_stack_pop_context() {
byte value = cpu_stack_pop(system); byte value = cpu_stack_pop();
value &= 0xef; // The B mask cannot be set as it is a CPU signal value &= 0xef; // The B mask cannot be set as it is a CPU signal
value |= 0x20; // This value is always set value |= 0x20; // This value is always set
system->cpu.status = value; cpu_state.status = value;
byte lo = cpu_stack_pop(system); byte lo = cpu_stack_pop();
address pc = cpu_stack_pop(system) << 8; address pc = cpu_stack_pop() << 8;
pc += lo; pc += lo;
system->cpu.program_counter = pc; cpu_state.program_counter = pc;
}
void cpu_trigger_oam_dma() {
cpu_state.oam_dma_triggered = true;
}
void cpu_trigger_nmi() {
cpu_state.nmi_requested = true;
}
CPU *cpu_get_state() {
return &cpu_state;
} }

View File

@ -19,6 +19,20 @@
#define CPU_STACK_ADDR 0x0100 #define CPU_STACK_ADDR 0x0100
// 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;
bool nmi_requested;
} CPU;
CPU *cpu_get_state();
/** /**
* Gets a flag from the CPU registers. * Gets a flag from the CPU registers.
* *
@ -26,11 +40,7 @@
* @param mask The flag mask * @param mask The flag mask
* @return The value of the flag. * @return The value of the flag.
*/ */
bool system_get_flag(System *system, byte mask); bool cpu_get_flag(byte mask);
static inline bool cpu_get_flag(CPU *cpu, byte mask) {
return cpu->status & mask;
}
/** /**
* Sets a flag in the CPU registers. * Sets a flag in the CPU registers.
@ -39,7 +49,7 @@ static inline bool cpu_get_flag(CPU *cpu, byte mask) {
* @param mask The flag mask * @param mask The flag mask
* @param set If the flag is set or not * @param set If the flag is set or not
*/ */
void system_set_flag(System *system, byte mask, bool set); void cpu_set_flag(byte mask, bool set);
/** /**
* Gets the next byte in the program. * Gets the next byte in the program.
@ -48,7 +58,7 @@ void system_set_flag(System *system, byte mask, bool set);
* @param system The system * @param system The system
* @return The value of the next byte. * @return The value of the next byte.
*/ */
byte cpu_get_next_byte(System *system); byte cpu_get_next_byte();
/** /**
* Gets the next word in the program. * Gets the next word in the program.
@ -57,7 +67,7 @@ byte cpu_get_next_byte(System *system);
* @param system The system * @param system The system
* @return The value of the next word. * @return The value of the next word.
*/ */
word cpu_get_next_word(System *system); word cpu_get_next_word();
/** /**
* Pushes a byte in to the stack. * Pushes a byte in to the stack.
@ -65,7 +75,7 @@ word cpu_get_next_word(System *system);
* @param system The system * @param system The system
* @param value The value to push to the stack * @param value The value to push to the stack
*/ */
void cpu_stack_push(System *system, byte value); void cpu_stack_push(byte value);
/** /**
* Pushes the execution context to the stack. * Pushes the execution context to the stack.
@ -73,7 +83,7 @@ void cpu_stack_push(System *system, byte value);
* *
* @param system The system * @param system The system
*/ */
void cpu_stack_push_context(System *system); void cpu_stack_push_context();
/** /**
* Pops a byte from the stack. * Pops a byte from the stack.
@ -81,7 +91,7 @@ void cpu_stack_push_context(System *system);
* @param system The system * @param system The system
* @return The value of the byte * @return The value of the byte
*/ */
byte cpu_stack_pop(System *system); byte cpu_stack_pop();
/** /**
* Pops an execution context from the stack and overwrite the current context. * Pops an execution context from the stack and overwrite the current context.
@ -89,13 +99,16 @@ byte cpu_stack_pop(System *system);
* *
* @param system The system * @param system The system
*/ */
void cpu_stack_pop_context(System *system); void cpu_stack_pop_context();
/** /**
* Adds wait cycles to the CPU. * Adds wait cycles to the CPU.
* *
* @param cycle_count The number of cycle to wait * @param cycle_count The number of cycle to wait
*/ */
void cpu_add_cycles(System *system, unsigned int cycle_count); void cpu_add_cycles(unsigned int cycle_count);
void cpu_trigger_oam_dma();
void cpu_trigger_nmi();
#endif //CPU_CPU_H #endif //CPU_CPU_H

View File

@ -7,52 +7,52 @@
#include <assert.h> #include <assert.h>
#include "log.h" #include "log.h"
address decode_operand_addr(System *system, AddressingMode addr_mode, bool *page_crossing) { address decode_operand_addr(AddressingMode addr_mode, bool *page_crossing) {
CPU registers = system->cpu; CPU *registers = cpu_get_state();
address operand_addr; address operand_addr;
if (addr_mode == ADDR_MODE_ZERO_PAGE) { if (addr_mode == ADDR_MODE_ZERO_PAGE) {
operand_addr = cpu_get_next_byte(system); operand_addr = cpu_get_next_byte();
} else if (addr_mode == ADDR_MODE_ZERO_PAGE_INDEXED_X) { } else if (addr_mode == ADDR_MODE_ZERO_PAGE_INDEXED_X) {
operand_addr = (cpu_get_next_byte(system) + registers.x) & 0xff; operand_addr = (cpu_get_next_byte() + registers->x) & 0xff;
} else if (addr_mode == ADDR_MODE_ZERO_PAGE_INDEXED_Y) { } else if (addr_mode == ADDR_MODE_ZERO_PAGE_INDEXED_Y) {
operand_addr = (cpu_get_next_byte(system) + registers.y) & 0xff; operand_addr = (cpu_get_next_byte() + registers->y) & 0xff;
} else if (addr_mode == ADDR_MODE_ABSOLUTE) { } else if (addr_mode == ADDR_MODE_ABSOLUTE) {
operand_addr = cpu_get_next_word(system); operand_addr = cpu_get_next_word();
} else if (addr_mode == ADDR_MODE_ABSOLUTE_INDEXED_X) { } else if (addr_mode == ADDR_MODE_ABSOLUTE_INDEXED_X) {
word addr = cpu_get_next_word(system); word addr = cpu_get_next_word();
word new_addr = addr + registers.x; word new_addr = addr + registers->x;
*page_crossing = (addr & 0xff00) != (new_addr & 0xff00); *page_crossing = (addr & 0xff00) != (new_addr & 0xff00);
operand_addr = new_addr; operand_addr = new_addr;
} else if (addr_mode == ADDR_MODE_ABSOLUTE_INDEXED_Y) { } else if (addr_mode == ADDR_MODE_ABSOLUTE_INDEXED_Y) {
word addr = cpu_get_next_word(system); word addr = cpu_get_next_word();
word new_addr = addr + registers.y; word new_addr = addr + registers->y;
*page_crossing = (addr & 0xff00) != (new_addr & 0xff00); *page_crossing = (addr & 0xff00) != (new_addr & 0xff00);
operand_addr = new_addr; operand_addr = new_addr;
} else if (addr_mode == ADDR_MODE_INDIRECT_JUMP) { } else if (addr_mode == ADDR_MODE_INDIRECT_JUMP) {
word addr = cpu_get_next_word(system); word addr = cpu_get_next_word();
if ((addr & 0xff) == 0xff) { if ((addr & 0xff) == 0xff) {
// Error in NES CPU for JMP op // Error in NES CPU for JMP op
word result = mem_get_byte(system, addr); word result = mem_get_byte(addr);
result += mem_get_byte(system, addr & 0xff00) << 8; result += mem_get_byte(addr & 0xff00) << 8;
operand_addr = result; operand_addr = result;
} else { } else {
operand_addr = mem_get_word(system, addr); operand_addr = mem_get_word(addr);
} }
} else if (addr_mode == ADDR_MODE_INDIRECT_X) { } else if (addr_mode == ADDR_MODE_INDIRECT_X) {
byte arg_addr = cpu_get_next_byte(system); byte arg_addr = cpu_get_next_byte();
word addr = mem_get_byte(system, (arg_addr + system->cpu.x) & 0xff); word addr = mem_get_byte((arg_addr + registers->x) & 0xff);
addr += mem_get_byte(system, (arg_addr + system->cpu.x + 1) & 0xff) << 8; addr += mem_get_byte((arg_addr + registers->x + 1) & 0xff) << 8;
operand_addr = addr; operand_addr = addr;
} else if (addr_mode == ADDR_MODE_INDIRECT_Y) { } else if (addr_mode == ADDR_MODE_INDIRECT_Y) {
byte arg_addr = cpu_get_next_byte(system); byte arg_addr = cpu_get_next_byte();
word addr = mem_get_byte(system, arg_addr) + (mem_get_byte(system, (arg_addr + 1) & 0xff) << 8); word addr = mem_get_byte(arg_addr) + (mem_get_byte((arg_addr + 1) & 0xff) << 8);
word new_addr = addr + registers.y; word new_addr = addr + registers->y;
*page_crossing = (addr & 0xff00) != (new_addr & 0xff00); *page_crossing = (addr & 0xff00) != (new_addr & 0xff00);
@ -65,7 +65,7 @@ address decode_operand_addr(System *system, AddressingMode addr_mode, bool *page
return operand_addr; return operand_addr;
} }
Operand decode_operand(System *system, AddressingMode addr_mode) { Operand decode_operand(AddressingMode addr_mode) {
Operand operand; Operand operand;
if (addr_mode == ADDR_MODE_ACCUMULATOR) { if (addr_mode == ADDR_MODE_ACCUMULATOR) {
@ -74,25 +74,25 @@ Operand decode_operand(System *system, AddressingMode addr_mode) {
operand.is_page_crossing = false; operand.is_page_crossing = false;
} else if (addr_mode == ADDR_MODE_IMMEDIATE) { } else if (addr_mode == ADDR_MODE_IMMEDIATE) {
operand.type = OPERAND_TYPE_IMMEDIATE; operand.type = OPERAND_TYPE_IMMEDIATE;
operand.value = cpu_get_next_byte(system); operand.value = cpu_get_next_byte();
operand.is_page_crossing = false; operand.is_page_crossing = false;
} else { } else {
operand.type = OPERAND_TYPE_ADDRESS; operand.type = OPERAND_TYPE_ADDRESS;
operand.value = decode_operand_addr(system, addr_mode, &operand.is_page_crossing); operand.value = decode_operand_addr(addr_mode, &operand.is_page_crossing);
} }
log_trace("Operand type: %s, value: %#02x", operand_name(&operand), operand.value); log_trace("Operand type: %s, value: %#02x", operand_name(&operand), operand.value);
return operand; return operand;
} }
byte read_operand(System *system, Operand operand) { byte read_operand(Operand operand) {
switch (operand.type) { switch (operand.type) {
case OPERAND_TYPE_ACCUMULATOR: case OPERAND_TYPE_ACCUMULATOR:
return system->cpu.accumulator; return cpu_get_state()->accumulator;
case OPERAND_TYPE_IMMEDIATE: case OPERAND_TYPE_IMMEDIATE:
return (byte) operand.value; return (byte) operand.value;
case OPERAND_TYPE_ADDRESS: case OPERAND_TYPE_ADDRESS:
return mem_get_byte(system, operand.value); return mem_get_byte(operand.value);
default: default:
assert(false); assert(false);
} }

View File

@ -37,11 +37,11 @@ typedef struct {
bool is_page_crossing; bool is_page_crossing;
} Operand; } Operand;
address decode_operand_addr(System *system, AddressingMode addr_mode, bool *page_crossing); address decode_operand_addr(AddressingMode addr_mode, bool *page_crossing);
Operand decode_operand(System *system, AddressingMode addr_mode); Operand decode_operand(AddressingMode addr_mode);
byte read_operand(System *system, Operand operand); byte read_operand(Operand operand);
char *get_addr_mode_name(AddressingMode addr_mode); char *get_addr_mode_name(AddressingMode addr_mode);

View File

@ -6,6 +6,7 @@
#include "log.h" #include "log.h"
#include "memory.h" #include "memory.h"
#include "../include/rom.h" #include "../include/rom.h"
#include "cpu.h"
#define RAM_MAX_ADDR 0x2000 #define RAM_MAX_ADDR 0x2000
#define RAM_BANK_SIZE 0x800 #define RAM_BANK_SIZE 0x800
@ -14,36 +15,34 @@
#define APU_MAX_ADDR 0x4020 #define APU_MAX_ADDR 0x4020
#define MAX_ADDR 0xffff #define MAX_ADDR 0xffff
byte mem_get_byte(System *system, address addr) { byte ram[RAM_SIZE];
byte mem_get_byte(address addr) {
assert(addr <= MAX_ADDR); assert(addr <= MAX_ADDR);
if (addr >= RAM_MAX_ADDR && addr < PPU_MAX_ADDR) { if (addr >= RAM_MAX_ADDR && addr < PPU_MAX_ADDR) {
byte reg = (addr - RAM_MAX_ADDR) % PPU_BANK_SIZE; byte reg = (addr - RAM_MAX_ADDR) % PPU_BANK_SIZE;
ppu_read_register(&system->ppu, reg); ppu_read_register(reg);
return system->ppu.registers[reg];
} }
if (addr >= PPU_MAX_ADDR && addr < APU_MAX_ADDR) { return ram[addr];
byte apu_addr = addr - PPU_MAX_ADDR;
return system->apu_registers[apu_addr];
} }
return system->ram[addr]; byte *mem_get_ptr(address addr) {
assert(addr <= MAX_ADDR);
return &ram[addr];
} }
word mem_get_word(System *system, address addr) { word mem_get_word(address addr) {
assert(addr < MAX_ADDR); assert(addr < MAX_ADDR);
if (addr >= RAM_MAX_ADDR && addr < APU_MAX_ADDR) { word word = ram[addr];
assert(false); word += ram[addr + 1] << 8; // Little endian
}
word word = system->ram[addr];
word += system->ram[addr + 1] << 8; // Little endian
return word; return word;
} }
void mem_set_byte(System *system, address addr, byte byte) { void mem_set_byte(address addr, byte byte) {
assert(addr < MAX_ADDR); assert(addr < MAX_ADDR);
log_trace("Writing '%02x' to address 0x%04x", byte, addr); log_trace("Writing '%02x' to address 0x%04x", byte, addr);
@ -54,24 +53,24 @@ void mem_set_byte(System *system, address addr, byte byte) {
// The value must also be cloned in the three mirrors // The value must also be cloned in the three mirrors
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
address ram_addr = init_ram_addr + RAM_BANK_SIZE * i; address ram_addr = init_ram_addr + RAM_BANK_SIZE * i;
system->ram[ram_addr] = byte; ram[ram_addr] = byte;
} }
} else if (addr < PPU_MAX_ADDR) { } else if (addr < PPU_MAX_ADDR) {
address reg_addr = (addr - RAM_MAX_ADDR) % PPU_BANK_SIZE; address reg_addr = (addr - RAM_MAX_ADDR) % PPU_BANK_SIZE;
int bank_count = (PPU_MAX_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++) { for (int i = 0; i < bank_count; i++) {
address ram_addr = reg_addr + PPU_BANK_SIZE * i; address ram_addr = reg_addr + PPU_BANK_SIZE * i + RAM_MAX_ADDR;
system->ppu.registers[ram_addr] = byte; ram[ram_addr] = byte;
} }
ppu_write_register(&system->ppu, reg_addr); ppu_write_register(reg_addr);
} else { } else {
system->ram[addr] = byte; ram[addr] = byte;
if (addr == PPU_REGISTER_OAM_DMA_ADDR) { if (addr == PPU_REGISTER_OAM_DMA_ADDR) {
// Writing to this address triggers an upload to the PPU memory // Writing to this address triggers an upload to the PPU memory
system->cpu.oam_dma_triggered = true; cpu_trigger_oam_dma();
} }
} }
} }

View File

@ -11,28 +11,34 @@
/** /**
* Gets a byte from a system's memory. * Gets a byte from a system's memory.
* *
* @param system A reference to the system
* @param addr The address to get * @param addr The address to get
* @return The value of the byte at the given address. * @return The value of the byte at the given address.
*/ */
byte mem_get_byte(System *system, address addr); byte mem_get_byte(address addr);
/**
* Gets a pointer to a byte in the memory.
* Should not be used by the CPU, because the PPU will not be triggered if reading some addresses.
*
* @param addr The address to get a pointer to
* @return A pointer to the byte in memory
*/
byte* mem_get_ptr(address addr);
/** /**
* Gets a word from a system's memory. * Gets a word from a system's memory.
* *
* @param system A reference to the system
* @param addr The address to get * @param addr The address to get
* @return The value of the word at the given address. * @return The value of the word at the given address.
*/ */
word mem_get_word(System *system, address addr); word mem_get_word(address addr);
/** /**
* Sets a byte in a system's memory. * Sets a byte in a system's memory.
* *
* @param system A reference to the system
* @param addr The address to set * @param addr The address to set
* @param value The value to set * @param value The value to set
*/ */
void mem_set_byte(System *system, address addr, byte value); void mem_set_byte(address addr, byte value);
#endif //NESEMULATOR_MEMORY_H #endif //NESEMULATOR_MEMORY_H

732
cpu/op.c

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,7 @@ enum op_code_base {
OP_CODE_BASE_SBC = 0xe0 OP_CODE_BASE_SBC = 0xe0
}; };
void process_op_code(System *system, byte op); void process_op_code(byte op);
AddressingMode get_op_addr_mode(byte op_code); AddressingMode get_op_addr_mode(byte op_code);

View File

@ -7,24 +7,24 @@
#include "../cpu/cpu.h" #include "../cpu/cpu.h"
void cv_print(CpuView *view) { void cv_print(CpuView *view) {
window_print(view->window, 0, 0, "PC: $%04x", view->cpu->program_counter); CPU *cpu_state = cpu_get_state();
window_print(view->window, 0, 1, "SP: %02x", view->cpu->stack_pointer); window_print(view->window, 0, 0, "PC: $%04x", cpu_state->program_counter);
window_print(view->window, 0, 2, "A: %02x", view->cpu->accumulator); window_print(view->window, 0, 1, "SP: %02x", cpu_state->stack_pointer);
window_print(view->window, 0, 3, "X: %02x", view->cpu->x); window_print(view->window, 0, 2, "A: %02x", cpu_state->accumulator);
window_print(view->window, 0, 4, "Y: %02x", view->cpu->y); window_print(view->window, 0, 3, "X: %02x", cpu_state->x);
window_print(view->window, 0, 5, "C: %01x", cpu_get_flag(view->cpu, CPU_STATUS_CARRY_MASK)); window_print(view->window, 0, 4, "Y: %02x", cpu_state->y);
window_print(view->window, 0, 6, "Z: %01x", cpu_get_flag(view->cpu, CPU_STATUS_ZERO_MASK)); window_print(view->window, 0, 5, "C: %01x", cpu_get_flag(CPU_STATUS_CARRY_MASK));
window_print(view->window, 0, 7, "I: %01x", cpu_get_flag(view->cpu, CPU_STATUS_INTERRUPT_DISABLE_MASK)); window_print(view->window, 0, 6, "Z: %01x", cpu_get_flag(CPU_STATUS_ZERO_MASK));
window_print(view->window, 0, 8, "D: %01x", cpu_get_flag(view->cpu, CPU_STATUS_DECIMAL_MASK)); window_print(view->window, 0, 7, "I: %01x", cpu_get_flag(CPU_STATUS_INTERRUPT_DISABLE_MASK));
window_print(view->window, 0, 9, "B: %01x", cpu_get_flag(view->cpu, CPU_STATUS_B_MASK)); window_print(view->window, 0, 8, "D: %01x", cpu_get_flag(CPU_STATUS_DECIMAL_MASK));
window_print(view->window, 0, 10, "O: %01x", cpu_get_flag(view->cpu, CPU_STATUS_OVERFLOW_MASK)); window_print(view->window, 0, 9, "B: %01x", cpu_get_flag(CPU_STATUS_B_MASK));
window_print(view->window, 0, 11, "N: %01x", cpu_get_flag(view->cpu, CPU_STATUS_NEGATIVE_MASK)); window_print(view->window, 0, 10, "O: %01x", cpu_get_flag(CPU_STATUS_OVERFLOW_MASK));
window_print(view->window, 0, 11, "N: %01x", cpu_get_flag(CPU_STATUS_NEGATIVE_MASK));
} }
CpuView *cv_init(CPU *cpu, int x, int y) { CpuView *cv_init(int x, int y) {
CpuView *view = malloc(sizeof(CpuView)); CpuView *view = malloc(sizeof(CpuView));
view->window = malloc(sizeof(Window)); view->window = malloc(sizeof(Window));
view->cpu = cpu;
window_init(view->window, x, y, CPU_VIEW_WIDTH, CPU_VIEW_HEIGHT, "CPU VIEW"); window_init(view->window, x, y, CPU_VIEW_WIDTH, CPU_VIEW_HEIGHT, "CPU VIEW");
cv_print(view); cv_print(view);

View File

@ -14,13 +14,12 @@
typedef struct cpu_view { typedef struct cpu_view {
Window *window; Window *window;
CPU *cpu;
} CpuView; } CpuView;
/** /**
* Initializes a CPU view for a system RAM. * Initializes a CPU view for a system RAM.
*/ */
CpuView *cv_init(CPU *cpu, int x, int y); CpuView *cv_init(int x, int y);
void cv_uninit(CpuView *cpu_view); void cv_uninit(CpuView *cpu_view);

View File

@ -23,18 +23,18 @@ void debugger_create_window() {
keypad(stdscr, true); keypad(stdscr, true);
} }
LinkedList debugger_create_interactive_windows(System *system) { LinkedList debugger_create_interactive_windows() {
LinkedList interactive_windows; LinkedList interactive_windows;
InteractWindow *window; InteractWindow *window;
interactive_windows = linked_list_init(true); interactive_windows = linked_list_init(true);
window = malloc(sizeof(InteractWindow)); window = malloc(sizeof(InteractWindow));
mv_init(window, system->ram, 0, 0); mv_init(window, 0, 0);
linked_list_add(&interactive_windows, window); linked_list_add(&interactive_windows, window);
window = malloc(sizeof(InteractWindow)); window = malloc(sizeof(InteractWindow));
pv_init(window, system, MEMORY_VIEW_WIDTH, 0); pv_init(window, MEMORY_VIEW_WIDTH, 0);
linked_list_add(&interactive_windows, window); linked_list_add(&interactive_windows, window);
return interactive_windows; return interactive_windows;
@ -62,8 +62,8 @@ void start_debugger(System *system) {
interactive_windows = debugger_create_interactive_windows(system); interactive_windows = debugger_create_interactive_windows(system);
current_window = interactive_windows.current->data; current_window = interactive_windows.current->data;
cpu_view = cv_init(&system->cpu, 0, MEMORY_VIEW_HEIGHT); cpu_view = cv_init(0, MEMORY_VIEW_HEIGHT);
ppu_view = ppv_init(&system->ppu, CPU_VIEW_WIDTH, MEMORY_VIEW_HEIGHT); ppu_view = ppv_init(CPU_VIEW_WIDTH, MEMORY_VIEW_HEIGHT);
cursor_enable(&current_window->cursor); cursor_enable(&current_window->cursor);

View File

@ -7,6 +7,6 @@
#include "../include/system.h" #include "../include/system.h"
void start_debugger(System *system); void start_debugger();
#endif //NESEMULATOR_DEBUGGER_H #endif //NESEMULATOR_DEBUGGER_H

View File

@ -3,6 +3,7 @@
#include "memory_view.h" #include "memory_view.h"
#include "dialog.h" #include "dialog.h"
#include "keys.h" #include "keys.h"
#include "../cpu/memory.h"
// //
// Created by william on 6/1/24. // Created by william on 6/1/24.
@ -40,10 +41,9 @@ void mv_handle_key_down(InteractWindow *window, int keycode) {
} }
} }
void mv_init(InteractWindow *interact, ram ram, int x, int y) { void mv_init(InteractWindow *interact, int x, int y) {
MemoryView *view = malloc(sizeof(MemoryView)); MemoryView *view = malloc(sizeof(MemoryView));
view->window = interact; view->window = interact;
view->ram = ram;
view->base_address = 0x0000; view->base_address = 0x0000;
interact->view = view; interact->view = view;
@ -61,7 +61,7 @@ void mv_init(InteractWindow *interact, ram ram, int x, int y) {
void mv_print(MemoryView *view) { void mv_print(MemoryView *view) {
for (int line = 0; line <= MEMORY_VIEW_LINE_COUNT; line++) { for (int line = 0; line <= MEMORY_VIEW_LINE_COUNT; line++) {
address line_address = view->base_address + line * (MEMORY_VIEW_LINE_BYTE_COUNT + 1); address line_address = view->base_address + line * (MEMORY_VIEW_LINE_BYTE_COUNT + 1);
byte *data = &view->ram[line_address]; byte *data = mem_get_ptr(line_address);
mv_write_line(view, line, line_address, data); mv_write_line(view, line, line_address, data);
} }

View File

@ -18,7 +18,6 @@
typedef struct memory_view { typedef struct memory_view {
InteractWindow *window; InteractWindow *window;
byte *ram;
address base_address; address base_address;
} MemoryView; } MemoryView;
@ -27,9 +26,8 @@ typedef struct memory_view {
* The viewer base address will be set to 0x0000, and the cursor (0, 0). * The viewer base address will be set to 0x0000, and the cursor (0, 0).
* The content of the memory will be printed on a new curses window. * The content of the memory will be printed on a new curses window.
* @param view A pointer to the view to initialize * @param view A pointer to the view to initialize
* @param ram A pointer to the RAM
*/ */
void mv_init(InteractWindow *interact, ram ram, int x, int y); void mv_init(InteractWindow *interact, int x, int y);
/** /**
* Prints the RAM content from the viewer base address. * Prints the RAM content from the viewer base address.

View File

@ -6,7 +6,7 @@
#include "ppu_view.h" #include "ppu_view.h"
void ppv_print_line(PpuView *view, byte reg, int line, char *fmt) { void ppv_print_line(PpuView *view, byte reg, int line, char *fmt) {
int reg_value = view->ppu->registers[reg]; int reg_value = ppu_get_state()->registers[reg];
window_print(view->window, 0, line, fmt); window_print(view->window, 0, line, fmt);
for (int i = 0; i < 0x8; i++) { for (int i = 0; i < 0x8; i++) {
@ -33,10 +33,9 @@ void ppv_print(PpuView *view) {
ppv_print_line(view, PPU_REGISTER_DATA, 7, " DATA:"); ppv_print_line(view, PPU_REGISTER_DATA, 7, " DATA:");
} }
PpuView *ppv_init(PPU *ppu, int x, int y) { PpuView *ppv_init(int x, int y) {
PpuView *view = malloc(sizeof(PpuView)); PpuView *view = malloc(sizeof(PpuView));
view->window = malloc(sizeof(Window)); view->window = malloc(sizeof(Window));
view->ppu = ppu;
window_init(view->window, x, y, PPU_VIEW_WIDTH, PPU_VIEW_HEIGHT, "PPU VIEW"); window_init(view->window, x, y, PPU_VIEW_WIDTH, PPU_VIEW_HEIGHT, "PPU VIEW");
ppv_print(view); ppv_print(view);

View File

@ -13,10 +13,9 @@
typedef struct ppu_view { typedef struct ppu_view {
Window *window; Window *window;
PPU *ppu;
} PpuView; } PpuView;
PpuView *ppv_init(PPU *ppu, int x, int y); PpuView *ppv_init(int x, int y);
void ppv_uninit(PpuView *ppu_view); void ppv_uninit(PpuView *ppu_view);

View File

@ -19,7 +19,7 @@ void decode_operands(ProgramView *view) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
byte op_code = view->ram[pc]; byte op_code = mem_get_byte(pc);
operand->addr = pc; operand->addr = pc;
operand->op_code = op_code; operand->op_code = op_code;
operand->addr_mode = get_op_addr_mode(op_code); operand->addr_mode = get_op_addr_mode(op_code);
@ -36,12 +36,12 @@ void decode_operands(ProgramView *view) {
case ADDR_MODE_ZERO_PAGE: case ADDR_MODE_ZERO_PAGE:
case ADDR_MODE_ZERO_PAGE_INDEXED_X: case ADDR_MODE_ZERO_PAGE_INDEXED_X:
case ADDR_MODE_ZERO_PAGE_INDEXED_Y: case ADDR_MODE_ZERO_PAGE_INDEXED_Y:
operand->value = view->ram[pc]; operand->value = mem_get_byte(pc);
pc += 1; pc += 1;
break; break;
default: default:
operand->value = view->ram[pc]; operand->value = mem_get_byte(pc);
operand->value += view->ram[pc + 1] << 8; operand->value += mem_get_byte(pc + 1) << 8;
pc += 2; pc += 2;
break; break;
} }
@ -183,11 +183,10 @@ void pv_deinit(InteractWindow *window) {
linked_list_uninit(&view->operands); linked_list_uninit(&view->operands);
} }
void pv_init(InteractWindow *interact, System *system, int x, int y) { void pv_init(InteractWindow *interact, int x, int y) {
ProgramView *view = malloc(sizeof(ProgramView)); ProgramView *view = malloc(sizeof(ProgramView));
view->window = interact; view->window = interact;
view->ram = system->ram; view->pc = &cpu_get_state()->program_counter;
view->pc = &system->cpu.program_counter;
view->operands = linked_list_init(false); view->operands = linked_list_init(false);
interact->view = view; interact->view = view;

View File

@ -25,13 +25,12 @@ typedef struct debug_operand {
typedef struct program_view { typedef struct program_view {
InteractWindow *window; InteractWindow *window;
byte *ram;
address *pc; address *pc;
LinkedList operands; LinkedList operands;
LinkedListNode *first_operand_node; LinkedListNode *first_operand_node;
LinkedListNode *last_operand_node; LinkedListNode *last_operand_node;
} ProgramView; } ProgramView;
void pv_init(InteractWindow *interact, System *system, int x, int y); void pv_init(InteractWindow *interact, int x, int y);
#endif //NESEMULATOR_PROGRAM_VIEW_H #endif //NESEMULATOR_PROGRAM_VIEW_H

View File

@ -1,7 +1,7 @@
/* /*
* ===================================================================================== * =====================================================================================
* *
* Filename: cpu.h * Filename: cpu_state.h
* *
* Description: 6502 CPU emulator headers * Description: 6502 CPU emulator headers
* *
@ -22,8 +22,8 @@
#ifndef NESEMULATOR_CPU_H #ifndef NESEMULATOR_CPU_H
#define NESEMULATOR_CPU_H #define NESEMULATOR_CPU_H
void cpu_init(CPU *cpu); void cpu_init();
void cpu_cycle(System *system); void cpu_cycle();
#endif #endif

View File

@ -10,7 +10,7 @@
typedef struct mapper { typedef struct mapper {
address prg_rom_start_addr; address prg_rom_start_addr;
void (*post_prg_load)(ram, unsigned int); void (*post_prg_load)(unsigned int);
} Mapper; } Mapper;
enum MapperType { enum MapperType {

View File

@ -56,15 +56,19 @@ typedef struct ppu {
address t; address t;
byte x; byte x;
bool w; bool w;
void (*trigger_nmi)();
} PPU; } PPU;
PPU *ppu_get_state();
/** /**
* Initializes the PPU, according to the power up state. * Initializes the PPU, according to the power up state.
* https://www.nesdev.org/wiki/PPU_power_up_state * https://www.nesdev.org/wiki/PPU_power_up_state
* *
* @param ppu * @param ppu
*/ */
void ppu_init(PPU *ppu, byte *registers_ram, byte *oam_dma_register); void ppu_init(byte *registers_ram, byte *oam_dma_register);
/** /**
* Cycles the PPU. * Cycles the PPU.
@ -72,7 +76,7 @@ void ppu_init(PPU *ppu, byte *registers_ram, byte *oam_dma_register);
* @param ppu * @param ppu
* @param ram * @param ram
*/ */
void ppu_cycle(PPU *ppu); void ppu_cycle();
/** /**
* Read a flag from the PPU registers. * Read a flag from the PPU registers.
@ -80,7 +84,7 @@ void ppu_cycle(PPU *ppu);
* @param reg The register index * @param reg The register index
* @param mask The flag mask * @param mask The flag mask
*/ */
bool ppu_read_flag(PPU *ppu, size_t reg, byte mask); bool ppu_read_flag(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).
@ -89,8 +93,8 @@ bool ppu_read_flag(PPU *ppu, size_t reg, byte mask);
* @param reg The register index * @param reg The register index
* @param mask The value mask * @param mask The value mask
*/ */
void ppu_read_register(PPU *ppu, byte reg); void ppu_read_register(byte reg);
void ppu_write_register(PPU *ppu, byte reg); void ppu_write_register(byte reg);
#endif //NESEMULATOR_PPU_H #endif //NESEMULATOR_PPU_H

View File

@ -24,9 +24,8 @@ typedef struct {
* Loads a ROM from a specified file path. * Loads a ROM from a specified file path.
* *
* @param path The file path * @param path The file path
* @param rom ROM
* @return A boolean indicating a success (true) or an error. * @return A boolean indicating a success (true) or an error.
*/ */
bool rom_load(char *path, System *system); bool rom_load(char *path);
#endif //NESEMULATOR_ROM_H #endif //NESEMULATOR_ROM_H

View File

@ -19,49 +19,33 @@
#define PPU_REGISTER_OAM_DMA_ADDR 0x4014 #define PPU_REGISTER_OAM_DMA_ADDR 0x4014
#define APU_REGISTERS_COUNT 24 #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;
bool nmi_requested;
} CPU;
typedef struct system { typedef struct system {
void *rom_header; void *rom_header;
CPU cpu;
PPU ppu;
Mapper mapper; Mapper mapper;
ram ram;
byte apu_registers[APU_REGISTERS_COUNT]; byte apu_registers[APU_REGISTERS_COUNT];
unsigned long cycle_count; unsigned long cycle_count;
} System; } System;
/** /**
* Initialize all components of a system. * Initialize all components of a system.
*
* @param system The system to initialize
*/ */
void system_init(System *system); void system_init();
void system_start(System *system); void system_start();
/** /**
* Starts the main loop of a system. * Starts the main loop of a system.
*
* @param system The system
*/ */
void system_loop(System *system); void system_loop();
/** /**
* De-initialize the components of a system. * De-initialize the components of a system.
*
* @param system The system to de-initialize
*/ */
void system_uninit(System *system); void system_uninit();
unsigned int system_get_cycles();
void system_add_cycles(unsigned int cycles);
Mapper *system_get_mapper();
#endif //NESEMULATOR_SYSTEM_H #endif //NESEMULATOR_SYSTEM_H

View File

@ -12,7 +12,7 @@ typedef unsigned char byte;
typedef unsigned short address; typedef unsigned short address;
typedef unsigned short word; typedef unsigned short word;
typedef byte ram[RAM_SIZE]; //typedef byte ram[RAM_SIZE];
typedef byte vram[VRAM_SIZE]; typedef byte vram[VRAM_SIZE];
#endif //NESEMULATOR_TYPES_H #endif //NESEMULATOR_TYPES_H

16
main.c
View File

@ -23,22 +23,20 @@
#include "include/system.h" #include "include/system.h"
int main() { int main() {
System system;
log_set_level(LOG_INFO); log_set_level(LOG_INFO);
system_init(&system); system_init();
char *rom_path = "../test_roms/smb.nes"; char *rom_path = "../test_roms/smb.nes";
if (!rom_load(rom_path, &system)) { if (!rom_load(rom_path)) {
system_uninit(&system); system_uninit();
return EXIT_FAILURE; return EXIT_FAILURE;
} }
system_start(&system); system_start();
// start_debugger(&system); // start_debugger();
system_loop(&system); system_loop();
system_uninit(&system); system_uninit();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -1,19 +1,20 @@
#include "../include/mapper.h" #include "../include/mapper.h"
#include "../include/rom.h" #include "../include/rom.h"
#include "../cpu/memory.h"
#include <string.h> #include <string.h>
#define SIMPLE_MAPPER_PRG_START_ADDR 0x8000 #define SIMPLE_MAPPER_PRG_START_ADDR 0x8000
#define PRG_PART_SIZE 0x4000 // 16Kb #define PRG_PART_SIZE 0x4000 // 16Kb
void post_prg_load(ram ram, unsigned int prg_size) { void post_prg_load(unsigned int prg_size) {
if (prg_size == 2) { if (prg_size == 2) {
// The whole space is occupied, nothing to do // The whole space is occupied, nothing to do
return; return;
} }
// We need to mirror the data in the upper ram // We need to mirror the data in the upper ram
byte *source = (byte *) &ram[SIMPLE_MAPPER_PRG_START_ADDR]; byte *source = mem_get_ptr(SIMPLE_MAPPER_PRG_START_ADDR);
byte *destination = (byte *) &ram[SIMPLE_MAPPER_PRG_START_ADDR + PRG_PART_SIZE]; byte *destination = mem_get_ptr(SIMPLE_MAPPER_PRG_START_ADDR + PRG_PART_SIZE);
memcpy(destination, source, PRG_PART_SIZE); memcpy(destination, source, PRG_PART_SIZE);
} }

View File

@ -14,31 +14,56 @@
// 10. This is where I'm stuck. I think I need to read the "sprites" section of https://wiki.nesdev.com/w/index.php/PPU_rendering very carefully. // 10. This is where I'm stuck. I think I need to read the "sprites" section of https://wiki.nesdev.com/w/index.php/PPU_rendering very carefully.
// //
#include <stddef.h>
#include "../include/ppu.h" #include "../include/ppu.h"
void ppu_init(PPU *ppu, byte *registers_ram, byte *oam_dma_register) { PPU ppu_state;
ppu->registers = registers_ram;
ppu->registers[PPU_REGISTER_CTRL] = 0x00; void ppu_init(byte *registers_ram, byte *oam_dma_register) {
ppu->registers[PPU_REGISTER_MASK] = 0x00; ppu_state.registers = registers_ram;
ppu->registers[PPU_REGISTER_STATUS] = 0x00; ppu_state.registers[PPU_REGISTER_CTRL] = 0x00;
ppu->registers[PPU_REGISTER_OAM_ADDR] = 0x00; ppu_state.registers[PPU_REGISTER_MASK] = 0x00;
ppu->registers[PPU_REGISTER_OAM_DATA] = 0x00; ppu_state.registers[PPU_REGISTER_STATUS] = 0x00;
ppu->registers[PPU_REGISTER_SCROLL] = 0x00; ppu_state.registers[PPU_REGISTER_OAM_ADDR] = 0x00;
ppu->registers[PPU_REGISTER_ADDR] = 0x00; ppu_state.registers[PPU_REGISTER_OAM_DATA] = 0x00;
ppu->registers[PPU_REGISTER_DATA] = 0x00; ppu_state.registers[PPU_REGISTER_SCROLL] = 0x00;
ppu->oam_dma_register = oam_dma_register; ppu_state.registers[PPU_REGISTER_ADDR] = 0x00;
ppu->odd_frame = false; ppu_state.registers[PPU_REGISTER_DATA] = 0x00;
ppu_state.oam_dma_register = oam_dma_register;
ppu_state.odd_frame = false;
}
PPU *ppu_get_state() {
return &ppu_state;
}
void ppu_status_set(byte mask, bool enabled) {
if (enabled) {
ppu_state.registers[PPU_REGISTER_STATUS] |= mask;
} else {
ppu_state.registers[PPU_REGISTER_STATUS] &= ~mask;
}
} }
long frame = 0; long frame = 0;
int x, y = 0; int x, y = 0;
void ppu_cycle(PPU *ppu) { void ppu_cycle() {
if (x == 1) {
if (y == 241) {
// VBlank start
ppu_status_set(PPU_STATUS_VBLANK, true);
}
if (y == 261) {
// VBlank clear
ppu_status_set(PPU_STATUS_VBLANK, false);
}
}
int frame_width = 341; int frame_width = 341;
int frame_height = 262; int frame_height = 262;
bool rendering_enabled = ppu_read_flag(ppu, PPU_REGISTER_MASK, PPU_MASK_SHOW_BG | PPU_MASK_SHOW_SP); bool rendering_enabled = ppu_read_flag(PPU_REGISTER_MASK, PPU_MASK_SHOW_BG | PPU_MASK_SHOW_SP);
if (rendering_enabled && ppu->odd_frame) { if (rendering_enabled && ppu_state.odd_frame) {
// With rendering enabled, the odd frames are shorter // With rendering enabled, the odd frames are shorter
// TODO: and doing the last cycle of the last dummy nametable fetch there instead // TODO: and doing the last cycle of the last dummy nametable fetch there instead
frame_width = 339; frame_width = 339;
@ -54,30 +79,30 @@ void ppu_cycle(PPU *ppu) {
if (y >= frame_height) { if (y >= frame_height) {
y = 0; y = 0;
frame++; frame++;
ppu->odd_frame = !ppu->odd_frame; ppu_state.odd_frame = !ppu_state.odd_frame;
} }
} }
bool ppu_read_flag(PPU *ppu, size_t reg, byte mask) { bool ppu_read_flag(size_t reg, byte mask) {
return ppu->registers[reg] & mask; return ppu_state.registers[reg] & mask;
} }
//byte ppu_read_register(PPU *ppu, size_t reg, byte mask) { //byte ppu_read_register(size_t reg, byte mask) {
// return ppu->registers[reg] & mask; // return ppu_state.registers[reg] & mask;
//} //}
void ppu_read_register(PPU *ppu, byte reg) { void ppu_read_register(byte reg) {
if (reg == PPU_REGISTER_STATUS) { if (reg == PPU_REGISTER_STATUS) {
ppu->w = false; ppu_state.w = false;
} }
} }
void ppu_write_register(PPU *ppu, byte reg) { void ppu_write_register(byte reg) {
if (reg == PPU_REGISTER_SCROLL || reg == PPU_REGISTER_ADDR) { if (reg == PPU_REGISTER_SCROLL || reg == PPU_REGISTER_ADDR) {
ppu->w = !ppu->w; ppu_state.w = !ppu_state.w;
} }
if (reg == PPU_REGISTER_OAM_DATA) { if (reg == PPU_REGISTER_OAM_DATA) {
ppu->registers[PPU_REGISTER_OAM_ADDR]++; ppu_state.registers[PPU_REGISTER_OAM_ADDR]++;
} }
} }

View File

@ -7,6 +7,7 @@
#include "log.h" #include "log.h"
#include "../include/rom.h" #include "../include/rom.h"
#include "../include/system.h" #include "../include/system.h"
#include "../cpu/memory.h"
// Flag 6 // Flag 6
#define NES_HEADER_FLAG_MIRRORING 0x01 #define NES_HEADER_FLAG_MIRRORING 0x01
@ -126,21 +127,22 @@ bool rom_ines_read_trainer(FILE *file, INesHeader *header) {
return false; return false;
} }
bool rom_ines_read_prg_rom(FILE *file, INesHeader *header, System *system) { bool rom_ines_read_prg_rom(FILE *file, INesHeader *header) {
unsigned int prg_rom_size = header->prg_rom_size * 16384; unsigned int prg_rom_size = header->prg_rom_size * 16384;
log_debug("Reading %d bytes PRG ROM", prg_rom_size); log_debug("Reading %d bytes PRG ROM", prg_rom_size);
if (fread(&system->ram[system->mapper.prg_rom_start_addr], sizeof(byte), prg_rom_size, file) < prg_rom_size) { byte *prg_rom_location = mem_get_ptr(system_get_mapper()->prg_rom_start_addr);
if (fread(prg_rom_location, sizeof(byte), prg_rom_size, file) < prg_rom_size) {
log_error("Failed to read PRG ROM"); log_error("Failed to read PRG ROM");
return false; return false;
} }
system->mapper.post_prg_load(&system->ram[0], header->prg_rom_size); system_get_mapper()->post_prg_load(header->prg_rom_size);
return true; return true;
} }
bool rom_ines_read_chr_rom(FILE *file, INesHeader *header, System *system) { bool rom_ines_read_chr_rom(FILE *file, INesHeader *header) {
if (header->chr_rom_size <= 0) { if (header->chr_rom_size <= 0) {
log_debug("No CHR ROM to read"); log_debug("No CHR ROM to read");
return true; return true;
@ -149,7 +151,8 @@ bool rom_ines_read_chr_rom(FILE *file, INesHeader *header, System *system) {
unsigned int chr_rom_size = header->chr_rom_size * 8192; unsigned int chr_rom_size = header->chr_rom_size * 8192;
log_debug("Reading %d bytes CHR ROM", chr_rom_size); log_debug("Reading %d bytes CHR ROM", chr_rom_size);
if (fread(system->ppu.vram, sizeof(byte), chr_rom_size, file) < chr_rom_size) { byte *chr_rom_location = ppu_get_state()->vram;
if (fread(chr_rom_location, sizeof(byte), chr_rom_size, file) < chr_rom_size) {
log_error("Failed to read CHR ROM"); log_error("Failed to read CHR ROM");
return false; return false;
} }
@ -157,11 +160,11 @@ bool rom_ines_read_chr_rom(FILE *file, INesHeader *header, System *system) {
return true; return true;
} }
bool rom_ines_read(const char header_buf[ROM_HEADER_SIZE], FILE *file, System *system) { bool rom_ines_read(const char header_buf[ROM_HEADER_SIZE], FILE *file) {
INesHeader header = read_header(header_buf); INesHeader header = read_header(header_buf);
system->rom_header = &header; // system->rom_header = &header;
return rom_ines_read_trainer(file, &header) && return rom_ines_read_trainer(file, &header) &&
rom_ines_read_prg_rom(file, &header, system) && rom_ines_read_prg_rom(file, &header) &&
rom_ines_read_chr_rom(file, &header, system); rom_ines_read_chr_rom(file, &header);
} }

View File

@ -8,7 +8,7 @@
#include "ines.c" #include "ines.c"
#include "../include/system.h" #include "../include/system.h"
bool rom_load(char *path, System *system) { bool rom_load(char *path) {
FILE *file = fopen(path, "r"); FILE *file = fopen(path, "r");
if (!file) { if (!file) {
log_error("Failed to open ROM"); log_error("Failed to open ROM");
@ -28,7 +28,7 @@ bool rom_load(char *path, System *system) {
} }
log_info("Reading iNes 1.0 ROM at %s", path); log_info("Reading iNes 1.0 ROM at %s", path);
rom_ines_read(header_buffer, file, system); rom_ines_read(header_buffer, file);
if (fclose(file) != 0) { if (fclose(file) != 0) {
log_error("Failed to close ROM file"); log_error("Failed to close ROM file");

View File

@ -7,25 +7,27 @@
#include "memory.h" #include "memory.h"
#include <unistd.h> #include <unistd.h>
#include <assert.h> #include <assert.h>
#include "log.h" #include "cpu.h"
void system_init(System *system) { System current_sys;
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); void system_init() {
ppu_init(&system->ppu, registers_base_addr, oam_dma_register); byte *registers_base_addr = mem_get_ptr(PPU_REGISTERS_BASE_ADDR);
byte *oam_dma_register = mem_get_ptr(PPU_REGISTER_OAM_DMA_ADDR);
system->mapper = get_mapper(MAPPER_TYPE_SIMPLE); cpu_init();
system->cycle_count = 7; ppu_init(registers_base_addr, oam_dma_register);
current_sys.mapper = get_mapper(MAPPER_TYPE_SIMPLE);
current_sys.cycle_count = 7;
} }
void system_start(System *system) { void system_start() {
address pc = mem_get_word(system, 0xfffc); address pc = mem_get_word(0xfffc);
system->cpu.program_counter = pc; cpu_get_state()->program_counter = pc;
} }
void system_loop(System *system) { void system_loop() {
assert(CPU_CLOCK_DIVISOR > PPU_CLOCK_DIVISOR); assert(CPU_CLOCK_DIVISOR > PPU_CLOCK_DIVISOR);
unsigned int master_cycle_per_frame = MASTER_CLOCK / FRAME_RATE; unsigned int master_cycle_per_frame = MASTER_CLOCK / FRAME_RATE;
@ -37,14 +39,14 @@ void system_loop(System *system) {
while (true) { while (true) {
// log_info("Frame %d", frame); // log_info("Frame %d", frame);
while (system->cycle_count < cpu_cycle_per_frame * frame) { while (current_sys.cycle_count < cpu_cycle_per_frame * frame) {
if (cpu_cycle_count == system->cycle_count) { if (cpu_cycle_count == current_sys.cycle_count) {
cpu_cycle(system); cpu_cycle();
} }
cpu_cycle_count++; cpu_cycle_count++;
for (int ppu_c = 0; ppu_c < ppu_cycle_per_cpu_cycle; ppu_c++) { for (int ppu_c = 0; ppu_c < ppu_cycle_per_cpu_cycle; ppu_c++) {
ppu_cycle(&system->ppu); ppu_cycle();
} }
} }
@ -53,5 +55,17 @@ void system_loop(System *system) {
} }
} }
void system_uninit(System *system) { void system_uninit() {
}
unsigned int system_get_cycles() {
return current_sys.cycle_count;
}
void system_add_cycles(unsigned int cycles) {
current_sys.cycle_count += cycles;
}
Mapper *system_get_mapper() {
return &current_sys.mapper;
} }