fixed zero flag handling in LDA, LDX, LDY

added BEQ+test
split memory function to a separate module
This commit is contained in:
jborza 2019-04-27 12:43:15 +02:00
parent 7b0a3a0de9
commit 2ddcf62212
6 changed files with 254 additions and 115 deletions

120
cpu.c
View File

@ -1,6 +1,7 @@
#include "state.h"
#include "cpu.h"
#include "opcodes.h"
#include "memory.h"
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
@ -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;

View File

@ -154,12 +154,14 @@
<ClCompile Include="cpu.c" />
<ClCompile Include="disassembler.c" />
<ClCompile Include="emu6502.c" />
<ClCompile Include="memory.c" />
<ClCompile Include="test6502.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="cpu.h" />
<ClInclude Include="disassembler.h" />
<ClInclude Include="flags.h" />
<ClInclude Include="memory.h" />
<ClInclude Include="opcodes.h" />
<ClInclude Include="state.h" />
<ClInclude Include="test6502.h" />

111
memory.c Normal file
View File

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

40
memory.h Normal file
View File

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

View File

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

View File

@ -3,4 +3,5 @@
//typedef unsigned __int8 uint8_t;
//typedef unsigned __int16 uint16_t;
typedef uint8_t byte;
typedef uint16_t word;
typedef uint16_t word;
typedef int8_t signed_byte;