Load and execute first instruction

This commit is contained in:
FyloZ 2023-12-23 16:35:23 -05:00
parent 41d70a5e1d
commit acce479cfa
Signed by: william
GPG Key ID: 835378AE9AF4AE97
12 changed files with 169 additions and 102 deletions

View File

@ -23,40 +23,19 @@
</configurations> </configurations>
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="0c3b231e-0637-4ac1-8964-c60fc9e9e691" name="Changes" comment="Gitignore"> <list default="true" id="0c3b231e-0637-4ac1-8964-c60fc9e9e691" name="Changes" comment="CPU">
<change afterPath="$PROJECT_DIR$/include/rom.h" afterDir="false" /> <change afterPath="$PROJECT_DIR$/tests/smb.nes" afterDir="false" />
<change afterPath="$PROJECT_DIR$/rom/CMakeLists.txt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/rom/ines.c" afterDir="false" />
<change afterPath="$PROJECT_DIR$/rom/rom.c" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/readme.txt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/ascii_1.chr" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/ascii_2.chr" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/ascii_3.chr" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/build_rom.s" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/colors.inc" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/console.s" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/crc.s" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/delay.s" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/devcart.bin" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/macros.inc" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/neshw.inc" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/ppu.s" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/print.s" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/shell.inc" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/shell.s" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/testing.s" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/text_out.s" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/readme.txt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/test_cpu_exec_space_apu.s" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/source/test_cpu_exec_space_ppuio.s" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/test_cpu_exec_space_apu.nes" afterDir="false" />
<change afterPath="$PROJECT_DIR$/tests/cpu_exec_space/test_cpu_exec_space_ppuio.nes" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/CMakeLists.txt" beforeDir="false" afterPath="$PROJECT_DIR$/CMakeLists.txt" afterDir="false" /> <change beforePath="$PROJECT_DIR$/cpu/cpu.c" beforeDir="false" afterPath="$PROJECT_DIR$/cpu/cpu.c" afterDir="false" />
<change beforePath="$PROJECT_DIR$/cpu/cpu.h" beforeDir="false" afterPath="$PROJECT_DIR$/cpu/cpu.h" afterDir="false" /> <change beforePath="$PROJECT_DIR$/cpu/cpu.h" beforeDir="false" afterPath="$PROJECT_DIR$/cpu/cpu.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/cpu/memory.c" beforeDir="false" afterPath="$PROJECT_DIR$/cpu/memory.c" afterDir="false" />
<change beforePath="$PROJECT_DIR$/cpu/op.c" beforeDir="false" afterPath="$PROJECT_DIR$/cpu/op.c" afterDir="false" /> <change beforePath="$PROJECT_DIR$/cpu/op.c" beforeDir="false" afterPath="$PROJECT_DIR$/cpu/op.c" afterDir="false" />
<change beforePath="$PROJECT_DIR$/cpu/ram.h" beforeDir="false" afterPath="$PROJECT_DIR$/cpu/ram.h" afterDir="false" /> <change beforePath="$PROJECT_DIR$/cpu/op.h" beforeDir="false" afterPath="$PROJECT_DIR$/cpu/op.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/include/cpu.h" beforeDir="false" afterPath="$PROJECT_DIR$/include/cpu.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/include/rom.h" beforeDir="false" afterPath="$PROJECT_DIR$/include/rom.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/main.c" beforeDir="false" afterPath="$PROJECT_DIR$/main.c" afterDir="false" /> <change beforePath="$PROJECT_DIR$/main.c" beforeDir="false" afterPath="$PROJECT_DIR$/main.c" afterDir="false" />
<change beforePath="$PROJECT_DIR$/rom/ines.c" beforeDir="false" afterPath="$PROJECT_DIR$/rom/ines.c" afterDir="false" />
<change beforePath="$PROJECT_DIR$/rom/rom.c" beforeDir="false" afterPath="$PROJECT_DIR$/rom/rom.c" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -128,11 +107,6 @@
</key> </key>
</component> </component>
<component name="RunManager" selected="CMake Application.NESEmulator"> <component name="RunManager" selected="CMake Application.NESEmulator">
<configuration default="true" type="CLionExternalRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true">
<method v="2">
<option name="CLION.EXTERNAL.BUILD" enabled="true" />
</method>
</configuration>
<configuration name="NESEmulator" type="CMakeListConfigurationType" factoryName="CMakeListConfigurationFactory" temporary="true"> <configuration name="NESEmulator" type="CMakeListConfigurationType" factoryName="CMakeListConfigurationFactory" temporary="true">
<method v="2" /> <method v="2" />
</configuration> </configuration>
@ -315,6 +289,7 @@
<workItem from="1700970472703" duration="9613000" /> <workItem from="1700970472703" duration="9613000" />
<workItem from="1701463001105" duration="2058000" /> <workItem from="1701463001105" duration="2058000" />
<workItem from="1701558929054" duration="14565000" /> <workItem from="1701558929054" duration="14565000" />
<workItem from="1703367277258" duration="1000" />
</task> </task>
<task id="LOCAL-00001" summary="Cpu opcodes implementation"> <task id="LOCAL-00001" summary="Cpu opcodes implementation">
<option name="closed" value="true" /> <option name="closed" value="true" />
@ -350,15 +325,4 @@
<MESSAGE value="Gitignore" /> <MESSAGE value="Gitignore" />
<option name="LAST_COMMIT_MESSAGE" value="Gitignore" /> <option name="LAST_COMMIT_MESSAGE" value="Gitignore" />
</component> </component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" type="com.jetbrains.cidr.execution.debugger.OCBreakpointType">
<url>file://$PROJECT_DIR$/rom/rom.c</url>
<line>32</line>
<option name="timeStamp" value="17" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
</project> </project>

