[comp.sys.cbm] Machine Language Smooth Scrolling Problem

kfink@jarthur.Claremont.EDU (Kevin Fink) (10/06/89)

My brother is working on smooth scrolling in machine language on the
Commodore 64. I don't understand what he's doing, but he sent me this to
post.

----------------------------------------------------------------------------

	I have a problem with the smooth scrolling on the Commodore 64. I
have written a program in machine language that scrolls the screen down one
bit at a time in the Y-direction, using the VIC-II chip's automatic
scrolling capabilities. It works fine, except for when it scrolls down every
eighth bit, it jerks a little. It does this because you have to move the
whole screen down one line, then tell the VIC-II to move the whole screen up
seven lines. The Commodore 64 Programmer's Reference Manual says, "You can
use the raster register to set up timing changes in your display so that you
can get rid of screen flicker." I put a raster interrupt routine in my
program to move the line down and change the VIC-II chip's setting while the
raster is off the visible display area, but either I did it wrong or it
didn't work. I am out of ideas and need help badly.

----------------------------------------------------------------------------

He's way out of my league in ML programming, but hopefully some of you can
help. Either reply to me here at Harvey Mudd and I'll forward it to him, or
if you're feeling really benevolent, mail him directly. His address is:

Steve Fink
23000 Grand Ronde Rd.
Grand Ronde, OR 97347
(503) 879-5354

Thanks.

Kevin Fink
kfink@jarthur.claremont.edu      -or-        kfink@hmcvax.bitnet
uunet!jarthur!kfink

jb@cbmtor.UUCP (Jim Butterfield ) (10/08/89)

Question from: kfink@jarthur.Claremont.EDU (Kevin Fink)...

> My brother is working on smooth scrolling in machine language ...
>   [he says...] "It works fine, except for when it scrolls down every
> eighth bit, it jerks a little. It does this because you have to move the
> whole screen down one line, then tell the VIC-II to move the whole screen 
> up seven lines."  [more details omitted] 

    Even if he's using a character screen, you don't have time to move
a thousand characters and their corresponding color pixels within
retrace time... and on high resolution, you couldn't even make the
attempt.
    Set up two screen memory areas.  While one is displaying, draw the 
other one (say, with the screen characters pre-moved as desired).
When you're ready for that eighth-bit scroll, wait for retrace and
then flip to the other screen.
--jim

-- 
--
:  My boss doesn't understand me...  :  Jim Butterfield, Toronto  :
:   .. and I'm my own boss!          :    jb@cbmtor               :

bskendig@phoenix.Princeton.EDU (Brian Scott Kendig) (10/09/89)

In article <2317@jarthur.Claremont.EDU> kfink@jarthur.claremont.edu (Kevin Fink) writes:
]	I have a problem with the smooth scrolling on the Commodore 64. I
]have written a program in machine language that scrolls the screen down one
]bit at a time in the Y-direction, using the VIC-II chip's automatic
]scrolling capabilities. It works fine, except for when it scrolls down every
]eighth bit, it jerks a little. It does this because you have to move the
]whole screen down one line, then tell the VIC-II to move the whole screen up
]seven lines. The Commodore 64 Programmer's Reference Manual says, "You can
]use the raster register to set up timing changes in your display so that you
]can get rid of screen flicker." I put a raster interrupt routine in my
]program to move the line down and change the VIC-II chip's setting while the
]raster is off the visible display area, but either I did it wrong or it
]didn't work. I am out of ideas and need help badly.
] ...
]help. Either reply to me here at Harvey Mudd and I'll forward it to him, or
]if you're feeling really benevolent, mail him directly. His address is:

No, better yet - post replies here to the net, please!  I've tried to
do smooth ML scrolling on a 128 (*very* similar to doing it on a 64)
several times, with no success.  I'd like to be able to define a
playing-field for a game, and have the field be several times larger
than the size of the computer monitor.  First of all, how do I
represent the playfield in memory (it's a low-res color bitmap), and
second of all, how do I scroll through it smoothly?

Replies are more than welcome, but code samples would be *heavenly*!

Thank you.
     << Brian >>
-- 
| Brian S. Kendig       |  I feel more like I   | bskendig                   |
| Computer Engineering  |  did when I got here  | @phoenix.Princeton.EDU     |
| Princeton University  |       than I do now.  | @PUCC.BITNET               |
| Systems Engineering, NASA Space Station Freedom / General Electric WP3     |

byrd@husc7.HARVARD.EDU (John Byrd) (10/11/89)

There have been a number of messages requesting help on "smooth scrolling"
for the 64 mode, and Jim Butterfield posted a very intelligent solution
to this problem.  However, his solution may have been a little difficult to
understand for novice programmers.  I hope that I can explain the smooth-
scroll idea here in more of lay-programmers' terms.

Locations $D011 and $D016 in the 64 contain the smooth-scroll information in
the lower 3 bits of each of the registers.  All the information on the screen
may be slid up to 7 pixels (dots) to the right and 7 pixels down.  So how does
a programmer achieve the sliding, scrolling effect popular in games?

When the screen picture has been slid to its maximum distance, e.g. when a
register contains $111 in its lower 3 bits, all the characters on the screen
must be moved via a software routine, and new characters must be added on to
one side of the screen.  At this point the register may be reset to $000.  The
effect is that of shifting the picture one pixel.  This procedure may be
reversed and used on the other register to facilitate scrolling in any 
direction.

The programmer must watch for "flicker" when designing and writing this
type of sliding routine.  If the raster (present line which is being drawn on
the television screen) happens to be on the screen when the screen is being
moved via the software routine, the screen will flicker.  This can be
resolved by checking location $D012.  The raster is on the screen only when
the value of this location is between 51 and 251, AND when the high bit of
location $D011 is OFF.  Otherwise it is okay to do the software scroll.

Jim Butterfield suggested that TWO separate screens should be maintained.  In 
essence, display one screen while preparing the other for viewing; then, 
when the raster is off the screen, swap the viewed screen and the hidden
screen.  This swap can generally be done with a few POKES and PEEKS, although
the BASIC language is inadvisable for this type of programming due to
its lack of speed.  Speed of processing is essential for raster techniques;
the raster makes a pass of the screen every 1/60th of a second.

I hope that this message gives a better understanding of scrolling
techniques.

------------------------------------------------------------------------------

John Byrd
byrd@husc7.bitnet
Q-Link: John Byrd

-----------------------------------------------------------------------------

leblanc@eecg.toronto.edu (Marcel LeBlanc) (10/12/89)

In article <2823@husc6.harvard.edu> byrd@husc7.UUCP (John Byrd) writes:
 [ much deleted ]
>The programmer must watch for "flicker" when designing and writing this
>type of sliding routine.  If the raster (present line which is being drawn on
>the television screen) happens to be on the screen when the screen is being
			^^^^^^^^^^^^^^^^^^^^^^^^^^^ (difficult to avoid!)
>moved via the software routine, the screen will flicker.  This can be
>resolved by checking location $D012.  The raster is on the screen only when
>the value of this location is between 51 and 251, AND when the high bit of
>location $D011 is OFF.  Otherwise it is okay to do the software scroll.

Have you ever actually tried this (without double buffering)?  As you point
out later in your posting, the display is redrawn every 1/60 sec.  This is a
pretty tough timing problem.  Try this loop:

					CYCLES
		ldy #0			2
	lsrc	lda screen+40,y		4/5	(4 on 216/256, 5 on 40/256)
	ldst	sta screen,y		5
		iny			2
		bne lsrc		3/2	(3 on 255/256, 2 on 1/256)
		inc lsrc+2		6
		inc ldst+2		6
		lda lsrc+2		4
		cmp #>[screen+$0400]	2
		bcc lsrc		3/2	(3 on 3/4, 2 on 1/4)

total cycles = 2 + ( (4+40/256+5+2+3)*256-1+6+6+4+2+3)*4-1
	     = 14577 us (not counting VIC cycles)

1/60 sec is only 16667 us.  I suppose you could start a scroll *UP* 8 scan
lines behind the raster, such that the scroll wouldn't catch up to the
raster and would complete it's job just before the raster reaches the
visible part of the screen on the next refresh.  But what about a scroll
*DOWN*?  Starting from the bottom of the screen just after the raster has
left the visible area doesn't leave enough time to complete the scroll
before the raster starts displaying the top of the visible area on the next
refresh!  Unless the code I've listed above is *REALLY* bad, I can't think
of a way to smooth *DOWN* scroll a screen unless you double buffer it.  If
I'm missing something obvious, please correct me!

>John Byrd

Marcel A. LeBlanc	   | University of Toronto -- Toronto, Canada
"leblanc@eecg.toronto.edu" | and: LMS Technologies Ltd, Fredericton, NB, Canada
-------------------------------------------------------------------------------
UUCP:	uunet!utai!eecg!leblanc    BITNET: leblanc@eecg.utoronto[.ca]

byrd@husc7.HARVARD.EDU (John Byrd) (10/20/89)

In article <1989Oct11.151724.18728@jarvis.csri.toronto.edu> leblanc@eecg.toronto.edu (Marcel LeBlanc) writes:
>In article <2823@husc6.harvard.edu> byrd@husc7.UUCP (John Byrd) writes:
> (much deleted)
>>The programmer must watch for "flicker" when designing and writing this
>>type of sliding routine.  If the raster (present line which is being drawn on
>>the television screen) happens to be on the screen when the screen is being
>			^^^^^^^^^^^^^^^^^^^^^^^^^^^ (difficult to avoid!)
>>moved via the software routine, the screen will flicker.  This can be
>>resolved by checking location $D012.  The raster is on the screen only when
>>the value of this location is between 51 and 251, AND when the high bit of
>>location $D011 is OFF.  Otherwise it is okay to do the software scroll.
>
>Have you ever actually tried this (without double buffering)?  As you point
>out later in your posting, the display is redrawn every 1/60 sec.  This is a
>pretty tough timing problem.  Try this loop:
>

 (huge amount of code deleted)

>total cycles = 2 + ( (4+40/256+5+2+3)*256-1+6+6+4+2+3)*4-1
>	     = 14577 us (not counting VIC cycles)

 (more deletion)

>Starting from the bottom of the screen just after the raster has
>left the visible area doesn't leave enough time to complete the scroll
>before the raster starts displaying the top of the visible area on the next
>refresh! 

 (still more deletion)

Okay, guys.  Marcel has a point (and can you believe how he arrived at it?)
When I implemented a scroll for a game, I did it in a way only *similar* to
the way listed above.  The scroll was left to right, and I didn't have to
worry about the down factor.  More on that later.

Marcel has pointed out that scrolling a screen full of characters in the time
it takes the raster (television scanning line) to leave the bottom of the 
screen and arrive at the top would be a tight timing squeeze, at best.  I
did indeed use double-buffering when scrolling left and right.  Double-
buffering refers to drawing a picture on one screen while viewing the other
screen (a "screen" being a screen-sized section of memory) and then swapping
the viewed screen and the drawing screen.

However, if you are willing to put up with a certain amount of flicker (and
most games and demos from Europe seem to not mind a little flicker) and are
confused by coding a double-buffer, then it is possible to survive with one
screen.

Regarding scrolling down.  This algorithm does it, starting from the top of
the screen and working its way down: 

1. Copy present character row into buffer B.
2. Copy buffer A into present character row.
3. Copy buffer B into buffer A.
4. Go to next lower character row, if there is one; if not, end.
5. Go to step 1.

But the big question: is it fast enough to get to the bottom of the screen
before the raster, released at the previous bottom edge, does?  I don't
think so, after Marcel's analysis.  It should take about 3 times as long as
Marcel's routine, or about 1/20 of a second.  Dang it, not fast enough!
However, if it were possible to do this much computation in 1/60 of a
second, and your normal down-scroll algorithm wasn't fast enough to beat
the raster to the top of the screen, this one would do it.

SPECIAL TO LeBlanc: Anyone who counts clock cycles is not completely sane. :)

