    ;; This example demonstrates hardware scrolling using the CRTC.
    ;;
    ;; This example will function on CPC, CPC+ and KC Compact and ALL CRTC
    ;; variants.
    ;;
    ;; The rate of horizontal scroll is a minimum of 2 CRTC characters.
    ;; This is equivalent to:
    ;; - 4 pixels in mode 0
    ;; - 8 pixels in mode 1
    ;; - 16 pixels in mode 2
    ;;
    ;; The rate of vertical scroll is a minimum of a CRTC character line (the number
    ;; of scanlines in the CRTC line is defined using register 9 of the CRTC).
    ;; With 8 scanlines per CRTC character line this is equivalent to 8 scanlines.
    ;;
    ;; The horizontal scroll rate can be improved to scroll by:
    ;; This is equivalent to:
    ;; - 2 pixels in mode 0
    ;; - 4 pixels in mode 1
    ;; - 8 pixels in mode 2
    ;;
    ;; Using register 3 of the CRTC. Another example will demonstrate this method.
    ;;
    ;; The vertical scroll rate can be improved to scroll by a single scanline at a time,
    ;; by using register 5 of the CRTC. Another example will demonstrate this method.
    ;;
    ;; It is very difficult to scroll the screen pixel by pixel using hardware alone
    ;; on the standard CPC hardware. If you want pixel scrolling using the hardware,
    ;; then please use the CPC+ enhancements.
    ;;
    ;; Hardware scrolling is made by changing the start address
    ;; of the display.
    ;;
    ;; Use the following keys:
    ;; Q - scroll screen up
    ;; A - scroll screen down
    ;; O - scroll screen to left
    ;; P - scroll screen to right

    ;; NOTE position of this code is not important
    org &4000
    nolist

    .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

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

    ;; set the scroll offset
    ld hl,(scroll_offset)
    call set_offset

    ;; 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 'Q'				; Q
    jp z,scroll_up
    cp 'A'				; A
    jp z,scroll_down
    cp 'O'				; O
    jp z,scroll_left
    cp 'P'				; P
    jp z,scroll_right
    ret

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

    .scroll_up
    ;; get the current scroll offset
    ld hl,(scroll_offset)

    ;; update it
    ;;
    ;; NOTE:
    ;; - 40 is the number of CRTC characters in a CRTC character row
    ;; and it is identical to the value written to CRTC register 1 (Horizontal Displayed).
    ;;
    ;; - If you change the width of the display (using register 1), then you must
    ;; change this to the same value for the scroll to operate correctly.

    ld bc,40
    or a
    sbc hl,bc

    ;; 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_down
    ;; get current scroll offset
    ld hl,(scroll_offset)

    ;; update it
    ;;
    ;; NOTE:
    ;; - 40 is the number of CRTC characters in a CRTC character row
    ;; and it is identical to the value written to CRTC register 1 (Horizontal Displayed).
    ;;
    ;; - If you change the width of the display (using register 1), then you must
    ;; change this to the same value for the scroll to operate correctly.
    ld bc,40
    add hl,bc

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

    ;;----------------------------------------------------------------------
    ;;
    ;; HL = scroll offset in CRTC character units

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

    ;;----------------------------------------------------------------------
    ;; HL = points to start of message
    ;; message is terminated with a 0 character

    .display_message
    ld a,(hl)			;; get character
    inc hl				;; increment message pointer
    or a				;; termination character?
    ret z
    call txt_output		;; display character
    jr display_message	;; loop

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

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

    ;;----------------------------------------------------------------------
    .message defb "Lets scroll!!",0