Thank you, Jetpack Stef

Stef, a Happiness Engineer with Jetpack, quickly responded to my support ticket. They have confirmed that my site is not commercial, and reclassified me as a personal site. This will allow me to continue viewing the Jetpack Stats without needing to pay for an upgraded commercial account. Thank you, Stef!

Normally, I don’t think it is fair to complain about “free” stuff. Jetpack does not force ads on my content, so, as far as I can tell, using their plug-in on WordPress for personal sites truly is free. I do, however, pay for a backup service they offer, so at least they get some money out of money.

Sadly, that backup plan is no longer available, and has been replaced by a more expensive offering which I would not be able to justify for a personal site like this. Still, better to have it and not need it, than need it and not have it available.

This is not a commercial site.

Today Jetpack Stats informed me I need to upgrade to a commercial plan for this site. For some reason, I have been flagged as a commercial site. Commercial sites can use it free if they have under 5000 visitors a month which, apparently, this humble blog exceeds. That alone is surprising, since I mostly post about a 1980s Radio Shack computer, and embedded C programming ;-)

I just wanted to assure my thousands of ‘bots that scan this site regularly that I:

  1. Have no ads on this site.
  2. Do not request donations.
  3. Do not have affiliate links.

About the closest I ever get to “commercial” is when I put an Amazon link to something ;-)

I thought I’d post this so the Jetpack Support folks have something to see when/if they check out my site to verify my claim.

What you PSET is not always what you PPOINT

Let’s consider this a part 2 for this 2024 post:

The 9 colors of the CoCo’s high resolution screens…

Read that first, then come back. I’ll wait.

Ready… PSET… GO!

In Microsoft Extended Color BASIC on the Radio Shack Color Computer, high resolution graphics command were added. There were fancy commands like DRAW for drawing complex designs, then simpler command like LINE and CIRCLE.

Even simpler was the PSET command which would set an individual pixel on the screen. This was the high resolution equivalent of SET in Color BASIC for the text screen (64×32 blocks).

I had a Commodore VIC-20 at the time, but remember getting a call from my Radio Shack sales guy, Don, to tell me they had just gotten in a new Extended BASIC that I should come out. I did, and fell instantly in love with being able to do things with simple commands versus confusing POKEs on my VIC.

I read through the manual in the store, and created programs on their CoCo. One of them used PSET to randomly place dots on the screen:

0 'RNDPSET.BAS
10 PMODE 3,1:PCLS:SCREEN 1,1
20 PSET(RND(256)-1,RND(192)-1,RND(4)-1)
30 GOTO 20

Oh the fun we had back then.

Use the SCREEN 1,0 color set and you get different colors:

The PMODE 4 screen (256×192) was only two colors, white and black, but TV set (composite video, NTSC) created artifact colors that made it look weird. The emulators try to simulate this effect:

Depending on the mode the CoCo power up, even column pixels would be red and odd would be blue, or the reverse. While the CoCo 3’s video hardware could control this, on the CoCo 1 and 2 it was “random.” You could keep hitting the rest button until the colors flipped. Thus, many CoCo 1/2 games started up to a solid red (or blue) screen, expecting you to know what to do to get it the color the game wanted, or, if the programmer was fancy, it might even tell you what to do:

Paper Route by Diecom

And if the programmer was really fancy they could just handle it in software based on what you told it you saw (“Press 1 if the screen is RED, 2 if it is blue” kind of thing.)

NOTE: I wanted to include a screenshot of this but I cannot remember which games worked like that. If you know, leave a comment and I’ll update this post.

Get to the PPOINT!

PSET would put a pixel on the screen using a specific color:

The syntax is:

PSET (X, Y, C)

The C was the color, and it accepted a value of 0-8. But, you did not get nine colors on any of the high resolution screens. You got either 2 (black/green or black/buff or using the alternate color set), or 4 (green/yellow/blue/red or buff/cyan/magenta/orange using the alternate color set). You had to know the range of the 4 color values to use for the mode you were using.

Or did you?

No. The C value could be 0-8 on any graphics screen, so most programs I saw used 0-1 for two color, or 0-3 for four color. And that’s not at all how Radio Shack described it in the manual… but it worked and kept us from having to memory color ranges based on modes. (See my 2024 article, link at the top of this one, for a table showing how this worked.)

