How much faster is HEX versus decimal on the CoCo?

This is a topic I have mentioned in earlier articles about speeding up BASIC. I recently found one of my test programs and thought I’d update it a bit and share it.

A quick recap…

In BASIC, numbers in the program are stored as the characters. If you have “A=123” that “123” appears in the BASIC code as those characters. When the program RUNs, basic has to convert that series of characters to a number.

It takes more work to parse a standard base-10 (decimal, digits 0-9) set of characters to a number than it does to parse a base-16 (HEX, digits 0-F). To demonstrate, I wrote a very simple parser. It takes a string and converts it to a number, manually. (BASIC already has a VAL keyword that does this, but I wanted to show how it works.) Note this does not look for negative (“-“) or decimals or scientific notation – just a basic number like “12345”.

Decimal String to Number

10 ' STRTONUM.BAS
20 LINE INPUT "TYPE A NUMBER: ";NM$
30 GOSUB 60
40 PRINT "THE VALUE IS :";NM
50 END
60 ' CONVERT NM$ TO NM NUMBER
70 NM=0:ZM=1
80 ' SCAN FROM RIGHT TO LEFT
90 FOR Z=LEN(NM$) TO 1 STEP -1
100 ' GET CHARACTER VALUE
110 ZC=ASC(MID$(NM$,Z,1))
120 ' EXIT IF NOT 0-9
130 IF ZC>=ASC("0") THEN IF ZC<=ASC("9") THEN 160
140 PRINT "NOT A NUMBER : ";NM$
150 RETURN
160 ' SUBTRACT ASCII "0" AND MATH
170 NM=NM+(ZC-ASC("0"))*ZM
175 PRINT ZC-ASC("0"); "*";ZM;"=";NM
180 ' 0, 10, 100, 1000, ETC.
190 ZM=ZM*10
200 NEXT
210 RETURN

If you have a number like “12345”, you start at the right-most ASCII character (“5”) and convert that to a number. You multiply that number by a multiplier that starts out as 1. So, “5 * 1 = 5”.

Then you multiply the multiplier by 10, so 1 becomes 1. You move to the next digit, “4”, and multiply it by the new multiplier. So, “4*10 = 40”. That is added to the previous value, so “5 + 40 = 45”.

The multiplier is multiplied by 10, so 10 becomes 100. The next digit is a “3”, so that becomes “3 * 100 = 300”. It is added to the sum – “300 + 45 = 345”.

Repeating, the multiplier is multiplied by 10 making it 1000. The next digit is a “2”, so that becomes “2 * 1000 = 2000”. That is added making “2000 + 345 = 2345”. I think we see the pattern.

After the multiplier is multiplied by 10 again, making it 10000, we go through this again with the final digit, the “1”. That makes “1 * 10000 = 10000” and that is added making “10000 + 2345 = 12345”.

Whew!

HEX String to Value

But, with a hexadecimal value, the math gets much simpler since each character of a hex string represents a set of four bits in a byte. A similar converter is much smaller.

10 ' STRTONUM.BAS
20 LINE INPUT "TYPE A HEXNUM: ";NM$
30 GOSUB 60
40 PRINT "THE VALUE IS :";NM
50 END
60 ' CONVERT NM$ TO NM NUMBER
70 NM=0
80 ' SCAN FROM LEFT TO RIGHT
90 FOR Z=1 TO LEN(NM$)
100 ' GET CHARACTER VALUE
110 ZC=ASC(MID$(NM$,Z,1))
120 ' EXIT IF NOT 0-9 OR A-F
130 IF ZC>=ASC("0") THEN IF ZC<=ASC("9") THEN 200
140 IF ZC>=ASC("A") THEN IF ZC<=ASC("F") THEN 180
150 PRINT "NOT A HEXNUM : ";NM$
160 RETURN
170 ' CONVERT A-F TO 10-15
180 ZC=ZC-(ASC("A")-(ASC("9")+1))
190 ' NOW MAKE 0-15
200 ZC=ZC-ASC("0")
210 PRINT NM;"+";ZC;"=";
220 NM=NM+ZC
230 PRINT NM
240 IF Z<LEN(NM$) THEN NM=NM*16
250 NEXT
260 RETURN

In BASIC, once it sees some characters starting with “&H”, it gets the value of the next character. If it is A-F, it subtracts from the value so it looks like the ASCII values after “0” to “9”.

Then it subtracts the ASCII of “0” so the value becomes 0-15.

If there is another character, it shifts this value four places to the left (multiply by 16 in the code above) and it moves on to the next digit and repeats.

Bit shifts (multiplying by power of 2) are much quicker than multiplying. It makes the whole parsing routine much faster.

But how much faster?

I wrote this sample program which sits in a loop and sets a variable 10,000 times — first setting using a normal base-10 number (“A=1”), and then again using the HEX version (“A=&H1”). After each one it prints the TIMER value, and at the end of the line it prints how much faster (or slower) the HEX version was over decimal.

