Author Archives: Allen Huffman

About Allen Huffman

Co-founder of Sub-Etha Software.

3X+1 in C#

For my day job, I do embedded C programming for PIC24 compilers and some Windows C programming in something called LabWindows. Lately, I’ve been touching some C# stuff, so I decided to revisit last night’s 3X+1 program by converting it to C#.

You can compile and run it online here: https://www.onlinegdb.com/online_csharp_compiler

// 3X+1

using System;
					
public class Program
{
	public static void Main()
	{
		while (true)
		{
			Int32 x = 0;

			Console.WriteLine();
			Console.Write("STARTING NUMBER? ");
			x = Int32.Parse(Console.ReadLine());
			
			while (true)
			{
				Console.Write(x);
				Console.Write(" ");
				
				if (x == 1) break;
				
				if ((x & 1) == 1) // Odd
				{
					x = x * 3 + 1;
				}
				else // Even
				{
					x = x / 2;
				}
			}
		}
	}
}

3X+1 and Color BASIC

For the past three weeks, I have found myself out-of-town for work. This week, I decided to bring my Raspberry Pi 400 along so I could play with it in the hotel room.

Raspberry Pi 400

I soon found myself toying around with the XRoar CoCo emulator, and I knew just what I wanted to program…

Last night, YouTube showed me a video about “the most dangerous problem in mathematics.”

The idea is you start with a number. If it is odd, you multiply it by 3 and add 1. If it is even, you divide it by 2. Repeat until you get to the pattern 4, 2, 1, 4, 2, 1, 4, 2, 1.

Math says that, so far, every number ends up at that pattern. No one has figured out a formula that leads to any number that does not end at 4, 2, 1.

With that in mind, I thought it would be fun to write the 3X+1 problem in CoCo Color BASIC. It looks like this:

0 REM 3X+1
10 PRINT:INPUT "STARTING NUMBER";X
20 PRINT X;
30 IF X=1 THEN 10
40 IF X AND 1 THEN X=X*3+1:GOTO 20
50 X=X/2:GOTO 20 

I tried to avoid using any Extended Color BASIC features such as HEX numbers (&H1) to speed things up. I even skipped ELSE so it could run on a VIC-20 or other system without that command.

I use “X AND 1” to test for a number being odd. Any odd number has the first bit set. 1 (00000001) does, 2 does not (00000010), 3 does (00000011), and so on.

It does have one flaw… if any number is greater than 32767, it will crash with a ?FC ERROR. Apparently Color BASIC’s AND cannot handle any value greater than 7 bits (01111111 = 32767).

Do you know of a different way to test for even or odd values? I can think of two, one of which would be terribly inefficient and the other not as inefficient but much worse than using AND.

Give it a shot and see if you can find the largest sequence of numbers a CoCo can calculatae.

Or better yet, find a better way to do this in Color BASIC.

Enjoy!

Arduino Serial output C macros

Here is a quickie.

In Arduino, instead of being able to use things like printf() and puchar(), console output is done by using the Serial library routines. It provides functions such as:

Serial.print();
Serial.println();
Serial.write();

These do not handle any character formatting like printf() does, but they can print strings, characters or numeric values in different formats. Where you might do something like:

int answer = 42;
printf("The answer is %d\r\n", answer);

…the Arduino version would need to be:

int answer = 42;
Serial.print("This answer is ");
Serial.print(answer);
Serial.println();

To handle printf-style formatting, you can us sprintf() to write the formatted string to a buffer, then use Serial.print() to output that. I found this blog post describing it.

I recently began porting my Arduino Telnet routine over to standard C to use on some PIC24 hardware I have at work. I decided I should revisit my Telnet code and try to make it portable, so the code could be built for Arduino or standard C. This would mean abstracting all console output, since printf() is not used on the Arduino.

I quickly came up with these Arduino-named macros I could use on C:

#include <stdio.h>
#include <stdlib.h>

#define SERIAL_PRINT(s)     printf(s)
#define SERIAL_PRINTLN(s)   printf(s"\r\n")
#define SERIAL_WRITE(c)     putchar(c)

int main()
{
    SERIAL_PRINT("1. This is a line");
    SERIAL_PRINTLN();
    SERIAL_PRINTLN();

    SERIAL_PRINTLN("2. This is a second line.");

    SERIAL_PRINT("3. This is a character:");
    SERIAL_WRITE('x');
    SERIAL_PRINTLN();

    SERIAL_PRINTLN("done.");

    return EXIT_SUCCESS;
}

