2344 lines
90 KiB
C
2344 lines
90 KiB
C
#include "unity.h"
|
|
#include "CPU_6502.h"
|
|
#include "memory.h"
|
|
#include "common.h"
|
|
|
|
#define NOP 0xEA
|
|
|
|
uint16_t last_read_address = 0;
|
|
uint16_t last_write_address = 0;
|
|
uint8_t last_write_value = 0;
|
|
|
|
void setUp(void) {
|
|
last_read_address = 0;
|
|
last_write_address = 0;
|
|
last_write_value = 0;
|
|
}
|
|
|
|
void tearDown(void) { }
|
|
|
|
uint8_t read_mem(void *context, uint16_t address) {
|
|
last_read_address = address;
|
|
return NOP;
|
|
}
|
|
|
|
void write_mem(void *context, uint16_t address, uint8_t value) {
|
|
last_write_address = address;
|
|
last_write_value = value;
|
|
}
|
|
|
|
void test_CPU_6502_new_delete(void) {
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(NULL, write_mem, read_mem);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
CPU_6502_delete(&cpu);
|
|
TEST_ASSERT_NULL(cpu);
|
|
}
|
|
|
|
void test_CPU_6502_initialization(void) {
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(NULL, write_mem, read_mem);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
TEST_ASSERT_EQUAL(CPU_6502_VECTOR_RESET_HI, last_read_address);
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xFF, cpu->state.sp, "Stack pointer should be initialized to 0xFF");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0xEAEA, cpu->state.pc, "Program counter should be pointing to 0xEAEA");
|
|
TEST_ASSERT_MESSAGE(cpu->state.status & CPU_6502_STATUS_UNUSED, "Unused bit should be set");
|
|
TEST_ASSERT_MESSAGE(cpu->state.status & CPU_6502_STATUS_INTERRUPT, "Interrupt bit should be set");
|
|
TEST_ASSERT_MESSAGE(cpu->state.status & CPU_6502_STATUS_BREAK, "Break bit should be set");
|
|
TEST_ASSERT_UNLESS_MESSAGE(cpu->state.status & CPU_6502_STATUS_DECIMAL, "Decimal bit should be set");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0, cpu->state.a, "Accumulator should be initialized to 0x00");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0, cpu->state.x, "X register should be initialized to 0x00");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0, cpu->state.y, "Y register should be initialized to 0x00");
|
|
CPU_6502_delete(&cpu);
|
|
}
|
|
|
|
void test_CPU_6502_nop_step(void) {
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(NULL, write_mem, read_mem);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0xEAEB, cpu->state.pc, "Program counter should be incremented by 1");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0xEAEA, last_read_address, "NOP should read from 0xEAEA");
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0xEAEC, cpu->state.pc, "Program counter should be incremented by 1");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0xEAEB, last_read_address, "NOP should read from 0xEAEB");
|
|
CPU_6502_delete(&cpu);
|
|
}
|
|
|
|
void test_CPU_6502_LDA_immediate(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(2, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
/* LDA #5 */
|
|
MEMORY_write(mem, 0, 0xA9);
|
|
MEMORY_write(mem, 1, 0x05);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func)MEMORY_write, (COMMON_read_func)MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "LDA #5 should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "LDA #5 should move pc after immediate data");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x05, cpu->state.a, "LDA #5 should load accumulator with 0x05");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_LDX(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(2, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
/* LDX #5 */
|
|
MEMORY_write(mem, 0, 0xA2);
|
|
MEMORY_write(mem, 1, 0x05);
|
|
/* LDX #0 */
|
|
MEMORY_write(mem, 2, 0xA2);
|
|
MEMORY_write(mem, 3, 0x00);
|
|
/* LDX #-1 */
|
|
MEMORY_write(mem, 4, 0xA2);
|
|
MEMORY_write(mem, 5, 0xFF);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func)MEMORY_write, (COMMON_read_func)MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "LDX #5 should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "LDX #5 should move pc after immediate data");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x05, cpu->state.x, "LDX #5 should load X register with 0x05");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "LDX #5 should not set zero flag for non-zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "LDX #5 should not set negative flag for positive value");
|
|
|
|
cycles = CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "LDX #0 should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0004, cpu->state.pc, "LDX #0 should move pc after immediate data");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x00, cpu->state.x, "LDX #0 should load X register with 0x00");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "LDX #0 should set zero flag for zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "LDX #0 should not set negative flag for zero value");
|
|
|
|
cycles = CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "LDX #-1 should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0006, cpu->state.pc, "LDX #-1 should move pc after immediate data");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xFF, cpu->state.x, "LDX #-1 should load X register with 0xFF");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "LDX #-1 should not set zero flag for non-zero value");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "LDX #-1 should set negative flag for negative value");
|
|
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_LDA_absolute(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(512, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
/* LDA $0102 */
|
|
MEMORY_write(mem, 0, 0xAD);
|
|
MEMORY_write(mem, 1, 0x02);
|
|
MEMORY_write(mem, 2, 0x01);
|
|
/* Value to load */
|
|
MEMORY_write(mem, 0x0102, 0x06);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func)MEMORY_write, (COMMON_read_func)MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x04, cycles, "LDA $0102 should consume 4 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0003, cpu->state.pc, "LDA $0102 should move pc after absolute address");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x06, cpu->state.a, "LDA $0102 should load accumulator with 0x06");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_LDA_zero_page(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(512, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
/* LDA $05 */
|
|
MEMORY_write(mem, 0, 0xA5);
|
|
MEMORY_write(mem, 1, 0x05);
|
|
/* Value to load */
|
|
MEMORY_write(mem, 0x0005, 0x07);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func)MEMORY_write, (COMMON_read_func)MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x03, cycles, "LDA $05 should consume 4 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "LDA $05 should move pc after zero page address");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x07, cpu->state.a, "LDA $05 should load accumulator with 0x07");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_LDA_zero_page_x(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(512, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
/* LDA $05,X */
|
|
MEMORY_write(mem, 0, 0xB5);
|
|
MEMORY_write(mem, 1, 0x05);
|
|
/* Value to load */
|
|
MEMORY_write(mem, 0x0007, 0x08);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func)MEMORY_write, (COMMON_read_func)MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.x = 0x02;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x04, cycles, "LDA $05,X should consume 4 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "LDA $05,X should move pc after zero page address");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x08, cpu->state.a, "LDA $05,X should load accumulator with 0x08");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_LDA_zero_page_x_should_not_carry(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(512, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
/* LDA $FF,X */
|
|
MEMORY_write(mem, 0, 0xB5);
|
|
MEMORY_write(mem, 1, 0xFF);
|
|
/* Value to load */
|
|
MEMORY_write(mem, 0x0005, 0x09);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func)MEMORY_write, (COMMON_read_func)MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.x = 0x06;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x04, cycles, "LDA $FF,X should consume 4 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "LDA $FF,X should move pc after zero page address");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x09, cpu->state.a, "LDA $FF,X should load accumulator with 0x09");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_LDA_absolute_x(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(512, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
/* LDA $0102,X */
|
|
MEMORY_write(mem, 0, 0xBD);
|
|
MEMORY_write(mem, 1, 0x02);
|
|
MEMORY_write(mem, 2, 0x01);
|
|
/* Value to load */
|
|
MEMORY_write(mem, 0x0104, 0x0A);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func)MEMORY_write, (COMMON_read_func)MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.x = 0x02;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x04, cycles, "LDA $0102,X should consume 4 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0003, cpu->state.pc, "LDA $0102,X should move pc after absolute address");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x0A, cpu->state.a, "LDA $0102,X should load accumulator with 0x0A");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_LDA_absolute_y(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(512, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
/* LDA $0102,Y */
|
|
MEMORY_write(mem, 0, 0xB9);
|
|
MEMORY_write(mem, 1, 0x02);
|
|
MEMORY_write(mem, 2, 0x01);
|
|
/* Value to load */
|
|
MEMORY_write(mem, 0x0104, 0x0A);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func)MEMORY_write, (COMMON_read_func)MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.y = 0x02;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x04, cycles, "LDA $0102,Y should consume 4 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0003, cpu->state.pc, "LDA $0102,Y should move pc after absolute address");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x0A, cpu->state.a, "LDA $0102,Y should load accumulator with 0x0A");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_LDA_indirect_x(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(512, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
/* LDA ($02,X) */
|
|
MEMORY_write(mem, 0, 0xA1);
|
|
MEMORY_write(mem, 1, 0x05);
|
|
/* ptr address */
|
|
MEMORY_write(mem, 0x07, 0x0A);
|
|
MEMORY_write(mem, 0x08, 0x01);
|
|
/* value to load */
|
|
MEMORY_write(mem, 0x010A, 0xC1);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func)MEMORY_write, (COMMON_read_func)MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.x = 0x02;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x06, cycles, "LDA ($02,X) should consume 4 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "LDA ($02,X) should move pc after absolute address");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xC1, cpu->state.a, "LDA ($02,X) should load accumulator with 0x0A");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_LDA_indirect_y(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(512, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
/* LDA ($02),Y */
|
|
MEMORY_write(mem, 0, 0xB1);
|
|
MEMORY_write(mem, 1, 0x05);
|
|
/* ptr address */
|
|
MEMORY_write(mem, 0x05, 0x0A);
|
|
MEMORY_write(mem, 0x06, 0x01);
|
|
/* value to load */
|
|
MEMORY_write(mem, 0x010C, 0xC1);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func)MEMORY_write, (COMMON_read_func)MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.y = 0x02;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x05, cycles, "LDA ($02),Y should consume 4 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "LDA ($02),Y should move pc after absolute address");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xC1, cpu->state.a, "LDA ($02),Y should load accumulator with 0xC1");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_LDY(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(2, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
/* LDY #5 */
|
|
MEMORY_write(mem, 0, 0xA0);
|
|
MEMORY_write(mem, 1, 0x05);
|
|
/* LDY #0 */
|
|
MEMORY_write(mem, 2, 0xA0);
|
|
MEMORY_write(mem, 3, 0x00);
|
|
/* LDY #-1 */
|
|
MEMORY_write(mem, 4, 0xA0);
|
|
MEMORY_write(mem, 5, 0xFF);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "LDY #5 should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "LDY #5 should move pc after immediate data");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x05, cpu->state.y, "LDY #5 should load Y register with 0x05");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO,
|
|
"LDY #5 should not set zero flag for non-zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE,
|
|
"LDY #5 should not set negative flag for positive value");
|
|
|
|
cycles = CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "LDY #0 should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0004, cpu->state.pc, "LDY #0 should move pc after immediate data");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x00, cpu->state.y, "LDY #0 should load Y register with 0x00");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "LDY #0 should set zero flag for zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE,
|
|
"LDY #0 should not set negative flag for zero value");
|
|
|
|
cycles = CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "LDY #-1 should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0006, cpu->state.pc, "LDY #-1 should move pc after immediate data");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xFF, cpu->state.y, "LDY #-1 should load Y register with 0xFF");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO,
|
|
"LDY #-1 should not set zero flag for non-zero value");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE,
|
|
"LDY #-1 should set negative flag for negative value");
|
|
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_JMP_absolute(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(512, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* JMP $0200 */
|
|
MEMORY_write(mem, 0, 0x4C);
|
|
MEMORY_write(mem, 1, 0x00);
|
|
MEMORY_write(mem, 2, 0x02);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x03, cycles, "JMP $0200 should consume 3 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0200, cpu->state.pc, "JMP $0200 should move pc to $0200");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_TXS(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(512, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* TXS */
|
|
MEMORY_write(mem, 0, 0x9A);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.sp = 0x00;
|
|
cpu->state.x = 0x05;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "TXS should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0001, cpu->state.pc, "TXS should move pc to $0001");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x05, cpu->state.sp, "TXS should set stack pointer to 0x05");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_STA(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* STA $0200 */
|
|
MEMORY_write(mem, 0, 0x8D);
|
|
MEMORY_write(mem, 1, 0x00);
|
|
MEMORY_write(mem, 2, 0x02);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.a = 0x05;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x04, cycles, "STA $0200 should consume 4 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0003, cpu->state.pc, "STA $0200 should move pc to $0003");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x05, MEMORY_read(mem, 0x0200), "STA $0200 should store 0x05 at $0200");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_BNE_with_equal(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* BNE $05 */
|
|
MEMORY_write(mem, 0, 0xD0);
|
|
MEMORY_write(mem, 1, 0x05);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status |= CPU_6502_STATUS_ZERO;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "BNE $0200 should consume 3 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "BNE should move pc to $0002");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_BNE_with_not_equal(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* BNE $05 */
|
|
MEMORY_write(mem, 0, 0xD0);
|
|
MEMORY_write(mem, 1, 0x05);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status &= ~CPU_6502_STATUS_ZERO;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "BNE $0200 should consume 4 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0007, cpu->state.pc, "BNE $0200 should move pc to $0002");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_BNE_with_not_equal_neg_offset(void) {
|
|
/*
|
|
* test:
|
|
* lda #2
|
|
* lda #1
|
|
* bne test
|
|
* lda #3
|
|
*/
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* LDA #2 */
|
|
MEMORY_write(mem, 0x0000, 0xA9);
|
|
MEMORY_write(mem, 0x0001, 0x02);
|
|
|
|
/* LDA #1 */
|
|
MEMORY_write(mem, 0x0002, 0xA9);
|
|
MEMORY_write(mem, 0x0003, 0x01);
|
|
|
|
/* BNE $FA */
|
|
MEMORY_write(mem, 0x0004, 0xD0);
|
|
MEMORY_write(mem, 0x0005, 0xFA);
|
|
|
|
/* LDA #3 */
|
|
MEMORY_write(mem, 0x0006, 0xA9);
|
|
MEMORY_write(mem, 0x0007, 0x03);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0002;
|
|
CPU_6502_step(cpu);
|
|
CPU_6502_step(cpu);
|
|
CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cpu->state.a, "Accumulator should hold 2 if proper branch was taken");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_DEX(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* DEX */
|
|
MEMORY_write(mem, 0, 0xCA);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.x = 0x05;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "DEX should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0001, cpu->state.pc, "DEX should move pc to $0001");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x04, cpu->state.x, "DEX should set x to 0x04");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO,
|
|
"DEX should not set zero flag for non-zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE,
|
|
"DEX should not set negative flag for positive value");
|
|
|
|
cpu->state.x = 0x01;
|
|
cpu->state.pc = 0x0000;
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO,
|
|
"DEX should set zero flag for zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE,
|
|
"DEX should not set negative flag for zero value");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO,
|
|
"DEX should not set zero flag for negative value");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE,
|
|
"DEX should set negative flag for negative value");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_BEQ(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* LDA #0 */
|
|
MEMORY_write(mem, 0, 0xA9);
|
|
MEMORY_write(mem, 1, 0x00);
|
|
|
|
/* BEQ test */
|
|
MEMORY_write(mem, 2, 0xF0);
|
|
MEMORY_write(mem, 3, 0x02);
|
|
|
|
/* LDA #2 */
|
|
MEMORY_write(mem, 4, 0xA9);
|
|
MEMORY_write(mem, 5, 0x02);
|
|
|
|
/* test: LDA #1 */
|
|
MEMORY_write(mem, 6, 0xA9);
|
|
MEMORY_write(mem, 7, 0x01);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
CPU_6502_step(cpu);
|
|
CPU_6502_PRINT_STATE(cpu->state);
|
|
CPU_6502_PRINT_STATUS(cpu->state.status);
|
|
CPU_6502_step(cpu);
|
|
CPU_6502_PRINT_STATE(cpu->state);
|
|
CPU_6502_PRINT_STATUS(cpu->state.status);
|
|
CPU_6502_step(cpu);
|
|
CPU_6502_PRINT_STATE(cpu->state);
|
|
CPU_6502_PRINT_STATUS(cpu->state.status);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x01, cpu->state.a, "1 should be loaded in accumulator");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_BEQ_equal(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* BEQ $05 */
|
|
MEMORY_write(mem, 0, 0xF0);
|
|
MEMORY_write(mem, 1, 0x05);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status |= CPU_6502_STATUS_ZERO;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "BEQ $0200 should consume 3 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0007, cpu->state.pc, "BEQ $0200 should move pc to $0007");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_BEQ_not_equal(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* BEQ $05 */
|
|
MEMORY_write(mem, 0, 0xF0);
|
|
MEMORY_write(mem, 1, 0x05);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "BEQ $0200 should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "BEQ $0200 should move pc to $0002");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_DEY(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* DEY */
|
|
MEMORY_write(mem, 0, 0x88);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.y = 0x05;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "DEY should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0001, cpu->state.pc, "DEY should move pc to $0001");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x04, cpu->state.y, "DEY should set y to 0x04");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO,
|
|
"DEY should not set zero flag for non-zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE,
|
|
"DEY should not set negative flag for positive value");
|
|
|
|
cpu->state.y = 0x01;
|
|
cpu->state.pc = 0x0000;
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO,
|
|
"DEY should set zero flag for zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE,
|
|
"DEY should not set negative flag for zero value");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO,
|
|
"DEY should not set zero flag for negative value");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE,
|
|
"DEY should set negative flag for negative value");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_CMP_less_than(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* CMP #$05 */
|
|
MEMORY_write(mem, 0, 0xC9);
|
|
MEMORY_write(mem, 1, 0x05);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.a = 0x04;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "CMP #$05 should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "CMP #$05 should move pc to $0002");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "CMP #$05 should not set zero flag for < memory value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "CMP #$05 should not set carry flag for < memory value");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "CMP #$05 should set negative flag for < memory value");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_CMP_greater_than(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* CMP #$05 */
|
|
MEMORY_write(mem, 0, 0xC9);
|
|
MEMORY_write(mem, 1, 0x05);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.a = 0x06;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "CMP #$05 should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "CMP #$05 should move pc to $0002");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "CMP #$05 should not set zero flag for > memory value");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "CMP #$05 should set carry flag for > memory value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "CMP #$05 should not set negative flag for > memory value");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_CMP_equal(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* CMP #$05 */
|
|
MEMORY_write(mem, 0, 0xC9);
|
|
MEMORY_write(mem, 1, 0x05);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.a = 0x05;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "CMP #$05 should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "CMP #$05 should move pc to $0002");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "CMP #$05 should set zero flag for = memory value");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "CMP #$05 should set carry flag for = memory value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "CMP #$05 should not set negative flag for = memory value");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_CLD(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* CLD */
|
|
MEMORY_write(mem, 0, 0xD8);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.status = CPU_6502_STATUS_DECIMAL;
|
|
|
|
cpu->state.pc = 0x0000;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "CLD should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0001, cpu->state.pc, "CLD should move pc to $0001");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_DECIMAL, "CLD should clear decimal flag");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_TYA(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* TYA */
|
|
MEMORY_write(mem, 0, 0x98);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.y = 0x05;
|
|
|
|
cpu->state.pc = 0x0000;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "TYA should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0001, cpu->state.pc, "TYA should move pc to $0001");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x05, cpu->state.a, "TYA should transfer Y to A");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "TYA should not set zero flag for non-zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "TYA should not set negative flag for non-negative value");
|
|
|
|
cpu->state.y = 0x00;
|
|
cpu->state.pc = 0x0000;
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "TYA should set zero flag for zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "TYA should not set negative flag for zero value");
|
|
|
|
cpu->state.y = 0xFF;
|
|
cpu->state.pc = 0x0000;
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "TYA should not set zero flag for negative value");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "TYA should set negative flag for negative value");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_TAX(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* TAX */
|
|
MEMORY_write(mem, 0, 0xAA);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.a = 0x05;
|
|
|
|
cpu->state.pc = 0x0000;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "TAX should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0001, cpu->state.pc, "TAX should move pc to $0001");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x05, cpu->state.x, "TAX should transfer A to X");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "TAX should not set zero flag for non-zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "TAX should not set negative flag for non-negative value");
|
|
|
|
cpu->state.a = 0x00;
|
|
cpu->state.pc = 0x0000;
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "TAX should set zero flag for zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "TAX should not set negative flag for zero value");
|
|
|
|
cpu->state.a = 0xFF;
|
|
cpu->state.pc = 0x0000;
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "TAX should not set zero flag for negative value");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "TAX should set negative flag for negative value");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_BPL(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* BPL */
|
|
MEMORY_write(mem, 0, 0x10);
|
|
MEMORY_write(mem, 1, 0x02);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status |= CPU_6502_STATUS_NEGATIVE;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "BPL should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "BPL should move pc to $0002");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status &= ~CPU_6502_STATUS_NEGATIVE;
|
|
cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "BPL should consume 3 cycles when branch is taken");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0004, cpu->state.pc, "BPL should move pc to $0002");
|
|
}
|
|
|
|
void test_CPU_6502_CLC(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* CLC */
|
|
MEMORY_write(mem, 0, 0x18);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status |= CPU_6502_STATUS_CARRY;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "CLC should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0001, cpu->state.pc, "CLC should move pc to $0001");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "CLC should clear carry flag");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_ADC_binary(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* ADC */
|
|
MEMORY_write(mem, 0, 0x69);
|
|
MEMORY_write(mem, 1, 0x05);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.a = 0x05;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "ADC should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "ADC should move pc to $0002");
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x0A, cpu->state.a, "ADC should add value to A");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "ADC should not set zero flag for non-zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "ADC should not set negative flag for non-negative value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "ADC should not set carry flag for non-carry value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_OVERFLOW, "ADC should not set overflow flag for non-overflow value");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.a = 0xFF;
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "ADC should not set zero flag for non-zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "ADC should not set negative flag for non-negative value");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "ADC should not set carry flag for when value is carried");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_OVERFLOW, "ADC should not set overflow flag for non-overflow value");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.a = 0x7F;
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "ADC should not set zero flag for non-zero value");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "ADC should set negative flag for negative value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "ADC should not set carry flag for non-carry value");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_OVERFLOW, "ADC should set overflow flag for overflow value");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_EOR(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* EOR */
|
|
MEMORY_write(mem, 0, 0x49);
|
|
MEMORY_write(mem, 1, 0x05);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.a = 0x0F;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "EOR should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "EOR should move pc to $0002");
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x0A, cpu->state.a, "EOR should add value to A");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "EOR should not set zero flag for non-zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "EOR should not set negative flag for non-negative value");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_SEI(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* SEI */
|
|
MEMORY_write(mem, 0, 0x78);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status &= ~CPU_6502_STATUS_INTERRUPT;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "SEI should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0001, cpu->state.pc, "SEI should move pc to $0001");
|
|
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_INTERRUPT, "SEI should set interrupt disable flag");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_INY(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* INY */
|
|
MEMORY_write(mem, 0, 0xC8);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.y = 0x0F;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "INY should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0001, cpu->state.pc, "INY should move pc to $0001");
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x10, cpu->state.y, "INY should increment Y");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "INY should not set zero flag for non-zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "INY should not set negative flag for non-negative value");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.y = 0xF4;
|
|
CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "INY should not set zero flag for non-zero value");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "INY should set negative flag for negative value");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.y = 0xFF;
|
|
CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "INY should set zero flag for zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "INY should not set negative flag for zero value");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_INX(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* INX */
|
|
MEMORY_write(mem, 0, 0xE8);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.x = 0x0F;
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "INX should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0001, cpu->state.pc, "INX should move pc to $0001");
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x10, cpu->state.x, "INX should increment X");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "INX should not set zero flag for non-zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "INX should not set negative flag for non-negative value");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.x = 0xF4;
|
|
CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "INX should not set zero flag for non-zero value");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "INX should set negative flag for negative value");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.x = 0xFF;
|
|
CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "INX should set zero flag for zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "INX should not set negative flag for zero value");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_INC(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* INC $05 */
|
|
MEMORY_write(mem, 0, 0xE6);
|
|
MEMORY_write(mem, 1, 0x05);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
MEMORY_write(mem, 0x05, 0x0F);
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x05, cycles, "INC $05 should consume 5 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "INC $05 should move pc to $0002");
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x10, MEMORY_read(mem, 0x05), "INC $05 should increment memory");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "INC $05 should not set zero flag for non-zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "INC $05 should not set negative flag for non-negative value");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
MEMORY_write(mem, 0x05, 0xF4);
|
|
CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "INC $05 should not set zero flag for non-zero value");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "INC $05 should set negative flag for negative value");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
MEMORY_write(mem, 0x05, 0xFF);
|
|
CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "INC $05 should set zero flag for zero value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "INC $05 should not set negative flag for zero value");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_JSR(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* JSR $1234 */
|
|
MEMORY_write(mem, 0, 0x20);
|
|
MEMORY_write(mem, 1, 0x34);
|
|
MEMORY_write(mem, 2, 0x12);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.sp = 0xFF;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x06, cycles, "JSR $1234 should consume 6 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x1234, cpu->state.pc, "JSR $1234 should move pc to $1234");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xFD, cpu->state.sp, "JSR $1234 should decrement sp to $FD");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x00, MEMORY_read(mem, 0x01FF), "JSR $1234 should push high byte of return address to $01FF");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, MEMORY_read(mem, 0x01FE), "JSR $1234 should push low byte of return address to $01FE");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_PHA(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* PHA */
|
|
MEMORY_write(mem, 0, 0x48);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.sp = 0xFF;
|
|
cpu->state.a = 0x0F;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x03, cycles, "PHA should consume 3 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0001, cpu->state.pc, "PHA should move pc to $0001");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xFE, cpu->state.sp, "PHA should decrement sp to $FE");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x0F, MEMORY_read(mem, 0x01FF), "PHA should push accumulator to $01FF");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_STY(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* STY $05 */
|
|
MEMORY_write(mem, 0, 0x84);
|
|
MEMORY_write(mem, 1, 0x05);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.y = 0x0F;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x03, cycles, "STY $05 should consume 3 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "STY $05 should move pc to $0002");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x0F, MEMORY_read(mem, 0x05), "STY $05 should store y register to $05");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_PLA(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* PLA */
|
|
MEMORY_write(mem, 0, 0x68);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.sp = 0xFE;
|
|
MEMORY_write(mem, 0x01FF, 0x0F);
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x04, cycles, "PLA should consume 4 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0001, cpu->state.pc, "PLA should move pc to $0001");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xFF, cpu->state.sp, "PLA should increment sp to $FF");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x0F, cpu->state.a, "PLA should load accumulator from $01FF");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_RTS(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* RTS */
|
|
MEMORY_write(mem, 0, 0x60);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.sp = 0xFD;
|
|
MEMORY_write(mem, 0x01FF, 0x00);
|
|
MEMORY_write(mem, 0x01FE, 0x03);
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x06, cycles, "RTS should consume 6 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0004, cpu->state.pc, "RTS should move pc to $0004");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xFF, cpu->state.sp, "RTS should decrement sp to $FF");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_JSR_RTS(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* JSR $0300 */
|
|
MEMORY_write(mem, 0, 0x20);
|
|
MEMORY_write(mem, 1, 0x00);
|
|
MEMORY_write(mem, 2, 0x03);
|
|
|
|
/* RTS */
|
|
MEMORY_write(mem, 0x0300, 0x60);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0300, cpu->state.pc, "JSR $0300 should move pc to $0301");
|
|
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0003, cpu->state.pc, "RTS should move pc to $0003");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_CPY(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* CPY #$0F */
|
|
MEMORY_write(mem, 0, 0xC0);
|
|
MEMORY_write(mem, 1, 0x0F);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.y = 0x0F;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "CPY #$0F should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "CPY #$0F should move pc to $0002");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x0F, cpu->state.y, "CPY #$0F should not change y register");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "CPY #$0F should set zero flag for equal values");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "CPY #$0F should clear negative flag for equal values");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "CPY #$0F should set carry flag for equal values");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.y = 0x10;
|
|
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "CPY #$0F should clear zero flag for non-equal values");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "CPY #$0F should clear negative flag for equal values");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "CPY #$0F should set carry flag when register > memory value");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.y = 0x0E;
|
|
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "CPY #$0F should clear zero flag for non-equal values");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "CPY #$0F should set negative flag when register < memory value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "CPY #$0F should clear carry flag when register < memory value");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_CPX(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* CPX #$0F */
|
|
MEMORY_write(mem, 0, 0xE0);
|
|
MEMORY_write(mem, 1, 0x0F);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.x = 0x0F;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "CPX #$0F should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "CPX #$0F should move pc to $0002");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x0F, cpu->state.x, "CPX #$0F should not change x register");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "CPX #$0F should set zero flag for equal values");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "CPX #$0F should clear negative flag for equal values");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "CPX #$0F should set carry flag for equal values");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.x = 0x10;
|
|
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "CPX #$0F should clear zero flag for non-equal values");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "CPX #$0F should clear negative flag for equal values");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "CPX #$0F should set carry flag when register > memory value");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.x = 0x0E;
|
|
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "CPX #$0F should clear zero flag for non-equal values");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "CPX #$0F should set negative flag when register < memory value");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "CPX #$0F should clear carry flag when register < memory value");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_BCC(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* BCC $03 */
|
|
MEMORY_write(mem, 0, 0x90);
|
|
MEMORY_write(mem, 1, 0x03);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status &= ~CPU_6502_STATUS_CARRY;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "BCC $03 should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0005, cpu->state.pc, "BCC $03 should move pc to $0003 when carry flag is clear");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status |= CPU_6502_STATUS_CARRY;
|
|
|
|
CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "BCC $03 should move pc to $0002 when carry flag is set");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_BMI(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* BMI $03 */
|
|
MEMORY_write(mem, 0, 0x30);
|
|
MEMORY_write(mem, 1, 0x03);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status |= CPU_6502_STATUS_NEGATIVE;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "BMI $03 should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0005, cpu->state.pc, "BMI $03 should move pc to $0005 when negative flag is set");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status &= ~CPU_6502_STATUS_NEGATIVE;
|
|
|
|
CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "BMI $03 should move pc to $0002 when negative flag is clear");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_BCS(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* BCS $03 */
|
|
MEMORY_write(mem, 0, 0xB0);
|
|
MEMORY_write(mem, 1, 0x03);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status |= CPU_6502_STATUS_CARRY;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "BCS $03 should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0005, cpu->state.pc, "BCS $03 should move pc to $0005 when carry flag is set");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status &= ~CPU_6502_STATUS_CARRY;
|
|
|
|
CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "BCS $03 should move pc to $0002 when carry flag is clear");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_TSX(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* TSX */
|
|
MEMORY_write(mem, 0, 0xBA);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.sp = 0x0F;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "TSX should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x0F, cpu->state.x, "TSX should set X register to stack pointer value");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_TXA(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* TXA */
|
|
MEMORY_write(mem, 0, 0x8A);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.x = 0x0F;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "TXA should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x0F, cpu->state.a, "TXA should set A register to X register value");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_PLP(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* PLP */
|
|
MEMORY_write(mem, 0, 0x28);
|
|
MEMORY_write(mem, 0x01FE, 0xB5);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.status = 0x00;
|
|
cpu->state.sp = 0xFD;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x04, cycles, "PLP should consume 4 cycles");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xB5, cpu->state.status, "PLP should set status register to stack value");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_BVC(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* BVC $03 */
|
|
MEMORY_write(mem, 0, 0x50);
|
|
MEMORY_write(mem, 1, 0x03);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status |= CPU_6502_STATUS_OVERFLOW;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "BVC $03 should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "BVC $03 should move pc to $0002 when overflow flag is clear");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status &= ~CPU_6502_STATUS_OVERFLOW;
|
|
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0005, cpu->state.pc, "BVC $03 should move pc to $0005 when overflow flag is set");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_BVS(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* BVS $03 */
|
|
MEMORY_write(mem, 0, 0x70);
|
|
MEMORY_write(mem, 1, 0x03);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status |= CPU_6502_STATUS_OVERFLOW;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "BVS $03 should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0005, cpu->state.pc, "BVS $03 should move pc to $0005 when overflow flag is set");
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status &= ~CPU_6502_STATUS_OVERFLOW;
|
|
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0002, cpu->state.pc, "BVS $03 should move pc to $0002 when overflow flag is clear");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_PHP(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* PHP */
|
|
MEMORY_write(mem, 0, 0x08);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status = 0xA5;
|
|
cpu->state.sp = 0xFD;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x03, cycles, "PHP should consume 3 cycles");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xA5, MEMORY_read(mem, 0x01FD), "PHP should push status register to stack");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_DEC(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* DEC */
|
|
MEMORY_write(mem, 0, 0xC6);
|
|
MEMORY_write(mem, 1, 0x0F);
|
|
|
|
MEMORY_write(mem, 0x000F, 0x04);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x05, cycles, "DEC accumulator should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x03, MEMORY_read(mem, 0x000F), "DEC accumulator should decrement accumulator by 1");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_DEC_accumulator(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* DEC */
|
|
MEMORY_write(mem, 0, 0x3A);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.a = 0x05;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "DEC accumulator should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x04, cpu->state.a, "DEC accumulator should decrement accumulator by 1");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_AND(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* AND immediate */
|
|
MEMORY_write(mem, 0, 0x29);
|
|
MEMORY_write(mem, 1, 0x0F);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.a = 0xFF;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "AND immediate should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x0F, cpu->state.a, "AND immediate should AND accumulator with immediate memory location");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_PHY(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* PHY */
|
|
MEMORY_write(mem, 0, 0x5A);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.y = 0xA5;
|
|
cpu->state.sp = 0xFD;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x03, cycles, "PHY should consume 3 cycles");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xA5, MEMORY_read(mem, 0x01FD), "PHY should push Y register to stack");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_PLY(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* PLY */
|
|
MEMORY_write(mem, 0, 0x7A);
|
|
|
|
MEMORY_write(mem, 0x01FE, 0xA5);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.sp = 0xFD;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x04, cycles, "PLY should consume 4 cycles");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xA5, cpu->state.y, "PLY should pull Y register from stack");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_JMP_indirect(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* JMP indirect */
|
|
MEMORY_write(mem, 0, 0x6C);
|
|
MEMORY_write(mem, 1, 0x0F);
|
|
MEMORY_write(mem, 2, 0x00);
|
|
|
|
MEMORY_write(mem, 0x000F, 0x0A);
|
|
MEMORY_write(mem, 0x0010, 0x0B);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x05, cycles, "JMP indirect should consume 5 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0B0A, cpu->state.pc, "JMP indirect should jump to address in memory location");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_BRK(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* BRK */
|
|
MEMORY_write(mem, 0, 0x00);
|
|
|
|
MEMORY_write(mem, 0xFFFE, 0x0A);
|
|
MEMORY_write(mem, 0xFFFF, 0x0B);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
uint8_t orig_status = cpu->state.status;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x07, cycles, "BRK should consume 7 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0B0A, cpu->state.pc, "BRK should jump to interrupt vector");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xFC, cpu->state.sp, "BRK should push PC & status to stack");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_BREAK, "BRK should set break flag in status register");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_INTERRUPT, "BRK should set interrupt flag in status register");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x00, MEMORY_read(mem, 0x01FF), "BRK should push PC HH to stack");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, MEMORY_read(mem, 0x01FE), "BRK should push PC LL to stack");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(orig_status, MEMORY_read(mem, 0x01FD), "BRK should push status register to stack");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_STX(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* STX absolute */
|
|
MEMORY_write(mem, 0, 0x8E);
|
|
MEMORY_write(mem, 1, 0x0F);
|
|
MEMORY_write(mem, 2, 0x00);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.x = 0x0A;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x04, cycles, "STX absolute should consume 4 cycles");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x0A, MEMORY_read(mem, 0x000F), "STX absolute should store X register in memory");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_RTI(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* RTI */
|
|
MEMORY_write(mem, 0, 0x40);
|
|
|
|
MEMORY_write(mem, 0x01FF, 0x0A);
|
|
MEMORY_write(mem, 0x01FE, 0x0B);
|
|
MEMORY_write(mem, 0x01FD, 0xB5);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.sp = 0xFC;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x06, cycles, "RTI should consume 6 cycles");
|
|
TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x0A0B, cpu->state.pc, "RTI should jump to address in memory location");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xFF, cpu->state.sp, "RTI should pull PC & status from stack");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xB5, cpu->state.status, "RTI should pull status register from stack");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_ORA(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* ORA immediate */
|
|
MEMORY_write(mem, 0, 0x09);
|
|
MEMORY_write(mem, 1, 0x0A);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.a = 0xB0;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "ORA immediate should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xBA, cpu->state.a, "ORA immediate should OR A register with value in memory");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "ORA immediate should set negative flag when result bit 7 is set");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "ORA immediate should not set zero flag when result is not zero");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_SEC(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* SEC */
|
|
MEMORY_write(mem, 0, 0x38);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status &= ~CPU_6502_STATUS_CARRY;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "SEC should consume 2 cycles");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "SEC should set carry flag in status register");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_CLI(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* CLI */
|
|
MEMORY_write(mem, 0, 0x58);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status |= CPU_6502_STATUS_INTERRUPT;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "CLI should consume 2 cycles");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_INTERRUPT, "CLI should clear interrupt disable flag in status register");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_SED(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* SED */
|
|
MEMORY_write(mem, 0, 0xF8);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status &= ~CPU_6502_STATUS_DECIMAL;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "SED should consume 2 cycles");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_DECIMAL, "SED should set decimal flag in status register");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_CLV(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* CLV */
|
|
MEMORY_write(mem, 0, 0xB8);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status |= CPU_6502_STATUS_OVERFLOW;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "CLV should consume 2 cycles");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_OVERFLOW, "CLV should clear overflow flag in status register");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_BIT(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* BIT zero page */
|
|
MEMORY_write(mem, 0x1000, 0x24);
|
|
MEMORY_write(mem, 0x1001, 0x00);
|
|
|
|
MEMORY_write(mem, 0x0001, 0x00);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x1000;
|
|
cpu->state.status |= CPU_6502_STATUS_NEGATIVE;
|
|
cpu->state.status |= CPU_6502_STATUS_OVERFLOW;
|
|
cpu->state.status &= ~CPU_6502_STATUS_ZERO;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x03, cycles, "BIT zero page should consume 3 cycles");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "BIT zero page should set zero flag in status register");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "BIT should set negative flag to bit 7 of memory location");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_OVERFLOW, "BIT should set overflow flag to bit 6 of memory location");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_ASL_accumulator(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* ASL accumulator */
|
|
MEMORY_write(mem, 0, 0x0A);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.a = 0x82;
|
|
cpu->state.status &= ~CPU_6502_STATUS_CARRY;
|
|
cpu->state.status |= CPU_6502_STATUS_ZERO;
|
|
cpu->state.status |= CPU_6502_STATUS_NEGATIVE;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "ASL accumulator should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x04, cpu->state.a, "ASL accumulator should shift left accumulator");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "ASL accumulator should set carry flag to input bit 7");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "ASL accumulator should set zero flag only if the result is 0");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "ASL accumulator should set negative flag only if result bit 7 is set");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_LSR_accumulator(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* LSR accumulator */
|
|
MEMORY_write(mem, 0, 0x4A);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.a = 0x83;
|
|
cpu->state.status |= CPU_6502_STATUS_CARRY;
|
|
cpu->state.status |= CPU_6502_STATUS_ZERO;
|
|
cpu->state.status |= CPU_6502_STATUS_NEGATIVE;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "LSR accumulator should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x41, cpu->state.a, "LSR accumulator should shift right accumulator");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "LSR accumulator should set carry flag to input bit 1 if the shifted bit is set");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "LSR accumulator should set zero flag only if the result is 0");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "LSR accumulator should set negative flag");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_ROL_accumulator(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* ROL accumulator */
|
|
MEMORY_write(mem, 0, 0x2A);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.a = 0x82;
|
|
cpu->state.status &= ~CPU_6502_STATUS_CARRY;
|
|
cpu->state.status |= CPU_6502_STATUS_ZERO;
|
|
cpu->state.status |= CPU_6502_STATUS_NEGATIVE;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "ROL accumulator should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x04, cpu->state.a, "ROL accumulator should shift left accumulator");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "ROL accumulator should set carry flag to input bit 7");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "ROL accumulator should set zero flag only if the result is 0");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "ROL accumulator should set negative flag only if result bit 7 is set");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_ROR_accumulator(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* ROR accumulator */
|
|
MEMORY_write(mem, 0, 0x6A);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.a = 0x83;
|
|
cpu->state.status |= CPU_6502_STATUS_CARRY;
|
|
cpu->state.status |= CPU_6502_STATUS_ZERO;
|
|
cpu->state.status |= CPU_6502_STATUS_NEGATIVE;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "ROR accumulator should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xC1, cpu->state.a, "ROR accumulator should shift right accumulator");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "ROR accumulator should set carry flag to input bit 1 if the shifted bit is set");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "ROR accumulator should set zero flag only if the result is 0");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "ROR accumulator should set negative flag");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_SBC(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* SBC immediate */
|
|
MEMORY_write(mem, 0, 0xE9);
|
|
MEMORY_write(mem, 1, 0x01);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.a = 0x01;
|
|
cpu->state.status |= CPU_6502_STATUS_CARRY;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "SBC immediate should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x00, cpu->state.a, "SBC immediate should subtract immediate from accumulator");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "SBC immediate should set carry flag if no borrow");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "SBC immediate should set zero flag if result is zero");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "SBC immediate should set negative flag if result bit 7 is not set");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_SBC_negative_result(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* SBC immediate */
|
|
MEMORY_write(mem, 0, 0xE9);
|
|
MEMORY_write(mem, 1, 0x02);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.a = 0x01;
|
|
cpu->state.status |= CPU_6502_STATUS_CARRY;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "SBC immediate should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xFF, cpu->state.a, "SBC immediate should subtract immediate from accumulator");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "SBC immediate should set carry flag if no borrow");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "SBC immediate should not set zero flag if result is not zero");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "SBC immediate should set negative flag if result bit 7 is not set");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_SBC_overflow(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
/* SBC immediate */
|
|
MEMORY_write(mem, 0, 0xE9);
|
|
/* 100 decimal */
|
|
MEMORY_write(mem, 1, 0x64);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func) MEMORY_write, (COMMON_read_func) MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->state.pc = 0x0000;
|
|
/* 2's compliment -100 decimal */
|
|
cpu->state.a = 0x9C;
|
|
cpu->state.status |= CPU_6502_STATUS_CARRY;
|
|
|
|
uint8_t cycles = CPU_6502_step(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x02, cycles, "SBC immediate should consume 2 cycles");
|
|
TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x38, cpu->state.a, "SBC immediate should subtract immediate from accumulator");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_CARRY, "SBC immediate should set carry flag if no borrow");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_ZERO, "SBC immediate should not set zero flag if result is not zero");
|
|
TEST_ASSERT_TRUE_MESSAGE(cpu->state.status & CPU_6502_STATUS_OVERFLOW, "SBC immediate should set overflow flag when overflow occurs");
|
|
TEST_ASSERT_FALSE_MESSAGE(cpu->state.status & CPU_6502_STATUS_NEGATIVE, "SBC immediate should set negative flag if result bit 7 is not set");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
void test_CPU_6502_counters_reset_with_cpu(void) {
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(NULL, write_mem, read_mem);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->counters.instructions = 1234;
|
|
cpu->counters.cycles = 5678;
|
|
|
|
CPU_6502_reset(cpu);
|
|
|
|
TEST_ASSERT_EQUAL_MESSAGE(0, cpu->counters.instructions, "CPU counters (instructions) should be reset when CPU is reset");
|
|
TEST_ASSERT_EQUAL_MESSAGE(0, cpu->counters.cycles, "CPU counters (cycles) should be reset when CPU is reset");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
TEST_ASSERT_NULL(cpu);
|
|
}
|
|
|
|
void test_CPU_6502_counters_instructions_increment_each_instruction(void) {
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(NULL, write_mem, read_mem);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->counters.instructions = 0;
|
|
cpu->counters.cycles = 0;
|
|
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_MESSAGE(1, cpu->counters.instructions, "CPU counters (instructions) should be incremented each instruction");
|
|
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_MESSAGE(2, cpu->counters.instructions, "CPU counters (instructions) should be incremented each instruction");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
TEST_ASSERT_NULL(cpu);
|
|
}
|
|
|
|
void test_CPU_6502_counters_cycles_increment_each_instruction(void) {
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(NULL, write_mem, read_mem);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
cpu->counters.instructions = 0;
|
|
cpu->counters.cycles = 0;
|
|
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_MESSAGE(2, cpu->counters.cycles, "CPU counters (cycles) should be incremented each instruction");
|
|
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_MESSAGE(4, cpu->counters.cycles, "CPU counters (cycles) should be incremented each instruction");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
TEST_ASSERT_NULL(cpu);
|
|
}
|
|
|
|
void test_CPU_6502_counters_branch(void) {
|
|
struct MEMORY_instance *mem = MEMORY_new(1024, false);
|
|
TEST_ASSERT_NOT_NULL(mem);
|
|
|
|
struct CPU_6502_instance *cpu = CPU_6502_new(mem, (COMMON_write_func)MEMORY_write, (COMMON_read_func)MEMORY_read);
|
|
TEST_ASSERT_NOT_NULL(cpu);
|
|
|
|
TEST_ASSERT_EQUAL(0, cpu->counters.branches);
|
|
|
|
/* BCC */
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status &= ~CPU_6502_STATUS_CARRY;
|
|
MEMORY_write(mem, 0x0000, 0x90);
|
|
MEMORY_write(mem, 0x0001, 0x02);
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_MESSAGE(1, cpu->counters.branches, "BCC should increment branch instruction counter");
|
|
TEST_ASSERT_EQUAL_MESSAGE(1, cpu->counters.branches_taken, "BCC should increment branch instruction taken counter when a branch is taken");
|
|
|
|
/* BCS */
|
|
cpu->state.pc = 0x0000;
|
|
MEMORY_write(mem, 0x0000, 0xB0);
|
|
MEMORY_write(mem, 0x0001, 0x02);
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_MESSAGE(2, cpu->counters.branches, "BCS should increment branch instruction counter");
|
|
TEST_ASSERT_EQUAL_MESSAGE(1, cpu->counters.branches_taken, "BCS should not increment branch instruction taken counter");
|
|
|
|
/* BEQ */
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status |= CPU_6502_STATUS_ZERO;
|
|
MEMORY_write(mem, 0x0000, 0xF0);
|
|
MEMORY_write(mem, 0x0001, 0x02);
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_MESSAGE(3, cpu->counters.branches, "BEQ should increment branch instruction counter");
|
|
TEST_ASSERT_EQUAL_MESSAGE(2, cpu->counters.branches_taken, "BEQ should not increment branch instruction taken counter");
|
|
|
|
/* BNE */
|
|
cpu->state.pc = 0x0000;
|
|
MEMORY_write(mem, 0x0000, 0xD0);
|
|
MEMORY_write(mem, 0x0001, 0x02);
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_MESSAGE(4, cpu->counters.branches, "BNE should increment branch instruction counter");
|
|
TEST_ASSERT_EQUAL_MESSAGE(2, cpu->counters.branches_taken, "BNE should not increment branch instruction taken counter");
|
|
|
|
/* BMI */
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status |= CPU_6502_STATUS_NEGATIVE;
|
|
MEMORY_write(mem, 0x0000, 0x30);
|
|
MEMORY_write(mem, 0x0001, 0x02);
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_MESSAGE(5, cpu->counters.branches, "BMI should increment branch instruction counter");
|
|
TEST_ASSERT_EQUAL_MESSAGE(3, cpu->counters.branches_taken, "BMI should not increment branch instruction taken counter");
|
|
|
|
/* BPL */
|
|
cpu->state.pc = 0x0000;
|
|
MEMORY_write(mem, 0x0000, 0x10);
|
|
MEMORY_write(mem, 0x0001, 0x02);
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_MESSAGE(6, cpu->counters.branches, "BPL should increment branch instruction counter");
|
|
TEST_ASSERT_EQUAL_MESSAGE(3, cpu->counters.branches_taken, "BMI should not increment branch instruction taken counter");
|
|
|
|
/* BVC */
|
|
cpu->state.pc = 0x0000;
|
|
cpu->state.status |= CPU_6502_STATUS_OVERFLOW;
|
|
MEMORY_write(mem, 0x0000, 0x50);
|
|
MEMORY_write(mem, 0x0001, 0x02);
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_MESSAGE(7, cpu->counters.branches, "BVC should increment branch instruction counter");
|
|
TEST_ASSERT_EQUAL_MESSAGE(3, cpu->counters.branches_taken, "BVC should increment branch instruction taken counter");
|
|
|
|
/* BVS */
|
|
cpu->state.pc = 0x0000;
|
|
MEMORY_write(mem, 0x0000, 0x70);
|
|
MEMORY_write(mem, 0x0001, 0x02);
|
|
CPU_6502_step(cpu);
|
|
TEST_ASSERT_EQUAL_MESSAGE(8, cpu->counters.branches, "BVS should increment branch instruction counter");
|
|
TEST_ASSERT_EQUAL_MESSAGE(4, cpu->counters.branches_taken, "BVS should not increment branch instruction taken counter");
|
|
|
|
CPU_6502_delete(&cpu);
|
|
MEMORY_delete(&mem);
|
|
}
|
|
|
|
int main(void) {
|
|
UNITY_BEGIN();
|
|
RUN_TEST(test_CPU_6502_new_delete);
|
|
RUN_TEST(test_CPU_6502_initialization);
|
|
RUN_TEST(test_CPU_6502_nop_step);
|
|
RUN_TEST(test_CPU_6502_LDA_immediate);
|
|
RUN_TEST(test_CPU_6502_LDA_absolute);
|
|
RUN_TEST(test_CPU_6502_LDA_zero_page);
|
|
RUN_TEST(test_CPU_6502_LDA_zero_page_x);
|
|
RUN_TEST(test_CPU_6502_LDA_zero_page_x_should_not_carry);
|
|
RUN_TEST(test_CPU_6502_LDA_absolute_x);
|
|
RUN_TEST(test_CPU_6502_LDA_absolute_y);
|
|
RUN_TEST(test_CPU_6502_LDA_indirect_x);
|
|
RUN_TEST(test_CPU_6502_LDA_indirect_y);
|
|
RUN_TEST(test_CPU_6502_LDX);
|
|
RUN_TEST(test_CPU_6502_LDY);
|
|
RUN_TEST(test_CPU_6502_JMP_absolute);
|
|
RUN_TEST(test_CPU_6502_TXS);
|
|
RUN_TEST(test_CPU_6502_STA);
|
|
RUN_TEST(test_CPU_6502_BNE_with_equal);
|
|
RUN_TEST(test_CPU_6502_BNE_with_not_equal);
|
|
RUN_TEST(test_CPU_6502_BNE_with_not_equal_neg_offset);
|
|
RUN_TEST(test_CPU_6502_DEX);
|
|
RUN_TEST(test_CPU_6502_DEY);
|
|
RUN_TEST(test_CPU_6502_BEQ_equal);
|
|
RUN_TEST(test_CPU_6502_BEQ_not_equal);
|
|
RUN_TEST(test_CPU_6502_CMP_less_than);
|
|
RUN_TEST(test_CPU_6502_CMP_greater_than);
|
|
RUN_TEST(test_CPU_6502_CMP_equal);
|
|
RUN_TEST(test_CPU_6502_CLD);
|
|
RUN_TEST(test_CPU_6502_TYA);
|
|
RUN_TEST(test_CPU_6502_TAX);
|
|
RUN_TEST(test_CPU_6502_BPL);
|
|
RUN_TEST(test_CPU_6502_CLC);
|
|
RUN_TEST(test_CPU_6502_ADC_binary);
|
|
RUN_TEST(test_CPU_6502_EOR);
|
|
RUN_TEST(test_CPU_6502_SEI);
|
|
RUN_TEST(test_CPU_6502_INY);
|
|
RUN_TEST(test_CPU_6502_INX);
|
|
RUN_TEST(test_CPU_6502_INC);
|
|
RUN_TEST(test_CPU_6502_JSR);
|
|
RUN_TEST(test_CPU_6502_PHA);
|
|
RUN_TEST(test_CPU_6502_STY);
|
|
RUN_TEST(test_CPU_6502_PLA);
|
|
RUN_TEST(test_CPU_6502_RTS);
|
|
RUN_TEST(test_CPU_6502_JSR_RTS);
|
|
RUN_TEST(test_CPU_6502_BEQ);
|
|
RUN_TEST(test_CPU_6502_CPY);
|
|
RUN_TEST(test_CPU_6502_CPX);
|
|
RUN_TEST(test_CPU_6502_BCC);
|
|
RUN_TEST(test_CPU_6502_BMI);
|
|
RUN_TEST(test_CPU_6502_BCS);
|
|
RUN_TEST(test_CPU_6502_TSX);
|
|
RUN_TEST(test_CPU_6502_TXA);
|
|
RUN_TEST(test_CPU_6502_PLP);
|
|
RUN_TEST(test_CPU_6502_BVC);
|
|
RUN_TEST(test_CPU_6502_BVS);
|
|
RUN_TEST(test_CPU_6502_PHP);
|
|
RUN_TEST(test_CPU_6502_DEC);
|
|
RUN_TEST(test_CPU_6502_DEC_accumulator);
|
|
RUN_TEST(test_CPU_6502_AND);
|
|
RUN_TEST(test_CPU_6502_PHY);
|
|
RUN_TEST(test_CPU_6502_PLY);
|
|
RUN_TEST(test_CPU_6502_JMP_indirect);
|
|
RUN_TEST(test_CPU_6502_BRK);
|
|
RUN_TEST(test_CPU_6502_STX);
|
|
RUN_TEST(test_CPU_6502_RTI);
|
|
RUN_TEST(test_CPU_6502_ORA);
|
|
RUN_TEST(test_CPU_6502_SEC);
|
|
RUN_TEST(test_CPU_6502_CLI);
|
|
RUN_TEST(test_CPU_6502_SED);
|
|
RUN_TEST(test_CPU_6502_CLV);
|
|
RUN_TEST(test_CPU_6502_BIT);
|
|
RUN_TEST(test_CPU_6502_ASL_accumulator);
|
|
RUN_TEST(test_CPU_6502_LSR_accumulator);
|
|
RUN_TEST(test_CPU_6502_ROL_accumulator);
|
|
RUN_TEST(test_CPU_6502_ROR_accumulator);
|
|
RUN_TEST(test_CPU_6502_SBC);
|
|
RUN_TEST(test_CPU_6502_SBC_negative_result);
|
|
RUN_TEST(test_CPU_6502_SBC_overflow);
|
|
RUN_TEST(test_CPU_6502_counters_reset_with_cpu);
|
|
RUN_TEST(test_CPU_6502_counters_instructions_increment_each_instruction);
|
|
RUN_TEST(test_CPU_6502_counters_cycles_increment_each_instruction);
|
|
RUN_TEST(test_CPU_6502_counters_branch);
|
|
return UNITY_END();
|
|
}
|