Perihelion tutorial 7: Difference between revisions

From AtariForumWiki
Jump to navigation Jump to search
>Admin
No edit summary
 
>Muguk
No edit summary
Line 4: Line 4:


   
   
Huh, so finally, as promised; the tutorial on scrollers. BTW, all my “huh” sounds aren’t like American huh, as in a question or as in a “oh yeah?”, but rather phew, like a sigh. Have you been waiting for this one? I hope you have, because it was a damn pain in the ass to write the scroller program, even though it’s simple. It began with me reaching to high, also, forgetting about the bitplane layout of the graphics memory. When I put a little lower ambition level, for the sake of keeping it simple, things went smoother. Now, Luca Turilli playing in Winamp, the mood is set, time to write. The people who already know how to do scrollers will probably laugh their ass off at this clumsy scroller, which really is bad in every way except learning the basic stuff, I’ll probably do a more advanced one later on; I’ve heard that building on knowledge is good.


              Huh, so finally, as promised; the tutorial on scrollers. BTW, all my “huh” sounds aren’t like American huh, as in a question or as in a “oh yeah?”, but rather phew, like a sigh. Have you been waiting for this one? I hope you have, because it was a damn pain in the ass to write the scroller program, even though it’s simple. It began with me reaching to high, also, forgetting about the bitplane layout of the graphics memory. When I put a little lower ambition level, for the sake of keeping it simple, things went smoother. Now, Luca Turilli playing in Winamp, the mood is set, time to write. The people who already know how to do scrollers will probably laugh their ass off at this clumsy scroller, which really is bad in every way except learning the basic stuff, I’ll probably do a more advanced one later on; I’ve heard that building on knowledge is good.
A few happy news first. I’ve gotten mail from three different people, excluding Maarten Martens. Thanks guys, you know who you are! One mail from FrEd highlighted a few misses I made, concerning the compatibility with Devpac 2. My initlib had a little bug. It works fine in Devpac 3, but not in 2. Two lines had d0-7 in them, it really should read d0-d7, but it's fixed now. I know some other things may also be difficult with other assemblers than Devpac 3, so if the code doesn’t work for you, just use Devpac 3. I have tested every piece of code with that on an original Atari ST(e), so there should be no problem. Thanks go out to FrEd for pointing this out, and also to Torsten Keltsch for telling me the a little about the workings of $fffc02.


            A few happy news first. I’ve gotten mail from three different people, excluding Maarten Martens. Thanks guys, you know who you are! One mail from FrEd highlighted a few misses I made, concerning the compatibility with Devpac 2. My initlib had a little bug. It works fine in Devpac 3, but not in 2. Two lines had d0-7 in them, it really should read d0-d7, but it's fixed now. I know some other things may also be difficult with other assemblers than Devpac 3, so if the code doesn’t work for you, just use Devpac 3. I have tested every piece of code with that on an original Atari ST(e), so there should be no problem. Thanks go out to FrEd for pointing this out, and also to Torsten Keltsch for telling me the a little about the workings of $fffc02.
A few happy news first. I’ve gotten mail from three different people, excluding Maarten Martens. Thanks guys, you know who you are! One mail from FrEd highlighted a few misses I made, concerning the compatibility with Devpac 2. My initlib had a little bug. It works fine in Devpac 3, but not in 2. Two lines had d0-7 in them, it really should read d0-d7. Since I support Devpac 3 though, I won’t make to much fuss about it. I know some other things may also be difficult with other assemblers than Devpac 3, so if the code doesn’t work for you, just use Devpac 3. I have tested every piece of code with that on an original Atari Ste, so there should be no problem. Thanks go out to FrEd for pointing this out, and also to MOdmate of Checkpoint for telling me a little about the workings of $fffc02.  


            A few happy news first. I’ve gotten mail from three different people, excluding Maarten Martens. Thanks guys, you know who you are! One mail from FrEd highlighted a few misses I made, concerning the compatibility with Devpac 2. My initlib had a little bug. It works fine in Devpac 3, but not in 2. Two lines had d0-7 in them, it really should read d0-d7. Since I support Devpac 3 though, I won’t make to much fuss about it. I know some other things may also be difficult with other assemblers than Devpac 3, so if the code doesn’t work for you, just use Devpac 3. I have tested every piece of code with that on an original Atari Ste, so there should be no problem. Thanks go out to FrEd for pointing this out, and also to MOdmate of Checkpoint for telling me a little about the workings of $fffc02.  
Yep, a scroller. I’m a bit unsure of where to start, but I guess I’ll just work from the top down. What does a scroller do? Letters go from the right of the screen, to the left of the screen (usually). New letters are brought in from the right, “outside” of the screen. How can this be achieved? The screen memory needs to be moved “to the left”, and then we need information to bring in the new characters from the right. OK, this seems to build on an idea to have letters stored as graphics. Huhm, yes, we have a font collection in a degas file. In that way, we’ll have letters in graphics format, we can take the information from the font file and put it on the screen. Then, we move the screen memory to the left. Easy? No, damn hard for a first timer at least.


              Yep, a scroller. I’m a bit unsure of where to start, but I guess I’ll just work from the top down. What does a scroller do? Letters go from the right of the screen, to the left of the screen (usually). New letters are brought in from the right, “outside” of the screen. How can this be achieved? The screen memory needs to be moved “to the left”, and then we need information to bring in the new characters from the right. OK, this seems to build on an idea to have letters stored as graphics. Huhm, yes, we have a font collection in a degas file. In that way, we’ll have letters in graphics format, we can take the information from the font file and put it on the screen. Then, we move the screen memory to the left. Easy? No, damn hard for a first timer at least.
