
Update 10/12/2016: Added information about shorter HSYNC width reducing brightness
on some televisions.

Added information about HSYNC<6 resulting in a unstable image on some televisions.

Update 11/12/2016: Added information about R9 set to 3 for a smoother vertical scroll.

Hardware scrolling
==================

The display is hardware scrolled by changing CRTC register 12 and 13.

To understand how the hardware scrolling works we need to understand how the CRTC's outputs are connected
within the CPC.

This is how the CRTC outputs are connected in the CPC:

Z80			6845

A15 	 	MA13
A14 	 	MA12
A13 	 	RA2
A12 	 	RA1
A11 	 	RA0
A10 	 	MA9
A9 	 	MA8
A8 	 	MA7
A7 	 	MA6
A6 	 	MA5
A5 	 	MA4
A4 	 	MA3
A3 	 	MA2
A2 	 	MA1
A1 	 	MA0
A0 		CCLK

MA0-MA13 and RA0-RA2 are outputs from the CRTC. CCLK is an output from the Gate-Array.

Within the CRTC there is an MA counter and a RA counter.

MA0-MA13 represents the current value of the MA counter.
RA0-RA2 represents the current value of the RA counter.

The MA counter is reloaded at the start of the frame from the values in CRTC register 12 and 13 and 
incremented for each char. We therefore have control over this.

The RA counter can't be set through the CRTC registers. We can only control it's maximum 
value through CRTC register 9.

Therefore we can control the start address of the display and with this we can perform 
hardware scrolling.

From the assignments above we can see bits 5 and 4 of CRTC register 12 define Z80 A15 and A14 and are 
effectively the 16KB page in the base 64K of RAM where the screen will start.

bit 5, bit 4:
0 0: &0000-&3fff
0 1: &4000-&7fff
1 0: &8000-&bfff
1 1: &c000-&ffff

Not all of the CRTC's MA output is connected in the CPC. This causes the display to repeat/wrap around to the
start. (note 2)

If MA=&3ff and is then incremented, internally the CRTC has the value &400, but because of the 
assignments above, MA9-MA0 outputs are zero and the graphics appear to wrap around.

However, if MA=&fff is incremented, then MA12 and MA13 changes and we have the basis for 
32KB overscan screen.

So to ensure the screen wraps for hardware scrolling bits 3 and 2 of register 12 should not be both 1.

We then use bits 1 and 0 of register 12 and bits 7..0 of register 13 to define the offset for the 
scroll.

The offset range then becomes 0-&3ff, we can use any offset value in this range, combined with the 
page to set the start of the screen and perform hardware scrolling.

Scrolling uses the entire offset range. This means we are sweeping through the entire 16KB page.

If R9 is 7 or greater then we can't use any of the 16KB page to store graphics or code. (note 1)

The MA/RA outputs are connected to RAM, they are used both for RAM refresh, and also to tell the Gate-
Array where to fetch the pixels for the display.

For each CRTC character the CPC video hardware fetches 2 bytes (CCLK changes between 0 and 1).  
Therefore the minimum by that can be scrolled horizontally by changing only R12/R13 is 2 bytes at a 
time. 

In Mode 0 with 2 pixels per byte, this is a minimum of 4 pixels.
In Mode 1 with 4 pixels per byte, this is a minimum of 8 pixels.
In Mode 2 with 8 pixels per byte, this is a minimum of 16 pixels.

CCLK is not under our control it is generated by the Gate-Array, therefore we could not use it to
offset the screen for example.

The width of a CRTC character is therefore 2 bytes.

The vertical height of a CRTC character is defined by R9+1 (R9 = Max Raster). 
For each character line, RA is reset, and incremented for each scanline of the character row.

So the normal minimum scroll changing only R12 and R13 is 2 bytes horizontally and 8 lines vertically.
 
By changing the start address of the screen we simulate scrolling without moving any pixel data.

If width*height of the visible display is greater than &400, the screen will repeat.
So another condition for hardware scrolling is that the width (defined by CRTC R1 and the height defined 
by CRTC R6 must be less than or equal to &400).

It is worth noting that in the CPC, the CRTC only has access to the base 64K of RAM. 



Programming the offset
======================
 
 To make it easier in code, we treat the offset and the page separately, and program it like so:
 
 
 .
 .
 .
 
 ld hl,(offset)
 ;; lock offset to range &0000-&03ff to ensure it remains as a 16KB screen and wraps around.
 ld a,h
 and &3
 ld h,a

 ;; combine base with upper bits of offset 
 ld a,(base)
 and &30
 or h
 
 ld bc,&bc0c
 out (c),c			;; select register 12
 inc b
 out (c),a			;; write register 12
 ld bc,&bc0d
 out (c),c			;; select register 13
 inc b
 out (c),l			;; write offset lower 8 bits
 
 .
 .
 .
 
 
offset:
	defw 0
base:
	defb &30      ;; &00->&0000-&3fff
		;; &10 -> &4000-&7fff
		;; &20 -> &8000-&bfff
		;; &30 -> &c000-&ffff.
 
 
set_scroll_offset.asm

Basis of Scrolling horizontally
===============================

Scrolling horizontally involves incrementing or decrementing our offset.

e.g. to scroll right

ld hl,(offset)
inc hl
ld (offset),hl

or to scroll left

ld hl,(offset)
dec hl
ld (offset),hl

If we increment we have this scenario:

 before:
 
 ABCDEFGH
 IJKLMNOP
 QRSTUVWX

 after:
 BCDEFGHI
 JKLMNOPQ
 RSTUVWXA

 The CRTC characters on the left appear on the right and it appears as if the screen is scrolling right.
 
 If we decrement we have this scenario:
 

 before:

 ABCDEFGH
 IJKLMNOP
 QRSTUVWX

 after:
 XABCDEFG
 HIJKLMNO
 PQRSTUVW

 The CRTC characters on the right appear on the left and it appears as if the screen is scrolling left.

