srctree

Johan parent 137f6ad8 39397082 31c39a62
Merge pull request #133 from johnor/inc-zeropage-indexed

Add INC for indexed zeropage addressing mode
core/include/core/opcode.h added: 187, removed: 49, total 138
@@ -149,7 +149,7 @@ enum Instruction : uint8_t {
BeqRelative = 0xF0,
// SbcIndirectY = 0xF1,
// SbcZeropageX = 0xF5,
// IncZeropageX = 0xF6,
IncZeropageX = 0xF6,
SedImplied = 0xF8,
// SbcAbsoluteY = 0xF9,
// SbcAbsoluteX = 0xFD,
@@ -224,6 +224,8 @@ enum class AddressMode {
IndirectIndexed
};
 
enum class MemoryAccess { Read, Write, ReadWrite };
 
struct Opcode {
Family family;
Instruction instruction;
@@ -232,6 +234,8 @@ struct Opcode {
 
Opcode decode(const uint8_t op);
 
MemoryAccess get_memory_access(const Family family);
 
std::string to_string(const Family family);
 
} // namespace n_e_s::core
 
core/src/mos6502.cpp added: 187, removed: 49, total 138
@@ -82,6 +82,8 @@ Pipeline Mos6502::parse_next_instruction() {
Pipeline result;
const uint8_t raw_opcode{mmu_->read_byte(registers_->pc++)};
current_opcode_ = decode(raw_opcode);
const MemoryAccess memory_access =
get_memory_access(current_opcode_->family);
 
if (current_opcode_->family == Family::Invalid) {
std::stringstream err;
@@ -117,7 +119,8 @@ Pipeline Mos6502::parse_next_instruction() {
break;
case Instruction::BitZeropage:
case Instruction::BitAbsolute:
result.append(create_addressing_steps(current_opcode_->address_mode));
result.append(create_addressing_steps(
current_opcode_->address_mode, memory_access));
 
result.push([=]() {
const uint8_t value = mmu_->read_byte(effective_address_);
@@ -339,17 +342,10 @@ Pipeline Mos6502::parse_next_instruction() {
case Instruction::NopImplied:
result.push([]() { /* Do nothing. */ });
break;
case Instruction::IncZeropage: {
result.append(create_zeropage_addressing_steps());
result.push([=]() { tmp_ = mmu_->read_byte(effective_address_); });
result.push([=]() { mmu_->write_byte(effective_address_, tmp_++); });
result.push([=]() {
set_zero(tmp_);
set_negative(tmp_);
mmu_->write_byte(effective_address_, tmp_);
});
case Instruction::IncZeropage:
case Instruction::IncZeropageX:
result.append(create_inc_instruction(*current_opcode_));
break;
}
case Instruction::InxImplied:
result.push([=]() {
++registers_->x;
@@ -480,9 +476,25 @@ Pipeline Mos6502::create_branch_instruction(
return result;
}
 
Pipeline Mos6502::create_add_instruction(Opcode opcode) {
Pipeline Mos6502::create_inc_instruction(const Opcode opcode) {
const MemoryAccess memory_access = get_memory_access(opcode.family);
Pipeline result;
result.append(create_addressing_steps(opcode.address_mode));
result.append(create_addressing_steps(opcode.address_mode, memory_access));
 
result.push([=]() {
const uint8_t new_value = tmp_ + 1;
set_zero(new_value);
set_negative(new_value);
mmu_->write_byte(effective_address_, new_value);
});
 
return result;
}
 
Pipeline Mos6502::create_add_instruction(Opcode opcode) {
const MemoryAccess memory_access = get_memory_access(opcode.family);
Pipeline result;
result.append(create_addressing_steps(opcode.address_mode, memory_access));
 
result.push([=]() {
const uint8_t a_before = registers_->a;
@@ -501,8 +513,9 @@ Pipeline Mos6502::create_add_instruction(Opcode opcode) {
}
 
Pipeline Mos6502::create_and_instruction(Opcode opcode) {
const MemoryAccess memory_access = get_memory_access(opcode.family);
Pipeline result;
result.append(create_addressing_steps(opcode.address_mode));
result.append(create_addressing_steps(opcode.address_mode, memory_access));
 
result.push([=]() {
const uint8_t operand = mmu_->read_byte(effective_address_);
@@ -515,9 +528,9 @@ Pipeline Mos6502::create_and_instruction(Opcode opcode) {
return result;
}
Pipeline Mos6502::create_store_instruction(Opcode opcode) {
const MemoryAccess memory_access = get_memory_access(opcode.family);
Pipeline result;
const bool is_write = true;
result.append(create_addressing_steps(opcode.address_mode, is_write));
result.append(create_addressing_steps(opcode.address_mode, memory_access));
 
uint8_t *reg{};
if (opcode.family == Family::STX) {
@@ -542,8 +555,9 @@ Pipeline Mos6502::create_load_instruction(Opcode opcode) {
reg = &registers_->a;
}
 
const MemoryAccess memory_access = get_memory_access(opcode.family);
Pipeline result;
result.append(create_addressing_steps(opcode.address_mode));
result.append(create_addressing_steps(opcode.address_mode, memory_access));
 
result.push([=]() {
*reg = mmu_->read_byte(effective_address_);
@@ -564,9 +578,9 @@ Pipeline Mos6502::create_compare_instruction(Opcode opcode) {
reg = &registers_->a;
}
 
const MemoryAccess memory_access = get_memory_access(opcode.family);
Pipeline result;
result.append(create_addressing_steps(opcode.address_mode));
 
result.append(create_addressing_steps(opcode.address_mode, memory_access));
result.push([=]() {
const uint8_t value = mmu_->read_byte(effective_address_);
// Compare instructions are not affected be the
@@ -580,8 +594,9 @@ Pipeline Mos6502::create_compare_instruction(Opcode opcode) {
}
 
Pipeline Mos6502::create_eor_instruction(Opcode opcode) {
const MemoryAccess memory_access = get_memory_access(opcode.family);
Pipeline result;
result.append(create_addressing_steps(opcode.address_mode));
result.append(create_addressing_steps(opcode.address_mode, memory_access));
 
result.push([=]() {
const uint8_t operand = mmu_->read_byte(effective_address_);
@@ -595,35 +610,38 @@ Pipeline Mos6502::create_eor_instruction(Opcode opcode) {
}
 
Pipeline Mos6502::create_addressing_steps(AddressMode address_mode,
bool is_write) {
const MemoryAccess access) {
Pipeline result;
 
switch (address_mode) {
case AddressMode::Zeropage:
result.append(create_zeropage_addressing_steps());
result.append(create_zeropage_addressing_steps(access));
break;
case AddressMode::ZeropageX:
result.append(create_zeropage_indexed_addressing_steps(&registers_->x));
result.append(create_zeropage_indexed_addressing_steps(
&registers_->x, access));
break;
case AddressMode::ZeropageY:
result.append(create_zeropage_indexed_addressing_steps(&registers_->y));
result.append(create_zeropage_indexed_addressing_steps(
&registers_->y, access));
break;
case AddressMode::Absolute:
result.append(create_absolute_addressing_steps());
break;
case AddressMode::AbsoluteX:
result.append(create_absolute_indexed_addressing_steps(
&registers_->x, is_write));
&registers_->x, access == MemoryAccess::Write));
break;
case AddressMode::AbsoluteY:
result.append(create_absolute_indexed_addressing_steps(
&registers_->y, is_write));
&registers_->y, access == MemoryAccess::Write));
break;
case AddressMode::IndexedIndirect:
result.append(create_indexed_indirect_addressing_steps());
break;
case AddressMode::IndirectIndexed:
result.append(create_indirect_indexed_addressing_steps(is_write));
result.append(create_indirect_indexed_addressing_steps(
access == MemoryAccess::Write));
break;
default:
break;
@@ -632,25 +650,53 @@ Pipeline Mos6502::create_addressing_steps(AddressMode address_mode,
return result;
}
 
Pipeline Mos6502::create_zeropage_addressing_steps() {
Pipeline Mos6502::create_zeropage_addressing_steps(MemoryAccess access) {
Pipeline result;
result.push([=]() {
effective_address_ = mmu_->read_byte(registers_->pc);
++registers_->pc;
});
if (access == MemoryAccess::Read || access == MemoryAccess::Write) {
result.push([=]() {
effective_address_ = mmu_->read_byte(registers_->pc);
++registers_->pc;
});
} else {
result.push([=]() {
effective_address_ = mmu_->read_byte(registers_->pc);
++registers_->pc;
});
result.push([=]() { tmp_ = mmu_->read_byte(effective_address_); });
result.push([=]() {
// Extra write with the old value
mmu_->write_byte(effective_address_, tmp_);
});
}
return result;
}
 
Pipeline Mos6502::create_zeropage_indexed_addressing_steps(
const uint8_t *index_reg) {
const uint8_t *index_reg,
MemoryAccess access) {
Pipeline result;
result.push([=]() { /* Empty */ });
result.push([=]() {
const uint8_t address = mmu_->read_byte(registers_->pc);
const uint8_t effective_address_low = address + *index_reg;
effective_address_ = effective_address_low;
++registers_->pc;
});
if (access == MemoryAccess::Read || access == MemoryAccess::Write) {
result.push([=]() { /* Empty */ });
result.push([=]() {
const uint8_t address = mmu_->read_byte(registers_->pc);
const uint8_t effective_address_low = address + *index_reg;
effective_address_ = effective_address_low;
++registers_->pc;
});
} else if (access == MemoryAccess::ReadWrite) {
result.push([=]() { /* Empty */ });
result.push([=]() {
const uint16_t address = mmu_->read_byte(registers_->pc);
const uint8_t effective_address_low = address + *index_reg;
effective_address_ = effective_address_low;
++registers_->pc;
});
result.push([=]() { tmp_ = mmu_->read_byte(effective_address_); });
result.push([=]() {
// Extra write to effective address with old value.
mmu_->write_byte(effective_address_, tmp_);
});
}
return result;
}
 
 
core/src/mos6502.h added: 187, removed: 49, total 138
@@ -81,6 +81,7 @@ private:
Pipeline parse_next_instruction();
 
Pipeline create_branch_instruction(const std::function<bool()> &condition);
Pipeline create_inc_instruction(Opcode opcode);
Pipeline create_add_instruction(Opcode opcode);
Pipeline create_and_instruction(Opcode opcode);
Pipeline create_store_instruction(Opcode opcode);
@@ -89,10 +90,11 @@ private:
Pipeline create_eor_instruction(Opcode opcode);
 
Pipeline create_addressing_steps(AddressMode address_mode,
bool is_write = false);
MemoryAccess access);
 
Pipeline create_zeropage_addressing_steps();
Pipeline create_zeropage_indexed_addressing_steps(const uint8_t *index_reg);
Pipeline create_zeropage_addressing_steps(MemoryAccess access);
Pipeline create_zeropage_indexed_addressing_steps(const uint8_t *index_reg,
MemoryAccess access);
Pipeline create_absolute_addressing_steps();
Pipeline create_absolute_indexed_addressing_steps(const uint8_t *index_reg,
bool is_write);
 
core/src/opcode.cpp added: 187, removed: 49, total 138
@@ -1,5 +1,6 @@
#include "core/opcode.h"
 
#include <stdexcept>
#include <string>
 
namespace n_e_s::core {
@@ -184,6 +185,8 @@ Opcode decode(const uint8_t op) {
return {Family::CPX, CpxAbsolute, AddressMode::Absolute};
case BeqRelative:
return {Family::BEQ, BeqRelative, AddressMode::Relative};
case IncZeropageX:
return {Family::INC, IncZeropageX, AddressMode::ZeropageX};
case SedImplied:
return {Family::SED, SedImplied, AddressMode::Implied};
case EorImmediate:
@@ -201,6 +204,68 @@ Opcode decode(const uint8_t op) {
}
}
 
MemoryAccess get_memory_access(const Family family) {
switch (family) {
// Return Read for instructions where memory access type has no meaning.
// Set correct access type when we implement missing addressing modes.
case Family::Invalid:
case Family::BRK:
case Family::PHP:
case Family::BPL:
case Family::CLC:
case Family::BIT:
case Family::PLP:
case Family::AND:
case Family::JSR:
case Family::BMI:
case Family::SEC:
case Family::LSR:
case Family::PHA:
case Family::JMP:
case Family::BVC:
case Family::CLI:
case Family::ADC:
case Family::PLA:
case Family::RTS:
case Family::BVS:
case Family::SEI:
case Family::TXS:
case Family::BCC:
case Family::LDX:
case Family::LDY:
case Family::LDA:
case Family::BCS:
case Family::CLV:
case Family::BNE:
case Family::CLD:
case Family::CPX:
case Family::NOP:
case Family::INX:
case Family::INY:
case Family::CPY:
case Family::CMP:
case Family::BEQ:
case Family::SED:
case Family::TYA:
case Family::TAY:
case Family::TAX:
case Family::TSX:
case Family::TXA:
case Family::DEY:
case Family::DEX:
case Family::EOR:
return MemoryAccess::Read;
case Family::STY:
case Family::STA:
case Family::STX:
return MemoryAccess::Write;
case Family::INC:
return MemoryAccess::ReadWrite;
}
// Should not happen
throw std::logic_error("Unknown family");
}
 
std::string to_string(const Family family) {
switch (family) {
case Family::Invalid:
 
core/test/src/test_cpu.cpp added: 187, removed: 49, total 138
@@ -118,6 +118,7 @@ enum Opcode : uint8_t {
NOP = 0xEA,
CPX_ABS = 0xEC,
BEQ = 0xF0,
INC_ZEROX = 0xF6,
SED = 0xF8,
};
 
@@ -1801,6 +1802,7 @@ TEST_F(CpuTest, inc_zero_increments) {
step_execution(5);
}
}
 
TEST_F(CpuTest, inc_zero_sets_z_flag) {
registers.pc = 0x1234;
expected.p |= Z_FLAG;
@@ -1838,6 +1840,25 @@ TEST_F(CpuTest, inc_zero_clears_n_flag) {
step_execution(5);
}
 
TEST_F(CpuTest, inc_zerox_increments) {
registers.pc = expected.pc = 0x4321;
registers.x = expected.x = 0xED;
 
stage_instruction(INC_ZEROX);
expected.pc += 1;
 
EXPECT_CALL(mmu, read_byte(0x4322)).WillOnce(Return(0x44));
EXPECT_CALL(mmu, read_byte(u16_to_u8(0x44 + 0xED))).WillOnce(Return(0x05));
{
InSequence s;
EXPECT_CALL(mmu, write_byte(u16_to_u8(0x44 + 0xED), 0x05));
EXPECT_CALL(mmu, write_byte(u16_to_u8(0x44 + 0xED), 0x06));
}
 
step_execution(6);
EXPECT_EQ(expected, registers);
}
 
// INX
TEST_F(CpuTest, inx_increments) {
stage_instruction(INX);