And my point is … PPOINT returns the value that “should” be used — not the value you PSET there! If you were using a screen that wanted colors 5-6-7-8 and you used colors 1-2-3-4, you could PSET color 1, but when you would PPOINT that pixel, you got back a 5 — the same color, but the value you were supposed to be using.

Thus, anyone who made use of PPOINT learned this. I did not use it, and I never learned it until 2024 in the comments to that earlier post.

So for fun, I wrote this program that cycles through each PMODE (0 to 4) and then PSETs each color value (0-8) to a pixel and then reads the pixel color back using PPOINT. It prints it out so we can see this:

The first value of the column is the C value that was used in PSET. The number after it is the return value from PPOINT. So “1) 5” means PSET(0,0,5) and P=PPOINT(0,0).

PMODE 0, PMODE 2 and PMODE 4 are a two color modes, so the range of colors you can use (0-8) are just the same two colors over and over – 0 and 5. PMODE 1 and PMODE 3 are four color modes, so you see the range repeating the same four color values over and over, different depending on the mode.

And, if you used the alternate color set (SCREEN 1,1), you got a different set of color values:

Here is the code:

10 'PPOINT0.BAS
20 'SET PIXEL USING COLOR
30 'THEN PPOINT THE COLOR
40 CLS
50 PRINT@6,"PMODES - SCREEN 1,0:"
60 PRINT STRING$(32,"-");
70 FOR M=0 TO 4
80 PMODE M,1:PCLS:SCREEN 1,0
90 PRINT:PRINT@64+M*7+1,M;
100 FOR C=0 TO 8
110 GOSUB 160
120 NEXT:NEXT

130 GOTO 130

140 'SET PIXEL WITH PSET
150 'GET PIXEL COLOR
160 PSET(0,0,C):P=PPOINT(0,0)
170 PRINT@96+M*7+32*C,USING "#) #";C;P;
180 RETURN

And slight changes for the other color set:

10 'PPOINT1.BAS
20 'SET PIXEL USING COLOR
30 'THEN PPOINT THE COLOR
40 CLS
50 PRINT@6,"PMODES - SCREEN 1,1:"
60 PRINT STRING$(32,"-");
70 FOR M=0 TO 4
80 PMODE M,1:PCLS:SCREEN 1,1
90 PRINT:PRINT@64+M*7+1,M;
100 FOR C=0 TO 8
110 GOSUB 160
120 NEXT:NEXT

130 GOTO 130

140 'SET PIXEL WITH PSET
150 'GET PIXEL COLOR
160 PSET(0,0,C):P=PPOINT(0,0)
170 PRINT@96+M*7+32*C,USING "#) #";C;P;
180 RETURN

It dawns on me now that a BASIC program could detect what screen was being used by PSETting something and then reading it back with PPOINT. Based on the number that returned, you could tell which PMODE and SCREEN was being used.

But that’s a program for a different time.

Until then…

Commodore is making a flip phone. In 2026.

If you have followed the rebirth of Commodore, you may be aware they brought out a new Commodore 64 computer. This model was using an FPGA to simulate/recreate the hardware in the original 1980s home computer, with a claim of being 99.9% compatible with existing Commodore 64 stuff.

What came next was a surprise (and even shock) to most Commodore followers: a flip phone.

https://commodore.net/callback

For $549.

The new Commodore has already taken some heat from fans and had to change an announced policy. They initially planned to lock down their new Commodore 64 to only work with the official firmware. That decision was not popular, and they changed their plans.

It seems there has been enough puzzled comments on the new flip phone that the CEO just sent out an e-mail. Here is an excerpt:

“A few years ago, my priorities changed. I became a father. And somewhere between the night feeds and the realization that I was spending entire evenings chasing likes on a glowing rectangle, I had to reckon with the kind of person I actually wanted to be. The conversations that followed, with my partner, with myself, changed my life.

I switched to a flip phone. Not as an experiment. As a decision. It became my daily driver. And I won’t pretend the first few weeks were easy. I’d catch myself reaching for it out of habit, standing in a checkout line with nothing to scroll. So I snapped it shut, put it away, and looked up. Turns out there was quite a lot going on.