Included in this tutorial should be a file called font.pi1, this is the font file, I stole it from James Ingrams demo tutorials, so I wouldn’t have to make my own. Immediately load this up and look at it, either using Degas, the program from tutorial 6 or any other method. Looki looki, lots of characters to choose from. Each character is 32 * 32 pixels big, resulting in 10 characters per line. This is all well and fine, the next step is to actually know how to point to the beginning of, for example, letter ‘C’. If we know where this letter begins, we can put it on our screen simply by moving the data into the screen memory. Just as we did when displaying a whole picture.


            Included in this tutorial should be a file called font.pi1, this is the font file, I stole it from James Ingrams demo tutorials, so I wouldn’t have to make my own. Immediately load this up and look at it, either using Degas, the program from tutorial 6 or any other method. Looki looki, lots of characters to choose from. Each character is 32 * 32 pixels big, resulting in 10 characters per line. This is all well and fine, the next step is to actually know how to point to the beginning of, for example, letter ‘C’. If we know where this letter begins, we can put it on our screen simply by moving the data into the screen memory. Just as we did when displaying a whole picture.
The font picture is aligned with the ASCII table, meaning that it looks like the ASCII table does. In this tutorial should also be included the ascii.txt file, in which you can look up the number for each character. As you can see, space (the first character in the font), begins at $20, then comes ‘!’ at $21 and so on. This means, that if we take the ASCII value for a character, and subtract by $20, we’ll have the corresponding number in the font. Huhm, a test perhaps. ‘C’ is at $43 in ASCII, subtract $20 makes $23, which is 35 (decimal). There are 10 characters per line, so we skip to the fourth line, begin counting; 0 (>), 1(?), 2(@), 3(A), 4(B), 5(C), yay, right on! (remember to start counting from 0).


            The font picture is aligned with the ASCII table, meaning that it looks like the ASCII table does. In this tutorial should also be included the ascii.txt file, in which you can look up the number for each character. As you can see, space (the first character in the font), begins at $20, then comes ‘!’ at $21 and so on. This means, that if we take the ASCII value for a character, and subtract by $20, we’ll have the corresponding number in the font. Huhm, a test perhaps. ‘C’ is at $43 in ASCII, subtract $20 makes $23, which is 35 (decimal). There are 10 characters per line, so we skip to the fourth line, begin counting; 0 (>), 1(?), 2(@), 3(A), 4(B), 5(C), yay, right on! (remember to start counting from 0).
Now we need to know what address this is at. The way to do this is to put the beginning of the font picture address in an address register, and increment by a number. Think of the font as a coordinate system, then ‘C’ would be at 3,5. We need to increment the pointer by a certain value for each coordinate, this shouldn’t be to hard.


            Now we need to know what address this is at. The way to do this is to put the beginning of the font picture address in an address register, and increment by a number. Think of the font as a coordinate system, then ‘C’ would be at 3,5. We need to increment the pointer by a certain value for each coordinate, this shouldn’t be to hard.
Each line is 160 bytes, and each character is 32 lines. This means that for every Y coordinate, we need to increment the pointer by 32 * 160 bytes, right? Think about it, if we want ‘*’ which is on the second line (1,0), we need to point to the font address + 32 lines down. Each character is 32 pixels wide, 16 pixels are 4 words, taking up 8 bytes, we need twice this. So for each X coordinate, we need to increment the pointer by 16 bytes.        


            Each line is 160 bytes, and each character is 32 lines. This means that for every Y coordinate, we need to increment the pointer by 32 * 160 bytes, right? Think about it, if we want ‘*’ which is on the second line (1,0), we need to point to the font address + 32 lines down. Each character is 32 pixels wide, 16 pixels are 4 words, taking up 8 bytes, we need twice this. So for each X coordinate, we need to increment the pointer by 16 bytes.         
Does this seem right? Let’s try. We want letter ‘C’, at 3,5. Thus we should increment by 3*32*160 + 5*16 = 15440 bytes. ‘C’ is about the middle of the screen and 15440 is about half of 32000, so it seems safe to assume that the formula above is working. Question is, how do we get the X and Y coordinates? We had a value for C, right, that was 35. The first digit seems to be the Y coordinate, and the second the X coordinate. If we divide 35 by 10 we get 3.5. 3 is the quotient and 5 the remainder. The instruction divu (DIVide Unsigned) puts the quotient in the lower word of a data register, and the remainder in the higher word. Swap is an instruction that swaps the low and high word in a data register. Great! We now have what wee need. The code looks like this:
 
            Does this seem right? Let’s try. We want letter ‘C’, at 3,5. Thus we should increment by 3*32*160 + 5*16 = 15440 bytes. ‘C’ is about the middle of the screen and 15440 is about half of 32000, so it seems safe to assume that the formula above is working. Question is, how do we get the X and Y coordinates? We had a value for C, right, that was 35. The first digit seems to be the Y coordinate, and the second the X coordinate. If we divide 35 by 10 we get 3.5. 3 is the quotient and 5 the remainder. The instruction divu (DIVide Unsigned) puts the quotient in the lower word of a data register, and the remainder in the higher word. Swap is an instruction that swaps the low and high word in a data register. Great! We now have what wee need. The code looks like this:


    
    
   move.l #character,a0 points to character
   move.l #character,a0 points to character
   move.l
   move.l  #font+34,a1     points to pixel start
 
  move.b  (a0),d0         put letter ascii value in d0
   
#font+34,a1  
 
points to pixel start
 
  move.b
 
   
(a0),d0
 
put letter ascii value in d0
 
   add.b #-$20,d0 align asciin with font number
   add.b #-$20,d0 align asciin with font number
   divu
   divu   #10,d0         10 letters per row
 
#10,d0
 
10 letters per row


   
   
   move.w d0,d1 d1 contains y value
   move.w d0,d1         d1 contains y value
   swap d0  
   swap d0  
   move.w
   move.w d0,d2          d2 contains x value


  mulu #16,d2         16 bytes for each letter
