Skip to content

Commit

Permalink
Merge pull request #85 from matheusgomes28/add-jmp-support
Browse files Browse the repository at this point in the history
Adding support for JMP instructions
  • Loading branch information
matheusgomes28 authored Oct 27, 2024
2 parents c8ce648 + d3e6dac commit ce8e59e
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 14 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,8 +364,8 @@ Total number of branching instructions: `8`.

| **Opcode** | **Addressing Mode** | **Opcode (Hex)** | **Bytes** | **Cycles** | **Supported** |
|------------|---------------------|------------------|-----------|------------|--------------------|
| JMP | Absolute | 0x4C | 3 | 3 | :x: |
| JMP | Indirect | 0x6C | 3 | 5 | :x: |
| JMP | Absolute | 0x4C | 3 | 3 | :white_check_mark: |
| JMP | Indirect | 0x6C | 3 | 5 | :ok: |
| JSR | Absolute | 0x20 | 3 | 6 | :x: |
| RTS | Absolute | 0x60 | 1 | 6 | :x: |

Expand Down
71 changes: 59 additions & 12 deletions emulator/emulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,22 +196,32 @@ inline std::uint16_t absolute_indexed(
return static_cast<std::uint16_t>(target_address & 0xffff);
}

/// @brief This function aids with the fetching of an indirect
/// address.
/// @param cpu the cpu with the memory to operate on
/// @param val the 16-bit address that contains the memory to
/// fetch
/// @return the 16-bit address result of the indirect fetch
inline std::uint16_t indirect(emulator::Cpu& cpu, std::uint16_t val)
{
auto const lsb = cpu.mem[val];
auto const hsb_pos = static_cast<std::uint16_t>((val + 1) & 0xff);
auto const hsb = cpu.mem[hsb_pos];
auto const addr = static_cast<std::uint16_t>((hsb << 8) | lsb);
return addr;
}

/// @brief This function aids with the fetching of an indexed indirect
/// opcode mode.
/// @param cpu the cpu with the memory to operate on
/// @param val the zeropage memory to apply the index to
/// @return the target address
inline std::uint16_t indexed_indirect(emulator::Cpu& cpu, std::uint8_t val)
{
// (hsb << 8) + lsb convert little endian to the address
// index first then indirect
std::uint16_t const indexed_address = static_cast<std::uint16_t>(val + cpu.reg.x);
std::uint16_t const zp_addr = static_cast<std::uint16_t>(indexed_address & 0xff);

auto const lsb = cpu.mem[zp_addr];
auto const hsb_pos = static_cast<std::uint16_t>((zp_addr + 1) & 0xff);
auto const hsb = cpu.mem[hsb_pos];
auto const addr = static_cast<std::uint16_t>((hsb << 8) | lsb);
return addr;
return indirect(cpu, zp_addr);
}

/// @brief This function aids with the fetching of an indirect indexed
Expand All @@ -221,11 +231,9 @@ inline std::uint16_t indexed_indirect(emulator::Cpu& cpu, std::uint8_t val)
/// @return the target address
inline std::uint16_t indirect_indexed(emulator::Cpu& cpu, std::uint8_t val)
{
auto const lsb = cpu.mem[static_cast<std::uint8_t>(val)];
auto const hsb_addr = static_cast<std::uint16_t>((val + 1) & 0xff);
auto const hsb = cpu.mem[hsb_addr];
auto const addr = static_cast<std::uint16_t>(((hsb << 8) | lsb) + cpu.reg.y);
return addr;
// indirect first then index
auto const addr = indirect(cpu, val);
return static_cast<std::uint16_t>(addr + cpu.reg.y);
}

/* Functions with no context */
Expand Down Expand Up @@ -1254,6 +1262,39 @@ std::optional<InstructionConfig> cmp_indirect_indexed(emulator::Cpu& cpu, std::s
return std::make_optional<InstructionConfig>(2, 5 + cycle_add);
}