But that flip phone was too dumb. And the others available were too smart. What I wanted just didn’t quite exist. I didn’t want social media, or a browser. It let the wrong things in. But I need apps like Uber, Maps, WhatsApp, HQ Music Player, and a 4K Camera. Turns out a lot of people were wrestling with the same thing. Parents. Students. People who missed being present. People who wanted the convenience of modern apps without the endless feeds. So together with a team of engineers and partners, we started building what we couldn’t find: a phone that sits comfortably between dumb and smart.

That’s why we built the Callback.”

– e-mail from Commodore, 6/22/2026

I see device addiction all around me. It is common for someone to reach for their phone and start dealing with messages as if they were life or death and can’t possibly wait until after supper. Folks are on their phones in movie theaters, in their cars at red lights, and while in bed as soon as their alarm goes off, or before going to sleep.

Such a device might be the only way these folks could break their addictions. It took me not having cell phone service for 8 years to break mine, and I still think I use my phone too much these days ;-)

But … I just wanted to add this:

Any smart phone is a dumb phone if you don’t install TikTok, Facebook, Instagram, Angry Birds, etc.

Read that again for impact.

Any smart phone can be a dumb phone. Just do not install the apps. Or if you have them installed, delete them. But folks won’t do that.

Do we expect folks who can’t delete their favorite apps to buy a whole new phone?

We shall see.

I’d kinda like to have one, but I’d just remove all the apps from an iPhone before I’d carry a flip phone in 2026.

Unless this new Commodore flip phone has a better camera.

What says you? Comment away!

Another downside of smart programmers

If I had a nickel for every time this has happened to me… I’d have quite a few nickels.

There are clear advantages to writing code out in the simplest and longest form. Not only is it easier to understand by programmers less talented than you (after all, you are super smart, and most people around you are super dumb), it can save time later when someone is trying to find that code. At least, in this example.

Consider this logging snippet:

if (some_condition)
{
log ("Some condition is ACTIVE.");
}
else
{
log ("Some condition is INACTIVE.");
}

But, you are super smart, so you write this in a more clever way. Perhaps something like this:

log ("Some condition is %s.", some_condition ? "ACTIVE" : "INACTIVE");

…or, heck, because you are super SUPER smart, maybe you save two bytes by doing it like this:

log ("Some condition is %sACTIVE.", some_condition ? "" : "IN");

I like that last one. That’s super SUPER smart.

And, in the case of embedded programming, perhaps saving a few bytes makes the difference between code compiling, or not. I have now faced multiple projects at two different companies where I had to do tricks like that just to get new code to fit in limited program space of the hardware I was using.

At my day job I was trying to figure out what was happening leading up to an entry I saw in the system log of the program. Lets pretend it was the entry in my example:

Some condition is INACTIVE.

My first instinct was to search for that phrases in the source code and see where it gets logged. If the code had been written “dumb”, I would have found it, and been working on it.

But it was not found.

Hmmm, well, let me search just for “INACTIVE”.

Had this been done the second way, I would have found it, and been working on it.

But it was not found.

Okay, let me try a third search for “Some condition is”… And I found it.

My point is, dumb code would have saved me time. And, sadly, *I* wrote this code some years ago and was doing it a “smarter” way. No big deal, just an extra minute … but I do wonder how many “extra minutes” we waste every year due to smarter code. I am pretty sure the Clean Code movement focuses on this type of stuff.

And don’t get me started on how I like to do “nicely formatted” output in source code. In C you can concat longer strings. Sppose you have some long 80 column text string like this:

char *string = "This is a really long line that I need to print in my program.";

That line could be so long it wraps around on an 80 column printout or screen. Many coding standards ask you to keep lines within 80 columns. C allows you to contact constant strings like this:

char *string = "This is a really long line that "
"I need to print in my program.";

I have seen this at most companies I have worked for (which is actually how I became aware of this). BUT, if you were trying to search for something where the split it, like “that I need”, you won’t find it. I have certainly ran into that many times over the years ;-)

No point. No complain. No suggestions. Just pointing out that, sometimes, smarter code wastes time later.

