Move states to global variables
This commit is contained in:
parent
7caf88171f
commit
22401f30ac
|
@ -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
156
cpu/cpu.c
|
@ -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;
|
||||
}
|
39
cpu/cpu.h
39
cpu/cpu.h
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
45
cpu/memory.c
45
cpu/memory.c
|
@ -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 system->ram[addr];
|
||||
return ram[addr];
|
||||
}
|
||||
|
||||
word mem_get_word(System *system, address addr) {
|
||||
byte *mem_get_ptr(address addr) {
|
||||
assert(addr <= MAX_ADDR);
|
||||
|
||||
return &ram[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();
|
||||
}
|
||||
}
|
||||
}
|
18
cpu/memory.h
18
cpu/memory.h
|
@ -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
|
||||
|
|
2
cpu/op.h
2
cpu/op.h
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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(¤t_window->cursor);
|
||||
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
|
||||
#include "../include/system.h"
|
||||
|
||||
void start_debugger(System *system);
|
||||
void start_debugger();
|
||||
|
||||
#endif //NESEMULATOR_DEBUGGER_H
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -47,8 +47,8 @@
|
|||
#define PPU_MASK_NONE 0xff
|
||||
|
||||
typedef struct ppu {
|
||||
byte* registers;
|
||||
byte* oam_dma_register;
|
||||
byte *registers;
|
||||
byte *oam_dma_register;
|
||||
byte vram[PPU_VRAM_SIZE];
|
||||
byte oam[PPU_OAM_SIZE];
|
||||
bool odd_frame;
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
16
main.c
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
77
ppu/ppu.c
77
ppu/ppu.c
|
@ -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]++;
|
||||
}
|
||||
}
|
21
rom/ines.c
21
rom/ines.c
|
@ -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);
|
||||
}
|
|
@ -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");
|
||||
|
|
48
system.c
48
system.c
|
@ -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 ¤t_sys.mapper;
|
||||
}
|
Loading…
Reference in New Issue