kosarev / zx

ZX Spectrum emulator written in a mix of Python and C++
https://twitter.com/z80tools
MIT License
21 stars 1 forks source link

Breakpoint not catched #7

Closed boriel closed 4 years ago

boriel commented 4 years ago

When using a breakpoint at 0x0008 (i.e. RST #8) the breakpoint is ignored sometimes.

Compile this listing with ZX Basic using zxb.py -TaB --debug-memory xor.bas and try to set a bp at address 0x0008

#define FINISH \
  ASM \
    RST 8 \
    DB 8 ;; "STOP" \
  END ASM

#define SHOW_OK \
  PRINT PAPER 4; INK 0; " OK "; PAPER 8; TAB 31;

#define SHOW_ERROR \
  PRINT PAPER 2; INK 0; FLASH 1; " ERROR "; PAPER 8; FLASH 0; TAB 31;

#define INIT(msg) \
  BORDER 0: PAPER 0: INK 7: CLS \
  PRINT msg

INIT("Test logical XOR")

DIM a as uByte = 5
DIM b as UByte = 0

if a xor b then
  SHOW_OK: PRINT
else SHOW_ERROR: PRINT
END IF

FINISH
boriel commented 4 years ago

A compiled version is also provided (see attachment, gzip compressed) xor.tzx.gz

boriel commented 4 years ago

This python script uses zx to create a screen snapshot once the execution reaches 0x0008 (it works with other tzx) but it's not catching it.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

__doc__ = "Creates or update a test result"

import sys
import zx

class TakeSnapshot(zx.Emulator):
    def __init__(self):
        # speed_factor=None results in maximum speed and
        # suppresses showing the emulator window.
        # super().__init__(speed_factor=0.01)
        super().__init__(speed_factor=None)

    def on_breakpoint(self):
        self.done = True

    def run_test(self, filename):
        # Auto-load the tape.
        self.load_tape(filename)

        # Catch the end of test.
        self.set_breakpoint(8)

        # Run the main loop until self.done is raised.
        self.main()

        # Get view to the video memory.
        screen = self.get_memory_view(0x4000, 6 * 1024 + 768)

        # Compare it with the etalon screenshot.
        with open(filename + '.scr', 'wb') as f:
            f.write(screen)

def main():
    with TakeSnapshot() as t:
        t.run_test(sys.argv[1])
    print('OK')

if __name__ == "__main__":
    main()
kosarev commented 4 years ago

@boriel Looked into it, and the problem seems to be that by the end of the end-of-tape frame the RST is already executed. That's before we even set the breakpoint up so the emulator doesn't catch it. The solution is thus to stop loading the tape as soon as we reached its end, regardless of whether or not it's in the middle of a frame. Will fix ASAP. Thanks for reporting, that's a really nice catch!

boriel commented 4 years ago

Also that explains why if using RST 0, it's not catched on start (and 0x0000 is obviously executed on start).

kosarev commented 4 years ago

@boriel Addressed in a09db51, 158ae7a, 4e5359f and 9eff2b4. Please confirm that the problem is resolved, and thanks again for reporting!

Also that explains why if using RST 0, it's not catched on start (and 0x0000 is obviously executed on start).

Ah, this must be a different story. Currently, a breakpoint gets triggered as some other instruction changes PC (that is, "jumps") to the address of the breakpoint, and not when we try to execute an instruction at that address. This generally simplifies and speeds up running and stepping code because this way we don't need to disable breakpoints at the current PC value, execute whatever is the next instruction, then re-enable the breakpoints back, and then proceed with a normal run. So this is as expected. But I will give it another thought. :-)

boriel commented 4 years ago

Ah good to know that indeed! :-) It's very useful that does not stop at 0x0000 on boot up. Indeed I prefer to use RST 8, because

RST 8
DB 8

Means STOP command, so running normally it won't reset :-) and I can visually check the results before creating the testcase.

boriel commented 4 years ago

I meant: with the update provided it works already for me! Thx! :grinning:

EDIT: I sold the skin before catching the bear :( The breakpoint seems to be catched now, but now taking a ram snapshot will report a python crash. Will wait for your fix (no hurries!), and thanks for providing this awesome tool! :pray:

kosarev commented 4 years ago

@boriel I guess the crash is due to the .cpp module wasn't rebuilt after update? This is necessary as the recent patches changed the format of the machine state structure that works as an interface between the Python and C++ code.

kosarev commented 4 years ago

Re: RST 8: yes, that makes perfect sense!

boriel commented 4 years ago

It was that. I'm testing zx (commit 67ac80f8 with z80 commit 01f3704) and it's working ok now! Many thanks! (From my side this issue is solved if you want to close it)