Especially when folks not as super smart as you try to work with it ;-)

And, a side effect of the smart code is that I wasted even more time blogging about it.

Until next time…

A slightly faster MID$ using VARPTR

TL:DNR – This is stupid. Don’t bother trying it :)


Here is another stupid thing I had to try in Color BASIC. See also my Stupid VARPTR tricks post.

Suppose you want to horizontally scroll a string across the CoCo’s 32-column text screen. You can build a long string (up to 255 characters) and then use a FOR/NEXT loop and MID$ to print 32 characters from within that longer string.

20 CLS:CLEAR 510:DIM A$
30 A$="":FORA=32TO255:A$=A$+CHR$(A):NEXT:A$=A$+STRING$(31,128)
40 TIMER=0
50 FORA=1TO224:PRINT@256,MID$(A$,A,32);:NEXT
60 PRINTTIMER

On a CoCo 3, you get a timer result of around 330-335. On a CoCo 1/2, it is slightly faster showing around 295. (There are less BASIC keyword tokens to look for so Color BASIC is faster than Exended BASIC which is faster than Super BASIC.)

When I first started benchmarking BASIC here years ago, I was using an emulator that only ran CoCo 1/2, and it never occurred to me it would be different on the CoCo 3. #TheMoreYouKnow

As I have discussed in earlier string theory articles (quite a few relating to this topic), MID$, LEFT$ and RIGHT$ all need to create a new string containing data from the original. Suppose you have this:

A$="THIS IS A TEST"

If you trim the string to the first four characters, like this:

A$=LEFT$(A$,4)

…BASIC is basically doing this:

  1. Allocate new string space for a 4 character string.
  2. Copy the first four characters from A$ into the new string.
  3. Make A$ point to the new string, which leaves the original A$ string memory available for cleanup.

This is why you need extra string memory to use these functions. If you clear JUST enough memory to hold a ten character string, you can’t do any MID$, LEFT$ or RIGHT$ with that string.

In order to make that work, you need at least 4 extra bytes reserved for string memory so LEFT$ can make the new string before deallocating the original. And if you add 4 bytes but try to use 5, you get that ?OS ERROR (out of string space) still.

So if you have a 255 character string and you use MID$(A$,0,32) to get 32 characters from that string into a new string (or use it directly with PRINT, which will directly access that string memory without a variable needed), it has to allocate 32 bytes from the reserved string memory and copy those 32 bytes over, then you can use it (PRINT or assign to a variable or whatever).

As my sample program runs, it is allocating 32 byte strings and copying 32 bytes over each time through the loop.

VARPTR, can you help?

After recently learning a weird trick involving changing where a string variable points to for its string data, that made me wonder if you could just manipulate the string address using VARPTR and then PRINT it, and be faster than the overhead of MID$.

I just had to try this…

Here is my test program. It first builds A$ to contain character 32 (space) to character 255 (end of semigraphics characters) and then pads that with 31 black blocks to be the full 255 character string size.

Starting at line 40 is a loop that will PRINT a substring of 32 characters in a loop, starting at the first character and going to the end, making the line scroll to the left.

When done, it prints how many TIMER ticks it took to do this.

Next, starting in line 70, it makes a new B$ that is a clone of A$. (No, it doesn’t need to clone it. I could have just manipulated A$ and saved a few lines.)

It then gets the address of that string descriptor using VARPTR, and saves the size (at the first byte) and the address of the string data (third and fourth byte). This allows restoring the string after the test, since manipulating strings this way could cause problems with strings and garbage collection.

For this specific example I know that the MSB and LSB of the new string are 126 and 1 on my emulated CoCo. That means I could make the string’s starting address be one higher in that memory by incrementing the second value. Had that second value been 250, once I incremented it 5 times to 255, I’d have to increment the first one and reset the second one to 0. (Clear as mud?)

Let’s just say I got lucky enough for this test, but even if this approach DID work, it would not work on all strings based on where they are in memory. It would need extra code for that, which would slow it down even further.

I POKE to change the size of B$ to 32. Then I can go in a loop POKEing the second byte of the start address up by one each time, and simply PRINTing the string. It will print 32 bytes from wherever it thinks the string data begins. It has no idea those values are being manipulated.

