Add disassembler
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b0d1121
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+~*
+build
diff --git "a/.~lock.6502.csv\043" "b/.~lock.6502.csv\043"
new file mode 100644
index 0000000..66010a7
--- /dev/null
+++ "b/.~lock.6502.csv\043"
@@ -0,0 +1 @@
+,ch,lk,28.07.2020 15:28,file:///home/ch/.config/libreoffice/4;
\ No newline at end of file
diff --git a/6502.csv b/6502.csv
new file mode 100644
index 0000000..ce8ec0d
--- /dev/null
+++ b/6502.csv
@@ -0,0 +1,182 @@
+opcode,mnemonic,addressing mode,bytes,cycles,flags
+,,,,,
+0x69,ADC,IMM,2,2,CZidbVN
+0x65,ADC,ZP,2,3,CZidbVN
+0x75,ADC,ZPX,2,4,CZidbVN
+0x6d,ADC,ABS,3,4,CZidbVN
+0x7d,ADC,AX,3,4,CZidbVN
+0x79,ADC,AY,3,4,CZidbVN
+0x61,ADC,ZIX,2,6,CZidbVN
+0x71,ADC,ZIY,2,5,CZidbVN
+,,,,,
+0x29,AND,IMM,2,2,cZidbvN
+0x25,AND,ZP,2,3,cZidbvN
+0x35,AND,ZPX,2,4,cZidbvN
+0x2d,AND,ABS,3,4,cZidbvN
+0x3d,AND,AX,3,4,cZidbvN
+0x39,AND,AY,3,4,cZidbvN
+0x21,AND,ZIX,2,6,cZidbvN
+0x31,AND,ZIY,2,5,cZidbvN
+,,,,,
+0x0a,ASL,ACC,1,2,CZidbvN
+0x06,ASL,ZP,2,5,CZidbvN
+0x16,ASL,ZPX,2,6,CZidbvN
+0x0e,ASL,ABS,3,6,CZidbvN
+0x1e,ASL,AX,3,6/7,CZidbvN
+,,,,,
+0x90,BCC,REL,2,2/3,czidbvn
+0xB0,BCS,REL,2,2/3,czidbvn
+0xF0,BEQ,REL,2,2/3,czidbvn
+0x30,BMI,REL,2,2/3,czidbvn
+0xD0,BNE,REL,2,2/3,czidbvn
+0x10,BPL,REL,2,2/3,czidbvn
+0x50,BVC,REL,2,2/3,czidbvn
+0x70,BVS,REL,2,2/3,czidbvn
+,,,,,
+0x24,BIT,ZP,2,3,cZidbVN
+0x2c,BIT,ABS,3,4,cZidbVN
+0x89,BIT,IMM,2,2,cZidbvn
+0x34,BIT,ZPX,2,4,cZidbVN
+0x3c,BIT,AX,3,4,cZidbVN
+,,,,,
+0x00,BRK,IMP,1,7,cziDBvn
+0x18,CLC,IMP,1,2,Czidbvn
+0xd8,CLD,IMP,1,2,cziDbvn
+0x58,CLI,IMP,1,2,czIdbvn
+0xb8,CLV,IMP,1,2,czidbVn
+0xea,NOP,IMP,1,2,czidbvn
+0x48,PHA,IMP,1,3,czidbvn
+0x68,PLA,IMP,1,4,cZidbvN
+0x08,PHP,IMP,1,3,czidbvn
+0x28,PLP,IMP,1,4,CZIDBVN
+0x40,RTI,IMP,1,6,czidbvn
+0x60,RTS,IMP,1,6,czidbvn
+0x38,SEC,IMP,1,2,Czidbvn
+0xf8,SED,IMP,1,2,cziDbvn
+0x78,SEI,IMP,1,2,czIdbvn
+0xaa,TAX,IMP,1,2,cZidbvN
+0x8a,TXA,IMP,1,2,cZidbvN
+0xa8,TAY,IMP,1,2,cZidbvN
+0x98,TYA,IMP,1,2,cZidbvN
+0xba,TSX,IMP,1,2,cZidbvN
+0x9a,TXS,IMP,1,2,czidbvn
+,,,,,
+0xc9,CMP,IMM,2,2,CZidbvN
+0xc5,CMP,ZP,2,3,CZidbvN
+0xd5,CMP,ZPX,2,4,CZidbvN
+0xcd,CMP,ABS,3,4,CZidbvN
+0xdd,CMP,AX,3,4,CZidbvN
+0xd9,CMP,AY,3,4,CZidbvN
+0xc1,CMP,ZIX,2,6,CZidbvN
+0xd1,CMP,ZIY,2,5,CZidbvN
+,,,,,
+0xe0,CPX,IMM,2,2,CZidbvN
+0xe4,CPX,ZP,2,3,CZidbvN
+0xec,CPX,ABS,3,4,CZidbvN
+,,,,,
+0xc0,CPY,IMM,2,2,CZidbvN
+0xc4,CPY,ZP,2,3,CZidbvN
+0xcc,CPY,ABS,3,4,CZidbvN
+,,,,,
+0xc6,DEC,ZP,2,5,cZidbvN
+0xd6,DEC,ZPX,2,6,cZidbvN
+0xce,DEC,ABS,3,6,cZidbvN
+0xde,DEC,AX,3,7,cZidbvN
+0x3a,DEC,ACC,1,2,cZidbvN
+,,,,,
+0xca,DEX,IMP,1,2,cZidbvN
+0x88,DEY,IMP,1,2,cZidbvN
+0xe8,INX,IMP,1,2,cZidbvN
+0xc8,INY,IMP,1,2,cZidbvN
+,,,,,
+0x49,EOR,IMM,2,2,cZidbvN
+0x45,EOR,ZP,2,3,cZidbvN
+0x55,EOR,ZPX,2,4,cZidbvN
+0x4d,EOR,ABS,3,4,cZidbvN
+0x5d,EOR,AX,3,4,cZidbvN
+0x59,EOR,AY,3,4,cZidbvN
+0x41,EOR,ZIX,2,6,cZidbvN
+0x51,EOR,ZIY,2,5,cZidbvN
+,,,,,
+0xe6,INC,ZP,2,5,cZidbvN
+0xf6,INC,ZPX,2,6,cZidbvN
+0xee,INC,ABS,3,6,cZidbvN
+0xfe,INC,AX,3,7,cZidbvN
+0x1a,INC,ACC,1,2,cZidbvN
+,,,,,
+0x4c,JMP,ABS,3,3,czidbvn
+0x6c,JMP,IND,3,5,czidbvn
+0x7c,JMP,AX,3,6,czidbvn
+0x20,JSR,ABS,3,6,czidbvn
+,,,,,
+0xa9,LDA,IMM,2,2,cZidbvN
+0xa5,LDA,ZP,2,3,cZidbvN
+0xb5,LDA,ZPX,2,4,cZidbvN
+0xad,LDA,ABS,3,4,cZidbvN
+0xbd,LDA,AX,3,4,cZidbvN
+0xb9,LDA,AY,3,4,cZidbvN
+0xa1,LDA,ZIX,2,6,cZidbvN
+0xb1,LDA,ZIY,2,5,cZidbvN
+,,,,,
+0xa2,LDX,IMM,2,2,cZidbvN
+0xa6,LDX,ZP,2,3,cZidbvN
+0xb6,LDX,ZPY,2,4,cZidbvN
+0xae,LDX,ABS,3,4,cZidbvN
+0xbe,LDX,AY,3,4,cZidbvN
+,,,,,
+0xa0,LDY,IMM,2,2,cZidbvN
+0xa4,LDY,ZP,2,3,cZidbvN
+0xb4,LDY,ZPX,2,4,cZidbvN
+0xac,LDY,ABS,3,4,cZidbvN
+0xbc,LDY,AX,3,4,cZidbvN
+,,,,,
+0x4a,LSR,ACC,1,2,cZidbvN
+0x46,LSR,ZP,2,5,cZidbvN
+0x56,LSR,ZPX,2,6,cZidbvN
+0x4e,LSR,ABS,3,6,cZidbvN
+0x5e,LSR,AX,3,6/7,cZidbvN
+,,,,,
+0x09,ORA,IMM,2,2,cZidbvN
+0x05,ORA,ZP,2,3,cZidbvN
+0x15,ORA,ZPX,2,4,cZidbvN
+0x0d,ORA,ABS,3,4,cZidbvN
+0x1d,ORA,AX,3,4,cZidbvN
+0x19,ORA,AY,3,4,cZidbvN
+0x01,ORA,ZIX,2,6,cZidbvN
+0x11,ORA,ZIY,2,5,cZidbvN
+,,,,,
+0x2a,ROL,ACC,1,2,CZidbvN
+0x26,ROL,ZP,2,5,CZidbvN
+0x36,ROL,ZPX,2,6,CZidbvN
+0x2e,ROL,ABS,3,6,CZidbvN
+0x3e,ROL,AX,3,6/7,CZidbvN
+,,,,,
+0x6a,ROR,ACC,1,2,CZidbvN
+0x66,ROR,ZP,2,5,CZidbvN
+0x76,ROR,ZPX,2,6,CZidbvN
+0x7e,ROR,ABS,3,6,CZidbvN
+0x6e,ROR,AX,3,6/7,CZidbvN
+,,,,,
+0xe9,SBC,IMM,2,2,CZidbVN
+0xe5,SBC,ZP,2,3,CZidbVN
+0xf5,SBC,ZPX,2,4,CZidbVN
+0xed,SBC,ABS,3,4,CZidbVN
+0xfd,SBC,AX,3,4,CZidbVN
+0xf9,SBC,AY,3,4,CZidbVN
+0xe1,SBC,ZIX,2,6,CZidbVN
+0xf1,SBC,ZIY,2,5,CZidbVN
+,,,,,
+0x85,STA,ZP,2,3,czidbvn
+0x95,STA,ZPX,2,4,czidbvn
+0x8d,STA,ABS,3,4,czidbvn
+0x9d,STA,AX,3,5,czidbvn
+0x99,STA,AY,3,5,czidbvn
+0x81,STA,ZIX,2,6,czidbvn
+0x91,STA,ZIY,2,6,czidbvn
+,,,,,
+0x86,STX,ZP,2,3,czidbvn
+0x96,STX,ZPY,2,4,czidbvn
+0x8e,STX,ABS,3,4,czidbvn
+0x84,STY,ZP,2,3,czidbvn
+0x94,STY,ZPX,2,4,czidbvn
+0x8c,STY,ABS,3,4,czidbvn
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..8c4167f
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.0)
+
+project(6502 VERSION 0.1.0 LANGUAGES C)
+
+option(GEN_INSTRUCTIONS_HEADER ON)
+
+if (${GEN_INSTRUCTIONS_HEADER})
+	add_custom_command(
+		OUTPUT instructions.h
+		DEPENDS csv2h.awk 6502.csv
+		COMMAND awk -f csv2h.awk 6502.csv > instructions.h
+		WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
+endif()
+
+add_executable(emu-6502 main.c cpu.c cpu.h instructions.h)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..11e1de0
--- /dev/null
+++ b/README.md
@@ -0,0 +1,10 @@
+# 6502 Toolchain
+
+This project aims to create a portable toolchain for developing,
+testing and debugging programs for the 6502 processor. An assembler
+may be implemented in the future but no work has been done on that
+yet.
+
+The `instructions.h` header is generated from `6502.csv` and contains
+definitions of every 6502 opcode, its mnemonic and addressing mode.
+It is built automatically by cmake. 
\ No newline at end of file
diff --git a/cpu.c b/cpu.c
new file mode 100644
index 0000000..f0d7d68
--- /dev/null
+++ b/cpu.c
@@ -0,0 +1,204 @@
+#include "cpu.h"
+#include "instructions.h"
+
+#include <endian.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define die(m, ...) \
+	printf("\033[31mError: " m "\033[0m\n", ##__VA_ARGS__); \
+	exit(1);
+
+cpu_t new_cpu()
+{
+	cpu_t cpu = { 0 };
+	cpu.regs[SR] = UNUSED; // unused flag always set
+	cpu.regs[SP] = 0xFD; // stack at is 0x100 + SP
+	cpu.pc = 0; // arbitrary program counter start
+	cpu.mem = malloc(0xFFFF);
+	memset(cpu.mem, 0, 0xFFFF);
+
+	if (!cpu.mem)
+	{
+		die("Could not allocate memory for CPU");
+	}
+
+	return cpu;
+}
+
+void free_cpu(cpu_t *cpu)
+{
+	free(cpu->mem);
+}
+
+void execute(cpu_t *cpu, const char *mnemonic, uint8_t op, uint16_t addr)
+{
+
+}
+
+uint16_t le_to_native(uint8_t a, uint8_t b)
+{
+#ifdef LITTLE_ENDIAN
+	return a << 8 | b;
+#else
+	return b << 8 | a;
+#endif
+}
+
+uint16_t fetch_le(cpu_t *cpu)
+{
+	uint8_t a = cpu->mem[cpu->pc++];
+	uint8_t b = cpu->mem[cpu->pc++];
+	return le_to_native(a, b);
+}
+
+uint16_t fetch_addr(cpu_t *cpu, uint8_t am)
+{
+	switch (am)
+	{
+		case AM_ACC:
+		case AM_IMP:
+			return 0;
+
+		// In both cases return immediate 8 bit value
+		case AM_IMM:
+		case AM_ZP:
+			return cpu->mem[cpu->pc++];
+
+		case AM_ABS:
+			return fetch_le(cpu); 
+
+		case AM_REL:
+		{
+			// PC should point to the opcode
+			// braces needed to avoid c stupidity
+			uint16_t pc = cpu->pc - 1;
+			return cpu->mem[cpu->pc++] + pc;
+		}
+
+		case AM_IND:
+		{
+			uint16_t addr = fetch_le(cpu);
+			uint8_t low = cpu->mem[addr],
+				high = cpu->mem[addr + 1];
+
+			return le_to_native(low, high);
+		}
+
+		case AM_AX:
+			return fetch_le(cpu) + cpu->regs[X];
+
+		case AM_AY:
+			return fetch_le(cpu) + cpu->regs[Y];
+
+		case AM_ZPX:
+			return cpu->mem[cpu->pc++] + cpu->regs[X];
+
+		case AM_ZPY:
+			return cpu->mem[cpu->pc++] + cpu->regs[Y];
+
+		case AM_ZIX:
+		{
+			uint8_t zp = cpu->mem[cpu->pc++];
+			return le_to_native(cpu->mem[zp + cpu->regs[X]], cpu->mem[zp + cpu->regs[X] + 1]);
+		}
+
+		case AM_ZIY:
+		{
+			uint8_t zp = cpu->mem[cpu->pc++];
+			uint16_t base = le_to_native(cpu->mem[zp], cpu->mem[zp + 1]);
+			return base + cpu->regs[Y];
+		}
+
+		default:
+			die("Unknown address mode %x", am);
+			return -1; // unreachable
+	}
+}
+
+void step(cpu_t *cpu)
+{
+	switch (cpu->mem[cpu->pc++])
+	{
+#define INST(mn, am, op) \
+		case op: \
+			execute(cpu, #mn, mn, fetch_addr(cpu, am)); \
+			break;
+
+		INSTRUCTIONS
+
+#undef INST
+
+		default:
+			die("Undefined opcode");
+	}
+}
+
+void dump_inst(cpu_t *cpu, const char *mn, uint16_t addr, uint8_t am)
+{
+	printf("\t%s\t", mn);
+
+	switch (am)
+	{
+		case AM_IMM:
+			printf("#");
+		case AM_REL:
+		case AM_ABS:
+		case AM_ZP:
+			printf("$%x", addr);
+			break;
+
+		case AM_IND:
+			printf("($%x)", addr);
+			break;
+
+		case AM_AX:
+		case AM_ZPX:
+			printf("$%x, X", addr);
+			break;
+
+		case AM_AY:
+		case AM_ZPY:
+			printf("$%x, Y", addr);
+			break;
+
+		case AM_ZIX:
+			printf("($%x, X)", addr);
+			break;
+
+		case AM_ZIY:
+			printf("($%x), Y", addr);
+			break;
+	}
+
+	printf("\n");
+}
+
+void disas_step(cpu_t *cpu)
+{
+	printf("%x", cpu->pc);
+	uint8_t op = cpu->mem[cpu->pc++];
+	switch (op)
+	{
+#define INST(mn, am, op) \
+		case op: \
+			dump_inst(cpu, #mn, fetch_addr(cpu, am), am); \
+			break;
+
+		INSTRUCTIONS
+
+#undef INST
+
+		default:
+			die("Undefined opcode %x", op);
+	}
+}
+
+void disas(cpu_t *cpu)
+{
+	while (cpu->pc < 0xFFFF)
+	{
+		disas_step(cpu);
+	}
+}
diff --git a/cpu.h b/cpu.h
new file mode 100644
index 0000000..bbe6c60
--- /dev/null
+++ b/cpu.h
@@ -0,0 +1,117 @@
+#pragma once
+
+#include <stdint.h>
+
+enum // Registers
+{
+	A, X, Y, SR, SP
+};
+
+enum // Flags
+{
+	CARRY	= 1 << 0,
+	ZERO	= 1 << 1,
+	NO_INT	= 1 << 2,
+	DECIMAL	= 1 << 3,
+	BREAK	= 1 << 4,
+	UNUSED	= 1 << 5,
+	OVERFLW	= 1 << 6,
+	NEGATIV	= 1 << 7,
+};
+
+enum // Address Modes
+{
+	AM_ACC,	// Accumulator implied as operand
+	AM_IMP,	// Implied operand
+	AM_IMM,	// Immediate operand (8 bit)
+	AM_ABS,	// Absolute operand (16 bit)
+	AM_ZP,	// Zero-page operand
+	AM_REL,	// Relative operand (signed 8 bit)
+	AM_IND,	// Absolute indirect operand (16 bit address of 16 bit address)
+	AM_AX,	// Absolute indexed with X
+	AM_AY,	// Absolute indexed with Y
+	AM_ZPX,	// Zero-page indexed with X
+	AM_ZPY,	// Zero-page indexed with Y
+	AM_ZIX,	// Zero-page indexed indirect (zp,x)
+	AM_ZIY,	// Zero-page indirect indexed (zp),y
+};
+
+enum // Opcodes
+{
+	LDA,
+	LDX,
+	LDY,
+	STA,
+	STX,
+	STY,
+	ADC,
+	SBC,
+	INC,
+	INX,
+	INY,
+	DEC,
+	DEX,
+	DEY,
+	ASL,
+	LSR,
+	ROL,
+	ROR,
+	AND,
+	ORA,
+	EOR,
+	CMP,
+	CPX,
+	CPY,
+	BIT,
+	BCC,
+	BCS,
+	BNE,
+	BEQ,
+	BPL,
+	BMI,
+	BVC,
+	BVS,
+	TAX,
+	TXA,
+	TAY,
+	TYA,
+	TSX,
+	TXS,
+	PHA,
+	PLA,
+	PHP,
+	PLP,
+	JMP,
+	JSR,
+	RTS,
+	RTI,
+	CLC,
+	SEC,
+	CLD,
+	SED,
+	CLI,
+	SEI,
+	CLV,
+	BRK,
+	NOP,
+};
+
+// Emulator instance, create with new_cpu()
+typedef struct
+{
+	uint8_t regs[5]; // A, X, Y, SP and SR registers
+	uint16_t pc;
+	uint8_t *mem;
+} cpu_t;
+
+// Argument type, includes both pointer and its value
+typedef struct
+{
+	uint16_t val; // Value at pointer (used by most instructions)
+	uint16_t ptr; // Pointer (used by jumps, etc)
+} arg_t;
+
+cpu_t new_cpu();
+void free_cpu(cpu_t *cpu);
+void die(const char *message);
+void disas(cpu_t *cpu);
diff --git a/csv2h.awk b/csv2h.awk
new file mode 100755
index 0000000..f98ba9e
--- /dev/null
+++ b/csv2h.awk
@@ -0,0 +1,10 @@
+BEGIN {
+	FS = ","
+	print "#pragma once\n"
+	print "// AUTO GENERATED FILE, DO NOT EDIT BY HAND"
+	print "#define INSTRUCTIONS \\"
+}
+
+/0x.+/ {
+	print "	INST(" $2 ",	AM_" $3 ",	" $1" ) \\"
+}
diff --git a/instructions.h b/instructions.h
new file mode 100644
index 0000000..ba3d99d
--- /dev/null
+++ b/instructions.h
@@ -0,0 +1,161 @@
+#pragma once
+
+// AUTO GENERATED FILE, DO NOT EDIT BY HAND
+#define INSTRUCTIONS \
+	INST(ADC,	AM_IMM,	0x69 ) \
+	INST(ADC,	AM_ZP,	0x65 ) \
+	INST(ADC,	AM_ZPX,	0x75 ) \
+	INST(ADC,	AM_ABS,	0x6d ) \
+	INST(ADC,	AM_AX,	0x7d ) \
+	INST(ADC,	AM_AY,	0x79 ) \
+	INST(ADC,	AM_ZIX,	0x61 ) \
+	INST(ADC,	AM_ZIY,	0x71 ) \
+	INST(AND,	AM_IMM,	0x29 ) \
+	INST(AND,	AM_ZP,	0x25 ) \
+	INST(AND,	AM_ZPX,	0x35 ) \
+	INST(AND,	AM_ABS,	0x2d ) \
+	INST(AND,	AM_AX,	0x3d ) \
+	INST(AND,	AM_AY,	0x39 ) \
+	INST(AND,	AM_ZIX,	0x21 ) \
+	INST(AND,	AM_ZIY,	0x31 ) \
+	INST(ASL,	AM_ACC,	0x0a ) \
+	INST(ASL,	AM_ZP,	0x06 ) \
+	INST(ASL,	AM_ZPX,	0x16 ) \
+	INST(ASL,	AM_ABS,	0x0e ) \
+	INST(ASL,	AM_AX,	0x1e ) \
+	INST(BCC,	AM_REL,	0x90 ) \
+	INST(BCS,	AM_REL,	0xB0 ) \
+	INST(BEQ,	AM_REL,	0xF0 ) \
+	INST(BMI,	AM_REL,	0x30 ) \
+	INST(BNE,	AM_REL,	0xD0 ) \
+	INST(BPL,	AM_REL,	0x10 ) \
+	INST(BVC,	AM_REL,	0x50 ) \
+	INST(BVS,	AM_REL,	0x70 ) \
+	INST(BIT,	AM_ZP,	0x24 ) \
+	INST(BIT,	AM_ABS,	0x2c ) \
+	INST(BIT,	AM_IMM,	0x89 ) \
+	INST(BIT,	AM_ZPX,	0x34 ) \
+	INST(BIT,	AM_AX,	0x3c ) \
+	INST(BRK,	AM_IMP,	0x00 ) \
+	INST(CLC,	AM_IMP,	0x18 ) \
+	INST(CLD,	AM_IMP,	0xd8 ) \
+	INST(CLI,	AM_IMP,	0x58 ) \
+	INST(CLV,	AM_IMP,	0xb8 ) \
+	INST(NOP,	AM_IMP,	0xea ) \
+	INST(PHA,	AM_IMP,	0x48 ) \
+	INST(PLA,	AM_IMP,	0x68 ) \
+	INST(PHP,	AM_IMP,	0x08 ) \
+	INST(PLP,	AM_IMP,	0x28 ) \
+	INST(RTI,	AM_IMP,	0x40 ) \
+	INST(RTS,	AM_IMP,	0x60 ) \
+	INST(SEC,	AM_IMP,	0x38 ) \
+	INST(SED,	AM_IMP,	0xf8 ) \
+	INST(SEI,	AM_IMP,	0x78 ) \
+	INST(TAX,	AM_IMP,	0xaa ) \
+	INST(TXA,	AM_IMP,	0x8a ) \
+	INST(TAY,	AM_IMP,	0xa8 ) \
+	INST(TYA,	AM_IMP,	0x98 ) \
+	INST(TSX,	AM_IMP,	0xba ) \
+	INST(TXS,	AM_IMP,	0x9a ) \
+	INST(CMP,	AM_IMM,	0xc9 ) \
+	INST(CMP,	AM_ZP,	0xc5 ) \
+	INST(CMP,	AM_ZPX,	0xd5 ) \
+	INST(CMP,	AM_ABS,	0xcd ) \
+	INST(CMP,	AM_AX,	0xdd ) \
+	INST(CMP,	AM_AY,	0xd9 ) \
+	INST(CMP,	AM_ZIX,	0xc1 ) \
+	INST(CMP,	AM_ZIY,	0xd1 ) \
+	INST(CPX,	AM_IMM,	0xe0 ) \
+	INST(CPX,	AM_ZP,	0xe4 ) \
+	INST(CPX,	AM_ABS,	0xec ) \
+	INST(CPY,	AM_IMM,	0xc0 ) \
+	INST(CPY,	AM_ZP,	0xc4 ) \
+	INST(CPY,	AM_ABS,	0xcc ) \
+	INST(DEC,	AM_ZP,	0xc6 ) \
+	INST(DEC,	AM_ZPX,	0xd6 ) \
+	INST(DEC,	AM_ABS,	0xce ) \
+	INST(DEC,	AM_AX,	0xde ) \
+	INST(DEC,	AM_ACC,	0x3a ) \
+	INST(DEX,	AM_IMP,	0xca ) \
+	INST(DEY,	AM_IMP,	0x88 ) \
+	INST(INX,	AM_IMP,	0xe8 ) \
+	INST(INY,	AM_IMP,	0xc8 ) \
+	INST(EOR,	AM_IMM,	0x49 ) \
+	INST(EOR,	AM_ZP,	0x45 ) \
+	INST(EOR,	AM_ZPX,	0x55 ) \
+	INST(EOR,	AM_ABS,	0x4d ) \
+	INST(EOR,	AM_AX,	0x5d ) \
+	INST(EOR,	AM_AY,	0x59 ) \
+	INST(EOR,	AM_ZIX,	0x41 ) \
+	INST(EOR,	AM_ZIY,	0x51 ) \
+	INST(INC,	AM_ZP,	0xe6 ) \
+	INST(INC,	AM_ZPX,	0xf6 ) \
+	INST(INC,	AM_ABS,	0xee ) \
+	INST(INC,	AM_AX,	0xfe ) \
+	INST(INC,	AM_ACC,	0x1a ) \
+	INST(JMP,	AM_ABS,	0x4c ) \
+	INST(JMP,	AM_IND,	0x6c ) \
+	INST(JMP,	AM_AX,	0x7c ) \
+	INST(JSR,	AM_ABS,	0x20 ) \
+	INST(LDA,	AM_IMM,	0xa9 ) \
+	INST(LDA,	AM_ZP,	0xa5 ) \
+	INST(LDA,	AM_ZPX,	0xb5 ) \
+	INST(LDA,	AM_ABS,	0xad ) \
+	INST(LDA,	AM_AX,	0xbd ) \
+	INST(LDA,	AM_AY,	0xb9 ) \
+	INST(LDA,	AM_ZIX,	0xa1 ) \
+	INST(LDA,	AM_ZIY,	0xb1 ) \
+	INST(LDX,	AM_IMM,	0xa2 ) \
+	INST(LDX,	AM_ZP,	0xa6 ) \
+	INST(LDX,	AM_ZPY,	0xb6 ) \
+	INST(LDX,	AM_ABS,	0xae ) \
+	INST(LDX,	AM_AY,	0xbe ) \
+	INST(LDY,	AM_IMM,	0xa0 ) \
+	INST(LDY,	AM_ZP,	0xa4 ) \
+	INST(LDY,	AM_ZPX,	0xb4 ) \
+	INST(LDY,	AM_ABS,	0xac ) \
+	INST(LDY,	AM_AX,	0xbc ) \
+	INST(LSR,	AM_ACC,	0x4a ) \
+	INST(LSR,	AM_ZP,	0x46 ) \
+	INST(LSR,	AM_ZPX,	0x56 ) \
+	INST(LSR,	AM_ABS,	0x4e ) \
+	INST(LSR,	AM_AX,	0x5e ) \
+	INST(ORA,	AM_IMM,	0x09 ) \
+	INST(ORA,	AM_ZP,	0x05 ) \
+	INST(ORA,	AM_ZPX,	0x15 ) \
+	INST(ORA,	AM_ABS,	0x0d ) \
+	INST(ORA,	AM_AX,	0x1d ) \
+	INST(ORA,	AM_AY,	0x19 ) \
+	INST(ORA,	AM_ZIX,	0x01 ) \
+	INST(ORA,	AM_ZIY,	0x11 ) \
+	INST(ROL,	AM_ACC,	0x2a ) \
+	INST(ROL,	AM_ZP,	0x26 ) \
+	INST(ROL,	AM_ZPX,	0x36 ) \
+	INST(ROL,	AM_ABS,	0x2e ) \
+	INST(ROL,	AM_AX,	0x3e ) \
+	INST(ROR,	AM_ACC,	0x6a ) \
+	INST(ROR,	AM_ZP,	0x66 ) \
+	INST(ROR,	AM_ZPX,	0x76 ) \
+	INST(ROR,	AM_ABS,	0x7e ) \
+	INST(ROR,	AM_AX,	0x6e ) \
+	INST(SBC,	AM_IMM,	0xe9 ) \
+	INST(SBC,	AM_ZP,	0xe5 ) \
+	INST(SBC,	AM_ZPX,	0xf5 ) \
+	INST(SBC,	AM_ABS,	0xed ) \
+	INST(SBC,	AM_AX,	0xfd ) \
+	INST(SBC,	AM_AY,	0xf9 ) \
+	INST(SBC,	AM_ZIX,	0xe1 ) \
+	INST(SBC,	AM_ZIY,	0xf1 ) \
+	INST(STA,	AM_ZP,	0x85 ) \
+	INST(STA,	AM_ZPX,	0x95 ) \
+	INST(STA,	AM_ABS,	0x8d ) \
+	INST(STA,	AM_AX,	0x9d ) \
+	INST(STA,	AM_AY,	0x99 ) \
+	INST(STA,	AM_ZIX,	0x81 ) \
+	INST(STA,	AM_ZIY,	0x91 ) \
+	INST(STX,	AM_ZP,	0x86 ) \
+	INST(STX,	AM_ZPY,	0x96 ) \
+	INST(STX,	AM_ABS,	0x8e ) \
+	INST(STY,	AM_ZP,	0x84 ) \
+	INST(STY,	AM_ZPX,	0x94 ) \
+	INST(STY,	AM_ABS,	0x8c ) \
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..8e6ec52
--- /dev/null
+++ b/main.c
@@ -0,0 +1,50 @@
+#include "cpu.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int main(int argc, char **argv)
+{
+	printf("6502 Emulator\n");
+
+	uint8_t disflag = 0,
+		runflag = 0,
+		helpflag = 0;
+
+	char c;
+
+	while ((c = getopt(argc, argv, "drh")) != -1)
+	{
+		switch (c)
+		{
+			case 'd':
+				disflag = 1;
+				break;
+			case 'r':
+				runflag = 1;
+				break;
+			case 'h':
+			case '?':
+				helpflag = 1;
+				break;
+		}
+	}
+
+	if (helpflag)
+	{
+		printf("-r to run, -d to disassemble");
+		return 0;
+	}
+
+	cpu_t cpu = new_cpu();
+	fread(cpu.mem, 0xFFFF, 1, stdin);
+
+	if (disflag)
+	{
+		disas(&cpu);
+	}
+
+	free_cpu(&cpu);
+}
diff --git a/test.dat b/test.dat
new file mode 100644
index 0000000..0d6ba15
--- /dev/null
+++ b/test.dat
Binary files differ