# Color BASIC Attract Screen – part 4

See also: part 1, part 2, part 3, part 4, unrelated, and part 5.

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
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
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
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!

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…

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``````

## 9 thoughts on “Color BASIC Attract Screen – part 4”

1. Jason Pittman

If those four corners bother you, then my attempt will really kick in that OCD when you notice how wonky the colors are moving…

10 CLS0:C=143:PRINT@268,”ATTRACT!”;
20 FOR ZZ=0TO1STEP0:FORX=0TO15:POKEX+1024,C:POKEX+1040,C:POKE1535-X,C:POKE1519-X,C:POKE1055+(32X),C:POKE1472-(32X),C:GOSUB50:NEXT:GOSUB50:NEXT
50 C=C+16:IF C>255 THEN C=143
60 RETURN

Also, another option using the substrings might be to fill the sides by printing two-character strings on the 32nd column so that a character spills over to the first column of the next line:

10 CLS 0:C=143:OF=1:CH\$=””
20 FOR X=0TO40:CH\$=CH\$+CHR\$(C):GOSUB 90:NEXT
30 FOR ST=0TO1STEP0
40 PRINT@0,MID\$(CH\$,OF,31):GOSUB 120
50 FORX=31TO480STEP32:PRINT@X,MID\$(CH\$,OF,2);:GOSUB 120:NEXT
60 PRINT@481,MID\$(CH\$,OF,30);:GOSUB120
70 NEXT
90 C=C+16:IF C>255 THEN C=143
100 RETURN
120 OF=OF+2:IF OF>7 THEN OF=OF-8
130 RETURN

1. Jason Pittman

Meh…when posting the comment, I guess it does some formatting, but in the first example on line 20, the two instances of “(32X)” should be “(32 times X)”. I think it decided that those two asterisks were meant to italicize the text between them. And, it cutesied the double quotes into directional quotes.

2. Allen Huffman Post author

I thought about the “print two characters” at the right edge, but couldn’t work out a way to make the colors rotate in the proper direction. I suppose pre rendering a huge string could get all the combinations, though.

1. Jason Pittman

I noticed this too. I was thinking that I might try it with two different strings, the first one being in order for the top and bottom, and the second might be something like 15263748 for the sides.

2. Jason Pittman

One more try at O.C.D-compliant “fast”:

10 DIM CL(24):FORX=0TO7:CL(X)=143+(X*16):CL(X+8)=CL(X):CL(X+16)=CL(X):NEXT