srctree

Johan parent 9a7ba84b 81b11a44 42b7142d
Merge pull request #274 from evilcorpltd/disassembler

Add lib for disassembler operations
.bazelignore added: 542, removed: 201, total 341
@@ -1,4 +1,5 @@
.git/
build/
buildclang/
buildclang-cl/
out/
 
CMakeLists.txt added: 542, removed: 201, total 341
@@ -2,14 +2,15 @@ cmake_minimum_required(VERSION 3.14)
 
project(n_e_s)
 
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
include(sanitizers)
include(cmake/sanitizers.cmake)
 
enable_testing()
 
add_subdirectory(third_party)
add_subdirectory(warnings)
 
add_subdirectory(application)
add_subdirectory(core)
add_subdirectory(disassembler)
add_subdirectory(nes)
add_subdirectory(nestest)
 
core/BUILD.bazel added: 542, removed: 201, total 341
@@ -11,6 +11,19 @@ cc_library(
visibility = ["//visibility:public"],
)
 
cc_library(
name = "core_test_utils",
hdrs = glob([
"test_utils/include/**/*.h",
]),
strip_include_prefix = "test_utils/include/",
visibility = ["//visibility:public"],
deps = [
":core",
"@gtest",
],
)
 
cc_test(
name = "core_test",
size = "small",
@@ -20,6 +33,7 @@ cc_test(
]),
deps = [
":core",
":core_test_utils",
"@gtest",
],
)
 
core/CMakeLists.txt added: 542, removed: 201, total 341
@@ -61,4 +61,5 @@ target_link_libraries(${PROJECT_NAME}
n_e_s::warnings
)
 
add_subdirectory(test_utils)
add_subdirectory(test)
 
core/test/CMakeLists.txt added: 542, removed: 201, total 341
@@ -1,29 +1,14 @@
include(FetchContent)
include(GoogleTest)
 
project(test_n_e_s_core)
 
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG e3f0319d89f4cbf32993de595d984183b1a9fc57 # 1.10 + build fix for clang on Windows
)
 
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
 