Basis of Scrolling vertically
=============================

The number of characters displayed on each line is defined by R1 of the CRTC. 
To scroll vertically we need to add or subtract this number from our offset so 
that the scroll moves up/down by an entire line.

For a normal screen of 40 characters (R1=40):

to scroll up:

ld hl,(offset)
ld bc,40
add hl,bc
ld (offset),hl


 before:

 ABCDEFGH
 IJKLMNOP
 QRSTUVWX

 after:

 IJKLMNOP
 QRSTUVWX
 ABCDEFGH

 The CRTC characters on the top appear on the bottom.


to scroll down

ld hl,(offset)
ld bc,40
or a
sbc hl,bc
ld (offset),hl
 
 If we subtract we have this scenario:
 
 before:

 ABCDEFGH
 IJKLMNOP
 QRSTUVWX

 after:

 QRSTUVWX
 ABCDEFGH
 IJKLMNOP

 The CRTC characters on the bottom appear at the top.

standard_horizontal_and_vertical.asm

 
Scrolling vertically and horizontally at the same time
======================================================

Scrolling diagonally involves a scroll in the horizontal and vertical. And in this case we 
need to draw 1 row and 1 column.

To achieve a perfect 45 degree diagonal scroll in mode 0:

- scroll 1 byte in horizontal and 2 scanlines vertically. This can be achieved with R3 and R5 method.
- or 2 bytes horizontally and 4 scanlines vertically. This can be achieved with R5 method or by reducing char height with R9.

Example code showing a diagonal scroll:

http://cpctech.cpc-live.com/source/diagscrl.asm

Drawing columns/rows
====================

When you draw a column/row to the screen to update the scroll you will notice it can flickers. This
happens when it takes time to draw the column/row.

When scrolling vertically this can be avoided if the screen is made shorter. 

Consider this (the -- line) indicates where the border ends:

 before:

 ABCDEFGH
 --------
 IJKLMNOP
 QRSTUVWX

 after:

 IJKLMNOP
 --------
 QRSTUVWX
 ABCDEFGH

In this example the screen is a single row tall. 

We could draw the line IJKLMNOP safely to update the scroll, then set R12/R13 after to reveal it.
The line would never flicker.

However the same is not true with horizontal because the way the screen is setup:

This is how it is setup:

0 -> R1-1
R1 -> R1*2-1

If we try and draw to R1 it's already visible. We can't make the screen shorter or wider.
If we made it shorter the problem just moves. So it's not possible with horizontal scroll.

To avoid the flicker we could have faster drawing functions or use double buffer.

NOTE: On the Plus we can hide the left column by extending the border into it. This makes the screen 
narrower but gives us the advantage that we can draw a new column here without flickering and in addition
if we're using Plus hardware sprites, we don't need to use double buffer and the extra complications
it can add.

Drawing note
============

All drawing is done in units of CRTC chars. Therefore you can optimise the drawing code to take this
into account.

The number of bytes to write for a column is:

2 * (R6*(R9+1))

for a normal screen of height 25, with 8 scanlines per char this is: 400 bytes

The number of bytes to write for a row is:

(R9+1) * (R1*2)

for a normal screen of width 40, with 8 scanlines per char this is:  640 bytes.

If the width is 32, then the number of bytes is 512.
If the height is 32, then the number of bytes is 512.
Therefore there is the same number of bytes to write in horizontal and vertical if they are the same.
There is no obvious advantage to scrolling vertically than horizontally in this case.


This shows that scrolling horizontally ONLY is potentially faster than scrolling vertically.

You don't need to worry about the bad region horizontally, and vertically you can even use SET/RES
instead of SCR_NEXT_LINE if you re-order your code. If you are drawing a tile from a tile map, 
split it into smaller chunks or make your tiles the same size as the crtc characters.


When drawing you may need to consider the bad region when moving horizontally and vertically.

When drawing a tile you can even use SET/RES instead of SCR_NEXT_LINE if you re-order your data. 

If you are drawing a tile from a tile map,  split it into smaller character based chunks or make your tiles the same size as the 
crtc characters.

e.g.

draw_tile:
ld a,(de)   ;; read tile gfx
ld (hl),a   ;; write to screen
inc l       ;; INC L here because memory address will be even, incrementing it will make it
            ;; odd but not overflow past 0x0ff.
inc de      ;; we could make this inc e if the tile graphics were aligned to 16 bytes.
ld a,(de)   ;; read tile gfx
ld (hl),a   ;; write to screen
dec l       ;; DEC L here to go back to the previous byte
inc de      ;; we could make this inc e if the tile graphics were aligned to 16 bytes

set 3,h     ;; move to next line

ld a,(de)   ;; read tile gfx
ld (hl),a   ;; write to screen
inc l       ;; INC L here because memory address will be even, incrementing it will make it
            ;; odd but not overflow past 0x0ff.
inc de      ;; we could make this inc e if the tile graphics were aligned to 16 bytes.
ld a,(de)   ;; read tile gfx
ld (hl),a   ;; write to screen
dec l       ;; DEC L here to go back to the previous byte
inc de      ;; we could make this inc e if the tile graphics were aligned to 16 bytes

set 4,h

;; other lines cut, but you get the idea ;)
;;etc...


Calculating screen address
==========================

A screen address is a RAM memory address corresponding to a position on the display.

The scroll offset and base define the top-left of the display area. 
The following code assumes we are calculating the address of the new column following updating the offset
with the appropiate scroll value.

We can calculate the screen address of this simply:

ld hl,(offset)  ;; offset in crtc chars
add hl,hl     ;; x2 (because 2 bytes per crtc char)
ld a,h      ;; enforce range 0-&3ff.
and &3
ld h,a
ld a,(base)
add a,a     ;; x2
add a,a   ;; x4 (to move bits into bit 7,6)
or h      ;; combine with offset
ld h,a
;; HL = screen address of top-left

