Skip to content

Commit

Permalink
[Sim][#50] Test more of ed-prefixed instructions.
Browse files Browse the repository at this point in the history
  • Loading branch information
kosarev committed Sep 23, 2022
1 parent 36b8777 commit 08185fd
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 6 deletions.
107 changes: 101 additions & 6 deletions tests/z80sim/z80sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ def clear():
def get(id):
if id is None:
id = f't{len(__class__.__literals) + 1}'
else:
assert id.strip() == id, repr(id)
assert id.lower() not in ('', '0', '1', 'true', 'false'), repr(id)

t = __class__.__literals.get(id)
if t is not None:
Expand Down Expand Up @@ -740,7 +743,7 @@ def cast(x, width=None):

if width is not None:
bits = bits.zero_extended(width)
assert bits.width == width, (width, bits)
assert bits.width == width, (width, bits.width, bits)

return bits

Expand Down Expand Up @@ -865,6 +868,9 @@ def __eq__(self, other):
a, b = __class__.zero_extend_to_same_width(self, other)
return Bool.get_and(*(Bool.get_eq(x, y) for x, y in zip(a, b)))

def __ne__(self, other):
return ~self.__eq__(other)

def __ge__(self, other):
a, b = __class__.zero_extend_to_same_width(self, other)
return (a - b)[a.width]
Expand Down Expand Up @@ -2059,15 +2065,19 @@ def check(x):
instr = instrs[-1]
cycles = TestedInstrs.get_cycles(instr, phase)
opcode, ticks = cycles[0]
if opcode == 0xed:
opcode, ticks = cycles[1]

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

def phased(x):
A = 7

def phased(x, offset=0):
if isinstance(x, str):
assert '_p' not in x, x
return f'{x}_p{phase}'
return f'{x}_p{phase + offset}'
return x

def bits(id):
Expand Down Expand Up @@ -2159,9 +2169,12 @@ def get_pc_plus(off):
# description of the behaviour?
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)'):
'<alu> {b, c, d, e, h, l, a}', '<alu> (hl)',
'adc/sbc hl, <rp>'):
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))
return check(a | f)
if instr == 'rlca/rrca/rla/rra':
return check(Bool.ifelse(phased('is_rl'), before[f'reg_a{i - 1}'],
Expand All @@ -2176,7 +2189,6 @@ def get_pc_plus(off):
return check(Bool.ifelse(phased('is_scf'), FALSE, before[CF]))

if n.startswith('reg_a'):
A = 7
i = int(n[-1])
if instr == 'cpl':
return check(~before[n])
Expand Down Expand Up @@ -2219,7 +2231,7 @@ def get_pc_plus(off):
i = int(n[-1])
if n.startswith('reg_w'):
i += 8
if instr in ('add hl, <rp>', 'rrd/rld'):
if instr in ('add hl, <rp>', 'rrd/rld', 'adc/sbc hl, <rp>'):
return check((get_hl() + 1)[i])
if instr in ('call nn', 'ex (sp), hl'):
wz = Bits.concat(Bits(phased('r2'), width=8),
Expand All @@ -2229,6 +2241,10 @@ def get_pc_plus(off):
wz = Bits.concat(Bits(phased('hi'), width=8),
Bits(phased('lo'), width=8))
return check(wz[i])
if instr == 'reti/retn/xretn':
wz = Bits.concat(Bits(phased('r4'), width=8),
Bits(phased('r3'), width=8))
return check(wz[i])
if instr == 'jr d':
d = Bits(phased('r'), width=8)
wz = get_pc_plus(2) + d.sign_extended(16)
Expand Down Expand Up @@ -2260,6 +2276,14 @@ def get_pc_plus(off):
store_wz = Bits.concat(get_a(), (lo + 1).truncated(8))
wz = Bits.ifelse(phased('is_store'), store_wz, load_wz)
return check(wz[i])
if instr == 'in/out r, (c)':
wz = get_bc() + 1
return check(wz[i])
if instr == 'ld <rp>, (nn)/ld (nn), <rp>':
lo = Bits(phased('r3'), width=8)
hi = Bits(phased('r4'), width=8)
wz = Bits.concat(hi, lo) + 1
return check(wz[i])

if n == IFF2:
if instr == 'ei/di':
Expand Down Expand Up @@ -2419,6 +2443,44 @@ def get_pc_plus(off):
if n == ZF:
return check(a == 0x00)

if instr == 'in/out r, (c)':
y = Bits(phased('y'), width=3)
io = Bits(phased('io'), width=8)
is_in = Bool.get(phased('is_in'))
a = Bits.ifelse(~is_in | (y != A), get_a(), io)
if n in (NF, HF):
return check(Bool.ifelse(is_in, FALSE, before[n]))
if n == PF:
return check(Bool.ifelse(is_in, io.parity(), before[n]))
if n in (XF, YF, SF):
i = int(n[-1])
return check(Bool.ifelse(is_in, io[i], before[n]))
if n == ZF:
return check(Bool.ifelse(is_in, io == 0, before[n]))
if n.startswith('reg_a'):
i = int(n[-1])
return check(a[i])

if instr == 'adc/sbc hl, <rp>':
hl, rp = get_hl(), get_rp(opcode[4:6])
cf = Bits.ifelse(before[CF], 1, 0)
is_adc = Bool.get(phased('q') + '_b0')
r = Bits.ifelse(is_adc, hl + rp + cf, ((hl - rp) ^ 0x10000) - cf)
if n == CF:
return check(r[16])
if n == NF:
return check(~is_adc)
if n == PF:
overflow = r[16] ^ r[15] ^ hl[15] ^ rp[15]
return check(overflow)
if n in (XF, YF, SF):
i = int(n[-1])
return check(r[i + 8])
if n == HF:
return check(r[12] ^ hl[12] ^ rp[12])
if n == ZF:
return check(r.truncated(16) == 0)

return check(before[n])


Expand Down Expand Up @@ -2706,6 +2768,19 @@ def xpqz(x, p, q, z):
# xx ppq zzz
return Bits.concat(bits(x, 2), bits(p, 2), bits(q, 1), bits(z, 3))

def pattern(p):
bits = []
for b in reversed(p):
if b == ' ':
continue
if b in ('0', '1'):
b = (b != '0')
else:
b = f'{phased(b)}_b{len(bits)}'
bits.append(b)

return Bits(bits)

def f(p, *, ticks=4):
# Variable names must be phased, so don't allow them here.
assert not isinstance(p, str)
Expand Down Expand Up @@ -2830,7 +2905,27 @@ def get_non_at_hl_r(id='reg'):
ifelse('is_store', 0x4f, 0x5f))))
yield 'rrd/rld', (
f(0xed), f(ifelse('is_rrd', 0x67, 0x6f)), r3(), e4(), w3())
yield 'in/out r, (c)', (
f(0xed), f(xyz(1, 'y', ifelse('is_in', 0, 1))), io4())
yield 'adc/sbc hl, <rp>', (
f(0xed), f(xpqz(1, 'p', 'q', 2)), e4(), e3())
yield 'ld <rp>, (nn)/ld (nn), <rp>', (
f(0xed), f(xpqz(1, 'p', 'q', 3)),
r3('r3'), r3('r4'), rw3('rw5'), rw3('rw6'))
yield 'neg/xneg', (f(0xed), f(xyz(1, 'y', 4)))
yield 'reti/retn/xretn', (
f(0xed), f(xyz(1, 'y', 5)), r3('r3'), r3('r4'))
yield 'im/xim n', (f(0xed), f(xyz(1, 'y', 6)))

