Author Archives: Allen Huffman

About Allen Huffman

Co-founder of Sub-Etha Software.

UnderColor’s spiral challenge from 1984 – part 2

See Also: part 1, part 2, with part 3 and part 4 coming (and maybe more).

These types of posts are probably my favorite. Someone posts something with a coding challenge, and folks start submitting their ideas, and I get to play collector and just repost without doing any work… (Well, except I am submitting ideas as well, this time.)

This all kicked off when Michael Pittsley shared a contest he found in a 1984 issue of UnderColor magazine that challenged BASIC programmers to draw a specific spiral pattern:

Michael tackled it with this code:

10 ' SPIRAL THING
20 '
30 XL=3:YT=0:XR=252:YB=188:PX=3
40 PMODE 4,1
50 PCLS
60 SCREEN 1,1
65 LINE (XL,YB) - (XR-PX,YB),PSET
200 ' RIGHT
210 XR=XR-PX
220 LINE - (XR,YB),PSET
300 ' UP
306 YT=YT+PX
307 IF YT=96 THEN 600
310 LINE - (XR,YT), PSET
400 ' LEFT
410 XL=XL+PX
420 LINE - (XL,YT),PSET
500 ' DOWN
510 YB=YB-PX
520 LINE - (XL,YB),PSET
550 GOTO 200
600 GOTO 600

A nicely formatted and documented (with REMmarks) example of the power BASIC. He was even aware that LINE does not need both a start and end point each time you use it. If you just do “LINE-(x,y),PSET” it will draw from the last point to the new coordinates. This greatly reduces the amount of parsing BASIC has to do versus if you wrote the program always having a pair of start/end points.

Let’s modify it with some timing stuff.

10 ' SPIRAL THING
20 '
25 TIMER=0
30 XL=3:YT=0:XR=252:YB=188:PX=3
40 PMODE 4,1
50 PCLS
60 SCREEN 1,1
65 LINE (XL,YB) - (XR-PX,YB),PSET
200 ' RIGHT
210 XR=XR-PX
220 LINE - (XR,YB),PSET
300 ' UP
306 YT=YT+PX
307 IF YT=96 THEN 600
310 LINE - (XR,YT), PSET
400 ' LEFT
410 XL=XL+PX
420 LINE - (XL,YT),PSET
500 ' DOWN
510 YB=YB-PX
520 LINE - (XL,YB),PSET
550 GOTO 200
600 PRINT TIMER/60

Running it reports 3.16666667 seconds at the end, so really close to the time my version using DRAW got. AND, that is with REMs and spaces and multiple lines to parse. Let’s see what tricks we can do to speed it up by removing spaces, REMs, and packing lines together:

25 TIMER=0:XL=3:YT=0:XR=252:YB=188:PX=3::PMODE4,1:PCLS:SCREEN1,1:LINE(XL,YB)-(XR-PX,YB),PSET
200 XR=XR-PX:LINE-(XR,YB),PSET:YT=YT+PX:IFYT=96THEN600
310 LINE-(XR,YT),PSET:XL=XL+PX:LINE-(XL,YT),PSET:YB=YB-PX:LINE-(XL,YB),PSET:GOTO200
600 PRINT TIMER/60

This drops it down to 3.03333334! Parsing LINE may be faster than parsing a DRAW string. Also, longer line numbers take longer to parse, so we could RENUM0,0,1:

0 TIMER=0:XL=3:YT=0:XR=252:YB=188:PX=3:PMODE4,1:PCLS:SCREEN1,1:LINE(XL,YB)-(XR-PX,YB),PSET
1 XR=XR-PX:LINE-(XR,YB),PSET:YT=YT+PX:IFYT=96THEN3
2 LINE-(XR,YT),PSET:XL=XL+PX:LINE-(XL,YT),PSET:YB=YB-PX:LINE-(XL,YB),PSET:GOTO1
3 PRINTTIMER/60

However, this did not seem to make any consistent measurable difference.

A further optimization could be done by changing the 2-letter variables to 1-letter. But, beyond that, it would take adjusting the logic to find more improvements. Parsing integers is slow, such as “IF YT=96” as is parsing line numbers (thus, lines 1, 2 and 3 should be faster to “GOTO 1” than 100, 200, 300 and “GOTO 100”).

And, one question:: Michael started in from the corner of the screen. Here is a screen shot done in the Xroar emulator with artifact colors turned off so we can see the actual lines better:

This means this version is drawing a few lines less than the version I made. I need to go back and re-read the article and see if I got the details wrong, or if Michael is just keeping the 3-pixel border more consistent. I like the 3-pixel padding, but perhaps the first line to the right on the bottom should have gone further (3 pixel padding) and then at the top left a bit more (3 pixel padding).

Meanwhile, in response to Michael Pittsley‘s post. Andrew Ayers took the version I did and modifies it to work on the CoCo 3. The original contest was a few years before the CoCo 3 even existed, but one would imagine if the article had appeared later it might have had a contest for fastest CoCo 1/2 version, and fastest CoCo 3 version:

A couple more slightly improved CoCo 3 versions of Allen’s code above:

Both of these take longer to run than the original Allen posted, even with the high-speed poke…at least, running on XRoar Online (no idea about real hardware).

– Andres Ayers

CoCo 3 RGB b/w 320×192:

5 POKE65497,0
10 TIMER=0
20 HSCREEN2:PALETTE0,0:PALETTE1,63:HCOLOR1
30 W=320:H=191:HDRAW"BM0,191"
40 HDRAW"R=W;"
50 HDRAW"U=H;L=W;"
60 H=H-3:W=W-3
70 HDRAW"D=H;R=W;"
80 H=H-3:W=W-3
90 IF H>0 THEN 50
100 TM=TIMER
110 IF INKEY$="" THEN 110
120 PRINT TM/60
130 POKE65496,0

CoCo 3 RGB b/w 640×192:

5 POKE65497,0
10 TIMER=0
20 HSCREEN4:PALETTE0,0:PALETTE1,63:HCOLOR1
30 W=640:H=191:HDRAW"BM0,191"
40 HDRAW"R=W;"
50 HDRAW"U=H;L=W;"
60 H=H-3:W=W-3
70 HDRAW"D=H;R=W;"
80 H=H-3:W=W-3
90 IF H>0 THEN 50
100 TM=TIMER
110 IF INKEY$="" THEN 110
120 PRINT TM/60
130 POKE65496,0

It has been so long since I worked in BASIC on the CoCo 3 that I had forgotten about HDRAW and such being there. And, as expected, it takes longer to draw 320 or 640 pixel lines than it would to draw the 256 pixel lines on the CoCo 1/2 PMODE 4 display.

This does make me wonder how it would compare if limited to the same 256×192 resolution of PMODE 4. I expect the overhead of banking in and out the CoCo 3 high resolution graphics screens will add some extra time, and likely working with a screen with more than just 1-bit color (on/off pixels) is more memory to plot through.

Anyone want to work on that experiment?

Make it faster!

Revisiting my version from part 1, I changed the screen to PCLS1 (to make it white) and set COLOR 0 (to make the drawing black) so it would look like the printout in the magazine. This looks nice, but now we cannot tell how close to the original border of the image my results are:

I also see that because of the 256×191, the very last line does not appear to have a 3 pixel padding. Maybe we can look at the math later.

I simply took my version and combined lines and removed any spaces:

0 'SPIRAL2.BAS
5 FORA=1TO1000:NEXT
10 TIMER=0:PMODE4,1:PCLS1:SCREEN1,1:COLOR0:W=255:H=191:DRAW"BM0,191R=W;"
50 DRAW"U=H;L=W;":H=H-3:W=W-3:DRAW"D=H;R=W;":H=H-3:W=W-3:IFH>0THEN50
100 TM=TIMER
110 IF INKEY$="" THEN 110
120 PRINT TM/60

I could also do the “RENUM0,0,1” trick, but all of this only gets me to 3.016666667 seconds.

NOTE: I put a FOR/NEXT at the start so when I type “RUN” I have a moment to release the ENTER key before the timing starts. If you hit a key during the drawing, BASIC tries to handle that key and it will slow down the program. Run it and pound on the keyboard while it is drawing and you can see it slow down quite a bit (I got 3.7 seconds doing that).

But I digress…

Let’s collect some more examples, and see what other methods folks come up with. Now I want to get the same logic, just DRAW versus LINE to draw the lines, and see which one is faster.

To be continued…

UnderColor’s spiral challenge from 1984 – part 1

See Also: part 1, part 2, with part 3 and part 4 coming (and maybe more).

And now back to CoCo …

– Michael Pittsley posted in the TRS-80 Color Computer (CoCo) group on Facebook:

