diff --git a/cpu.c b/cpu.c index c0dc254..d8ac92c 100644 --- a/cpu.c +++ b/cpu.c @@ -14,7 +14,7 @@ int is_negative(byte value) { return ((1 << 7) & value) != 0; } -void set_z_flag(State6502* state, byte value) { +void set_z_flag(State6502 * state, byte value) { state->flags.z = value == 0; } @@ -22,6 +22,7 @@ void set_NV_flags(State6502 * state, byte value) { //N flag state->flags.n = is_negative(value); //TODO implement NV flags + state->flags.v = ((1 << 6) & value) != 0; } void set_NZ_flags(State6502 * state, byte value) { @@ -125,7 +126,7 @@ void JMP(State6502 * state, word address) { state->pc = address; } -void SBC(State6502* state, byte operand) { +void SBC(State6502 * state, byte operand) { //subtract operand from A word operand_word = operand; //borrow 0x100 from the carry flag if set @@ -133,29 +134,37 @@ void SBC(State6502* state, byte operand) { operand_word += 0x100; state->flags.c = 0; } - word result = state->a - operand_word; + word result_word = state->a - operand_word; + byte result = result_word & 0xFF; // overflow flag if the the result doesn't fit into the signed byte range -128 to 127 - state->flags.v = (state->a ^ operand) & 0x80) && ((state->a ^ (result) & 0x80); + state->flags.v = ((state->a ^ operand) & 0x80) && ((state->a ^ result) & 0x80); state->a -= result; state->flags.n = is_negative(state->a); state->flags.z = state->a == 0; } -void ADC(State6502* state, byte operand) { +void ADC(State6502 * state, byte operand) { //add operand to A - word result_word = operand + state->a + state->flags.c ? 1 : 0; + word result_word = operand + state->a + (state->flags.c ? 1 : 0); byte result = result_word & 0xFF; //set overflow flag if the result's sign would change - the result doesn't fit into a signed byte //there is overflow if the inputs do not have different signs and the input sign is different from the output sign state->flags.v = !((state->a ^ operand) & 0x80) && ((state->a ^ result) & 0x80); - state->a = state->a & 0xFF; + state->a = result; state->flags.n = is_negative(state->a); state->flags.z = state->a == 0; state->flags.c = result_word > 0xFF; } +void BIT(State6502 * state, byte operand) { + //BIT sets the Z flag as though the value in the address tested were ANDed with the accumulator. + //The N and V flags are set to match bits 7 and 6 respectively in the value stored at the tested address. + set_NV_flags(state, operand); + state->flags.z = (state->a & operand) == 0; +} + void cmp_internal(State6502 * state, byte register_value, byte operand) { //set carry flag if A >= M state->flags.c = register_value >= operand; @@ -177,40 +186,40 @@ void CPY(State6502 * state, byte operand) { cmp_internal(state, state->y, operand); } -byte asl(State6502* state, byte operand) { +byte asl(State6502 * state, byte operand) { byte result = operand << 1; state->flags.c = operand > 0x80; set_NZ_flags(state, result); return result; } -void ASL_A(State6502* state) { +void ASL_A(State6502 * state) { state->a = asl(state, state->a); } -void ASL_MEM(State6502* state, word address) { +void ASL_MEM(State6502 * state, word address) { byte operand = state->memory[address]; state->memory[address] = operand; state->memory[address] = asl(state, operand); } -byte lsr(State6502* state, byte operand) { +byte lsr(State6502 * state, byte operand) { byte result = operand >> 1; state->flags.c = (operand & 0x01) != 0; set_NZ_flags(state, result); return result; } -void LSR_A(State6502* state) { +void LSR_A(State6502 * state) { state->a = lsr(state, state->a); } -void LSR_MEM(State6502* state, word address) { +void LSR_MEM(State6502 * state, word address) { byte operand = state->memory[address]; state->memory[address] = lsr(state, operand); } -byte rol(State6502* state, byte operand) { +byte rol(State6502 * state, byte operand) { word result_word = (operand << 1) | state->flags.c; state->flags.c = result_word > 0xFF; byte result = result_word & 0xFF; @@ -218,16 +227,16 @@ byte rol(State6502* state, byte operand) { return result; } -void ROL_A(State6502* state) { +void ROL_A(State6502 * state) { state->a = rol(state, state->a); } -void ROL_MEM(State6502* state, word address) { +void ROL_MEM(State6502 * state, word address) { byte operand = state->memory[address]; state->memory[address] = rol(state, operand); } -byte ror(State6502* state, byte operand) { +byte ror(State6502 * state, byte operand) { word result_word = (operand >> 1) | (state->flags.c << 7); state->flags.c = (result_word & 0x01) != 0; byte result = result_word & 0xFF; @@ -388,8 +397,8 @@ int emulate_6502_op(State6502 * state) { case BPL_REL: unimplemented_instruction(state); break; case BVC_REL: unimplemented_instruction(state); break; case BVS_REL: unimplemented_instruction(state); break; - case BIT_ZP: unimplemented_instruction(state); break; - case BIT_ABS: unimplemented_instruction(state); break; + case BIT_ZP: BIT(state, get_byte_zero_page(state)); break; + case BIT_ABS: BIT(state, get_byte_absolute(state)); break; case BRK: state->running = 0; state->flags.b = 1; break; //BRK diff --git a/test6502.c b/test6502.c index 908bfd7..6bb73c0 100644 --- a/test6502.c +++ b/test6502.c @@ -1927,7 +1927,7 @@ void test_CMP_ABS_greater_2() { test_step(&state); assert_flag_z(&state, 0x00); // 0x82 != 0x1A - assert_flag_n(&state, 0x00); + assert_flag_n(&state, 0x00); assert_flag_c(&state, 0x01); // 0x82 > 0x1A test_cleanup(&state); @@ -1990,7 +1990,7 @@ void test_SBC_IMM() { State6502 state = create_blank_state(); state.a = 0x08; - char program[] = { SBC_IMM, 0x06}; + char program[] = { SBC_IMM, 0x06 }; memcpy(state.memory, program, sizeof(program)); test_step(&state); @@ -2022,6 +2022,56 @@ void test_SBC_IMM_carry() { test_cleanup(&state); } +// ADC + +void test_ADC_IMM_exec(byte a, byte c, byte operand, byte expected_a, byte expected_n, byte expected_z, byte expected_c, byte expected_v) { + State6502 state = create_blank_state(); + state.a = a; + state.flags.c = c; + char program[] = { ADC_IMM, operand }; + memcpy(state.memory, program, sizeof(program)); + //act + test_step(&state); + //assert + assertA(&state, expected_a); + assert_flag_n(&state, expected_n); + assert_flag_z(&state, expected_z); + assert_flag_c(&state, expected_c); + assert_flag_v(&state, expected_v); +} + +void test_ADC_IMM_multiple() { + //A, C, OP => A, N, Z, C, V + test_ADC_IMM_exec(2, 0, 3, 5, 0, 0, 0, 0); //straight addition + test_ADC_IMM_exec(2, 1, 3, 6, 0, 0, 0, 0); //straight addition with carry + test_ADC_IMM_exec(2, 0, 254, 0, 0, 1, 1, 0); //carry and zero + test_ADC_IMM_exec(2, 0, 253, 255, 1, 0, 0, 1); //just negative + test_ADC_IMM_exec(253, 0, 6, 3, 0, 0, 1, 1); //carry and overflow + test_ADC_IMM_exec(125, 1, 2, 128, 1, 0, 0, 1); //negative and overflow +} + +// BIT +void test_BIT_exec(byte a, byte expected_a, byte expected_n, byte expected_v, byte expected_z) { + State6502 state = create_blank_state(); + state.a = a; + char program[] = { BIT_ABS, 0x45, 0x03}; + memcpy(state.memory, program, sizeof(program)); + state.memory[0x0345] = 0xF3; + //act + test_step(&state); + //assert + assertA(&state, expected_a); + assert_flag_n(&state, expected_n); + assert_flag_z(&state, expected_z); + assert_flag_v(&state, expected_v); +} + +void test_BIT_multiple() { + test_BIT_exec(128, 128, 1, 1, 0); + test_BIT_exec(5, 5, 1, 1, 0); + test_BIT_exec(4, 4, 1, 1, 1); // 128 & 4 = 0 -> Z = 1 + test_BIT_exec(3, 3, 1, 1, 0); +} ///////////////////// @@ -2045,6 +2095,8 @@ fp* tests_php_plp[] = { test_PHP, test_PLP }; fp* tests_jmp[] = { test_JMP, test_JMP_IND, test_JMP_IND_wrap }; fp* tests_cmp[] = { test_CMP_ABS_equal, test_CMP_ABS_greater, test_CMP_ABS_greater_2, test_CMP_ABS_less_than, test_CPX_ABS, test_CPY_ABS }; fp* tests_sbc[] = { test_SBC_IMM }; +fp* tests_adc[] = { test_ADC_IMM_multiple }; +fp* tests_bit[] = { test_BIT_multiple }; #define RUN(suite) run_suite(suite, sizeof(suite)/sizeof(fp*)) @@ -2054,6 +2106,8 @@ void run_suite(fp * *suite, int size) { } void run_tests() { + RUN(tests_bit); + RUN(tests_adc); RUN(tests_sbc); RUN(tests_lda); RUN(tests_ora);