mirror of
https://github.com/rkujawa/rk65c02.git
synced 2025-04-13 08:37:02 +00:00
Preliminary support for emulation of instructions.
Some refactoring while here.
This commit is contained in:
parent
f106e227cd
commit
52ce9bff8c
@ -217,7 +217,7 @@ OP_SMB5_ZP,"smb5",ZP,2,NULL
|
||||
OP_CLD,"cld",IMPLIED,1,NULL
|
||||
OP_CMP_ABSY,"cmp",ABSOLUTEY,3,NULL
|
||||
OP_PHX,"phx",IMPLIED,1,NULL
|
||||
OP_STP,"stp",IMPLIED,1,NULL
|
||||
OP_STP,"stp",IMPLIED,1,emul_stp
|
||||
OP_NOPI,"nop",ABSOLUTE,3,NULL
|
||||
OP_CMP_ABSX,"cmp",ABSOLUTEX,3,NULL
|
||||
OP_DEC_ABSX,"dec",ABSOLUTEX,3,NULL
|
||||
|
|
@ -3,8 +3,15 @@
|
||||
#include "emulation.h"
|
||||
|
||||
void
|
||||
emul_nop(void)
|
||||
emul_nop(rk65c02emu_t *e, instruction_t *i)
|
||||
{
|
||||
printf("nop!\n");
|
||||
printf("nop!\n");
|
||||
printf("nop!\n");
|
||||
}
|
||||
|
||||
void
|
||||
emul_stp(rk65c02emu_t *e, instruction_t *i)
|
||||
{
|
||||
e->state = STOPPED;
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
#ifndef _EMULATION_H_
|
||||
#define _EMULATION_H_
|
||||
|
||||
void emul_nop(void);
|
||||
#include "rk65c02.h"
|
||||
#include "instruction.h"
|
||||
|
||||
void emul_nop(rk65c02emu_t *, instruction_t *);
|
||||
void emul_stp(rk65c02emu_t *, instruction_t *);
|
||||
|
||||
#endif /* _EMULATION_H_*/
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "bus.h"
|
||||
#include "rk65c02.h"
|
||||
#include "65c02isa.h"
|
||||
#include "instruction.h"
|
||||
|
||||
@ -14,15 +15,15 @@ instruction_t
|
||||
instruction_fetch(bus_t *b, uint16_t addr)
|
||||
{
|
||||
instruction_t i;
|
||||
uint8_t op;
|
||||
instrdef_t id;
|
||||
|
||||
op = bus_read_1(b, addr);
|
||||
i.def = instrdef_get(op);
|
||||
i.opcode = bus_read_1(b, addr);
|
||||
id = instruction_decode(i.opcode);
|
||||
|
||||
//assert(i.def.opcode != OP_UNIMPL);
|
||||
|
||||
/* handle operands */
|
||||
switch (i.def.mode) {
|
||||
switch (id.mode) {
|
||||
case IMMEDIATE:
|
||||
case ZP:
|
||||
case ZPX:
|
||||
@ -49,54 +50,64 @@ instruction_fetch(bus_t *b, uint16_t addr)
|
||||
return i;
|
||||
}
|
||||
|
||||
/*void
|
||||
instruction_execute(rk65c02emu_t *e, instruction_t *i)
|
||||
{
|
||||
id.emul();
|
||||
e->regs.PC += id.size;
|
||||
}*/
|
||||
|
||||
void
|
||||
instruction_print(instruction_t *i)
|
||||
{
|
||||
switch (i->def.mode) {
|
||||
instrdef_t id;
|
||||
|
||||
id = instruction_decode(i->opcode);
|
||||
switch (id.mode) {
|
||||
case IMPLIED:
|
||||
printf("%s", i->def.mnemonic);
|
||||
printf("%s", id.mnemonic);
|
||||
break;
|
||||
case ACCUMULATOR:
|
||||
printf("%s A", i->def.mnemonic);
|
||||
printf("%s A", id.mnemonic);
|
||||
break;
|
||||
case IMMEDIATE:
|
||||
printf("%s #%X", i->def.mnemonic, i->op1);
|
||||
printf("%s #%X", id.mnemonic, i->op1);
|
||||
break;
|
||||
case ZP:
|
||||
printf("%s %X", i->def.mnemonic, i->op1);
|
||||
printf("%s %X", id.mnemonic, i->op1);
|
||||
break;
|
||||
case ZPX:
|
||||
printf("%s %X,X", i->def.mnemonic, i->op1);
|
||||
printf("%s %X,X", id.mnemonic, i->op1);
|
||||
break;
|
||||
case ZPY:
|
||||
printf("%s %X,Y", i->def.mnemonic, i->op1);
|
||||
printf("%s %X,Y", id.mnemonic, i->op1);
|
||||
break;
|
||||
case IZP:
|
||||
printf("%s (%X)", i->def.mnemonic, i->op1);
|
||||
printf("%s (%X)", id.mnemonic, i->op1);
|
||||
break;
|
||||
case IZPX:
|
||||
printf("%s (%X,X)", i->def.mnemonic, i->op1);
|
||||
printf("%s (%X,X)", id.mnemonic, i->op1);
|
||||
break;
|
||||
case IZPY:
|
||||
printf("%s (%X),Y", i->def.mnemonic, i->op1);
|
||||
printf("%s (%X),Y", id.mnemonic, i->op1);
|
||||
break;
|
||||
case ABSOLUTE:
|
||||
printf("%s %02X%02X", i->def.mnemonic, i->op2, i->op1);
|
||||
printf("%s %02X%02X", id.mnemonic, i->op2, i->op1);
|
||||
break;
|
||||
case ABSOLUTEX:
|
||||
printf("%s %02X%02X,X", i->def.mnemonic, i->op2, i->op1);
|
||||
printf("%s %02X%02X,X", id.mnemonic, i->op2, i->op1);
|
||||
break;
|
||||
case ABSOLUTEY:
|
||||
printf("%s %02X%02X,Y", i->def.mnemonic, i->op2, i->op1);
|
||||
printf("%s %02X%02X,Y", id.mnemonic, i->op2, i->op1);
|
||||
break;
|
||||
case IABSOLUTE:
|
||||
printf("%s (%02X%02X)", i->def.mnemonic, i->op2, i->op1);
|
||||
printf("%s (%02X%02X)", id.mnemonic, i->op2, i->op1);
|
||||
break;
|
||||
case IABSOLUTEX:
|
||||
printf("%s (%02X%02X,X)", i->def.mnemonic, i->op2, i->op1);
|
||||
printf("%s (%02X%02X,X)", id.mnemonic, i->op2, i->op1);
|
||||
break;
|
||||
case RELATIVE:
|
||||
printf("%s %02X%02X", i->def.mnemonic, i->op2, i->op1);
|
||||
printf("%s %02X%02X", id.mnemonic, i->op2, i->op1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -105,17 +116,19 @@ void
|
||||
disassemble(bus_t *b, uint16_t addr)
|
||||
{
|
||||
instruction_t i;
|
||||
instrdef_t id;
|
||||
|
||||
i = instruction_fetch(b, addr);
|
||||
id = instruction_decode(i.opcode);
|
||||
|
||||
printf("%X:\t", addr);
|
||||
instruction_print(&i);
|
||||
printf("\t\t// %X", i.def.opcode);
|
||||
printf("\t\t// %X", id.opcode);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
instrdef_t
|
||||
instrdef_get(uint8_t opcode)
|
||||
instruction_decode(uint8_t opcode)
|
||||
{
|
||||
instrdef_t id;
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
#ifndef _INSTRUCTION_H_
|
||||
#define _INSTRUCTION_H_
|
||||
|
||||
#include "rk65c02.h"
|
||||
|
||||
typedef enum {
|
||||
IMPLIED,
|
||||
IMMEDIATE,
|
||||
@ -19,27 +21,28 @@ typedef enum {
|
||||
ACCUMULATOR
|
||||
} addressing_t;
|
||||
|
||||
struct instrdef {
|
||||
uint8_t opcode;
|
||||
const char *mnemonic;
|
||||
addressing_t mode;
|
||||
uint8_t size;
|
||||
void (*emul)(void);
|
||||
};
|
||||
|
||||
typedef struct instrdef instrdef_t;
|
||||
|
||||
struct instruction {
|
||||
instrdef_t def;
|
||||
uint8_t opcode;
|
||||
uint8_t op1;
|
||||
uint8_t op2;
|
||||
};
|
||||
|
||||
typedef struct instruction instruction_t;
|
||||
|
||||
struct instrdef {
|
||||
uint8_t opcode;
|
||||
const char *mnemonic;
|
||||
addressing_t mode;
|
||||
uint8_t size;
|
||||
void (*emul)(rk65c02emu_t *e, instruction_t *i);
|
||||
};
|
||||
|
||||
typedef struct instrdef instrdef_t;
|
||||
|
||||
instruction_t instruction_fetch(bus_t *, uint16_t);
|
||||
instrdef_t instruction_decode(uint8_t);
|
||||
void instruction_print(instruction_t *);
|
||||
void disassemble(bus_t *, uint16_t);
|
||||
instrdef_t instrdef_get(uint8_t);
|
||||
//void instruction_execute(rk65c02emu_t *, instruction_t *);
|
||||
|
||||
#endif /* _INSTRUCTION_H_ */
|
||||
|
@ -10,29 +10,40 @@
|
||||
#include "instruction.h"
|
||||
#include "rk65c02.h"
|
||||
|
||||
static bool run = false;
|
||||
|
||||
void
|
||||
rk6502_start(bus_t *b, uint16_t addr) {
|
||||
instruction_t i;
|
||||
reg_state_t r;
|
||||
rk65c02emu_t
|
||||
rk65c02_init(bus_t *b)
|
||||
{
|
||||
rk65c02emu_t e;
|
||||
|
||||
e.bus = b;
|
||||
e.regs = &r;
|
||||
e.regs->PC = addr;
|
||||
e.state = STOPPED;
|
||||
|
||||
run = true;
|
||||
while (run) {
|
||||
disassemble(e.bus, e.regs->PC);
|
||||
i = instruction_fetch(e.bus, e.regs->PC);
|
||||
return e;
|
||||
}
|
||||
|
||||
//execute(i, r);
|
||||
void
|
||||
rk65c02_start(rk65c02emu_t *e) {
|
||||
instruction_t i;
|
||||
instrdef_t id;
|
||||
|
||||
if (i.def.opcode == 0xDB) // STP
|
||||
run = false;
|
||||
e->state = RUNNING;
|
||||
while (e->state == RUNNING) {
|
||||
disassemble(e->bus, e->regs.PC);
|
||||
i = instruction_fetch(e->bus, e->regs.PC);
|
||||
id = instruction_decode(i.opcode);
|
||||
|
||||
// instruction_execute(e, i);
|
||||
if (id.emul != NULL)
|
||||
id.emul(e, &i);
|
||||
else
|
||||
printf("unimplemented opcode %X\n", i.opcode);
|
||||
|
||||
|
||||
/* if (i.opcode == 0xDB) // STP
|
||||
e->state = STOPPED;*/
|
||||
|
||||
e->regs.PC += id.size;
|
||||
|
||||
e.regs->PC += i.def.size;
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
@ -2,11 +2,10 @@
|
||||
#define _RK6502_H_
|
||||
|
||||
#include "bus.h"
|
||||
#include "instruction.h"
|
||||
|
||||
typedef enum {
|
||||
STOPPED,
|
||||
RUNNIG,
|
||||
RUNNING,
|
||||
STEPPING
|
||||
} emu_state_t;
|
||||
|
||||
@ -25,9 +24,13 @@ typedef struct reg_state reg_state_t;
|
||||
struct rk65c02emu {
|
||||
emu_state_t state;
|
||||
bus_t *bus;
|
||||
reg_state_t *regs;
|
||||
reg_state_t regs;
|
||||
};
|
||||
|
||||
typedef struct rk65c02emu rk65c02emu_t;
|
||||
|
||||
rk65c02emu_t rk65c02_init(bus_t *);
|
||||
void rk65c02_start(rk65c02emu_t *);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,11 +1,20 @@
|
||||
CFLAGS=-Wall -I../src
|
||||
CFLAGS=-Wall -I../src -g
|
||||
LDFLAGS=-latf-c
|
||||
RK6502LIB=../src/librk65c02.a
|
||||
TESTS=test_bus test_emulation
|
||||
|
||||
test_bus : test_bus.o
|
||||
all : $(TESTS)
|
||||
|
||||
test_bus : test_bus.o
|
||||
$(CC) -o test_bus $(LDFLAGS) $< $(RK6502LIB)
|
||||
|
||||
test_emulation : test_emulation.o
|
||||
$(CC) -o test_emulation $(LDFLAGS) $< $(RK6502LIB)
|
||||
|
||||
%.o : %.c
|
||||
$(CC) $(CFLAGS) -c $<
|
||||
|
||||
clean :
|
||||
rm -f *.o
|
||||
rm -f $(TESTS)
|
||||
|
||||
|
@ -20,7 +20,7 @@ ATF_TC_BODY(bus__init, tc)
|
||||
ATF_TC_WITHOUT_HEAD(bus__foo);
|
||||
ATF_TC_BODY(bus__foo, tc)
|
||||
{
|
||||
bus_t b;
|
||||
// bus_t b;
|
||||
}
|
||||
|
||||
ATF_TP_ADD_TCS(tp)
|
||||
|
36
test/test_emulation.c
Normal file
36
test/test_emulation.c
Normal file
@ -0,0 +1,36 @@
|
||||
#include <atf-c.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bus.h"
|
||||
#include "rk65c02.h"
|
||||
|
||||
ATF_TC_WITHOUT_HEAD(emulation_nop);
|
||||
ATF_TC_BODY(emulation_nop, tc)
|
||||
{
|
||||
rk65c02emu_t e;
|
||||
bus_t b;
|
||||
|
||||
b = bus_init();
|
||||
e = rk65c02_init(&b);
|
||||
|
||||
e.regs.PC = 0;
|
||||
|
||||
bus_write_1(&b, 0, 0xEA);
|
||||
bus_write_1(&b, 1, 0xDB);
|
||||
|
||||
rk65c02_start(&e);
|
||||
|
||||
ATF_CHECK(e.regs.PC == 2);
|
||||
|
||||
bus_finish(&b);
|
||||
}
|
||||
|
||||
ATF_TP_ADD_TCS(tp)
|
||||
{
|
||||
ATF_TP_ADD_TC(tp, emulation_nop);
|
||||
|
||||
return (atf_no_error());
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user