This article is the third of a serie of five about how to code a one-pixel sine scroll on Amiga, an effect commonly used by coders of demos and other cracktros. For example, in this cracktro by Supplex:
In the first article, we learned how to install a development environment on an Amiga emulated with WinUAE, and how to code a basic Copper list to display something on the screen. In the second article, we learned how to set up a 16×16 font to display the columns of pixels of its characters, and to use triple buffering to display the pictures on the screen without any flickering.
In this third article, we shall go to the heart of the matter by learning how to draw and animate the sine scroll, first with the CPU, then with the Blitter.
Click here to download the archive of the source and data of the program hereby explained.
If you’re using Notepad++, click here to download and enhanced version of the UDL 68K Assembly (v3).
NB : This article may be best read while listening to the great module composed by Nuke / Anarchy for the diskmag part of Stolen Data #7, but this is just a matter of personal taste…
Cliquez ici pour lire cet article en français.
Scroll and animate the sine scroll
The main loop may now be described. It accomplishes the following tasks:
- wait for the electron beam to finish drawing the picture;
- roll the three bitplanes to display the newly drawn picture;
- wait for the Blitter and tell it to erase bitplane C that contains the penultimage picture;
- draw the text in bitplane B that contains the antepenultimate picture;
- animate the index of the first column of the first character of text to draw;
- animate the sine of this first column;
- test if the left mouse button is pressed.
The first three tasks have already been described. Let’s describe the others.
The sine scroll is drawn by a loop that draws SCROLL_DX columns of consecutive characters, starting at column SCROLL_X in the bitplane. The index of the first column to draw and the index of first character it belongs to are stored in the variables scrollColumn and scrollChar, respectively. The offset of the sinus of the first column of the sine scroll is stored in the variable angle.
Let’s animate the sine scroll in the main loop.
Having the text moving along a sine curve would be of no interest if this curve were not to be animated: it would look like as if the text was moving along a roller coaster. For this reason, we decrement the offset of the sine of the first column of the sine scroll on each frame, and doing this, we watch for the underflow:
move.w angle,d0 sub.w #(SINE_SPEED_FRAME<<1),d0 bge _angleFrameNoLoop add.w #(360<<1),d0 _angleFrameNoLoop: move.w d0,angle
Moreover, the text must scroll from the left to the right. To this end, we must increment the index of the first column in the text by SCROLL_SPEED. We must then watch for two overflows: when the last column of a character is reached, we must jump to the first column of the next character; when the last character of the text is reached, we must jump to the first character of the text:
move.w scrollColumn,d0 addq.w #SCROLL_SPEED,d0 cmp.b #15,d0 ;Is the next column after the last column of the current character? ble _scrollNextColumn ;If not, keep this column sub.b #15,d0 ;If yes, change for a column in the next character... move.w scrollChar,d1 addq.w #1,d1 ;...and move to the next character lea text,a0 move.b (a0,d1.w),d2 bne _scrollNextChar ;...and check if this next character lies beyond the end of the text clr.w d1 ;...and if yes, loop on the first character of the text _scrollNextChar: move.w d1,scrollChar _scrollNextColumn: move.w d0,scrollColumn
We may now draw the sine scroll.
Drawing the sine scroll is accomplished by the latter loop that lies in the main loop. Before we code this main loop, we must initialize a number of things to maximize the usage of the CPU registers. This way, we avoid reading data from the memory, which saves CPU time cycles.
First, we compute the offset (D6) of the word in the bitplane where the bit that matches the first column to be drawn lies, and we remember this bit (D7):
;Compute the offset of the word in the bitplane where lies the first column to be drawn moveq #SCROLL_X,d6 lsr.w #3,d6 ;Offset of the byte where the column lies bclr #0,d6 ;Offset of the word where the column lies (same thing as lsr.w #4 then lsl.w #1) ;Compute the bit in this word that matches this column moveq #SCROLL_X,d4 and.w #$000F,d4 moveq #15,d7 sub.b d4,d7 ;Bit dans le mot
Next, we compute the address (A0) of the next character and the address (A1) of the word in the 16x16 font that contains its current column (D4). This column has to be drawn in the current bitplane column that was just computed:
move.w scrollChar,d0 lea text,a0 lea (a0,d0.w),a0 move.w scrollPixel,d4 clr.w d1 move.b (a0)+,d1 subi.b #$20,d1 lsl.w #5,d1 ;32 bytes per character in the 16x16 font move.w d4,d2 ;Column of the character to be drawn lsl.w #1,d2 ;2 bytes per line in the 16x16 font add.w d2,d1 move.l font16,a1 lea (a1,d1.w),a1 ;Address of the column to be drawn
Note that in the previous code, the offset of the first column of a character is computed by substracting $20 to the ASCII code of this character - the characters in the 8x8 font are sorted in ascending ASCII order, which makes this it possible.
Next, we initialize various registers that are intensively used in the loop, starting with the offset of the sine for the current column (D0) and the number of the columns we still have to draw (D1):
move.w angle,d0 move.w #SCROLL_DX-1,d1 move.l bitplaneB,a2