d0,d2
  mulu #32,d1         32 lines per row
  mulu    #160,d1        160 bytes per row


   
   
d2 contains x value
  move.l  #font+34,a0    put font screen start in a0


   
   
  mulu #16,d2 16 bytes for each letter
   add.l d2,d1         add x and y value together
  mulu #32,d1 32 lines per row
   add.l   d1,a0           a0 points to correct letter
  mulu
 
#160,d1
 
160 bytes per row
 
  move.l
 
#font+34,a0
 
put font screen start in a0
 
   add.l d2,d1 add x and y value together
   add.l
 
d1,a0
 
a0 points to correct letter


   
   
Line 100: Line 52:
   
   


            Since each character is an ASCII value, we only use a byte to represent it. If we put things inside “”, that means we want the ASCII value. So the message dc.b “C”, means that message is a byte containing the ASCII value for C. We could just as well have written message dc.b $43, but this is more difficult to understand. Hopefully, the code will speak for itself with the comments and the theory given above. This is not a complete program, but just a code snippet to show the font part. More will follow.
Since each character is an ASCII value, we only use a byte to represent it. If we put things inside “”, that means we want the ASCII value. So the message dc.b “C”, means that message is a byte containing the ASCII value for C. We could just as well have written message dc.b $43, but this is more difficult to understand. Hopefully, the code will speak for itself with the comments and the theory given above. This is not a complete program, but just a code snippet to show the font part. More will follow.


            We know how to point to the font, now we need to know how to shift the screen memory, in order to achieve the scrolling effect. One would think that all it took was a big loop moving bytes. Like so (a0 and a1 contain the address of the screen memory)
We know how to point to the font, now we need to know how to shift the screen memory, in order to achieve the scrolling effect. One would think that all it took was a big loop moving bytes. Like so (a0 and a1 contain the address of the screen memory)


    
    
Line 110: Line 62:
move.b (a1)+,(a0)+ 
move.b (a1)+,(a0)+ 


            For each loop we take the byte one byte ahead, and move it one byte to the left. This should move 8 pixels each loop, right? Wrong! Totally wrong! The screen is made of 16 pixel clusters, each cluster being 8 bytes long. So when you just barge in and move single bytes like that, you’ll misalign the whole shit. Not only will the colours be misaligned, the pixels will be as well. Consider this memory configuration.
For each loop we take the byte one byte ahead, and move it one byte to the left. This should move 8 pixels each loop, right? Wrong! Totally wrong! The screen is made of 16 pixel clusters, each cluster being 8 bytes long. So when you just barge in and move single bytes like that, you’ll misalign the whole shit. Not only will the colours be misaligned, the pixels will be as well. Consider this memory configuration.


    
    
Line 148: Line 100:


Not really, the pixels we had before. So, in order to overcome this in an easy way we move 16 pixels each time. This will produce a very fast scroller, but an easy one to code for. If we move 16 pixels, we won’t have to worry about getting misaligned bitplanes, since the 16 pixel clusters will never be broken up, like they were above. a0 and a1 contains the screen address, while a2 points to the character in the font.   
Not really, the pixels we had before. So, in order to overcome this in an easy way we move 16 pixels each time. This will produce a very fast scroller, but an easy one to code for. If we move 16 pixels, we won’t have to worry about getting misaligned bitplanes, since the 16 pixel clusters will never be broken up, like they were above. a0 and a1 contains the screen address, while a2 points to the character in the font.   
add.l


add.l #8,a1     put a1 16 pixels ahead of a0   
#8,a1
 
put a1 16 pixels ahead of a0   


   
   
Line 164: Line 111:
move.w (a1)+,(a0)+
move.w (a1)+,(a0)+
move.w (a1)+,(a0)+ 16 pixels moved   
move.w (a1)+,(a0)+ 16 pixels moved   
dbf
dbf     d0,scroll       keep moving 16 pixel clusters
 
move.l  #18,d0         reset loop counter
d0,scroll
 
keep moving 16 pixel clusters
 
move.l
 
   
#18,d0
 
reset loop counter
 
move.w (a2),(a0)+ 
move.w (a2),(a0)+ 
move.w 2(a2),(a0)+ 
move.w 2(a2),(a0)+ 
move.w 4(a2),(a0)+  16 pixels of the font
move.w 4(a2),(a0)+  16 pixels of the font
move.w 6(a2),(a0)+ character moved in  
move.w 6(a2),(a0)+ character moved in  
add.l #8,a1 increment screen pointer, align with a0   
add.l #8,a1         increment screen pointer, align with a0   
add.l
add.l   #160,a2         next line of font   
 
#160,a2
 
next line of font
 
   
dbf d1,scroll do another line   
dbf d1,scroll do another line   


                
                


            This is all just a bunch of move words, and some adds to keep everything aligned. The first move section will move 4 words from a1, which points one 16 bit cluster ahead of a0, to a0. This is repeated 19 times. After this loop, a0 points to the beginning of the last 16 pixel cluster, and a0 points to the beginning of the second line. For the last 16 pixel cluster, we want information from the font, not from the screen. So here we move information from a2 into a0. Instead of post incrementing a2, I use indexes. After the font data is moved onto the screen, I add 8 to a1, so that it will again be 16 pixels ahead of a0. Since a0 was incremented during the font move part, and a1 was not. 160 is added to a2, so that the font pointer will now point to the next line in the font. Repeat for 32 lines.
