Selling five 6TB Western Digital Red Pro hard drives

Now that my Synology DS1522+ has had all its drives upgraded, I am selling the five 6TB drives I was using before. These drives were purchased during July-August 2022. I chose them because they offered a five year warranty, which means the drives still have warranty coverage in to 2027.

These drives went as low as $150 on Amazon (so, $750 of drives) and I will sell them for half that — $375 for all five. All have had a 2-pass “secure erase” done with no issues reported. Since they still have warranty in to 2027, if there was an issue, they should still be covered.

Let me know if you are interested. I am about to list them on eBay — I’ve always had success selling batches of drives there for beyond my minimum, but maybe someone here wants them first.

Data paranoia, Drobos and Synology NAS

I am in the slow process of upgrading five WD 6TB hard drives in my Synology DS1522+ NAS to Seagate 8TB drives. While folks I asked overwhelming say the larger drives (16TB, 20TB, etc.) “have not had any issues,” I am old school and do not want to make that large of a storage jump just yet. For those as data paranoid as I am, some tips:

  1. Do a low-level format (or “secure erase”) on each new drive first. This will write to every sector, and can catch issues. I have only caught one (or maybe two) drive with issues of the years, but the time spent is worth it to me. I’d rather catch a problem before I install and send them back for a replacement, rather than having an issue show up much later (possibly when the drive is out of warranty).
  2. Until someone can say that a “20TB” drive is as reliable as a smaller drive, upgrage to the next size up that gives you enough storage. The less dense the data, the safer it “should” be. Also, if a 6TB drive fails, the rebuild time to replace it will be significantly faster than replacing a 20TB drive. And, during the rebuilt time, your data is at risk. I run dual drive redundancy so during the 12 hours my NAS rebuilds, if I have a second drive fail, I am still okay… but if that happens I have no protection from a third failure. Doing 20 hours rebuilds creates a much larger window for data loss if something goes terribly wrong.
  3. And, of course, make sure anything important is on a backup drive (I use a standalone 10TB just to clone my most important “can’t live without” data), and have an offsite backup of that (I then have that entire drive backed up to a cloud backup service).

If my home burns down, I should at least be able to get back the 10TB of “can’t live without” data from my offsite backup.

Hardware Redundancy is Better

Sadly, Synology units are much more expensive than my Drobos were. My Drobo 5C was $300 retail. I had two that I paid maybe $250 each for. That let me have two 5-bay units for hardware redundancy. This means if a Drobo suddenly died, I still had a second unit with duplicate data (I would sync the two drives). Spending $500 for two 5-drive units was an easier investment than the $1400 it would take for me to buy two DS1522+.

Eventually I do plan to have a duplicate Synology unit. It doesn’t matter what features the device has if one morning it has died. I would have zero access to my data until the unit is replaced or repaired. Having backup hardware is what I prefer.

But I am data paranoid.

How about you? Are you unlucky, like I am, and have had drives “die suddenly” over the years? After that happens enough, the paranoia sets in. I can’t think of a time in the past decades where I didn’t have three backups of everything important ;-)

DJI RC-N3 remote won’t work with Otterbox Defender Case

Updates:

  • 2024-10-15 – Added link to replacement cable that will fit.

Finding accessories that do not work with a phone in a protective case is not new. Ever since I bought the first iPhone in 2007 I have encountered this issue. It seems accessory makers expect everyone to use their phones unprotected.

It gets worse when you want actual protection (not just a scratch guard case) and use something like a rugged Otterbox Defender case. Almost no accessories seem to work with it, though today’s Defender case is a bit better.

But not with the DJI RC-N3 remote controller.

The unit comes with a USB-C to USB-C cable preinstalled, but includes a USB-C to Lightning cable in the box. Unfortunately, they run the cable out of the side of the plug just a tiny bit too low for it to plug in to the Otterbox Defender opening.

As you will see in this photo, it “almost” works. Had the cable coming out of the side (going down in this photo) been a tiny bit closer to the right end (in this photo), it looks like it would work without issue.

In chatting with DJI Support, I learned that this cable connection is required (at least when pairing to a DJI Neo drone). I thought it might just be used for charging the phone while it is being used with the screen on.

“The Neo can be connected to the Fly APP wireless, but if you want to use the RC-N3 to control it, it’s necessary to connect it with a cable.”

They recommended me take the phone out of the case when using the drone./