When done, I print the time it took and restore the original B$ values back.

10 'VARMID.BAS
20 CLS:CLEAR 510:DIM A$
30 A$="":FORA=32TO255:A$=A$+CHR$(A):NEXT:A$=A$+STRING$(31,128)
40 TIMER=0
50 FORA=1TO224:PRINT@256,MID$(A$,A,32);:NEXT
60 PRINTTIMER
70 DIM B$,V,O2,O3,S1,S2,SS
80 ' CLONE A$. GET VARPTR OF B$.
90 ' V POINTS TO SIZE
100 ' O2-O3 POINTS TO START
110 B$=A$:V=VARPTR(B$):O2=V+2:O3=V+3
120 ' SS - SAVE SIZE
130 ' S1-S2 - SAVE START
140 SS=PEEK(V):S1=PEEK(O2):S2=PEEK(O3)
150 ' CHANGE SIZE TO 32
160 POKE V,32
170 ' CHANGE START ADDR THEN
180 ' PRINT.
190 TIMER=0
200 FORA=0TO223:POKEO3,S2+A:PRINT@256,B$;:NEXT
210 PRINTTIMER
220 ' RESTORE ORIGINAL STRING
230 POKE V,SS:POKE O2,S1:POKE O3,S2

Sadly, it is only a tiny bit faster, and if I had to take care of both address bytes in the VARPTR memory, it would be slower and, without testing this, I assume slower than just using MID$.

It was a fun experiment, but “sometimes the juice is not worth the squeeze.”

Until next time…

The CHANGE command on PDP8 BASIC is weird. And neat.

Recently, Rick “Shanghai” Adams pointed me to a BASIC program he was working on for a PDP-8. Much like I now spend my time playing with BASIC from 1980 on a CoCo 1 emulator, Rick is going even further back and playing with a version of BASIC from a machine created in the 1960s.

And its a bit different.

I have previously posted about this machine (well, PDP systems in general) when I was trying to find the origins of the INSTR command in BASIC. Then I was able to dig up a 1971 PDP-11 manual that referenced this function for that BASIC.

Rick sent me a link to a two-player game called CHOMP. I was unfamiliar with it, but see it has its own wiki page.

https://en.wikipedia.org/wiki/Chomp

On that entry, it is presented as a chocolate bar made up of squares you can break off. The top left square is poison, so whoever takes that final piece loses.

By Lord Belbury – Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=86379139

This helps me understand why the came is called CHOMP.

But I digress… This really has nothing to do with that game, though CHOMP will likely return in a future blog post here.

BASIC may have been cooler in the 1960s…

Rick has a BASIC preprocessor that allows him to write easy to read source code, then convert it into messy BASIC code with line numbers later. Here is a Guest The Number game from Rick’s GitHub:

https://github.com/yggdrasilradio/b8pp/blob/master/guess.txt

	' Generate random number
randomize
x = int(100 * rnd(0)) + 1

' Intro
print "I'VE THOUGHT OF A NUMBER BETWEEN 1 AND 100."
print "CAN YOU GUESS WHAT IT IS?"

n = 0
60 n = n + 1
print
print "YOUR GUESS";
input g
if g <> x then 130

' Player wins!
print
print "YOU GUESSED IT IN"; n; "GUESSES!"

if n <= 7 then 120
print "BUT IT SHOULD HAVE ONLY TAKEN YOU 7 GUESSES."

120 stop

130 if g > x then 160
print "TOO LOW."
goto 60

160 print "TOO HIGH."
goto 60

end

That text is processed by his script and becomes this:

https://github.com/yggdrasilradio/b8pp/blob/master/guess.bas

1RANDOM\X=INT(100*RND(0))+1
2PRINT"I'VE THOUGHT OF A NUMBER BETWEEN 1 AND 100."
3PRINT"CAN YOU GUESS WHAT IT IS?"\N=0
60N=N+1\PRINT\PRINT"YOUR GUESS";\INPUTG\IFG<>XTHEN130\PRINT
61PRINT"YOU GUESSED IT IN";N;"GUESSES!"\IFN<=7THEN120
62PRINT"BUT IT SHOULD HAVE ONLY TAKEN YOU 7 GUESSES."
120STOP
130IFG>XTHEN160\PRINT"TOO LOW."\GOTO60
160PRINT"TOO HIGH."\GOTO60\END

