You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
609 lines
21 KiB
C
609 lines
21 KiB
C
/*
|
|
* Some definitions...
|
|
*/
|
|
#include <stdio.h>
|
|
#include "emu6502.h"
|
|
|
|
// TODO: At 0x35d1, bug on SDC.
|
|
|
|
// fn(cpu)
|
|
typedef void (*CPUopcodefn)(struct CPU6502*, word);
|
|
|
|
typedef struct CPUopcode {
|
|
byte argsize; // can be up to 2
|
|
byte cycles; // number of cycles required
|
|
CPUopcodefn function;
|
|
} CPUopcode;
|
|
|
|
#define DEFOPCODE(name, argsize, cycles, op) \
|
|
static void opcode_fn_##name(CPU6502* cpu, word arg) { \
|
|
op; \
|
|
} \
|
|
CPUopcode opcode_##name = \
|
|
(CPUopcode){ argsize, cycles, opcode_fn_##name }
|
|
|
|
// typedef sbyte int8_t
|
|
|
|
#define _CPUREAD(addr) CPUREAD(cpu, addr)
|
|
#define _CPUWRITE(addr,v) CPUWRITE(cpu, addr, v)
|
|
#define _CPUREAD16(addr) ((_CPUREAD((addr)+1) << 8) | _CPUREAD((addr)))
|
|
#define _CPUPUSH(v) CPUPUSH(cpu,(v))
|
|
#define _CPUPOP() CPUPOP(cpu)
|
|
#define _CPUPUSH16(v) ({_CPUPUSH((v)>>8); _CPUPUSH(v);})
|
|
inline static word CPUPOP16(CPU6502* cpu) {
|
|
byte a = _CPUPOP();
|
|
return (_CPUPOP() << 8) | a;
|
|
}
|
|
#define _CPUPOP16() CPUPOP16(cpu)
|
|
#define _B(v) (v&0xff)
|
|
|
|
// Addressing modes
|
|
#define AM_IMM_8 _B(arg)
|
|
#define AM_IMM_16 arg
|
|
// Read
|
|
#define RM_ZP _CPUREAD(_B(arg))
|
|
#define RM_ZP_X _CPUREAD((_B(arg) + cpu->x)&0xff)
|
|
#define RM_ZP_Y _CPUREAD((_B(arg) + cpu->y)&0xff)
|
|
#define RM_ABS _CPUREAD(arg)
|
|
#define RM_ABS16 _CPUREAD16(arg)
|
|
#define RM_ABS_X _CPUREAD(arg + (word)cpu->x)
|
|
#define RM_ABS_Y _CPUREAD(arg + (word)cpu->y)
|
|
#define RM_IND _CPUREAD(_CPUREAD16(arg))
|
|
#define RM_IND_X _CPUREAD(_CPUREAD16((arg + cpu->x)&0xff))
|
|
#define RM_IND_Y _CPUREAD(_CPUREAD16(arg) + (word)cpu->y)
|
|
|
|
// Write
|
|
#define WM_ZP(r) _CPUWRITE(_B(arg), cpu->r)
|
|
#define WM_ZP_X(r) _CPUWRITE((_B(arg) + cpu->x)&0xff, cpu->r)
|
|
#define WM_ZP_Y(r) _CPUWRITE((_B(arg) + cpu->y)&0xff, cpu->r)
|
|
#define WM_ABS(r) _CPUWRITE(arg, cpu->r)
|
|
#define WM_ABS_X(r) _CPUWRITE(arg + (word)cpu->x, cpu->r)
|
|
#define WM_ABS_Y(r) _CPUWRITE(arg + (word)cpu->y, cpu->r)
|
|
#define WM_IND(r) _CPUWRITE(_CPUREAD16(arg), cpu->r)
|
|
#define WM_IND_X(r) _CPUWRITE(_CPUREAD16((arg + cpu->x)&0xff), cpu->r)
|
|
#define WM_IND_Y(r) _CPUWRITE(_CPUREAD16(arg) + (word)cpu->y, cpu->r)
|
|
|
|
#define WMM_ZP(v) _CPUWRITE(_B(arg), v)
|
|
#define WMM_ZP_X(v) _CPUWRITE((_B(arg) + cpu->x)&0xff, v)
|
|
#define WMM_ABS(v) _CPUWRITE(arg, v)
|
|
#define WMM_ABS_X(v) _CPUWRITE(arg + (word)cpu->x, v)
|
|
|
|
// Flags update
|
|
#define _SETFLAGBIT(b,v) (cpu->flags = ((cpu->flags&~b) | ((v)*b)))
|
|
#define _UPNEG(v) _SETFLAGBIT(CPUFLAG_NEGATIVE, ((v) & 0x80) > 0)
|
|
#define _UPZERO(v) _SETFLAGBIT(CPUFLAG_ZERO, (v) == 0)
|
|
#define _UPCARRY(u,v) _SETFLAGBIT(CPUFLAG_CARRY, ((v) < (u)) || \
|
|
((v) == (u) && cpu->flags&CPUFLAG_CARRY))
|
|
// #define _UPBORRW(u,v) _SETFLAGBIT(CPUFLAG_CARRY, (v) < (u))
|
|
// To use only in SUBREG and ADDREG
|
|
#define _UPOVERFL() _SETFLAGBIT(CPUFLAG_OVERFLOW, \
|
|
((~((word)*r^val))&((word)*r^result)&0x0080) != 0)
|
|
// ((~(v ^ *r) & (result ^ v)) & 0x80) != 0)
|
|
// #define SIGN(v) ((v) < 0 ? -1 : 1)
|
|
// #define _UPOVERFL() _SETFLAGBIT(CPUFLAG_OVERFLOW, ((*r^v) & 0x80) != 0)
|
|
|
|
/* Non-working
|
|
inline static byte INT2BCD(byte v) {
|
|
short a = (((v/10)<<4)|(v%10));
|
|
a = ((a + 100) % 100) - 100;
|
|
return (byte)a;
|
|
}
|
|
#define BCD2INT(v) ((v>>4)*10+(v&0xf))
|
|
*/
|
|
|
|
// Set registers with appropriate flags
|
|
#define _SETREG(r,v) ({cpu->r = (v); _UPNEG(cpu->r); _UPZERO(cpu->r);})
|
|
inline static void ADDREG(CPU6502* cpu, byte* r, byte v) {
|
|
word result;
|
|
word val = v;
|
|
/*
|
|
if (cpu->flags & CPUFLAG_DECIMAL)
|
|
result = INT2BCD(BCD2INT(*r) + BCD2INT(v));
|
|
else
|
|
*/
|
|
result = (word)*r + val + (word)(cpu->flags & CPUFLAG_CARRY);
|
|
// _SETFLAGBIT(CPUFLAG_OVERFLOW, result > 128);
|
|
_UPCARRY(*r, (byte)result); _UPOVERFL();
|
|
*r = result;
|
|
_UPNEG((byte)result); _UPZERO((byte)result);
|
|
}
|
|
// Lost my sanity when figuring this out.
|
|
inline static void SUBREG(CPU6502* cpu, byte* r, byte v) {
|
|
word result;
|
|
word val = (word)v ^ 0x00ff;
|
|
/*
|
|
if (cpu->flags & CPUFLAG_DECIMAL)
|
|
result = INT2BCD(BCD2INT(*r) - BCD2INT(v));
|
|
else
|
|
*/
|
|
result = (word)*r + val + (word)(cpu->flags & CPUFLAG_CARRY);
|
|
_UPCARRY(*r, (byte)result); _UPOVERFL();
|
|
_UPNEG((byte)result); _UPZERO((byte)result);
|
|
|
|
*r = result;
|
|
}
|
|
#define _ADDREG(r,v) ADDREG(cpu, &(cpu->r), v)
|
|
#define _SUBREG(r,v) SUBREG(cpu, &(cpu->r), v)
|
|
|
|
//////// NOP
|
|
DEFOPCODE(nop, 0, 2, (void)cpu; (void)arg); // Do nothing
|
|
// DEFOPCODE(brk, 0, 2, (void)cpu; (void)arg); // Do nothing
|
|
DEFOPCODE(brk, 0, 7, {
|
|
cpu->pc++;
|
|
_CPUPUSH16(cpu->pc);
|
|
_CPUPUSH(cpu->flags | CPUFLAG_BREAK);
|
|
_SETFLAGBIT(CPUFLAG_INTERRUPT, 1);
|
|
cpu->pc = _CPUREAD16(0xfffe);
|
|
});
|
|
|
|
///////////////// Flag instructions
|
|
DEFOPCODE(clc, 0, 2, _SETFLAGBIT(CPUFLAG_CARRY, 0));
|
|
DEFOPCODE(sec, 0, 2, _SETFLAGBIT(CPUFLAG_CARRY, 1));
|
|
DEFOPCODE(cld, 0, 2, _SETFLAGBIT(CPUFLAG_DECIMAL, 0));
|
|
DEFOPCODE(sed, 0, 2, _SETFLAGBIT(CPUFLAG_DECIMAL, 1));
|
|
DEFOPCODE(clv, 0, 2, _SETFLAGBIT(CPUFLAG_OVERFLOW, 0));
|
|
DEFOPCODE(cli, 0, 2, _SETFLAGBIT(CPUFLAG_INTERRUPT, 0));
|
|
DEFOPCODE(sei, 0, 2, _SETFLAGBIT(CPUFLAG_INTERRUPT, 1));
|
|
|
|
///////////////// Jump instructions
|
|
DEFOPCODE(jmp_a, 2, 3, cpu->pc = AM_IMM_16); // absolute
|
|
DEFOPCODE(jmp_i, 2, 5, cpu->pc = RM_ABS16); // indirect
|
|
|
|
#define _BRANCH(c) ({if (cpu->flags&c) cpu->pc += (signed char)(arg);})
|
|
#define _NBRANCH(c) ({if (!(cpu->flags&c)) cpu->pc += (signed char)(arg);})
|
|
DEFOPCODE(bpl, 1, 2, _NBRANCH(CPUFLAG_NEGATIVE));
|
|
DEFOPCODE(bmi, 1, 2, _BRANCH(CPUFLAG_NEGATIVE));
|
|
DEFOPCODE(bvc, 1, 2, _NBRANCH(CPUFLAG_OVERFLOW));
|
|
DEFOPCODE(bvs, 1, 2, _BRANCH(CPUFLAG_OVERFLOW));
|
|
DEFOPCODE(bcc, 1, 2, _NBRANCH(CPUFLAG_CARRY));
|
|
DEFOPCODE(bcs, 1, 2, _BRANCH(CPUFLAG_CARRY));
|
|
DEFOPCODE(bne, 1, 2, _NBRANCH(CPUFLAG_ZERO));
|
|
DEFOPCODE(beq, 1, 2, _BRANCH(CPUFLAG_ZERO));
|
|
|
|
DEFOPCODE(jsr_a, 2, 6, _CPUPUSH16(cpu->pc - 1); cpu->pc = AM_IMM_16);
|
|
DEFOPCODE(rts, 0, 6, cpu->pc = _CPUPOP16() + 1);
|
|
DEFOPCODE(rti, 0, 6, cpu->flags = _CPUPOP(); cpu->pc = _CPUPOP16(););
|
|
|
|
///////////////// Compare instructions
|
|
|
|
inline static void CMPREG(CPU6502* cpu, byte* r, byte v) {
|
|
_SETFLAGBIT(CPUFLAG_ZERO, *r == v);
|
|
_SETFLAGBIT(CPUFLAG_CARRY, *r >= v);
|
|
_UPNEG(*r - v);
|
|
}
|
|
#define _CMPREG(r,v) CMPREG(cpu, &(cpu->r), v)
|
|
// #define _BIT(v) ({_UPZERO(cpu->a & v);cpu->flags&=0x3f|((v)&0xc0);})
|
|
#define _BIT(v) ({ \
|
|
_UPZERO(cpu->a & v); \
|
|
cpu->flags = ((cpu->flags)&0x3f)|((v)&0xc0); })
|
|
|
|
DEFOPCODE(cmp_i, 1, 2, _CMPREG(a, AM_IMM_8));
|
|
DEFOPCODE(cmp_d, 1, 3, _CMPREG(a, RM_ZP));
|
|
DEFOPCODE(cmp_dx, 1, 4, _CMPREG(a, RM_ZP_X));
|
|
DEFOPCODE(cmp_a, 2, 4, _CMPREG(a, RM_ABS));
|
|
DEFOPCODE(cmp_ax, 2, 4, _CMPREG(a, RM_ABS_X));
|
|
DEFOPCODE(cmp_ay, 2, 4, _CMPREG(a, RM_ABS_Y));
|
|
DEFOPCODE(cmp_ix, 1, 6, _CMPREG(a, RM_IND_X));
|
|
DEFOPCODE(cmp_iy, 1, 5, _CMPREG(a, RM_IND_Y));
|
|
|
|
DEFOPCODE(cpx_i, 1, 2, _CMPREG(x, AM_IMM_8));
|
|
DEFOPCODE(cpx_d, 1, 3, _CMPREG(x, RM_ZP));
|
|
DEFOPCODE(cpx_a, 2, 4, _CMPREG(x, RM_ABS));
|
|
|
|
DEFOPCODE(cpy_i, 1, 2, _CMPREG(y, AM_IMM_8));
|
|
DEFOPCODE(cpy_d, 1, 3, _CMPREG(y, RM_ZP));
|
|
DEFOPCODE(cpy_a, 2, 4, _CMPREG(y, RM_ABS));
|
|
|
|
DEFOPCODE(bit_d, 1, 3, _BIT(RM_ZP));
|
|
DEFOPCODE(bit_a, 2, 4, _BIT(RM_ABS));
|
|
|
|
///////////////// Stack instructions
|
|
|
|
DEFOPCODE(pha, 0, 3, _CPUPUSH(cpu->a));
|
|
DEFOPCODE(php, 0, 3, _CPUPUSH(cpu->flags | CPUFLAG_BREAK));
|
|
DEFOPCODE(txs, 0, 2, cpu->sp = cpu->x);
|
|
DEFOPCODE(pla, 0, 4, _SETREG(a, _CPUPOP()));
|
|
DEFOPCODE(tsx, 0, 4, _SETREG(x, cpu->sp));
|
|
DEFOPCODE(plp, 0, 4, cpu->flags = _CPUPOP());
|
|
|
|
///////////////// Bitwise instructions
|
|
|
|
DEFOPCODE(and_i, 1, 2, _SETREG(a, cpu->a & AM_IMM_8));
|
|
DEFOPCODE(and_d, 1, 3, _SETREG(a, cpu->a & RM_ZP));
|
|
DEFOPCODE(and_dx, 1, 4, _SETREG(a, cpu->a & RM_ZP_X));
|
|
DEFOPCODE(and_a, 2, 4, _SETREG(a, cpu->a & RM_ABS));
|
|
DEFOPCODE(and_ax, 2, 4, _SETREG(a, cpu->a & RM_ABS_X));
|
|
DEFOPCODE(and_ay, 2, 4, _SETREG(a, cpu->a & RM_ABS_Y));
|
|
DEFOPCODE(and_ix, 1, 6, _SETREG(a, cpu->a & RM_IND_X));
|
|
DEFOPCODE(and_iy, 1, 5, _SETREG(a, cpu->a & RM_IND_Y));
|
|
|
|
DEFOPCODE(ora_i, 1, 2, _SETREG(a, cpu->a | AM_IMM_8));
|
|
DEFOPCODE(ora_d, 1, 3, _SETREG(a, cpu->a | RM_ZP));
|
|
DEFOPCODE(ora_dx, 1, 4, _SETREG(a, cpu->a | RM_ZP_X));
|
|
DEFOPCODE(ora_a, 2, 4, _SETREG(a, cpu->a | RM_ABS));
|
|
DEFOPCODE(ora_ax, 2, 4, _SETREG(a, cpu->a | RM_ABS_X));
|
|
DEFOPCODE(ora_ay, 2, 4, _SETREG(a, cpu->a | RM_ABS_Y));
|
|
DEFOPCODE(ora_ix, 1, 6, _SETREG(a, cpu->a | RM_IND_X));
|
|
DEFOPCODE(ora_iy, 1, 5, _SETREG(a, cpu->a | RM_IND_Y));
|
|
|
|
DEFOPCODE(eor_i, 1, 2, _SETREG(a, cpu->a ^ AM_IMM_8));
|
|
DEFOPCODE(eor_d, 1, 3, _SETREG(a, cpu->a ^ RM_ZP));
|
|
DEFOPCODE(eor_dx, 1, 4, _SETREG(a, cpu->a ^ RM_ZP_X));
|
|
DEFOPCODE(eor_a, 2, 4, _SETREG(a, cpu->a ^ RM_ABS));
|
|
DEFOPCODE(eor_ax, 2, 4, _SETREG(a, cpu->a ^ RM_ABS_X));
|
|
DEFOPCODE(eor_ay, 2, 4, _SETREG(a, cpu->a ^ RM_ABS_Y));
|
|
DEFOPCODE(eor_ix, 1, 6, _SETREG(a, cpu->a ^ RM_IND_X));
|
|
DEFOPCODE(eor_iy, 1, 5, _SETREG(a, cpu->a ^ RM_IND_Y));
|
|
|
|
inline static byte ASL(CPU6502* cpu, byte i) {
|
|
byte result = i;
|
|
_SETFLAGBIT(CPUFLAG_CARRY, (i&0x80)!=0);
|
|
result <<= 1; _UPNEG(result); _UPZERO(result);
|
|
return result;
|
|
}
|
|
#define _ASL(v) ASL(cpu, v)
|
|
inline static byte LSR(CPU6502* cpu, byte i) {
|
|
byte result = i;
|
|
_SETFLAGBIT(CPUFLAG_CARRY, (i&0x1)!=0);
|
|
result >>= 1; _UPNEG(result); _UPZERO(result);
|
|
return result;
|
|
}
|
|
#define _LSR(v) LSR(cpu, v)
|
|
|
|
inline static byte ROL(CPU6502* cpu, byte i) {
|
|
byte m = (cpu->flags & CPUFLAG_CARRY) != 0;
|
|
byte result = i;
|
|
_SETFLAGBIT(CPUFLAG_CARRY, (i&0x80)!=0);
|
|
result <<= 1; result |= m;
|
|
_UPNEG(result); _UPZERO(result);
|
|
return result;
|
|
}
|
|
#define _ROL(v) ROL(cpu, v)
|
|
inline static byte ROR(CPU6502* cpu, byte i) {
|
|
byte m = (cpu->flags & CPUFLAG_CARRY) != 0;
|
|
byte result = i;
|
|
_SETFLAGBIT(CPUFLAG_CARRY, (i&0x1)!=0);
|
|
result >>= 1; result |= m<<7;
|
|
_UPNEG(result); _UPZERO(result);
|
|
return result;
|
|
}
|
|
#define _ROR(v) ROR(cpu, v)
|
|
/*
|
|
#define _ROL(v) (_ASL(v) | ((cpu->flags&CPUFLAG_CARRY)!=0))
|
|
#define _ROR(v) (_LSR(v) | (((cpu->flags&CPUFLAG_CARRY)!=0)*0x80))
|
|
*/
|
|
|
|
DEFOPCODE(asl_i, 0, 2, cpu->a = _ASL(cpu->a));
|
|
DEFOPCODE(asl_d, 1, 5, WMM_ZP(_ASL(RM_ZP)));
|
|
DEFOPCODE(asl_dx, 1, 6, WMM_ZP_X(_ASL(RM_ZP_X)));
|
|
DEFOPCODE(asl_a, 2, 6, WMM_ABS(_ASL(RM_ABS)));
|
|
DEFOPCODE(asl_ax, 2, 7, WMM_ABS_X(_ASL(RM_ABS_X)));
|
|
|
|
DEFOPCODE(lsr_i, 0, 2, cpu->a = _LSR(cpu->a));
|
|
DEFOPCODE(lsr_d, 1, 5, WMM_ZP(_LSR(RM_ZP)));
|
|
DEFOPCODE(lsr_dx, 1, 6, WMM_ZP_X(_LSR(RM_ZP_X)));
|
|
DEFOPCODE(lsr_a, 2, 6, WMM_ABS(_LSR(RM_ABS)));
|
|
DEFOPCODE(lsr_ax, 2, 7, WMM_ABS_X(_LSR(RM_ABS_X)));
|
|
|
|
DEFOPCODE(rol_i, 0, 2, cpu->a = _ROL(cpu->a));
|
|
DEFOPCODE(rol_d, 1, 5, WMM_ZP(_ROL(RM_ZP)));
|
|
DEFOPCODE(rol_dx, 1, 6, WMM_ZP_X(_ROL(RM_ZP_X)));
|
|
DEFOPCODE(rol_a, 2, 6, WMM_ABS(_ROL(RM_ABS)));
|
|
DEFOPCODE(rol_ax, 2, 7, WMM_ABS_X(_ROL(RM_ABS_X)));
|
|
|
|
DEFOPCODE(ror_i, 0, 2, cpu->a = _ROR(cpu->a));
|
|
DEFOPCODE(ror_d, 1, 5, WMM_ZP(_ROR(RM_ZP)));
|
|
DEFOPCODE(ror_dx, 1, 6, WMM_ZP_X(_ROR(RM_ZP_X)));
|
|
DEFOPCODE(ror_a, 2, 6, WMM_ABS(_ROR(RM_ABS)));
|
|
DEFOPCODE(ror_ax, 2, 7, WMM_ABS_X(_ROR(RM_ABS_X)));
|
|
|
|
///////////////// Arithmetic instructions
|
|
DEFOPCODE(adc_i, 1, 2, _ADDREG(a, AM_IMM_8)); // #i
|
|
DEFOPCODE(adc_d, 1, 3, _ADDREG(a, RM_ZP)); // d
|
|
DEFOPCODE(adc_dx, 1, 4, _ADDREG(a, RM_ZP_X)); // d, x
|
|
DEFOPCODE(adc_a, 2, 4, _ADDREG(a, RM_ABS)); // abs
|
|
DEFOPCODE(adc_ax, 2, 4, _ADDREG(a, RM_ABS_X)); // abs, x
|
|
DEFOPCODE(adc_ay, 2, 4, _ADDREG(a, RM_ABS_Y)); // abs, y
|
|
DEFOPCODE(adc_ix, 1, 6, _ADDREG(a, RM_IND_X)); // (d, x)
|
|
DEFOPCODE(adc_iy, 1, 5, _ADDREG(a, RM_IND_Y)); // (d), y
|
|
|
|
DEFOPCODE(sbc_i, 1, 2, _SUBREG(a, AM_IMM_8)); // #i
|
|
DEFOPCODE(sbc_d, 1, 3, _SUBREG(a, RM_ZP)); // d
|
|
DEFOPCODE(sbc_dx, 1, 4, _SUBREG(a, RM_ZP_X)); // d, x
|
|
DEFOPCODE(sbc_a, 2, 4, _SUBREG(a, RM_ABS)); // abs
|
|
DEFOPCODE(sbc_ax, 2, 4, _SUBREG(a, RM_ABS_X)); // abs, x
|
|
DEFOPCODE(sbc_ay, 2, 4, _SUBREG(a, RM_ABS_Y)); // abs, y
|
|
DEFOPCODE(sbc_ix, 1, 6, _SUBREG(a, RM_IND_X)); // (d, x)
|
|
DEFOPCODE(sbc_iy, 1, 5, _SUBREG(a, RM_IND_Y)); // (d), y
|
|
|
|
///////////////// Load/Store instructions
|
|
//////// LDA
|
|
DEFOPCODE(lda_i, 1, 2, _SETREG(a, AM_IMM_8)); // LDA #i
|
|
DEFOPCODE(lda_d, 1, 3, _SETREG(a, RM_ZP)); // LDA d
|
|
DEFOPCODE(lda_dx, 1, 4, _SETREG(a, RM_ZP_X)); // LDA d, x
|
|
DEFOPCODE(lda_a, 2, 4, _SETREG(a, RM_ABS)); // LDA abs
|
|
DEFOPCODE(lda_ax, 2, 4, _SETREG(a, RM_ABS_X)); // LDA abs, x
|
|
DEFOPCODE(lda_ay, 2, 4, _SETREG(a, RM_ABS_Y)); // LDA abs, y
|
|
DEFOPCODE(lda_ix, 1, 6, _SETREG(a, RM_IND_X)); // LDA (d, x)
|
|
DEFOPCODE(lda_iy, 1, 5, _SETREG(a, RM_IND_Y)); // LDA (d), y
|
|
|
|
//////// LDX
|
|
DEFOPCODE(ldx_i, 1, 2, _SETREG(x, AM_IMM_8)); // LDX #i
|
|
DEFOPCODE(ldx_d, 1, 3, _SETREG(x, RM_ZP)); // LDX d
|
|
DEFOPCODE(ldx_dy, 1, 4, _SETREG(x, RM_ZP_Y)); // LDX d, y
|
|
DEFOPCODE(ldx_a, 2, 4, _SETREG(x, RM_ABS)); // LDX abs
|
|
DEFOPCODE(ldx_ay, 2, 4, _SETREG(x, RM_ABS_Y)); // LDX abs, y
|
|
|
|
//////// LDY
|
|
DEFOPCODE(ldy_i, 1, 2, _SETREG(y, AM_IMM_8)); // LDY #i
|
|
DEFOPCODE(ldy_d, 1, 3, _SETREG(y, RM_ZP)); // LDY d
|
|
DEFOPCODE(ldy_dx, 1, 4, _SETREG(y, RM_ZP_X)); // LDY d, x
|
|
DEFOPCODE(ldy_a, 2, 4, _SETREG(y, RM_ABS)); // LDY abs
|
|
DEFOPCODE(ldy_ax, 2, 4, _SETREG(y, RM_ABS_X)); // LDY abs, x
|
|
|
|
//////// STA
|
|
DEFOPCODE(sta_d, 1, 3, WM_ZP(a));
|
|
DEFOPCODE(sta_dx, 1, 4, WM_ZP_X(a));
|
|
DEFOPCODE(sta_a , 2, 4, WM_ABS(a));
|
|
DEFOPCODE(sta_ax, 2, 5, WM_ABS_X(a));
|
|
DEFOPCODE(sta_ay, 2, 5, WM_ABS_Y(a));
|
|
DEFOPCODE(sta_ix, 1, 6, WM_IND_X(a));
|
|
DEFOPCODE(sta_iy, 1, 6, WM_IND_Y(a));
|
|
|
|
//////// STX
|
|
DEFOPCODE(stx_d, 1, 3, WM_ZP(x));
|
|
DEFOPCODE(stx_dy, 1, 3, WM_ZP_Y(x));
|
|
DEFOPCODE(stx_a , 2, 3, WM_ABS(x));
|
|
|
|
//////// STY
|
|
DEFOPCODE(sty_d, 1, 3, WM_ZP(y));
|
|
DEFOPCODE(sty_dx, 1, 3, WM_ZP_X(y));
|
|
DEFOPCODE(sty_a , 2, 3, WM_ABS(y));
|
|
|
|
//////// DEC
|
|
inline static byte DEC(CPU6502* cpu, byte i) {
|
|
i -= 1;
|
|
_UPZERO(i); _UPNEG(i);
|
|
return i;
|
|
}
|
|
#define _DEC(v) DEC(cpu, v)
|
|
DEFOPCODE(dec_d, 1, 3, _CPUWRITE(arg, _DEC(RM_ZP)));
|
|
DEFOPCODE(dec_dx, 1, 3, _CPUWRITE(arg + cpu->x, _DEC(RM_ZP_X)));
|
|
DEFOPCODE(dec_a, 2, 3, _CPUWRITE(arg, _DEC(RM_ABS)));
|
|
DEFOPCODE(dec_ax, 2, 3, _CPUWRITE(arg + cpu->x, _DEC(RM_ABS_X)));
|
|
|
|
//////// INC
|
|
inline static byte INC(CPU6502* cpu, byte i) {
|
|
i += 1;
|
|
_UPZERO(i); _UPNEG(i);
|
|
return i;
|
|
}
|
|
#define _INC(v) INC(cpu, v)
|
|
DEFOPCODE(inc_d, 1, 3, _CPUWRITE(arg, _INC(RM_ZP)));
|
|
DEFOPCODE(inc_dx, 1, 3, _CPUWRITE(arg + cpu->x, _INC(RM_ZP_X)));
|
|
DEFOPCODE(inc_a, 2, 3, _CPUWRITE(arg, _INC(RM_ABS)));
|
|
DEFOPCODE(inc_ax, 2, 3, _CPUWRITE(arg + cpu->x, _INC(RM_ABS_X)));
|
|
|
|
//////// TAX TAY TXA TYA
|
|
DEFOPCODE(tax, 0, 2, _SETREG(x, cpu->a));
|
|
DEFOPCODE(tay, 0, 2, _SETREG(y, cpu->a));
|
|
DEFOPCODE(txa, 0, 2, _SETREG(a, cpu->x));
|
|
DEFOPCODE(tya, 0, 2, _SETREG(a, cpu->y));
|
|
|
|
//////// DEX DEY INX INY
|
|
DEFOPCODE(dex, 0, 2, _SETREG(x, cpu->x - 1));
|
|
DEFOPCODE(dey, 0, 2, _SETREG(y, cpu->y - 1));
|
|
DEFOPCODE(inx, 0, 2, _SETREG(x, cpu->x + 1));
|
|
DEFOPCODE(iny, 0, 2, _SETREG(y, cpu->y + 1));
|
|
|
|
// All opcodes will be stored here.
|
|
CPUopcode opcodes[256];
|
|
|
|
// Yes I know this is incredibly cursed.
|
|
void emu6502_init_cpu(CPU6502* cpu) {
|
|
CPUopcode* _opcodes = (CPUopcode[256]){
|
|
// 0
|
|
opcode_brk, opcode_ora_ix, opcode_nop, opcode_nop,
|
|
opcode_nop, opcode_ora_d, opcode_asl_d, opcode_nop,
|
|
opcode_php, opcode_ora_i, opcode_asl_i, opcode_nop,
|
|
opcode_nop, opcode_ora_a, opcode_asl_a, opcode_nop,
|
|
// 1
|
|
opcode_bpl, opcode_ora_iy, opcode_nop, opcode_nop,
|
|
opcode_nop, opcode_ora_dx, opcode_asl_dx, opcode_nop,
|
|
opcode_clc, opcode_ora_ay, opcode_nop, opcode_nop,
|
|
opcode_nop, opcode_ora_ax, opcode_asl_ax, opcode_nop,
|
|
// 2
|
|
opcode_jsr_a, opcode_and_ix, opcode_nop, opcode_nop,
|
|
opcode_bit_d, opcode_and_d, opcode_rol_d, opcode_nop,
|
|
opcode_plp, opcode_and_i, opcode_rol_i, opcode_nop,
|
|
opcode_bit_a, opcode_and_a, opcode_rol_a, opcode_nop,
|
|
// 3
|
|
opcode_bmi, opcode_and_iy, opcode_nop, opcode_nop,
|
|
opcode_nop, opcode_and_dx, opcode_rol_dx, opcode_nop,
|
|
opcode_sec, opcode_and_ay, opcode_nop, opcode_nop,
|
|
opcode_nop, opcode_and_ax, opcode_rol_ax, opcode_nop,
|
|
// 4
|
|
opcode_rti, opcode_eor_ix, opcode_nop, opcode_nop,
|
|
opcode_nop, opcode_eor_d, opcode_lsr_d, opcode_nop,
|
|
opcode_pha, opcode_eor_i, opcode_lsr_i, opcode_nop,
|
|
opcode_jmp_a, opcode_eor_a, opcode_lsr_a, opcode_nop,
|
|
// 5
|
|
opcode_bvc, opcode_eor_iy, opcode_nop, opcode_nop,
|
|
opcode_nop, opcode_eor_dx, opcode_lsr_dx, opcode_nop,
|
|
opcode_cli, opcode_eor_ay, opcode_nop, opcode_nop,
|
|
opcode_nop, opcode_eor_ax, opcode_lsr_ax, opcode_nop,
|
|
// 6
|
|
opcode_rts, opcode_adc_ix, opcode_nop, opcode_nop,
|
|
opcode_nop, opcode_adc_d, opcode_ror_d, opcode_nop,
|
|
opcode_pla, opcode_adc_i, opcode_ror_i, opcode_nop,
|
|
opcode_jmp_i, opcode_adc_a, opcode_ror_a, opcode_nop,
|
|
// 7
|
|
opcode_bvs, opcode_adc_iy, opcode_nop, opcode_nop,
|
|
opcode_nop, opcode_adc_dx, opcode_ror_dx, opcode_nop,
|
|
opcode_sei, opcode_adc_ay, opcode_nop, opcode_nop,
|
|
opcode_nop, opcode_adc_ax, opcode_ror_ax, opcode_nop,
|
|
// 8
|
|
opcode_nop, opcode_sta_ix, opcode_nop, opcode_nop,
|
|
opcode_sty_d, opcode_sta_d, opcode_stx_d, opcode_nop,
|
|
opcode_dey, opcode_nop, opcode_txa, opcode_nop,
|
|
opcode_sty_a, opcode_sta_a, opcode_stx_a, opcode_nop,
|
|
// 9
|
|
opcode_bcc, opcode_sta_iy, opcode_nop, opcode_nop,
|
|
opcode_sty_dx, opcode_sta_dx, opcode_stx_dy, opcode_nop,
|
|
opcode_tya, opcode_sta_ay, opcode_txs, opcode_nop,
|
|
opcode_nop, opcode_sta_ax, opcode_nop, opcode_nop,
|
|
// a
|
|
opcode_ldy_i, opcode_lda_ix, opcode_ldx_i, opcode_nop,
|
|
opcode_ldy_d, opcode_lda_d, opcode_ldx_d, opcode_nop,
|
|
opcode_tay, opcode_lda_i, opcode_tax, opcode_nop,
|
|
opcode_ldy_a, opcode_lda_a, opcode_ldx_a, opcode_nop,
|
|
// b
|
|
opcode_bcs, opcode_lda_iy, opcode_nop, opcode_nop,
|
|
opcode_ldy_dx, opcode_lda_dx, opcode_ldx_dy, opcode_nop,
|
|
opcode_clv, opcode_lda_ay, opcode_tsx, opcode_nop,
|
|
opcode_ldy_ax, opcode_lda_ax, opcode_ldx_ay, opcode_nop,
|
|
// c
|
|
opcode_cpy_i, opcode_cmp_ix, opcode_nop, opcode_nop,
|
|
opcode_cpy_d, opcode_cmp_d, opcode_dec_d, opcode_nop,
|
|
opcode_iny, opcode_cmp_i, opcode_dex, opcode_nop,
|
|
opcode_cpy_a, opcode_cmp_a, opcode_dec_a, opcode_nop,
|
|
// d
|
|
opcode_bne, opcode_cmp_iy, opcode_nop, opcode_nop,
|
|
opcode_nop, opcode_cmp_dx, opcode_dec_dx, opcode_nop,
|
|
opcode_cld, opcode_cmp_ay, opcode_nop, opcode_nop,
|
|
opcode_nop, opcode_cmp_ax, opcode_dec_ax, opcode_nop,
|
|
// e
|
|
opcode_cpx_i, opcode_sbc_ix, opcode_nop, opcode_nop,
|
|
opcode_cpx_d, opcode_sbc_d, opcode_inc_d, opcode_nop,
|
|
opcode_inx, opcode_sbc_i, opcode_nop, opcode_nop,
|
|
opcode_cpx_a, opcode_sbc_a, opcode_inc_a, opcode_nop,
|
|
// f
|
|
opcode_beq, opcode_sbc_iy, opcode_nop, opcode_nop,
|
|
opcode_nop, opcode_sbc_dx, opcode_inc_dx, opcode_nop,
|
|
opcode_sed, opcode_sbc_ay, opcode_nop, opcode_nop,
|
|
opcode_nop, opcode_sbc_ax, opcode_inc_ax, opcode_nop,
|
|
};
|
|
|
|
// Truly a C moment
|
|
for (int i = 0; i < 256; ++i)
|
|
opcodes[i] = _opcodes[i];
|
|
|
|
cpu->flags = 0x20;
|
|
cpu->sp = cpu->a = cpu->x = cpu->y = 0;
|
|
emu6502_reset(cpu);
|
|
}
|
|
|
|
void emu6502_reset(CPU6502* cpu) {
|
|
cpu->pc = _CPUREAD16(0xfffc);
|
|
}
|
|
void emu6502_irq(CPU6502* cpu) {
|
|
_CPUPUSH16(cpu->pc);
|
|
_CPUPUSH(cpu->flags);
|
|
cpu->flags |= CPUFLAG_INTERRUPT;
|
|
cpu->pc = _CPUREAD16(0xfffe);
|
|
}
|
|
void emu6502_nmi(CPU6502* cpu) {
|
|
_CPUPUSH16(cpu->pc);
|
|
_CPUPUSH(cpu->flags);
|
|
cpu->flags |= CPUFLAG_INTERRUPT;
|
|
cpu->pc = _CPUREAD16(0xfffa);
|
|
}
|
|
|
|
void emu6502_run_opcode(CPU6502* cpu) {
|
|
CPUopcode *op = opcodes + cpu->read(cpu, cpu->pc++);
|
|
word arg;
|
|
switch (op->argsize) {
|
|
case 0: arg = 0; break;
|
|
case 1: arg = cpu->read(cpu, cpu->pc++); break;
|
|
case 2:
|
|
arg = (cpu->read(cpu, cpu->pc+1) << 8) | cpu->read(cpu, cpu->pc);
|
|
cpu->pc += 2;
|
|
break;
|
|
default: break;
|
|
}
|
|
op->function(cpu, arg);
|
|
cpu->flags |= 0x20; // temp fix
|
|
}
|
|
|
|
//// TESTING CODE
|
|
|
|
#ifdef EMU6502_TESTING
|
|
byte program[0x10000];
|
|
static void view_registers(CPU6502* cpu) {
|
|
printf("A = %02x\nX = %02x\nY = %02x\nPC = %04x\n"
|
|
"SP = %02x\nFLAGS = %02x\n",
|
|
cpu->a, cpu->x, cpu->y, cpu->pc, cpu->sp, cpu->flags);
|
|
|
|
for (int i = 0; i < 0x10; ++i)
|
|
printf("%02x ", _CPUREAD(0x1f0 + i));
|
|
putchar('\n');
|
|
}
|
|
|
|
static byte read_memory(CPU6502* cpu, word addr) {
|
|
/*
|
|
if (addr <= 0x600f && addr >= 0x6000) {
|
|
switch (addr & 0xf) {
|
|
case 0: return getchar(); break;
|
|
}
|
|
return 0;
|
|
}
|
|
else
|
|
return program[addr];
|
|
*/
|
|
return program[addr];
|
|
}
|
|
static void write_memory(CPU6502* cpu, word addr, byte val) {
|
|
/*
|
|
if (addr < 0x4000)
|
|
program[addr] = val;
|
|
else if (addr <= 0x600f && addr >= 0x6000) {
|
|
switch (addr & 0xf) {
|
|
case 0: putchar(val); break;
|
|
case 1: view_registers(cpu); break;
|
|
}
|
|
}
|
|
*/
|
|
program[addr] = val;
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
if (argc != 2) return 0;
|
|
// Load program ROM
|
|
FILE *rom = fopen(argv[1], "r");
|
|
int c;
|
|
for (word i = 0x0000; (c=fgetc(rom)) != EOF; ++i)
|
|
program[i] = c;
|
|
fclose(rom);
|
|
|
|
CPU6502 cpu;
|
|
cpu.write = write_memory;
|
|
cpu.read = read_memory;
|
|
emu6502_init_cpu(&cpu);
|
|
cpu.a = 0;
|
|
cpu.x = 0;
|
|
cpu.y = 0;
|
|
cpu.pc = 0x0400;
|
|
cpu.sp = 0;
|
|
cpu.flags = 0x20;
|
|
|
|
program[0xf0] = 0x42;
|
|
program[0xf1] = 0x41;
|
|
program[0x4142] = 0x10;
|
|
|
|
// while (cpu.read(&cpu, cpu.pc) != 0xea) {
|
|
word pprevaddr = 0xffff;
|
|
word prevaddr = 0xffff;
|
|
while (cpu.pc != 0x4def && cpu.pc != prevaddr) {
|
|
pprevaddr = prevaddr;
|
|
prevaddr = cpu.pc;
|
|
emu6502_run_opcode(&cpu);
|
|
|
|
printf("PC=%04x SP=%02x A=%02x X=%02x Y=%02x S=%02x\n",
|
|
cpu.pc, cpu.sp, cpu.a, cpu.x, cpu.y, cpu.flags);
|
|
}
|
|
fprintf(stderr, "Stop at %04x\n", cpu.pc);
|
|
// view_registers(&cpu);
|
|
return 0;
|
|
}
|
|
#endif
|