We can use this fact for drawing a column on the left, or a row at the top.

To calculate the top-right side of the screen for drawing a column on the right:

ld hl,(offset)
ld bc,40-1      ;; width of screen in chars (R1-1)
add hl,bc
ld a,h
and &3
ld h,a
ld a,(base)
add a,a
add a,a
or h
ld h,a
;; HL = screen address

To calculate the bottom-left for drawing a row at the bottom:

;; perform the following (height-1)*width

ld b,25-1   ;; height of screen in chars (R6-1)
ld hl,0
ld de,40    ;; width of screen in chars (R1-1)
mull: add hl,de
djnz mul1

;; HL = offset from the top-left to bottom-right ignoring scroll offset
ld de,(offset)
add hl,de   ;; add on the offset
ld a,h
and &3
ld h,a
ld a,(base)
add a,a
add a,a
or h
ld h,a
;;HL = screen address

NOTE: Because the screen scrolls in fixed units, the calculation is made simple.
NOTE: In the above code we could avoid the multiply if we never change the width and height of the scroll area, the value therefore becomes a constant we can add.

Screen addresses in a hardware scrolling screen
===============================================

When we are drawing to a hardware scrolling screen we need different functions for moving to the 
next line (SCR_NEXT_LINE), moving to the previous line (SCR_PREV_LINE), moving to the previous
byte (SCR_PREV_BYTE) and moving to the next byte (SCR_NEXT_BYTE) from our current position. These 
functions need to work with the wrapping of the screen.

e.g.


A normal sprite drawing routine looks like this:

;; HL = memory address
;; DE = sprite pixel data
ld c,sprite_width
ld b,sprite_height
loop_height:
push hl
loop_width:
ld a,(de)			;; read sprite pixels
ld (hl),a			;; write to screen
inc hl				;; increment screen position to move to next byte to the right
inc de				;; increment sprite pixel positions
dec c
jr nz,loop_width
pop hl
call SCR_NEXT_LINE
pop bc
djnz loop_height

Problem screen addressess
=========================

It is worth noting that the assignment of CRTC's MA to memory address which automatically wraps 
the screen causes additional problems for us.

These problems do not exist if the screen is static or software scrolled.

Under BASIC press RETURN until the screen scrolls.
Type the following:

poke &c7ff,&ff
poke &c000,&ff

You will see that &c000 is to the right of &c7ff. A simple increment makes &c800 from &c7ff and not &c000.
The same happens at &cfff,&d7ff,&dfff,&e7ff,&efff,&f7ff and &ffff. These are byte offsets of:
&7ff,&fff,&17ff, &1fff, &27ff, &2fff,&37ff and &3fff.

These byte offsets represent the point at which the screen wraps.

We need something a bit different:

;; HL = memory address
;; DE = sprite pixel data
ld c,sprite_width
ld b,sprite_height
loop_height:
push hl
loop_width:
ld a,(de)			;; read sprite pixels
ld (hl),a			;; write to screen
call SCR_NEXT_BYTE
inc de				;; increment sprite pixel positions
dec c
jr nz,loop_width
pop hl
call SCR_NEXT_LINE
pop bc
djnz loop_height

As you can see additional work is done, so drawing to a hardware scrolling screen can be slower.

Look at the functions in the OS to see implementations that work correctly for hardware scrolling 
screens.

It is worth noting that because the hardware fetches two bytes for each CRTC char we could have
2 sprite drawing functions:

This would draw a sprite which has a screen address which is even:

;; HL = memory address
;; DE = sprite pixel data
ld c,sprite_width/2
ld b,sprite_height
loop_height:
push hl
loop_width:
ld a,(de)			;; read sprite pixels
ld (hl),a			;; write to screen
inc hl         ;; increment to odd byte
inc de
ld a,(de)
ld (hl),a
call SCR_NEXT_BYTE
inc de				;; increment sprite pixel positions
dec c
jr nz,loop_width
pop hl
call SCR_NEXT_LINE
pop bc
djnz loop_height

This would draw a sprite that has a screen address that is odd:

;; HL = memory address
;; DE = sprite pixel data
ld c,sprite_width/2
ld b,sprite_height
loop_height:
push hl
loop_width:
ld a,(de)			;; read sprite pixels
ld (hl),a			;; write to screen
call SCR_NEXT_BYTE
inc de
ld a,(de)
ld (hl),a
inc hl
inc de				;; increment sprite pixel positions
dec c
jr nz,loop_width
pop hl
call SCR_NEXT_LINE
pop bc
djnz loop_height

NOTE: That this special code is only needed to cope with the problem region. If a sprite doesn't
overlap this region then we can use normal INC to move to the next byte to the right.

NOTE: When we draw a column to the screen we need the special SCR_NEXT_LINE but we never need the 
special SCR_NEXT_BYTE.

NOTE: When we draw a row we may need to use the special SCR_NEXT_BYTE in addition to the SCR_NEXT_LINE
depending on how we draw it.

Example: http://www.cpctech.org.uk/source/scrlchk.asm

We can avoid the problem region:

Scrolling vertical only:
========================

If we ensure the problem area always remains on the left or right side of the screen when scrolling
we don't need to worry about it and we can scroll continuously.

To do this set the screen width (R1) so that &400 divided by the width has a zero remainder. (e.g. &400/32 = 32 with no remainder)
Valid values are power of 2, e.g. 1,2,4,8,16,32,64,128 etc.

A good value is 32 because the screen width is not reduced that much from the default of 40.

Mission Genocide uses this method.

Example: vertical_no_problem.asm

