Old C dog, new C tricks part 3: char *line vs char line[]

You, of course, already knew this. But I learn from your comments, so please leave some. Thanks!

This may be the next “old dog, new trick” I adapt too.

When I started learning C in the late 1980s, I had a compiler manual (not very useful for learning the language) and a Pocket C Reference book — both for pre-ANSI K&R C. I may have had another “big” C book, but I mostly remember using the Pocket C Book.

Looking back at some of my early code, I find I was declaring “fixed” strings like this:

And this shows us:

char version[5]="0.00"; /* Version number... */

Odd. Did I really count the bytes (plus 0 at the end) for every string like that? Not always. I found this one:

char filename[28]="cocofest3.map";

…but I think I remember why 28. In the OS-9/6809 operating system, directory entries were 32 bytes. The first 28 were the filename (yep, back in the 80s there were operating systems with filenames longer than FILENAME.EXT), and then three at the end were the LSN (logical sector number) where the File ID sector was. (More or less accurate.)

I also found arrays:

int *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };

But why is that an int??? That must have been a typo/bug. Other places, I did it more correctly:

char *items[] = { /* employee info prompt thingies */
   "Employee :",
   "Min/Week :",
   "Max/Week :",
   "Min/Shift:",
   "Max/Shift:"
};

At some point in my C history, I just started using pointers to strings like this:

char *version = "1.0.42b-delta";

I guess I got tired of [brackets]. I mean, “they work the same way”, don’t they?

void function (char *line)
{
    if (NULL != line)
    {
        printf ("Line: '%s'\n", line);
    }
}

…and…

void function (char line[])
{
    if (NULL != line)
    {
        printf ("Line: '%s'\n", line);
    }
}

…both end up with line pointing to the start of wherever the bytes to that string are in memory. I’ve seen main() done the same ways:

int main (int arc, char *argv[] )

…and…

int main ( int argc, char **argv )

For years, I’ve been doing it the second way, but all my early code was *argv[] so I suspect that is how I learned it from my early K&R C books.

I have no idea why I changed, or when, but probably in the mid-to-late 1990s. I started working for Microware Systems Corporation in Des Moines, Iowa in 1995. This was the first place I used an ANSI-C compiler. In code samples from the training courses I taught, some used “*argv[]” but ones I wrote used “**argv”.

Does it matter?

Not really. But let’s talk about it anyway…

There was a comment left on one my articles last year that pointed out something different I had no considered: sizeof

If you have “char *line” you cannot use sizeof() to give you anything but the size of the pointer (“sizeof(line)”) or the size of a character (or whatever data type used) that it points to (“sizeof(*line)”).

If you have “char line[]”, you can get the size of the array (number of characters, in this case) or the size of one of the elements in it:

#include <stdio.h>
#include <stdlib.h> // for EXIT_SUCCESS
int main(void)
{
    char *line1 = "1234567890";
    
    char line2[] = "1234567890";
    
    printf ("sizeof(line1)  = %zu\n", sizeof(line1));
    printf ("sizeof(*line1) = %zu\n", sizeof(*line1));
    printf ("\n");
    printf ("sizeof(line2)  = %zu\n", sizeof(line2));
    printf ("sizeof(*line2) = %zu\n", sizeof(*line2));
    return EXIT_SUCCESS;
}

This produces:

sizeof(line1)  = 8  <- size of a 64-bit pointer
sizeof(*line1) = 1 <- size of a char

sizeof(line2) = 11 <- size of the character array
sizeof(*line2) = 1 <- size of a char

I cannot remember ever using sizeof() on a string constant. You may recall I was surprised it worked when I learned about it a few months ago.

But, now that I am aware, I think I may start moving myself back to where I started and using the [brackets] when I have constant strings. Using sizeof() in the program just embeds a constant value, while strlen() is a function that walks through each byte looking for the end zero, thus adding more code space and more execution time.

If I wanted to copy some constant string into a buffer, I could try these two approaches:

// Copy message into buffer.
char *line1 = "This is a message.";
strncpy (buffer, line1, strlen(line1)); // strlen
printf ("Buffer: '%s'\n", buffer);
char line2[] = "This is a message.";
strncpy (buffer, line2, sizeof(*line2)); // sizeof
printf ("Buffer: '%s'\n", buffer);

And the results are the same:

Buffer: 'This is a message.'
Buffer: 'This is a message.'

I would use the second version since using sizeof(*line2) avoids the overhead of strlen() scanning through each byte in the string looking for the end zero.

NOTE: As was pointed out in the comments, strlen() returns the number of characters up to the zero. “Hello” is a strlen() of 5. But sizeof() is the full array or characters including the 0 at the end so “Hello” would have a sizeof() of 6.

