diff --git a/cpu.c b/cpu.c index 4ae12a0..01b2e0c 100644 --- a/cpu.c +++ b/cpu.c @@ -1,6 +1,7 @@ #include "state.h" #include "cpu.h" #include "opcodes.h" +#include "memory.h" #include #include #include @@ -93,17 +94,17 @@ void AND(State6502 * state, byte operand) { //load accumulator void LDA(State6502 * state, byte operand) { state->a = operand; - set_NV_flags(state, state->a); + set_NZ_flags(state, state->a); } void LDX(State6502 * state, byte operand) { state->x = operand; - set_NV_flags(state, state->x); + set_NZ_flags(state, state->x); } void LDY(State6502 * state, byte operand) { state->y = operand; - set_NV_flags(state, state->y); + set_NZ_flags(state, state->y); } void STA(State6502 * state, word address) { @@ -278,6 +279,12 @@ void RTS_(State6502* state) { state->pc = address + 1; } +void BEQ(State6502* state) { + word address = get_address_relative(state); + if (state->flags.z) + state->pc = address; +} + word pop_word(State6502 * state) { byte low = pop_byte(state); byte high = pop_byte(state); @@ -285,111 +292,6 @@ word pop_word(State6502 * state) { return result; } -word read_word(State6502 * state, word address) { - return state->memory[address] | state->memory[address + 1] << 8; -} - -word get_address_zero_page(State6502 * state) { - return pop_byte(state); -} - -byte get_byte_zero_page(State6502 * state) { - //8 bit addressing, only the first 256 bytes of the memory - return state->memory[get_address_zero_page(state)]; -} - -word get_address_zero_page_x(State6502 * state) { - //address is zero page, so wraparound byte - byte address = pop_byte(state) + state->x; - return address; -} - -byte get_byte_zero_page_x(State6502 * state) { - return state->memory[get_address_zero_page_x(state)]; -} - -word get_address_zero_page_y(State6502 * state) { - //address is zero page, so wraparound byte - byte address = pop_byte(state) + state->y; - return address; -} - -byte get_byte_zero_page_y(State6502 * state) { - return state->memory[get_address_zero_page_y(state)]; -} - -word get_address_absolute(State6502 * state) { - //absolute indexed, 16 bits - word address = pop_word(state); - return address; -} - -byte get_byte_absolute(State6502 * state) -{ - //absolute indexed, 16 bits - return state->memory[get_address_absolute(state)]; -} - -word get_address_absolute_x(State6502 * state) { - //absolute added with the contents of x register - word address = pop_word(state) + state->x; - return address; -} - -byte get_byte_absolute_x(State6502 * state) { - return state->memory[get_address_absolute_x(state)]; -} - -word get_address_absolute_y(State6502 * state) { - //absolute added with the contents of x register - word address = pop_word(state) + state->y; - return address; -} - -byte get_byte_absolute_y(State6502 * state) { - //absolute added with the contents of y register - return state->memory[get_address_absolute_y(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); - if ((indirect_address & 0xFF) == 0xFF) { - //avoid crossing the page boundary - return state->memory[indirect_address] | state->memory[indirect_address - 0xFF] << 8; - } - else { - return read_word(state, indirect_address); - } -} - -word get_address_indirect_x(State6502 * state) { - //pre-indexed indirect with the X register - //zero-page address is added to x register - byte indirect_address = pop_byte(state) + state->x; - //pointing to address of a word holding the address of the operand - word address = read_word(state, indirect_address); - return address; -} - -byte get_byte_indirect_x(State6502 * state) { - //pre-indexed indirect with the X register - return state->memory[get_address_indirect_x(state)]; -} - -word get_address_indirect_y(State6502 * state) { - //post-indexed indirect - //zero-page address as an argument - byte indirect_address = pop_byte(state); - //the address and the following byte is read as a word, adding Y register - word address = read_word(state, indirect_address) + state->y; - return address; -} - -byte get_byte_indirect_y(State6502 * state) { - return state->memory[get_address_indirect_y(state)]; -} - int emulate_6502_op(State6502 * state) { byte* opcode = &state->memory[state->pc++]; switch (*opcode) { @@ -416,7 +318,7 @@ int emulate_6502_op(State6502 * state) { case ASL_ABSX: ASL_MEM(state, get_address_absolute_x(state)); break; case BCC_REL: unimplemented_instruction(state); break; case BCS_REL: unimplemented_instruction(state); break; - case BEQ_REL: unimplemented_instruction(state); break; + case BEQ_REL: BEQ(state); break; case BMI_REL: unimplemented_instruction(state); break; case BNE_REL: unimplemented_instruction(state); break; case BPL_REL: unimplemented_instruction(state); break; diff --git a/emu6502.vcxproj b/emu6502.vcxproj index a084356..488157f 100644 --- a/emu6502.vcxproj +++ b/emu6502.vcxproj @@ -154,12 +154,14 @@ + + diff --git a/memory.c b/memory.c new file mode 100644 index 0000000..1d35808 --- /dev/null +++ b/memory.c @@ -0,0 +1,111 @@ +#include "memory.h" + +word read_word(State6502* state, word address) { + return state->memory[address] | state->memory[address + 1] << 8; +} + +word get_address_zero_page(State6502* state) { + return pop_byte(state); +} + +byte get_byte_zero_page(State6502* state) { + //8 bit addressing, only the first 256 bytes of the memory + return state->memory[get_address_zero_page(state)]; +} + +word get_address_zero_page_x(State6502* state) { + //address is zero page, so wraparound byte + byte address = pop_byte(state) + state->x; + return address; +} + +byte get_byte_zero_page_x(State6502* state) { + return state->memory[get_address_zero_page_x(state)]; +} + +word get_address_zero_page_y(State6502* state) { + //address is zero page, so wraparound byte + byte address = pop_byte(state) + state->y; + return address; +} + +byte get_byte_zero_page_y(State6502* state) { + return state->memory[get_address_zero_page_y(state)]; +} + +word get_address_absolute(State6502* state) { + //absolute indexed, 16 bits + word address = pop_word(state); + return address; +} + +byte get_byte_absolute(State6502* state) +{ + //absolute indexed, 16 bits + return state->memory[get_address_absolute(state)]; +} + +word get_address_absolute_x(State6502* state) { + //absolute added with the contents of x register + word address = pop_word(state) + state->x; + return address; +} + +byte get_byte_absolute_x(State6502* state) { + return state->memory[get_address_absolute_x(state)]; +} + +word get_address_absolute_y(State6502* state) { + //absolute added with the contents of x register + word address = pop_word(state) + state->y; + return address; +} + +byte get_byte_absolute_y(State6502* state) { + //absolute added with the contents of y register + return state->memory[get_address_absolute_y(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); + if ((indirect_address & 0xFF) == 0xFF) { + //avoid crossing the page boundary + return state->memory[indirect_address] | state->memory[indirect_address - 0xFF] << 8; + } + else { + return read_word(state, indirect_address); + } +} + +word get_address_indirect_x(State6502* state) { + //pre-indexed indirect with the X register + //zero-page address is added to x register + byte indirect_address = pop_byte(state) + state->x; + //pointing to address of a word holding the address of the operand + word address = read_word(state, indirect_address); + return address; +} + +byte get_byte_indirect_x(State6502* state) { + //pre-indexed indirect with the X register + return state->memory[get_address_indirect_x(state)]; +} + +word get_address_indirect_y(State6502* state) { + //post-indexed indirect + //zero-page address as an argument + byte indirect_address = pop_byte(state); + //the address and the following byte is read as a word, adding Y register + word address = read_word(state, indirect_address) + state->y; + return address; +} + +byte get_byte_indirect_y(State6502* state) { + return state->memory[get_address_indirect_y(state)]; +} + +word get_address_relative(State6502* state) { + int8_t address = (int8_t)pop_byte(state); + return state->pc + address; +} \ No newline at end of file diff --git a/memory.h b/memory.h new file mode 100644 index 0000000..2911644 --- /dev/null +++ b/memory.h @@ -0,0 +1,40 @@ +#pragma once +#include "state.h" + +word read_word(State6502* state, word address); + +word get_address_zero_page(State6502* state); + +byte get_byte_zero_page(State6502* state); + +word get_address_zero_page_x(State6502* state); + +byte get_byte_zero_page_x(State6502* state); + +word get_address_zero_page_y(State6502* state); + +byte get_byte_zero_page_y(State6502* state); + +word get_address_absolute(State6502* state); + +byte get_byte_absolute(State6502* state); + +word get_address_absolute_x(State6502* state); + +byte get_byte_absolute_x(State6502* state); + +word get_address_absolute_y(State6502* state); + +byte get_byte_absolute_y(State6502* state); + +word get_address_indirect_jmp(State6502* state); + +word get_address_indirect_x(State6502* state); + +byte get_byte_indirect_x(State6502* state); + +word get_address_indirect_y(State6502* state); + +byte get_byte_indirect_y(State6502* state); + +word get_address_relative(State6502* state); \ No newline at end of file diff --git a/test6502.c b/test6502.c index 465bc8a..2b626db 100644 --- a/test6502.c +++ b/test6502.c @@ -163,10 +163,30 @@ void test_LDA_IMM() { //assert assertA(&state, 0xAA); + assert_flag_n(&state, 1); test_cleanup(&state); } +void test_LDA_IMM_zero() { + //initialize + State6502 state = create_blank_state(); + + //arrange + char program[] = { LDA_IMM, 0x00 }; + memcpy(state.memory, program, sizeof(program)); + + //act + test_step(&state); + + //assert + assertA(&state, 0x00); + assert_flag_z(&state, 1); + + test_cleanup(&state); +} + + void test_LDA_ZP() { //initialize State6502 state = create_blank_state(); @@ -843,6 +863,25 @@ void test_LDX_IMM() { //assert assertX(&state, 0xAA); + assert_flag_n(&state, 1); + + test_cleanup(&state); +} + +void test_LDX_IMM_zero() { + //initialize + State6502 state = create_blank_state(); + + //arrange + char program[] = { LDX_IMM, 0x00 }; //LDX #$AA + memcpy(state.memory, program, sizeof(program)); + + //act + test_step(&state); + + //assert + assertX(&state, 0x00); + assert_flag_z(&state, 1); test_cleanup(&state); } @@ -938,6 +977,25 @@ void test_LDY_IMM() { //assert assertY(&state, 0xAA); + assert_flag_n(&state, 1); + + test_cleanup(&state); +} + +void test_LDY_IMM_zero() { + //initialize + State6502 state = create_blank_state(); + + //arrange + char program[] = { LDY_IMM, 0x00 }; + memcpy(state.memory, program, sizeof(program)); + + //act + test_step(&state); + + //assert + assertY(&state, 0x00); + assert_flag_z(&state, 1); test_cleanup(&state); } @@ -2203,14 +2261,38 @@ void test_BRK() { assert_flag_b(&state, 1); } +//BEQ, BCC, ... + +void test_BEQ() { + State6502 state = create_blank_state(); + char program[] = { LDX_IMM, 0x00, BEQ_REL, 0xFC }; + memcpy(state.memory, program, sizeof(program)); + //act + test_step(&state); + test_step(&state); + //assert + assert_pc(&state, 0x00); +} + +void test_BEQ_skip() { + State6502 state = create_blank_state(); + char program[] = { LDX_IMM, 0x01, BEQ_REL, 0xFC }; + memcpy(state.memory, program, sizeof(program)); + //act + test_step(&state); + test_step(&state); + //assert + assert_pc(&state, 0x04); +} + ///////////////////// typedef void fp(); -fp* tests_lda[] = { test_LDA_IMM, test_LDA_ZP, test_LDA_ZPX, test_LDA_ZPX_wraparound, test_LDA_ABS, test_LDA_ABSX, test_LDA_ABSY, test_LDA_INDX, test_LDA_INDY }; +fp* tests_lda[] = { test_LDA_IMM, test_LDA_IMM_zero, test_LDA_ZP, test_LDA_ZPX, test_LDA_ZPX_wraparound, test_LDA_ABS, test_LDA_ABSX, test_LDA_ABSY, test_LDA_INDX, test_LDA_INDY }; fp* tests_ora[] = { test_ORA_IMM, test_ORA_ZP, test_ORA_ZPX, test_ORA_ABS, test_ORA_ABSX, test_ORA_ABSY, test_ORA_INDX, test_ORA_INDY }; fp* tests_and[] = { test_AND_IMM, test_AND_ZP, test_AND_ZPX, test_AND_ABS, test_AND_ABSX, test_AND_ABSY, test_AND_INDX, test_AND_INDY }; -fp* tests_ldx[] = { test_LDX_IMM, test_LDX_ZP, test_LDX_ZPY, test_LDX_ABS, test_LDX_ABSY }; -fp* tests_ldy[] = { test_LDY_IMM, test_LDY_ZP, test_LDY_ZPX, test_LDY_ABS, test_LDY_ABSX }; +fp* tests_ldx[] = { test_LDX_IMM, test_LDX_IMM_zero, test_LDX_ZP, test_LDX_ZPY, test_LDX_ABS, test_LDX_ABSY }; +fp* tests_ldy[] = { test_LDY_IMM, test_LDY_IMM_zero, test_LDY_ZP, test_LDY_ZPX, test_LDY_ABS, test_LDY_ABSX }; fp* tests_stx[] = { test_STX_ZP, test_STX_ZPY, test_STX_ABS }; fp* tests_sty[] = { test_STY_ZP, test_STY_ZPX, test_STY_ABS }; fp* tests_inx_iny_dex_dey[] = { test_DEX, test_DEX_wraparound, test_DEY, test_DEY_wraparound, test_INX, test_INX_wraparound, test_INY, test_INY_wraparound }; @@ -2229,6 +2311,7 @@ fp* tests_adc[] = { test_ADC_IMM_multiple }; fp* tests_bit[] = { test_BIT_multiple }; fp* tests_jsr_rts[] = { test_JSR, test_JSR_RTS }; fp* tests_brk[] = { test_BRK }; +fp* tests_branch[] = { test_BEQ, test_BEQ_skip }; #define RUN(suite) run_suite(suite, sizeof(suite)/sizeof(fp*)) @@ -2241,16 +2324,16 @@ void run_suite(fp * *suite, int size) { } void run_tests() { + RUN(tests_branch); RUN(tests_sbc); RUN(tests_brk); RUN(tests_jsr_rts); RUN(tests_bit); RUN(tests_adc); - RUN(tests_lda); RUN(tests_ora); RUN(tests_and); - RUN(tests_ldx); RUN(tests_lda); + RUN(tests_ldx); RUN(tests_ldy); RUN(tests_stx); RUN(tests_sty); diff --git a/types.h b/types.h index 0e18a7f..3b1a7c9 100644 --- a/types.h +++ b/types.h @@ -3,4 +3,5 @@ //typedef unsigned __int8 uint8_t; //typedef unsigned __int16 uint16_t; typedef uint8_t byte; -typedef uint16_t word; \ No newline at end of file +typedef uint16_t word; +typedef int8_t signed_byte; \ No newline at end of file