Although 32 is a narrower screen you can make the screen taller (up to 32 chars) to compensate.
This method is more suited to a vertical scrolling shoot em up.

Horizontal only:
================

If we are scrolling horizontally only then we can limit the amount we scroll and avoid the problem area.
The scroll is not continuous.

The maximum scroll offset is defined as follows:

&400-(R1*R6)

At this offset the problem is in the bottom right of the screen and never reached.

With a screen height of 25 this doesn't give much movement left/right so we have to reduce the screen height
to allow more. For each char line we reduce the height by we gain 1 extra screen width in scrolling.
e.g. if we reduce the screen from 25 to 22 we get 3 screen's width of scroll.

Action Force uses this method and reduces the scrolled area to 17. 

Example: horizontal_no_problem.asm

All directional scroll:
=======================

If both the vertical and horizontal are to be scrolled we can avoid the problem area by restricting the scroll vertically and horizontally.
This scroll is not continuous.

To do this we reduce the height of the screen and for each line we define the minimum and maximum scroll offsets
horizontally.

The min offset for each line is:

0
crtc_r1
crtc_r1*2
crtc_r2*3 
etc.

The maximum that we can scroll horizontally per line is:

max_horizontal_scroll_per_line = &400 - (r1*num_char_lines_vertical) - (r1*r6))
 
where num_char_lines_vertical is the number of char lines we wish to scroll vertically.

Then the maximum for each line:

0 + max_horizontal_scroll_per_line
crtc_r1 + max_horizontal_scroll_per_line
(crtc_r1*2) + max_horizontal_scroll_per_line


If the scrolling is unlimited:

It is not possible to avoid the problem area.
For a horizontal scroller, you could introduce areas where there is less action. At this point reset the scroll and redraw the graphics.
You can now continue to scroll more until you hit the limit again.