…and that is the code that he loads and runs on the PDP emulator.

The first thing you will notice is the use of a backslash. Initially, I thought this was just in place of a colon. For example, I see things like:

PRINT "TOO HIGH"\GOTO 60

…and that looks like it is just…

PRINT "TOO HIGH":GOTO 60

…but once I started converting PDP CHOMP to work on the CoCo, I realized that was not the intent of the backslash there. It appears to be a way to have multiple lines without line numbers. The logic does not continue to flow through those slashes:

60N=N+1\PRINT\PRINT"YOUR GUESS";\INPUTG\IFG<>XTHEN130\PRINT

Above, if G is not equal to X, it goes to line 130. There is a PRINT after that which would NEVER be reached if these were just colons:

60N=N+1:PRINT:PRINT"YOUR GUESS";:INPUTG:IFG<>XTHEN130:PRINT

To port this to Microsoft Color BASIC, I would have to do something like this:

60 N=N+1:PRINT:PRINT"YOUR GUESS";:INPUTG:IFG<>XTHEN130
61 PRINT

And guess what? That is also not important to this blog post. But might be later to a follow up blog post about porting PDP BASIC CHOMP to CoCo BASIC CHOMP.

CHANGE is … good?

Rick has entered the Logiker Christmas Challenge in the past with entries done in this much earlier BASIC. I have seen him comment on the far more limited STRING handling in that version of BASIC which required some very different approaches to the challenges.

Now I understand what he means.

While looking at his CHOMP source code, there is a keyword I had never seen before – CHANGE. He explained it to me:

“The CHANGE statement converts strings to and from an array, and the zeroth element of the array is the length of the string”

– Rick Adams

I saw it used like this:

40	print
print "YOUR MOVE";
input m$
change m$ to m

Or, in the converted BASIC:

40PRINT\PRINT"YOUR MOVE";\INPUTM$\CHANGEM$TOM

So if you did something like this:

M$="ABC"
CHANGE M$ TO M

…then you would get this:

FOR I=0 TO M(0):PRINT M(I):NEXT
3
65
66
67
  • M(0) is the length of the converted string.
  • M(1) is the first character of the string (“A”)
  • M(2) is the second character of the string (“B)”
  • M(3) is the third character of the string (“C”)

That’s kinda neat. To do the same thing on Color BASIC, we could make subroutines like this:

2000 'M TO M$
2010 M$="":FORZ=1TOM(0):M$=M$+CHR$(M(Z)):NEXT:RETURN

3000 'M$ TO M
3010 M(0)=LEN(M$):FORZ=1TOM(0):M(Z)=ASC(MID$(M$,Z)):NEXT:RETURN

And then we could change “CHANGE M$ TO M” to “GOSUB 3000” and “CHANGE M TO M$” to GOSUB 2000.

10 INPUT M$:GOSUB 3000
20 FOR I=0 TO M(0):PRINT M(I):NEXT:END

Or reverse it, if we created one manually:

10 M(0)=3:M(1)=65:M(2)=66:M(3)=67:GOSUB 2000
20 PRINT M$:END

Though Rick pointed out with MID$() and such available, there would be far easier ways to do this. But those subroutines are what I will be using so I can more directly run his PDP BASIC code with minimal changes.

Have you ever worked on a BASIC that used the CHANGE command? If so, what was it? Leave a comment, please.

Until next time…

Crashing with VR360 video in Final Cut Pro on macOS 26

Updates:

  • 2026-06-11 – I have an active support ticket with Apple and it has been escalated to engineering. They requested me send them a generic (none of my content) project that crashes as well as diagnostic logs. I was able to create a brand new Library and then a new 360 project then add things to it — a few titles, some FCP sound effects, a Generator item and a background, and even that crashes. I also did a short project that was non-360 and had no issues with that. Let’s hope they can figure this out!

I am posting this to get this into the search engines. I am hoping to hear from others that have had this same issue, and might have a solution.