This is all just a bunch of move words, and some adds to keep everything aligned. The first move section will move 4 words from a1, which points one 16 bit cluster ahead of a0, to a0. This is repeated 19 times. After this loop, a0 points to the beginning of the last 16 pixel cluster, and a0 points to the beginning of the second line. For the last 16 pixel cluster, we want information from the font, not from the screen. So here we move information from a2 into a0. Instead of post incrementing a2, I use indexes. After the font data is moved onto the screen, I add 8 to a1, so that it will again be 16 pixels ahead of a0. Since a0 was incremented during the font move part, and a1 was not. 160 is added to a2, so that the font pointer will now point to the next line in the font. Repeat for 32 lines.


            Now the two most important techniques have been covered, how to know where the character is in the font, and how to scroll. Now we mix and match. In order to synchronize the entire scroller to the VBL, I put a wait VBL trap in the beginning of the main loop. Then I do my stuff, and in the end of the main loop, I check if the space bar is pressed, if it is, just drop out of the loop. If space bar is not pressed, then the main loop will begin again, with a VBL wait, making sure that the main loop is looped through at 50 times a second. You’ll probably be wondering exactly how I determine whether the space bar is pressed.
Now the two most important techniques have been covered, how to know where the character is in the font, and how to scroll. Now we mix and match. In order to synchronize the entire scroller to the VBL, I put a wait VBL trap in the beginning of the main loop. Then I do my stuff, and in the end of the main loop, I check if the space bar is pressed, if it is, just drop out of the loop. If space bar is not pressed, then the main loop will begin again, with a VBL wait, making sure that the main loop is looped through at 50 times a second. You’ll probably be wondering exactly how I determine whether the space bar is pressed.


            This little piece will do the trick; cmp.b #$39,$fffc02. Uh, says you, looking at the ASCII table (hopefully) and wondering how $39 can be space, when it should be $20. The $fffc02 part can be easily guessed, this is probably where the last key press end up, but why $39? ASCII deals with characters, and special characters like line feed and so. There’s also something called scan codes. Every key on the keyboard has its value, it’s scan code so you’ll be able to determine what key was pressed. Look at the Scancodes.gif file included.  
This little piece will do the trick; cmp.b #$39,$fffc02. Uh, says you, looking at the ASCII table (hopefully) and wondering how $39 can be space, when it should be $20. The $fffc02 part can be easily guessed, this is probably where the last key press end up, but why $39? ASCII deals with characters, and special characters like line feed and so. There’s also something called scan codes. Every key on the keyboard has its value, it’s scan code so you’ll be able to determine what key was pressed. Look at the Scancodes.gif file included.  


            While we’re still on the topic, I might as well give you the full detail. You can also check when a key is released, not just pressed. When the key is released, the high bit of $fffc02 is set, meaning you get a whole different value. Consider this.
While we’re still on the topic, I might as well give you the full detail. You can also check when a key is released, not just pressed. When the key is released, the high bit of $fffc02 is set, meaning you get a whole different value. Consider this.


   
   


             %00111001                $39            space pressed
             %00111001                $39            space pressed
             %10111001                $b9            space released
             %10111001                $b9            space released


So, if you cmp.b #$b9, then you check if space is released. This can be used in many fun ways, like changing the background to red when space is pressed, then checking to see when space is released and then restore background. Or accelerate a car in a car game until the button is released, at which time you begin deceleration. I don’t know how often this is updated or how fast you can really press keys. Say for example that you check $fffc02 every VBL to see what key is pressed and released, suppose this dude is like Flash, and manage to press a button, then release it and press another within 1/50 of a second, then you’d loose the check for the release of the key, but I doubt you’ll have to worry about this. Back to reality, here’s the scroller.
 
            So, if you cmp.b #$b9, then you check if space is released. This can be used in many fun ways, like changing the background to red when space is pressed, then checking to see when space is released and then restore background. Or accelerate a car in a car game until the button is released, at which time you begin deceleration. I don’t know how often this is updated or how fast you can really press keys. Say for example that you check $fffc02 every VBL to see what key is pressed and released, suppose this dude is like Flash, and manage to press a button, then release it and press another within 1/50 of a second, then you’d loose the check for the release of the key, but I doubt you’ll have to worry about this. Back to reality, here’s the scroller.


    
    
jsr
jsr     initialise   
 
movem.l font+2,d0-d7
movem.l d0-d7,$ff8240
initialise   
 
movem.l font+2,d0-d7
movem.l
 
d0-d7,$ff8240
 
 
   
   
move.w #2,-(a7)  get physbase   
move.w #2,-(a7)  get physbase   
trap  #14
trap  #14
addq.l #2,a7 
addq.l #2,a7 
move.l d0,screen store screen memory
move.l d0,screen store screen memory
main
main
move.w #37,-(sp) wait vbl   
move.w #37,-(sp) wait vbl   
trap  #14 
trap  #14 
addq.l
addq.l  #2,sp
 
   
#2,sp
 
cmp #0,font_counter check if new character in message   
cmp #0,font_counter check if new character in message   
bne  
bne     has_character           if not, skip get new character   
 
has_character
 
if not, skip get new character   


   
   
Line 263: Line 158:


* we need to point to a new characetr in the font
* we need to point to a new characetr in the font
move.l message_pointer,a0 pointer into the message
move.l message_pointer,a0 pointer into the message
clr.l d0 clear, just to be sure
clr.l d0                 clear, just to be sure
move.b
move.b  (a0),d0                 put letter ascii value in d0   
 
   
(a0),d0
 
put letter ascii value in d0   


   
   
cmp #0,d0 end of message?   
cmp #0,d0 end of message?   
bne
bne     not_end                 if not, branch
 
not_end
 
if not, branch


   
   
Line 293: Line 177:
add.l #1,message_pointer point to next character   
add.l #1,message_pointer point to next character   
add.b #-$20,d0 align ascii with font number
add.b #-$20,d0 align ascii with font number
divu  
divu   #10,d0         10 letters per row   
 
move.w d0,d1 d1 contains y value
#10,d0
 
10 letters per row
 
   
move.w d0,d1 d1 contains y value
swap d0 
swap d0 
move.w
move.w  d0,d2   d2 contains x value
 
   
d0,d2  
 
d2 contains x value
 