“Actually, we suggest you remove the protective cover when connect RC-N3 with your phone.

Because the phone case may cause the disconnection or not recognized.”

Imagine if you had a fancy $1500 phone and needed to remove it from its protective case to use a $199 drone. This is not an optimal solution. And, not any type of solution for a Defender case, which requires disassembly. It would be fine for those rubber slip over covers, but those are more scratch protectors (though I expect they do help from minor dumps and drops).

They did suggest I could try a charging cable, and somewhere I should have the USB-C charging cable that came with my iPhone. I’ll give it a go and see if it works.

Until then, if you plan to use this remote with a phone in a Defender case, you may need to look for an alternative cable to hook it up with.

Good luck! Let me know in the comments if you run in to this problem, and what your solution is (if you have one).

Update

I found an $8 cable on Amazon that will fit in the Otterbox Defender case just fine:

https://www.amazon.com/dp/B09MB2YW88

It has the cable moved out. This is all DJI needs to do.

However, this cable will not “store” inside the remote. The USB-C end is also taller, and the phone holder part that slides out will not slide fully back when this cable is plugged in.

I will keep looking and see if I can find a “more better” cable that will fit the Otterbox Defender and also fit inside the remote when stored.

To be continue…

What are the odds?

Back in the early 1980s, a friend of mine had TRS-80 Model III in their house. His mother was a writer, and had gotten it to use for writing books.

I believe it was this TRS-80 where I saw a version of Monopoly that taught me a strategy I had never learned as a kid:

The computer bought EVERYTHING it landed on, even if it had to mortgage properties to do so.

This simple strategy was a gamble, since you could end up with too many mortgaged properties and no source of income from rent. But. by doing this, the computer would end up owning so many random pieces it made it difficult to own a monopoly yourself.

And since then, that is how I played Monopoly. When it worked, it created some awfully long games (if no one had a monopoly to quickly drive other players bankrupt when they landed on their hotels).

In more modern times, I have watched YouTube videos concerning Monopoly strategies. They will break down the statistical odds of landing on any particular piece of property. For example, you know a pair of dice can produce values from 2 to 12. If truly random, the odds of getting a “1 and 1” should be the same as getting a “3 and 5.” Rather than focus on dice rolls, the strategies take into consideration things that alter locations: Go To Jail, Advance to Go, Take a Ride on Reading, etc.

These cards existing means there are more chances for a player to end up on Go, Reading Railroad, Jail, etc. This means the property squares after those spots have more chances of being landed on.

Board with board games. Move along…

But I digress… As Internet Rabbit Holes do, this led me to watch other videos about statistics in things like card games. In a randomly shuffled deck, there should be as much chance for the first card to be Ace of Spaces as there is for it to be Three of Clubs. It is random, after all.

For that first card drawn, that is a 1 out of 52 chance to be any card in the deck. (52 cards in the deck.)

But as a game plays on, there are fewer cards, so the odds of getting any of the remaining cards increases. For the second card drawn, you now know there is a 0% chance of getting whatever the first card is, and a 1 in 51 chance of getting any of the rest.

And so it continues…

For games like Blackjack or 21, you do not really care if it is a Ten of Diamonds or a King of Hearts or a Queen of Clubs or a Jack of Spades. They all have the value of 10 in the game. Thus, the likelihood of drawing a ten card is much higher than any other card in the deck.

You have four suits (clubs, spades, hearts, diamonds) so there are four of each card – Aces, Two through Ten, Jacks, Queens, and Kings. This means there are 16 cards in the deck that could be a value of 10 in the game. When you draw the first card, you should have a 16 in 52 chance of it being a ten card. That is about a 33% chance!

If you pay attention to what cards have been seen (either by you having it, or seeing it face up with another player), you can eliminate those cards from the possibilities — changing the odds of what you will get.

This is basically what I understand card counting to be. If you play a game, and you know you’ve seen three Kings so far (either in your hand, or played by others), you now know instead of four chances to draw a King, you only have one.

Math is hard. Make the computer do it.

I know this has been done before, and quite possible even on a Radio Shack Color Computer, but I thought it might be fun to create a program that displays the percentage likelihood of drawing a particular card from a deck. I suppose it could have the Blackjack/21 rule, where it treads 10, Jack, Queen and King the same, versus a “whole deck” rule where each card is unique (where you really need an 8 of Clubs to complete some run in poker or whatever game that does that; I barely know blackjack, and have never played poker).