add_executable(${PROJECT_NAME}
src/cast_helpers.h
src/cpu_test.h
src/fake_mmu.h
src/fake_ppu.h
src/hexprinter.h
src/icpu_helpers.cpp
src/icpu_helpers.h
src/main.cpp
src/mock_membank.h
src/mock_mmu.h
src/mock_ppu.h
src/opcode.h
src/test_cpu.cpp
src/test_cpu_absolute_indexed_instructions.cpp
@@ -62,6 +47,7 @@ set_target_properties(${PROJECT_NAME}
target_link_libraries(${PROJECT_NAME}
PRIVATE
n_e_s::core
n_e_s::core::test_utils
n_e_s::warnings
gmock
)
 
core/test/src/cpu_test.h added: 542, removed: 201, total 341
@@ -1,7 +1,7 @@
#pragma once
 
#include "fake_ppu.h"
#include "mock_mmu.h"
#include "nes/core/test/fake_ppu.h"
#include "nes/core/test/mock_mmu.h"
 
#include "nes/core/cpu_factory.h"
#include "nes/core/imos6502.h"
 
core/test/src/test_cpuintegration.cpp added: 542, removed: 201, total 341
@@ -1,8 +1,8 @@
#include "nes/core/cpu_factory.h"
 
#include "fake_mmu.h"
#include "fake_ppu.h"
#include "icpu_helpers.h"
#include "nes/core/test/fake_mmu.h"
#include "nes/core/test/fake_ppu.h"
 
#include <gtest/gtest.h>
 
 
core/test/src/test_mmu.cpp added: 542, removed: 201, total 341
@@ -2,7 +2,7 @@
#include "nes/core/membank_factory.h"
#include "nes/core/mmu_factory.h"
 
#include "mock_ppu.h"
#include "nes/core/test/mock_ppu.h"
 
#include <gtest/gtest.h>
 
 
core/test/src/test_ppu.cpp added: 542, removed: 201, total 341
@@ -1,7 +1,7 @@
#include "nes/core/invalid_address.h"
#include "nes/core/ppu_factory.h"
 
#include "mock_mmu.h"
#include "nes/core/test/mock_mmu.h"
 
#include <gtest/gtest.h>
 
 
filename was Deleted added: 542, removed: 201, total 341
@@ -0,0 +1,24 @@
include(GoogleTest)
 
project(n_e_s_core_test_utils)
 
add_library(${PROJECT_NAME} INTERFACE)
add_library(n_e_s::core::test_utils ALIAS ${PROJECT_NAME})
 
target_include_directories(${PROJECT_NAME}
INTERFACE
include
)
 
set_target_properties(${PROJECT_NAME}
PROPERTIES
INTERFACE_COMPILE_FEATURES cxx_std_17
)
 
target_link_libraries(${PROJECT_NAME}
INTERFACE
n_e_s::core
n_e_s::warnings
gmock
)
 
 
filename was Deleted added: 542, removed: 201, total 341
 
filename was Deleted added: 542, removed: 201, total 341
 
filename was Deleted added: 542, removed: 201, total 341
 
filename was Deleted added: 542, removed: 201, total 341
 
filename was Deleted added: 542, removed: 201, total 341
 
filename was Deleted added: 542, removed: 201, total 341
@@ -0,0 +1,32 @@
cc_library(
name = "disassembler",
srcs = glob([
"src/**",
]),
hdrs = glob([
"include/**/*.h",
]),
copts = ["-Idisassembler/src"],
strip_include_prefix = "include/",
visibility = ["//visibility:public"],
deps = [
"//core",
"@fmtlib",
],
)
 
cc_test(
name = "disassembler_test",
size = "small",
srcs = glob([
"test/src/*.cpp",
"test/src/*.h",
]),
deps = [
":disassembler",
"//core",
"//core:core_test_utils",
"@gtest",
],
)
 
 
filename was Deleted added: 542, removed: 201, total 341
@@ -0,0 +1,36 @@
project(n_e_s_disassembler)
 
add_library(${PROJECT_NAME}
include/nes/disassembler.h
src/disassembler.cpp
)
add_library(n_e_s::disassembler ALIAS ${PROJECT_NAME})
 
target_compile_features(${PROJECT_NAME}
PRIVATE
cxx_std_17
)
 
target_link_libraries(${PROJECT_NAME}
PUBLIC
n_e_s::core
PRIVATE
fmt
n_e_s::warnings
)
 
target_include_directories(${PROJECT_NAME}
PUBLIC
include
PRIVATE
src
)
 
set_target_properties(${PROJECT_NAME}
PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)
 
add_subdirectory(test)
 
filename was Deleted added: 542, removed: 201, total 341
@@ -0,0 +1,40 @@
#pragma once
 
#include "nes/core/immu.h"
#include "nes/core/imos6502.h"
#include "nes/core/opcode.h"
 
namespace n_e_s::dis {
 
constexpr int get_arg_count(n_e_s::core::AddressMode mode) {
switch (mode) {
case n_e_s::core::AddressMode::Implied:
case n_e_s::core::AddressMode::Accumulator:
return 0;
case n_e_s::core::AddressMode::Immediate:
case n_e_s::core::AddressMode::Zeropage:
case n_e_s::core::AddressMode::ZeropageX:
case n_e_s::core::AddressMode::ZeropageY:
case n_e_s::core::AddressMode::Relative:
case n_e_s::core::AddressMode::IndexedIndirect:
case n_e_s::core::AddressMode::IndirectIndexed:
return 1;
case n_e_s::core::AddressMode::Absolute:
case n_e_s::core::AddressMode::AbsoluteX:
case n_e_s::core::AddressMode::AbsoluteY:
case n_e_s::core::AddressMode::Indirect:
return 2;
}
return 0;
}
 
std::string disassemble(const uint16_t address,
const n_e_s::core::IMmu &mmu,
const n_e_s::core::CpuRegisters &reg);
 
std::string get_memory_string(const n_e_s::core::Opcode &opcode,
const uint16_t address,
const n_e_s::core::IMmu &mmu,
const n_e_s::core::CpuRegisters &reg);
 
} // namespace n_e_s::dis
 
filename was Deleted added: 542, removed: 201, total 341
@@ -0,0 +1,141 @@
#include "nes/disassembler.h"
 
#include "nes/core/immu.h"
#include "nes/core/imos6502.h"
#include "nes/core/opcode.h"
 
#include <fmt/format.h>
#include <string>
 
namespace n_e_s::dis {
 
namespace {
 
constexpr bool is_negative(uint8_t byte) {
return (byte & (1u << 7u)) != 0;
}
 
constexpr uint8_t low_bits(uint8_t byte) {
return static_cast<uint8_t>(
byte & static_cast<uint8_t>(~static_cast<uint8_t>(1u << 7u)));
}
 
constexpr int8_t to_signed(uint8_t byte) {
if (is_negative(byte)) {
return low_bits(byte) - static_cast<uint8_t>(128);
}
return low_bits(byte);
}
 
} // namespace
 
std::string disassemble(const uint16_t address,
const n_e_s::core::IMmu &mmu,
const n_e_s::core::CpuRegisters &reg) {
const auto opcode = n_e_s::core::decode(mmu.read_byte(address));
 
const auto family = n_e_s::core::to_string(opcode.family);
const std::string mem_string =
n_e_s::dis::get_memory_string(opcode, address, mmu, reg);
 
if (mem_string.empty()) {
return std::string(family);
}
return fmt::format("{} {}", family, mem_string);
}
 
std::string get_memory_string(const n_e_s::core::Opcode &opcode,
const uint16_t address,
const n_e_s::core::IMmu &mmu,
const n_e_s::core::CpuRegisters &reg) {
const uint8_t op1 = mmu.read_byte(address + 1u);
const uint8_t op2 = mmu.read_byte(address + 2u);
 
switch (opcode.address_mode) {
case n_e_s::core::AddressMode::Implied:
break;
case n_e_s::core::AddressMode::Immediate:
return fmt::format("#${:02X}", op1);
case n_e_s::core::AddressMode::Zeropage:
return fmt::format("${:02X} = {:02X}", op1, mmu.read_byte(op1));
case n_e_s::core::AddressMode::ZeropageX: {
const uint8_t effective_addr = op1 + reg.x;
return fmt::format("${:02X},X @ {:02X} = {:02X}",
op1,
effective_addr,
mmu.read_byte(effective_addr));
}
case n_e_s::core::AddressMode::ZeropageY: {
const uint8_t effective_addr = op1 + reg.y;
return fmt::format("${:02X},Y @ {:02X} = {:02X}",
op1,
effective_addr,
mmu.read_byte(effective_addr));
}
case n_e_s::core::AddressMode::Relative:
return fmt::format("${:04X}", to_signed(op1) + address + 2);
case n_e_s::core::AddressMode::Accumulator:
return "A";
case n_e_s::core::AddressMode::IndexedIndirect: {
const uint8_t midaddr = op1 + reg.x;
const uint16_t effective_addr =
mmu.read_byte(midaddr) +
(mmu.read_byte(static_cast<uint8_t>(midaddr + 1u)) << 8u);
return fmt::format("(${:02X},X) @ {:02X} = {:04X} = {:02X}",
op1,
midaddr,
effective_addr,
mmu.read_byte(effective_addr));
} break;
case n_e_s::core::AddressMode::IndirectIndexed: {
const uint8_t op1_plus_one =
mmu.read_byte(static_cast<uint8_t>(op1 + 1u));
const uint16_t midaddr =
mmu.read_byte(op1) | static_cast<uint16_t>(op1_plus_one << 8u);
const uint16_t effective_addr = midaddr + reg.y;
return fmt::format("(${:02X}),Y = {:04X} @ {:04X} = {:02X}",
op1,
midaddr,
effective_addr,
mmu.read_byte(effective_addr));
} break;
case n_e_s::core::AddressMode::Absolute:
if (opcode.family != n_e_s::core::Family::JSR &&
opcode.family != n_e_s::core::Family::JMP) {
const uint8_t resulting_addr =
mmu.read_byte(static_cast<uint16_t>(op2 << 8u) | op1);
return fmt::format(
"${:02X}{:02X} = {:02X}", op2, op1, resulting_addr);
} else {
return fmt::format("${:02X}{:02X}", op2, op1);
}
case n_e_s::core::AddressMode::AbsoluteX: {
const uint16_t midaddr = op1 | static_cast<uint16_t>(op2 << 8u);
const uint16_t effective_addr = midaddr + reg.x;
return fmt::format("${:04X},X @ {:04X} = {:02X}",
midaddr,
effective_addr,
mmu.read_byte(effective_addr));
}
case n_e_s::core::AddressMode::AbsoluteY: {
const uint16_t midaddr = op1 | static_cast<uint16_t>(op2 << 8u);
const uint16_t effective_addr = midaddr + reg.y;
return fmt::format("${:04X},Y @ {:04X} = {:02X}",
midaddr,
effective_addr,
mmu.read_byte(effective_addr));
}
case n_e_s::core::AddressMode::Indirect:
const uint16_t effective_addr1 = static_cast<uint16_t>(op2 << 8u) | op1;
const uint16_t effective_addr2 = static_cast<uint16_t>(op2 << 8u) |
static_cast<uint8_t>(op1 + 1u);
return fmt::format("(${:02X}{:02X}) = {:02X}{:02X}",
op2,
op1,
mmu.read_byte(effective_addr2),
mmu.read_byte(effective_addr1));
}
return "";
}
 
} // namespace n_e_s::dis
 
filename was Deleted added: 542, removed: 201, total 341
@@ -0,0 +1,32 @@
include(GoogleTest)
 
project(test_n_e_s_disassembler)
 
add_executable(${PROJECT_NAME}
src/main.cpp
src/test_disassembler.cpp
)
 
target_compile_features(${PROJECT_NAME}
PRIVATE
cxx_std_17
)
 
set_target_properties(${PROJECT_NAME}
PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)
 
target_link_libraries(${PROJECT_NAME}
PRIVATE
n_e_s::core
n_e_s::core::test_utils
n_e_s::disassembler
n_e_s::warnings
gmock
)
 
gtest_discover_tests(${PROJECT_NAME})
 
 
filename was Deleted added: 542, removed: 201, total 341
@@ -0,0 +1,7 @@
#include <gtest/gtest.h>
 
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
 
 
filename was Deleted added: 542, removed: 201, total 341
@@ -0,0 +1,149 @@
#include "nes/core/imos6502.h"
#include "nes/core/opcode.h"
#include "nes/disassembler.h"
 
#include "nes/core/test/fake_mmu.h"
 
#include <gtest/gtest.h>
 
using namespace n_e_s::dis;
 
namespace {
 
class DisassemblerFixture : public testing::Test {
public:
n_e_s::core::test::FakeMmu mmu;
n_e_s::core::CpuRegisters reg;
 
void load_hex_dump(uint16_t address, const std::vector<uint8_t> &data) {
for (auto d : data) {
mmu.write_byte(address++, d);
}
}
};
 
TEST(get_arg_count, returns_correct_count) {
EXPECT_EQ(0, get_arg_count(n_e_s::core::AddressMode::Implied));
EXPECT_EQ(0, get_arg_count(n_e_s::core::AddressMode::Accumulator));
EXPECT_EQ(1, get_arg_count(n_e_s::core::AddressMode::Immediate));
EXPECT_EQ(1, get_arg_count(n_e_s::core::AddressMode::Zeropage));
EXPECT_EQ(1, get_arg_count(n_e_s::core::AddressMode::ZeropageX));
EXPECT_EQ(1, get_arg_count(n_e_s::core::AddressMode::ZeropageY));
EXPECT_EQ(1, get_arg_count(n_e_s::core::AddressMode::Relative));
EXPECT_EQ(1, get_arg_count(n_e_s::core::AddressMode::IndexedIndirect));
EXPECT_EQ(1, get_arg_count(n_e_s::core::AddressMode::IndirectIndexed));
EXPECT_EQ(2, get_arg_count(n_e_s::core::AddressMode::Absolute));
EXPECT_EQ(2, get_arg_count(n_e_s::core::AddressMode::AbsoluteX));
EXPECT_EQ(2, get_arg_count(n_e_s::core::AddressMode::AbsoluteY));
EXPECT_EQ(2, get_arg_count(n_e_s::core::AddressMode::Indirect));
}
 
// No args
TEST_F(DisassemblerFixture, disassemble_implied) {
load_hex_dump(0x1234, {n_e_s::core::PhpImplied, 0x00, 0x01});
const std::string res = disassemble(0x1234, mmu, reg);
EXPECT_EQ("PHP", res);
}
 
TEST_F(DisassemblerFixture, disassemble_accumulator) {
load_hex_dump(0x1234, {n_e_s::core::AslAccumulator, 0x00, 0x01});
const std::string res = disassemble(0x1234, mmu, reg);
EXPECT_EQ("ASL A", res);
}
 
// 1 arg
TEST_F(DisassemblerFixture, disassemble_immediate) {
load_hex_dump(0x1234, {n_e_s::core::AndImmediate, 0xAB, 0x01});
const std::string res = disassemble(0x1234, mmu, reg);
EXPECT_EQ("AND #$AB", res);
}
 
TEST_F(DisassemblerFixture, disassemble_zeropage) {
load_hex_dump(0x1234, {n_e_s::core::BitZeropage, 0xAB, 0x01});
load_hex_dump(0xAB, {0x42});
const std::string res = disassemble(0x1234, mmu, reg);
EXPECT_EQ("BIT $AB = 42", res);
}
 
TEST_F(DisassemblerFixture, disassemble_zeropagex) {
reg.x = 0x02;
load_hex_dump(0x1234, {n_e_s::core::SbcZeropageX, 0xAB, 0x01});
load_hex_dump(0xAD, {0x42});
const std::string res = disassemble(0x1234, mmu, reg);
EXPECT_EQ("SBC $AB,X @ AD = 42", res);
}
 
TEST_F(DisassemblerFixture, disassemble_zeropagey) {
reg.y = 0x02;
load_hex_dump(0x1234, {n_e_s::core::LaxZeropageY, 0xAB, 0x01});
load_hex_dump(0xAD, {0x42});
const std::string res = disassemble(0x1234, mmu, reg);
EXPECT_EQ("LAX $AB,Y @ AD = 42", res);
}
 
TEST_F(DisassemblerFixture, disassemble_relative) {
load_hex_dump(0x1234, {n_e_s::core::BccRelative, 0x12, 0x01});
const std::string res = disassemble(0x1234, mmu, reg);
EXPECT_EQ("BCC $1248", res);
}
 
TEST_F(DisassemblerFixture, disassemble_indexed_indirect) {
reg.x = 0x02;
load_hex_dump(0x1234, {n_e_s::core::AdcIndirectX, 0x12, 0x01});
load_hex_dump(0x12 + 0x02, {0x60});
load_hex_dump(0x12 + 0x02 + 0x01, {0xCD});
load_hex_dump(0xCD60, {0x42});
const std::string res = disassemble(0x1234, mmu, reg);
EXPECT_EQ("ADC ($12,X) @ 14 = CD60 = 42", res);
}
 
TEST_F(DisassemblerFixture, disassemble_indirect_indexed) {
reg.y = 0x02;
load_hex_dump(0x1234, {n_e_s::core::CmpIndirectY, 0x12, 0x01});
load_hex_dump(0x12 + 0x01, {0x60});
load_hex_dump(0x12, {0xCD});
load_hex_dump(0x60CD, {0xAB});
load_hex_dump(0x60CD + 0x02, {0x42});
const std::string res = disassemble(0x1234, mmu, reg);
EXPECT_EQ("CMP ($12),Y = 60CD @ 60CF = 42", res);
}
 
// 2 args
TEST_F(DisassemblerFixture, disassemble_absolute) {
load_hex_dump(0x1234, {n_e_s::core::AslAbsolute, 0x78, 0x56});
load_hex_dump(0x5678, {0x42});
const std::string res = disassemble(0x1234, mmu, reg);
EXPECT_EQ("ASL $5678 = 42", res);
}
 
TEST_F(DisassemblerFixture, disassemble_absolute_jmp) {
load_hex_dump(0x1234, {n_e_s::core::JmpAbsolute, 0x78, 0x56});
const std::string res = disassemble(0x1234, mmu, reg);
EXPECT_EQ("JMP $5678", res);
}
 
TEST_F(DisassemblerFixture, disassemble_absolutex) {
reg.x = 0x03;
load_hex_dump(0x1234, {n_e_s::core::LdaAbsoluteX, 0x78, 0x56});
load_hex_dump(0x5678 + 0x03, {0x42});
const std::string res = disassemble(0x1234, mmu, reg);
EXPECT_EQ("LDA $5678,X @ 567B = 42", res);
}
 
TEST_F(DisassemblerFixture, disassemble_absolutey) {
reg.y = 0x03;
load_hex_dump(0x1234, {n_e_s::core::LdxAbsoluteY, 0x78, 0x56});
load_hex_dump(0x5678 + 0x03, {0x42});
const std::string res = disassemble(0x1234, mmu, reg);
EXPECT_EQ("LDX $5678,Y @ 567B = 42", res);
}
 
TEST_F(DisassemblerFixture, disassemble_indirect) {
load_hex_dump(0x1234, {n_e_s::core::JmpIndirect, 0x78, 0x56});
load_hex_dump(0x5678, {0x42});
load_hex_dump(0x5679, {0x43});
const std::string res = disassemble(0x1234, mmu, reg);
EXPECT_EQ("JMP ($5678) = 4342", res);
}
 
} // namespace
 
nes/test/CMakeLists.txt added: 542, removed: 201, total 341
@@ -1,17 +1,7 @@
include(FetchContent)
include(GoogleTest)
 
project(test_n_e_s_nes)
 
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG e3f0319d89f4cbf32993de595d984183b1a9fc57 # 1.10 + build fix for clang on Windows
)
 
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
 
add_executable(${PROJECT_NAME}
src/main.cpp
src/test_nes.cpp
 
nestest/BUILD.bazel added: 542, removed: 201, total 341
@@ -4,6 +4,7 @@ cc_binary(
deps = [
"//core",
"//nes",
"//disassembler",
"@fmtlib",
],
)
 
nestest/CMakeLists.txt added: 542, removed: 201, total 341
@@ -1,15 +1,5 @@
include(FetchContent)
 
project(nestest)
 
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt
GIT_TAG 567ed03f88d493b51e5efc24f9f4cad47dc5c76d # 6.2.0 with build fix for -Wshadow
)
 
FetchContent_MakeAvailable(fmt)
 
add_executable(${PROJECT_NAME}
src/main.cpp
)
@@ -18,6 +8,7 @@ target_link_libraries(${PROJECT_NAME}
PRIVATE
n_e_s::nes
n_e_s::core
n_e_s::disassembler
n_e_s::warnings
fmt
)
 
nestest/src/main.cpp added: 542, removed: 201, total 341
@@ -8,26 +8,11 @@
#include "nes/core/imos6502.h"
#include "nes/core/ippu.h"
#include "nes/core/opcode.h"
#include "nes/disassembler.h"
#include "nes/nes.h"
 
namespace {
 
constexpr bool is_negative(uint8_t byte) {
return (byte & (1u << 7u)) != 0;
}
 
constexpr uint8_t low_bits(uint8_t byte) {
return static_cast<uint8_t>(
byte & static_cast<uint8_t>(~static_cast<uint8_t>(1u << 7u)));
}
 
constexpr int8_t to_signed(uint8_t byte) {
if (is_negative(byte)) {
return low_bits(byte) - static_cast<uint8_t>(128);
}
return low_bits(byte);
}
 
constexpr bool is_undocumented(const n_e_s::core::Opcode &opcode) {
if (opcode.family == n_e_s::core::Family::NOP &&
opcode.instruction != n_e_s::core::Instruction::NopImplied) {
@@ -48,120 +33,6 @@ constexpr bool is_undocumented(const n_e_s::core::Opcode &opcode) {
return false;
}
 
constexpr int get_arg_count(n_e_s::core::AddressMode mode) {
switch (mode) {
case n_e_s::core::AddressMode::Implied:
case n_e_s::core::AddressMode::Accumulator:
return 0;
case n_e_s::core::AddressMode::Immediate:
case n_e_s::core::AddressMode::Zeropage:
case n_e_s::core::AddressMode::ZeropageX:
case n_e_s::core::AddressMode::ZeropageY:
case n_e_s::core::AddressMode::Relative:
case n_e_s::core::AddressMode::IndexedIndirect:
case n_e_s::core::AddressMode::IndirectIndexed:
return 1;
case n_e_s::core::AddressMode::Absolute:
case n_e_s::core::AddressMode::AbsoluteX:
case n_e_s::core::AddressMode::AbsoluteY:
case n_e_s::core::AddressMode::Indirect:
return 2;
}
return 0;
}
 
std::string get_memory_string(const n_e_s::core::Opcode &opcode,
const uint8_t op1,
const uint8_t op2,
const n_e_s::nes::Nes &nes) {
switch (opcode.address_mode) {
case n_e_s::core::AddressMode::Implied:
break;
case n_e_s::core::AddressMode::Immediate:
return fmt::format("#${:02X}", op1);
case n_e_s::core::AddressMode::Zeropage:
return fmt::format("${:02X} = {:02X}", op1, nes.mmu().read_byte(op1));
case n_e_s::core::AddressMode::ZeropageX: {
const uint8_t effective_addr = op1 + nes.cpu_registers().x;
return fmt::format("${:02X},X @ {:02X} = {:02X}",
op1,
effective_addr,
nes.mmu().read_byte(effective_addr));
}
case n_e_s::core::AddressMode::ZeropageY: {
const uint8_t effective_addr = op1 + nes.cpu_registers().y;
return fmt::format("${:02X},Y @ {:02X} = {:02X}",
op1,
effective_addr,
nes.mmu().read_byte(effective_addr));
}
case n_e_s::core::AddressMode::Relative:
return fmt::format(
"${:04X}", to_signed(op1) + nes.cpu().state().start_pc + 2);
case n_e_s::core::AddressMode::Accumulator:
return "A";
case n_e_s::core::AddressMode::IndexedIndirect: {
const uint8_t midaddr = op1 + nes.cpu_registers().x;
const uint16_t effective_addr =
nes.mmu().read_byte(midaddr) +
(nes.mmu().read_byte(static_cast<uint8_t>(midaddr + 1u)) << 8u);
return fmt::format("(${:02X},X) @ {:02X} = {:04X} = {:02X}",
op1,
midaddr,
effective_addr,
nes.mmu().read_byte(effective_addr));
} break;
case n_e_s::core::AddressMode::IndirectIndexed: {
const uint8_t op1_plus_one =
nes.mmu().read_byte(static_cast<uint8_t>(op1 + 1u));
const uint16_t midaddr = nes.mmu().read_byte(op1) |
static_cast<uint16_t>(op1_plus_one << 8u);
const uint16_t effective_addr = midaddr + nes.cpu_registers().y;
return fmt::format("(${:02X}),Y = {:04X} @ {:04X} = {:02X}",
op1,
midaddr,
effective_addr,
nes.mmu().read_byte(effective_addr));
} break;
case n_e_s::core::AddressMode::Absolute:
if (opcode.family != n_e_s::core::Family::JSR &&
opcode.family != n_e_s::core::Family::JMP) {
const uint8_t resulting_addr =
nes.mmu().read_byte(static_cast<uint16_t>(op2 << 8u) | op1);
return fmt::format(
"${:02X}{:02X} = {:02X}", op2, op1, resulting_addr);
} else {
return fmt::format("${:02X}{:02X}", op2, op1);
}
case n_e_s::core::AddressMode::AbsoluteX: {
const uint16_t midaddr = op1 | static_cast<uint16_t>(op2 << 8u);
const uint16_t effective_addr = midaddr + nes.cpu_registers().x;
return fmt::format("${:04X},X @ {:04X} = {:02X}",
midaddr,
effective_addr,
nes.mmu().read_byte(effective_addr));
}
case n_e_s::core::AddressMode::AbsoluteY: {
const uint16_t midaddr = op1 | static_cast<uint16_t>(op2 << 8u);
const uint16_t effective_addr = midaddr + nes.cpu_registers().y;
return fmt::format("${:04X},Y @ {:04X} = {:02X}",
midaddr,
effective_addr,
nes.mmu().read_byte(effective_addr));
}
case n_e_s::core::AddressMode::Indirect:
const uint16_t effective_addr1 = static_cast<uint16_t>(op2 << 8u) | op1;
const uint16_t effective_addr2 = static_cast<uint16_t>(op2 << 8u) |
static_cast<uint8_t>(op1 + 1u);
return fmt::format("(${:02X}{:02X}) = {:02X}{:02X}",
op2,
op1,
nes.mmu().read_byte(effective_addr2),
nes.mmu().read_byte(effective_addr1));
}
return "";
}
 
std::string get_reg_string(const n_e_s::core::CpuRegisters &reg) {
return fmt::format("A:{:02X} X:{:02X} Y:{:02X} P:{:02X} SP:{:02X}",
reg.a,
@@ -181,7 +52,8 @@ std::string get_opcode_string(const n_e_s::nes::Nes &nes) {
std::string result =
fmt::format("{:04X} {:02X}", state.start_pc, raw_opcode);
 
const int arg_count = get_arg_count(state.current_opcode->address_mode);
const int arg_count =
n_e_s::dis::get_arg_count(state.current_opcode->address_mode);
if (arg_count >= 1) {
result += fmt::format(" {:02X}", op1);
if (arg_count >= 2) {
@@ -203,10 +75,11 @@ std::string get_execution_string(const n_e_s::nes::Nes &nes) {
const n_e_s::core::IMos6502 &cpu = nes.cpu();
const auto state = cpu.state();
const std::string opcode_string = get_opcode_string(nes);
const std::uint8_t op1 = nes.mmu().read_byte(state.start_pc + 1u);
const std::uint8_t op2 = nes.mmu().read_byte(state.start_pc + 2u);
const std::string memory_string =
get_memory_string(*state.current_opcode, op1, op2, nes);
n_e_s::dis::get_memory_string(*state.current_opcode,
state.start_pc,
nes.mmu(),
nes.cpu_registers());
 
const std::string reg_string = get_reg_string(nes.cpu_registers());
 
 
filename was Deleted added: 542, removed: 201, total 341
@@ -0,0 +1,2 @@
add_subdirectory(fmt)
add_subdirectory(gtest)
 
filename was Deleted added: 542, removed: 201, total 341
@@ -0,0 +1,9 @@
include(FetchContent)
 
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt
GIT_TAG 567ed03f88d493b51e5efc24f9f4cad47dc5c76d # 6.2.0 with build fix for -Wshadow
)
 
FetchContent_MakeAvailable(fmt)
 
filename was Deleted added: 542, removed: 201, total 341
@@ -0,0 +1,11 @@
include(FetchContent)
 
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG e3f0319d89f4cbf32993de595d984183b1a9fc57 # 1.10 + build fix for clang on Windows
)
 
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)