Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion ColecoView.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,5 +168,9 @@ def perform_is_executable(self):
def perform_get_entry_point(self):
return 0

# undocumented but looks to match arch.address_size
# so should be in bytes and should equal arch.address_size
# but this breaks .synthetic_builtins when the rom mapping uses the whole memory
# so we'll leave it at 8
def perform_get_address_size(self):
return 2
return 8
186 changes: 180 additions & 6 deletions Z80IL.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,29 @@ def operand_to_il(oper_type, oper_val, il, size_hint=0, peel_load=False):
else:
raise Exception("unknown operand type: " + str(oper_type))

def exchange(lhs_reg, rhs_reg, il):
# temp0 = lhs
il.append(il.expr(LowLevelILOperation.LLIL_SET_REG,
LLIL_TEMP(0),
il.reg(2, lhs_reg),
size = 2
))

# lhs = rhs
il.append(il.set_reg(2,
lhs_reg,
il.reg(2, rhs_reg)
))

# rhs = temp0
il.append(il.set_reg(2,
rhs_reg,
il.expr(LowLevelILOperation.LLIL_REG, LLIL_TEMP(0), 2)
))

def expressionify(size, foo, il, temps_are_conds=False):
""" turns the "reg or constant" operands to get_flag_write_low_level_il()
into lifted expressions """
if isinstance(foo, int):
return foo

if isinstance(foo, ILRegister):
# LowLevelILExpr is different than ILRegister
if temps_are_conds and LLIL_TEMP(foo.index):
Expand Down Expand Up @@ -251,12 +268,44 @@ def gen_flag_il(op, size, write_type, flag, operands, il):
return il.set_flag('h', il.test_bit(1, il.reg(1, 'F'), il.const(1, 1<<4)))
if op == LowLevelILOperation.LLIL_XOR:
return il.const(1, 0)
if op == LowLevelILOperation.LLIL_ADD:
# we've overflowed bottom nybble if it's lower after an add
original_bottom_nybble = il.and_expr(size, expressionify(size, operands[0], il), il.const(size, 0x0F))
result = il.add(size, expressionify(size, operands[0], il), expressionify(size, operands[1], il))
result_bottom_nybble = il.and_expr(size, result, il.const(size, 0x0F))
return il.compare_unsigned_less_than(size, result_bottom_nybble, original_bottom_nybble)
if op == LowLevelILOperation.LLIL_ADC:
# we've overflowed bottom nybble if it's lower after an adc
original_bottom_nybble = il.and_expr(size, expressionify(size, operands[0], il), il.const(size, 0x0F))
result = il.add_carry(size, expressionify(size, operands[0], il), expressionify(size, operands[1], il), il.flag("c"))
result_bottom_nybble = il.and_expr(size, result, il.const(size, 0x0F))
return il.compare_unsigned_less_than(size, result_bottom_nybble, original_bottom_nybble)
if op == LowLevelILOperation.LLIL_SUB:
# we've overflowed bottom nybble if it's higher after a sub
original_bottom_nybble = il.and_expr(size, expressionify(size, operands[0], il), il.const(size, 0x0F))
result = il.sub(size, expressionify(size, operands[0], il), expressionify(size, operands[1], il))
result_bottom_nybble = il.and_expr(size, result, il.const(size, 0x0F))
return il.compare_unsigned_greater_than(size, result_bottom_nybble, original_bottom_nybble)
if op == LowLevelILOperation.LLIL_SBB:
# we've overflowed bottom nybble if it's higher after a sbc
original_bottom_nybble = il.and_expr(size, expressionify(size, operands[0], il), il.const(size, 0x0F))
result = il.sub_borrow(size, expressionify(size, operands[0], il), expressionify(size, operands[1], il), il.flag("c"))
result_bottom_nybble = il.and_expr(size, result, il.const(size, 0x0F))
return il.compare_unsigned_greater_than(size, result_bottom_nybble, original_bottom_nybble)
if op == LowLevelILOperation.LLIL_NEG:
# if bottom nybble != 0 then we've had to borrow and therefore h needs to be set
bottom_nybble = il.and_expr(size, expressionify(size, operands[0], il), il.const(size, 0x0F))
return il.compare_not_equal(size, bottom_nybble, il.const(size, 0x00))