Many of us have our CoCos and have memories or how good we once were writing basic programs on it. Including myself. I found this article in the first UnderColor magazine. It was a contest to see who could write an ECB program that created a spiral. — Write an Extended Basic program that draws a spiral figure on graphics screen 0 on PMODE 4. The figure, when done should look like the picture. Use any combination of Basic commands, but no assembly language. The winner will be the person whose program executes in the shortest possible time. (Entries that simply list a series of LINE commands will be disqualified). I took a stab at it and realized how much I had forgotten about basic, so this was fun for me. I put my results as the first comment. Feel free to try your hand at it, post a screen shot and the time it took to complete.

– Michael Pittsley

This caught my attention.

UnderColor magazine (1984-1985) was one I never saw, though the name sounds familiar so I may have at least read a reference to it, or seen an ad for it somewhere. You can find the issues preserved here:

Search – TRS-80 Color Computer Archive

The article in question appeared in the first issue, which you can read here:

UNDERCOLOR Vol1 No 1 Dec 10, 1984

The article, by Bill Barden, presented a contest to see who could write a program in BASIC (no assembly allowed) that would generate a spiral as demonstrated by this graphic:

The winner would be the program that could do this in the least amount of time.

I have discussed drawing spirals on the CoCo in the past, but not like this. I also wrote about spirals for a text mode attract screen, but not like this.

So now let’s spiral like this.

LINE versus DRAW

The most obvious approach would be to use the LINE command. It takes a set of X and Y coordinates and draws a line between them, like this:

LINE (0,0)-(255,191),PSET

However, with what I know about BASIC these days (and wish I knew back then), that is alot of parsing of numbers and characters and such. That makes it slower than it might need to be.

One shortcut is that LINE remembers where it left off, so you can start a new line just by specifying the destination:

LINE-(127,0),PSET

Doing this trick should speed up a spiral program, since you only need to give the starting point once, then you can just “draw to the next spot” from then on out.

But I did not attempt this. Instead, I thought about DRAW.

The DRAW command is very powerful, and does allow you to draw to specific coordinates. You can do a “blank” draw just to move the starting point, like this:

DRAW"BM0,191"

That will do a Blank Move to (0,191), which is the lower left corner of the screen and the location where the spiral is supposed to start.

You can then do things like…

DRAW"R10"

…and that will draw a line 10 pixels to the right. (Well, the coordinates are scaled, I think, so it is 10 pixels on a PMODE 4 screen, but at other lower resolutions, that number of pixels will be scaled down.)

How can we spiral like that? One way would be to build a string and append it:

X=100
X$=STR$(X)
DRAW"R"+X$

That works, but all that parsing and creating strings and such would certainly be slower than using a built-in feature of DRAW which lets you use a variable inside the quotes! You just put “=” before the variable name, and a “;” after it.

X=100
DRAW"R=X;"

That will draw to the right however many pixels X is set to!

Could this be faster than line?

Here is what I came up with:

0 'SPIRAL1.BAS
10 TIMER=0
20 PMODE4,1:PCLS:SCREEN1,1
30 W=255:H=191:DRAW"BM0,191"
40 DRAW"R=W;"
50 DRAW"U=H;L=W;"
60 H=H-3:W=W-3
70 DRAW"D=H;R=W;"
80 H=H-3:W=W-3
90 IF H>0 THEN 50
100 TM=TIMER
110 IF INKEY$="" THEN 110
120 PRINT TM/60

This sets a TIMER variable at the start, draws the spiral, then reads the value of the TIMER again. When you press any key, the program exits and prints the time (TIMER/60) it took to run the main code.

Here is what I get:

And pressing a key shows me:

3.03333334

Three seconds.

I expect I can optimize my program to speed it up. In the meantime, what can you come up with? Is there a faster way?

Let’s play!

C and VLAs (Variable Length Arrays)

When you are old (or “experienced” if you prefer), you begin to realize how much of what you learned is wrong. Even if it was “right” when you learned it. I think of all peers that went through computer courses at colleges back in the late 1980s or 1990s, learning now-obsolete languages and being taught methods and approaches that are today considered wrong.

When I learned C, it was on a pre-ANSI K&R C compiler. I learned it on my Radio Shack Color Computer 3 under the OS-9 operating system, with assistance from a friend of mine who had learned C on his Commodore Amiga.

I had alot of new things to learn in 1995 when I took a job with Microware Systems Corporation (creator of OS-9 and the K&R compiler I had learned on). Their Ultra-C compiler was an ANSI compiler, and it did things quite different.