I plan to play with this when I get time, but I decided to post this now in case others might want to work on it as well.

I envision a program that displays all the cards on the screen with a percentage below it. As cards are randomly drawn, that card’s percentage goes to 0% and all the others are adjusted.

It might be fun to visualize.

More to come, when time allows…

M1PQ

Grade school (and junior high, and high school, and college) would have been easier if I could remember all the “facts” I read in text books, or that my teachers told me. But, most of them left my mind moments after they entered. Yet, somehow I can remember…

POKE 113,0:EXEC 40999

I also remember things like:

POKE 65495,0

And even…

FOR A=0 TO 255:POKE 140,0:EXEC 43345:NEXT

Those Radio Shack Color Computer BASIC commands seem to be forever stored in my long term memory. Even some 6809 assembly remained after decades of non-use:

START LDX #1024
LOOP INC ,X+
CMPX #1535
BNE LOOP
BRA START

Why do I still remember this!?

Another thing that is etched in to my memory is M1PQ.

In my junior high school English class (around 1981-1982) I met a boy named Jimmy. Jimmy was my gateway in to home computers, Hitchhiker’s Guide to the Galaxy, phone phreaking and much more. I believe he also introduced me to bulletin board systems.

Neither of us had a home computer (as we called them back then) yet, but we’d visit the local Radio Shack (so long ago, it not only existed, but has a space in the name separating the words). Using a TRS-80 Model III and their 300 baud modem, we’d dial in to local Houston, Texas BBSes. Radio Shack is also where I learned to program BASIC. I recall using a book Jimmy had, and writing programs out on paper, then going in on Saturday to type them in and see if they worked. (Back then, Radio Shacks and most everything else was closed on Sunday in Texas due to “Blue Laws,” whatever they were.)

Apple NET-WORKS

At the time, few “home computers” existed. The IBM-PC, if it existed at all, would have been brand new and not generally known by non-business customers. If a school had a “computer room” it would have been most likely populated with Apple 2 machines or TRS-80s. (Though one of my schools had some Commodore PETs.)

Most of the bulletin board systems I dialed in to ran on Apple 2s using software called NET-WORKS. Here is the Wikipedia entry:

https://en.wikipedia.org/wiki/Net-Works_II

The BBS Documentary website has a disk image of it:

http://software.bbsdocumentary.com/APPLE/II/NETWORKS/

And as I wrote up this article, I even found a website about this BBS, including some documentation!

https://www.callapple.org/documentation/networksii-bbs/

In those early days, many BBSes did not have the concept of “username” and “password.” Some just used a password! I guess we were more honest back then, since if you registered and tried a password someone else already had, the BBS would just say “that password is already taken.” Security? What’s that?

Some systems used a username and password, similar to today, but you didn’t even get to choose your password. They used a user number encoded in to a randomly generated password. Apple 2 Net-Works (and, I believe, BBS Express for the Atari 8-bits) did that. For example, if you were user 42, your randomly assigned password might be “A42BC”. I have never seen the source, but it appeared to just chose three random letters and stuck the user number after the first letter. I recall my BBS Express password was a similar format, but “A42BCD” with an extra character.

But Apple Net-Works had a flaw. When you first ran the software to configure it, it would assign a password for the SysOp (system operator) who would be in control of the BBS. It would generate the password M1PQ. SysOps who did not realize this would be using a password that hackers would most certainly try as their first guess.

I don’t know where I learned about this, but probably on some hacker or phreaker BBS … which was probably running the Net-Works software ;-)

At the time, I thought this was some kind of secret backdoor built in to the software, but today I expect it may have been caused by the non randomness of random.

See also: my article on the RND command in Color BASIC.

If you powered up an Apple 2 fresh, and printed ten random numbers between 1-100, I expect you’d get the same series of numbers each time you powered up and did that. (Any Apple 2 users here who can confirm?)

You get these same random numbers every time you power up the CoCo.

If Apple BASIC worked like that, then perhaps it was just code not taking any steps to seed the random number generator so each time it ran from a fresh power up, it would generate the same sequences of “random” numbers.

Either way, M1PQ was a well-known SysOp password for the NET-WORKS BBS software.

You can see I have even referenced it here on this site:

The 1983 Radio Shack Color Computer BBS package is back… This time on Arduino!

