import ctypes
import enum
import re
import sys
from six import integer_types, string_types, add_metaclass
from six.moves import range
from functools import partial
from .registers import Registers, REGISTER_NAMES
from ..mm import u32_t, i32_t, UINT16_FMT, UINT32_FMT
from ..util import str2int
from ..errors import EncodingLargeValueError, UnalignedJumpTargetError, AccessViolationError, AssemblerError
PO_REGISTER = r'(?P<register_n{operand_index}>(?:r\d\d?)|(?:sp)|(?:fp))'
PO_AREGISTER = r'(?P<address_register>r\d\d?|sp|fp)(?:\[(?:(?P<offset_sign>-|\+)?(?P<offset_immediate>0x[0-9a-fA-F]+|\d+))\])?'
PO_IMMEDIATE = r'(?:(?P<immediate>(?:-|\+)?(?:0x[0-9a-fA-F]+|\d+))|(?P<immediate_address>&[a-zA-Z_\.][a-zA-Z0-9_]*))'
[docs]def UINT20_FMT(i):
return '0x%05X' % (i & 0xFFFFF)
[docs]def encoding_to_u32(inst):
return ctypes.cast(ctypes.byref(inst), ctypes.POINTER(u32_t)).contents.value
if hasattr(sys, 'pypy_version_info'):
def u32_to_encoding(u, encoding):
class _Cast(ctypes.Union):
_pack_ = 0
_fields_ = [
('overall', u32_t),
('encoding', encoding)
]
caster = _Cast()
caster.overall = u32_t(u).value
return caster.encoding
else:
[docs] def u32_to_encoding(u, encoding):
u = u32_t(u)
e = encoding()
ctypes.cast(ctypes.byref(e), ctypes.POINTER(encoding))[0] = ctypes.cast(ctypes.byref(u), ctypes.POINTER(encoding)).contents
return e
[docs]def IE_OPCODE():
return ('opcode', u32_t, 6)
[docs]def IE_FLAG(n):
return (n, u32_t, 1)
[docs]def IE_REG(n):
return (n, u32_t, 5)
[docs]def IE_IMM(n, l):
return (n, u32_t, l)
[docs]class Encoding(ctypes.LittleEndianStructure):
@staticmethod
[docs]class EncodingR(ctypes.LittleEndianStructure):
_pack_ = 0
_fields_ = [
IE_OPCODE(), # 0
IE_REG('reg1'), # 6
IE_REG('reg2'), # 11
IE_FLAG('immediate_flag'), # 16
IE_IMM('immediate', 15), # 17
]
@staticmethod
[docs] def fill_reloc_slot(logger, inst, slot):
logger.debug('fill_reloc_slot: inst=%s, slot=%s', inst, slot)
slot.patch_offset = 17
slot.patch_size = 15
@staticmethod
def __repr__(self):
return '<EncodingR: opcode=%s, reg1=%s, reg2=%s, immediate_flag=%s, immediate=%s>' % (self.opcode, self.reg1, self.reg2, self.immediate_flag, UINT16_FMT(self.immediate))
[docs]class EncodingC(ctypes.LittleEndianStructure):
_pack_ = 0
_fields_ = [
IE_OPCODE(), # 0
IE_REG('reg'), # 6
IE_IMM('flag', 3), # 11
IE_FLAG('value'), # 14
IE_FLAG('immediate_flag'), # 15
IE_IMM('immediate', 16) # 16
]
@staticmethod
[docs] def fill_reloc_slot(logger, inst, slot):
logger.debug('fill_reloc_slot: inst=%s, slot=%s', inst, slot)
slot.patch_offset = 16
slot.patch_size = 16
@staticmethod
def __repr__(self):
return '<EncodingC: opcode=%s, reg=%s, flag=%s, value=%s, immediate_flag=%s, immediate=%s>' % (self.opcode, self.reg, self.flag, self.value, self.immediate_flag, UINT16_FMT(self.immediate))
[docs]class EncodingI(ctypes.LittleEndianStructure):
_pack_ = 0
_fields_ = [
IE_OPCODE(), # 0
IE_REG('reg'), # 6
IE_FLAG('immediate_flag'), # 11
IE_IMM('immediate', 20), # 12
]
@staticmethod
[docs] def fill_reloc_slot(logger, inst, slot):
logger.debug('fill_reloc_slot: inst=%s, slot=%s', inst, slot)
slot.patch_offset = 12
slot.patch_size = 20
@staticmethod
def __repr__(self):
return '<EncodingI: opcode=%s, reg=%s, immediate_flag=%s, immediate=%s>' % (self.opcode, self.reg, self.immediate_flag, UINT20_FMT(self.immediate))
[docs]class EncodingA(ctypes.LittleEndianStructure):
_pack_ = 0
_fields_ = [
IE_OPCODE(), # 0
IE_REG('reg1'), # 6
IE_REG('reg2'), # 11
IE_REG('reg3') # 16
]
def __repr__(self):
return '<EncodingA: opcode=%s, reg1=%s, reg2=%s, reg3=%s>' % (self.opcode, self.reg1, self.reg2, self.reg3)
[docs]def ENCODE(logger, buffer, inst, field, size, value, raise_on_large_value = False):
logger.debug('ENCODE: inst=%s, field=%s, size=%s, value=%s, raise_on_large_value=%s', inst, field, size, value, raise_on_large_value)
setattr(inst, field, value)
logger.debug('ENCODE: inst=%s', inst)
if value >= 2 ** size:
e = buffer.get_error(EncodingLargeValueError, 'inst=%s, field=%s, size=%s, value=%s' % (inst, field, size, UINT32_FMT(value)))
if raise_on_large_value is True:
raise e
e.log(logger.warn)
[docs]class Descriptor(object):
mnemonic = None
opcode = None
operands = None
# this is a default encoding, and by the way it silents Codacy's warning
encoding = EncodingR
pattern = None
relative_address = False
inst_aligned = False
def __init__(self, instruction_set):
super(Descriptor, self).__init__()
self.instruction_set = instruction_set
pattern = r'\s*' + self.mnemonic
if self.operands:
operand_patterns = []
self.operands = [ot.strip() for ot in self.operands.split(',')]
for operand_index, operand_types in zip(list(range(0, len(self.operands))), self.operands):
operand_pattern = []
for operand_type in operand_types:
if operand_type == 'r':
operand_pattern.append(PO_REGISTER.format(operand_index = operand_index))
elif operand_type == 'a':
operand_pattern.append(PO_AREGISTER)
elif operand_type == 'i':
operand_pattern.append(PO_IMMEDIATE)
else:
raise Exception('Unhandled operand type: {}'.format(operand_type))
operand_patterns.append('(?:' + '|'.join(operand_pattern) + ')')
pattern += r' ' + r',\s*'.join(operand_patterns)
pattern = r'^' + pattern + '(?:\s*[;#].*)?$'
self.pattern = re.compile(pattern, re.MULTILINE)
self.instruction_set.instructions.append(self)
@staticmethod
[docs] def jit(core, inst):
return None
@staticmethod
[docs] def execute(core, inst):
raise NotImplementedError('%s does not implement execute method' % inst.opcode)
@staticmethod
[docs] def assemble_operands(logger, buffer, inst, operands):
pass
@staticmethod
[docs] def fill_reloc_slot(logger, inst, slot):
inst.fill_reloc_slot(logger, inst, slot)
@staticmethod
[docs] def disassemble_operands(logger, inst):
return []
@classmethod
[docs] def disassemble_mnemonic(cls, inst):
return cls.mnemonic
[docs] def emit_instruction(self, logger, buffer, line):
DEBUG = logger.debug
DEBUG('emit_instruction: input line: %s', line)
binst = self.encoding()
DEBUG('emit_instruction: encoding=%s', self.encoding.__name__)
DEBUG('emit_instruction: desc is %s', self)
binst.opcode = self.opcode
raw_match = self.pattern.match(line)
matches = raw_match.groupdict()
DEBUG('emit_instruction: matches=%s', matches)
operands = {}
def str2reg(r):
if r == 'sp':
return Registers.SP
if r == 'fp':
return Registers.FP
return Registers(int(r[1:]))
if self.operands and len(self.operands):
for operand_index in range(0, len(self.operands)):
reg_group_name = 'register_n{}'.format(operand_index)
if reg_group_name in matches and matches[reg_group_name]:
operands[reg_group_name] = str2reg(matches[reg_group_name])
elif 'address_register' in matches and matches['address_register']:
operands['areg'] = str2reg(matches['address_register'])
if 'pointer' in matches and matches['pointer'] is not None:
if matches['pointer'] == '[':
operands['pointer'] = 'offset'
elif matches['pointer'] == '(':
operands['pointer'] = 'segment'
else:
raise Exception('Unhandled pointer type: {}'.format(matches))
if 'offset_register' in matches and matches['offset_register'] is not None:
operands['offset_register'] = str2reg(matches['offset_register'])
elif 'offset_immediate' in matches and matches['offset_immediate'] is not None:
k = -1 if 'offset_sign' in matches and matches['offset_sign'] and matches['offset_sign'].strip() == '-' else 1
operands['offset_immediate'] = k * str2int(matches['offset_immediate'])
elif 'immediate' in matches and matches['immediate']:
operands['immediate'] = str2int(matches['immediate'])
elif 'immediate_address' in matches and matches['immediate_address'] is not None:
operands['immediate'] = matches['immediate_address']
else:
raise Exception('Unhandled operand: {}'.format(matches))
else:
pass
try:
self.assemble_operands(logger, buffer, binst, operands)
except AssemblerError as e:
e.location = buffer.location.copy()
raise e
return binst
[docs]class Descriptor_R(Descriptor):
operands = 'r'
encoding = EncodingR
@staticmethod
[docs] def assemble_operands(logger, buffer, inst, operands):
logger.debug('assemble_operands: inst=%s, operands=%s', inst, operands)
ENCODE(logger, buffer, inst, 'reg1', 5, operands['register_n0'])
@staticmethod
[docs] def disassemble_operands(logger, inst):
return [REGISTER_NAMES[inst.reg1]]
[docs]class Descriptor_I(Descriptor):
operands = 'i'
@staticmethod
[docs] def assemble_operands(logger, buffer, inst, operands):
from .assemble import Reference
logger.debug('assemble_operands: inst=%s, operands=%s', inst, operands)
v = operands['immediate']
if isinstance(v, integer_types):
inst.immediate = v
elif isinstance(v, string_types):
inst.refers_to = Reference(label = v)
@staticmethod
[docs] def disassemble_operands(logger, inst):
return [str(inst.refers_to) if hasattr(inst, 'refers_to') and inst.refers_to is not None else UINT16_FMT(inst.immediate)]
[docs]class Descriptor_RI(Descriptor):
operands = 'ri'
encoding = EncodingI
@staticmethod
[docs] def assemble_operands(logger, buffer, inst, operands):
from .assemble import Reference
logger.debug('assemble_operands: inst=%s, operands=%s', inst, operands)
if 'register_n0' in operands:
ENCODE(logger, buffer, inst, 'reg', 5, operands['register_n0'])
else:
v = operands['immediate']
ENCODE(logger, buffer, inst, 'immediate_flag', 1, 1)
if isinstance(v, integer_types):
ENCODE(logger, buffer, inst, 'immediate', 20, v)
elif isinstance(v, string_types):
inst.refers_to = Reference(label = v)
@staticmethod
[docs] def disassemble_operands(logger, inst):
if inst.immediate_flag == 0:
return [REGISTER_NAMES[inst.reg]]
return [str(inst.refers_to) if hasattr(inst, 'refers_to') and inst.refers_to is not None else UINT32_FMT(inst.immediate)]
[docs]class Descriptor_R_I(Descriptor):
operands = 'r,i'
encoding = EncodingI
@staticmethod
[docs] def assemble_operands(logger, buffer, inst, operands):
from .assemble import Reference
logger.debug('assemble_operands: inst=%s, operands=%s', inst, operands)
ENCODE(logger, buffer, inst, 'reg', 5, operands['register_n0'])
ENCODE(logger, buffer, inst, 'immediate_flag', 1, 1)
v = operands['immediate']
if isinstance(v, integer_types):
ENCODE(logger, buffer, inst, 'immediate', 20, v)
elif isinstance(v, string_types):
inst.refers_to = Reference(label = v)
@staticmethod
[docs] def disassemble_operands(logger, inst):
return [REGISTER_NAMES[inst.reg], str(inst.refers_to) if hasattr(inst, 'refers_to') and inst.refers_to is not None else UINT20_FMT(inst.immediate)]
[docs]class Descriptor_R_RI(Descriptor):
operands = 'r,ri'
encoding = EncodingR
@staticmethod
[docs] def assemble_operands(logger, buffer, inst, operands):
from .assemble import Reference
logger.debug('assemble_operands: inst=%s, operands=%s', inst, operands)
ENCODE(logger, buffer, inst, 'reg1', 5, operands['register_n0'])
if 'register_n1' in operands:
ENCODE(logger, buffer, inst, 'reg2', 5, operands['register_n1'])
else:
ENCODE(logger, buffer, inst, 'immediate_flag', 1, 1)
v = operands['immediate']
if isinstance(v, integer_types):
ENCODE(logger, buffer, inst, 'immediate', 15, v)
elif isinstance(v, string_types):
inst.refers_to = Reference(label = v)
@staticmethod
[docs] def disassemble_operands(logger, inst):
if inst.immediate_flag == 0:
return [REGISTER_NAMES[inst.reg1], REGISTER_NAMES[inst.reg2]]
return [REGISTER_NAMES[inst.reg1], str(inst.refers_to) if hasattr(inst, 'refers_to') and inst.refers_to is not None else UINT16_FMT(inst.immediate)]
[docs]class Descriptor_R_R(Descriptor):
operands = 'r,r'
encoding = EncodingR
@staticmethod
[docs] def assemble_operands(logger, buffer, inst, operands):
logger.debug('assemble_operands: inst=%s, operands=%s', inst, operands)
ENCODE(logger, buffer, inst, 'reg1', 5, operands['register_n0'])
ENCODE(logger, buffer, inst, 'reg2', 5, operands['register_n1'])
@staticmethod
[docs] def disassemble_operands(logger, inst):
return [REGISTER_NAMES[inst.reg1], REGISTER_NAMES[inst.reg2]]
[docs]class Descriptor_RI_R(Descriptor):
operands = 'ri,r'
encoding = EncodingR
@staticmethod
[docs] def assemble_operands(logger, buffer, inst, operands):
from .assemble import Reference
logger.debug('assemble_operands: inst=%s, operands=%s', inst, operands)
ENCODE(logger, buffer, inst, 'reg2', 5, operands['register_n1'])
if 'register_n0' in operands:
ENCODE(logger, buffer, inst, 'reg1', 5, operands['register_n0'])
else:
ENCODE(logger, buffer, inst, 'immediate_flag', 1, 1)
v = operands['immediate']
if isinstance(v, integer_types):
ENCODE(logger, buffer, inst, 'immediate', 15, v)
elif isinstance(v, string_types):
inst.refers_to = Reference(label = v)
@staticmethod
[docs] def disassemble_operands(logger, inst):
if inst.immediate_flag == 0:
return [REGISTER_NAMES[inst.reg1], REGISTER_NAMES[inst.reg2]]
return [str(inst.refers_to) if hasattr(inst, 'refers_to') and inst.refers_to is not None else UINT16_FMT(inst.immediate), REGISTER_NAMES[inst.reg2]]
@add_metaclass(InstructionSetMetaclass)
[docs]class InstructionSet(object):
instruction_set_id = None
opcodes = None
@classmethod
[docs] def init(cls):
if hasattr(cls, 'opcode_desc_map'):
return
cls.opcode_desc_map = {}
cls.opcode_encoding_map = {}
for desc in cls.instructions:
cls.opcode_desc_map[desc.opcode] = desc
cls.opcode_encoding_map[desc.opcode] = desc.encoding
@classmethod
[docs] def decode_instruction(cls, logger, inst, core = None):
logger.debug('%s.decode_instruction: inst=%s, core=%s', cls.__name__, inst, core)
opcode = inst & 0x3F
if opcode not in cls.opcode_desc_map:
from ..cpu import InvalidOpcodeError
raise InvalidOpcodeError(opcode, ip = core.current_ip if core is not None else None, core = core)
return u32_to_encoding(inst, cls.opcode_encoding_map[opcode]), cls.opcode_desc_map[opcode], opcode
@classmethod
[docs] def disassemble_instruction(cls, logger, inst):
logger.debug('%s.disassemble_instruction: inst=%s (%s)', cls.__name__, inst, inst.__class__.__name__)
if isinstance(inst, ctypes.LittleEndianStructure):
inst, desc = inst, cls.opcode_desc_map[inst.opcode]
else:
inst, desc, _ = cls.decode_instruction(logger, inst)
mnemonic = desc.disassemble_mnemonic(inst)
operands = desc.disassemble_operands(logger, inst)
return (mnemonic + ' ' + ', '.join(operands)) if operands else mnemonic
#
# Main instruction set
#
[docs]def RI_VAL(core, inst, reg, sign_extend = True):
if inst.immediate_flag == 1:
if sign_extend is True:
return inst.sign_extend_immediate(core.LOGGER, inst)
return u32_t(0x00000000 | inst.immediate).value
return core.registers.map[getattr(inst, reg)].value
[docs]def RI_ADDR(core, inst, reg):
core.DEBUG('RI_ADDR: inst=%s, reg=%s', inst, reg)
base = core.registers.map[reg].value
offset = inst.sign_extend_immediate(core.LOGGER, inst) if inst.immediate_flag == 1 else 0
return u32_t(base + offset).value
[docs]def JUMP(core, inst):
core.DEBUG('JUMP: inst=%s', inst)
core.DEBUG(' IP=%s', UINT32_FMT(core.registers.ip.value))
if inst.immediate_flag == 0:
core.DEBUG(' register=%d, value=%s', inst.reg, UINT32_FMT(core.registers.map[inst.reg].value))
core.registers.ip.value = core.registers.map[inst.reg].value
else:
v = inst.sign_extend_immediate(core.LOGGER, inst)
nip = u32_t(core.registers.ip.value)
nip.value += (v << 2)
core.DEBUG(' offset=%s, aligned=%s, ip=%s, new=%s', UINT32_FMT(v), UINT32_FMT(v << 2), UINT32_FMT(core.registers.ip.value), UINT32_FMT(nip.value))
core.registers.ip.value = nip.value
core.DEBUG('JUMP: new ip=%s', UINT32_FMT(core.registers.ip.value))
[docs]def update_arith_flags(core, reg):
"""
Set relevant arithmetic flags according to content of registers. Flags are set to zero at the beginning,
then content of each register is examined, and ``S`` and ``Z`` flags are set.
``E`` flag is not touched, ``O`` flag is set to zero.
:param u32_t reg: register
"""
core.arith_zero = False
core.arith_overflow = False
core.arith_sign = False
if reg.value == 0:
core.arith_zero = True
if reg.value & 0x80000000 != 0:
core.arith_sign = True
[docs]class DuckyOpcodes(enum.IntEnum):
NOP = 0
# Memory load/store
LW = 1
LS = 2
LB = 3
STW = 4
STS = 5
STB = 6
CAS = 7
LA = 8
LI = 9
LIU = 10
MOV = 11
SWP = 12
INT = 13
RETINT = 14
CALL = 15
RET = 16
CLI = 17
STI = 18
RST = 19
HLT = 20
IDLE = 21
LPM = 22
IPI = 23
PUSH = 24
POP = 25
INC = 26
DEC = 27
ADD = 28
SUB = 29
MUL = 30
DIV = 31
UDIV = 32
MOD = 33
AND = 34
OR = 35
XOR = 36
NOT = 37
SHIFTL = 38
SHIFTR = 39
OUTW = 40
OUTS = 41
OUTB = 42
INW = 43
INS = 44
INB = 45
# Branch instructions
J = 46
# Condition instructions
CMP = 47
CMPU = 48
SET = 49
BRANCH = 50
# Control instructions
CTR = 60
CTW = 61
FPTC = 62
SIS = 63
[docs]class NOP(Descriptor):
mnemonic = 'nop'
opcode = DuckyOpcodes.NOP
encoding = EncodingI
@staticmethod
[docs] def execute(core, inst):
pass
#
# Interrupts
#
[docs]class INT(Descriptor_RI):
mnemonic = 'int'
opcode = DuckyOpcodes.INT
@staticmethod
[docs] def execute(core, inst):
core.do_int(RI_VAL(core, inst, 'reg'))
[docs]class IPI(Descriptor_R_RI):
mnemonic = 'ipi'
opcode = DuckyOpcodes.IPI
@staticmethod
[docs] def execute(core, inst):
core.check_protected_ins()
cpuid = core.registers.map[inst.reg1].value
cpuid, coreid = cpuid >> 16, cpuid & 0xFFFF
core.cpu.machine.cpus[cpuid].cores[coreid].irq(RI_VAL(core, inst, 'reg2'))
[docs]class RETINT(Descriptor):
mnemonic = 'retint'
opcode = DuckyOpcodes.RETINT
encoding = EncodingI
@staticmethod
[docs] def execute(core, inst):
core.check_protected_ins()
core.exit_interrupt()
#
# Jumps
#
class _JUMP(Descriptor):
operands = 'ri'
encoding = EncodingI
relative_address = True
inst_aligned = True
@staticmethod
def assemble_operands(logger, buffer, inst, operands):
from .assemble import Reference
logger.debug('assemble_operands: inst=%s, operands=%s', inst, operands)
if 'register_n0' in operands:
ENCODE(logger, buffer, inst, 'reg', 5, operands['register_n0'])
else:
v = operands['immediate']
ENCODE(logger, buffer, inst, 'immediate_flag', 1, 1)
if isinstance(v, integer_types):
if v & 0x3 != 0:
raise buffer.get_error(UnalignedJumpTargetError, 'address=%s' % UINT32_FMT(v))
ENCODE(logger, buffer, inst, 'immediate', 20, v >> 2)
elif isinstance(v, string_types):
inst.refers_to = Reference(label = v)
@staticmethod
def disassemble_operands(logger, inst):
if inst.immediate_flag == 0:
return [REGISTER_NAMES[inst.reg]]
return [str(inst.refers_to) if hasattr(inst, 'refers_to') and inst.refers_to is not None else UINT32_FMT(inst.immediate << 2)]
[docs]class CALL(_JUMP):
mnemonic = 'call'
opcode = DuckyOpcodes.CALL
@staticmethod
[docs] def execute(core, inst):
core.create_frame()
JUMP(core, inst)
if core.check_frames:
core.frames[-1].IP = core.registers.ip.value
@staticmethod
[docs] def jit(core, inst):
ip = core.registers.ip
sp = core.registers.sp
fp = core.registers.fp
push = core.raw_push
if inst.immediate_flag == 0:
reg = core.registers.map[inst.reg]
def __jit_call():
push(ip.value)
push(fp.value)
fp.value = sp.value
ip.value = reg.value
return __jit_call
else:
i = inst.sign_extend_immediate(core.LOGGER, inst) << 2
def __jit_call():
push(ip.value)
push(fp.value)
fp.value = sp.value
ip.value += i
return __jit_call
[docs]class J(_JUMP):
mnemonic = 'j'
opcode = DuckyOpcodes.J
@staticmethod
[docs] def execute(core, inst):
JUMP(core, inst)
@staticmethod
[docs] def jit(core, inst):
ip = core.registers.ip
if inst.immediate_flag == 0:
reg = core.registers.map[inst.reg]
def __jit_j():
ip.value = reg.value
return __jit_j
else:
i = inst.sign_extend_immediate(core.LOGGER, inst) << 2
def __jit_j():
ip.value += i
return __jit_j
[docs]class RET(Descriptor):
mnemonic = 'ret'
opcode = DuckyOpcodes.RET
encoding = EncodingI
@staticmethod
[docs] def execute(core, inst):
core.destroy_frame()
@staticmethod
[docs] def jit(core, inst):
ip = core.registers.ip
fp = core.registers.fp
pop = core.raw_pop
def __jit_ret():
fp.value = pop()
ip.value = pop()
return __jit_ret
#
# CPU
#
[docs]class LPM(Descriptor):
mnemonic = 'lpm'
opcode = DuckyOpcodes.LPM
encoding = EncodingI
@staticmethod
[docs] def execute(core, inst):
core.check_protected_ins()
core.privileged = False
[docs]class CLI(Descriptor):
mnemonic = 'cli'
opcode = DuckyOpcodes.CLI
encoding = EncodingI
@staticmethod
[docs] def execute(core, inst):
core.check_protected_ins()
core.hwint_allowed = False
[docs]class STI(Descriptor):
mnemonic = 'sti'
opcode = DuckyOpcodes.STI
encoding = EncodingI
@staticmethod
[docs] def execute(core, inst):
core.check_protected_ins()
core.hwint_allowed = True
[docs]class HLT(Descriptor_RI):
mnemonic = 'hlt'
opcode = DuckyOpcodes.HLT
@staticmethod
[docs] def execute(core, inst):
core.check_protected_ins()
core.exit_code = RI_VAL(core, inst, 'reg')
core.halt()
[docs]class RST(Descriptor):
mnemonic = 'rst'
opcode = DuckyOpcodes.RST
encoding = EncodingI
@staticmethod
[docs] def execute(core, inst):
core.check_protected_ins()
core.reset()
[docs]class IDLE(Descriptor):
mnemonic = 'idle'
opcode = DuckyOpcodes.IDLE
encoding = EncodingI
@staticmethod
[docs] def execute(core, inst):
core.change_runnable_state(idle = True)
[docs]class SIS(Descriptor_RI):
mnemonic = 'sis'
opcode = DuckyOpcodes.SIS
@staticmethod
[docs] def execute(core, inst):
core.instruction_set = get_instruction_set(RI_VAL(core, inst, 'reg'))
#
# Stack
#
[docs]class PUSH(Descriptor_RI):
mnemonic = 'push'
opcode = DuckyOpcodes.PUSH
@staticmethod
[docs] def execute(core, inst):
core.raw_push(RI_VAL(core, inst, 'reg'))
@staticmethod
[docs] def jit(core, inst):
push = core.raw_push
if inst.immediate_flag == 0:
reg = core.registers.map[inst.reg]
def __jit_push():
push(reg.value)
return __jit_push
else:
i = inst.sign_extend_immediate(core.LOGGER, inst)
def __jit_push():
push(i)
return __jit_push
[docs]class POP(Descriptor_R):
mnemonic = 'pop'
opcode = DuckyOpcodes.POP
@staticmethod
[docs] def execute(core, inst):
core.pop(inst.reg1)
update_arith_flags(core, core.registers.map[inst.reg1])
@staticmethod
[docs] def jit(core, inst):
pop = core.raw_pop
reg = core.registers.map[inst.reg1]
def __jit_pop():
reg.value = v = pop()
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = (v & 0x80000000) != 0
return __jit_pop
#
# Arithmetic
#
[docs]class INC(Descriptor_R):
mnemonic = 'inc'
opcode = DuckyOpcodes.INC
@staticmethod
[docs] def execute(core, inst):
core.registers.map[inst.reg1].value += 1
update_arith_flags(core, core.registers.map[inst.reg1])
core.arith_overflow = core.registers.map[inst.reg1].value == 0
@staticmethod
[docs] def jit(core, inst):
reg = core.registers.map[inst.reg1]
def __jit_inc():
old, new = reg.value, reg.value + 1
reg.value = new
if old == 0:
core.arith_zero = core.arith_overflow = core.arith_sign = False
elif old == 0xFFFFFFFF:
core.arith_zero = core.arith_overflow = True
core.arith_sign = False
else:
core.arith_zero = False
core.arith_overflow = False
core.arith_sign = (new & 0x80000000) != 0
return __jit_inc
[docs]class DEC(Descriptor_R):
mnemonic = 'dec'
opcode = DuckyOpcodes.DEC
@staticmethod
[docs] def execute(core, inst):
core.registers.map[inst.reg1].value -= 1
update_arith_flags(core, core.registers.map[inst.reg1])
@staticmethod
[docs] def jit(core, inst):
reg = core.registers.map[inst.reg1]
def __jit_dec():
old, new = reg.value, reg.value - 1
reg.value = new
if old == 0:
core.arith_zero = core.arith_overflow = False
core.arith_sign = True
elif old == 1:
core.arith_zero = True
core.arith_overflow = core.arith_sign = False
else:
core.arith_zero = core.arith_overflow = False
core.arith_sign = (new & 0x80000000) != 0
return __jit_dec
class _BINOP(Descriptor_R_RI):
encoding = EncodingR
@staticmethod
def execute(core, inst):
r = core.registers.map[inst.reg1]
v = RI_VAL(core, inst, 'reg2')
if inst.opcode == DuckyOpcodes.ADD:
v = r.value + v
elif inst.opcode == DuckyOpcodes.SUB:
v = r.value - v
elif inst.opcode == DuckyOpcodes.MUL:
x = i32_t(r.value).value
y = i32_t(v).value
v = x * y
elif inst.opcode == DuckyOpcodes.DIV:
x = i32_t(r.value).value
y = i32_t(v).value
if abs(y) > abs(x):
v = 0
else:
v = x // y
elif inst.opcode == DuckyOpcodes.UDIV:
x = u32_t(r.value).value
y = u32_t(v).value
v = x // y
elif inst.opcode == DuckyOpcodes.MOD:
x = i32_t(r.value).value
y = i32_t(v).value
v = x % y
r.value = v
update_arith_flags(core, r)
if v > 0xFFFFFFFF:
core.arith_overflow = True
[docs]class ADD(_BINOP):
mnemonic = 'add'
opcode = DuckyOpcodes.ADD
@staticmethod
[docs] def jit(core, inst):
core.DEBUG('JIT: %s', inst)
if inst.immediate_flag == 1:
reg = core.registers.map[inst.reg1]
i = inst.sign_extend_immediate(core.LOGGER, inst)
def __jit_add():
reg.value = v = reg.value + i
core.arith_zero = reg.value == 0
core.arith_overflow = v > 0xFFFFFFFF
core.arith_sign = (v & 0x80000000) != 0
return __jit_add
else:
reg1 = core.registers.map[inst.reg1]
reg2 = core.registers.map[inst.reg2]
def __jit_add():
reg1.value = v = reg1.value + reg2.value
core.arith_zero = reg1.value == 0
core.arith_overflow = v > 0xFFFFFFFF
core.arith_sign = (v & 0x80000000) != 0
return __jit_add
[docs]class SUB(_BINOP):
mnemonic = 'sub'
opcode = DuckyOpcodes.SUB
@staticmethod
[docs] def jit(core, inst):
core.DEBUG('JIT: %s', inst)
if inst.immediate_flag == 1:
reg = core.registers.map[inst.reg1]
i = inst.sign_extend_immediate(core.LOGGER, inst)
def __jit_sub():
reg.value = v = reg.value - i
core.arith_zero = reg.value == 0
core.arith_overflow = v > 0xFFFFFFFF
core.arith_sign = (v & 0x80000000) != 0
return __jit_sub
else:
reg1 = core.registers.map[inst.reg1]
reg2 = core.registers.map[inst.reg2]
def __jit_sub():
reg1.value = v = reg1.value - reg2.value
core.arith_zero = reg1.value == 0
core.arith_overflow = v > 0xFFFFFFFF
core.arith_sign = (v & 0x80000000) != 0
return __jit_sub
[docs]class MUL(_BINOP):
mnemonic = 'mul'
opcode = DuckyOpcodes.MUL
@staticmethod
[docs] def jit(core, inst):
core.DEBUG('JIT: %s', inst)
if inst.immediate_flag == 1:
reg = core.registers.map[inst.reg1]
i = i32_t(inst.sign_extend_immediate(core.LOGGER, inst)).value
def __jit_mul():
reg.value = v = i32_t(reg.value).value * i
core.arith_zero = reg.value == 0
core.arith_overflow = v > 0xFFFFFFFF
core.arith_sign = (v & 0x80000000) != 0
return __jit_mul
else:
reg1 = core.registers.map[inst.reg1]
reg2 = core.registers.map[inst.reg2]
def __jit_mul():
reg1.value = v = i32_t(reg1.value).value * i32_t(reg2.value).value
core.arith_zero = reg1.value == 0
core.arith_overflow = v > 0xFFFFFFFF
core.arith_sign = (v & 0x80000000) != 0
return __jit_mul
[docs]class DIV(_BINOP):
mnemonic = 'div'
opcode = DuckyOpcodes.DIV
[docs]class UDIV(_BINOP):
mnemonic = 'udiv'
opcode = DuckyOpcodes.UDIV
[docs]class MOD(_BINOP):
mnemonic = 'mod'
opcode = DuckyOpcodes.MOD
#
# Conditional and unconditional jumps
#
class _COND(Descriptor):
encoding = EncodingC
FLAGS = ['arith_equal', 'arith_zero', 'arith_overflow', 'arith_sign', 'l', 'g']
GFLAGS = [0, 1, 2, 3]
MNEMONICS = ['e', 'z', 'o', 's', 'g', 'l']
@staticmethod
def set_condition(logger, buffer, inst, flag, value):
logger.debug('set_condition: flag=%s, value=%s', flag, value)
ENCODE(logger, buffer, inst, 'flag', 3, _COND.FLAGS.index(flag))
ENCODE(logger, buffer, inst, 'value', 1, 1 if value is True else 0)
@staticmethod
def evaluate(core, inst):
# genuine flags
if inst.flag in _COND.GFLAGS and inst.value == getattr(core, _COND.FLAGS[inst.flag]):
return True
# "less than" flag
if inst.flag == 4:
if inst.value == 1 and core.arith_sign is True and core.arith_equal is not True:
return True
if inst.value == 0 and (core.arith_sign is not True or core.arith_equal is True):
return True
# "greater than" flag
if inst.flag == 5:
if inst.value == 1 and core.arith_sign is not True and core.arith_equal is not True:
return True
if inst.value == 0 and (core.arith_sign is True or core.arith_equal is True):
return True
return False
class _BRANCH(_COND):
operands = 'ri'
opcode = DuckyOpcodes.BRANCH
relative_address = True
inst_aligned = True
@classmethod
def assemble_operands(cls, logger, buffer, inst, operands):
from .assemble import Reference
logger.debug('assemble_operands: inst=%s, operands=%s', inst, operands)
if 'register_n0' in operands:
ENCODE(logger, buffer, inst, 'reg', 5, operands['register_n0'])
else:
v = operands['immediate']
ENCODE(logger, buffer, inst, 'immediate_flag', 1, 1)
if isinstance(v, integer_types):
if v & 0x3 != 0:
raise buffer.get_error(UnalignedJumpTargetError, 'address=%s' % UINT32_FMT(v))
ENCODE(logger, buffer, inst, 'immediate', 16, v >> 2)
elif isinstance(v, string_types):
inst.refers_to = Reference(label = v)
set_condition = partial(_COND.set_condition, logger, buffer, inst)
if cls is BE:
set_condition('arith_equal', True)
elif cls is BNE:
set_condition('arith_equal', False)
elif cls is BZ:
set_condition('arith_zero', True)
elif cls is BNZ:
set_condition('arith_zero', False)
elif cls is BO:
set_condition('arith_overflow', True)
elif cls is BNO:
set_condition('arith_overflow', False)
elif cls is BS:
set_condition('arith_sign', True)
elif cls is BNS:
set_condition('arith_sign', False)
elif cls is BL:
set_condition('l', True)
elif cls is BLE:
set_condition('g', False)
elif cls is BG:
set_condition('g', True)
elif cls is BGE:
set_condition('l', False)
@staticmethod
def fill_reloc_slot(logger, inst, slot):
inst.fill_reloc_slot(logger, inst, slot)
slot.flags.inst_aligned = True
@staticmethod
def disassemble_operands(logger, inst):
if inst.immediate_flag == 0:
return [REGISTER_NAMES[inst.reg]]
return [str(inst.refers_to) if hasattr(inst, 'refers_to') and inst.refers_to is not None else UINT16_FMT(inst.immediate << 2)]
@staticmethod
def disassemble_mnemonic(inst):
if inst.flag in _COND.GFLAGS:
return 'b%s%s' % ('n' if inst.value == 0 else '', _COND.MNEMONICS[inst.flag])
else:
if inst.flag == _COND.FLAGS.index('l'):
return 'bl' if inst.value == 1 else 'bge'
elif inst.flag == _COND.FLAGS.index('g'):
return 'bg' if inst.value == 1 else 'ble'
@staticmethod
def execute(core, inst):
if _COND.evaluate(core, inst):
JUMP(core, inst)
@staticmethod
def jit(core, inst):
core.DEBUG('JIT: %s', inst)
if inst.immediate_flag == 1:
i = inst.sign_extend_immediate(core.LOGGER, inst) << 2
else:
reg = core.registers.map[inst.reg]
ip = core.registers.ip
if inst.flag in _COND.GFLAGS:
if inst.flag == 0:
if inst.value == 0:
if inst.immediate_flag == 0:
def __branch_ne():
if core.arith_equal is False:
ip.value = reg.value
return __branch_ne
else:
def __branch_ne():
if core.arith_equal is False:
ip.value += i
return __branch_ne
else:
if inst.immediate_flag == 0:
def __branch_e():
if core.arith_equal is True:
ip.value = reg.value
return __branch_e
else:
def __branch_e():
if core.arith_equal is True:
ip.value += i
return __branch_e
elif inst.flag == 1:
if inst.value == 0:
if inst.immediate_flag == 0:
def __branch_nz():
if core.arith_zero is False:
ip.value = reg.value
return __branch_nz
else:
def __branch_nz():
if core.arith_zero is False:
ip.value += i
return __branch_nz
else:
if inst.immediate_flag == 0:
def __branch_z():
if core.arith_zero is True:
ip.value = reg.value
return __branch_z
else:
def __branch_z():
if core.arith_zero is True:
ip.value += i
return __branch_z
elif inst.flag == 2:
if inst.value == 0:
if inst.immediate_flag == 0:
def __branch_no():
if core.arith_overflow is False:
ip.value = reg.value
return __branch_no
else:
def __branch_no():
if core.arith_overflow is False:
ip.value += i
return __branch_no
else:
if inst.immediate_flag == 0:
def __branch_o():
if core.arith_overflow is True:
ip.value = reg.value
return __branch_o
else:
def __branch_o():
if core.arith_overflow is True:
ip.value += i
return __branch_o
elif inst.flag == 3:
if inst.value == 0:
if inst.immediate_flag == 0:
def __branch_ns():
if core.arith_sign is False:
ip.value = reg.value
return __branch_ns
else:
def __branch_ns():
if core.arith_sign is False:
ip.value += i
return __branch_ns
else:
if inst.immediate_flag == 0:
def __branch_s():
if core.arith_sign is True:
ip.value = reg.value
return __branch_s
else:
def __branch_s():
if core.arith_sign is True:
ip.value += i
return __branch_s
return None
class _SET(_COND):
operands = 'r'
opcode = DuckyOpcodes.SET
@classmethod
def assemble_operands(cls, logger, buffer, inst, operands):
logger.debug('assemble_operands: inst=%s, operands=%s', inst, operands)
ENCODE(logger, buffer, inst, 'reg', 5, operands['register_n0'])
set_condition = partial(_COND.set_condition, logger, buffer, inst)
if cls is SETE:
set_condition('arith_equal', True)
elif cls is SETNE:
set_condition('arith_equal', False)
elif cls is SETZ:
set_condition('arith_zero', True)
elif cls is SETNZ:
set_condition('arith_zero', False)
elif cls is SETO:
set_condition('arith_overflow', True)
elif cls is SETNO:
set_condition('arith_overflow', False)
elif cls is SETS:
set_condition('arith_sign', True)
elif cls is SETNS:
set_condition('arith_sign', False)
elif cls is SETL:
set_condition('l', True)
elif cls is SETLE:
set_condition('g', False)
elif cls is SETG:
set_condition('g', True)
elif cls is SETGE:
set_condition('l', False)
@staticmethod
def disassemble_operands(logger, inst):
return [REGISTER_NAMES[inst.reg]]
@staticmethod
def disassemble_mnemonic(inst):
if inst.flag in _COND.GFLAGS:
return 'set%s%s' % ('n' if inst.value == 0 else '', _COND.MNEMONICS[inst.flag])
else:
return 'set%s%s' % (_COND.MNEMONICS[inst.flag], 'e' if inst.value == 1 else '')
@staticmethod
def execute(core, inst):
core.registers.map[inst.reg].value = 1 if _COND.evaluate(core, inst) is True else 0
update_arith_flags(core, core.registers.map[inst.reg])
class _CMP(Descriptor_R_RI):
encoding = EncodingR
@staticmethod
def evaluate(core, x, y, signed = True):
"""
Compare two numbers, and update relevant flags. Signed comparison is used unless ``signed`` is ``False``.
All arithmetic flags are set to zero before the relevant ones are set.
``O`` flag is reset like the others, therefore caller has to take care of it's setting if it's required
to set it.
:param u32 x: left hand number
:param u32 y: right hand number
:param bool signed: use signed, defaults to ``True``
"""
core.arith_equal = False
core.arith_zero = False
core.arith_overflow = False
core.arith_sign = False
if x == y:
core.arith_equal = True
if x == 0:
core.arith_zero = True
return
if signed:
x = i32_t(x).value
y = i32_t(y).value
if x < y:
core.arith_sign = True
[docs]class CMP(_CMP):
mnemonic = 'cmp'
opcode = DuckyOpcodes.CMP
@staticmethod
[docs] def execute(core, inst):
_CMP.evaluate(core, core.registers.map[inst.reg1].value, RI_VAL(core, inst, 'reg2'))
@staticmethod
[docs] def jit(core, inst):
if inst.immediate_flag == 0:
reg1 = core.registers.map[inst.reg1]
reg2 = core.registers.map[inst.reg2]
def __jit_cmp():
x = reg1.value
y = reg2.value
core.arith_overflow = False
if x == y:
core.arith_equal = True
core.arith_zero = x == 0
core.arith_sign = False
return
core.arith_equal = False
core.arith_zero = False
core.arith_sign = i32_t(x).value < i32_t(y).value
return __jit_cmp
else:
reg = core.registers.map[inst.reg1]
y = inst.sign_extend_immediate(core.LOGGER, inst)
y_signed = i32_t(y).value
def __jit_cmp():
x = reg.value
core.arith_overflow = False
if x == y:
core.arith_equal = True
core.arith_zero = x == 0
core.arith_sign = False
return
core.arith_equal = False
core.arith_zero = False
core.arith_sign = i32_t(x).value < y_signed
return __jit_cmp
return None
[docs]class CMPU(_CMP):
mnemonic = 'cmpu'
opcode = DuckyOpcodes.CMPU
@staticmethod
[docs] def execute(core, inst):
_CMP.evaluate(core, core.registers.map[inst.reg1].value, RI_VAL(core, inst, 'reg2', sign_extend = False), signed = False)
[docs]class BE(_BRANCH):
mnemonic = 'be'
[docs]class BNE(_BRANCH):
mnemonic = 'bne'
[docs]class BNS(_BRANCH):
mnemonic = 'bns'
[docs]class BNZ(_BRANCH):
mnemonic = 'bnz'
[docs]class BS(_BRANCH):
mnemonic = 'bs'
[docs]class BZ(_BRANCH):
mnemonic = 'bz'
[docs]class BO(_BRANCH):
mnemonic = 'bo'
[docs]class BNO(_BRANCH):
mnemonic = 'bno'
[docs]class BG(_BRANCH):
mnemonic = 'bg'
[docs]class BGE(_BRANCH):
mnemonic = 'bge'
[docs]class BL(_BRANCH):
mnemonic = 'bl'
[docs]class BLE(_BRANCH):
mnemonic = 'ble'
[docs]class SETE(_SET):
mnemonic = 'sete'
[docs]class SETNE(_SET):
mnemonic = 'setne'
[docs]class SETZ(_SET):
mnemonic = 'setz'
[docs]class SETNZ(_SET):
mnemonic = 'setnz'
[docs]class SETO(_SET):
mnemonic = 'seto'
[docs]class SETNO(_SET):
mnemonic = 'setno'
[docs]class SETS(_SET):
mnemonic = 'sets'
[docs]class SETNS(_SET):
mnemonic = 'setns'
[docs]class SETG(_SET):
mnemonic = 'setg'
[docs]class SETGE(_SET):
mnemonic = 'setge'
[docs]class SETL(_SET):
mnemonic = 'setl'
[docs]class SETLE(_SET):
mnemonic = 'setle'
#
# IO
#
class _IN(Descriptor_R_RI):
encoding = EncodingR
@staticmethod
def execute(core, inst):
port = RI_VAL(core, inst, 'reg2')
core.check_protected_port(port)
r = core.registers.map[inst.reg1]
dev = core.cpu.machine.ports[port]
if inst.opcode == DuckyOpcodes.INB:
r.value = dev.read_u8(port)
elif inst.opcode == DuckyOpcodes.INS:
r.value = dev.read_u16(port)
else:
r.value = dev.read_u32(port)
update_arith_flags(core, r)
@staticmethod
def jit(core, inst):
reg = core.registers.map[inst.reg1]
if inst.immediate_flag == 1:
port = inst.sign_extend_immediate(core.LOGGER, inst)
dev = core.cpu.machine.ports[port]
is_protected = partial(dev.is_port_protected, port)
if inst.opcode == DuckyOpcodes.INB:
reader = dev.read_u8
def __jit_inb():
if not core.privileged and is_protected():
raise AccessViolationError('Access to port not allowed in unprivileged mode: inst={}, port={}'.format(inst, port))
reg.value = v = reader(port)
core.arith_zero = v == 0
core.arith_overflow = core.arith_sign = False
return __jit_inb
return None
[docs]class INB(_IN):
mnemonic = 'inb'
opcode = DuckyOpcodes.INB
[docs]class INS(_IN):
mnemonic = 'ins'
opcode = DuckyOpcodes.INS
[docs]class INW(_IN):
mnemonic = 'inw'
opcode = DuckyOpcodes.INW
class _OUT(Descriptor_RI_R):
encoding = EncodingR
@staticmethod
def execute(core, inst):
port = RI_VAL(core, inst, 'reg1')
core.check_protected_port(port)
r = core.registers.map[inst.reg2]
dev = core.cpu.machine.ports[port]
if inst.opcode == DuckyOpcodes.OUTB:
dev.write_u8(port, r.value & 0xFF)
elif inst.opcode == DuckyOpcodes.OUTS:
dev.write_u16(port, r.value & 0xFFFF)
else:
dev.write_u32(port, r.value)
@staticmethod
def jit(core, inst):
reg = core.registers.map[inst.reg2]
if inst.immediate_flag == 1:
port = inst.sign_extend_immediate(core.LOGGER, inst)
dev = core.cpu.machine.ports[port]
is_protected = partial(dev.is_port_protected, port)
if inst.opcode == DuckyOpcodes.OUTB:
writer = dev.write_u8
def __jit_outb():
if not core.privileged and is_protected():
raise AccessViolationError('Access to port not allowed in unprivileged mode: inst={}, port={}'.format(inst, port))
writer(port, reg.value & 0xFF)
return __jit_outb
return None
[docs]class OUTB(_OUT):
mnemonic = 'outb'
opcode = DuckyOpcodes.OUTB
[docs]class OUTW(_OUT):
mnemonic = 'outw'
opcode = DuckyOpcodes.OUTW
[docs]class OUTS(_OUT):
mnemonic = 'outs'
opcode = DuckyOpcodes.OUTS
#
# Bit operations
#
class _BITOP(Descriptor_R_RI):
encoding = EncodingR
@staticmethod
def execute(core, inst):
r = core.registers.map[inst.reg1]
v = RI_VAL(core, inst, 'reg2')
if inst.opcode == DuckyOpcodes.AND:
value = r.value & v
elif inst.opcode == DuckyOpcodes.OR:
value = r.value | v
elif inst.opcode == DuckyOpcodes.XOR:
value = r.value ^ v
elif inst.opcode == DuckyOpcodes.SHIFTL:
value = r.value << min(v, 32)
elif inst.opcode == DuckyOpcodes.SHIFTR:
value = r.value >> min(v, 32)
r.value = value
update_arith_flags(core, r)
if value > 0xFFFFFFFF:
core.arith_overflow = True
[docs]class AND(_BITOP):
mnemonic = 'and'
opcode = DuckyOpcodes.AND
@staticmethod
[docs] def jit(core, inst):
if inst.immediate_flag == 1:
reg = core.registers.map[inst.reg1]
i = inst.sign_extend_immediate(core.LOGGER, inst)
def __jit_and():
reg.value = v = reg.value & i
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = (v & 0x80000000) != 0
return __jit_and
else:
reg1 = core.registers.map[inst.reg1]
reg2 = core.registers.map[inst.reg2]
def __jit_and():
reg1.value = v = reg1.value & reg2.value
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = (v & 0x80000000) != 0
return __jit_and
[docs]class OR(_BITOP):
mnemonic = 'or'
opcode = DuckyOpcodes.OR
@staticmethod
[docs] def jit(core, inst):
if inst.immediate_flag == 1:
reg = core.registers.map[inst.reg1]
i = inst.sign_extend_immediate(core.LOGGER, inst)
def __jit_or():
reg.value = v = reg.value | i
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = (v & 0x80000000) != 0
return __jit_or
else:
reg1 = core.registers.map[inst.reg1]
reg2 = core.registers.map[inst.reg2]
def __jit_or():
reg1.value = v = reg1.value | reg2.value
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = (v & 0x80000000) != 0
return __jit_or
[docs]class XOR(_BITOP):
mnemonic = 'xor'
opcode = DuckyOpcodes.XOR
@staticmethod
[docs] def jit(core, inst):
if inst.immediate_flag == 1:
reg = core.registers.map[inst.reg1]
i = inst.sign_extend_immediate(core.LOGGER, inst)
def __jit_xor():
reg.value = v = reg.value ^ i
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = (v & 0x80000000) != 0
return __jit_xor
else:
reg1 = core.registers.map[inst.reg1]
reg2 = core.registers.map[inst.reg2]
def __jit_xor():
reg1.value = v = reg1.value ^ reg2.value
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = (v & 0x80000000) != 0
return __jit_xor
[docs]class SHIFTL(_BITOP):
mnemonic = 'shiftl'
opcode = DuckyOpcodes.SHIFTL
@staticmethod
[docs] def jit(core, inst):
if inst.immediate_flag == 1:
reg = core.registers.map[inst.reg1]
i = min(inst.sign_extend_immediate(core.LOGGER, inst), 32)
if i == 0:
def __jit_shiftl():
v = reg.value
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = (v & 0x80000000) != 0
return __jit_shiftl
elif i == 32:
def __jit_shiftl():
v = reg.value << i
reg.value = 0
core.arith_zero = True
core.arith_overflow = (v & ~0xFFFFFFFF) != 0
core.arith_sign = False
return __jit_shiftl
else:
def __jit_shiftl():
v = reg.value << i
reg.value = v
core.arith_zero = (v & 0xFFFFFFFF) == 0
core.arith_overflow = (v & ~0xFFFFFFFF) != 0
core.arith_sign = (v & 0x80000000) != 0
return __jit_shiftl
else:
reg1 = core.registers.map[inst.reg1]
reg2 = core.registers.map[inst.reg2]
def __jit_shiftl():
i = min(reg2.value, 32)
if i == 0:
v = reg1.value
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = (v & 0x80000000) != 0
elif i == 32:
v = reg1.value << i
reg1.value = 0
core.arith_zero = True
core.arith_overflow = (v & ~0xFFFFFFFF) != 0
core.arith_sign = False
else:
v = reg1.value << i
reg1.value = v
core.arith_zero = (v & 0xFFFFFFFF) == 0
core.arith_overflow = (v & ~0xFFFFFFFF) != 0
core.arith_sign = (v & 0x80000000) != 0
return __jit_shiftl
[docs]class SHIFTR(_BITOP):
mnemonic = 'shiftr'
opcode = DuckyOpcodes.SHIFTR
@staticmethod
[docs] def jit(core, inst):
if inst.immediate_flag == 1:
reg = core.registers.map[inst.reg1]
i = min(inst.sign_extend_immediate(core.LOGGER, inst), 32)
if i == 0:
def __jit_shiftr():
v = reg.value
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = (v & 0x80000000) != 0
return __jit_shiftr
elif i == 32:
def __jit_shiftr():
reg.value = 0
core.arith_zero = True
core.arith_overflow = core.arith_sign = False
return __jit_shiftr
else:
def __jit_shiftr():
reg.value = v = reg.value >> i
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = False
return __jit_shiftr
else:
reg1 = core.registers.map[inst.reg1]
reg2 = core.registers.map[inst.reg2]
def __jit_shiftr():
i = min(reg2.value, 32)
if i == 0:
v = reg1.value
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = (v & 0x80000000) != 0
elif i == 32:
reg1.value = 0
core.arith_zero = True
core.arith_overflow = core.arith_sign = False
else:
reg1.value = v = reg1.value >> i
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = False
return __jit_shiftr
[docs]class NOT(Descriptor_R):
mnemonic = 'not'
opcode = DuckyOpcodes.NOT
encoding = EncodingR
@staticmethod
[docs] def execute(core, inst):
r = core.registers.map[inst.reg1]
r.value = ~r.value
update_arith_flags(core, r)
#
# Memory load/store operations
#
[docs]class CAS(Descriptor):
mnemonic = 'cas'
operands = 'r,r,r'
opcode = DuckyOpcodes.CAS
encoding = EncodingA
@staticmethod
[docs] def assemble_operands(logger, buffer, inst, operands):
logger.debug('assemble_operands: inst=%s, operands=%s', inst, operands)
ENCODE(logger, buffer, inst, 'reg1', 5, operands['register_n0'])
ENCODE(logger, buffer, inst, 'reg2', 5, operands['register_n1'])
ENCODE(logger, buffer, inst, 'reg3', 5, operands['register_n2'])
@staticmethod
[docs] def disassemble_operands(logger, inst):
return [
REGISTER_NAMES[inst.reg1],
REGISTER_NAMES[inst.reg2],
REGISTER_NAMES[inst.reg3]
]
@staticmethod
[docs] def execute(core, inst):
core.arith_equal = False
reg1, reg2, reg3 = core.registers.map[inst.reg1], core.registers.map[inst.reg2], core.registers.map[inst.reg3]
core.DEBUG('CAS.execute: reg1=%s (%s), reg2=%s (%s), reg3=%s (%s)', inst.reg1, UINT32_FMT(reg1.value), inst.reg2, UINT32_FMT(reg2.value), inst.reg3, UINT32_FMT(reg3.value))
addr = core.registers.map[inst.reg1].value
actual_value = core.MEM_IN32(reg1.value)
core.DEBUG('CAS.execute: value=%s', UINT32_FMT(actual_value))
if actual_value == reg2.value:
core.MEM_OUT32(addr, reg3.value)
core.arith_equal = True
else:
reg2.value = actual_value
class _LOAD(Descriptor):
operands = 'r,a'
encoding = EncodingR
@staticmethod
def assemble_operands(logger, buffer, inst, operands):
logger.debug('assemble_operands: inst=%s, operands=%s', inst, operands)
ENCODE(logger, buffer, inst, 'reg1', 5, operands['register_n0'])
ENCODE(logger, buffer, inst, 'reg2', 5, operands['areg'])
offset_immediate = operands.get('offset_immediate')
if offset_immediate is not None:
ENCODE(logger, buffer, inst, 'immediate_flag', 1, 1)
ENCODE(logger, buffer, inst, 'immediate', 15, int(offset_immediate))
@staticmethod
def disassemble_operands(logger, inst):
operands = [REGISTER_NAMES[inst.reg1]]
if inst.immediate_flag == 1:
operands.append('%s[%s]' % (REGISTER_NAMES[inst.reg2], inst.sign_extend_immediate(logger, inst)))
else:
operands.append(REGISTER_NAMES[inst.reg2])
return operands
@staticmethod
def execute(core, inst):
r = core.registers.map[inst.reg1]
addr = RI_ADDR(core, inst, inst.reg2)
if inst.opcode == DuckyOpcodes.LW:
r.value = core.MEM_IN32(addr)
elif inst.opcode == DuckyOpcodes.LS:
r.value = core.MEM_IN16(addr)
else:
r.value = core.MEM_IN8(addr)
update_arith_flags(core, r)
@staticmethod
def jit(core, inst):
reg1 = core.registers.map[inst.reg1]
reg2 = core.registers.map[inst.reg2]
if inst.opcode == DuckyOpcodes.LW:
reader = core.MEM_IN32
if inst.immediate_flag == 1:
offset = inst.sign_extend_immediate(core.LOGGER, inst)
if offset == 0:
def __jit_lw():
reg1.value = v = reader(reg2.value)
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = (v & 0x80000000) != 0
return __jit_lw
else:
def __jit_lw():
reg1.value = v = reader((reg2.value + offset) % 4294967296)
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = (v & 0x80000000) != 0
return __jit_lw
else:
def __jit_lw():
reg1.value = v = reader(reg2.value)
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = (v & 0x80000000) != 0
return __jit_lw
elif inst.opcode == DuckyOpcodes.LS:
reader = core.MEM_IN16
if inst.immediate_flag == 1:
offset = inst.sign_extend_immediate(core.LOGGER, inst)
if offset == 0:
def __jit_ls():
reg1.value = v = reader(reg2.value)
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = False
return __jit_ls
else:
def __jit_ls():
reg1.value = v = reader((reg2.value + offset) % 4294967296)
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = False
return __jit_ls
else:
def __jit_ls():
reg1.value = v = reader(reg2.value)
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = False
return __jit_ls
elif inst.opcode == DuckyOpcodes.LB:
reader = core.MEM_IN8
if inst.immediate_flag == 1:
offset = inst.sign_extend_immediate(core.LOGGER, inst)
if offset == 0:
def __jit_lb():
reg1.value = v = reader(reg2.value)
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = False
return __jit_lb
else:
def __jit_lb():
reg1.value = v = reader((reg2.value + offset) % 4294967296)
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = False
return __jit_lb
else:
def __jit_lb():
reg1.value = v = reader(reg2.value)
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = False
return __jit_lb
return None
class _STORE(Descriptor):
operands = 'a,r'
encoding = EncodingR
@staticmethod
def assemble_operands(logger, buffer, inst, operands):
logger.debug('assemble_operands: inst=%s, operands=%s', inst, operands)
ENCODE(logger, buffer, inst, 'reg1', 5, operands['areg'])
ENCODE(logger, buffer, inst, 'reg2', 5, operands['register_n1'])
offset_immediate = operands.get('offset_immediate')
if offset_immediate is not None:
ENCODE(logger, buffer, inst, 'immediate_flag', 1, 1)
ENCODE(logger, buffer, inst, 'immediate', 15, int(offset_immediate))
@staticmethod
def disassemble_operands(logger, inst):
operands = []
if inst.immediate_flag == 1:
operands.append('%s[%s]' % (REGISTER_NAMES[inst.reg1], inst.sign_extend_immediate(logger, inst)))
else:
operands.append(REGISTER_NAMES[inst.reg1])
operands.append(REGISTER_NAMES[inst.reg2])
return operands
@staticmethod
def execute(core, inst):
addr = RI_ADDR(core, inst, inst.reg1)
if inst.opcode == DuckyOpcodes.STW:
core.MEM_OUT32(addr, core.registers.map[inst.reg2].value)
elif inst.opcode == DuckyOpcodes.STS:
core.MEM_OUT16(addr, core.registers.map[inst.reg2].value & 0xFFFF)
else:
core.MEM_OUT8(addr, core.registers.map[inst.reg2].value & 0xFF)
@staticmethod
def jit(core, inst):
reg1 = core.registers.map[inst.reg1]
reg2 = core.registers.map[inst.reg2]
if inst.opcode == DuckyOpcodes.STW:
writer = core.MEM_OUT32
if inst.immediate_flag == 1:
offset = inst.sign_extend_immediate(core.LOGGER, inst)
if offset == 0:
def __jit_stw():
writer(reg1.value, reg2.value)
return __jit_stw
else:
def __jit_stw():
writer((reg1.value + offset) % 4294967296, reg2.value)
return __jit_stw
else:
def __jit_stw():
writer(reg1.value, reg2.value)
return __jit_stw
elif inst.opcode == DuckyOpcodes.STS:
writer = core.MEM_OUT16
if inst.immediate_flag == 1:
offset = inst.sign_extend_immediate(core.LOGGER, inst)
if offset == 0:
def __jit_sts():
writer(reg1.value, reg2.value)
return __jit_sts
else:
def __jit_sts():
writer((reg1.value + offset) % 4294967296, reg2.value)
return __jit_sts
else:
def __jit_sts():
writer(reg1.value, reg2.value)
return __jit_sts
elif inst.opcode == DuckyOpcodes.STB:
writer = core.MEM_OUT8
if inst.immediate_flag == 1:
offset = inst.sign_extend_immediate(core.LOGGER, inst)
if offset == 0:
def __jit_stb():
writer(reg1.value, reg2.value & 0xFF)
return __jit_stb
else:
def __jit_stb():
writer((reg1.value + offset) % 4294967296, reg2.value & 0xFF)
return __jit_stb
else:
def __jit_stb():
writer(reg1.value, reg2.value & 0xFF)
return __jit_stb
return None
class _LOAD_IMM(Descriptor_R_I):
@classmethod
def load(cls, core, inst):
raise NotImplementedError('%s does not implement "load immediate" method' % cls.__name__)
@classmethod
def execute(cls, core, inst):
cls.load(core, inst)
update_arith_flags(core, core.registers.map[inst.reg])
[docs]class LW(_LOAD):
mnemonic = 'lw'
opcode = DuckyOpcodes.LW
[docs]class LS(_LOAD):
mnemonic = 'ls'
opcode = DuckyOpcodes.LS
[docs]class LB(_LOAD):
mnemonic = 'lb'
opcode = DuckyOpcodes.LB
[docs]class LI(_LOAD_IMM):
mnemonic = 'li'
opcode = DuckyOpcodes.LI
@classmethod
[docs] def load(cls, core, inst):
core.registers.map[inst.reg].value = inst.sign_extend_immediate(core.LOGGER, inst)
@staticmethod
[docs] def jit(core, inst):
reg = core.registers.map[inst.reg]
i = inst.sign_extend_immediate(core.LOGGER, inst)
if i == 0:
def __jit_li():
reg.value = 0
core.arith_zero = True
core.arith_overflow = False
core.arith_sign = False
return __jit_li
else:
sign = (i & 0x80000000) != 0
def __jit_li():
reg.value = i
core.arith_zero = False
core.arith_overflow = False
core.arith_sign = sign
return __jit_li
[docs]class LIU(_LOAD_IMM):
mnemonic = 'liu'
opcode = DuckyOpcodes.LIU
@classmethod
[docs] def load(cls, core, inst):
r = core.registers.map[inst.reg]
r.value = (r.value & 0xFFFF) | ((inst.sign_extend_immediate(core.LOGGER, inst) & 0xFFFF) << 16)
[docs]class LA(_LOAD_IMM):
mnemonic = 'la'
opcode = DuckyOpcodes.LA
relative_address = True
@classmethod
[docs] def load(cls, core, inst):
core.registers.map[inst.reg].value = core.registers.ip.value + inst.sign_extend_immediate(core.LOGGER, inst)
@staticmethod
[docs] def jit(core, inst):
reg = core.registers.map[inst.reg]
ip = core.registers.ip
offset = inst.sign_extend_immediate(core.LOGGER, inst)
if offset == 0:
def __jit_la():
reg.value = v = ip.value
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = (v & 0x80000000) != 0
return __jit_la
else:
def __jit_la():
reg.value = v = ip.value + offset
core.arith_zero = v == 0
core.arith_overflow = False
core.arith_sign = (v & 0x80000000) != 0
return __jit_la
[docs]class STW(_STORE):
mnemonic = 'stw'
opcode = DuckyOpcodes.STW
[docs]class STS(_STORE):
mnemonic = 'sts'
opcode = DuckyOpcodes.STS
[docs]class STB(_STORE):
mnemonic = 'stb'
opcode = DuckyOpcodes.STB
[docs]class MOV(Descriptor_R_R):
mnemonic = 'mov'
opcode = DuckyOpcodes.MOV
encoding = EncodingR
@staticmethod
[docs] def execute(core, inst):
core.registers.map[inst.reg1].value = core.registers.map[inst.reg2].value
@staticmethod
[docs] def jit(core, inst):
reg1 = core.registers.map[inst.reg1]
reg2 = core.registers.map[inst.reg2]
def __jit_mov():
reg1.value = reg2.value
return __jit_mov
[docs]class SWP(Descriptor_R_R):
mnemonic = 'swp'
opcode = DuckyOpcodes.SWP
encoding = EncodingR
@staticmethod
[docs] def execute(core, inst):
r1 = core.registers.map[inst.reg1]
r2 = core.registers.map[inst.reg2]
r1.value, r2.value = r2.value, r1.value
@staticmethod
[docs] def jit(core, inst):
reg1 = core.registers.map[inst.reg1]
reg2 = core.registers.map[inst.reg2]
def __jit_swp():
reg1.value, reg2.value = reg2.value, reg1.value
return __jit_swp
#
# Control instructions
#
[docs]class CTR(Descriptor_R_R):
mnemonic = 'ctr'
opcode = DuckyOpcodes.CTR
encoding = EncodingR
@staticmethod
[docs] def execute(core, inst):
core.registers.map[inst.reg1].value = core.control_coprocessor.read(inst.reg2)
update_arith_flags(core, core.registers.map[inst.reg1])
[docs]class CTW(Descriptor_R_R):
mnemonic = 'ctw'
opcode = DuckyOpcodes.CTW
encoding = EncodingR
@staticmethod
[docs] def execute(core, inst):
core.control_coprocessor.write(inst.reg1, core.registers.map[inst.reg2].value)
[docs]class FPTC(Descriptor):
mnemonic = 'fptc'
opcode = DuckyOpcodes.FPTC
encoding = EncodingI
@staticmethod
[docs] def execute(core, inst):
core.mmu.release_ptes()
[docs]class DuckyInstructionSet(InstructionSet):
instruction_set_id = 0
opcodes = DuckyOpcodes
NOP(DuckyInstructionSet)
INT(DuckyInstructionSet)
IPI(DuckyInstructionSet)
RETINT(DuckyInstructionSet)
CALL(DuckyInstructionSet)
RET(DuckyInstructionSet)
CLI(DuckyInstructionSet)
STI(DuckyInstructionSet)
HLT(DuckyInstructionSet)
RST(DuckyInstructionSet)
IDLE(DuckyInstructionSet)
PUSH(DuckyInstructionSet)
POP(DuckyInstructionSet)
INC(DuckyInstructionSet)
DEC(DuckyInstructionSet)
ADD(DuckyInstructionSet)
SUB(DuckyInstructionSet)
CMP(DuckyInstructionSet)
J(DuckyInstructionSet)
INW(DuckyInstructionSet)
INB(DuckyInstructionSet)
INS(DuckyInstructionSet)
OUTS(DuckyInstructionSet)
OUTW(DuckyInstructionSet)
OUTB(DuckyInstructionSet)
AND(DuckyInstructionSet)
OR(DuckyInstructionSet)
XOR(DuckyInstructionSet)
NOT(DuckyInstructionSet)
SHIFTL(DuckyInstructionSet)
SHIFTR(DuckyInstructionSet)
# Memory access
LW(DuckyInstructionSet)
LS(DuckyInstructionSet)
LB(DuckyInstructionSet)
LI(DuckyInstructionSet)
LIU(DuckyInstructionSet)
LA(DuckyInstructionSet)
STW(DuckyInstructionSet)
STS(DuckyInstructionSet)
STB(DuckyInstructionSet)
MOV(DuckyInstructionSet)
SWP(DuckyInstructionSet)
MUL(DuckyInstructionSet)
DIV(DuckyInstructionSet)
UDIV(DuckyInstructionSet)
MOD(DuckyInstructionSet)
CMPU(DuckyInstructionSet)
CAS(DuckyInstructionSet)
SIS(DuckyInstructionSet)
# Branching instructions
BE(DuckyInstructionSet)
BNE(DuckyInstructionSet)
BZ(DuckyInstructionSet)
BNZ(DuckyInstructionSet)
BO(DuckyInstructionSet)
BNO(DuckyInstructionSet)
BS(DuckyInstructionSet)
BNS(DuckyInstructionSet)
BG(DuckyInstructionSet)
BGE(DuckyInstructionSet)
BL(DuckyInstructionSet)
BLE(DuckyInstructionSet)
# SET* instructions
SETE(DuckyInstructionSet)
SETNE(DuckyInstructionSet)
SETZ(DuckyInstructionSet)
SETNZ(DuckyInstructionSet)
SETO(DuckyInstructionSet)
SETNO(DuckyInstructionSet)
SETS(DuckyInstructionSet)
SETNS(DuckyInstructionSet)
SETG(DuckyInstructionSet)
SETGE(DuckyInstructionSet)
SETL(DuckyInstructionSet)
SETLE(DuckyInstructionSet)
LPM(DuckyInstructionSet)
# Control instructions
CTR(DuckyInstructionSet)
CTW(DuckyInstructionSet)
FPTC(DuckyInstructionSet)
DuckyInstructionSet.init()
INSTRUCTION_SETS = {
DuckyInstructionSet.instruction_set_id: DuckyInstructionSet
}
[docs]def get_instruction_set(i, exc = None):
from ..cpu import InvalidInstructionSetError
exc = exc or InvalidInstructionSetError
if i not in INSTRUCTION_SETS:
raise exc(i)
return INSTRUCTION_SETS[i]