View File

@ -1,6 +1,7 @@
#include "../include/cpu.h" #include "../include/cpu.h"
#include "cpu.h" #include "cpu.h"
#include "memory.h" #include "memory.h"
#include "op.h"
/* /*
* ===================================================================================== * =====================================================================================
@ -24,12 +25,27 @@ CpuRegisters registers;
Mapper mapper; Mapper mapper;
void cpu_init() { void cpu_init() {
registers.program_counter = 0x0000; registers.program_counter = 0xc000;
registers.stack_pointer = 0xff; registers.stack_pointer = 0xff;
registers.accumulator = 0x00; registers.accumulator = 0x00;
registers.x = 0x00; registers.x = 0x00;
registers.y = 0x00; registers.y = 0x00;
registers.status = 0x00; registers.status = 0x00;
mapper = get_mapper(MAPPER_TYPE_SIMPLE);
}
void cpu_step() {
int i = 0;
while (i < 10) {
byte op = cpu_get_next_byte();
process_op_code(op);
i += 1;
}
}
void cpu_add_cycles(unsigned int cycle_count) {
} }
// === Registers === // === Registers ===

View File

@ -58,4 +58,6 @@ void cpu_stack_push_context();
byte cpu_stack_pop(); byte cpu_stack_pop();
void cpu_stack_pop_context(); void cpu_stack_pop_context();
void cpu_add_cycles(unsigned int cycle_count);
#endif //CPU_CPU_H #endif //CPU_CPU_H

View File

@ -4,15 +4,30 @@
#include "memory.h" #include "memory.h"
#include "ram.h" #include "ram.h"
#include "../include/rom.h"
byte mem_get_byte(Mapper *mapper, address addr) { byte mem_get_byte(Mapper *mapper, address addr) {
address redirected_addr = mapper->redirect_addr(addr); address redirected_addr = mapper->redirect_addr(addr);
return ram_get_byte(redirected_addr);
if (redirected_addr < 0x0800) {
return ram_get_byte(redirected_addr);
} else if (redirected_addr >= 0x4020) {
return rom_prg_get_byte(redirected_addr - 0x4020);
}
return 0;
} }
word mem_get_word(Mapper *mapper, address addr) { word mem_get_word(Mapper *mapper, address addr) {
address redirected_addr = mapper->redirect_addr(addr); address redirected_addr = mapper->redirect_addr(addr);
return ram_get_word(redirected_addr);
if (redirected_addr < 0x0800) {
return ram_get_word(redirected_addr);
} else if (redirected_addr >= 0x4020) {
return rom_prg_get_word(redirected_addr - 0x4020);
}
return 0;
} }
void mem_set_byte(Mapper *mapper, address addr, byte byte) { void mem_set_byte(Mapper *mapper, address addr, byte byte) {

View File

@ -10,7 +10,7 @@
#define IS_OP_CODE_MODE(op, op_code, addr_mode) \ #define IS_OP_CODE_MODE(op, op_code, addr_mode) \
case op_code: \ case op_code: \
log_debug("OP: %s", "op"); \ log_debug("OP: %s", #op); \
op_ ## op(ADDR_MODE_ ## addr_mode); \ op_ ## op(ADDR_MODE_ ## addr_mode); \
break; break;
@ -809,7 +809,7 @@ void op_XAA(AddressingMode addr_mode) {
assert(false); assert(false);
} }
void process_op_code(int op) { void process_op_code(byte op) {
switch (op) { switch (op) {
// CTRL // CTRL
IS_OP_CODE(BRK, 0x00) IS_OP_CODE(BRK, 0x00)

View File

@ -1,3 +1,5 @@
#include "../include/cpu.h"
#ifndef CPU_OP_H #ifndef CPU_OP_H
#define CPU_OP_H #define CPU_OP_H
@ -31,4 +33,6 @@ typedef enum {
ADDR_MODE_ZERO_PAGE_INDEXED_Y, // d,y ADDR_MODE_ZERO_PAGE_INDEXED_Y, // d,y
} AddressingMode; } AddressingMode;
#endif void process_op_code(byte op);
#endif

View File

@ -23,11 +23,7 @@ typedef unsigned char byte;
typedef unsigned short address; typedef unsigned short address;
typedef unsigned short word; typedef unsigned short word;
/** void cpu_init();
* @brief Set clock void cpu_step();
*/
void cpu_step_to(int cycle);
void cpu_add_cycles(int count);
#endif #endif

