Color BASIC Attract Screen – part 4

See also: part 1 and part 2.

Beyond removing some spaces and a REM statement, here is the smallest I have been able to get my “attract” program:

10 ' ATTRACT4.BAS
20 FOR I=0 TO 3:READ L(I),LD(I),CL(I),CD(I):NEXT:Z=143:CLS 0:PRINT @268,"ATTRACT!";
30 Z=Z+16:IF Z>255 THEN Z=143
40 FOR I=0 TO 3:POKE L(I),Z:L(I)=L(I)+LD(I):FOR C=0 TO 3:IF L(I)=CL(C) THEN LD(I)=CD(C)
50 NEXT:NEXT:GOTO 30
60 DATA 1024,1,1024,1,1047,1,1055,32,1535,-1,1535,-1,1512,-1,1504,-32

(We could reduce it by one line by sticking the DATA statement on the end of line 50, now that I look at it.)

Let’s rewind and look at the original, which used individual variables for each of the moving color blocks:

10 ' ATTRACT.BAS
20 A=1024:B=A+23:C=1535:D=C-23:Z=143
30 AD=1:BD=1:CD=-1:DD=-1
40 CLS 0:PRINT @268,"ATTRACT!";
50 POKE A,Z:POKE B,Z:POKE C,Z:POKE D,Z
60 Z=Z+16:IF Z>255 THEN Z=143
70 A=A+AD
80 IF A=1055 THEN AD=32
90 IF A=1535 THEN AD=-1
100 IF A=1504 THEN AD=-32
110 IF A=1024 THEN AD=1
120 ' 
130 B=B+BD
140 IF B=1055 THEN BD=32
150 IF B=1535 THEN BD=-1
160 IF B=1504 THEN BD=-32
170 IF B=1024 THEN BD=1
180 ' 
190 C=C+CD
200 IF C=1055 THEN CD=32
210 IF C=1535 THEN CD=-1
220 IF C=1504 THEN CD=-32
230 IF C=1024 THEN CD=1
240 ' 
250 D=D+DD
260 IF D=1055 THEN DD=32
270 IF D=1535 THEN DD=-1
280 IF D=1504 THEN DD=-32
290 IF D=1024 THEN DD=1
300 GOTO 50

This was then converted to us an array:

10 ' ATTRACT2.BAS
20 L(0)=1024:L(1)=1024+23:L(2)=1535:L(3)=1535-23
30 Z=143
40 CL(0)=1024:CD(0)=1
50 CL(1)=1055:CD(1)=32
60 CL(2)=1535:CD(2)=-1
70 CL(3)=1504:CD(3)=-32
80 CLS 0:PRINT @268,"ATTRACT!";
90 LD(0)=1:LD(1)=1:LD(2)=-1:LD(3)=-1
100 FOR I=0 TO 3:POKE L(I),Z:NEXT
110 Z=Z+16:IF Z>255 THEN Z=143
120 FOR I=0 TO 3:L(I)=L(I)+LD(I):NEXT
130 FOR L=0 TO 3
140 FOR C=0 TO 3
150 IF L(L)=CL(C) THEN LD(L)=CD(C)
160 NEXT
170 NEXT
180 GOTO 100

And then it was converted to use READ/DATA instead of hard-coding values:

10 ' ATTRACT3.BAS
20 FOR I=0 TO 3
30 READ L(I),LD(I),CL(I),CD(I)
40 NEXT
50 Z=143
60 CLS 0:PRINT @268,"ATTRACT!";
70 Z=Z+16:IF Z>255 THEN Z=143
80 FOR I=0 TO 3
90 POKE L(I),Z
100 L(I)=L(I)+LD(I)
110 FOR C=0 TO 3
120 IF L(I)=CL(C) THEN LD(I)=CD(C)
130 NEXT
140 NEXT
150 GOTO 70
160 ' L,LD,CL,CD
170 DATA 1024,1,1024,1
180 DATA 1047,1,1055,32
190 DATA 1535,-1,1535,-1
200 DATA 1512,-1,1504,-32

Shuffling code around is fun.

But it’s still really slow.

10 PRINT “FASTER”

There are other ways to do similar effects, such as with strings. We could make a string that contained a repeating series of the color block characters, like this:

FOR I=0 TO 7:B$=B$+CHR$(143+16*I):NEXT

Then we could duplicate that 8-character string a few times until we had a string that was twice the length of the 32 column screen:

B$=B$+B$+B$+B$+B$+B$+B$+B$

Then we could make the entire thing move by printing the MID$ of it, like this:

FOR I=1 TO 32
PRINT@0,MID$(B$,33-I,32);
PRINT@480,MID$(B$,I,31);
NEXT

We print one section @0 for the top line, and the other @480 for the bottom line. Unfortunately, using PRINT instead of POKE means if we ever print on the bottom right location, the screen would scroll, so the bottom right block has to be left un-printed (thus, printing 31 characters for the bottom line instead of the full 32). This bothers me so apparently I do have O.C.D. Maybe we can fix that later.

But, it gives the advantage of scrolling ALL the blocks, and is super fast. Check it out:

10 ' ATTRACT5.BAS
20 CLS 0:PRINT @268,"ATTRACT!";
30 FOR I=0 TO 7:B$=B$+CHR$(143+16*I):NEXT
40 B$=B$+B$+B$+B$+B$+B$+B$+B$
50 FOR I=1 TO 32
60 PRINT@0,MID$(B$,33-I,32);
70 PRINT@480,MID$(B$,I,31);
80 NEXT:GOTO 50

That’s not bad, but only gives the top and bottom rows (minus that bottom right location). But, it’s fast!