char line[] = "1234567890";
printf ("strlen(line) = %u\n", strlen(line));
printf ("sizeof(line) = %u\n", sizeof(line));
strlen(line) = 10
sizeof(line) = 11

If you wanted them to be the same, it would be “sizeof(line)-1”.

It’s all fun and games until you pass a parameter…

This “benefit” of sizeof() is not useful if you are passing the string in to a function. It just ends up like a pointer to wherever the string is stored:

#include <stdio.h>
#include <stdlib.h> // for EXIT_SUCCESS
#include <string.h>
void function1 (char *line)
{
    printf ("function1():\n");
    printf ("sizeof(line)  = %zu\n", sizeof(line));
    printf ("sizeof(*line) = %zu\n", sizeof(*line));
    printf ("strlen(line)  = %zu\n", strlen(line));
}
void function2 (char line[])
{
    printf ("function2():\n");
    printf ("sizeof(line)  = %zu\n", sizeof(line));
    printf ("sizeof(*line) = %zu\n", sizeof(*line));
    printf ("strlen(line)  = %zu\n", strlen(line));
}
int main(void)
{
    char *line1 = "1234567890";
    printf ("Line 1: '%s'\n", line1);
    function1 (line1);
    function2 (line1);
    
    printf ("\n");
    char line2[] = "1234567890";
    printf ("Line 2: '%s'\n", line2);
    function1 (line2);
    function2 (line2);
    return EXIT_SUCCESS;
}

Above, I create a “*line” pointer to a string then pass it in to two functions. The first expects a *line as the parameter, and the second expects a line[].

Then I do a “line[]” array and pass it to the same two functions.

The results are the same:

Line 1: '1234567890'
function1():
sizeof(line) = 8
sizeof(*line) = 1
strlen(line) = 10
function2():
sizeof(line) = 8
sizeof(*line) = 1
strlen(line) = 10

Line 2: '1234567890'
function1():
sizeof(line) = 8
sizeof(*line) = 1
strlen(line) = 10
function2():
sizeof(line) = 8
sizeof(*line) = 1
strlen(line) = 10

And, if you use a “good compiler,” you may get a warning about doing sizeof() like this:

main.c: In function ‘function2’:
main.c:16:44: warning: ‘sizeof’ on array function parameter ‘line’ will return size of ‘char *’ [-Wsizeof-array-argument]
   16 |     printf ("sizeof(line)  = %zu\n", sizeof(line));

Notice that warning was from function2(), and not from function1(). This is one difference in using “*line” versus “line[]” in the functions. For function1(), no warning is given:

void function1 (char *line)
{
    printf ("function1():\n");
    printf ("sizeof(line)  = %zu\n", sizeof(line));
    printf ("sizeof(*line) = %zu\n", sizeof(*line));
    printf ("strlen(line)  = %zu\n", strlen(line));
}

Since the function takes a “pointer to one or more chars”, doing sizeof() what that pointer points to makes sense. It is what you asked for. The “C gibberish” website says:

declare line as pointer to char

https://cdecl.org/?q=char+*line

But for the second one…

void function2 (char line[])
{
    printf ("function2():\n");
    printf ("sizeof(line)  = %zu\n", sizeof(line));
    printf ("sizeof(*line) = %zu\n", sizeof(*line));
    printf ("strlen(line)  = %zu\n", strlen(line));
}

…a warning is given about the “sizeof(line)” because it cannot tell us the size of a line[] array — it became a pointer to the character memory when it went into the function. But because the function parameter was “line[]”.

declare line as array of char

https://cdecl.org/?q=char+line%5B%5D

Doing sizeof() an “array of char” is valid. But it was passed into the function, even though the parameter was a line[] it is passed as a pointer to the data. I guess this is one of those “I’m sorry, Dave. I’m afraid I can’t do that” moments ;-)

Is this useful? It certainly will let you use sizeof() instead of strlen() on a string if you have direct access to the string variable. But passing strings into functions? Not so much. (Or am I mistaken?)

But I do think I am going to try to go back to using “line[]” for my string declarations. I like retro.

Until next time…

CoCo 3 BASIC and ON ERR GOTO and negative line numbers

Juan Castro strikes again, this time providing a short program for me to try on a CoCo 3 (I used the Xroar Online emulator) that shows another quirk in the handling of 16-bit values on the CoCo.

The reason this requires a CoCo 3 is because it makes use of the ONERR GOTO command that was added for Super Extended Color BASIC.

I recall using these back in my CoCo 3 days. On page 227 of the Color Computer Computer 3 manual is this description:

ON ERR GOTO line number

“Goes to the line number if an error occurs during program execution.”

There are two special variables that get set when an error is trapped by “ON ERR”.

ERNO