xnop_00 = pattern('00 xxx xxx')
xnop_01 = pattern('01 11x 111')
xnop_10 = ifelse('is_100', pattern('10 0xx xxx'),
pattern('10 1xx 1xx'))
xnop_11 = pattern('11 xxx xxx')
xnop = ifelse('is_0',
ifelse('is_00', xnop_00, xnop_01),
ifelse('is_10', xnop_10, xnop_11))
yield 'xnop', (f(0xed), f(xnop))


def test_instructions():
Expand Down
6 changes: 6 additions & 0 deletions z80.h
Original file line number Diff line number Diff line change
Expand Up @@ -825,15 +825,18 @@ class internals::decoder_base : public B {
fast_u8 p = get_p_part(op);

switch(op) {
// TODO: Combine with decoding xneg's.
case 0x44:
// NEG f(4) f(4)
return self().on_neg();
// TODO: Combine with decoding xim's.
case 0x46:
// IM im[y] f(4) f(4)
return self().on_im(0);
case 0x56:
case 0x5e:
return self().on_im(y - 1);
// TODO: Combine with decoding xretn's.
case 0x4d:
// RETI f(4) f(4) r(3) r(3)
return self().on_reti();
Expand Down Expand Up @@ -863,6 +866,7 @@ class internals::decoder_base : public B {
// RLD f(4) f(4) r(3) e(4) w(3)
return self().on_rld();
}
// TODO: Can be just switch(op & (x_mask | z_mask)) ?
if((op & x_mask) == 0100) {
switch(op & z_mask) {
case 0: {
Expand Down Expand Up @@ -3484,6 +3488,7 @@ class internals::executor_base : public B {
self().on_set_a(a);
self().on_set_f(f);
self().on_write_cycle(hl, get_low8(t)); }
// TODO: Combine with on_adc_hl_rp()?
void on_sbc_hl_rp(regp rp) {
fast_u16 hl = self().on_get_hl();
fast_u16 n = self().on_get_regp(rp, iregp::hl);
Expand All @@ -3500,6 +3505,7 @@ class internals::executor_base : public B {
zf_ari(r16) | hf_ari(r16 >> 8, hl >> 8, n >> 8) |
(pf_ari(r32 >> 8, hl >> 8, n >> 8) ^
(of ? pf_mask : 0)) |
// TODO: Can we just look at bit 16 of the 32-bit result?
cf_ari(r16 > hl || of) | nf_mask;

self().on_set_wz(inc16(hl));
Expand Down

0 comments on commit 08185fd

Please sign in to comment.