├── .gitignore ├── LICENSE-0BSD.txt ├── README.md ├── __init__.py ├── doc ├── cheatsheet.ods ├── cheatsheet.pdf ├── cheatsheet.svg └── demo.png ├── mc ├── __init__.py ├── coding.py ├── instr.py └── tokens.py └── plugin.json /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /LICENSE-0BSD.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2024 Catherine 2 | 3 | Permission to use, copy, modify, and/or distribute this software for 4 | any purpose with or without fee is hereby granted. 5 | 6 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 7 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 8 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 9 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 10 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 11 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 12 | OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Binary Ninja Avnera plugin 2 | ========================== 3 | 4 | The Avnera plugin provides an Avnera AV6xxx/AV7xxx architecture for Binary Ninja. Even the existence of this architecture is not made public by Avnera; the function of the opcodes is determined via black box reverse engineering, and the mnemonics are all made up by Catherine "whitequark". 5 | 6 | Dedicated with 🧡 to [Frederick G. Weiss and his colleagues at Avnera Corporation](https://fccid.io/V3CAVMD7F11A/Letter/Confidential-Letter-1082866.pdf). 7 | 8 | ## Features 9 | 10 | * All of the instructions described in [this document](https://github.com/Prehistoricman/AV7300/blob/master/Instruction%20set%20notes.txt) are implemented, and their function (especially the use of flags) has been extensively manually verified by examining control and data flow in the available blobs. 11 | * All of the instructions are lifted to LLIL. 12 | * `MOV` instructions are fused during disassembly to `MOVW` and `MOVP` instructions when they are used in pairs implementing 16-bit operations to enable BN to discover more pointers during analysis. 13 | * `ADD R0, R0; RLC Rn` instructions are fused during disassembly to `LSL RnR0` instructions. 14 | * Opcode `B8` is not implemented as its function is not precisely known. It is likely some kind of trap, possibly debug trap. 15 | * Opcodes `A0` to `AF`, `B0` to `B7`, `BB`, `BD`, and `BE` are not implemented as their function is not known. 16 | * Opcodes `C1`, `C3`, `C5`, `C7`, `D1`, `D3`, `D5`, `D7`, `D9`, `DB`, `DD`, `DF`, `F1`, `F3`, `F5`, `F7`, `F9`, `FB`, `FD`, `FF` have known functionality but their decoding is disabled as they aren't present in the available blobs and appear to be invalid opcodes that unintentionally perform a function. 17 | 18 | ![demonstration of disassembly and decompiled HLIL on a small function implementing a 32-bit multiply](doc/demo.png) 19 | 20 | ## Devices 21 | 22 | This plugin supports at least the following devices: 23 | 24 | * AV7300 25 | * AV7320 26 | * AV6202 27 | 28 | If you've successfully used the plugin with another device, please add it to the list and send a pull request! 29 | 30 | ## Cheatsheet 31 | 32 | [![a table laying out all of the opcodes in a 16x16 grid](doc/cheatsheet.svg)](doc/cheatsheet.svg) 33 | 34 | ## Acknowledgements 35 | 36 | This plugin is heavily based on the excellent [AV7300 research](https://github.com/Prehistoricman/AV7300) by [@Prehistoricman](https://github.com/Prehistoricman). 37 | 38 | ## License 39 | 40 | [0-clause BSD](LICENSE-0BSD.txt) 41 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from binaryninja import Architecture, RegisterInfo, IntrinsicInfo, InstructionInfo, CallingConvention, Settings 4 | from binaryninja.enums import Endianness, FlagRole, LowLevelILFlagCondition 5 | from binaryninja.types import Type 6 | from binaryninja.log import log_error 7 | 8 | from . import mc 9 | 10 | 11 | __all__ = ['Avnera'] 12 | 13 | 14 | class Avnera(Architecture): 15 | name = 'avnera' 16 | endianness = Endianness.LittleEndian 17 | address_size = 2 18 | default_int_size = 1 19 | opcode_display_length = 6 # enough for a `MOVW [#abs], RbRa` fusion but not for wider ones 20 | 21 | regs = { 22 | 'R0': RegisterInfo('R0', 1), 23 | 'R1': RegisterInfo('R1', 1), 24 | 'R2': RegisterInfo('R2', 1), 25 | 'R3': RegisterInfo('R3', 1), 26 | 'R4': RegisterInfo('R4', 1), 27 | 'R5': RegisterInfo('R5', 1), 28 | 'R6': RegisterInfo('R6', 1), 29 | 'R7': RegisterInfo('R7', 1), 30 | 'SP': RegisterInfo('SP', 2), 31 | } 32 | stack_pointer = 'SP' 33 | 34 | flags = [ 35 | 'Z', # zero 36 | 'C', # carry 37 | 'N', # negative 38 | 'B', # register bank 39 | 'F4', # flag 4 40 | 'I', # interrupt 41 | 'F6', # flag 6 42 | 'F7', # flag 7 43 | ] 44 | flag_roles = { 45 | 'Z': FlagRole.ZeroFlagRole, 46 | 'C': FlagRole.CarryFlagRole, 47 | 'N': FlagRole.NegativeSignFlagRole, 48 | 'B': FlagRole.SpecialFlagRole, 49 | 'F4': FlagRole.SpecialFlagRole, 50 | 'I': FlagRole.SpecialFlagRole, 51 | 'F6': FlagRole.SpecialFlagRole, 52 | 'F7': FlagRole.SpecialFlagRole, 53 | } 54 | flag_write_types = [ 55 | 'Z', 56 | 'C', 57 | 'CN', 58 | ] 59 | flags_written_by_flag_write_type = { 60 | 'Z': ['Z'], 61 | 'C': ['C'], 62 | 'CN': ['C', 'N'], 63 | } 64 | 65 | def get_instruction_info(self, data, addr): 66 | try: 67 | if decoded := mc.decode(data, addr): 68 | info = InstructionInfo() 69 | decoded.analyze(info, addr) 70 | return info 71 | except Exception as exc: 72 | log_error(f'Avnera.get_instruction_info() failed at {addr:#x}: {exc}') 73 | 74 | def get_instruction_text(self, data, addr): 75 | try: 76 | if decoded := mc.decode(data, addr): 77 | encoded = data[:decoded.length()] 78 | recoded = mc.encode(decoded, addr) 79 | if encoded != recoded: 80 | log_error('Instruction roundtrip error:') 81 | log_error(''.join(str(token) for token in decoded.render())) 82 | log_error('Old: {}'.format(encoded.hex())) 83 | log_error('New: {}'.format(recoded.hex())) 84 | return decoded.render(), decoded.length() 85 | except Exception as exc: 86 | log_error(f'Avnera.get_instruction_text() failed at {addr:#x}: {exc}') 87 | 88 | def get_instruction_low_level_il(self, data, addr, il): 89 | try: 90 | if decoded := mc.decode(data, addr): 91 | decoded.lift(il, addr) 92 | return decoded.length() 93 | except Exception as exc: 94 | log_error(f'Avnera.get_instruction_low_level_il() failed at {addr:#x}: {exc}') 95 | 96 | def convert_to_nop(self, data, addr): 97 | # MOV R0, R0 98 | # Note that this instruction changes ZF. 99 | return b'\x70' * len(data) 100 | 101 | 102 | class AvneraCCallingConvention(CallingConvention): 103 | caller_saved_regs = ['R7', 'R6'] 104 | int_arg_regs = ['R5', 'R4', 'R3', 'R2', 'R1', 'R0'] 105 | int_return_reg = 'R0' 106 | high_int_return_reg = 'R1' 107 | 108 | 109 | settings = Settings() 110 | settings.register_setting('arch.avnera.disassembly.pseudoOps', json.dumps({ 111 | 'title': 'Avnera Disassembly Pseudo-Op', 112 | 'description': 'Enable use of pseudo-op instructions (MOVW, MOVP, LSL) in Avnera disassembly. Be aware that disabling this setting will impair lifting.', 113 | 'type': 'boolean', 114 | 'default': True 115 | })) 116 | 117 | 118 | arch = Avnera.register() 119 | arch.register_calling_convention(default_cc := AvneraCCallingConvention(arch, 'default')) 120 | arch.default_calling_convention = default_cc 121 | -------------------------------------------------------------------------------- /doc/cheatsheet.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitequark/binja-avnera/51a9e061015ea84ebfffed6ff7db9f2bc21ec2ca/doc/cheatsheet.ods -------------------------------------------------------------------------------- /doc/cheatsheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitequark/binja-avnera/51a9e061015ea84ebfffed6ff7db9f2bc21ec2ca/doc/cheatsheet.pdf -------------------------------------------------------------------------------- /doc/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitequark/binja-avnera/51a9e061015ea84ebfffed6ff7db9f2bc21ec2ca/doc/demo.png -------------------------------------------------------------------------------- /mc/__init__.py: -------------------------------------------------------------------------------- 1 | import binaryninja 2 | 3 | from .coding import * 4 | from .instr import Instruction 5 | 6 | 7 | __all__ = ['iter_decode', 'iter_encode', 'decode', 'encode', 'disassemble'] 8 | 9 | 10 | def iter_decode(data, addr): 11 | decoder = Decoder(data) 12 | while True: 13 | try: 14 | instr = Instruction(decoder=decoder) 15 | if instr is None: 16 | raise NotImplementedError( 17 | f'Cannot decode opcode {data[decoder.pos]:#04x} ' 18 | f'at address {addr + decoder.pos:#06x}') 19 | instr.decode(decoder, addr) 20 | yield instr, addr 21 | addr += instr.length() 22 | except coding.BufferTooShort: 23 | break 24 | 25 | 26 | def iter_encode(iter, addr): 27 | encoder = Encoder() 28 | for instr in iter: 29 | instr.encode(encoder, addr) 30 | addr += instr.length() 31 | return encoder.buf 32 | 33 | 34 | def fusion(iter): 35 | try: 36 | instr1, addr1 = next(iter) 37 | except StopIteration: 38 | return 39 | while True: 40 | try: 41 | instr2, addr2 = next(iter) 42 | except (StopIteration, NotImplementedError): 43 | yield instr1, addr1 44 | break 45 | if instr12 := instr1.fuse(instr2): 46 | yield instr12, addr1 47 | try: 48 | instr1, addr1 = next(iter) 49 | except (StopIteration, NotImplementedError): 50 | break 51 | else: 52 | yield instr1, addr1 53 | instr1, addr1 = instr2, addr2 54 | 55 | 56 | def _create_decoder(data, addr, *, bv=None): 57 | if binaryninja.Settings().get_bool("arch.avnera.disassembly.pseudoOps", bv): 58 | # MOV -> MOVP -> MOVW 59 | return fusion(fusion(iter_decode(data, addr))) 60 | else: 61 | return iter_decode(data, addr) 62 | 63 | 64 | def decode(data, addr, *, bv=None): 65 | try: 66 | instr, _ = next(_create_decoder(data, addr, bv=bv)) 67 | return instr 68 | except StopIteration: 69 | return None 70 | except NotImplementedError as e: 71 | binaryninja.log_warn(e) 72 | 73 | 74 | def encode(instr, addr): 75 | return iter_encode([instr], addr) 76 | 77 | 78 | def disassemble(data, addr, *, bv=None): 79 | for instr, addr in _create_decoder(data, addr, bv=bv): 80 | instr.display(addr) 81 | -------------------------------------------------------------------------------- /mc/coding.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | 4 | __all__ = ['Decoder', 'Encoder'] 5 | 6 | 7 | class BufferTooShort(Exception): 8 | pass 9 | 10 | 11 | class Decoder: 12 | def __init__(self, buf): 13 | self.buf, self.pos = buf, 0 14 | 15 | def peek(self, offset): 16 | if len(self.buf) - self.pos <= offset: 17 | raise BufferTooShort 18 | 19 | return self.buf[self.pos + offset] 20 | 21 | def _unpack(self, fmt): 22 | size = struct.calcsize(fmt) 23 | if len(self.buf) - self.pos < size: 24 | raise BufferTooShort 25 | 26 | items = struct.unpack_from('<' + fmt, self.buf, self.pos) 27 | self.pos += size 28 | if len(items) == 1: 29 | return items[0] 30 | else: 31 | return items 32 | 33 | def unsigned_byte(self): 34 | return self._unpack('B') 35 | 36 | def signed_byte(self): 37 | return self._unpack('b') 38 | 39 | def unsigned_word(self): 40 | return self._unpack('H') 41 | 42 | def signed_word(self): 43 | return self._unpack('h') 44 | 45 | 46 | class Encoder: 47 | def __init__(self): 48 | self.buf = bytearray() 49 | 50 | def _pack(self, fmt, *items): 51 | offset = len(self.buf) 52 | self.buf += b'\x00' * struct.calcsize(fmt) 53 | struct.pack_into('<' + fmt, self.buf, offset, *items) 54 | 55 | def unsigned_byte(self, value): 56 | self._pack('B', value) 57 | 58 | def signed_byte(self, value): 59 | self._pack('b', value) 60 | 61 | def unsigned_word(self, value): 62 | self._pack('H', value) 63 | 64 | def signed_word(self, value): 65 | self._pack('h', value) 66 | -------------------------------------------------------------------------------- /mc/instr.py: -------------------------------------------------------------------------------- 1 | from binaryninja.enums import BranchType 2 | from binaryninja.lowlevelil import LowLevelILLabel, LLIL_TEMP 3 | 4 | from .tokens import * 5 | 6 | 7 | __all__ = ['Instruction'] 8 | 9 | 10 | class Operand: 11 | def render(self): 12 | return asm(('text', 'unimplemented')) 13 | 14 | def lift(self, il): 15 | return il.unimplemented() 16 | 17 | def lift_assign(self, il, value): 18 | il.append(value) 19 | il.append(il.unimplemented()) 20 | 21 | 22 | class Instruction: 23 | opcodes = {} 24 | 25 | def __new__(cls, *args, decoder=None): 26 | if decoder is None: 27 | return object.__new__(cls) 28 | else: 29 | opcode = decoder.peek(0) 30 | if opcode in cls.opcodes: 31 | return object.__new__(cls.opcodes[opcode]) 32 | else: 33 | return None 34 | 35 | def length(self): 36 | return 1 37 | 38 | def name(self): 39 | return type(self).__name__.split('_')[0] # slightly cursed 40 | 41 | def decode(self, decoder, addr): 42 | self.opcode = decoder.unsigned_byte() 43 | 44 | def encode(self, encoder, addr): 45 | encoder = encoder.unsigned_byte(self.opcode) 46 | 47 | def fuse(self, sister): 48 | return None 49 | 50 | def operands(self): 51 | yield from () 52 | 53 | def render(self): 54 | tokens = asm( 55 | ('instr', self.name()), 56 | ('opsep', ' ' * (6 - len(self.name()))) 57 | ) 58 | for index, operand in enumerate(self.operands()): 59 | if index > 0: 60 | tokens += asm(('opsep', ', ')) 61 | tokens += operand.render() 62 | return tokens 63 | 64 | def display(self, addr): 65 | print(f'{addr:04X}:\t' + ''.join(str(token) for token in self.render())) 66 | 67 | def analyze(self, info, addr): 68 | info.length += self.length() 69 | 70 | def lift(self, il, addr): 71 | operands = tuple(self.operands()) 72 | if len(operands) == 0: 73 | il.append(il.unimplemented()) 74 | else: 75 | il_value = self.lift_operation(il, *(operand.lift(il) for operand in operands)) 76 | operands[0].lift_assign(il, il_value) 77 | 78 | def lift_operation(self, il, *il_operands): 79 | return il.unimplemented() 80 | 81 | 82 | class PseudoInstruction(Instruction): 83 | def __init__(self, *parts): 84 | self.parts = parts 85 | 86 | def __len__(self): 87 | return len(self.parts) 88 | 89 | def __getitem__(self, index): 90 | return self.parts[index] 91 | 92 | def length(self): 93 | return sum(part.length() for part in self) 94 | 95 | def decode(self, decoder, addr): 96 | raise TypeError('Cannot decode pseudoinstructions') 97 | 98 | def encode(self, encoder, addr): 99 | for part in self: 100 | part.encode(encoder, addr) 101 | addr += part.length() 102 | 103 | 104 | class Reg8Operand: 105 | def __init__(self, reg): 106 | self._reg = reg 107 | 108 | def render(self): 109 | return asm(('reg', self._reg)) 110 | 111 | def lift(self, il): 112 | return il.reg(1, self._reg) 113 | 114 | def lift_assign(self, il, value): 115 | # 0000c0b9 e8f1b0 MOV R0, [data_b0f1] 116 | # 0000c0bc 98fb JNZ #C0B9 117 | il.append(il.set_reg(1, self._reg, value, 'Z')) 118 | 119 | 120 | class InstrHasReg8: 121 | @property 122 | def reg(self): 123 | return f'R{self.opcode & 7}' 124 | 125 | 126 | class Reg16Operand: 127 | def __init__(self, regs): 128 | self._regs = regs 129 | 130 | def render(self): 131 | return asm( 132 | ('reg', self._regs[0]), 133 | ('reg', self._regs[1]), 134 | ) 135 | 136 | def lift(self, il): 137 | return il.reg_split(1, *self._regs) 138 | 139 | def lift_assign(self, il, value): 140 | il.append(il.set_reg_split(1, *self._regs, value)) 141 | il.append(il.set_flag('Z', il.compare_equal(1, il.reg(1, self._regs[-1]), il.const(1, 0)))) 142 | 143 | 144 | class InstrHasReg16: 145 | @property 146 | def regs(self): 147 | # This matches how invalid encodings seem to be processed. 148 | return f'R{self.opcode & 7 | 1}', f'R{self.opcode & 7}' 149 | 150 | 151 | class InstrHasFlag: 152 | @property 153 | def flag(self): 154 | return ['Z', 'C', 'N', 'B', 'F4', 'I', 'F6', 'F7'][self.opcode & 7] 155 | 156 | 157 | class FlagOperand: 158 | def __init__(self, flag): 159 | self._flag = flag 160 | 161 | def render(self): 162 | return asm(('reg', self._flag)) 163 | 164 | def lift(self, il): 165 | return il.flag(self._flag) 166 | 167 | def lift_assign(self, il, value): 168 | il.append(il.set_flag(self._flag, value)) 169 | 170 | 171 | class InstrHasImm: 172 | def length(self): 173 | return super().length() + 1 174 | 175 | def decode(self, decoder, addr): 176 | super().decode(decoder, addr) 177 | self.imm = decoder.unsigned_byte() 178 | 179 | def encode(self, encoder, addr): 180 | super().encode(encoder, addr) 181 | encoder.unsigned_byte(self.imm) 182 | 183 | 184 | class ImmOperand: 185 | def __init__(self, imm, *, width=1): 186 | self._imm = imm 187 | self._width = width 188 | 189 | def render(self): 190 | if self._width == 1: 191 | return asm(('int', f'#{self._imm:02X}', self._imm)) 192 | if self._width == 2: 193 | return asm(('addr', f'#{self._imm:04X}', self._imm)) 194 | assert False 195 | 196 | def lift(self, il): 197 | return il.const(self._width, self._imm) 198 | 199 | 200 | class InstrHasAbs: 201 | def length(self): 202 | return super().length() + 2 203 | 204 | def decode(self, decoder, addr): 205 | super().decode(decoder, addr) 206 | self.addr = decoder.unsigned_word() 207 | 208 | def encode(self, encoder, addr): 209 | super().encode(encoder, addr) 210 | encoder.unsigned_word(self.addr) 211 | 212 | 213 | class MemAbsOperand: 214 | def __init__(self, addr, *, width=1): 215 | self._addr = addr 216 | self._width = width 217 | 218 | def render(self): 219 | return asm( 220 | ('begmem', '['), 221 | ('addr', f'#{self._addr:04X}', self._addr), 222 | ('endmem', ']') 223 | ) 224 | 225 | def lift(self, il): 226 | return il.load(self._width, il.const_pointer(2, self._addr)) 227 | 228 | def lift_assign(self, il, value): 229 | il.append(il.store(self._width, il.const_pointer(2, self._addr), value)) 230 | 231 | 232 | class MemRegOperand: 233 | def __init__(self, regs, *, width=1): 234 | self._regs = regs 235 | self._width = width 236 | 237 | def render(self): 238 | return asm( 239 | ('begmem', '['), 240 | ('reg', self._regs[0]), 241 | ('reg', self._regs[1]), 242 | ('endmem', ']') 243 | ) 244 | 245 | def lift(self, il): 246 | return il.load(self._width, il.reg_split(1, *self._regs)) 247 | 248 | def lift_assign(self, il, value): 249 | il.append(il.store(self._width, il.reg_split(1, *self._regs), value)) 250 | 251 | 252 | class MemIdxOperand: 253 | def __init__(self, regs, off): 254 | self._regs = regs 255 | self._off = off 256 | 257 | def render(self): 258 | return asm( 259 | ('begmem', '['), 260 | ('reg', self._regs[0]), 261 | ('reg', self._regs[1]), 262 | ('opsep', '+'), 263 | ('int', f'{self._off}', self._off), 264 | ('endmem', ']'), 265 | ) 266 | 267 | def lift(self, il): 268 | return il.load(1, il.add(2, il.reg_split(1, *self._regs), il.const(2, self._off))) 269 | 270 | def lift_assign(self, il, value): 271 | il.append(il.store(1, il.add(2, il.reg_split(1, *self._regs), il.const(2, self._off)), value)) 272 | 273 | 274 | class CodeOperand: 275 | def __init__(self, pc): 276 | self._pc = pc 277 | 278 | def render(self): 279 | return asm(('addr', f'#{self._pc:04X}', self._pc)) 280 | 281 | 282 | class PseudoHasReg16: 283 | @property 284 | def regs(self): 285 | if self[0].reg > self[1].reg: 286 | return (self[0].reg, self[1].reg) 287 | else: 288 | return (self[1].reg, self[0].reg) 289 | 290 | 291 | class PseudoHasAbs: 292 | @property 293 | def addr(self): 294 | return min(self[0].addr, self[1].addr) 295 | 296 | 297 | class UnaryInstruction(Instruction): 298 | def operands(self): 299 | yield Reg8Operand(self.reg) 300 | 301 | 302 | class BinaryInstruction(Instruction): 303 | def operands(self): 304 | yield Reg8Operand('R0') 305 | yield Reg8Operand(self.reg) 306 | 307 | 308 | class INC(InstrHasReg8, UnaryInstruction): 309 | def lift_operation(self, il, il_arg): 310 | return il.add(1, il_arg, il.const(1, 1), 'CN') 311 | 312 | 313 | class ADDC(InstrHasReg8, BinaryInstruction): 314 | def lift_operation(self, il, il_arg1, il_arg2): 315 | return il.add(1, il_arg1, il.add(1, il_arg2, il.flag('C')), 'CN') 316 | 317 | 318 | class ADD(InstrHasReg8, BinaryInstruction): 319 | def fuse(self, sister): 320 | if self.reg == 'R0' and isinstance(sister, RLC) and sister.reg != 'R0': 321 | return LSL(self, sister) 322 | 323 | def lift_operation(self, il, il_arg1, il_arg2): 324 | return il.add(1, il_arg1, il_arg2, 'CN') 325 | 326 | 327 | class LSL(PseudoInstruction): 328 | @property 329 | def regs(self): 330 | return self[1].reg, 'R0' 331 | 332 | def operands(self): 333 | yield Reg16Operand(self.regs) 334 | 335 | def lift_operation(self, il, il_arg): 336 | return il.shift_left(2, il_arg, il.const(1, 1), 'C') 337 | 338 | 339 | class DEC(InstrHasReg8, UnaryInstruction): 340 | def lift_operation(self, il, il_arg): 341 | return il.sub(1, il_arg, il.const(1, 1), 'CN') 342 | 343 | def lift(self, il, addr): 344 | super().lift(il, addr) 345 | il.append(il.set_flag('C', il.not_expr(1, il.flag('C')))) # borrow -> carry 346 | 347 | 348 | class SUBB(InstrHasReg8, BinaryInstruction): 349 | def lift_operation(self, il, il_arg1, il_arg2): 350 | return il.sub(1, il_arg1, il.add(1, il_arg2, il.flag('C')), 'CN') # carry -> borrow 351 | 352 | def lift(self, il, addr): 353 | super().lift(il, addr) 354 | il.append(il.set_flag('C', il.not_expr(1, il.flag('C')))) # borrow -> carry 355 | 356 | 357 | class RLC(InstrHasReg8, UnaryInstruction): 358 | def lift(self, il, addr): 359 | temp = LLIL_TEMP(il.temp_reg_count) 360 | il.append(il.set_reg(1, temp, il.test_bit(1, il.reg(1, self.reg), il.const(1, 1 << 7)))) 361 | result = il.rotate_left_carry(1, il.reg(1, self.reg), il.const(1, 1), il.flag('C')) 362 | il.append(il.set_reg(1, self.reg, result, 'Z')) 363 | il.append(il.set_flag('C', il.reg(1, temp))) 364 | 365 | 366 | class RRC(InstrHasReg8, UnaryInstruction): 367 | def lift(self, il, addr): 368 | temp = LLIL_TEMP(il.temp_reg_count) 369 | il.append(il.set_reg(1, temp, il.test_bit(1, il.reg(1, self.reg), il.const(1, 1 << 0)))) 370 | result = il.rotate_right_carry(1, il.reg(1, self.reg), il.const(1, 1), il.flag('C')) 371 | il.append(il.set_reg(1, self.reg, result, 'Z')) 372 | il.append(il.set_flag('C', il.reg(1, temp))) 373 | 374 | 375 | class OR(InstrHasReg8, BinaryInstruction): 376 | def lift_operation(self, il, il_arg1, il_arg2): 377 | return il.or_expr(1, il_arg1, il_arg2) 378 | 379 | def lift(self, il, addr): 380 | if self.reg == 'R1': # `OR R0, R1` -> `Z = R1:R0 == 0` 381 | il.append(il.set_flag('Z', il.compare_equal(1, il.reg_split(1, 'R1', 'R0'), il.const(2, 0)))) 382 | il.append(il.set_reg(1, 'R0', il.or_expr(1, il.reg(1, 'R0'), il.reg(1, 'R1')))) 383 | else: 384 | super().lift(il, addr) 385 | 386 | 387 | class AND(InstrHasReg8, BinaryInstruction): 388 | def lift_operation(self, il, il_arg1, il_arg2): 389 | return il.and_expr(1, il_arg1, il_arg2) 390 | 391 | 392 | class XOR(InstrHasReg8, BinaryInstruction): 393 | def lift_operation(self, il, il_arg1, il_arg2): 394 | return il.xor_expr(1, il_arg1, il_arg2) 395 | 396 | 397 | class BIT(Instruction): 398 | @property 399 | def bit(self): 400 | return self.opcode & 0x7 401 | 402 | def render(self): 403 | return Instruction.render(self) + asm( 404 | ('reg', 'R0'), 405 | ('opsep', ', '), 406 | ('int', f'{self.bit}', self.bit), 407 | ) 408 | 409 | def lift(self, il, addr): 410 | il.append(il.set_flag('Z', il.test_bit(1, il.reg(1, 'R0'), il.const(1, 1 << self.bit)))) 411 | 412 | 413 | class CMP(InstrHasReg8, BinaryInstruction): 414 | def lift(self, il, addr): 415 | il.append(il.sub(1, il.reg(1, 'R0'), il.reg(1, self.reg), 'CN')) 416 | il.append(il.set_flag('C', il.not_expr(1, il.flag('C')))) # borrow -> carry 417 | il.append(il.set_flag('Z', il.compare_equal(1, il.reg(1, 'R0'), il.reg(1, self.reg)))) 418 | 419 | 420 | class FlagInstruction(InstrHasFlag, Instruction): 421 | def operands(self): 422 | yield FlagOperand(self.flag) 423 | 424 | 425 | class SET(FlagInstruction): 426 | def name(self): 427 | return 'SET' 428 | 429 | def lift_operation(self, il, il_flag): 430 | return il.const(1, 1) 431 | 432 | 433 | class CLR(FlagInstruction): 434 | def name(self): 435 | return 'CLR' 436 | 437 | def lift_operation(self, il, il_flag): 438 | return il.const(1, 0) 439 | 440 | 441 | class INC_Reg16(InstrHasReg16, Instruction): 442 | def name(self): 443 | return 'INC' 444 | 445 | def operands(self): 446 | yield Reg16Operand(self.regs) 447 | 448 | def lift_operation(self, il, il_arg): 449 | # FIXME: completely unclear how flags work here 450 | return il.add(2, il_arg, il.const(2, 1)) 451 | 452 | 453 | class MoveInstruction(Instruction): 454 | def lift_operation(self, il, il_dest, il_src): 455 | return il_src 456 | 457 | 458 | class MovePseudoInstruction(PseudoInstruction): 459 | def lift_operation(self, il, il_dest, il_src): 460 | return il_src 461 | 462 | 463 | class MOVP(MovePseudoInstruction): 464 | def operands(self): 465 | ld_operands = tuple(self[0].operands()) 466 | st_operands = tuple(self[1].operands()) 467 | yield st_operands[0] 468 | yield ld_operands[1] 469 | 470 | def lift(self, il, addr): 471 | super().lift(il, addr) 472 | self[0].lift(il, addr) # lift side effects of the load 473 | 474 | 475 | class MOV_R0_Reg(InstrHasReg8, MoveInstruction): 476 | def operands(self): 477 | yield Reg8Operand('R0') 478 | yield Reg8Operand(self.reg) 479 | 480 | 481 | class MOV_Reg_R0(InstrHasReg8, MoveInstruction): 482 | def operands(self): 483 | yield Reg8Operand(self.reg) 484 | yield Reg8Operand('R0') 485 | 486 | 487 | class MOV_Reg_Imm(InstrHasImm, InstrHasReg8, MoveInstruction): 488 | def fuse(self, sister): 489 | if isinstance(sister, MOV_Reg_Imm) and self.opcode ^ sister.opcode == 1: 490 | return MOVW_Reg_Imm(self, sister) 491 | if isinstance(sister, MOV_MemAbs_Reg) and sister.reg == 'R0': 492 | return MOVP_MemAbs_Imm(self, sister) 493 | 494 | def operands(self): 495 | yield Reg8Operand(self.reg) 496 | yield ImmOperand(self.imm) 497 | 498 | 499 | class MOVW_Reg_Imm(PseudoHasReg16, MovePseudoInstruction): 500 | @property 501 | def imm(self): 502 | if self[0].reg > self[1].reg: 503 | return self[0].imm << 8 | self[1].imm 504 | else: 505 | return self[1].imm << 8 | self[0].imm 506 | 507 | def operands(self): 508 | yield Reg16Operand(self.regs) 509 | yield ImmOperand(self.imm, width=2) 510 | 511 | 512 | class MOVP_MemAbs_Imm(MOVP): 513 | def fuse(self, sister): 514 | if isinstance(sister, MOVP_MemAbs_Imm) and abs(self.addr - sister.addr) == 1: 515 | return MOVW_MemAbs_Imm(self, sister) 516 | 517 | @property 518 | def imm(self): 519 | return self[0].imm 520 | 521 | @property 522 | def addr(self): 523 | return self[1].addr 524 | 525 | 526 | class MOVW_MemAbs_Imm(PseudoHasAbs, MovePseudoInstruction): 527 | @property 528 | def imm(self): 529 | if self[0].addr > self[1].addr: 530 | return self[0].imm << 8 | self[1].imm 531 | else: 532 | return self[1].imm << 8 | self[0].imm 533 | 534 | def operands(self): 535 | yield MemAbsOperand(self.addr, width=2) 536 | yield ImmOperand(self.imm, width=2) 537 | 538 | def lift(self, il, addr): 539 | super().lift(il, addr) 540 | self[1][0].lift(il, addr) # lift side effects of the load 541 | 542 | 543 | class MOV_R0_MemReg(InstrHasReg16, MoveInstruction): 544 | def operands(self): 545 | yield Reg8Operand('R0') 546 | yield MemRegOperand(self.regs) 547 | 548 | 549 | class MOV_MemReg_R0(InstrHasReg16, MoveInstruction): 550 | def operands(self): 551 | yield MemRegOperand(self.regs) 552 | yield Reg8Operand('R0') 553 | 554 | 555 | class MOV_Reg_MemAbs(InstrHasAbs, InstrHasReg8, MoveInstruction): 556 | def fuse(self, sister): 557 | if (isinstance(sister, MOV_Reg_MemAbs) and 558 | self.opcode ^ sister.opcode == 1 and abs(self.addr - sister.addr) == 1 and 559 | (self.reg > sister.reg) == (self.addr > sister.addr)): 560 | return MOVW_Reg_MemAbs(self, sister) 561 | 562 | def operands(self): 563 | yield Reg8Operand(self.reg) 564 | yield MemAbsOperand(self.addr) 565 | 566 | 567 | class MOV_MemAbs_Reg(InstrHasAbs, InstrHasReg8, MoveInstruction): 568 | def fuse(self, sister): 569 | if (isinstance(sister, MOV_MemAbs_Reg) and 570 | self.opcode ^ sister.opcode == 1 and abs(self.addr - sister.addr) == 1 and 571 | (self.reg > sister.reg) == (self.addr > sister.addr)): 572 | return MOVW_MemAbs_Reg(self, sister) 573 | 574 | def operands(self): 575 | yield MemAbsOperand(self.addr) 576 | yield Reg8Operand(self.reg) 577 | 578 | 579 | class MOVW_Reg_MemAbs(PseudoHasAbs, PseudoHasReg16, MovePseudoInstruction): 580 | def operands(self): 581 | yield Reg16Operand(self.regs) 582 | yield MemAbsOperand(self.addr, width=2) 583 | 584 | 585 | class MOVW_MemAbs_Reg(PseudoHasAbs, PseudoHasReg16, MovePseudoInstruction): 586 | def operands(self): 587 | yield MemAbsOperand(self.addr, width=2) 588 | yield Reg16Operand(self.regs) 589 | 590 | 591 | class MOV_R0_MemIdx(InstrHasImm, InstrHasReg16, MoveInstruction): 592 | def operands(self): 593 | yield Reg8Operand('R0') 594 | yield MemIdxOperand(self.regs, self.imm) 595 | 596 | 597 | class MOV_MemIdx_R0(InstrHasImm, InstrHasReg16, MoveInstruction): 598 | def operands(self): 599 | yield MemIdxOperand(self.regs, self.imm) 600 | yield Reg8Operand('R0') 601 | 602 | 603 | class PUSH(InstrHasReg8, Instruction): 604 | def operands(self): 605 | yield Reg8Operand(self.reg) 606 | 607 | def lift(self, il, addr): 608 | il.append(il.push(1, il.reg(1, self.reg))) 609 | 610 | 611 | class POP(InstrHasReg8, Instruction): 612 | def operands(self): 613 | yield Reg8Operand(self.reg) 614 | 615 | def lift(self, il, addr): 616 | il.append(il.set_reg(1, self.reg, il.pop(1))) 617 | 618 | 619 | class JMP_Rel(InstrHasFlag, Instruction): 620 | def length(self): 621 | return super().length() + 1 622 | 623 | def decode(self, decoder, addr): 624 | super().decode(decoder, addr) 625 | self.pc = addr + self.length() + decoder.signed_byte() 626 | 627 | def encode(self, encoder, addr): 628 | super().encode(encoder, addr) 629 | encoder.signed_byte(self.pc - addr - self.length()) 630 | 631 | @property 632 | def inverted(self): 633 | return bool(self.opcode & 8 == 0) 634 | 635 | def name(self): 636 | if self.inverted: 637 | return f'JN{self.flag}' 638 | else: 639 | return f'J{self.flag}' 640 | 641 | def operands(self): 642 | yield CodeOperand(self.pc) 643 | 644 | def analyze(self, info, addr): 645 | super().analyze(info, addr) 646 | info.add_branch(BranchType.TrueBranch, self.pc) 647 | info.add_branch(BranchType.FalseBranch, addr + self.length()) 648 | 649 | def lift(self, il, addr): 650 | cond = il.not_expr(1, il.flag(self.flag)) if self.inverted else il.flag(self.flag) 651 | if_true = (il.get_label_for_address(il.arch, self.pc) or 652 | il.add_label_for_address(il.arch, self.pc)) 653 | if_false = (il.get_label_for_address(il.arch, addr + self.length()) or 654 | il.add_label_for_address(il.arch, addr + self.length())) 655 | if if_true and if_false: # common path; keeps LLIL clean 656 | il.append(il.if_expr(cond, if_true, if_false)) 657 | else: # exceptional path; e.g. if a conditional jump falls through into another function 658 | if_true = LowLevelILLabel() 659 | if_false = LowLevelILLabel() 660 | il.append(il.if_expr(cond, if_true, if_false)) 661 | il.mark_label(if_true) 662 | il.append(il.jump(il.const_pointer(2, self.pc))) 663 | il.mark_label(if_false) 664 | il.append(il.jump(il.const_pointer(2, addr + self.length()))) 665 | 666 | 667 | class JMP_Abs(Instruction): 668 | def length(self): 669 | return super().length() + 2 670 | 671 | def decode(self, decoder, addr): 672 | super().decode(decoder, addr) 673 | self.pc = decoder.unsigned_word() 674 | 675 | def encode(self, encoder, addr): 676 | super().encode(encoder, addr) 677 | encoder.unsigned_word(self.pc) 678 | 679 | def operands(self): 680 | yield CodeOperand(self.pc) 681 | 682 | def analyze(self, info, addr): 683 | super().analyze(info, addr) 684 | info.add_branch(BranchType.UnconditionalBranch, self.pc) 685 | 686 | def lift(self, il, addr): 687 | if label := il.get_label_for_address(il.arch, self.pc): 688 | il.append(il.goto(label)) 689 | else: 690 | il.append(il.jump(il.const_pointer(2, self.pc))) 691 | 692 | 693 | class CALL(Instruction): 694 | def length(self): 695 | return super().length() + 2 696 | 697 | def decode(self, decoder, addr): 698 | super().decode(decoder, addr) 699 | self.pc = decoder.unsigned_word() 700 | 701 | def encode(self, encoder, addr): 702 | super().encode(encoder, addr) 703 | encoder.unsigned_word(self.pc) 704 | 705 | def operands(self): 706 | yield CodeOperand(self.pc) 707 | 708 | def analyze(self, info, addr): 709 | super().analyze(info, addr) 710 | info.add_branch(BranchType.CallDestination, self.pc) 711 | 712 | def lift(self, il, addr): 713 | il.append(il.call(il.const_pointer(2, self.pc))) 714 | 715 | 716 | class RET(Instruction): 717 | def analyze(self, info, addr): 718 | super().analyze(info, addr) 719 | info.add_branch(BranchType.FunctionReturn) 720 | 721 | def lift(self, il, addr): 722 | il.append(il.ret(il.pop(2))) 723 | 724 | 725 | class IRET(RET): 726 | pass 727 | 728 | 729 | class UNKN(Instruction): 730 | def operands(self): 731 | yield ImmOperand(self.opcode) 732 | 733 | def lift(self, il, addr): 734 | il.append(il.unimplemented()) 735 | 736 | 737 | Instruction.opcodes.update({ 738 | 0x00: INC, 739 | 0x01: INC, 740 | 0x02: INC, 741 | 0x03: INC, 742 | 0x04: INC, 743 | 0x05: INC, 744 | 0x06: INC, 745 | 0x07: INC, 746 | 0x08: ADDC, 747 | 0x09: ADDC, 748 | 0x0a: ADDC, 749 | 0x0b: ADDC, 750 | 0x0c: ADDC, 751 | 0x0d: ADDC, 752 | 0x0e: ADDC, 753 | 0x0f: ADDC, 754 | 0x10: MOV_R0_Reg, 755 | 0x11: MOV_R0_Reg, 756 | 0x12: MOV_R0_Reg, 757 | 0x13: MOV_R0_Reg, 758 | 0x14: MOV_R0_Reg, 759 | 0x15: MOV_R0_Reg, 760 | 0x16: MOV_R0_Reg, 761 | 0x17: MOV_R0_Reg, 762 | 0x18: OR, 763 | 0x19: OR, 764 | 0x1a: OR, 765 | 0x1b: OR, 766 | 0x1c: OR, 767 | 0x1d: OR, 768 | 0x1e: OR, 769 | 0x1f: OR, 770 | 0x20: AND, 771 | 0x21: AND, 772 | 0x22: AND, 773 | 0x23: AND, 774 | 0x24: AND, 775 | 0x25: AND, 776 | 0x26: AND, 777 | 0x27: AND, 778 | 0x28: XOR, 779 | 0x29: XOR, 780 | 0x2a: XOR, 781 | 0x2b: XOR, 782 | 0x2c: XOR, 783 | 0x2d: XOR, 784 | 0x2e: XOR, 785 | 0x2f: XOR, 786 | 0x30: RLC, 787 | 0x31: RLC, 788 | 0x32: RLC, 789 | 0x33: RLC, 790 | 0x34: RLC, 791 | 0x35: RLC, 792 | 0x36: RLC, 793 | 0x37: RLC, 794 | 0x38: RRC, 795 | 0x39: RRC, 796 | 0x3a: RRC, 797 | 0x3b: RRC, 798 | 0x3c: RRC, 799 | 0x3d: RRC, 800 | 0x3e: RRC, 801 | 0x3f: RRC, 802 | 0x40: DEC, 803 | 0x41: DEC, 804 | 0x42: DEC, 805 | 0x43: DEC, 806 | 0x44: DEC, 807 | 0x45: DEC, 808 | 0x46: DEC, 809 | 0x47: DEC, 810 | 0x48: SUBB, 811 | 0x49: SUBB, 812 | 0x4a: SUBB, 813 | 0x4b: SUBB, 814 | 0x4c: SUBB, 815 | 0x4d: SUBB, 816 | 0x4e: SUBB, 817 | 0x4f: SUBB, 818 | 0x50: ADD, 819 | 0x51: ADD, 820 | 0x52: ADD, 821 | 0x53: ADD, 822 | 0x54: ADD, 823 | 0x55: ADD, 824 | 0x56: ADD, 825 | 0x57: ADD, 826 | 0x58: SET, 827 | 0x59: SET, 828 | 0x5a: SET, 829 | 0x5b: SET, 830 | 0x5c: SET, 831 | 0x5d: SET, 832 | 0x5e: SET, 833 | 0x5f: SET, 834 | 0x60: BIT, 835 | 0x61: BIT, 836 | 0x62: BIT, 837 | 0x63: BIT, 838 | 0x64: BIT, 839 | 0x65: BIT, 840 | 0x66: BIT, 841 | 0x67: BIT, 842 | 0x68: CLR, 843 | 0x69: CLR, 844 | 0x6a: CLR, 845 | 0x6b: CLR, 846 | 0x6c: CLR, 847 | 0x6d: CLR, 848 | 0x6e: CLR, 849 | 0x6f: CLR, 850 | 0x70: MOV_Reg_R0, 851 | 0x71: MOV_Reg_R0, 852 | 0x72: MOV_Reg_R0, 853 | 0x73: MOV_Reg_R0, 854 | 0x74: MOV_Reg_R0, 855 | 0x75: MOV_Reg_R0, 856 | 0x76: MOV_Reg_R0, 857 | 0x77: MOV_Reg_R0, 858 | 0x78: CMP, 859 | 0x79: CMP, 860 | 0x7a: CMP, 861 | 0x7b: CMP, 862 | 0x7c: CMP, 863 | 0x7d: CMP, 864 | 0x7e: CMP, 865 | 0x7f: CMP, 866 | 0x80: PUSH, 867 | 0x81: PUSH, 868 | 0x82: PUSH, 869 | 0x83: PUSH, 870 | 0x84: PUSH, 871 | 0x85: PUSH, 872 | 0x86: PUSH, 873 | 0x87: PUSH, 874 | 0x88: POP, 875 | 0x89: POP, 876 | 0x8a: POP, 877 | 0x8b: POP, 878 | 0x8c: POP, 879 | 0x8d: POP, 880 | 0x8e: POP, 881 | 0x8f: POP, 882 | 0x90: JMP_Rel, 883 | 0x91: JMP_Rel, 884 | 0x92: JMP_Rel, 885 | 0x93: JMP_Rel, 886 | 0x94: JMP_Rel, 887 | 0x95: JMP_Rel, 888 | 0x96: JMP_Rel, 889 | 0x97: JMP_Rel, 890 | 0x98: JMP_Rel, 891 | 0x99: JMP_Rel, 892 | 0x9a: JMP_Rel, 893 | 0x9b: JMP_Rel, 894 | 0x9c: JMP_Rel, 895 | 0x9d: JMP_Rel, 896 | 0x9e: JMP_Rel, 897 | 0x9f: JMP_Rel, 898 | 0xa0: UNKN, 899 | # 0xa1: UNK, 900 | # 0xa2: UNK, 901 | # 0xa3: UNK, 902 | # 0xa4: UNK, 903 | # 0xa5: UNK, 904 | # 0xa6: UNK, 905 | # 0xa7: UNK, 906 | # 0xa8: UNK, 907 | # 0xa9: UNK, 908 | # 0xaa: UNK, 909 | # 0xab: UNK, 910 | # 0xac: UNK, 911 | # 0xad: UNK, 912 | # 0xae: UNK, 913 | # 0xaf: UNK, 914 | # 0xb0: , 915 | # 0xb1: , 916 | # 0xb2: , 917 | # 0xb3: , 918 | # 0xb4: , 919 | # 0xb5: , 920 | # 0xb6: , 921 | # 0xb7: , 922 | # 0xb8: , 923 | 0xb9: RET, 924 | 0xba: IRET, 925 | # 0xbb: , 926 | 0xbc: JMP_Abs, 927 | # 0xbd: , 928 | # 0xbe: , 929 | 0xbf: CALL, 930 | 0xc0: INC_Reg16, 931 | # 0xc1: INC_Reg16, # broken in CPU 932 | 0xc2: INC_Reg16, 933 | # 0xc3: INC_Reg16, # broken in CPU 934 | 0xc4: INC_Reg16, 935 | # 0xc5: INC_Reg16, # broken in CPU 936 | 0xc6: INC_Reg16, 937 | # 0xc7: INC_Reg16, # broken in CPU 938 | 0xc8: MOV_MemAbs_Reg, 939 | 0xc9: MOV_MemAbs_Reg, 940 | 0xca: MOV_MemAbs_Reg, 941 | 0xcb: MOV_MemAbs_Reg, 942 | 0xcc: MOV_MemAbs_Reg, 943 | 0xcd: MOV_MemAbs_Reg, 944 | 0xce: MOV_MemAbs_Reg, 945 | 0xcf: MOV_MemAbs_Reg, 946 | 0xd0: MOV_MemReg_R0, 947 | # 0xd1: MOV_MemReg_R0, # broken in CPU 948 | 0xd2: MOV_MemReg_R0, 949 | # 0xd3: MOV_MemReg_R0, # broken in CPU 950 | 0xd4: MOV_MemReg_R0, 951 | # 0xd5: MOV_MemReg_R0, # broken in CPU 952 | 0xd6: MOV_MemReg_R0, 953 | # 0xd7: MOV_MemReg_R0, # broken in CPU 954 | 0xd8: MOV_MemIdx_R0, 955 | # 0xd9: MOV_MemIdx_Reg, # broken in CPU 956 | 0xda: MOV_MemIdx_R0, 957 | # 0xdb: MOV_MemIdx_Reg, # broken in CPU 958 | 0xdc: MOV_MemIdx_R0, 959 | # 0xdd: MOV_MemIdx_Reg, # broken in CPU 960 | 0xde: MOV_MemIdx_R0, 961 | # 0xdf: MOV_MemIdx_Reg, # broken in CPU 962 | 0xe0: MOV_Reg_Imm, 963 | 0xe1: MOV_Reg_Imm, 964 | 0xe2: MOV_Reg_Imm, 965 | 0xe3: MOV_Reg_Imm, 966 | 0xe4: MOV_Reg_Imm, 967 | 0xe5: MOV_Reg_Imm, 968 | 0xe6: MOV_Reg_Imm, 969 | 0xe7: MOV_Reg_Imm, 970 | 0xe8: MOV_Reg_MemAbs, 971 | 0xe9: MOV_Reg_MemAbs, 972 | 0xea: MOV_Reg_MemAbs, 973 | 0xeb: MOV_Reg_MemAbs, 974 | 0xec: MOV_Reg_MemAbs, 975 | 0xed: MOV_Reg_MemAbs, 976 | 0xee: MOV_Reg_MemAbs, 977 | 0xef: MOV_Reg_MemAbs, 978 | 0xf0: MOV_R0_MemReg, 979 | # 0xf1: MOV_R0_MemReg, # broken in CPU 980 | 0xf2: MOV_R0_MemReg, 981 | # 0xf3: MOV_R0_MemReg, # broken in CPU 982 | 0xf4: MOV_R0_MemReg, 983 | # 0xf5: MOV_R0_MemReg, # broken in CPU 984 | 0xf6: MOV_R0_MemReg, 985 | # 0xf7: MOV_R0_MemReg, # broken in CPU 986 | 0xf8: MOV_R0_MemIdx, 987 | # 0xf9: MOV_Reg_MemIdx, # broken in CPU 988 | 0xfa: MOV_R0_MemIdx, 989 | # 0xfb: MOV_Reg_MemIdx, # broken in CPU 990 | 0xfc: MOV_R0_MemIdx, 991 | # 0xfd: MOV_Reg_MemIdx, # broken in CPU 992 | 0xfe: MOV_R0_MemIdx, 993 | # 0xff: MOV_Reg_MemIdx, # broken in CPU 994 | }) 995 | -------------------------------------------------------------------------------- /mc/tokens.py: -------------------------------------------------------------------------------- 1 | try: 2 | from binaryninja import InstructionTextToken 3 | from binaryninja.enums import InstructionTextTokenType 4 | except ImportError: 5 | InstructionTextToken = None 6 | 7 | 8 | __all__ = ['token', 'asm'] 9 | 10 | 11 | def token(kind, text, *data): 12 | if InstructionTextToken is None: 13 | return text 14 | else: 15 | if kind == 'instr': 16 | tokenType = InstructionTextTokenType.InstructionToken 17 | elif kind == 'opsep': 18 | tokenType = InstructionTextTokenType.OperandSeparatorToken 19 | elif kind == 'reg': 20 | tokenType = InstructionTextTokenType.RegisterToken 21 | elif kind == 'int': 22 | tokenType = InstructionTextTokenType.IntegerToken 23 | elif kind == 'addr': 24 | tokenType = InstructionTextTokenType.PossibleAddressToken 25 | elif kind == 'begmem': 26 | tokenType = InstructionTextTokenType.BeginMemoryOperandToken 27 | elif kind == 'endmem': 28 | tokenType = InstructionTextTokenType.EndMemoryOperandToken 29 | elif kind == 'text': 30 | tokenType = InstructionTextTokenType.TextToken 31 | else: 32 | raise ValueError('Invalid token kind {}'.format(kind)) 33 | return InstructionTextToken(tokenType, text, *data) 34 | 35 | 36 | def asm(*parts): 37 | return [token(*part) for part in parts] 38 | -------------------------------------------------------------------------------- /plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "pluginmetadataversion": 2, 3 | "name": "Avnera Architecture", 4 | "type": ["arch"], 5 | "api": ["python3"], 6 | "description": "A plugin providing the Avnera AV6xxx/AV7xxx architecture.\n\nWith warm regards to Frederick G. Weiss and colleagues.", 7 | "version": "0.1", 8 | "author": "Catherine", 9 | "minimumbinaryninjaversion": 5428, 10 | "platforms": ["Darwin", "Linux", "Windows"], 11 | "license": { 12 | "name": "BSD-0-clause", 13 | "text": "Copyright (C) 2024 Catherine \n\nPermission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE." 14 | } 15 | } 16 | --------------------------------------------------------------------------------