“Returns an error number that corresponds to the error that occurred.”

ERLIN

“Returns the line number where the error occurred.”

So in this simple program, with a mis-spelled “PRINT” command…

10 ON ERR GOTO 50000
20 PRUNT "I TYPE POORLY"
30 END
50000 PRINT "ERROR";ERNO;"IN LINE";ERLIN

When I run that, I see:

ERROR 1 IN LINE 20

1 must be the value of ?SN ERROR. The manual says to look for “BASIC Error Messages” in the back of this book, but the page is titled ERROR CODES. It is on page 321. It lists errors 1 to 39.

But that is not important for this post.

ERLIN, which you see reported the error was in line 20, has a problem.

If the line number with the error has the high bit set (higher than 32767), it gets reported as a negative number! Changing the program to make the PRUNT typo happen on a later line shows this:

10 ON ERR GOTO 50000
32768 PRUNT "I TYPE POORLY"
32769 END
50000 PRINT "ERROR";ERNO;"IN LINE";ERLIN

If I run this version, I see:

ERROR 1 IN LINE-32768

If the error line is moved to 32767, you will it it report properly.

The book Super Extended BASIC Unraveled II mentions this bug on page 33:

“The ERLIN function will return a negative number if the line number in which the error occurred is greater than 32767. This is caused by the fact that the ERLIN function returns the line number as a two-byte integer instead of a floating point number, as it should.”

– Super Extended BASIC Unraveled II, page 33

I did not know there was a 2-byte integer routine in the ROMs. Always learning something new! (Or, re-learning in case I actually did learn this decades ago.)

Thanks, Juan, for getting my dusty brain thinking.

Until next time…

Color BASIC decimal, hex and octal are not the same?

There is something wonderful about having a poor memory. Whether I knew something before or not, I do not remember it now, so I get the pleasure of “learning something new” even if I previously knew the new thing.

In a reply to my Color BASIC and octal post, Juan Castro mentioned another quirk about how BASIC sees numbers represented as decimals versus hex or octal.

AND, OR, AND NOT demand signed 16-bit integers as input, whereas the &H/&O expressions (and consequantly my &B extension) return UNsigneded integers. That is VERY annoying:

PRINT &H8001 AND &H0007
?FC ERROR
OK

Come the %#&*£ ON!

– Juan Castro

I am unsure about this, since after the parser reads through the code, it turns any hex or octal or decimal value intothe internal 5-byte floating point representation of the value. “A=65535” turns into exactly the same type of variable as “A=&HFFFF”.

AND and OR should deal with them exactly the same way. In Juan’s example, decimal values will generate the same ?FC ERROR. But there is a workaround…

I stumbled upon this when playing around with using AND and OR to set or test bits. My Color BASIC and 16-bits and AND and OR and NOT article explains what I was trying to do, and how I ultimately ended up achieving it.

In Juan’s example, &H8001 is 32769 in decimal. &H0007 is 7. Having the “high bit” (bit 15) set of the first value effectively makes it a negative number:

1000000000000001 = &H8001 (32769)
^ ^
bit15 bit0

Any value with that bit set will not work with AND or OR, regardless if you represented it in decimal, hex or octal. You can go up to &H7FFF (32767) using AND or OR, but past that, you get the ?FC ERROR:

Juan has been working on some BASIC enhancements, adding new features (like a method of representing values in binary — which I would find quite useful). But seeing numbers in binary not work would be even more frustrating:

PRINT &B100000000000000 AND &H1
?FC ERROR

…at least, that is my assumption about what is “&B” binary representation would do.

Here is how to make Juan’s code work:

PRINT (&H8007-65536) AND &H0007
1

Any value that has that high bit set needs to have 65536 subtracted from it, then you get the results you would expect.

But unfortunately, you cannot use AND to test for that high bit to be set ;-) so instead you have to do silly things like this:

V=&H8007
IF V>&H7FFF THEN V=V-65536
PRINT V AND &H0001

And it’s just that easy!

Though, if you print V, you will see it is a negative number, as you might have guessed. If you don’t need to PRINT V, doing something as simple as this will let you AND and/or OR on values that have the high bit set.

Yuck.

“Come the %#&*£ ON!” is right.

Until next time…

Rainbow Check Plus

A followup from my previous post… This box, which appeared in Rainbow magazine, was called Rainbow Check Plus:

Rainbow Magazine checksums

It first appeared in the February 1984 issue, and was even featured on the cover:

The article “Rainbow Check Plus for the CoCo and MC-10” appears on page 21, and was written by H. Allen Curtis. (Why is that name so familiar?) I did not realize it was available for the MC-10 as well. Here is the listing for the CoCo version:

10 CLS:X=256*PEEK(35)+178
20 CLEAR25,X-1
30 X=256*PEEK(35)+178
40 FOR Z=X TOX+77
50 READ Y:W=W+Y:PRINT Z,Y;W
60 POKE Z,Y:NEXT
70 IFW=7985THEN80ELSEPRINT"DATA ERROR":STOP
80 EXEC X:END
90 DATA 182,1,106,167,140,60,134
100 DATA 126,183,1,106,190,1,107
110 DATA 175,140,50,48,140,4,191
120 DATA 1,107,57,129,10,38,38
130 DATA 52,22,79,158,25,230,129
140 DATA 39,12,171,128,171,128
150 DATA 230,132,38,250,48,1,32
160 DATA 240,183,2,222,48,140,14
170 DATA 159,166,166,132,28,254
180 DATA 189,173,198,53,22,126,0
190 DATA 0,135,255,134,40,55
200 DATA 51,52,41,0

But wait … there’s more. As I was looking into this, I learned that “Rainbow Check Plus” was a follow-up to “Rainbow Check” which appeared in January 1983. It seems the original “Rainbow Check” would not catch some key items:

  • Incorrect line numbers. “300 CLS” and “390 CLS” would provide the same check value.
  • Incorrect variables. It would not catch a wrong variable. The example given is that “F” would be the same as “E”.
  • Incorrect command words. SGN or SIN would provide the same checksum.

Now I really wonder what it was doing to generate the numbers, if it wasn’t looking at the actual line data ;-)

The original version appeared on page 95 as part of the “Assembly Corner” column by Dennis L. Lewandoski. The column was called “Let’s End Those Typing Errors Once and For All.” This version appeared in the form of an assembly language program listing, as well as a BASIC loader with DATA statements.

Here is the BASIC program listing for the original 1983 “Rainbow Check” version:

10 CLS:IFPEEK(116)=127 THEN X=32688 ELSE X=16304
20 CLEAR25,X-1
30 IFPEEK(116)=127 THENX=32688 ELSE X=16304
40 FORZ=X TO X+77
50 READY:W=W+Y:PRINTZ,Y;W
60 POKE Z,Y : NEXT
70 IFW=5718 THEN80 ELSE PRINT"DATA ERROR":STOP
80 EXEC X:END
90 DATA 182,1,106,167,141,0,68
100 DATA 134,126,183,1,106,190
110 DATA 1,107,175,141,0,57,48
120 DATA 141,0,4,191,1,107,57
130 DATA 129,10,38,44,52,22,220
140 DATA 27,147,25,142,4,0,141
150 DATA 6,31,152,141,2,32,25
160 DATA 52,2,68,68,68,68
170 DATA 141,4,53,2,132
180 DATA 15,129,9,46,4,139,112
190 DATA 32,2,139,55,167,128,57
200 DATA 53,22,126,0,0

Unfortunately, the assembly was not provided on Rainbow on Tape, so I would have to type it all in by hand.

I suspect I will have to do just that, as well as disassemble the 1984 version and see how both worked, which might explain the limitations of the original.

Until then…

Dragon User magazine and typing in assembly

Over on YouTube, user @ms-ex8em pointed me to the November 1988 issue of Dragon User magazine and a little program that helps you type in machine code program listings accurately.

I recall spending countless hours typing in machine code programs from Rainbow Magazine “back in the day”. These programs would be a few lines of BASIC followed by dozens or hundreds of lines of DATA statements with numbers that represented the bytes of the machine language program.

One such program I specifically remember was Zonx, a cool game with background sound effects. It appeared in the October 1985 issue. You can see what I typed in by checking out page 65. And page 67. And page 68. And page 70. And page 71.

https://colorcomputerarchive.com/repo/Documents/Magazines/Rainbow,%20The/The%20Rainbow%20Vol.%2006%20No.%2003%20-%20October%201986.pdf

And, somehow, I managed to type all of those numbers in and get a working program!

Rainbow did have one thing to help — and perhaps I used it. They had a small program you could run, then as you typed in your program listing, when you got to specific lines you could hit a key and it would spit out a number (a checksum?) and see if it matched their numbers:

Above, you can see what number should be expected when you got to line 150, 310, 440 and so on. After all the code was typed in, the final number it should give you was 238. This required you to type in the program exactly as it appeared including spaces and any other formatting.

It was not optimal, but it was better than nothing.

I also saw some BASIC loaders for machine language that took matters into their own hands, and had built-in checksum values in their DATA statements. For example, they would put a specific amount of numbers on each line, with a checksum of those numbers at the end. Say that amount of numbers was five. A line might look like this:

1500 DATA 1,1,1,1,1,5

In that silly example, you could add up ever five bytes you READ and then compare that to the 6th byte in each line. If it matched, continue. If it didn’t, the loader code would print some kind of message, hopefully indicating what line had a mismatch.

