Although I grew up with the Commodore 64, I didn't do very much graphics programming on it. So when I actually tried to, I was surprised by the quirk of the VIC-II chip that there were blocks of RAM that were essentially invisible to the VIC-II chip because it always mapped the character set ROM there. This ROM only made sense as a character set, but you could also see it in bitmap mode, in sprites, and even in screen memory.
I'd never seen it in screen memory, but today I decided to see if I could. I looked up what I needed to online, typed
POKE 53272,69
, and there it was, five columns of strange patterns, with 125 PETSCII character glyphs each rendered as
eight PETSCII glyphs. You could kind of work out how the top row must map to @ABCD
, how the right side or that row
kind of showed the same symmetry as the letters B, C, and D did. It was a tenuous resemblance at best, but it was there.
Then I wondered, what if I made a custom character set, where each character was its own screen code, rendered into bits? So character 0 would be blank, character 255 would be filled, character 24 would have the middle two rows filled, etc. Then it would kind of look like a bitmap again, and you'd be able to see the letters all stretched out and sideways.
Well, I managed to bang out a BASIC program to do it. The algorithm went like this:
for each character X from 0 to 255
for each bit/row Y from 0 to 7
if the number X has bit Y set,
set row Y of character X to all ones
else
set row Y of character X to all zeroes
If I tried to translate this directly into BASIC, and used TI to benchmark it, it would look like this:
0 ti$="000000"
10 poke 53281,14:print"{lblu}{clr}":poke 53281,6
20 poke 53272,72:sc=8192:ff=255
30 for x=0 to ff
40 for y=0 to 7
50 poke sc,0
55 if x and 2^y then poke sc,ff
60 sc=sc+1
70 next y,x
75 print ti
80 get a$:if a$=""then 80
90 poke 53272,21
This takes about 83 seconds (5033 frames) to finish the loop, despite the obvious optimizations of
incrementing the variable SC
instead of computing 8192+X*8+Y
every time, and of using the constant
FF
to hold the number 255 so that BASIC doesn't have to convert it to floating point thousands of times.
The version in the source code takes just over 36 seconds, due to understanding which things are fast or
slow in CBM BASIC. The caltulation 2^Y
uses a very slow and complicated floating point power algorithm.
So instead, it stores the bit mask in the variable B
and multiplies it by 2 for each row. The floating
point routines can multiply by 2 very quickly. And instead of using an IF
statement,
it uses some clever logic to turn the bitmask into 0 or 255. (X AND B)
is either 0
or a positive integer,
but (X AND B)>0
is either 0
or -1
. And a fast way to turn -1
into any integer up to 32768 is with
AND
. I used to do things like ((X AND B)>0)*-255
, but multiplication is slower than AND
.
Change the poke statement in line 20 from 72
to 104
to see the lowercase letters instead.
I was surprised how little code it took, but 36 seconds is still a long time to wait for a render. so I wrote an assembly language program that does the same thing. It compiles to an 80-byte PRG file.
0000000 01 08 0b 08 0a 00 9e 32 30 36 31 00 00 00 a9 00
0000010 85 fb a9 01 a8 25 fb f0 02 a9 ff 8d 00 20 ee 1b
0000020 08 d0 03 ee 1c 08 98 2a 90 ea e6 fb 18 d0 e3 a9
0000030 20 8d 1c 08 a9 48 8d 18 d0 a5 c6 f0 fc c6 c6 a9
0000040 68 8d 18 d0 a5 c6 f0 fc c6 c6 a9 15 8d 18 d0 60
0000050
I wonder if there's any other way to do anything cool by pointing the VIC-II to the character set ROM out of context?