mirror of
https://github.com/mist64/perfect6502.git
synced 2024-12-27 20:29:43 +00:00
575 lines
14 KiB
C
575 lines
14 KiB
C
#include <stdio.h>
|
|
#include <strings.h>
|
|
|
|
#include "perfect6502.h"
|
|
|
|
typedef unsigned char uint8_t;
|
|
typedef unsigned short uint16_t;
|
|
typedef unsigned int BOOL;
|
|
|
|
extern uint8_t memory[65536];
|
|
|
|
#define YES 1
|
|
#define NO 0
|
|
|
|
#define BRK_LENGTH 2 /* BRK pushes PC + 2 onto the stack */
|
|
|
|
#define MAX_CYCLES 100
|
|
#define SETUP_ADDR 0xF400
|
|
#define INSTRUCTION_ADDR 0xF800
|
|
#define BRK_VECTOR 0xFC00
|
|
|
|
#define MAGIC_8 0xEA
|
|
#define MAGIC_16 0xAB1E
|
|
#define MAGIC_IZX 0x1328
|
|
#define MAGIC_IZY 0x1979
|
|
#define X_OFFSET 5
|
|
#define Y_OFFSET 10
|
|
|
|
#define IS_READ_CYCLE ((cycle & 1) && readRW())
|
|
#define IS_WRITE_CYCLE ((cycle & 1) && !readRW())
|
|
#define IS_READING(a) (IS_READ_CYCLE && readAddressBus() == (a))
|
|
|
|
struct {
|
|
BOOL crash;
|
|
int length;
|
|
int cycles;
|
|
int addmode;
|
|
BOOL zp;
|
|
BOOL abs;
|
|
BOOL zpx;
|
|
BOOL absx;
|
|
BOOL zpy;
|
|
BOOL absy;
|
|
BOOL izx;
|
|
BOOL izy;
|
|
BOOL reads;
|
|
BOOL writes;
|
|
BOOL inputa;
|
|
BOOL inputx;
|
|
BOOL inputy;
|
|
BOOL inputs;
|
|
BOOL inputp;
|
|
BOOL outputa;
|
|
BOOL outputx;
|
|
BOOL outputy;
|
|
BOOL outputs;
|
|
BOOL outputp;
|
|
} data[256];
|
|
|
|
enum {
|
|
ADDMODE_UNKNOWN,
|
|
ADDMODE_IZY,
|
|
ADDMODE_IZX,
|
|
ADDMODE_ZPY,
|
|
ADDMODE_ZPX,
|
|
ADDMODE_ZP,
|
|
ADDMODE_ABSY,
|
|
ADDMODE_ABSX,
|
|
ADDMODE_ABS,
|
|
};
|
|
|
|
uint16_t initial_s, initial_p, initial_a, initial_x, initial_y;
|
|
|
|
void
|
|
setup_memory(uint8_t opcode)
|
|
{
|
|
bzero(memory, 65536);
|
|
|
|
memory[0xFFFC] = SETUP_ADDR & 0xFF;
|
|
memory[0xFFFD] = SETUP_ADDR >> 8;
|
|
uint16_t addr = SETUP_ADDR;
|
|
memory[addr++] = 0xA2; /* LDA #S */
|
|
initial_s = addr;
|
|
memory[addr++] = 0x7F;
|
|
memory[addr++] = 0x9A; /* TXS */
|
|
memory[addr++] = 0xA9; /* LDA #P */
|
|
initial_p = addr;
|
|
memory[addr++] = 0;
|
|
memory[addr++] = 0x48; /* PHA */
|
|
memory[addr++] = 0xA9; /* LHA #A */
|
|
initial_a = addr;
|
|
memory[addr++] = 0;
|
|
memory[addr++] = 0xA2; /* LDX #X */
|
|
initial_x = addr;
|
|
memory[addr++] = 0;
|
|
memory[addr++] = 0xA0; /* LDY #Y */
|
|
initial_y = addr;
|
|
memory[addr++] = 0;
|
|
memory[addr++] = 0x28; /* PLP */
|
|
memory[addr++] = 0x4C; /* JMP */
|
|
memory[addr++] = INSTRUCTION_ADDR & 0xFF;
|
|
memory[addr++] = INSTRUCTION_ADDR >> 8;
|
|
|
|
memory[INSTRUCTION_ADDR + 0] = opcode;
|
|
memory[INSTRUCTION_ADDR + 1] = 0;
|
|
memory[INSTRUCTION_ADDR + 2] = 0;
|
|
memory[INSTRUCTION_ADDR + 3] = 0;
|
|
|
|
memory[0xFFFE] = BRK_VECTOR & 0xFF;
|
|
memory[0xFFFF] = BRK_VECTOR >> 8;
|
|
memory[BRK_VECTOR] = 0x00; /* loop there */
|
|
}
|
|
|
|
void
|
|
resetChip_test()
|
|
{
|
|
resetChip();
|
|
for (int i = 0; i < 62; i++)
|
|
step();
|
|
|
|
cycle = -1;
|
|
}
|
|
|
|
int
|
|
main()
|
|
{
|
|
initAndResetChip();
|
|
|
|
for (int opcode = 0x00; opcode <= 0xFF; opcode++) {
|
|
// for (int opcode = 0xA9; opcode <= 0xAA; opcode++) {
|
|
// for (int opcode = 0x15; opcode <= 0x15; opcode++) {
|
|
printf("$%02X: ", opcode);
|
|
|
|
/**************************************************
|
|
* find out length of instruction in bytes
|
|
**************************************************/
|
|
setup_memory(opcode);
|
|
resetChip_test();
|
|
int i;
|
|
for (i = 0; i < MAX_CYCLES; i++) {
|
|
step();
|
|
if (IS_READING(BRK_VECTOR))
|
|
break;
|
|
};
|
|
|
|
if (i == MAX_CYCLES) {
|
|
data[opcode].crash = YES;
|
|
} else {
|
|
data[opcode].crash = NO;
|
|
uint16_t brk_addr = memory[0x0100+readSP()+2] | memory[0x0100+readSP()+3]<<8;
|
|
data[opcode].length = brk_addr - INSTRUCTION_ADDR - BRK_LENGTH;
|
|
|
|
/**************************************************
|
|
* find out length of instruction in cycles
|
|
**************************************************/
|
|
setup_memory(opcode);
|
|
resetChip_test();
|
|
for (i = 0; i < MAX_CYCLES; i++) {
|
|
step();
|
|
// chipStatus();
|
|
//printf("cycle = %d %x\n", cycle, readIR());
|
|
if (readIR() == 0x00)
|
|
break;
|
|
};
|
|
if (cycle)
|
|
data[opcode].cycles = cycle / 2;
|
|
else
|
|
data[opcode].cycles = 0;
|
|
|
|
/**************************************************
|
|
* find out zp or abs reads
|
|
**************************************************/
|
|
setup_memory(opcode);
|
|
memory[initial_x] = X_OFFSET;
|
|
memory[initial_y] = Y_OFFSET;
|
|
memory[MAGIC_8 + X_OFFSET + 0] = MAGIC_IZX & 0xFF;
|
|
memory[MAGIC_8 + X_OFFSET + 1] = MAGIC_IZX >> 8;
|
|
memory[MAGIC_8 + 0] = MAGIC_IZY & 0xFF;
|
|
memory[MAGIC_8 + 1] = MAGIC_IZY >> 8;
|
|
resetChip_test();
|
|
if (data[opcode].length == 2) {
|
|
memory[INSTRUCTION_ADDR + 1] = MAGIC_8;
|
|
} else if (data[opcode].length == 3) {
|
|
memory[INSTRUCTION_ADDR + 1] = MAGIC_16 & 0xFF;
|
|
memory[INSTRUCTION_ADDR + 2] = MAGIC_16 >> 8;
|
|
}
|
|
|
|
data[opcode].zp = NO;
|
|
data[opcode].abs = NO;
|
|
data[opcode].zpx = NO;
|
|
data[opcode].absx = NO;
|
|
data[opcode].zpy = NO;
|
|
data[opcode].absy = NO;
|
|
data[opcode].izx = NO;
|
|
data[opcode].izy = NO;
|
|
|
|
data[opcode].reads = NO;
|
|
data[opcode].writes = NO;
|
|
|
|
for (i = 0; i < data[opcode].cycles * 2 + 2; i++) {
|
|
step();
|
|
if (IS_READ_CYCLE || IS_WRITE_CYCLE) {
|
|
//printf("RW@ %X\n", readAddressBus());
|
|
BOOL is_data_access = YES;
|
|
if (readAddressBus() == MAGIC_8)
|
|
data[opcode].zp = YES;
|
|
else if (readAddressBus() == MAGIC_16)
|
|
data[opcode].abs = YES;
|
|
else if (readAddressBus() == MAGIC_8 + X_OFFSET)
|
|
data[opcode].zpx = YES;
|
|
else if (readAddressBus() == MAGIC_16 + X_OFFSET)
|
|
data[opcode].absx = YES;
|
|
else if (readAddressBus() == MAGIC_8 + Y_OFFSET)
|
|
data[opcode].zpy = YES;
|
|
else if (readAddressBus() == MAGIC_16 + Y_OFFSET)
|
|
data[opcode].absy = YES;
|
|
else if (readAddressBus() == MAGIC_IZX)
|
|
data[opcode].izx = YES;
|
|
else if (readAddressBus() == MAGIC_IZY + Y_OFFSET)
|
|
data[opcode].izy = YES;
|
|
else
|
|
is_data_access = NO;
|
|
if (is_data_access)
|
|
if (IS_READ_CYCLE)
|
|
data[opcode].reads = YES;
|
|
if (IS_WRITE_CYCLE)
|
|
data[opcode].writes = YES;
|
|
}
|
|
};
|
|
|
|
data[opcode].addmode = ADDMODE_UNKNOWN;
|
|
if (data[opcode].izy) {
|
|
data[opcode].addmode = ADDMODE_IZY;
|
|
} else if (data[opcode].izx) {
|
|
data[opcode].addmode = ADDMODE_IZX;
|
|
} else if (data[opcode].zpy) {
|
|
data[opcode].addmode = ADDMODE_ZPY;
|
|
} else if (data[opcode].zpx) {
|
|
data[opcode].addmode = ADDMODE_ZPX;
|
|
} else if (data[opcode].zp) {
|
|
data[opcode].addmode = ADDMODE_ZP;
|
|
} else if (data[opcode].absy) {
|
|
data[opcode].addmode = ADDMODE_ABSY;
|
|
} else if (data[opcode].absx) {
|
|
data[opcode].addmode = ADDMODE_ABSX;
|
|
} else if (data[opcode].abs) {
|
|
data[opcode].addmode = ADDMODE_ABS;
|
|
}
|
|
|
|
/**************************************************/
|
|
|
|
uint8_t magics[] = {
|
|
/* all 8 bit primes */
|
|
2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
|
|
// 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
|
|
// 73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
|
|
// 127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
|
|
// 179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
|
|
233, 239, 241, 251,
|
|
/* and some other interesting numbers */
|
|
0, 1, 0x55, 0xAA, 0xFF
|
|
};
|
|
/**************************************************
|
|
* find out inputs
|
|
**************************************************/
|
|
//printf("AAA\n");
|
|
for (int k = 0; k < 5; k++) {
|
|
BOOL different = NO;
|
|
int reads, writes;
|
|
uint16_t read[100], write[100], write_data[100];
|
|
uint8_t end_a, end_x, end_y, end_s, end_p;
|
|
for (int j = 0; j < sizeof(magics)/sizeof(*magics); j++) {
|
|
setup_memory(opcode);
|
|
if (data[opcode].length == 2) {
|
|
memory[INSTRUCTION_ADDR + 1] = MAGIC_8;
|
|
} else if (data[opcode].length == 3) {
|
|
memory[INSTRUCTION_ADDR + 1] = MAGIC_16 & 0xFF;
|
|
memory[INSTRUCTION_ADDR + 2] = MAGIC_16 >> 8;
|
|
}
|
|
switch (k) {
|
|
case 0: memory[initial_a] = magics[j]; break;
|
|
case 1: memory[initial_x] = magics[j]; break;
|
|
case 2: memory[initial_y] = magics[j]; break;
|
|
case 3: memory[initial_s] = magics[j]; break;
|
|
case 4: memory[initial_p] = magics[j]; break;
|
|
}
|
|
#define MAGIC_DATA8 3
|
|
switch (data[opcode].addmode) {
|
|
case ADDMODE_IZY:
|
|
//TODO
|
|
break;
|
|
case ADDMODE_IZX:
|
|
//TODO
|
|
break;
|
|
case ADDMODE_ZPY:
|
|
memory[MAGIC_8 + memory[initial_y]] = MAGIC_DATA8;
|
|
break;
|
|
case ADDMODE_ZPX:
|
|
memory[MAGIC_8 + memory[initial_x]] = MAGIC_DATA8;
|
|
break;
|
|
case ADDMODE_ZP:
|
|
memory[MAGIC_8] = MAGIC_DATA8;
|
|
break;
|
|
case ADDMODE_ABSY:
|
|
memory[MAGIC_16 + memory[initial_y]] = MAGIC_DATA8;
|
|
break;
|
|
case ADDMODE_ABSX:
|
|
memory[MAGIC_16 + memory[initial_x]] = MAGIC_DATA8;
|
|
break;
|
|
case ADDMODE_ABS:
|
|
memory[MAGIC_16] = MAGIC_DATA8;
|
|
break;
|
|
}
|
|
resetChip_test();
|
|
writes = 0;
|
|
reads = 0;
|
|
for (i = 0; i < data[opcode].cycles * 2 + 2; i++) {
|
|
step();
|
|
if (IS_READ_CYCLE) {
|
|
if (!j)
|
|
read[reads++] = readAddressBus();
|
|
else
|
|
if (read[reads++] != readAddressBus()) {
|
|
different = YES;
|
|
//printf("[[[%d]]]", __LINE__);
|
|
break;
|
|
}
|
|
}
|
|
if (IS_WRITE_CYCLE) {
|
|
if (!j) {
|
|
write[writes] = readAddressBus();
|
|
write_data[writes++] = readDataBus();
|
|
} else {
|
|
if (write[writes] != readAddressBus()) {
|
|
different = YES;
|
|
//printf("[[[%d]]]", __LINE__);
|
|
break;
|
|
}
|
|
if (write_data[writes++] != readDataBus()) {
|
|
//printf("[[[%d:k=%d;%x@%x/%x]]]", __LINE__, k, write[writes-1], write_data[writes-1], readDataBus());
|
|
different = YES;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
if (different) /* bus cycles were different */
|
|
break;
|
|
/* changes A */
|
|
if (!(k == 0)) {
|
|
if (!j) {
|
|
end_a = readA();
|
|
} else {
|
|
if (end_a != readA()) {
|
|
different = YES;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* changes X */
|
|
if (!(k == 1)) {
|
|
if (!j) {
|
|
end_x = readX();
|
|
} else {
|
|
if (end_x != readX()) {
|
|
different = YES;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* changes Y */
|
|
if (!(k == 2)) {
|
|
if (!j) {
|
|
end_y = readY();
|
|
} else {
|
|
if (end_y != readY()) {
|
|
different = YES;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* changes S */
|
|
if (!(k == 3)) {
|
|
if (!j) {
|
|
end_s = readSP();
|
|
} else {
|
|
if (end_s != readSP()) {
|
|
different = YES;
|
|
//printf("[%x/%x]", end_s, readSP());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* changes P */
|
|
if (!(k == 4)) {
|
|
if (!j) {
|
|
end_p = readP();
|
|
} else {
|
|
if (end_p != readP()) {
|
|
different = YES;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//printf("[%d DIFF: %d]", k, different);
|
|
switch (k) {
|
|
case 0: data[opcode].inputa = different; break;
|
|
case 1: data[opcode].inputx = different; break;
|
|
case 2: data[opcode].inputy = different; break;
|
|
case 3: data[opcode].inputs = different; break;
|
|
case 4: data[opcode].inputp = different; break;
|
|
}
|
|
}
|
|
//printf("BBB\n");
|
|
|
|
/**************************************************
|
|
* find out outputs
|
|
**************************************************/
|
|
data[opcode].outputa = NO;
|
|
data[opcode].outputx = NO;
|
|
data[opcode].outputy = NO;
|
|
data[opcode].outputs = NO;
|
|
data[opcode].outputp = NO;
|
|
|
|
for (int j = 0; j < sizeof(magics)/sizeof(*magics) - 5; j++) {
|
|
setup_memory(opcode);
|
|
memory[initial_a] = magics[j + 0];
|
|
memory[initial_x] = magics[j + 1];
|
|
memory[initial_y] = magics[j + 2];
|
|
memory[initial_s] = magics[j + 3];
|
|
memory[initial_p] = magics[j + 4];
|
|
if (data[opcode].length == 2) {
|
|
memory[INSTRUCTION_ADDR + 1] = MAGIC_8;
|
|
} else if (data[opcode].length == 3) {
|
|
memory[INSTRUCTION_ADDR + 1] = MAGIC_16 & 0xFF;
|
|
memory[INSTRUCTION_ADDR + 2] = MAGIC_16 >> 8;
|
|
}
|
|
switch (data[opcode].addmode) {
|
|
case ADDMODE_IZY:
|
|
//TODO
|
|
break;
|
|
case ADDMODE_IZX:
|
|
//TODO
|
|
break;
|
|
case ADDMODE_ZPY:
|
|
memory[MAGIC_8 + memory[initial_y]] = MAGIC_DATA8;
|
|
break;
|
|
case ADDMODE_ZPX:
|
|
memory[MAGIC_8 + memory[initial_x]] = MAGIC_DATA8;
|
|
break;
|
|
case ADDMODE_ZP:
|
|
memory[MAGIC_8] = MAGIC_DATA8;
|
|
break;
|
|
case ADDMODE_ABSY:
|
|
memory[MAGIC_16 + memory[initial_y]] = MAGIC_DATA8;
|
|
break;
|
|
case ADDMODE_ABSX:
|
|
memory[MAGIC_16 + memory[initial_x]] = MAGIC_DATA8;
|
|
break;
|
|
case ADDMODE_ABS:
|
|
memory[MAGIC_16] = MAGIC_DATA8;
|
|
break;
|
|
}
|
|
resetChip_test();
|
|
for (i = 0; i < data[opcode].cycles * 2 + 2; i++) {
|
|
step();
|
|
};
|
|
if (readA() != magics[j + 0])
|
|
data[opcode].outputa = YES;
|
|
if (readX() != magics[j + 1])
|
|
data[opcode].outputx = YES;
|
|
if (readY() != magics[j + 2])
|
|
data[opcode].outputy = YES;
|
|
if (readSP() != magics[j + 3])
|
|
data[opcode].outputs = YES;
|
|
if ((readP() & 0xCF) != (magics[j + 4] & 0xCF)) /* NV#BDIZC */
|
|
data[opcode].outputp = YES;
|
|
}
|
|
}
|
|
|
|
if (data[opcode].absx || data[opcode].zpx || data[opcode].izx) {
|
|
if (!data[opcode].inputx)
|
|
printf("input X?? ");
|
|
data[opcode].inputx = NO;
|
|
}
|
|
if (data[opcode].absy || data[opcode].zpy || data[opcode].izy) {
|
|
if (!data[opcode].inputy)
|
|
printf("input Y?? ");
|
|
data[opcode].inputy = NO;
|
|
}
|
|
|
|
if (data[opcode].crash) {
|
|
printf("CRASH\n");
|
|
} else {
|
|
printf("bytes: ");
|
|
if (data[opcode].length < 0 || data[opcode].length > 9)
|
|
printf("X ");
|
|
else
|
|
printf("%d ", data[opcode].length);
|
|
printf("cycles: %d ", data[opcode].cycles);
|
|
if (data[opcode].inputa)
|
|
printf("A");
|
|
else
|
|
printf("_");
|
|
if (data[opcode].inputx)
|
|
printf("X");
|
|
else
|
|
printf("_");
|
|
if (data[opcode].inputy)
|
|
printf("Y");
|
|
else
|
|
printf("_");
|
|
if (data[opcode].inputs)
|
|
printf("S");
|
|
else
|
|
printf("_");
|
|
if (data[opcode].inputp)
|
|
printf("P");
|
|
else
|
|
printf("_");
|
|
|
|
printf("=>");
|
|
|
|
if (data[opcode].outputa)
|
|
printf("A");
|
|
else
|
|
printf("_");
|
|
if (data[opcode].outputx)
|
|
printf("X");
|
|
else
|
|
printf("_");
|
|
if (data[opcode].outputy)
|
|
printf("Y");
|
|
else
|
|
printf("_");
|
|
if (data[opcode].outputs)
|
|
printf("S");
|
|
else
|
|
printf("_");
|
|
if (data[opcode].outputp)
|
|
printf("P");
|
|
else
|
|
printf("_");
|
|
|
|
printf(" ");
|
|
|
|
if (data[opcode].reads)
|
|
printf("R");
|
|
else
|
|
printf("_");
|
|
|
|
if (data[opcode].writes)
|
|
printf("W");
|
|
else
|
|
printf("_");
|
|
|
|
printf(" ");
|
|
|
|
switch (data[opcode].addmode) {
|
|
case ADDMODE_IZY: printf("izy"); break;
|
|
case ADDMODE_IZX: printf("izx"); break;
|
|
case ADDMODE_ZPY: printf("zpy"); break;
|
|
case ADDMODE_ZPX: printf("zpx"); break;
|
|
case ADDMODE_ZP: printf("zp"); break;
|
|
case ADDMODE_ABSY: printf("absy"); break;
|
|
case ADDMODE_ABSX: printf("absx"); break;
|
|
case ADDMODE_ABS: printf("abs"); break;
|
|
}
|
|
|
|
printf("\n");
|
|
}
|
|
}
|
|
}
|