/hacks/6502dis.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>

typedef struct Op Op;
struct Op{
	char	*name;
	int	am;
};

enum
{
	Accumulator,
	Absolute,
	AbsoluteX,
	AbsoluteY,
	Immediate,
	Implicit,
	Indirect,
	XIndirect,
	IndirectY,
	Relative,
	ZeroPage,
	ZeroPageX,
	ZeroPageY,
};

FILE	*f;
unsigned int	pc = 0x0000;
Op	opcode[] = {
	[0x00]	= { "brk",	Implicit },
	[0x01]	= { "ora",	XIndirect },
	[0x05]	= { "ora",	ZeroPage },
	[0x06]	= { "asl",	ZeroPage },
	[0x08]	= { "php",	Implicit },
	[0x09]	= { "ora",	Immediate },
	[0x0A]	= { "asl",	Accumulator },
	[0x0D]	= { "ora",	Absolute },
	[0x0E]	= { "asl",	Absolute },
	[0x10]	= { "bpl",	Relative },
	[0x11]	= { "ora",	IndirectY },
	[0x15]	= { "ora",	ZeroPageX },
	[0x16]	= { "asl",	ZeroPageX },
	[0x18]	= { "clc",	Implicit },
	[0x19]	= { "ora",	AbsoluteY },
	[0x1D]	= { "ora",	AbsoluteX },
	[0x1E]	= { "asl",	AbsoluteX },
	[0x20]	= { "jsr",	Absolute },
	[0x21]	= { "and",	XIndirect },
	[0x24]	= { "bit",	ZeroPage },
	[0x25]	= { "and",	ZeroPage },
	[0x26]	= { "rol",	ZeroPage },
	[0x28]	= { "plp",	Implicit },
	[0x29]	= { "and",	Immediate },
	[0x2A]	= { "rol",	Accumulator },
	[0x2C]	= { "bit",	Absolute },
	[0x2D]	= { "and",	Absolute },
	[0x2E]	= { "rol",	Absolute },
	[0x30]	= { "bmi",	Relative },
	[0x31]	= { "and",	IndirectY },
	[0x35]	= { "and",	ZeroPageX },
	[0x36]	= { "rol",	ZeroPageX },
	[0x38]	= { "sec",	Implicit },
	[0x39]	= { "and",	AbsoluteY },
	[0x3D]	= { "and",	AbsoluteX },
	[0x3E]	= { "rol",	AbsoluteX },
	[0x40]	= { "rti",	Implicit },
	[0x41]	= { "eor",	XIndirect },
	[0x45]	= { "eor",	ZeroPage },
	[0x46]	= { "lsr",	ZeroPage },
	[0x48]	= { "pha",	Implicit },
	[0x49]	= { "eor",	Immediate },
	[0x4A]	= { "lsr",	Accumulator },
	[0x4C]	= { "jmp",	Absolute },
	[0x4D]	= { "eor",	Absolute },
	[0x4E]	= { "lsr",	Absolute },
	[0x50]	= { "bvc",	Relative },
	[0x51]	= { "eor",	IndirectY },
	[0x55]	= { "eor",	ZeroPageX },
	[0x56]	= { "lsr",	ZeroPageX },
	[0x58]	= { "cli",	Implicit },
	[0x59]	= { "eor",	AbsoluteY },
	[0x5D]	= { "eor",	AbsoluteX },
	[0x5E]	= { "lsr",	AbsoluteX },
	[0x60]	= { "rts",	Implicit },
	[0x61]	= { "adc",	IndirectY },
	[0x65]	= { "adc",	ZeroPage },
	[0x66]	= { "ror",	ZeroPage },
	[0x68]	= { "pla",	Implicit },
	[0x69]	= { "adc",	Immediate },
	[0x6A]	= { "ror",	Accumulator },
	[0x6C]	= { "jmp",	Indirect },
	[0x6D]	= { "adc",	Absolute },
	[0x6E]	= { "ror",	Absolute },
	[0x70]	= { "bvs",	Relative },
	[0x71]	= { "adc",	IndirectY },
	[0x75]	= { "adc",	ZeroPage },
	[0x76]	= { "ror",	ZeroPage },
	[0x78]	= { "sei",	Implicit },
	[0x79]	= { "adc",	AbsoluteY },
	[0x7D]	= { "adc",	AbsoluteX },
	[0x7E]	= { "ror",	AbsoluteX },
	[0x81]	= { "sta",	XIndirect },
	[0x84]	= { "sty",	ZeroPage },
	[0x85]	= { "sta",	ZeroPage },
	[0x86]	= { "stx",	ZeroPage },
	[0x88]	= { "dey",	Implicit },
	[0x8A]	= { "txa",	Implicit },
	[0x8C]	= { "sty",	Absolute },
	[0x8D]	= { "sta",	Absolute },
	[0x8E]	= { "stx",	Absolute },
	[0x90]	= { "bcc",	Relative },
	[0x91]	= { "sta",	IndirectY },
	[0x94]	= { "sty",	ZeroPageX },
	[0x95]	= { "sta",	ZeroPageX },
	[0x96]	= { "stx",	ZeroPageY },
	[0x98]	= { "tya",	Implicit },
	[0x99]	= { "sta",	AbsoluteY },
	[0x9A]	= { "txs",	Implicit },
	[0x9D]	= { "sta",	AbsoluteX },
	[0xA0]	= { "ldy",	Immediate },
	[0xA1]	= { "lda",	IndirectY },
	[0xA2]	= { "ldx",	Immediate },
	[0xA4]	= { "ldy",	ZeroPage },
	[0xA5]	= { "lda",	ZeroPage },
	[0xA6]	= { "ldx",	ZeroPage },
	[0xA8]	= { "tay",	Implicit },
	[0xA9]	= { "lda",	Absolute },
	[0xAA]	= { "tax",	Implicit },
	[0xAC]	= { "ldy",	Absolute },
	[0xAD]	= { "lda",	Absolute },
	[0xAE]	= { "ldx",	Absolute },
	[0xB0]	= { "bcs",	Relative },
	[0xB1]	= { "lda",	IndirectY },
	[0xB4]	= { "ldy",	ZeroPageX },
	[0xB5]	= { "ldx",	ZeroPageY },
	[0xB6]	= { "ldx",	ZeroPageY },
	[0xB8]	= { "clv",	Implicit },
	[0xB9]	= { "lda",	AbsoluteY },
	[0xBA]	= { "tsx",	Implicit },
	[0xBC]	= { "ldy",	AbsoluteX },
	[0xBD]	= { "lda",	AbsoluteX },
	[0xBE]	= { "ldx",	AbsoluteY },
	[0xC0]	= { "cpy",	Immediate },
	[0xC1]	= { "cmp",	XIndirect },
	[0xC4]	= { "cpy",	ZeroPage },
	[0xC5]	= { "cmp",	ZeroPage },
	[0xC6]	= { "dec",	ZeroPage },
	[0xC8]	= { "iny",	Implicit },
	[0xC9]	= { "cmp",	Immediate },
	[0xCA]	= { "dex",	Implicit },
	[0xCC]	= { "cpy",	Absolute },
	[0xCD]	= { "cmp",	Absolute },
	[0xCE]	= { "dec",	Absolute },
	[0xD0]	= { "bne",	Relative },
	[0xD1]	= { "cmp",	IndirectY },
	[0xD5]	= { "cmp",	ZeroPageX },
	[0xD6]	= { "dec",	ZeroPageX },
	[0xD8]	= { "cld",	Implicit },
	[0xD9]	= { "cmp",	AbsoluteY },
	[0xDD]	= { "cmp",	AbsoluteX },
	[0xDE]	= { "dec",	AbsoluteX },
	[0xE0]	= { "cpx",	Immediate },
	[0xE1]	= { "sbc",	XIndirect },
	[0xE4]	= { "cpx",	ZeroPage },
	[0xE5]	= { "sbc",	ZeroPage },
	[0xE6]	= { "inc",	ZeroPage },
	[0xE8]	= { "inx",	Implicit },
	[0xE9]	= { "sbc",	Immediate },
	[0xEA]	= { "nop",	Implicit },
	[0xEC]	= { "cpx",	Absolute },
	[0xED]	= { "sbc",	Absolute },
	[0xEE]	= { "inc",	Absolute },
	[0xF0]	= { "beq",	Relative },
	[0xF1]	= { "sbc",	IndirectY },
	[0xF5]	= { "sbc",	ZeroPageX },
	[0xF6]	= { "inc",	ZeroPageX },
	[0xF8]	= { "sed",	Implicit },
	[0xF9]	= { "sbc",	AbsoluteY },
	[0xFD]	= { "sbc",	AbsoluteX },
	[0xFE]	= { "inc",	AbsoluteX },
};

