Synchronising with the CRTC and display
The display is controlled by the CRTC and the Gate-Array.
The CRTC generates horizontal and vertical sync signals, and can be used to generate a variety of screen displays.
The Gate-Array converts bytes into pixels using the current screen-mode and palette settings.
Therefore we can synchronise with the CRTC, to an exact point where the internal counters are exactly what we want, or to a exact position on the display.
By synchronising to the display cycle any effect using the CRTC and Gate-Array is possible:
- rasters (Gate-Array)
- split-rasters (Gate-Array)
- diagonal-rasters (Gate-Array)
- vertical rupture/splitting (CRTC)
- horizontal rupture/splitting (CRTC)
- multiple modes (Gate-Array)
- Synchronising using the VSYNC
The VSYNC signal of the CRTC is connected directly to PPI port B, therefore it is possible to test the state of the VSYNC.
It is possible to synchronise to the start of the VSYNC using the following code: e.g.
ld b,&f5 ;; PPI port B input .wait_vsync in a,(c) ;; [4] read PPI port B input ;; (bit 0 = "1" if vsync is active, ;; or bit 0 = "0" if vsync is in-active) rra ;; [1] put bit 0 into carry flag jp nc,wait_vsync ;; [3] if carry not set, loop, otherwise continue .continue
Note: This code assumes that the VSYNC is not already active before the code is executed.
However, this will not be accurate enough:
At the best, VSYNC will become active exactly when PPI port B is read (on the final execution cycle of the "in a,(c)" instruction), and at the program-counter defined by the "continue" label, we will then be synchronised to 4 cycles (the time for "rra" and "jp" instructions) from the start of the VSYNC signal. At the worst, we will just miss seeing the VSYNC become active the first time PPI port B is read (since it would become active just after "in a,(c)" has executed), and then it will be another 7 cycles (time for "rra", "jp nc" and all but the last execution cycle of "in a,(c)"), before the VSYNC is detected. Then when we get to the program-counter defined by "continue" label, we will then be synchronised to 11 cycles from the start of the VSYNC signal. Therefore the timing can differ.
In reality the timing can vary each frame, and depends on the program code following the synchronisation code.
Therefore, if we relied on VSYNC alone our effect would not be stable. For rasters this would mean they would "shake" which is useless for for split rasters.
Synchronising using interrupts The best method to synchronise is done by using interrupts for the following reasons:
on the CPC: the standard interrupt is generated by the Gate-Array using the HSYNC signal from the CRTC
on the CPC+: all the interrupts are generated by the ASIC using the HSYNC signal from the CRTC.
Since the interrupts are based on the HSYNC signal from the CRTC, the position of the interrupt is directly related to the display cycle.
The time between the HSYNC and the interrupt request is always the same.
If interrupts will be acknowledge by the Z80, the time between interrupt request and the execution of the interrupt handler will be the same.
We do not need to monitor the state of a signal, therefore there will not be a variable time between detection and synchronisation as there is for synchronising with the start of the VSYNC.
On the CPC and KC Compact there is a single interrupt source.
On the CPC+ there are 3 interrupt sources (programmable raster interrupt, DMA channel 0, DMA channel 1 and DMA channel 2).
Synchronisation in practice In practice we use both methods of synchronisation.
The VSYNC is used as a reference point in a display cycle, therefore we use VSYNC synchronisation to get to this point. Then we use interrupts to perform exact synchronisation within the display cycle.
Sychronising using interrupts on the CPC On the CPC, we are limited to the standard interrupt. This is generated by the Gate-Array using HSYNCs from the CRTC. See the document about interrupt generation for furthur information.
The position can be predicted, and to some limited degree, controlled. To position our effect exactly where we wanted, we would have to wait a specified number of interrupts until we are close to the position we want, then waste some time until we are at exactly the position we require.
e.g.
di ;; disable maskable interrupts im 1 ;; interrupt mode 0 (jump to interrupt handler at &0038) ld hl,&c9fb ;; poke EI,RET to interrupt handler. ld (&0038),hl ei ;; enable interrupts ;; first synchronise with the vsync ld b,&f5 .vsync_sync in a,(c) rra jr nc,vsync_sync ;; wait 3 interrupts so we are close to the position ;; we want halt halt halt ;; at this point we are synchronised to the monitor draw cycle ;; now waste some time until we are at the exact point ld b,32 .waste_time djnz waste_time ;; we are now synchronised to exactly the point we want . . .
Synchronising using interrupts on the CPC+ On the CPC+, with the ASIC special features enabled, we can use programmable raster interrupts, or DMA interrupts.
Programmable raster interrupts are the best, because we can define the exact scan-line we want the interrupt to trigger on. DMA interrupts are best if we want a regular repeating interrupt. e.g. every 8 scanlines.
The following example shows how to use raster interrupt.
e.g.
;; disable interrupts di ;; unlock asic to gain access to asic registers ld b,&bc ld hl,sequence ld e,17 .seq ld a,(hl) out (c),a inc hl dec e jr nz,seq ;; page-in asic registers ld bc,&7fb8 out (c),c ;; setup interrupt handler im 1 ;; write EI:RET to interrupt handler ld hl,&c9fb ld (&0038),hl ;; enable interrupts ei ld b,&f5 .vsync_sync in a,(c) rra jr nc,vsync_sync ;; set scan-line that interrupt will be triggered on ld a,30 ;; set the programmable raster interrupt register of the CPC+ ld (&6800),a halt ;; we are now synchronised to line 30 of the visible area ;; if we require, we can waste some time, to get to the exact point we want . . . ;; this is the sequence to unlock the ASIC extra features .sequence defb &ff,&00,&ff,&77,&b3,&51,&a8,&d4,&62,&39,&9c,&46,&2b,&15,&8a,&cd,&ee