And as a bonus, at the end, it compares “A=.” (that is a fast shortcut for “A=0”) to “A=&H0” to show one case where there is something faster than HEX.

10 'DECVSHEX2.BAS
20 DIM A,I,TD,TH:MX=10000

30 PRINT "DO NOT PRESS KEYS WHILE RUNNING"
40 FOR I=1 TO 1000:NEXT I

50 PRINT "DEC TIME HEX TIME DIFF"

60 TIMER=0:FOR I=1 TO MX
70 A=0
80 NEXT:TD=TIMER:GOSUB 920
90 TIMER=0:FOR I=1 TO MX
100 A=&H0
110 NEXT:TH=TIMER:GOSUB 930

120 TIMER=0:FOR I=1 TO MX
130 A=1
140 NEXT:TD=TIMER:GOSUB 920
150 TIMER=0:FOR I=1 TO MX
160 A=&H1
170 NEXT:TH=TIMER:GOSUB 930

180 TIMER=0:FOR I=1 TO MX
190 A=2
200 NEXT:TD=TIMER:GOSUB 920
210 TIMER=0:FOR I=1 TO MX
220 A=&H2
230 NEXT:TH=TIMER:GOSUB 930

240 TIMER=0:FOR I=1 TO MX
250 A=3
260 NEXT:TD=TIMER:GOSUB 920
270 TIMER=0:FOR I=1 TO MX
280 A=&H3
290 NEXT:TH=TIMER:GOSUB 930

300 TIMER=0:FOR I=1 TO MX
310 A=4
320 NEXT:TD=TIMER:GOSUB 920
330 TIMER=0:FOR I=1 TO MX
340 A=&H4
350 NEXT:TH=TIMER:GOSUB 930

360 TIMER=0:FOR I=1 TO MX
370 A=5
380 NEXT:TD=TIMER:GOSUB 920
390 TIMER=0:FOR I=1 TO MX
400 A=&H5
410 NEXT:TH=TIMER:GOSUB 930

420 TIMER=0:FOR I=1 TO MX
430 A=6
440 NEXT:TD=TIMER:GOSUB 920
450 TIMER=0:FOR I=1 TO MX
460 A=&H6
470 NEXT:TH=TIMER:GOSUB 930

480 TIMER=0:FOR I=1 TO MX
490 A=7
500 NEXT:TD=TIMER:GOSUB 920
510 TIMER=0:FOR I=1 TO MX
520 A=&H7
530 NEXT:TH=TIMER:GOSUB 930

540 TIMER=0:FOR I=1 TO MX
550 A=8
560 NEXT:TD=TIMER:GOSUB 920
570 TIMER=0:FOR I=1 TO MX
580 A=&H8
590 NEXT:TH=TIMER:GOSUB 930

600 TIMER=0:FOR I=1 TO MX
610 A=9
620 NEXT:TD=TIMER:GOSUB 920
630 TIMER=0:FOR I=1 TO MX
640 A=&H9
650 NEXT:TH=TIMER:GOSUB 930

660 TIMER=0:FOR I=1 TO MX
670 A=10
680 NEXT:TD=TIMER:GOSUB 920
690 TIMER=0:FOR I=1 TO MX
700 A=&HA
710 NEXT:TH=TIMER:GOSUB 930

720 TIMER=0:FOR I=1 TO MX
730 A=11
740 NEXT:TD=TIMER:GOSUB 920
750 TIMER=0:FOR I=1 TO MX
760 A=&HB
770 NEXT:TH=TIMER:GOSUB 930

780 GOTO 850

790 TIMER=0:FOR I=1 TO MX
800 A=12
810 NEXT:TD=TIMER:GOSUB 920
820 TIMER=0:FOR I=1 TO MX
830 A=&HC
840 NEXT:TH=TIMER:GOSUB 930

850 TIMER=0:FOR I=1 TO MX
860 A=.
870 NEXT:TD=TIMER:PRINT " . ";:PRINT USING"#####";TD;:PRINT" ";
880 TIMER=0:FOR I=1 TO MX
890 A=&H0
900 NEXT:TH=TIMER:GOSUB 930

910 GOTO 910

920 PRINT USING"### ##### ";A;TD;:PRINT" ";:RETURN

930 PRINT "&H";HEX$(A);" ";:PRINT USING"##### ";TH;:PRINT USING "###.##%";((TD-TH)/TD)*100:RETURN

Running this program (eventually) displays:

So the answer is — for zero, 7% faster. For other single digit values, only slightly faster. For multiple digit values, about 30% faster. BUT, if using zero, using the period (“A=.”) will be about 16% faster than using HEX.

Thank you for coming to my talk.

Until next time…

Leave a Reply

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