Add step 1 of assembler
diff --git a/.gitignore b/.gitignore
index 798ef86..9f27f48 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,5 @@
 .~*
 build
+#*#
+*~
+~*
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
index 6dc59b1..747fb4d 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,6 @@
 [submodule "nuklear"]
 	path = nuklear
 	url = https://github.com/Immediate-Mode-UI/Nuklear.git
+[submodule "as/libcollect"]
+	path = as/libcollect
+	url = https://github.com/swissChili/libcollect
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cfb97fe..5a27b55 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,9 +3,12 @@
 project(6502 VERSION 0.1.0 LANGUAGES C)
 
 option(GEN_INSTRUCTIONS_HEADER ON)
+option(BUILD_ASSEMBLER ON)
 
 include_directories(nuklear)
 
+subdirs(as)
+
 if (${GEN_INSTRUCTIONS_HEADER})
 	add_custom_command(
 		OUTPUT instructions.h
diff --git a/README.md b/README.md
index 924f97d..2d9e8ed 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# 6502 Toolchain
+# [6502 Toolchain](https://6502.swisschili.sh)
 
-[![Screenshot](screenshot.png)](colors.webm)
+[![Screenshot](screenshot.png)](https://6502.swisschili.sh)
 
 This project aims to create a portable toolchain for developing,
 testing and debugging programs for the 6502 processor. An assembler
diff --git a/as/CMakeLists.txt b/as/CMakeLists.txt
new file mode 100644
index 0000000..e2062c2
--- /dev/null
+++ b/as/CMakeLists.txt
@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 3.0)
+
+project(6502 VERSION 0.1.0 LANGUAGES C)
+
+subdirs(libcollect)
+include_directories(libcollect/include)
+
+add_executable(6502-as main.c as.h as.c)
+target_link_libraries(6502-as collect)
diff --git a/as/as.c b/as/as.c
new file mode 100644
index 0000000..61ad6bd
--- /dev/null
+++ b/as/as.c
@@ -0,0 +1,439 @@
+#include "as.h"
+#include "../cpu.h"
+#include "../instructions.h"
+#include "../mnemonics.h"
+
+#include <collect/map.h>
+#include <collect/vector.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdbool.h>
+
+enum
+{
+	ARG_16,						/* Absolute 16 bit argument */
+	ARG_8,						/* Absolute 8 bit argument */
+	ARG_8REL,					/* Relative 8 bit argument */
+	ARG_REL,					/* Relative label */
+	ARG_ABS,					/* Absolute label */
+	ARG_IMP,					/* Implied argument */
+};
+
+typedef struct
+{
+	uint8_t opcode;
+	uint8_t arg_type;
+	union
+	{
+		char label[32];
+		uint16_t long_arg;
+		uint8_t byte_arg;
+		int8_t rel_arg;
+	};
+} inst_t;
+
+void print_inst(inst_t *arg)
+{
+	char *arg_types =
+		"16  8   8RELREL ABS IMP ";
+	
+	printf("\033[33mInst: %.4s $%x ", arg_types + arg->arg_type * 4, arg->opcode);
+
+	switch (arg->arg_type)
+	{
+	case ARG_16:
+		printf("%x", arg->long_arg);
+		break;
+	case ARG_8:
+		printf("%x", arg->byte_arg);
+		break;
+	case ARG_8REL:
+		printf("%d", arg->rel_arg);
+		break;
+	case ARG_REL:
+	case ARG_ABS:
+		printf("%s", arg->label);
+		break;
+	}
+
+	printf("\033[0m\n");
+}
+
+bool is_ident(char c)
+{
+	return c && (isalpha(c) || isdigit(c));
+}
+
+uint32_t skip_ws(char **code)
+{
+	uint32_t len = 0;
+
+	for (; isspace(**code); (*code)++, len++)
+	{}
+
+	return len;
+}
+
+uint32_t skip_to_eol(char **code)
+{
+	uint32_t len = 0;
+
+	for (; **code && **code != '\n'; (*code)++, len++)
+	{}
+
+	if (**code)
+		(*code)++;
+
+	return len;
+}
+
+char *parse_label_name(char **code)
+{
+	char *start = *code;
+	for (; is_ident(**code); (*code)++)
+	{}
+
+	if (start == *code)
+		return false;
+
+	**code = 0;
+	return start;
+}
+
+char *parse_label(char **code)
+{
+	char *start = *code;
+
+	for (; is_ident(**code); (*code)++)
+	{}
+
+	skip_ws(code);
+
+	if (**code == ':')
+	{
+		**code = 0;
+		(*code)++;
+		return start;
+	}
+
+	*code = start;
+
+	return NULL;
+}
+
+char *parse_inst(char **code)
+{
+	char *start = *code;
+
+	for (; isalpha(**code); (*code)++)
+	{}
+
+	**code = 0;
+
+	if (start == *code)
+		return NULL;
+
+	(*code)++;
+	return start;
+}
+
+bool is_eol(char c)
+{
+	return c == ';' ||
+		c == '\n' ||
+		c == '\r' ||
+		c == '\0';
+}
+
+bool skip(char **code, const char *p)
+{
+	for (; *p && *p == **code; p++, (*code)++)
+	{}
+
+	if (!*p)
+		return true;
+	return false;
+}
+
+bool parse_num(char **code, uint64_t *num)
+{
+	char *start = *code;
+	int base = 10;
+	if (**code == '$')
+	{
+		base = 16;
+		(*code)++;
+	}
+	
+	skip_ws(code);
+
+	char *endptr = *code;
+	int64_t val = strtol(*code, &endptr, base);
+
+	if (*code == endptr)
+	{
+		*code = start;
+		return false;
+	}
+	*num = val;
+	*code = endptr;
+	return true;
+}
+
+bool parse_num_max(char **code, uint64_t *num, uint64_t max)
+{
+	uint64_t n;
+	if (parse_num(code, &n))
+	{
+		if (n > max)
+			return false;
+
+		*num = n;
+		return true;
+	}
+	else return false;
+}
+
+bool parse_u8(char **code, uint8_t *num)
+{
+	uint64_t n;
+	if (!parse_num_max(code, &n, 0xFF))
+		return false;
+
+	*num = n & 0xFF;
+	return true;
+}
+
+bool parse_u16(char **code, uint16_t *num)
+{
+	uint64_t n;
+	if (!parse_num_max(code, &n, 0xFFFF))
+		return false;
+
+	*num = n & 0xFFFF;
+	return true;
+}
+
+bool ws_end(char **code)
+{
+	skip_ws(code);
+	return is_eol(**code);
+}
+
+bool parse_arg(char *code, int am, inst_t *inst)
+{
+	skip_ws(&code);
+
+	uint16_t num;
+	uint8_t num8;
+	char *lbl;
+	
+	switch (am)
+	{
+	case AM_ACC:
+	case AM_IMP:
+		printf("Trying AM_IMP on '%.8s'\n", code);
+		skip_ws(&code);
+		if (is_eol(*code))
+		{
+			inst->arg_type = ARG_IMP;
+			return ws_end(&code);
+		}
+		break;
+
+	case AM_IMM:
+		printf("Trying AM_IMM on '%.8s'\n", code);
+		if (!skip(&code, "#"))
+			return false;
+		skip_ws(&code);
+	case AM_ZP:
+		if (parse_u8(&code, &num8))
+		{
+			inst->arg_type = ARG_8;
+			inst->byte_arg = num8;
+
+			return ws_end(&code);
+		}
+		break;
+
+	case AM_ABS:
+		if (parse_u16(&code, &num))
+		{
+			inst->arg_type = ARG_16;
+			inst->long_arg = num;
+			return true;
+		}
+		else if ((lbl = parse_label_name(&code)))
+		{
+			inst->arg_type = ARG_ABS;
+			strncpy(inst->label, lbl, 32);
+			return true;
+		}
+		break;
+
+	case AM_REL:
+		if (parse_u8(&code, &num8))
+		{
+			inst->arg_type = ARG_8REL;
+			inst->rel_arg = num;
+			return ws_end(&code);
+		}
+		else if ((lbl = parse_label_name(&code)))
+		{
+			inst->arg_type = ARG_REL;
+			strncpy(inst->label, lbl, 32);
+			return ws_end(&code);
+		}
+		break;
+
+	case AM_IND:
+		if (!skip(&code,"("))
+			return false;
+		
+		if (!parse_u16(&code, &num))
+			return false;
+
+		if (!skip(&code, ")"))
+			return false;
+
+		inst->arg_type = ARG_16;
+		inst->long_arg = num;
+		return true;
+
+	case AM_AX:
+	case AM_ZPX:
+	case AM_AY:
+	case AM_ZPY:
+		if (am == AM_AX || am == AM_AY)
+		{
+			if (!parse_u16(&code, &num))
+				return false;
+			inst->arg_type = ARG_16;
+			inst->long_arg = num;
+		}
+		else
+		{
+			if (!parse_u8(&code, &num8))
+				return false;
+			inst->arg_type = ARG_8;
+			inst->byte_arg = num8;
+		}
+		if (!skip(&code, ","))
+			return false;
+
+		skip_ws(&code);
+
+		if (tolower(*code) != (am == AM_AY || am == AM_ZPY ? 'y' : 'x'))
+			return false;
+
+		return ws_end(&code);
+
+	case AM_ZIX:
+		if (!skip(&code, "("))
+			break;
+		skip_ws(&code);
+		if (!parse_u8(&code, &num8))
+			break;
+		skip_ws(&code);
+		if (!skip(&code, ","))
+			break;
+		skip_ws(&code);
+		if (tolower(*code) != 'x')
+			return false;
+		skip_ws(&code);
+
+		if (!skip(&code, ")"))
+			break;
+
+		inst->arg_type = ARG_8;
+		inst->byte_arg = num8;
+		return ws_end(&code);
+
+	case AM_ZIY:
+		if (!skip(&code, "("))
+			break;
+		skip_ws(&code);
+		if (!parse_u8(&code, &num8))
+			break;
+		skip_ws(&code);
+		if (!skip(&code, ")"))
+			break;
+		skip_ws(&code);
+		if (!skip(&code, ","))
+			break;
+		skip_ws(&code);
+		if (tolower(*code) != 'x')
+			break;
+
+		inst->arg_type = ARG_8;
+		inst->byte_arg = num8;
+		return ws_end(&code);
+	}
+	return false;
+}
+
+uint32_t assemble(char *code, FILE *out)
+{
+	uintptr_t num_insts = 0;
+	uint32_t line_no = 1;
+	map *labels = new_map();
+	vector *insts = new_vector();
+	char *line;
+
+	printf("Assembling File\n");
+	printf("%s\n", code);
+
+	line = strtok(code, "\r\n");
+	
+	while (line)
+	{
+		skip_ws(&line);
+
+		printf("\033[36m%.9s\033[0m\n", line);
+		
+		char *label = parse_label(&line),
+			*mn = parse_inst(&line);
+		int32_t mnemonic = -1;
+
+		if (label)
+		{
+			map_set(labels, label, (void *)num_insts);
+			printf("Set label %s at %lu\n", label, num_insts);
+		}
+
+		if (mn)
+		{
+#define MN(a) if (!strcasecmp(mn, #a)) \
+				mnemonic = a;		   \
+			else
+
+			MNEMONICS;
+#undef MN
+
+			printf("Got instruction %s %d\n", mn, mnemonic);
+
+			inst_t arg;
+			// printf("Parsing '%s'\n", line);
+#define INST(_mn, am, op, len) \
+			if (mnemonic == _mn && parse_arg(line, am, &arg)) \
+			{												  \
+				arg.opcode = op;							  \
+				print_inst(&arg);							  \
+			}												  \
+			else
+
+			INSTRUCTIONS
+			{
+				printf("\033[31mCould not be parsed: %s '%s'\033[0m\n", mn, line);
+			}
+#undef INST
+		}
+
+		num_insts++;
+		line = strtok(NULL, "\r\n");
+	}
+
+	free_map(labels);
+
+	return num_insts;
+}
diff --git a/as/as.h b/as/as.h
new file mode 100644
index 0000000..4e167dd
--- /dev/null
+++ b/as/as.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <stdio.h>
+#include <stdint.h>
+
+/*
+ * @returns NULL on failure, printing info to stderr
+ */
+uint32_t assemble(char *code, FILE *out);
diff --git a/as/libcollect b/as/libcollect
new file mode 160000
index 0000000..e9ee522
--- /dev/null
+++ b/as/libcollect
@@ -0,0 +1 @@
+Subproject commit e9ee5221d307378150d2119939025b8709da178a
diff --git a/as/main.c b/as/main.c
new file mode 100644
index 0000000..cf966f8
--- /dev/null
+++ b/as/main.c
@@ -0,0 +1,44 @@
+#include "as.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <bits/getopt_core.h>
+#include <unistd.h>
+
+int main(int argc, char **argv)
+{
+	char c;
+	FILE *in = stdin,
+		*out = stdout;
+
+	while ((c = getopt(argc, argv, "i:o:")) != -1)
+	{
+		switch (c)
+		{
+		case 'i':
+			in = fopen(optarg, "r");
+			break;
+		case 'o':
+			out = fopen(optarg, "w");
+			break;
+		case 'h':
+		case '?':
+			printf("6502 assembler\n"
+				   "Usage:\n"
+				   "    -i <input> set input file (default stdin)\n"
+				   "    -o <output> set output file (default stdout)\n");
+		}
+	}
+
+	fseek(in, 0, SEEK_END);
+	ssize_t len = ftell(in);
+	fseek(in, 0, SEEK_SET);
+
+	char *text = malloc(len + 1);
+	fread(text, len, 1, in);
+	text[len] = 0;
+
+	uint32_t built = assemble(text, out);
+
+	free(text);
+}
diff --git a/as/test/test.s b/as/test/test.s
new file mode 100644
index 0000000..edca24a
--- /dev/null
+++ b/as/test/test.s
@@ -0,0 +1,5 @@
+start:
+	lda #$32 					; Store $32 in a
+	tax							; Transfer a to x
+	stx $200					; Store x at $200
+	jmp ($FFAA)					; Jump to the address at $FFAA
diff --git a/cpu.c b/cpu.c
index 12f9af7..ce489cc 100644
--- a/cpu.c
+++ b/cpu.c
@@ -11,9 +11,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#define die(m, ...) \
-	printf("\033[31m" m "\033[0m\n", ##__VA_ARGS__); \
-	exit(1);
 
 #define warn(m, ...) \
 	printf("\033[33m" m "\033[0m\n", ##__VA_ARGS__);
@@ -534,7 +531,7 @@
 	uint8_t op = cpu->mem[cpu->pc++];
 	switch (op)
 	{
-#define INST(mn, am, op) \
+#define INST(mn, am, op, len)						\
 		case op: \
 			execute(cpu, #mn, mn, fetch_addr(cpu, am, 0, &cpu->pc), am); \
 			break;
@@ -599,7 +596,7 @@
 	uint8_t op = cpu->mem[(*pc)++];
 	switch (op)
 	{
-#define INST(mn, am, op) \
+#define INST(mn, am, op, len)						\
 		case op: \
 			end += dump_inst(cpu, end, #mn, \
 				fetch_addr(cpu, am, FETCH_NO_INDIRECTION, pc).ptr, am); \
diff --git a/cpu.h b/cpu.h
index 130ef05..05acebc 100644
--- a/cpu.h
+++ b/cpu.h
@@ -1,5 +1,7 @@
 #pragma once
 
+#include "mnemonics.h"
+
 #include <stdint.h>
 #include <stdbool.h>
 #include <mqueue.h>
@@ -48,62 +50,9 @@
 
 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,
+#define MN(name) name,
+	MNEMONICS
+#undef MN
 };
 
 enum // Fetch flags
diff --git a/csv2h.awk b/csv2h.awk
index f98ba9e..dfebaec 100755
--- a/csv2h.awk
+++ b/csv2h.awk
@@ -6,5 +6,5 @@
 }
 
 /0x.+/ {
-	print "	INST(" $2 ",	AM_" $3 ",	" $1" ) \\"
+	print "	INST(" $2 ", AM_" $3 ", " $1", " $4 ") \\"
 }
diff --git a/instructions.h b/instructions.h
index ba3d99d..082016f 100644
--- a/instructions.h
+++ b/instructions.h
@@ -2,160 +2,160 @@
 
 // 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 ) \
+	INST(ADC, AM_IMM, 0x69, 2) \
+	INST(ADC, AM_ZP, 0x65, 2) \
+	INST(ADC, AM_ZPX, 0x75, 2) \
+	INST(ADC, AM_ABS, 0x6d, 3) \
+	INST(ADC, AM_AX, 0x7d, 3) \
+	INST(ADC, AM_AY, 0x79, 3) \
+	INST(ADC, AM_ZIX, 0x61, 2) \
+	INST(ADC, AM_ZIY, 0x71, 2) \
+	INST(AND, AM_IMM, 0x29, 2) \
+	INST(AND, AM_ZP, 0x25, 2) \
+	INST(AND, AM_ZPX, 0x35, 2) \
+	INST(AND, AM_ABS, 0x2d, 3) \
+	INST(AND, AM_AX, 0x3d, 3) \
+	INST(AND, AM_AY, 0x39, 3) \
+	INST(AND, AM_ZIX, 0x21, 2) \
+	INST(AND, AM_ZIY, 0x31, 2) \
+	INST(ASL, AM_ACC, 0x0a, 1) \
+	INST(ASL, AM_ZP, 0x06, 2) \
+	INST(ASL, AM_ZPX, 0x16, 2) \
+	INST(ASL, AM_ABS, 0x0e, 3) \
+	INST(ASL, AM_AX, 0x1e, 3) \
+	INST(BCC, AM_REL, 0x90, 2) \
+	INST(BCS, AM_REL, 0xB0, 2) \
+	INST(BEQ, AM_REL, 0xF0, 2) \
+	INST(BMI, AM_REL, 0x30, 2) \
+	INST(BNE, AM_REL, 0xD0, 2) \
+	INST(BPL, AM_REL, 0x10, 2) \
+	INST(BVC, AM_REL, 0x50, 2) \
+	INST(BVS, AM_REL, 0x70, 2) \
+	INST(BIT, AM_ZP, 0x24, 2) \
+	INST(BIT, AM_ABS, 0x2c, 3) \
+	INST(BIT, AM_IMM, 0x89, 2) \
+	INST(BIT, AM_ZPX, 0x34, 2) \
+	INST(BIT, AM_AX, 0x3c, 3) \
+	INST(BRK, AM_IMP, 0x00, 1) \
+	INST(CLC, AM_IMP, 0x18, 1) \
+	INST(CLD, AM_IMP, 0xd8, 1) \
+	INST(CLI, AM_IMP, 0x58, 1) \
+	INST(CLV, AM_IMP, 0xb8, 1) \
+	INST(NOP, AM_IMP, 0xea, 1) \
+	INST(PHA, AM_IMP, 0x48, 1) \
+	INST(PLA, AM_IMP, 0x68, 1) \
+	INST(PHP, AM_IMP, 0x08, 1) \
+	INST(PLP, AM_IMP, 0x28, 1) \
+	INST(RTI, AM_IMP, 0x40, 1) \
+	INST(RTS, AM_IMP, 0x60, 1) \
+	INST(SEC, AM_IMP, 0x38, 1) \
+	INST(SED, AM_IMP, 0xf8, 1) \
+	INST(SEI, AM_IMP, 0x78, 1) \
+	INST(TAX, AM_IMP, 0xaa, 1) \
+	INST(TXA, AM_IMP, 0x8a, 1) \
+	INST(TAY, AM_IMP, 0xa8, 1) \
+	INST(TYA, AM_IMP, 0x98, 1) \
+	INST(TSX, AM_IMP, 0xba, 1) \
+	INST(TXS, AM_IMP, 0x9a, 1) \
+	INST(CMP, AM_IMM, 0xc9, 2) \
+	INST(CMP, AM_ZP, 0xc5, 2) \
+	INST(CMP, AM_ZPX, 0xd5, 2) \
+	INST(CMP, AM_ABS, 0xcd, 3) \
+	INST(CMP, AM_AX, 0xdd, 3) \
+	INST(CMP, AM_AY, 0xd9, 3) \
+	INST(CMP, AM_ZIX, 0xc1, 2) \
+	INST(CMP, AM_ZIY, 0xd1, 2) \
+	INST(CPX, AM_IMM, 0xe0, 2) \
+	INST(CPX, AM_ZP, 0xe4, 2) \
+	INST(CPX, AM_ABS, 0xec, 3) \
+	INST(CPY, AM_IMM, 0xc0, 2) \
+	INST(CPY, AM_ZP, 0xc4, 2) \
+	INST(CPY, AM_ABS, 0xcc, 3) \
+	INST(DEC, AM_ZP, 0xc6, 2) \
+	INST(DEC, AM_ZPX, 0xd6, 2) \
+	INST(DEC, AM_ABS, 0xce, 3) \
+	INST(DEC, AM_AX, 0xde, 3) \
+	INST(DEC, AM_ACC, 0x3a, 1) \
+	INST(DEX, AM_IMP, 0xca, 1) \
+	INST(DEY, AM_IMP, 0x88, 1) \
+	INST(INX, AM_IMP, 0xe8, 1) \
+	INST(INY, AM_IMP, 0xc8, 1) \
+	INST(EOR, AM_IMM, 0x49, 2) \
+	INST(EOR, AM_ZP, 0x45, 2) \
+	INST(EOR, AM_ZPX, 0x55, 2) \
+	INST(EOR, AM_ABS, 0x4d, 3) \
+	INST(EOR, AM_AX, 0x5d, 3) \
+	INST(EOR, AM_AY, 0x59, 3) \
+	INST(EOR, AM_ZIX, 0x41, 2) \
+	INST(EOR, AM_ZIY, 0x51, 2) \
+	INST(INC, AM_ZP, 0xe6, 2) \
+	INST(INC, AM_ZPX, 0xf6, 2) \
+	INST(INC, AM_ABS, 0xee, 3) \
+	INST(INC, AM_AX, 0xfe, 3) \
+	INST(INC, AM_ACC, 0x1a, 1) \
+	INST(JMP, AM_ABS, 0x4c, 3) \
+	INST(JMP, AM_IND, 0x6c, 3) \
+	INST(JMP, AM_AX, 0x7c, 3) \
+	INST(JSR, AM_ABS, 0x20, 3) \
+	INST(LDA, AM_IMM, 0xa9, 2) \
+	INST(LDA, AM_ZP, 0xa5, 2) \
+	INST(LDA, AM_ZPX, 0xb5, 2) \
+	INST(LDA, AM_ABS, 0xad, 3) \
+	INST(LDA, AM_AX, 0xbd, 3) \
+	INST(LDA, AM_AY, 0xb9, 3) \
+	INST(LDA, AM_ZIX, 0xa1, 2) \
+	INST(LDA, AM_ZIY, 0xb1, 2) \
+	INST(LDX, AM_IMM, 0xa2, 2) \
+	INST(LDX, AM_ZP, 0xa6, 2) \
+	INST(LDX, AM_ZPY, 0xb6, 2) \
+	INST(LDX, AM_ABS, 0xae, 3) \
+	INST(LDX, AM_AY, 0xbe, 3) \
+	INST(LDY, AM_IMM, 0xa0, 2) \
+	INST(LDY, AM_ZP, 0xa4, 2) \
+	INST(LDY, AM_ZPX, 0xb4, 2) \
+	INST(LDY, AM_ABS, 0xac, 3) \
+	INST(LDY, AM_AX, 0xbc, 3) \
+	INST(LSR, AM_ACC, 0x4a, 1) \
+	INST(LSR, AM_ZP, 0x46, 2) \
+	INST(LSR, AM_ZPX, 0x56, 2) \
+	INST(LSR, AM_ABS, 0x4e, 3) \
+	INST(LSR, AM_AX, 0x5e, 3) \
+	INST(ORA, AM_IMM, 0x09, 2) \
+	INST(ORA, AM_ZP, 0x05, 2) \
+	INST(ORA, AM_ZPX, 0x15, 2) \
+	INST(ORA, AM_ABS, 0x0d, 3) \
+	INST(ORA, AM_AX, 0x1d, 3) \
+	INST(ORA, AM_AY, 0x19, 3) \
+	INST(ORA, AM_ZIX, 0x01, 2) \
+	INST(ORA, AM_ZIY, 0x11, 2) \
+	INST(ROL, AM_ACC, 0x2a, 1) \
+	INST(ROL, AM_ZP, 0x26, 2) \
+	INST(ROL, AM_ZPX, 0x36, 2) \
+	INST(ROL, AM_ABS, 0x2e, 3) \
+	INST(ROL, AM_AX, 0x3e, 3) \
+	INST(ROR, AM_ACC, 0x6a, 1) \
+	INST(ROR, AM_ZP, 0x66, 2) \
+	INST(ROR, AM_ZPX, 0x76, 2) \
+	INST(ROR, AM_ABS, 0x7e, 3) \
+	INST(ROR, AM_AX, 0x6e, 3) \
+	INST(SBC, AM_IMM, 0xe9, 2) \
+	INST(SBC, AM_ZP, 0xe5, 2) \
+	INST(SBC, AM_ZPX, 0xf5, 2) \
+	INST(SBC, AM_ABS, 0xed, 3) \
+	INST(SBC, AM_AX, 0xfd, 3) \
+	INST(SBC, AM_AY, 0xf9, 3) \
+	INST(SBC, AM_ZIX, 0xe1, 2) \
+	INST(SBC, AM_ZIY, 0xf1, 2) \
+	INST(STA, AM_ZP, 0x85, 2) \
+	INST(STA, AM_ZPX, 0x95, 2) \
+	INST(STA, AM_ABS, 0x8d, 3) \
+	INST(STA, AM_AX, 0x9d, 3) \
+	INST(STA, AM_AY, 0x99, 3) \
+	INST(STA, AM_ZIX, 0x81, 2) \
+	INST(STA, AM_ZIY, 0x91, 2) \
+	INST(STX, AM_ZP, 0x86, 2) \
+	INST(STX, AM_ZPY, 0x96, 2) \
+	INST(STX, AM_ABS, 0x8e, 3) \
+	INST(STY, AM_ZP, 0x84, 2) \
+	INST(STY, AM_ZPX, 0x94, 2) \
+	INST(STY, AM_ABS, 0x8c, 3) \
diff --git a/mnemonics.h b/mnemonics.h
new file mode 100644
index 0000000..e9d6dfe
--- /dev/null
+++ b/mnemonics.h
@@ -0,0 +1,62 @@
+
+#pragma once
+
+// File not auto generated (unfortunately), needs to be kept up to date manually
+
+#define MNEMONICS								\
+	MN(LDA)										\
+		MN(LDX)									\
+		MN(LDY)									\
+		MN(STA)									\
+		MN(STX)									\
+		MN(STY)									\
+		MN(ADC)									\
+		MN(SBC)									\
+		MN(INC)									\
+		MN(INX)									\
+		MN(INY)									\
+		MN(DEC)									\
+		MN(DEX)									\
+		MN(DEY)									\
+		MN(ASL)									\
+		MN(LSR)									\
+		MN(ROL)									\
+		MN(ROR)									\
+		MN(AND)									\
+		MN(ORA)									\
+		MN(EOR)									\
+		MN(CMP)									\
+		MN(CPX)									\
+		MN(CPY)									\
+		MN(BIT)									\
+		MN(BCC)									\
+		MN(BCS)									\
+		MN(BNE)									\
+		MN(BEQ)									\
+		MN(BPL)									\
+		MN(BMI)									\
+		MN(BVC)									\
+		MN(BVS)									\
+		MN(TAX)									\
+		MN(TXA)									\
+		MN(TAY)									\
+		MN(TYA)									\
+		MN(TSX)									\
+		MN(TXS)									\
+		MN(PHA)									\
+		MN(PLA)									\
+		MN(PHP)									\
+		MN(PLP)									\
+		MN(JMP)									\
+		MN(JSR)									\
+		MN(RTS)									\
+		MN(RTI)									\
+		MN(CLC)									\
+		MN(SEC)									\
+		MN(CLD)									\
+		MN(SED)									\
+		MN(CLI)									\
+		MN(SEI)									\
+		MN(CLV)									\
+		MN(BRK)									\
+		MN(NOP)