1
0
mirror of https://github.com/jborza/emu6502.git synced 2025-02-19 07:30:57 +00:00

BIT implementation + tests

ADC, SBC fixes
This commit is contained in:
jborza 2019-04-21 15:31:53 +02:00
parent f5fd1d21c5
commit 1845eb4692
2 changed files with 84 additions and 21 deletions

47
cpu.c
View File

@ -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

View File

@ -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);