Ignoring the Serial.begin() setup code that Arduino requires, this would let me replace console output in the program with these macros. For C, it would use the macros as defined above. For Arduino, it would be something like…

#define SERIAL_PRINT(s)     Serial.print(s)
#define SERIAL_PRINTLN(s)   Serial.println(s)
#define SERIAL_WRITE(c)     Serial.write(c)

By using output macros like that, my code would still look familiar to Arduino folks, but build on a standard C environment (for the most part).

This isn’t the most efficient way to do it, since Arduino code like this…

  Serial.print("[");
  Serial.print(val);
  Serial.println("]");

…would be one printf() in C:

printf ("[%d]\n", val);

But, if I wanted to keep code portable, C can certainly do three separate printf()s to do the same output as Arduino, so we code for the lowest level output.

One thing I don’t do, yet, is handle porting things like:

Serial.print(val, HEX);

On Arduino, that outputs the val variable in HEX. I’m not quite sure how I’d make a portable macro for that, unless I did something like:

#define SERIAL_PRINT_HEX(v) Serial.print(v, HEX)

#define SERIAL_PRINT_HEX(v) printf("%x, v)

That would let me do:

SERIAL_PRINT("[");
SERIAL_PRINT_HEX(val);
SERIAL_PRINTLN("]");

I expect to add more macros as-needed when I port code over. This may be less efficient, but it’s easier to make Arduino-style console output code work on C than the other way around.

Cheers…

C: (too) many happy returns…

Here’s another quick C thing…

One of the jobs I had used a pretty complete coding style guide for C. One of the things they insisted on was only one “return” in any function that returns values. For example:

int function(int x)
{
   if SOMETHING
   {
      return 100;
   }
   else SOMETHING ELSE
   {
      return 200;
   }
   else
   {
      return 0;
   }
}

The above function returns values 100, 200 or 0 based on the input (1, 2 or anything else). It has three different places where a value is returned. This saves code, compared to doing it like this:

int function(int x)
{
   int value;

   if SOMETHING
   {
      value = 100;
   }
   else if SOMETHING ELSE
   {
      value = 200;
   }
   else
   {
      value = 0;
   }

   return value;
}

Above, you see we use a variable, and then have three places where it could be set, and then we return that value in one spot at the end of the function. This probably generates larger code and would take longer to run than the top example.

But if you can afford those extra bytes and clock cycles, it is a much better way to do this — at least form a maintenance and debugging standpoint.

I have accepted this, but only today did I run in to a situation where this approach would have saved me some time and frustration. In my case, I was encounter a compiler warning about a function not returning a value where it was defined to return a value. I looked and confirmed the function was indeed returning a value. What was going on?

The problem was that it used multiple returns, and did something like this:

int function(int x)
{
   int value;

   if (!ValueIsValid(x)) return;

   if SOMETHING
   {
      value = 100;
   }
   else if >OMETHING ELSE
   {
      value = 200;
   }
   else
   {
      value = 0;
   }

   return value;
}

Somewhere in the program was a check that just did a “return” and the compiler was seeing that, but my eyes were looking at the lower portion of the program where a value was clearly being returned.

I am guessing the function originally did not return a value, and when a return value was added later, that initial “return;” was not corrected, leaving a compiler warning. This warning may have been in the code for a long time and was simply left alone because someone couldn’t figure it out (my situation) or wasn’t concerned about compiler warnings.

Today, the warning bugged me enough that I did a deep dive through the function, line-by-line, trying to figure out what was going on. And I found it. A simple correction could have been this:

int function(int x)
{
   int value;

   if (!ValueIsValid(x)) return 0; // FIXED: Add missing return value.

   if SOMETHING
   {
      value = 100;
   }
   else if SOMETHING ELSE
   {
      value = 200;
   }
   else
   {
      value = 0;
   }

   return value;
}

That resolved the compiler warning, but still left two spots where a value was returned, so I ended up doing something like this:

int function(int x)
{
   int value;

   if (ValueIsValid(x) == true) // do this if valid
   {
      if SOMETHING
      {
         value = 100;
      }
      else SOMETHING ELSE
      {
         value = 200;
      }
      else
      {
         value = 0;
      }
   }
   else // Not valid
   {
      value = 0;
   }

   return value;
}

Now there is only one place the function returns, and it only processeses things if the initial value appears valid.

I will sleep better at night.

I sleep on a soap box.

GEEKPI’s Getting Started with MicroPython on Raspberry Pi Pico kit

