185 lines
6.4 KiB
C
185 lines
6.4 KiB
C
|
#include <stdint.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#define CHIPS_IMPL
|
||
|
#include "m6502.h"
|
||
|
|
||
|
#define lament(...) fprintf(stderr, __VA_ARGS__)
|
||
|
#define error_when(cond, ...) do { \
|
||
|
if ((cond) || errno) { \
|
||
|
lament(__VA_ARGS__); \
|
||
|
lament(": %s\n", strerror(errno)); \
|
||
|
goto error; \
|
||
|
} \
|
||
|
} while (0)
|
||
|
|
||
|
// setup 64 kBytes of memory
|
||
|
#define MEMSIZE 65536
|
||
|
static uint8_t mem[MEMSIZE] = {0};
|
||
|
|
||
|
// NOTE: renamed KIL to JAM for consistency.
|
||
|
static const char instrnames[] =
|
||
|
"BRK\0ORA\0JAM\0SLO\0NOP\0ORA\0ASL\0SLO\0PHP\0ORA\0ASL\0ANC\0NOP\0ORA\0ASL\0SLO\0"
|
||
|
"BPL\0ORA\0JAM\0SLO\0NOP\0ORA\0ASL\0SLO\0CLC\0ORA\0NOP\0SLO\0NOP\0ORA\0ASL\0SLO\0"
|
||
|
"JSR\0AND\0JAM\0RLA\0BIT\0AND\0ROL\0RLA\0PLP\0AND\0ROL\0ANC\0BIT\0AND\0ROL\0RLA\0"
|
||
|
"BMI\0AND\0JAM\0RLA\0NOP\0AND\0ROL\0RLA\0SEC\0AND\0NOP\0RLA\0NOP\0AND\0ROL\0RLA\0"
|
||
|
"RTI\0EOR\0JAM\0SRE\0NOP\0EOR\0LSR\0SRE\0PHA\0EOR\0LSR\0ALR\0JMP\0EOR\0LSR\0SRE\0"
|
||
|
"BVC\0EOR\0JAM\0SRE\0NOP\0EOR\0LSR\0SRE\0CLI\0EOR\0NOP\0SRE\0NOP\0EOR\0LSR\0SRE\0"
|
||
|
"RTS\0ADC\0JAM\0RRA\0NOP\0ADC\0ROR\0RRA\0PLA\0ADC\0ROR\0ARR\0JMP\0ADC\0ROR\0RRA\0"
|
||
|
"BVS\0ADC\0JAM\0RRA\0NOP\0ADC\0ROR\0RRA\0SEI\0ADC\0NOP\0RRA\0NOP\0ADC\0ROR\0RRA\0"
|
||
|
"NOP\0STA\0NOP\0SAX\0STY\0STA\0STX\0SAX\0DEY\0NOP\0TXA\0XAA\0STY\0STA\0STX\0SAX\0"
|
||
|
"BCC\0STA\0JAM\0AHX\0STY\0STA\0STX\0SAX\0TYA\0STA\0TXS\0TAS\0SHY\0STA\0SHX\0AHX\0"
|
||
|
"LDY\0LDA\0LDX\0LAX\0LDY\0LDA\0LDX\0LAX\0TAY\0LDA\0TAX\0LAX\0LDY\0LDA\0LDX\0LAX\0"
|
||
|
"BCS\0LDA\0JAM\0LAX\0LDY\0LDA\0LDX\0LAX\0CLV\0LDA\0TSX\0LAS\0LDY\0LDA\0LDX\0LAX\0"
|
||
|
"CPY\0CMP\0NOP\0DCP\0CPY\0CMP\0DEC\0DCP\0INY\0CMP\0DEX\0AXS\0CPY\0CMP\0DEC\0DCP\0"
|
||
|
"BNE\0CMP\0JAM\0DCP\0NOP\0CMP\0DEC\0DCP\0CLD\0CMP\0NOP\0DCP\0NOP\0CMP\0DEC\0DCP\0"
|
||
|
"CPX\0SBC\0NOP\0ISC\0CPX\0SBC\0INC\0ISC\0INX\0SBC\0NOP\0SBC\0CPX\0SBC\0INC\0ISC\0"
|
||
|
"BEQ\0SBC\0JAM\0ISC\0NOP\0SBC\0INC\0ISC\0SED\0SBC\0NOP\0ISC\0NOP\0SBC\0INC\0ISC";
|
||
|
|
||
|
static const char documented[] = {
|
||
|
1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0,
|
||
|
1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0,
|
||
|
1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0,
|
||
|
1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0,
|
||
|
1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0,
|
||
|
1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0,
|
||
|
1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0,
|
||
|
1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0,
|
||
|
0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0,
|
||
|
1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0,
|
||
|
1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0,
|
||
|
1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0,
|
||
|
1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0,
|
||
|
1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0,
|
||
|
1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0,
|
||
|
1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0
|
||
|
};
|
||
|
|
||
|
static int loadmem(const char *fp) {
|
||
|
FILE *f = NULL;
|
||
|
long size = MEMSIZE;
|
||
|
|
||
|
errno = 0;
|
||
|
f = fopen(fp, "rb");
|
||
|
error_when(f == NULL, "Error opening file: %s", fp);
|
||
|
error_when(fread(mem, 1, size, f) != (size_t)size, "Error reading %li bytes from file: %s", size, fp);
|
||
|
error_when(fclose(f) != 0, "Error closing file: %s", fp);
|
||
|
return 0;
|
||
|
|
||
|
error:
|
||
|
//return 65; // EX_DATAERR
|
||
|
return 66; // EX_NOINPUT
|
||
|
}
|
||
|
|
||
|
static void memdebug(const m6502_t cpu, long instrs) {
|
||
|
uint64_t pins = cpu.PINS;
|
||
|
uint16_t pc = cpu.PC;
|
||
|
uint8_t instr = cpu.IR >> 3;
|
||
|
int ic = cpu.IR & 7;
|
||
|
uint16_t addr = M6502_GET_ADDR(pins);
|
||
|
const char *mode = (pins & M6502_RW) ? "READ " : "WRITE ";
|
||
|
uint8_t value = (pins & M6502_RW) ? mem[addr] : M6502_GET_DATA(pins);
|
||
|
const char *instrname = instrnames + instr * 4;
|
||
|
const char *ok = documented[instr] ? "..." : "!!!";
|
||
|
lament("[%4li.%i:$%04X:$%02X (%s %s)] %s mem[0x%04X]=0x%02X;\n",
|
||
|
instrs, ic, pc, instr, instrname, ok, mode, addr, value);
|
||
|
}
|
||
|
|
||
|
static void xxd(const uint8_t *start, int length) {
|
||
|
while (length >= 16) {
|
||
|
printf("%08x: ", (unsigned int)(start - mem));
|
||
|
for (int i = 0; i < 16; i += 2) {
|
||
|
printf("%02x%02x ", start[i], start[i + 1]);
|
||
|
}
|
||
|
printf(" ");
|
||
|
for (int i = 0; i < 16; i++) {
|
||
|
uint8_t value = start[i];
|
||
|
if (value < 0x20 || value >= 0x7F) {
|
||
|
value = '.';
|
||
|
}
|
||
|
printf("%c", value);
|
||
|
}
|
||
|
printf("\n");
|
||
|
start += 16; // FIXME: can technically invoke undefined behavior on boundary.
|
||
|
length -= 16;
|
||
|
}
|
||
|
// TODO: handle the remainder.
|
||
|
//for (int i = 0; i < length; i += 2) {
|
||
|
//}
|
||
|
}
|
||
|
|
||
|
int main(int argc, char **argv) {
|
||
|
char *name = NULL;
|
||
|
long cycle = 0;
|
||
|
long oldcycle = 0;
|
||
|
long instrs = 0;
|
||
|
long instr_limit = 1000;
|
||
|
uint64_t pins;
|
||
|
m6502_t cpu;
|
||
|
m6502_desc_t desc = {0};
|
||
|
|
||
|
if (argc <= 0 || argv == NULL || argv[0] == NULL) {
|
||
|
lament("You've met with a terrible fate.\n");
|
||
|
return 64; // EX_USAGE
|
||
|
}
|
||
|
|
||
|
name = argv[0];
|
||
|
if (argc != 2 && argc != 3) {
|
||
|
lament("usage: %s {ram.bin} [instructions]\n", name);
|
||
|
return 64; // EX_USAGE
|
||
|
}
|
||
|
|
||
|
if (argc == 3) {
|
||
|
instr_limit = strtol(argv[2], NULL, 0); // can be negative, i guess.
|
||
|
if (errno) {
|
||
|
lament("%s: failed to parse integer: %s\n", name, argv[2]);
|
||
|
return 64; // EX_USAGE
|
||
|
}
|
||
|
}
|
||
|
|
||
|
{
|
||
|
int res = 0;
|
||
|
if ((res = loadmem(argv[1]))) return res;
|
||
|
}
|
||
|
|
||
|
// initialize the CPU
|
||
|
desc.bcd_disabled = true; // TODO: do this from instructions?
|
||
|
pins = m6502_init(&cpu, &desc);
|
||
|
for (;; cycle++) {
|
||
|
// run the CPU emulation for one tick
|
||
|
pins = m6502_tick(&cpu, pins);
|
||
|
// extract 16-bit address from pin mask
|
||
|
const uint16_t addr = M6502_GET_ADDR(pins);
|
||
|
// perform memory access
|
||
|
if (pins & M6502_RW) {
|
||
|
// a memory read
|
||
|
uint8_t value = mem[addr];
|
||
|
memdebug(cpu, instrs);
|
||
|
M6502_SET_DATA(pins, value);
|
||
|
} else {
|
||
|
// a memory write
|
||
|
uint8_t value = M6502_GET_DATA(pins);
|
||
|
memdebug(cpu, instrs);
|
||
|
mem[addr] = value;
|
||
|
}
|
||
|
if (cycle >= oldcycle + 8) {
|
||
|
lament("CPU is locked up!\n");
|
||
|
break;
|
||
|
}
|
||
|
if (pins & M6502_SYNC) {
|
||
|
instrs++;
|
||
|
oldcycle = cycle;
|
||
|
if (instrs >= instr_limit) break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fflush(stdout);
|
||
|
fflush(stderr);
|
||
|
printf("cpu.PC=0x%04X, cpu.A=0x%02X, cpu.X=0x%02X, cpu.Y=0x%02X, cpu.S=0x%02X, cpu.P=0x%02X;\n",
|
||
|
cpu.PC, cpu.A, cpu.X, cpu.Y, cpu.S, cpu.P);
|
||
|
lament("exiting after %li instructions and %li cycles.\n", instrs, cycle + 1);
|
||
|
xxd(mem, 0x100);
|
||
|
return 0;
|
||
|
}
|