if flag == 'n':
if op == LowLevelILOperation.LLIL_POP:
return il.set_flag('n', il.test_bit(1, il.reg(1, 'F'), il.const(1, 1<<1)))
if op == LowLevelILOperation.LLIL_XOR:
return il.const(1, 0)
if op in [LowLevelILOperation.LLIL_ADD, LowLevelILOperation.LLIL_ADC]:
return il.const(1, 0)
if op in [LowLevelILOperation.LLIL_SUB, LowLevelILOperation.LLIL_SBB, LowLevelILOperation.LLIL_NEG]:
return il.const(1, 1)

if flag == 'pv':
if op == LowLevelILOperation.LLIL_SBB:
Expand Down Expand Up @@ -345,6 +394,7 @@ def gen_instr_il(addr, decoded, il):
tmp = il.set_reg(size, reg2str(oper_val), tmp)
il.append(tmp)
else:
# this shouldn't ever be hit as all the opcodes have lhs = register
il.append(il.unimplemented())

elif decoded.op == OP.AND:
Expand All @@ -364,8 +414,8 @@ def gen_instr_il(addr, decoded, il):
if oper_type == OPER_TYPE.ADDR:
il.append(il.call(il.const_pointer(2, oper_val)))
else:
# TODO: handle the conditional
il.append(il.unimplemented())
tmp = il.call(il.const_pointer(2, operb_val))
append_conditional_instr(oper_val, tmp, il)

elif decoded.op == OP.CCF:
il.append(il.set_flag('c', il.not_expr(0, il.flag('c'))))
Expand All @@ -377,6 +427,69 @@ def gen_instr_il(addr, decoded, il):
sub = il.sub(1, lhs, rhs, flags='*')
il.append(sub)

elif decoded.op == OP.CPL:
tmp = il.reg(1, 'A')
tmp = il.xor_expr(1, il.const(1, 0xFF), tmp, flags='*')
tmp = il.set_reg(1, 'A', tmp)
il.append(tmp)

elif decoded.op == OP.DAA:
# correct BCD after arithmetic
# based on page 17-18 http://www.z80.info/zip/z80-documented.pdf
# and pseudocode from https://stackoverflow.com/questions/8119577/z80-daa-instruction/57837042#57837042

# first step, find diff
# initialise to 0
diff = LLIL_TEMP(0)
il.append(il.set_reg(1, diff, il.const(1, 0)))

# lower carry
# if lower nybble > 9 OR H flag set then we diff by 0x06
label_add_6 = LowLevelILLabel()
label_dont_add_6 = LowLevelILLabel()
lower_nybble = il.xor_expr(1, il.const(1, 0x0F), il.reg(1, 'A'))
cond_gt_9 = il.compare_unsigned_greater_than(1, lower_nybble, il.const(1, 0x09))
cond_hf_set = il.flag('h')
cond = il.or_expr(1, cond_gt_9, cond_hf_set)

il.append(il.if_expr(cond, label_add_6, label_dont_add_6))
il.mark_label(label_add_6)
il.append(il.set_reg(1, diff, il.add(1, il.reg(1, diff), il.const(1, 0x06))))
il.mark_label(label_dont_add_6)

# upper carry
# if byte > 0x99 or C flag set then we diff by 0x60
label_add_60 = LowLevelILLabel()
label_dont_add_60 = LowLevelILLabel()
cond_gt_99 = il.compare_unsigned_greater_than(1, il.reg(1, 'A'), il.const(1, 0x99))
cond_cf_set = il.flag('c')
cond = il.or_expr(1, cond_gt_99, cond_cf_set)

il.append(il.if_expr(cond, label_add_60, label_dont_add_60))
il.mark_label(label_add_60)
il.append(il.set_reg(1, diff, il.add(1, il.reg(1, diff), il.const(1, 0x60))))
# set C flag here now as this is also the condition
# we never reset it but we do set it if we make a high nybble adjustment
il.append(il.set_flag('c', il.const(1, 1)))
il.mark_label(label_dont_add_60)

# set h flag
# ideally the flag should evaluate to an expression that looks good in an if statement in HLIL
# i suspect these flags never get queried
# TODO implement h flag

# apply adjustment
label_neg_diff = LowLevelILLabel()
label_add_diff = LowLevelILLabel()