mulu  #16,d2 16 bytes for each letter   
mulu  #16,d2 16 bytes for each letter   
mulu #32,d1 32 lines per row
mulu #32,d1 32 lines per row
mulu
mulu   #160,d1   160 bytes per row   
 
#160,d1
 
160 bytes per row   


   
   
move.l #font+34,a0 put font screen start in a0   
move.l #font+34,a0 put font screen start in a0   
add.l d2,d1 add x and y value together   
add.l d2,d1 add x and y value together   
add.l
add.l   d1,a0        a0 points to correct letter


move.l a0,font_address store calculated pointer
d1,a0


a0 points to correct letter
move.l a0,font_address store calculated pointer
has_character 
has_character 
add.w
add.w   #-1,font_counter
 
#-1,font_counter
 
move.l screen,a0 
move.l screen,a0 
move.l screen,a1 
move.l screen,a1 
move.l font_address,a2
move.l font_address,a2
add.l
add.l   #8,a1             put a1 16 pixels ahead of a0
 
#8,a1
 
put a1 16 pixels ahead of a0


   
   
Line 362: Line 206:
move.w (a1)+,(a0)+
move.w (a1)+,(a0)+
move.w (a1)+,(a0)+
move.w (a1)+,(a0)+
move.w
move.w  (a1)+,(a0)+  16 pixels moved   
 
dbf d0,scroll     keep moving 16 pixel clusters   
   
move.l  #18,d0         reset loop counter   
(a1)+,(a0)+
 
   
16 pixels moved
 
   
dbf d0,scroll keep moving 16 pixel clusters   
move.l  
 
   
#18,d0
 
reset loop counter   


   
   
move.w (a2),(a0)+ 
move.w (a2),(a0)+ 
move.w 2(a2),(a0)+
move.w 2(a2),(a0)+
move.w 4(a2),(a0)+ 16 pixels of the font
move.w 4(a2),(a0)+ 16 pixels of the font
move.w 6(a2),(a0)+ character moved in  
move.w 6(a2),(a0)+ character moved in  
add.l  #8,a1  increment screen pointer, align with a0   
add.l  #8,a1  increment screen pointer, align with a0   
add.l  
add.l   #160,a2     next line of font   
 
dbf     d1,scroll   do another line   
#160,a2
 
next line of font
 
   
dbf  
 
d1,scroll
 
do another line   


   
   
add.l  #8,font_address move 16 pixels forward in font
add.l  #8,font_address move 16 pixels forward in font
cmp.b #$39,$fffc02 space pressed?
cmp.b #$39,$fffc02 space pressed?
bne  
bne     main           if not, repeat main   
 
main
 
if not, repeat main   


jsr   restore
jsr
 
restore
 
clr.l -(a7)
clr.l -(a7)
trap
trap   #1   
 
#1
 
   
include
include  initlib.s   
 
   
initlib.s   


   
   
Line 449: Line 244:


   
   
There are really only two more small things that are new; the font counter and the message pointer. Also take not how I put in the scrolling message, by just using lots of dc.b, and a ‘0’ as an end control character. By changing the text here, you can obviously change the scroller message. Perhaps to the well known “Hello World!”, which I most deliberately avoided.


            There are really only two more small things that are new; the font counter and the message pointer. Also take not how I put in the scrolling message, by just using lots of dc.b, and a ‘0’ as an end control character. By changing the text here, you can obviously change the scroller message. Perhaps to the well known “Hello World!”, which I most deliberately avoided.
So what’s the font counter and message address? Well, the font counter keeps track of when it’s time to calculate a new address for a new character. This is set to 2, because every second loop, a whole character has been moved to the screen and the address for the next character in the message will have to be calculated. Had we scrolled 8 pixels each VBL, the font counter would have been set to 4 instead.
 
            So what’s the font counter and message address? Well, the font counter keeps track of when it’s time to calculate a new address for a new character. This is set to 2, because every second loop, a whole character has been moved to the screen and the address for the next character in the message will have to be calculated. Had we scrolled 8 pixels each VBL, the font counter would have been set to 4 instead.


            The message pointer is an index into the message. In order to know which character to get next time, we must have some pointer into the message. The message pointer begins by pointing to the message, which is good, since that’s where the first character is. The first time through the main loop, the font counter will announce that an address for a new character will have to be calculated. That address is calculated and stored. The message pointer will then point to the next character in the scroller message, which is space, and so on. When the whole message has been scrolled through, the value 0 (not character ‘0’) will be moved from the message. This is a signal that the message is at an end, and the message pointer will be reset, once again pointing to the start of the message. I hope it’s understandable, the one tricky part is all the tests and branches, just walk through them a couple of times, slowly. You can use pen and paper for this, or the MonST.
The message pointer is an index into the message. In order to know which character to get next time, we must have some pointer into the message. The message pointer begins by pointing to the message, which is good, since that’s where the first character is. The first time through the main loop, the font counter will announce that an address for a new character will have to be calculated. That address is calculated and stored. The message pointer will then point to the next character in the scroller message, which is space, and so on. When the whole message has been scrolled through, the value 0 (not character ‘0’) will be moved from the message. This is a signal that the message is at an end, and the message pointer will be reset, once again pointing to the start of the message. I hope it’s understandable, the one tricky part is all the tests and branches, just walk through them a couple of times, slowly. You can use pen and paper for this, or the MonST.


            We’ve begun to get somewhere. If you’ve paid attention so far, you’ll have acquired quite some programming skills. There are still some basic things to cover, in order to be really self sufficient (mainly timers, double buffering, sprites and bit manipulation) but you’re on a good way. Now might be the time to look at alternative sources and learn something from there. For example, you could begin to look at James Ingram’s demo coding tutorials. That was where I began, I found them quite hard but now we’ve gotten more or less to the level where he begins his stuff. That is that from me right now. Upon request from MOdmate of Checkpoint, the next tutorial will probably be on timers, I think. This means we’ll be able to remove the bottom and top borders, cool stuff!   