It seems only yesterday I first mentioned the new Raspberry Pi Pico. At this time of its introduction, I wondered two things:

  1. Why did they use the “Raspberry Pi” name for a new piece of hardware that does not run Linux. It was closer to an Arduino than a Pi. I expect this naming will cause confusion, since folks have had years learning what a “Pi” can do (video, audio, keyboard, mouse, etc.) and the Pico does none of that.
  2. Why did they bother with a $4 Pico, if a PiZero can be had for only $1 more.

#2 is answered with “because folks like me dislike the slowness of booting a full OS and all the hassles of dealing with Linux for an embedded project.” However, I already use Arduinos for that purpose. The Pico just seemed more like the larger Arduino models.

Side Note: To me, and many others, “Arduino” will always mean “Arduino Uno“, the tiny and cheap Arduino with 4K of RAM. Because it was the version that started things up, the name Arduino is mostly associated with these smaller limited models. But, Arduino even makes more powerful versions that can run Linux.

#1 I think is “just because it will cause confusion.” I think the same thing about the “new” Atari VCS. (If you didn’t know, there is a new Atari out — and it is called the same thing the original Atari VCS was called back in 1977. No confusion there. ;-)

But I digress…

I recently received a $45 GEEKPI BASIC Pico kit to review. You can find it on Amazon (see that link).

Reading through the specs of this $4 circuit board, I find it is pretty impressive. Speeds up to 133MHz, 264K of RAM, and 2MB of Flash storage. It has enough power, memory and storage to run things like Python, which a 4K Arduino just cannot do.

The included manual had me download a Python IDE, then plug up and connect to the Pico with a USB cable. I could then type and run my first “Hello World” program in Python. You can even copy the “main.py” python script to the Pico so it will power up and run on it’s own. (A few other steps were needed to install MicroPython on the Pico, but they were easy and only took a few minutes.)

Impressive.

The initial downside is that the Pico does not come with the header pins soldered on. I had to turn to a coworker to do this for me so I could plug it into the breadboard and hook up some wires for a blinking LED example.

I expect at some point (if not already) you will be able to buy a model with those pins already soldered on, much like you can do with a Pi Zero.

I do not know when I’ll have time to fully explore the “power of the Pico,” but it looks like it will be a fun time. It appears to be quite capable with I/O and protocols (SPI, I2C, UARTs, etc.).

More to come…

char versus C versus C#

Updates:

  • 2020-07-16 – Added a few additional notes.

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.

Thank you, manual.

Researching 8-bit floating point.

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:

http://www.toves.org/books/float/

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.

But it doesn’t seem very useful.

C, printf, and pointers.

I learned a new C thing today.

But first, an old C thing.

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:

printf ("Dump (0x%x, %u)\r\n", (uintptr_t)ptr, size);

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:

