Skip to content

Commit

Permalink
[Sim][#50] Test 'rot/res/set r'.
Browse files Browse the repository at this point in the history
  • Loading branch information
kosarev committed Sep 27, 2022
1 parent 08185fd commit a36c40a
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 6 deletions.
102 changes: 98 additions & 4 deletions tests/z80sim/z80sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -824,7 +824,19 @@ def truncated(self, width):
return __class__(self.bits[:width])

def __lshift__(self, n):
return __class__((FALSE,) * n + self.bits)
if isinstance(n, int):
return __class__((FALSE,) * n + self.bits)

n = __class__.cast(n)
if n.width == 0:
return self

s = self << __class__(n.bits[:-1])
return __class__.ifelse(n.msb, s << (1 << (n.width - 1)), s)

@staticmethod
def get_mask(n):
return Bits(1) << n

@staticmethod
def get_or(*args):
Expand Down Expand Up @@ -2065,14 +2077,14 @@ def check(x):
instr = instrs[-1]
cycles = TestedInstrs.get_cycles(instr, phase)
opcode, ticks = cycles[0]
if opcode == 0xed:
if opcode in (0xcb, 0xed):
opcode, ticks = cycles[1]

prev_instr = None
if len(instrs) >= 2:
prev_instr = instrs[-2]

A = 7
AT_HL, A = 6, 7

def phased(x, offset=0):
if isinstance(x, str):
Expand Down Expand Up @@ -2170,11 +2182,15 @@ def get_pc_plus(off):
if prev_instr in ('scf/ccf', 'add hl, <rp>', '<alu> n',
'inc/dec {b, c, d, e, h, l, a}', 'inc/dec (hl)',
'<alu> {b, c, d, e, h, l, a}', '<alu> (hl)',
'adc/sbc hl, <rp>'):
'adc/sbc hl, <rp>', 'bit (hl)'):
return check(a)
f = before[f'reg_f{i}']
if prev_instr == 'in/out r, (c)':
return check(Bool.ifelse(phased('is_in', -1), a, a | f))
if prev_instr == 'rot/res/set (hl)':
return check(Bool.ifelse(phased('is_rot', -1), a, a | f))
if prev_instr == 'rot/bit/res/set {b, c, d, e, h, l, a}':
return check(Bool.ifelse(phased('is_rot_bit', -1), a, a | f))
return check(a | f)
if instr == 'rlca/rrca/rla/rra':
return check(Bool.ifelse(phased('is_rl'), before[f'reg_a{i - 1}'],
Expand Down Expand Up @@ -2481,6 +2497,70 @@ def get_pc_plus(off):
if n == ZF:
return check(r.truncated(16) == 0)

if instr in ('bit (hl)', 'rot/res/set (hl)',
'rot/bit/res/set {b, c, d, e, h, l, a}'):
y = Bits(phased('y'), width=3)

reg = Bits(opcode[0:3])
v = get_r(reg)

# RLC, RRC, RL, RR, SLA, SRA, SLL, SRL
is_right = y[0]
rot_cf = Bool.ifelse(is_right, v[0], v[7])

is_nine_bit = y[1]
is_shift = y[2]
cf = before[CF]
b = Bool.ifelse(
is_shift,
Bool.ifelse(is_nine_bit, ~is_right, is_right & v[7]),
Bool.ifelse(is_nine_bit, cf, rot_cf))

rot_r = Bits.ifelse(is_right,
Bits(v.bits[1:8] + (b,)),
Bits((b,) + v.bits[0:7]))

m = Bits.get_mask(y)
bit_r = v & m
set_r = v | m
res_r = v & ~m

ROT, BIT, RES, SET = range(4)
op = Bits(opcode[6:8])

if n == CF:
return check(Bool.ifelse(op == ROT, rot_cf, cf))
if n == NF:
return check(Bool.ifelse(op == ROT, FALSE,
(op != BIT) & before[n]))
if n == HF:
return check(Bool.ifelse(op == ROT, FALSE,
(op == BIT) | before[n]))
if n == PF:
return check(Bool.ifelse(
op == BIT, bit_r == 0,
Bool.ifelse(op == ROT, rot_r.parity(), before[n])))
if n in (XF, YF):
i = int(n[-1])
return check(Bool.ifelse(
op == BIT,
Bool.ifelse(reg == AT_HL, before[f'reg_w{i}'], v[i]),
Bool.ifelse(op == ROT, rot_r[i], before[n])))
if n == ZF:
return check(Bool.ifelse(
op == BIT, bit_r == 0,
Bool.ifelse(op == ROT, rot_r == 0, before[n])))
if n == SF:
return check(Bool.ifelse(
op == BIT, bit_r[7],
Bool.ifelse(op == ROT, rot_r[7], before[n])))
if n.startswith('reg_a'):
i = int(n[-1])
return check(Bool.ifelse(
(op == BIT) | (reg != A), before[n],
Bool.ifelse(op == ROT, rot_r[i],
Bool.ifelse(op == RES, res_r[i], set_r[i]))))

return check(before[n])


Expand Down Expand Up @@ -2927,6 +3007,20 @@ def get_non_at_hl_r(id='reg'):
ifelse('is_10', xnop_10, xnop_11))
yield 'xnop', (f(0xed), f(xnop))

ROT, BIT, RES, SET = range(4)
rot_bit = ifelse('is_rot', ROT, BIT)
res_set = ifelse('is_res', RES, SET)
rot_bit_res_set = ifelse('is_rot_bit', rot_bit, res_set)
yield 'rot/bit/res/set {b, c, d, e, h, l, a}', (
f(0xcb), f(xyz(rot_bit_res_set, 'y', get_non_at_hl_r())))

yield 'bit (hl)', (
f(0xcb), f(xyz(BIT, 'y', AT_HL)), r4())

rot_res_set = ifelse('is_rot', ROT, res_set)
yield 'rot/res/set (hl)', (
f(0xcb), f(xyz(rot_res_set, 'y', AT_HL)), r4(), w3())


def test_instructions():
seqs = []
Expand Down
10 changes: 8 additions & 2 deletions z80.h
Original file line number Diff line number Diff line change
Expand Up @@ -3685,26 +3685,29 @@ class internals::executor_base : public B {
void do_rot(rot k, fast_u8 &n, fast_u8 &f) {
fast_u8 t = n;
bool cf = f & cf_mask;

// TODO: Can this be simplified? E.g., in all cases the
// direction is defined by bit 0. See testing nodes in
// z80sim.py.
switch(k) {
case rot::rlc:
n = rol8(n);
f = (n & (sf_mask | yf_mask | xf_mask | cf_mask)) | zf_ari(n) |
pf_log(n);
break;
case rot::rrc:
// TODO: Use ror()?
n = mask8((n >> 1) | (n << 7));
f = (n & (sf_mask | yf_mask | xf_mask)) | zf_ari(n) | pf_log(n) |
cf_ari(t & 0x01);
break;
case rot::rl:
n = mask8((n << 1) | (cf ? 1 : 0));
// TODO: We don't need to read F here.
f = (n & (sf_mask | yf_mask | xf_mask)) | zf_ari(n) | pf_log(n) |
cf_ari(t & 0x80);
break;
case rot::rr:
n = (n >> 1) | ((cf ? 1u : 0u) << 7);
// TODO: We don't need to read F here.
f = (n & (sf_mask | yf_mask | xf_mask)) | zf_ari(n) | pf_log(n) |
cf_ari(t & 0x01);
break;
Expand All @@ -3716,11 +3719,13 @@ class internals::executor_base : public B {
break;
case rot::sra:
n = (n >> 1) | (n & 0x80);
// TODO: We don't need to read F here.
f = (n & (sf_mask | yf_mask | xf_mask)) | zf_ari(n) | pf_log(n) |
cf_ari(t & 0x01);
break;
case rot::sll:
n = mask8(n << 1) | 1;
// TODO: We don't need to read F here.
f = (n & (sf_mask | yf_mask | xf_mask)) | zf_ari(n) | pf_log(n) |
cf_ari(t & 0x80);
break;
Expand Down Expand Up @@ -3844,6 +3849,7 @@ class internals::executor_base : public B {
return ((x >> 6) ^ (x >> 5)) & pf_mask;
}

// TODO: Rename to parity8(). Deprecate pf_log().
static fast_u8 pf_log(fast_u8 n) {
// Compute parity. First, half the range of bits to
// consider by xor'ing nibbles of the passed value. Then,
Expand Down

0 comments on commit a36c40a

Please sign in to comment.