import abc from .arch_msp430 import ArchMSP430 from pyvex.lifting.util import Instruction, JumpKind, ParseError, Type import bitstring from bitstring import Bits import logging l = logging.getLogger(__name__) REGISTER_TYPE = Type.int_16 BYTE_TYPE = Type.int_8 INDEX_TYPE = Type.int_16 STATUS_REG_IND = 2 CARRY_BIT_IND = 0 NEGATIVE_BIT_IND = 2 ZERO_BIT_IND = 1 OVERFLOW_BIT_IND = 8 ## ## NOTE: The bitstream legend for this arch is: # s: source # d: destination # A: source addressing mode # a: destination addressing mode # S: Extension word source immediate # D: extension word destination immediate # b: byte/word flag # o: opcode # O: Offset immediate # Lots of things are going to be interpreted as signed immediates. Here's a quickie to load them def bits_to_signed_int(s): return Bits(bin=s).int class MSP430Instruction(Instruction): opcode = None def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.commit_func = None # Default flag handling def zero(self, *args): #pylint: disable=unused-argument retval = args[-1] return retval == self.constant(0, retval.ty) def negative(self, *args): #pylint: disable=unused-argument retval = args[-1] return retval[15] if self.data['b'] == '0' else retval[7] def carry(self, *args): #pylint: disable=unused-argument,no-self-use return None def overflow(self, *args): #pylint: disable=unused-argument,no-self-use return None # Some common stuff we use around def get_sr(self): return self.get('sr', REGISTER_TYPE) def get_pc(self): return self.get('pc', REGISTER_TYPE) def put_sr(self, val): return self.put(val, 2) def get_carry(self): return self.get_sr()[CARRY_BIT_IND] def get_zero(self): return self.get_sr()[ZERO_BIT_IND] def get_negative(self): return self.get_sr()[NEGATIVE_BIT_IND] def get_overflow(self): return self.get_sr()[OVERFLOW_BIT_IND] def commit_result(self, res): #pylint: disable=not-callable if self.commit_func is not None: self.commit_func(res) def match_instruction(self, data, bitstrm): # NOTE: The matching behavior for instructions is a "try-them-all-until-it-fits" approach. # Static bits are already checked, so we just look for the opcode. if data['o'] != self.opcode: raise ParseError("Invalid opcode, expected %s, got %s" % (self.opcode, data['o'])) return True def parse(self, bitstrm): """ MSP430 instructions can have one or two extension words for 16 bit immediates We therefore extend the normal parsing so that we make sure we can get another word if we have to. """ data = Instruction.parse(self, bitstrm) data['S'] = None data['D'] = None # We don't always have a source or destination. # Theoretically I could put these in the TypeXInstruction classes, but # I'm lazy. Note that we resolve these here, as opposed to later, due to # needing to fiddle with the bitstream. l.debug(data) if 's' in data: src_mode = int(data['A'], 2) if (src_mode == ArchMSP430.Mode.INDEXED_MODE and data['s'] != '0011') \ or (data['s'] == '0000' and src_mode == ArchMSP430.Mode.INDIRECT_AUTOINCREMENT_MODE): data['S'] = bitstring.Bits(uint=bitstrm.read('uintle:16'), length=16).bin self.bitwidth += 16 # pylint: disable=no-member if 'd' in data: dst_mode = int(data['a'], 2) if dst_mode == ArchMSP430.Mode.INDEXED_MODE: data['D'] = bitstring.Bits(uint=bitstrm.read('uintle:16'), length=16).bin self.bitwidth += 16 # pylint: disable=no-member return data def compute_flags(self, *args): """ Compute the flags touched by each instruction and store them in the status register """ z = self.zero(*args) n = self.negative(*args) c = self.carry(*args) # pylint: disable=assignment-from-no-return,assignment-from-none o = self.overflow(*args) # pylint: disable=assignment-from-no-return,assignment-from-none self.set_flags(z, n, c, o) def set_flags(self, z, n, c, o): # TODO: FIXME: This isn't actually efficient. if not z and not o and not c and not n: return flags = [(z, ZERO_BIT_IND, 'Z'), (n, NEGATIVE_BIT_IND, 'N'), (o, OVERFLOW_BIT_IND, 'V'), (c, CARRY_BIT_IND, 'C')] sreg = self.get_sr() for flag, offset, _ in flags: if flag: sreg = sreg & ~(1 << offset) | (flag.cast_to(Type.int_16) << offset).cast_to(sreg.ty) self.put_sr(sreg) ## ## Functions for dealing with MSP430's complex addressing modes ## def decorate_src(self, src_bits, mode_bits, imm_bits): """ Computes the decorated source operand for disassembly """ src = ArchMSP430.register_index[int(src_bits, 2)] src_mode = int(mode_bits, 2) # Load the immediate word src_imm = None if imm_bits: src_imm = bits_to_signed_int(imm_bits) # Symbolic and Immediate modes use the PC as the source. if src == 'pc': if src_mode == ArchMSP430.Mode.INDEXED_MODE: src_mode = ArchMSP430.Mode.SYMBOLIC_MODE elif src_mode == ArchMSP430.Mode.INDIRECT_AUTOINCREMENT_MODE: src_mode = ArchMSP430.Mode.IMMEDIATE_MODE # Resolve the constant generator stuff. elif src == 'cg': if src_mode == ArchMSP430.Mode.REGISTER_MODE: src_mode = ArchMSP430.Mode.CONSTANT_MODE0 elif src_mode == ArchMSP430.Mode.INDEXED_MODE: src_mode = ArchMSP430.Mode.CONSTANT_MODE1 elif src_mode == ArchMSP430.Mode.INDIRECT_REGISTER_MODE: src_mode = ArchMSP430.Mode.CONSTANT_MODE2 else: src_mode = ArchMSP430.Mode.CONSTANT_MODE_NEG1 # If you use the SR as the source. things get weird. elif src == 'sr': if src_mode == ArchMSP430.Mode.INDEXED_MODE: src_mode = ArchMSP430.Mode.ABSOLUTE_MODE elif src_mode == ArchMSP430.Mode.INDIRECT_REGISTER_MODE: src_mode = ArchMSP430.Mode.CONSTANT_MODE4 elif src_mode == ArchMSP430.Mode.INDIRECT_AUTOINCREMENT_MODE: src_mode = ArchMSP430.Mode.CONSTANT_MODE8 # Fetch constants if src_mode == ArchMSP430.Mode.CONSTANT_MODE0: src_str = "#0" elif src_mode == ArchMSP430.Mode.CONSTANT_MODE1: src_str = "#1" elif src_mode == ArchMSP430.Mode.CONSTANT_MODE2: src_str = "#2" elif src_mode == ArchMSP430.Mode.CONSTANT_MODE4: src_str = "#4" elif src_mode == ArchMSP430.Mode.CONSTANT_MODE8: src_str = "#8" elif src_mode == ArchMSP430.Mode.CONSTANT_MODE_NEG1: src_str = "#-1" # Fetch immediate. elif src_mode == ArchMSP430.Mode.IMMEDIATE_MODE: src_str = str(bits_to_signed_int(imm_bits)) # Symbolic mode: Add the immediate to the PC elif src_mode == ArchMSP430.Mode.SYMBOLIC_MODE: src_str = "%s+%d" % (src, bits_to_signed_int(imm_bits)) else: # Register mode can write-out to the source for one-operand, so set the writeout src_str = self.decorate_reg(src, src_mode, src_imm) return src_str def fetch_src(self, src_bits, mode_bits, imm_bits, ty): """ Fetch the ``source'' operand of an instruction. Returns the source as a VexValue, and, if it exists, a function for how it can be written to if needed (e.g., one-operand instructions) :param src_bits: bit-string of the src :param mode_bits: bit-string of the mode :param imm_bits: bit-string of the immediate :param ty: The type to use (the byte type or word type) :return: The src as a VexValue, and a lambda describing how to write to it if necessary """ src_num = int(src_bits, 2) src_name = ArchMSP430.register_index[src_num] src_mode = int(mode_bits, 2) writeout = None # Load the immediate word src_imm = None if imm_bits: src_imm = bits_to_signed_int(imm_bits) # Symbolic and Immediate modes use the PC as the source. if src_name == 'pc': if src_mode == ArchMSP430.Mode.INDEXED_MODE: src_mode = ArchMSP430.Mode.SYMBOLIC_MODE elif src_mode == ArchMSP430.Mode.INDIRECT_AUTOINCREMENT_MODE: src_mode = ArchMSP430.Mode.IMMEDIATE_MODE # Resolve the constant generator stuff. elif src_name == 'cg': if src_mode == ArchMSP430.Mode.REGISTER_MODE: src_mode = ArchMSP430.Mode.CONSTANT_MODE0 elif src_mode == ArchMSP430.Mode.INDEXED_MODE: src_mode = ArchMSP430.Mode.CONSTANT_MODE1 elif src_mode == ArchMSP430.Mode.INDIRECT_REGISTER_MODE: src_mode = ArchMSP430.Mode.CONSTANT_MODE2 else: src_mode = ArchMSP430.Mode.CONSTANT_MODE_NEG1 # If you use the SR as the source. things get weird. elif src_name == 'sr': if src_mode == ArchMSP430.Mode.INDEXED_MODE: src_mode = ArchMSP430.Mode.ABSOLUTE_MODE elif src_mode == ArchMSP430.Mode.INDIRECT_REGISTER_MODE: src_mode = ArchMSP430.Mode.CONSTANT_MODE4 elif src_mode == ArchMSP430.Mode.INDIRECT_AUTOINCREMENT_MODE: src_mode = ArchMSP430.Mode.CONSTANT_MODE8 # Fetch constants if src_mode == ArchMSP430.Mode.CONSTANT_MODE0: src_vv = self.constant(0, ty) elif src_mode == ArchMSP430.Mode.CONSTANT_MODE1: src_vv = self.constant(1, ty) elif src_mode == ArchMSP430.Mode.CONSTANT_MODE2: src_vv = self.constant(2, ty) elif src_mode == ArchMSP430.Mode.CONSTANT_MODE4: src_vv = self.constant(4, ty) elif src_mode == ArchMSP430.Mode.CONSTANT_MODE8: src_vv = self.constant(8, ty) elif src_mode == ArchMSP430.Mode.CONSTANT_MODE_NEG1: src_vv = self.constant(-1, ty) # Fetch immediate. elif src_mode == ArchMSP430.Mode.IMMEDIATE_MODE: src_vv = self.constant(bits_to_signed_int(imm_bits), ty) # Symbolic mode: Add the immediate to the PC elif src_mode == ArchMSP430.Mode.SYMBOLIC_MODE: src_vv = self.get(src_num, Type.int_16) + bits_to_signed_int(imm_bits) + 2 else: # Register mode can write-out to the source for one-operand, so set the writeout src_vv, writeout = self.fetch_reg(src_num, src_mode, src_imm, ty) return src_vv, writeout def decorate_dst(self, dst_bits, mode_bits, imm_bits): """ Computes the decorated destination operand for disassembly """ dst = ArchMSP430.register_index[int(dst_bits, 2)] dst_mode = int(mode_bits, 2) dst_imm = None # Using sr as the dst enables "absolute addressing" if dst == 'sr' and dst_mode == ArchMSP430.Mode.INDEXED_MODE: dst_mode = ArchMSP430.Mode.ABSOLUTE_MODE if imm_bits: dst_imm = bits_to_signed_int(imm_bits) # two-op instructions always have a dst dst_str = self.decorate_reg(dst, dst_mode, dst_imm) # val = val.cast_to(ty) return dst_str def fetch_dst(self, dst_bits, mode_bits, imm_bits, ty): """ Fetch the destination argument. :param dst_bits: :param mode_bits: :param imm_bits: :param ty: :return: The VexValue representing the destination, and the writeout function for it """ dst_num = int(dst_bits, 2) dst_name = ArchMSP430.register_index[dst_num] dst_mode = int(mode_bits, 2) dst_imm = None # Using sr as the dst enables "absolute addressing" if dst_name == 'sr' and dst_mode == ArchMSP430.Mode.INDEXED_MODE: dst_mode = ArchMSP430.Mode.ABSOLUTE_MODE if imm_bits: dst_imm = int(imm_bits, 2) # two-op instructions always have a dst and a writeout val, writeout = self.fetch_reg(dst_num, dst_mode, dst_imm, ty) # val = val.cast_to(ty) return val, writeout def decorate_reg(self, reg_name, reg_mode, imm): # pylint: disable=no-self-use """ Decorate the register argument used for disassembly """ # Boring register mode. A write is just a Put. if reg_mode == ArchMSP430.Mode.REGISTER_MODE: reg_str = reg_name # Indexed mode, add the immediate to the register elif reg_mode == ArchMSP430.Mode.INDEXED_MODE: reg_str = "%d(%s)" % (imm, reg_name) # Indirect mode; fetch address in register; store is a write there. elif reg_mode == ArchMSP430.Mode.INDIRECT_REGISTER_MODE: reg_str = "@%s" % reg_str # Indirect Autoincrement mode. Increment the register by the type size, then access it elif reg_mode == ArchMSP430.Mode.INDIRECT_AUTOINCREMENT_MODE: reg_str = "@%s+" % reg_name elif reg_mode == ArchMSP430.Mode.ABSOLUTE_MODE: reg_str = imm else: raise Exception('Unknown mode found') return reg_str def fetch_reg(self, reg_num, reg_mode, imm, ty): """ Resolve the operand for register-based modes. :param reg_num: The Register Number :param reg_mode: The Register Mode :param imm: The immediate word, if any :param ty: The Type (byte or word) :return: The VexValue of the operand, and the writeout function, if any. """ # Boring register mode. A write is just a Put. if reg_mode == ArchMSP430.Mode.REGISTER_MODE: # Fetch the register reg_vv = self.get(reg_num, ty) val = reg_vv writeout = lambda v: self.put(v.cast_to(REGISTER_TYPE), reg_num) # Indexed mode, add the immediate to the register # A write here is a store to reg + imm elif reg_mode == ArchMSP430.Mode.INDEXED_MODE: # Fetch the register reg_vv = self.get(reg_num, REGISTER_TYPE) addr_val = reg_vv + imm val = self.load(addr_val, ty) writeout = lambda v: self.store(v, addr_val) # Indirect mode; fetch address in register; store is a write there. elif reg_mode == ArchMSP430.Mode.INDIRECT_REGISTER_MODE: # Fetch the register reg_vv = self.get(reg_num, REGISTER_TYPE) val = self.load(reg_vv, ty) writeout = lambda v: self.store(v, reg_vv) # Indirect Autoincrement mode. Increment the register by the type size, then access it elif reg_mode == ArchMSP430.Mode.INDIRECT_AUTOINCREMENT_MODE: if ty == Type.int_16: incconst = self.constant(2, REGISTER_TYPE) else: incconst = self.constant(1, REGISTER_TYPE) # Fetch the register reg_vv = self.get(reg_num, REGISTER_TYPE) # Do the increment, now self.put(reg_vv + incconst, reg_num) # Now load it. val = self.load(reg_vv, ty) writeout = lambda v: self.store(v, reg_num) elif reg_mode == ArchMSP430.Mode.ABSOLUTE_MODE: imm_vv = self.constant(imm, REGISTER_TYPE) val = self.load(imm_vv, ty) writeout = lambda v: self.store(v, imm_vv) else: raise Exception('Unknown mode found') return val, writeout # The TypeXInstruction classes will do this. @abc.abstractmethod def fetch_operands(self): pass ## ## MSP430 has three instruction "types" (which type is which varies depending on which docs you read) ## These define the formats, and number of arguments. ## Here are the classes for those: ## class Type1Instruction(MSP430Instruction): # A single argument bin_format = "000100ooobAAssss" def disassemble(self): self.name = self.name if self.data['b'] == '0' else self.name + ".b" src = self.decorate_src(self.data['s'], self.data['A'], self.data['S']) return self.addr, self.name, [src, ] @abc.abstractmethod def compute_result(self, src): # pylint: disable=arguments-differ pass def fetch_operands(self): ty = Type.int_16 if self.data['b'] == '0' else Type.int_8 src, self.commit_func = self.fetch_src(self.data['s'], self.data['A'], self.data['S'], ty) return (src, ) class Type2Instruction(MSP430Instruction): # No argument; jumps and branches bin_format = "001oooOOOOOOOOOO" def disassemble(self): return self.addr, self.name, ["$" + str((bits_to_signed_int(self.data['O']) + 1) * 2)] # No flags for all of type2 def compute_flags(self, *args): pass @abc.abstractmethod def compute_result(self, offset): # pylint: disable=arguments-differ pass def fetch_operands(self): dst = self.addr + ((bits_to_signed_int(self.data['O']) + 1) * 2) return (self.constant(dst, Type.int_16), ) class Type3Instruction(MSP430Instruction): # Two arguments bin_format = 'oooossssabAAdddd' def disassemble(self): self.name = self.name if self.data['b'] == '0' else self.name + ".b" src = self.decorate_src(self.data['s'], self.data['A'], self.data['S']) dst = self.decorate_dst(self.data['d'], self.data['a'], self.data['D']) return self.addr, self.name, [src, dst] @abc.abstractmethod def compute_result(self, src, dst): # pylint: disable=arguments-differ pass def fetch_operands(self): ty = Type.int_16 if self.data['b'] == '0' else Type.int_8 src, _ = self.fetch_src(self.data['s'], self.data['A'], self.data['S'], ty) dst, self.commit_func = self.fetch_dst(self.data['d'], self.data['a'], self.data['D'], ty) return src, dst ## ## Single Operand Instructions (type 1) ## class Instruction_RRC(Type1Instruction): # Rotate Right logical with carry-in. opcode = "000" name = 'rrc' def compute_result(self, src): # Get carry-in carryin = self.get_carry() # Do it src >>= 1 # Put the carry-in in the right place if self.data['b'] == '1': src[7] = carryin else: src[15] = carryin # Write it out return src def carry(self, src, ret): # pylint: disable=arguments-differ return src[0] class Instruction_SWPB(Type1Instruction): # Swap byte halves. No B/W forms. opcode = '001' name = 'swpb' def compute_result(self, src): low_half = src.cast_to(Type.int_8).cast_to(Type.int_16) << self.constant(8, Type.int_8) # FIXME: TODO: high_half = src >> 8 return high_half | low_half class Instruction_RRA(Type1Instruction): # Rotate Right Arithmetic. Right shift with sign-extend. opcode = "010" name = 'rra' def compute_result(self, src, writeout): # Do it src >>= 1 # A shitty sign-extend if self.data['b'] == '1': src[7] = src[6] else: src[15] = src[14] return src def carry(self, src, ret): # pylint: disable=arguments-differ return src[0] class Instruction_SXT(Type1Instruction): # Sign extend 8 to 16 bits. # No b/w form. opcode = '011' name = 'sxt' def compute_result(self, src): return src.cast_to(Type.int_16, signed=True) class Instruction_PUSH(Type1Instruction): # Push src onto the stack. opcode = '100' name = 'push' def compute_result(self, src): # Decrement SP sp = self.get(1, REGISTER_TYPE) sp -= 2 # Store src at SP self.store(src, sp) # Store SP. No write-out. self.put(sp, 'sp') # No flags. def negative(self, src, ret): # pylint: disable=arguments-differ pass def zero(self, src, ret): # pylint: disable=arguments-differ pass class Instruction_CALL(Type1Instruction): opcode = '101' name = 'call' # Call src. Pushes PC. No flags. def compute_result(self, src): # Push the next instruction's address pc = self.get_pc() + self.bytewidth sp = self.get('sp', Type.int_16) sp = sp - 2 self.store(pc, sp) self.put(sp, 'sp') # This ends the BB, update the IRSB self.jump(None, src, jumpkind=JumpKind.Call) def negative(self, src, ret): # pylint: disable=arguments-differ pass def zero(self, src, ret): # pylint: disable=arguments-differ pass class Instruction_RETI(Type1Instruction): # Return *from interrupt* # Pop SR AND PC. opcode = '110' name = 'reti' def disassemble(self): return self.addr, self.name, [] def compute_result(self, src): # Pop the saved SR sp = self.get(1, REGISTER_TYPE) sr = self.get_sr() sp += 2 # Pop the saved PC newpc = self.load(sp, Type.int_16) sp += 2 # Store the popped values self.put_sr(sr) # Jump to PC (setting the jumpkind) self.jump(None, newpc, jumpkind=JumpKind.Ret) def negative(self, *args): pass def zero(self, *args): pass ## ## Two operand instructions. ## class Instruction_MOV(Type3Instruction): # Boring move. No flags. opcode = '0100' name = 'mov' def fetch_operands(self): if self.data['s'] == '0001' and self.data['d'] == '0000': return None, None else: return Type3Instruction.fetch_operands(self) def disassemble(self): # support useful pseudo-ops for disassembly addr, name, args = Type3Instruction.disassemble(self) if self.data['d'] == '0000': if self.data['s'] == '0001': return addr, 'ret', [] else: # If we're setting PC, but not from SP+, it's a BR instead return addr, 'br', [args[0]] else: return addr, name, args def compute_result(self, src, dst): # HACK: In MSP430, a MOV to R0 from SP is a RET. # VEX would like it very much if you set the jumpkind. if self.data['d'] == '0000': if self.data['s'] == '0001': sp = self.get('sp', REGISTER_TYPE) newpc = self.load(sp, REGISTER_TYPE) sp = sp + 2 self.put(sp, 'sp') self.jump(None, newpc, jumpkind=JumpKind.Ret) else: # If we're setting PC, but not from SP+, it's a BR instead self.jump(None, self.constant(self.addr, REGISTER_TYPE)) return src def negative(self, src, dst, ret): # pylint: disable=arguments-differ pass def zero(self, src, dst, ret): # pylint: disable=arguments-differ pass class Instruction_ADD(Type3Instruction): # Add src + dst, set carry opcode = '0101' name = 'add' def compute_result(self, src, dst): return dst + src def carry(self, src, dst, ret): if self.data['b'] == '0': src17 = src.cast_to(Type.int_17) dst17 = dst.cast_to(Type.int_17) ret17 = self.compute_result(src17, dst17) c = ret17[16] else: src9 = src.cast_to(Type.int_9) dst9 = dst.cast_to(Type.int_9) ret9 = self.compute_result(src9, dst9) c = ret9[8] return c def overflow(self, src, dst, ret): # pylint: disable=arguments-differ if self.data['b'] == '0': return (ret[15] ^ src[15]) & (ret[15] ^ dst[15]) else: return (ret[7] ^ src[7]) & (ret[7] ^ dst[7]) class Instruction_ADDC(Instruction_ADD): # dst = src + dst + C opcode = '0110' name = 'addc' def compute_result(self, src, dst): return dst + src + self.get_carry().cast_to(src.ty) class Instruction_SUB(Type3Instruction): # dst = dst + ~src + 1 # or # dst = dst - src opcode = '1000' name = "sub" def compute_result(self, src, dst): return dst - src def carry(self, src, dst, ret): # pylint: disable=arguments-differ return dst >= src def overflow(self, src, dst, ret): # pylint: disable=arguments-differ if self.data['b'] == '0': return (dst[15] ^ src[15]) & (ret[15] ^ dst[15]) else: return (dst[7] ^ src[7]) & (ret[7] ^ dst[7]) class Instruction_SUBC(Instruction_SUB): # dst = dst + ~src + C # or # dst = dst - src - 1 + C opcode = '0111' name = 'subc' def compute_result(self, src, dst): return dst - src - self.constant(1, src.ty) + self.get_carry() def carry(self, src, dst, ret): # Works for .w and .b mode # Equivalent with checking the carry out of the MSB of dst + ~src + C src17 = src.cast_to(Type.int_17) dst17 = dst.cast_to(Type.int_17) one17 = self.constant(1, Type.int_17) cr17 = self.get_carry().cast_to(Type.int_17) return dst17 >= src17 + one17 - cr17 class Instruction_CMP(Instruction_SUB): opcode = '1001' name = 'cmp' def commit_result(self, res): pass class Instruction_DADD(Type3Instruction): opcode = '1010' name = 'dadd' def compute_result(self, src, dst): # Ya know... fuck this... srcs = [] dsts = [] bits = 8 if self.data['b'] == '1' else 16 ret = self.constant(0, BYTE_TYPE if self.data['b'] == '1' else REGISTER_TYPE) for x in range(0, bits, 4): srcs += src[x:x+3] dsts += dst[x:x+3] carry = self.get_carry() rets = [] for s, d in zip(srcs, dsts): r = s + d + carry carry = r / 10 r %= 10 rets += r self._carry = carry #Carry computed in-line. save it. # Smash the digits back together for r, x in zip(rets, range(0, bits, 4)): ret |= (r << x).cast_to(Type.int_16) return ret def carry(self, src, dst, ret): # pylint: disable=arguments-differ return self._carry def overflow(self, src, dst, ret): # pylint: disable=arguments-differ return None # WTF: Docs say this is actually undefined!? class Instruction_BIC(Type3Instruction): # Bit Clear. dst = ~src & dst opcode = '1100' name = 'bic' def compute_result(self, src, dst): return ~src & dst def negative(self, src, dst, ret): # pylint: disable=arguments-differ pass def zero(self, src, dst, ret): # pylint: disable=arguments-differ pass class Instruction_BIS(Type3Instruction): # Bit Set. Normal people call this "or" opcode = '1101' name = 'bis' def compute_result(self, src, dst): return src | dst class Instruction_XOR(Type3Instruction): # Exclusive Or opcode = "1110" name = 'xor' def compute_result(self, src, dst): return src ^ dst def carry(self, src, dst, ret): # pylint: disable=arguments-differ return ret != self.constant(0, ret.ty) def overflow(self, src, dst, ret): # pylint: disable=arguments-differ if self.data['b'] == '1': return src[7] & dst[7] else: return src[15] & dst[15] class Instruction_AND(Type3Instruction): # Logical and. opcode = "1111" name = 'and' def compute_result(self, src, dst): return src & dst def overflow(self, src, dst, ret): # pylint: disable=arguments-differ return self.constant(0, ret.ty) def carry(self, src, dst, ret): # pylint: disable=arguments-differ return ret != self.constant(0, ret.ty) class Instruction_BIT(Instruction_AND): # Bit Test. Just update flags. No write-out opcode = "1011" name = "bit" def commit_result(self, *args): pass ## ## Zero-operand Jumps ## class Instruction_JNE(Type2Instruction): opcode = '000' name = 'jne' def compute_result(self, dst): self.jump(self.get_zero() == 0, dst) class Instruction_JEQ(Type2Instruction): opcode = '001' name = 'jeq' def compute_result(self, dst): self.jump(self.get_zero() != 0, dst) class Instruction_JNC(Type2Instruction): opcode = '010' name = 'jnc' def compute_result(self, dst): self.jump(self.get_carry() == 0, dst) class Instruction_JC(Type2Instruction): opcode = '011' name = 'jc' def compute_result(self, dst): self.jump(self.get_carry() != 0, dst) class Instruction_JN(Type2Instruction): opcode = '100' name = 'jn' def compute_result(self, dst): self.jump(self.get_negative() != 0, dst) class Instruction_JGE(Type2Instruction): opcode = '101' name = 'jge' def compute_result(self, dst): self.jump(self.get_negative() == self.get_overflow(), dst) class Instruction_JL(Type2Instruction): opcode = '110' name = 'jl' def compute_result(self, dst): self.jump(self.get_negative() != self.get_overflow(), dst) class Instruction_JMP(Type2Instruction): opcode = '111' name = 'jmp' def compute_result(self, dst): self.jump(None, dst)