ATTRACT5.BAS

Since the orders of the colors is the same on the top and bottom, we’d really need to reverse the bottom characters to make it look like it’s rotating versus just reversing. Let’s tweak that:

10 ' ATTRACT6.BAS
20 CLS 0:PRINT @268,"ATTRACT!";
30 FOR I=0 TO 7:B$=B$+CHR$(143+16*I)
35 R$=R$+CHR$(255-16*I):NEXT
40 B$=B$+B$+B$+B$+B$+B$+B$+B$
45 R$=R$+R$+R$+R$+R$+R$+R$+R$
50 FOR I=1 TO 32
60 PRINT@0,MID$(B$,33-I,32);
70 PRINT@480,MID$(R$,I,31);
80 NEXT:GOTO 50

That’s a bit better. But getting the sides to work is a bit more work and it will slow things down quite a bit. But let’s try anyway.

Initially, I tried scanning down the sides of the string using MID$, like this:

FOR J=1 TO 14
PRINT@480-32*J,MID$(R$,39-J+I,1);
PRINT@31+32*J,MID$(R$,33-J+I,1);
NEXT

But that was very, very slow. You could see it “paint” the sides. Each time you use MID$, a new string is created (with data copied from the first string). That’s a bunch of memory shuffling just for one character.

Then I thought, since I can’t get the speed up from a horizontal string being PRINTed, it was probably faster to just use CHR$().

I tried that, and it was still too slow.

Benchmark Digression: POKE vs PRINT

This led me back to an earlier benchmark discussion… Since I cannot get any benefit of using PRINT for a vertical column of characters, I could switch to the faster POKE method. This would also allow me to fill that bottom right character block. My O.C.D. approves.

To prove this to myself, again, I did two quick benchmarks — one using PRINT@ and the other using POKE.

0 ' LRBENCH1.BAS
1 ' 4745
10 C=143+16
20 TIMER=0:FOR A=1 TO 1000
30 FOR P=1024 TO 1535 STEP 32
40 POKEP,C
50 NEXT
60 NEXT:PRINT TIMER

0 ' LRBENCH2.BAS
1 ' 6013
10 C=143+16
20 TIMER=0:FOR A=1 TO 1000
30 FOR P=0 TO 511 STEP 32
40 PRINT@P,CHR$(C);
50 NEXT
60 NEXT:PRINT TIMER

Line 1 has the time that it printed for me in the Xroar emulator.

POKE will be the way.

However, there is still a problem: Math.

It just doesn’t add up…

The CoCo screen is 32×16. There are 8 colors. That means those 8 colors can repeat four times along the top of the screen, and four times along the bottom, leaving only 14 on each side going vertical. 32+32+14+14 is 92, which is not evenly divisible by our 8 colors. If we represent them as numbers, they would look like this:

If you start at the top left corner and go across, repeating 12345678 over and over, you end up back at the top left on 4. We have three colors that won’t fit. This means even if I had a nice fast routine for rotating the colors, they would not be evenly balanced using this format.

However…

…if I leave out the four corners, we get 88, and that divides just fine by our 8 colors!

Thus, the actual O.C.D.-compliant border I want to go for would look like this:

The only problem is … how can this be done fast in BASIC?

To be continued…

Bonus: Show Your Work

Here are the stupid BASIC programs I wrote to make the previous four screens:

0 ' border1.bas
10 CLS:C=113:L=1024
20 ' RIGHT
30 L=1024:D=1:T=31:GOSUB 110
40 ' DOWN
50 L=1087:D=32:T=13:GOSUB 110
60 ' LEFT
70 L=1535:D=-1:T=31:GOSUB 110
80 ' UP
90 L=1472:D=-32:T=13:GOSUB 110
100 GOTO 100
110 ' L=LOC, D=DELTA, T=TIMES
120 POKE L,C
130 C=C+1:IF C>120 THEN C=113
140 IF T=0 THEN RETURN
150 L=L+D:IF L>1023 THEN IF L<1536 THEN 170
160 L=L-D:SOUND 200,1
170 T=T-1:GOTO 120
0 ' border2.bas
10 CLS:C=113:L=1024
20 ' RIGHT
30 L=1025:D=1:T=29:GOSUB 110
40 ' DOWN
50 L=1087:D=32:T=13:GOSUB 110
60 ' LEFT
70 L=1534:D=-1:T=29:GOSUB 110
80 ' UP
90 L=1472:D=-32:T=13:GOSUB 110
100 GOTO 100
110 ' L=LOC, D=DELTA, T=TIMES
120 POKE L,C
130 C=C+1:IF C>120 THEN C=113
140 IF T=0 THEN RETURN
150 L=L+D:IF L>1023 THEN IF L<1536 THEN 170
160 L=L-D:SOUND 200,1
170 T=T-1:GOTO 120
0 ' border3.bas
10 CLS 0:C=143:L=1024
20 ' RIGHT
30 L=1025:D=1:T=29:GOSUB 110
40 ' DOWN
50 L=1087:D=32:T=13:GOSUB 110
60 ' LEFT
70 L=1534:D=-1:T=29:GOSUB 110
80 ' UP
90 L=1472:D=-32:T=13:GOSUB 110
100 GOTO 100
110 ' L=LOC, D=DELTA, T=TIMES
120 POKE L,C
130 C=C+16:IF C>255 THEN C=143
140 IF T=0 THEN RETURN
150 L=L+D:IF L>1023 THEN IF L<1536 THEN 170
160 L=L-D:SOUND 200,1
170 T=T-1:GOTO 120

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.