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

;; Scroll a mode 0 screen by mode 2 pixel scroll amount

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

scr_set_mode equ &bc0e
txt_output equ &bb5a
scr_initialise equ &bbff

;; reset colours
call scr_initialise

xor a
call scr_set_mode

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

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

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

;; main loop

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

ld b,&f5
in a,(c)
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.


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

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

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

;; holds a number between 0 and 15 which is the pixel offset 
;; for the scroll
defb 0

;; this is the sequence to unlock the ASIC extra features
defb &ff,&00,&ff,&77,&b3,&51,&a8,&d4,&62,&39,&9c,&46,&2b,&15,&8a,&cd,&ee