From e0fcdb9164ea5963e84561a7e97b34f5e58b955c Mon Sep 17 00:00:00 2001 From: Richard Dymond Date: Wed, 27 Mar 2024 17:48:34 -0300 Subject: [PATCH] Make Simulator use {djnz,ldir}_fast() only if interrupts are disabled --- skoolkit/simulator.py | 5 +- skoolkit/skoolmacro.py | 2 +- skoolkit/trace.py | 2 +- tests/test_simulator.py | 35 +++++++----- tests/test_trace.py | 118 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 17 deletions(-) diff --git a/skoolkit/simulator.py b/skoolkit/simulator.py index 2abfa807..cb27487f 100644 --- a/skoolkit/simulator.py +++ b/skoolkit/simulator.py @@ -393,7 +393,7 @@ def djnz(self, registers, memory): registers[15] = R1[registers[15]] # R def djnz_fast(self, registers, memory): - if memory[(registers[24] + 1) % 65536] == 0xFE: + if registers[26] == 0 and memory[(registers[24] + 1) % 65536] == 0xFE: b = (registers[2] - 1) % 256 registers[2] = 0 r = registers[15] @@ -737,6 +737,9 @@ def ldi(self, registers, memory, inc, repeat): registers[15] = R2[registers[15]] # R def ldir_fast(self, registers, memory, inc): + if registers[26]: + self.ldi(registers, memory, inc, 1) + return de = registers[5] + 256 * registers[4] bc = registers[3] + 256 * registers[2] hl = registers[7] + 256 * registers[6] diff --git a/skoolkit/skoolmacro.py b/skoolkit/skoolmacro.py index 350eb0bc..e78bc972 100644 --- a/skoolkit/skoolmacro.py +++ b/skoolkit/skoolmacro.py @@ -571,7 +571,7 @@ def _read_sim_state(writer, execint, reg=None, clear=0): if v >= 0: registers[r] = v state = {a: registers.pop(a) for a in ('iff', 'im', 'halted', 'tstates', 'fffd', 'ay')} - config = {'fast_djnz': execint < 1, 'fast_ldir': execint < 1} + config = {'fast_djnz': True, 'fast_ldir': True} if len(writer.snapshot) == 0x20000: config['frame_duration'] = FRAME_DURATIONS[1] config['int_active'] = INT_ACTIVE[1] diff --git a/skoolkit/trace.py b/skoolkit/trace.py index 298774f9..874dbc6f 100644 --- a/skoolkit/trace.py +++ b/skoolkit/trace.py @@ -172,7 +172,7 @@ def run(snafile, options, config): registers[reg] = get_int_param(val, True) except ValueError: raise SkoolKitError("Cannot parse register value: {}".format(spec)) - fast = options.verbose == 0 and not options.interrupts + fast = options.verbose == 0 and options.max_operations == 0 and options.max_tstates == 0 sim_config = {'fast_djnz': fast, 'fast_ldir': fast} if snapshot: border = snapshot.border diff --git a/tests/test_simulator.py b/tests/test_simulator.py index 26f33406..fb8f5518 100644 --- a/tests/test_simulator.py +++ b/tests/test_simulator.py @@ -1923,16 +1923,20 @@ def test_djnz_fast(self): registers = simulator.registers start = 35732 - for offset, b_in, b_out, timing, r_out, end in ( - (-2, 100, 0, 1295, 100, start + 2), - (-2, 1, 0, 8, 1, start + 2), - (-2, 0, 0, 3323, 0, start + 2), - (-3, 100, 99, 13, 1, start - 1), - (-3, 1, 0, 8, 1, start + 2), - (-3, 0, 255, 13, 1, start - 1), + for offset, iff, b_in, b_out, timing, r_out, end in ( + (-2, 0, 100, 0, 1295, 100, start + 2), + (-2, 0, 1, 0, 8, 1, start + 2), + (-2, 0, 0, 0, 3323, 0, start + 2), + (-2, 1, 100, 99, 13, 1, start), + (-2, 1, 1, 0, 8, 1, start + 2), + (-2, 1, 0, 255, 13, 1, start), + (-3, 0, 100, 99, 13, 1, start - 1), + (-3, 0, 1, 0, 8, 1, start + 2), + (-3, 0, 0, 255, 13, 1, start - 1), ): operation = f'DJNZ ${start + 2 + offset:04X}' data = (16, offset & 0xFF) + registers[IFF] = iff registers[B] = b_in registers[R] = 0 reg_out = {B: b_out, R: r_out} @@ -2261,14 +2265,17 @@ def test_ldir_fast(self): start = 30000 at_hl = 250 - for bc_in, bc_out, de_in, de_out, hl_in, hl_out, f_out, r_out, timing, end in ( - # SZ5H3PNC - (52, 1, 29950, 30001, 40000, 40051, 0b00100100, 102, 1066, start), # 0xED overwritten - (51, 0, 29950, 30001, 40000, 40051, 0b00101000, 102, 1066, start + 2), # 0xED overwritten - (50, 0, 29950, 30000, 40000, 40050, 0b00101000, 100, 1045, start + 2), - ( 1, 0, 29950, 29951, 40000, 40001, 0b00101000, 2, 16, start + 2), - ( 0, 1, 30002, 30001, 30002, 30001, 0b00100100, 126, 1376230, start), # 0xED overwritten + for iff, bc_in, bc_out, de_in, de_out, hl_in, hl_out, f_out, r_out, timing, end in ( + # SZ5H3PNC + (0, 52, 1, 29950, 30001, 40000, 40051, 0b00100100, 102, 1066, start), # 0xED overwritten + (0, 51, 0, 29950, 30001, 40000, 40051, 0b00101000, 102, 1066, start + 2), # 0xED overwritten + (0, 50, 0, 29950, 30000, 40000, 40050, 0b00101000, 100, 1045, start + 2), + (0, 1, 0, 29950, 29951, 40000, 40001, 0b00101000, 2, 16, start + 2), + (0, 0, 1, 30002, 30001, 30002, 30001, 0b00100100, 126, 1376230, start), # 0xED overwritten + (1, 50, 49, 50000, 50001, 40000, 40001, 0b00100100, 2, 21, start), + (1, 1, 0, 50000, 50001, 40000, 40001, 0b00101000, 2, 16, start + 2), ): + registers[IFF] = iff registers[B] = bc_in // 256 registers[C] = bc_in % 256 registers[D] = de_in // 256 diff --git a/tests/test_trace.py b/tests/test_trace.py index 9639211b..4507ea97 100644 --- a/tests/test_trace.py +++ b/tests/test_trace.py @@ -1178,6 +1178,44 @@ def test_option_max_operations(self): self.assertEqual(error, '') self.assertEqual(dedent(exp_output).strip(), output.rstrip()) + @patch.object(trace, 'write_snapshot', mock_write_snapshot) + def test_option_max_operations_disables_fast_djnz(self): + data = ( + 0xF3, # $8000 DI + 0x06, 0x02, # $8001 LD B,2 + 0x10, 0xFE, # $8003 DJNZ $8002 + ) + binfile = self.write_bin_file(data, suffix='.bin') + start = 32768 + exp_output = """ + Stopped at $8003: 3 operations + Wrote out.z80 + """ + output, error = self.run_trace(f'-o {start} -m 3 {binfile} out.z80') + self.assertEqual(error, '') + self.assertEqual(dedent(exp_output).strip(), output.rstrip()) + self.assertLessEqual({'BC=256', 'R=3'}, set(s_reg)) + + @patch.object(trace, 'write_snapshot', mock_write_snapshot) + def test_option_max_operations_disables_fast_ldir(self): + data = ( + 0xF3, # $8000 DI + 0x01, 0x02, 0x00, # $8001 LD BC,$0002 + 0x11, 0x01, 0xC0, # $8004 LD DE,$C001 + 0x21, 0x00, 0xC0, # $8007 LD HL,$C000 + 0xED, 0xB0, # $800A LDIR + ) + binfile = self.write_bin_file(data, suffix='.bin') + start = 32768 + exp_output = """ + Stopped at $800A: 5 operations + Wrote out.z80 + """ + output, error = self.run_trace(f'-o {start} -m 5 {binfile} out.z80') + self.assertEqual(error, '') + self.assertEqual(dedent(exp_output).strip(), output.rstrip()) + self.assertLessEqual({'BC=1', 'DE=49154', 'HL=49153', 'R=6'}, set(s_reg)) + def test_option_max_tstates(self): data = [ 0xAF, # XOR A @@ -1195,6 +1233,44 @@ def test_option_max_tstates(self): self.assertEqual(error, '') self.assertEqual(dedent(exp_output).strip(), output.rstrip()) + @patch.object(trace, 'write_snapshot', mock_write_snapshot) + def test_option_max_tstates_disables_fast_djnz(self): + data = ( + 0xF3, # $8000 DI ; [4] + 0x06, 0x02, # $8001 LD B,2 ; [7] + 0x10, 0xFE, # $8003 DJNZ $8002 ; [13/8] + ) + binfile = self.write_bin_file(data, suffix='.bin') + start = 32768 + exp_output = """ + Stopped at $8003: 24 T-states + Wrote out.z80 + """ + output, error = self.run_trace(f'-o {start} -M 24 {binfile} out.z80') + self.assertEqual(error, '') + self.assertEqual(dedent(exp_output).strip(), output.rstrip()) + self.assertLessEqual({'BC=256', 'R=3'}, set(s_reg)) + + @patch.object(trace, 'write_snapshot', mock_write_snapshot) + def test_option_max_tstates_disables_fast_ldir(self): + data = ( + 0xF3, # $8000 DI ; [4] + 0x01, 0x02, 0x00, # $8001 LD BC,$0002 ; [10] + 0x11, 0x01, 0xC0, # $8004 LD DE,$C001 ; [10] + 0x21, 0x00, 0xC0, # $8007 LD HL,$C000 ; [10] + 0xED, 0xB0, # $800A LDIR ; [21/16] + ) + binfile = self.write_bin_file(data, suffix='.bin') + start = 32768 + exp_output = """ + Stopped at $800A: 55 T-states + Wrote out.z80 + """ + output, error = self.run_trace(f'-o {start} -M 55 {binfile} out.z80') + self.assertEqual(error, '') + self.assertEqual(dedent(exp_output).strip(), output.rstrip()) + self.assertLessEqual({'BC=1', 'DE=49154', 'HL=49153', 'R=6'}, set(s_reg)) + def test_option_no_interrupts(self): data = [ 0x00, # $8000 NOP ; t=69886 (interrupt would normally follow) @@ -1495,6 +1571,48 @@ def test_option_verbose(self): """ self.assertEqual(dedent(exp_output).strip(), output.rstrip()) + def test_option_verbose_disables_fast_djnz(self): + data = ( + 0xF3, # $8000 DI + 0x06, 0x02, # $8001 LD B,2 + 0x10, 0xFE, # $8003 DJNZ $8002 + ) + binfile = self.write_bin_file(data, suffix='.bin') + start = 32768 + exp_output = """ + $8000 DI + $8001 LD B,$02 + $8003 DJNZ $8003 + $8003 DJNZ $8003 + Stopped at $8005 + """ + output, error = self.run_trace(f'-v -o {start} -S 0x8005 {binfile}') + self.assertEqual(error, '') + self.assertEqual(dedent(exp_output).strip(), output.rstrip()) + + def test_option_verbose_disables_fast_ldir(self): + data = ( + 0xF3, # $8000 DI + 0x01, 0x02, 0x00, # $8001 LD BC,$0002 + 0x11, 0x01, 0xC0, # $8004 LD DE,$C001 + 0x21, 0x00, 0xC0, # $8007 LD HL,$C000 + 0xED, 0xB0, # $800A LDIR + ) + binfile = self.write_bin_file(data, suffix='.bin') + start = 32768 + exp_output = """ + $8000 DI + $8001 LD BC,$0002 + $8004 LD DE,$C001 + $8007 LD HL,$C000 + $800A LDIR + $800A LDIR + Stopped at $800C + """ + output, error = self.run_trace(f'-v -o {start} -S 0x800C {binfile}') + self.assertEqual(error, '') + self.assertEqual(dedent(exp_output).strip(), output.rstrip()) + def test_option_vv(self): data = [ 0xAF, # XOR A