See also: part 1, part 2, part 3, part 4, part 5, part 6, part 7 and more (coming “soon”).
Before I get started today, I wanted to share a comment about part 2 left by Paul Fiscarelli on the Sub-Etha Software Facebook page:
Allen – one minor optimization in your assembly routine. You can remove line 130 CMPA #0. The zero flag will be set if your call to POLCAT [$A000] returns a no-keypress in the A-register, so the CMPA is redundant.
Paul Fiscarelli
Awesome! Thanks, Paul! It can be like this:
ORG $3F00
START LDX #1024
LOOP JSR [$A002]
CMPA #0 *REDUNDANT
BEQ LOOP
STA ,X+
CMPX #1536
BNE LOOP
BRA START
And now back to the article…
After a few digressions, today I will finally get back to the original purpose of this article: seeing what is the fastest way to read they keyboard in Color BASIC. Specifically, reading things like arrow keys that repeat when you hold them down. This is useful for game programs where you probably want the most speed.
We start with some code from Jim McClellan that enables INKEY$ to keep reporting an arrow key as long as it is held down. Normally, INKEY$ reports one key then won’t give another until a new key is pressed (or the same key is released then re-pressed).
0 REM keyread.bas
10 CLS
20 POKE 341,255:POKE 342,255:POKE 343,255:POKE 344,255
30 I$=INKEY$:IF I$="" THEN GOTO 20
40 PRINT ASC(I$)
50 GOTO 20
The POKEs in line 20 do something that allows INKEY$ to keep reading the four arrow keys. Parsing four POKE statements every time through a loop is time consuming, so I will present a few alternatives.
It’s benchmark time!
First, a quick-and-dirty benchmark. This will reset the BASIC timer, then do those four pokes 1000 times and print the value of TIMER:
0 REM keybench.bas
10 TIMER=0
20 FORA=1TO1000
30 POKE341,255:POKE342,255:POKE343,255:POKE344,255
40 NEXT
50 PRINTTIMER
I removed unneeded spaces in line 30, and when I run this in Xroar using Color BASIC 1.1, it prints 1812.
The first optimization I did was change decimal values to HEX values:
10 TIMER=0
20 FORA=1TO1000
30 POKE&H155,&HFF:POKE&H156,&HFF:POKE&H157,&HFF:POKE&H158,&HFF
40 NEXT
50 PRINTTIMER
By changing the decimal values (341, 342, 343, 345 and 255) into HEX values, the result prints 862. This is over twice as fast! Nice.
I was curious if parsing four values was faster or slower than doing all four inside a FOR/NEXT loop, so I tried that:
10 TIMER=0
20 FORA=1TO1000
30 FORZ=&H155 TO&H158:POKEZ,&HFF:NEXT
40 NEXT
50 PRINTTIMER
And the space in the FOR command is required when typing it in by hand because the tokenizer doesn’t know when the HEX value ends and the next keyword, TO, begins. This method uses more memory since it needs an extra variable and some overhead for the FOR loop.
It also turns out to be slower. This one shows me 1148. Okay, so it’s faster to brute force through four POKEs than put them in a loop, but I expect at some point the loop is faster. (i.e., maybe it’s faster to FOR/NEXT 100 POKEs than do 100 separate POKEs… Or maybe not. Maybe some day I’ll try. But I digress…)
In my benchmarking BASIC series, I shared how using a variable can be faster than constant values. It can be much quicker to look up a variable value than parse characters and turn that into a value. I tried this:
1 V=341:W=342:X=343:Y=344:Z=255
10 TIMER=0
20 FORA=1TO1000
30 POKEV,Z:POKEW,Z:POKEX,Z:POKEY,Z
40 NEXT
50 PRINTTIMER
This uses even more memory than the FOR loop since it now takes five extra variables, but the payoff may be worth it. It prints 653! That is a third the time the original decimal version took.
However, the more variables a program uses, the longer it takes to look up variables further at the end of the variable table. You could always do this with V, W, X, Y, Z being the first variables in the list, assuming you’d look them up every time through the main program loop, but if you have other variables that need to be looked up more often, you might want those first, slowing down these… Does that make sense?
Thus, “your mileage may vary.” You can declare variables in the order they should be on the variable stack, with the ones you look up the most at the front of the list, and the ones you rarely use at the end:
DIM V,W,X,Y,Z,A
Looking at the previous example, I notice that Z (the value 255) is used four times on that line. I wonder what happens if I declare it first? I’ll just define it manually at the start of line 1:
1 Z=255:V=341:W=342:X=343:Y=344
10 TIMER=0
20 FORA=1TO1000
30 POKEV,Z:POKEW,Z:POKEX,Z:POKEY,Z
40 NEXT
50 PRINTTIMER
With this, the four lookups for Z should be faster. Indeed, this prints 632! Yep, changing the position of that one Z variable sped it up ever so slightly.
Does that matter? In a game, every few moments you can save in the main loop speeds the game up. Maybe it might.
My vote would be to start with the HEX version, and once the game is written, start playing with variable order and see if moving the POKE values into variables will help.
But is this the fastest was to read repeating keys in Color BASIC? Is there another way to do it that will work with all variations of Color BASIC?
Comment if you know a faster way I should look at.
Until next time…