Cpu opcodes implementation
This commit is contained in:
parent
ff720237d3
commit
83f4516a46
|
@ -12,6 +12,7 @@
|
|||
<component name="CMakeRunConfigurationManager">
|
||||
<generated>
|
||||
<config projectName="NESEmulator" targetName="NESEmulator" />
|
||||
<config projectName="NESEmulator" targetName="Mappers" />
|
||||
<config projectName="NESEmulator" targetName="CPU" />
|
||||
</generated>
|
||||
</component>
|
||||
|
@ -22,26 +23,25 @@
|
|||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="0c3b231e-0637-4ac1-8964-c60fc9e9e691" name="Changes" comment="">
|
||||
<change afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/.idea/modules.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/.idea/nes.iml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/conan_provider.cmake" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/conandata.yml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/conanfile.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/cpu/CMakeLists.txt" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/cpu/mem.c" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/cpu/op.c" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/include/cpu/cpu.h" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/include/cpu/mem.h" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Makefile" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/obj/cpu.o" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/obj/main.o" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/cpu/cpu.c" beforeDir="false" afterPath="$PROJECT_DIR$/cpu/cpu.c" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/cpu/cpu.h" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/cpu/op.c" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/cpu/op.h" beforeDir="false" afterPath="$PROJECT_DIR$/include/cpu/op.h" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main.c" beforeDir="false" afterPath="$PROJECT_DIR$/main.c" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/cpu/cpu.h" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/cpu/memory.c" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/cpu/memory.h" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/cpu/ram.c" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/cpu/ram.h" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/include/mapper.h" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/mappers/CMakeLists.txt" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/mappers/mappers.c" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/mappers/simple_mapper.c" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/CMakeLists.txt" beforeDir="false" afterPath="$PROJECT_DIR$/CMakeLists.txt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/cpu/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/mem.c" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/cpu/op.c" beforeDir="false" afterPath="$PROJECT_DIR$/cpu/op.c" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/include/cpu/cpu.h" beforeDir="false" afterPath="$PROJECT_DIR$/include/cpu.h" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/include/cpu/mem.h" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/include/cpu/op.h" beforeDir="false" afterPath="$PROJECT_DIR$/cpu/op.h" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/main.c" beforeDir="false" afterPath="$PROJECT_DIR$/main.c" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
|
@ -78,35 +78,38 @@
|
|||
<component name="ProjectViewState">
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"ASKED_ADD_EXTERNAL_FILES": "true",
|
||||
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.cidr.known.project.marker": "true",
|
||||
"cf.first.check.clang-format": "false",
|
||||
"cidr.known.project.marker": "true",
|
||||
"com.jfrog.conanplugin.addconansupport": "true",
|
||||
"com.jfrog.conanplugin.automanage.cmake.advanced.settings": "true",
|
||||
"com.jfrog.conanplugin.conanexecutable": "conan",
|
||||
"com.jfrog.conanplugin.hasbeensetup": "true",
|
||||
"git-widget-placeholder": "master",
|
||||
"last_opened_file_path": "/d/p/nes/CMakeLists.txt",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"settings.editor.selected.configurable": "preferences.pluginManager",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
"ASKED_ADD_EXTERNAL_FILES": "true",
|
||||
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.cidr.known.project.marker": "true",
|
||||
"WebServerToolWindowFactoryState": "false",
|
||||
"cf.first.check.clang-format": "false",
|
||||
"cidr.known.project.marker": "true",
|
||||
"com.jfrog.conanplugin.addconansupport": "true",
|
||||
"com.jfrog.conanplugin.automanage.cmake.advanced.settings": "true",
|
||||
"com.jfrog.conanplugin.conanexecutable": "conan",
|
||||
"com.jfrog.conanplugin.hasbeensetup": "true",
|
||||
"git-widget-placeholder": "master",
|
||||
"last_opened_file_path": "/home/william/Dev/ETS/LOG710/Lab2/CMakeLists.txt",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"settings.editor.selected.configurable": "preferences.sourceCode.C/C++",
|
||||
"structure.view.defaults.are.configured": "true",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}</component>
|
||||
}]]></component>
|
||||
<component name="RecentsManager">
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$/include/cpu" />
|
||||
<recent name="$PROJECT_DIR$/include" />
|
||||
<recent name="$PROJECT_DIR$/cpu" />
|
||||
<recent name="$PROJECT_DIR$/include/cpu" />
|
||||
<recent name="$PROJECT_DIR$" />
|
||||
<recent name="$PROJECT_DIR$/src" />
|
||||
<recent name="$PROJECT_DIR$/include" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="RunManager" selected="CMake Application.NESEmulator">
|
||||
|
@ -118,19 +121,143 @@
|
|||
<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 default="true" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true">
|
||||
<method v="2">
|
||||
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
<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>
|
||||
<list>
|
||||
<item itemvalue="CMake Application.NESEmulator" />
|
||||
<item itemvalue="CMake Application.CPU" />
|
||||
<item itemvalue="CMake Application.Mappers" />
|
||||
<item itemvalue="CMake Debug.NESEmulator" />
|
||||
</list>
|
||||
<recent_temporary>
|
||||
|
@ -151,6 +278,14 @@
|
|||
<workItem from="1696127133169" duration="3618000" />
|
||||
<workItem from="1696203038713" duration="1394000" />
|
||||
<workItem from="1696539868309" duration="1000" />
|
||||
<workItem from="1696705497743" duration="219000" />
|
||||
<workItem from="1696705726453" duration="4200000" />
|
||||
<workItem from="1696811216438" duration="1141000" />
|
||||
<workItem from="1697310877679" duration="207000" />
|
||||
<workItem from="1697340607326" duration="3636000" />
|
||||
<workItem from="1697386768217" duration="19159000" />
|
||||
<workItem from="1697491210595" duration="714000" />
|
||||
<workItem from="1698343311430" duration="84000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
|
|
|
@ -2,13 +2,16 @@ cmake_minimum_required(VERSION 3.10)
|
|||
project(NESEmulator VERSION 0.1)
|
||||
|
||||
add_subdirectory(cpu)
|
||||
list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/cpu")
|
||||
add_subdirectory(mappers)
|
||||
list(APPEND EXTRA_INCLUDES
|
||||
"${PROJECT_SOURCE_DIR}/cpu"
|
||||
"${PROJECT_SOURCE_DIR}/mappers")
|
||||
|
||||
add_executable(NESEmulator main.c)
|
||||
|
||||
find_package(log.c)
|
||||
|
||||
target_link_libraries(NESEmulator CPU log.c::log.c)
|
||||
target_link_libraries(NESEmulator CPU Mappers log.c::log.c)
|
||||
target_include_directories(NESEmulator PUBLIC
|
||||
"${PROJECT_BINARY_DIR}"
|
||||
${EXTRA_INCLUDES})
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
add_library(CPU
|
||||
cpu.c
|
||||
op.c
|
||||
mem.c)
|
||||
ram.c
|
||||
memory.c
|
||||
cpu.h)
|
||||
|
||||
find_package(log.c)
|
||||
target_link_libraries(CPU log.c::log.c)
|
89
cpu/cpu.c
89
cpu/cpu.c
|
@ -1,3 +1,7 @@
|
|||
#include "../include/cpu.h"
|
||||
#include "cpu.h"
|
||||
#include "memory.h"
|
||||
|
||||
/*
|
||||
* =====================================================================================
|
||||
*
|
||||
|
@ -15,13 +19,86 @@
|
|||
*
|
||||
* =====================================================================================
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../include/cpu/op.h"
|
||||
CpuRegisters registers;
|
||||
Mapper mapper;
|
||||
|
||||
long cpu_clock = 0;
|
||||
|
||||
void cpu_step_to(long cycle) {
|
||||
void cpu_init() {
|
||||
registers.program_counter = 0x0000;
|
||||
registers.stack_pointer = 0xff;
|
||||
registers.accumulator = 0x00;
|
||||
registers.x = 0x00;
|
||||
registers.y = 0x00;
|
||||
registers.status = 0x00;
|
||||
}
|
||||
|
||||
// === Registers ===
|
||||
CpuRegisters* cpu_get_registers() {
|
||||
return ®isters;
|
||||
}
|
||||
|
||||
byte cpu_get_flag(byte mask) {
|
||||
return registers.status & mask;
|
||||
}
|
||||
|
||||
void cpu_set_flag(bool set, byte mask) {
|
||||
if (set) {
|
||||
registers.status |= mask;
|
||||
} else {
|
||||
registers.status &= ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
// === Memory ===
|
||||
byte cpu_get_next_byte() {
|
||||
byte next_byte = mem_get_byte(&mapper, registers.program_counter);
|
||||
registers.program_counter++;
|
||||
return next_byte;
|
||||
}
|
||||
|
||||
word cpu_get_next_word() {
|
||||
word next_word = mem_get_word(&mapper, registers.program_counter);
|
||||
registers.program_counter += 2;
|
||||
return next_word;
|
||||
}
|
||||
|
||||
byte cpu_peek_byte(address addr) {
|
||||
return mem_get_byte(&mapper, addr);
|
||||
}
|
||||
|
||||
word cpu_peek_word(address addr) {
|
||||
return mem_get_word(&mapper, addr);
|
||||
}
|
||||
|
||||
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++;
|
||||
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_pop_context() {
|
||||
registers.status = cpu_stack_pop();
|
||||
|
||||
byte lo = cpu_stack_pop();
|
||||
address pc = cpu_stack_pop() << 8;
|
||||
pc += lo;
|
||||
registers.program_counter = pc;
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// Created by william on 10/15/23.
|
||||
//
|
||||
|
||||
#ifndef CPU_CPU_H
|
||||
#define CPU_CPU_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
// Reference: https://www.nesdev.org/wiki/Status_flags
|
||||
#define CPU_STATUS_CARRY_MASK 0x01
|
||||
#define CPU_STATUS_ZERO_MASK 0x02
|
||||
#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,
|
||||
OPERAND_TYPE_ADDRESS
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
word value;
|
||||
enum OperandType type;
|
||||
bool is_page_crossing;
|
||||
} Operand;
|
||||
|
||||
CpuRegisters* cpu_get_registers();
|
||||
byte cpu_get_flag(byte mask);
|
||||
void cpu_set_flag(bool set, byte mask);
|
||||
|
||||
byte cpu_get_next_byte();
|
||||
word cpu_get_next_word();
|
||||
|
||||
byte cpu_peek_byte(address addr);
|
||||
word cpu_peek_word(address addr);
|
||||
void cpu_push_byte(byte value, address addr);
|
||||
|
||||
void cpu_stack_push(byte value);
|
||||
void cpu_stack_push_context();
|
||||
byte cpu_stack_pop();
|
||||
void cpu_stack_pop_context();
|
||||
|
||||
#endif //CPU_CPU_H
|
26
cpu/mem.c
26
cpu/mem.c
|
@ -1,26 +0,0 @@
|
|||
//
|
||||
// Created by william on 30/09/23.
|
||||
//
|
||||
|
||||
#include "../include/cpu/mem.h"
|
||||
#include "../include/cpu/op.h"
|
||||
|
||||
#include "../include/cpu/cpu.h"
|
||||
|
||||
unsigned short get_memory_address(addr_mode_t addr_mode, unsigned short operand) {
|
||||
if (addr_mode == ADDR_MODE_ABSOLUTE) {
|
||||
cpu_add_cycles(4);
|
||||
return operand;
|
||||
} else if (addr_mode == ADDR_MODE_ABSOLUTE_INDEXED_X) {
|
||||
unsigned char x = cpu_get_registers()->x;
|
||||
cpu_add_cycles(4);
|
||||
return operand + x;
|
||||
} else if (addr_mode == ADDR_MODE_ABSOLUTE_INDEXED_Y) {
|
||||
unsigned char y = cpu_get_registers()->y;
|
||||
cpu_add_cycles(4);
|
||||
return operand + y;
|
||||
} else if (addr_mode == ADDR_MODE_INDEXED_INDIRECT) {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// Created by william on 10/15/23.
|
||||
//
|
||||
|
||||
#include "memory.h"
|
||||
#include "ram.h"
|
||||
|
||||
byte mem_get_byte(Mapper *mapper, address addr) {
|
||||
address redirected_addr = mapper->redirect_addr(addr);
|
||||
return ram_get_byte(redirected_addr);
|
||||
}
|
||||
|
||||
word mem_get_word(Mapper *mapper, address addr) {
|
||||
address redirected_addr = mapper->redirect_addr(addr);
|
||||
return ram_get_word(redirected_addr);
|
||||
}
|
||||
|
||||
void mem_set_byte(Mapper *mapper, address addr, byte byte) {
|
||||
address redirected_addr = mapper->redirect_addr(addr);
|
||||
ram_set_byte(redirected_addr, byte);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// Created by william on 10/15/23.
|
||||
//
|
||||
|
||||
#include "../include/mapper.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);
|
||||
|
||||
#endif //NESEMULATOR_MEMORY_H
|
667
cpu/op.c
667
cpu/op.c
|
@ -1,6 +1,13 @@
|
|||
#include "../include/cpu/op.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "op.h"
|
||||
#include "cpu.h"
|
||||
#include "../include/cpu.h"
|
||||
|
||||
// Reference: https://www.nesdev.org/wiki/CPU_unofficial_opcodes
|
||||
// https://www.middle-engine.com/blog/posts/2020/06/23/programming-the-nes-the-6502-in-detail
|
||||
|
||||
#define IS_OP_CODE_MODE(op, op_code, addr_mode) \
|
||||
case op_code: \
|
||||
|
@ -14,20 +21,20 @@
|
|||
IS_OP_CODE_MODE(op, OP_CODE_BASE_ ## op + offset, addr_mode)
|
||||
|
||||
#define IS_ALU_OP_CODE(op) \
|
||||
IS_ALU_OP_CODE_(op, 0x01, INDEXED_INDIRECT) \
|
||||
IS_ALU_OP_CODE_(op, 0x01, INDIRECT_X) \
|
||||
IS_ALU_OP_CODE_(op, 0x05, ZERO_PAGE) \
|
||||
IS_ALU_OP_CODE_(op, 0x09, IMMEDIATE) \
|
||||
IS_ALU_OP_CODE_(op, 0x0d, ABSOLUTE) \
|
||||
IS_ALU_OP_CODE_(op, 0x11, INDIRECT_INDEXED) \
|
||||
IS_ALU_OP_CODE_(op, 0x11, INDIRECT_Y) \
|
||||
IS_ALU_OP_CODE_(op, 0x15, ZERO_PAGE_INDEXED_X) \
|
||||
IS_ALU_OP_CODE_(op, 0x19, ABSOLUTE_INDEXED_Y) \
|
||||
IS_ALU_OP_CODE_(op, 0x1d, ABSOLUTE_INDEXED_X)
|
||||
|
||||
#define IS_ALU_OP_CODE_NO_IMMEDIATE(op) \
|
||||
IS_ALU_OP_CODE_(op, 0x01, INDEXED_INDIRECT) \
|
||||
IS_ALU_OP_CODE_(op, 0x01, INDIRECT_X) \
|
||||
IS_ALU_OP_CODE_(op, 0x05, ZERO_PAGE) \
|
||||
IS_ALU_OP_CODE_(op, 0x0d, ABSOLUTE) \
|
||||
IS_ALU_OP_CODE_(op, 0x11, INDIRECT_INDEXED) \
|
||||
IS_ALU_OP_CODE_(op, 0x11, INDIRECT_Y) \
|
||||
IS_ALU_OP_CODE_(op, 0x15, ZERO_PAGE_INDEXED_X) \
|
||||
IS_ALU_OP_CODE_(op, 0x19, ABSOLUTE_INDEXED_Y) \
|
||||
IS_ALU_OP_CODE_(op, 0x1d, ABSOLUTE_INDEXED_X)
|
||||
|
@ -46,316 +53,760 @@
|
|||
IS_OP_CODE_MODE(op, OP_CODE_BASE_ ## line + offset, addr_mode)
|
||||
|
||||
#define IS_UNOFFICIAL_OP_CODE(op, line) \
|
||||
IS_UNOFFICIAL_OP_CODE_(op, line, 0x03, INDEXED_INDIRECT) \
|
||||
IS_UNOFFICIAL_OP_CODE_(op, line, 0x03, INDIRECT_X) \
|
||||
IS_UNOFFICIAL_OP_CODE_(op, line, 0x07, ZERO_PAGE) \
|
||||
IS_UNOFFICIAL_OP_CODE_(op, line, 0x0f, ABSOLUTE) \
|
||||
IS_UNOFFICIAL_OP_CODE_(op, line, 0x13, INDIRECT_INDEXED) \
|
||||
IS_UNOFFICIAL_OP_CODE_(op, line, 0x13, INDIRECT_Y) \
|
||||
IS_UNOFFICIAL_OP_CODE_(op, line, 0x17, ZERO_PAGE_INDEXED_X) \
|
||||
IS_UNOFFICIAL_OP_CODE_(op, line, 0x1b, ABSOLUTE_INDEXED_Y) \
|
||||
IS_UNOFFICIAL_OP_CODE_(op, line, 0x1f, ABSOLUTE_INDEXED_X)
|
||||
|
||||
void op_ADC(addr_mode_t addr_mode) {
|
||||
|
||||
address decode_operand_addr(AddressingMode addr_mode, bool *page_crossing) {
|
||||
CpuRegisters *registers = cpu_get_registers();
|
||||
|
||||
if (addr_mode == ADDR_MODE_ZERO_PAGE) {
|
||||
return cpu_get_next_byte();
|
||||
} else if (addr_mode == ADDR_MODE_ZERO_PAGE_INDEXED_X) {
|
||||
return (cpu_get_next_byte() + registers->x) & 0xff;
|
||||
} else if (addr_mode == ADDR_MODE_ZERO_PAGE_INDEXED_Y) {
|
||||
return (cpu_get_next_byte() + registers->y) & 0xff;
|
||||
} else if (addr_mode == ADDR_MODE_ABSOLUTE || addr_mode == ADDR_MODE_ABSOLUTE_JUMP) {
|
||||
return cpu_get_next_word();
|
||||
} else if (addr_mode == ADDR_MODE_ABSOLUTE_INDEXED_X) {
|
||||
word addr = cpu_get_next_word();
|
||||
word new_addr = addr + registers->x;
|
||||
|
||||
*page_crossing = (addr & 0xff00) != (new_addr & 0xff00);
|
||||
|
||||
return new_addr;
|
||||
} else if (addr_mode == ADDR_MODE_ABSOLUTE_INDEXED_Y) {
|
||||
word addr = cpu_get_next_word();
|
||||
word new_addr = addr + registers->y;
|
||||
|
||||
*page_crossing = (addr & 0xff00) != (new_addr & 0xff00);
|
||||
|
||||
return new_addr;
|
||||
} else if (addr_mode == ADDR_MODE_INDIRECT_JUMP) {
|
||||
word addr = cpu_get_next_word();
|
||||
if ((addr & 0xff) == 0xff) {
|
||||
// Error in NES CPU for JMP op
|
||||
word result = cpu_peek_byte(addr);
|
||||
result += cpu_peek_byte(addr & 0xff00) << 8;
|
||||
return result;
|
||||
}
|
||||
return cpu_peek_word(addr);
|
||||
} else if (addr_mode == ADDR_MODE_INDIRECT_X) {
|
||||
byte addr = cpu_get_next_byte();
|
||||
word result = cpu_peek_byte((addr + registers->x) & 0xff);
|
||||
result += cpu_peek_byte((addr + registers->x + 1) & 0xff) << 8;
|
||||
return result;
|
||||
} else if (addr_mode == ADDR_MODE_INDIRECT_Y) {
|
||||
byte arg_addr = cpu_get_next_byte();
|
||||
word addr = cpu_peek_byte(arg_addr) + (cpu_peek_byte((arg_addr + 1) & 0xff) << 8);
|
||||
word new_addr = addr + registers->y;
|
||||
|
||||
*page_crossing = (addr & 0xff00) != (new_addr & 0xff00);
|
||||
|
||||
return new_addr;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
Operand decode_operand(AddressingMode addr_mode) {
|
||||
Operand operand;
|
||||
|
||||
if (addr_mode == ADDR_MODE_ACCUMULATOR) {
|
||||
operand.type = OPERAND_TYPE_ACCUMULATOR;
|
||||
operand.value = 0;
|
||||
operand.is_page_crossing = false;
|
||||
} else if (addr_mode == ADDR_MODE_IMMEDIATE) {
|
||||
operand.type = OPERAND_TYPE_IMMEDIATE;
|
||||
operand.value = cpu_get_next_byte();
|
||||
operand.is_page_crossing = false;
|
||||
} else {
|
||||
operand.type = OPERAND_TYPE_ADDRESS;
|
||||
operand.value = decode_operand_addr(addr_mode, &operand.is_page_crossing);
|
||||
}
|
||||
|
||||
return operand;
|
||||
}
|
||||
|
||||
/* === CTRL === */
|
||||
void op_BRK(addr_mode_t addr_mode) {
|
||||
byte read_operand(Operand operand) {
|
||||
switch (operand.type) {
|
||||
case OPERAND_TYPE_ACCUMULATOR:
|
||||
return cpu_get_registers()->accumulator;
|
||||
case OPERAND_TYPE_IMMEDIATE:
|
||||
return (byte) operand.value;
|
||||
case OPERAND_TYPE_ADDRESS:
|
||||
return cpu_peek_byte(operand.value);
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void write_operand(Operand operand, byte value) {
|
||||
switch (operand.type) {
|
||||
case OPERAND_TYPE_ACCUMULATOR:
|
||||
cpu_get_registers()->accumulator = value;
|
||||
break;
|
||||
case OPERAND_TYPE_ADDRESS:
|
||||
cpu_push_byte(operand.value, value);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void op_BPL(addr_mode_t addr_mode) {
|
||||
bool is_sign_overflow(byte val1, byte val2, byte result) {
|
||||
return ((val1 & 0x80) == (val2 & 0x80)) &&
|
||||
((val1 & 0x80) != (result & 0x80));
|
||||
}
|
||||
|
||||
byte get_cycle_count(Operand operand, AddressingMode addr_mode) {
|
||||
switch (addr_mode) {
|
||||
case ADDR_MODE_ACCUMULATOR:
|
||||
case ADDR_MODE_IMPLICIT:
|
||||
return 2;
|
||||
case ADDR_MODE_ZERO_PAGE:
|
||||
return 3;
|
||||
case ADDR_MODE_ZERO_PAGE_INDEXED_X:
|
||||
case ADDR_MODE_ZERO_PAGE_INDEXED_Y:
|
||||
case ADDR_MODE_ABSOLUTE:
|
||||
return 4;
|
||||
case ADDR_MODE_ABSOLUTE_INDEXED_X:
|
||||
case ADDR_MODE_ABSOLUTE_INDEXED_Y:
|
||||
return operand.is_page_crossing ? 5 : 4;
|
||||
case ADDR_MODE_INDIRECT_X:
|
||||
return 6;
|
||||
case ADDR_MODE_INDIRECT_Y:
|
||||
return operand.is_page_crossing ? 6 : 5;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void op_BIT(addr_mode_t addr_mode) {
|
||||
byte get_shift_cycle_count(AddressingMode addr_mode) {
|
||||
switch (addr_mode) {
|
||||
case ADDR_MODE_ACCUMULATOR:
|
||||
return 2;
|
||||
case ADDR_MODE_ZERO_PAGE:
|
||||
return 5;
|
||||
case ADDR_MODE_ZERO_PAGE_INDEXED_X:
|
||||
case ADDR_MODE_ABSOLUTE:
|
||||
return 6;
|
||||
case ADDR_MODE_ABSOLUTE_INDEXED_X:
|
||||
return 7;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void set_acl_flags(byte result) {
|
||||
cpu_set_flag(result == 0, CPU_STATUS_ZERO_MASK);
|
||||
cpu_set_flag(result & 0x80, CPU_STATUS_NEGATIVE_MASK);
|
||||
}
|
||||
|
||||
void op_BMI(addr_mode_t addr_mode) {
|
||||
byte get_branch_cycle_count(bool branching, char offset) {
|
||||
address target = cpu_get_registers()->program_counter;
|
||||
byte cycle_count = 2;
|
||||
|
||||
}
|
||||
if (branching) {
|
||||
cycle_count += 1;
|
||||
|
||||
void op_BVC(addr_mode_t addr_mode) {
|
||||
if ((target & 0xff00) != ((target - offset) & 0xff00)) {
|
||||
cycle_count += 2;
|
||||
}
|
||||
}
|
||||
|
||||
return cycle_count;
|
||||
}
|
||||
|
||||
void op_BVS(addr_mode_t addr_mode) {
|
||||
void op_branch(bool branching) {
|
||||
char offset = (char) cpu_get_next_byte();
|
||||
|
||||
if (branching) {
|
||||
address counter = cpu_get_registers()->program_counter;
|
||||
address target = counter + offset;
|
||||
cpu_get_registers()->program_counter = target;
|
||||
}
|
||||
|
||||
void op_BCC(addr_mode_t addr_mode) {
|
||||
|
||||
cpu_add_cycles(get_branch_cycle_count(branching, offset));
|
||||
}
|
||||
|
||||
void op_BCS(addr_mode_t addr_mode) {
|
||||
void add_with_carry(byte value) {
|
||||
byte acc = cpu_get_registers()->accumulator;
|
||||
|
||||
}
|
||||
byte addition = acc + value;
|
||||
bool overflow = false;
|
||||
|
||||
void op_BNE(addr_mode_t addr_mode) {
|
||||
// Check for overflow
|
||||
if (addition < acc) {
|
||||
overflow = true;
|
||||
}
|
||||
|
||||
// Add carry flag and check for overflow again
|
||||
byte result = addition + cpu_get_flag(CPU_STATUS_CARRY_MASK);
|
||||
if (result < addition) {
|
||||
overflow = true;
|
||||
}
|
||||
|
||||
void op_BEQ(addr_mode_t addr_mode) {
|
||||
cpu_get_registers()->accumulator = acc;
|
||||
|
||||
cpu_set_flag(overflow, CPU_STATUS_CARRY_MASK);
|
||||
cpu_set_flag(is_sign_overflow(acc, value, result));
|
||||
|
||||
set_acl_flags(result);
|
||||
}
|
||||
|
||||
void op_PHP(addr_mode_t addr_mode) {
|
||||
void op_ADC(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
|
||||
}
|
||||
byte value = read_operand(operand);
|
||||
add_with_carry(value);
|
||||
|
||||
void op_CLC(addr_mode_t addr_mode) {
|
||||
cpu_add_cycles(get_cycle_count(operand, addr_mode));
|
||||
}
|
||||
|
||||
void op_AHX(AddressingMode addr_mode) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void op_JSR(addr_mode_t addr_mode) {
|
||||
void op_ALR(AddressingMode addr_mode) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void op_ANC(AddressingMode addr_mode) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void op_PLP(addr_mode_t addr_mode) {
|
||||
void op_AND(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte value = read_operand(operand);
|
||||
byte acc = cpu_get_registers()->accumulator;
|
||||
|
||||
}
|
||||
byte result = acc & value;
|
||||
cpu_get_registers()->accumulator = result;
|
||||
|
||||
void op_SEC(addr_mode_t addr_mode) {
|
||||
set_acl_flags(result);
|
||||
cpu_add_cycles(get_cycle_count(operand, addr_mode));
|
||||
}
|
||||
|
||||
void op_ARR(AddressingMode addr_mode) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void op_RTI(addr_mode_t addr_mode) {
|
||||
void op_ASL(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte value = read_operand(operand);
|
||||
|
||||
byte result = value << 1;
|
||||
write_operand(operand, result);
|
||||
|
||||
cpu_set_flag(value & 0x80, CPU_STATUS_CARRY_MASK);
|
||||
set_acl_flags(result);
|
||||
cpu_add_cycles(get_shift_cycle_count(addr_mode));
|
||||
}
|
||||
|
||||
void op_PHA(addr_mode_t addr_mode) {
|
||||
void op_AXS(AddressingMode addr_mode) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void op_BCC(AddressingMode addr_mode) {
|
||||
op_branch(!cpu_get_flag(CPU_STATUS_CARRY_MASK));
|
||||
}
|
||||
|
||||
void op_JMP(addr_mode_t addr_mode) {
|
||||
void op_BCS(AddressingMode addr_mode) {
|
||||
op_branch(cpu_get_flag(CPU_STATUS_CARRY_MASK));
|
||||
}
|
||||
|
||||
void op_BEQ(AddressingMode addr_mode) {
|
||||
op_branch(cpu_get_flag(CPU_STATUS_ZERO_MASK));
|
||||
}
|
||||
|
||||
void op_CLI(addr_mode_t addr_mode) {
|
||||
void op_BIT(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte value = read_operand(operand);
|
||||
byte acc = cpu_get_registers()->accumulator;
|
||||
|
||||
}
|
||||
byte result = value & acc;
|
||||
|
||||
void op_RTS(addr_mode_t addr_mode) {
|
||||
cpu_set_flag(result == 0, CPU_STATUS_ZERO_MASK);
|
||||
cpu_set_flag(result & 0x40, CPU_STATUS_OVERFLOW_MASK);
|
||||
cpu_set_flag(result & 0x80, CPU_STATUS_NEGATIVE_MASK);
|
||||
}
|
||||
|
||||
void op_BMI(AddressingMode addr_mode) {
|
||||
op_branch(cpu_get_flag(CPU_STATUS_NEGATIVE_MASK));
|
||||
}
|
||||
|
||||
void op_PLA(addr_mode_t addr_mode) {
|
||||
void op_BNE(AddressingMode addr_mode) {
|
||||
op_branch(!cpu_get_flag(CPU_STATUS_ZERO_MASK));
|
||||
}
|
||||
|
||||
void op_BPL(AddressingMode addr_mode) {
|
||||
op_branch(!cpu_get_flag(CPU_STATUS_NEGATIVE_MASK));
|
||||
}
|
||||
|
||||
void op_SEI(addr_mode_t addr_mode) {
|
||||
// Stops program execution, useful for debugging
|
||||
void op_BRK(AddressingMode addr_mode) {
|
||||
cpu_stack_push_context();
|
||||
|
||||
// TODO Load IRQ interrupt vector in PC at $FFFE/F
|
||||
|
||||
cpu_set_flag(true, CPU_STATUS_B_MASK);
|
||||
cpu_add_cycles(7);
|
||||
}
|
||||
|
||||
void op_STY(addr_mode_t addr_mode) {
|
||||
void op_BVC(AddressingMode addr_mode) {
|
||||
op_branch(!cpu_get_flag(CPU_STATUS_OVERFLOW_MASK));
|
||||
}
|
||||
|
||||
void op_BVS(AddressingMode addr_mode) {
|
||||
op_branch(cpu_get_flag(CPU_STATUS_OVERFLOW_MASK));
|
||||
}
|
||||
|
||||
void op_DEY(addr_mode_t addr_mode) {
|
||||
void op_CLC(AddressingMode addr_mode) {
|
||||
cpu_set_flag(false, CPU_STATUS_CARRY_MASK);
|
||||
cpu_add_cycles(2);
|
||||
}
|
||||
|
||||
void op_CLD(AddressingMode addr_mode) {
|
||||
cpu_set_flag(false, CPU_STATUS_DECIMAL_MASK);
|
||||
cpu_add_cycles(2);
|
||||
}
|
||||
|
||||
void op_TYA(addr_mode_t addr_mode) {
|
||||
void op_CLI(AddressingMode addr_mode) {
|
||||
cpu_set_flag(false, CPU_STATUS_INTERRUPT_DISABLE_MASK);
|
||||
cpu_add_cycles(2);
|
||||
}
|
||||
|
||||
void op_CLV(AddressingMode addr_mode) {
|
||||
cpu_set_flag(false, CPU_STATUS_OVERFLOW_MASK);
|
||||
cpu_add_cycles(2);
|
||||
}
|
||||
|
||||
void op_LDY(addr_mode_t addr_mode) {
|
||||
void op_CMP(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte value = read_operand(operand);
|
||||
byte acc = cpu_get_registers()->accumulator;
|
||||
|
||||
}
|
||||
byte result = acc - value;
|
||||
|
||||
void op_TAY(addr_mode_t addr_mode) {
|
||||
cpu_set_flag(acc >= value, CPU_STATUS_CARRY_MASK);
|
||||
cpu_set_flag(result == 0, CPU_STATUS_ZERO_MASK);
|
||||
cpu_set_flag(result & 0x80, CPU_STATUS_NEGATIVE_MASK);
|
||||
|
||||
cpu_add_cycles(get_cycle_count(operand, addr_mode));
|
||||
}
|
||||
|
||||
void op_CLV(addr_mode_t addr_mode) {
|
||||
void op_CPX(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte value = read_operand(operand);
|
||||
byte x = cpu_get_registers()->x;
|
||||
|
||||
}
|
||||
byte result = x - value;
|
||||
|
||||
void op_CPY(addr_mode_t addr_mode) {
|
||||
cpu_set_flag(x >= value, CPU_STATUS_CARRY_MASK);
|
||||
cpu_set_flag(result == 0, CPU_STATUS_ZERO_MASK);
|
||||
cpu_set_flag(result & 0x80, CPU_STATUS_NEGATIVE_MASK);
|
||||
|
||||
cpu_add_cycles(get_cycle_count(operand, addr_mode));
|
||||
}
|
||||
|
||||
void op_INY(addr_mode_t addr_mode) {
|
||||
void op_CPY(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte value = read_operand(operand);
|
||||
byte y = cpu_get_registers()->y;
|
||||
|
||||
}
|
||||
byte result = y - value;
|
||||
|
||||
void op_CLD(addr_mode_t addr_mode) {
|
||||
cpu_set_flag(y >= value, CPU_STATUS_CARRY_MASK);
|
||||
cpu_set_flag(result == 0, CPU_STATUS_ZERO_MASK);
|
||||
cpu_set_flag(result & 0x80, CPU_STATUS_NEGATIVE_MASK);
|
||||
|
||||
cpu_add_cycles(get_cycle_count(operand, addr_mode));
|
||||
}
|
||||
|
||||
void op_CPX(addr_mode_t addr_mode) {
|
||||
|
||||
void op_DCP(AddressingMode addr_mode) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void op_INX(addr_mode_t addr_mode) {
|
||||
void op_DEC(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte value = read_operand(operand);
|
||||
|
||||
byte result = value - 1;
|
||||
|
||||
set_acl_flags(result);
|
||||
write_operand(operand, result);
|
||||
cpu_add_cycles(get_cycle_count(operand, addr_mode));
|
||||
}
|
||||
|
||||
void op_SED(addr_mode_t addr_mode) {
|
||||
void op_DEX(AddressingMode addr_mode) {
|
||||
byte x = cpu_get_registers()->x;
|
||||
|
||||
byte result = x - 1;
|
||||
cpu_get_registers()->x = result;
|
||||
|
||||
set_acl_flags(result);
|
||||
cpu_add_cycles(2);
|
||||
}
|
||||
|
||||
/* === ALU === */
|
||||
void op_ORA(addr_mode_t addr_mode) {
|
||||
void op_DEY(AddressingMode addr_mode) {
|
||||
byte y = cpu_get_registers()->y;
|
||||
|
||||
byte result = y - 1;
|
||||
cpu_get_registers()->y = result;
|
||||
|
||||
set_acl_flags(result);
|
||||
cpu_add_cycles(2);
|
||||
}
|
||||
|
||||
void op_AND(addr_mode_t addr_mode) {
|
||||
void op_EOR(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte value = read_operand(operand);
|
||||
byte acc = cpu_get_registers()->accumulator;
|
||||
|
||||
acc ^= value;
|
||||
cpu_get_registers()->accumulator = acc;
|
||||
|
||||
set_acl_flags(acc);
|
||||
cpu_add_cycles(get_cycle_count(operand, addr_mode));
|
||||
}
|
||||
|
||||
void op_EOR(addr_mode_t addr_mode) {
|
||||
void op_INC(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte value = read_operand(operand);
|
||||
|
||||
value += 1;
|
||||
write_operand(operand, value);
|
||||
|
||||
set_acl_flags(value);
|
||||
cpu_add_cycles(get_shift_cycle_count(addr_mode));
|
||||
}
|
||||
|
||||
void op_ADC(addr_mode_t addr_mode) {
|
||||
void op_INX(AddressingMode addr_mode) {
|
||||
byte x = cpu_get_registers()->x;
|
||||
|
||||
x += 1;
|
||||
cpu_get_registers()->x = x;
|
||||
|
||||
set_acl_flags(x);
|
||||
cpu_add_cycles(2);
|
||||
}
|
||||
|
||||
void op_STA(addr_mode_t addr_mode) {
|
||||
void op_INY(AddressingMode addr_mode) {
|
||||
byte y = cpu_get_registers()->y;
|
||||
|
||||
}
|
||||
y += 1;
|
||||
cpu_get_registers()->y = y;
|
||||
|
||||
void op_LDA(addr_mode_t addr_mode) {
|
||||
set_acl_flags(y);
|
||||
cpu_add_cycles(2);
|
||||
}
|
||||
|
||||
void op_ISC(AddressingMode addr_mode) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void op_CMP(addr_mode_t addr_mode) {
|
||||
void op_JMP(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte addr = read_operand(operand);
|
||||
|
||||
cpu_get_registers()->program_counter = addr;
|
||||
|
||||
// TODO WN: Handle CPU bug?
|
||||
// > An original 6502 has does not correctly fetch the target address if the indirect vector falls on a page boundary (e.g. $xxFF where xx is any value from $00 to $FF).
|
||||
// > In this case fetches the LSB from $xxFF as expected but takes the MSB from $xx00.
|
||||
// > This is fixed in some later chips like the 65SC02 so for compatibility always ensure the indirect vector is not at the end of the page.
|
||||
}
|
||||
|
||||
void op_SBC(addr_mode_t addr_mode) {
|
||||
void op_JSR(AddressingMode addr_mode) {
|
||||
// Push the program counter on the stack
|
||||
address program_counter = cpu_get_registers()->program_counter - 1;
|
||||
cpu_stack_push(program_counter >> 8);
|
||||
cpu_stack_push(program_counter & 0xff);
|
||||
|
||||
}
|
||||
// Updates the program counter to the address in the operand
|
||||
address addr = decode_operand_addr(addr_mode, NULL);
|
||||
cpu_get_registers()->program_counter = addr;
|
||||
|
||||
void op_NOP(addr_mode_t addr_mode) {
|
||||
cpu_add_cycles(6);
|
||||
}
|
||||
|
||||
void op_LAX(AddressingMode addr_mode) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
/* RMW */
|
||||
void op_ASL(addr_mode_t addr_mode) {
|
||||
void op_LDA(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte value = read_operand(operand);
|
||||
|
||||
cpu_get_registers()->accumulator = value;
|
||||
|
||||
set_acl_flags(value);
|
||||
cpu_add_cycles(get_cycle_count(operand, addr_mode));
|
||||
}
|
||||
|
||||
void op_ROL(addr_mode_t addr_mode) {
|
||||
void op_LDX(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte value = read_operand(operand);
|
||||
|
||||
cpu_get_registers()->x = value;
|
||||
|
||||
set_acl_flags(value);
|
||||
cpu_add_cycles(get_cycle_count(operand, addr_mode));
|
||||
}
|
||||
|
||||
void op_LSR(addr_mode_t addr_mode) {
|
||||
void op_LDY(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte value = read_operand(operand);
|
||||
|
||||
cpu_get_registers()->y = value;
|
||||
|
||||
set_acl_flags(value);
|
||||
cpu_add_cycles(get_cycle_count(operand, addr_mode));
|
||||
}
|
||||
|
||||
void op_ROR(addr_mode_t addr_mode) {
|
||||
void op_LSR(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte value = read_operand(operand);
|
||||
|
||||
}
|
||||
// Put bit 0 in the carry flag
|
||||
cpu_set_flag(value & 0x01, CPU_STATUS_CARRY_MASK);
|
||||
|
||||
void op_STX(addr_mode_t addr_mode) {
|
||||
value >>= 1;
|
||||
write_operand(operand, value);
|
||||
|
||||
set_acl_flags(value);
|
||||
cpu_add_cycles(get_shift_cycle_count(addr_mode));
|
||||
}
|
||||
|
||||
void op_TXA(addr_mode_t addr_mode) {
|
||||
|
||||
void op_NOP(AddressingMode addr_mode) {
|
||||
cpu_add_cycles(2);
|
||||
}
|
||||
|
||||
void op_TXS(addr_mode_t addr_mode) {
|
||||
void op_ORA(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte value = read_operand(operand);
|
||||
byte acc = cpu_get_registers()->accumulator;
|
||||
|
||||
acc |= value;
|
||||
cpu_get_registers()->accumulator = acc;
|
||||
|
||||
set_acl_flags(acc);
|
||||
cpu_add_cycles(get_cycle_count(operand, addr_mode));
|
||||
}
|
||||
|
||||
void op_SHX(addr_mode_t addr_mode) {
|
||||
void op_PHA(AddressingMode addr_mode) {
|
||||
byte acc = cpu_get_registers()->accumulator;
|
||||
cpu_stack_push(acc);
|
||||
|
||||
cpu_add_cycles(3);
|
||||
}
|
||||
|
||||
void op_LDX(addr_mode_t addr_mode) {
|
||||
void op_PHP(AddressingMode addr_mode) {
|
||||
byte status = cpu_get_registers()->status;
|
||||
cpu_stack_push(status);
|
||||
|
||||
cpu_add_cycles(3);
|
||||
}
|
||||
|
||||
void op_TAX(addr_mode_t addr_mode) {
|
||||
void op_PLA(AddressingMode addr_mode) {
|
||||
byte value = cpu_stack_pop();
|
||||
cpu_get_registers()->accumulator = value;
|
||||
|
||||
cpu_add_cycles(4);
|
||||
}
|
||||
|
||||
void op_TSX(addr_mode_t addr_mode) {
|
||||
void op_PLP(AddressingMode addr_mode) {
|
||||
byte value = cpu_stack_pop();
|
||||
cpu_get_registers()->status = value;
|
||||
|
||||
cpu_add_cycles(4);
|
||||
}
|
||||
|
||||
void op_DEC(addr_mode_t addr_mode) {
|
||||
|
||||
void op_RLA(AddressingMode addr_mode) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void op_DEX(addr_mode_t addr_mode) {
|
||||
void op_ROL(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte value = read_operand(operand);
|
||||
byte carry = cpu_get_flag(CPU_STATUS_CARRY_MASK);
|
||||
|
||||
cpu_set_flag(value & 0x80, CPU_STATUS_CARRY_MASK);
|
||||
value = (value << 1) | carry;
|
||||
write_operand(operand, value);
|
||||
|
||||
set_acl_flags(value);
|
||||
cpu_add_cycles(get_shift_cycle_count(addr_mode));
|
||||
}
|
||||
|
||||
void op_INC(addr_mode_t addr_mode) {
|
||||
void op_ROR(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte value = read_operand(operand);
|
||||
byte carry = cpu_get_flag(CPU_STATUS_CARRY_MASK);
|
||||
|
||||
cpu_set_flag(value & 0x01, CPU_STATUS_CARRY_MASK);
|
||||
value = (value >> 1) | (carry << 7);
|
||||
write_operand(operand, value);
|
||||
|
||||
set_acl_flags(value);
|
||||
cpu_add_cycles(get_shift_cycle_count(addr_mode));
|
||||
}
|
||||
|
||||
// Unofficial
|
||||
void op_STP(addr_mode_t addr_mode) {
|
||||
void op_RRA(AddressingMode addr_mode) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void op_RTI(AddressingMode addr_mode) {
|
||||
cpu_stack_pop_context();
|
||||
cpu_add_cycles(6);
|
||||
}
|
||||
|
||||
void op_SHY(addr_mode_t addr_mode) {
|
||||
void op_RTS(AddressingMode addr_mode) {
|
||||
byte lo = cpu_stack_pop();
|
||||
address pc = cpu_stack_pop() << 8;
|
||||
pc += lo;
|
||||
|
||||
cpu_get_registers()->program_counter = pc - 1;
|
||||
cpu_add_cycles(6);
|
||||
}
|
||||
|
||||
void op_SLO(addr_mode_t addr_mode) {
|
||||
|
||||
void op_SAX(AddressingMode addr_mode) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void op_RLA(addr_mode_t addr_mode) {
|
||||
void op_SBC(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte value = read_operand(operand);
|
||||
|
||||
}
|
||||
add_with_carry(~value);
|
||||
|
||||
void op_SRE(addr_mode_t addr_mode) {
|
||||
cpu_add_cycles(get_cycle_count(operand, addr_mode));
|
||||
}
|
||||
|
||||
void op_SEC(AddressingMode addr_mode) {
|
||||
cpu_set_flag(1, CPU_STATUS_CARRY_MASK);
|
||||
cpu_add_cycles(2);
|
||||
}
|
||||
|
||||
void op_RRA(addr_mode_t addr_mode) {
|
||||
void op_SED(AddressingMode addr_mode) {
|
||||
cpu_set_flag(1, CPU_STATUS_DECIMAL_MASK);
|
||||
cpu_add_cycles(2);
|
||||
}
|
||||
|
||||
void op_SEI(AddressingMode addr_mode) {
|
||||
cpu_set_flag(1, CPU_STATUS_INTERRUPT_DISABLE_MASK);
|
||||
cpu_add_cycles(2);
|
||||
}
|
||||
|
||||
void op_SAX(addr_mode_t addr_mode) {
|
||||
void op_SHX(AddressingMode addr_mode) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void op_SHY(AddressingMode addr_mode) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void op_LAX(addr_mode_t addr_mode) {
|
||||
void op_SLO(AddressingMode addr_mode) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void op_SRE(AddressingMode addr_mode) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void op_DCP(addr_mode_t addr_mode) {
|
||||
void op_STA(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte acc = cpu_get_registers()->accumulator;
|
||||
assert(operand.type == OPERAND_TYPE_ADDRESS);
|
||||
|
||||
cpu_push_byte(acc, operand.value);
|
||||
|
||||
operand.is_page_crossing = true;
|
||||
cpu_add_cycles(get_cycle_count(operand, addr_mode));
|
||||
}
|
||||
|
||||
void op_ISC(addr_mode_t addr_mode) {
|
||||
void op_STP(AddressingMode addr_mode) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void op_STX(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte x = cpu_get_registers()->x;
|
||||
assert(operand.type == OPERAND_TYPE_ADDRESS);
|
||||
|
||||
cpu_push_byte(x, operand.value);
|
||||
|
||||
cpu_add_cycles(get_cycle_count(operand, addr_mode));
|
||||
}
|
||||
|
||||
void op_ANC(addr_mode_t addr_mode) {
|
||||
void op_STY(AddressingMode addr_mode) {
|
||||
Operand operand = decode_operand(addr_mode);
|
||||
byte y = cpu_get_registers()->y;
|
||||
assert(operand.type == OPERAND_TYPE_ADDRESS);
|
||||
|
||||
cpu_push_byte(y, operand.value);
|
||||
|
||||
cpu_add_cycles(get_cycle_count(operand, addr_mode));
|
||||
}
|
||||
|
||||
void op_ALR(addr_mode_t addr_mode) {
|
||||
void op_TAS(AddressingMode addr_mode) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void op_TAX(AddressingMode addr_mode) {
|
||||
byte acc = cpu_get_registers()->accumulator;
|
||||
cpu_get_registers()->x = acc;
|
||||
|
||||
set_acl_flags(acc);
|
||||
cpu_add_cycles(2);
|
||||
}
|
||||
|
||||
void op_ARR(addr_mode_t addr_mode) {
|
||||
void op_TAY(AddressingMode addr_mode) {
|
||||
byte acc = cpu_get_registers()->accumulator;
|
||||
cpu_get_registers()->y = acc;
|
||||
|
||||
set_acl_flags(acc);
|
||||
cpu_add_cycles(2);
|
||||
}
|
||||
|
||||
void op_XAA(addr_mode_t addr_mode) {
|
||||
void op_TSX(AddressingMode addr_mode) {
|
||||
byte value = cpu_stack_pop();
|
||||
cpu_get_registers()->x = value;
|
||||
|
||||
set_acl_flags(value);
|
||||
cpu_add_cycles(2);
|
||||
}
|
||||
|
||||
void op_AXS(addr_mode_t addr_mode) {
|
||||
void op_TXA(AddressingMode addr_mode) {
|
||||
byte x = cpu_get_registers()->x;
|
||||
cpu_get_registers()->accumulator = x;
|
||||
|
||||
set_acl_flags(x);
|
||||
cpu_add_cycles(2);
|
||||
}
|
||||
|
||||
void op_AHX(addr_mode_t addr_mode) {
|
||||
void op_TXS(AddressingMode addr_mode) {
|
||||
byte x = cpu_get_registers()->x;
|
||||
cpu_stack_push(x);
|
||||
|
||||
cpu_add_cycles(2);
|
||||
}
|
||||
|
||||
void op_TAS(addr_mode_t addr_mode) {
|
||||
void op_TYA(AddressingMode addr_mode) {
|
||||
byte y = cpu_get_registers()->y;
|
||||
cpu_get_registers()->accumulator = y;
|
||||
|
||||
set_acl_flags(y);
|
||||
cpu_add_cycles(2);
|
||||
}
|
||||
|
||||
void op_XAA(AddressingMode addr_mode) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void process_op_code(int op) {
|
||||
|
@ -509,20 +960,20 @@ void process_op_code(int op) {
|
|||
IS_OP_CODE_MODE(AXS, 0xcb, IMMEDIATE)
|
||||
IS_OP_CODE_MODE(SBC, 0xeb, IMMEDIATE)
|
||||
|
||||
IS_OP_CODE_MODE(SAX, 0x83, INDEXED_INDIRECT)
|
||||
IS_OP_CODE_MODE(SAX, 0x83, INDIRECT_X)
|
||||
IS_OP_CODE_MODE(SAX, 0x87, ZERO_PAGE)
|
||||
IS_OP_CODE_MODE(XAA, 0x8b, IMMEDIATE)
|
||||
IS_OP_CODE_MODE(SAX, 0x8f, ABSOLUTE)
|
||||
IS_OP_CODE_MODE(AHX, 0x93, INDIRECT_INDEXED)
|
||||
IS_OP_CODE_MODE(AHX, 0x93, INDIRECT_Y)
|
||||
IS_OP_CODE_MODE(SAX, 0x97, ZERO_PAGE_INDEXED_Y)
|
||||
IS_OP_CODE_MODE(TAS, 0x9b, ABSOLUTE_INDEXED_Y)
|
||||
IS_OP_CODE_MODE(AHX, 0x9f, ABSOLUTE_INDEXED_Y)
|
||||
|
||||
IS_OP_CODE_MODE(LAX, 0xa3, INDEXED_INDIRECT)
|
||||
IS_OP_CODE_MODE(LAX, 0xa3, INDIRECT_X)
|
||||
IS_OP_CODE_MODE(LAX, 0xa7, ZERO_PAGE)
|
||||
IS_OP_CODE_MODE(LAX, 0xab, IMMEDIATE)
|
||||
IS_OP_CODE_MODE(LAX, 0xaf, ABSOLUTE)
|
||||
IS_OP_CODE_MODE(LAX, 0xb3, INDIRECT_INDEXED)
|
||||
IS_OP_CODE_MODE(LAX, 0xb3, INDIRECT_Y)
|
||||
IS_OP_CODE_MODE(LAX, 0xb7, ZERO_PAGE_INDEXED_Y)
|
||||
IS_OP_CODE_MODE(LAX, 0xbb, ABSOLUTE_INDEXED_Y)
|
||||
IS_OP_CODE_MODE(LAX, 0xbf, ABSOLUTE_INDEXED_Y)
|
||||
|
|
|
@ -22,13 +22,13 @@ typedef enum {
|
|||
ADDR_MODE_ACCUMULATOR, // A
|
||||
ADDR_MODE_IMMEDIATE, // #i
|
||||
ADDR_MODE_IMPLICIT, // Imp
|
||||
ADDR_MODE_INDEXED_INDIRECT, // (d,x)
|
||||
ADDR_MODE_INDIRECT_X, // (d,x)
|
||||
ADDR_MODE_INDIRECT_JUMP, //
|
||||
ADDR_MODE_INDIRECT_INDEXED, // (d),y
|
||||
ADDR_MODE_INDIRECT_Y, // (d),y
|
||||
ADDR_MODE_RELATIVE, // label
|
||||
ADDR_MODE_ZERO_PAGE, // d
|
||||
ADDR_MODE_ZERO_PAGE_INDEXED_X, // d,x
|
||||
ADDR_MODE_ZERO_PAGE_INDEXED_Y, // d,y
|
||||
} addr_mode_t;
|
||||
} AddressingMode;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// 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;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// Created by william on 30/09/23.
|
||||
//
|
||||
|
||||
#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 init_ram();
|
||||
void clean_ram();
|
||||
|
||||
void ram_set_byte(address addr, byte byte);
|
||||
byte ram_get_byte(address addr);
|
||||
word ram_get_word(address addr);
|
||||
|
||||
#endif //NESEMULATOR_RAM_H
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* =====================================================================================
|
||||
*
|
||||
* Filename: cpu.h
|
||||
*
|
||||
* Description: 6502 CPU emulator headers
|
||||
*
|
||||
* Version: 1.0
|
||||
* Created: 2023-09-21 10:12:33 PM
|
||||
* Revision: none
|
||||
* Compiler: gcc
|
||||
*
|
||||
* Author: William Nolin,
|
||||
* Organization:
|
||||
*
|
||||
* =====================================================================================
|
||||
*/
|
||||
|
||||
#ifndef NESEMULATOR_CPU_H
|
||||
#define NESEMULATOR_CPU_H
|
||||
|
||||
typedef unsigned char byte;
|
||||
typedef unsigned short address;
|
||||
typedef unsigned short word;
|
||||
|
||||
/**
|
||||
* @brief Set clock
|
||||
*/
|
||||
void cpu_step_to(int cycle);
|
||||
|
||||
void cpu_add_cycles(int count);
|
||||
|
||||
#endif
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* =====================================================================================
|
||||
*
|
||||
* Filename: cpu.h
|
||||
*
|
||||
* Description: 6502 CPU emulator headers
|
||||
*
|
||||
* Version: 1.0
|
||||
* Created: 2023-09-21 10:12:33 PM
|
||||
* Revision: none
|
||||
* Compiler: gcc
|
||||
*
|
||||
* Author: William Nolin,
|
||||
* Organization:
|
||||
*
|
||||
* =====================================================================================
|
||||
*/
|
||||
|
||||
#ifndef EMU_CPU_H
|
||||
#define EMU_CPU_H
|
||||
|
||||
// Reference: https://www.nesdev.org/wiki/Status_flags
|
||||
#define CPU_STATUS_CARRY_MASK = 0x01;
|
||||
#define CPU_STATUS_ZERO_MASK = 0x02;
|
||||
#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;
|
||||
|
||||
// Reference: https://www.nesdev.org/obelisk-6502-guide/registers.html
|
||||
typedef struct {
|
||||
unsigned short program_counter;
|
||||
unsigned char stack_pointer;
|
||||
unsigned char accumulator;
|
||||
unsigned char x;
|
||||
unsigned char y;
|
||||
unsigned char status;
|
||||
} cpu_registers_t;
|
||||
|
||||
/**
|
||||
* @brief Set clock
|
||||
*/
|
||||
void cpu_step_to(int cycle);
|
||||
|
||||
void cpu_add_cycles(int count);
|
||||
|
||||
cpu_registers_t* cpu_get_registers();
|
||||
|
||||
#endif
|
|
@ -1,8 +0,0 @@
|
|||
//
|
||||
// Created by william on 30/09/23.
|
||||
//
|
||||
|
||||
#ifndef NESEMULATOR_MEM_H
|
||||
#define NESEMULATOR_MEM_H
|
||||
|
||||
#endif //NESEMULATOR_MEM_H
|
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// Created by william on 10/15/23.
|
||||
//
|
||||
|
||||
#ifndef NESEMULATOR_MAPPER_H
|
||||
#define NESEMULATOR_MAPPER_H
|
||||
|
||||
#include "../include/cpu.h"
|
||||
|
||||
typedef struct {
|
||||
address (*redirect_addr)(unsigned short);
|
||||
} Mapper;
|
||||
|
||||
enum MapperType {
|
||||
MAPPER_TYPE_SIMPLE
|
||||
};
|
||||
|
||||
Mapper get_mapper(enum MapperType type);
|
||||
|
||||
#endif //NESEMULATOR_MAPPER_H
|
10
main.c
10
main.c
|
@ -18,12 +18,16 @@
|
|||
#include <log.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "include/cpu/cpu.h"
|
||||
#include "include/cpu.h"
|
||||
#include "ram.h"
|
||||
|
||||
int main() {
|
||||
cpu_step_to(0);
|
||||
init_ram();
|
||||
|
||||
ram_set_byte(0x10, 0xf);
|
||||
ram_get_byte(0xffff);
|
||||
|
||||
clean_ram();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
add_library(Mappers
|
||||
simple_mapper.c
|
||||
mappers.c)
|
||||
|
||||
find_package(log.c)
|
||||
target_link_libraries(Mappers log.c::log.c)
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// Created by william on 10/15/23.
|
||||
//
|
||||
|
||||
#include <log.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../include/mapper.h"
|
||||
#include "simple_mapper.c"
|
||||
|
||||
Mapper get_mapper(enum MapperType type) {
|
||||
switch (type) {
|
||||
case MAPPER_TYPE_SIMPLE:
|
||||
return get_simple_mapper();
|
||||
default:
|
||||
log_error("Unsupported mapper %u", type);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#include "../include/mapper.h"
|
||||
|
||||
address redirect_addr(address addr) {
|
||||
return addr;
|
||||
}
|
||||
|
||||
Mapper get_simple_mapper() {
|
||||
Mapper mapper;
|
||||
mapper.redirect_addr = &redirect_addr;
|
||||
return mapper;
|
||||
}
|
Loading…
Reference in New Issue