1
0
mirror of https://github.com/pevans/erc-c.git synced 2024-12-21 08:30:55 +00:00

Add functions for instructions, cycles, and address modes.

This also adds a new table for cycles, and adds unit test functions for
the work as well.
This commit is contained in:
Peter Evans 2017-12-04 23:30:18 -06:00
parent 87de0a877a
commit 54473be302
5 changed files with 131 additions and 1 deletions

View File

@ -46,6 +46,21 @@ typedef struct {
// we found the value we care about.
vm_16bit last_addr;
/*
* This field contains the number of CPU cycles that the last
* instruction handled should consume. In order to accurately
* emulate any architecture, we must model the type of "wait" time
* each instruction would cause.
*
* It should also be pointed out that the number of cycles is both
* informed by the instruction _and_ the address mode. For example,
* an instruction executed in zero-page address mode would consume
* fewer cycles than one executed in absolute address mode, because
* in the latter, the CPU would have to read ahead to discover a
* 16-bit operand vs. the 8-bit operand in the former.
*/
int cycles;
// Our program counter register; this is what we'll use to determine
// where we're "at" in memory while executing opcodes. We use a
// 16-bit register because our memory is 64k large.
@ -84,7 +99,13 @@ extern void mos6502_push_stack(mos6502 *, vm_16bit);
extern vm_16bit mos6502_pop_stack(mos6502 *);
extern void mos6502_set_status(mos6502 *, vm_8bit);
extern void mos6502_modify_status(mos6502 *, vm_8bit, vm_8bit);
extern int mos6502_cycles(mos6502 *, vm_8bit);
extern int mos6502_instruction(vm_8bit);
/*
* Below are some functions that are defined in mos6502.addr.c
*/
extern int mos6502_addr_mode(vm_8bit);
extern mos6502_address_resolver mos6502_get_address_resolver(int);
/*
@ -129,7 +150,8 @@ DECL_ADDR_MODE(zpx);
DECL_ADDR_MODE(zpy);
/*
* And now, our instruction handlers
* And now, our instruction handlers; held generally in mos6502.*.c
* (excepting mos6502.addr.c).
*/
DECL_INST(adc);
DECL_INST(and);

View File

@ -84,6 +84,12 @@ mos6502_get_address_resolver(int addr_mode)
vm_16bit eff_addr = addr; \
cpu->last_addr = eff_addr
int
mos6502_addr_mode(vm_8bit opcode)
{
return addr_modes[opcode];
}
/*
* In the ACC address mode, the instruction will consider just the A
* register. (It's probably the simplest resolution mode for us to

View File

@ -49,6 +49,26 @@ static int instructions[] = {
BEQ, SBC, NOP, NOP, NOP, SBC, INC, NOP, SED, SBC, NOP, NOP, NOP, SBC, INC, NOP, // Fx
};
static int cycles[] = {
// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0, // 0x
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 1x
6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0, // 2x
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 3x
6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0, // 4x
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 5x
6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0, // 6x
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 7x
0, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0, // 8x
2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0, // 9x
2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0, // Ax
2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0, // Bx
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 3, 0, // Cx
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // Dx
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // Ex
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // Fx
};
/*
* Build a new mos6502 struct object, and also build the memory contents
* used therein. All registers should be zeroed out.
@ -169,3 +189,50 @@ mos6502_modify_status(mos6502 *cpu, vm_8bit status, vm_8bit oper)
}
}
}
int
mos6502_instruction(vm_8bit opcode)
{
return instructions[opcode];
}
int
mos6502_cycles(mos6502 *cpu, vm_8bit opcode)
{
/*
* In some contexts, we may need to return an additional cycle.
*/
int modif = 0;
int addr_mode;
int lo_addr;
addr_mode = mos6502_addr_mode(opcode);
// Mainly we care about the lo byte of the last effective address
lo_addr = cpu->last_addr & 0xFF;
// Ok, here's the deal: if you are using an address mode that uses
// any of the index registers, you need to return an additional
// cycle if the lo byte of the address plus that index would cross a
// memory page boundary
switch (addr_mode) {
case ABX:
if (lo_addr + cpu->X > 255) {
modif = 1;
}
break;
case ABY:
case INY:
if (lo_addr + cpu->Y > 255) {
modif = 1;
}
break;
default:
break;
}
return cycles[opcode] + modif;
}

View File

@ -3,6 +3,13 @@
#include "mos6502.h"
#include "mos6502.enums.h"
Test(mos6502, addr_mode)
{
cr_assert_eq(mos6502_addr_mode(0xEA), IMP);
cr_assert_eq(mos6502_addr_mode(0xD6), ZPX);
cr_assert_eq(mos6502_addr_mode(0xF0), REL);
}
Test(mos6502, get_address_resolver) {
INIT_ADDR_MODE();

View File

@ -93,3 +93,31 @@ Test(mos6502, set_status)
END_CPU_TEST(mos6502);
}
Test(mos6502, instruction)
{
cr_assert_eq(mos6502_instruction(0x1D), ORA);
cr_assert_eq(mos6502_instruction(0xD8), CLD);
cr_assert_eq(mos6502_instruction(0x98), TYA);
}
Test(mos6502, cycles)
{
START_CPU_TEST(mos6502);
cr_assert_eq(mos6502_cycles(cpu, 0x76), 6);
cr_assert_eq(mos6502_cycles(cpu, 0xBA), 2);
// In this case, we aren't cross a page boundary, and the number of
// cycles should stay at 4
cpu->last_addr = 0x5070;
cpu->X = 23;
cr_assert_eq(mos6502_cycles(cpu, 0x1D), 4);
// Testing that crossing a page boundary adds one to the number of
// cycles
cpu->X = 200;
cr_assert_eq(mos6502_cycles(cpu, 0x1D), 5);
END_CPU_TEST(mos6502);
}