Memtest
This commit is contained in:
commit
eec3b750bb
5 changed files with 1501 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
emu6502
|
||||||
|
*.o
|
||||||
|
*.bin
|
25
Makefile
Normal file
25
Makefile
Normal file
|
@ -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
|
101
emu6502.c
Normal file
101
emu6502.c
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
971
fake6502.c
Normal file
971
fake6502.c
Normal file
|
@ -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 <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
//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;
|
||||||
|
}
|
401
memtest-f000.901465.asm
Normal file
401
memtest-f000.901465.asm
Normal file
|
@ -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
|
||||||
|
ldx #>scrptr
|
||||||
|
stx >addr
|
||||||
|
ldy #$00
|
||||||
|
;; initialize screen pointer
|
||||||
|
lda #<scrptr
|
||||||
|
sta <scroff
|
||||||
|
lda #>scrptr
|
||||||
|
sta >scroff
|
||||||
|
clearloop:
|
||||||
|
lda #" "
|
||||||
|
sta (<addr),Y
|
||||||
|
iny
|
||||||
|
cpy #$00
|
||||||
|
bne clearloop
|
||||||
|
inx
|
||||||
|
stx >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 (<scroff),Y
|
||||||
|
|
||||||
|
pass:
|
||||||
|
;; load start address
|
||||||
|
ldx #>memstart
|
||||||
|
ldy #<memstart
|
||||||
|
;; set up screen ptr
|
||||||
|
lda #$00
|
||||||
|
sta <addr
|
||||||
|
stx >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 (<addr),Y
|
||||||
|
;; compare current address to $FF, "continue" if equal
|
||||||
|
cmp (<addr),Y
|
||||||
|
bne print
|
||||||
|
jmp loopend
|
||||||
|
|
||||||
|
print:
|
||||||
|
;; memory content was not equal, print
|
||||||
|
|
||||||
|
;; space
|
||||||
|
lda #" "
|
||||||
|
ldy #$00
|
||||||
|
sta (<scroff),Y
|
||||||
|
;; increment
|
||||||
|
ldy <scroff
|
||||||
|
ldx >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
|
||||||
|
stx >scroff
|
||||||
|
|
||||||
|
;; upper nibble of X
|
||||||
|
lda >addr
|
||||||
|
lsr
|
||||||
|
lsr
|
||||||
|
lsr
|
||||||
|
lsr
|
||||||
|
tay
|
||||||
|
lda hexchars,Y
|
||||||
|
ldy #$00
|
||||||
|
sta (<scroff),Y
|
||||||
|
;; increment
|
||||||
|
ldy <scroff
|
||||||
|
ldx >scroff
|
||||||
|
iny
|
||||||
|
cpy #$00
|
||||||
|
bne nowrap2
|
||||||
|
inx
|
||||||
|
nowrap2:
|
||||||
|
cpx #$83
|
||||||
|
bne nowrap2a
|
||||||
|
cpy #$98
|
||||||
|
bne nowrap2a
|
||||||
|
ldx #$80
|
||||||
|
ldy #$00
|
||||||
|
nowrap2a:
|
||||||
|
sty <scroff
|
||||||
|
stx >scroff
|
||||||
|
|
||||||
|
;; lower nibble of X
|
||||||
|
lda >addr
|
||||||
|
and #$0f
|
||||||
|
tay
|
||||||
|
lda hexchars,Y
|
||||||
|
ldy #$00
|
||||||
|
sta (<scroff),Y
|
||||||
|
;; increment
|
||||||
|
ldy <scroff
|
||||||
|
ldx >scroff
|
||||||
|
iny
|
||||||
|
cpy #$00
|
||||||
|
bne nowrap3
|
||||||
|
inx
|
||||||
|
nowrap3:
|
||||||
|
cpx #$83
|
||||||
|
bne nowrap3a
|
||||||
|
cpy #$98
|
||||||
|
bne nowrap3a
|
||||||
|
ldx #$80
|
||||||
|
ldy #$00
|
||||||
|
nowrap3a:
|
||||||
|
sty <scroff
|
||||||
|
stx >scroff
|
||||||
|
|
||||||
|
;; upper nibble of Y
|
||||||
|
lda aoff
|
||||||
|
lsr
|
||||||
|
lsr
|
||||||
|
lsr
|
||||||
|
lsr
|
||||||
|
tay
|
||||||
|
lda hexchars,Y
|
||||||
|
ldy #$00
|
||||||
|
sta (<scroff),Y
|
||||||
|
;; increment
|
||||||
|
ldy <scroff
|
||||||
|
ldx >scroff
|
||||||
|
iny
|
||||||
|
cpy #$00
|
||||||
|
bne nowrap4
|
||||||
|
inx
|
||||||
|
nowrap4:
|
||||||
|
cpx #$83
|
||||||
|
bne nowrap4a
|
||||||
|
cpy #$98
|
||||||
|
bne nowrap4a
|
||||||
|
ldx #$80
|
||||||
|
ldy #$00
|
||||||
|
nowrap4a:
|
||||||
|
sty <scroff
|
||||||
|
stx >scroff
|
||||||
|
|
||||||
|
;; lower nibble of Y
|
||||||
|
lda aoff
|
||||||
|
and #$0f
|
||||||
|
tay
|
||||||
|
lda hexchars,Y
|
||||||
|
ldy #$00
|
||||||
|
sta (<scroff),Y
|
||||||
|
;; increment
|
||||||
|
ldy <scroff
|
||||||
|
ldx >scroff
|
||||||
|
iny
|
||||||
|
cpy #$00
|
||||||
|
bne nowrap5
|
||||||
|
inx
|
||||||
|
nowrap5:
|
||||||
|
cpx #$83
|
||||||
|
bne nowrap5a
|
||||||
|
cpy #$98
|
||||||
|
bne nowrap5a
|
||||||
|
ldx #$80
|
||||||
|
ldy #$00
|
||||||
|
nowrap5a:
|
||||||
|
sty <scroff
|
||||||
|
stx >scroff
|
||||||
|
|
||||||
|
;; space
|
||||||
|
lda #">"
|
||||||
|
ldy #$00
|
||||||
|
sta (<scroff),Y
|
||||||
|
;; increment
|
||||||
|
ldy <scroff
|
||||||
|
ldx >scroff
|
||||||
|
iny
|
||||||
|
cpy #$00
|
||||||
|
bne nowrap6
|
||||||
|
inx
|
||||||
|
nowrap6:
|
||||||
|
cpx #$83
|
||||||
|
bne nowrap6a
|
||||||
|
cpy #$98
|
||||||
|
bne nowrap6a
|
||||||
|
ldx #$80
|
||||||
|
ldy #$00
|
||||||
|
nowrap6a:
|
||||||
|
sty <scroff
|
||||||
|
stx >scroff
|
||||||
|
|
||||||
|
;; upper nibble of pass char
|
||||||
|
lda ipass
|
||||||
|
asl
|
||||||
|
tay
|
||||||
|
lda passchars,Y
|
||||||
|
ldy #$00
|
||||||
|
sta (<scroff),Y
|
||||||
|
;; increment
|
||||||
|
ldy <scroff
|
||||||
|
ldx >scroff
|
||||||
|
iny
|
||||||
|
cpy #$00
|
||||||
|
bne nowrap7
|
||||||
|
inx
|
||||||
|
nowrap7:
|
||||||
|
cpx #$83
|
||||||
|
bne nowrap7a
|
||||||
|
cpy #$98
|
||||||
|
bne nowrap7a
|
||||||
|
ldx #$80
|
||||||
|
ldy #$00
|
||||||
|
nowrap7a:
|
||||||
|
sty <scroff
|
||||||
|
stx >scroff
|
||||||
|
|
||||||
|
;; lower nibble of pass char
|
||||||
|
lda ipass
|
||||||
|
asl
|
||||||
|
tay
|
||||||
|
iny
|
||||||
|
lda passchars,Y
|
||||||
|
ldy #$00
|
||||||
|
sta (<scroff),Y
|
||||||
|
;; increment
|
||||||
|
ldy <scroff
|
||||||
|
ldx >scroff
|
||||||
|
iny
|
||||||
|
cpy #$00
|
||||||
|
bne nowrap8
|
||||||
|
inx
|
||||||
|
nowrap8:
|
||||||
|
cpx #$83
|
||||||
|
bne nowrap8a
|
||||||
|
cpy #$98
|
||||||
|
bne nowrap8a
|
||||||
|
ldx #$80
|
||||||
|
ldy #$00
|
||||||
|
nowrap8a:
|
||||||
|
sty <scroff
|
||||||
|
stx >scroff
|
||||||
|
|
||||||
|
;; upper nibble of actual value
|
||||||
|
ldy aoff
|
||||||
|
lda (<addr),Y
|
||||||
|
lsr
|
||||||
|
lsr
|
||||||
|
lsr
|
||||||
|
lsr
|
||||||
|
tay
|
||||||
|
lda hexchars,Y
|
||||||
|
ldy #$00
|
||||||
|
sta (<scroff),Y
|
||||||
|
;; increment
|
||||||
|
ldy <scroff
|
||||||
|
ldx >scroff
|
||||||
|
iny
|
||||||
|
cpy #$00
|
||||||
|
bne nowrap9
|
||||||
|
inx
|
||||||
|
nowrap9:
|
||||||
|
cpx #$83
|
||||||
|
bne nowrap9a
|
||||||
|
cpy #$98
|
||||||
|
bne nowrap9a
|
||||||
|
ldx #$80
|
||||||
|
ldy #$00
|
||||||
|
nowrap9a:
|
||||||
|
sty <scroff
|
||||||
|
stx >scroff
|
||||||
|
|
||||||
|
;; lower nibble of actual value
|
||||||
|
ldy aoff
|
||||||
|
lda (<addr),Y
|
||||||
|
and #$0f
|
||||||
|
tay
|
||||||
|
lda hexchars,Y
|
||||||
|
ldy #$00
|
||||||
|
sta (<scroff),Y
|
||||||
|
;; increment
|
||||||
|
ldy <scroff
|
||||||
|
ldx >scroff
|
||||||
|
iny
|
||||||
|
cpy #$00
|
||||||
|
bne nowrap10
|
||||||
|
inx
|
||||||
|
nowrap10:
|
||||||
|
cpx #$83
|
||||||
|
bne nowrap10a
|
||||||
|
cpy #$98
|
||||||
|
bne nowrap10a
|
||||||
|
ldx #$80
|
||||||
|
ldy #$00
|
||||||
|
nowrap10a:
|
||||||
|
sty <scroff
|
||||||
|
stx >scroff
|
||||||
|
|
||||||
|
;; print cursor
|
||||||
|
lda #"#"
|
||||||
|
ldy #$00
|
||||||
|
sta (<scroff),Y
|
||||||
|
|
||||||
|
loopend:
|
||||||
|
ldx >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, >main ; NMIV
|
||||||
|
.byt <main, >main ; RESV
|
||||||
|
.byt <main, >main ; IRQV
|
Loading…
Reference in a new issue