#include <stdlib.h>
#include "cpu_65c02.h"
+#define NMI_VECTOR 0xfffa
+#define RESET_VECTOR 0xfffc
+#define IRQ_VECTOR 0xfffe
+
// gcc specific
#define INLINE __attribute__((always_inline))
#define INY() cpu_65c02_inc(self, EA_Y)
#define JMP(lvalue) cpu_65c02_jmp(self, (lvalue))
#define JSR(lvalue) cpu_65c02_jsr(self, (lvalue))
-#define LDA(rvalue) cpu_65c02_t(self, (rvalue), EA_A)
-#define LDX(rvalue) cpu_65c02_t(self, (rvalue), EA_X)
-#define LDY(rvalue) cpu_65c02_t(self, (rvalue), EA_Y)
+#define LDA(rvalue) cpu_65c02_ld(self, EA_A, (rvalue))
+#define LDX(rvalue) cpu_65c02_ld(self, EA_X, (rvalue))
+#define LDY(rvalue) cpu_65c02_ld(self, EA_Y, (rvalue))
#define LSR(lvalue) cpu_65c02_lsr(self, (lvalue))
#define NOP() cpu_65c02_nop(self)
#define ORA(rvalue) cpu_65c02_ora(self, (rvalue))
#define PHX() cpu_65c02_ph(self, X)
#define PHY() cpu_65c02_ph(self, Y)
#define PLA() cpu_65c02_pl(self, EA_A)
-#define PLP() cpu_65c02_pl(self, EA_P)
+// note: cpu_65c02_plp() is like cpu_65c02_pl(), but does not set Z/N flags,
+// and forces unimplemented flags to 1 (need to ensure they stay 1 for php)
+#define PLP() cpu_65c02_plp(self)
#define PLX() cpu_65c02_pl(self, EA_X)
#define PLY() cpu_65c02_pl(self, EA_Y)
#define RMB0(lvalue) cpu_65c02_rmb(self, 1, (lvalue))
#define ROR(lvalue) cpu_65c02_ror(self, (lvalue))
#define RTI() cpu_65c02_rti(self)
#define RTS() cpu_65c02_rts(self)
-#define SBC(rvalue) cpu_65c02_adc(self, (rvalue) ^ 0xff)
+#define SBC(rvalue) cpu_65c02_sbc(self, (rvalue))
#define SEC() cpu_65c02_se(self, FLAG_C)
#define SED() cpu_65c02_se(self, FLAG_D)
#define SEI() cpu_65c02_se(self, FLAG_I)
#define SMB5(lvalue) cpu_65c02_smb(self, 0x20, (lvalue))
#define SMB6(lvalue) cpu_65c02_smb(self, 0x40, (lvalue))
#define SMB7(lvalue) cpu_65c02_smb(self, 0x80, (lvalue))
-// note: cpu_65c02_st() is like cpu_65c02_t() but no flags
#define STA(lvalue) cpu_65c02_st(self, A, (lvalue))
#define STX(lvalue) cpu_65c02_st(self, X, (lvalue))
#define STY(lvalue) cpu_65c02_st(self, Y, (lvalue))
#define STZ(lvalue) cpu_65c02_st(self, 0, (lvalue))
-#define TAX() cpu_65c02_t(self, A, EA_X)
-#define TAY() cpu_65c02_t(self, A, EA_Y)
+#define TAX() cpu_65c02_ld(self, EA_X, A)
+#define TAY() cpu_65c02_ld(self, EA_Y, A)
#define TRB(lvalue) cpu_65c02_trb(self, (lvalue))
#define TSB(lvalue) cpu_65c02_tsb(self, (lvalue))
-#define TSX() cpu_65c02_t(self, S, EA_X)
-#define TXA() cpu_65c02_t(self, X, EA_A)
-#define TXS() cpu_65c02_t(self, X, EA_S)
-#define TYA() cpu_65c02_t(self, Y, EA_A)
+#define TSX() cpu_65c02_ld(self, EA_X, S)
+#define TXA() cpu_65c02_ld(self, EA_A, X)
+// TXS is the only transfer instruction that does not set flags
+#define TXS() cpu_65c02_st(self, X, EA_S)
+#define TYA() cpu_65c02_ld(self, EA_A, Y)
#define WAI() cpu_65c02_wai(self)
// memory (or internal register memory) access
}
INLINE int cpu_65c02_ea_rel(struct cpu_65c02 *self) {
+ int offset = FB();
+ offset -= (offset << 1) & 0x100; // sign extend
int addr = PC;
- int rvalue = FB();
- rvalue -= (rvalue << 1) & 0x100; // sign extend
- self->cycles += ((addr & 0xff) + (rvalue & 0xff)) >> 8;
- return addr + rvalue;
+ self->cycles += ((addr & 0xff) + (offset & 0xff)) >> 8;
+ return addr + offset;
}
INLINE int cpu_65c02_ea_zpg(struct cpu_65c02 *self) {
// instruction execute
INLINE void cpu_65c02_adc(struct cpu_65c02 *self, int rvalue) {
- if (P & FLAG_D)
- abort();
- int partial = (A & 0x7f) + (rvalue & 0x7f) + (P & FLAG_C);
- int result = A + rvalue + (P & FLAG_C);
- A = result & 0xff;
+ int result0, result1, result2;
+ if (P & FLAG_D) {
+ result0 = (A & 0xf) + (rvalue & 0xf) + (P & FLAG_C);
+ if (result0 >= 0xa)
+ result0 += 6;
+ result0 += (A & 0x70) + (rvalue & 0x70);
+ result1 = (A & 0x80) + (rvalue & 0x80) + result0;
+ result2 = result1;
+ if (result2 >= 0xa0)
+ result2 += 0x60;
+ }
+ else {
+ result0 = (A & 0x7f) + (rvalue & 0x7f) + (P & FLAG_C);
+ result1 = (A & 0x80) + (rvalue & 0x80) + result0;
+ result2 = result1;
+ }
+ A = result2 & 0xff;
P =
(P & ~(FLAG_C | FLAG_Z | FLAG_V | FLAG_N)) |
- (result >> 8) | // C
- (((result & 0xff) == 0) << 1) | // Z
- (((partial >> 1) ^ (result >> 2)) & FLAG_V) |
- (result & FLAG_N);
+ ((result2 >> 8) & FLAG_C) | // C
+ (((result2 & 0xff) == 0) << 1) | // Z
+ (((result0 >> 1) ^ (result1 >> 2)) & FLAG_V) |
+ (result2 & FLAG_N);
}
INLINE void cpu_65c02_and(struct cpu_65c02 *self, int rvalue) {
}
INLINE void cpu_65c02_brk(struct cpu_65c02 *self) {
- abort();
+ ++self->cycles; // operand is ignored
+ PHW((PC + 1) & 0xffff);
+ PHB(P); // already has break flag set
+ PC = RW(IRQ_VECTOR);
+ P = (P & ~FLAG_D) | FLAG_I;
}
INLINE void cpu_65c02_cl(struct cpu_65c02 *self, int flag) {
INLINE void cpu_65c02_cmp(struct cpu_65c02 *self, int rvalue0, int rvalue1) {
rvalue1 ^= 0xff;
- int partial = (rvalue0 & 0x7f) + (rvalue1 & 0x7f);
- int result = rvalue0 + rvalue1;
+ int result = rvalue0 + rvalue1 + 1;
P =
- (P & ~(FLAG_C | FLAG_Z | FLAG_V | FLAG_N)) |
+ (P & ~(FLAG_C | FLAG_Z | FLAG_N)) |
(result >> 8) | // C
(((result & 0xff) == 0) << 1) | // Z
- (((partial >> 1) ^ (result >> 2)) & FLAG_V) |
(result & FLAG_N);
}
PC = lvalue;
}
+INLINE void cpu_65c02_ld(struct cpu_65c02 *self, int lvalue, int rvalue) {
+ WB(lvalue, rvalue);
+ P =
+ (P & ~(FLAG_Z | FLAG_N)) |
+ ((rvalue == 0) << 1) | // Z
+ (rvalue & FLAG_N);
+}
+
INLINE void cpu_65c02_lsr(struct cpu_65c02 *self, int lvalue) {
int result = RB(lvalue);
++self->cycles;
}
INLINE void cpu_65c02_pl(struct cpu_65c02 *self, int lvalue) {
- WB(lvalue, PLB());
+ int result = PLB();
+ WB(lvalue, result);
+ P =
+ (P & ~(FLAG_Z | FLAG_N)) |
+ ((result == 0) << 1) | // Z
+ (result & FLAG_N);
+}
+
+INLINE void cpu_65c02_plp(struct cpu_65c02 *self) {
+ P = PLB() | 0x30;
}
INLINE void cpu_65c02_rmb(struct cpu_65c02 *self, int n, int lvalue) {
}
INLINE void cpu_65c02_rti(struct cpu_65c02 *self) {
- abort();
+ P = PLB() | 0x30;
+ PC = PLW();
}
INLINE void cpu_65c02_rts(struct cpu_65c02 *self) {
PC = (PLW() + 1) & 0xffff;
}
+INLINE void cpu_65c02_sbc(struct cpu_65c02 *self, int rvalue) {
+ rvalue ^= 0xff;
+ int result0, result1, result2;
+ if (P & FLAG_D) {
+ result0 = (A & 0xf) + (rvalue & 0xf) + (P & FLAG_C);
+ if (result0 < 0x10)
+ result0 -= 6;
+ result0 += (A & 0x70) + (rvalue & 0x70);
+ result1 = (A & 0x80) + (rvalue & 0x80) + result0;
+ result2 = result1;
+ if (result2 < 0x100)
+ result2 -= 0x60;
+ }
+ else {
+ result0 = (A & 0x7f) + (rvalue & 0x7f) + (P & FLAG_C);
+ result1 = (A & 0x80) + (rvalue & 0x80) + result0;
+ result2 = result1;
+ }
+ A = result2 & 0xff;
+ P =
+ (P & ~(FLAG_C | FLAG_Z | FLAG_V | FLAG_N)) |
+ ((result2 >> 8) & FLAG_C) | // C
+ (((result2 & 0xff) == 0) << 1) | // Z
+ (((result0 >> 1) ^ (result1 >> 2)) & FLAG_V) |
+ (result2 & FLAG_N);
+}
+
INLINE void cpu_65c02_se(struct cpu_65c02 *self, int flag) {
P |= flag;
}
WB(lvalue, rvalue);
}
-INLINE void cpu_65c02_t(struct cpu_65c02 *self, int rvalue, int lvalue) {
- WB(lvalue, rvalue);
- P =
- (P & ~(FLAG_Z | FLAG_N)) |
- ((rvalue == 0) << 1) | // Z
- (rvalue & FLAG_N);
-}
-
INLINE void cpu_65c02_trb(struct cpu_65c02 *self, int lvalue) {
abort();
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#if VREMU6502
+#include "vrEmu6502/src/vrEmu6502.h"
+#else
#include "cpu_65c02.h"
+#endif
+
+#define REG_TRACE 1
+#define MEM_TRACE 1
#define MEM_SIZE 0x10000
uint8_t mem[MEM_SIZE];
}
int read_byte(int addr) {
+#if MEM_TRACE
+ int data = mem[addr];
+ printf("addr=%04x rd=%02x\n", addr, data);
+ return data;
+#else
return mem[addr];
+#endif
}
void write_byte(int addr, int data) {
+#if MEM_TRACE
+ printf("addr=%04x wr=%02x\n", addr, data);
+#endif
mem[addr] = data;
}
+#if VREMU6502
+uint8_t mem_read(uint16_t addr, bool isDbg) {
+ return read_byte(addr);
+}
+
+void mem_write(uint16_t addr, uint8_t val) {
+ write_byte(addr, val);
+}
+#endif
+
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "usage: %s image.ihx\n", argv[0]);
exit(EXIT_FAILURE);
}
- char *image_ihx = argv[1];
+ int entry_point = load_ihx(argv[1]);
+
+#if VREMU6502
+ mem[0xfffc] = (uint8_t)(entry_point & 0xff);
+ mem[0xfffd] = (uint8_t)(entry_point >> 8);
+
+ VrEmu6502 *cpu = vrEmu6502New(CPU_65C02, mem_read, mem_write);
+ if (cpu == NULL) {
+ perror("malloc()");
+ exit(EXIT_FAILURE);
+ }
+ while (true) {
+#if REG_TRACE
+ printf(
+ "pc=%04x a=%02x x=%02x y=%02x s=%02x p=%02x c=%d z=%d i=%d d=%d v=%d n=%d\n",
+ vrEmu6502GetPC(cpu),
+ vrEmu6502GetAcc(cpu),
+ vrEmu6502GetX(cpu),
+ vrEmu6502GetY(cpu),
+ vrEmu6502GetStackPointer(cpu),
+ vrEmu6502GetStatus(cpu) | 0x30,
+ vrEmu6502GetStatus(cpu) & 1,
+ (vrEmu6502GetStatus(cpu) >> 1) & 1,
+ (vrEmu6502GetStatus(cpu) >> 2) & 1,
+ (vrEmu6502GetStatus(cpu) >> 3) & 1,
+ (vrEmu6502GetStatus(cpu) >> 6) & 1,
+ (vrEmu6502GetStatus(cpu) >> 7) & 1
+ );
+#endif
+
+ int pc = vrEmu6502GetPC(cpu);
+ int i;
+ vrEmu6502RunInstrs(cpu, 1, &i);
+ if (pc == vrEmu6502GetPC(cpu)) {
+ printf("hung at %04x\n", pc);
+ break;
+ }
+ }
+#else
struct cpu_65c02 cpu;
memset(&cpu, 0, sizeof(struct cpu_65c02));
+ cpu.pc = entry_point;
cpu.regs[CPU_65C02_REG_S] = 0xff;
- cpu.pc = load_ihx(image_ihx);
+ cpu.regs[CPU_65C02_REG_P] = 0x30;
cpu.read_byte = read_byte;
cpu.write_byte = write_byte;
- while (true)
+ while (true) {
+#if REG_TRACE
+ printf(
+ "pc=%04x a=%02x x=%02x y=%02x s=%02x p=%02x c=%d z=%d i=%d d=%d v=%d n=%d\n",
+ cpu.pc,
+ cpu.regs[CPU_65C02_REG_A],
+ cpu.regs[CPU_65C02_REG_X],
+ cpu.regs[CPU_65C02_REG_Y],
+ cpu.regs[CPU_65C02_REG_S],
+ cpu.regs[CPU_65C02_REG_P],
+ (cpu.regs[CPU_65C02_REG_P] >> CPU_65C02_BIT_C) & 1,
+ (cpu.regs[CPU_65C02_REG_P] >> CPU_65C02_BIT_Z) & 1,
+ (cpu.regs[CPU_65C02_REG_P] >> CPU_65C02_BIT_I) & 1,
+ (cpu.regs[CPU_65C02_REG_P] >> CPU_65C02_BIT_D) & 1,
+ (cpu.regs[CPU_65C02_REG_P] >> CPU_65C02_BIT_V) & 1,
+ (cpu.regs[CPU_65C02_REG_P] >> CPU_65C02_BIT_N) & 1
+ );
+#endif
+
+ int pc = cpu.pc;
cpu_65c02_execute(&cpu);
+ if (pc == cpu.pc) {
+ printf("hung at %04x\n", pc);
+ break;
+ }
+ }
+#endif
+
+ return 0;
}