I will never write a shell script again.

With my initial success using the CMOC compiler to build a CoCo program, I wanted to automate my steps (build and copy to disk image).

Back in my OS-9 days, I used an OS-9 shell script called “doit” that would manually do the steps to build whatever project it was designed for. This would do the compile, optimize, link, etc. steps for me.

I wanted to make a shell that let me specify the C file then build and copy it to some emulator disk image. I thought it might be like:

doit hello.c

I couldn’t remember how to pass parameters in to a shell script, so instead of doing a websearch, I just asked ChatGPT:

“how can you pass a parameter to a unix shell script?

To pass a parameter to a Unix shell script, you can use the $1, $2, $3, etc. variables, which correspond to the first, second, third, etc. arguments passed to the script. For example, if you want to pass a filename as a parameter to your script, you could use the following syntax:

Copy code./myscript.sh myfile.txt

Then, in the script, you can access the parameter using the $1 variable, like this:

#!/bin/bash

# Print the filename passed as a parameter
echo "The filename is: $1"

Alternatively, you can use the shift command to access all of the arguments passed to the script, like this:

#!/bin/bash

# Print all of the arguments passed to the script
echo "The arguments are: $@"

# Shift the arguments to the left, so that $1 now refers to the second argument, $2 now refers to the third argument, etc.
shift

# Print all of the arguments again, after shifting
echo "The arguments are now: $@"

In this example, the shift command is used to move all of the arguments to the left by one position, so that the second argument is now accessible using the $1 variable, the third argument is accessible using the $2 variable, and so on. This can be useful if you want to access all of the arguments passed to the script, but you don’t know in advance how many arguments will be passed.

– chat.openai.com

That was quicker than a Google search! I decided to just tell it what I wanted to do:

Write a shell script that takes one parameter (a name) and then runs “cmoc name” and then “decb copy name.bin C.DSK,NAME.BIN -2r”

Here is an example of a Unix shell script that takes one parameter (a name) and then runs the cmoc and decb commands using that parameter:

#!/bin/bash

# Get the name parameter
name=$1

# Run the cmoc command using the name parameter
cmoc $name

# Run the decb command using the name parameter
decb copy $name.bin C.DSK,$name.BIN -2r

In this script, the first line specifies the shell interpreter to use (in this case, /bin/bash), and the second line uses the $1 variable to retrieve the first argument passed to the script (i.e., the name parameter). The third and fourth lines use the cmoc and decb commands, respectively, to perform the specified operations using the name parameter.

To run this script, you would use the following command:

Copy code./myscript.sh myname

Replace myscript.sh with the name of your script file, and myname with the name you want to use as the parameter. The script will then run the cmoc and decb commands using the specified name.

– chat.openai.com

Well, if it can do that, maybe it an make it even better…

update that script so the parameter’s extension is removed and replaced with .bin for the copy

To update the script so that the extension of the parameter is removed and replaced with .bin for the decb copy command, you can use the basename and dirname commands to extract the base name and directory path of the parameter, respectively, and then use the sed command to remove the extension from the base name. Here is an example of how you could do this:

Copy code#!/bin/bash

# Get the name parameter name=$1

# Use the basename and dirname commands to extract the base name and directory path of the parameter
dir=$(dirname $name) base=$(basename $name)

# Use the sed command to remove the extension from the base
namename_without_ext=$(echo $base | sed -e "s/\.[^.]*$//")

# Run the cmoc command using the name parameter
cmoc $name

# Run the decb copy command using the modified name
decb copy $name_without_ext.bin $dir/C.DSK,$name_without_ext.BIN -2r

In this updated script, the basename and dirname commands are used to extract the base name and directory path of the parameter. The sed command is then used to remove the extension from the base name, and the modified name is used as the argument to the decb copy command.

To run this script, you would use the same command as before:

Copy code./myscript.sh myname

This time, however, the decb copy command will use the modified name (i.e., the name with the extension removed and replaced with .bin) instead of the original name. For example, if you pass myfile.txt as the parameter, the decb copy command will use myfile.bin as the destination filename.

– chat.openai.com

And maybe even more better than that!

can you uppercase the second $name_without_ext in the decb line?