>Marcel A. LeBlanc	   | University of Toronto -- Toronto, Canada
>"leblanc@eecg.toronto.edu"| and: LMS Technologies Ltd, Fredericton, NB, Canada
>------------------------------------------------------------------------------
>UUCP:	uunet!utai!eecg!leblanc    BITNET: leblanc@eecg.utoronto[.ca]

John Byrd
"byrd@husc7.harvard.edu"

I can't think of an intelligent quote....

mccaslan@cetus1a.cs.utk.edu (Donald McCasland) (10/29/89)

>Regarding scrolling down.  This algorithm does it, starting from the top of
>the screen and working its way down: 
>
>1. Copy present character row into buffer B.
>2. Copy buffer A into present character row.
>3. Copy buffer B into buffer A.
>4. Go to next lower character row, if there is one; if not, end.
>5. Go to step 1.
>
>But the big question: is it fast enough to get to the bottom of the screen
>before the raster, released at the previous bottom edge, does?  I don't
>think so, after Marcel's analysis.  It should take about 3 times as long as
>Marcel's routine, or about 1/20 of a second.  Dang it, not fast enough!
>However, if it were possible to do this much computation in 1/60 of a
>second, and your normal down-scroll algorithm wasn't fast enough to beat
>the raster to the top of the screen, this one would do it.

