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
# 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
*
@ -26,133 +26,147 @@
* =====================================================================================
*/
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;
cpu->nmi_requested = false;
CPU cpu_state;
void cpu_init() {
cpu_state.program_counter = 0x8000;
cpu_state.stack_pointer = 0xfd;
cpu_state.accumulator = 0x00;
cpu_state.x = 0x00;
cpu_state.y = 0x00;
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) {
log_debug("%#02x %#02x %s \t A:%#02x X:%#02x Y:%#02x F:%#02x SP:%#02x \t [%d]",
cpu.program_counter,
void print_registers(byte op, unsigned long cycle_count) {
log_info("%#02x %#02x %s \t A:%#02x X:%#02x Y:%#02x F:%#02x SP:%#02x \t [%d]",
cpu_state.program_counter,
op,
get_op_code_name(op),
cpu.accumulator,
cpu.x,
cpu.y,
cpu.status,
cpu.stack_pointer,
cpu_state.accumulator,
cpu_state.x,
cpu_state.y,
cpu_state.status,
cpu_state.stack_pointer,
cycle_count);
}
void cpu_process_nmi(System *system) {
cpu_stack_push_context(system);
void cpu_process_nmi() {
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);
system->cpu.nmi_requested = false;
system->cpu.program_counter = handler_addr;
cpu_state.nmi_requested = false;
cpu_state.program_counter = handler_addr;
}
void oam_dma_upload(System *system) {
byte page_high_addr = *system->ppu.oam_dma_register;
void oam_dma_upload() {
byte page_high_addr = *ppu_get_state()->oam_dma_register; // TODO
address page_addr = ((address) page_high_addr) << 8;
byte n = 0xff;
byte *ram_source = &system->ram[page_addr];
byte *oam_destination = system->ppu.oam;
byte *ram_source = mem_get_ptr(page_addr);
byte *oam_destination = ppu_get_state()->oam;
memcpy(oam_destination, ram_source, n);
log_debug("OAM DMA %#04x", page_addr);
cpu_add_cycles(system, 513); // TODO
cpu_add_cycles(513); // TODO
}
void cpu_cycle(System *system) {
if (system->cpu.nmi_requested) {
cpu_process_nmi(system);
void cpu_cycle() {
if (cpu_state.nmi_requested) {
cpu_process_nmi();
}
if (system->cpu.oam_dma_triggered) {
oam_dma_upload(system);
system->cpu.oam_dma_triggered = false;
if (cpu_state.oam_dma_triggered) {
oam_dma_upload();
cpu_state.oam_dma_triggered = false;
return;
}
CPU cpu = system->cpu;
byte op = cpu_get_next_byte(system);
CPU cpu = cpu_state;
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) {
system->cycle_count += cycle_count;
void cpu_add_cycles(unsigned int cycle_count) {
system_add_cycles(cycle_count);
}
// === Registers ===
bool system_get_flag(System *system, byte mask) {
return cpu_get_flag(&system->cpu, mask);
bool cpu_get_flag(byte 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) {
system->cpu.status |= mask;
cpu_state.status |= mask;
} else {
system->cpu.status &= ~mask;
cpu_state.status &= ~mask;
}
}
byte cpu_get_next_byte(System *system) {
byte next_byte = mem_get_byte(system, system->cpu.program_counter);
system->cpu.program_counter++;
byte cpu_get_next_byte() {
byte next_byte = mem_get_byte(cpu_state.program_counter);
cpu_state.program_counter++;
return next_byte;
}
word cpu_get_next_word(System *system) {
word next_word = mem_get_word(system, system->cpu.program_counter);
system->cpu.program_counter += 2;
word cpu_get_next_word() {
word next_word = mem_get_word(cpu_state.program_counter);
cpu_state.program_counter += 2;
return next_word;
}
void cpu_stack_push(System *system, byte value) {
assert(system->cpu.stack_pointer > 0);
void cpu_stack_push(byte value) {
assert(cpu_state.stack_pointer > 0);
address mem_addr = CPU_STACK_ADDR | system->cpu.stack_pointer;
mem_set_byte(system, mem_addr, value);
system->cpu.stack_pointer--;
address mem_addr = CPU_STACK_ADDR | cpu_state.stack_pointer;
mem_set_byte(mem_addr, value);
cpu_state.stack_pointer--;
}
byte cpu_stack_pop(System *system) {
assert(system->cpu.stack_pointer < 0xff);
byte cpu_stack_pop() {
assert(cpu_state.stack_pointer < 0xff);
system->cpu.stack_pointer++;
address mem_addr = CPU_STACK_ADDR | system->cpu.stack_pointer;
byte value = mem_get_byte(system, mem_addr);
cpu_state.stack_pointer++;
address mem_addr = CPU_STACK_ADDR | cpu_state.stack_pointer;
byte value = mem_get_byte(mem_addr);
return value;
}
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_push_context() {
cpu_stack_push(cpu_state.program_counter >> 8);
cpu_stack_push(cpu_state.program_counter & 0xff);
cpu_stack_push(cpu_state.status);
}
void cpu_stack_pop_context(System *system) {
byte value = cpu_stack_pop(system);
void cpu_stack_pop_context() {
byte value = cpu_stack_pop();
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_state.status = value;
byte lo = cpu_stack_pop(system);
address pc = cpu_stack_pop(system) << 8;
byte lo = cpu_stack_pop();
address pc = cpu_stack_pop() << 8;
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
// 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.
*
@ -26,11 +40,7 @@
* @param mask The flag mask
* @return The value of the flag.
*/
bool system_get_flag(System *system, byte mask);
static inline bool cpu_get_flag(CPU *cpu, byte mask) {
return cpu->status & mask;
}
bool cpu_get_flag(byte mask);
/**
* 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 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.
@ -48,7 +58,7 @@ void system_set_flag(System *system, byte mask, bool set);
* @param system The system
* @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.
@ -57,7 +67,7 @@ byte cpu_get_next_byte(System *system);
* @param system The system
* @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.
@ -65,7 +75,7 @@ word cpu_get_next_word(System *system);
* @param system The system
* @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.
@ -73,7 +83,7 @@ void cpu_stack_push(System *system, byte value);
*
* @param system The system
*/
void cpu_stack_push_context(System *system);
void cpu_stack_push_context();
/**
* Pops a byte from the stack.
@ -81,7 +91,7 @@ void cpu_stack_push_context(System *system);
* @param system The system
* @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.
@ -89,13 +99,16 @@ byte cpu_stack_pop(System *system);
*
* @param system The system
*/
void cpu_stack_pop_context(System *system);
void cpu_stack_pop_context();
/**
* 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);
void cpu_add_cycles(unsigned int cycle_count);
void cpu_trigger_oam_dma();
void cpu_trigger_nmi();
#endif //CPU_CPU_H

View File

@ -7,52 +7,52 @@
#include <assert.h>
#include "log.h"
address decode_operand_addr(System *system, AddressingMode addr_mode, bool *page_crossing) {
CPU registers = system->cpu;
address decode_operand_addr(AddressingMode addr_mode, bool *page_crossing) {
CPU *registers = cpu_get_state();
address operand_addr;
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) {
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) {
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) {
operand_addr = cpu_get_next_word(system);
operand_addr = cpu_get_next_word();
} else if (addr_mode == ADDR_MODE_ABSOLUTE_INDEXED_X) {
word addr = cpu_get_next_word(system);
word new_addr = addr + registers.x;
word addr = cpu_get_next_word();
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(system);
word new_addr = addr + registers.y;
word addr = cpu_get_next_word();
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(system);
word addr = cpu_get_next_word();
if ((addr & 0xff) == 0xff) {
// Error in NES CPU for JMP op
word result = mem_get_byte(system, addr);
result += mem_get_byte(system, addr & 0xff00) << 8;
word result = mem_get_byte(addr);
result += mem_get_byte(addr & 0xff00) << 8;
operand_addr = result;
} else {
operand_addr = mem_get_word(system, addr);
operand_addr = mem_get_word(addr);
}
} 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);
addr += mem_get_byte(system, (arg_addr + system->cpu.x + 1) & 0xff) << 8;
word addr = mem_get_byte((arg_addr + registers->x) & 0xff);
addr += mem_get_byte((arg_addr + registers->x + 1) & 0xff) << 8;
operand_addr = addr;
} else if (addr_mode == ADDR_MODE_INDIRECT_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;
byte arg_addr = cpu_get_next_byte();
word addr = mem_get_byte(arg_addr) + (mem_get_byte((arg_addr + 1) & 0xff) << 8);
word new_addr = addr + registers->y;
*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;
}
Operand decode_operand(System *system, AddressingMode addr_mode) {
Operand decode_operand(AddressingMode addr_mode) {
Operand operand;
if (addr_mode == ADDR_MODE_ACCUMULATOR) {
@ -74,25 +74,25 @@ Operand decode_operand(System *system, 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(system);
operand.value = cpu_get_next_byte();
operand.is_page_crossing = false;
} else {
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);
return operand;
}
byte read_operand(System *system, Operand operand) {
byte read_operand(Operand operand) {
switch (operand.type) {
case OPERAND_TYPE_ACCUMULATOR:
return system->cpu.accumulator;
return cpu_get_state()->accumulator;
case OPERAND_TYPE_IMMEDIATE:
return (byte) operand.value;
case OPERAND_TYPE_ADDRESS:
return mem_get_byte(system, operand.value);
return mem_get_byte(operand.value);
default:
assert(false);
}

View File

@ -37,11 +37,11 @@ typedef struct {
bool is_page_crossing;
} 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);

View File

@ -6,6 +6,7 @@
#include "log.h"
#include "memory.h"
#include "../include/rom.h"
#include "cpu.h"
#define RAM_MAX_ADDR 0x2000
#define RAM_BANK_SIZE 0x800
@ -14,36 +15,34 @@
#define APU_MAX_ADDR 0x4020
#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);
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];
ppu_read_register(reg);
}
if (addr >= PPU_MAX_ADDR && addr < APU_MAX_ADDR) {
byte apu_addr = addr - PPU_MAX_ADDR;
return system->apu_registers[apu_addr];
return ram[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);
if (addr >= RAM_MAX_ADDR && addr < APU_MAX_ADDR) {
assert(false);
}
word word = system->ram[addr];
word += system->ram[addr + 1] << 8; // Little endian
word word = ram[addr];
word += ram[addr + 1] << 8; // Little endian
return word;
}
void mem_set_byte(System *system, address addr, byte byte) {
void mem_set_byte(address addr, byte byte) {
assert(addr < MAX_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
for (int i = 0; i < 4; 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) {
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;
address ram_addr = reg_addr + PPU_BANK_SIZE * i + RAM_MAX_ADDR;
ram[ram_addr] = byte;
}
ppu_write_register(&system->ppu, reg_addr);
ppu_write_register(reg_addr);
} else {
system->ram[addr] = byte;
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;
cpu_trigger_oam_dma();
}
}
}

View File

@ -11,28 +11,34 @@
/**
* 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);
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.
*
* @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);
word mem_get_word(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);
void mem_set_byte(address addr, byte value);
#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
};
void process_op_code(System *system, byte op);
void process_op_code(byte op);
AddressingMode get_op_addr_mode(byte op_code);

View File

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

View File

@ -14,13 +14,12 @@
typedef struct cpu_view {
Window *window;
CPU *cpu;
} CpuView;
/**
* 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);

View File

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

View File

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

View File

@ -3,6 +3,7 @@
#include "memory_view.h"
#include "dialog.h"
#include "keys.h"
#include "../cpu/memory.h"
//
// 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));
view->window = interact;
view->ram = ram;
view->base_address = 0x0000;
interact->view = view;
@ -61,7 +61,7 @@ void mv_init(InteractWindow *interact, ram ram, int x, int y) {
void mv_print(MemoryView *view) {
for (int line = 0; line <= MEMORY_VIEW_LINE_COUNT; line++) {
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);
}

View File

@ -18,7 +18,6 @@
typedef struct memory_view {
InteractWindow *window;
byte *ram;
address base_address;
} MemoryView;
@ -27,9 +26,8 @@ typedef struct memory_view {
* 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.
* @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.

View File

@ -6,7 +6,7 @@
#include "ppu_view.h"
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);
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:");
}
PpuView *ppv_init(PPU *ppu, int x, int y) {
PpuView *ppv_init(int x, int y) {
PpuView *view = malloc(sizeof(PpuView));
view->window = malloc(sizeof(Window));
view->ppu = ppu;
window_init(view->window, x, y, PPU_VIEW_WIDTH, PPU_VIEW_HEIGHT, "PPU VIEW");
ppv_print(view);

View File

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

View File

@ -19,7 +19,7 @@ void decode_operands(ProgramView *view) {
exit(EXIT_FAILURE);
}
byte op_code = view->ram[pc];
byte op_code = mem_get_byte(pc);
operand->addr = pc;
operand->op_code = 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_INDEXED_X:
case ADDR_MODE_ZERO_PAGE_INDEXED_Y:
operand->value = view->ram[pc];
operand->value = mem_get_byte(pc);
pc += 1;
break;
default:
operand->value = view->ram[pc];
operand->value += view->ram[pc + 1] << 8;
operand->value = mem_get_byte(pc);
operand->value += mem_get_byte(pc + 1) << 8;
pc += 2;
break;
}
@ -183,11 +183,10 @@ void pv_deinit(InteractWindow *window) {
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));
view->window = interact;
view->ram = system->ram;
view->pc = &system->cpu.program_counter;
view->pc = &cpu_get_state()->program_counter;
view->operands = linked_list_init(false);
interact->view = view;

View File

@ -25,13 +25,12 @@ typedef struct debug_operand {
typedef struct program_view {
InteractWindow *window;
byte *ram;
address *pc;
LinkedList operands;
LinkedListNode *first_operand_node;
LinkedListNode *last_operand_node;
} 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

View File

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

View File

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

View File

@ -56,15 +56,19 @@ typedef struct ppu {
address t;
byte x;
bool w;
void (*trigger_nmi)();
} PPU;
PPU *ppu_get_state();
/**
* 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);
void ppu_init(byte *registers_ram, byte *oam_dma_register);
/**
* Cycles the PPU.
@ -72,7 +76,7 @@ void ppu_init(PPU *ppu, byte *registers_ram, byte *oam_dma_register);
* @param ppu
* @param ram
*/
void ppu_cycle(PPU *ppu);
void ppu_cycle();
/**
* Read a flag from the PPU registers.
@ -80,7 +84,7 @@ void ppu_cycle(PPU *ppu);
* @param reg The register index
* @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).
@ -89,8 +93,8 @@ bool ppu_read_flag(PPU *ppu, size_t reg, byte mask);
* @param reg The register index
* @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

View File

@ -24,9 +24,8 @@ typedef struct {
* 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);
bool rom_load(char *path);
#endif //NESEMULATOR_ROM_H

View File

@ -19,49 +19,33 @@
#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;
bool nmi_requested;
} 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_init();
void system_start(System *system);
void system_start();
/**
* 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.
*
* @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

View File

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

16
main.c
View File

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

View File

@ -1,19 +1,20 @@
#include "../include/mapper.h"
#include "../include/rom.h"
#include "../cpu/memory.h"
#include <string.h>
#define SIMPLE_MAPPER_PRG_START_ADDR 0x8000
#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) {
// 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];
byte *source = mem_get_ptr(SIMPLE_MAPPER_PRG_START_ADDR);
byte *destination = mem_get_ptr(SIMPLE_MAPPER_PRG_START_ADDR + 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.
//
#include <stddef.h>
#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;
PPU ppu_state;
void ppu_init(byte *registers_ram, byte *oam_dma_register) {
ppu_state.registers = registers_ram;
ppu_state.registers[PPU_REGISTER_CTRL] = 0x00;
ppu_state.registers[PPU_REGISTER_MASK] = 0x00;
ppu_state.registers[PPU_REGISTER_STATUS] = 0x00;
ppu_state.registers[PPU_REGISTER_OAM_ADDR] = 0x00;
ppu_state.registers[PPU_REGISTER_OAM_DATA] = 0x00;
ppu_state.registers[PPU_REGISTER_SCROLL] = 0x00;
ppu_state.registers[PPU_REGISTER_ADDR] = 0x00;
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;
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_height = 262;
bool rendering_enabled = ppu_read_flag(ppu, PPU_REGISTER_MASK, PPU_MASK_SHOW_BG | PPU_MASK_SHOW_SP);
if (rendering_enabled && ppu->odd_frame) {
bool rendering_enabled = ppu_read_flag(PPU_REGISTER_MASK, PPU_MASK_SHOW_BG | PPU_MASK_SHOW_SP);
if (rendering_enabled && ppu_state.odd_frame) {
// With rendering enabled, the odd frames are shorter
// TODO: and doing the last cycle of the last dummy nametable fetch there instead
frame_width = 339;
@ -54,30 +79,30 @@ void ppu_cycle(PPU *ppu) {
if (y >= frame_height) {
y = 0;
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) {
return ppu->registers[reg] & mask;
bool ppu_read_flag(size_t reg, byte mask) {
return ppu_state.registers[reg] & mask;
}
//byte ppu_read_register(PPU *ppu, size_t reg, byte mask) {
// return ppu->registers[reg] & mask;
//byte ppu_read_register(size_t reg, byte 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) {
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) {
ppu->w = !ppu->w;
ppu_state.w = !ppu_state.w;
}
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 "../include/rom.h"
#include "../include/system.h"
#include "../cpu/memory.h"
// Flag 6
#define NES_HEADER_FLAG_MIRRORING 0x01
@ -126,21 +127,22 @@ bool rom_ines_read_trainer(FILE *file, INesHeader *header) {
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;
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");
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;
}
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) {
log_debug("No CHR ROM to read");
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;
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");
return false;
}
@ -157,11 +160,11 @@ bool rom_ines_read_chr_rom(FILE *file, INesHeader *header, System *system) {
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);
system->rom_header = &header;
// system->rom_header = &header;
return rom_ines_read_trainer(file, &header) &&
rom_ines_read_prg_rom(file, &header, system) &&
rom_ines_read_chr_rom(file, &header, system);
rom_ines_read_prg_rom(file, &header) &&
rom_ines_read_chr_rom(file, &header);
}

View File

@ -8,7 +8,7 @@
#include "ines.c"
#include "../include/system.h"
bool rom_load(char *path, System *system) {
bool rom_load(char *path) {
FILE *file = fopen(path, "r");
if (!file) {
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);
rom_ines_read(header_buffer, file, system);
rom_ines_read(header_buffer, file);
if (fclose(file) != 0) {
log_error("Failed to close ROM file");

View File

@ -7,25 +7,27 @@
#include "memory.h"
#include <unistd.h>
#include <assert.h>
#include "log.h"
#include "cpu.h"
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];
System current_sys;
cpu_init(&system->cpu);
ppu_init(&system->ppu, registers_base_addr, oam_dma_register);
void system_init() {
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);
system->cycle_count = 7;
cpu_init();
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) {
address pc = mem_get_word(system, 0xfffc);
system->cpu.program_counter = pc;
void system_start() {
address pc = mem_get_word(0xfffc);
cpu_get_state()->program_counter = pc;
}
void system_loop(System *system) {
void system_loop() {
assert(CPU_CLOCK_DIVISOR > PPU_CLOCK_DIVISOR);
unsigned int master_cycle_per_frame = MASTER_CLOCK / FRAME_RATE;
@ -37,14 +39,14 @@ void system_loop(System *system) {
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);
while (current_sys.cycle_count < cpu_cycle_per_frame * frame) {
if (cpu_cycle_count == current_sys.cycle_count) {
cpu_cycle();
}
cpu_cycle_count++;
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;
}