This solution is possible if you are using double buffer (because you don't see the flicker of redrawing) and there is a genuine break
from the action.

If the action continues, then you don't have any choice you have to accept you will see the problem area and use slower routines.

NOTE: If we are using hardware sprites ONLY on the Plus we don't need to worry about this problem area at all. We are
free to use any width/height screen provided the graphics don't repeat (e.g. R1*R6<=1024). The  hardware sprites don't have this problem.
If we are mixing software and hardware sprites we do still need to consider this problem.

Hardware scrolling and double buffering
=======================================

If we change our base value between 2 values we can do double buffering AND hardware scrolling together.
We then update the horizontal scroll values at the same time as we update the double buffering to the hardware.

It is worth noting that if you have scrolled the screen and drawn a column or row, you will need to 
redraw/copy the column onto the other screen. 

The reason you must do this is that the other screen doesn't have these graphics. If you attempted to draw to it while
it was visible you would destroy the graphics.

So, record the fact you have updated the columns/rows and repeat it after swapping screens.


Hardware scrolling and panels
=============================

It is possible to have a panel with a hardware scrolling screen without using the rupture technique. 

The panel is within the 16KB that is scrolled. However when the screen is scrolled the panel will move too because it's part of the graphics that are scrolling
so it must be moved back so it doesn't appear to move.

Some games choose to redraw the panel while the scrolling is being done:
* Rick Dangerous
* Rick Dangerous 2
* Sly Spy

Some games choose to hide it:
* Ghosts n Goblins
* Gothik
* The Vikings

Ghosts and Goblins removes it from the display performs the scrolling and then restores it when the 
scrolling has finished. 

Gothik and 'The Vikings' have the panel at the bottom of the screen and to avoid redrawing the panel 
while scrolling the height of the screen is reduced by the size of the panel using CRTC R6 (Vertical Displayed). 
e.g if the panel is 3 chars tall, when scrolling the screen is 3 chars shorter.

The height is then restored when the scrolling stops and the panel is redrawn. This method can only be 
used if the panel is at the bottom of the screen because:
* R6 affects the height
* scrolling in any direction isn't affected by the height. Where the scrolling is vertically the same location 
is drawn to regardless of the height of the panel.
* it's not possible to offset the screen to compensate for the panel.

TODO: Hiding a panel at the top of the screen?

If you use this method it is advised that the panel remains hidden until the scrolling stops.

If you choose to redraw the panel it is much easier if the panel is at the top (with no graphics above)
or at the bottom (no graphics below). If the panel was in the middle or was within the screen 
then you would need to redraw the graphics around it as you scroll. Ghosts and Goblins avoids that by 
removing the score.


 Example: TODO.

Panel on Amstrad Plus/GX4000
============================

On the Plus instead of using rupture or putting the panel inside the scrolling area we can use the split screen feature to have a panel.

Where the panel is below the scrolling area we use the raster interrupt to reset the soft scroll back to 0 otherwise the panel would also soft scroll.

Example:

CRTC R12/R13 define the location of the panel. Split screen address defines the scroll position.
smooth_scroll_mode0_with_panel_top.asm

CRTC R12/R13 define the scroll position, split screen address defines the panel location.
smooth_scroll_mode0_with_panel_bottom.asm
 
Background animation on a hardware scrolling screen
===================================================

There are two ways to animate the background on a hardware scrolling screen:
1. redraw the tiles that change
2. use colour cycling.

Redrawing the tiles is slower because you are re-writing the bytes to the screen.

'The Vikings' uses colour cycling to animate the water tiles. However, to avoid other tiles changing
colour you must reserve part of the palette for the colour cycling, this reduces the number of 
colours available to the other tiles.



 
Calculating screen address
==========================

If we are to draw sprites, we need to convert between our sprite coordinates and a memory address
for drawing sprites.

If we consider the top-left to be 0,0 and we have coordinates where X increases going right
and Y increases going down. X is in byte coordinates, Y is in scan-lines.

First we must calculate which char line we are on.

Char_line = y/8

Now we can do this:

offset from start = char_line*R1

we then add the scroll offset:

total offset = offset from start + scroll offset


we then work out the offset from the start of line:

offset_from_start_of_line = x/2

then we add this:

total offset = total offset + offset_from_start_of_line

this gives the total offset.

we range fix it:

total offset = total offset and &3ff

then multiply by 2 to get byte offset:

total_byte_offset = total_offset * 2

This gives us the screen position of the crtc char of our coordinate.

the scan line offset is the scan line within the char:

scan_line = y & 7.

We now need to add the scan-line offset:

total_byte_offset = total_byte_offset + (scan_line * 0x0800)

finally, if x was odd, we incremnent the value giving the final address.

We can simplify this down a bit by using look-up tables.


Smoother vertical scrolling
===========================


R5 to add scan-lines
--------------------

R5 is used to add additional scan-lines to the frame.

R5 to add scan-lines (simple)
-----------------------------

If we add additional lines the display the screen will move down.

To scroll smoothly vertically we add between 0 and R9 scanlines (0-7) and then when the value wraps we update the scroll offset
and reset the value. Repeat this to have a smooth vertical scroll.

e.g.

1. R5 = 0, scroll offset = 0
2. R5 = 1, scroll offset = 0
3. R5 = 2, scroll offset = 0
4. R5 = 3, scroll offset = 0
5. R5 = 3, scroll offset = 0
6. R5 = 4, scroll offset = 0
7. R5 = 5, scroll offset = 0
8. R5 = 6, scroll offset = 0
9. R5 = 7, scroll offset = 0
10. R5 = 0, scroll offset = 40
11. R5 = 1, scroll offset = 40
12. R5 = 2, scroll offset = 40
etc

This method  shortens and lengthens the duration of a frame and the position of the vertical sync.

The frame is no longer fixed at 50hz. 

The CPC monitor is tolerant of this changing and depending on the v-hold dial position on the back of the monitor
it will keep a stable image without the entire image rolling. Often adjusting the v-hold a little will make the image stable.

Some televisions or modulators may not be able to cope with this method and the scrolling will not be smooth OR the screen 
will never be stable.

With this method there will also be flicker above and below the screen but there are ways to remove that.

Legend of Kage uses this method but doesn't hide the flicker. LED Storm uses this method and makes the screen taller to hide
the flicker.
 
r5_soft_vertical_scroll.asm

R5 to add scan-lines (using rupture)
------------------------------------

NOTE: Using this method there will be flickering either above or below the screen or both but it can sometimes be avoided.

There are 2 ways to use R5 to achieve line by line vertical scrolling.

Method 1: Without "rupture": If we add additional lines the display will move vertically in a similar 
way to using R3 to  move the screen horizontally. We add between 0 and R9-1 lines (i.e. between 0 and 7).

e.g.

1. R5 = 0, scroll offset = 0
2. R5 = 1, scroll offset = 0
3. R5 = 2, scroll offset = 0
4. R5 = 3, scroll offset = 0
5. R5 = 3, scroll offset = 0
6. R5 = 4, scroll offset = 0
7. R5 = 5, scroll offset = 0
8. R5 = 6, scroll offset = 0
9. R5 = 7, scroll offset = 0
10. R5 = 0, scroll offset = 40
11. R5 = 1, scroll offset = 40
12. R5 = 2, scroll offset = 40
etc

This method effectively shortens and lengthens the duration of a frame, and in addition the
position of the vertical sync. The frame is no longer fixed at 50hz. The CPC monitor is generally 
quite tolerant and will accept that and will sync to the difference without the display moving.

Sometimes you may need to turn the vertical adjust knob on the back of the monitor to make sure it's always
gives a steady scroll. Some televisions or modulators may not be able to cope with it and the scrolling will not
be smooth OR the screen will never be stable.

With this method there will be flicker above and below the screen. The screen is normally set to the height you want. (e.g. 25 lines).

Legend of Kage uses this method but doesn't hide the flicker.

Example code showing this with flicker:
http://cpctech.cpc-live.com/source/scrlhrz.asm

When R5=7, the screen will be moved down 7 lines compared to when R5=0.

To avoid flicker at the top you need to blank 8 - number of lines added. e.g. you add 1 line you need to blank 7.
This means that the top-border appears to always appear in the same place.

To avoid flicker at the bottom you also need to blank lines, this time the number of lines added.

The result is that the screen will be 2 char lines shorter than you specify but there will not be any flicker.

This may sound good, but it's complicated by the fact that when you scroll, these blanked out areas will potentially move into the main
area. So you need to redraw them when this happens.


Changing R9 height:
-------------------

Normally R9 is set to 7, which corresponds to a height of 8 lines. 

We can change the value of R9 to a smaller value to make a smoother vertical scroll however the height of the scrolled
area is reduced and we will need to adjust R7 and R4 to compensate.

Druid 2: Enlightment uses this method for the scrolling part of the screen.

When  R9 is less than 7, then RA0-RA2 doesn't cover it's entire range and therefore not all lines are visible.
We can therefore use those non-visible areas of screen ram to store code/data.

Example: 

r9_soft_vertical_scroll.asm

The example has R9 set to 3 (4 lines per char). 

Changing R9 height method 2:
----------------------------

We can change the value of R9 to shorten a single char line prior to the scroll. 

* Shorten char line before scroll and then 'reverse' it after.

To shorten the line use interrupts to locate the position on the screen, set the new R9, burn cycles until the char line is done, then restore R9 back for
the scroll.

Then after the scroll to 'reverse' it, we use height - r9 we set, and wait until that's done, then restore that back for the remainder of the frame.

In this way we set the scroll and compensated for it.

Example: 

r9_soft_vertical_scroll_v2.asm

Smoother Horizontal scrolling using R3 (simple)
===============================================

We can make the horizontal scrolling slower and smoother so that it scrolls at the rate of 1 byte at a time compared
to the usual 2 bytes at a time.

R3 is used to define the vertical and horizontal sync widths. The sync is passed from the 
CRTC through the Gate-Array and out to the monitor.

For programmed horizontal sync widths greater than 6, the gate-array will output a sync of width 6 to the monitor.

For programmed horizontal sync widths less than 6, the gate-array will output a sync of that width to the monitor 
(e.g. 1,2,3,4 or 5) and the screen will increasingly move horizontally on the monitor by the width of a byte. 

A sync width of 0 is a special case. On some CRTC it means no horizontal sync, for others it means a horizontal sync of width 16.

Longshot has found that to have a smooth scroll for all CPCs you should change the value between 4 and 5.

This type of scrolling relies on the monitor or television's response to the horizontal sync output from the 
CPC and the way the CPC outputs the sync. It always works on CPC monitors when values 4 and 5 are used.

We follow this sequence:

1. R3 = 5, scroll offset = 0
2. R3 = 4, scroll offset = 0
3. R3 = 5, scroll offset = 1
4. R3 = 4, scroll offset = 1
etc.

Doing this will give the illusion we are scrolling at the rate of 1 byte horizontally. Note that the left and right edges of 
the display will flicker.

Legend of Kage uses this method.

Example code showing this:
r3_soft_horizontal_scroll.asm

Some televisions require a longer HSYNC pulse (which means a longer horizontal blanking) in order to display the picture 
correctly. If the border is not black then reducing the HSYNC length will cause the picture to reduce brightness AND/OR loose 
colour. Therefore for televisions that DO support R3 scrolling then make sure the border is set to black to ensure the 
picture remains bright.

Some televisions can't handle a HSYNC length lower than 6 correctly and the image may not be be visible. Some televisions will 
stabalise the screen and always use the same hsync and may not move at all.

Smoother Horizontal scrolling using R3 (rupture)
===============================================

If you are using rupture to have a static part of the screen (above or below) then you will need to change R3 back to 6 so the panel doesn't 
also move.

The Amstrad monitor doesn't respond immediately to R3 changes and can take up to 16 scanlines for it to do so.
Games such as Prehistorik 2 have a gap between the scroll and the panel for this reason.

a panel at the bottom of the screen, you will need to change R3 back to 6 so the panel doesn't 
also move. The monitor doesn't respond immediately to R3 changes, it can take up to 16 scanlines for it to do so.
Games such as Prehistorik 2 have a gap between the scroll and the panel for this reason.

If the game is only horizonally scrolling, consider putting the panel at the top, this will avoid the need for the extra scanlines.
The panel would have no scroll from R3, the scroll would use R3. Note that you'd need to make a gap between panel and main scroll area
but this can be done quite simply.




One way to avoid this is to expand the width of the display horizontally so this is hidden by the sides of the monitor, e.g. 
by setting the width to 48.

But, if we expand the width, we also have to reduce the height otherwise the graphics will repeat for 
the same reasons we need them to for the hardware scroll to work.

To avoid repetition of graphics, width * height must not be greater than &400 (the maximum scroll offset).
So if the width is set to 48, the height can be a maximum of 12 lines. (&400/48 = 21)

NOTE: If you are using rupture to make a panel at the bottom of the screen, you will need to change R3 back to 6 so the panel doesn't 
also move. The monitor doesn't respond immediately to R3 changes, it can take up to 16 scanlines for it to do so.
Games such as Prehistorik 2 have a gap between the scroll and the panel for this reason.

If the game is only horizonally scrolling, consider putting the panel at the top, this will avoid the need for the extra scanlines.
The panel would have no scroll from R3, the scroll would use R3. Note that you'd need to make a gap between panel and main scroll area
but this can be done quite simply.

Example: TODO.


NOTE: On the Plus we don't need to use R3, we can set the smooth horizontal scroll value instead of R3 
and in addition we would not get the flickering sides.

http://cpctech.cpc-live.com/source/pscrlhrz.asm

Smoother Vertical scrolling using R5
====================================

R5 is used to add additional scan-lines to the frame. 

NOTE: Using this method there will be flickering either above or below the screen or both but it can sometimes be avoided.

There are 2 ways to use R5 to achieve line by line vertical scrolling.

Method 1: Without "rupture": If we add additional lines the display will move vertically in a similar 
way to using R3 to  move the screen horizontally. We add between 0 and R9-1 lines (i.e. between 0 and 7).

e.g.

1. R5 = 0, scroll offset = 0
2. R5 = 1, scroll offset = 0
3. R5 = 2, scroll offset = 0
4. R5 = 3, scroll offset = 0
5. R5 = 3, scroll offset = 0
6. R5 = 4, scroll offset = 0
7. R5 = 5, scroll offset = 0
8. R5 = 6, scroll offset = 0
9. R5 = 7, scroll offset = 0
10. R5 = 0, scroll offset = 40
11. R5 = 1, scroll offset = 40
12. R5 = 2, scroll offset = 40
etc

This method effectively shortens and lengthens the duration of a frame, and in addition the
position of the vertical sync. The frame is no longer fixed at 50hz. The CPC monitor is generally 
quite tolerant and will accept that and will sync to the difference without the display moving.

Sometimes you may need to turn the vertical adjust knob on the back of the monitor to make sure it's always
gives a steady scroll. Some televisions or modulators may not be able to cope with it and the scrolling will not
be smooth OR the screen will never be stable.

With this method there will be flicker above and below the screen. The screen is normally set to the height you want. (e.g. 25 lines).

Legend of Kage uses this method but doesn't hide the flicker.

Example code showing this with flicker:
http://cpctech.cpc-live.com/source/scrlhrz.asm

When R5=7, the screen will be moved down 7 lines compared to when R5=0.

To avoid flicker at the top you need to blank 8 - number of lines added. e.g. you add 1 line you need to blank 7.
This means that the top-border appears to always appear in the same place.

To avoid flicker at the bottom you also need to blank lines, this time the number of lines added.

The result is that the screen will be 2 char lines shorter than you specify but there will not be any flicker.

This may sound good, but it's complicated by the fact that when you scroll, these blanked out areas will potentially move into the main
area. So you need to redraw them when this happens.

Example code showing this without flicker:
TODO.

Method 2: Using "rupture".

This method is more technical and involves good timing.

For this method we need to split the screen into at least 2 sections. 

With this method you add a number of scanlines using R5 to the end of one section, then add a corresponding amount to another section so that
the total number of added lines is always the same (normally 8). This ensures the time for a frame is the same regardless of the scroll.

Where R5 is added there is potentially flicker.

Mission Genocide splits the display into 2 sections. The top section is scrolling. The bottom section is a panel which is static.
The top section is 32 chars wide, 28 chars tall. The panel is 32 chars wide and 4 chars tall.

The total height fits well within the monitor's display area.

This method works great but is harder to implement because you need good understanding of the CRTC,
understand the "rupture"/"splitting" method. This method requires the display be actively maintained 
each frame and testing on multiple CRTCs to ensure it works on all.

For games we use the CPC raster interrupts to perform these actions leaving the remaining time
free for the game to use.

The rupture method works on all monitors and televisions.

Mission Genocide uses this method.

Example: 
http://cpctech.cpc-live.com/source/misgen.asm

To avoid flicker with this method:

Mission Genocide doesn't have flicker at the top. The flicker is removed by blanking one or more lines as described for the other method.

However there is no flicker between the scrolling section and the panel this is caused because R6>R4.  What this means is that when R5 is 0, 
we see R4 character lines, but when it's set, we see additional scan-lines are added and we see 
the pixels from them (effectively R4+1). The pixels continue on from the scrolling section. 

This appears as if the scrolling section is seamless.

If we set vertical total to be the same as vertical displayed (R6=R4), when we add lines with R5 we would see border and flicker. The amount 
of border depends on the number of lines we add. 

So to take advantage of this we must reduce the height by 1 char line. This means that when the wrap happens we don't
see the graphics from the top repeating at the bottom.

The panel cut happens when the top part finishes the crtc does this automatically.

We also need to make sure the panel is not too tall. If we did it would wrap onto the top of the display and it could even
pollute the scanlines added to make the scroll.

Games that use vertical scroll using R5 and using rupture method normally have the panel at the bottom. 
There are a few reasons:
1. It is easier to code the panel to display at the bottom (the display starts at the start of the frame, we can use this fact for the crtc
to automatically display the panel exactly as we want without needing to burn additional cpu cycles)
2. If the panel is at the bottom it can be directly underneath the scroll. There is a seamless join. 

If the panel was at the top:
1. You have to split the screen into more sections so that when you define the start address it is in the correct position on the display to make
the top panel.

2. You would need to set the vertical displayed of the section so that when lines are added with R5 this doesn't "pollute" the top of the display.
Then you have flicker at the top, and when the scroll area finishes you would also have flicker at the bottom.

If a game scrolls vertically, uses rupture and has a panel at the bottom,  and always the same direction we can blank out the top few lines (to hide the R5 effect). When the screen is scrolled
these then become hidden and new graphics are scrolled in from the bottom. We then only need to redraw the blanked out lines to put it back.
There is no need to fix up the graphics at the bottom.

NOTE: On the Plus we don't need to use either of these techniques. We can set the Plus soft vertical
scroll. This will not cause flickering top/bottom and will not affect the display timing.
 
http://cpctech.cpc-live.com/source/pscrlvrt.asm

Smoother vertical scrolling using R9
====================================

Changing height
===============



Smoother scrolling without R3/R5
================================
 
There are other ways to achieve smoother scrolling on the CPC without using R3/R5 at the expense
of more RAM or by reducing the screen area.

If you are scrolling continuously (e.g. in a horizontal shoot em up), you can use two pages of 16KB.
One page is offset by 1 byte compared to the other.

By swapping between these and hardware scrolling you can scroll at a smoother rate.

To achieve pixel-by-pixel scrolling in mode 0 at normal resolutions, you would need 4 x 16KB 
(all the RAM available to the CRTC). By combining this method with R3, you can achieve pixel by pixel 
hardware scrolling.

The reason you need 4 screens is that normal scrolling is at a rate of 2 bytes which is 4 mode 0 pixels.

If the screen height was reduced (e.g. to a half using R6), you could effectively have 2 screens in 
each 16KB. Each screen would be seperated by height*width chars. Now you can have pixel by pixel scrolling
in 2 * 16KB. NOTE: You can't use R9 to reduce the height of the screen with this method because you
would need to be able to set the screen start using RA2-RA0 too which is not possible.

Example: TODO.


Smoother Scrolling on the Amstrad Plus/GX4000
====================================

The methods used to scroll the screen are compatible with the Plus.

However, the Plus has additional support for smooth scrolling which makes this all easier:

1. Soft horizontal scroll (pixel by pixel horizontal scroll adjustment)
2. Soft vertical scroll (line by line vertical scroll adjustment)
3. Masking the left side of the screen (allows column of graphics to be updated without flickering and
therefore single buffered scrolling is smooth and flicker free)

The screen is scrolled using R12/R13 (no need to use R9, R5, R3). Then use the soft horizontal scroll to
smoothly scroll pixel by pixel horizontally and the vertical soft scroll to scroll smoothly line by line vertically.

Example:

smooth_scroll_mode0.asm
smooth_scroll_mode1.asm

Enable the left column mask so that you can plot new graphics there and update the scroll flicker free.

smooth_scroll_mode0_mask_column.asm


Reducing flicker
================

Vertical scrolling
==================

Single direction scrolling
--------------------------

a) If the simple R5 method is used to the scroll the screen there will be flicker at the top/bottom when the screen is scrolled.
With interrupts we can blank these lines using R1. 