To uppercase the second instance of $name_without_ext in the decb line, you can use the tr command to convert the string to uppercase. Here is an example of how you could do this:

#!/bin/bash 

# Get the name parameter
name=$1

# Use the basename and dirname commands to extract the base name and directory path of the parameter
dir=$(dirname $name)
base=$(basename $name) 

# Use the sed command to remove the extension from the base namename_without_ext=$(echo $base | sed -e "s/\.[^.]*$//") 

# Use the tr command to convert the name to uppercase name_upper=$(echo $name_without_ext | tr "[:lower:]" "[:upper:]")

# Run the cmoc command using the name parameter
cmoc $name

# Run the decb copy command using the modified name decb copy $name_without_ext.bin $dir/C.DSK,$name_upper.BIN -2r

In this updated script, the tr command is used to convert the $name_without_ext variable to uppercase, and the resulting string is stored in the $name_upper variable. The $name_upper variable is then used as the second argument to the decb copy command, so the second instance of the name will be uppercase.

To run this script, you would use the same command as before:

Copy code./myscript.sh myname

This time, the decb copy command will use the uppercase version of the name as the second argument. For example, if you pass myfile.txt as the parameter, the decb copy command will be run as follows:

Copy codedecb copy myfile.bin mydir/C.DSK,MYFILE.BIN -2r

Notice that the second instance of the name (i.e., MYFILE.BIN) is now in uppercase.

– chat.openai.com

It worked great! But it didn’t tell me what it was doing, so…

Make it print what is happening at the cmoc and decb steps

To make the script print what is happening at the cmoc and decb steps, you can simply add some echo statements to the script. Here is an example of how you could do this:

#!/bin/bash

# Get the name parameter
name=$1 

# Use the basename and dirname commands to extract the base name and directory path of the parameter
dir=$(dirname $name) base=$(basename $name) 

# Use the sed command to remove the extension from the base
namename_without_ext=$(echo $base | sed -e "s/\.[^.]*$//") 

# Use the tr command to convert the name to uppercase name_upper=$(echo $name_without_ext | tr "[:lower:]" "[:upper:]") 

# Print a message before running the cmoc command
echo "Running cmoc on $name..." 

# Run the cmoc command using the name parameter
cmoc $name 

# Print a message before running the decb copy command
echo "Running decb copy on $name_without_ext.bin..." 

# Run the decb copy command using the modified name
decb copy $name_without_ext.bin $dir/C.DSK,$name_upper.BIN -2r

In this updated script, two echo statements have been added to print a message before running the cmoc and decb copy commands, respectively. These messages will be printed to the console and will indicate what is happening at each step of the script.

To run this script, you would use the same command as before:

Copy code./myscript.sh myname

When you run the script, you should see the following output:

Copy codeRunning cmoc on myname... (output from cmoc command) Running decb copy on myfile.bin... (output from decb copy command)

The output from the cmoc and decb copy commands will be shown after the messages indicating what is happening at each step.

– chat.openai.com

And that’s how I got this…

I will never write a shell script again…

My first C program for CoCo DISK BASIC.

On this day in history … I built the CMOC compiler and compiled my first C program for non-OS-9 CoCO.

I created this source file:

int main()
{
	char *ptr = 1024;
	while (ptr < 1536) *ptr++ = 128;
	return 0;
}

I compiled it using “cmoc hello.c” and it produces “hello.bin”.

I created a new blank disk image using “decb dskini C.DSK”.

I copied the binary to that disk image using “decb copy hello.bin C.DSK,HELLO.BIN -2”

I booted up the XRoar emulator and mounted that disk image as the first drive.

I did LOADM”HELLO” and then EXEC.

And so it begins…

Tackling the Logiker 2022 Vintage Computing Christmas Challenge – part 1

See also: part 1, part 2, part 3 and part 4.

Here we go again! Over in the Facebook Color Computer group, David M. shared a link to this year’s Vintage Computing Christmas Challenge from Logiker. Although I did not submit an entry, I did play with last year’s challenge on my TRS-80 Color Computer.

Last year, it was this:

This year, the challenge is a bit more challenging. Per the challenge website, here is sample code for Commodore:

 10 print"{clear}"
 20 print""
 30 print""
 40 print""
 50 print"               *       *"
 60 print"               **     **"
 70 print"               ***   ***"
 80 print"               **** ****"
 90 print"           *****************"
