Thought I would go ahead and post my own implementation. It's COMPLETELY ungolfed, but it's a full implementation.
- 668 lines of C. (not counting blank lines or lines with only comments)
- Supports (I think) all undocumented instructions.
- Supports BCD.
- CPU clock cycle timing. (including adjustments on certain page-boundary wraps)
- Can execute instructions by either single-step or by specifying number of ticks.
- Supports hooking an external function to be called after every instruction is exectued. This was because it was originally for a NES emulator and I used this for audio timing.
/* Fake6502 CPU emulator core v1.1 *******************
* (c)2011-2013 Mike Chambers *
*****************************************************/
#include <stdio.h>
#include <stdint.h>
//externally supplied functions
extern uint8_t read6502(uint16_t address);
extern void write6502(uint16_t address, uint8_t value);
//6502 defines
#define UNDOCUMENTED //when this is defined, undocumented opcodes are handled.
//otherwise, they're simply treated as NOPs.
//#define NES_CPU //when this is defined, the binary-coded decimal (BCD)
//status flag is not honored by ADC and SBC. the 2A03
//CPU in the Nintendo Entertainment System does not
//support BCD operation.
#define FLAG_CARRY 0x01
#define FLAG_ZERO 0x02
#define FLAG_INTERRUPT 0x04
#define FLAG_DECIMAL 0x08
#define FLAG_BREAK 0x10
#define FLAG_CONSTANT 0x20
#define FLAG_OVERFLOW 0x40
#define FLAG_SIGN 0x80
#define BASE_STACK 0x100
#define saveaccum(n) a = (uint8_t)((n) & 0x00FF)
//flag modifier macros
#define setcarry() status |= FLAG_CARRY
#define clearcarry() status &= (~FLAG_CARRY)
#define setzero() status |= FLAG_ZERO
#define clearzero() status &= (~FLAG_ZERO)
#define setinterrupt() status |= FLAG_INTERRUPT
#define clearinterrupt() status &= (~FLAG_INTERRUPT)
#define setdecimal() status |= FLAG_DECIMAL
#define cleardecimal() status &= (~FLAG_DECIMAL)
#define setoverflow() status |= FLAG_OVERFLOW
#define clearoverflow() status &= (~FLAG_OVERFLOW)
#define setsign() status |= FLAG_SIGN
#define clearsign() status &= (~FLAG_SIGN)
//flag calculation macros
#define zerocalc(n) {\
if ((n) & 0x00FF) clearzero();\
else setzero();\
}
#define signcalc(n) {\
if ((n) & 0x0080) setsign();\
else clearsign();\
}
#define carrycalc(n) {\
if ((n) & 0xFF00) setcarry();\
else clearcarry();\
}
#define overflowcalc(n, m, o) { /* n = result, m = accumulator, o = memory */ \
if (((n) ^ (uint16_t)(m)) & ((n) ^ (o)) & 0x0080) setoverflow();\
else clearoverflow();\
}
//6502 CPU registers
uint16_t pc;
uint8_t sp, a, x, y, status = FLAG_CONSTANT;
//helper variables
uint64_t instructions = 0; //keep track of total instructions executed
uint32_t clockticks6502 = 0, clockgoal6502 = 0;
uint16_t oldpc, ea, reladdr, value, result;
uint8_t opcode, oldstatus;
//a few general functions used by various other functions
void push16(uint16_t pushval) {
write6502(BASE_STACK + sp, (pushval >> 8) & 0xFF);
write6502(BASE_STACK + ((sp - 1) & 0xFF), pushval & 0xFF);
sp -= 2;
}
void push8(uint8_t pushval) {
write6502(BASE_STACK + sp--, pushval);
}
uint16_t pull16() {
uint16_t temp16;
temp16 = read6502(BASE_STACK + ((sp + 1) & 0xFF)) | ((uint16_t)read6502(BASE_STACK + ((sp + 2) & 0xFF)) << 8);
sp += 2;
return(temp16);
}
uint8_t pull8() {
return (read6502(BASE_STACK + ++sp));
}
void reset6502() {
pc = (uint16_t)read6502(0xFFFC) | ((uint16_t)read6502(0xFFFD) << 8);
a = 0;
x = 0;
y = 0;
sp = 0xFD;
status |= FLAG_CONSTANT;
}
static void (*addrtable[256])();
static void (*optable[256])();
uint8_t penaltyop, penaltyaddr;
//addressing mode functions, calculates effective addresses
static void imp() { //implied
}
static void acc() { //accumulator
}
static void imm() { //immediate
ea = pc++;
}
static void zp() { //zero-page
ea = (uint16_t)read6502((uint16_t)pc++);
}
static void zpx() { //zero-page,X
ea = ((uint16_t)read6502((uint16_t)pc++) + (uint16_t)x) & 0xFF; //zero-page wraparound
}
static void zpy() { //zero-page,Y
ea = ((uint16_t)read6502((uint16_t)pc++) + (uint16_t)y) & 0xFF; //zero-page wraparound
}
static void rel() { //relative for branch ops (8-bit immediate value, sign-extended)
reladdr = (uint16_t)read6502(pc++);
if (reladdr & 0x80) reladdr |= 0xFF00;
}
static void abso() { //absolute
ea = (uint16_t)read6502(pc) | ((uint16_t)read6502(pc+1) << 8);
pc += 2;
}
static void absx() { //absolute,X
uint16_t startpage;
ea = ((uint16_t)read6502(pc) | ((uint16_t)read6502(pc+1) << 8));
startpage = ea & 0xFF00;
ea += (uint16_t)x;
if (startpage != (ea & 0xFF00)) { //one cycle penlty for page-crossing on some opcodes
penaltyaddr = 1;
}
pc += 2;
}
static void absy() { //absolute,Y
uint16_t startpage;
ea = ((uint16_t)read6502(pc) | ((uint16_t)read6502(pc+1) << 8));
startpage = ea & 0xFF00;
ea += (uint16_t)y;
if (startpage != (ea & 0xFF00)) { //one cycle penlty for page-crossing on some opcodes
penaltyaddr = 1;
}
pc += 2;
}
static void ind() { //indirect
uint16_t eahelp, eahelp2;
eahelp = (uint16_t)read6502(pc) | (uint16_t)((uint16_t)read6502(pc+1) << 8);
eahelp2 = (eahelp & 0xFF00) | ((eahelp + 1) & 0x00FF); //replicate 6502 page-boundary wraparound bug
ea = (uint16_t)read6502(eahelp) | ((uint16_t)read6502(eahelp2) << 8);
pc += 2;
}
static void indx() { // (indirect,X)
uint16_t eahelp;
eahelp = (uint16_t)(((uint16_t)read6502(pc++) + (uint16_t)x) & 0xFF); //zero-page wraparound for table pointer
ea = (uint16_t)read6502(eahelp & 0x00FF) | ((uint16_t)read6502((eahelp+1) & 0x00FF) << 8);
}
static void indy() { // (indirect),Y
uint16_t eahelp, eahelp2, startpage;
eahelp = (uint16_t)read6502(pc++);
eahelp2 = (eahelp & 0xFF00) | ((eahelp + 1) & 0x00FF); //zero-page wraparound
ea = (uint16_t)read6502(eahelp) | ((uint16_t)read6502(eahelp2) << 8);
startpage = ea & 0xFF00;
ea += (uint16_t)y;
if (startpage != (ea & 0xFF00)) { //one cycle penlty for page-crossing on some opcodes
penaltyaddr = 1;
}
}
static uint16_t getvalue() {
if (addrtable[opcode] == acc) return((uint16_t)a);
else return((uint16_t)read6502(ea));
}
static void putvalue(uint16_t saveval) {
if (addrtable[opcode] == acc) a = (uint8_t)(saveval & 0x00FF);
else write6502(ea, (saveval & 0x00FF));
}
//instruction handler functions
static void adc() {
penaltyop = 1;
value = getvalue();
result = (uint16_t)a + value + (uint16_t)(status & FLAG_CARRY);
carrycalc(result);
zerocalc(result);
overflowcalc(result, a, value);
signcalc(result);
#ifndef NES_CPU
if (status & FLAG_DECIMAL) {
clearcarry();
if ((a & 0x0F) > 0x09) {
a += 0x06;
}
if ((a & 0xF0) > 0x90) {
a += 0x60;
setcarry();
}
clockticks6502++;
}
#endif
saveaccum(result);
}
static void and() {
penaltyop = 1;
value = getvalue();
result = (uint16_t)a & value;
zerocalc(result);
signcalc(result);
saveaccum(result);
}
static void asl() {
value = getvalue();
result = value << 1;
carrycalc(result);
zerocalc(result);
signcalc(result);
putvalue(result);
}
static void bcc() {
if ((status & FLAG_CARRY) == 0) {
oldpc = pc;
pc += reladdr;
if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
else clockticks6502++;
}
}
static void bcs() {
if ((status & FLAG_CARRY) == FLAG_CARRY) {
oldpc = pc;
pc += reladdr;
if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
else clockticks6502++;
}
}
static void beq() {
if ((status & FLAG_ZERO) == FLAG_ZERO) {
oldpc = pc;
pc += reladdr;
if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
else clockticks6502++;
}
}
static void bit() {
value = getvalue();
result = (uint16_t)a & value;
zerocalc(result);
status = (status & 0x3F) | (uint8_t)(value & 0xC0);
}
static void bmi() {
if ((status & FLAG_SIGN) == FLAG_SIGN) {
oldpc = pc;
pc += reladdr;
if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
else clockticks6502++;
}
}
static void bne() {
if ((status & FLAG_ZERO) == 0) {
oldpc = pc;
pc += reladdr;
if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
else clockticks6502++;
}
}
static void bpl() {
if ((status & FLAG_SIGN) == 0) {
oldpc = pc;
pc += reladdr;
if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
else clockticks6502++;
}
}
static void brk() {
pc++;
push16(pc); //push next instruction address onto stack
push8(status | FLAG_BREAK); //push CPU status to stack
setinterrupt(); //set interrupt flag
pc = (uint16_t)read6502(0xFFFE) | ((uint16_t)read6502(0xFFFF) << 8);
}
static void bvc() {
if ((status & FLAG_OVERFLOW) == 0) {
oldpc = pc;
pc += reladdr;
if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
else clockticks6502++;
}
}
static void bvs() {
if ((status & FLAG_OVERFLOW) == FLAG_OVERFLOW) {
oldpc = pc;
pc += reladdr;
if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
else clockticks6502++;
}
}
static void clc() {
clearcarry();
}
static void cld() {
cleardecimal();
}
static void cli() {
clearinterrupt();
}
static void clv() {
clearoverflow();
}
static void cmp() {
penaltyop = 1;
value = getvalue();
result = (uint16_t)a - value;
if (a >= (uint8_t)(value & 0x00FF)) setcarry();
else clearcarry();
if (a == (uint8_t)(value & 0x00FF)) setzero();
else clearzero();
signcalc(result);
}
static void cpx() {
value = getvalue();
result = (uint16_t)x - value;
if (x >= (uint8_t)(value & 0x00FF)) setcarry();
else clearcarry();
if (x == (uint8_t)(value & 0x00FF)) setzero();
else clearzero();
signcalc(result);
}
static void cpy() {
value = getvalue();
result = (uint16_t)y - value;
if (y >= (uint8_t)(value & 0x00FF)) setcarry();
else clearcarry();
if (y == (uint8_t)(value & 0x00FF)) setzero();
else clearzero();
signcalc(result);
}
static void dec() {
value = getvalue();
result = value - 1;
zerocalc(result);
signcalc(result);
putvalue(result);
}
static void dex() {
x--;
zerocalc(x);
signcalc(x);
}
static void dey() {
y--;
zerocalc(y);
signcalc(y);
}
static void eor() {
penaltyop = 1;
value = getvalue();
result = (uint16_t)a ^ value;
zerocalc(result);
signcalc(result);
saveaccum(result);
}
static void inc() {
value = getvalue();
result = value + 1;
zerocalc(result);
signcalc(result);
putvalue(result);
}
static void inx() {
x++;
zerocalc(x);
signcalc(x);
}
static void iny() {
y++;
zerocalc(y);
signcalc(y);
}
static void jmp() {
pc = ea;
}
static void jsr() {
push16(pc - 1);
pc = ea;
}
static void lda() {
penaltyop = 1;
value = getvalue();
a = (uint8_t)(value & 0x00FF);
zerocalc(a);
signcalc(a);
}
static void ldx() {
penaltyop = 1;
value = getvalue();
x = (uint8_t)(value & 0x00FF);
zerocalc(x);
signcalc(x);
}
static void ldy() {
penaltyop = 1;
value = getvalue();
y = (uint8_t)(value & 0x00FF);
zerocalc(y);
signcalc(y);
}
static void lsr() {
value = getvalue();
result = value >> 1;
if (value & 1) setcarry();
else clearcarry();
zerocalc(result);
signcalc(result);
putvalue(result);
}
static void nop() {
switch (opcode) {
case 0x1C:
case 0x3C:
case 0x5C:
case 0x7C:
case 0xDC:
case 0xFC:
penaltyop = 1;
break;
}
}
static void ora() {
penaltyop = 1;
value = getvalue();
result = (uint16_t)a | value;
zerocalc(result);
signcalc(result);
saveaccum(result);
}
static void pha() {
push8(a);
}
static void php() {
push8(status | FLAG_BREAK);
}
static void pla() {
a = pull8();
zerocalc(a);
signcalc(a);
}
static void plp() {
status = pull8() | FLAG_CONSTANT;
}
static void rol() {
value = getvalue();
result = (value << 1) | (status & FLAG_CARRY);
carrycalc(result);
zerocalc(result);
signcalc(result);
putvalue(result);
}
static void ror() {
value = getvalue();
result = (value >> 1) | ((status & FLAG_CARRY) << 7);
if (value & 1) setcarry();
else clearcarry();
zerocalc(result);
signcalc(result);
putvalue(result);
}
static void rti() {
status = pull8();
value = pull16();
pc = value;
}
static void rts() {
value = pull16();
pc = value + 1;
}
static void sbc() {
penaltyop = 1;
value = getvalue() ^ 0x00FF;
result = (uint16_t)a + value + (uint16_t)(status & FLAG_CARRY);
carrycalc(result);
zerocalc(result);
overflowcalc(result, a, value);
signcalc(result);
#ifndef NES_CPU
if (status & FLAG_DECIMAL) {
clearcarry();
a -= 0x66;
if ((a & 0x0F) > 0x09) {
a += 0x06;
}
if ((a & 0xF0) > 0x90) {
a += 0x60;
setcarry();
}
clockticks6502++;
}
#endif
saveaccum(result);
}
static void sec() {
setcarry();
}
static void sed() {
setdecimal();
}
static void sei() {
setinterrupt();
}
static void sta() {
putvalue(a);
}
static void stx() {
putvalue(x);
}
static void sty() {
putvalue(y);
}
static void tax() {
x = a;
zerocalc(x);
signcalc(x);
}
static void tay() {
y = a;
zerocalc(y);
signcalc(y);
}
static void tsx() {
x = sp;
zerocalc(x);
signcalc(x);
}
static void txa() {
a = x;
zerocalc(a);
signcalc(a);
}
static void txs() {
sp = x;
}
static void tya() {
a = y;
zerocalc(a);
signcalc(a);
}
//undocumented instructions
#ifdef UNDOCUMENTED
static void lax() {
lda();
ldx();
}
static void sax() {
sta();
stx();
putvalue(a & x);
if (penaltyop && penaltyaddr) clockticks6502--;
}
static void dcp() {
dec();
cmp();
if (penaltyop && penaltyaddr) clockticks6502--;
}
static void isb() {
inc();
sbc();
if (penaltyop && penaltyaddr) clockticks6502--;
}
static void slo() {
asl();
ora();
if (penaltyop && penaltyaddr) clockticks6502--;
}
static void rla() {
rol();
and();
if (penaltyop && penaltyaddr) clockticks6502--;
}
static void sre() {
lsr();
eor();
if (penaltyop && penaltyaddr) clockticks6502--;
}
static void rra() {
ror();
adc();
if (penaltyop && penaltyaddr) clockticks6502--;
}
#else
#define lax nop
#define sax nop
#define dcp nop
#define isb nop
#define slo nop
#define rla nop
#define sre nop
#define rra nop
#endif
static void (*addrtable[256])() = {
/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */
/* 0 */ imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, abso, abso, abso, abso, /* 0 */
/* 1 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 1 */
/* 2 */ abso, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, abso, abso, abso, abso, /* 2 */
/* 3 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 3 */
/* 4 */ imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, abso, abso, abso, abso, /* 4 */
/* 5 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 5 */
/* 6 */ imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, ind, abso, abso, abso, /* 6 */
/* 7 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 7 */
/* 8 */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* 8 */
/* 9 */ rel, indy, imp, indy, zpx, zpx, zpy, zpy, imp, absy, imp, absy, absx, absx, absy, absy, /* 9 */
/* A */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* A */
/* B */ rel, indy, imp, indy, zpx, zpx, zpy, zpy, imp, absy, imp, absy, absx, absx, absy, absy, /* B */
/* C */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* C */
/* D */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* D */
/* E */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* E */
/* F */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx /* F */
};
static void (*optable[256])() = {
/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */
/* 0 */ brk, ora, nop, slo, nop, ora, asl, slo, php, ora, asl, nop, nop, ora, asl, slo, /* 0 */
/* 1 */ bpl, ora, nop, slo, nop, ora, asl, slo, clc, ora, nop, slo, nop, ora, asl, slo, /* 1 */
/* 2 */ jsr, and, nop, rla, bit, and, rol, rla, plp, and, rol, nop, bit, and, rol, rla, /* 2 */
/* 3 */ bmi, and, nop, rla, nop, and, rol, rla, sec, and, nop, rla, nop, and, rol, rla, /* 3 */
/* 4 */ rti, eor, nop, sre, nop, eor, lsr, sre, pha, eor, lsr, nop, jmp, eor, lsr, sre, /* 4 */
/* 5 */ bvc, eor, nop, sre, nop, eor, lsr, sre, cli, eor, nop, sre, nop, eor, lsr, sre, /* 5 */
/* 6 */ rts, adc, nop, rra, nop, adc, ror, rra, pla, adc, ror, nop, jmp, adc, ror, rra, /* 6 */
/* 7 */ bvs, adc, nop, rra, nop, adc, ror, rra, sei, adc, nop, rra, nop, adc, ror, rra, /* 7 */
/* 8 */ nop, sta, nop, sax, sty, sta, stx, sax, dey, nop, txa, nop, sty, sta, stx, sax, /* 8 */
/* 9 */ bcc, sta, nop, nop, sty, sta, stx, sax, tya, sta, txs, nop, nop, sta, nop, nop, /* 9 */
/* A */ ldy, lda, ldx, lax, ldy, lda, ldx, lax, tay, lda, tax, nop, ldy, lda, ldx, lax, /* A */
/* B */ bcs, lda, nop, lax, ldy, lda, ldx, lax, clv, lda, tsx, lax, ldy, lda, ldx, lax, /* B */
/* C */ cpy, cmp, nop, dcp, cpy, cmp, dec, dcp, iny, cmp, dex, nop, cpy, cmp, dec, dcp, /* C */
/* D */ bne, cmp, nop, dcp, nop, cmp, dec, dcp, cld, cmp, nop, dcp, nop, cmp, dec, dcp, /* D */
/* E */ cpx, sbc, nop, isb, cpx, sbc, inc, isb, inx, sbc, nop, sbc, cpx, sbc, inc, isb, /* E */
/* F */ beq, sbc, nop, isb, nop, sbc, inc, isb, sed, sbc, nop, isb, nop, sbc, inc, isb /* F */
};
static const uint32_t ticktable[256] = {
/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */
/* 0 */ 7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, /* 0 */
/* 1 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 1 */
/* 2 */ 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6, /* 2 */
/* 3 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 3 */
/* 4 */ 6, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6, /* 4 */
/* 5 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 5 */
/* 6 */ 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6, /* 6 */
/* 7 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 7 */
/* 8 */ 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, /* 8 */
/* 9 */ 2, 6, 2, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5, /* 9 */
/* A */ 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, /* A */
/* B */ 2, 5, 2, 5, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4, /* B */
/* C */ 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, /* C */
/* D */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* D */
/* E */ 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, /* E */
/* F */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7 /* F */
};
void nmi6502() {
push16(pc);
push8(status);
status |= FLAG_INTERRUPT;
pc = (uint16_t)read6502(0xFFFA) | ((uint16_t)read6502(0xFFFB) << 8);
}
void irq6502() {
push16(pc);
push8(status);
status |= FLAG_INTERRUPT;
pc = (uint16_t)read6502(0xFFFE) | ((uint16_t)read6502(0xFFFF) << 8);
}
uint8_t callexternal = 0;
void (*loopexternal)();
void exec6502(uint32_t tickcount) {
clockgoal6502 += tickcount;
while (clockticks6502 < clockgoal6502) {
opcode = read6502(pc++);
penaltyop = 0;
penaltyaddr = 0;
(*addrtable[opcode])();
(*optable[opcode])();
clockticks6502 += ticktable[opcode];
if (penaltyop && penaltyaddr) clockticks6502++;
instructions++;
if (callexternal) (*loopexternal)();
}
}
void step6502() {
opcode = read6502(pc++);
penaltyop = 0;
penaltyaddr = 0;
(*addrtable[opcode])();
(*optable[opcode])();
clockticks6502 += ticktable[opcode];
if (penaltyop && penaltyaddr) clockticks6502++;
clockgoal6502 = clockticks6502;
instructions++;
if (callexternal) (*loopexternal)();
}
void hookexternal(void *funcptr) {
if (funcptr != (void *)NULL) {
loopexternal = funcptr;
callexternal = 1;
} else callexternal = 0;
}
@professorfish It's tagged popularity contest; the most popular answer wins. – FUZxxl – 2015-07-11T21:11:14.753
@FUZxxl Look at the date on the comment before replying to it. That was nearly a year ago ;). I've given it a reopen vote now that it's been edited – None – 2015-07-12T22:16:02.003
@professorfish I assumed you didn't know about the edit, so I gave an answer to you. – FUZxxl – 2015-07-12T23:16:38.447
Hey, quick question: is the $F001/$F004 thing still the I/O mechanism used? I tried the ehbasic binary linked on the question with my 6507 emulator (https://github.com/chesterbr/ruby2600/blob/master/lib/ruby2600/cpu.rb) and it seems to be calling I/O vectors on $0205 and $0207 (which seem to be these: https://github.com/jefftranter/6502/blob/master/asm/ehbasic/basic.asm#L7715-L7718 - they are not set by the initial page 2 initialization - the binary only copies the vectors before those, matching the source). Did something change on that URL since the initial post?
– chesterbr – 2016-09-12T03:57:30.617Seems to be a disparity in this sentence: "If there is no more input to read, it should return a value of zero. This causes EhBASIC to keep polling until it's non-zero." – Igby Largeman – 2013-10-16T03:30:17.570
Er, my mistake. I didn't explain it well. I meant to explain that EhBASIC polls the value at that location until it's non-zero, which lets it know the byte is valid keyboard input. That's why if there's no more input to read, the emulator should return zero there. I'll edit that. – Mike C – 2013-10-16T04:56:49.333
I might post my own 6502 core eventually, but I'm going to wait for a few entries from others first. Hopefully somebody gives this challenge a shot. There were quite a few solutions to the 8086 challenge, so clearly there are smart enough people here to do this. 8086 is much more difficult! – Mike C – 2013-10-16T17:44:33.283
1I would love to try this, though not in any competitive sense. The problem for me is finding the time. I think it would be good if you could provide another test program which will exercise the emulator thoroughly and produce some easily verifiable output, similar to what was done for the 8086 challenge. – Igby Largeman – 2013-10-17T01:14:12.063
I'd love to see your results if you get a chance to try! I like your idea for another more thorough test. I found/compiled one, and edited the challenge. Also, this isn't necessarily a competitive thing. I thought it would just be really interesting to see what different people came up with. – Mike C – 2013-10-17T05:15:51.047
Okay… I'm workin' on it… How are you scoring entries? Size? Beauty? Cleverness? Or is it just no score, just for fun? – MtnViewMark – 2013-10-18T06:55:26.917
Glad to see somebody accept the challenge! :D No score from me, but whatever you think will get you the most upvotes. Just write it however you think best. FWIW, I was able to implement one in 674 lines of C. I was personally going for size and manageability, but it's not the fastest one ever written. – Mike C – 2013-10-18T18:43:44.803
This is interesting to me, as I've recently been thinking it would be cool to have a self bootstrapped Commodore emulator. Zero operating system support, just an emulator directly accessing PC hardware and/or using the BIOS (or other machine, but PC is the obvious choice for such a project). I may see what I can come up with. Depending on personal time constraints. – CasaDeRobison – 2013-10-20T23:20:09.927
CasaDeRobison: Cool idea. I'd play with that a bit too if you made it. – Mike C – 2013-10-21T03:40:11.000
To anybody working on this: If you have technical issues, you can ask here. I'll answer. It would be good in case others have the same questions. – Mike C – 2013-10-21T03:41:59.290
Status update: My emulator just passed the AllSuiteA test! Current line count: ~450 lines for the 6502, and another ~220 lines for ROM file loader & execution trace w/disassembly. — Two URLs that helped me: overflow computation and B flag behavior — Still need to do BCD mode, and the I/O
– MtnViewMark – 2013-10-21T07:33:58.090Wow, that was fast! Did you buy baggies of pure caffeine somewhere? I spent nearly two weeks on mine. Good job. Looking forward to seeing this, MtnViewMark. – Mike C – 2013-10-22T02:29:18.840
Status update 2: Runs ehBasic. Semi-golf'd version is under 350 lines total. Still have a few tricks up my sleeve - expect a submission in the next week. – MtnViewMark – 2013-10-26T06:18:51.493
it writes the byte to memory location $F001: "fooi", pronounced "phooey!", the sound of spitting out a byte. – Kaz – 2017-08-03T19:21:50.290
Could the challenge be extended to include the 6510 (the processor in the Commodore 64)? It's almost identical but also has six I/O bits that the 64 used for bank switching so it could address all 64k of RAM and still access the ROM. – Andrew Wyld – 2014-06-17T13:01:51.697
2How do you determine who wins? (there has to be a winner) – None – 2014-07-22T15:10:15.693