In that era of the C89/C90 standard, arrays were just arrays and we liked it that way:

int array[42];

if you wanted things to be more flexible, you had to malloc() memory yourself.

int *array = malloc (sizeof(int)*42);

…and remember to stay within your boundaries and clean up/free that memory when you were done with it.

But C99 changed this, somewhat, with the introduction of VLAs (Variable Length Arrays). Now you could declare an array using a variable like this:

int x=42;

int array(x);

Neat. I do not think I have ever used this. One downside is you cannot do this with static variables, since those are created/reserved at compile time. But it is still neat.

But today I learned, you couldn’t rely on VLA is you were using C11. Apparently, they became optional that year. A compiler would define a special define if it did not support them:

__STD_NO_VLA__

But at least for twelve years of the standard, you could rely on them, before not being able to rely on them.

And then C23 happened, which I just learned made VLAs mandatory again.

So, uh, I guess if you have the latest and greatest, you can use them. For now. Until some future change makes them option again. Or removes them. Or whatever.

Still neat.

But I doubt any of the embedded C compilers I use for my day job support them.

Ciaran “Xroar” Anscomb’s PCLEAR 0 without assembly!

On the CoCo mailing list, Ciaran (author of the Xroar emulator), said this:

FWIW this is the bodge I have saved out for doing PCLEAR0 without such
ROM patching:

POKE183,PEEK(183)-6:POKE188,PEEK(188)-6:PCLEAR1:POKE183,PEEK(183)+6:POKE188,PEEK(188)+6

Annoyingly verbose, but should account for DOS, and works on the
Dragon too.

..ciaran

https://pairlist5.pair.net/mailman/listinfo/coco

This topic came up because of Juan Castro‘s experiments with updating HDB-DOS to add new functionality on a CoCo 1 and 2 (but that is a discussion for a dedicated blog post sometime). Juan had recently “fixed” Extended Color BASIC to allow using “PCLEAR 0” to remove all graphics memory and give more RAM to BASIC. I have discussed PCLEAR 0 in the past

This mysterious line performs a PCLEAR 0 without needing to load and run program of assembly code!

POKE183,PEEK(183)-6:POKE188,PEEK(188)-6:PCLEAR1:POKE183,PEEK(183)+6:POKE188,PEEK(188)+6

And it works!

But … how does it work!?!

Ciaran, you’ve got some ‘splainin’ to do…

Until then…

A safer memcpy with very limited use cases

Here is a quick one… At my day job, I found lines of code like this:

memcpy(systemDataPtr->serialNumber, resp.serialNumber, 16);

A quick peek at systemDataPtr->serialNumber shows it defined as this:

unsigned char serialNumber[MAX_SERIAL_NUMBER_LENGTH];

…with that constant defined as:

#define MAX_SERIAL_NUMBER_LENGTH        16

So while 16 is correct, the use of hard-coded “magic numbers” (hat tip to a previous manager, Pete S., who introduced me to that term) is probably best to be avoided. Change that #define, and things could go horribly wrong with a memory overrun or massive nuclear explosion or something.

One simple fix is to use the #define in the memcpy:

memcpy(systemDataPtr->serialNumber, resp.serialNumber, MAX_SERIAL_NUMBER_LENGTH);

This, of course, assumes that resp.serialNumber is also 16. Let’s see:

char serialNumber[16];

Ah, magic number! In this case, it comes from a DLL header file that does not share that #define, and the header file for the DLL was made by someone who had never made a Windows DLL before (me) and did not make #defines for these various lengths.

What if the DLL value ever got out-of-sync? Worst case, not all data would be copied (only 16 bytes). That seems fine. But if the DLL value became smaller, like 10, then the memcpy would still copy 16 bytes, copying the 10 from the DLL buffer plus 6 bytes of data in memory after it — buffer overrun?

In this case, since the destination buffer can hold 16 bytes, and we only copy up 16 bytes, the worst case is we could get some unintended data in that buffer.

sizeof() exists for a reason.

One thing I tend to do is use sizeof() instead of hard-coded numbers or the #define, since it will continue to work if the source buffer ever got changed from using the #define:

memcpy(systemDataPtr->serialNumber, resp.serialNumber, sizeof(systemDataPtr->serialNumber));

But this still has the same issue if the source resp.serialNumber became larger.

A safer, and more ridiculous, memcpy

