From df2f125588b5cdb33d36aba008a93e53c0f3054a Mon Sep 17 00:00:00 2001 From: jborza Date: Fri, 19 Apr 2019 10:34:07 +0200 Subject: [PATCH] fixed pop_byte_from_stack, fixed JMP_IND, added tests for CMP --- cpu.c | 70 ++++++++++++++++-------- test6502.c | 153 +++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 155 insertions(+), 68 deletions(-) diff --git a/cpu.c b/cpu.c index 718fd54..c5f6a1a 100644 --- a/cpu.c +++ b/cpu.c @@ -10,9 +10,13 @@ void* unimplemented_instruction(State6502* state) { exit(1); } -void set_NV_flags(State6502* state, byte value) { +int is_negative(byte value) { + return ((1 << 7) & value) != 0; +} + +void set_NV_flags(State6502 * state, byte value) { //N flag - state->flags.n = ((1 << 7) & value) != 0; + state->flags.n = is_negative(value); //TODO implement NV flags } @@ -25,7 +29,7 @@ void set_NZ_flags(State6502 * state, byte value) { state->flags.z = 1; } //N flag - state->flags.n = ((1 << 7) & value) != 0; + state->flags.n = is_negative(value); } void clear_flags(State6502 * state) { @@ -59,8 +63,7 @@ void push_byte_to_stack(State6502 * state, byte value) { } byte pop_byte_from_stack(State6502 * state) { - return state->memory[STACK_HOME + state->sp++]; - state->sp++; + return state->memory[STACK_HOME + ++(state->sp)]; } //bitwise or with accumulator @@ -124,6 +127,27 @@ void JMP(State6502 * state, word address) { state->pc = address; } +void cmp_internal(State6502 * state, byte register_value, byte operand) { + //set carry flag if A >= M + state->flags.c = register_value >= operand; + //set zero flag if A == M + state->flags.z = register_value == operand; + //set negative flag if A - M is negative + state->flags.n = is_negative(register_value - operand); +} + +void CMP(State6502 * state, byte operand) { + cmp_internal(state, state->a, operand); +} + +void CPX(State6502 * state, byte operand) { + cmp_internal(state, state->x, operand); +} + +void CPY(State6502 * state, byte operand) { + cmp_internal(state, state->y, operand); +} + word pop_word(State6502 * state) { byte low = pop_byte(state); byte high = pop_byte(state); @@ -199,10 +223,10 @@ byte get_byte_absolute_y(State6502 * state) { word get_address_indirect_jmp(State6502 * state) { //AN INDIRECT JUMP MUST NEVER USE A VECTOR BEGINNING ON THE LAST BYTE OF A PAGE - word indirect_address = pop_word(state); + word indirect_address = pop_word(state); if ((indirect_address & 0xFF) == 0xFF) { //avoid crossing the page boundary - return state->memory[indirect_address] | state->memory[indirect_address + 1 - 0xFF] << 8; + return state->memory[indirect_address] | state->memory[indirect_address - 0xFF] << 8; } else { return read_word(state, indirect_address); @@ -300,22 +324,22 @@ int emulate_6502_op(State6502 * state) { case TXA: state->a = state->x; set_NZ_flags(state, state->a); break; case TAY: state->y = state->a; set_NZ_flags(state, state->y); break; case TYA: state->a = state->y; set_NZ_flags(state, state->a); break; - case TSX: state->x = state->sp; set_NZ_flags(state, state->x); break; //TODO test - case TXS: state->sp = state->x; set_NZ_flags(state, state->x); break; //TODO test - case CMP_IMM: unimplemented_instruction(state); break; - case CMP_ZP: unimplemented_instruction(state); break; - case CMP_ZPX: unimplemented_instruction(state); break; - case CMP_ABS: unimplemented_instruction(state); break; - case CMP_ABSX: unimplemented_instruction(state); break; - case CMP_ABSY: unimplemented_instruction(state); break; - case CMP_INDX: unimplemented_instruction(state); break; - case CMP_INDY: unimplemented_instruction(state); break; - case CPX_IMM: unimplemented_instruction(state); break; - case CPX_ZP: unimplemented_instruction(state); break; - case CPX_ABS: unimplemented_instruction(state); break; - case CPY_IMM: unimplemented_instruction(state); break; - case CPY_ZP: unimplemented_instruction(state); break; - case CPY_ABS: unimplemented_instruction(state); break; + case TSX: state->x = state->sp; set_NZ_flags(state, state->x); break; + case TXS: state->sp = state->x; set_NZ_flags(state, state->x); break; + case CMP_IMM: CMP(state, pop_byte(state)); break; + case CMP_ZP: CMP(state, get_byte_zero_page(state)); break; + case CMP_ZPX: CMP(state, get_byte_zero_page_x(state)); break; //TODO test + case CMP_ABS: CMP(state, get_byte_absolute(state)); break;//TODO test + case CMP_ABSX: CMP(state, get_byte_absolute_x(state)); break;//TODO test + case CMP_ABSY: CMP(state, get_byte_absolute_y(state)); break;//TODO test + case CMP_INDX: CMP(state, get_byte_indirect_x(state)); break;//TODO test + case CMP_INDY: CMP(state, get_byte_indirect_y(state)); break;//TODO test + case CPX_IMM: CPX(state, pop_byte(state)); break;//TODO test + case CPX_ZP: CPX(state, get_byte_zero_page(state)); break;//TODO test + case CPX_ABS: CPX(state, get_byte_absolute(state)); break;//TODO test + case CPY_IMM: CPY(state, pop_byte(state)); break;//TODO test + case CPY_ZP: CPY(state, get_byte_zero_page(state)); break;//TODO test + case CPY_ABS: CPY(state, get_byte_absolute(state)); break;//TODO test case DEC_ZP: DEC(state, get_address_zero_page(state)); break; case DEC_ZPX: DEC(state, get_address_zero_page_x(state)); break; case DEC_ABS: DEC(state, get_address_absolute(state)); break; diff --git a/test6502.c b/test6502.c index 9de5d30..7f6d0c3 100644 --- a/test6502.c +++ b/test6502.c @@ -7,7 +7,9 @@ #include "state.h" #include "disassembler.h" #include "cpu.h" - +#ifdef _DEBUG +#include +#endif void print_state(State6502* state) { printf("\tC=%d,Z=%d,I=%d,D=%d,B=%d,V=%d,N=%d\n", state->flags.c, state->flags.z, state->flags.i, state->flags.d, state->flags.b, state->flags.v, state->flags.n); @@ -54,10 +56,17 @@ State6502 create_blank_state() { return state; } +void exit_or_break() { +#ifdef _DEBUG + raise(SIGINT); +#endif + exit(1); +} + void assert_register(State6502 * state, byte expected, byte actual, char* name) { if (actual != expected) { printf("Unexpected value in %s, expected %02X, was %02X", name, expected, actual); - exit(1); + exit_or_break(); } } @@ -80,7 +89,7 @@ void assert_sp(State6502 * state, byte expected) { void assert_pc(State6502 * state, word expected) { if (state->pc != expected) { printf("Unexpected value in SP, expected %02X, was %02X", expected, state->pc); - exit(1); + exit_or_break(); } } @@ -88,57 +97,43 @@ void assert_pc(State6502 * state, word expected) { void assert_memory(State6502 * state, word address, byte expected) { if (state->memory[address] != expected) { printf("Unexpected value in $%04X, expected %02X, was %02X", address, expected, state->memory[address]); - exit(1); + exit_or_break(); + } +} + +void assert_flag(byte flag_value, byte expected, char* flag_name) { + if (flag_value != expected) { + printf("Unexpected value in flag %s, expected %d, was %d", flag_name, expected, flag_value); + exit_or_break(); } } void assert_flag_n(State6502 * state, byte expected) { - if (state->flags.n != expected) { - printf("Unexpected value in flag N, expected %d, was %d", expected, state->flags.n); - exit(1); - } + assert_flag(state->flags.n, expected, "N"); } void assert_flag_z(State6502 * state, byte expected) { - if (state->flags.z != expected) { - printf("Unexpected value in flag Z, expected %d, was %d", expected, state->flags.z); - exit(1); - } + assert_flag(state->flags.z, expected, "Z"); } void assert_flag_c(State6502 * state, byte expected) { - if (state->flags.c != expected) { - printf("Unexpected value in flag C, expected %d, was %d", expected, state->flags.c); - exit(1); - } + assert_flag(state->flags.c, expected, "C"); } void assert_flag_i(State6502 * state, byte expected) { - if (state->flags.i != expected) { - printf("Unexpected value in flag I, expected %d, was %d", expected, state->flags.i); - exit(1); - } + assert_flag(state->flags.i, expected, "I"); } void assert_flag_d(State6502 * state, byte expected) { - if (state->flags.d != expected) { - printf("Unexpected value in flag D, expected %d, was %d", expected, state->flags.d); - exit(1); - } + assert_flag(state->flags.d, expected, "D"); } void assert_flag_v(State6502 * state, byte expected) { - if (state->flags.v != expected) { - printf("Unexpected value in flag D, expected %d, was %d", expected, state->flags.v); - exit(1); - } + assert_flag(state->flags.v, expected, "V"); } -void assert_flag_b(State6502* state, byte expected) { - if (state->flags.v != expected) { - printf("Unexpected value in flag D, expected %d, was %d", expected, state->flags.v); - exit(1); - } +void assert_flag_b(State6502 * state, byte expected) { + assert_flag(state->flags.b, expected, "B"); } //////////////////////////////////////// @@ -1704,7 +1699,7 @@ void test_PLA() { //initialize State6502 state = create_blank_state(); state.sp = 0xFE; - state.memory[0x1FE] = 0xBB; + state.memory[0x1FF] = 0xBB; //arrange char program[] = { PLA }; @@ -1807,6 +1802,7 @@ void test_PLP() { //arrange char program[] = { PLP }; memcpy(state.memory, program, sizeof(program)); + state.memory[0x1FF] = 0xFF; //all flags should be on //act test_step(&state); @@ -1850,18 +1846,16 @@ void test_JMP_IND() { State6502 state = create_blank_state(); //arrange - char program[] = { JMP_IND, 0xFF, 0x02 }; + char program[] = { JMP_IND, 0x04, 0x02 }; memcpy(state.memory, program, sizeof(program)); - state.memory[0x02FF] = 0x01; - state.memory[0x0200] = 0xAA; - state.memory[0x01AA] = 0xFF; //target - //NOT state.memory[0x03FF] ! as there is no wrap + state.memory[0x0204] = 0xAA; + state.memory[0x0205] = 0x01; //act test_step(&state); //assert - assert_pc(&state, 0x1AA); + assert_pc(&state, 0x01AA); //cleanup test_cleanup(&state); @@ -1874,10 +1868,9 @@ void test_JMP_IND_wrap() { //arrange char program[] = { JMP_IND, 0xFF, 0x02 }; memcpy(state.memory, program, sizeof(program)); - state.memory[0x02FF] = 0x01; - state.memory[0x0200] = 0xAA; - state.memory[0x01AA] = 0xFF; //target - //NOT state.memory[0x03FF] ! as there is no wrap + state.memory[0x02FF] = 0xAA; + state.memory[0x0200] = 0x01; + state.memory[0x0300] = 0xFF; //NOT this one! //act test_step(&state); @@ -1889,6 +1882,74 @@ void test_JMP_IND_wrap() { test_cleanup(&state); } +// CMP, CPX, CPY + +void test_CMP_ABS_equal() { + State6502 state = create_blank_state(); + state.a = 0x1A; + + char program[] = { CMP_ABS, 0x45, 0x03 }; + memcpy(state.memory, program, sizeof(program)); + state.memory[0x0345] = 0x1A; + test_step(&state); + + assert_flag_z(&state, 0x01); + assert_flag_n(&state, 0x00); + assert_flag_c(&state, 0x01); + + test_cleanup(&state); +} + +void test_CMP_ABS_greater() { + State6502 state = create_blank_state(); + state.a = 0x30; + + char program[] = { CMP_ABS, 0x45, 0x03 }; + memcpy(state.memory, program, sizeof(program)); + state.memory[0x0345] = 0x1A; + test_step(&state); + + assert_flag_z(&state, 0x00); + assert_flag_n(&state, 0x00); + assert_flag_c(&state, 0x01); //as A > memory + + test_cleanup(&state); +} + +void test_CMP_ABS_greater_2() { + State6502 state = create_blank_state(); + state.a = 0x82; + state.flags.n = 1; + + char program[] = { CMP_ABS, 0x45, 0x03 }; + memcpy(state.memory, program, sizeof(program)); + state.memory[0x0345] = 0x1A; + test_step(&state); + + assert_flag_z(&state, 0x00); // 0x82 != 0x1A + assert_flag_n(&state, 0x00); + assert_flag_c(&state, 0x01); // 0x82 > 0x1A + + test_cleanup(&state); +} + + +void test_CMP_ABS_less_than() { + State6502 state = create_blank_state(); + state.a = 0x08; + + char program[] = { CMP_ABS, 0x45, 0x03 }; + memcpy(state.memory, program, sizeof(program)); + state.memory[0x0345] = 0x1A; + test_step(&state); + + assert_flag_z(&state, 0x00); + assert_flag_n(&state, 0x01); + assert_flag_c(&state, 0x00); + + test_cleanup(&state); +} + ///////////////////// typedef void fp(); @@ -1908,7 +1969,8 @@ fp* tests_sta[] = { test_STA_ZP, test_STA_ZPX, test_STA_ABS, test_STA_ABSX, test fp* tests_pha_pla[] = { test_PHA, test_PLA, test_PHA_PLA }; fp* tests_txs_tsx[] = { test_TXS, test_TSX }; fp* tests_php_plp[] = { test_PHP, test_PLP }; -fp* tests_jmp[] = { test_JMP, test_JMP_IND }; +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 }; #define RUN(suite) run_suite(suite, sizeof(suite)/sizeof(fp*)) @@ -1936,4 +1998,5 @@ void run_tests() { RUN(tests_txs_tsx); RUN(tests_jmp); RUN(tests_php_plp); + RUN(tests_cmp); } \ No newline at end of file