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