100 print"            ***************"
110 print"             *************"
120 print"              ***********"
130 print"               *********"
140 print"              ***********"
150 print"             *************"
160 print"            ***************"
170 print"           *****************"
180 print"               **** ****"
190 print"               ***   ***"
200 print"               **     **"
210 print"               *       *"
220 goto 220

Starting with that un-optimized version, I will change it to work on the CoCo 1/2/3’s 32-column screen by adjusting it to be properly centered on that display.

 10 CLS
 50 PRINT"           *       *"
 60 PRINT"           **     **"
 70 PRINT"           ***   ***"
 80 PRINT"           **** ****"
 90 PRINT"       *****************"
100 PRINT"        ***************"
110 PRINT"         *************"
120 PRINT"          ***********"
130 PRINT"           *********"
140 PRINT"          ***********"
150 PRINT"         *************"
160 PRINT"        ***************"
170 PRINT"       *****************"
180 PRINT"           **** ****"
190 PRINT"           ***   ***"
200 PRINT"           **     **"
210 PRINT"           *       *"
220 GOTO 220

Unfortunately, this design is 17 rows tall, and the CoCo’s standard display is only 16. It won’t fit:

We should still be able to enter the challenge by having the program print this pattern, even if it scrolls off the screen a bit. To get one extra line there, we can get rid of the line feed at the end of the final PRINT statement in line 210 by adding a semi-colon to the end:

210 PRINT"           *       *";

And so it begins…

And so it begins

The goal is to make this as small as possible. There were many ways to approach last year’s Christmas tree challenge, and you can read about the results and a follow-up with suggestions folks gave to save a byte or two.

A simple thing is to remove the spaces at the front and replace them with the TAB() command:

 10 CLS
 50 PRINTTAB(7)"    *       *"
 60 PRINTTAB(7)"    **     **"
 70 PRINTTAB(7)"    ***   ***"
 80 PRINTTAB(7)"    **** ****"
 90 PRINTTAB(7)"*****************"
100 PRINTTAB(7)" ***************"
110 PRINTTAB(7)"  *************"
120 PRINTTAB(7)"   ***********"
130 PRINTTAB(7)"    *********"
140 PRINTTAB(7)"   ***********"
150 PRINTTAB(7)"  *************"
160 PRINTTAB(7)" ***************"
170 PRINTTAB(7)"*****************"
180 PRINTTAB(7)"    **** ****"
190 PRINTTAB(7)"    ***   ***"
200 PRINTTAB(7)"    **     **"
210 PRINTTAB(7)"    *       *";
220 GOTO 220

Although this only looks like it saves a character per line (“TAB(8)” versus “seven spaces”), the code itself will be smaller since the TAB command tokenizes down to one (or maybe two?) bytes.

Also, the ending quote is not needed if it’s the last thing on a line, so they could be removed:

 50 PRINTTAB(7)"    *       *
 60 PRINTTAB(7)"    **     **
 70 PRINTTAB(7)"    ***   ***

That would save one byte per line.

But, each line number consumes 5-bytes on it’s own, so a better way to save space would be to pack the lines together. Each line you eliminate saves five bytes. That would become pretty unreadable though, but let’s do it anyway:

10 CLS:PRINTTAB(7)"    *       *":PRINTTAB(7)"    **     **":PRINTTAB(7)"    ***   ***":PRINTTAB(7)"    **** ****":PRINTTAB(7)"*****************":PRINTTAB(7)" ***************":PRINTTAB(7)"  *************":PRINTTAB(7)"   ***********"
130 PRINTTAB(7)"    *********":PRINTTAB(7)"   ***********":PRINTTAB(7)"  *************":PRINTTAB(7)" ***************":PRINTTAB(7)"*****************":PRINTTAB(7)"    **** ****":PRINTTAB(7)"    ***   ***":PRINTTAB(7)"    **     **"
210 PRINTTAB(7)"    *       *";
220 GOTO 220

That’s quite the unreadable mess!

