commit eec3b750bb4eb72915eb6b671425efbc4a36adad Author: s3lph Date: Sun Feb 6 06:52:57 2022 +0100 Memtest diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0696c40 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +emu6502 +*.o +*.bin \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..68a6297 --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ + +.PHONY: all memtest-f000.901465.bin memtest-f000.901465.o ascii memtest-f000.901465+ascii.o emu5602 + +all: memtest-f000.901465.bin ascii emu5602 + +clean: + rm -f memtest-f000.901465.bin memtest-f000.901465.o + rm -f memtest-f000.901465+ascii.bin memtest-f000.901465+ascii.o + rm -f emu6502 + +memtest-f000.901465.bin: memtest-f000.901465.o + dd if=memtest-f000.901465.o bs=1 skip=2 of=memtest-f000.901465.bin + +memtest-f000.901465.o: + xa -M -A F000 -O PETSCREEN -c -C -v -o memtest-f000.901465.o memtest-f000.901465.asm + +ascii: memtest-f000.901465+ascii.o + dd if=memtest-f000.901465+ascii.o bs=1 skip=2 of=memtest-f000.901465+ascii.bin + +memtest-f000.901465+ascii.o: + xa -M -A F000 -O ASCII -c -C -v -o memtest-f000.901465+ascii.o memtest-f000.901465.asm + + +emu5602: + gcc -o emu6502 emu6502.c fake6502.c diff --git a/emu6502.c b/emu6502.c new file mode 100644 index 0000000..c768993 --- /dev/null +++ b/emu6502.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +extern uint32_t instructions; +extern void reset6502(void); +extern void step6502(void); + +uint8_t *ram, *screen, *a000, *b000, *c000, *d000, *e000, *f000; + + +uint8_t read6502(uint16_t address) { + if (address == 0xf27e) { + printf("DONE\n"); + exit(0); + } + uint8_t value = 0x00; + if (address < 0x4000) { + // emulate memory error in uppermost page + if (address >= 0x3ff0) { + value = ram[address] & 0xf7; + } else { + value = ram[address]; + } + printf("%08x: READ %04x -> %02x\n", instructions, address, value); + } else if (address >= 0x8000 && address <= 0x8fff) { + // screen buffer is mirrored 4 times, discard upper 2 bytes + value = screen[address & 0x03ff]; + printf("%08x: READ %04x -> %02x\n", instructions, address, value); + } else if (address >= 0xa000 && address <= 0xafff) { + value = a000[address-0xa000]; + printf("%08x: READ %04x -> %02x\n", instructions, address, value); + } else if (address >= 0xb000 && address <= 0xbfff) { + value = b000[address-0xb000]; + printf("%08x: READ %04x -> %02x\n", instructions, address, value); + } else if (address >= 0xc000 && address <= 0xcfff) { + value = c000[address-0xc000]; + printf("%08x: READ %04x -> %02x\n", instructions, address, value); + } else if (address >= 0xd000 && address <= 0xdfff) { + value = d000[address-0xd000]; + printf("%08x: READ %04x -> %02x\n", instructions, address, value); + } else if (address >= 0xe000 && address <= 0xe7ff) { + value = e000[address-0xe000]; + printf("%08x: READ %04x -> %02x\n", instructions, address, value); + } else if (address >= 0xf000) { + value = f000[address-0xf000]; + printf("%08x: READ %04x -> %02x\n", instructions, address, value); + } else { + printf("%08x: READ %04x -> not implemented\n", instructions, address); + } + return value; +} + +void write6502(uint16_t address, uint8_t value) { + if (address < 0x4000) { + printf("%08x: WRITE %04x <- %02x\n", instructions, address, value); + ram[address] = value; + } else if (address >= 0x800 && address <= 0x8fff) { + printf("%08x: WRITE %04x <- %02x, SCREEN(%d,%d)\n", instructions, address, value, (address&0x03ff)%40, (address&0x03ff)/40); + screen[address & 0x03ff] = value; + } else { + printf("%08x: WRITE %04x <- %02x; READONLY\n", instructions, address, value); + } +} + +int main() { + ram = (uint8_t*) malloc(0x4000); + screen = (uint8_t*) malloc(0x400); + memset(screen, 'X', 0x400); + int afd = open("memtest-9000.901465.bin", O_RDONLY); + a000 = mmap(0, 0x1000, PROT_READ, MAP_PRIVATE, afd, 0); + int bfd = open("memtest-9000.901465.bin", O_RDONLY); + b000 = mmap(0, 0x1000, PROT_READ, MAP_PRIVATE, bfd, 0); + int cfd = open("basic-2-c000.901465-01.bin", O_RDONLY); + c000 = mmap(0, 0x1000, PROT_READ, MAP_PRIVATE, cfd, 0); + int dfd = open("basic-2-d000.901465-02.bin", O_RDONLY); + d000 = mmap(0, 0x1000, PROT_READ, MAP_PRIVATE, dfd, 0); + int efd = open("edit-2-n.901447-24.bin", O_RDONLY); + e000 = mmap(0, 0x0800, PROT_READ, MAP_PRIVATE, efd, 0); + //int ffd = open("kernal-2.901465-03.bin", O_RDONLY); + int ffd = open("memtest-f000.901465+ascii.bin", O_RDONLY); + f000 = mmap(0, 0x1000, PROT_READ, MAP_PRIVATE, ffd, 0); + + reset6502(); + while (1) { + usleep(1); + step6502(); + printf("pass: %02x, address: %02x%02x\n", ram[4], ram[1], ram[2]); + printf("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n"); + for (uint8_t row = 0; row < 25; ++row) { + printf("┃%.40s┃\n", screen + 40*row); + } + printf("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n"); + } + +} diff --git a/fake6502.c b/fake6502.c new file mode 100644 index 0000000..0f6bfaa --- /dev/null +++ b/fake6502.c @@ -0,0 +1,971 @@ +/* Fake6502 CPU emulator core v1.1 ******************* + * (c)2011 Mike Chambers (miker00lz@gmail.com) * + ***************************************************** + * v1.1 - Small bugfix in BIT opcode, but it was the * + * difference between a few games in my NES * + * emulator working and being broken! * + * I went through the rest carefully again * + * after fixing it just to make sure I didn't * + * have any other typos! (Dec. 17, 2011) * + * * + * v1.0 - First release (Nov. 24, 2011) * + ***************************************************** + * LICENSE: This source code is released into the * + * public domain, but if you use it please do give * + * credit. I put a lot of effort into writing this! * + * * + ***************************************************** + * Fake6502 is a MOS Technology 6502 CPU emulation * + * engine in C. It was written as part of a Nintendo * + * Entertainment System emulator I've been writing. * + * * + * A couple important things to know about are two * + * defines in the code. One is "UNDOCUMENTED" which, * + * when defined, allows Fake6502 to compile with * + * full support for the more predictable * + * undocumented instructions of the 6502. If it is * + * undefined, undocumented opcodes just act as NOPs. * + * * + * The other define is "NES_CPU", which causes the * + * code to compile without support for binary-coded * + * decimal (BCD) support for the ADC and SBC * + * opcodes. The Ricoh 2A03 CPU in the NES does not * + * support BCD, but is otherwise identical to the * + * standard MOS 6502. (Note that this define is * + * enabled in this file if you haven't changed it * + * yourself. If you're not emulating a NES, you * + * should comment it out.) * + * * + * If you do discover an error in timing accuracy, * + * or operation in general please e-mail me at the * + * address above so that I can fix it. Thank you! * + * * + ***************************************************** + * Usage: * + * * + * Fake6502 requires you to provide two external * + * functions: * + * * + * uint8_t read6502(uint16_t address) * + * void write6502(uint16_t address, uint8_t value) * + * * + * You may optionally pass Fake6502 the pointer to a * + * function which you want to be called after every * + * emulated instruction. This function should be a * + * void with no parameters expected to be passed to * + * it. * + * * + * This can be very useful. For example, in a NES * + * emulator, you check the number of clock ticks * + * that have passed so you can know when to handle * + * APU events. * + * * + * To pass Fake6502 this pointer, use the * + * hookexternal(void *funcptr) function provided. * + * * + * To disable the hook later, pass NULL to it. * + ***************************************************** + * Useful functions in this emulator: * + * * + * void reset6502() * + * - Call this once before you begin execution. * + * * + * void exec6502(uint32_t tickcount) * + * - Execute 6502 code up to the next specified * + * count of clock ticks. * + * * + * void step6502() * + * - Execute a single instrution. * + * * + * void irq6502() * + * - Trigger a hardware IRQ in the 6502 core. * + * * + * void nmi6502() * + * - Trigger an NMI in the 6502 core. * + * * + * void hookexternal(void *funcptr) * + * - Pass a pointer to a void function taking no * + * parameters. This will cause Fake6502 to call * + * that function once after each emulated * + * instruction. * + * * + ***************************************************** + * Useful variables in this emulator: * + * * + * uint32_t clockticks6502 * + * - A running total of the emulated cycle count. * + * * + * uint32_t instructions * + * - A running total of the total emulated * + * instruction count. This is not related to * + * clock cycle timing. * + * * + *****************************************************/ + +#include +#include + +//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; + + +//helper variables +uint32_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; + +//externally supplied functions +extern uint8_t read6502(uint16_t address); +extern void write6502(uint16_t address, uint8_t value); + +//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 uint16_t getvalue16() { + return((uint16_t)read6502(ea) | ((uint16_t)read6502(ea+1) << 8)); +} + +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++); + status |= FLAG_CONSTANT; + + penaltyop = 0; + penaltyaddr = 0; + + (*addrtable[opcode])(); + (*optable[opcode])(); + clockticks6502 += ticktable[opcode]; + if (penaltyop && penaltyaddr) clockticks6502++; + + instructions++; + + if (callexternal) (*loopexternal)(); + } + +} + +void step6502() { + opcode = read6502(pc++); + status |= FLAG_CONSTANT; + + 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; +} diff --git a/memtest-f000.901465.asm b/memtest-f000.901465.asm new file mode 100644 index 0000000..3174d00 --- /dev/null +++ b/memtest-f000.901465.asm @@ -0,0 +1,401 @@ + + .word $f000 + * = $f000 + + scrptr = $8000 + eoscr = $83e7 ; last screen address + lastline = $83c0 ; start of last line + + memstart = $0010 + memend = $4000 + + addr = $0100 + aoff = $02 + ipass = $03 + pbyte = $04 + scroff = $0605 + +main: + sei + ;; clear screen + ldx #scrptr + stx >addr + ldy #$00 + ;; initialize screen pointer + lda #scrptr + sta >scroff +clearloop: + lda #" " + sta (addr + cpx #$84 + bne clearloop + + ;; print infotext + ldy #$00 +textloop: + lda infotext,Y + sta lastline,Y + iny + cpy #$20 + bne textloop + + ;; set up first pass with $FF + lda #$00 + sta ipass + lda #"#" + ldy #$00 + sta (memstart + ldy #addr + sty aoff + ldy ipass + lda passbytes,Y + sta pbyte + lda passchars,Y + sta eoscr + +loop: + ldx >addr + ldy aoff + ;; break loop when end of DRAM is reached + cpx #>memend + bne noskip + jmp passend + +noskip: + stx eoscr-2 ; show current address as chars on screen + sty eoscr-1 + ;; store pass value at the current address + lda pbyte + sta (scroff + iny + cpy #$00 + bne nowrap1 + inx + cpx #$83 +nowrap1: + cpx #$83 + bne nowrap1a + cpy #$98 + bne nowrap1a + ldx #$80 + ldy #$00 +nowrap1a: + sty scroff + + ;; upper nibble of X + lda >addr + lsr + lsr + lsr + lsr + tay + lda hexchars,Y + ldy #$00 + sta (scroff + iny + cpy #$00 + bne nowrap2 + inx +nowrap2: + cpx #$83 + bne nowrap2a + cpy #$98 + bne nowrap2a + ldx #$80 + ldy #$00 +nowrap2a: + sty scroff + + ;; lower nibble of X + lda >addr + and #$0f + tay + lda hexchars,Y + ldy #$00 + sta (scroff + iny + cpy #$00 + bne nowrap3 + inx +nowrap3: + cpx #$83 + bne nowrap3a + cpy #$98 + bne nowrap3a + ldx #$80 + ldy #$00 +nowrap3a: + sty scroff + + ;; upper nibble of Y + lda aoff + lsr + lsr + lsr + lsr + tay + lda hexchars,Y + ldy #$00 + sta (scroff + iny + cpy #$00 + bne nowrap4 + inx +nowrap4: + cpx #$83 + bne nowrap4a + cpy #$98 + bne nowrap4a + ldx #$80 + ldy #$00 +nowrap4a: + sty scroff + + ;; lower nibble of Y + lda aoff + and #$0f + tay + lda hexchars,Y + ldy #$00 + sta (scroff + iny + cpy #$00 + bne nowrap5 + inx +nowrap5: + cpx #$83 + bne nowrap5a + cpy #$98 + bne nowrap5a + ldx #$80 + ldy #$00 +nowrap5a: + sty scroff + + ;; space + lda #">" + ldy #$00 + sta (scroff + iny + cpy #$00 + bne nowrap6 + inx +nowrap6: + cpx #$83 + bne nowrap6a + cpy #$98 + bne nowrap6a + ldx #$80 + ldy #$00 +nowrap6a: + sty scroff + + ;; upper nibble of pass char + lda ipass + asl + tay + lda passchars,Y + ldy #$00 + sta (scroff + iny + cpy #$00 + bne nowrap7 + inx +nowrap7: + cpx #$83 + bne nowrap7a + cpy #$98 + bne nowrap7a + ldx #$80 + ldy #$00 +nowrap7a: + sty scroff + + ;; lower nibble of pass char + lda ipass + asl + tay + iny + lda passchars,Y + ldy #$00 + sta (scroff + iny + cpy #$00 + bne nowrap8 + inx +nowrap8: + cpx #$83 + bne nowrap8a + cpy #$98 + bne nowrap8a + ldx #$80 + ldy #$00 +nowrap8a: + sty scroff + + ;; upper nibble of actual value + ldy aoff + lda (scroff + iny + cpy #$00 + bne nowrap9 + inx +nowrap9: + cpx #$83 + bne nowrap9a + cpy #$98 + bne nowrap9a + ldx #$80 + ldy #$00 +nowrap9a: + sty scroff + + ;; lower nibble of actual value + ldy aoff + lda (scroff + iny + cpy #$00 + bne nowrap10 + inx +nowrap10: + cpx #$83 + bne nowrap10a + cpy #$98 + bne nowrap10a + ldx #$80 + ldy #$00 +nowrap10a: + sty scroff + + ;; print cursor + lda #"#" + ldy #$00 + sta (addr + ldy aoff + ;; increment memory address + iny + cpy #$00 + bne loopend2 ; branch on overflow (iny sets Z, but not V) + inx ; increment y when x overflowed +loopend2: + stx >addr + sty aoff + jmp loop + +passend: + ldy ipass + lda passbytes,Y + cmp #$7F + beq eot + iny + sty ipass + jmp pass + + +hexchars: + .asc "0123456789ABCDEF" +passbytes: + .byt $FF, $00, $AA, $55, $01, $02, $04, $08, $10, $20, $40, $80, $FE, $FD, $FB, $F7, $EF, $DF, $BF, $7F +passchars: + .asc "FF00AA550102040810204080FEFDFBF7EFDFBF7F" +infotext: + .asc " = MOS6502 memtest 2022, s3lph =" + +eot: + ;; done, loop forever + jmp eot + + + ;; Fill with FF + * = $fffa + .dsb (*-eot-3), $ff + ;; 6502 vectors + * = $fffa + .byt main ; NMIV + .byt main ; RESV + .byt main ; IRQV