Yes, I am an extremely unpracticed and unknowledgeable c64 user.
However, (this may seem stupid) has anyone thought of waiting for the
raster to be past the first 8 or so (far enough past so that the information
writing wouldn't catch up to the raster) character rows on the screen, and
then applying the aforementioned (I bet that's spelled wrong) buffering
algorithm.  In another words, what I'm trying to say is, would it be
possible to draw, smoothly, the screen while the raster is on another
part of the screen, rather than off the screen?

					Sincerely,
					Don McCasland
					The Priest at Nemi
____________________________________________________________
|"It has been rumored that the Peano Axioms of propositional|
| calculus have been shown to have direct links to the      |
| peyote plants found in the Southwest of the U.S."         |
-------------------------------------------------------------

leblanc@eecg.toronto.edu (Marcel LeBlanc) (10/30/89)

In article <1274@utkcs2.cs.utk.edu> mccaslan@cetus1a.cs.utk.edu (Donald McCasland) writes:
>>Regarding scrolling down.  This algorithm does it, starting from the top of
>>the screen and working its way down: 
>>
>>1. Copy present character row into buffer B.
>>2. Copy buffer A into present character row.
>>3. Copy buffer B into buffer A.
>>4. Go to next lower character row, if there is one; if not, end.
>>5. Go to step 1.
>>
>>But the big question: is it fast enough to get to the bottom of the screen
>>before the raster, released at the previous bottom edge, does?  I don't
>>think so, after Marcel's analysis.  It should take about 3 times as long as
>>Marcel's routine, or about 1/20 of a second.  Dang it, not fast enough!
>>However, if it were possible to do this much computation in 1/60 of a
>>second, and your normal down-scroll algorithm wasn't fast enough to beat
>>the raster to the top of the screen, this one would do it.
>
>Yes, I am an extremely unpracticed and unknowledgeable c64 user.
>However, (this may seem stupid) has anyone thought of waiting for the
>raster to be past the first 8 or so (far enough past so that the information
>writing wouldn't catch up to the raster) character rows on the screen, and
>then applying the aforementioned (I bet that's spelled wrong) buffering
>algorithm.  In another words, what I'm trying to say is, would it be
>possible to draw, smoothly, the screen while the raster is on another
>part of the screen, rather than off the screen?

This is exactly what my original posting on this subject addressed.  With
the scroll _UP_ algorithm that I presented, I does indeed seem possible to
start the scroll at the top of the screen once the raster has reached the
2nd character row, and have it complete before the raster starts the next
frame.  The problem is that this doesn't work with the scroll _DOWN_
algorithm.  The simple algorithm that I presented started from the bottom of
the screen, moving up.  This doesn't allow enough time to complete the
scroll before the raster starts the next frame.  The scroll _DOWN_ algorithm
quoted at the beginning of this posting starts at the top of the screen and
moves down.  Because of this, it has more time to complete the scroll.
However, it still won't be able to scroll smoothly without double buffering
because it performs about 3 times as many byte moves as the simpler
algorithm, which requires about 3 times as many CPU cycles.

>					Don McCasland

Marcel A. LeBlanc	   | University of Toronto -- Toronto, Canada
"leblanc@eecg.toronto.edu" | and: LMS Technologies Ltd, Fredericton, NB, Canada
-------------------------------------------------------------------------------
UUCP:	uunet!utai!eecg!leblanc    BITNET: leblanc@eecg.utoronto[.ca]