    ;; This example shows how to scroll the screen vertically using the
    ;; CPC+ "soft" hardware scroll. This scroll is smooth because it will scroll
    ;; a scanline 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,(scanline_offset)
    ;; shift into bits required for writing to hardware
    add a,a
    add a,a
    add a,a
    add a,a
    ;; 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_up

    ;; loop
    jp main_loop

    ;;----------------------------------------------------------------------
    ;; adjust scroll parameters to scroll the screen up
    ;;
    ;; Each CRTC character is 8 scanlines tall, therefore the CRTC scroll offset
    ;; will scroll the screen up by 8 scanlines at a time.
    ;;
    ;; We use the CPC+ "soft" hardware scroll to set the scanline offset within
    ;; each CRTC character.
    ;;
    ;; The CPC+ "soft" hardware scroll is updated for every scanlines.
    ;; The CRTC scroll offset is only updated once for every 8 scanlines.

    .scroll_up

    ;; get scanline offset
    ld a,(scanline_offset)
    inc a
    ;; ensure it is in range
    and &7
    ;; store scanline offset
    ld (scanline_offset),a
    cp 0
    ret nz

    ;; by now we have scrolled through 8 scanlines using the CPC+ "soft" hardware
    ;; scroll, now we need to update the screen start address

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

    ld bc,40						;; this is the same as the value written to CRTC register 1
    								;; and defines the width of the display in CRTC characters.
    add hl,bc

    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 7 which is the scanline adjustment
    ;; for the scroll
    .scanline_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