Re: Still got one in the loft
To output graphics the ZX80 and ZX81 rely on the Z80 — it jumps to video memory and attempts to execute. The video logic (discrete or ULA, respectively) spots that based on the high bit of the address and from then on it pushes whatever opcode the CPU was going to fetch into a latch and forces a NOP to the CPU. It's a Z80 so it follows up the opcode fetch with a DRAM refresh cycle, during which the video logic forces the just fetched opcode plus a three-bit-counter on top of the low 9 bits of the refresh address and uses whatever comes back on the bus as the next 8 pixels of video output.
The internal RAM is static, so there's no actual refresh.
(and I've simplified that description a little by omitting HALT; there is some intelligence in there so that if the CPU reads a HALT then it actually gets a HALT, which is how the 1kb machines don't have to maintain a full 32x24 text buffer unless the user has actually filled the display with characters)
So, the two main options for high-res output are:
(1) find a suitable set of locations in ROM to cover a decent number of the possible range of pixel patterns, and use that as your target for the refresh cycles — on a Z80 you can set the top 9 bits of a refresh address, as DRAMs at the time had 7-bit address buses and therefore the Z80 increments across only the low 7 bits. This is how any high-resolution game you played that had odd graphics that weirdly gained or lost lumps as they moved worked.
(2) move the refresh address into RAM. Technically relies on a quirk of the bus, but works with the internal 1kb of RAM and with many expansion packs. But not all. And it's hard to get that much into the internal 1kb. So this approach is better in terms of fidelity, worse in terms of compatibility.
Source: I wrote an emulator, of the attempts-to-be-entirely-accurate variety.