;; Smooth horizontal scroll
;;
;; This code demonstrates a method to scroll the screen, using the hardware,
;; in byte size units.
;; 
;; This is equivalent to:
;; - 2 pixels in mode 0, 
;; - 4 pixels in mode 1,
;; - 8 pixels in mode 2.
;;
;; The standard hardware scroll method, using register 12 and 13 alone, will
;; scroll the screen in 2 byte units (the width of a CRTC character).
;;
;; This is equivalent to:
;; - 4 pixels in mode 0, 
;; - 8 pixels in mode 1,
;; - 16 pixels in mode 2.
;;
;; So this method provides a method for smoother scroll in the horizontal
;; direction.
;;  
;; The effect relies on the reaction of the monitor to the horizontal sync from 
;; the CRTC so it is only guaranteed to work properly on a real Amstrad CPC 
;; with an Amstrad monitor.
;;
;; At best, this method will result in a smooth scroll, but at worse the scroll
;; will be far from smooth. The parameters may need adjusting for other CRTC
;; models.
;;
;; Use O and P to scroll the screen left and right.
;; 
;; This scroll method can be used on CPC,CPC+ and KC Compact.

;; the position of this code is not important
org &4000


.km_read_key equ &bb1b
.mc_wait_flyback equ &bd19
.scr_set_mode equ &bc0e
.txt_output	equ &bb5a

;; set the screen mode
ld a,1
call scr_set_mode

;; display a message (so we can see how the screen is scrolling; by the movement
;; of the message)
ld hl,message
call display_message


.loop
;; wait for vsync 
;;
;; (synchronises scroll with the screen refresh; as a result it
;; updates at a constant rate and is smooth)
call mc_wait_flyback

;; write scroll parameters to hardware
call update_scroll

;; check if any keys are pressed (they will update the scroll offset)
call check_keys

;; delay long enough for the vsync to become inactive 
;; so that mc_wait_flyback will synchronise with the start of the 
;; vsync.
halt

halt

;; loop
jp loop

;;----------------------------------------------------------------------
.check_keys
;; test if any key has been pressed
call km_read_key
ret nc
;; A = code of the key that has been pressed
;;
;; check the codes we are using and handle appropiatly.
cp 'O'					; O
jp z,scroll_left
cp 'P'					; P
jp z,scroll_right
ret

;;----------------------------------------------------------------------
.scroll_left
ld a,(scroll_adjustment)
xor 1					;; 0->1->0...
ld (scroll_adjustment),a
or a
ret z

;; get current scroll offset
ld hl,(scroll_offset)

;; update it
dec hl

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

;; store new scroll offset. It is now ready to be written to the CRTC.
ld (scroll_offset),hl
ret

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

.scroll_right
ld a,(scroll_adjustment)
xor 1					;; 0->1->0...
ld (scroll_adjustment),a
or a
ret z

;; get current scroll offset
ld hl,(scroll_offset)

;; update it
inc hl

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

;; store new scroll offset. It is now ready to be written to the CRTC.
ld (scroll_offset),hl
ret


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

.update_scroll
;; write scroll adjustment
ld a,(scroll_adjustment)

add &f5			;; This value alternates between &F5 and &F6
					;; and controls the horizontal position of the visible
					;; area on the monitor display. The effect these
					;; values have on the position relies on the reaction
					;; of the monitor to the horizontal sync output of
					;; the CRTC. So for some monitors this may not result
					;; in a smooth scroll as we want. This value may need
					;; adjustment for your monitor and CRTC variant.
					;;
					;; From BASIC try the following two lines to see the
					;; screen move, which is the basis of this effect:
					;;
					;; OUT &BC00,3:OUT &BD00,&F5 and
					;; OUT &BC00,3:OUT &BD00,&F6 

ld bc,&bc03			;; select CRTC register 3 (horizontal sync width)
out (c),c
inc b
out (c),a			;; set vertical and horizontal sync widths

ld hl,(scroll_offset)

;; write scroll offset (in CRTC character width units)
ld bc,&bc0c				;; select CRTC register 12
out (c),c
ld b,&bd				;; B = I/O address for CRTC register write

;; combine with scroll base
ld a,(scroll_base)
or h
out (c),a

ld bc,&bc0d				;; select CRTC register 13
out (c),c
ld b,&bd				;; B = I/O address for CRTC register write
out (c),l
ret





;;----------------------------------------------------------------------
;; scroll parameters

.scroll_adjustment defb 0

;; high byte of the screen base address
;; &00 -> screen uses &0000-&3fff
;; &10 -> screen uses &4000-&7fff
;; &20 -> screen uses &8000-&bfff
;; &30 -> screen uses &c000-&ffff

.scroll_base defb &30

;; the scroll offset in CRTC character units
.scroll_offset defw 0