int
readc(void)
{
	pc++;
	return fgetc(f);
}

void
putop(Op *o)
{
	int lo, hi;

	printf("0x%04x\t", pc-1);
	switch(o->am){
	case Accumulator:
	case Implicit:
		break;
	case Immediate:
	case XIndirect:
	case IndirectY:
	case Relative:
	case ZeroPage:
	case ZeroPageX:
	case ZeroPageY:
		lo = readc();
		break;
	case Absolute:
	case AbsoluteX:
	case AbsoluteY:
	case Indirect:
		hi = readc();
		lo = readc();
		break;
	}
	printf("%s", o->name);
	switch(o->am){
	case Accumulator:
		printf(" A");
		break;
	case Absolute:
		printf(" $%02x%02x", lo, hi);
		break;
	case AbsoluteX:
		printf(" $%02x%02x,X", lo, hi);
		break;
	case AbsoluteY:
		printf(" $%02x%02x,Y", lo, hi);
		break;
	case Immediate:
		printf(" #$%02x", lo);
		break;
	case Implicit:
		break;
	case Indirect:
		printf(" (%02x%02x)", lo, hi);
		break;
	case XIndirect:
		printf(" (%02x,X)", lo);
		break;
	case IndirectY:
		printf(" (%02x),Y", lo);
		break;
	case Relative:
		printf(" $80%02x", (unsigned char)(lo+pc));
		break;
	case ZeroPage:
		printf(" $%02x", lo);
		break;
	case ZeroPageX:
		printf(" $%02x,X", lo);
		break;
	case ZeroPageY:
		printf(" $%02x,Y", lo);
		break;
	}
	putchar('\n');
}

int
readnum(char *s, unsigned long *val)
{
	char *endp;

	errno = 0;
	*val = strtoul(s, &endp, 0);
	if((errno!=0) || (*endp!='\0'))
		return -1;
	else
		return 0;
}

void
usage(void)
{
	fprintf(stderr, "usage: 6502dis [ -n nstart ] FILENAME\n");
	exit(1);
}

int
main(int argc, char *argv[])
{
	int c;
	extern int optind;
	Op *o;
	unsigned long tmp;

	while((c = getopt(argc, argv, ":s:")) != -1){
		switch(c){
		case 's':
			if(readnum(optarg, &tmp) < 0){
				fprintf(stderr, "6502dis: invalid starting address\n");
				exit(1);
			}else
				pc = tmp;
			break;
		case '?':
			usage();
		}
	}
	if(optind != argc-1)
		usage();
	f = fopen(argv[optind], "r");
	if(!f){
		fprintf(stderr, "6502dis: %s: ", argv[optind]);
		perror("");
		exit(1);
	}
	while((c = readc()) != EOF){
		o = &opcode[c];
		if(!o->name){
			printf("0x%04x\t.byte $%02x\n", pc-1, c);
			continue;
		}
		putop(o);
	}
	return 0;
}