Set R1 to 0 at the start of the frame.

At a fixed scanline at the top, set R1 to the width of the screen.

At a fixed scanline at the bottom, set R1 to 0 again.

In this way we can hide the flicker at the top and the bottom.

r5_soft_vertical_scroll_no_flicker.asm


b) If we are using rupture and there is a panel at the bottom as long as R6 is large enough there will not be flicker
at the bottom.

c) If we are using rupture and there is a panel at the top, we can use the following method to hide the normally flickering lines in the border.
This method is used by Pinball Dreams.



R5 is used to add additional scan-lines to the frame. 

NOTE: Using this method there will be flickering either above or below the screen or both but it can sometimes be avoided.

Horizontal scrolling

 To scroll an image we need to draw 1 row with new graphics.
 

Example code that shows changing the offset to make a horizontal and vertical scroll:

http://cpctech.cpc-live.com/source/hardscrl.asm


To complete the scroll we draw 1 CRTC column (the height of the scrolled screen) with new graphics. 
If we didn't do that the same graphics would wrap around and move up/down when scrolling.
 
 

Scrolling a tilemap
===================

A tilemap consists of a rectangular 2 dimensional array of numbers (a tilemap has a width and a height). 
Each number in the tilemap is the index of the tile to display in that position.

e.g.