The reason I mention this is convoluted. Yesterday I received a letter from Change Healthcare, which I had never heard of. It was informing me of a data breach that happened this past February 2024. I was affected, and could sign up for free credit monitoring.

I had never heard of this company, and suspected this to be a scam. What a clever way to get folks to give out their personal information — “hey, sign up to free credit monitoring. Just give us your social security number, date of birth, and account numbers to monitor.” Yeah, right.

But some searching led to many news articles about the data breach, including one on my insurance provider’s website. Apparently they used Change Healthcare and thus, this alert was legit.

I decided to sign up for the credit monitoring. During that process, I saw that the site had some tools you could use. One was a password verifier. I typed in “monkey123” (a famous password of modern times) and it said it was medium strength. Well, that password verifier sucks, I thought. But then I noticed it explained this password was in leaks and should not be used.

Interesting. Instead of just telling you the quality of your password, it could tell you if that particular password has been found in a data breach.

So naturally, I had to type in M1PQ:

M1PQ – The Apple Net-Works BBS default password.

Somewhere, out there, someone actually used M1PQ – the most infamous default password from the early 1980s BBS scene – on the internet, somewhere, and it got leaked.

So, kids, don’t use M1PQ. It wasn’t a good password in 1984, and it is not a good password in 2024.

Yet, it’s one password I have never forgotten.

DJI Neo drone app bugs and problems

Last Updated: 10/1/2024

NOTE: This article may be edited as new problems are found.

I just received my DJI Neo drone. This page will try to document various problems I have had with the DJI Fly iPhone app.

  • iOS App Version: 1.14.2 (on an iPhone 15 Pro)
  • Neo Firmware Version: 01.00.0300

Problems

Repeated Firmware Updates – So far, my DJI Neo has wanted to download a firmware update three times (maybe four). Each time, it completes, and tells me to restart the Neo. After that, the firmware remains 01.00.0300. Perhaps it is updating something else and this is a poorly worded message.

Connection Issues – Multiple attempts are needed almost every time I have tried to connect the phone to the Neo. I get the “Join network” iOS message, tap “Join”, and then it says it is unable to connect. When I check the WiFi settings of the phone, it shows it is connected to the DJI Neo’s WiFi hotspot.

Unable to Download Photos/Videos – There are two bugs here. One is that it just won’t download, and the workaround is shutting of cellular. That seems to help “most” of the time. The second issue is when you select photos/videos, but the “download” icon remains greyed out. Even killing the app and re-running it does not always make this work. Trail and error eventually makes it work for me.