This could still be made better, since the text lines were kept under the input buffer limitation size, but when you enter that line, BASIC compresses it (tokenizes keywords like PRINT, TAB and GOTO) making it take less space. You can then sometimes EDIT the line, Xtend to the end and type a few more characters.

That may or may not be allowed for the Logiker challenge. And since I want to provide code here you could copy and then load in to an emulator, I’ll keep it to the limit of what you could type in.

In the next installment, I’ll see if my brane can figure out a way to generate this code using program logic rather than brute-force PRINT statements.

Until then…

My old Space Invaders game running on an arcade cabinet!

A few years back, Color Computer community member Rietveld Rietveld took an Arcade1Up Rampage cabinet and converted it so I could run other software — such as emulators. Images have been shown of it running the Color Computer emulator and software.

Now, with the official 1.0 release of the NitrOS9 “Ease of Use”, that has been installed on this arcade machine and, by request, my OS-9 game Invaders09 has been ran on it.

I never imagined that my “arcade” game would eventually be running on a (sorta) arcade machine. Thanks, Rietveld!

Reversing bits in C

In my day job, we have a device that needs data sent to it with the bits reversed. For example, if we were sending an 8-bit value of 128, that bit pattern is 10000000. The device expects the high bit first so we’d send it 00000001.

In one system, we do an 8-bit bit reversal using a lookup table. I suppose that one needed it to be really fast.

In another (using a faster PIC24 chip with more RAM, flash and CPU speed), we do it with a simple C routine that was easy to understand.

I suppose this breaks down to four main approaches to take:

  • Smallest Code Size – for when ROM/flash is at a premium, even if the code is a confusingf mess.
  • Smallest Memory Usage – for when RAM is at a premium, even if the code is a confusing mess.
  • Fastest – for when speed is the most important thing, even if the code is a confusing mess.
  • Clean Code – easiest to understand and maintain, for when you don’t want code to be a confusing mess.

In our system, which is made up of multiple independent boards with their own CPUs and firmware, we do indeed have some places where code size is most important (because we are out of room), and other places where speed is most important.

When I noticed we did it two different ways, I wondered if there might be even more approaches we could consider.

I did a quick search on “fastest way to reverse bits in C” and found a variety of resources, and wanted to point out this fun one:

https://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious

At that section of this lengthy article are a number of methods to reverse bits. Two of them make use of systems that support 64-bit math and do it with just one line of C code (though I honestly have no understanding of how they work).

Just in case you ever need to do this, I hope this pointer is useful to you.

Happy reading!

ChatGPT AI can analyze 6809 assembly language!

I gave it this:

clear
    lda #96
clearA
    ldx #1024
loop
    sta ,x+
    cmpx #1536
    bne loop
    rts

…and ChatGPT gave me this:

The clear subroutine in this code is used to clear the screen and initialize it with a grid of green squares. The subroutine begins by loading the value 96 into the accumulator register A using the LDA instruction. This value represents the color value for green, which will be used to fill the screen with green squares.

Next, the LDX instruction is used to load the value 1024 into the X register. This value is the memory address of the top left corner of the screen, and it will be used as the starting point for the POKE operations that will be used to fill the screen with green squares.

The loop label marks the start of a loop that will be used to fill the screen with green squares. Inside the loop, the STA ,X+ instruction is used to store the value in the accumulator register A at the memory location pointed to by the X register, and then increment the X register by one. This has the effect of setting the value at the current memory location to the color value for green, and then moving to the next memory location on the screen.

The CMPX instruction is then used to compare the value in the X register to the value 1536. If the X register is less than 1536, it means that there are still memory locations on the screen that need to be filled with the color value for green. In this case, the BNE instruction is used to branch to the loop label, causing the loop to be repeated and the next memory location on the screen to be filled with the color value for green.

If the X register is equal to 1536, it means that all of the memory locations on the screen have been filled with the color value for green, and the loop can be exited. In this case, the BNE instruction will not be executed, and the RTS instruction will be executed, causing the subroutine to return to the main program. This completes the execution of the clear subroutine.

– ChatGPT AI

Now, it may have figured some of this out because I previously fed it a longer (and fully commented) bit of 6809 code that contained that routine as one of its several functions. That’s the only way I can see it knew the screen location and a green block.

https://chat.openai.com/chat, ya’ll.