/* Begin jump instructions */
std::optional<InstructionConfig> jmp_abs(emulator::Cpu& cpu, std::span<const std::uint8_t> program)
{
ENABLE_PROFILER(cpu);
if ((cpu.reg.pc + 2) >= program.size())
{
return std::nullopt;
}

auto const lsb = program[cpu.reg.pc + 1];
auto const hsb = program[cpu.reg.pc + 2];
auto const addr = static_cast<std::uint16_t>((hsb << 8) | lsb);
cpu.reg.pc = addr;
return std::make_optional<InstructionConfig>(0, 3);
}

std::optional<InstructionConfig> jmp_indirect(emulator::Cpu& cpu, std::span<const std::uint8_t> program)
{
ENABLE_PROFILER(cpu);
if ((cpu.reg.pc + 2) >= program.size())
{
return std::nullopt;
}

auto const lsb = program[cpu.reg.pc + 1];
auto const hsb = program[cpu.reg.pc + 2];
auto const addr = static_cast<std::uint16_t>((hsb << 8) | lsb);
auto const indirect_addr = indirect(cpu, addr);
cpu.reg.pc = indirect_addr;
return std::make_optional<InstructionConfig>(0, 5);
}
/* End jump instructions */

// Branching functions here
std::optional<InstructionConfig> bne(emulator::Cpu& cpu, std::span<const std::uint8_t> program)
{
Expand Down Expand Up @@ -1683,6 +1724,11 @@ std::array<Instruction, 256> get_instructions()
supported_instructions[0xc1] = cmp_indexed_indirect;
supported_instructions[0xd1] = cmp_indirect_indexed;

// Jump opcodes
supported_instructions[0x4c] = jmp_abs;
supported_instructions[0x6c] = jmp_indirect;

// Branching opcodes
supported_instructions[0xf0] = branch_flag_value<true>(&emulator::Flags::z);
supported_instructions[0xd0] = branch_flag_value<false>(&emulator::Flags::z);
supported_instructions[0x30] = branch_flag_value<true>(&emulator::Flags::n);
Expand All @@ -1692,6 +1738,7 @@ std::array<Instruction, 256> get_instructions()
supported_instructions[0x70] = branch_flag_value<true>(&emulator::Flags::v);
supported_instructions[0x50] = branch_flag_value<false>(&emulator::Flags::v);

// INC opcodes
supported_instructions[0xe6] = inc_zeropage;
supported_instructions[0xf6] = inc_zeropage_plus_x;
supported_instructions[0xee] = inc_absolute;
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ create_tests(eor_zeropage_indexed_tests)
create_tests(eor_zeropage_tests)
create_tests(flags_tests)
create_tests(increment_tests)
create_tests(jmp_tests)
create_tests(ld_absolute_indexed_tests)
create_tests(ld_absolute_tests)
create_tests(ld_immediate_tests)
Expand Down
34 changes: 34 additions & 0 deletions tests/jmp_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import emulator;

#include "common.h"

#include <gtest/gtest.h>

#include <array>
#include <cstdint>

// NOLINTNEXTLINE
TEST(JMPTests, AbsoluteTests)
{
std::array<std::uint8_t, 0xffff> program{};

emulator::Cpu cpu;

// 0x0000 -> 0x5554 (3 cycles)
program[0x0000] = 0x4c;
program[0x0001] = 0x54;
program[0x0002] = 0x55;

// 0x5554 -> 0xfffe (3 cycles)
program[0x5554] = 0x4c;
program[0x5555] = 0xfe;
program[0x5556] = 0xff;

// noop to end the program
// 0xfffe noop (2 cycles)
program[0xfffe] = 0xea;


EXPECT_EQ(emulator::execute(cpu, program), 8);
EXPECT_EQ(cpu.reg.pc, 0xffff);
}

0 comments on commit ce8e59e

Please sign in to comment.