diff --git a/ColecoView.py b/ColecoView.py index d928f71..3824aef 100644 --- a/ColecoView.py +++ b/ColecoView.py @@ -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 diff --git a/Z80IL.py b/Z80IL.py index f711891..eee3874 100644 --- a/Z80IL.py +++ b/Z80IL.py @@ -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): @@ -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: @@ -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: @@ -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')))) @@ -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') @@ -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: @@ -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()) @@ -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: @@ -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<= 0 and oper_val <= 7 + mask = il.const(1, 1<