... the saga continues ...
Having watched this colourful flock of birds for some time now, we get a little more adventurous. There's that little animation on page 64 of the User's manual, which sends a ball on diagonal paths around the screen to bounce off the border. However, it looks quite flickery, something which ML could surely improve upon (I set the border colour to blue instead of white, and corrected the typo in line 140):
Code: Select all
10 PRINT "{CLR}"
20 POKE 36879, 14
30 POKE 36878, 15
40 X = 1
50 Y = 1
60 DX = 1
70 DY = 1
80 POKE 7680 + X + 22*Y, 81
90 FOR T=1 TO 10: NEXT
100 POKE 7680 + X + 22*Y, 32
110 X = X + DX
120 IF X=0 OR X=21 THEN DX=-DX: POKE 36876, 220
130 Y = Y + DY
140 IF Y=0 OR Y=22 THEN DY=-DY: POKE 36876, 230
150 POKE 36876,0
160 GOTO 80
Lines 80 and 100 don't
look especially promising though. Our little, small 6502 in the VIC-20 isn't all that good at multiplying numbers.
Adding
up 16-bit numbers isn't that hard though - so we build a
table that contains the addresses of all screen lines. Separated into low- and high-bytes! Starting
up VICMON, the program again goes to $1800, the low-bytes of the screen line addresses go to $1600 and the high-bytes go to $1700. That means we can use a single value of the X register to index into *both* parts of the table!
We obtain the next entry in the table by adding 22 = $0016 to the previous value. As soon X reaches 22, there have 23 entries (0..22) been written, the table then is complete. First, a check whether the table is built as wanted - the BRK re-enters VICMON:
That looks good! Combining the low- and high-bytes of both table parts we get $1E00, $1E16, $1E2C, ...
up to $1FE4. Let's check: 7680 + 22*22 = 8164 = $1FE4. Exactly what was expected!
That table being prepared, we can start with the actual translation of the original BASIC program. We continue at $1822, overwriting the BRK instruction there. Now, what's the PETSCII code of 'clear screen'? Easy. We once ask again the H command and find $93 in the pattern buffer ($22 is the double quote). The POKEs in the lines 20 and 30 are as easy as pie. And the variables X, Y, DX and DY go into the zeropage. $F7..$FA are normally used for RS232 operations, but we don't do any of that. So $F7..$FA are free for our purpose:
Of course there's no need to reload A with 1 for each store. We're quite lucky here.
Remember address
$183B. That is the start address of the main loop!
The index registers now have their roles reversed somewhat: The X register is loaded from $F8 (which is defined to hold the
Y co-ordinate!) and indexes into the line address table. The instructions at $183D..$1845 place the line address into a pointer at $FB, and use the (indirect),Y address mode to index into the line. As a consequence, Y is loaded from $F7, and holds the value of the
X co-ordinate. Still not confused?
LDA #$51 and
STA ($FB),Y truly put the ball into place.
In line 90, the BASIC program now calls for a FOR loop to keep the ball at that place for a short time. No use to do this the same way in machine code. This would be way too fast.
Instead, we call the VIC chip for help. One of its registers contains the number of the current screen line, and the instructions at $184F..$1855 loop until the first raster lines in the top border are drawn on screen.
Plenty of time before the screen window is scanned by the electron beam of the CRT to do all the following actions, and finally place the ball at the next position.
First, the 'old' ball is erased. We also move the "sound off" POKE 36876,0 from the end of the loop to here, so any sound blip is at least heard 1/60 or 1/50 of a second. The instructions at $185F..$1864 correspond to line 110 (X=X+DX). The next line of the BASIC program uses IF to check, whether the ball has touched the left or right border, and
we need to resolve the forward references of two branch instructions (the branch 'targets' $1866 and $186A are just
placeholders!):
After $1864, no need for a CMP #$00. We've just found out, where the BEQ has to go into the "THEN" clause: at $186C!
There we go.
The THEN clause inverts the sign of DX. That's actually the same as if we'd subtract the original value from zero, and that's how we do it: DX = -DX (= 0 - DX!). Let's not forget the POKE into 36876:
STA $900C is the last instruction of the THEN clause. When the IF condition of line 110 isn't true, the branch at $186A should continue at $1878. So, we go a few lines
up and correct also that BNE:
Lines 130 and 140, which handle the Y co-ordinate, are translated quite the same. Here's how it looks after those two branches also have been corrected:
Line 150 (the "sound off" POKE) has already been handled.
Instead of just reentering the main loop at
$183B (you remember?) we check for the STOP key, with JSR $FFE1. When STOP is pressed, the BRK instruction at $1896 returns us to VICMON. Before you start the program with G 1800, save the hard work:
And there it is!
Blip - blop - blip - blop - ...
... actually, the program as shown has a small bug, which however only manifests on NTSC. Can you spot it? We'll take a
look into this, and fix that bug.