il.append(il.if_expr(il.flag('n'), label_neg_diff, label_add_diff))
il.mark_label(label_neg_diff)
il.append(il.set_reg(1, diff, il.neg_expr(1, il.reg(1, diff))))
il.mark_label(label_add_diff)
il.append(il.set_reg(1, 'A', il.add(1, il.reg(1, 'A'), il.reg(1, diff), 'z')))

# TODO s and pv flags

elif decoded.op == OP.DJNZ:
# decrement B
tmp = il.reg(1, 'B')
Expand Down Expand Up @@ -423,6 +536,11 @@ def gen_instr_il(addr, decoded, il):
il.expr(LowLevelILOperation.LLIL_REG, LLIL_TEMP(0), 2)
))

elif decoded.op == OP.EXX:
exchange('BC', "BC'", il)
exchange('DE', "DE'", il)
exchange('HL', "HL'", il)

elif decoded.op == OP.INC:
# inc reg can be 1-byte or 2-byte
if oper_type == OPER_TYPE.REG:
Expand Down Expand Up @@ -491,6 +609,12 @@ def gen_instr_il(addr, decoded, il):
if do_mark:
il.mark_label(f)

elif decoded.op == OP.NEG:
tmp = il.reg(1, 'A')
tmp = il.sub(1, il.const(1, 0), tmp, flags='*')
tmp = il.set_reg(1, 'A', tmp)
il.append(tmp)

elif decoded.op == OP.NOP:
il.append(il.nop())

Expand Down Expand Up @@ -589,7 +713,7 @@ def gen_instr_il(addr, decoded, il):

elif decoded.op in [OP.RR, OP.RRA]:
# rotate THROUGH carry: b7=c, c=b0
# z80 'RL' -> llil 'RRC'
# z80 'RR' -> llil 'RRC'
if decoded.op == OP.RRA:
src = il.reg(1, 'A')
else:
Expand All @@ -605,6 +729,56 @@ def gen_instr_il(addr, decoded, il):
tmp2 = operand_to_il(oper_type, oper_val, il, 1, peel_load=True)
il.append(il.store(1, tmp2, rot))

elif decoded.op in [OP.RRC, OP.RRCA]:
# rotate and COPY to carry: b0=c, c=b8
# z80 'RR' -> llil 'ROR'
if decoded.op == OP.RRCA:
src = il.reg(1, 'A')
else:
src = operand_to_il(oper_type, oper_val, il, 1)

rot = il.rotate_right(1, src, il.const(1, 1), flags='c')

if decoded.op == OP.RRCA:
il.append(il.set_reg(1, 'A', rot))
elif oper_type == OPER_TYPE.REG:
il.append(il.set_reg(1, reg2str(oper_val), rot))
else:
tmp2 = operand_to_il(oper_type, oper_val, il, 1, peel_load=True)
il.append(il.store(1, tmp2, rot))

elif decoded.op == OP.RST:
# this is like call but we zero extend
il.append(il.call(il.const_pointer(2, oper_val)))

elif decoded.op == OP.RES:
assert oper_type == OPER_TYPE.IMM
assert oper_val >= 0 and oper_val <= 7
mask = il.const(1, (1<<oper_val) ^ 0xFF)
operand = operand_to_il(operb_type, operb_val, il, 1)
result = il.and_expr(1, operand, mask)

if operb_type == OPER_TYPE.REG:
tmp = il.set_reg(1, reg2str(operb_val), result)
else:
tmp = il.store(1, operand_to_il(operb_type, operb_val, il, 1, peel_load=True), result)

il.append(tmp)

elif decoded.op == OP.SET:
assert oper_type == OPER_TYPE.IMM
assert oper_val >= 0 and oper_val <= 7
mask = il.const(1, 1<<oper_val)
operand = operand_to_il(operb_type, operb_val, il, 1)
result = il.or_expr(1, operand, mask)

if operb_type == OPER_TYPE.REG:
tmp = il.set_reg(1, reg2str(operb_val), result)
else:
tmp = il.store(1, operand_to_il(operb_type, operb_val, il, 1, peel_load=True), result)

il.append(tmp)

elif decoded.op == OP.SRA:
tmp = operand_to_il(oper_type, oper_val, il, 1)
tmp = il.arith_shift_right(1, tmp, il.const(1, 1), flags='c')
Expand Down