#include <string.h>
#include "cpu_65c02.h"
-#define NMI_VECTOR 0xfffa
-#define RESET_VECTOR 0xfffc
-#define IRQ_VECTOR 0xfffe
-
-// gcc specific
-#define INLINE __attribute__((always_inline))
-
-// routines that take an effective address can also operate on a register
-// by means of special memory locations that are inaccessible by software
-#define EA_A (CPU_65C02_REG_A - CPU_65C02_N_REGS)
-#define EA_X (CPU_65C02_REG_X - CPU_65C02_N_REGS)
-#define EA_Y (CPU_65C02_REG_Y - CPU_65C02_N_REGS)
-#define EA_P (CPU_65C02_REG_P - CPU_65C02_N_REGS)
-#define EA_S (CPU_65C02_REG_S - CPU_65C02_N_REGS)
-
// initialization
void cpu_65c02_init(
struct cpu_65c02 *self,
self->write_byte = write_byte;
}
-// memory (or internal register memory) access
-INLINE int cpu_65c02_rb(struct cpu_65c02 *self, int addr) {
- if (addr < 0)
- return self->regs.mem[CPU_65C02_N_REGS + addr];
- self->cycles += 1;
- return self->read_byte(addr & 0xffff);
-}
-
-INLINE int cpu_65c02_rw(struct cpu_65c02 *self, int addr) {
- int data = cpu_65c02_rb(self, addr);
- return data | (cpu_65c02_rb(self, addr + 1) << 8);
-}
-
-INLINE int cpu_65c02_rw_zpg(struct cpu_65c02 *self, int addr) {
- int data = cpu_65c02_rb(self, addr & 0xff);
- return data | (cpu_65c02_rb(self, (addr + 1) & 0xff) << 8);
-}
-
-INLINE void cpu_65c02_wb(struct cpu_65c02 *self, int addr, int data) {
- self->cycles += 1;
- if (addr < 0)
- self->regs.mem[CPU_65C02_N_REGS + addr] = data;
- else
- self->write_byte(addr, data);
-}
-
-INLINE void cpu_65c02_ww(struct cpu_65c02 *self, int addr, int data) {
- cpu_65c02_wb(self, addr, data & 0xff);
- cpu_65c02_wb(self, addr + 1, data >> 8);
-}
-
-INLINE int cpu_65c02_fb(struct cpu_65c02 *self) {
- int data = cpu_65c02_rb(self, self->regs.word.pc++);
- return data;
-}
-
-INLINE int cpu_65c02_fw(struct cpu_65c02 *self) {
- int data = cpu_65c02_fb(self);
- return data | (cpu_65c02_fb(self) << 8);
-}
-
-INLINE void cpu_65c02_phb(struct cpu_65c02 *self, int data) {
- cpu_65c02_wb(self, self->regs.word.sp, data);
- --self->regs.byte.s;
-}
-
-INLINE void cpu_65c02_phw(struct cpu_65c02 *self, int data) {
- cpu_65c02_phb(self, data >> 8);
- cpu_65c02_phb(self, data & 0xff);
-}
-
-INLINE int cpu_65c02_plb(struct cpu_65c02 *self) {
- ++self->regs.byte.s;
- return cpu_65c02_rb(self, self->regs.word.sp);
-}
-
-INLINE int cpu_65c02_plw(struct cpu_65c02 *self) {
- int data = cpu_65c02_plb(self);
- return data | (cpu_65c02_plb(self) << 8);
-}
-
-// effective address calculation
-INLINE int cpu_65c02_ea_abs(struct cpu_65c02 *self) {
- return cpu_65c02_fw(self);
-}
-
-INLINE int cpu_65c02_ea_abs_idx(struct cpu_65c02 *self, int rvalue) {
- int addr = cpu_65c02_ea_abs(self);
- self->cycles += ((addr & 0xff) + (rvalue & 0xff)) >> 8;
- return addr + rvalue;
-}
-
-INLINE int cpu_65c02_ea_abs_idx_ind(struct cpu_65c02 *self, int rvalue) {
- return cpu_65c02_rw(self, cpu_65c02_ea_abs_idx(self, rvalue));
-}
-
-INLINE int cpu_65c02_ea_abs_ind(struct cpu_65c02 *self) {
- return cpu_65c02_rw(self, cpu_65c02_ea_abs(self));
-}
-
-INLINE int cpu_65c02_ea_rel(struct cpu_65c02 *self) {
- int offset = cpu_65c02_fb(self);
- return offset - ((offset << 1) & 0x100);
-}
-
-INLINE int cpu_65c02_ea_zpg(struct cpu_65c02 *self) {
- return cpu_65c02_fb(self);
-}
-
-INLINE int cpu_65c02_ea_zpg_idx(struct cpu_65c02 *self, int rvalue) {
- return (cpu_65c02_ea_zpg(self) + rvalue) & 0xff;
-}
-
-INLINE int cpu_65c02_ea_zpg_idx_ind(struct cpu_65c02 *self, int rvalue) {
- return cpu_65c02_rw_zpg(self, cpu_65c02_ea_zpg_idx(self, rvalue));
-}
-
-INLINE int cpu_65c02_ea_zpg_ind(struct cpu_65c02 *self) {
- return cpu_65c02_rw_zpg(self, cpu_65c02_ea_zpg(self));
-}
-
-INLINE int cpu_65c02_ea_zpg_ind_idx(struct cpu_65c02 *self, int rvalue) {
- int addr = cpu_65c02_rw_zpg(self, cpu_65c02_ea_zpg(self));
- self->cycles += ((addr & 0xff) + (rvalue & 0xff)) >> 8;
- return addr + rvalue;
-}
-
-// instruction execute
-INLINE void cpu_65c02_adc(struct cpu_65c02 *self, int rvalue) {
- int result0, result1, result2;
- if (self->regs.bit.df) {
- result0 = (self->regs.byte.a & 0xf) + (rvalue & 0xf) + self->regs.bit.cf;
- if (result0 >= 0xa)
- result0 = (result0 + 6) & 0x1f;
- result0 += (self->regs.byte.a & 0x70) + (rvalue & 0x70);
- result1 = (self->regs.byte.a & 0x80) + (rvalue & 0x80) + result0;
- result2 = result1;
- if (result2 >= 0xa0)
- result2 = (result2 + 0x60) & 0x1ff;
- }
- else {
- result0 = (self->regs.byte.a & 0x7f) + (rvalue & 0x7f) + self->regs.bit.cf;
- result1 = (self->regs.byte.a & 0x80) + (rvalue & 0x80) + result0;
- result2 = result1;
- }
- self->regs.byte.a = result2 & 0xff;
- self->regs.bit.cf = result2 >> 8;
- self->regs.bit.zf = (result2 & 0xff) == 0;
- self->regs.bit.vf = ((result0 >> 7) ^ (result1 >> 8)) & 1;
- self->regs.bit.nf = (result2 >> 7) & 1;
-}
-
-INLINE void cpu_65c02_and(struct cpu_65c02 *self, int rvalue) {
- int result = self->regs.byte.a & rvalue;
- self->regs.byte.a = result;
- self->regs.bit.zf = result == 0;
- self->regs.bit.nf = (result >> 7) & 1;
-}
-
-INLINE void cpu_65c02_asl(struct cpu_65c02 *self, int lvalue) {
- int result = cpu_65c02_rb(self, lvalue) << 1;
- ++self->cycles;
- cpu_65c02_wb(self, lvalue, result & 0xff);
- self->regs.bit.cf = result >> 8;
- self->regs.bit.zf = (result & 0xff) == 0;
- self->regs.bit.nf = (result >> 7) & 1;
-}
-
-INLINE void cpu_65c02_bbr(struct cpu_65c02 *self, int rvalue, int lvalue) {
- if (rvalue == 0) {
- self->cycles += ((self->regs.word.pc & 0xff) + (lvalue & 0xff)) >> 8;
- self->regs.word.pc = (self->regs.word.pc + lvalue) & 0xffff;
- }
-}
-
-INLINE void cpu_65c02_bbs(struct cpu_65c02 *self, int rvalue, int lvalue) {
- if (rvalue) {
- self->cycles += ((self->regs.word.pc & 0xff) + (lvalue & 0xff)) >> 8;
- self->regs.word.pc = (self->regs.word.pc + lvalue) & 0xffff;
- }
-}
-
-INLINE void cpu_65c02_bc(struct cpu_65c02 *self, int flag, int lvalue) {
- if ((self->regs.byte.p & flag) == 0) {
- self->cycles += ((self->regs.word.pc & 0xff) + (lvalue & 0xff)) >> 8;
- self->regs.word.pc = (self->regs.word.pc + lvalue) & 0xffff;
- }
-}
-
-INLINE void cpu_65c02_bs(struct cpu_65c02 *self, int flag, int lvalue) {
- if (self->regs.byte.p & flag) {
- self->cycles += ((self->regs.word.pc & 0xff) + (lvalue & 0xff)) >> 8;
- self->regs.word.pc = (self->regs.word.pc + lvalue) & 0xffff;
- }
-}
-
-INLINE void cpu_65c02_bit(struct cpu_65c02 *self, int rvalue) {
- int result = self->regs.byte.a & rvalue;
- self->regs.bit.zf = result == 0;
- self->regs.bit.vf = (rvalue >> 6) & 1;
- self->regs.bit.nf = (rvalue >> 7) & 1;
-}
-
-INLINE void cpu_65c02_bit_imm(struct cpu_65c02 *self, int rvalue) {
- int result = self->regs.byte.a & rvalue;
- self->regs.bit.zf = result == 0;
-}
-
-INLINE void cpu_65c02_brk(struct cpu_65c02 *self) {
- ++self->cycles;
- cpu_65c02_phw(self, (self->regs.word.pc + 1) & 0xffff);
- cpu_65c02_phb(self, self->regs.byte.p);
- self->regs.word.pc = cpu_65c02_rw(self, IRQ_VECTOR);
- self->regs.bit._if = true;
- self->regs.bit.df = false;
-}
-
-INLINE void cpu_65c02_cl(struct cpu_65c02 *self, int flag) {
- self->regs.byte.p &= ~flag;
-}
-
-INLINE void cpu_65c02_cmp(struct cpu_65c02 *self, int rvalue0, int rvalue1) {
- rvalue1 ^= 0xff;
- int result = rvalue0 + rvalue1 + 1;
- self->regs.bit.cf = result >> 8;
- self->regs.bit.zf = (result & 0xff) == 0;
- self->regs.bit.nf = (result >> 7) & 1;
-}
-
-INLINE void cpu_65c02_dec(struct cpu_65c02 *self, int lvalue) {
- int result = (cpu_65c02_rb(self, lvalue) - 1) & 0xff;
- ++self->cycles;
- cpu_65c02_wb(self, lvalue, result);
- self->regs.bit.zf = result == 0;
- self->regs.bit.nf = (result >> 7) & 1;
-}
-
-INLINE void cpu_65c02_eor(struct cpu_65c02 *self, int rvalue) {
- int result = self->regs.byte.a ^ rvalue;
- self->regs.byte.a = result;
- self->regs.bit.zf = result == 0;
- self->regs.bit.nf = (result >> 7) & 1;
-}
-
-INLINE void cpu_65c02_ill11(struct cpu_65c02 *self) {
-}
-
-INLINE void cpu_65c02_ill22(struct cpu_65c02 *self) {
- ++self->cycles;
- self->regs.word.pc = (self->regs.word.pc + 1) & 0xffff;
-}
-
-INLINE void cpu_65c02_ill23(struct cpu_65c02 *self) {
- self->cycles += 2;
- self->regs.word.pc = (self->regs.word.pc + 1) & 0xffff;
-}
-
-INLINE void cpu_65c02_ill24(struct cpu_65c02 *self) {
- self->cycles += 3;
- self->regs.word.pc = (self->regs.word.pc + 1) & 0xffff;
-}
-
-INLINE void cpu_65c02_ill34(struct cpu_65c02 *self) {
- self->cycles += 3;
- self->regs.word.pc = (self->regs.word.pc + 2) & 0xffff;
-}
-
-INLINE void cpu_65c02_ill38(struct cpu_65c02 *self) {
- self->cycles += 7;
- self->regs.word.pc = (self->regs.word.pc + 2) & 0xffff;
-}
-
-INLINE void cpu_65c02_inc(struct cpu_65c02 *self, int lvalue) {
- int result = (cpu_65c02_rb(self, lvalue) + 1) & 0xff;
- ++self->cycles;
- cpu_65c02_wb(self, lvalue, result);
- self->regs.bit.zf = result == 0;
- self->regs.bit.nf = (result >> 7) & 1;
-}
-
-INLINE void cpu_65c02_jmp(struct cpu_65c02 *self, int lvalue) {
- self->regs.word.pc = lvalue;
-}
-
-INLINE void cpu_65c02_jsr(struct cpu_65c02 *self, int lvalue) {
- cpu_65c02_phw(self, (self->regs.word.pc - 1) & 0xffff);
- self->regs.word.pc = lvalue;
-}
-
-INLINE void cpu_65c02_ld(struct cpu_65c02 *self, int lvalue, int rvalue) {
- cpu_65c02_wb(self, lvalue, rvalue);
- self->regs.bit.zf = rvalue == 0;
- self->regs.bit.nf = (rvalue >> 7) & 1;
-}
-
-INLINE void cpu_65c02_lsr(struct cpu_65c02 *self, int lvalue) {
- int result = cpu_65c02_rb(self, lvalue);
- ++self->cycles;
- cpu_65c02_wb(self, lvalue, result >> 1);
- self->regs.bit.cf = result & 1;
- self->regs.bit.zf = (result & 0xfe) == 0;
- self->regs.bit.nf = false;
-}
-
-INLINE void cpu_65c02_nop(struct cpu_65c02 *self) {
-}
-
-INLINE void cpu_65c02_ora(struct cpu_65c02 *self, int rvalue) {
- int result = self->regs.byte.a | rvalue;
- self->regs.byte.a = result;
- self->regs.bit.zf = result == 0;
- self->regs.bit.nf = (result >> 7) & 1;
-}
-
-INLINE void cpu_65c02_ph(struct cpu_65c02 *self, int rvalue) {
- cpu_65c02_phb(self, rvalue);
-}
-
-INLINE void cpu_65c02_pl(struct cpu_65c02 *self, int lvalue) {
- int result = cpu_65c02_plb(self);
- cpu_65c02_wb(self, lvalue, result);
- self->regs.bit.zf = result == 0;
- self->regs.bit.nf = (result >> 7) & 1;
-}
-
-INLINE void cpu_65c02_plp(struct cpu_65c02 *self) {
- self->regs.byte.p = cpu_65c02_plb(self) | 0x30;
-}
-
-INLINE void cpu_65c02_rmb(struct cpu_65c02 *self, int rvalue, int lvalue) {
- int result = cpu_65c02_rb(self, lvalue) & ~rvalue;
- ++self->cycles;
- cpu_65c02_wb(self, lvalue, result);
-}
-
-INLINE void cpu_65c02_rol(struct cpu_65c02 *self, int lvalue) {
- int result = (cpu_65c02_rb(self, lvalue) << 1) | self->regs.bit.cf;
- ++self->cycles;
- cpu_65c02_wb(self, lvalue, result & 0xff);
- self->regs.bit.cf = result >> 8;
- self->regs.bit.zf = (result & 0xff) == 0;
- self->regs.bit.nf = (result >> 7) & 1;
-}
-
-INLINE void cpu_65c02_ror(struct cpu_65c02 *self, int lvalue) {
- int result = cpu_65c02_rb(self, lvalue) | (self->regs.bit.cf << 8);
- ++self->cycles;
- cpu_65c02_wb(self, lvalue, result >> 1);
- self->regs.bit.cf = result & 1;
- self->regs.bit.zf = (result & 0x1fe) == 0;
- self->regs.bit.nf = (result >> 8) & 1;
-}
-
-INLINE void cpu_65c02_rti(struct cpu_65c02 *self) {
- self->regs.byte.p = cpu_65c02_plb(self) | 0x30;
- self->regs.word.pc = cpu_65c02_plw(self);
-}
-
-INLINE void cpu_65c02_rts(struct cpu_65c02 *self) {
- self->regs.word.pc = (cpu_65c02_plw(self) + 1) & 0xffff;
-}
-
-INLINE void cpu_65c02_sbc(struct cpu_65c02 *self, int rvalue) {
- rvalue ^= 0xff;
- int result0, result1, result2;
- if (self->regs.bit.df) {
- result0 = (self->regs.byte.a & 0xf) + (rvalue & 0xf) + self->regs.bit.cf;
- if (result0 < 0x10)
- result0 = (result0 - 6) & 0x1f;
- result0 += (self->regs.byte.a & 0x70) + (rvalue & 0x70);
- result1 = (self->regs.byte.a & 0x80) + (rvalue & 0x80) + result0;
- result2 = result1;
- if (result2 < 0x100)
- result2 = (result2 - 0x60) & 0x1ff;
- }
- else {
- result0 = (self->regs.byte.a & 0x7f) + (rvalue & 0x7f) + self->regs.bit.cf;
- result1 = (self->regs.byte.a & 0x80) + (rvalue & 0x80) + result0;
- result2 = result1;
- }
- self->regs.byte.a = result2 & 0xff;
- self->regs.bit.cf = result2 >> 8;
- self->regs.bit.zf = (result2 & 0xff) == 0;
- self->regs.bit.vf = ((result0 >> 7) ^ (result1 >> 8)) & 1;
- self->regs.bit.nf = (result2 >> 7) & 1;
-}
-
-INLINE void cpu_65c02_se(struct cpu_65c02 *self, int flag) {
- self->regs.byte.p |= flag;
-}
-
-INLINE void cpu_65c02_smb(struct cpu_65c02 *self, int rvalue, int lvalue) {
- int result = cpu_65c02_rb(self, lvalue) | rvalue;
- ++self->cycles;
- cpu_65c02_wb(self, lvalue, result);
-}
-
-INLINE void cpu_65c02_st(struct cpu_65c02 *self, int rvalue, int lvalue) {
- cpu_65c02_wb(self, lvalue, rvalue);
-}
-
-INLINE void cpu_65c02_stp(struct cpu_65c02 *self) {
- abort();
-}
-
-INLINE void cpu_65c02_trb(struct cpu_65c02 *self, int lvalue) {
- int result = cpu_65c02_rb(self, lvalue);
- ++self->cycles;
- cpu_65c02_wb(self, lvalue, result & ~self->regs.byte.a);
- result &= self->regs.byte.a;
- self->regs.bit.zf = result == 0;
-}
-
-INLINE void cpu_65c02_tsb(struct cpu_65c02 *self, int lvalue) {
- int result = cpu_65c02_rb(self, lvalue);
- ++self->cycles;
- cpu_65c02_wb(self, lvalue, result | self->regs.byte.a);
- result &= self->regs.byte.a;
- self->regs.bit.zf = result == 0;
-}
-
-INLINE void cpu_65c02_wai(struct cpu_65c02 *self) {
- abort();
-}
-
// instruction decode
void cpu_65c02_execute(struct cpu_65c02 *self) {
switch (cpu_65c02_fb(self)) {
cpu_65c02_ora(self, cpu_65c02_fb(self));
break;
case 0x0a:
- cpu_65c02_asl(self, EA_A);
+ cpu_65c02_asl(self, CPU_65C02_EA_A);
break;
case 0x0b:
cpu_65c02_ill11(self);
cpu_65c02_ora(self, cpu_65c02_rb(self, cpu_65c02_ea_abs_idx(self, self->regs.byte.y)));
break;
case 0x1a:
- cpu_65c02_inc(self, EA_A);
+ cpu_65c02_inc(self, CPU_65C02_EA_A);
break;
case 0x1b:
cpu_65c02_ill11(self);
cpu_65c02_and(self, cpu_65c02_fb(self));
break;
case 0x2a:
- cpu_65c02_rol(self, EA_A);
+ cpu_65c02_rol(self, CPU_65C02_EA_A);
break;
case 0x2b:
cpu_65c02_ill11(self);
cpu_65c02_and(self, cpu_65c02_rb(self, cpu_65c02_ea_abs_idx(self, self->regs.byte.y)));
break;
case 0x3a:
- cpu_65c02_dec(self, EA_A);
+ cpu_65c02_dec(self, CPU_65C02_EA_A);
break;
case 0x3b:
cpu_65c02_ill11(self);
cpu_65c02_eor(self, cpu_65c02_fb(self));
break;
case 0x4a:
- cpu_65c02_lsr(self, EA_A);
+ cpu_65c02_lsr(self, CPU_65C02_EA_A);
break;
case 0x4b:
cpu_65c02_ill11(self);
cpu_65c02_rmb(self, 0x40, cpu_65c02_ea_zpg(self));
break;
case 0x68:
- cpu_65c02_pl(self, EA_A);
+ cpu_65c02_pl(self, CPU_65C02_EA_A);
break;
case 0x69:
cpu_65c02_adc(self, cpu_65c02_fb(self));
break;
case 0x6a:
- cpu_65c02_ror(self, EA_A);
+ cpu_65c02_ror(self, CPU_65C02_EA_A);
break;
case 0x6b:
cpu_65c02_ill11(self);
cpu_65c02_adc(self, cpu_65c02_rb(self, cpu_65c02_ea_abs_idx(self, self->regs.byte.y)));
break;
case 0x7a:
- cpu_65c02_pl(self, EA_Y);
+ cpu_65c02_pl(self, CPU_65C02_EA_Y);
break;
case 0x7b:
cpu_65c02_ill11(self);
cpu_65c02_smb(self, 1, cpu_65c02_ea_zpg(self));
break;
case 0x88:
- cpu_65c02_dec(self, EA_Y);
+ cpu_65c02_dec(self, CPU_65C02_EA_Y);
break;
case 0x89:
cpu_65c02_bit_imm(self, cpu_65c02_fb(self));
break;
case 0x8a:
- cpu_65c02_ld(self, EA_A, self->regs.byte.x);
+ cpu_65c02_ld(self, CPU_65C02_EA_A, self->regs.byte.x);
break;
case 0x8b:
cpu_65c02_ill11(self);
cpu_65c02_smb(self, 2, cpu_65c02_ea_zpg(self));
break;
case 0x98:
- cpu_65c02_ld(self, EA_A, self->regs.byte.y);
+ cpu_65c02_ld(self, CPU_65C02_EA_A, self->regs.byte.y);
break;
case 0x99:
cpu_65c02_st(self, self->regs.byte.a, cpu_65c02_ea_abs_idx(self, self->regs.byte.y));
break;
case 0x9a:
- cpu_65c02_st(self, self->regs.byte.x, EA_S);
+ cpu_65c02_st(self, self->regs.byte.x, CPU_65C02_EA_S);
break;
case 0x9b:
cpu_65c02_ill11(self);
}
break;
case 0xa0:
- cpu_65c02_ld(self, EA_Y, cpu_65c02_fb(self));
+ cpu_65c02_ld(self, CPU_65C02_EA_Y, cpu_65c02_fb(self));
break;
case 0xa1:
- cpu_65c02_ld(self, EA_A, cpu_65c02_rb(self, cpu_65c02_ea_zpg_idx_ind(self, self->regs.byte.x)));
+ cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_rb(self, cpu_65c02_ea_zpg_idx_ind(self, self->regs.byte.x)));
break;
case 0xa2:
- cpu_65c02_ld(self, EA_X, cpu_65c02_fb(self));
+ cpu_65c02_ld(self, CPU_65C02_EA_X, cpu_65c02_fb(self));
break;
case 0xa3:
cpu_65c02_ill11(self);
break;
case 0xa4:
- cpu_65c02_ld(self, EA_Y, cpu_65c02_rb(self, cpu_65c02_ea_zpg(self)));
+ cpu_65c02_ld(self, CPU_65C02_EA_Y, cpu_65c02_rb(self, cpu_65c02_ea_zpg(self)));
break;
case 0xa5:
- cpu_65c02_ld(self, EA_A, cpu_65c02_rb(self, cpu_65c02_ea_zpg(self)));
+ cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_rb(self, cpu_65c02_ea_zpg(self)));
break;
case 0xa6:
- cpu_65c02_ld(self, EA_X, cpu_65c02_rb(self, cpu_65c02_ea_zpg(self)));
+ cpu_65c02_ld(self, CPU_65C02_EA_X, cpu_65c02_rb(self, cpu_65c02_ea_zpg(self)));
break;
case 0xa7:
cpu_65c02_smb(self, 4, cpu_65c02_ea_zpg(self));
break;
case 0xa8:
- cpu_65c02_ld(self, EA_Y, self->regs.byte.a);
+ cpu_65c02_ld(self, CPU_65C02_EA_Y, self->regs.byte.a);
break;
case 0xa9:
- cpu_65c02_ld(self, EA_A, cpu_65c02_fb(self));
+ cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_fb(self));
break;
case 0xaa:
- cpu_65c02_ld(self, EA_X, self->regs.byte.a);
+ cpu_65c02_ld(self, CPU_65C02_EA_X, self->regs.byte.a);
break;
case 0xab:
cpu_65c02_ill11(self);
break;
case 0xac:
- cpu_65c02_ld(self, EA_Y, cpu_65c02_rb(self, cpu_65c02_ea_abs(self)));
+ cpu_65c02_ld(self, CPU_65C02_EA_Y, cpu_65c02_rb(self, cpu_65c02_ea_abs(self)));
break;
case 0xad:
- cpu_65c02_ld(self, EA_A, cpu_65c02_rb(self, cpu_65c02_ea_abs(self)));
+ cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_rb(self, cpu_65c02_ea_abs(self)));
break;
case 0xae:
- cpu_65c02_ld(self, EA_X, cpu_65c02_rb(self, cpu_65c02_ea_abs(self)));
+ cpu_65c02_ld(self, CPU_65C02_EA_X, cpu_65c02_rb(self, cpu_65c02_ea_abs(self)));
break;
case 0xaf:
{
cpu_65c02_bs(self, (1 << CPU_65C02_REG_P_BIT_C), cpu_65c02_ea_rel(self));
break;
case 0xb1:
- cpu_65c02_ld(self, EA_A, cpu_65c02_rb(self, cpu_65c02_ea_zpg_ind_idx(self, self->regs.byte.y)));
+ cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_rb(self, cpu_65c02_ea_zpg_ind_idx(self, self->regs.byte.y)));
break;
case 0xb2:
- cpu_65c02_ld(self, EA_A, cpu_65c02_rb(self, cpu_65c02_ea_zpg_ind(self)));
+ cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_rb(self, cpu_65c02_ea_zpg_ind(self)));
break;
case 0xb3:
cpu_65c02_ill11(self);
break;
case 0xb4:
- cpu_65c02_ld(self, EA_Y, cpu_65c02_rb(self, cpu_65c02_ea_zpg_idx(self, self->regs.byte.x)));
+ cpu_65c02_ld(self, CPU_65C02_EA_Y, cpu_65c02_rb(self, cpu_65c02_ea_zpg_idx(self, self->regs.byte.x)));
break;
case 0xb5:
- cpu_65c02_ld(self, EA_A, cpu_65c02_rb(self, cpu_65c02_ea_zpg_idx(self, self->regs.byte.x)));
+ cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_rb(self, cpu_65c02_ea_zpg_idx(self, self->regs.byte.x)));
break;
case 0xb6:
- cpu_65c02_ld(self, EA_X, cpu_65c02_rb(self, cpu_65c02_ea_zpg_idx(self, self->regs.byte.y)));
+ cpu_65c02_ld(self, CPU_65C02_EA_X, cpu_65c02_rb(self, cpu_65c02_ea_zpg_idx(self, self->regs.byte.y)));
break;
case 0xb7:
cpu_65c02_smb(self, 8, cpu_65c02_ea_zpg(self));
cpu_65c02_cl(self, (1 << CPU_65C02_REG_P_BIT_V));
break;
case 0xb9:
- cpu_65c02_ld(self, EA_A, cpu_65c02_rb(self, cpu_65c02_ea_abs_idx(self, self->regs.byte.y)));
+ cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_rb(self, cpu_65c02_ea_abs_idx(self, self->regs.byte.y)));
break;
case 0xba:
- cpu_65c02_ld(self, EA_X, self->regs.byte.s);
+ cpu_65c02_ld(self, CPU_65C02_EA_X, self->regs.byte.s);
break;
case 0xbb:
cpu_65c02_ill11(self);
break;
case 0xbc:
- cpu_65c02_ld(self, EA_Y, cpu_65c02_rb(self, cpu_65c02_ea_abs_idx(self, self->regs.byte.x)));
+ cpu_65c02_ld(self, CPU_65C02_EA_Y, cpu_65c02_rb(self, cpu_65c02_ea_abs_idx(self, self->regs.byte.x)));
break;
case 0xbd:
- cpu_65c02_ld(self, EA_A, cpu_65c02_rb(self, cpu_65c02_ea_abs_idx(self, self->regs.byte.x)));
+ cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_rb(self, cpu_65c02_ea_abs_idx(self, self->regs.byte.x)));
break;
case 0xbe:
- cpu_65c02_ld(self, EA_X, cpu_65c02_rb(self, cpu_65c02_ea_abs_idx(self, self->regs.byte.y)));
+ cpu_65c02_ld(self, CPU_65C02_EA_X, cpu_65c02_rb(self, cpu_65c02_ea_abs_idx(self, self->regs.byte.y)));
break;
case 0xbf:
{
cpu_65c02_smb(self, 0x10, cpu_65c02_ea_zpg(self));
break;
case 0xc8:
- cpu_65c02_inc(self, EA_Y);
+ cpu_65c02_inc(self, CPU_65C02_EA_Y);
break;
case 0xc9:
cpu_65c02_cmp(self, self->regs.byte.a, cpu_65c02_fb(self));
break;
case 0xca:
- cpu_65c02_dec(self, EA_X);
+ cpu_65c02_dec(self, CPU_65C02_EA_X);
break;
case 0xcb:
cpu_65c02_wai(self);
cpu_65c02_smb(self, 0x40, cpu_65c02_ea_zpg(self));
break;
case 0xe8:
- cpu_65c02_inc(self, EA_X);
+ cpu_65c02_inc(self, CPU_65C02_EA_X);
break;
case 0xe9:
cpu_65c02_sbc(self, cpu_65c02_fb(self));
cpu_65c02_sbc(self, cpu_65c02_rb(self, cpu_65c02_ea_abs_idx(self, self->regs.byte.y)));
break;
case 0xfa:
- cpu_65c02_pl(self, EA_X);
+ cpu_65c02_pl(self, CPU_65C02_EA_X);
break;
case 0xfb:
cpu_65c02_ill11(self);
#ifndef _CPU_65C02_H
#define _CPU_65C02_H
+#include <stdbool.h>
#include <stdint.h>
+#include <stdlib.h>
-// internal register numbering
-#define CPU_65C02_REG_A 0
-#define CPU_65C02_REG_X 1
-#define CPU_65C02_REG_Y 2
-#define CPU_65C02_REG_P 3
-#define CPU_65C02_REG_SP 4
-#define CPU_65C02_REG_S 4
-#define CPU_65C02_REG_PC 6
-#define CPU_65C02_N_REGS 8
+// gcc specific
+#ifndef ALWAYS_INLINE
+#define ALWAYS_INLINE __attribute__((always_inline))
+#endif
+#ifndef PACKED
+#define PACKED __attribute__((__packed__))
+#endif
+
+#define CPU_65C02_NMI_VECTOR 0xfffa
+#define CPU_65C02_RESET_VECTOR 0xfffc
+#define CPU_65C02_IRQ_VECTOR 0xfffe
// bits within REG_P
#define CPU_65C02_REG_P_BIT_C 0
#define CPU_65C02_REG_P_BIT_V 6
#define CPU_65C02_REG_P_BIT_N 7
+// special memory locations (negative address)
+#define CPU_65C02_EA_A (-8)
+#define CPU_65C02_EA_X (-7)
+#define CPU_65C02_EA_Y (-6)
+#define CPU_65C02_EA_P (-5)
+#define CPU_65C02_EA_S (-4)
+
+// registers, in same order as special memory locations, but reversed on
+// big endian hardware where special memory address will be complemented
+// (this allows special memory to always look like it is little endian)
union cpu_65c02_regs {
- struct {
- uint8_t _fill0[3];
+#if __BYTE_ORDER == __BIG_ENDIAN
+ PACKED struct {
+ uint16_t _fill_pc;
+ uint16_t _fill_sp;
+ uint8_t nf : 1;
+ uint8_t vf : 1;
+ uint8_t _fill_5f: 1;
+ uint8_t _fill_4f: 1;
+ uint8_t df : 1;
+ uint8_t _if : 1;
+ uint8_t zf : 1;
+ uint8_t cf : 1;
+ uint8_t _fill_y;
+ uint8_t _fill_x;
+ uint8_t _fill_a;
+ } bit;
+ PACKED struct {
+ uint16_t _fill_pc;
+ uint8_t _fill_sph;
+ uint8_t s;
+ uint8_t p;
+ uint8_t y;
+ uint8_t x;
+ uint8_t a;
+ } byte;
+ PACKED struct {
+ uint16_t pc;
+ uint16_t sp;
+ uint8_t _fill_p;
+ uint8_t _fill_y;
+ uint8_t _fill_x;
+ uint8_t _fill_a;
+ } word;
+ uint8_t mem_be[8];
+#else
+ PACKED struct {
+ uint8_t _fill_a;
+ uint8_t _fill_x;
+ uint8_t _fill_y;
uint8_t cf : 1;
uint8_t zf : 1;
uint8_t _if : 1;
uint8_t df : 1;
- uint8_t _fill1: 2;
+ uint8_t _fill_4f: 1;
+ uint8_t _fill_5f: 1;
uint8_t vf : 1;
uint8_t nf : 1;
+ uint16_t _fill_sp;
+ uint16_t _fill_pc;
} bit;
- struct {
+ PACKED struct {
uint8_t a;
uint8_t x;
uint8_t y;
uint8_t p;
uint8_t s;
+ uint8_t _fill_sph;
+ uint16_t _fill_pc;
} byte;
- struct {
- uint16_t _fill0[2];
+ PACKED struct {
+ uint8_t _fill_a;
+ uint8_t _fill_x;
+ uint8_t _fill_y;
+ uint8_t _fill_p;
uint16_t sp;
uint16_t pc;
} word;
- uint8_t mem[CPU_65C02_N_REGS];
+ uint8_t mem_le[8];
+#endif
};
struct cpu_65c02 {
union cpu_65c02_regs regs;
};
+// memory (or internal register memory) access
+static ALWAYS_INLINE int cpu_65c02_rb(struct cpu_65c02 *self, int addr) {
+ if (addr < 0)
+#if __BYTE_ORDER == __BIG_ENDIAN
+ return self->regs.mem_be[~addr];
+#else
+ return self->regs.mem_le[addr - CPU_65C02_EA_A];
+#endif
+ self->cycles += 1;
+ return self->read_byte(addr & 0xffff);
+}
+
+static ALWAYS_INLINE int cpu_65c02_rw(struct cpu_65c02 *self, int addr) {
+ int data = cpu_65c02_rb(self, addr);
+ return data | (cpu_65c02_rb(self, addr + 1) << 8);
+}
+
+static ALWAYS_INLINE int cpu_65c02_rw_zpg(struct cpu_65c02 *self, int addr) {
+ int data = cpu_65c02_rb(self, addr & 0xff);
+ return data | (cpu_65c02_rb(self, (addr + 1) & 0xff) << 8);
+}
+
+static ALWAYS_INLINE void cpu_65c02_wb(struct cpu_65c02 *self, int addr, int data) {
+ self->cycles += 1;
+ if (addr < 0)
+#if __BYTE_ORDER == __BIG_ENDIAN
+ self->regs.mem_be[~addr] = data;
+#else
+ self->regs.mem_le[addr - CPU_65C02_EA_A] = data;
+#endif
+ else
+ self->write_byte(addr, data);
+}
+
+static ALWAYS_INLINE void cpu_65c02_ww(struct cpu_65c02 *self, int addr, int data) {
+ cpu_65c02_wb(self, addr, data & 0xff);
+ cpu_65c02_wb(self, addr + 1, data >> 8);
+}
+
+static ALWAYS_INLINE int cpu_65c02_fb(struct cpu_65c02 *self) {
+ int data = cpu_65c02_rb(self, self->regs.word.pc++);
+ return data;
+}
+
+static ALWAYS_INLINE int cpu_65c02_fw(struct cpu_65c02 *self) {
+ int data = cpu_65c02_fb(self);
+ return data | (cpu_65c02_fb(self) << 8);
+}
+
+static ALWAYS_INLINE void cpu_65c02_phb(struct cpu_65c02 *self, int data) {
+ cpu_65c02_wb(self, self->regs.word.sp, data);
+ --self->regs.byte.s;
+}
+
+static ALWAYS_INLINE void cpu_65c02_phw(struct cpu_65c02 *self, int data) {
+ cpu_65c02_phb(self, data >> 8);
+ cpu_65c02_phb(self, data & 0xff);
+}
+
+static ALWAYS_INLINE int cpu_65c02_plb(struct cpu_65c02 *self) {
+ ++self->regs.byte.s;
+ return cpu_65c02_rb(self, self->regs.word.sp);
+}
+
+static ALWAYS_INLINE int cpu_65c02_plw(struct cpu_65c02 *self) {
+ int data = cpu_65c02_plb(self);
+ return data | (cpu_65c02_plb(self) << 8);
+}
+
+// effective address calculation
+static ALWAYS_INLINE int cpu_65c02_ea_abs(struct cpu_65c02 *self) {
+ return cpu_65c02_fw(self);
+}
+
+static ALWAYS_INLINE int cpu_65c02_ea_abs_idx(struct cpu_65c02 *self, int rvalue) {
+ int addr = cpu_65c02_ea_abs(self);
+ self->cycles += ((addr & 0xff) + (rvalue & 0xff)) >> 8;
+ return addr + rvalue;
+}
+
+static ALWAYS_INLINE int cpu_65c02_ea_abs_idx_ind(struct cpu_65c02 *self, int rvalue) {
+ return cpu_65c02_rw(self, cpu_65c02_ea_abs_idx(self, rvalue));
+}
+
+static ALWAYS_INLINE int cpu_65c02_ea_abs_ind(struct cpu_65c02 *self) {
+ return cpu_65c02_rw(self, cpu_65c02_ea_abs(self));
+}
+
+static ALWAYS_INLINE int cpu_65c02_ea_rel(struct cpu_65c02 *self) {
+ int offset = cpu_65c02_fb(self);
+ return offset - ((offset << 1) & 0x100);
+}
+
+static ALWAYS_INLINE int cpu_65c02_ea_zpg(struct cpu_65c02 *self) {
+ return cpu_65c02_fb(self);
+}
+
+static ALWAYS_INLINE int cpu_65c02_ea_zpg_idx(struct cpu_65c02 *self, int rvalue) {
+ return (cpu_65c02_ea_zpg(self) + rvalue) & 0xff;
+}
+
+static ALWAYS_INLINE int cpu_65c02_ea_zpg_idx_ind(struct cpu_65c02 *self, int rvalue) {
+ return cpu_65c02_rw_zpg(self, cpu_65c02_ea_zpg_idx(self, rvalue));
+}
+
+static ALWAYS_INLINE int cpu_65c02_ea_zpg_ind(struct cpu_65c02 *self) {
+ return cpu_65c02_rw_zpg(self, cpu_65c02_ea_zpg(self));
+}
+
+static ALWAYS_INLINE int cpu_65c02_ea_zpg_ind_idx(struct cpu_65c02 *self, int rvalue) {
+ int addr = cpu_65c02_rw_zpg(self, cpu_65c02_ea_zpg(self));
+ self->cycles += ((addr & 0xff) + (rvalue & 0xff)) >> 8;
+ return addr + rvalue;
+}
+
+// instruction execute
+static ALWAYS_INLINE void cpu_65c02_adc(struct cpu_65c02 *self, int rvalue) {
+ int result0, result1, result2;
+ if (self->regs.bit.df) {
+ result0 = (self->regs.byte.a & 0xf) + (rvalue & 0xf) + self->regs.bit.cf;
+ if (result0 >= 0xa)
+ result0 = (result0 + 6) & 0x1f;
+ result0 += (self->regs.byte.a & 0x70) + (rvalue & 0x70);
+ result1 = (self->regs.byte.a & 0x80) + (rvalue & 0x80) + result0;
+ result2 = result1;
+ if (result2 >= 0xa0)
+ result2 = (result2 + 0x60) & 0x1ff;
+ }
+ else {
+ result0 = (self->regs.byte.a & 0x7f) + (rvalue & 0x7f) + self->regs.bit.cf;
+ result1 = (self->regs.byte.a & 0x80) + (rvalue & 0x80) + result0;
+ result2 = result1;
+ }
+ self->regs.byte.a = result2 & 0xff;
+ self->regs.bit.cf = result2 >> 8;
+ self->regs.bit.zf = (result2 & 0xff) == 0;
+ self->regs.bit.vf = ((result0 >> 7) ^ (result1 >> 8)) & 1;
+ self->regs.bit.nf = (result2 >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_and(struct cpu_65c02 *self, int rvalue) {
+ int result = self->regs.byte.a & rvalue;
+ self->regs.byte.a = result;
+ self->regs.bit.zf = result == 0;
+ self->regs.bit.nf = (result >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_asl(struct cpu_65c02 *self, int lvalue) {
+ int result = cpu_65c02_rb(self, lvalue) << 1;
+ ++self->cycles;
+ cpu_65c02_wb(self, lvalue, result & 0xff);
+ self->regs.bit.cf = result >> 8;
+ self->regs.bit.zf = (result & 0xff) == 0;
+ self->regs.bit.nf = (result >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_bbr(struct cpu_65c02 *self, int rvalue, int lvalue) {
+ if (rvalue == 0) {
+ self->cycles += ((self->regs.word.pc & 0xff) + (lvalue & 0xff)) >> 8;
+ self->regs.word.pc = (self->regs.word.pc + lvalue) & 0xffff;
+ }
+}
+
+static ALWAYS_INLINE void cpu_65c02_bbs(struct cpu_65c02 *self, int rvalue, int lvalue) {
+ if (rvalue) {
+ self->cycles += ((self->regs.word.pc & 0xff) + (lvalue & 0xff)) >> 8;
+ self->regs.word.pc = (self->regs.word.pc + lvalue) & 0xffff;
+ }
+}
+
+static ALWAYS_INLINE void cpu_65c02_bc(struct cpu_65c02 *self, int flag, int lvalue) {
+ if ((self->regs.byte.p & flag) == 0) {
+ self->cycles += ((self->regs.word.pc & 0xff) + (lvalue & 0xff)) >> 8;
+ self->regs.word.pc = (self->regs.word.pc + lvalue) & 0xffff;
+ }
+}
+
+static ALWAYS_INLINE void cpu_65c02_bs(struct cpu_65c02 *self, int flag, int lvalue) {
+ if (self->regs.byte.p & flag) {
+ self->cycles += ((self->regs.word.pc & 0xff) + (lvalue & 0xff)) >> 8;
+ self->regs.word.pc = (self->regs.word.pc + lvalue) & 0xffff;
+ }
+}
+
+static ALWAYS_INLINE void cpu_65c02_bit(struct cpu_65c02 *self, int rvalue) {
+ int result = self->regs.byte.a & rvalue;
+ self->regs.bit.zf = result == 0;
+ self->regs.bit.vf = (rvalue >> 6) & 1;
+ self->regs.bit.nf = (rvalue >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_bit_imm(struct cpu_65c02 *self, int rvalue) {
+ int result = self->regs.byte.a & rvalue;
+ self->regs.bit.zf = result == 0;
+}
+
+static ALWAYS_INLINE void cpu_65c02_brk(struct cpu_65c02 *self) {
+ ++self->cycles;
+ cpu_65c02_phw(self, (self->regs.word.pc + 1) & 0xffff);
+ cpu_65c02_phb(self, self->regs.byte.p);
+ self->regs.word.pc = cpu_65c02_rw(self, CPU_65C02_IRQ_VECTOR);
+ self->regs.bit._if = true;
+ self->regs.bit.df = false;
+}
+
+static ALWAYS_INLINE void cpu_65c02_cl(struct cpu_65c02 *self, int flag) {
+ self->regs.byte.p &= ~flag;
+}
+
+static ALWAYS_INLINE void cpu_65c02_cmp(struct cpu_65c02 *self, int rvalue0, int rvalue1) {
+ rvalue1 ^= 0xff;
+ int result = rvalue0 + rvalue1 + 1;
+ self->regs.bit.cf = result >> 8;
+ self->regs.bit.zf = (result & 0xff) == 0;
+ self->regs.bit.nf = (result >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_dec(struct cpu_65c02 *self, int lvalue) {
+ int result = (cpu_65c02_rb(self, lvalue) - 1) & 0xff;
+ ++self->cycles;
+ cpu_65c02_wb(self, lvalue, result);
+ self->regs.bit.zf = result == 0;
+ self->regs.bit.nf = (result >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_eor(struct cpu_65c02 *self, int rvalue) {
+ int result = self->regs.byte.a ^ rvalue;
+ self->regs.byte.a = result;
+ self->regs.bit.zf = result == 0;
+ self->regs.bit.nf = (result >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_ill11(struct cpu_65c02 *self) {
+}
+
+static ALWAYS_INLINE void cpu_65c02_ill22(struct cpu_65c02 *self) {
+ ++self->cycles;
+ self->regs.word.pc = (self->regs.word.pc + 1) & 0xffff;
+}
+
+static ALWAYS_INLINE void cpu_65c02_ill23(struct cpu_65c02 *self) {
+ self->cycles += 2;
+ self->regs.word.pc = (self->regs.word.pc + 1) & 0xffff;
+}
+
+static ALWAYS_INLINE void cpu_65c02_ill24(struct cpu_65c02 *self) {
+ self->cycles += 3;
+ self->regs.word.pc = (self->regs.word.pc + 1) & 0xffff;
+}
+
+static ALWAYS_INLINE void cpu_65c02_ill34(struct cpu_65c02 *self) {
+ self->cycles += 3;
+ self->regs.word.pc = (self->regs.word.pc + 2) & 0xffff;
+}
+
+static ALWAYS_INLINE void cpu_65c02_ill38(struct cpu_65c02 *self) {
+ self->cycles += 7;
+ self->regs.word.pc = (self->regs.word.pc + 2) & 0xffff;
+}
+
+static ALWAYS_INLINE void cpu_65c02_inc(struct cpu_65c02 *self, int lvalue) {
+ int result = (cpu_65c02_rb(self, lvalue) + 1) & 0xff;
+ ++self->cycles;
+ cpu_65c02_wb(self, lvalue, result);
+ self->regs.bit.zf = result == 0;
+ self->regs.bit.nf = (result >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_jmp(struct cpu_65c02 *self, int lvalue) {
+ self->regs.word.pc = lvalue;
+}
+
+static ALWAYS_INLINE void cpu_65c02_jsr(struct cpu_65c02 *self, int lvalue) {
+ cpu_65c02_phw(self, (self->regs.word.pc - 1) & 0xffff);
+ self->regs.word.pc = lvalue;
+}
+
+static ALWAYS_INLINE void cpu_65c02_ld(struct cpu_65c02 *self, int lvalue, int rvalue) {
+ cpu_65c02_wb(self, lvalue, rvalue);
+ self->regs.bit.zf = rvalue == 0;
+ self->regs.bit.nf = (rvalue >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_lsr(struct cpu_65c02 *self, int lvalue) {
+ int result = cpu_65c02_rb(self, lvalue);
+ ++self->cycles;
+ cpu_65c02_wb(self, lvalue, result >> 1);
+ self->regs.bit.cf = result & 1;
+ self->regs.bit.zf = (result & 0xfe) == 0;
+ self->regs.bit.nf = false;
+}
+
+static ALWAYS_INLINE void cpu_65c02_nop(struct cpu_65c02 *self) {
+}
+
+static ALWAYS_INLINE void cpu_65c02_ora(struct cpu_65c02 *self, int rvalue) {
+ int result = self->regs.byte.a | rvalue;
+ self->regs.byte.a = result;
+ self->regs.bit.zf = result == 0;
+ self->regs.bit.nf = (result >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_ph(struct cpu_65c02 *self, int rvalue) {
+ cpu_65c02_phb(self, rvalue);
+}
+
+static ALWAYS_INLINE void cpu_65c02_pl(struct cpu_65c02 *self, int lvalue) {
+ int result = cpu_65c02_plb(self);
+ cpu_65c02_wb(self, lvalue, result);
+ self->regs.bit.zf = result == 0;
+ self->regs.bit.nf = (result >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_plp(struct cpu_65c02 *self) {
+ self->regs.byte.p = cpu_65c02_plb(self) | 0x30;
+}
+
+static ALWAYS_INLINE void cpu_65c02_rmb(struct cpu_65c02 *self, int rvalue, int lvalue) {
+ int result = cpu_65c02_rb(self, lvalue) & ~rvalue;
+ ++self->cycles;
+ cpu_65c02_wb(self, lvalue, result);
+}
+
+static ALWAYS_INLINE void cpu_65c02_rol(struct cpu_65c02 *self, int lvalue) {
+ int result = (cpu_65c02_rb(self, lvalue) << 1) | self->regs.bit.cf;
+ ++self->cycles;
+ cpu_65c02_wb(self, lvalue, result & 0xff);
+ self->regs.bit.cf = result >> 8;
+ self->regs.bit.zf = (result & 0xff) == 0;
+ self->regs.bit.nf = (result >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_ror(struct cpu_65c02 *self, int lvalue) {
+ int result = cpu_65c02_rb(self, lvalue) | (self->regs.bit.cf << 8);
+ ++self->cycles;
+ cpu_65c02_wb(self, lvalue, result >> 1);
+ self->regs.bit.cf = result & 1;
+ self->regs.bit.zf = (result & 0x1fe) == 0;
+ self->regs.bit.nf = (result >> 8) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_rti(struct cpu_65c02 *self) {
+ self->regs.byte.p = cpu_65c02_plb(self) | 0x30;
+ self->regs.word.pc = cpu_65c02_plw(self);
+}
+
+static ALWAYS_INLINE void cpu_65c02_rts(struct cpu_65c02 *self) {
+ self->regs.word.pc = (cpu_65c02_plw(self) + 1) & 0xffff;
+}
+
+static ALWAYS_INLINE void cpu_65c02_sbc(struct cpu_65c02 *self, int rvalue) {
+ rvalue ^= 0xff;
+ int result0, result1, result2;
+ if (self->regs.bit.df) {
+ result0 = (self->regs.byte.a & 0xf) + (rvalue & 0xf) + self->regs.bit.cf;
+ if (result0 < 0x10)
+ result0 = (result0 - 6) & 0x1f;
+ result0 += (self->regs.byte.a & 0x70) + (rvalue & 0x70);
+ result1 = (self->regs.byte.a & 0x80) + (rvalue & 0x80) + result0;
+ result2 = result1;
+ if (result2 < 0x100)
+ result2 = (result2 - 0x60) & 0x1ff;
+ }
+ else {
+ result0 = (self->regs.byte.a & 0x7f) + (rvalue & 0x7f) + self->regs.bit.cf;
+ result1 = (self->regs.byte.a & 0x80) + (rvalue & 0x80) + result0;
+ result2 = result1;
+ }
+ self->regs.byte.a = result2 & 0xff;
+ self->regs.bit.cf = result2 >> 8;
+ self->regs.bit.zf = (result2 & 0xff) == 0;
+ self->regs.bit.vf = ((result0 >> 7) ^ (result1 >> 8)) & 1;
+ self->regs.bit.nf = (result2 >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_se(struct cpu_65c02 *self, int flag) {
+ self->regs.byte.p |= flag;
+}
+
+static ALWAYS_INLINE void cpu_65c02_smb(struct cpu_65c02 *self, int rvalue, int lvalue) {
+ int result = cpu_65c02_rb(self, lvalue) | rvalue;
+ ++self->cycles;
+ cpu_65c02_wb(self, lvalue, result);
+}
+
+static ALWAYS_INLINE void cpu_65c02_st(struct cpu_65c02 *self, int rvalue, int lvalue) {
+ cpu_65c02_wb(self, lvalue, rvalue);
+}
+
+static ALWAYS_INLINE void cpu_65c02_stp(struct cpu_65c02 *self) {
+ abort();
+}
+
+static ALWAYS_INLINE void cpu_65c02_trb(struct cpu_65c02 *self, int lvalue) {
+ int result = cpu_65c02_rb(self, lvalue);
+ ++self->cycles;
+ cpu_65c02_wb(self, lvalue, result & ~self->regs.byte.a);
+ result &= self->regs.byte.a;
+ self->regs.bit.zf = result == 0;
+}
+
+static ALWAYS_INLINE void cpu_65c02_tsb(struct cpu_65c02 *self, int lvalue) {
+ int result = cpu_65c02_rb(self, lvalue);
+ ++self->cycles;
+ cpu_65c02_wb(self, lvalue, result | self->regs.byte.a);
+ result &= self->regs.byte.a;
+ self->regs.bit.zf = result == 0;
+}
+
+static ALWAYS_INLINE void cpu_65c02_wai(struct cpu_65c02 *self) {
+ abort();
+}
+
+// prototypes
void cpu_65c02_init(
struct cpu_65c02 *self,
int pc,