srctree

Johan parent 1d3bbb98 9d2fe977 d989d52a
Merge pull request #217 from johnor/right-shift-instructions

Implement Ror and Lsr
core/include/core/opcode.h added: 191, removed: 35, total 156
@@ -43,39 +43,39 @@ enum Instruction : uint8_t {
RtiImplied = 0x40,
EorIndirectX = 0x41,
EorZeropage = 0x45,
// LsrZeropage = 0x46,
LsrZeropage = 0x46,
PhaImplied = 0x48,
EorImmediate = 0x49,
LsrAccumulator = 0x4A,
JmpAbsolute = 0x4C,
EorAbsolute = 0x4D,
// LsrAbsolute = 0x4E,
LsrAbsolute = 0x4E,
BvcRelative = 0x50,
EorIndirectY = 0x51,
EorZeropageX = 0x55,
// LsrZeropageX = 0x56,
LsrZeropageX = 0x56,
CliImplied = 0x58,
EorAbsoluteY = 0x59,
EorAbsoluteX = 0x5D,
// LsrAbsoluteX = 0x5E,
LsrAbsoluteX = 0x5E,
RtsImplied = 0x60,
AdcIndirectX = 0x61,
AdcZeropage = 0x65,
// RorZeropage = 0x66,
RorZeropage = 0x66,
PlaImplied = 0x68,
AdcImmediate = 0x69,
RorAccumulator = 0x6A,
JmpIndirect = 0x6C,
AdcAbsolute = 0x6D,
// RorAbsolute = 0x6E,
RorAbsolute = 0x6E,
BvsRelative = 0x70,
AdcIndirectY = 0x71,
AdcZeropageX = 0x75,
// RorZeropageX = 0x76,
RorZeropageX = 0x76,
SeiImplied = 0x78,
AdcAbsoluteY = 0x79,
AdcAbsoluteX = 0x7D,
// RorAbsoluteX = 0x7E,
RorAbsoluteX = 0x7E,
StaIndexedIndirect = 0x81,
StyZeropage = 0x84,
StaZeropage = 0x85,
 
core/src/mos6502.cpp added: 191, removed: 35, total 156
@@ -194,14 +194,13 @@ Pipeline Mos6502::parse_next_instruction() {
case Instruction::SecImplied:
result.push([=]() { set_flag(C_FLAG); });
break;
case Instruction::LsrZeropage:
case Instruction::LsrAbsolute:
case Instruction::LsrAccumulator:
result.push([=]() {
set_carry((registers_->a & 1u) != 0);
registers_->a &= ~1u;
registers_->a >>= 1u;
set_zero(registers_->a);
clear_flag(N_FLAG);
});
case Instruction::LsrAbsoluteX:
case Instruction::LsrZeropageX:
result.append(
create_right_shift_instruction(*state_.current_opcode, false));
break;
case Instruction::PhaImplied:
result.push([=]() {
@@ -482,23 +481,17 @@ Pipeline Mos6502::parse_next_instruction() {
case Instruction::EorAbsoluteY:
result.append(create_eor_instruction(*state_.current_opcode));
break;
case RolAccumulator:
case Instruction::RolAccumulator:
result.append(
create_left_shift_instruction(*state_.current_opcode, true));
break;
case Instruction::RorZeropage:
case Instruction::RorAbsolute:
case Instruction::RorAccumulator:
result.push([=]() {
const uint8_t carry_in = registers_->p & C_FLAG
? static_cast<uint8_t>(0x80)
: static_cast<uint8_t>(0x00);
const bool carry_out = (registers_->a & 0x01u) == 0x01;
const uint16_t temp_result =
static_cast<uint16_t>(registers_->a >> 1u) | carry_in;
registers_->a = static_cast<uint8_t>(temp_result);
set_carry(carry_out);
set_zero(registers_->a);
set_negative(registers_->a);
});
case Instruction::RorZeropageX:
case Instruction::RorAbsoluteX:
result.append(
create_right_shift_instruction(*state_.current_opcode, true));
break;
case Instruction::OraImmediate:
case Instruction::OraAbsolute:
@@ -845,6 +838,37 @@ Pipeline Mos6502::create_left_shift_instruction(Opcode opcode,
return result;
}
 
Pipeline Mos6502::create_right_shift_instruction(Opcode opcode,
bool shift_in_carry) {
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 source_value =
opcode.address_mode == AddressMode::Accumulator ? registers_->a
: tmp_;
 
uint8_t shifted_value = source_value >> 1u;
if (shift_in_carry) {
const auto carry = registers_->p & C_FLAG
? static_cast<uint8_t>(0b1000'0000)
: static_cast<uint8_t>(0x00);
shifted_value |= carry;
}
set_carry(source_value & 0x01u);
set_zero(shifted_value);
set_negative(shifted_value);
 
if (opcode.address_mode == AddressMode::Accumulator) {
registers_->a = shifted_value;
} else {
mmu_->write_byte(effective_address_, shifted_value);
}
});
return result;
}
 
Pipeline Mos6502::create_addressing_steps(AddressMode address_mode,
const MemoryAccess access) {
Pipeline result;
 
core/src/mos6502.h added: 191, removed: 35, total 156
@@ -99,6 +99,7 @@ private:
Pipeline create_eor_instruction(Opcode opcode);
Pipeline create_ora_instruction(Opcode opcode);
Pipeline create_left_shift_instruction(Opcode opcode, bool shift_in_carry);
Pipeline create_right_shift_instruction(Opcode opcode, bool shift_in_carry);
 
Pipeline create_addressing_steps(AddressMode address_mode,
MemoryAccess access);
 
core/src/opcode.cpp added: 191, removed: 35, total 156
@@ -59,24 +59,32 @@ Opcode decode(const uint8_t op) {
return {Family::ADC, AdcIndirectX, AddressMode::IndexedIndirect};
case AdcZeropage:
return {Family::ADC, AdcZeropage, AddressMode::Zeropage};
case RorZeropage:
return {Family::ROR, RorZeropage, AddressMode::Zeropage};
case PlaImplied:
return {Family::PLA, PlaImplied, AddressMode::Implied};
case AdcImmediate:
return {Family::ADC, AdcImmediate, AddressMode::Immediate};
case AdcAbsolute:
return {Family::ADC, AdcAbsolute, AddressMode::Absolute};
case RorAbsolute:
return {Family::ROR, RorAbsolute, AddressMode::Absolute};
case BvsRelative:
return {Family::BVS, BvsRelative, AddressMode::Relative};
case AdcIndirectY:
return {Family::ADC, AdcIndirectY, AddressMode::IndirectIndexed};
case AdcZeropageX:
return {Family::ADC, AdcZeropageX, AddressMode::ZeropageX};
case RorZeropageX:
return {Family::ROR, RorZeropageX, AddressMode::ZeropageX};
case SeiImplied:
return {Family::SEI, SeiImplied, AddressMode::Implied};
case AdcAbsoluteY:
return {Family::ADC, AdcAbsoluteY, AddressMode::AbsoluteY};
case AdcAbsoluteX:
return {Family::ADC, AdcAbsoluteX, AddressMode::AbsoluteX};
case RorAbsoluteX:
return {Family::ROR, RorAbsoluteX, AddressMode::AbsoluteX};
case StaIndexedIndirect:
return {Family::STA, StaIndexedIndirect, AddressMode::IndexedIndirect};
case StyZeropage:
@@ -239,16 +247,24 @@ Opcode decode(const uint8_t op) {
return {Family::EOR, EorIndirectX, AddressMode::IndexedIndirect};
case EorZeropage:
return {Family::EOR, EorZeropage, AddressMode::Zeropage};
case LsrZeropage:
return {Family::LSR, LsrZeropage, AddressMode::Zeropage};
case EorImmediate:
return {Family::EOR, EorImmediate, AddressMode::Immediate};
case EorAbsolute:
return {Family::EOR, EorAbsolute, AddressMode::Absolute};
case LsrAbsolute:
return {Family::LSR, LsrAbsolute, AddressMode::Absolute};
case EorIndirectY:
return {Family::EOR, EorIndirectY, AddressMode::IndirectIndexed};
case EorZeropageX:
return {Family::EOR, EorZeropageX, AddressMode::ZeropageX};
case LsrZeropageX:
return {Family::LSR, LsrZeropageX, AddressMode::ZeropageX};
case EorAbsoluteX:
return {Family::EOR, EorAbsoluteX, AddressMode::AbsoluteX};
case LsrAbsoluteX:
return {Family::LSR, LsrAbsoluteX, AddressMode::AbsoluteX};
case EorAbsoluteY:
return {Family::EOR, EorAbsoluteY, AddressMode::AbsoluteY};
case RolAccumulator:
@@ -292,7 +308,6 @@ MemoryAccess get_memory_access(const Family family) {
case Family::JSR:
case Family::BMI:
case Family::SEC:
case Family::LSR:
case Family::PHA:
case Family::JMP:
case Family::BVC:
@@ -339,6 +354,7 @@ MemoryAccess get_memory_access(const Family family) {
case Family::ROL:
case Family::ROR:
case Family::ASL:
case Family::LSR:
return MemoryAccess::ReadWrite;
}
// Should not happen
 
core/test/src/test_cpu.cpp added: 191, removed: 35, total 156
@@ -54,31 +54,39 @@ enum Opcode : uint8_t {
RTI = 0x40,
EOR_INXIND = 0x41,
EOR_ZERO = 0x45,
LSR_ACC = 0x4A,
LSR_ZERO = 0x46,
PHA = 0x48,
EOR_IMM = 0x49,
LSR_ACC = 0x4A,
JMP = 0x4C,
EOR_ABS = 0x4D,
LSR_ABS = 0x4E,
BVC = 0x50,
EOR_INDINX = 0x51,
EOR_ZEROX = 0x55,
LSR_ZEROX = 0x56,
CLI = 0x58,
EOR_ABSY = 0x59,
EOR_ABSX = 0x5D,
LSR_ABSX = 0x5E,
RTS = 0x60,
ADC_INDX = 0x61,
ADC_ZERO = 0x65,
ROR_ZERO = 0x66,
PLA = 0x68,
ADC_IMM = 0x69,
ROR_ACC = 0x6A,
JMP_IND = 0x6C,
ADC_ABS = 0x6D,
ROR_ABS = 0x6E,
BVS = 0x70,
ADC_INDY = 0x71,
ADC_ZEROX = 0x75,
ROR_ZEROX = 0x76,
SEI = 0x78,
ADC_ABSY = 0x79,
ADC_ABSX = 0x7D,
ROR_ABSX = 0x7E,
STA_INXIND = 0x81,
TXA = 0x8A,
STY_ABS = 0x8C,
@@ -616,6 +624,29 @@ public:
EXPECT_EQ(expected, registers);
}
 
void run_readwrite_instruction(uint8_t instruction,
IndexReg index_reg,
uint8_t new_memory_content) {
effective_address = u16_to_u8(index_value + reg_value);
set_index_reg(index_reg, reg_value);
registers.pc = expected.pc = start_pc;
stage_instruction(instruction);
expected.pc += 1;
 
EXPECT_CALL(mmu, read_byte(start_pc + 1)).WillOnce(Return(index_value));
EXPECT_CALL(mmu, read_byte(index_value))
.WillOnce(Return(0xCD)); // Dummy read
EXPECT_CALL(mmu, read_byte(effective_address))
.WillOnce(Return(memory_value));
EXPECT_CALL(mmu,
write_byte(effective_address,
memory_value)); // Extra write with old value
EXPECT_CALL(mmu, write_byte(effective_address, new_memory_content));
 
step_execution(6);
EXPECT_EQ(expected, registers);
}
 
void load_sets_reg(uint8_t instruction,
uint8_t *target_reg,
IndexReg index_reg) {
@@ -1277,6 +1308,47 @@ TEST_F(CpuTest, lsr_a_clears_c_z_n_flags) {
EXPECT_EQ(expected, registers);
}
 
TEST_F(CpuZeropageTest, lsr_zeropage_shifts) {
memory_content = 0b01001000;
run_readwrite_instruction(LSR_ZERO, 0b00100100);
}
TEST_F(CpuZeropageIndexedTest, lsr_zeropagex_sets_reg) {
memory_value = 0b01001000;
run_readwrite_instruction(LSR_ZEROX, IndexReg::X, 0b00100100);
}
TEST_F(CpuAbsoluteTest, lsr_abs_shifts) {
memory_content = 0b01001000;
run_readwrite_instruction(LSR_ABS, 0b00100100);
}
TEST_F(CpuAbsoluteTest, lsr_abs_shifts_in_zero) {
memory_content = 0b01001000;
registers.p = C_FLAG;
run_readwrite_instruction(LSR_ABS, 0b00100100);
}
TEST_F(CpuAbsoluteTest, lsr_abs_shifts_out_carry) {
memory_content = 0b01001001;
expected.p = C_FLAG;
run_readwrite_instruction(LSR_ABS, 0b00100100);
}
TEST_F(CpuTest, lsr_absx_shifts) {
registers.pc = expected.pc = 0x4321;
registers.x = expected.x = 0xED;
 
stage_instruction(LSR_ABSX);
expected.pc += 2;
 
EXPECT_CALL(mmu, read_byte(registers.pc + 1u)).WillOnce(Return(0x34));
EXPECT_CALL(mmu, read_byte(registers.pc + 2u)).WillOnce(Return(0x12));
EXPECT_CALL(mmu, read_byte(0x1234 + 0xED - 0x0100))
.WillOnce(Return(0xDEAD));
EXPECT_CALL(mmu, read_byte(0x1234 + 0xED)).WillOnce(Return(0b01001000));
EXPECT_CALL(mmu, write_byte(0x1234 + 0xED, 0b01001000));
EXPECT_CALL(mmu, write_byte(0x1234 + 0xED, 0b00100100));
 
step_execution(7);
EXPECT_EQ(expected, registers);
}
 
TEST_F(CpuTest, pha) {
stage_instruction(PHA);
registers.sp = 0x05;
@@ -3164,6 +3236,49 @@ TEST_F(CpuTest, ror_a_set_neg_retain_c) {
step_execution(2);
EXPECT_EQ(expected, registers);
}
 
TEST_F(CpuZeropageTest, ror_zeropage_shifts) {
memory_content = 0b01001000;
run_readwrite_instruction(ROR_ZERO, 0b00100100);
}
TEST_F(CpuZeropageIndexedTest, ror_zeropagex_sets_reg) {
memory_value = 0b01001000;
run_readwrite_instruction(ROR_ZEROX, IndexReg::X, 0b00100100);
}
TEST_F(CpuAbsoluteTest, ror_abs_shifts) {
memory_content = 0b01001000;
run_readwrite_instruction(ROR_ABS, 0b00100100);
}
TEST_F(CpuAbsoluteTest, ror_abs_shifts_in_carry) {
memory_content = 0b01001000;
registers.p = C_FLAG;
expected.p = N_FLAG;
run_readwrite_instruction(ROR_ABS, 0b10100100);
}
TEST_F(CpuAbsoluteTest, ror_abs_shifts_out_carry) {
memory_content = 0b01001001;
expected.p = C_FLAG;
run_readwrite_instruction(ROR_ABS, 0b00100100);
}
TEST_F(CpuTest, ror_absx_shifts) {
registers.pc = expected.pc = 0x4321;
registers.x = expected.x = 0xED;
 
stage_instruction(ROR_ABSX);
expected.pc += 2;
 
EXPECT_CALL(mmu, read_byte(registers.pc + 1u)).WillOnce(Return(0x34));
EXPECT_CALL(mmu, read_byte(registers.pc + 2u)).WillOnce(Return(0x12));
EXPECT_CALL(mmu, read_byte(0x1234 + 0xED - 0x0100))
.WillOnce(Return(0xDEAD));
EXPECT_CALL(mmu, read_byte(0x1234 + 0xED)).WillOnce(Return(0b01001000));
EXPECT_CALL(mmu, write_byte(0x1234 + 0xED, 0b01001000));
EXPECT_CALL(mmu, write_byte(0x1234 + 0xED, 0b00100100));
 
step_execution(7);
EXPECT_EQ(expected, registers);
}
 
// ORA, IMM
TEST_F(CpuTest, ora_imm) {
registers.pc = expected.pc = 0x3210;