mirror of
https://github.com/jborza/emu6502.git
synced 2025-02-19 07:30:57 +00:00
fixed pop_byte_from_stack, fixed JMP_IND, added tests for CMP
This commit is contained in:
parent
285bb840af
commit
df2f125588
70
cpu.c
70
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;
|
||||
|
153
test6502.c
153
test6502.c
@ -7,7 +7,9 @@
|
||||
#include "state.h"
|
||||
#include "disassembler.h"
|
||||
#include "cpu.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
#include <signal.h>
|
||||
#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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user