A tilemap of:

0,0,0,1,1,1
1,1,1,2,2,2
0,1,0,1,0,0

this uses 3 tiles. With indexs 0,1,and 2.
We would have three tiles in our tileset.

Each tile in the tileset has the same width and height.

The tilemap is wider or taller (or both) than the screen.

We only see a region of the tilemap at a time.

When we scroll a tilemap we are moving through the tilemap, changing the location of the region
we are looking at.

So to scroll a tilemap we need to know the x,y position of this region. The width and height are fixed
and defined by the size of our visible area.

If the tile dimensions are the same as the size of a crtc char it is much easier for us. Otherwise
we need to have a idea of how far "into" the tile we are both horizontally and vertically.

We update our position "within" the tile, then when we have moved a complete tile width we update
the x position of our region. Similarly if we have moved a complete tile height we update the y 
position of our region.

To display it, we initially draw all tiles in the region we can see.

When we scroll, we need to then draw new pixels from the tiles we now see.

If the tile is larger than a a crtc char then we draw part of the tile which is the same size as a 
crtc character.

As we continue to scroll we draw another part of the tile and eventually the whole tile is scrolled
into view.

For the cpc, we need to therefore calculate what parts of the tilemap to draw for the rows/columns
we scroll.

Example: TODO.


Sprites on a hardware scrolling screen
======================================