View File

@ -5,12 +5,25 @@
#ifndef NESEMULATOR_ROM_H #ifndef NESEMULATOR_ROM_H
#define NESEMULATOR_ROM_H #define NESEMULATOR_ROM_H
// The size of the header in a ROM file, in bytes
#include "cpu.h"
#define ROM_HEADER_SIZE 16
// The size of the trainer in a ROM file, in bytes
#define ROM_TRAINER_SIZE 512
typedef struct { typedef struct {
char* prg_rom; byte *prg_rom;
char* chr_rom; byte *chr_rom;
void* header; void *header;
} Rom; } Rom;
int read_rom(char* path); int rom_load(char *path);
void rom_uninit();
byte rom_prg_get_byte(address addr);
word rom_prg_get_word(address addr);
#endif //NESEMULATOR_ROM_H #endif //NESEMULATOR_ROM_H

10
main.c
View File

@ -20,8 +20,14 @@
#include "include/rom.h" #include "include/rom.h"
int main() { int main() {
char *rom_path = "../tests/cpu_exec_space/test_cpu_exec_space_ppuio.nes"; char *rom_path = "../tests/smb.nes";
read_rom(rom_path);
cpu_init();
rom_load(rom_path);
cpu_step();
rom_uninit();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -110,31 +110,60 @@ INesHeader read_header(const char header_buf[16]) {
return header; return header;
} }
bool rom_nes_read(const char header_buf[16], FILE *file, Rom *rom) { bool rom_ines_read_trainer(FILE *file, INesHeader *header) {
INesHeader header = read_header(header_buf); if (!header->flags.has_trainer) {
rom->header = &header; log_debug("ROM does not contains trainer");
return true;
// We don't support the trainer, so we skip ahead by 512 bytes if needed.
if (header.flags.has_trainer && !fseek(file, 512, SEEK_CUR)) {
perror("Failed to seek ahead of trainer ROM section");
return false;
} }
unsigned int prg_rom_size = header.prg_rom_size * 16384; // We don't support the trainer, so we skip ahead instead.
if (fseek(file, ROM_TRAINER_SIZE, SEEK_CUR)) {
log_debug("ROM has trainer, skipping %d bytes", ROM_TRAINER_SIZE);
return true;
}
log_error("Failed to skip trainer");
return false;
}
bool rom_ines_read_prg_rom(FILE *file, INesHeader *header, Rom *rom) {
unsigned int prg_rom_size = header->prg_rom_size * 16384;
rom->prg_rom = (char *) malloc(prg_rom_size * sizeof(char)); rom->prg_rom = (char *) malloc(prg_rom_size * sizeof(char));
if (fread(rom->prg_rom, sizeof(char), prg_rom_size, file) < prg_rom_size) {
perror("Failed to read PRG ROM");
return false;
}
if (header.chr_rom_size > 0) { log_debug("Reading %d bytes PRG ROM", prg_rom_size);
unsigned int chr_rom_size = header.chr_rom_size * 8192;
rom->chr_rom = (char *) malloc(chr_rom_size * sizeof(char)); if (fread(rom->prg_rom, sizeof(char), prg_rom_size, file) < prg_rom_size) {
if (fread(rom->chr_rom, sizeof(char), chr_rom_size, file) < chr_rom_size) { log_error("Failed to read PRG ROM");
perror("Failed to read CHR ROM"); return false;
return false;
}
} }
return true; return true;
}
bool rom_ines_read_chr_rom(FILE *file, INesHeader *header, Rom *rom) {
if (header->chr_rom_size <= 0) {
log_debug("No CHR ROM to read");
return true;
}
unsigned int chr_rom_size = header->chr_rom_size * 8192;
rom->chr_rom = (char *) malloc(chr_rom_size * sizeof(char));
log_debug("Reading %d bytes CHR ROM", chr_rom_size);
if (fread(rom->chr_rom, sizeof(char), chr_rom_size, file) < chr_rom_size) {
log_error("Failed to read CHR ROM");
return false;
}
return true;
}
bool rom_ines_read(const char header_buf[ROM_HEADER_SIZE], FILE *file, Rom *rom) {
INesHeader header = read_header(header_buf);
rom->header = &header;
return rom_ines_read_trainer(file, &header) &&
rom_ines_read_prg_rom(file, &header, rom) &&
rom_ines_read_chr_rom(file, &header, rom);
} }

View File

@ -4,51 +4,73 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <assert.h>
#include "../include/rom.h" #include "../include/rom.h"
#include "ines.c" #include "ines.c"
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
void rom_init(Rom *rom) { Rom rom;
rom->header = NULL;
rom->prg_rom = NULL; void rom_init() {
rom->chr_rom = NULL; rom.header = NULL;
rom.prg_rom = NULL;
rom.chr_rom = NULL;
} }
void rom_uninit(Rom *rom) { int rom_load(char *path) {
free(rom->prg_rom); rom_init();
free(rom->chr_rom);
}
int read_rom(char *path) {
FILE *file = fopen(path, "r"); FILE *file = fopen(path, "r");
if (!file) { if (!file) {
perror("Failed to open ROM"); log_error("Failed to open ROM");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
char header_buffer[16] = {0}; char header_buffer[ROM_HEADER_SIZE] = {0};
size_t read_size = fread(header_buffer, sizeof(char), ARRAY_SIZE(header_buffer), file); size_t read_size = fread(header_buffer, sizeof(char), ARRAY_SIZE(header_buffer), file);
if (read_size < ARRAY_SIZE(header_buffer)) { if (read_size < ARRAY_SIZE(header_buffer)) {
perror("Failed to read ROM"); log_error("Failed to read ROM");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (!rom_is_ines(header_buffer)) { if (!rom_is_ines(header_buffer)) {
perror("Only iNes ROMs are supported"); log_error("Only iNes ROMs are supported");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
Rom rom; log_info("Reading iNes 1.0 ROM at %s", path);
rom_init(&rom); rom_ines_read(header_buffer, file, &rom);
rom_nes_read(header_buffer, file, &rom);
rom_uninit(&rom);
if (fclose(file) != 0) { if (fclose(file) != 0) {
perror("Failed to close ROM file"); log_error("Failed to close ROM file");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
return 0; return 0;
}
void rom_uninit() {
assert(rom.prg_rom != NULL);
assert(rom.chr_rom != NULL);
free(rom.prg_rom);
free(rom.chr_rom);
log_info("Cleared ROM data");
}
byte rom_prg_get_byte(address addr) {
assert(rom.prg_rom != NULL);
return rom.prg_rom[addr];
}
word rom_prg_get_word(address addr) {
assert(rom.prg_rom != NULL);
word word = rom.prg_rom[addr];
word += rom.prg_rom[addr + 1] << 8; // Little endian
return word;
} }

BIN
tests/smb.nes Normal file

Binary file not shown.