I always appreciated program listings that did that.

Now, back to @ms-ex8em… The code they shared was quite different. You can find it on page 12:

https://colorcomputerarchive.com/repo/Documents/Magazines/Dragon%20User/Dragon%20User%20-%208811%20-%20November%201988.pdf

I will do my best to type it in here, accurately.

10 REM HEX LOADER
20 CLEAR 200,31599
30 INPUT"START";ST
40 INPUT"END";ED
50 FOR J=ST to ED STEP 8
60 PRINT USING"##### : ";J;
70 INPUT A$
80 CS=0
90 FOR K=1 TO LEN(A$)
100 CS=CS+K*VAL("&H"+MID$(A$,K,1))
110 NEXT K
120 INPUT" = ";C
130 IF S<>CS THEN PRINT"CHECKSUM ERROR-TRY AGAIN":SOUND 1,1:GOTO 60
140 FOR K=0 TO 7
150 POKE J+K,VAL("&H"+MID$(K*2+1,2)
160 NEXT K,J

First comment… There appears to be a mistake in line 150. It should have two closing parens at the end of the line.

Now let’s see what this code is doing.

In line 10, the CLEAR reserves memory (so BASIC cannot use it) starting at 31599. This is because the machine language program will load starting at 31600.

In line 40, the user is asked to input the start and end of the program they will be typing in.

In line 50, there is a FOR/NEXT that goes from start to end, stepping by 8. This tells me they expect 8 bytes of data on each line the user types in. The first input should be at 31600, then the next at 31608 and so on.

The user types in the 16 characters (representing 8 hex bytes) shown in the box below. They look like this:

31600 : 8E7B6F9F74Be012B  = 1016

In line 80, a checksum is calculated by adding up the value of each character typed in that line.

In line 120, the user is asked to type in the checksum shown in the data listing.

In line 130, if the number the user typed in (from the data listing) does not match value the program calculated, an error message prints and it goes back to let the user retype the entire line.

If it matched, line 140 will then go through those 16 characters and create the byte value for each of the 8 data bytes and POKE them into memory at the proper place.

This repeats until the end address is reached. At that point, some new assembly language program has been loaded into memory starting at 31600.

Pretty cool!

The problem @ms-ex8em was seeing with this program was a ?TM ERROR. That is most likely showing up in line 150 because of the missing paren. Fix that, then maybe the rest will work ;-)

I thought this was a neat way to enter in machine code. What other neat ways did you see back then?

Comment away…

Need help linking Windows C to FTDI static libraries

UPDATE: A support person with FTDI reached out to me, and confirms their static library can only be used with Microsoft’s compiler. It will not work with GCC, etc. That is the root of my issue. My next question is going to be: Can I do C code with Visual Studio? More to come…


This should be a simple problem to solve, and this problem may be related to using the wrong compiler (a very old Clang 3.3).

FTDI provides device drivers for things like RS232 interfaces. FTDI drivers typically come with Windows, Linux and Mac. When writing a program to use the FTDI device, they provide header files, DLLs and .libs. These come in a .zip file:

FT4222HQ – FTDI

Inside this zip are two header files. One is for the common FTDI stuff (Open, Close, etc.). The second is for the FT4222 which is a USB-to-I2C chipset.

Both are provided as .libs for DLLs, or static libraries. They also provide 32-bit and 64-bit versions of each.

Header files:

\LibFT4222-v1.4.7\imports\ftd2xx
\LibFT4222-v1.4.7\imports\LibFT4222\inc

64-bit static libraries:

\LibFT4222-v1.4.7\imports\ftd2xx\lib\amd64
\LibFT4222-v1.4.7\imports\LibFT4222\lib\ucrt\amd64

The files I am using in my test project are:

LibTest.c
ftd2xx.h
LibFT4222.h
ftd2xx.lib (681 KB, 64-bit static library)
LibFT4222-64.lib (12,918 KB, 64-bit static library)

There are defines for each of the libraries that are supposed to be set before including the corresponding header file:

#define FTD2XX_STATIC
#define FT4222_STATIC

#ifndef FTD2XX_STATIC
#error FTD2XX_STATICmust be defined to use static libs.
#endif

#ifndef FT4222_STATIC
#error FT4222_STATIC must be defined to use static libs.
#endif

…though I would normally set it in my project settings.

This “should” be simple, but on LabWindows/CVI 2017 (which uses Clang 3.3), it has a two sets of linker errors to some Windows library stuff that I do not know how to resolve.

When I make a project in Code::Blocks which uses GCC, I only get one set of six mangled references.

GCC errors:

