2017-12-02 19:05:53 +00:00
|
|
|
#ifndef _MOS6502_H_
|
|
|
|
#define _MOS6502_H_
|
|
|
|
|
|
|
|
#include "vm_bits.h"
|
|
|
|
#include "vm_segment.h"
|
|
|
|
|
|
|
|
#define MOS6502_MEMSIZE 65536
|
|
|
|
|
|
|
|
#define SET_ARITH_STATUS(v) \
|
|
|
|
cpu->P &= ~NEGATIVE; \
|
|
|
|
cpu->P &= ~ZERO; \
|
|
|
|
cpu->P &= ~CARRY; \
|
|
|
|
if ((v) == 0) cpu->P |= ZERO; \
|
|
|
|
if ((v) > 0) cpu->P |= CARRY; \
|
|
|
|
if ((v) & 0x80) cpu->P |= NEGATIVE
|
|
|
|
|
|
|
|
#define SET_PC_BYTE(cpu, off, byte) \
|
|
|
|
vm_segment_set(cpu->memory, cpu->PC + off, byte)
|
|
|
|
|
|
|
|
#define INIT_ADDR_MODE() \
|
|
|
|
mos6502 *cpu; \
|
|
|
|
cpu = mos6502_create()
|
|
|
|
|
|
|
|
#define END_ADDR_MODE() \
|
|
|
|
mos6502_free(cpu)
|
|
|
|
|
2017-12-02 19:27:30 +00:00
|
|
|
#define START_CPU_TEST(t) \
|
|
|
|
t *cpu; \
|
|
|
|
cpu = t##_create()
|
|
|
|
|
|
|
|
#define END_CPU_TEST(t) \
|
|
|
|
t##_free(cpu)
|
|
|
|
|
2017-12-02 19:05:53 +00:00
|
|
|
#define DEFINE_INST(inst) \
|
|
|
|
void mos6502_handle_##inst (mos6502 *cpu, vm_8bit oper)
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
// Our memory.
|
|
|
|
vm_segment *memory;
|
|
|
|
|
|
|
|
// This contains the last _effective_ address we've resolved in one
|
|
|
|
// of our address modes. In absolute mode, this would be the literal
|
|
|
|
// operand we read from memory; in indirect mode, this will be the
|
|
|
|
// address we _find_ after dereferencing the operand we read from
|
|
|
|
// memory. Another way of thinking of this is, this address is where
|
|
|
|
// we found the value we care about.
|
|
|
|
vm_16bit last_addr;
|
|
|
|
|
2017-12-05 05:30:18 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2017-12-02 19:05:53 +00:00
|
|
|
// 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.
|
|
|
|
vm_16bit PC;
|
|
|
|
|
|
|
|
// This is the accumulator register. It's used in most arithmetic
|
|
|
|
// operations, and anything like that which you need to do will end
|
|
|
|
// up storing the value here.
|
|
|
|
vm_8bit A;
|
|
|
|
|
|
|
|
// The X and Y registers are our index registers. They're provided
|
|
|
|
// to aid looping over tables, but they can also be used for other
|
|
|
|
// purposes.
|
|
|
|
vm_8bit X, Y;
|
|
|
|
|
|
|
|
// The P register is our status flag register. (I presume 'P' means
|
|
|
|
// 'predicate'.) Each bit stands for some kind of status.
|
|
|
|
vm_8bit P;
|
|
|
|
|
|
|
|
// The S register is our stack counter register. It indicates how
|
|
|
|
// far into the stack we've gone.
|
|
|
|
vm_8bit S;
|
|
|
|
} mos6502;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is a small convenience so that we don't need to expose the
|
|
|
|
* somewhat regrettable syntax for function pointers to any main source
|
|
|
|
* file
|
|
|
|
*/
|
|
|
|
typedef vm_8bit (*mos6502_address_resolver)(mos6502 *);
|
|
|
|
|
|
|
|
extern mos6502 *mos6502_create();
|
|
|
|
extern void mos6502_free(mos6502 *);
|
|
|
|
extern vm_8bit mos6502_next_byte(mos6502 *);
|
|
|
|
extern void mos6502_push_stack(mos6502 *, vm_16bit);
|
|
|
|
extern vm_16bit mos6502_pop_stack(mos6502 *);
|
2017-12-04 02:19:17 +00:00
|
|
|
extern void mos6502_set_status(mos6502 *, vm_8bit);
|
|
|
|
extern void mos6502_modify_status(mos6502 *, vm_8bit, vm_8bit);
|
2017-12-05 05:30:18 +00:00
|
|
|
extern int mos6502_cycles(mos6502 *, vm_8bit);
|
|
|
|
extern int mos6502_instruction(vm_8bit);
|
2017-12-02 19:05:53 +00:00
|
|
|
|
2017-12-05 05:30:18 +00:00
|
|
|
/*
|
|
|
|
* Below are some functions that are defined in mos6502.addr.c
|
|
|
|
*/
|
|
|
|
extern int mos6502_addr_mode(vm_8bit);
|
2017-12-02 19:05:53 +00:00
|
|
|
extern mos6502_address_resolver mos6502_get_address_resolver(int);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In some address mode resolution, we must factor the carry bit into
|
|
|
|
* the arithmetic we perform. In all those cases, if the carry bit is
|
|
|
|
* set, we must only add 1 to the addition. The carry variable is,
|
|
|
|
* therefore, the literal value we are adding, rather than a boolean
|
|
|
|
* signifier.
|
|
|
|
*/
|
|
|
|
#define CARRY_BIT() \
|
|
|
|
vm_8bit carry = 0; \
|
|
|
|
if (cpu->P & CARRY) carry = 1
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A uniform way of declaring resolve functions for address modes, which
|
|
|
|
* is useful in the event that we need to change the function signature.
|
|
|
|
*/
|
|
|
|
#define DECL_ADDR_MODE(x) \
|
|
|
|
extern vm_8bit mos6502_resolve_##x (mos6502 *)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Similarly, a uniform way of declaring instruction handler functions,
|
|
|
|
* for the same reasons.
|
|
|
|
*/
|
|
|
|
#define DECL_INST(x) \
|
|
|
|
extern void mos6502_handle_##x (mos6502 *, vm_8bit)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* All of our address modes
|
|
|
|
*/
|
|
|
|
DECL_ADDR_MODE(acc);
|
|
|
|
DECL_ADDR_MODE(abs);
|
|
|
|
DECL_ADDR_MODE(abx);
|
|
|
|
DECL_ADDR_MODE(aby);
|
|
|
|
DECL_ADDR_MODE(imm);
|
|
|
|
DECL_ADDR_MODE(ind);
|
|
|
|
DECL_ADDR_MODE(idx);
|
|
|
|
DECL_ADDR_MODE(idy);
|
|
|
|
DECL_ADDR_MODE(rel);
|
|
|
|
DECL_ADDR_MODE(zpg);
|
|
|
|
DECL_ADDR_MODE(zpx);
|
|
|
|
DECL_ADDR_MODE(zpy);
|
|
|
|
|
|
|
|
/*
|
2017-12-05 05:30:18 +00:00
|
|
|
* And now, our instruction handlers; held generally in mos6502.*.c
|
|
|
|
* (excepting mos6502.addr.c).
|
2017-12-02 19:05:53 +00:00
|
|
|
*/
|
|
|
|
DECL_INST(adc);
|
|
|
|
DECL_INST(and);
|
|
|
|
DECL_INST(asl);
|
|
|
|
DECL_INST(bcc);
|
|
|
|
DECL_INST(bcs);
|
|
|
|
DECL_INST(beq);
|
|
|
|
DECL_INST(bit);
|
|
|
|
DECL_INST(bmi);
|
|
|
|
DECL_INST(bne);
|
|
|
|
DECL_INST(bpl);
|
|
|
|
DECL_INST(brk);
|
|
|
|
DECL_INST(bvc);
|
|
|
|
DECL_INST(bvs);
|
|
|
|
DECL_INST(clc);
|
|
|
|
DECL_INST(cld);
|
|
|
|
DECL_INST(cli);
|
|
|
|
DECL_INST(clv);
|
|
|
|
DECL_INST(cmp);
|
|
|
|
DECL_INST(cpx);
|
|
|
|
DECL_INST(cpy);
|
|
|
|
DECL_INST(dec);
|
|
|
|
DECL_INST(dex);
|
|
|
|
DECL_INST(dey);
|
|
|
|
DECL_INST(eor);
|
|
|
|
DECL_INST(inc);
|
|
|
|
DECL_INST(inx);
|
|
|
|
DECL_INST(iny);
|
|
|
|
DECL_INST(jmp);
|
|
|
|
DECL_INST(jsr);
|
|
|
|
DECL_INST(lda);
|
|
|
|
DECL_INST(ldx);
|
|
|
|
DECL_INST(ldy);
|
|
|
|
DECL_INST(lsr);
|
|
|
|
DECL_INST(nop);
|
|
|
|
DECL_INST(ora);
|
|
|
|
DECL_INST(pha);
|
|
|
|
DECL_INST(php);
|
|
|
|
DECL_INST(pla);
|
|
|
|
DECL_INST(plp);
|
|
|
|
DECL_INST(rol);
|
|
|
|
DECL_INST(ror);
|
|
|
|
DECL_INST(rti);
|
|
|
|
DECL_INST(rts);
|
|
|
|
DECL_INST(sbc);
|
|
|
|
DECL_INST(sec);
|
|
|
|
DECL_INST(sed);
|
|
|
|
DECL_INST(sei);
|
|
|
|
DECL_INST(sta);
|
|
|
|
DECL_INST(stx);
|
|
|
|
DECL_INST(sty);
|
|
|
|
DECL_INST(tax);
|
|
|
|
DECL_INST(tay);
|
|
|
|
DECL_INST(tsx);
|
|
|
|
DECL_INST(txa);
|
|
|
|
DECL_INST(txs);
|
|
|
|
DECL_INST(tya);
|
|
|
|
|
|
|
|
#endif
|