From 697468384de5ca1f666fe1ac3d921f0835598a8f Mon Sep 17 00:00:00 2001 From: Sam Russell Date: Wed, 28 Feb 2024 21:38:58 +0100 Subject: [PATCH 1/7] Lifted more opcodes, minor bugfix in ColecoView bugfix: add perform_get_address_size(self) to ColecoView feature: lifted RST, conditional CALL, CPL, NEG, RES, SET --- ColecoView.py | 6 +++++- Z80IL.py | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 3 deletions(-) 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..d1dbd87 100644 --- a/Z80IL.py +++ b/Z80IL.py @@ -345,6 +345,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 +365,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 +378,12 @@ 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.DJNZ: # decrement B tmp = il.reg(1, 'B') @@ -491,6 +498,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()) @@ -605,6 +618,38 @@ 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 == 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< Date: Wed, 28 Feb 2024 22:20:04 +0100 Subject: [PATCH 2/7] Implement RRC and RRCA --- Z80IL.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Z80IL.py b/Z80IL.py index d1dbd87..b458f36 100644 --- a/Z80IL.py +++ b/Z80IL.py @@ -602,7 +602,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: @@ -618,6 +618,24 @@ 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))) From c7b1194a610c5bca834de6e421a44285ed4d5b6d Mon Sep 17 00:00:00 2001 From: Sam Russell Date: Wed, 28 Feb 2024 22:34:59 +0100 Subject: [PATCH 3/7] Implement EXX opcode --- Z80IL.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Z80IL.py b/Z80IL.py index b458f36..fe4dc1d 100644 --- a/Z80IL.py +++ b/Z80IL.py @@ -186,6 +186,26 @@ 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 """ @@ -430,6 +450,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: From 68cf3162f5f83c66600ce3bcc0e6ba21d710c900 Mon Sep 17 00:00:00 2001 From: Sam Russell Date: Thu, 29 Feb 2024 08:45:11 +0100 Subject: [PATCH 4/7] expressionify: remove shortcut on int at start --- Z80IL.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/Z80IL.py b/Z80IL.py index fe4dc1d..74c4752 100644 --- a/Z80IL.py +++ b/Z80IL.py @@ -209,9 +209,6 @@ def exchange(lhs_reg, rhs_reg, il): 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): From 6477aa5e6e6af28182939f43773a2ddb6a747aa7 Mon Sep 17 00:00:00 2001 From: Sam Russell Date: Thu, 29 Feb 2024 11:55:32 +0100 Subject: [PATCH 5/7] Implement DAA --- Z80IL.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/Z80IL.py b/Z80IL.py index 74c4752..576c47e 100644 --- a/Z80IL.py +++ b/Z80IL.py @@ -401,6 +401,63 @@ def gen_instr_il(addr, decoded, il): 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') From 5e9c2ac5466d5fad1649c5dbf9b4693a2549bb4d Mon Sep 17 00:00:00 2001 From: Sam Russell Date: Thu, 29 Feb 2024 12:29:24 +0100 Subject: [PATCH 6/7] Implement BCD flags --- Z80IL.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Z80IL.py b/Z80IL.py index 576c47e..4d703ea 100644 --- a/Z80IL.py +++ b/Z80IL.py @@ -268,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_SUB: + # 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: From 072450106155b3ccf2fb19a17d90eb30d2248733 Mon Sep 17 00:00:00 2001 From: Sam Russell Date: Thu, 29 Feb 2024 12:57:19 +0100 Subject: [PATCH 7/7] minor bugfix, s/SBC/SBB --- Z80IL.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Z80IL.py b/Z80IL.py index 4d703ea..eee3874 100644 --- a/Z80IL.py +++ b/Z80IL.py @@ -286,7 +286,7 @@ def gen_flag_il(op, size, write_type, flag, operands, il): 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_SUB: + 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"))