Like in other CPC games we will need to clip our sprites to the sides of the visible area if they are
meant to go outside the screen. If we don't do this they will wrap onto the other sides of the screen. 
e.g. if we move left then they will wrap onto the right side of the screen.

Example: TODO.

Erasing sprites
===============

Like in any other cpc game we must erase the sprites in their old position before drawing them in their
new position.

Like other games there are a few approaches that work.

1. We could reserve another 16KB for a background screen. This screen doesn't have sprites but it does 
have the background. We erase the sprites by copying the background to the screen. If we are double
buffering this means 3 * 16KB is used. This could work well in a 128KB game, where the background screen
could be in the extra RAM.

Example: http://cpctech.cpc-live.com/source/backbuff.asm

An extension to this is to only store a region equal to the size of the sprite:

Example: http://cpctech.cpc-live.com/source/maskspr.asm

2. We could identify which tiles are under the sprite. Erasing the sprites now involves redrawing those 
tiles but it's furthur complicated by the fact that on the left, top, right and bottom edges we could have
partial tiles (if the tiles are larger than a crtc character). This is slower but good where
we have limited RAM.

3. We could use a method used in Mission Genocide. If we sacrifice some colours, we can setup the palette
so we can use OR to draw a sprite and AND to remove it. This would be quick and is used in many hardware
scrolling games. Good for limited RAM, fast, but less colourful. Careful choice of colours means
the background and foreground sprites look nice. 

Example: http://cpctech.cpc-live.com/source/rotospr.asm

Types of scroll
===============

1. Chunky scroll.

With a 'chunky scroll' we simulate a slower scroll rate by using standard hardware scrolling without
smoothing it, and then every x frames we advance the scroll by a char line or char column.

The scroll will step.

Sprites can continue to move pixel by pixel.

This method is good for games that continuously scroll.

This kind of scrolling can be seen in MSX1 games.

chunky_horizontal.asm
chunky_vertical.asm

2. Push scroll method 1

When the player approaches the left/right/top/bottom side of the screen it will begin to scroll. 

The screen scrolls a fixed amount forward and as it does the sprite is moved back too.

When this method works well the push scroll amount is not too quick to be jarring, the player continues to move and
animate and so do the enemies.

In addition the scroll is not triggered quickly again if you keep moving because the region in which you can move
without causing it to scroll is large.

Sometimes games get it wrong and it's jarring, they scroll the screen too quickly, the region is too small, they
scroll the entire screen, player and enemies stop etc.

push_scroll1.asm

3. Push scroll method 2

When the player approaches the left/right side of the screen it will begin to scroll. There is normally a "margin"
i.e. 8 chars on the left/8 chars on the right, this is the point at which the scroll should be triggered.


4. Push scroll method 3

When the player approaches the a position on the screen (normally the centre but it may be to the left) the 
screen will scroll and the player remains at that position on the screen.

When they stop moving the scroll stops. The scroll will also stop if they reach the edges of the map, and they are
allowed to move up to the edges of the screen without it scrolling.


