Compare commits

...

2 Commits

Author SHA1 Message Date
william cebee66076 Things 2024-01-06 14:27:09 -05:00
william 9c7b882f46 Added logging for operand decoding 2023-12-23 17:10:31 -05:00
52 changed files with 1855 additions and 644 deletions

View File

@ -14,6 +14,7 @@
<config projectName="NESEmulator" targetName="NESEmulator" />
<config projectName="NESEmulator" targetName="ROM" />
<config projectName="NESEmulator" targetName="Mappers" />
<config projectName="NESEmulator" targetName="PPU" />
<config projectName="NESEmulator" targetName="CPU" />
</generated>
</component>
@ -23,19 +24,59 @@
</configurations>
</component>
<component name="ChangeListManager">
<list default="true" id="0c3b231e-0637-4ac1-8964-c60fc9e9e691" name="Changes" comment="CPU">
<change afterPath="$PROJECT_DIR$/tests/smb.nes" afterDir="false" />
<list default="true" id="0c3b231e-0637-4ac1-8964-c60fc9e9e691" name="Changes" comment="Added logging for operand decoding">
<change afterPath="$PROJECT_DIR$/include/ppu.h" afterDir="false" />
<change afterPath="$PROJECT_DIR$/include/system.h" afterDir="false" />
<change afterPath="$PROJECT_DIR$/include/types.h" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ppu/CMakeLists.txt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ppu/ppu.c" afterDir="false" />
<change afterPath="$PROJECT_DIR$/system.c" afterDir="false" />
<change afterPath="$PROJECT_DIR$/test_roms/nestest.fdb" afterDir="false" />
<change afterPath="$PROJECT_DIR$/test_roms/nestest.nes" afterDir="false" />
<change afterPath="$PROJECT_DIR$/test_roms/smb.fdb" 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$/conandata.yml" beforeDir="false" afterPath="$PROJECT_DIR$/conandata.yml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/cpu/CMakeLists.txt" beforeDir="false" afterPath="$PROJECT_DIR$/cpu/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/memory.h" beforeDir="false" afterPath="$PROJECT_DIR$/cpu/memory.h" 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.h" beforeDir="false" afterPath="$PROJECT_DIR$/cpu/op.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/cpu/ram.c" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/cpu/ram.h" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/include/cpu.h" beforeDir="false" afterPath="$PROJECT_DIR$/include/cpu.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/include/mapper.h" beforeDir="false" afterPath="$PROJECT_DIR$/include/mapper.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$/mappers/simple_mapper.c" beforeDir="false" afterPath="$PROJECT_DIR$/mappers/simple_mapper.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" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/readme.txt" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/readme.txt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/ascii_1.chr" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/common/ascii_1.chr" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/ascii_2.chr" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/common/ascii_2.chr" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/ascii_3.chr" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/common/ascii_3.chr" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/build_rom.s" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/common/build_rom.s" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/colors.inc" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/common/colors.inc" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/console.s" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/common/console.s" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/crc.s" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/common/crc.s" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/delay.s" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/common/delay.s" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/devcart.bin" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/common/devcart.bin" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/macros.inc" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/common/macros.inc" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/neshw.inc" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/common/neshw.inc" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/ppu.s" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/common/ppu.s" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/print.s" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/common/print.s" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/shell.inc" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/common/shell.inc" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/shell.s" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/common/shell.s" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/testing.s" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/common/testing.s" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/common/text_out.s" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/common/text_out.s" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/readme.txt" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/readme.txt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/test_cpu_exec_space_apu.s" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/test_cpu_exec_space_apu.s" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/source/test_cpu_exec_space_ppuio.s" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/source/test_cpu_exec_space_ppuio.s" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/test_cpu_exec_space_apu.nes" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/test_cpu_exec_space_apu.nes" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/cpu_exec_space/test_cpu_exec_space_ppuio.nes" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/cpu_exec_space/test_cpu_exec_space_ppuio.nes" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/smb.nes" beforeDir="false" afterPath="$PROJECT_DIR$/test_roms/smb.nes" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -56,6 +97,9 @@
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="ProblemsViewState">
<option name="selectedTabId" value="CurrentFile" />
</component>
<component name="ProjectApplicationVersion">
<option name="ide" value="CLion" />
<option name="majorVersion" value="2023" />
@ -99,17 +143,148 @@
}</component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/rom" />
<recent name="$PROJECT_DIR$/include" />
<recent name="$PROJECT_DIR$/rom" />
<recent name="$PROJECT_DIR$/cpu" />
<recent name="$PROJECT_DIR$/include/cpu" />
<recent name="$PROJECT_DIR$" />
</key>
</component>
<component name="RunManager" selected="CMake Application.NESEmulator">
<configuration default="true" type="PythonConfigurationType" factoryName="Python">
<module name="nesemu" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration default="true" type="Tox" factoryName="Tox">
<module name="nesemu" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<method v="2" />
</configuration>
<configuration default="true" type="tests" factoryName="Autodetect">
<module name="nesemu" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="_new_additionalArguments" value="&quot;&quot;" />
<option name="_new_target" value="&quot;&quot;" />
<option name="_new_targetType" value="&quot;PATH&quot;" />
<method v="2" />
</configuration>
<configuration default="true" type="tests" factoryName="Doctests">
<module name="nesemu" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="" />
<option name="CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="FOLDER_NAME" value="" />
<option name="TEST_TYPE" value="TEST_SCRIPT" />
<option name="PATTERN" value="" />
<option name="USE_PATTERN" value="false" />
<method v="2" />
</configuration>
<configuration default="true" type="tests" factoryName="Nosetests">
<module name="nesemu" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="_new_regexPattern" value="&quot;&quot;" />
<option name="_new_additionalArguments" value="&quot;&quot;" />
<option name="_new_target" value="&quot;&quot;" />
<option name="_new_targetType" value="&quot;PATH&quot;" />
<method v="2" />
</configuration>
<configuration default="true" type="tests" factoryName="Twisted Trial">
<module name="nesemu" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="_new_additionalArguments" value="&quot;&quot;" />
<option name="_new_target" value="&quot;&quot;" />
<option name="_new_targetType" value="&quot;PATH&quot;" />
<method v="2" />
</configuration>
<configuration default="true" type="tests" factoryName="Unittests">
<module name="nesemu" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="_new_additionalArguments" value="&quot;&quot;" />
<option name="_new_target" value="&quot;&quot;" />
<option name="_new_targetType" value="&quot;PATH&quot;" />
<method v="2" />
</configuration>
<configuration default="true" type="tests" factoryName="py.test">
<module name="nesemu" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="_new_keywords" value="&quot;&quot;" />
<option name="_new_parameters" value="&quot;&quot;" />
<option name="_new_additionalArguments" value="&quot;&quot;" />
<option name="_new_target" value="&quot;&quot;" />
<option name="_new_targetType" value="&quot;PATH&quot;" />
<method v="2" />
</configuration>
<configuration name="NESEmulator" type="CMakeListConfigurationType" factoryName="CMakeListConfigurationFactory" temporary="true">
<method v="2" />
</configuration>
<configuration name="NESEmulator" type="CMakeListConfigurationType" factoryName="CMakeListConfigurationFactory" temporary="true">
<method v="2" />
</configuration>
<configuration name="CPU" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="NESEmulator" TARGET_NAME="CPU" CONFIG_NAME="Debug">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
</configuration>
<configuration name="CPU" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="NESEmulator" TARGET_NAME="CPU" CONFIG_NAME="Debug">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
@ -120,11 +295,31 @@
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
</configuration>
<configuration name="Mappers" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="NESEmulator" TARGET_NAME="Mappers" CONFIG_NAME="Debug">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
</configuration>
<configuration name="NESEmulator" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="NESEmulator" TARGET_NAME="NESEmulator" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="NESEmulator" RUN_TARGET_NAME="NESEmulator">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
</configuration>
<configuration name="NESEmulator" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="NESEmulator" TARGET_NAME="NESEmulator" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="NESEmulator" RUN_TARGET_NAME="NESEmulator">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
</configuration>
<configuration name="PPU" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="NESEmulator" TARGET_NAME="PPU" CONFIG_NAME="Debug">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
</configuration>
<configuration name="ROM" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="NESEmulator" TARGET_NAME="ROM" CONFIG_NAME="Debug">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
</configuration>
<configuration name="ROM" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="NESEmulator" TARGET_NAME="ROM" CONFIG_NAME="Debug">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
@ -257,6 +452,7 @@
<item itemvalue="CMake Application.NESEmulator" />
<item itemvalue="CMake Application.CPU" />
<item itemvalue="CMake Application.Mappers" />
<item itemvalue="CMake Application.PPU" />
<item itemvalue="CMake Application.ROM" />
<item itemvalue="CMake Debug.NESEmulator" />
</list>
@ -290,6 +486,12 @@
<workItem from="1701463001105" duration="2058000" />
<workItem from="1701558929054" duration="14565000" />
<workItem from="1703367277258" duration="1000" />
<workItem from="1703810207562" duration="27667000" />
<workItem from="1703912983973" duration="23543000" />
<workItem from="1704429138262" duration="2629000" />
<workItem from="1704484992884" duration="2000" />
<workItem from="1704501418104" duration="8204000" />
<workItem from="1704569084127" duration="3000" />
</task>
<task id="LOCAL-00001" summary="Cpu opcodes implementation">
<option name="closed" value="true" />
@ -307,7 +509,15 @@
<option name="project" value="LOCAL" />
<updated>1701463073548</updated>
</task>
<option name="localTasksCounter" value="3" />
<task id="LOCAL-00003" summary="Added logging for operand decoding">
<option name="closed" value="true" />
<created>1703369431911</created>
<option name="number" value="00003" />
<option name="presentableId" value="LOCAL-00003" />
<option name="project" value="LOCAL" />
<updated>1703369431911</updated>
</task>
<option name="localTasksCounter" value="4" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@ -323,6 +533,18 @@
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
<MESSAGE value="Cpu opcodes implementation" />
<MESSAGE value="Gitignore" />
<option name="LAST_COMMIT_MESSAGE" value="Gitignore" />
<MESSAGE value="Added logging for operand decoding" />
<option name="LAST_COMMIT_MESSAGE" value="Added logging for operand decoding" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" type="com.jetbrains.cidr.execution.debugger.OCBreakpointType">
<url>file://$PROJECT_DIR$/system.c</url>
<line>51</line>
<option name="timeStamp" value="8" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
</project>