..\..\LibFT4222-v1.4.7\imports\ftd2xx\lib\amd64\ftd2xx.lib(x64\Release\FTD2XX.obj):(.text$mn+0xec)||undefined reference to `??3@YAXPEAX_K@Z'|
..\..\LibFT4222-v1.4.7\imports\ftd2xx\lib\amd64\ftd2xx.lib(x64\Release\FTD2XX.obj):(.text$mn+0x16d)||undefined reference to `??3@YAXPEAX_K@Z'|
..\..\LibFT4222-v1.4.7\imports\ftd2xx\lib\amd64\ftd2xx.lib(x64\Release\FTD2XX.obj):(.text$mn+0x1e5)||undefined reference to `??3@YAXPEAX_K@Z'|
..\..\LibFT4222-v1.4.7\imports\ftd2xx\lib\amd64\ftd2xx.lib(x64\Release\FTD2XX.obj):(.text$mn+0x25d)||undefined reference to `??3@YAXPEAX_K@Z'|
..\..\LibFT4222-v1.4.7\imports\ftd2xx\lib\amd64\ftd2xx.lib(x64\Release\FTD2XX.obj):(.text$mn+0x2d5)||undefined reference to `??3@YAXPEAX_K@Z'|
..\..\LibFT4222-v1.4.7\imports\ftd2xx\lib\amd64\ftd2xx.lib(x64\Release\fteepd.obj):(.data$r+0x0)||undefined reference to `??_7type_info@@6B@'|
||error: ld returned 1 exit status|
||=== Build failed: 7 error(s), 20 warning(s) (0 minute(s), 1 second(s)) ===|

Since GCC gets further, I am going to try to use it.

This sample project is just a test to link to Open and Close functions in the FTD2XX library. Nothing is being used in the FT4222 library, so it really is not part of this test, but ultimately I will need it.

My test program is:

#define FTD2XX_STATIC
#define FT4222_STATIC

// -lsetupapi -ladvapi32 -luser32

#ifndef FT4222_STATIC
#error FT4222_STATIC must be defined to use static libs.
#endif

#include <windows.h>

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

#include "ftd2xx.h" // Add to include path.
#include "LibFT4222.h" // Add to include path.

int main (void)
{
FT_STATUS ftStatus = 0;
FT_HANDLE ftHandle;

ftStatus = FT_OpenEx ((void*)"PrecisePower A",
FT_OPEN_BY_DESCRIPTION,
&ftHandle);

if (FT_OK == ftStatus)
{
FT_Close (ftHandle);
}

return 0;
}

I am posting this to my blog in case someone else is searching for a solution for this.

Meanwhile, if you think you know how to get this to work, please let me know.

IMPORTANT INFO: The FTDI readme says it works in Visual Studio. They have one example they provide that does static linking and it builds just fine. I have not tested it to see if it actually works, and do not know how to tell if it truly is using the static libraries. (We have always used the DLL version of the libraries, and those work fine.)

Is is possible they made a library that will not work in Clang or GCC, but does work in Microsoft’s compiler?

Tips appreciate.

Recreating the Pac-Man maze in 1993

Many years ago, I played with drawing a 3-D maze under OS-9 on my Radio Shack CoCo 3. That demo is on the NitrOS-9 “Ease of Use” release under the name “semaze“. It lets you wander around a simple line maze:

There is even a map you can bring up with “?”:

As I mentioned years ago on this site, I had played with turning that into an OS-9 version of the classic Phantom Slayer 3-D game on the CoCo 1. I got as far as having my maze with a “phantom” in it that would wander around:

An unfinished CoCo 3 OS-9 3-D maze game inspired by the old “Phantom Slayer” by MED SYSTEMS.

But the oddest version was probably me putting the Pac-Man maze into my 3-D “engine” and then deciding to put dots around it, and make it refresh every half second or so, making it a “real time” game. With a prototype ghost (which appeared just as a large circle) wandering around!

And bringing up the map showed the Pac-Man maze:

In the days before the Internet, the only way I would have been able to recreate this map is if I had access to an accurate home port (I did not), or a picture in a magazine (maybe). I am guessing I based it on some CoCo Pac-Man clone with a fairly accurate maze.

How well did I do? Here is my CoCo version, then the actual arcade game, and the Arduino TV-Out version I was working on a decade ago (very accurate, down to the tiles).

I can see a few errors, like the bottom walls under the power pellets going into the maze an extra tile. And the dots are off. Today, I understand how the Pac-Man maze is laid out with tiles so it would be much easier to recreate my 3-D “DotMaze” game with an accurate map.

Maybe I will.

Old C dog, new C tricks part 2: i won’t use i

See Also: part 1, part 2, part 3, part 4 and part 5.

When I learned BASIC, manuals always showed the FOR/NEXT loop using the variable “I”:

FOR I=1 TO 100:NEXT I

“I” had no idea why this was, back then, but since then I have been told there was some history with some programming language that had certain letters reserved for certain features, such as as I for loops.

But is this true? A quick Google search produced an “AI summary”:

The convention of using “i” as the loop counter variable originates from the mathematical notation where “i,” “j,” and “k” are commonly used as indices or subscripts to represent integers in sequences or arrays. This practice was adopted early in computer science and has persisted due to its conciseness and familiarity among programmers. While any valid variable name could technically be used, “i” serves as a readily recognizable and easily understood placeholder for the loop index, especially in simple iterations. In nested loops, “j” and “k” are conventionally used for the inner loop counters.

– Google AI Summary of my search result.

But that’s not important right now…

I suppose I had a bit of O.C.D. in me, because starting with “I” seemed weird.

I would do my loops starting with “A”.

FOR A=1 TO 100:NEXT A

When variables needed to mean something, I’d use the one or two-character variable name that made sense — NM$ for Name string, UC for User Count, etc. But for loops and other things, I’d use A, B, C, D, etc.

C books were similar, showing “i” for loops:

for (int i=0; i<100; i++)
{
    ...stuff...
}

For some reason, I always used “i” in C rather than a, b, c… My nested loops looked just like the AI summary described:

for (int i=0; i<10; i++)
{
    for (int j=0; j<10; j++)
    {
        for (int k=0; k<10; k++)
        {
            ....stuff....
        }
    }
}

It was at a previous job that another embedded programmer said something to me that changed this forever.

“You can’t search for i…”

– paraphrased quote from embedded programmer I used to work with.

While today there are modern editors that let you specify how a search works — full word or partial, ignore case or case sensitive, and even regular expressions — but you can never depend on having access to those tools. Some jobs are very restrictive about the software you are allowed to install on your work computer. Some simply don’t allow any installs by employees: you get the set of preconfigured apps for the position (Microsoft Office for some roles, compilers and such for others, etc.) and that might be it.

He told me he used “idx” for loops, rather than “I”. And that was enough to change my C coding habits instantly. Even since, when I do a loop, it’s like this…

for (idx=0; idx<100; idx++)
{
    ...stuf...
}

And when I’m looping through things of a given type, it do things like:

for (int cardIdx=0; cardIdx<52; cardIdx++)
{
    ...stuf...
}

Who says you can’t teach an old dog new tricks?

What do you use for your loops? And why?

Comments, if you have them…

Color BASIC and octal

Updates:

  • 2025-02-19 – Thanks to a comment from William Astle, the term “decimal” number has replaced my incorrect use of “integer.” And per a comment from James Jones, a clarification that all values you input are still stored internally as the same type of numeric floating point – the parsing converts them all to this floating point representation.

Color BASIC had one type of number you could use: decimal. These are the normal numbers we use every day like 1, 50392 or 42.

A=42
PRINT "A IS";A

When Extended Color BASIC was added, the CoCo accepted the following notations for numeric constants: integer, hexadecimal and octal. (Internally, BASIC converts them all to the same numeric representation of a floating point value.)

Getting Started with Extended Color BASIC describes hex and octal as follows:

Extended Color BASIC lets you use both hexadecimal and octal constants.

Hexadecimal numbers are quantities represented in Base 16 notation, composed of the numerals 0 to 9 and the “numerals” A to F. Hexadecimal constants must be in the range 0 to FFFF, corresponding to the decimal range 0 to 65535.

To indicate that a number is an octal constant, precede it with the symbol &H, as shown here:

&HA010 &HFE &HDl &HC &H4000

Octal numbers are quantities represented in Base 8 notation, composed of the numerals 0 to 7. Octal constants must be in the range 0 to 177777. The computer stores them as two-byte integers that correspond to the decimal range 0 to 65535.

To indicate that a number is an octal constant, precede it with the symbol &0 or &, as shown here:

&070 &044 U1777 &7170 &17 &01234

The use of “hex” and octal constants is convenient in programs that reference memory locations and contents. For further information, read a book on machine-language programming.

– Getting Started With Extended Color BASIC (p. 195, 1984 revision)

I most likely learned hexadecimal when I was learning assembly language. Today, I use hex values often in C programming. But octal? I do not think I have ever used it for anything. Have you?

I do believe I have seen it used “in the real world.” In the late 1980s (possibly early 1990s) I had a Kawai K1 synthesizer keyboard. It organized its instrument settings (called “patches” in reference to early synthesizers using “patch cables” to route signals do different processors) into two groups of Internal (onboard storage, uppercase “I” and lowercase “I”) and two groups of External (memory card, uppercase “E” and lowercase “e”). Each section had eight patches.

