I am mostly a simple C programmer, but I do touch a bit of C# at my day job.
If you don’t think about what is going on behind the scenes, languages like Java and C# are quite fun to work with. For instance, if I was pulling bytes out of a buffer in C, I’d have to write all the code manually. For example:
Buffer: [ A | B | C | D | D | E | E | E | E ]
Let’s say I wanted to pull three one-byte values out of the buffer (A, B and C), followed by a two-byte value (DD), and a four byte value (EEEE). There are many ways to do this, but one lazy way (which breaks if the data is written on a system with different endianness to how data is stored) might be:
#include <stdint.h>
uint8_t a, b, c;
uint16_t d;
uint32_t e;
a = Buffer[0];
b = Buffer[1];
c = Buffer[2];
memcpy (&d, &Buffer[3], 2);
memcpy (&e, &Buffer[5], 4);
There is much to critique about this example, but this post is not about being “safe” or “portable.” It is just an example.
In C#, I assume there are many ways to achieve this, but the one I was exposed to used a BitConverter class that can pull bytes from a buffer (array of bytes) and load them in to a variable. I think it would look something like this:
UInt8 a, b, c;
UInt16 d;
UInt32 e;
a = Buffer[0];
b = Buffer[1];
c = Buffer[2];
d = BitConverter.ToInt16(Buffer, 3);
e = BitConverter.ToInt32(Buffer, 5);
…or something like that. I found this code in something new I am working on. It makes sense, but I wondered why some bytes were being copied directly (a, b and c) and others went through BitConverter. Does BitConverter not have a byte copy?
I checked the BitConverter page and saw there was no ToUInt8 method, but there was a ToChar method. In C, “char” is signed, representing -127 to 128. If we wanted a byte, we’d really want an “unsigned char” (0-255), and I did not see a ToUChar method. Was that why the author did not use it?
Here’s where I learned something new…
The description of ToChar says it “Returns a Unicode character converted from two bytes“.
Two bytes? Unicode can represent more characters than normal 8-bit ASCII, so it looks like a C# char is not the same as a C char. Indeed, checking the Char page confirms it is a 16-bit value.
I’m glad I read the fine manual before trying to “fix” this code like this:
Char a, b, c;
UInt16 d;
UInt32 e;
// The ToChar will not work as intended!
a = BitConverter.ToChar(Buffer, 0); //Buffer[0];
b = BitConverter.ToChar(Buffer, 1); //Buffer[1];
c = BitConverter.ToChar(Buffer, 2); //Buffer[2];
d = BitConverter.ToInt16(Buffer, 3);
e = BitConverter.ToInt32(Buffer, 5);
For a C programmer experimenting in C# code, that might look fine, but had I just tried it without reading the manual first, I’d have been puzzled why it was not copying the values I expected from the buffer.
Making assumptions about languages, even simple things like a data type “char”, can cause problems.
Recently, I ran in to a situation where a floating point value (represent current) was being converted to a byte value before being sent off in a status message. Thus, any calculations on the other side were being done with whole values (1, 2, 42, etc.). I was asked if the precision could be increased (adding a decimal place) and realized “you can’t get there from here.”
This made me wonder how much could be done with an 8-bit floating point representation. A quick web search led me to this article:
I also found a few others discussion other methods of representing a floating point value with only 8-bits.
Now I kinda want to code up such a routine in C and do some tests to see if it would be better than our round-to-whole-number approach.
Has anyone reading this already done this? I think it would be a fun way to learn more about how floating point representation (sign, mantissa, exponent) works.
When you write “standard C” and want to print a value using printf, you are at the mercy of standard C data types. For example, “%d” is a “Signed decimal integer” and “%u” is an “Unsigned decimal integer.”
There apparently mean the data types “int” and “unsigned int”.
int x;
printf ("x is %d\n", x);
unsinged int y;
printf ("y is %u\n", y);
At my day job, I am using a PIC24 processor, which is a 16-bit chip and is Harvard Architecture, similar to an Arduino UNO processor. When I try to write generic code on a PC using GCC (64-bit compiler), I run in to things like this:
char *ptr = 0x1234;
printf ("ptr is 0x%x\n", ptr);
%x expects an “Unsigned decimal value”. ptr is NOT a “Unsigned decimal value” that %x expects, so it will give a warning like:
warning: initialization of 'char *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
warning: format '%x' expects argument of type 'unsigned int', but argument 2 has type 'char *' [-Wformat=]
This seems like a reasonable warning. In the past, I’ve cast the pointer to an unsigned int like this:
char *ptr = 0x1234;
printf ("ptr is 0x%x\n", (unsigned int)ptr);
A 16-bit compiler may be just fine with this, but it would generate warnings on a 32 or 64-bit compiler because the “int” is a “long” or “long long” to them. Thus, different casting is needed.
That makes code non-portable, because printing a long int (“%lu”) expects different things on different architectures. In other words, I can cast my code to compile cleanly on the 16-bit system, but then I get warnings on the 64-bit system. Or vise versa.
…but that’s not what this post is about. Instead, it’s about why I was casting in the first place — to print pointers.
I was unaware that a “%p” had been added specifically for printing pointers. Unfortunately, it was not supported in the 16-bit PIC compiler I am using, so that won’t work.
inttypes.h
Instead, I found a stack overflow post that introduced me to “inttypes.h” and uintptr_t. I was unaware of this and see the casting lets me do something like this:
This will cast the pointer to an unsigned integer value which can then be printed!
On some systems.
There’s still the problem with a %u expecting an “int” but the value might be a “long int” (if it’s a 32-bit or 64-bit pointer).
PRIxWHAT?
It looks like someone thought of this, and my original “non portable printf” casting issue.
I see this file also contains #defines for various printf outputting types such as PRIXPTR, PRIX, PRIU8, and lowercase versions where it makes sense like PRIxPTR. These turn in to quoted strings like “d” or “X” or whatever. Meaning, you could use them to get the output type that matches what you want on the compiler you are using, rather than hard-coding %d.
uint32_t x = 42;
printf ("This is my value: %" PRIu32 "\n", x);
Above, PRIu32 turns in to the appropriate %u for printing a 32-bit value. This might be %u on one system, or %lu on another (depending on the size of “int” on the architecture).
Neat!
That lets me printfs be portable, IF the compiler supports these defines.
…as long as your compiler supports it, that is.
Fortunately, my PIC compiler does, so from now on I’ll stop hard-coding %x when printing pointers, and using those defines when printing stdint types like uint32_t:
Recently, I was annoyed to find that there did not seem to be any way to set a black pixel on the CoCo’s normal green background. I have since been schooled in the simplest way to make this work, which I will share after a long digressing ramble.
Never the Same Color Twice
The CoCo’s MC6847 VDG chip provides nine colors. Commenter Jason wrote:
“It always bothered me that the CoCo had nine colors in semi-graphics modes. The number nine should raise a red flag for anyone who is familiar with computers and the tendencies for things to be powers of two.“ … “It’s interesting that seven of the eight colors are from the NTSC test pattern (https://en.wikipedia.org/wiki/SMPTE_color_bars) which leads me to believe they’re all a particular frequency distance from each other. This would make the circuitry simpler.”
– Jason
I suppose it’s actually eight foregrounds colors with a black background, which matches the black border of the screen. There was even a test pattern program included in one of Radio Shack’s quick reference guide that I still have:
Color Adjustment Test Display
5 FOR X = 0 TO 63
10 FOR Y = 0 TO 31
15 C = INT(X/8+1)
20 SET(X,Y,C)
25 NEXT Y,X
30 GOTO 30
That produces the following output, showing the eight possible colors (plus the black background):
Color Adjustment Test Display, Radio Shack TRS-80 Color Computer Quick Reference Guide, page 55.
And here is the NTSC test pattern Jason referenced:
By Denelson83 – Own work, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=1067498
This made me want to alter the program to make it render something with the matching colors in the correct order (and with the Xroar emulator set to not emulate a crappy 1980s RF modulated TV signal):
The 7 NTSC color bar colors that the CoCo produces.
The extra color that is not shown is orange. I wonder why those eight colors (plus black) were chosen? And what makes the colors used by the two PMODE high res graphics screens? I’ll have to revisit that in the future.
But I digress…
You can’t, even if you SET your mind to it
My original article was written because I noticed you couldn’t SET a black pixel on a normal CoCo text screen. Even though the manual listed nine colors, with zero being black, attempting to do SET(X,Y,0) would result in that pixel being set to the green background color instead of black — the same as SET(X,Y,1). While other colors acted as you expected…
CoCo SET command.
SET seemed to be treating color 0 (black) as 1 (green). Because reasons.
In order to SET a black pixel on the normal text screen, extra code would be needed.
Ciaran Anscomb
Xroar emulator author Ciaran Anscomb was the first to respond with his GOSUB routine to achieve the desired effects:
I mean I think you’re making it harder by asking for it to work with plain CLS and not CLS1, but in that case:
His method would PEEK the character value at the 2×2 block that was being SET and, if that value was less than 128, it would change it to 143 and then use RESET… And it works:
10 CLS
20 FOR A=0 TO 31
30 X=A:Y=A:GOSUB 100
40 NEXT
50 GOTO 50
99 ' Ciaran Anscomb
100 IFPEEK(1024+INT(Y/2)*32+INT(X/2))<128THENPOKE1024+INT(Y/2)*32+INT(X/2),143
110 RESET(X,Y):RETURN
Jim Gerrie
BASIC programmer extroidinaire Jim Gerrie provided his take on this routine:
His version sets some pixels to color 1, and some to color 0 (using the “.” shortcut), based on some math with X and Y and divisions and integer conversions and … well, stuff I don’t grasp.
His also works! But as it draws, you can see it blipping surrounding pixels in the 2×2 block on then off. And while it passes the test case which drew a diagonal line, it doesn’t allow for setting arbitrary pixels near each other. They turn into full blocks.
However, he also added a second attempt:
I know that my first suggestion above is a bit of a cheat. Here’s a more robust suggestion:
10 CLS 20 FOR A=0 TO 31 30 X=A:Y=A:GOSUB 100 40 NEXT 50 GOTO 50 100 IFPOINT(X,Y)<.THENXX=(X/2-INT(X/2)=.)-(X/2-INT(X/2)>.):YY=(Y/2-INT(Y/2)=.)-(Y/2-INT(Y/2)>.):SET(X,Y,1):SET(X-XX,Y,1):SET(X-XX,Y-YY,1):SET(X,Y-YY,1) 101 RESET(X,Y):RETURN
– Jim Gerrie
This version passes the test as well, and looks like it better handles setting pixels at any position without impacting pixels around it.
What’s the POINT?
Ciaran made use of PEEK to detect what was on the screen before adding something new, and Jim figured out what pixels to set back to the background color. Neither did it the way I was expecting — using POINT:
POINT (X,Y) Tests whether specified graphics cell is on or off, x (horizontal) = 0-63; y (vertical) = 0-31. The value returned is -1 if the cell is in a text character mode; 0 if it is off, or the color code If it is on, See CLS for color codes.
IF POINT(10,10) THEN PRINT "ON" ELSE PRINT "OFF"
I expected I’d see folks use this to see if a pixel was set, and handle accordingly. Somehow. But as I read this description (from the Quick Reference Guide), I see that note that says “The value returned is -1 if the cell is in a text character mode.”
Text character mode? It’s just the background, isn’t it?
All green backgrounds are not the same
And that takes me back to Ciaran’s code:
IF PEEK(1024+INT(Y/2)*32+INT(X/2))<128 . . .
Less than 128 is a text character. The graphics blocks (2×2) start as 128. If the square is a text character then set it to 143. So what is that? That is a 2×2 graphics block that has all pixels set to the green color. And that green color is the same color as the background screen. Which isn’t 143 when you use CLS. Try this:
CLS:PRINT PEEK(1024)
If you clear the screen then PEEK to see what value is at the top left character (1024), it returns 96. 96 is the space character (yeah, ASCII is 32, but values in screen memory aren’t ASCII).
Ciaran’s code sees if it’s anything (including that green space), set it to 143, which is a green block that looks the same. Try this:
CLS 1:PRINT PEEK(1024)
That will print 143. Yet, visually, CLS and CLS 1 look the same. But, CLS is filling the screen with the space text character (96) and CLS 1 fills it with the green graphics character (143)! CLS 0-8 fill the screen with solid graphics characters, and CLS with no parameter is the space.
Now, I knew about character 143 looking like the normal space but not being one, because we used to use this as a cheap “copy protection” method. On DISK, you could save out a file like this:
SAVE "HELLO.BAS"
…and you’d get a file on BASIC called HELLO.BAS. But, if you did this:
SAVE "HELLO"+CHR$(143)+".BAS"
…Disk BASIC would write out a file called HELLO(char 143).BAS. When you did a DIR they would look the same, but you couldn’t do a LOAD”HELLO.BAS” to get the one with the CHR$(143) in it. Unless you exempted the disk directory bytes you would not know there was an “invisible” character at the end of the “HELLO” filename.
Sneaky. And I did this with some of my own programs.
But years later, when the CoCo 3 came out, it’s 40 and 80 column screen did NOT support the 2×2 graphics block characters, and this trick was no longer as sneaky since you would see “HELLO(some funky character).BAS” in the directory listing and know something weird had been done.
But I digress, again…
Why do it the hard way, anyway?
It turns out, even though I knew about the “add CHR$(143)” trick, I had forgotten (or never knew/realized) that CLS and CLS 1 filled the screen with different characters. And, if the screen has a graphics character at the position, RESET will then work to change that pixel back to black.
Ciaran got me exploring this because in his e-mail he added:
If you allow CLS1, the problem solves itself :)
– Ciaran Anscomb
I had to follow up and ask what he meant by this. And, well, nevermrind, then. All I needed to do was start the program with CLS 1 instead of CLS 0, and then I could use RESET() to set individual black pixels on the screen.
I could have a subroutine that expect X, Y and C (for color) and if the color was 0 (black), do a RESET, else do a SET:
0 REM RESET
10 CLS 1
20 FOR A=0 TO 31
30 X=A:Y=A:C=0:GOSUB 100
40 NEXT
50 GOTO 50
100 IF C=0 THEN RESET(X,Y) ELSE SET(X,Y,C)
110 RETURN
And that works fine on any CLS 0-8 screen. Remember, SET(X,Y,0) never gives you a black pixel. 0 seems to mean “background color” instead, while RESET(X,Y) seems to mean “set to black”.
To me, this is a bit counterintuitive, since today I would expect “reset” to mean “set to background color” but this isn’t a graphics mode — it’s just graphics characters on a screen, so the only way BASIC could have done this is if it remembered what the CLS # value was, and made RESET set to that color. Which would be extra ROM space for a simple enhancement that most could work around with RESET instead.
I was in the middle of writing more on my CoCo Base-64 encoding series and stumbled upon some weirdness with the DATA command. Consider this silly program:
0 REM baddata.bas
10 READ A$:IF A$="" THEN END
15 PRINT A$;:GOTO 10
20 DATA ":"
30 DATA HELLO
40 DATA ""
This will print:
:HELLO
I know I could just have done DATA “:HELLO” but stay with me on this..
If you try to combine lines like this:
0 REM baddata.bas
10 READ A$:IF A$="" THEN END
15 PRINT A$;:GOTO 10
20 DATA ":":DATA HELLO
40 DATA ""
…you get this:
Color BASIC DATA quirk.
When I get a moment, I’ll have to look at the ROM disassembly and see what is going on.
Previously, I started discussing C compiler warnings, with specific intent to discuss this one:
warning: comparing floating point with == or != is unsafe [-Wfloat-equal]
I presented a simple question — what would this print?
int main()
{
float var1;
var1 = 902.1;
if (var1 == 902.1)
{
printf ("it is!\n");
}
else
{
printf ("it is NOT.\n");
}
return EXIT_SUCCESS;
}
On my Windows 10 computer running GCC (via the free Code:Blocks editor), I get see “it is NOT.” This goes back to an earlier article I wrote about how 902.1 cannot be represented with a 32-bit floating point. In that article, I tested setting a “float” and a “double” to 902.1 and printed the results:
As you can see, a 64-bit double precision floating point value can accurately display 902.1, while a 32-bit floating point cannot. The key here is that in C a floating point number defaults to being double precision, so when you say:
var = 123.456;
…you are assigning 123.456 (a double) to var (a float).
float var1;
var1 = 902.1;
var1 (a float) was being assigned 902.1 (a double) and being truncated. There is a potential loss of precision that a compiler warning could tell you about.
If, with warnings enabled, comparing a floating point using == (equal) or != (not equal) is considered “unsafe,” how do we make it safe?
Ah, compiler warnings. We hate you, yet we need you.
Recently at my day job, I noticed we were running with a very low C compiler warning level. I decided to crank it up a bit then rebuild.
It was a disasters. Thousands of compiler warnings scrolled by as it churned away for about five minutes. Many were coming from header files provided by the tool company! Things like windows.h and even things they included from standard ANSI C headers like stdint.h. What a mess!
I was able to figure out how to disable certain warnings to clean up the ones I could not fix, leaving me with a ton of legitimate warnings in our own code.
Some were harmless (if you knew what you were doing), like comparing signed to unsigned values, which I’ve previously discussed. Some complained about missing or incorrect prototypes.
A favorite useful one is when it complains about initialized variables that could be accessed, such as:
int variable;
switch (something)
{
case 1:
variable = 10;
break;
default:
break;
}
printd ("variable: %d\n", variable);
Above, if “something” did not match one of the case statements (1 in this example), it would never be set, and the print would be using whatever random crap was in the space occupied by “variable” later. This is a nice warning to have.
Others were about unused variables or unused parameters. For example:
int function (int notUsed)
{
return 42;
}
Compiler warnings can notify you that you have a variable declared that isn’t getting used. You can tell the compiler “Yeah, I know” by doing this:
int function (int notUsed)
{
(void)notUsed; // Not used!
return 42;
}
Each time you clean up a warning like that, you tell the compiler (and others who inspect the code) “I meant to do that!” And, sometimes you find actual bugs that were hidden before.
I’ve written a bit about floating point issues in the past, but the warning today is this one:
warning: comparing floating point with == or != is unsafe [-Wfloat-equal]
Basically, while you can do…
int a;
int b;
if (a == b) { ...something... };
…you cannot…
float a;
float b;
if (a == b) { ...something...}
…at least, you can’t without getting a warning. Due to how floating point works, simple code may fail in interesting ways:
int main()
{
float var1;
var1 = 902.1;
if (var1 == 902.1)
{
printf ("it is!\n");
}
else
{
printf ("it is NOT.\n");
}
return EXIT_SUCCESS;
}
if (input(DIGITAL_IN1) == HIGH)
{
...
}
if (input(DIGITAL_IN2) == HIGH)
{
...
}
if (input(DIGITAL_IN3) == HIGH)
{
...
}
This code led me to having to open schematics and figure out what digital input 1, 2 and 3 where.
So I used the free CodeBlocks editor refactor “Rename Symbol” feature to change their names to:
if (input(ROBOT_ON_FIRE_IN1) == HIGH)
{
...
}
if (input(BATTERY_OVERLOAD_IN2) == HIGH)
{
...
}
if (input(DUST_BIN_FULL_IN3) == HIGH)
{
...
}
…but using names that made sense for my application ;-)
Someone must have thought it was important to include the input number (IN1, IN2 and IN3) — possibly so they would not need to look through defines to see what value goes to what input — so I kept those in my names.
But now, when I find this code months down the line, I’ll immediately know which function is most likely detecting that the robot is on fire.
2020-04-30 – Added missing semicolon in code and updated example.
NOTE: This article was originally written a few years ago, so some references may be out of date.
I have been enjoying working on the SirSound project the past month, as well as some fun challenges at my day job. Every now and then I run into something I’d like to do that is not doable in C, or doable but not proper to do in C, or … maybe doable. It’s sometimes difficult for me to find answers when I do not know how to ask the question.
With that said, I have a C question for anyone who might be able to answer it.
Using my multi-track music sequencer as an example, consider representing data like this:
It’s easy to create a sequence like this in C. Here’s some pseudo code:
track1 = { C, C, C, C };
track2 = { E, E, E, E };
sequence1 = { track1, track 2};
I thought there might be a clever way to do all of this with one initializer. If I treat the data like nested XML (like the first example), I thought it might be possible to do something like this:
…though the address of “light” is quite different than the address of a structure which contains a pointer that should point to “light”.
I was going to make each one a pointer to an array of words, so I could have a tree of words like the earlier example (with kitchen/light and kitchen/fan):
It seems like all I’m doing lately is regurgitating things that Robin of 8-Bit Show and Tell has already done.
So let’s do that again.
Don’t blame me. Blame YouTube!
YouTube did what YouTube does and it showed me another of Robin’s well-done videos. This one caught my attention because it dealt with the Commodore VIC-20 and its Super Expander cartridge.
The main thing that pulled me away from Commodore was seeing the TRS-80 Color Computer’s Extended Color BASIC. The CoCo had simple commands to play music and draw lines, boxes and circles. It also had this wondrous ELSE command I’d only heard rumors about.
On the VIC-20, it seemed you needed to use POKE and PEEK for just about anything graphics or sound related. Thus I gave up a computer with programmable characters and a hardware sound chip for a machine that had neither. On my new CoCo, at least I could draw a circle and play music without needing pages of DATA statements and cryptic PEEKS and POKEs.
Commodore was aware of this shortcoming, and they sold the Super Expander as a way to make up for it. Not only did it provide an extra 3K of memory (giving a whopping 6.5K for BASIC), it also added new commands to do “high resolution” graphics including drawing lines and circles, as well as ways to PRINT music using simple notation.
I used the Super Expander to do TV titles for fishing videos my father shot and edited. It was a thrill to see my VIC-20 graphics on TV screens at the Houston Boat Show.
But no one else could run my programs unless they had purchased the Super Expander as well.
But I digress.
(And besides, the Commodore 64 was $600 when it came out, and I was able to get a 64K CoCo 1 for $300 at the time.)
Don’t blame YouTube. Blame Twitter.
Robin’s video was making use of the Super Expander to let the VIC-20 solve a challenge initiated by Twitter user Dataram_57. On June 8th, 2019, they wrote:
I challenge every mathematician and programmer to solve this problem. Write the equation that you will use to change the position of the red point according to time and the animation. (You can use every function without if statement)???? #programming#math#mathgames#programmerpic.twitter.com/UDgGv1pajB
— D̨̯̞̜͇a̶͙̤t̵̪̤̺̪ͅḁ̱̳̱͠r̡a̦͞m͍̩̗͉͢ (@Dataram_57) June 8, 2019
This was, more or less, a classic bouncing ball program very much like the ones I have been writing about lately. But, all mine certainly made use of IF. Here’s how mine started:
0 REM bounce.bas
10 CLS:X=0:Y=0:XM=1:YM=1
20 PRINT@Y*32+X,"O";
30 X=X+XM:IF X<1 OR X>30 THEN XM=-XM
40 Y=Y+YM:IF Y<1 OR Y>13 THEN YM=-YM
50 GOTO 20
That’s not a very good example. It doesn’t erase itself, nor does it use the bottom line to avoid screen scrolling when the ball hits the bottom right position. It does show how I would use X and Y coordinates then an XM (X movement) and YM (Y movement) variable to increment or decrement them based on if they hit an edge.
The parameters of Dataram_57’s challenge were as follows:
Width: 90
Height: 80
Starting Position: 0,0
Time: ???
I wrote a quick graphical program do do this using my X/Y/XM/YM method:
0 REM dataram_57 twitter challenge
1 POKE 65495,0
10 W=89:H=79:X=0:Y=0:T=0:XM=1:YM=1
20 PMODE0,1:PCLS0:SCREEN1,1
30 LINE(0,0)-(89,79),PSET,BF
40 PSET(X,Y,0)
50 X=X+XM:IF X<0 THEN XM=1:GOTO 50 ELSE IF X>=W THEN XM=-1:GOTO 50
60 Y=Y+YM:IF Y<0 THEN YM=1:GOTO 60 ELSE IF Y>=H THEN YM=-1:GOTO 60
70 T=T+1:IF T=7031 THEN END
80 GOTO 40
The first thing to notice is that I draw a filled box from 0,0 to 89,79 and then set black pixels in it. This lets me visually verify my line is going all the way to the edge of the 90×80 target area. Also, I am using the CoCo 1/2 double speed poke since this is time consuming. If you do this on a CoCo 3, feel free to use POKE 65497,0 instead.
Twitter user Dataram_57’s challenge running on a CoCo.
Eventually the area should be entirely black when every dot has been erased.
How long has this been going on?
I did some tests and figured out that it takes 7032 iterations (0-7031) for the dot to cycle through the entire 90×80 area before it has erased all the other dots.
With that in mind, I propose we turn this into both a logic and optimization challenge. On the CoCo, let’s see if we can use the PMODE 0 screen (128×96 resolution with 1 color). We can put this in a modified version of benchmark framework for 7032 cycles and see how fast we can do it. (By modified, I am removing the outer “try this three times and average the results” loop.)
My example, using IFs, looks like this:
0 REM dataram_57 twitter challenge 2
1 POKE 65495,0
5 DIM TM,A
10 TIMER=0:TM=TIMER
15 W=89:H=79:X=0:Y=0:XM=1:YM=1
16 PMODE0,1:PCLS0:SCREEN1,1
17 LINE(0,0)-(89,79),PSET,BF
20 FORA=0TO7030
30 PSET(X,Y,0)
40 X=X+XM:IF X<0 THEN XM=1:GOTO 40 ELSE IF X>=W THEN XM=-1:GOTO 40
50 Y=Y+YM:IF Y<0 THEN YM=1:GOTO 50 ELSE IF Y>=H THEN YM=-1:GOTO 50
80 NEXT
90 PRINTTIMER:END
Mine, running in Xroar, displays 9808 at the end. And it’s not the correct way to meet the requirements of the challenge, so … the real versions may be faster, or slower.
Your challenge, should you decide to accept it…
Our challenge is to:
Rewrite this to work WITHOUT using any “IFs”.
Try to make it as fast as possible while keeping the benchmark code (lines 5, 10, 20, 80 and 90) intact. You can add variables to the DIM, but otherwise leave those lines alone.
What says you?
Credit where credit is due…
And lastly, for those who want to cheat, here is the solution that Robin came up with using the VIC-20 Super Expander cartridge…
Is his the only way? The best way? The fastest way?