It's probably bad form to cite yourself, but I wrote up my understanding of the ZX80 and ZX81 hardware here, scraped together from a few different sources but most notably Wilf Rigter's hardware and software summary.
Your assessment is right. The standard ZX80 ROM loop outputs a screen, checks for keypresses, and repeats. That loop is exactly the length of a frame if no key is pressed. If a key is pressed then additional processing happens, so the start of the next frame is delayed. The TV is then out of sync.
Interrupts are used during display generation to communicate the end of a line. The logic is trivial: during a refresh cycle bit 6 of the refresh address is echoed to the interrupt line. So when bit 6 is 0, an interrupt is signalled.
However:
- the Z80 performs an interrupt if it is being signalled one cycle before the end of an instruction. Therefore an interrupt is communicated only if the final part of an instruction is a refresh cycle; and
- when it is signalled is a function of the rate at which the refresh counter has increased. Which also depends upon the particular instructions being performed.
Therefore the interrupt mechanism doesn't have reliable behaviour during arbitrary code. It can't be used for line counting or any other purpose anywhere other than during the output cycle.
As a result the base ZX80 has no means to keep track of display position outside of its display loop. So it doesn't even try; it just allows the display to lose sync.
Digressions:
There are ZX80 games which do not roll through very careful cycle counting of every possible execution path.
The interrupt logic isn't so much for keeping track of position, it's more to allow the compact display to work — a ZX80 will put a HALT at the end of any line that is less than 32 bytes wide, and not use any storage for the rest of the line. The Z80 will HALT when it gets there and then stay where it is until the interrupt is signalled. So rather than the display taking a fixed 32*24 = 768 bytes on a 1kb machine, it takes only 32 + (number of visible characters).
The ZX81 remedies the situation with a horizontal counter that generates both NMI and WAIT. NMI is edge triggered, not level, so you don't need to worry about where the Z80 is in its stream when you signal it. Using WAIT as well overcomes the problem of phase: it's essentially true to say that there's a maximum amount of time it can take for an NMI to begin processing, and the NMI response honours WAIT so you can stretch the NMI response to a fixed time relative to the horizontal scan.