The Z80 has the surprising feature of a second set of registers. I suppose these were intended to be used for rapid task switching or interrupt handling,
Indeed they were intended for fast interrupt reaction. In a simple, general way, this saved the time to push the main process's registers onto the stack and restore them again. They even went so far to spend one of the very few available single-byte opcodes to get the absolute minimum execution time - as the Z80 Technical Manual states on p.26:
08H allows the programmer to switch between the two pairs of
accumulator flag registers while
D9H allows the programmer to switch
between the duplicate set of six general purpose registers. These OP
codes are only one byte in length to absolutely minimize the time
necessary to perform the exchange so that the duplicate banks can be
used to effect very fast interrupt response times.
EXX only take 4 T-cycles, while even just pushing a simple 16-bit register would take 11 cycles, plus another 15 to load it again. 8 T-cycles instead of 25 or more cycles is a considerably faster reaction, isn't it?
That's also why there are two
EX* instructions, as very simple routines may only (use and) need to preserve the flags and
A. This leaves the whole second set (except
AF) for other purposes, such as being used in normal software, or for even more speedup in I/O.
After all, the second set can not only be used for some kind of fast 'stack' but also be prepared for a certain I/O operation. Think maybe of a serial interface receiving at high speed. Loading things like the memory pointer where received data is to be placed, the number of bytes to receive and so on, does take quite some time (16 T-Cycles for a 16 Bit pointer, 13 for a byte value) - and they need to be stored later on as well.
If these values are placed in the second register set before the high speed interrupt-driven routine gets active, no loads and stores are to be executed. Interrupt service time gets reduced to the absolute minimum, not only causing less interruption of the main process but also working up to higher speeds.
The Z80 design was quite focused on a more flexible, configurable and faster interrupt handling than the 8080.
though I think if I were programming a Z80 retro computer, I would be more likely to use them for fast access to global variables.
I can't see much gain here. Sure, 6 additional bytes or 3 pointers, but at the same time, you can't access the other ones. So there are not many cases where the secondary register set is helpful - besides interrupts and 'dead end' subroutines.
Such small snippets of Z80 code as I have seen, do not use them, but then, that's not surprising; they are something that would be expected to be only used in large programs.
Well, it's exactly the region where they are useful - to speed up small functions.
Back in the day, I was on 6502 machines, so I never had occasion to write anything non-trivial on the Z80.
Did both, and while they need different approaches, the result is usually quite similar.
Did anyone ever use that second register bank, either for its intended purpose, or just to get more registers within a single task?
It was quite common to use them either for interrupts (mostly in embedded systems) or 'dead end' routines.