Interrupts
Contents
Default Interrupt Handler
The default interrupt handler is at address $0038 in ROM and is automatically called by the Z80 in Interrupt Mode 1.
It:
- updates the system variable frame counter;
- scans the keyboard and updates the keyboard state system variables.
The default interrupt handler preserves all registers and has no external requirements.
You can replace the interrupt handler by either:
- Setting the Z80 to interrupt mode 2 (IM 2) and configuring the address for a custom interrupt handler. Due to the restrictions on the Z80's interrupt specification, this requires placing the interrupt handler at an address where the high and low bytes are the same.
- Using Next Memory Management to page out the ROM at address $0038 and replace it with a RAM page with your own routine at offset $38.
Interrupt Timing
The standard Spectrum ULA raises an interrupt every video frame. This is very commonly used in games to synchronize graphics with the updates of the screen.
You can adjust the timing of video interrupts using Video Line Interrupt Control Register ($22). This will allow you to add an extra interrupt that occurs at a particular line address, or disable the standard ULA interrupt.
Using this register to "move" the standard interrupt to just after the final line of the visible screen can increase the time available for processing between frames.
Hardware IM2 Mode
Different from the regular IM2 mode which, from the programmer's perspective, matches the behavior of the original ZX Spectrum, a new "Hardware IM2 mode" exists in the core version 3.02.00 and above. It allows configuring different interrupt handlers for different interrupt sources.
Hardware IM2 mode is turned on with bit 0 of Next Register 0xC0 (Interrupt Control ($C0)) and has a full set of prioritized and chained interrupts, each with well-defined separate vectors. In this mode, when the IM2 occurs the lower 8 address bits are set as described in the reference documentation of the C0 next register: https://gitlab.com/SpectrumNext/ZX_Spectrum_Next_FPGA/-/blob/master/cores/zxnext/nextreg.txt?ref_type=heads
Interrupt requests are cached and held while a higher priority interrupt is being serviced, then triggered (potentially late) as soon as the higher priority interrupt is returned. All returns in this mode have to be done with reti to allow the hardware to track the priorities.
In standard mode and on regular Spectrums, if an interrupt is missed (the /INT line is released before the interrupt is accepted) then the interrupt is lost forever.
Extra titbits about timing from discord by AA
The copper counts according to the pixel currently being generated with (0,0) corresponding to the (0,0) position of the ula's 256x192 screen. You can adjust where vertical 0 is for the copper via nextreg but not horizontal position (yet?). Line interrupt & raster line position also use the copper's vertical coordinate.
The ula actually reads pixel and attribute data 12 pixels before they are displayed. So if the copper registers x=0, indicating pixel 0 is being generated, then the ula fetched that pixel/attr data during the previous 12 cycles.
The line interrupt for line=y occurs at line=y-1, x=256 iirc. Ie, the interrupt occurs just after the 256-pixel area has been drawn in the PREVIOUS line. That's to give maximum time to do things BEFORE the indicated line is drawn.
The vbi interrupt from the ula occurs where it does on the spectrum which I can't remember offhand. It's in the vertical blank time, probably at x=0 or 1. The 128K video frame as it a little later at x=1 because the 128K machines have an RC capacitor on the pcb on the /int line from the ula.
If you need more precision with interrupt timing, you can get the copper to generate interrupts via a nextreg.