Naturally, I came up with a ridiculous “solution”: A safer memcpy() that is much more of a pain to use because you have to know the size of each buffer and tell it the size of each buffer so it can make sure not to copy something larger than will fit into the destination buffer.

Here is the prototype of memcpy():

void * memcpy ( void * destination, const void * source, size_t num );

It will blindly copy up to “num” bytes from “source” to “destination”. But a ridiculous safer memcpy might look like this:

void * memcpy_safer ( void * destination, size_t sizeOfDestination,
                      const void * source, size_t sizeOfSource,
                      size_t num );

Just think of the extra overhead to add two more parameters for every use! Plus, it is a longer function name so you get to type even more! Wonderful.

Here is a quick implementation:

void * memcpy_safer ( void * destination, size_t sizeOfDestination,
                      const void * source, size_t sizeOfSource,
                      size_t num )
{
    // Use whichever size is the smallest.
    if ((num > sizeOfDestination) || (num > sizeOfSource))
    {
        if (sizeOfDestination < sizeOfSource)
        {
            num = sizeOfDestination;
        }
        else
        {
            num = sizeOfSource;
        }
    }
    
    return memcpy ( destination, source, num);
}

Now that the function knows the size of source and destination, it can do a simple check to make sure to use whichever is smallest for the max number of bytes to copy — regardless (or irregardless, depending on which you prefer) of how many bytes you specified.

If both buffers are larger than the “num” passed in, num is used. But if “num” is larger than either buffer, it is reduced to match the largest of the two buffers.

Note that this does not do any NULL checks, so it is relying on the behavior of memcpy() which would likely be a crash if NULL is passed in. To improve, NULL checks could be added.

I decided to pass this function into Bing CoPilot and see what it “thought” about this:

Limitations & Risks

Silent truncation: If num is too large, the function silently reduces it. This can lead to subtle bugs if the caller expects all bytes to be copied.

No error signaling: There’s no return code or logging to indicate that truncation occurred.

Still not “safe” in all cases: If destination or source are NULL, or if the buffers overlap, memcpy may still cause undefined behavior.

No runtime buffer validation: It assumes the caller provides correct buffer sizes, which may not be guaranteed.

– Bing CoPilot

And, of course, the A.I. offered up these changes:

#include <string.h>
#include <stddef.h>
#include <stdio.h>

void * memcpy_safer ( void * destination, size_t sizeOfDestination,
                      const void * source, size_t sizeOfSource,
                      size_t num )
{
    if (!destination || !source) {
        fprintf(stderr, "Error: NULL pointer passed to memcpy_safer.\n");
        return NULL;
    }

    if (num > sizeOfDestination || num > sizeOfSource) {
        size_t adjusted = (sizeOfDestination < sizeOfSource) ? sizeOfDestination : sizeOfSource;
        fprintf(stderr, "Warning: Truncating copy from %zu to %zu bytes.\n", num, adjusted);
        num = adjusted;
    }

    return memcpy(destination, source, num);
}

That version adds NULL checks, returns a NULL if either buffer passed in was NULL, and adds prints to standard error if a NULL happens or if the value was truncated.

Not bad, predictive language model.

My ridiculous test program

Here is my test program, which I wrote using the Online GDB C compiler:

/******************************************************************************

Welcome to GDB Online.
  GDB online is an online compiler and debugger tool for C, C++, Python, PHP, Ruby, 
  C#, OCaml, VB, Perl, Swift, Prolog, Javascript, Pascal, COBOL, HTML, CSS, JS
  Code, Compile, Run and Debug online from anywhere in world.

*******************************************************************************/
#include <stdint.h> // for uint8_t
#include <stdio.h>  // for printf()
#include <stdlib.h> // for EXIT_SUCCESS
#include <string.h> // for memcpy()

/*---------------------------------------------------------------------------*/
// PROTOTYPES
/*---------------------------------------------------------------------------*/

void * memcpy_safer ( void * destination, size_t sizeOfDestination,
                      const void * source, size_t sizeOfSource,
                      size_t num );

void * memcpy_safer2 ( void * destination, size_t sizeOfDestination,
                       const void * source, size_t sizeOfSource,
                       size_t num );

void initializeBuffer (void *dataPtr, size_t dataSize, uint8_t value);

void dumpBuffer (const char* prefix, void *dataPtr, size_t dataSize);

/*---------------------------------------------------------------------------*/
// MAIN
/*---------------------------------------------------------------------------*/

