;; This example shows how to scroll the screen vertically using the
;; CPC+ "soft" hardware scroll. This scroll is smooth because it will scroll
;; a scanline at a time.
;;
;; This example will only work on the CPC+.
;;
;; The scroll is made by changing the start of the screen using the CRTC,
;; (which will scroll the screen vertically by the number of scanlines defined by register 9),
;; and a scan-line adjustment defined using the CPC+ "soft" hardware scroll register.

;; Scroll a mode 2 screen vertically.

;; The location of this code is important. It must not be located
;; between &4000-&7fff.
org &8000
nolist

scr_set_mode equ &bc0e
txt_output equ &bb5a

ld a,2
call scr_set_mode

ld bc,24*80
ld d,' '
.l1
inc d
ld a,d
cp &7f
jr nz,no_char_reset
ld d,' '
.no_char_reset
ld a,d
call txt_output
dec bc
ld a,b
or c
jr nz,l1

;;----------------------------------------------------------------------
;; unlock asic to gain access to asic registers
di
ld b,&bc
ld hl,sequence
ld e,17
.seq 
ld a,(hl)
out (c),a
inc hl
dec e
jr nz,seq
ei

;;----------------------------------------------------------------------
;; install a interrupt handler
;;
;; We install our own interrupt handler for this reason:
;; - To stop the firmware interrupt from being executed, this will
;; ensure that our direct access to the hardware will not be interrupted
;; by the firmware, and that the values we write are not re-written by
;; the firmware.

di								;; disable interrupts
im 1							;; set interrupt mode 1 (jump to &0038 when interrupt occurs)
ld hl,&c9fb						;; EI:RET
ld (&0038),hl					;; &0038 is executed
ei

;;----------------------------------------------------------------------
;; main loop

.main_loop
;; wait for start of vsync. This test assumes that the start of the vsync
;; has not yet happened.

ld b,&f5
.ml2
in a,(c)
rra
jr nc,ml2

;; The vsync has just started, we can safely setup the scroll
;; without the display being effected.

;;-----------------------------------------------------------------------
;; update vertical scan-line scroll adjustment

;; page in ASIC ram
;; ASIC registers will be paged into memory range &4000-&7fff
ld bc,&7fb8
out (c),c

;; get scan-line scroll adjustment
ld a,(scanline_offset)
;; shift into bits required for writing to hardware
add a,a
add a,a
add a,a
add a,a
;; write to "soft" hardware scroll register of CPC+
ld (&6804),a

;; page out ASIC ram
ld bc,&7fa0
out (c),c

;;-----------------------------------------------------------------------
;; update CRTC with scroll offset

ld hl,(scroll_offset)		;; get scroll offset

ld a,h
or &30					;; This defines the "base" of the screen in 16k units.
						;; &00 -> screen uses &0000-&3fff
						;; &10 -> screen uses &4000-&7fff
						;; &20 -> screen uses &8000-&bfff
						;; &30 -> screen uses &c000-&ffff
ld h,a

ld bc,&bc0c				;; select CRTC register 12
out (c),c

inc b					;; B = &BD
out (c),h				;; write to CRTC register 12

dec b
inc c					;; BC = &BC0D
out (c),c				;; select CRTC register 13

inc b
out (c),l				;; write to CRTC register 13

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

;; we need to wait long enough for the VSYNC signal to finish, so that the
;; test at the beginning of this loop will synchronise with the *start* of the
;; vsync. 

;; this first HALT will catch the interrupt that occurs two scanlines from
;; the start of the VSYNC, the second will delay a furthur 52 scanlines. The maximum
;; duration for the VSYNC is 16 scanlines.
halt

halt

;; update the scroll ready for the next update of the display
call scroll_up

;; loop
jp main_loop

;;----------------------------------------------------------------------
;; adjust scroll parameters to scroll the screen up
;;
;; Each CRTC character is 8 scanlines tall, therefore the CRTC scroll offset 
;; will scroll the screen up by 8 scanlines at a time.
;;
;; We use the CPC+ "soft" hardware scroll to set the scanline offset within
;; each CRTC character.
;;
;; The CPC+ "soft" hardware scroll is updated for every scanlines.
;; The CRTC scroll offset is only updated once for every 8 scanlines.

.scroll_up

;; get scanline offset
ld a,(scanline_offset)
inc a
;; ensure it is in range
and &7
;; store scanline offset
ld (scanline_offset),a
cp 0
ret nz

;; by now we have scrolled through 8 scanlines using the CPC+ "soft" hardware
;; scroll, now we need to update the screen start address 

;; get the crtc scroll offset
ld hl,(scroll_offset)

ld bc,40						;; this is the same as the value written to CRTC register 1
								;; and defines the width of the display in CRTC characters.
add hl,bc

ld a,h							;; ensure the scroll offset is in the range &300-&3ff
and &3
ld h,a

;; store the crtc scroll offset
ld (scroll_offset),hl
ret

;;----------------------------------------------------------------------
;; scroll offset of the screen to be written to CRTC register 12 and 13
;; This value is defined in "CRTC" characters.
.scroll_offset
defw 0

;;----------------------------------------------------------------------
;; holds a number between 0 and 7 which is the scanline adjustment
;; for the scroll
.scanline_offset
defb 0

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