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:
parent
87de0a877a
commit
54473be302
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user