#!/usr/bin/env python

import string
items = map(lambda x: x.strip().split("\t")[1:], open("TableNicer", "r").readlines())
items = items[1:]
opcodes = {}

def opsym(opcode):
	# almost only even columns can be NOP. Columns 6,8,E have no NOPs.
	lo = opcode&0xF
	if lo == 0: # branches, NOP, LDY, CPY, CPX
		return ["BRK", "BPL", "JSR", "BMI", "RTI", "BVC", "RTS", "BVS", "NOP", "BCC", "LDY", "BCS", "CPY", "BNE", "CPX", "BEQ"][opcode>>4]
	elif lo in [2, 3, 7, 0xB, 0xF]: # invalid, but just make stuff up in order to match docs
		if opcode == 0xA2:
			return "LDX"
		elif opcode in [0x82, 0xC2, 0xE2]:
			return "NOP"
		elif opcode == 0xEB:
			return "SBC"
		else:
			return "invalid"
	if opcode < 0x80: # 
		if opcode == 0x4C:
			return "JMP" # abs 3
		elif opcode == 0x6C:
			return "JMP" # ind 5
		if lo == 8: # flags
			return ["PHP", "CLC", "PLP", "SEC", "PHA", "CLI", "PLA", "SEI"][opcode>>4]
		sub1 = opcode&3
		if sub1 == 0: # various
			if opcode == 0x24:
				return "BIT" # zp 3
			elif opcode == 0x2C:
				return "BIT" # abs 4
			return "NOP"
		elif sub1 == 1:
			return ["ORA", "AND", "EOR", "ADC"][opcode>>5]
		elif sub1 == 2:
			if opcode in [0x1A, 0x3A, 0x5A, 0x7A]:
				return "NOP" # 2, instable
			return ["ASL", "ROL", "LSR", "ROR"][opcode>>5]
	elif opcode >= 0x80 and opcode < 0xC0: # load/store
		if opcode == 0x89:
			return "NOP" # NOP imm 2, invalid
		elif opcode == 0x88:
			return "DEY" # 2
		elif opcode >= 0xA0: # load
			if opcode == 0xA8: # special names
				return "TAY"
			elif opcode == 0xAA: # special names
				return "TAX"
			elif opcode == 0xBA: # special names
				return "TSX"
			elif opcode == 0xC8:
				return "INY" # 2
			elif opcode == 0xB8:
				return "CLV" # 2
			return "LD%s" % (["Y", "A", "X", "X"][opcode&3], )
		else: # store
			if opcode == 0x8A: # special names
				return "TXA"
			elif opcode == 0x98: # special names
				return "TYA"
			elif opcode == 0x9A: # special names
				return "TXS"
			elif opcode in [0x9C, 0x9E]:
				return "invalid"
			return "ST%s" % (["Y", "A", "X", "X"][opcode&3], )
	elif opcode >= 0xC0: # compare, subtract
		if opcode in [0xC2, 0xE2]:
			return "NOP" # imm 2
		elif opcode == 0xCA:
			return "DEX" # 2
		elif opcode in [0xD4, 0xDA, 0xDC, 0xF4, 0xFA, 0xFC]:
			return "NOP" # invalid
		elif opcode == 0xEB:
			return "SBC" # invalid
		elif lo in [6,0xE]: # inc, dec, technically also for 7 and 0xF, but it's broken in the real hardware.
			return ["DEC", "DEC", "INC", "INC"][(opcode>>4) - 0xC]
		elif lo == 8: # INY, more flags, INX
			return ["INY", "CLD", "INX", "SED"][(opcode >>4) - 0xC]
		sub1 = opcode&3
		if opcode >= 0xE0 and sub1 == 1:
			return "SBC"
		else:
			if opcode >= 0xE0:
				if opcode == 0xEA:
					return "NOP" # 2
				return "CPX"
			elif opcode in [0xC0, 0xC4, 0xCC]:
				return "CPY"
			else:
				return "CMP"
	return "???"
def addressingMode(opcode):
	"""
	# immediate
	a abs
	ZX zero page X
	ZY zero page Y
	aX abs X
	aY abs Y
	IX indirect zeropage X
	IY indirect zeropage Y
	r  relative
	None  implicit
	"""
	lo = opcode&0xF
	if lo == 0:
		if (opcode>>4)&1 == 1:
			return "r"
		if opcode == 0x20:
			return "V" # JSR
		elif opcode in [0x80, 0xA0, 0xC0, 0xE0]:
			return "v"
		else:
			return None
	elif lo == 2:
		return "v"
	elif lo in [1,3]: # indirect
		return ["IX", "IY"][(opcode>>4)&1]
	elif lo >= 4 and lo < 8: # zero page direct
		if opcode >= 0x80 and opcode < 0xC0:
			if lo == 6 or lo == 7:
				return ["Z", "ZY"][(opcode>>4)&1]
		return ["Z", "ZX"][(opcode>>4)&1]
	elif lo == 8 or lo == 0xA: # flags
		return None
	elif lo == 9 or lo == 0xB: # flags
		return ["v", "aY"][(opcode>>4)&1]
	else: # big
		if opcode == 0x4C: # JMP##
			return "V"
		if opcode >= 0x80 and opcode < 0xC0:
			if lo >= 0xE:
				return ["a", "aY"][(opcode>>4)&1]
		return ["a", "aX"][(opcode>>4)&1]
	return "v"
for firstDigit, row in enumerate(items):
	for secondDigit, cell in enumerate(row):
		opcode = firstDigit*16 + secondDigit
		#print "%s%s=%s" % (firstDigit, secondDigit, cell),
		opcodes[opcode] = cell
	#print
invalidSyms = ["SLO", "KIL", "ISC", "XAA", "RRA", "RLA", "DCP", "ANC", "ARR", "SRE", "ALR", "SAX", "LAX", "LAS", "AHX", "TAS", "AXS", "SHX", "SHY"]
ops = {}
for firstDigit, row in enumerate(items):
	for secondDigit, cell in enumerate(row):
		opcode = firstDigit*16 + secondDigit
		r = opsym(opcode)
		cell0 = cell.split(" ")[0]
		if cell0 in invalidSyms:
			cell0 = "invalid"
		if r != cell0: # and cell0 not in invalidSyms and (opcode&0x3)!=3:
			print hex(opcode), opsym(opcode), cell0
			assert(r == cell0)
		parts = cell.split(" ")[1:]
		while len(parts) > 0 and parts[0].strip() == "":
			parts = parts[1:]
		if len(parts) > 0: # not invalid
			mstr = parts[0]
			cmstr = {
				'zp': "Z",
				'zpx': "ZX",
				'zpy': "ZY",
				'abs': "a",
				'abx': "aX",
				'aby': "aY",
				'imm': "v",
				'izx': "IX",
				'izy': "IY",
				'rel': "r",
				'i16': "V",
			}.get(mstr)
			if cmstr is None:
				assert(mstr[0] in "0123456789")
				cmstr = None
			if addressingMode(opcode) != cmstr:
				print addressingMode(opcode), cmstr, cell0, hex(opcode)
			assert(addressingMode(opcode) == cmstr)
			if r != "invalid":
				if r not in ops:
					ops[r] = {}
				ops[r][cmstr] = opcode
for r, items in ops.items():
	if 'v' not in items and items.keys() != [None]:
		pass
	else:
		print r, items