My 1987 Paper Route program I wrote for Laverne Ricks in Lufkin, Texas

Recently on LinkedIn, I saw a post by Rogelio Perea about his Tandy Model 102 portable computer. Although I never owned one, I remember this machine well. I always wanted one. Having a “portable” computer really wasn’t a thing back in those days, and this one even had a modem built in!

I recalled how, when I worked for Radio Shack, I wrote a program on our display Model 102 that we used to track our customer repairs. We had to have everything on paper in those days, but would type things in to the computer so we could find them quicker when a customer inquired about their repair status. I wish I had a copy, but I think the only place it ever existed was the memory of that specific computer.

I do, however, have the program it was based on. It started out as a PEN-PAL program for the Radio Shack Color Computer:

This program, in turn, was based on work I had done for my *ALL RAM* cassette-based BBS program.

*ALLRAM* used BASIC arrays to store the userlog and message base in memory, and would load/save them to tape. My Pen-Pal program did the same thing, but was updated to load from either Tape or Disk.

That Pen-Pal program was also the basis for a custom program I wrote for a lady who ran a large newspaper route where I lived at the time. Her name was Laverne Ricks, and I remember this because I put it on the title screen of the program I wrote for her:

According to comments in the source code, I wrote this on 7/8/1987. This was the summer after I graduated high school, and I had just moved to Lufkin, Texas. Since this was before I got a job at Radio Shack, I am not sure how she and I got connected (possibly through the Lufkin Area CoCo Club or someone at Radio Shack).

The initial version of the program has a menu very similar to the Pen-Pal program:

You can see I had made it look a bit nicer by using graphical black block characters instead of rows of equal signs ;-)

I also found a second version, where I added built-in help:

THIS must be why I wrote the BASIC windowing routine I have written about before. It offered several screens of help:

Neat!

The program has ways to Add a new record:

Ah, the days before spell checkers. You should see the misspellings in my furniture store inventory program I did a year or so later… But I digress.

And you could use arrow keys to flip through them:

Y

You could Delete entries, Edit them, and Insert new ones (at the location you were currently viewing, which was important since a Paper Route was organized by delivery location rather than alphabetical or whatever). There was a way to Jump to a specific entry as well.

And, of course, you could Search by field:

Looking at it 35 years later, I’m pretty impressed with the features it offered. I have no idea what I based all of this on since I’d only owned a VIC-20 and a CoCo at that point and certainly had never worked with any kind of database software that I can recall.

Side note: A short while later, I would do something like this again for someone who ran a garbage collection route, though I think it was for a Tandy 1000 and was using a Deskmate Database.

This is what let me to search out Laverne Ricks and see where she was these days. Sadly, she passed away in 2011.

https://www.legacy.com/obituaries/lufkindailynews/obituary.aspx?n=juanita-laverne-ricks&pid=149650362&fhid=11038

Rest in peace, Laverne Ricks. A tiny bit of your legacy will be remembered here, always.

Until next time…

Insat360 X3 now allows changing WiFi password

The Insta360 app has an option to change the password on the X3 camera. This may also work on other cameras, but I have not tested it. I have done a password change, but have not verified this actually did anything.

This option will cause the camera to display an authorization prompt, and once confirmed, you can type in a new WiFi password which the device will use.

I encourage ALL Insta360 camera owners to do this, as the default password is well documented and it allows anyone within WiFi range to access and download any photos/videos on the camera.

Arduino, ZigBee and motion sensor help needed.

For a future project, I need to make use of remote triggers. These could be motion sensors, beam sensors, pressure mats, etc.

The ZigBee standard seems to be the way to go since I can find cheap consumer motion sensors that run on batteries. There also seems to be ZigBee repeaters, which allow giving the distance I need simply by plugging them in from place to place to create a mesh network.

XBee might be another option, if cheap motion sensors and repeaters are also available.

The goal is to have a central location be able to read the motion sensor status for many sensors, that could be spread out beyond walls hundreds of feet away.

Any pointers to where I might get started would be appreciated. Ideally I’d drive this all by a low-cost Arduino since the device will be used in an area where power might not be stable (and I wouldn’t want to corrupt the Linux file system on a Raspberry Pi).

Thanks…