I recorded 360 video (7680×3840) using Insta360 X5 and a DJI Osmo 360 cameras. I export the 360 video out to a flat video file using Insta360 Studio or DJI Studio desktop apps. I then bring this in to Final Cut Pro for editing. I have been doing this for years with no problems.

A few months ago, I went to make a new video. I found my project, that worked fine last year, now crashes Final Cut Pro. Sometimes it crashes on opening the project, sometimes when clicking around, sometimes it works for 15 minutes then crashes when I click on a view icon, etc. Very random.

I tried making brand new empty projects and recreating my template manually. Still crashes.

I eventually reset my macOS and then restored from a Time Machine backup. Even this “clean” reinstall still crashed.

I spent an hour or so on with Apple Support and we never figured out what was causing it, but we could even make it crash using only template items built in to Final Cut Pro — none of my graphics, video, or audio files.

The next test was to install a fresh copy of macOS with nothing else on it. I logged in to my Apple Store account and downloaded Final Cut Pro — the only app on this install of macOS.

And I can still get that to crash.

It looks like some bug with 360 video and the current Final Cut Pro, or some issue with the current macOS version.

Anyone encountered this?

Revisiting PUT defined by ASCII strings

This is a followup (and correction) to my PUT defined by ASCII strings article. That article, itself, was a followup to my Extended Color BASIC PUT from DATA article. They both discuss a method to use the GET/PUT graphics commands with pre-loaded data to PUT on the screen, rather than having to render/draw something first and then GET it so you could PUT it later.

I post such articles for two main reasons:

  1. So I can learn from all the much-smarter programmers who leave comments with advice/suggestions/corrections.
  2. So I can find it again when I’ve forgotten how it worked.

#2 is what led me to my article as I wanted to use the code for another experiment. And that’s when I realized my original PUTDATA.BAS program had a bug: it PUTs the tanks flipped vertically!

The program defined 8×8 characters as ASCII strings. I used a tank with four versions – one for each direction. They looked like this:

580 'TANK UP
590 DATA " XX "
600 DATA " XX "
610 DATA "XX XX XX"
620 DATA "XXXXXXXX"
630 DATA "XXXXXXXX"
640 DATA "XXXXXXXX"
650 DATA "XXXXXXXX"
660 DATA "XX XX"

A routine READs those strings from the DATA statements, then walks through the characters looking for an “X”. If it finds one, it sets a bit in a number variable. At the end of the scan, it now has a byte value which can be loaded into the PUT data.

However, my routine to scan the strings does it backwards and the image is reversed left-to-right:

380 '
390 'READ DATA AND POKE AT L
400 '
410 PRINT
420 'READ STRINGS AND CONVERT
430 'TO BYTES.
440 FOR Z=0 TO 7:V=0
450 READ Z$:PRINT Z$,;
460 FOR BT=1 TO 8
470 'FASTER WAY
480 IF MID$(Z$,BT,1)="X" THEN V=V+BT(BT-1)
490 'SLOW WAY
500 'IF MID$(Z$,BT,1)="X" THEN V=V+2^(BT-1)
510 NEXT:PRINT INT(V);HEX$(V)
520 POKE L+Z,(NOT V)+256:NEXT:RETURN
530 NEXT:PRINT V;HEX$(V):NEXT
540 RETURN

I did not notice this until I tried to use this code for a new experiment, and it was not PUTting what I expected. But for the tanks, being symmetrical, I did not catch it in my original demo. The UP and DOWN tanks looked correct, and I failed to notice my LEFT and RIGHT tanks were reversed.

The bug is from the loop at line 460 that walks through the bits from 1 to 8, and uses that value as the index into the string using MID$. A bit from 1 to 8 is rightmost to leftmost, but on a string, 1 to 8 is leftmost to rightmost. One of them needs to work backwards.

Since I already do some math to calculate the bit (to base-0 counting, 0-7 instead of 1-8), I just made my change there:

470 'FASTER WAY
480 IF MID$(Z$,BT,1)="X" THEN V=V+BT(8-BT)
490 'SLOW WAY
500 'IF MID$(Z$,BT,1)="X" THEN V=V+2^(8-BT)