View File

@ -2,19 +2,24 @@ cmake_minimum_required(VERSION 3.10)
project(NESEmulator VERSION 0.1)
add_subdirectory(cpu)
add_subdirectory(ppu)
add_subdirectory(mappers)
add_subdirectory(rom)
list(APPEND EXTRA_INCLUDES
"${PROJECT_SOURCE_DIR}/cpu"
"${PROJECT_SOURCE_DIR}/ppu"
"${PROJECT_SOURCE_DIR}/mappers"
"${PROJECT_SOURCE_DIR}/rom")
add_executable(NESEmulator main.c)
add_executable(NESEmulator main.c
system.c
include/system.h
include/types.h)
find_package(log.c)
target_link_libraries(NESEmulator CPU Mappers ROM log.c::log.c)
target_link_libraries(NESEmulator CPU PPU Mappers ROM log.c::log.c)
target_include_directories(NESEmulator PUBLIC
"${PROJECT_BINARY_DIR}"
${EXTRA_INCLUDES})

View File

@ -2,4 +2,5 @@
# To keep your changes, remove these comment lines, but the plugin won't be able to modify your requirements
requirements:
- "libcheck/0.15.2"
- "log.c/cci.20200620"

View File

@ -1,7 +1,6 @@
add_library(CPU
cpu.c
op.c
ram.c
memory.c
cpu.h)

