Awhile back I ported 8-Bit Show and Tell‘s “10 PRINT RACER” from Commodore PET to CoCo. I tried to make it a literal port, keeping the code as close as I could to the original. I did, however, mention a few things that could make it faster, taking advantage of things like Extended Color BASIC’s hex values (&H2 is faster to parse than 2, for instance).
The other day, MiaM left a comment on the original article:
It might be faster to use A=ASC(INKEY$) and IF A=4 instead of IF A$=CHR$(4)
– MiaM
Intriguing. The original Commodore version, the direction was read by using GET A$, and I simply converted that over to A$=INKEY$ for Color BASIC. Here is a look at Robin’s Commodore PET original:
1 REM 10 PRINT RACER: 8-BIT SHOW & TELL 5 R$="":PRINT"{CLR}INIT:";:FORX=1TO75:M$=CHR$(205.5+RND(.)):R$=R$+M$:PRINTM$;:NEXT 10 PRINT"{CLR}":C=20:R=13:W=15:D=0:S=32768 20 L=0:FORZ=0TO1STEP0:X=RND(.)*10 30 IFX<4THENR=R-1:IFR<1THENR=1 40 IFX>6THENR=R+1:IFR+W>37THENR=37-W 50 RN=RND(.)*35+1:PRINTMID$(R$,RN,R);SPC(W);MID$(R$,RN,39-R-W) 60 D=D+1:L=L+1:IFL>49THENL=0:W=W-1:IFW<3THENW=3 70 IFD<25THENNEXT 75 GETA$:IFA$="4"THENC=C-1 80 IFA$="6"THENC=C+1 90 P=PEEK(S+C):IFP<>32THEN200 100 POKES+C,42:NEXT 200 PRINTSPC(17)"CRASH!":IFD>HTHENH=D 205 PRINT,"SCORE:"D" HIGH:"H 210 FORX=1TO2000:NEXT:POKE158,0 220 GETA$:IFA$=""THEN220 230 GOTO10
And here is my Color BASIC conversion:
0 ' 10 PRINT RACER 1 ' BY WWW.8BITSHOWANDTELL.COM 2 ' 3 ' PORTED FROM PET TO COCO 4 ' BY SUBETHASOFTWARE.COM 5 R$="":CLS:PRINT"INIT:";:FORX=1TO75:M$=CHR$(47+45*(RND(2)-1)):R$=R$+M$:PRINTM$;:NEXT 6 S$=STRING$(32," ") 10 CLS:C=16:R=10:W=12:D=0:S=1024 20 L=0:FORZ=0TO1STEP0:X=RND(.)*10 30 IFX<4THENR=R-1:IFR<1THENR=1 40 IFX>5THENR=R+1:IFR+W>29THENR=29-W 50 RN=RND(.)*28+1:PRINTMID$(R$,RN,R);MID$(S$,1,W);MID$(R$,RN,31-R-W) 60 D=D+1:L=L+1:IFL>49THENL=0:W=W-1:IFW<3THENW=3 70 IFD<16THENNEXT 75 A$=INKEY$:IFA$=CHR$(8)THENC=C-1 80 IFA$=CHR$(9)THENC=C+1 90 P=PEEK(S+C):IFP<>96THEN200 100 POKES+C,106:NEXT 200 PRINTTAB(13)"CRASH!":IFD>H THENH=D 205 PRINTTAB(6)"SCORE:"D" HIGH:"H 210 FORX=1TO2000:NEXT:A$=INKEY$ 220 A$=INKEY$:IFA$=""THEN220 230 GOTO10
The block of code MiaM refers to is this:
75 GETA$:IFA$="4"THENC=C-1 80 IFA$="6"THENC=C+1 75 A$=INKEY$:IFA$=CHR$(8)THENC=C-1 80 IFA$=CHR$(9)THENC=C+1
On the Commodore PET, without arrow keys, it used “4” and “6” on the numeric keypad for Left and Right. On the CoCo, I changed that to the Left Arrow key and the Right Arrow key.
The Commodore PET has much less work to do looking for A$=”4″ versus A$=CHR$(8) not he CoCo (due to all the parsing). I could have made the CoCo use letter keys like “A” for left and “S” for right to get similar performance.
But what MiaM suggests may be faster. Instead of comparing strings like A$=CHR$(8), the suggestion is to use BASIC’s ASC() keyword to return the numeric value of the character, then compare a numeric value rather than a string compare.
Which is faster? A one character string compare, or ASC() and a number compare?
Let’s find out.
Comparing a String to a String
For this, I dug out my old BENCH.BAS benchmarking code and inserted the first method I wanted to test — the way the Commodore PET did it:
5 DIM TE,TM,B,A,TT 10 FORA=0TO3:TIMER=0:TM=TIMER 20 FORB=0TO1000 30 A$=INKEY$:IF A$="4" THEN REM 70 NEXT 80 TE=TIMER-TM:PRINTA,TE 90 TT=TT+TE:NEXT:PRINTTT/A:END
Comparing A$ to a quoted value in this loop produces 515.
Comparing a String to a CHR$
My conversion changed this to comparing to a CHR$(8) value, like this:
0 REM ascvsstringcompare.BAS 5 DIM TE,TM,B,A,TT 10 FORA=0TO3:TIMER=0:TM=TIMER 20 FORB=0TO1000 30 A$=INKEY$:IF A$="4" THEN REM 30 A$=INKEY$:IF A$=CHR$(8) THEN REM 70 NEXT 80 TE=TIMER-TM:PRINTA,TE 90 TT=TT+TE:NEXT:PRINTTT/A:END
This produces a slower 628. No surprise, due to having to parse CHR$() and the number. I could easily speed up the CoCo port by using quoted characters like “A” for Left and “S” for Right.
But I really wanted to use the arrow keys.
ASC and you shall receive…
The new suggestion is to use ASC. ASC will convert a character to its ASCII value (or PETASCII on a Commodore, I would suppose). For example:
PRINT ASC("A") 65
The cool suggestion was to try using INKEY$ as the parameter inside of ASC(), and skipping the use of a variable entirely. Unfortunately, when I tried it, I received:
?FC ERROR
Function Call error. Because, if no key is pressed, INKEY$ returns nothing, which I suppose would be like trying to do:
PRINT ASC("")
We have been able to use INKEY$ directly in other functions, such as INSTR (looking up a character inside a string), and that works even when passing in “”:
PRINT INSTR("","ABCDE") 0
But ASC() won’t work without a character, at least not in Color BASIC. And, even if we used A$=INKEY$, we can’t pass A$ in to ASC() if it is empty (no key pressed) which means we’d need an extra check like:
30 A$=INKEY$:IF A$<>"" THEN IF ASC(A$)=4 THEN ..
The more parsing, the slower. This produced 539, which isn’t as slow as I expected. It’s slower than doing IF A$=”4″ but faster than IF A$=CHR$(8). Thus, it would be faster in my CoCo port than my original.
This did give me another thing to try. ASC() allows you to pass in a string that contains more than one character, but it only acts upon the first letter. You can do this:
PRINT ASC("ALLEN TRIED THIS") 65
This means I could always pad the return of INKEY$ with another character so it would either be whatever keys he user pressed, or my other character if nothing was pressed. Like this:
30 IF ASC(INKEY$+".")=8 THEN REM
If no key has been pressed, this would try to parse “”+”.”, and give me the ASCII of “.”.
If a key had been pressed, this would parse that character (like “4.” if I pressed a 4).
As I learned when I first stated my benchmarking BASIC series, string manipulation is slow. Very slow. So I expect this to be very slow.
To my surprise, it returns 520! Just a smidge slower than the original IF A$=”4″ string compare! I’m actually quite surprised.
Now, in the actual 10 PRINT RACER game, which is doing lots of string manipulations to generate the game maze, this could end up being much slower if it had to move around other larger strings. But, still worth a shot.
Thank you, MiaM! Neat idea, even if Color BASIC wouldn’t let me do it the cool way you suggested.
Until next time…
Bonus
Numbers verses string compares:
30 IF Z=4 THEN REM
That gives me 350. Even though decimal values are much slower to parse than HEX values, they are still faster than strings.
But, in pure Color BASIC, there is no way to get input from a keypress to a number other than ASC. BUT, you could PEEK some BASIC RAM value that is the key being held down, and do it that way (which is something I have discussed earlier).
Any more ideas?