void Dump(void *ptr, unsigned int size)
 {
     printf ("Dump (0x%"PRIXPTR", %u)\r\n", (uintptr_t)ptr, size);

At least, I think I will.

Until next time…

Raspberry Pi Pico?

For those who find the unwieldy huge size of the Raspberry Pi Zero off-putting, the Raspberry Pi Foundation has now released a Raspberry Pi Pico.

It uses a custom chip, the RP2040, and seems to be more of an Arduino than a Pi. It has 264KB of memory and can be programmed in C/C++ or MicroPython. Thus, it is not a Linux system.

The unfortunate name of calling it a Pi may cause some confusion.

But it’s still neat. With the Pi group entering this market, it finally gives them something to compete with Arduino. The Pi is great, but having a slow-booting disk base OS that can corrupt the file system if you do not shut down properly each time was not a good fit for embedded designs.

The Pi Zero is $5, and the new Pi Pico is $4. This is a great price point compared to things like the mini Arduinos, but it’s for folks who can solder if you want to hook anything to it. Since the Pi Zeros are sold with versions that have header pins soldered on (for $5 more), it seems likely someone will do that for the Pico allowing folks who don’t solder well (such as myself) to make use of this item.

My current favorite supplier of Pi items is Vilros:

More to come…

Benchmarking the CoCo keyboard – part 7

See also: part 1, part 2, part 3, part 4, part 5, part 6, part 7 and more (coming “soon”).

NOTE: This one is gonna jump around a bit, referring to examples in the previous installment, so hang tight…

Some comments from the previous installment, where I shared some code that wasn’t speeding up like I thought it should when I replaced hard-coded values with variables. As the post when live, I read through it and noticed my mistake. I added a comment to see if anyone else could spot it:

0 REM arrowbench9.bas
5 V=&HF7:U=&H155:D=&H156:L=&H157:R=&H158
10 TIMER=0:FOR A=1 TO 1000
20 POKEU,V:POKED,V:POKEL,V:POKER,V
30 IF PEEK(U)=V THEN IF Y>.THEN Y=Y-1:GOTO90
40 IF PEEK(D)=V THEN IF Y<&HDF THEN Y=Y+1:GOTO90
50 IF PEEK(L)=V THEN IF X>.THEN X=X-1:GOTO90
60 IF PEEK(R)=V THEN IF X<&HFE THEN X=X+1:GOTO90
90 NEXT:PRINT TIMER

craig immediately chimed in with the issue:

In arrowbench9.bas it pokes $F7 instead of $FF ?

In test, shouldn’t line 10 should use same as arrowbench9.bas line 20 ?

Can you use ‘next’ instead of ‘goto90’ ?

– craig

Right off the bat, craig noticed my mistake. In like 20, it’s supposed to POKE those four keyboard column values to 255 (&HFF). But, when I substituted the variables, I did not make a variable for 255 — instead, I incorrectly used the V variable which was the &HF7 value the PEEK would change to when that key was being held down. Oops! Thus, I was never resetting it so it was, apparently, always acting as if the key was being held down, processing all the variable X/Y stuff every time.

A fix to the original arrowbench2.bas might look like this:

0 REM arrowbench10.bas
5 Z=&HFF:V=&HF7:U=&H155:D=&H156:L=&H157:R=&H158
10 TIMER=0:FOR A=1 TO 1000
20 POKEU,Z:POKED,Z:POKEL,Z:POKER,Z
30 IF PEEK(U)=V THEN IF Y>.THEN Y=Y-1
40 IF PEEK(D)=V THEN IF Y<&HDF THEN Y=Y+1
50 IF PEEK(L)=V THEN IF X>.THEN X=X-1
60 IF PEEK(R)=V THEN IF X<&HFE THEN X=X+1
90 NEXT:PRINT TIMER

Now the keyboard table is properly reset back to 255 before each scan. While the original arrowbench2.bas reported 2890, this version (using the properly variable to reset those locations) reports 2351 and is indeed faster. My bad.

As to the other comments, that was yet another typo, where I created variables then forgot to use them:

5 V=&HF7:U=&H155:D=&H156:L=&H157:R=&H158
10 POKE&H155,&HFF:POKE&H156,&HFF:POKE&H157,&HFF:POKE&H158,&HFF
20 PRINT HEX$(PEEK(U))" "HEX$(PEEK(D))" "HEX$(PEEK(L))" "HEX$(PEEK(R)),HEX$(PEEK(&H155))" "HEX$(PEEK(&H156))" "HEX$(PEEK(&H157))" "HEX$(PEEK(&H158))
30 GOTO 10

…should have used U/D/L/R and a new Z in line 10, just like the example before.

As to using “NEXT” instead of “GOTO 90”, this refers to trying to bypass additional checks if one key is satisfied:

0 REM arrowbench11.bas
5 Z=&HFF:V=&HF7:U=&H155:D=&H156:L=&H157:R=&H158
10 TIMER=0:FOR A=1 TO 1000
20 POKEU,Z:POKED,Z:POKEL,Z:POKER,Z
30 IF PEEK(U)=V THEN IF Y>.THEN Y=Y-1:GOTO90
40 IF PEEK(D)=V THEN IF Y<&HDF THEN Y=Y+1:GOTO90
50 IF PEEK(L)=V THEN IF X>.THEN X=X-1:GOTO90
60 IF PEEK(R)=V THEN IF X<&HFE THEN X=X+1:GOTO90
90 NEXT:PRINT TIMER

In this benchmark example, I did GOTO 90 to go to the “end of what we are timing” line. But if this was being used in a program, a NEXT would have been faster than scanning forward to find line 90 and then doing the NEXT. BUT, if I tried that here, when NEXT was done (1 to 1000), it would not return and would go to the next line — and if that was 40, 50 or 60, it would do the check then try a NEXT and error with a “?NF ERROR” (next without for).

But, the point is well made — NEXT with a check after it could even be faster than a GOTO (scanning lines) to a next. That would be a fun benchmark.

Faster, even.

craig also pointed out an interesting optimization… Rather than clear all four keyboard values, whether they need it or not, why not just clear the one(s) that changed?

Faster.. what about moving line 20 out of the loop and only poking the matching peeks?

IF PEEK(U)=V THEN POKEU,F:IF Y>.THEN Y=Y-1:GOTO90

F=&HFF

For diagonals, change to GOTO50 on line 30.
Line 60 needs no GOTO90

– craig

This is worthy of an updated benchmark. Let’s take arrowbench10.bas above (2351) and modify it like this:

0 REM arrowbench12.bas
5 Z=&HFF:V=&HF7:U=&H155:D=&H156:L=&H157:R=&H158
10 TIMER=0:FOR A=1 TO 1000
30 IF PEEK(U)=V THENPOKEU,Z:IF Y>.THEN Y=Y-1
40 IF PEEK(D)=V THENPOKED,Z:IF Y<&HDF THEN Y=Y+1
50 IF PEEK(L)=V THENPOKEL,Z:IF X>.THEN X=X-1
60 IF PEEK(R)=V THENPOKER,Z:IF X<&HFE THEN X=X+1
90 NEXT:PRINT TIMER

Now it only clears the value if it was changed. This produces a value of … 1603! We have a winner! Great improvement, craig!

Also, when I added the “GOTO 90” at the end of the example, to bypass the other checks (eliminating the possibility of diagonals), craig is suggesting the code could simply skip checking DOWN if we had an UP (GOTO 50 moves to the Left/Right check next) and, likewise, like 50 could GOTO 90 to skip over the Right check. Thus:

  • If UP is pressed…
    • Check for LEFT. If LEFT is pressed…
      • Exit checks. We have an UP and a LEFT
    • else Check for RIGHT. If RIGHT is pressed…
      • Exit checks. We have an UP and RIGHT.
    • else Exit checks. We have UP.
  • else check DOWN. If DOWN is pressed…
    • Check for LEFT. If LEFT is pressed…
      • Exit checks. We have DOWN and LEFT
    • else Check for RIGHT. If RIGHT is pressed…
      • Exit checks. We have DOWN and RIGHT.
    • else Exit checks. We have DOWN.

When I write it out that way, you can see that this type of logic (adding GOTO to skip steps) means that the program would be fastest checking for UP and LEFT. And slowest for checking for JUST down. This means a game would move at different speeds based on which direction is detected. While this is a great optimization, it may not be desirable since a game may wish consistent speed (i.e. always worst case) than having a speed that varies.

Big thanks to craig for spotting my typo, and providing these two additional optimizations. I really like the “only POKE if it changed” one. It would be fun to benchmark and see if “worst case” all four arrows were being held down, is this slower than just clearing them all at once.

IF ELSE SLOWER

One additional note about using IF… Once BASIC starts processing a line, it has to parse the entire line whether that code is executed or not. For example:

IF A=42 THEN DO THIS:DO THAT:DO THE OTHER:DO NOTHING:DO OVER

Even if A is not 42, BASIC still has to at least scan through all the tokens and such on the rest of the line to skip it. Also, since ELSE could be used on Extended Color BASIC, there could be an ELSE clause that still needs to be processed:

10 IF A=42 THEN DO THIS:DO THAT:DO THE OTHER:DO NOTHING:DO OVER ELSE ...

Because of this, IF/ELSE can actually be slower than doing something like:

10 IF A=42 THEN 30
20 DO OVER:GOTO 40
30 DO THIS:DO THAT:DO THE OTHER:DO NOTHING:GOTO 40
40 ...continue... 

This looks quite awful, but that’s how I had to program on my Commodore VIC-20 because it had no ELSE. And, it turns out, this can be quite a bit faster! Now line 10 checks for A to be 42, and if it is, it skips a line (which is fast) to get to 30 and hande it. If it is NOT 42, it quickly skips two lines instead to having to parse through a whole line of BASIC tokens.

I benchmarked something like this in an earlier article series, and was very impressed at the speedups that can be achieved just by making any IF line use a GOTO… though if it’s likely the line is true (A=42) more often than it’s not, it might not make sense. One size does not fit all.

And with that, I’ll end today’s installment without providing anything new, other than some handy speedups craig showed us.

We now have some quick ideas on using arrow keys to change X and Y coordinates. X and Y coordinates are used in games like Atari Adventure and Pac-Man to know which direction to send an enemy at the player. In the case of Pac-Man (see my earlier article series on that one), the ghosts target spots around (or on top of ) Pac-Man and decide which direction to turn based on which choice would be closer (using the wonderful Pythagorean theorem we learned about in school).

BUT, if our program was not using that, and just wanted a screen location to POKE a player character to, we could probably simplify this keyboard code a bit and use less variables.

To be continued…