We’ve begun to get somewhere. If you’ve paid attention so far, you’ll have acquired quite some programming skills. There are still some basic things to cover, in order to be really self sufficient (mainly timers, double buffering, sprites and bit manipulation) but you’re on a good way. Now might be the time to look at alternative sources and learn something from there. For example, you could begin to look at James Ingram’s demo coding tutorials. That was where I began, I found them quite hard but now we’ve gotten more or less to the level where he begins his stuff. That is that from me right now. Upon request from MOdmate of Checkpoint, the next tutorial will probably be on timers, I think. This means we’ll be able to remove the bottom and top borders, cool stuff!   


   
   
Line 462: Line 256:
Warrior Munk of poSTmortem, 2002-04-27  
Warrior Munk of poSTmortem, 2002-04-27  


“My grandfather taught, me the energy of life goes in a circle, in a perfect flow; balanced. He said, until I find my centre, my circle will never be whole”  
''“My grandfather taught, me the energy of life goes in a circle, in a perfect flow; balanced. He said, until I find my centre, my circle will never be whole”''


- The One   
- The One   


Last edited 2002-06-14
Last edited 2002-06-14

Revision as of 09:35, 2 February 2006


The Atari ST M68000 tutorial part 7 – on scrollers


Huh, so finally, as promised; the tutorial on scrollers. BTW, all my “huh” sounds aren’t like American huh, as in a question or as in a “oh yeah?”, but rather phew, like a sigh. Have you been waiting for this one? I hope you have, because it was a damn pain in the ass to write the scroller program, even though it’s simple. It began with me reaching to high, also, forgetting about the bitplane layout of the graphics memory. When I put a little lower ambition level, for the sake of keeping it simple, things went smoother. Now, Luca Turilli playing in Winamp, the mood is set, time to write. The people who already know how to do scrollers will probably laugh their ass off at this clumsy scroller, which really is bad in every way except learning the basic stuff, I’ll probably do a more advanced one later on; I’ve heard that building on knowledge is good.

A few happy news first. I’ve gotten mail from three different people, excluding Maarten Martens. Thanks guys, you know who you are! One mail from FrEd highlighted a few misses I made, concerning the compatibility with Devpac 2. My initlib had a little bug. It works fine in Devpac 3, but not in 2. Two lines had d0-7 in them, it really should read d0-d7, but it's fixed now. I know some other things may also be difficult with other assemblers than Devpac 3, so if the code doesn’t work for you, just use Devpac 3. I have tested every piece of code with that on an original Atari ST(e), so there should be no problem. Thanks go out to FrEd for pointing this out, and also to Torsten Keltsch for telling me the a little about the workings of $fffc02.

A few happy news first. I’ve gotten mail from three different people, excluding Maarten Martens. Thanks guys, you know who you are! One mail from FrEd highlighted a few misses I made, concerning the compatibility with Devpac 2. My initlib had a little bug. It works fine in Devpac 3, but not in 2. Two lines had d0-7 in them, it really should read d0-d7. Since I support Devpac 3 though, I won’t make to much fuss about it. I know some other things may also be difficult with other assemblers than Devpac 3, so if the code doesn’t work for you, just use Devpac 3. I have tested every piece of code with that on an original Atari Ste, so there should be no problem. Thanks go out to FrEd for pointing this out, and also to MOdmate of Checkpoint for telling me a little about the workings of $fffc02.

Yep, a scroller. I’m a bit unsure of where to start, but I guess I’ll just work from the top down. What does a scroller do? Letters go from the right of the screen, to the left of the screen (usually). New letters are brought in from the right, “outside” of the screen. How can this be achieved? The screen memory needs to be moved “to the left”, and then we need information to bring in the new characters from the right. OK, this seems to build on an idea to have letters stored as graphics. Huhm, yes, we have a font collection in a degas file. In that way, we’ll have letters in graphics format, we can take the information from the font file and put it on the screen. Then, we move the screen memory to the left. Easy? No, damn hard for a first timer at least.

Included in this tutorial should be a file called font.pi1, this is the font file, I stole it from James Ingrams demo tutorials, so I wouldn’t have to make my own. Immediately load this up and look at it, either using Degas, the program from tutorial 6 or any other method. Looki looki, lots of characters to choose from. Each character is 32 * 32 pixels big, resulting in 10 characters per line. This is all well and fine, the next step is to actually know how to point to the beginning of, for example, letter ‘C’. If we know where this letter begins, we can put it on our screen simply by moving the data into the screen memory. Just as we did when displaying a whole picture.

The font picture is aligned with the ASCII table, meaning that it looks like the ASCII table does. In this tutorial should also be included the ascii.txt file, in which you can look up the number for each character. As you can see, space (the first character in the font), begins at $20, then comes ‘!’ at $21 and so on. This means, that if we take the ASCII value for a character, and subtract by $20, we’ll have the corresponding number in the font. Huhm, a test perhaps. ‘C’ is at $43 in ASCII, subtract $20 makes $23, which is 35 (decimal). There are 10 characters per line, so we skip to the fourth line, begin counting; 0 (>), 1(?), 2(@), 3(A), 4(B), 5(C), yay, right on! (remember to start counting from 0).

Now we need to know what address this is at. The way to do this is to put the beginning of the font picture address in an address register, and increment by a number. Think of the font as a coordinate system, then ‘C’ would be at 3,5. We need to increment the pointer by a certain value for each coordinate, this shouldn’t be to hard.

Each line is 160 bytes, and each character is 32 lines. This means that for every Y coordinate, we need to increment the pointer by 32 * 160 bytes, right? Think about it, if we want ‘*’ which is on the second line (1,0), we need to point to the font address + 32 lines down. Each character is 32 pixels wide, 16 pixels are 4 words, taking up 8 bytes, we need twice this. So for each X coordinate, we need to increment the pointer by 16 bytes.