Chinese (?) Pop-Up Message – I have no idea what this means, but I have seen it multiple times when “FlySafe requires update.” (And how often should this be updated? I’ve seen this message several times and I have only played with the drone yesterday and today, so far.)

    Various Lockups on Gallery Screen – Multiple times, I have seen the gallery screen become unresponsive. I cannot tap on anything. This usually happens when it is trying to download, and changes.

    Comments?

    If you have encountered any of these bugs, or different ones, please leave a comment with details. Thanks!

    More to be added…

    DJI Neo error downloading videos to phone

    And how to download them, in the first place…

    If you have a DJI Neo selfie drone, and you find yourself unable to download videos to your phone (in my case, an iPhone), try turning off Cellular Data. Once I did that, mine worked as expected.

    This is my first experience with a DJI product. I was surprised that the app was a bit clunky. I had difficulty trying to figure out how to batch download photos and videos. In case anyone else runs in to this, here are the steps:

    1. Connect the DJI Neo connected to the DJI Fly app. (Does yours work? Mine takes multiple attempts, even with the current latest firmware.)
    2. Select “Album” from the bottom left of the control screen. (Or, if you worry about accidentally flying the drone, you can exit out of the control screen and back to the connection screen and select it from there.)
    3. Select the Checkmark box near the top right. That will enable checkboxes on each thumbnail. You can tap just the ones you want, or…
    4. You may have to swipe down on the thumbnails to find it, but you can tap on the text “Batch Select“. That will check all the files.
    5. The bottom right will be a “download” icon. Tap that and your videos/photos will be downloaded in to your Photos app. (Does yours work? Mine has, once, but most of the time that icon remains greyed out.)
    6. To get out of this mode (so you can exit back to the control screen), you have to tape the checkbox at the top right again to turn off the image selection. It seems awkward that the “exit” goes away when you are in select mode.

    This app is clearly not ready for prime time, but the drone itself seems very neat. Actually, what it can do at this size and price point borders on “magic” in my eyes.

    More to come…

    printf portability problems persist… possibly.

    TL:DNR – You all probably already knew this, but I just learned about inttypes.h. (Well, not actually “just”; I found out about it last year for a different reason, but I re-learned about it now for this issue…)

    I was today years old when I learned that there was a solution to a bothersome warning that most C programmers probably never face: printing ints or longs in code that will compile on 16-bit or 32/64-bit systems.

    For example, this code works fine on my 16-bit PIC24 compiler and a 32/64-bit compiler:

    int x = 42;
    printf ("X is %dn", x);
    
    long y = 42;
    printf ("Y is %ldn", y);

    This is because “%d” represents and “int“, whatever that is on the system — 16-bit, 32-bit or 64-bit — and “%ld” represents a “long int“, whatever that is on the system.

    On my 16-bit PIC24 compiler, “int” is 16-bits and “long int” is 32-bits.

    On my PC compiler “int” is 32-bits, and “long int” is 64-bits.

    But int isn’t portable, is int?

    As far as I recall, the C standard says an int is “at least 16-bits.” If you want to represent a 16-bit value in any compliant ANSI-C code, you can use int. It may be using 32 or 64 bits (or more?), but it will at least hold 16-bits.

    What if you need to represent 32 bits? This code works fine on my PC compiler, but would not work as expected on my 16-bit system:

    unsigned int value = 0xaabbaabb;
    
    printf ("value: %u (0x%x) - ", value, value);
    
    for (int bit = 31; bit >= 0; bit--)
    {
        if ( (value & (1<<bit)) == 0)
        {
            printf ("0");
        }
        else
        {
            printf ("1");
        }
    }
    printf ("n");

    On a 16-bit system, an “unsigned int” only holds 16-bits, so the results will not be what one would expect. (A good compiler might even warn you about that, if you have warnings enabled… which you should.)

    stdint.h, anyone?

    In my embedded world, writing generic ANSI-C code is not always optimal. If we must have 32-bits, using “long int” works on my current system, but what if that code gets ported to a 32-bit ARM processor later? On that machine, “int” becomes 32-bits, and “long” might be 64-bits.

    Having too many bits is not as much of an issue as not having enough, but the stdint.h header file solves this by letting us request what we actually want to use. For example:

    #include <stdio.h>
    #include <stdint.h> // added
    
    int main()
    {
        uint32_t value = 0xaabbaabb; // changed
        
        printf ("value: %u (0x%x) - ", value, value);
        
        for (int bit = 31; bit >= 0; bit--)
        {
            if ( (value & (1<<bit)) == 0)
            {
                printf ("0");
            }
            else
            {
                printf ("1");
            }
        }
        printf ("n");
    
        return 0;
    }

    Now we have code that works on a 16-bit system as well as a 32/64-bit system.

    Or do we?

    There is a problem, which I never knew the solution to until recently.

    printf ("value: %u (0x%x) - ", value, value);

    That line will compile without warnings on my PC compiler, but I get a warning on my 16-bit compiler. On a 16-bit compiler, “%u” is for printing an “unsigned int”, as is “%x”. But on that compiler, the “uint32_t” represents a 32-bit value. Normal 16-bit compilers would probably call this an “unsigned long”, but my PIC24 compiler has its own internal variable types, so I see this in stdint.h:

    typedef unsigned int32 uint32_t;

    On the Arduino IDE, it looks more normal:

    typedef unsigned long int uint32_t;

    And a “good” compiler (with warnings enabled) should alert you that you are trying to print a variable larger than the “%u” or “%x” handles.

    So while this works fine on my 32-bit compiler…

    // For my 32/64-bit system:
    uint32_t value32 = 42;
    printf ("%u", value32);

    …it gives a warning on the 16-bit ones. To make it compile on the 16-bit compiler, I change it to use “%lu” like this:

    // For my 16-bit system:
    uint32_t value32 = 42;
    printf ("%lu", value32);

    …but then that code will generate a compiler warning on my 32/64-bit system ;-)

    There are some #ifdefs you can use to detect architecture, or make your own using sizeof() and such, that can make code that compiles without warnings, but C already solved this for us.

    Hello, inttypes.h! Where have you been all my C-life?

    On a whim, I asked ChatGPT about this the other day and it showed me define/macros that are in inttypes.h that take care of this.

    If you want to print a 32-bit value, instead of using “%u” (on a 32/64-bit system) or “%lu” on a 16-bit, you can use PRIu32 which represents whatever print code is needed to print a “u” that is 32-bits:

    #define PRIu32 "lu"

    Instead of this…

    uint32_t value = 42;
    printf ("value is %u\n", value);

    …you do this:

    uint32_t value = 42;
    printf ("value is %" PRIu32 "\n", value);

    Because of how the C preprocessor concatenates strings, that ends up creating:

    printf ("value is %lu\n", value); // %lu

    But on a 32/64-bit compiler, that same header file might represent it as:

    #define PRIu32 "u"

    Thus, writing that same code using this define would produce this on the 32/64-bit system:

    printf ("value is %u\n", value); // %u

    Tada! Warnings eliminated.

    And now I realize I have used this before, for a different reason:

    uintptr_t
    PRIxPTR

    If you try to print the address of something, like this:

    void *ptr = 0x1234;
    printf ("ptr is 0x%x\n", ptr);

    …you should get a compiler warning similar to this:

    warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘void *’ [-Wformat=]

    %x is for printing an “unsigned int”, and ptr is a “void *”. Over the years, I made this go away by casting:

    printf ("ptr is 0x%x\n", (unsigned int)ptr);

    But, on my 32/64-bit compiler, the “unsigned int” is a 32-bit value, and %x is not for 32-bit values. Thus, I still get a warning. There, I would use “%lx” for a “long int”.

    To make that go away, last year I learned about using PRIxPTR to represent the printf code for printing a pointer as hex:

    printf ("pointer is 0x%" PRIxPTR "\n",

    On my 16-bit compiler, it is:

    #define PRIxPTR "lx"

    This is because pointers are 32-bit on a PIC24 (even though an “int” on that same system is 16-bits).

    On the 32/64-bit compiler (GNU-C in this case), it changes depending on if the system:

    #ifdef _WIN64
    ...
    #define PRIxPTR "I64x" // 64-bit mode
    ...
    else
    ...
    #define PRIxPTR "x" // 32-bit mode
    ...
    #endif

    I64 is something new to me since I never write 64-bit code, but clearly this shows there is some extended printf formatting for 64-bit values, versus just using “%x” for the default int size (32-bits) and “%lx” for the long size.

    Instead of casting to an “(unsigned int)” or “(unsigned long int)” before printing, there is a special “uintptr_t” type that will be “whatever size a pointer is.

    This gives me a warning:

    printf ("ptr is 0x%" PRIxPTR "\n", (unsigned int)ptr);

    warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘unsigned int’ [-Wformat=]

    But I can simply change the casting of the pointer:

    printf ("ptr is 0x%" PRIxPTR "\n", (uintptr_t)ptr);

    You may have also noticed I still have a warning when declaring the pointer with a value:

    void *ptr = 0x1234;

    warning: initialization of ‘void *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]

    Getting rid of this is as simple as making sure the value is cast to a “void *”:

    void *ptr = (void*)0x1234;

    This is what happens when you learn C on a K&R compiler in the late 1980s and go to sleep for awhile without keeping up with all the subsequent standards, including one from 2023 that I just found out about while typing this up!

    Per BING CoPilot…

    • C89/C90 (ANSI X3.159-1989): The first standard for the C programming language, published by ANSI in 1989 and later adopted by ISO as ISO/IEC 9899:1990.
    • C95 (ISO/IEC 9899/AMD1:1995): A normative amendment to the original standard, adding support for international character sets.
    • C99 (ISO/IEC 9899:1999): Introduced several new features, including inline functions, variable-length arrays, and new data types like long long int.
    • C11 (ISO/IEC 9899:2011): Added features like multi-threading support, improved Unicode support, and type-generic macros.
    • C17 (ISO/IEC 9899:2018): A bug-fix release that addressed defects in the C11 standard without introducing new features.
    • C23 (ISO/IEC 9899:2023): The latest standard, which includes various improvements and new features to keep the language modern and efficient.

    The more you know…

    Though, I assume all the younguns that grew up in the ANSI-C world already know this. I grew up when you had to write functions like this:

    /* Function definition */
    int add(x, y)
    int x, y;
    {
    return x + y;
    }

    Now to get myself in the habit of never using “%u”, “%d”, etc. when using stdint.h types…

    Until then…

    Counting characters in a string in Color BASIC

    And so it begins…

    I want to visualize some playing card statistics, and the easiest way I could think of to do this was to write a program in BASIC. Of course.

    There are many approaches I can take, so as I put figure it out I will “show my work” and experiments that help me figure out which one will work best.

    For my task, I will greatly simplify a deck of cards by ignoring suits (I just care if it’s an Ace, a Five, a King or whatever). I figure I can store it as a 52-character string representing each card:

    A123456789JQKA123456789JQKA123456789JQKA123456789JQK

    I could “shuffle” this “deck” and end up with a string in “random” order. I believe I have touched on randomizing a list in the past, and had some great suggestions in comments on how to do it better. Forgetting all of that, let’s just say I end up with as string that gets randomized.

    But I digress…

    The next question will be: “How many Kings are left in the deck?”

    Some of you will already see I am heading down a poor path for doing this, but let’s start there anyway.

    One way of counting a specific character in a string is to loop through it and use MID$ to pull out an individual character. Something like this:

    FOR A=1 TO LEN(A$)
    IF MID$(A$,A,1)="S" THEN C=C+1
    NEXT

    That would count all the “S” characters that appear in the string. Since every time MID$ is used it has to build a new string representing that portion of the original string, this should be our slowest way to do this. On a system with tons of strings in use, string manipulation gets real slow. For such a simple program, this might be fast enough.

    Another approach, which was originally shown to me during a word wrap article as a submission, would be to use VARPTR to get the memory location of the 5-byte string ID memory, and then go to the memory where the string bytes are stored and use PEEK to look for them. You can find details in my earlier article on VARTPR.

    The memory location that VARPTR returns will have the length of string as the first byte (byte 0), then an empty byte (byte 1, always 0), then the next two bytes will be the address where the string is stored (bytes 2 and 3) followed by a zero (byte 4). Knowing this, something like this would do the same thing as MID$:

    A=VARPTR(A$)
    SL=PEEK(A)
    SS=PEEK(A+2)*256+PEEK(A+3)
    FOR A=SS TO SS+SL-1
    IF PEEK(A)=ASC("S") THEN C=C+1
    NEXT

    And do it faster.

    VARPTR is a legal BASIC function, but it still seems nasty to reach in to string memory to do this. Thus, I came up with the idea of using INSTR. This function returns the start location of a matching string in another string, or 0 if not found:

    PRINT INSTR("ABCDEFG","D")

    That should print 4, since a “D” is located at the 4th position in the string.

    You can also add an additional parameter which is where in the string to start searching. Doing this:

    PRINT INSTR(5,"ABCDEFG","D")

    …would print 0, because it starts scanning at the 5th character (just past the D) of the string, and then won’t find anymore.

    I could start using INSTR with a position of 1 (first character), and if it comes back with a value other than 0, I found one. That value will be the position of the found character. I could then loop back and use that position + 1 to scan again at the character after the match. Repeat until a 0 (no more found) is returned. That lets the scan for characters be done by the assembly code of the BASIC ROM and is even faster. It looks like this:

    F=0
    xxx F=INSTR(F+1,A$,T$):IF F>0 THEN C=C+1:GOTO xxx

    And we put them all together in a benchmark test program…

    10 ' COUNTSTR.BAS
    20 A$="THIS IS A STRING I AM GOING TO USE FOR TESTING. I WANT IT TO BE VERY LONG SO IT TAKES A LONG TIME TO PARSE."
    30 T$="S":T=ASC(T$)
    40 '
    50 ' MID$
    60 '
    70 PRINT "MID$:";TAB(9);
    80 TIMER=0:C=0
    90 FOR A=1 TO LEN(A$)
    100 IF MID$(A$,A,1)=T$ THEN C=C+1
    110 NEXT
    120 PRINT C,TIMER
    
    130 '
    140 ' VARPTR
    150 '
    160 PRINT "VARPTR:";TAB(9);
    170 TIMER=0:C=0
    180 A=VARPTR(A$)
    190 SL=PEEK(A)
    200 SS=PEEK(A+2)*256+PEEK(A+3)
    210 FOR A=SS TO SS+SL-1
    220 IF PEEK(A)=T THEN C=C+1
    230 NEXT
    240 PRINT C,TIMER
    
    250 '
    260 ' INSTR
    270 '
    280 PRINT "INSTR:";TAB(9);
    290 TIMER=0:C=0:F=0
    300 F=INSTR(F+1,A$,T$):IF F>0 THEN C=C+1:GOTO 300
    310 PRINT C,TIMER

    And running prints:

    Wow, using INSTR is six times faster than MID$? And four times faster than VARPTR. Nice.

    Now you know a bit about what I need to do. I need to represent cards in a deck (and be able to “draw” cards from that deck) and calculate how many of a specific card remain in the deck.

    Since I do not need to track or show the suits (hearts, spaced, clubs, diamonds), I figure I could use one byte in a string.

    To be continued … but in the meantime, do you have a better approach? Comment away!

    Splitting up strings in C source code.

    When printing out multiple lines of text in C, it is common to see code like this:

    printf ("+--------------------+\n");
    printf ("| Welcome to my BBS! |\n");
    printf ("+--------------------+\n");
    printf ("| C)hat    G)oodbye  |\n");
    printf ("| E)mail   H)elp     |\n");
    printf ("+--------------------+\n");

    That looks okay, but is calling a function for each line. You could just as easily combine multiple lines and embed the “\n” new line escape code in one long string.

    printf ("+--------------------+\n| Welcome to my BBS! |\n+--------------------+\n| C)hat    G)oodbye  |\n| E)mail   H)elp     |\n+--------------------+\n");

    Not only does it make the code a bit smaller (no overhead of making the printf call multiple times), it should be a bit faster since it removes the overhead of going in and out of a function.

    But man is that ugly.

    At some point, I learned about the automatic string concatenation that the C preprocessor (?) does. That allows you to break up quoted lines like this:

    const char *message = "This is a very long message that is too wide for "
        "my source code editor so I split it up into separate lines.\n";

    “Back in the day” if you had C code that went to the next line, you were supposed to put a \ at the end of the line.

    if ((something == true) && \
        (somethingElse == false) && \
        (somethingCompletelyDifferent == banana))
    {

    …but modern compilers do not seem to care about source code line length, so you can usually do this:

    printf ("+--------------------+\n"
            "| Welcome to my BBS! |\n"
            "+--------------------+\n"
            "| C)hat    G)oodbye  |\n"
            "| E)mail   H)elp     |\n"
            "+--------------------+\n");

    That looks odd if you aren’t aware of it, but makes for efficient code that is easy to read.

    However, not all compilers are created equally. A previous job used a compiler that did not allow constant strings any longer than 80 characters! If you did something like this, it would not compile:

    printf ("12345678901234567890123456789012345678901234567890123456789012345678901234567890x");

    I had to contact their support to have them explain the weird error it gave me. On that compiler, trying to do this would also fail:

    printf ("1234567890"
            "1234567890"
            "1234567890"
            "1234567890"
            "1234567890"
            "1234567890"
            "1234567890"
            "1234567890x");

    But that is not important to the story. I just mention it to explain that my background as an embedded C programmer has me limited, often, by sub-standard C compilers that do not support all the greatness you might get on a PC/Mac compiler.

    These days, I tend to break all my multi-line prints up like that, so the source code resembles the output:

    printf ("This is the first line.\n"
            "\n"
            "And we skipped a line above and below.\n"
            "\n"
            "The end.\n");

    I know that may look odd, but it visually indicates that there will be a skipped line between those lines of text, where this does not:

    printf ("This is the first line.\n\n"
            "And we skipped a line above and below.\n\n"
            "The end.\n");

    Do any of you do this?

    And, while today any monitor will display more than 80 columns, printers still default to this 80 column text. Sure, you can downsize the font (but the older I get, the less I want to read small print). Some coding standards I have worked under want source code lines to be under 80 characters, which does make doing a printout code review much easier.

    And this led me to breaking up long lines like this…

    printf ("This is a very long line that is too long for our"
            "80 character printout\n");

    That code would print one line of text, but the source is short enough to fit within the 80 column width preferred by that coding standard.

    And here is why I hate it…

    I have split lines up like this in the past, and created issues when I later tried to find where in the code some message was generated. For example, if I wanted to find “This is a very long line that is too long for our 80 character printout” and searched for that full string, it would not show up. It does not exist in the source code. It has a break in between.

    Even searching for “our 80 character” would not be found due to this.

    And that’s the downside of what I just presented, and why you may not want to do it that way.

    Thank you for coming to my presentation.