My first commercial CoCo program was a MIDI Librarian that could read the patches from the synthesizer and save them to disk, or load patches from disk into the keyboard. The patch screen in question looked like this:

You will see that each area had four banks (A-D) and each of those had eight patches (1-8). Uppercase “I” was A(1-8), B(1-8), C(1-8) and D(1-8), and lowercase “i” was the same. This was also the same for the External banks.

Thus, octal. I could have used octal in my program — but I do not know if I even realized this was octal at the time. I’d have to think about how I would have even done that. Maybe something like this:

0 'OCTALIN.BAS (2-15-2025)
10 LINE INPUT "BANK (0-7):";B$
20 LINE INPUT "PATCH(0-7):";P$
30 NM=VAL("&O"+B$+P$)
40 PRINT "THAT IS PATCH #";NM

…though I do not recall how the MIDI messages. It could have just been a user interface thing and internally the patches were still 1-whatever.

But I digress.

Juan Castro posted to the Color Computer e-mail list about a bug in the octal routines:

Octal constant (Ex.: &O14 = 12 decimal)

It accepts 8 as a digit. D’oh. Try “PRINT &O18” in a CoCo 1 or CoCo 2.

Without having a CoCo3 at hand to check I’ll wager it patches that bug. All
it has to do is replace a LBHI with a LBHS.

Juan Castro
Enviado do meu Olivetti Programma 101
http://retropolis.com.br

Since an octal value should only be 0-7, a value of 8 is not valid octal.

10 CLS:PRINT"DEC","OCT"
20 FOR A=0 TO 10
30 PRINT A,VAL("&O"+STR$(A))
40 NEXT

This should print 0-7 then error out. Yet…

And is there really no better error than SYNTAX for this type of problem?

And naturally, I had to see what HEX values do. A valid hex digit is 0-9 and A-F, representing 0 to 15. Will it do a syntax error as well if I go past the letter F?

It appears it does not. It seems to bail on the parsing and print them as two variables. Which is a neat trick, since you have to put a space or semicolon between two variables if you want to print them otherwise.

To confirm that is what it is doing, I tried this:

Ah, so it is not treating this as variables “H” and “G”. And I just learned something new (and probably useless).

Something new (and probably useless)…

Integer values are things like 1, 2, or 152032. They are floating point values, so you can also have things like 1.2 or 33.5. Though, not always. Floating point values cannot represent every decimal value accurately, but that’s a story for another article…

Somewhere, someone learned that a decimal (period) by itself would be treated as zero. The fastest way to assign 0 to a variable in Color BASIC is like this:

A=.

Weird, but it works. It seems the parser starts looking for a fractional amount, like “.25” or whatever, so it processes the period and then there is nothing else so the code returns with 0 (the initialization value when the function is called). It is still weird, though.

I found out tonight that the same thing applies to hex. You can do this:

A=&H

…and you get a zero. Try “PRINT &H” to see for yourself.

Of course, I had to try a quick benchmark, sitting in a loop doing something like “Z=.” and another run doing “Z=&H”. 1000 times through with “Z=.” used 140 timer ticks. The “Z=&H” used 154. I guess that is the overhead of parsing two characters instead of one.

But I digress. Again.

I have not looked up what BASIC is doing (see the excellent “BASIC Unravelled” books for the disassembly of the BASIC ROMs), but I suspect the parser starts looking and finds the “&” followed by the “H” and is ready to parse… it then hits an invalid character, so the parsing is over and 0 is there. Then it sees the G, which is not a command so it is treated like a variable.

Using “PRINT” was deceiving. Had I just tried this:

A=&HG

…I would have got what I was expecting:

?SN ERROR

Thanks, Juan, for sharing this. I learned about a bug, and in writing this, discovered another odd thing, which is less odd now that I understand what happened.

Until next time…

Sub-Etha graphical adventures?

Around 93-94, I created a text adventure that featured black and white digitized pictures from the 1992 Atlanta CoCoFest. It was more of a “simulation” than a game — and even called itself a simulation on the splash screen. But, while browsing through the source code, I found there were quite a number of things you could “do” in the game, while the score let you know how much of it you completed.

I also found one from a Chicago CoCoFest, which seems to be complete other than missing the images. Rooms were numbers with the vendor name and description. It was kind of like “playing” one of my old CoCoFest reports. Trippy.

And a weird 3-D first-person Pac-Man game I was working on…

Now that I have Xroar running the current Ease of Use NitrOS-9 build, and have my CoCoSDC “real CoCo” hard drive image mounted as the second drive, I may finally be able to dig through all this stuff and see what else I have.

Like finding I made a Towel V1.05” — after I moved to Iowa! I have no recollection of that. Lots of stuff to get archived and posted online.

Should be fun.