Things
This commit is contained in:
parent
9c7b882f46
commit
cebee66076
|
@ -14,6 +14,7 @@
|
||||||
<config projectName="NESEmulator" targetName="NESEmulator" />
|
<config projectName="NESEmulator" targetName="NESEmulator" />
|
||||||
<config projectName="NESEmulator" targetName="ROM" />
|
<config projectName="NESEmulator" targetName="ROM" />
|
||||||
<config projectName="NESEmulator" targetName="Mappers" />
|
<config projectName="NESEmulator" targetName="Mappers" />
|
||||||
|
<config projectName="NESEmulator" targetName="PPU" />
|
||||||
<config projectName="NESEmulator" targetName="CPU" />
|
<config projectName="NESEmulator" targetName="CPU" />
|
||||||
</generated>
|
</generated>
|
||||||
</component>
|
</component>
|
||||||
|
@ -23,12 +24,59 @@
|
||||||
</configurations>
|
</configurations>
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="0c3b231e-0637-4ac1-8964-c60fc9e9e691" name="Changes" comment="CPU">
|
<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$/.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.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/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.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/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>
|
</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" />
|
||||||
|
@ -49,6 +97,9 @@
|
||||||
<component name="Git.Settings">
|
<component name="Git.Settings">
|
||||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="ProblemsViewState">
|
||||||
|
<option name="selectedTabId" value="CurrentFile" />
|
||||||
|
</component>
|
||||||
<component name="ProjectApplicationVersion">
|
<component name="ProjectApplicationVersion">
|
||||||
<option name="ide" value="CLion" />
|
<option name="ide" value="CLion" />
|
||||||
<option name="majorVersion" value="2023" />
|
<option name="majorVersion" value="2023" />
|
||||||
|
@ -92,17 +143,148 @@
|
||||||
}</component>
|
}</component>
|
||||||
<component name="RecentsManager">
|
<component name="RecentsManager">
|
||||||
<key name="MoveFile.RECENT_KEYS">
|
<key name="MoveFile.RECENT_KEYS">
|
||||||
<recent name="$PROJECT_DIR$/rom" />
|
|
||||||
<recent name="$PROJECT_DIR$/include" />
|
<recent name="$PROJECT_DIR$/include" />
|
||||||
|
<recent name="$PROJECT_DIR$/rom" />
|
||||||
<recent name="$PROJECT_DIR$/cpu" />
|
<recent name="$PROJECT_DIR$/cpu" />
|
||||||
<recent name="$PROJECT_DIR$/include/cpu" />
|
<recent name="$PROJECT_DIR$/include/cpu" />
|
||||||
<recent name="$PROJECT_DIR$" />
|
<recent name="$PROJECT_DIR$" />
|
||||||
</key>
|
</key>
|
||||||
</component>
|
</component>
|
||||||
<component name="RunManager" selected="CMake Application.NESEmulator">
|
<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="""" />
|
||||||
|
<option name="_new_target" value="""" />
|
||||||
|
<option name="_new_targetType" value=""PATH"" />
|
||||||
|
<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="""" />
|
||||||
|
<option name="_new_additionalArguments" value="""" />
|
||||||
|
<option name="_new_target" value="""" />
|
||||||
|
<option name="_new_targetType" value=""PATH"" />
|
||||||
|
<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="""" />
|
||||||
|
<option name="_new_target" value="""" />
|
||||||
|
<option name="_new_targetType" value=""PATH"" />
|
||||||
|
<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="""" />
|
||||||
|
<option name="_new_target" value="""" />
|
||||||
|
<option name="_new_targetType" value=""PATH"" />
|
||||||
|
<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="""" />
|
||||||
|
<option name="_new_parameters" value="""" />
|
||||||
|
<option name="_new_additionalArguments" value="""" />
|
||||||
|
<option name="_new_target" value="""" />
|
||||||
|
<option name="_new_targetType" value=""PATH"" />
|
||||||
|
<method v="2" />
|
||||||
|
</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>
|
||||||
|
<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">
|
<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">
|
<method v="2">
|
||||||
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
||||||
|
@ -113,11 +295,31 @@
|
||||||
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
</configuration>
|
</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">
|
<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">
|
<method v="2">
|
||||||
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
</configuration>
|
</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">
|
<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">
|
<method v="2">
|
||||||
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
||||||
|
@ -250,6 +452,7 @@
|
||||||
<item itemvalue="CMake Application.NESEmulator" />
|
<item itemvalue="CMake Application.NESEmulator" />
|
||||||
<item itemvalue="CMake Application.CPU" />
|
<item itemvalue="CMake Application.CPU" />
|
||||||
<item itemvalue="CMake Application.Mappers" />
|
<item itemvalue="CMake Application.Mappers" />
|
||||||
|
<item itemvalue="CMake Application.PPU" />
|
||||||
<item itemvalue="CMake Application.ROM" />
|
<item itemvalue="CMake Application.ROM" />
|
||||||
<item itemvalue="CMake Debug.NESEmulator" />
|
<item itemvalue="CMake Debug.NESEmulator" />
|
||||||
</list>
|
</list>
|
||||||
|
@ -283,6 +486,12 @@
|
||||||
<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" />
|
<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>
|
||||||
<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" />
|
||||||
|
@ -300,7 +509,15 @@
|
||||||
<option name="project" value="LOCAL" />
|
<option name="project" value="LOCAL" />
|
||||||
<updated>1701463073548</updated>
|
<updated>1701463073548</updated>
|
||||||
</task>
|
</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 />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="TypeScriptGeneratedFilesManager">
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
|
@ -316,6 +533,18 @@
|
||||||
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
|
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
|
||||||
<MESSAGE value="Cpu opcodes implementation" />
|
<MESSAGE value="Cpu opcodes implementation" />
|
||||||
<MESSAGE value="Gitignore" />
|
<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>
|
</component>
|
||||||
</project>
|
</project>
|
|
@ -2,19 +2,24 @@ cmake_minimum_required(VERSION 3.10)
|
||||||
project(NESEmulator VERSION 0.1)
|
project(NESEmulator VERSION 0.1)
|
||||||
|
|
||||||
add_subdirectory(cpu)
|
add_subdirectory(cpu)
|
||||||
|
add_subdirectory(ppu)
|
||||||
add_subdirectory(mappers)
|
add_subdirectory(mappers)
|
||||||
add_subdirectory(rom)
|
add_subdirectory(rom)
|
||||||
|
|
||||||
list(APPEND EXTRA_INCLUDES
|
list(APPEND EXTRA_INCLUDES
|
||||||
"${PROJECT_SOURCE_DIR}/cpu"
|
"${PROJECT_SOURCE_DIR}/cpu"
|
||||||
|
"${PROJECT_SOURCE_DIR}/ppu"
|
||||||
"${PROJECT_SOURCE_DIR}/mappers"
|
"${PROJECT_SOURCE_DIR}/mappers"
|
||||||
"${PROJECT_SOURCE_DIR}/rom")
|
"${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)
|
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
|
target_include_directories(NESEmulator PUBLIC
|
||||||
"${PROJECT_BINARY_DIR}"
|
"${PROJECT_BINARY_DIR}"
|
||||||
${EXTRA_INCLUDES})
|
${EXTRA_INCLUDES})
|
||||||
|
|
|
@ -2,4 +2,5 @@
|
||||||
# To keep your changes, remove these comment lines, but the plugin won't be able to modify your requirements
|
# To keep your changes, remove these comment lines, but the plugin won't be able to modify your requirements
|
||||||
|
|
||||||
requirements:
|
requirements:
|
||||||
|
- "libcheck/0.15.2"
|
||||||
- "log.c/cci.20200620"
|
- "log.c/cci.20200620"
|
|
@ -1,7 +1,6 @@
|
||||||
add_library(CPU
|
add_library(CPU
|
||||||
cpu.c
|
cpu.c
|
||||||
op.c
|
op.c
|
||||||
ram.c
|
|
||||||
memory.c
|
memory.c
|
||||||
cpu.h)
|
cpu.h)
|
||||||
|
|
||||||
|
|
152
cpu/cpu.c
152
cpu/cpu.c
|
@ -1,4 +1,6 @@
|
||||||
#include <log.h>
|
#include <log.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
#include "../include/cpu.h"
|
#include "../include/cpu.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
@ -22,104 +24,120 @@
|
||||||
* =====================================================================================
|
* =====================================================================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CpuRegisters registers;
|
void cpu_init(CPU *cpu) {
|
||||||
Mapper mapper;
|
cpu->program_counter = 0x8000;
|
||||||
unsigned int wait_cycle_count = 0;
|
cpu->stack_pointer = 0xfd;
|
||||||
|
cpu->accumulator = 0x00;
|
||||||
void cpu_init() {
|
cpu->x = 0x00;
|
||||||
registers.program_counter = 0xc000;
|
cpu->y = 0x00;
|
||||||
registers.stack_pointer = 0xff;
|
cpu->status = 0x04;
|
||||||
registers.accumulator = 0x00;
|
cpu->oam_dma_triggered = false;
|
||||||
registers.x = 0x00;
|
|
||||||
registers.y = 0x00;
|
|
||||||
registers.status = 0x00;
|
|
||||||
|
|
||||||
mapper = get_mapper(MAPPER_TYPE_SIMPLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cpu_step() {
|
void print_registers(CPU cpu, byte op, unsigned long cycle_count) {
|
||||||
int i = 0;
|
log_debug("%#02x %#02x %s \t A:%#02x X:%#02x Y:%#02x F:%#02x SP:%#02x \t [%d]",
|
||||||
while (i < 10) {
|
cpu.program_counter,
|
||||||
byte op = cpu_get_next_byte();
|
op,
|
||||||
process_op_code(op);
|
get_op_code_name(op),
|
||||||
i += 1;
|
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) {
|
||||||
wait_cycle_count += cycle_count;
|
system->cycle_count += cycle_count;
|
||||||
log_trace("Waiting for %d cycles", cycle_count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Registers ===
|
// === Registers ===
|
||||||
CpuRegisters *cpu_get_registers() {
|
bool cpu_get_flag(System *system, byte mask) {
|
||||||
return ®isters;
|
return system->cpu.status & mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte cpu_get_flag(byte mask) {
|
void cpu_set_flag(System *system, byte mask, bool set) {
|
||||||
return registers.status & mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cpu_set_flag(bool set, byte mask) {
|
|
||||||
if (set) {
|
if (set) {
|
||||||
registers.status |= mask;
|
system->cpu.status |= mask;
|
||||||
} else {
|
} else {
|
||||||
registers.status &= ~mask;
|
system->cpu.status &= ~mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Memory ===
|
byte cpu_get_next_byte(System *system) {
|
||||||
byte cpu_get_next_byte() {
|
byte next_byte = mem_get_byte(system, system->cpu.program_counter);
|
||||||
byte next_byte = mem_get_byte(&mapper, registers.program_counter);
|
system->cpu.program_counter++;
|
||||||
registers.program_counter++;
|
|
||||||
return next_byte;
|
return next_byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
word cpu_get_next_word() {
|
word cpu_get_next_word(System *system) {
|
||||||
word next_word = mem_get_word(&mapper, registers.program_counter);
|
word next_word = mem_get_word(system, system->cpu.program_counter);
|
||||||
registers.program_counter += 2;
|
system->cpu.program_counter += 2;
|
||||||
return next_word;
|
return next_word;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte cpu_peek_byte(address addr) {
|
void cpu_stack_push(System *system, byte value) {
|
||||||
return mem_get_byte(&mapper, addr);
|
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) {
|
byte cpu_stack_pop(System *system) {
|
||||||
return mem_get_word(&mapper, addr);
|
assert(system->cpu.stack_pointer < 0xff);
|
||||||
}
|
|
||||||
|
|
||||||
void cpu_push_byte(byte value, address addr) {
|
system->cpu.stack_pointer++;
|
||||||
mem_set_byte(&mapper, addr, value);
|
address mem_addr = CPU_STACK_ADDR | system->cpu.stack_pointer;
|
||||||
}
|
byte value = mem_get_byte(system, mem_addr);
|
||||||
|
|
||||||
// === 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++;
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cpu_stack_push_context() {
|
void cpu_stack_push_context(System *system) {
|
||||||
cpu_stack_push(registers.program_counter >> 8);
|
cpu_stack_push(system, system->cpu.program_counter >> 8);
|
||||||
cpu_stack_push(registers.program_counter & 0xff);
|
cpu_stack_push(system, system->cpu.program_counter & 0xff);
|
||||||
cpu_stack_push(registers.status);
|
cpu_stack_push(system, system->cpu.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cpu_stack_pop_context() {
|
void cpu_stack_pop_context(System *system) {
|
||||||
registers.status = cpu_stack_pop();
|
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();
|
byte lo = cpu_stack_pop(system);
|
||||||
address pc = cpu_stack_pop() << 8;
|
address pc = cpu_stack_pop(system) << 8;
|
||||||
pc += lo;
|
pc += lo;
|
||||||
registers.program_counter = pc;
|
system->cpu.program_counter = pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *operand_name(Operand *operand) {
|
char *operand_name(Operand *operand) {
|
||||||
|
|
102
cpu/cpu.h
102
cpu/cpu.h
|
@ -14,22 +14,11 @@
|
||||||
#define CPU_STATUS_INTERRUPT_DISABLE_MASK 0x04
|
#define CPU_STATUS_INTERRUPT_DISABLE_MASK 0x04
|
||||||
#define CPU_STATUS_DECIMAL_MASK 0x08
|
#define CPU_STATUS_DECIMAL_MASK 0x08
|
||||||
#define CPU_STATUS_B_MASK 0x10
|
#define CPU_STATUS_B_MASK 0x10
|
||||||
#define CPU_STATUS_I_MASK 0x20
|
|
||||||
#define CPU_STATUS_OVERFLOW_MASK 0x40
|
#define CPU_STATUS_OVERFLOW_MASK 0x40
|
||||||
#define CPU_STATUS_NEGATIVE_MASK 0x80
|
#define CPU_STATUS_NEGATIVE_MASK 0x80
|
||||||
|
|
||||||
#define CPU_STACK_ADDR 0x0100
|
#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 {
|
enum OperandType {
|
||||||
OPERAND_TYPE_ACCUMULATOR,
|
OPERAND_TYPE_ACCUMULATOR,
|
||||||
OPERAND_TYPE_IMMEDIATE,
|
OPERAND_TYPE_IMMEDIATE,
|
||||||
|
@ -42,24 +31,87 @@ typedef struct {
|
||||||
bool is_page_crossing;
|
bool is_page_crossing;
|
||||||
} Operand;
|
} Operand;
|
||||||
|
|
||||||
char* operand_name(Operand *operand);
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
CpuRegisters* cpu_get_registers();
|
/**
|
||||||
byte cpu_get_flag(byte mask);
|
* Gets a flag from the CPU registers.
|
||||||
void cpu_set_flag(bool set, byte mask);
|
*
|
||||||
|
* @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_get_next_byte();
|
/**
|
||||||
word cpu_get_next_word();
|
* 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);
|
||||||
|
|
||||||
byte cpu_peek_byte(address addr);
|
/**
|
||||||
word cpu_peek_word(address addr);
|
* Gets the next byte in the program.
|
||||||
void cpu_push_byte(byte value, address addr);
|
* 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_stack_push(byte value);
|
/**
|
||||||
void cpu_stack_push_context();
|
* Gets the next word in the program.
|
||||||
byte cpu_stack_pop();
|
* Increases the system program counter by 2.
|
||||||
void cpu_stack_pop_context();
|
*
|
||||||
|
* @param system The system
|
||||||
|
* @return The value of the next word.
|
||||||
|
*/
|
||||||
|
word cpu_get_next_word(System *system);
|
||||||
|
|
||||||
void cpu_add_cycles(unsigned int cycle_count);
|
/**
|
||||||
|
* 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
|
#endif //CPU_CPU_H
|
||||||
|
|
83
cpu/memory.c
83
cpu/memory.c
|
@ -2,35 +2,76 @@
|
||||||
// Created by william on 10/15/23.
|
// Created by william on 10/15/23.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <log.h>
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "ram.h"
|
|
||||||
#include "../include/rom.h"
|
#include "../include/rom.h"
|
||||||
|
|
||||||
byte mem_get_byte(Mapper *mapper, address addr) {
|
#define RAM_MAX_ADDR 0x2000
|
||||||
address redirected_addr = mapper->redirect_addr(addr);
|
#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) {
|
byte mem_get_byte(System *system, address addr) {
|
||||||
return ram_get_byte(redirected_addr);
|
assert(addr <= MAX_ADDR);
|
||||||
} else if (redirected_addr >= 0x4020) {
|
|
||||||
return rom_prg_get_byte(redirected_addr - 0x4020);
|
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;
|
if (addr >= PPU_MAX_ADDR && addr < APU_MAX_ADDR) {
|
||||||
}
|
byte apu_addr = addr - PPU_MAX_ADDR;
|
||||||
|
return system->apu_registers[apu_addr];
|
||||||
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;
|
return system->ram[addr];
|
||||||
}
|
}
|
||||||
|
|
||||||
void mem_set_byte(Mapper *mapper, address addr, byte byte) {
|
word mem_get_word(System *system, address addr) {
|
||||||
address redirected_addr = mapper->redirect_addr(addr);
|
assert(addr < MAX_ADDR);
|
||||||
ram_set_byte(redirected_addr, byte);
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
30
cpu/memory.h
30
cpu/memory.h
|
@ -3,12 +3,36 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "../include/mapper.h"
|
#include "../include/mapper.h"
|
||||||
|
#include "../include/system.h"
|
||||||
|
|
||||||
#ifndef NESEMULATOR_MEMORY_H
|
#ifndef NESEMULATOR_MEMORY_H
|
||||||
#define NESEMULATOR_MEMORY_H
|
#define NESEMULATOR_MEMORY_H
|
||||||
|
|
||||||
byte mem_get_byte(Mapper *mapper, address addr);
|
/**
|
||||||
word mem_get_word(Mapper *mapper, address addr);
|
* Gets a byte from a system's memory.
|
||||||
void mem_set_byte(Mapper *mapper, address addr, byte byte);
|
*
|
||||||
|
* @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
|
#endif //NESEMULATOR_MEMORY_H
|
||||||
|
|
3
cpu/op.h
3
cpu/op.h
|
@ -33,6 +33,7 @@ typedef enum {
|
||||||
ADDR_MODE_ZERO_PAGE_INDEXED_Y, // d,y
|
ADDR_MODE_ZERO_PAGE_INDEXED_Y, // d,y
|
||||||
} AddressingMode;
|
} AddressingMode;
|
||||||
|
|
||||||
void process_op_code(byte op);
|
void process_op_code(System *system, byte op);
|
||||||
|
char* get_op_code_name(byte op);
|
||||||
|
|
||||||
#endif
|
#endif
|
22
cpu/ram.c
22
cpu/ram.c
|
@ -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;
|
|
||||||
}
|
|
19
cpu/ram.h
19
cpu/ram.h
|
@ -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
|
|
|
@ -16,14 +16,14 @@
|
||||||
* =====================================================================================
|
* =====================================================================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "system.h"
|
||||||
|
|
||||||
#ifndef NESEMULATOR_CPU_H
|
#ifndef NESEMULATOR_CPU_H
|
||||||
#define NESEMULATOR_CPU_H
|
#define NESEMULATOR_CPU_H
|
||||||
|
|
||||||
typedef unsigned char byte;
|
void cpu_init(CPU *cpu);
|
||||||
typedef unsigned short address;
|
|
||||||
typedef unsigned short word;
|
|
||||||
|
|
||||||
void cpu_init();
|
void cpu_cycle(System *system);
|
||||||
void cpu_step();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2,13 +2,15 @@
|
||||||
// Created by william on 10/15/23.
|
// Created by william on 10/15/23.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
#ifndef NESEMULATOR_MAPPER_H
|
#ifndef NESEMULATOR_MAPPER_H
|
||||||
#define NESEMULATOR_MAPPER_H
|
#define NESEMULATOR_MAPPER_H
|
||||||
|
|
||||||
#include "../include/cpu.h"
|
typedef struct mapper {
|
||||||
|
address prg_rom_start_addr;
|
||||||
|
|
||||||
typedef struct {
|
void (*post_prg_load)(ram, unsigned int);
|
||||||
address (*redirect_addr)(unsigned short);
|
|
||||||
} Mapper;
|
} Mapper;
|
||||||
|
|
||||||
enum MapperType {
|
enum MapperType {
|
||||||
|
|
|
@ -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
|
|
@ -2,12 +2,14 @@
|
||||||
// Created by william on 12/2/23.
|
// Created by william on 12/2/23.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "types.h"
|
||||||
|
#include "system.h"
|
||||||
|
|
||||||
#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
|
// The size of the header in a ROM file, in bytes
|
||||||
#include "cpu.h"
|
|
||||||
|
|
||||||
#define ROM_HEADER_SIZE 16
|
#define ROM_HEADER_SIZE 16
|
||||||
// The size of the trainer in a ROM file, in bytes
|
// The size of the trainer in a ROM file, in bytes
|
||||||
#define ROM_TRAINER_SIZE 512
|
#define ROM_TRAINER_SIZE 512
|
||||||
|
@ -18,12 +20,13 @@ typedef struct {
|
||||||
void *header;
|
void *header;
|
||||||
} Rom;
|
} Rom;
|
||||||
|
|
||||||
int rom_load(char *path);
|
/**
|
||||||
|
* Loads a ROM from a specified file path.
|
||||||
void rom_uninit();
|
*
|
||||||
|
* @param path The file path
|
||||||
byte rom_prg_get_byte(address addr);
|
* @param rom ROM
|
||||||
|
* @return A boolean indicating a success (true) or an error.
|
||||||
word rom_prg_get_word(address addr);
|
*/
|
||||||
|
bool rom_load(char *path, System *system);
|
||||||
|
|
||||||
#endif //NESEMULATOR_ROM_H
|
#endif //NESEMULATOR_ROM_H
|
|
@ -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
|
|
@ -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
22
main.c
|
@ -16,19 +16,27 @@
|
||||||
* =====================================================================================
|
* =====================================================================================
|
||||||
*/
|
*/
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <log.h>
|
||||||
|
|
||||||
#include "include/rom.h"
|
#include "include/rom.h"
|
||||||
|
#include "include/system.h"
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
char *rom_path = "../tests/smb.nes";
|
log_set_level(LOG_INFO);
|
||||||
|
|
||||||
cpu_init();
|
char *rom_path = "../test_roms/nestest.nes";
|
||||||
rom_load(rom_path);
|
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;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,25 @@
|
||||||
#include "../include/mapper.h"
|
#include "../include/mapper.h"
|
||||||
|
#include "../include/rom.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
address redirect_addr(address addr) {
|
#define SIMPLE_MAPPER_PRG_START_ADDR 0x8000
|
||||||
return addr;
|
#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 get_simple_mapper() {
|
||||||
Mapper 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;
|
return mapper;
|
||||||
}
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
add_library(PPU
|
||||||
|
ppu.c)
|
||||||
|
|
||||||
|
find_package(log.c)
|
||||||
|
target_link_libraries(PPU log.c::log.c)
|
|
@ -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]++;
|
||||||
|
}
|
||||||
|
}
|
24
rom/ines.c
24
rom/ines.c
|
@ -4,9 +4,9 @@
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <log.h>
|
#include <log.h>
|
||||||
#include "../include/rom.h"
|
#include "../include/rom.h"
|
||||||
|
#include "../include/system.h"
|
||||||
|
|
||||||
// Flag 6
|
// Flag 6
|
||||||
#define NES_HEADER_FLAG_MIRRORING 0x01
|
#define NES_HEADER_FLAG_MIRRORING 0x01
|
||||||
|
@ -126,32 +126,30 @@ bool rom_ines_read_trainer(FILE *file, INesHeader *header) {
|
||||||
return false;
|
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;
|
unsigned int prg_rom_size = header->prg_rom_size * 16384;
|
||||||
rom->prg_rom = (byte *) malloc(prg_rom_size * sizeof(byte));
|
|
||||||
|
|
||||||
log_debug("Reading %d bytes PRG ROM", prg_rom_size);
|
log_debug("Reading %d bytes PRG ROM", prg_rom_size);
|
||||||
|
|
||||||
if (fread(rom->prg_rom, sizeof(byte), 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");
|
log_error("Failed to read PRG ROM");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
system->mapper.post_prg_load(&system->ram[0], header->prg_rom_size);
|
||||||
|
|
||||||
return true;
|
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) {
|
if (header->chr_rom_size <= 0) {
|
||||||
log_debug("No CHR ROM to read");
|
log_debug("No CHR ROM to read");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int chr_rom_size = header->chr_rom_size * 8192;
|
unsigned int chr_rom_size = header->chr_rom_size * 8192;
|
||||||
rom->chr_rom = (byte *) malloc(chr_rom_size * sizeof(byte));
|
|
||||||
|
|
||||||
log_debug("Reading %d bytes CHR ROM", chr_rom_size);
|
log_debug("Reading %d bytes CHR ROM", chr_rom_size);
|
||||||
|
|
||||||
if (fread(rom->chr_rom, sizeof(byte), 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");
|
log_error("Failed to read CHR ROM");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -159,11 +157,11 @@ bool rom_ines_read_chr_rom(FILE *file, INesHeader *header, Rom *rom) {
|
||||||
return true;
|
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);
|
INesHeader header = read_header(header_buf);
|
||||||
rom->header = &header;
|
system->rom_header = &header;
|
||||||
|
|
||||||
return rom_ines_read_trainer(file, &header) &&
|
return rom_ines_read_trainer(file, &header) &&
|
||||||
rom_ines_read_prg_rom(file, &header, rom) &&
|
rom_ines_read_prg_rom(file, &header, system) &&
|
||||||
rom_ines_read_chr_rom(file, &header, rom);
|
rom_ines_read_chr_rom(file, &header, system);
|
||||||
}
|
}
|
57
rom/rom.c
57
rom/rom.c
|
@ -3,74 +3,37 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include "../include/rom.h"
|
#include "../include/rom.h"
|
||||||
#include "ines.c"
|
#include "ines.c"
|
||||||
|
#include "../include/system.h"
|
||||||
|
|
||||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
bool rom_load(char *path, System *system) {
|
||||||
|
|
||||||
Rom rom;
|
|
||||||
|
|
||||||
void rom_init() {
|
|
||||||
rom.header = NULL;
|
|
||||||
rom.prg_rom = NULL;
|
|
||||||
rom.chr_rom = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int rom_load(char *path) {
|
|
||||||
rom_init();
|
|
||||||
|
|
||||||
FILE *file = fopen(path, "r");
|
FILE *file = fopen(path, "r");
|
||||||
if (!file) {
|
if (!file) {
|
||||||
log_error("Failed to open ROM");
|
log_error("Failed to open ROM");
|
||||||
return EXIT_FAILURE;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char header_buffer[ROM_HEADER_SIZE] = {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), ROM_HEADER_SIZE, file);
|
||||||
if (read_size < ARRAY_SIZE(header_buffer)) {
|
if (read_size < ROM_HEADER_SIZE) {
|
||||||
log_error("Failed to read ROM");
|
log_error("Failed to read ROM");
|
||||||
return EXIT_FAILURE;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rom_is_ines(header_buffer)) {
|
if (!rom_is_ines(header_buffer)) {
|
||||||
log_error("Only iNes ROMs are supported");
|
log_error("Only iNes ROMs are supported");
|
||||||
return EXIT_FAILURE;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_info("Reading iNes 1.0 ROM at %s", path);
|
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) {
|
if (fclose(file) != 0) {
|
||||||
log_error("Failed to close ROM file");
|
log_error("Failed to close ROM file");
|
||||||
return EXIT_FAILURE;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
|
@ -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) {
|
||||||
|
}
|
|
@ -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=""
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
BreakPoint: startAddr=00000022 endAddr=00000000 flags=ER--X- condition="" desc=""
|
Loading…
Reference in New Issue