Does this seem right? Let’s try. We want letter ‘C’, at 3,5. Thus we should increment by 3*32*160 + 5*16 = 15440 bytes. ‘C’ is about the middle of the screen and 15440 is about half of 32000, so it seems safe to assume that the formula above is working. Question is, how do we get the X and Y coordinates? We had a value for C, right, that was 35. The first digit seems to be the Y coordinate, and the second the X coordinate. If we divide 35 by 10 we get 3.5. 3 is the quotient and 5 the remainder. The instruction divu (DIVide Unsigned) puts the quotient in the lower word of a data register, and the remainder in the higher word. Swap is an instruction that swaps the low and high word in a data register. Great! We now have what wee need. The code looks like this:


 	move.l 	#character,a0 	points to character
 	move.l  #font+34,a1     points to pixel start
  	move.b  (a0),d0         put letter ascii value in d0
 	add.b 	#-$20,d0 	align asciin with font number
 	divu    #10,d0          10 letters per row


 	move.w 	d0,d1 	        d1 contains y value
 	swap 	d0 	 
 	move.w  d0,d2           d2 contains x value
 	mulu 	#16,d2 	        16 bytes for each letter
 	mulu 	#32,d1 	        32 lines per row
 	mulu    #160,d1         160 bytes per row


 	move.l  #font+34,a0     put font screen start in a0


 	add.l 	d2,d1  	        add x and y value together
 	add.l   d1,a0           a0 points to correct letter


section data font incbin font.pi1 character dc.b “C”


Since each character is an ASCII value, we only use a byte to represent it. If we put things inside “”, that means we want the ASCII value. So the message dc.b “C”, means that message is a byte containing the ASCII value for C. We could just as well have written message dc.b $43, but this is more difficult to understand. Hopefully, the code will speak for itself with the comments and the theory given above. This is not a complete program, but just a code snippet to show the font part. More will follow.

We know how to point to the font, now we need to know how to shift the screen memory, in order to achieve the scrolling effect. One would think that all it took was a big loop moving bytes. Like so (a0 and a1 contain the address of the screen memory)


add.l #1,a1 put a1 8 pixels ahead of a0 move.l #159,d0 scroll a line loop move.b (a1)+,(a0)+

For each loop we take the byte one byte ahead, and move it one byte to the left. This should move 8 pixels each loop, right? Wrong! Totally wrong! The screen is made of 16 pixel clusters, each cluster being 8 bytes long. So when you just barge in and move single bytes like that, you’ll misalign the whole shit. Not only will the colours be misaligned, the pixels will be as well. Consider this memory configuration.


First byte Second byte %11000000 %00000000 first word %11000000 %00000101 second word %01000000 %00000110 third word %01100000 %00000000 fourth word %00000000


...


fifth word


$3F800000 $00000642 pixels

If we use the move loop from above, the first byte will drop out, the second byte will be moved into the first byte, the first byte of the second word will go into the second byte of the first word and so on, in the end, we get this.


First byte Second byte %00000000 %11000000 first word %00000101 %01000000 second word %00000110 %01100000 third word %00000000


%00000000


fourth word


$00000542 $17400000 pixels

Not really, the pixels we had before. So, in order to overcome this in an easy way we move 16 pixels each time. This will produce a very fast scroller, but an easy one to code for. If we move 16 pixels, we won’t have to worry about getting misaligned bitplanes, since the 16 pixel clusters will never be broken up, like they were above. a0 and a1 contains the screen address, while a2 points to the character in the font.

add.l #8,a1 put a1 16 pixels ahead of a0


move.l #31,d1 32 lines to scroll move.l #18,d0 19 16 pixel clusters + font part scroll move.w (a1)+,(a0)+ move.w (a1)+,(a0)+ move.w (a1)+,(a0)+ move.w (a1)+,(a0)+ 16 pixels moved dbf d0,scroll keep moving 16 pixel clusters move.l #18,d0 reset loop counter move.w (a2),(a0)+ move.w 2(a2),(a0)+ move.w 4(a2),(a0)+ 16 pixels of the font move.w 6(a2),(a0)+ character moved in add.l #8,a1 increment screen pointer, align with a0 add.l #160,a2 next line of font dbf d1,scroll do another line


This is all just a bunch of move words, and some adds to keep everything aligned. The first move section will move 4 words from a1, which points one 16 bit cluster ahead of a0, to a0. This is repeated 19 times. After this loop, a0 points to the beginning of the last 16 pixel cluster, and a0 points to the beginning of the second line. For the last 16 pixel cluster, we want information from the font, not from the screen. So here we move information from a2 into a0. Instead of post incrementing a2, I use indexes. After the font data is moved onto the screen, I add 8 to a1, so that it will again be 16 pixels ahead of a0. Since a0 was incremented during the font move part, and a1 was not. 160 is added to a2, so that the font pointer will now point to the next line in the font. Repeat for 32 lines.

Now the two most important techniques have been covered, how to know where the character is in the font, and how to scroll. Now we mix and match. In order to synchronize the entire scroller to the VBL, I put a wait VBL trap in the beginning of the main loop. Then I do my stuff, and in the end of the main loop, I check if the space bar is pressed, if it is, just drop out of the loop. If space bar is not pressed, then the main loop will begin again, with a VBL wait, making sure that the main loop is looped through at 50 times a second. You’ll probably be wondering exactly how I determine whether the space bar is pressed.

This little piece will do the trick; cmp.b #$39,$fffc02. Uh, says you, looking at the ASCII table (hopefully) and wondering how $39 can be space, when it should be $20. The $fffc02 part can be easily guessed, this is probably where the last key press end up, but why $39? ASCII deals with characters, and special characters like line feed and so. There’s also something called scan codes. Every key on the keyboard has its value, it’s scan code so you’ll be able to determine what key was pressed. Look at the Scancodes.gif file included.

