;; This example shows how to scroll the screen horizonatally using the
;; CPC+ "soft" hardware scroll. This scroll is smooth because it will scroll
;; a pixel 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.


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


;;----------------------------------------------------------------------
;; 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,(horz_pixel_offset)
;; bits  3..0 define the horizontal pixel scroll offset
;; bits 6..4 define the vertical scanline scroll offset

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

;; loop
jp main_loop

;;----------------------------------------------------------------------
;; adjust scroll parameters to scroll the screen right
;;
;; Each CRTC character is 2 bytes.
;;
;; In mode 0 there are 2 pixels per byte, there are 4 pixels for each CRTC character.
;; In mode 1 there are 4 pixels per byte, there are 8 pixels for each CRTC character.
;; In mode 2 there are 8 pixels per byte, there are 16 pixels for each CRTC character.
;;
;; The horizontal pixel scroll offset is defined for mode 2 resolution.
;; Pixels in mode 1 are twice the width of mode 2 pixels.
;; Pixels in mode 0 are four times the width of mode 2 pixels.
;;
;; The horizontal pixel scroll offset is updated for every pixel.
;; The CRTC scroll offset is updated for every CRTC character (every 16 pixels in mode 2
;; OR every 8 pixels in mode 1 OR every 4 pixels in mode 0).

.scroll_right
;; get horizontal pixel scroll offset
ld a,(horz_pixel_offset)

sub 1							;; increments for pixel scrolling:
								;; 1 for mode 2
								;; 2 for mode 1
								;; 4 for mode 0
and &f
ld (horz_pixel_offset),a
cp 0
ret nz

;; by now we have scrolled through:
;; - 16 pixels in mode 2
;; - 8 pixels in mode 1
;; - 4 pixels in mode 0


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

inc hl							;; update offset

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 15 which is the pixel offset 
;; for the scroll
.horz_pixel_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
