Load and execute first instruction
This commit is contained in:
parent
41d70a5e1d
commit
acce479cfa
|
@ -23,40 +23,19 @@
|
|||
</configurations>
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="0c3b231e-0637-4ac1-8964-c60fc9e9e691" name="Changes" comment="Gitignore">
|
||||
<change afterPath="$PROJECT_DIR$/include/rom.h" 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" />
|
||||
<list default="true" id="0c3b231e-0637-4ac1-8964-c60fc9e9e691" name="Changes" comment="CPU">
|
||||
<change afterPath="$PROJECT_DIR$/tests/smb.nes" 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/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/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$/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>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
|
@ -128,11 +107,6 @@
|
|||
</key>
|
||||
</component>
|
||||
<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">
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
|
@ -315,6 +289,7 @@
|
|||
<workItem from="1700970472703" duration="9613000" />
|
||||
<workItem from="1701463001105" duration="2058000" />
|
||||
<workItem from="1701558929054" duration="14565000" />
|
||||
<workItem from="1703367277258" duration="1000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="Cpu opcodes implementation">
|
||||
<option name="closed" value="true" />
|
||||
|
@ -350,15 +325,4 @@
|
|||
<MESSAGE value="Gitignore" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="Gitignore" />
|
||||
</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>
|
18
cpu/cpu.c
18
cpu/cpu.c
|
@ -1,6 +1,7 @@
|
|||
#include "../include/cpu.h"
|
||||
#include "cpu.h"
|
||||
#include "memory.h"
|
||||
#include "op.h"
|
||||
|
||||
/*
|
||||
* =====================================================================================
|
||||
|
@ -24,12 +25,27 @@ CpuRegisters registers;
|
|||
Mapper mapper;
|
||||
|
||||
void cpu_init() {
|
||||
registers.program_counter = 0x0000;
|
||||
registers.program_counter = 0xc000;
|
||||
registers.stack_pointer = 0xff;
|
||||
registers.accumulator = 0x00;
|
||||
registers.x = 0x00;
|
||||
registers.y = 0x00;
|
||||
registers.status = 0x00;
|
||||
|
||||
mapper = get_mapper(MAPPER_TYPE_SIMPLE);
|
||||
}
|
||||
|
||||
void cpu_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 ===
|
||||
|
|
|
@ -58,4 +58,6 @@ void cpu_stack_push_context();
|
|||
byte cpu_stack_pop();
|
||||
void cpu_stack_pop_context();
|
||||
|
||||
void cpu_add_cycles(unsigned int cycle_count);
|
||||
|
||||
#endif //CPU_CPU_H
|
||||
|
|
15
cpu/memory.c
15
cpu/memory.c
|
@ -4,15 +4,30 @@
|
|||
|
||||
#include "memory.h"
|
||||
#include "ram.h"
|
||||
#include "../include/rom.h"
|
||||
|
||||
byte mem_get_byte(Mapper *mapper, address addr) {
|
||||
address redirected_addr = mapper->redirect_addr(addr);
|
||||
|
||||
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) {
|
||||
address redirected_addr = mapper->redirect_addr(addr);
|
||||
|
||||
if (redirected_addr < 0x0800) {
|
||||
return ram_get_word(redirected_addr);
|
||||
} else if (redirected_addr >= 0x4020) {
|
||||
return rom_prg_get_word(redirected_addr - 0x4020);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mem_set_byte(Mapper *mapper, address addr, byte byte) {
|
||||
|
|
4
cpu/op.c
4
cpu/op.c
|
@ -10,7 +10,7 @@
|
|||
|
||||
#define IS_OP_CODE_MODE(op, op_code, addr_mode) \
|
||||
case op_code: \
|
||||
log_debug("OP: %s", "op"); \
|
||||
log_debug("OP: %s", #op); \
|
||||
op_ ## op(ADDR_MODE_ ## addr_mode); \
|
||||
break;
|
||||
|
||||
|
@ -809,7 +809,7 @@ void op_XAA(AddressingMode addr_mode) {
|
|||
assert(false);
|
||||
}
|
||||
|
||||
void process_op_code(int op) {
|
||||
void process_op_code(byte op) {
|
||||
switch (op) {
|
||||
// CTRL
|
||||
IS_OP_CODE(BRK, 0x00)
|
||||
|
|
4
cpu/op.h
4
cpu/op.h
|
@ -1,3 +1,5 @@
|
|||
#include "../include/cpu.h"
|
||||
|
||||
#ifndef CPU_OP_H
|
||||
#define CPU_OP_H
|
||||
|
||||
|
@ -31,4 +33,6 @@ typedef enum {
|
|||
ADDR_MODE_ZERO_PAGE_INDEXED_Y, // d,y
|
||||
} AddressingMode;
|
||||
|
||||
void process_op_code(byte op);
|
||||
|
||||
#endif
|
|
@ -23,11 +23,7 @@ typedef unsigned char byte;
|
|||
typedef unsigned short address;
|
||||
typedef unsigned short word;
|
||||
|
||||
/**
|
||||
* @brief Set clock
|
||||
*/
|
||||
void cpu_step_to(int cycle);
|
||||
|
||||
void cpu_add_cycles(int count);
|
||||
void cpu_init();
|
||||
void cpu_step();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -5,12 +5,25 @@
|
|||
#ifndef NESEMULATOR_ROM_H
|
||||
#define NESEMULATOR_ROM_H
|
||||
|
||||
// The size of the header in a ROM file, in bytes
|
||||
#include "cpu.h"
|
||||
|
||||
#define ROM_HEADER_SIZE 16
|
||||
// The size of the trainer in a ROM file, in bytes
|
||||
#define ROM_TRAINER_SIZE 512
|
||||
|
||||
typedef struct {
|
||||
char* prg_rom;
|
||||
char* chr_rom;
|
||||
byte *prg_rom;
|
||||
byte *chr_rom;
|
||||
void *header;
|
||||
} 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
|
10
main.c
10
main.c
|
@ -20,8 +20,14 @@
|
|||
#include "include/rom.h"
|
||||
|
||||
int main() {
|
||||
char *rom_path = "../tests/cpu_exec_space/test_cpu_exec_space_ppuio.nes";
|
||||
read_rom(rom_path);
|
||||
char *rom_path = "../tests/smb.nes";
|
||||
|
||||
cpu_init();
|
||||
rom_load(rom_path);
|
||||
|
||||
cpu_step();
|
||||
|
||||
rom_uninit();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
63
rom/ines.c
63
rom/ines.c
|
@ -110,31 +110,60 @@ INesHeader read_header(const char header_buf[16]) {
|
|||
return header;
|
||||
}
|
||||
|
||||
bool rom_nes_read(const char header_buf[16], FILE *file, Rom *rom) {
|
||||
INesHeader header = read_header(header_buf);
|
||||
rom->header = &header;
|
||||
bool rom_ines_read_trainer(FILE *file, INesHeader *header) {
|
||||
if (!header->flags.has_trainer) {
|
||||
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");
|
||||
// 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;
|
||||
}
|
||||
|
||||
unsigned int prg_rom_size = header.prg_rom_size * 16384;
|
||||
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));
|
||||
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) {
|
||||
unsigned int chr_rom_size = header.chr_rom_size * 8192;
|
||||
rom->chr_rom = (char *) malloc(chr_rom_size * sizeof(char));
|
||||
if (fread(rom->chr_rom, sizeof(char), chr_rom_size, file) < chr_rom_size) {
|
||||
perror("Failed to read CHR ROM");
|
||||
log_debug("Reading %d bytes PRG ROM", prg_rom_size);
|
||||
|
||||
if (fread(rom->prg_rom, sizeof(char), prg_rom_size, file) < prg_rom_size) {
|
||||
log_error("Failed to read PRG ROM");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
58
rom/rom.c
58
rom/rom.c
|
@ -4,51 +4,73 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "../include/rom.h"
|
||||
#include "ines.c"
|
||||
|
||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||
|
||||
void rom_init(Rom *rom) {
|
||||
rom->header = NULL;
|
||||
rom->prg_rom = NULL;
|
||||
rom->chr_rom = NULL;
|
||||
Rom rom;
|
||||
|
||||
void rom_init() {
|
||||
rom.header = NULL;
|
||||
rom.prg_rom = NULL;
|
||||
rom.chr_rom = NULL;
|
||||
}
|
||||
|
||||
void rom_uninit(Rom *rom) {
|
||||
free(rom->prg_rom);
|
||||
free(rom->chr_rom);
|
||||
}
|
||||
int rom_load(char *path) {
|
||||
rom_init();
|
||||
|
||||
int read_rom(char *path) {
|
||||
FILE *file = fopen(path, "r");
|
||||
if (!file) {
|
||||
perror("Failed to open ROM");
|
||||
log_error("Failed to open ROM");
|
||||
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);
|
||||
if (read_size < ARRAY_SIZE(header_buffer)) {
|
||||
perror("Failed to read ROM");
|
||||
log_error("Failed to read ROM");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!rom_is_ines(header_buffer)) {
|
||||
perror("Only iNes ROMs are supported");
|
||||
log_error("Only iNes ROMs are supported");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Rom rom;
|
||||
rom_init(&rom);
|
||||
rom_nes_read(header_buffer, file, &rom);
|
||||
rom_uninit(&rom);
|
||||
log_info("Reading iNes 1.0 ROM at %s", path);
|
||||
rom_ines_read(header_buffer, file, &rom);
|
||||
|
||||
if (fclose(file) != 0) {
|
||||
perror("Failed to close ROM file");
|
||||
log_error("Failed to close ROM file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
Binary file not shown.
Loading…
Reference in New Issue