While we’re still on the topic, I might as well give you the full detail. You can also check when a key is released, not just pressed. When the key is released, the high bit of $fffc02 is set, meaning you get a whole different value. Consider this.


           %00111001                $39            space pressed
           %10111001                $b9            space released

So, if you cmp.b #$b9, then you check if space is released. This can be used in many fun ways, like changing the background to red when space is pressed, then checking to see when space is released and then restore background. Or accelerate a car in a car game until the button is released, at which time you begin deceleration. I don’t know how often this is updated or how fast you can really press keys. Say for example that you check $fffc02 every VBL to see what key is pressed and released, suppose this dude is like Flash, and manage to press a button, then release it and press another within 1/50 of a second, then you’d loose the check for the release of the key, but I doubt you’ll have to worry about this. Back to reality, here’s the scroller.


jsr initialise movem.l font+2,d0-d7 movem.l d0-d7,$ff8240

move.w #2,-(a7) get physbase trap #14 addq.l #2,a7 move.l d0,screen store screen memory main move.w #37,-(sp) wait vbl trap #14 addq.l #2,sp cmp #0,font_counter check if new character in message bne has_character if not, skip get new character


move.w #2,font_counter reset font_counter

  • we need to point to a new characetr in the font

move.l message_pointer,a0 pointer into the message clr.l d0 clear, just to be sure move.b (a0),d0 put letter ascii value in d0


cmp #0,d0 end of message? bne not_end if not, branch


move.l #message,message_pointer reset message_pointer move.l message_pointer,a0 clr.l d0 clear, just to be sure move.b (a0),d0 put letter ascii value in d0 not_end

  • now we have a character in d0 for sure

add.l #1,message_pointer point to next character add.b #-$20,d0 align ascii with font number divu #10,d0 10 letters per row move.w d0,d1 d1 contains y value swap d0 move.w d0,d2 d2 contains x value mulu #16,d2 16 bytes for each letter mulu #32,d1 32 lines per row mulu #160,d1 160 bytes per row


move.l #font+34,a0 put font screen start in a0 add.l d2,d1 add x and y value together add.l d1,a0 a0 points to correct letter

move.l a0,font_address store calculated pointer

has_character add.w #-1,font_counter move.l screen,a0 move.l screen,a1 move.l font_address,a2 add.l #8,a1 put a1 16 pixels ahead of a0


move.l #31,d1 32 lines to scroll move.l #18,d0 19 16 pixel clusters + font part scroll move.w (a1)+,(a0)+ move.w (a1)+,(a0)+ move.w (a1)+,(a0)+ move.w (a1)+,(a0)+ 16 pixels moved dbf d0,scroll keep moving 16 pixel clusters move.l #18,d0 reset loop counter


move.w (a2),(a0)+ move.w 2(a2),(a0)+ move.w 4(a2),(a0)+ 16 pixels of the font move.w 6(a2),(a0)+ character moved in add.l #8,a1 increment screen pointer, align with a0 add.l #160,a2 next line of font dbf d1,scroll do another line


add.l #8,font_address move 16 pixels forward in font cmp.b #$39,$fffc02 space pressed? bne main if not, repeat main

jsr restore clr.l -(a7) trap #1

include initlib.s


section data font incbin font.pi1 screen dc.l 0 font_address dc.l 0 font_counter dc.w 0 message dc.b "A COOL SCROLLER! BUT A BIT FAST," dc.b " SCROLLING 16 PIXELS EACH VBL." dc.b " THAT'S 2.5 SCREENS EACH SECOND!" dc.b " ",0 message_pointer dc.l message


There are really only two more small things that are new; the font counter and the message pointer. Also take not how I put in the scrolling message, by just using lots of dc.b, and a ‘0’ as an end control character. By changing the text here, you can obviously change the scroller message. Perhaps to the well known “Hello World!”, which I most deliberately avoided.

So what’s the font counter and message address? Well, the font counter keeps track of when it’s time to calculate a new address for a new character. This is set to 2, because every second loop, a whole character has been moved to the screen and the address for the next character in the message will have to be calculated. Had we scrolled 8 pixels each VBL, the font counter would have been set to 4 instead.

The message pointer is an index into the message. In order to know which character to get next time, we must have some pointer into the message. The message pointer begins by pointing to the message, which is good, since that’s where the first character is. The first time through the main loop, the font counter will announce that an address for a new character will have to be calculated. That address is calculated and stored. The message pointer will then point to the next character in the scroller message, which is space, and so on. When the whole message has been scrolled through, the value 0 (not character ‘0’) will be moved from the message. This is a signal that the message is at an end, and the message pointer will be reset, once again pointing to the start of the message. I hope it’s understandable, the one tricky part is all the tests and branches, just walk through them a couple of times, slowly. You can use pen and paper for this, or the MonST.

We’ve begun to get somewhere. If you’ve paid attention so far, you’ll have acquired quite some programming skills. There are still some basic things to cover, in order to be really self sufficient (mainly timers, double buffering, sprites and bit manipulation) but you’re on a good way. Now might be the time to look at alternative sources and learn something from there. For example, you could begin to look at James Ingram’s demo coding tutorials. That was where I began, I found them quite hard but now we’ve gotten more or less to the level where he begins his stuff. That is that from me right now. Upon request from MOdmate of Checkpoint, the next tutorial will probably be on timers, I think. This means we’ll be able to remove the bottom and top borders, cool stuff!


Warrior Munk of poSTmortem, 2002-04-27

“My grandfather taught, me the energy of life goes in a circle, in a perfect flow; balanced. He said, until I find my centre, my circle will never be whole”

- The One

Last edited 2002-06-14