int main()
{
    uint8_t smallerBuffer[10];
    uint8_t largerBuffer[15];
    
    // Test 1: copy longer buffer into smaller buffer.
    
    printf ("\nInitialized buffers:\n\n");    
    
    // Initialize buffers with something we can identify later.
    initializeBuffer (smallerBuffer, sizeof(smallerBuffer), 0x1);
    dumpBuffer ("smallerBuffer", smallerBuffer, sizeof(smallerBuffer));

    initializeBuffer (largerBuffer, sizeof(largerBuffer), 0x2);
    dumpBuffer ("largerBuffer ", largerBuffer, sizeof(largerBuffer));

    printf ("\nTest 1: Copying largerBuffer into smallerBuffer...\n\n");

    memcpy_safer (smallerBuffer, sizeof(smallerBuffer), largerBuffer, sizeof(largerBuffer), 42);

    dumpBuffer ("smallerBuffer", smallerBuffer, sizeof(smallerBuffer));

    // Test 2: copy smaller buffer into larger buffer.

    printf ("\nInitialized buffers:\n\n");

    // Initialize buffers with something we can identify later.
    initializeBuffer (smallerBuffer, sizeof(smallerBuffer), 0x1);
    dumpBuffer ("smallerBuffer", smallerBuffer, sizeof(smallerBuffer));

    initializeBuffer (largerBuffer, sizeof(largerBuffer), 0x2);
    dumpBuffer ("largerBuffer ", largerBuffer, sizeof(largerBuffer));

    printf ("\nTest 2: Copying smallerBuffer into largerBuffer...\n\n");

    memcpy_safer (largerBuffer, sizeof(largerBuffer), smallerBuffer, sizeof(smallerBuffer), 42);

    dumpBuffer ("largerBuffer ", largerBuffer, sizeof(largerBuffer));

    return EXIT_SUCCESS;
}


/*---------------------------------------------------------------------------*/
// FUNCTIONS
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
// My ridiculous "safer" memcpy.
/*---------------------------------------------------------------------------*/
void * memcpy_safer ( void * destination, size_t sizeOfDestination,
                      const void * source, size_t sizeOfSource,
                      size_t num )
{
    // Use whichever size is the smallest.
    if ((num > sizeOfDestination) || (num > sizeOfSource))
    {
        if (sizeOfDestination < sizeOfSource)
        {
            num = sizeOfDestination;
        }
        else
        {
            num = sizeOfSource;
        }
    }
    
    return memcpy ( destination, source, num);
}


/*---------------------------------------------------------------------------*/
// Bing CoPilot changes.
/*---------------------------------------------------------------------------*/
void * memcpy_safer2 ( void * destination, size_t sizeOfDestination,
                       const void * source, size_t sizeOfSource,
                       size_t num )
{
    if (!destination || !source) {
        fprintf(stderr, "Error: NULL pointer passed to memcpy_safer.\n");
        return NULL;
    }

    if (num > sizeOfDestination || num > sizeOfSource) {
        size_t adjusted = (sizeOfDestination < sizeOfSource) ? sizeOfDestination : sizeOfSource;
        fprintf(stderr, "Warning: Truncating copy from %zu to %zu bytes.\n", num, adjusted);
        num = adjusted;
    }

    return memcpy(destination, source, num);
}


/*---------------------------------------------------------------------------*/
// Utility function to initialize a buffer to a set value.
/*---------------------------------------------------------------------------*/
void initializeBuffer (void *dataPtr, size_t dataSize, uint8_t value)
{
    if (NULL != dataPtr)
    {
        memset (dataPtr, value, dataSize);
    }
}


/*---------------------------------------------------------------------------*/
// Utility function to dump bytes in a buffer, with an optional prefix.
/*---------------------------------------------------------------------------*/
void dumpBuffer (const char* prefix, void *dataPtr, size_t dataSize)
{
    if (NULL != dataPtr)
    {
        if (NULL != prefix)
        {
            printf ("%s: ", prefix);
        }

        for (size_t idx=0; idx<dataSize; idx++)
        {
            printf ("%02x ", ((uint8_t*)dataPtr)[idx]);
        }
        printf ("\n");
    }
}

// End of memcpy_safer.c

If you want to run it there, you can use this link:

https://onlinegdb.com/Eu7FToIcQ

But of course, I am not using this code. It is ridiculous and requires extra typing.

Besides, I know exactly what I am doing in C and never make any mistakes… Really.

Until next time…

Google Street View scripts and A.I. emojis