162
cpu/cpu.c
View File

@ -1,3 +1,6 @@
#include <log.h>
#include <assert.h>
#include <string.h>
#include "../include/cpu.h"
#include "cpu.h"
#include "memory.h"
@ -21,100 +24,129 @@
* =====================================================================================
*/
CpuRegisters registers;
Mapper mapper;
void cpu_init() {
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_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;
}
void cpu_step() {
int i = 0;
while (i < 10) {
byte op = cpu_get_next_byte();
process_op_code(op);
i += 1;
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,
op,
get_op_code_name(op),
cpu.accumulator,
cpu.x,
cpu.y,
cpu.status,
cpu.stack_pointer,
cycle_count);
}
void oam_dma_upload(System *system) {
byte page_high_addr = *system->ppu.oam_dma_register;
address page_addr = ((address) page_high_addr) << 8;
byte n = 0xff;
byte *ram_source = &system->ram[page_addr];
byte *oam_destination = system->ppu.oam;
memcpy(oam_destination, ram_source, n);
log_debug("OAM DMA %#04x", page_addr);
cpu_add_cycles(system, 513); // TODO
}
void cpu_cycle(System *system) {
if (system->cpu.oam_dma_triggered) {
oam_dma_upload(system);
system->cpu.oam_dma_triggered = false;
return;
}
CPU registers = system->cpu;
byte op = cpu_get_next_byte(system);
print_registers(registers, op, system->cycle_count);
process_op_code(system, op);
}
void cpu_add_cycles(unsigned int cycle_count) {
void cpu_add_cycles(System *system, unsigned int cycle_count) {
system->cycle_count += cycle_count;
}
// === Registers ===
CpuRegisters* cpu_get_registers() {
return &registers;
bool cpu_get_flag(System *system, byte mask) {
return system->cpu.status & mask;
}
byte cpu_get_flag(byte mask) {
return registers.status & mask;
}
void cpu_set_flag(bool set, byte mask) {
void cpu_set_flag(System *system, byte mask, bool set) {
if (set) {
registers.status |= mask;
system->cpu.status |= mask;
} else {
registers.status &= ~mask;
system->cpu.status &= ~mask;
}
}
// === Memory ===
byte cpu_get_next_byte() {
byte next_byte = mem_get_byte(&mapper, registers.program_counter);
registers.program_counter++;
byte cpu_get_next_byte(System *system) {
byte next_byte = mem_get_byte(system, system->cpu.program_counter);
system->cpu.program_counter++;
return next_byte;
}
word cpu_get_next_word() {
word next_word = mem_get_word(&mapper, registers.program_counter);
registers.program_counter += 2;
word cpu_get_next_word(System *system) {
word next_word = mem_get_word(system, system->cpu.program_counter);
system->cpu.program_counter += 2;
return next_word;
}
byte cpu_peek_byte(address addr) {
return mem_get_byte(&mapper, addr);
void cpu_stack_push(System *system, byte value) {
assert(system->cpu.stack_pointer > 0);
address mem_addr = CPU_STACK_ADDR | system->cpu.stack_pointer;
mem_set_byte(system, mem_addr, value);
system->cpu.stack_pointer--;
}
word cpu_peek_word(address addr) {
return mem_get_word(&mapper, addr);
}
byte cpu_stack_pop(System *system) {
assert(system->cpu.stack_pointer < 0xff);
void cpu_push_byte(byte value, address addr) {
mem_set_byte(&mapper, addr, value);
}
// === Stack ===
void cpu_stack_push(byte value) {
address mem_addr = CPU_STACK_ADDR | registers.stack_pointer;
cpu_push_byte(value, mem_addr);
registers.stack_pointer--;
}
byte cpu_stack_pop() {
address mem_addr = CPU_STACK_ADDR | registers.stack_pointer;
byte value = cpu_peek_byte(mem_addr);
registers.stack_pointer++;
system->cpu.stack_pointer++;
address mem_addr = CPU_STACK_ADDR | system->cpu.stack_pointer;
byte value = mem_get_byte(system, mem_addr);
return value;
}
void cpu_stack_push_context() {
cpu_stack_push(registers.program_counter >> 8);
cpu_stack_push(registers.program_counter & 0xff);
cpu_stack_push(registers.status);
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_pop_context() {
registers.status = cpu_stack_pop();
void cpu_stack_pop_context(System *system) {
byte value = cpu_stack_pop(system);
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;
byte lo = cpu_stack_pop();
address pc = cpu_stack_pop() << 8;
byte lo = cpu_stack_pop(system);
address pc = cpu_stack_pop(system) << 8;
pc += lo;
registers.program_counter = pc;
system->cpu.program_counter = pc;
}
char *operand_name(Operand *operand) {
switch (operand->type) {
case OPERAND_TYPE_ACCUMULATOR:
return "Accumulator";
case OPERAND_TYPE_IMMEDIATE:
return "Immediate";
case OPERAND_TYPE_ADDRESS:
return "Address";
}
}

102
cpu/cpu.h
View File

@ -14,22 +14,11 @@
#define CPU_STATUS_INTERRUPT_DISABLE_MASK 0x04
#define CPU_STATUS_DECIMAL_MASK 0x08
#define CPU_STATUS_B_MASK 0x10
#define CPU_STATUS_I_MASK 0x20
#define CPU_STATUS_OVERFLOW_MASK 0x40
#define CPU_STATUS_NEGATIVE_MASK 0x80
#define CPU_STACK_ADDR 0x0100
// Reference: https://www.nesdev.org/obelisk-6502-guide/registers.html
typedef struct {
address program_counter;
byte stack_pointer;
byte accumulator;
byte x;
byte y;
byte status;
} CpuRegisters;
enum OperandType {
OPERAND_TYPE_ACCUMULATOR,
OPERAND_TYPE_IMMEDIATE,
@ -42,22 +31,87 @@ typedef struct {
bool is_page_crossing;
} Operand;
CpuRegisters* cpu_get_registers();
byte cpu_get_flag(byte mask);
void cpu_set_flag(bool set, byte mask);
/**
* Gets the name of the type of an operand, for logging.
*
* @param operand The operand
* @return The name of the operand's type.
*/
char *operand_name(Operand *operand);
byte cpu_get_next_byte();
word cpu_get_next_word();
/**
* Gets a flag from the CPU registers.
*
* @param system The system
* @param mask The flag mask
* @return The value of the flag.
*/
bool cpu_get_flag(System *system, byte mask);
byte cpu_peek_byte(address addr);
word cpu_peek_word(address addr);
void cpu_push_byte(byte value, address addr);
/**
* Sets a flag in the CPU registers.
*
* @param system The system
* @param mask The flag mask
* @param set If the flag is set or not
*/
void cpu_set_flag(System *system, byte mask, bool set);
void cpu_stack_push(byte value);
void cpu_stack_push_context();
byte cpu_stack_pop();
void cpu_stack_pop_context();
/**
* Gets the next byte in the program.
* Increases the system program counter.
*
* @param system The system
* @return The value of the next byte.
*/
byte cpu_get_next_byte(System *system);
void cpu_add_cycles(unsigned int cycle_count);
/**
* Gets the next word in the program.
* Increases the system program counter by 2.
*
* @param system The system
* @return The value of the next word.
*/
word cpu_get_next_word(System *system);
/**
* Pushes a byte in to the stack.
*
* @param system The system
* @param value The value to push to the stack
*/
void cpu_stack_push(System *system, byte value);
/**
* Pushes the execution context to the stack.
* This includes the program counter and the CPU status.
*
* @param system The system
*/
void cpu_stack_push_context(System *system);
/**
* Pops a byte from the stack.
*
* @param system The system
* @return The value of the byte
*/
byte cpu_stack_pop(System *system);
/**
* Pops an execution context from the stack and overwrite the current context.
* This includes the program counter and the CPU status.
*
* @param system The system
*/
void cpu_stack_pop_context(System *system);
/**
* 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);
#endif //CPU_CPU_H

View File

@ -2,35 +2,76 @@
// Created by william on 10/15/23.
//
#include <assert.h>
#include <log.h>
#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);
#define RAM_MAX_ADDR 0x2000
#define RAM_BANK_SIZE 0x800
#define PPU_MAX_ADDR 0x4000
#define PPU_BANK_SIZE 0x8
#define APU_MAX_ADDR 0x4020
#define MAX_ADDR 0xffff
if (redirected_addr < 0x0800) {
return ram_get_byte(redirected_addr);
} else if (redirected_addr >= 0x4020) {
return rom_prg_get_byte(redirected_addr - 0x4020);
byte mem_get_byte(System *system, 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];
}
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);
if (addr >= PPU_MAX_ADDR && addr < APU_MAX_ADDR) {
byte apu_addr = addr - PPU_MAX_ADDR;
return system->apu_registers[apu_addr];
}
return 0;
return system->ram[addr];
}
void mem_set_byte(Mapper *mapper, address addr, byte byte) {
address redirected_addr = mapper->redirect_addr(addr);
ram_set_byte(redirected_addr, byte);
word mem_get_word(System *system, 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
return word;
}
void mem_set_byte(System *system, address addr, byte byte) {
assert(addr < MAX_ADDR);
log_trace("Writing '%02x' to address 0x%04x", byte, addr);
if (addr < RAM_MAX_ADDR) {
address init_ram_addr = addr % RAM_BANK_SIZE;
// 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;
}
} 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;
}
ppu_write_register(&system->ppu, reg_addr);
} else {
system->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;
}
}
}

View File

@ -3,12 +3,36 @@
//
#include "../include/mapper.h"
#include "../include/system.h"
#ifndef NESEMULATOR_MEMORY_H
#define NESEMULATOR_MEMORY_H
byte mem_get_byte(Mapper *mapper, address addr);
word mem_get_word(Mapper *mapper, address addr);
void mem_set_byte(Mapper *mapper, address addr, byte byte);
/**
* 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);
/**
* 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);
/**
* 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);
#endif //NESEMULATOR_MEMORY_H

1380
cpu/op.c

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,7 @@ typedef enum {
ADDR_MODE_ZERO_PAGE_INDEXED_Y, // d,y
} AddressingMode;
void process_op_code(byte op);
void process_op_code(System *system, byte op);
char* get_op_code_name(byte op);
#endif

View File

@ -1,22 +0,0 @@
//
// Created by william on 30/09/23.
//
#include "ram.h"
#include "../include/cpu.h"
byte ram[MEM_RAM_AMOUNT];
void ram_set_byte(address addr, byte byte) {
ram[addr] = byte;
}
byte ram_get_byte(address addr) {
return ram[addr];
}
word ram_get_word(address addr) {
word word = ram_get_byte(addr);
word += ram_get_byte(addr + 1) << 8; // Little endian
return word;
}

View File

@ -1,19 +0,0 @@
//
// Created by william on 30/09/23.
//
#include "cpu.h"
#ifndef NESEMULATOR_RAM_H
#define NESEMULATOR_RAM_H
// The 6502 CPU has 2 KiB of RAM
#define MEM_RAM_AMOUNT 2048
typedef unsigned short address;
void ram_set_byte(address addr, byte byte);
byte ram_get_byte(address addr);
word ram_get_word(address addr);
#endif //NESEMULATOR_RAM_H

View File

@ -16,14 +16,14 @@
* =====================================================================================
*/
#include "types.h"
#include "system.h"
#ifndef NESEMULATOR_CPU_H
#define NESEMULATOR_CPU_H
typedef unsigned char byte;
typedef unsigned short address;
typedef unsigned short word;
void cpu_init(CPU *cpu);
void cpu_init();
void cpu_step();
void cpu_cycle(System *system);
#endif

View File

@ -2,13 +2,15 @@
// Created by william on 10/15/23.
//
#include "types.h"
#ifndef NESEMULATOR_MAPPER_H
#define NESEMULATOR_MAPPER_H
#include "../include/cpu.h"
typedef struct mapper {
address prg_rom_start_addr;
typedef struct {
address (*redirect_addr)(unsigned short);
void (*post_prg_load)(ram, unsigned int);
} Mapper;
enum MapperType {

96
include/ppu.h Normal file
View File

@ -0,0 +1,96 @@
//
// Created by william on 12/30/23.
//
#include <stdbool.h>
#include <stddef.h>
#include "types.h"
#ifndef NESEMULATOR_PPU_H
#define NESEMULATOR_PPU_H
#define PPU_REGISTER_SIZE 0x8
#define PPU_VRAM_SIZE 0x4000
#define PPU_OAM_SIZE 0xff
#define PPU_REGISTER_CTRL 0x00
#define PPU_REGISTER_MASK 0x01
#define PPU_REGISTER_STATUS 0x02
#define PPU_REGISTER_OAM_ADDR 0x03
#define PPU_REGISTER_OAM_DATA 0x04
#define PPU_REGISTER_SCROLL 0x05
#define PPU_REGISTER_ADDR 0x06
#define PPU_REGISTER_DATA 0x07
#define PPU_CTRL_BASE_NAMETABLE_ADDR 0x3
#define PPU_CTRL_VRAM_ADDR_INCREMENT 0x4
#define PPU_CTRL_SP_PATTERN_TABLE_ADDR 0x8
#define PPU_CTRL_BG_PATTERN_TABLE_ADDR 0x10
#define PPU_CTRL_SP_SIZE 0x20
#define PPU_CTRL_MODE_SELECT 0x40
#define PPU_CTRL_GEN_VBLANK_NMI 0x80
#define PPU_MASK_GREYSCALE 0x1
#define PPU_MASK_SHOW_BG_LEFT 0x2
#define PPU_MASK_SHOW_SP_LEFT 0x4
#define PPU_MASK_SHOW_BG 0x8
#define PPU_MASK_SHOW_SP 0x10
#define PPU_MASK_EMP_RED 0x20
#define PPU_MASK_EMP_GREEN 0x40
#define PPU_MASK_EMP_BLUE 0x80
#define PPU_STATUS_OPEN_BUS 0x1f
#define PPU_STATUS_SP_OVERFLOW 0x20
#define PPU_STATUS_SP_0_HIT 0x40
#define PPU_STATUS_VBLANK 0x80
#define PPU_MASK_NONE 0xff
typedef struct ppu {
byte* registers;
byte* oam_dma_register;
byte vram[PPU_VRAM_SIZE];
byte oam[PPU_OAM_SIZE];
bool odd_frame;
address v;
address t;
byte x;
bool w;
} PPU;
/**
* 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);
/**
* Cycles the PPU.
*
* @param ppu
* @param ram
*/
void ppu_cycle(PPU *ppu);
/**
* Read a flag from the PPU registers.
*
* @param reg The register index
* @param mask The flag mask
*/
bool ppu_read_flag(PPU *ppu, size_t reg, byte mask);
/**
* Read a value from the PPU registers. Does not apply any offset to the value, a mask of 0x20 will either result in 0x20 (true) or 0x0 (false).
* Read a value from the PPU registers. Does not apply any offset to the value, a mask of 0x20 will either result in 0x20 (true) or 0x0 (false).
*
* @param reg The register index
* @param mask The value mask
*/
void ppu_read_register(PPU *ppu, byte reg);
void ppu_write_register(PPU *ppu, byte reg);
#endif //NESEMULATOR_PPU_H

View File

@ -2,12 +2,14 @@
// Created by william on 12/2/23.
//
#include <stdbool.h>
#include "types.h"
#include "system.h"
#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
@ -18,12 +20,13 @@ typedef struct {
void *header;
} Rom;
int rom_load(char *path);
void rom_uninit();
byte rom_prg_get_byte(address addr);
word rom_prg_get_word(address addr);
/**
* 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);
#endif //NESEMULATOR_ROM_H

66
include/system.h Normal file
View File

@ -0,0 +1,66 @@
//
// Created by william on 12/23/23.
//
#include "types.h"
#include "mapper.h"
#include "ppu.h"
#ifndef NESEMULATOR_SYSTEM_H
#define NESEMULATOR_SYSTEM_H
// NTSC NES Master Clock (~21.47 MHz)
#define MASTER_CLOCK 21477272
#define CPU_CLOCK_DIVISOR 12
#define PPU_CLOCK_DIVISOR 4
#define FRAME_RATE 60
#define PPU_REGISTERS_BASE_ADDR 0x2000
#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;
} 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_start(System *system);
/**
* Starts the main loop of a system.
*
* @param system The system
*/
void system_loop(System *system);
/**
* De-initialize the components of a system.
*
* @param system The system to de-initialize
*/
void system_uninit(System *system);
#endif //NESEMULATOR_SYSTEM_H

18
include/types.h Normal file
View File

@ -0,0 +1,18 @@
//
// Created by william on 12/26/23.
//
#ifndef NESEMULATOR_TYPES_H
#define NESEMULATOR_TYPES_H
#define RAM_SIZE 0xffff
#define VRAM_SIZE 0x4000
typedef unsigned char byte;
typedef unsigned short address;
typedef unsigned short word;
typedef byte ram[RAM_SIZE];
typedef byte vram[VRAM_SIZE];
#endif //NESEMULATOR_TYPES_H

22
main.c
View File

@ -16,19 +16,27 @@
* =====================================================================================
*/
#include <stdlib.h>
#include <log.h>
#include "include/rom.h"
#include "include/system.h"
int main() {
char *rom_path = "../tests/smb.nes";
log_set_level(LOG_INFO);
cpu_init();
rom_load(rom_path);
char *rom_path = "../test_roms/nestest.nes";
System system;
cpu_step();
system_init(&system);
rom_uninit();
if (!rom_load(rom_path, &system)) {
system_uninit(&system);
return EXIT_FAILURE;
}
system_start(&system);
system_loop(&system);
system_uninit(&system);
return EXIT_SUCCESS;
}
}

View File

@ -1,11 +1,25 @@
#include "../include/mapper.h"
#include "../include/rom.h"
#include <string.h>
address redirect_addr(address addr) {
return addr;
#define SIMPLE_MAPPER_PRG_START_ADDR 0x8000
#define PRG_PART_SIZE 0x4000 // 16Kb
void post_prg_load(ram ram, 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];
memcpy(destination, source, PRG_PART_SIZE);
}
Mapper get_simple_mapper() {
Mapper mapper;
mapper.redirect_addr = &redirect_addr;
mapper.prg_rom_start_addr = SIMPLE_MAPPER_PRG_START_ADDR;
mapper.post_prg_load = &post_prg_load;
return mapper;
}

5
ppu/CMakeLists.txt Normal file
View File

@ -0,0 +1,5 @@
add_library(PPU
ppu.c)
find_package(log.c)
target_link_libraries(PPU log.c::log.c)

47
ppu/ppu.c Normal file
View File

@ -0,0 +1,47 @@
//
// Created by william on 12/30/23.
//
#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;
}
void ppu_cycle(PPU *ppu) {
}
bool ppu_read_flag(PPU *ppu, size_t reg, byte mask) {
return ppu->registers[reg] & mask;
}
//byte ppu_read_register(PPU *ppu, size_t reg, byte mask) {
// return ppu->registers[reg] & mask;
//}
void ppu_read_register(PPU *ppu, byte reg) {
if (reg == PPU_REGISTER_STATUS) {
ppu->w = false;
}
}
void ppu_write_register(PPU *ppu, byte reg) {
if (reg == PPU_REGISTER_SCROLL || reg == PPU_REGISTER_ADDR) {
ppu->w = !ppu->w;
}
if (reg == PPU_REGISTER_OAM_DATA) {
ppu->registers[PPU_REGISTER_OAM_ADDR]++;
}
}

View File

@ -4,9 +4,9 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <log.h>
#include "../include/rom.h"
#include "../include/system.h"
// Flag 6
#define NES_HEADER_FLAG_MIRRORING 0x01
@ -126,32 +126,30 @@ bool rom_ines_read_trainer(FILE *file, INesHeader *header) {
return false;
}
bool rom_ines_read_prg_rom(FILE *file, INesHeader *header, Rom *rom) {
bool rom_ines_read_prg_rom(FILE *file, INesHeader *header, System *system) {
unsigned int prg_rom_size = header->prg_rom_size * 16384;
rom->prg_rom = (char *) malloc(prg_rom_size * sizeof(char));
log_debug("Reading %d bytes PRG ROM", prg_rom_size);
if (fread(rom->prg_rom, sizeof(char), prg_rom_size, file) < prg_rom_size) {
if (fread(&system->ram[system->mapper.prg_rom_start_addr], 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);
return true;
}
bool rom_ines_read_chr_rom(FILE *file, INesHeader *header, Rom *rom) {
bool rom_ines_read_chr_rom(FILE *file, INesHeader *header, System *system) {
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) {
if (fread(system->ppu.vram, sizeof(byte), chr_rom_size, file) < chr_rom_size) {
log_error("Failed to read CHR ROM");
return false;
}
@ -159,11 +157,11 @@ bool rom_ines_read_chr_rom(FILE *file, INesHeader *header, Rom *rom) {
return true;
}
bool rom_ines_read(const char header_buf[ROM_HEADER_SIZE], FILE *file, Rom *rom) {
bool rom_ines_read(const char header_buf[ROM_HEADER_SIZE], FILE *file, System *system) {
INesHeader header = read_header(header_buf);
rom->header = &header;
system->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);
rom_ines_read_prg_rom(file, &header, system) &&
rom_ines_read_chr_rom(file, &header, system);
}

View File

@ -3,74 +3,37 @@
//
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "../include/rom.h"
#include "ines.c"
#include "../include/system.h"
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
Rom rom;
void rom_init() {
rom.header = NULL;
rom.prg_rom = NULL;
rom.chr_rom = NULL;
}
int rom_load(char *path) {
rom_init();
bool rom_load(char *path, System *system) {
FILE *file = fopen(path, "r");
if (!file) {
log_error("Failed to open ROM");
return EXIT_FAILURE;
return false;
}
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)) {
size_t read_size = fread(header_buffer, sizeof(char), ROM_HEADER_SIZE, file);
if (read_size < ROM_HEADER_SIZE) {
log_error("Failed to read ROM");
return EXIT_FAILURE;
return false;
}
if (!rom_is_ines(header_buffer)) {
log_error("Only iNes ROMs are supported");
return EXIT_FAILURE;
return false;
}
log_info("Reading iNes 1.0 ROM at %s", path);
rom_ines_read(header_buffer, file, &rom);
rom_ines_read(header_buffer, file, system);
if (fclose(file) != 0) {
log_error("Failed to close ROM file");
return EXIT_FAILURE;
return false;
}
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;
return true;
}

57
system.c Normal file
View File

@ -0,0 +1,57 @@
//
// Created by william on 12/23/23.
//
#include "include/cpu.h"
#include "include/system.h"
#include "memory.h"
#include <unistd.h>
#include <assert.h>
#include <log.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];
cpu_init(&system->cpu);
ppu_init(&system->ppu, registers_base_addr, oam_dma_register);
system->mapper = get_mapper(MAPPER_TYPE_SIMPLE);
system->cycle_count = 7;
}
void system_start(System *system) {
address pc = mem_get_word(system, 0xfffc);
system->cpu.program_counter = pc;
}
void system_loop(System *system) {
assert(CPU_CLOCK_DIVISOR > PPU_CLOCK_DIVISOR);
unsigned int master_cycle_per_frame = MASTER_CLOCK / FRAME_RATE;
unsigned int cpu_cycle_per_frame = master_cycle_per_frame / CPU_CLOCK_DIVISOR;
unsigned int ppu_cycle_per_cpu_cycle = CPU_CLOCK_DIVISOR / PPU_CLOCK_DIVISOR;
long frame = 1;
long cpu_cycle_count = 0;
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);
}
cpu_cycle_count++;
for (int ppu_c = 0; ppu_c < ppu_cycle_per_cpu_cycle; ppu_c++) {
ppu_cycle(&system->ppu);
}
}
frame++;
usleep(17000); // Wait 16.6666ms
}
}
void system_uninit(System *system) {
}

3
test_roms/nestest.fdb Normal file
View File

@ -0,0 +1,3 @@
BreakPoint: startAddr=00000014 endAddr=00000000 flags=ER--X- condition="" desc=""
BreakPoint: startAddr=00000023 endAddr=00000000 flags=ER--X- condition="" desc=""
BreakPoint: startAddr=00000000 endAddr=00000000 flags=EC--X- condition="" desc=""

BIN
test_roms/nestest.nes Normal file

Binary file not shown.

1
test_roms/smb.fdb Normal file
View File

@ -0,0 +1 @@
BreakPoint: startAddr=00000022 endAddr=00000000 flags=ER--X- condition="" desc=""