Now I get the tanks facing the proper directions my test program PUTs them on the screen: UP, DOWN, LEFT then RIGHT.

I have added this code to my GitHub repository in “CoCo Programs” under “basic”:

https://github.com/allenhuffman/CoCo-Programs/tree/main/basic/tank

And here is the full version with the fix:

0 'PUTDATA2.BAS (FIXED BUG)
1 '2026-05-06
2 'SHOWS HOW TO DO IT USING
3 'STRINGS RATHER THAN #S
4 '

10 'TO SPEED UP STR-TO-BYTE
20 DIM BT(7):FOR BT=0 TO 7:BT(BT)=2^BT:NEXT
30 '
40 'VARIABLES USED IN THE
50 'POKE-TO-ARRAY ROUTINE MUST
60 'BE PRE-ALLOCATED OR THEY
70 'WILL CAUSE ARRAY MEM TO
80 'SHIFT WHEN IT GET USED.
90 '
100 DIM L,V,Z,Z$
110 '
120 'EACH ARRAY ENTRY CAN STORE
130 '5 BYTES. AN 8X8 SINGLE
140 'COLOR CHARACTER IS 8, SO
150 'WE NEED TWO ARRAY ENTRIES
160 '(10 BYTES) TO FIT THE 8
170 'BYTE CHARACTER.
180 '
190 DIM TU(1),TD(1),TL(1),TR(1):GOSUB 320

200 '
210 ' TEST PROGRAM
220 '
230 PMODE 4,1:PCLS 1:SCREEN 1,1
240 PUT (0,0)-(7,7),TU
250 PUT (16,0)-(16+7,7),TD
260 PUT (32,0)-(32+7,7),TL
270 PUT (48,0)-(48+7,7),TR
280 GOTO 280

290 '
300 'LOAD SPRITE CHARACTERS
310 '
320 PRINT "LOADING DATA";
330 L=VARPTR(TU(0)):GOSUB 390
340 L=VARPTR(TD(0)):GOSUB 390
350 L=VARPTR(TL(0)):GOSUB 390
360 L=VARPTR(TR(0)):GOSUB 390
370 RETURN

380 '
390 'READ DATA AND POKE AT L
400 '
410 PRINT
420 'READ STRINGS AND CONVERT
430 'TO BYTES.
440 FOR Z=0 TO 7:V=0
450 READ Z$:PRINT Z$,;
460 FOR BT=1 TO 8
470 'FASTER WAY
480 IF MID$(Z$,BT,1)="X" THEN V=V+BT(8-BT)
490 'SLOW WAY
500 'IF MID$(Z$,BT,1)="X" THEN V=V+2^(8-BT)
510 NEXT:PRINT INT(V);HEX$(V)
520 POKE L+Z,(NOT V)+256:NEXT:RETURN
530 NEXT:PRINT V;HEX$(V):NEXT
540 RETURN

550 '
560 '8X8 SPRITE CHARACTERS
570 '
580 'TANK UP
590 DATA " XX "
600 DATA " XX "
610 DATA "XX XX XX"
620 DATA "XXXXXXXX"
630 DATA "XXXXXXXX"
640 DATA "XXXXXXXX"
650 DATA "XXXXXXXX"
660 DATA "XX XX"

670 'TANK DOWN
680 DATA "XX XX"
690 DATA "XXXXXXXX"
700 DATA "XXXXXXXX"
710 DATA "XXXXXXXX"
720 DATA "XXXXXXXX"
730 DATA "XX XX XX"
740 DATA " XX "
750 DATA " XX "

760 'TANK LEFT
770 DATA " XXXXXX"
780 DATA " XXXXXX"
790 DATA " XXXX "
800 DATA "XXXXXXX "
810 DATA "XXXXXXX "
820 DATA " XXXX "
830 DATA " XXXXXX"
840 DATA " XXXXXX"

850 'TANK RIGHT
860 DATA "XXXXXX "
870 DATA "XXXXXX "
880 DATA " XXXX "
890 DATA " XXXXXXX"
900 DATA " XXXXXXX"
910 DATA " XXXX "
920 DATA "XXXXXX "
930 DATA "XXXXXX "

Until next bug … I mean, time. Until next time…