;; This example shows how to double buffer a standard 16K screen 
;; using the hardware. 
;;
;; This example shows how to lock the double buffer rate. 
;;
;; The disadvantage:
;; - game is locked at a fixed rate so is slow
;; Advantage:
;; - game is the same speed and doesn't speed up or slow down
;; - game is always flicker free because of the way we manage the double buffer.
;; 
;; 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

;; rate is in multiples of 50hz.
buffer_rate equ 2			;; 1 = 50hz, 2 = 25Hz

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

start:


xor a
ld (double_buffer_done),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.

;; go back and syncronise.
;; our code must be done within the time defined by the rate
;; e.g. if 1, we must complete our code within 50th of a second.
;; if 2 we must complete our code within 25th of a second.

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

;; decrement count and check if it's reached zero
ld a,(buffer_swap_count)
dec a
jr nz,int_handler2

;; 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

ld a,1
ld (double_buffer_done),a

;; reset count
ld a,buffer_rate

int_handler2:
;; store count
ld (buffer_swap_count),a

pop hl
pop bc
ei
reti

;;---------------------------------------------------------------------------
;; this is the counter, when it reaches zero the double buffer swap is done
;; it is then reset back to it's initial value for next time
buffer_swap_count:
defb buffer_rate

;;---------------------------------------------------------------------------
;; 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

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

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

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

end