#!/usr/bin/env python
# I, Danny Milosavljevic, hereby place this file into the public domain.
# -*- coding: utf-8 -*-

# TODO force minimum cycle length of any instruction to 2.

import sys
from symbols import *
import ConfigParser

def err(message):
    print >>sys.stderr, "error: %s" % message
    sys.exit(1)
    return 42

def to_signed_byte(value):
    return value if value < 0x80 else -(256 - value)

# derive this
class BRKHandler(object):
    def __init__(self, CPU):
        self.CPU = CPU
    def call_hook(self, PC):
        pass
    def get_points(self):
        return [1,2,3]

class Registers(object):
    def __init__(self): # FIXME default values...
        self.PC = 0
        self.SP = 0xFF
        self.X = 0
        self.Y = 0
        self.A = 0

def branch_opcode_P(opcode):
    return (opcode & 0x1F) == 0x10

class CPU(object):
    def __init__(self, MMU):
        self.BRK_handlers = []
        self.B_unit_testing = False
        self.B_disasm = False
        self.disasm_JSR_mask_beginning = 999999
        self.disasm_JSR_level = 0
        self.cycle_counter = 0
        self.B_debug_stack = False
        self.B_track_JSR = True
        CPU.opcode_to_mnem.append("XXX") # ShedSkin
        CPU.opcode_to_mnem = CPU.opcode_to_mnem[:-1] # ShedSkin
        assert len(CPU.opcode_to_mnem) == 0x100, "CPU opcode map covers all 256 possible opcodes"
        self.B_in_interrupt = False
        self.registers = Registers()
        self.MMU = MMU
        self.flags = set() # of N, V, B, D, I, Z, C.
        self.flags.add("I")
        self.flags.discard("C")
        self.flags.add("Z")
        #for mnem in set(CPU.opcode_to_mnem):
        #   if not hasattr(self, mnem) and mnem not in CPU.exotic_opcodes:
        #       raise NotImplementedError("warning: instruction %r not implemented")
        if False: # ShedSkin
            value = self.load_value_unadvancing(S_Z)
            value = self.load_value_advancing(S_Z)
            self.update_flags_by_number(value)
    def add_BRK_handler(self, handler):
        self.BRK_handlers.append(handler)
    def write_register(self, name, value):
        assert isinstance(value, int), "CPU.write_register: value is an integer"
        if name == S_PC:
            self.registers.PC = value
        elif name == S_A:
            self.registers.A = value
        elif name == S_X:
            self.registers.X = value
        elif name == S_Y:
            self.registers.Y = value
        elif name == S_SP:
            self.registers.SP = value
        else:
            assert False, "CPU.write_register: register is known"
        #print("registers", self.registers)
        #if self.B_disasm:
        #   print("%r:=%r" % (chr(name), value))

    def read_register_X(self):
        return(self.read_register(S_X))
    def read_register(self, name):
        r = self.registers
        return r.PC if name == S_PC else \
               r.A if name == S_A else \
               r.X if name == S_X else \
               r.Y if name == S_Y else \
               r.SP if name == S_SP else \
               err("unknown register")

    def disasm(self, PC):
        if self.B_in_interrupt:
            return(S_NONE)
        opcode = self.MMU.read_memory(PC)
        mnem = CPU.opcode_to_mnem[opcode]
        addressing_modes = CPU.LDX_addressing_modes if mnem == "LDX" else \
            CPU.LDY_addressing_modes if mnem == "LDY" else \
            CPU.LDA_addressing_modes if mnem == "LDA" else \
            CPU.CMP_addressing_modes if mnem == "CMP" else \
            CPU.CPX_addressing_modes if mnem == "CPX" else \
            CPU.CPY_addressing_modes if mnem == "CPY" else \
            CPU.ADC_addressing_modes if mnem == "ADC" else \
            CPU.SBC_addressing_modes if mnem == "SBC" else \
            CPU.BIT_addressing_modes if mnem == "BIT" else \
            CPU.AND_addressing_modes if mnem == "AND" else \
            CPU.EOR_addressing_modes if mnem == "EOR" else \
            CPU.ORA_addressing_modes if mnem == "ORA" else \
            CPU.INC_addressing_modes if mnem == "INC" else \
            CPU.DEC_addressing_modes if mnem == "DEC" else \
            CPU.ASL_addressing_modes if mnem == "ASL" else \
            CPU.LSR_addressing_modes if mnem == "LSR" else \
            CPU.SRE_addressing_modes if mnem == "SRE" else \
            CPU.ROL_addressing_modes if mnem == "ROL" else \
            CPU.ROR_addressing_modes if mnem == "ROR" else \
            CPU.STX_addressing_modes if mnem == "STX" else \
            CPU.STY_addressing_modes if mnem == "STY" else \
            CPU.STA_addressing_modes if mnem == "STA" else \
            CPU.NOP_addressing_modes if mnem == "NOP" else \
            CPU.LAX_addressing_modes if mnem == "LAX" else \
            CPU.DCP_addressing_modes if mnem == "DCP" else \
            CPU.JMP_addressing_modes if mnem == "JMP" else \
            CPU.JSR_addressing_modes if mnem == "JSR" else \
            {42: S_XXX}
        if branch_opcode_P(opcode):
            addressing_mode = S_REL
        else:
            addressing_mode = addressing_modes[opcode] if opcode in addressing_modes else S_NONE
        #addressing_mode = addressing_modes.get(opcode) or S_NONE
        # EQ addressing_mode = getattr(self.__class__, mnem + "_addressing_modes")[opcode]

        #if opcode == 0x88: # DEY
        #    print "[Z+$22] is $%02X" % (self.read_zero_page_memory(0x22), )
        args = ""
        if addressing_mode == S_HASH:
            args = "#%r" % self.MMU.read_memory(PC + 1, 1)
        elif addressing_mode == S_ABS_X:
            base = (self.MMU.read_memory(PC + 1, 2))
            args = "[$%04X+X] ABS ; %s" % (base, self.MMU.known_addresses[base] if base in self.MMU.known_addresses else "")
        elif addressing_mode == S_ABS:
            v = self.MMU.read_memory(PC + 1, 2)
            base = v
            args = "[$%04X] ABS ; %s" % (base, self.MMU.known_addresses[v] if v in self.MMU.known_addresses else "")
            if self.B_track_JSR and mnem in ["JSR", "JMP"]:
                address = (self.MMU.read_memory(PC + 1, 2))
                args = "#$%X" % address
                if address in self.MMU.known_addresses:
                    args = "%s ; %s" % (args, self.MMU.known_addresses[address])
                if mnem == "JSR" and not self.B_in_interrupt:
                    self.disasm_JSR_level += 1
                    if address in self.MMU.known_addresses and self.disasm_JSR_mask_beginning == 999999:
                        self.disasm_JSR_mask_beginning = self.disasm_JSR_level
                        print("A %02X, X %02X, Y %02X, SP %04X, PC %04X %02X %s %s ; (fast forward)" % (self.registers.A, self.registers.X, self.registers.Y, self.registers.SP | 0x100, PC, opcode, mnem, args))
        elif addressing_mode == S_IND2: # JMP IND
            base = self.MMU.read_memory(PC + 1, 2)
            args = "[$%04X] IND2 ; %s" % (base, self.MMU.known_addresses[base] if base in self.MMU.known_addresses else "")
        elif addressing_mode == S_ABS_Y:
            base = (self.MMU.read_memory(PC + 1, 2))
            args = "[$%04X+Y] ABS ; %s" % (base, self.MMU.known_addresses[base] if base in self.MMU.known_addresses else "")
        elif addressing_mode == S_Z:
            v = (self.MMU.read_memory(PC + 1, 1))
            base = v
            args = "[Z+$%02X] ; %s" % (base, self.MMU.known_addresses[base] if base in self.MMU.known_addresses else "")
        elif addressing_mode == S_Z_X:
            base = (self.MMU.read_memory(PC + 1, 1))
            args = "[Z+$%02X+X] ; %s" % (base, self.MMU.known_addresses[base] if base in self.MMU.known_addresses else "")
        elif addressing_mode == S_Z_Y:
            base = (self.MMU.read_memory(PC + 1, 1))
            args = "[Z+$%02X+Y] ; %s" % (base, self.MMU.known_addresses[base] if base in self.MMU.known_addresses else "")
        elif addressing_mode == S_IND_X:
            base = (self.MMU.read_memory(PC + 1, 1))
            actualAddr = self.read_zero_page_memory((base + self.read_register(S_X))&0xFF, 2)
            args = "[Z+$%02X+X] ; %s ; => $%X" % (base, self.MMU.known_addresses[base] if base in self.MMU.known_addresses else "", actualAddr)
        elif addressing_mode == S_IND_Y:
            base = (self.MMU.read_memory(PC + 1, 1))
            actualAddr = self.read_zero_page_memory(base, 2) + self.read_register(S_Y)
            args = "[[Z+$%02X]+Y] ; %s ; => $%X" % (base, self.MMU.known_addresses[base] if base in self.MMU.known_addresses else "", actualAddr)
        elif addressing_mode == S_A:
            args = "A"
        elif addressing_mode == S_REL: # branch
            if branch_opcode_P(opcode):
                offset = to_signed_byte(self.MMU.read_memory(PC + 1, 1))
                v = PC + offset + 2
                args = "%r ; $%X ; %s" % (offset, v, self.MMU.known_addresses[v] if v in self.MMU.known_addresses else "")
        elif addressing_mode != S_NONE:
            print >>sys.stderr, "error: unknown addressing mode $%X." % addressing_mode
            assert False, "CPU.disasm: addressing mode is known"
        else:
            if self.B_track_JSR and mnem == "RTS" and not self.B_in_interrupt:
                self.disasm_JSR_level -= 1
                if self.disasm_JSR_mask_beginning != 999999 and self.disasm_JSR_level < self.disasm_JSR_mask_beginning: # we left the proc, so kill the filter.
                    self.disasm_JSR_mask_beginning = 999999
                    return(addressing_mode)
        if self.disasm_JSR_level < self.disasm_JSR_mask_beginning or self.B_in_interrupt:
            print("A %02X, X %02X, Y %02X, SP %04X, PC %04X %02X %s %s" % (self.registers.A, self.registers.X, self.registers.Y, self.registers.SP | 0x100, PC, opcode, mnem, args))
        return(addressing_mode)

    def disable_JSR_masking(self):
        """ for disasm """
        self.B_track_JSR = False
        
    def step(self):
        PC = self.read_register(S_PC)
        opcode = self.read_memory(PC)
        #if PC == 0xE43D:  #FIXME
        #   self.B_disasm = False
        if self.B_disasm:
            self.disasm(PC)
        # TODO maybe use set_PC so we notice when someone walks into our hooks.
        self.write_register(S_PC, PC + 1)
        #sys.stdout.write("\t" + mnem)
        #print(hex(opcode))
        if opcode == 0x0:
            return self.BRK(opcode)
        elif opcode == 0x1:
            return self.ORA(opcode)
        elif opcode == 0x2:
            return self.KIL(opcode)
        elif opcode == 0x3:
            return self.SLO(opcode)
        elif opcode == 0x4:
            return self.NOP(opcode)
        elif opcode == 0x5:
            return self.ORA(opcode)
        elif opcode == 0x6:
            return self.ASL(opcode)
        elif opcode == 0x7:
            return self.SLO(opcode)
        elif opcode == 0x8:
            return self.PHP(opcode)
        elif opcode == 0x9:
            return self.ORA(opcode)
        elif opcode == 0xA:
            return self.ASL(opcode)
        elif opcode == 0xB:
            return self.ANC(opcode)
        elif opcode == 0xC:
            return self.NOP(opcode)
        elif opcode == 0xD:
            return self.ORA(opcode)
        elif opcode == 0xE:
            return self.ASL(opcode)
        elif opcode == 0xF:
            return self.SLO(opcode)
        elif opcode == 0x10:
            return self.BPL(opcode)
        elif opcode == 0x11:
            return self.ORA(opcode)
        elif opcode == 0x12:
            return self.KIL(opcode)
        elif opcode == 0x13:
            return self.SLO(opcode)
        elif opcode == 0x14:
            return self.NOP(opcode)
        elif opcode == 0x15:
            return self.ORA(opcode)
        elif opcode == 0x16:
            return self.ASL(opcode)
        elif opcode == 0x17:
            return self.SLO(opcode)
        elif opcode == 0x18:
            return self.CLC(opcode)
        elif opcode == 0x19:
            return self.ORA(opcode)
        elif opcode == 0x1A:
            return self.NOP(opcode)
        elif opcode == 0x1B:
            return self.SLO(opcode)
        elif opcode == 0x1C:
            return self.NOP(opcode)
        elif opcode == 0x1D:
            return self.ORA(opcode)
        elif opcode == 0x1E:
            return self.ASL(opcode)
        elif opcode == 0x1F:
            return self.SLO(opcode)
        elif opcode == 0x20:
            return self.JSR(opcode)
        elif opcode == 0x21:
            return self.AND(opcode)
        elif opcode == 0x22:
            return self.KIL(opcode)
        elif opcode == 0x23:
            return self.RLA(opcode)
        elif opcode == 0x24:
            return self.BIT(opcode)
        elif opcode == 0x25:
            return self.AND(opcode)
        elif opcode == 0x26:
            return self.ROL(opcode)
        elif opcode == 0x27:
            return self.RLA(opcode)
        elif opcode == 0x28:
            return self.PLP(opcode)
        elif opcode == 0x29:
            return self.AND(opcode)
        elif opcode == 0x2A:
            return self.ROL(opcode)
        elif opcode == 0x2B:
            return self.ANC(opcode)
        elif opcode == 0x2C:
            return self.BIT(opcode)
        elif opcode == 0x2D:
            return self.AND(opcode)
        elif opcode == 0x2E:
            return self.ROL(opcode)
        elif opcode == 0x2F:
            return self.RLA(opcode)
        elif opcode == 0x30:
            return self.BMI(opcode)
        elif opcode == 0x31:
            return self.AND(opcode)
        elif opcode == 0x32:
            return self.KIL(opcode)
        elif opcode == 0x33:
            return self.RLA(opcode)
        elif opcode == 0x34:
            return self.NOP(opcode)
        elif opcode == 0x35:
            return self.AND(opcode)
        elif opcode == 0x36:
            return self.ROL(opcode)
        elif opcode == 0x37:
            return self.RLA(opcode)
        elif opcode == 0x38:
            return self.SEC(opcode)
        elif opcode == 0x39:
            return self.AND(opcode)
        elif opcode == 0x3A:
            return self.NOP(opcode)
        elif opcode == 0x3B:
            return self.RLA(opcode)
        elif opcode == 0x3C:
            return self.NOP(opcode)
        elif opcode == 0x3D:
            return self.AND(opcode)
        elif opcode == 0x3E:
            return self.ROL(opcode)
        elif opcode == 0x3F:
            return self.RLA(opcode)
        elif opcode == 0x40:
            return self.RTI(opcode)
        elif opcode == 0x41:
            return self.EOR(opcode)
        elif opcode == 0x42:
            return self.KIL(opcode)
        elif opcode == 0x43:
            return self.SRE(opcode)
        elif opcode == 0x44:
            return self.NOP(opcode)
        elif opcode == 0x45:
            return self.EOR(opcode)
        elif opcode == 0x46:
            return self.LSR(opcode)
        elif opcode == 0x47:
            return self.SRE(opcode)
        elif opcode == 0x48:
            return self.PHA(opcode)
        elif opcode == 0x49:
            return self.EOR(opcode)
        elif opcode == 0x4A:
            return self.LSR(opcode)
        elif opcode == 0x4B:
            return self.ALR(opcode)
        elif opcode == 0x4C:
            return self.JMP(opcode)
        elif opcode == 0x4D:
            return self.EOR(opcode)
        elif opcode == 0x4E:
            return self.LSR(opcode)
        elif opcode == 0x4F:
            return self.SRE(opcode)
        elif opcode == 0x50:
            return self.BVC(opcode)
        elif opcode == 0x51:
            return self.EOR(opcode)
        elif opcode == 0x52:
            return self.KIL(opcode)
        elif opcode == 0x53:
            return self.SRE(opcode)
        elif opcode == 0x54:
            return self.NOP(opcode)
        elif opcode == 0x55:
            return self.EOR(opcode)
        elif opcode == 0x56:
            return self.LSR(opcode)
        elif opcode == 0x57:
            return self.SRE(opcode)
        elif opcode == 0x58:
            return self.CLI(opcode)
        elif opcode == 0x59:
            return self.EOR(opcode)
        elif opcode == 0x5A:
            return self.NOP(opcode)
        elif opcode == 0x5B:
            return self.SRE(opcode)
        elif opcode == 0x5C:
            return self.NOP(opcode)
        elif opcode == 0x5D:
            return self.EOR(opcode)
        elif opcode == 0x5E:
            return self.LSR(opcode)
        elif opcode == 0x5F:
            return self.SRE(opcode)
        elif opcode == 0x60:
            return self.RTS(opcode)
        elif opcode == 0x61:
            return self.ADC(opcode)
        elif opcode == 0x62:
            return self.KIL(opcode)
        elif opcode == 0x63:
            return self.RRA(opcode)
        elif opcode == 0x64:
            return self.NOP(opcode)
        elif opcode == 0x65:
            return self.ADC(opcode)
        elif opcode == 0x66:
            return self.ROR(opcode)
        elif opcode == 0x67:
            return self.RRA(opcode)
        elif opcode == 0x68:
            return self.PLA(opcode)
        elif opcode == 0x69:
            return self.ADC(opcode)
        elif opcode == 0x6A:
            return self.ROR(opcode)
        elif opcode == 0x6B:
            return self.ARR(opcode)
        elif opcode == 0x6C:
            return self.JMP(opcode)
        elif opcode == 0x6D:
            return self.ADC(opcode)
        elif opcode == 0x6E:
            return self.ROR(opcode)
        elif opcode == 0x6F:
            return self.RRA(opcode)
        elif opcode == 0x70:
            return self.BVS(opcode)
        elif opcode == 0x71:
            return self.ADC(opcode)
        elif opcode == 0x72:
            return self.KIL(opcode)
        elif opcode == 0x73:
            return self.RRA(opcode)
        elif opcode == 0x74:
            return self.NOP(opcode)
        elif opcode == 0x75:
            return self.ADC(opcode)
        elif opcode == 0x76:
            return self.ROR(opcode)
        elif opcode == 0x77:
            return self.RRA(opcode)
        elif opcode == 0x78:
            return self.SEI(opcode)
        elif opcode == 0x79:
            return self.ADC(opcode)
        elif opcode == 0x7A:
            return self.NOP(opcode)
        elif opcode == 0x7B:
            return self.RRA(opcode)
        elif opcode == 0x7C:
            return self.NOP(opcode)
        elif opcode == 0x7D:
            return self.ADC(opcode)
        elif opcode == 0x7E:
            return self.ROR(opcode)
        elif opcode == 0x7F:
            return self.RRA(opcode)
        elif opcode == 0x80:
            return self.NOP(opcode)
        elif opcode == 0x81:
            return self.STA(opcode)
        elif opcode == 0x82:
            return self.NOP(opcode)
        elif opcode == 0x83:
            return self.SAX(opcode)
        elif opcode == 0x84:
            return self.STY(opcode)
        elif opcode == 0x85:
            return self.STA(opcode)
        elif opcode == 0x86:
            return self.STX(opcode)
        elif opcode == 0x87:
            return self.SAX(opcode)
        elif opcode == 0x88:
            return self.DEY(opcode)
        elif opcode == 0x89:
            return self.NOP(opcode)
        elif opcode == 0x8A:
            return self.TXA(opcode)
        elif opcode == 0x8B:
            return self.XAA(opcode)
        elif opcode == 0x8C:
            return self.STY(opcode)
        elif opcode == 0x8D:
            return self.STA(opcode)
        elif opcode == 0x8E:
            return self.STX(opcode)
        elif opcode == 0x8F:
            return self.SAX(opcode)
        elif opcode == 0x90:
            return self.BCC(opcode)
        elif opcode == 0x91:
            return self.STA(opcode)
        elif opcode == 0x92:
            return self.KIL(opcode)
        elif opcode == 0x93:
            return self.AHX(opcode)
        elif opcode == 0x94:
            return self.STY(opcode)
        elif opcode == 0x95:
            return self.STA(opcode)
        elif opcode == 0x96:
            return self.STX(opcode)
        elif opcode == 0x97:
            return self.SAX(opcode)
        elif opcode == 0x98:
            return self.TYA(opcode)
        elif opcode == 0x99:
            return self.STA(opcode)
        elif opcode == 0x9A:
            return self.TXS(opcode)
        elif opcode == 0x9B:
            return self.TAS(opcode)
        elif opcode == 0x9C:
            return self.SHY(opcode)
        elif opcode == 0x9D:
            return self.STA(opcode)
        elif opcode == 0x9E:
            return self.SHX(opcode)
        elif opcode == 0x9F:
            return self.AHX(opcode)
        elif opcode == 0xA0:
            return self.LDY(opcode)
        elif opcode == 0xA1:
            return self.LDA(opcode)
        elif opcode == 0xA2:
            return self.LDX(opcode)
        elif opcode == 0xA3:
            return self.LAX(opcode)
        elif opcode == 0xA4:
            return self.LDY(opcode)
        elif opcode == 0xA5:
            return self.LDA(opcode)
        elif opcode == 0xA6:
            return self.LDX(opcode)
        elif opcode == 0xA7:
            return self.LAX(opcode)
        elif opcode == 0xA8:
            return self.TAY(opcode)
        elif opcode == 0xA9:
            return self.LDA(opcode)
        elif opcode == 0xAA:
            return self.TAX(opcode)
        elif opcode == 0xAB:
            return self.LAX(opcode)
        elif opcode == 0xAC:
            return self.LDY(opcode)
        elif opcode == 0xAD:
            return self.LDA(opcode)
        elif opcode == 0xAE:
            return self.LDX(opcode)
        elif opcode == 0xAF:
            return self.LAX(opcode)
        elif opcode == 0xB0:
            return self.BCS(opcode)
        elif opcode == 0xB1:
            return self.LDA(opcode)
        elif opcode == 0xB2:
            return self.KIL(opcode)
        elif opcode == 0xB3:
            return self.LAX(opcode)
        elif opcode == 0xB4:
            return self.LDY(opcode)
        elif opcode == 0xB5:
            return self.LDA(opcode)
        elif opcode == 0xB6:
            return self.LDX(opcode)
        elif opcode == 0xB7:
            return self.LAX(opcode)
        elif opcode == 0xB8:
            return self.CLV(opcode)
        elif opcode == 0xB9:
            return self.LDA(opcode)
        elif opcode == 0xBA:
            return self.TSX(opcode)
        elif opcode == 0xBB:
            return self.LAS(opcode)
        elif opcode == 0xBC:
            return self.LDY(opcode)
        elif opcode == 0xBD:
            return self.LDA(opcode)
        elif opcode == 0xBE:
            return self.LDX(opcode)
        elif opcode == 0xBF:
            return self.LAX(opcode)
        elif opcode == 0xC0:
            return self.CPY(opcode)
        elif opcode == 0xC1:
            return self.CMP(opcode)
        elif opcode == 0xC2:
            return self.NOP(opcode)
        elif opcode == 0xC3:
            return self.DCP(opcode)
        elif opcode == 0xC4:
            return self.CPY(opcode)
        elif opcode == 0xC5:
            return self.CMP(opcode)
        elif opcode == 0xC6:
            return self.DEC(opcode)
        elif opcode == 0xC7:
            return self.DCP(opcode)
        elif opcode == 0xC8:
            return self.INY(opcode)
        elif opcode == 0xC9:
            return self.CMP(opcode)
        elif opcode == 0xCA:
            return self.DEX(opcode)
        elif opcode == 0xCB:
            return self.AXS(opcode)
        elif opcode == 0xCC:
            return self.CPY(opcode)
        elif opcode == 0xCD:
            return self.CMP(opcode)
        elif opcode == 0xCE:
            return self.DEC(opcode)
        elif opcode == 0xCF:
            return self.DCP(opcode)
        elif opcode == 0xD0:
            return self.BNE(opcode)
        elif opcode == 0xD1:
            return self.CMP(opcode)
        elif opcode == 0xD2:
            return self.KIL(opcode)
        elif opcode == 0xD3:
            return self.DCP(opcode)
        elif opcode == 0xD4:
            return self.NOP(opcode)
        elif opcode == 0xD5:
            return self.CMP(opcode)
        elif opcode == 0xD6:
            return self.DEC(opcode)
        elif opcode == 0xD7:
            return self.DCP(opcode)
        elif opcode == 0xD8:
            return self.CLD(opcode)
        elif opcode == 0xD9:
            return self.CMP(opcode)
        elif opcode == 0xDA:
            return self.NOP(opcode)
        elif opcode == 0xDB:
            return self.DCP(opcode)
        elif opcode == 0xDC:
            return self.NOP(opcode)
        elif opcode == 0xDD:
            return self.CMP(opcode)
        elif opcode == 0xDE:
            return self.DEC(opcode)
        elif opcode == 0xDF:
            return self.DCP(opcode)
        elif opcode == 0xE0:
            return self.CPX(opcode)
        elif opcode == 0xE1:
            return self.SBC(opcode)
        elif opcode == 0xE2:
            return self.NOP(opcode)
        elif opcode == 0xE3:
            return self.ISC(opcode)
        elif opcode == 0xE4:
            return self.CPX(opcode)
        elif opcode == 0xE5:
            return self.SBC(opcode)
        elif opcode == 0xE6:
            return self.INC(opcode)
        elif opcode == 0xE7:
            return self.ISC(opcode)
        elif opcode == 0xE8:
            return self.INX(opcode)
        elif opcode == 0xE9:
            return self.SBC(opcode)
        elif opcode == 0xEA:
            return self.NOP(opcode)
        elif opcode == 0xEB:
            return self.SBC(opcode)
        elif opcode == 0xEC:
            return self.CPX(opcode)
        elif opcode == 0xED:
            return self.SBC(opcode)
        elif opcode == 0xEE:
            return self.INC(opcode)
        elif opcode == 0xEF:
            return self.ISC(opcode)
        elif opcode == 0xF0:
            return self.BEQ(opcode)
        elif opcode == 0xF1:
            return self.SBC(opcode)
        elif opcode == 0xF2:
            return self.KIL(opcode)
        elif opcode == 0xF3:
            return self.ISC(opcode)
        elif opcode == 0xF4:
            return self.NOP(opcode)
        elif opcode == 0xF5:
            return self.SBC(opcode)
        elif opcode == 0xF6:
            return self.INC(opcode)
        elif opcode == 0xF7:
            return self.ISC(opcode)
        elif opcode == 0xF8:
            return self.SED(opcode)
        elif opcode == 0xF9:
            return self.SBC(opcode)
        elif opcode == 0xFA:
            return self.NOP(opcode)
        elif opcode == 0xFB:
            return self.ISC(opcode)
        elif opcode == 0xFC:
            return self.NOP(opcode)
        elif opcode == 0xFD:
            return self.SBC(opcode)
        elif opcode == 0xFE:
            return self.INC(opcode)
        elif opcode == 0xFF:
            return self.ISC(opcode)
        #fn = self.opcode_to_fn[opcode]
        #fn(self, opcode)
        #print("done")

    # TODO DCP {adr} = DEC {adr} + CMP {adr}

    def update_flags_by_number(self, value):
        """ assumes 8 bit number, be careful. """
        assert isinstance(value, int), "CPU.update_flags_by_number: value is a number"
        if value < 0 or ((value & 128) != 0):
            self.flags.add("N")
        else:
            self.flags.discard("N")
        if value == 0:
            self.flags.add("Z")
        else:
            self.flags.discard("Z")
        return value
    def consume_operand(self, size):
        PC = self.read_register(S_PC)
        value = self.read_memory(PC, size)
        self.write_register(S_PC, PC + size)
        return value
    def consume_unsigned_operand(self, size):
        """ returns the operand as an integer, not as a buffer """
        value = self.consume_operand(size)
        return value
    def consume_signed_operand(self, size):
        """ returns the operand as an integer, not as a buffer """
        value = to_signed_byte(self.consume_operand(size))
        #value = (endian.unpack_signed_16_bit if size == 2 else endian.unpack_signed if size == 1 else err("invalid operand size"))(value)
        #print(value)
        return value
    def read_memory(self, address, size = 1):
        self.cycle_counter += size
        return self.MMU.read_memory(address, size)
    def write_memory(self, address, value, size = 1):
        self.cycle_counter += size
        return self.MMU.write_memory(address, value, size)
    def read_zero_page_memory(self, address, size = 1):
        assert size < 3, "CPU.read_zero_page_memory: size<3"
        assert size > 0, "CPU.read_zero_page_memory: size>0"
        if size == 2 and address == 0xFF:
            return self.read_memory(address, 1) | (self.read_memory(0, 1) << 8)
        else:
            return self.read_memory(address, size)
    def store_value(self, addressing_mode, value, size = 1):
        #print("MODE", addressing_mode)
        if addressing_mode == S_Z:
            self.write_memory(self.consume_unsigned_operand(1), value, size)
        elif addressing_mode == S_Z_Y:
            # FIXME unsigned?
            self.write_memory((self.consume_unsigned_operand(1) + (self.read_register(S_Y)) & 0xFF), value, size)
        elif addressing_mode == S_Z_X:
            # FIXME unsigned?
            self.write_memory((self.consume_unsigned_operand(1) + (self.read_register(S_X)) & 0xFF), value, size)
        elif addressing_mode == S_ABS:
            self.write_memory(self.consume_unsigned_operand(2), value, size)
        elif addressing_mode == S_ABS_Y:
            # FIXME unsigned.
            self.write_memory((self.consume_unsigned_operand(2) + self.read_register(S_Y)) & 0xFFFF, value, size)
        elif addressing_mode == S_ABS_X:
            # FIXME unsigned.
            self.write_memory((self.consume_unsigned_operand(2) + self.read_register(S_X)) & 0xFFFF, value, size)
        elif addressing_mode == S_IND_X:
            base = self.consume_unsigned_operand(1)
            # FIXME signed?
            offset = (self.read_register(S_X))
            address = self.read_zero_page_memory((base + offset) & 0xFF, 2)
            assert address != 0, "CPU.store_value: debugging sentinel to avoid dereferencing a 0 pointer on IND."
            self.write_memory(address, value, size)
        elif addressing_mode == S_IND_Y: # [[$a]+Y]
            base = self.consume_unsigned_operand(1)
            #print("base would be $%X" % base)
            address = self.read_zero_page_memory(base, 2)
            # FIXME signed?
            offset = (self.read_register(S_Y))
            #print("address would be $%X+$%X" % (address, offset))
            assert address != 0, "CPU.store_value: debugging sentinel to avoid dereferencing a 0 pointer on IND."
            #print("offset %r" % offset)
            self.write_memory((address + offset) & 0xFFFF, value, size)
        elif addressing_mode == S_A:
            self.write_register(S_A, value)
        else:
            print("error", addressing_mode)
            assert False, "CPU.store_value: addressing mode is known"
    def load_value_unadvancing(self, addressing_mode): # mostly INC and shift instructions...
        old_PC = self.read_register(S_PC)
        result = self.load_value_advancing(addressing_mode)
        self.write_register(S_PC, old_PC)
        return result
    def setaddr(self, value):
        self.addr = value
        return(value)
    def load_value_advancing(self, addressing_mode, opcode = 0):
        # mask_addressing_modes = [S_HASH, S_Z, S_Z_X, S_Z_Y, S_ABS, S_ABS_X, S_ABS_Y, S_IND_X, S_IND_Y]
        #sys.stdout.write({
        #   0: S_HASH,
        #   0x1C: S_ABS_Y,
        #}.get(addressing_mode) or str(addressing_mode))
        # FIXME is unsigned correct?
        #print(addressing_mode)
        result =  self.read_register(S_A) if addressing_mode == S_A else \
                self.consume_unsigned_operand(1) if addressing_mode == S_HASH else \
            self.read_zero_page_memory(self.setaddr(self.consume_unsigned_operand(1))) if addressing_mode == S_Z else \
            self.read_zero_page_memory((self.consume_unsigned_operand(1) + self.read_register(S_X)) & 0xFF) if addressing_mode == S_Z_X else \
            self.read_zero_page_memory((self.consume_unsigned_operand(1) + self.read_register(S_Y)) & 0xFF) if addressing_mode == S_Z_Y else \
            self.read_memory(self.consume_unsigned_operand(2)) if addressing_mode == S_ABS else \
            self.read_memory((self.consume_unsigned_operand(2) + self.read_register(S_X)) & 0xFFFF) if addressing_mode == S_ABS_X else \
            self.read_memory((self.consume_unsigned_operand(2) + self.read_register(S_Y)) & 0xFFFF) if addressing_mode == S_ABS_Y else \
            self.read_memory(self.read_zero_page_memory((self.consume_unsigned_operand(1) + self.read_register(S_X)) & 0xFF, 2)) if addressing_mode == S_IND_X else \
            self.read_memory((self.read_zero_page_memory(self.consume_unsigned_operand(1), 2) + self.read_register(S_Y)) & 0xFFFF) if addressing_mode == S_IND_Y else \
            err("invalid addressing mode %r" % addressing_mode)
        return(result)

    LDX_addressing_modes = {
            0xA2: S_HASH,
            0xA6: S_Z,
            0xB6: S_Z_Y,
            0xAE: S_ABS,
            0xBE: S_ABS_Y,
    }
    def LDX(self, opcode = 0xA2):
        addressing_mode = CPU.LDX_addressing_modes[opcode]
        value = self.load_value_advancing(addressing_mode)
        self.write_register(S_X, value)
        self.update_flags_by_number(value)

    LDY_addressing_modes = {
            0xA0: S_HASH,
            0xA4: S_Z,
            0xB4: S_Z_X,
            0xAC: S_ABS,
            0xBC: S_ABS_X,
    }
    def LDY(self, opcode):
        addressing_mode = CPU.LDY_addressing_modes[opcode]
        value = self.load_value_advancing(addressing_mode)
        self.write_register(S_Y, value)
        self.update_flags_by_number(value)

    LDA_addressing_modes = {
            0xA9: S_HASH,
            0xA5: S_Z,
            0xB5: S_Z_X,
            0xAD: S_ABS,
            0xBD: S_ABS_X,
            0xB9: S_ABS_Y,
            0xA1: S_IND_X,
            0xB1: S_IND_Y,
    }
    def LDA(self, opcode):
        addressing_mode = CPU.LDA_addressing_modes[opcode]
        value = self.load_value_advancing(addressing_mode, opcode)
        #if value is None:
        #   print("ADR", CPU.LDA_addressing_modes[opcode])
        #print("LDA result is %r" % value)
        self.write_register(S_A, value)
        self.update_flags_by_number(value)

    def compare(self, value, reference_value):
        result = reference_value - value
        self.update_flags_by_number(result)
        #print("CMP RES", result)
        if reference_value >= value:
            self.flags.add("C")
        else:
            self.flags.discard("C")
            assert "N" in self.flags, "CPU.compare: N is in flags"

    CMP_addressing_modes = {
            0xC9: S_HASH,
            0xC5: S_Z,
            0xD5: S_Z_X,
            0xCD: S_ABS,
            0xDD: S_ABS_X,
            0xD9: S_ABS_Y,
            0xC1: S_IND_X,
            0xD1: S_IND_Y,
    }
    def CMP(self, opcode):
        """ compare with A """
        # FIXME negative numbers?
        assert opcode in [0xC1, 0xC5, 0xC9, 0xCD, 0xD1, 0xD5, 0xD9, 0xDD], "CPU.CMP opcode is in known set"
        reference_value = self.read_register(S_A)
        addressing_mode = CPU.CMP_addressing_modes[opcode]
        return self.compare(self.load_value_advancing(addressing_mode), reference_value)

    CPX_addressing_modes = {
            0xE0: S_HASH,
            0xE4: S_Z,
            0xEC: S_ABS,
    }
    def CPX(self, opcode):
        """ compare with X """
        reference_value = self.read_register(S_X)
        addressing_mode = CPU.CPX_addressing_modes[opcode]
        return self.compare(self.load_value_advancing(addressing_mode), reference_value)

    CPY_addressing_modes = {
            0xC0: S_HASH,
            0xC4: S_Z,
            0xCC: S_ABS,
    }
    def CPY(self, opcode):
        """ compare with Y """
        # FIXME negative numbers?
        reference_value = self.read_register(S_Y)
        addressing_mode = CPU.CPY_addressing_modes[opcode]
        return self.compare(self.load_value_advancing(addressing_mode), reference_value)

    def add_BCD(self, a, b): # unsigned
        carry = 1 if "C" in self.flags else 0  
        # N and Z are invalid on 6502
        a0 = a & 0xF
        a1 = a >> 4
        b0 = b & 0xF
        b1 = b >> 4
        r0 = a0 + b0 + carry
        r1 = a1 + b1 + (1 if r0 > 9 else 0)
        if r0 > 9:
            r0 = r0 - 10
        if r1 > 9:
            r1 = r1 - 10
            self.flags.add("C")
        else:
            self.flags.discard("C")
        # TODO overflow.
        value = (r1 << 4) | r0
        self.write_register(S_A, value)
        self.update_flags_by_number(value)
        return value
    def subtract_BCD(self, a, b):
        uncarry = 0 if "C" in self.flags else 1
        # N and Z are invalid on 6502
        a0 = a & 0xF
        a1 = a >> 4
        b0 = b & 0xF
        b1 = b >> 4
        r0 = a0 - b0 - uncarry
        r1 = a1 - b1 - (1 if r0 < 0 else 0)
        if r0 < 0:
            r0 = 10 + r0
        if r1 < 0:
            r1 = 10 + r1
            self.flags.discard("C")
        else:
            self.flags.add("C")
        # TODO overflow.
        value = (r1 << 4) | r0
        self.write_register(S_A, value)
        self.update_flags_by_number(value)
        return value
    def add(self, operand_0, operand_1):
        carry = 1 if "C" in self.flags else 0
        value = (operand_0 + operand_1 + carry)
        B_overflow_1 = False
        a_value = value
        if (value & 0xFF) != value: # that is, value>0xFF.
            self.flags.add("C")
            B_overflow_1 = True
        else:
            self.flags.discard("C")
        value = value & 0xFF
        # 0x7F+1 overflow
        # 0x80+0xFF overflow
        B_overflow = ((operand_0 ^ operand_1) & 0x80) == 0 and ((operand_0 ^ value) & 0x80) != 0
        #((operand_0 ^ operand_1) & (operand_0 ^ (value & 0xFF)) & 0x80) != 0
        #B_overflow = ((operand_1 & 0x80) == 0 and (operand_0 & 0x80) == 0 and (value & 0x80) != 0) or \
        #             ((operand_1 & 0x80) != 0 and (operand_0 & 0x80) != 0 and (value & 0x80) == 0)
        if B_overflow:
            self.flags.add("V")
        else:
            #if B_overflow_1 != False:
            #   print("whoops") # , a_value, operand_0, operand_1)
            # FIXME assert(B_overflow_1 == False)
            self.flags.discard("V")
        #self.store_value(addressing_mode, value)
        self.write_register(S_A, value)
        self.update_flags_by_number(value)
        return value
    ADC_addressing_modes = {
        0x69: S_HASH,
        0x65: S_Z,
        0x75: S_Z_X,
        0x6D: S_ABS,
        0x7D: S_ABS_X,
        0x79: S_ABS_Y,
        0x61: S_IND_X,
        0x71: S_IND_Y,
    }
    def ADC(self, opcode):
        """ add with carry """
        # TODO BCD arithmetic.
        addressing_mode = CPU.ADC_addressing_modes[opcode]
        operand_0 = self.read_register(S_A)
        operand_1 = self.load_value_advancing(addressing_mode)
        #print("ADC", operand_0, operand_1)
        if "D" in self.flags:
            self.add_BCD(operand_0, operand_1)
        else:
            self.add(operand_0, operand_1)
        # for BCD: carry:=result>$99
        # for BCD: N:=value of bit 7

    SBC_addressing_modes = {
        0xE9: S_HASH,
        0xE5: S_Z,
        0xF5: S_Z_X,
        0xED: S_ABS,
        0xFD: S_ABS_X,
        0xF9: S_ABS_Y,
        0xE1: S_IND_X,
        0xF1: S_IND_Y,
    }
    def SBC(self, opcode):
        """ subtract with carry """
        # TODO BCD arithmetic.
        addressing_mode = CPU.SBC_addressing_modes[opcode]
        operand_0 = self.read_register(S_A)
        operand_1 = self.load_value_advancing(addressing_mode)
        if "D" in self.flags:
            self.subtract_BCD(operand_0, operand_1)
        else:
            self.add(operand_0, operand_1 ^ 0xFF)
        #B_overflow = ((operand_0 ^ operand_1) & (operand_0 ^ (result & 0xFF)) & 0x80) != 0
        #if B_overflow:
        #   self.flags.add("V")
        #else:
        #   self.flags.discard("V")
        #if result < 0 or (result & 128) != 0:
        #   self.flags.add("C")
        #else:
        #   self.flags.discard("C") # FIXME test.
        #self.store_value(addressing_mode, value)

    def test_bits(self, addressing_mode):
        reference_value = self.read_register(S_A)
        value = self.load_value_advancing(addressing_mode)
        result = value & reference_value
        return result, value

    BIT_addressing_modes = {
        0x24: S_Z,
        0x2C: S_ABS,
    }
    def BIT(self, opcode = 0x24):
        """ like AND, but does not store the result (but just the flags). """
        reference_value = self.read_register(S_A)
        result, operand = self.test_bits(CPU.BIT_addressing_modes[opcode])
        self.update_flags_by_number(result)
        if (operand & 64) != 0:
            self.flags.add("V")
        else:
            self.flags.discard("V")
        if (operand & 128) != 0:
            self.flags.add("N")
        else:
            self.flags.discard("N")
        #return result

    AND_addressing_modes = {
            0x29: S_HASH,
            0x25: S_Z,
            0x35: S_Z_X,
            0x2D: S_ABS,
            0x3D: S_ABS_X,
            0x39: S_ABS_Y,
            0x21: S_IND_X,
            0x31: S_IND_Y,
        }
    def AND(self, opcode):
        """ AND with A """
        value, operand = self.test_bits(CPU.AND_addressing_modes[opcode])
        self.write_register(S_A, value)
        self.update_flags_by_number(value)

    EOR_addressing_modes = {
            0x49: S_HASH,
            0x45: S_Z,
            0x55: S_Z_X,
            0x4D: S_ABS,
            0x5D: S_ABS_X,
            0x59: S_ABS_Y,
            0x41: S_IND_X,
            0x51: S_IND_Y,
    }

    def EOR(self, opcode):
        """ exclusive OR """
        reference_value = self.read_register(S_A)
        addressing_mode = CPU.EOR_addressing_modes[opcode]
        value = self.load_value_advancing(addressing_mode)
        result = value ^ reference_value
        self.write_register(S_A, result)
        self.update_flags_by_number(result)

    ORA_addressing_modes = {
            0x09: S_HASH,
            0x05: S_Z,
            0x15: S_Z_X,
            0x0D: S_ABS,
            0x1D: S_ABS_X,
            0x19: S_ABS_Y,
            0x01: S_IND_X,
            0x11: S_IND_Y,
    }
    def ORA(self, opcode = 0x1):
        """ ORA with A """
        reference_value = self.read_register(S_A)
        addressing_mode = CPU.ORA_addressing_modes[opcode]
        value = self.load_value_advancing(addressing_mode)
        result = value | reference_value
        self.write_register(S_A, result)
        self.update_flags_by_number(result)

    def TXS(self, opcode = 0x9A):
        """ transfer X to stack pointer """
        # does NOT set negative flag!
        self.write_register(S_SP, self.read_register(S_X))

    def TAY(self, opcode = 0xA8):
        """ transfer A to Y """
        self.write_register(S_Y, self.update_flags_by_number(self.read_register(S_A)))

    def TYA(self, opcode = 0x98):
        """ transfer Y to A """
        self.write_register(S_A, self.update_flags_by_number(self.read_register(S_Y)))

    def TAX(self, opcode = 0xAA):
        """ transfer A to X """
        self.write_register(S_X, self.update_flags_by_number(self.read_register(S_A)))

    def TSX(self, opcode = 0xBA):
        """ transfer SP to X """
        value = self.update_flags_by_number(self.read_register(S_SP))
        self.write_register(S_X, value)

    def TXA(self, opcode = 0x8A):
        """ transfer X to A """
        self.write_register(S_A, self.update_flags_by_number(self.read_register(S_X)))

    def CLD(self, opcode = 0xD8):
        """ Clear Decimal """
        self.flags.discard("D")

    def SED(self, opcode = 0xF8):
        """ Set Decimal """
        self.flags.add("D")

    NOP_addressing_modes = {
            0xEA: S_A,
            0x1A: S_A,
            0x3A: S_A,
            0x5A: S_A,
            0x7A: S_A,
            0xDA: S_A,
            0xFA: S_A,
            0x04: S_Z,
            0x44: S_Z,
            0x64: S_Z,
            0x0C: S_ABS,
            0x1C: S_ABS_X,
            0x3C: S_ABS_X,
            0x5C: S_ABS_X,
            0x7C: S_ABS_X,
            0xDC: S_ABS_X,
            0xFC: S_ABS_X,
            0x9B: S_ABS_Y, # C64DTV
            0x14: S_Z_X,
            0x34: S_Z_X,
            0x54: S_Z_X,
            0x74: S_Z_X,
            0xD4: S_Z_X,
            0xF4: S_Z_X,
            0x80: S_HASH,
            0x82: S_HASH,
            0x89: S_HASH,
            0xC2: S_HASH,
            0xE2: S_HASH,
    }
    def NOP(self, opcode):
        """ No operation """
        addressing_mode = CPU.NOP_addressing_modes[opcode]
        value = self.load_value_advancing(addressing_mode)
        #self.consume_operand(1) # dummy so you can replace any 1-arg instruction's opcode by BRK.

    def DEX(self, opcode):
        result = (self.read_register(S_X) - 1) & 0xFF
        self.write_register(S_X, result)
        self.update_flags_by_number(result)

    def INX(self, opcode = 0xE8):
        result = ((self.read_register(S_X)) + 1) & 0xFF
        self.write_register(S_X, result)
        self.update_flags_by_number(result)

    def DEY(self, opcode = 0x88):
        result = (self.read_register(S_Y) - 1) & 0xFF
        self.write_register(S_Y, result)
        self.update_flags_by_number(result)

    def INY(self, opcode = 0xC8):
        result = ((self.read_register(S_Y)) + 1) & 0xFF
        self.write_register(S_Y, result)
        self.update_flags_by_number(result)

    DEC_addressing_modes = {
            0xC6: S_Z,
            0xD6: S_Z_X,
            0xCE: S_ABS,
            0xDE: S_ABS_X,
    }
    def DEC(self, opcode = 0xC6):
        addressing_mode = CPU.DEC_addressing_modes[opcode]
        value = self.load_value_unadvancing(addressing_mode)
        result = (value - 1) & 0xFF
        self.store_value(addressing_mode, result)
        self.update_flags_by_number(result)

    DCP_addressing_modes = {
            0xC7: S_Z,
            0xD7: S_Z_X,
            0xCF: S_ABS,
            0xDF: S_ABS_X,
            0xDB: S_ABS_Y,
            0xC3: S_IND_X,
            0xD3: S_IND_Y,
    }
    def DCP(self, opcode = 0xC3):
        addressing_mode = CPU.DCP_addressing_modes[opcode]
        value = self.load_value_unadvancing(addressing_mode)
        result = (value - 1) & 0xFF
        self.store_value(addressing_mode, result)
        self.update_flags_by_number(result)
        self.compare(result, self.read_register(S_A))

    INC_addressing_modes = {
            0xE6: S_Z,
            0xF6: S_Z_X,
            0xEE: S_ABS,
            0xFE: S_ABS_X,
    }
    def INC(self, opcode):
        addressing_mode = CPU.INC_addressing_modes[opcode]
        value = self.load_value_unadvancing(addressing_mode)
        result = (value + 1) & 0xFF
        self.store_value(addressing_mode, result)
        self.update_flags_by_number(result)

    ASL_addressing_modes = {
            0x0A: S_A,
            0x06: S_Z,
            0x16: S_Z_X,
            0x0E: S_ABS,
            0x1E: S_ABS_X,
    }
    def ASL(self, opcode):
        addressing_mode = CPU.ASL_addressing_modes[opcode]
        value = self.load_value_unadvancing(addressing_mode)
        if (value & 128) != 0:
            self.flags.add("C")
        else:
            self.flags.discard("C")
        result = (value << 1) & 0xFF
        self.store_value(addressing_mode, result)
        self.update_flags_by_number(result)

    LSR_addressing_modes = {
            0x4A: S_A,
            0x46: S_Z,
            0x56: S_Z_X,
            0x4E: S_ABS,
            0x5E: S_ABS_X,
    }
    def LSR(self, opcode):
        addressing_mode = CPU.LSR_addressing_modes[opcode]
        value = self.load_value_unadvancing(addressing_mode)
        if (value & 1) != 0:
            self.flags.add("C")
        else:
            self.flags.discard("C")
        result = (value >> 1) & 0xFF
        self.store_value(addressing_mode, result)
        self.update_flags_by_number(result)

    ROL_addressing_modes = {
            0x2A: S_A,
            0x26: S_Z,
            0x36: S_Z_X,
            0x2E: S_ABS,
            0x3E: S_ABS_X,
    }
    def ROL(self, opcode):
        addressing_mode = CPU.ROL_addressing_modes[opcode]
        value = self.load_value_unadvancing(addressing_mode)
        value = ((value << 1) | (1 if "C" in self.flags else 0))
        result = value & 0xFF
        if (value & 0x100) != 0:
            self.flags.add("C")
        else:
            self.flags.discard("C")
        self.store_value(addressing_mode, result)
        self.update_flags_by_number(result)
        
    ROR_addressing_modes = {
            0x6A: S_A,
            0x66: S_Z,
            0x76: S_Z_X,
            0x6E: S_ABS,
            0x7E: S_ABS_X,
    }
    def ROR(self, opcode = 0x66):
        addressing_mode = CPU.ROR_addressing_modes[opcode]
        value = self.load_value_unadvancing(addressing_mode)
        result = ((value >> 1) | (128 if "C" in self.flags else 0))  & 0xFF
        if (value & 1) != 0:
            self.flags.add("C")
        else:
            self.flags.discard("C")
        #(self.flags.add if value & 1 else self.flags.discard)("C") # yes, the old value!
        self.store_value(addressing_mode, result)
        self.update_flags_by_number(result)
    
    status_positions = ["C", "Z", "I", "D", "B", "5", "V", "N"]

    def read_page_stuck_memory(self, address, size):
        assert size == 2, "read_page_stuck_memory size is 2"
        if (address & 0xFF) == 0xFF:
            return self.read_memory(address) | (self.read_memory(address &~ 0xFF) << 8)
        else:
            return self.read_memory(address, 2)

    def pop_status(self):
        flags_bin = self.stack_pop(1)
        self.flags = set([(flag_name if (flags_bin & (1 << flag_bit)) != 0 else "") for flag_bit, flag_name in enumerate(CPU.status_positions)])
        self.flags.discard("")

    def push_status(self):
        flags_bin = sum([((1 << flag_bit) if flag_name in self.flags else 0) for flag_bit, flag_name in enumerate(CPU.status_positions)])
        self.stack_push(flags_bin, 1)

    def stack_push(self, value, size):
        """
        actually:
        RAM[sp+256] = value >> 8
        sp_1=sp-1
        sp_1&=255
        RAM[sp-1+256] = value & 0xFF
        sp_2=sp_1-1
        sp_2&=255
        """
        assert isinstance(value, int), "CPU.stack_push: value is an integer"
        SP = self.read_register(S_SP)
        base = 0x100
        for i in range(size): # easier debugging when it doesn't skip slots.
            SP -= 1
            self.write_register(S_SP, SP)
        #SP -= size
        #self.write_register(S_SP, SP)
        address = base + SP + 1
        self.write_memory(address, value, size)
        if self.B_debug_stack:
            print("stack push %r at $%X" % (value, address))

    def stack_peek(self, size):
        SP = self.read_register(S_SP)
        base = 0x100
        value_bin = self.read_memory(base + SP + 1, size)
        return value_bin

    def stack_pop(self, size):
        value_bin = self.stack_peek(size) # UP
        SP = self.read_register(S_SP)
        self.write_register(S_SP, SP + size)
        base = 0x100
        if self.B_debug_stack:
            print("stack pop %r at $%X" % (value_bin, base + SP + 1))
        #print("stack peek %r" % self.stack_peek(2))
        return value_bin

    def set_PC(self, new_PC):
        self.write_register(S_PC, new_PC)

    def branch(self, offset):
        old_PC = self.read_register(S_PC)
        new_PC = (old_PC + offset) & 0xFFFF
        self.cycle_counter += 1 if (old_PC &~ 0xFF) == (new_PC &~ 0xFF) else 3
        self.set_PC(new_PC)
            
    def BNE(self, opcode = 0xD0):
        assert opcode == 0xD0, "CPU.BNE: opcode is known"
        offset = self.consume_signed_operand(1)
        if "Z" not in self.flags:
            #print("OFFSET", offset)
            self.branch(offset)

    def BEQ(self, opcode = 0xF0):
        offset = self.consume_signed_operand(1)
        if "Z" in self.flags:
            #print("OFFSET", offset)
            self.branch(offset)

    def BPL(self, opcode = 0x10):
        offset = self.consume_signed_operand(1)
        if "N" not in self.flags:
            #print("OFFSET", offset)
            self.branch(offset)

    def BMI(self, opcode = 0x30):
        offset = self.consume_signed_operand(1)
        if "N" in self.flags:
            #print("OFFSET", offset)
            self.branch(offset)

    def BCS(self, opcode = 0xB0):
        offset = self.consume_signed_operand(1)
        if "C" in self.flags:
            #print("OFFSET", offset)
            self.branch(offset)

    def BCC(self, opcode = 0x90):
        offset = self.consume_signed_operand(1)
        if "C" not in self.flags:
            #print("OFFSET", offset)
            self.branch(offset)

    def BVS(self, opcode = 0x70):
        offset = self.consume_signed_operand(1)
        if "V" in self.flags:
            #print("OFFSET", offset)
            self.branch(offset)

    def BVC(self, opcode = 0x50):
        offset = self.consume_signed_operand(1)
        if "V" not in self.flags:
            #print("OFFSET", offset)
            self.branch(offset)

    JMP_addressing_modes = {
            0x4C: S_ABS,
            0x6C: S_IND2,
    }
    def JMP(self, opcode = 0x4C):
        address = self.consume_unsigned_operand(2)
        if opcode == 0x6C: # indirect jump
            value = self.read_page_stuck_memory(address, 2)
        else:
            value = address
        self.set_PC(value)


    JSR_addressing_modes = {
            0x20: S_ABS,
    }
    def JSR(self, opcode = 0x20):
        assert opcode == 0x20, "CPU.JSR: opcode is known"
        #self.push_status()
        new_PC = self.consume_unsigned_operand(2)
        self.stack_push(self.read_register(S_PC) - 1, 2)
        self.set_PC(new_PC)

    STX_addressing_modes = {
        0x86: S_Z,
        0x96: S_Z_Y,
        0x8E: S_ABS,
    }
    def STX(self, opcode):
        """ store X into memory """
        self.store_value(CPU.STX_addressing_modes[opcode], self.read_register(S_X))

    STY_addressing_modes = {
        0x84: S_Z,
        0x94: S_Z_X,
        0x8C: S_ABS,
    }
    def STY(self, opcode):
        """ store Y into memory """
        self.store_value(CPU.STY_addressing_modes[opcode], self.read_register(S_Y))

    STA_addressing_modes = {
        0x85: S_Z,
        0x95: S_Z_X,
        0x8D: S_ABS,
        0x9D: S_ABS_X,
        0x99: S_ABS_Y,
        0x81: S_IND_X,
        0x91: S_IND_Y,
    }
    def STA(self, opcode = 0x81):
        """ store A into memory """
        self.store_value(CPU.STA_addressing_modes[opcode], self.read_register(S_A))

    def RTS(self, opcode = 0x60):
        """ return from subroutine """
        PC = (self.stack_pop(2))
        self.set_PC(PC + 1)
        #self.pop_status()

    def RTI(self, opcode = 0x40):
        """ return from interrupt """
        self.pop_status()
        PC = (self.stack_pop(2))
        self.B_in_interrupt = False
        self.set_PC(PC)

    def SEI(self, opcode = 0x78):
        """ Set Interrupt Disable """
        self.flags.add("I")

    def CLI(self, opcode = 0x58):
        """ Clear Interrupt Disable """
        self.flags.discard("I")

    def clear_Z(self): # mostly for unit tests.
        self.flags.discard("Z")

    def set_Z(self): # mostly for unit tests.
        self.flags.add("Z")

    def clear_N(self): # mostly for unit tests.
        self.flags.discard("N")

    def set_N(self): # mostly for unit tests.
        self.flags.add("N")

    def clear_B(self): # mostly for unit tests.
        self.flags.discard("B")

    def set_B(self): # mostly for unit tests.
        self.flags.add("B")

    def set_V(self): # mostly for unit tests.
        self.flags.add("V")

    def CLC(self, opcode = 0x18):
        """ Clear Carry """
        self.flags.discard("C")

    def SEC(self, opcode = 0x38):
        """ Set Carry """
        self.flags.add("C")

    def CLV(self, opcode = 0xB8):
        """ Clear Overflow """
        self.flags.discard("V")

    def BRK(self, opcode):
        """ software debugging (NMI) """
        self.consume_operand(1) # dummy so you can replace any 1-arg instruction's opcode by BRK.
        old_PC = self.read_register(S_PC) - 2
        for handler in self.BRK_handlers:
            if handler.call_hook(old_PC):
                return
        if self.test_call_hook(old_PC):
            return
        #if not self.B_unit_testing:
        self.cause_interrupt(True)

    def test_call_hook(self, hook_PC):
        if not self.B_unit_testing:
            return(False)
        MMU = self.MMU
        if hook_PC == 0x8000 or hook_PC == 0xA474:
            sys.exit(0)
            return(True)
        elif hook_PC == 0xFFE4: # scan keyboard
            self.write_register(S_A, 3)
            self.RTS()
            # TODO restart CPU.
            return(True)
        elif hook_PC == 0xE16F: # load
            name_P = MMU.read_memory(0xBB) | (MMU.read_memory(0xBC) << 8)
            name_L = MMU.read_memory(0xB7)
            pass # FIXME
            self.RTS()
            self.write_register(S_PC, 0x816)
            return(True)
        elif hook_PC == 0xFFD2: # putc
            MMU.write_memory(0x30C, 0, 1)
            sys.stdout.write(chr(self.read_register(S_A))) # TODO convert from petscii.
            self.RTS()
            # TODO restart CPU.
            return(True)
        else:
            return(False)
    def cause_interrupt(self, B_BRK):  # IRQ and BRK.
        if self.B_in_interrupt:
            return
        if self.B_disasm:
            print("causing interrupt")
        self.B_in_interrupt = True
        address = 0xFFFE # ISR vector.
        old_PC = self.read_register(S_PC)
        self.stack_push(old_PC, 2)
        new_PC = self.read_memory(address, 2)
        self.push_status()
        self.SEI(0x78)
        #print("NEW PC $%X" % new_PC)
        if B_BRK:
            self.flags.add("B")
        self.set_PC(new_PC)
    # TODO cause NMI

    def PHP(self, opcode):
        """ push processor status """
        self.push_status()

    def PLP(self, opcode):
        """ pull processor status """
        self.pop_status()

    def PHA(self, opcode):
        """ push A """
        self.stack_push(self.read_register(S_A), 1)

    def PLA(self, opcode):
        """ pull A """
        value = self.stack_pop(1)
        self.write_register(S_A, value)
        self.update_flags_by_number(value)

    opcode_to_mnem = [
        "BRK", 
        "ORA",
        "KIL", 
        "SLO",
        "NOP",
        "ORA",
        "ASL",
        "SLO",
        "PHP",
        "ORA",
        "ASL",
        "ANC",
        "NOP",
        "ORA",
        "ASL",
        "SLO",
        "BPL",
        "ORA",
        "KIL",
        "SLO",
        "NOP",
        "ORA",
        "ASL",
        "SLO",
        "CLC",
        "ORA",
        "NOP",
        "SLO",
        "NOP",
        "ORA",
        "ASL",
        "SLO",
        "JSR",
        "AND",
        "KIL",
        "RLA",
        "BIT",
        "AND",
        "ROL",
        "RLA",
        "PLP",
        "AND",
        "ROL",
        "ANC",
        "BIT",
        "AND",
        "ROL",
        "RLA",
        "BMI",
        "AND",
        "KIL",
        "RLA",
        "NOP",
        "AND",
        "ROL",
        "RLA",
        "SEC",
        "AND",
        "NOP",
        "RLA",
        "NOP",
        "AND",
        "ROL",
        "RLA",
        "RTI",
        "EOR",
        "KIL",
        "SRE",
        "NOP",
        "EOR",
        "LSR",
        "SRE",
        "PHA",
        "EOR",
        "LSR",
        "ALR",
        "JMP",
        "EOR",
        "LSR",
        "SRE",
        "BVC",
        "EOR",
        "KIL",
        "SRE",
        "NOP",
        "EOR",
        "LSR",
        "SRE",
        "CLI",
        "EOR",
        "NOP",
        "SRE",
        "NOP",
        "EOR",
        "LSR",
        "SRE",
        "RTS",
        "ADC",
        "KIL",
        "RRA", # ROR then ADC
        "NOP",
        "ADC",
        "ROR",
        "RRA",
        "PLA",
        "ADC",
        "ROR",
        "ARR",
        "JMP",
        "ADC",
        "ROR",
        "RRA",
        "BVS",
        "ADC",
        "KIL",
        "RRA",
        "NOP",
        "ADC",
        "ROR",
        "RRA",
        "SEI",
        "ADC",
        "NOP",
        "RRA",
        "NOP",
        "ADC",
        "ROR",
        "RRA",
        "NOP",
        "STA",
        "NOP",
        "SAX",
        "STY",
        "STA",
        "STX",
        "SAX",
        "DEY",
        "NOP",
        "TXA",
        "XAA",
        "STY",
        "STA",
        "STX",
        "SAX",
        "BCC",
        "STA",
        "KIL",
        "AHX",
        "STY",
        "STA",
        "STX",
        "SAX",
        "TYA",
        "STA",
        "TXS",
        "TAS", # unstable.
        "SHY",
        "STA",
        "SHX",
        "AHX",
        "LDY",
        "LDA",  
        "LDX",
        "LAX",
        "LDY",
        "LDA",
        "LDX",
        "LAX",
        "TAY",
        "LDA",
        "TAX",
        "LAX",
        "LDY",
        "LDA",
        "LDX",
        "LAX",
        "BCS",
        "LDA",
        "KIL",
        "LAX",
        "LDY",
        "LDA",
        "LDX",
        "LAX",
        "CLV",
        "LDA",
        "TSX",
        "LAS",
        "LDY",
        "LDA",
        "LDX",
        "LAX",
        "CPY",
        "CMP",
        "NOP",
        "DCP",
        "CPY",
        "CMP",
        "DEC",
        "DCP",
        "INY",
        "CMP",
        "DEX",
        "AXS",
        "CPY",
        "CMP",
        "DEC",
        "DCP",
        "BNE",
        "CMP",
        "KIL",
        "DCP",
        "NOP",
        "CMP",
        "DEC",
        "DCP",
        "CLD",
        "CMP",
        "NOP",
        "DCP",
        "NOP",
        "CMP",
        "DEC",
        "DCP",
        "CPX",
        "SBC",
        "NOP",
        "ISC",
        "CPX",
        "SBC",
        "INC",
        "ISC",
        "INX",
        "SBC",
        "NOP",
        "SBC",
        "CPX",
        "SBC",
        "INC",
        "ISC",
        "BEQ",
        "SBC",
        "KIL",
        "ISC",
        "NOP",
        "SBC",
        "INC",
        "ISC", # INC then SBC
        "SED",
        "SBC",
        "NOP",
        "ISC",
        "NOP",
        "SBC",
        "INC",
        "ISC",
    ]

    def AHX(self, opcode):
        # page stuck 0x93, 0x9F
        raise NotImplementedError("AHX not implemented")
        sys.exit(1)

    def ALR(self, opcode):
        raise NotImplementedError("ALR not implemented")
        sys.exit(1)

    def ANC(self, opcode):
        raise NotImplementedError("ANC not implemented")
        sys.exit(1)

    def ARR(self, opcode):
        raise NotImplementedError("ARR not implemented")
        sys.exit(1)

    def AXS(self, opcode = 0xCB):
        raise NotImplementedError("AXS not implemented")
        sys.exit(1)

    def ISC(self, opcode):
        PC = self.read_register(S_PC)
        print("PC")
        print(PC)
        sys.stdout.flush()
        raise NotImplementedError("ISC not implemented")
        sys.exit(1) # INC whatever; SBC whatever
        # EF abcd
        # FF abcd,X
        # FB abcd,Y
        # E7 ab
        # F7 ab,X
        # E3 (ab,X)
        # F3 (ab),Y
    def KIL(self, opcode):
        raise NotImplementedError("KIL not implemented")
        sys.exit(1)

    def LAS(self, opcode):
        raise NotImplementedError("LAS not implemented")
        sys.exit(1)

    LAX_addressing_modes = {
            0xA3: S_IND_X,
            0xA7: S_Z,
            0xB7: S_Z_Y,
            0xAB: S_HASH, # crashy on real C64
            0xAF: S_ABS,
            0xBF: S_ABS_Y,
            0xB3: S_IND_Y,
    }
    def LAX(self, opcode):
        addressing_mode = CPU.LAX_addressing_modes[opcode]
        value = self.load_value_advancing(addressing_mode)
        #if value is None:
        #   print("ADR", CPU.LDA_addressing_modes[opcode])
        #print("LDA result is %r" % value)
        self.write_register(S_A, value)
        self.write_register(S_X, value)
        self.update_flags_by_number(value)

    def RLA(self, opcode):
        raise NotImplementedError("RLA not implemented")
        sys.exit(1)

    def RRA(self, opcode):
        raise NotImplementedError("RRA not implemented")
        sys.exit(1)

    def SAX(self, opcode):
        raise NotImplementedError("SAX not implemented")
        sys.exit(1)

    def SHX(self, opcode):
        # page stuck
        raise NotImplementedError("SHX not implemented")
        sys.exit(1)

    def SHY(self, opcode):
        # page stuck
        raise NotImplementedError("SHY not implemented")
        sys.exit(1)

    def SLO(self, opcode):
        raise NotImplementedError("SLO not implemented")
        sys.exit(1)

    SRE_addressing_modes = {
            0x4A: S_A,
            0x47: S_Z,
            0x53: S_Z_Y,
            0x57: S_Z_X,
            0x4F: S_ABS,
            0x5F: S_ABS_X,
            0x5B: S_ABS_Y,
            0x43: S_IND_X,
            0x54: S_IND_Y,
    }
    def SRE(self, opcode): # FIXME test
        addressing_mode = CPU.SRE_addressing_modes[opcode]
        value = self.load_value_unadvancing(addressing_mode)
        if (value & 1) != 0:
            self.flags.add("C")
        else:
            self.flags.discard("C")
        result = (value >> 1) & 0xFF
        self.store_value(addressing_mode, result)
        self.write_register(S_A, result ^ self.read_register(S_A))
        self.update_flags_by_number(result)
    def TAS(self, opcode = 0x9B):
        raise NotImplementedError("TAS not implemented")
        sys.exit(1)

    def XAA(self, opcode):
        raise NotImplementedError("XAA not implemented")
        sys.exit(1)


    exotic_opcodes = set(["RRA", "TAS", "SLO", "KIL", "SHX", "SHY", "SAX", "LAS", "XAS", "ALR", "RLA", "DCP", "AHX", "ARR", "LAX", "ANC", "ISC", "XAA", "AXS", ]) # SRE

if __name__ == "__main__":
    """
    CPU_1 = CPU(MMU)
    CPU_1.write_register(S_PC, 0)
    #CPU_1.INC(0xE6)
    #print(CPU_1.read_register(S_PC))
    value = open(sys.argv[1], "rb").read()
    for i in range(len(value)):
        CPU_1.MMU.write_memory(i, ord(value[i]), 1)
    PC = 0
    CPU_1.B_disasm = True
    for i in range(100):
        CPU_1.step()
    """
