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

/*
* 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