def __init__(self, nes): self.nes = nes self._addrmodes = {} self._opcodes = [None] * 256 self.reg = Mpu6502.Registers() self.memory = Memory(0x10000) self.cycles = 0 self.halt_cycles = None self._init_addrmodes() self._init_opcodes() self.reset()
class Mpu6502(object): class InvalidOpcodeException(BaseException): def __init__(self, opcode): self._opcode = opcode def __str__(self): return 'Invalid OpCode: 0x%02x' % self._opcode def get_opcode(self): return self._opcode class Registers(object): def __init__(self): self.pc = None # Program Counter, 16 bit self.sp = None # Stack Pointer, 8 bit self.ac = None # Accumulator, 8 bit self.x = None # General register, 8 bit self.y = None # General register, 8 bit self.ps = Bitset( # Processor Status, 8 * 1 bit ('carry', 1), ('zero', 1), ('interrupt', 1), ('decimal', 1), ('break_', 1), ('sixth', 1), ('overflow', 1), ('negative', 1), ) def __init__(self, nes): self.nes = nes self._addrmodes = {} self._opcodes = [None] * 256 self.reg = Mpu6502.Registers() self.memory = Memory(0x10000) self.cycles = 0 self.halt_cycles = None self._init_addrmodes() self._init_opcodes() self.reset() def __str__(self): return '<mpu6502 PC:%s PS:%s SP:%s AC:%s X:%s Y:%s>' % ( self.reg.pc, self.reg.ps, self.reg.sp, self.reg.ac, self.reg.x, self.reg.y, ) repr = __str__ def _init_addrmodes(self): for key in dir(self): fn = getattr(self, key) if not hasattr(fn, 'mnemonic'): continue self._addrmodes[fn.mnemonic] = fn def _init_opcodes(self): for key in dir(self): fn = getattr(self, key) if not hasattr(fn, 'opcodes'): continue for opcode, mnemonic, cycles in fn.opcodes: self._opcodes[opcode] = (fn, self._addrmodes[mnemonic], cycles) def _set_nz_flags(self, value): self.reg.ps.zero = 0 == value self.reg.ps.negative = value >> 7 def reset(self): self.reg.x = 0 self.reg.y = 0 self.reg.ac = 0 self.reg.pc = 0 self.reg.ps.set(0) self.reg.sp = 0xFF self.memory.reset() self.cycles = 0 self.halt_cycles = 0 def interrupt(self, type): if type == 'reset': self.reg.pc = self.memory.get_word(0xFFFC) elif type == 'nmi': self.push_word(self.reg.pc) self.push_byte(int(self.reg.ps)) self.reg.ps.interrupt = 1 self.cycles += 7 self.reg.pc = self.memory.get_word(0xFFFA) return 7 else: assert False, type def add_halt_cycles(self, cycles): self.halt_cycles += cycles def run(self, org=None): if org is None: org = self.memory.get_word(0xFFFC) self.reg.pc = org while True: self.step() def step(self): if self.halt_cycles > 0: halt_cycles = min(8, self.halt_cycles) self.cycles += halt_cycles self.halt_cycles -= halt_cycles return halt_cycles prev_cycles = self.cycles opcode = self.memory.get_byte(self.reg.pc) if not self._opcodes[opcode]: raise Mpu6502.InvalidOpcodeException(opcode) if self.nes.logfile: self.trace(opcode) self.reg.pc = (self.reg.pc + 1) & 0xFFFF self.execute_opcode(opcode) return self.cycles - prev_cycles def execute_opcode(self, opcode): fn, addrmode, cycles = self._opcodes[opcode] fn_args = getargspec(fn).args kwargs = {} offset = value = None if addrmode.mnemonic in ('impl', 'imm', 'acc'): value, extra_cycles = addrmode() else: offset, extra_cycles = addrmode() self.reg.pc += addrmode.num_operands if 'value' in fn_args and offset is not None: value = self.memory.get_byte(offset) if 'opcode' in fn_args: kwargs['opcode'] = opcode if 'value' in fn_args: kwargs['value'] = value if 'offset' in fn_args: kwargs['offset'] = offset ret = fn(**kwargs) self.cycles += cycles if ret is not None: self.cycles += ret if getattr(fn, 'use_extra_cycles', False): self.cycles += extra_cycles return ret # addressing modes {{{ @defaddrmode('acc', 0) def get_accumulator(self): return self.reg.ac, 0 @defaddrmode('impl', 0) def get_implied(self): return None, 0 @defaddrmode('imm', 1) def get_immediate(self): return self.memory.get_byte(self.reg.pc), 0 @defaddrmode('zp', 1) def get_zero_page(self): return self.memory.get_byte(self.reg.pc), 0 @defaddrmode('zp x', 1) def get_zero_page_x(self): return (self.reg.x + self.memory.get_byte(self.reg.pc)) & 0xFF, 0 @defaddrmode('zp y', 1) def get_zero_page_y(self): return (self.reg.y + self.memory.get_byte(self.reg.pc)) & 0xFF, 0 @defaddrmode('abs', 2) def get_absolute(self): return self.memory.get_word(self.reg.pc), 0 @defaddrmode('abs ind', 2) def get_absolute_indirect(self): op, _ = self.get_absolute() # Low and high byte must be taken from the same page; it wraps around in # these cases. low = self.memory.get_byte(op) high = self.memory.get_byte((op & 0xFF00) + ((op + 1) & 0xFF)) return (high << 8) | low, 0 @defaddrmode('abs x', 2) def get_absolute_x(self): arg = self.memory.get_word(self.reg.pc) arg_and_x = arg + self.reg.x extra_cycles = 0 if arg_and_x & 0xFF00 != arg & 0xFF00: extra_cycles = 1 return arg_and_x & 0xFFFF, extra_cycles @defaddrmode('abs y', 2) def get_absolute_y(self): arg = self.memory.get_word(self.reg.pc) arg_and_y = arg + self.reg.y extra_cycles = 0 if arg_and_y & 0xFF00 != arg & 0xFF00: extra_cycles = 1 return arg_and_y & 0xFFFF, extra_cycles @defaddrmode('zp ind x', 1) def get_indirect_x(self): pos, _ = self.get_zero_page_x() # If pos 0xFF, MSB is taken from 0x00 low = self.memory.get_byte(pos) high = self.memory.get_byte((pos + 1) & 0xFF) return (high << 8) | low, 0 @defaddrmode('zp ind y', 1) def get_indirect_y(self): operand = self.memory.get_byte(self.reg.pc) if operand == 0xFF: arg = (self.memory.get_word(0) << 8) + self.memory.get_byte(0xFF) else: arg = self.memory.get_word(operand) arg_and_y = arg + self.reg.y & 0xFFFF if arg_and_y == 0xFFFF: assert False extra_cycles = 0 if arg_and_y & 0xFF00 != arg & 0xFF00: extra_cycles += 1 return arg_and_y, extra_cycles # }}} # stack methods {{{ def push_byte(self, value): self.memory.set_byte(0x100 + self.reg.sp, value) self.reg.sp -= 1 def push_word(self, value): self.push_byte((value >> 8) & 0xFF) self.push_byte(value & 0xFF) def pop_byte(self): self.reg.sp += 1 return self.memory.get_byte(0x100 + self.reg.sp) def pop_word(self): a = self.pop_byte() b = self.pop_byte() return a | (b << 8) # }}} # valid opcodes {{{ @opcode_use_extra_cycles @defopcode((0xA9, 'imm', 2), (0xA5, 'zp', 3), (0xB5, 'zp x', 4), (0xAD, 'abs', 4), (0xBD, 'abs x', 4), (0xB9, 'abs y', 4), (0xA1, 'zp ind x', 6), (0xB1, 'zp ind y', 5)) def op_lda(self, value): self.reg.ac = value self._set_nz_flags(self.reg.ac) @opcode_use_extra_cycles @defopcode((0xA2, 'imm', 2), (0xA6, 'zp', 3), (0xB6, 'zp y', 4), (0xAE, 'abs', 4), (0xBE, 'abs y', 4)) def op_ldx(self, value): self.reg.x = value self._set_nz_flags(self.reg.x) @opcode_use_extra_cycles @defopcode((0xA0, 'imm', 2), (0xA4, 'zp', 3), (0xB4, 'zp x', 4), (0xAC, 'abs', 4), (0xBC, 'abs x', 4)) def op_ldy(self, value): self.reg.y = value self._set_nz_flags(self.reg.y) @defopcode((0x85, 'zp', 3), (0x95, 'zp x', 4), (0x8D, 'abs', 4), (0x9D, 'abs x', 5), (0x99, 'abs y', 5), (0x81, 'zp ind x', 6), (0x91, 'zp ind y', 6)) def op_sta(self, offset): self.memory.set_byte(offset, self.reg.ac) @defopcode((0x86, 'zp', 3), (0x96, 'zp y', 4), (0x8E, 'abs', 4)) def op_stx(self, offset): self.memory.set_byte(offset, self.reg.x) @defopcode((0x84, 'zp', 3), (0x94, 'zp x', 4), (0x8C, 'abs', 4)) def op_sty(self, offset): self.memory.set_byte(offset, self.reg.y) @opcode_use_extra_cycles @defopcode((0x29, 'imm', 2), (0x25, 'zp', 3), (0x35, 'zp x',4 ), (0x2D, 'abs', 4), (0x3D, 'abs x', 4), (0x39, 'abs y', 4), (0x21, 'zp ind x', 6), (0x31, 'zp ind y', 5)) def op_and(self, value): self.reg.ac &= value self._set_nz_flags(self.reg.ac) @opcode_use_extra_cycles @defopcode((0x09, 'imm', 2), (0x05, 'zp', 3), (0x15, 'zp x', 4), (0x0D, 'abs', 4), (0x1D, 'abs x', 4), (0x19, 'abs y', 4), (0x01, 'zp ind x', 6), (0x11, 'zp ind y', 5)) def op_ora(self, value): self.reg.ac |= value self._set_nz_flags(self.reg.ac) @opcode_use_extra_cycles @defopcode((0x49, 'imm', 2), (0x45, 'zp', 3), (0x55, 'zp x', 4), (0x4D, 'abs', 4), (0x5D, 'abs x', 4), (0x59, 'abs y', 4), (0x41, 'zp ind x', 6), (0x51, 'zp ind y', 5)) def op_eor(self, value): self.reg.ac ^= value self._set_nz_flags(self.reg.ac) @defopcode((0x24, 'zp', 3), (0x2C, 'abs', 4)) def op_bit(self, value): self.reg.ps.zero = 0 == value & self.reg.ac self.reg.ps.negative = value >> 7 self.reg.ps.overflow = (value >> 6) & 1 @opcode_use_extra_cycles @defopcode((0x69, 'imm', 2), (0x65, 'zp', 3), (0x75, 'zp x', 4), (0x6D, 'abs', 4), (0x7D, 'abs x', 4), (0x79, 'abs y', 4), (0x61, 'zp ind x', 6), (0x71, 'zp ind y', 5)) def op_adc(self, value): carry = self.reg.ps.carry # TODO: Decimal mode removed temporarily. result = signed_byte(value) + signed_byte(self.reg.ac) + carry self.reg.ps.overflow = result > 127 or result < -128 self.reg.ps.carry = value + self.reg.ac + carry > 255 result &= 0xFF self.reg.ac = result self._set_nz_flags(self.reg.ac) @opcode_use_extra_cycles @defopcode((0xE9, 'imm', 2), (0xE5, 'zp', 3), (0xF5, 'zp x', 4), (0xED, 'abs', 4), (0xFD, 'abs x', 4), (0xF9, 'abs y', 4), (0xE1, 'zp ind x', 6), (0xF1, 'zp ind y', 5)) def op_sbc(self, value): return self.op_adc(value ^ 0xFF) @defopcode((0x0A, 'acc', 2), (0x06, 'zp', 5), (0x16, 'zp x', 6), (0x0E, 'abs', 6), (0x1E, 'abs x', 7)) def op_asl(self, offset, value): self.reg.ps.carry = value >> 7 value = (value << 1) & 0xFF if offset is None: # 0x0A self.reg.ac = value else: self.memory.set_byte(offset, value) self._set_nz_flags(value) @defopcode((0x4A, 'acc', 2), (0x46, 'zp', 5), (0x56, 'zp x', 6), (0x4E, 'abs', 6), (0x5E, 'abs x', 7)) def op_lsr(self, offset, value): self.reg.ps.carry = value & 1 value >>= 1 if offset is None: # 0x4A self.reg.ac = value else: self.memory.set_byte(offset, value) self._set_nz_flags(value) @defopcode((0x2A, 'acc', 2), (0x26, 'zp', 5), (0x36, 'zp x', 6), (0x2E, 'abs', 6), (0x3E, 'abs x', 7)) def op_rol(self, offset, value): carry = self.reg.ps.carry self.reg.ps.carry = value >> 7 value = ((value << 1) + carry) & 0xFF if offset is None: # 0x2A self.reg.ac = value else: self.memory.set_byte(offset, value) self._set_nz_flags(value) @defopcode((0x6A, 'acc', 2), (0x66, 'zp', 5), (0x76, 'zp x', 6), (0x6E, 'abs', 6), (0x7E, 'abs x', 7)) def op_ror(self, offset, value): new_carry = value & 1 value = (value >> 1) | (self.reg.ps.carry << 7) self.reg.ps.carry = new_carry if offset is None: # 0x6A self.reg.ac = value else: self.memory.set_byte(offset, value) self._set_nz_flags(value) @opcode_use_extra_cycles @defopcode((0xC9, 'imm', 2), (0xC5, 'zp', 3), (0xD5, 'zp x', 4), (0xCD, 'abs', 4), (0xDD, 'abs x', 4), (0xD9, 'abs y', 4), (0xC1, 'zp ind x', 6), (0xD1, 'zp ind y', 5)) def op_cmp(self, value): self.reg.ps.carry = self.reg.ac >= value self._set_nz_flags((self.reg.ac - value) & 0xFF) @defopcode((0xE0, 'imm', 2), (0xE4, 'zp', 3), (0xEC, 'abs', 4)) def op_cpx(self, value): self.reg.ps.carry = self.reg.x >= value self._set_nz_flags((self.reg.x - value) & 0xFF) @defopcode((0xC0, 'imm', 2), (0xC4, 'zp', 3), (0xCC, 'abs', 4)) def op_cpy(self, value): self.reg.ps.carry = self.reg.y >= value self._set_nz_flags((self.reg.y - value) & 0xFF) @defopcode((0xE6, 'zp', 5), (0xF6, 'zp x', 6), (0xEE, 'abs', 6), (0xFE, 'abs x', 7)) def op_inc(self, offset, value): value = (value + 1) & 0xFF self.memory.set_byte(offset, value) self._set_nz_flags(value) @defopcode((0xC6, 'zp', 5), (0xd6, 'zp x', 6), (0xCE, 'abs', 6), (0xDE, 'abs x', 7)) def op_dec(self, offset, value): value = (value - 1) & 0xFF self.memory.set_byte(offset, value) self._set_nz_flags(value) @defopcode_implied(0xE8, 2) def op_inx(self): self.reg.x = (self.reg.x + 1) & 0xFF self._set_nz_flags(self.reg.x) @defopcode_implied(0xCA, 2) def op_dex(self): self.reg.x = (self.reg.x - 1) & 0xFF self._set_nz_flags(self.reg.x) @defopcode_implied(0xC8, 2) def op_iny(self): self.reg.y = (self.reg.y + 1) & 0xFF self._set_nz_flags(self.reg.y) @defopcode_implied(0x88, 2) def op_dey(self): self.reg.y = (self.reg.y - 1) & 0xFF self._set_nz_flags(self.reg.y) @defopcode_implied(0xAA, 2) def op_tax(self): self.reg.x = self.reg.ac self._set_nz_flags(self.reg.x) @defopcode_implied(0xA8, 2) def op_tay(self): self.reg.y = self.reg.ac self._set_nz_flags(self.reg.y) @defopcode_implied(0xBA, 2) def op_tsx(self): self.reg.x = self.reg.sp self._set_nz_flags(self.reg.x) @defopcode_implied(0x8A, 2) def op_txa(self): self.reg.ac = self.reg.x self._set_nz_flags(self.reg.ac) @defopcode_implied(0x9A, 2) def op_txs(self): self.reg.sp = self.reg.x @defopcode_implied(0x98, 2) def op_tya(self): self.reg.ac = self.reg.y self._set_nz_flags(self.reg.ac) @defopcode_implied(0x48, 3) def op_pha(self): self.push_byte(self.reg.ac) @defopcode_implied(0x08, 3) def op_php(self): self.push_byte(int(self.reg.ps) | (1 << 4) | (1 << 5)) @defopcode_implied(0x68, 4) def op_pla(self): self.reg.ac = self.pop_byte() self._set_nz_flags(self.reg.ac) @defopcode_implied(0x28, 4) def op_plp(self): ps = self.pop_byte() & ~(1 << 4) | (1 << 5) self.reg.ps.set(ps) @defopcode_implied(0x18, 2) def op_clc(self): self.reg.ps.carry = 0 @defopcode_implied(0xD8, 2) def op_cld(self): self.reg.ps.decimal = 0 @defopcode_implied(0x58, 2) def op_cli(self): self.reg.ps.interrupt = 0 @defopcode_implied(0xB8, 2) def op_clv(self): self.reg.ps.overflow = 0 @defopcode_implied(0x38, 2) def op_sec(self): self.reg.ps.carry = 1 @defopcode_implied(0xF8, 2) def op_sed(self): self.reg.ps.decimal = 1 @defopcode_implied(0x78, 2) def op_sei(self): self.reg.ps.interrupt = 1 @defopcode_implied(0xEA, 2) def op_nop(self): pass @defopcode_implied(0x00, 7) def op_brk(self): self.push_word((self.reg.pc + 1) & 0xFFFF) self.push_byte(int(self.reg.ps) | (1 << 4) | (1 << 5)) self.reg.pc = self.memory.get_word(0xFFFE) @defopcode_implied(0x40, 6) def op_rti(self): self.reg.ps.set(self.pop_byte() | (1 << 5)) self.reg.pc = self.pop_word() @defopcode((0x4C, 'abs', 3), (0x6C, 'abs ind', 5)) def op_jmp(self, offset): self.reg.pc = offset @defopcode((0x20, 'abs', 6)) def op_jsr(self, offset): self.push_word((self.reg.pc - 1) & 0xFFFF) self.reg.pc = offset @defopcode_implied(0x60, 6) def op_rts(self, value): self.reg.pc = (self.pop_word() + 1) & 0xFFFF @defopcode((0x90, 'imm', 2), (0xB0, 'imm', 2), (0xD0, 'imm', 2), (0xF0, 'imm', 2), (0x10, 'imm', 2), (0x30, 'imm', 2), (0x50, 'imm', 2), (0x70, 'imm', 2)) def op_branch(self, value, opcode): flags = { 0x90: 'carry', 0xB0: 'carry', 0xD0: 'zero', 0xF0: 'zero', 0x10: 'negative', 0x30: 'negative', 0x50: 'overflow', 0x70: 'overflow', } cond = bool(getattr(self.reg.ps, flags[opcode])) branch_if_true = opcode in (0xB0, 0xF0, 0x30, 0x70) if cond == branch_if_true: old_pc = self.reg.pc new_pc = (self.reg.pc + signed_byte(value)) & 0xFFFF self.reg.pc = new_pc return 2 if (old_pc & 0xFF00) != (new_pc & 0xFF00) else 1 # }}} # invalid opcodes {{{ @opcode_use_extra_cycles @opcode_invalid @defopcode((0x04, 'zp', 3), (0x14, 'zp x', 4), (0x34, 'zp x', 4), (0x44, 'zp', 3), (0x54, 'zp x', 4), (0x64, 'zp', 3), (0x74, 'zp x', 4), (0x80, 'imm', 2), (0x82, 'imm', 2), (0x89, 'imm', 2), (0xC2, 'imm', 2), (0xD4, 'zp x', 4), (0xE2, 'imm', 2), (0xF4, 'zp x', 4), (0x0C, 'abs', 4), (0x1C, 'abs x', 4), (0x3C, 'abs x', 4), (0x5C, 'abs x', 4), (0x7C, 'abs x', 4), (0xDC, 'abs x', 4), (0xFC, 'abs x', 4), (0x1A, 'impl', 2), (0x3A, 'impl', 2), (0x5A, 'impl', 2), (0x7A, 'impl', 2), (0xDA, 'impl', 2), (0xFA, 'impl', 2)) def op_nop2(self): pass @opcode_use_extra_cycles @opcode_invalid @defopcode((0xA7, 'zp', 3), (0xB7, 'zp y', 4), (0xAF, 'abs', 4), (0xBF, 'abs y', 4), (0xA3, 'zp ind x', 6), (0xB3, 'zp ind y', 5)) def op_lax(self, value): self.reg.x = value self.reg.ac = value self._set_nz_flags(self.reg.ac) @opcode_invalid @defopcode((0x87, 'zp', 3), (0x97, 'zp y', 4), (0x83, 'zp ind x', 6), (0x8F, 'abs', 4)) def op_sax(self, offset): self.memory.set_byte(offset, self.reg.ac & self.reg.x) @opcode_invalid @defopcode((0xEB, 'imm', 2)) def op_sbc2(self, value): return self.op_sbc(value) @opcode_invalid @defopcode((0xC7, 'zp', 5), (0xD7, 'zp x', 6), (0xCF, 'abs', 6), (0xDF, 'abs x', 7), (0xDB, 'abs y', 7), (0xC3, 'zp ind x', 8), (0xD3, 'zp ind y', 8)) def op_dcp(self, offset, value): value = (value - 1) & 0xFF self.op_cmp(value) self.memory.set_byte(offset, value) @opcode_invalid @defopcode((0xE7, 'zp', 5), (0xF7, 'zp x', 6), (0xEF, 'abs', 6), (0xFF, 'abs x', 7), (0xFB, 'abs y', 7), (0xE3, 'zp ind x', 8), (0xF3, 'zp ind y', 8)) def op_isb(self, offset, value): value = (value + 1) & 0xFF self.op_sbc(value) self.memory.set_byte(offset, value) @opcode_invalid @defopcode((0x07, 'zp', 5), (0x17, 'zp x', 6), (0x0F, 'abs', 6), (0x1F, 'abs x', 7), (0x1B, 'abs y', 7), (0x03, 'zp ind x', 8), (0x13, 'zp ind y', 8)) def op_slo(self, offset, value): self.reg.ps.carry = value >> 7 value = (value << 1) & 0xFF self.op_ora(value) self.memory.set_byte(offset, value) @opcode_invalid @defopcode((0x27, 'zp', 5), (0x37, 'zp x', 6), (0x2F, 'abs', 6), (0x3F, 'abs x', 7), (0x3B, 'abs y', 7), (0x23, 'zp ind x', 8), (0x33, 'zp ind y', 8)) def op_rla(self, offset, value): carry = self.reg.ps.carry self.reg.ps.carry = value >> 7 value = ((value << 1) & 0xFF) | carry self.op_and(value) self.memory.set_byte(offset, value) @opcode_invalid @defopcode((0x47, 'zp', 5), (0x57, 'zp x', 6), (0x4F, 'abs', 6), (0x5F, 'abs x', 7), (0x5B, 'abs y', 7), (0x43, 'zp ind x', 8), (0x53, 'zp ind y', 8)) def op_sre(self, offset, value): carry = value & 1 value >>= 1 self.op_eor(value) self.reg.ps.carry = carry self.memory.set_byte(offset, value) @opcode_invalid @defopcode((0x67, 'zp', 5), (0x77, 'zp x', 6), (0x6F, 'abs', 6), (0x7F, 'abs x', 7), (0x7B, 'abs y', 7), (0x63, 'zp ind x', 8), (0x73, 'zp ind y', 8)) def op_rra(self, offset, value): carry = self.reg.ps.carry self.reg.ps.carry = value & 1 value = (value >> 1) | (carry << 7) self.op_adc(value) self.memory.set_byte(offset, value) # }}} # trace / debug {{{ def trace(self, opcode): fn, addrmode, _ = self._opcodes[opcode] num_operands = addrmode.num_operands operands = [self.memory.get_byte(self.reg.pc + i) for i in xrange(1, num_operands + 1)] nestest_trace = getattr(self.nes.logfile, 'nestest_trace', False) asm = disassemble(self, opcode, operands, nestest_trace) cyc = (self.cycles * 3) % 341 if nestest_trace: sl = (self.cycles * 3) / 341 sl += 241 while sl >= 261: sl -= 262 else: sl = self.nes.ppu.scanline flags = '' for key, val in (('negative', 'n'), ('overflow', 'v'), ('sixth', 'u'), ('break_', 'b'), ('decimal', 'd'), ('interrupt', 'i'), ('zero', 'z'), ('carry', 'c')): flags += val.upper() if getattr(self.reg.ps, key) else val if nestest_trace: string = ( ('%04X %02X %s %s%s A:%02X ' 'X:%02X Y:%02X P:%02X SP:%02X CYC:%3d SL:%d') % ( self.reg.pc, opcode, ' '.join('%02X' % o for o in operands).ljust(5), '*' if getattr(fn, 'invalid_opcode', False) else ' ', asm, self.reg.ac, self.reg.x, self.reg.y, int(self.reg.ps), self.reg.sp, cyc, sl, ) ) else: string = ( ('%04X %02X %s %s%s A:%02X ' 'X:%02X Y:%02X S:%02X P:%s') % ( self.reg.pc, opcode, ' '.join('%02X' % o for o in operands).ljust(5), '*' if getattr(fn, 'invalid_opcode', False) else ' ', asm, self.reg.ac, self.reg.x, self.reg.y, self.reg.sp, flags, ) ) self.nes.log(string)