When capturing video for Google Street View, Google recommends using 1 frame per second video for walking, and 5 frames per second for biking and lower speeds. A full 30 or even 60 fps video is unnecessarily huge and will take much longer to upload and process … and most of the frames will be discarded by Google anyway.

I had one of the A.I.s (probably CoPilot) automate using the ffmpeg open source command line tool so I could batch convert files in a directory. A very rough work-in-progress version is on my GitHub now:

allenhuffman/GoogleStreetViewScripts: Scripts for converting videos before uploading to Google Street View

I have noticed the A.I.s are starting to put emojis in things — including code and scripts they generate!

I don’t even know how to type emojis in uMacs or VI ;-) but apparently they are supported these days.

Have you noticed the increase in emojis in A.I. responses lately?

I’d end this post with an emoji, but I do not know how to type one in WordPress . . .

DJI OSMO 360 launched… but you cannot buy it (in the USA)

UPDATE: There is now a third party reseller offering the DJI Osmo 360 on Amazon, but shipping doesn’t happen until mid-September. Since this is not Amazon shipping or an official DJI storefront, buyer beware. Check reseller ratings before ordering anything.

UPDATE #2: More “confirmation” as I check out Facebook posts. I keep seeing posts from folks who have had access to pre-release review units saying “not available in the US for the foreseeable future.” That is unfortunate. It looks like the Osmo 360 might be better for low-light 360 and I’d get one just for that.

UPDATE #3: According to this REDDIT post, which includes a chat response from DJI, there is no official sale of the DJI Osmo 360 in the USA at this time. BUYER BEWARE of any third party sellers offering it in the USA.


This morning at 7am central time, DJI officially launched their new DJI OSMO 360 camera. But, like almost everything else on the DJI website, it is unavailable in the U.S.A.

This situation was predicted by many DJI followers, based on how almost nothing they sell is currently available to purchase in the U.S.A. Basically no drones, no cameras, and no microphones are being sold to the U.S.A. currently.

The reasons speculated by video bloggers are varied, and are all based on speculation. The two top speculations I have seen include:

  • No Slave Labor – The situation with the Uyghurs (pronounced “weegers”, which you may have heard of) being used as forced/slave labor. Apparently there is something passed by the previous administration that would prevent those items from being brought into the U.S.A.
  • Tariffs – The situation with tariffs and our current administration. This one does not really ring true, since tariffs would seemingly only make things more expensive. And, plenty of other China-based entities have products available here in the U.S.A. Insta360 products are readily available, and you can still order from Alibaba, Aliexpress and Temu, for example.

Thus, my goal of getting one of these on launch day will be delayed while we figure out if we can get them at all. Instead of a pre-order or long delivery window, they just say “out of stock.” But so does most other things I have spot-checked on the website.

The FOMO is real!

If you get one here in the U.S.A., please leave a comment and tell us where and how you got it. This isn’t even listed on Amazon… yet.

More to come…

“GPS data jumps around a lot” and Google Street View

Updates:

  • 2025-09-03 – Added details on how I got the python tool running on macOS.

Recently I posted a list of Google Street View upload errors I have received. I found a way to deal with one of them.

GPS data jumps around a lot

After some “research” (which you can assume means “googling” and “asking A.I.”) I learned that there can be gaps in GPS data that make the movement appear to spike. I found a Mac Store program called GPX Editor which I purchased for the wonderful price of $4.99 so I could look at the data closer:

GPX Editor showing spikes in the GPS data.

This program allowed me to look at my data in a much finer (i.e. zoomed in) way than Google Maps does. I could see spikes in the data that looked as if I suddenly popped to another location, as if I went from walking to zooming at 17 mph. This may have been caused by a GPS glitch as I walked under a bridge or some other obstacle that affected the GPS reading.

While you are looking at that screen shot, notice on the right side you can see how much time there is between GPS entries. Google Street View wants 5 seconds or less, I believe, so when gaps appear at 8 seconds, that will also cause an error and the GPX file will be rejected.

What to do, what to do…

Google wants real GPS data, but it seems fixing an obvious glitch like this should be acceptable. Basically, just moving a point back to where it was. I am sure there are tools like GPX Editor that would allow moving one point and fixing it.

But I am lazy, and wondered if there was an easier way.

I ended up looking for a tool to interpolate the GPS data. Here is the one I found:

https://github.com/remisalmon/gpx-interpolate

It will read a GPX file and then smooth out the points. Here is the example screen shot from that Github repository:

The black dots are actually GPS entries, and the red is where it can create new entries. For slow data, such as walking, this should work really well. And, I notice it has the ability to limit how often the data ends up in the file:

usage: gpx_interpolate.py [-h] [-r RES] [-n NUM] [-s] FILE [FILE ...]

interpolate GPX files using piecewise cubic Hermite splines

positional arguments:
FILE GPX file

optional arguments:
-h, --help show this help message and exit
-r RES, --res RES interpolation resolution in meters (default: 1)
-n NUM, --num NUM force point count in output (default: disabled)
-s, --speed save interpolated speed

Running gpx_interpolate on macOS

Side note: To get this running, I had to install a few other things. In order to install them, I had to use some a “virtual environment” for Python so the stuff I installed was isolated from the Python that came with macOS (or so I think). Here were the commands I ran:

cd Movies ***OR WHEREVER YOU WANT TO INSTALL THIS***
python3 -m venv venv
source venv/bin/activate
pip install gpxpy
pip3 install numpy
pip3 install scipy

After that, I could “python3 gpx_interpolate.py -r 1 my_gpx_file.gpx” and get the interpolated file.

The instructions I used (from Google’s A.I.) said I would need to “source venv/bin/activate” each time I started a Terminal and wanted to use this. I will update this as I learn more and understand what this is all about.

The results…

Perhaps I could use the “-r” option so it drops a marker every 1 meter or so. As long as I was walking at a regular pace, that should make new data that is much smoother without the jumps and gaps.

GPX Editor showing less spikes in the GPS data after applying interpolation.

This file now seems to have much more consistent entries, and still follows the walking path closely.

I was able to upload the video file and this new interpolated GPX file and it is now live on Google.

But I am sure we can do better. To be continued…

I only understand this a bit…

NOTE: Credit to the content of the article, other than my comments about the content, goes to whatever A.I. engine Microsoft uses for CoPilot…

While trying out a feature of Microsoft’s CoPilot A.I., I decided to ask it a simple question:

What is a good way to count the set bits in a 32-bit integer in the C programming language?

At my day job, we collect a byte from boards that multiplex eight sub-boards. There can be up to four of these multiplexer boards for a total of 32 sub-boards being multiplexed. Bits in those bytes will be set to indicate if a sub-board was found. The original designer has code that retrieves a byte from each multiplexer board then combines them into a 32-bit integer. There is code that counts the set bits to make sure we found as many boards as the system is configured to expect.

To my surprise, CoPilot gave me five different routines. The first was easy to understand, and a bit more clever than what I would have created. It called this the Iterative Bitwise Method with a description of “simple and portable, but not the fastest.”

int countSetBits(int n) {
    int count = 0;
    while (n) {
        count += n & 1;
        n >>= 1;
    }
    return count;
}

Next was something called the Brian Kernighan’s Algorithm, which was “efficient for sparse bit patterns.”

int countSetBits(int n) {
    int count = 0;
    while (n) {
        n &= (n - 1);
        count++;
    }
    return count;
}

Third was a Lookup Table Method for “fast for repeated calls but requires extra memory.”

unsigned char lookupTable[256];

void initializeLookupTable() {
    for (int i = 0; i < 256; i++) {
        lookupTable[i] = countSetBits(i); // Use any method to count bits
    }
}

int countSetBitsUsingLookup(int n) {
    return lookupTable[n & 0xFF] +
           lookupTable[(n >> 8) & 0xFF] +
           lookupTable[(n >> 16) & 0xFF] +
           lookupTable[(n >> 24) & 0xFF];
}

Fourth was something I had never heard of: Compiler Intrinsics. It said “may modern compilers provide built-in functions to count set bits efficiently” and described it as “extremely fast but platform-dependent.”

#include <intrin.h>

int countSetBits(int n) {
    return __popcnt(n);
}

In my world of “barely C” embedded compiles that don’t even support the modern printf formats for printing pointers, I do not expect to have access to this.

And fifth was Parallel Bitwise Operations, described as “fast and portable, but harder to understands.”

And that is the version that made me post this:

int countSetBits(int n) {
    n = n - ((n >> 1) & 0x55555555);
    n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
    n = (n + (n >> 4)) & 0x0F0F0F0F;
    n = n + (n >> 8);
    n = n + (n >> 16);
    return n & 0x3F;
}

Harder to understand is an understatement. My brane hurts just looking at that trying to figure out how it works.

And I thought I’d share that hurt with you.

Have a good C day…