;; This example shows how to double buffer a standard 16K screen 
;; using the hardware. 
;;
;; This example shows how to use a variable rate double buffer.
;; The disadvantage is that the game speeds up and slows down, but the update of the screen
;; remains free of flicker.
;;
;; Advantage:
;; - our code can take longer than 1/2 frames
;; Disadvantage:
;; - game speeds up and slows down depending on how much is going on.
;; Game will never go faster than 50hz, but will appear to speed up and slow down.

;; The example is minimal and just shows how
;; you set the screen base to do the double buffer it doesn't show
;; how to draw on double buffered screens.
;;
;; One screen will be at &4000-&7fff.
;; Second screen will be at &c000-&ffff.
;;
;;
;; location for our code, not inside visible screen area (this allows
;; us to scroll the screen if we need)
org &8000


;;---------------------------------------------------------------------------

start:

;; this indicates a swap has been done
xor a
ld (double_buffer_done),a

;; we set this to indicate we want a swap done
xor a
ld (double_buffer_swap),a

di
im 1
ld a,&c3
ld hl,int_handler
ld (&0038),a
ld (int_handler),hl
ei

;; we display the visible screen
ld a,&c0
ld (base_visible),a
;; we draw to the hidden screen, this is it's initial state
ld a,&40
ld (base_hidden),a

loop:
;; wait for indication that swap has been done
;; if we're quicker than the rate, we wait until the swap is done
;; before continuing.
ld a,(double_buffer_done)
or a
jr z,loop
;; reset that we've seen it
xor a
ld (double_buffer_done),a

;; ...
;; perform code.
;; ...

;; we want to swap please.
ld a,1
ld (double_buffer_swap),a

;; now wait until it's been done

jp loop

;;---------------------------------------------------------------------------

int_handler:
;; store registers we will change
push af
push bc
push hl

;; in VSYNC?
ld b,&f5
in a,(c)
rra
jr nc,int_handler2 ;; no, skip double buffer

;; code wants swap to be performed?
ld a,(double_buffer_swap)
or a
jr z,int_handler2
;; reset flag so code can set it next time
xor a
ld (double_buffer_swap),a

;; reached zero, so we can perform swap now


;; in VSYNC
;;
;; here we update our double buffer

;; set screen address using CRTC
;; 
;; we set R12/R13.
ld a,(base_visible)
;; make the value correct for crtc
srl a
srl a

ld bc,&bc0c		;; CRTC R12
out (c),c
;; B = &BD
inc b
out (c),a
dec b
inc c
;; BC=&BC0D
out (c),c			;; CRTC R13
inc b
;; B = &BD
xor a
out (c),a

;;
;; &c0-&40 AND &40-&c0
ld a,(base_visible)
xor &80
ld (base_visible),a

;; code should now use this to work out how to draw

;; &40-&c0 AND &c0-&40
ld a,(base_hidden)
xor &80
ld (base_hidden),a

;; indicate to code we did the buffer swap
;; code will see this
ld a,1
ld (double_buffer_done),a

int_handler2:

pop hl
pop bc
ei
reti

;;---------------------------------------------------------------------------
;; this is 0 if the swap is not done, 1 otherwise.
;; this indicates to the code that the swap has been done
double_buffer_done:
defb 0

;;---------------------------------------------------------------------------
;; code sets this to 1 when it wants the swap.
;; it then waits until the swap is done and then resets this.
double_buffer_swap:
defb 0

;;---------------------------------------------------------------------------

;; this is the visible screen
base_visible:
defb &c0

;; this is the hidden screen we draw to
base_hidden:
defb &40

end start