Mimsy: Hacks

42 Astoundingly Useful Scripts and Automations for the Macintosh

Work faster and more reliably. Add actions to the services menu and the menu bar, and create drag-and-drop apps to make your Macintosh play music, roll dice, and talk. Create ASCII art from photos. There’s a script for all of that in 42 Astounding Scripts for the Macintosh.

Adding parenthetical asides to photograph titles on macOS—Wednesday, February 12th, 2020
Mustang sales photos

Sadly, the only photos I have of my old Mustang are of getting ready to sell it. Which doesn’t merit a “Mustang” keyword.

In 42 Astoundingly Useful Scripts and Automations for the Macintosh I have a script for changing the titles of all selected photographs to a sequential title. That is, “Yosemite — 1”, “Yosemite — 2”, “Yosemite — 3” and so on. I use this regularly after coming home from a vacation. But recently I’ve had a need for leaving the title as is but adding a parenthetical. So that if I have a collection of photos that I didn’t take, I might want to add “ (photo by dad)” to the end of each, changing, for example, “Flag in the Snow” to “Flag in the Snow (photo by dad)”.

This is a much simpler task. It doesn’t require sorting the photos, because it isn’t adding a sequential number to the title. Nor does it require appending a different text (the sequence number) to each photo. The same text is appended to all photos.

Except that it makes no sense to change, for example, “Jerry at Adams Avenue Street Fair 2003” to “Jerry at Adams Avenue Street Fair 2003 (Adams Avenue Street Fair)”. So the script does check to make sure that the text being added does not already exist in the title.

[toggle code]

  • -- add a parenthetical to selected photo titles
  • -- Jerry Stratton astoundingscripts.com
  • tell application "Photos"
    • set selectedPhotos to selection
    • set photoCount to count of selectedPhotos
    • --change this number if you regularly change more than 50 photos at a time
    • if photoCount > 50 then
      • display alert "There are " & photoCount & " photos selected." buttons {"Change", "Cancel"} cancel button "Cancel"
    • end if
    • set baseParenthetical to the text returned of (display dialog "Text for parenthetical" default answer "")
    • set parenthetical to " (" & baseParenthetical & ")"
    • repeat with selectedPhoto in selectedPhotos
      • if the name of selectedPhoto does not contain baseParenthetical then
        • set the name of selectedPhoto to name of selectedPhoto & parenthetical
      • end if
    • end repeat
    • say "Finished!"
  • end tell
Rainbow Magazine BASIC program preflight tool—Wednesday, January 8th, 2020
Vicious Vic by Jay R. Hoggins

Vicious Vic appears to be a variation on Robot War, with vikings in place of robots.

There are a lot of typing mistakes I make that are easily detectable: extra characters from fat fingers, mistyped parentheses, and even mistyped numbers. In an attempt to make typing BASIC programs from The Rainbow easier, as well as improve my ability to use Textastic with rcheck, I wrote this script. I wrote it somewhat haphazardly, adding new checks when I noticed that an error was detectable.

The basic idea is that since each line wraps at 32 characters, when I hit the end of the line in the Rainbow listing, I hit return. The script detects whether a line in the text file is a continuation of the previous line or a new line by looking for the line number. This solves the problem of finding a text editor that wraps at 32 characters.

But it also opens the possibility of doing some preflighting. If a line in the text file is not the final subline in a BASIC code line, it must be 32 characters long. If it isn’t, I mistyped something. And with my fat fingers, this is the most common typo. Most typos are discovered merely by making sure that each subline except the final one is exactly 32 characters.

There are also warnings, things that are probably a problem but might not be. These do not halt the program, and are displayed at the bottom of the results. They are not piped to a file, so they appear in the terminal if you’re piping.

For example, if the full BASIC line is longer than 249 characters, I probably mistyped something. That’s the maximum number of characters you can type on a new BASIC line on the Color Computer. It’s a warning, not an error, because it is possible to get more characters into a line, and it occasionally happens with the one-line and two-line contest winners, since they’re specifically trying to stuff as much into a line as possible. More than 255 characters is an error. I haven’t seen anything longer than that.1

It also checks the line number. If it isn’t greater than the previous line number, I mistyped something.2

And it checks for other things that I’ve noticed are detectable. If there aren’t the same number of close parentheses as open parentheses in the full BASIC line, that’s a problem.3 It looks for mistyped and missing colons, where possible.

Have a Merry Scripting Christmas with Persistence of Vision—Wednesday, December 25th, 2019

Some of the best Christmas mornings I remember are mornings when I holed myself away and built things. On earlier Christmases, it could have been Legos, or off-brand erector sets, or a model train. Later, it might have been building characters or adventures using the new Dungeons and Dragons Basic or Expert boxed set I found under the tree.

When sitting at the computer, every day was Christmas, and in many ways it still is. As you can see from the majority of this blog, I’m still finding joy in the ASCII art script. So why not a touch more joy on Christmas morning? Consider this my erector set gift to you.

Except for this image, all of the ASCII art created using the asciiArt script in 42 Astounding Scripts came from photographs or drawings. That is, a human hand was involved in its creation.

This Christmas image, on the other hand, was nearly completely scripted. Obviously, the version with Linus’s Bible quote over the Christmas scene came from the asciiArt script, but the Christmas scene itself came from the Persistence of Vision raytracer. The only hand-placed part of the image was the phrase “Merry Christmas” in the upper left.

Persistence of Vision is a lot of fun to play with, Christmas morning or any morning. Or, as was often the case for me (and still often is) late into the night.

Remember to install brew and then “brew install povray” as described in the bubble cake example if you haven’t already. To create an image from this POV-Ray script file, run the povray program on it, as you would any other command-line script or program.

  • $ povray Christmas.pov

This will create, by default, an 800 by 600 PNG image with the same filename as the .pov file but with the extension .png. In this case, if you name the file “Christmas.pov” as I did, the file will come out “Christmas.png”.

You can adjust the width and/or height using the Width= and Height= command line options.

TRS-80 Color Computer RCHECK+ in Perl—Wednesday, November 20th, 2019
CoCo Terminal profile example

The only text editor I could find that would wrap exactly at 32 characters was vi in the Terminal.

I may be the only person who still enjoys typing in code from old books and magazines, but I do enjoy it, and enough to have written a modern version of those old books, 42 Astoundingly Useful Scripts and Automations for the Macintosh. I recently acquired a TRS-80 Color Computer 2, so I’ve been typing in old code from The Rainbow.

One of the big advantages of The Rainbow over other magazines of its era is the program RCHECK+. It’s a machine-language program that you load into memory before typing the code, and when you press the down arrow, it calculates a very simple checksum from the code you’ve typed.1 The listing in the magazine contains a series of checkpoints, such as this for TRENCH in the July 1986 issue of The Rainbow:

checkpointchecksum
7208
15178
20102
2828
3783
51188
END180

After typing line 7, the checksum should be 208; after typing line 15, it should be 178; and after typing in the entire program, it should be 180.

It turns out that this is a very simple checksum. All it’s doing is adding up the ASCII values of the code in RAM, cycling back to zero after a sum of 255. So the checksum is always from 0 to 255.2

The program is so useful that, even though I would much prefer to type BASIC code on a modern computer in a modern text editor with a modern keyboard, I didn’t.3 The values that RCHECK+ uses are the tokenized values, which means that GOTO is not G plus O plus T plus O, but rather the sum of hex 81 and A5, the tokenization of GOTO. But then I discovered that the decb program from the ToolShed suite can take a text BASIC listing and tokenize it to a standalone file.

Which makes it possible to write rcheck in Perl and run it on a text file created by decb.

Photo-editing with Persistence of Vision—Wednesday, September 18th, 2019
Raisin-Coconut Torte

There are more languages for scripting than are included in the current version of macOS, and it may very well be that in the future even the currently-installed languages will need to be installed. At the moment, the best way to install new languages and tools for the command-line is Homebrew

When I wrote 42 Astounding Scripts I deliberately left out languages that are not included with macOS. But if I had decided to relax that requirement, the language I would have relaxed it for would have been the Persistence of Vision Raytracer.

The easiest way to install it is probably using brew.

  • $ brew install povray

This installs the latest version of Persistence of Vision available for macOS. The command is povray. I have two tutorials for using POVRay online already; the one I used to use in classes at the University is Simple Photorealism using Persistence of Vision. There’s also Persistence of Text which contains several smaller tutorials.

The povray command, unfortunately, does not support a shebang line, nor does it support accessing arguments on the command-line. This means that you cannot create standalone script files as you can with Perl or Python. You usually end up hardcoding the options into the file. For example, suppose you want to overlay a flock of brightly-colored transparent spheres in front of a photo.

Building the Replica 1 Plus Apple 1 kit—Wednesday, May 1st, 2019

This is a photo of an Apple 1 kit running on my television set. It’s the Briel Replica 1 from ReActiveMicro. The kit was mostly dead easy to assemble. The hardest part was trusting myself when the instructions weren’t completely clear. The biggest lack—and it was only in, I think, two cases—had to do with polarization, that is, which direction a part needs to be. The instructions almost always mention whether a part is polarized. In two cases it does not:

  1. The crystal is not polarized, as far as I can tell.
  2. The 6821 chip does not have a notch to orient it to the socket. It does have a dot, and the dot is on the same side as where the notch would be if it had one.

The board itself is laid out nicely. The resistors and capacitors have their ratings listed on the board. This made the kit almost, but not quite, paint-by-numbers easy. If you’ve done electronics soldering in the past, you should have no problem putting this together.

The only problem I ran into was, during testing, everything went right; then I plugged the PS2 keyboard in and everything that went right kept happening over and over. What’s supposed to happen is that you reset the computer to get the cursor. This worked. Then it continued happening without pressing reset. I kept getting a new cursor, floating down the left of the television set—but only after I hit reset once myself.

Replica 1 first step: resistors

The first step is to put the resistors in. What a wide expanse of green!

I did what the instructions recommend when it doesn’t work, I went over the soldering on every part, joint by joint. I went over the entire underside, part by part, joint by joint. I found some soldering jobs that were worse than others; I fixed them. But there was really nothing that should have been causing a bad connection or a short.

I plugged it in again, pressed reset, and this time waited before pronouncing it a success and plugging in the keyboard. Sure enough, about every 1 ½ seconds I got a new cursor. Just a line of backslashes going down the left of the television set.

I went back over every connection again, and also strengthened the joints that are mainly structural, such as for holding in the keyboard socket and the composite video RCA plug.

BASIC auto-numbering and labels—Wednesday, January 9th, 2019
Model 100 Triangle

Programming on an 8-line screen means barely seeing the current subroutine.

While there are lots of things I dislike about writing in old-school BASIC, number one on the list is probably having to deal with cross-referencing line numbers. It is always annoying to have to add new code, renumber the existing lines, and then make sure that any cross-references have also been fixed.

So, of course, I wrote a Perl script to help me write BASIC programs (Zip file, 3.0 KB).

This allows me to write BASIC programs without line numbers, placing labels where I need them. Some of this is what I vaguely remember from programming in BASIC09 on OS-9 on the Tandy Color Computer.

Here’s a simple program to calculate the day of the week for any date since 1752:

OriginalWith automatic line numbers
  • REM This program computes the day of the week
  • REM Restriction: The date must be after 1752
  • DIM j$(7)
  • LET j$(1) = "Sunday"
  • LET j$(2) = "Monday"
  • LET j$(3) = "Tuesday"
  • LET j$(4) = "Wednesday"
  • LET j$(5) = "Thursday"
  • LET j$(6) = "Friday"
  • LET j$(7) = "Saturday"
  • #LABEL:GETDATE
  • PRINT "Month, day, and year (m, d, yyyy)";
  • INPUT m,d,y
  • IF y > 1752 THEN CALCULATE
  • PRINT "YEAR MUST NOT BE PRIOR TO 1753"
  • GOTO GETDATE
  • #LABEL:CALCULATE
  • LET k = INT(0.6+(1/m))
  • LET l = y-k
  • LET o = m+12*k
  • LET p = l/100
  • LET z1 = INT(p/4)
  • LET z2 = INT(p)
  • LET z3 = INT((5*l)/4)
  • LET z4 = INT(13*(o+1)/5)
  • LET z = z4+z3-z2+z1+d-1
  • LET z = z-(7*int(z/7))+1
  • PRINT m;"/";d;"/";y;" is a ";j$(z)
  • 10 REM This program computes the day of the week
  • 20 REM Restriction: The date must be after 1752
  • 30 DIM j$(7)
  • 40 LET j$(1) = "Sunday"
  • 50 LET j$(2) = "Monday"
  • 60 LET j$(3) = "Tuesday"
  • 70 LET j$(4) = "Wednesday"
  • 80 LET j$(5) = "Thursday"
  • 90 LET j$(6) = "Friday"
  • 100 LET j$(7) = "Saturday"
  • 200 REM getdate
  • 210 PRINT "Month, day, and year (m, d, yyyy)";
  • 220 INPUT m,d,y
  • 230 IF y > 1752 THEN 300
  • 240 PRINT "YEAR MUST NOT BE PRIOR TO 1753"
  • 250 GOTO 200
  • 300 REM calculate
  • 310 LET k = INT(0.6+(1/m))
  • 320 LET l = y-k
  • 330 LET o = m+12*k
  • 340 LET p = l/100
  • 350 LET z1 = INT(p/4)
  • 360 LET z2 = INT(p)
  • 370 LET z3 = INT((5*l)/4)
  • 380 LET z4 = INT(13*(o+1)/5)
  • 390 LET z = z4+z3-z2+z1+d-1
  • 400 LET z = z-(7*int(z/7))+1
  • 410 PRINT m;"/";d;"/";y;" is a ";j$(z)

The script converted the labels for CALCULATE and for GETDATE into line numbers in the references at lines 140 and 160. It also skipped the second and third sections to line 200 and 300, recognizing that an empty line meant a new section.

Here’s a simpler script with more sections in it, modified from the DWP 230 manual.1

OriginalWith automatic line numbers

[toggle code]

  • REM PRESTIGE ELITE 12 (DIABLO P/N 303033-01)
  • CLEAR 100
  • CSID$=CHR$(0)+CHR$(6*16+7):PAGE$=CHR$(1)+CHR$(11*16+8)
  • DIM CL$(95), SL(95), HL(95)
  • REM Switch to IBM mode
  • LPRINT CHR$(7)
  • INPUT "Did you hear a beep (y/n)";A$
  • IF A$ = "y" THEN PRINT "Already in IBM mode." ELSE PRINT "Changing to IBM mode.":LPRINT CHR$(27);CHR$(33)
  • INPUT "Wheel rotation";RO
  • REM Read character data
  • FOR I = 0 TO 95
    • READ C$, PS, HS
    • IF LEN(C$) > 1 THEN C$ = CHR$(VAL(C$))
    • CL$(I) = C$:SL(I) = PS:HL(I) = HS
    • PRINT C$;
  • NEXT I
  • PRINT
  • REM Push data to printer at page PAGE$
  • LPRINT CHR$(27);"=";CHR$(12*16+7);CHR$(0);CHR$(17);
  • LPRINT CSID$;PAGE$
  • FOR I = 0 TO 95
    • IF I+RO <= 95 THEN CHOICE = I+RO ELSE CHOICE = I-(96-RO)
    • LPRINT CL$(CHOICE);
    • LPRINT CHR$(HL(CHOICE)*16+SL(CHOICE)*2);
  • NEXT I
  • LPRINT CHR$(32);CHR$(255)
  • LPRINT CHR$(27);"[T";CHR$(5);CHR$(0);
  • LPRINT CSID$;PAGE$;
  • LPRINT CHR$(2)
  • END
  • # character, spacing, hammer strength
  • #INCREMENT:1
  • DATA a, 5, 2
  • DATA n, 5, 3
  • DATA r, 4, 2
  • DATA m, 7, 3
  • DATA c, 5, 2
  • DATA s, 4, 3
  • DATA d, 5, 3
  • DATA h, 5, 3
  • DATA l, 3, 2
  • DATA f, 4, 3
  • DATA k, 5, 3
  • DATA ",", 3, 1

[toggle code]

  • 10 REM PRESTIGE ELITE 12 (DIABLO P/N 303033-01)
  • 20 CLEAR 100
  • 30 CSID$=CHR$(0)+CHR$(6*16+7):PAGE$=CHR$(1)+CHR$(11*16+8)
  • 40 DIM CL$(95), SL(95), HL(95)
  • 100 REM Switch to IBM mode
  • 110 LPRINT CHR$(7)
  • 120 INPUT "Did you hear a beep (y/n)";A$
  • 130 IF A$ = "y" THEN PRINT "Already in IBM mode." ELSE PRINT "Changing to IBM mode.":LPRINT CHR$(27);CHR$(33)
  • 140 INPUT "Wheel rotation";RO
  • 200 REM Read character data
  • 210 FOR I = 0 TO 95
    • 220 READ C$, PS, HS
    • 230 IF LEN(C$) > 1 THEN C$ = CHR$(VAL(C$))
    • 240 CL$(I) = C$:SL(I) = PS:HL(I) = HS
    • 250 PRINT C$;
  • 260 NEXT I
  • 270 PRINT
  • 300 REM Push data to printer at page PAGE$
  • 310 LPRINT CHR$(27);"=";CHR$(12*16+7);CHR$(0);CHR$(17);
  • 320 LPRINT CSID$;PAGE$
  • 330 FOR I = 0 TO 95
    • 340 IF I+RO <= 95 THEN CHOICE = I+RO ELSE CHOICE = I-(96-RO)
    • 350 LPRINT CL$(CHOICE);
    • 360 LPRINT CHR$(HL(CHOICE)*16+SL(CHOICE)*2);
  • 370 NEXT I
  • 380 LPRINT CHR$(32);CHR$(255)
  • 390 LPRINT CHR$(27);"[T";CHR$(5);CHR$(0);
  • 400 LPRINT CSID$;PAGE$;
  • 410 LPRINT CHR$(2)
  • 420 END
  • 999 REM character, spacing, hammer strength
  • 1000 DATA a, 5, 2
  • 1001 DATA n, 5, 3
  • 1002 DATA r, 4, 2
  • 1003 DATA m, 7, 3
  • 1004 DATA c, 5, 2
  • 1005 DATA s, 4, 3

You can see that it moved the blank-line sections up to the nearest hundred, and the pound-sign section up to the nearest thousand. It also incremented the data statements by 1 instead of the default 10. In this case that’s useful because it helps me ensure I have the correct number of DATA statements. Each DATA statement corresponds to a daisy wheel position, and there are 96 of them (0 to 95, in the numbering of this BASIC program).

Also helping to ensure correct data lines, the #DATA directive takes all succeeding lines and puts them into data statements whose size (unless one element is greater than about 30 characters long) does not exceed 40 characters per line. #ENDDATA ends the data collection, potentially with a final data statement, such as “DATA end”. This makes it easy to keep DATA elements in order, such as alphabetical order, using sorting services on macOS.

OriginalWith automatic line numbers
  • #DATA feelings
  • despair
  • dread
  • enchantment
  • grief
  • guilt
  • hate
  • joy
  • love
  • pain
  • passion
  • pride
  • remorse
  • sadness
  • sorrow
  • #ENDDATA end
  • 2999 REM feelings
  • 3000 DATA despair, dread, enchantment
  • 3010 DATA grief, guilt, hate, joy, love
  • 3020 DATA pain, passion, pride, remorse
  • 3030 DATA sadness, sorrow
  • 3040 DATA end

DATA statements are assumed to jump to the next thousandth line.

While sorrowful dogs brood: The TRS-80 Model 100 Poet—Wednesday, December 26th, 2018

At Tandy Assembly 2018, I bought a DWP-230 daisywheel printer, and I bought the December 1982 issue of 80 micro. That issue included the fifth installment of Jay Chidsey’s “Bit Smitten” series.

This month Jay Chidsey shows you how to gain access to string data for use in programs which require random selection of words.

It was written for the Model I or III and only 16K of RAM. There was nothing in it, however, that used the specific capabilities of the Model I/III. Because of the ever-present variations on different versions of BASIC, even though both the I and 100 use Microsoft BASIC, some conversions were necessary.

The biggest change was generating random numbers. The Model I/III used RND(N) to generate random integers from 1 to N. The Model 100 only generates random numbers between 0 and 1. It’s up to the programmer to convert that to the desired integer range. The standard way of doing this is to multiply the random fraction by N, take the integer from that, and add 1.

  • REM random number from one to N
  • R = INT(RND(1)*N)+1

Further, while Chidsey didn’t use it, the Model I/III had the ability to use a semi-random seed1 so that the same results don’t come up every time you run the program. The Model 100 lacks that ability, which means the programmer has to seed the random number generator randomly. The standard means of doing this was to poll the clock. If you poll the seconds, you have a semi-reasonable random number from 0 to 59, giving you sixty seeds. I chose, instead to poll the ones digit of the seconds twice, in order to get a potentially more random seed from 0 to 99. There are two places in the program where the time could conceivably provide random seconds: the time the program starts up, and the time that the user says, start generating poetry.

  • 50 R1 = VAL(RIGHT$(TIME$, 1)) 'Tens digit for random seed
  • 280 PRINT:PRINT "Press any key to wax poetic.";
  • 290 GOSUB 5200
  • 300 REM seed randomizer
  • 310 CLS:PRINT "Cogitating..."
  • 320 R2 = VAL(RIGHT$(TIME$, 1)) 'Ones digit for random seed
  • 330 SD = R1*10+R2
  • 340 IF SD < 1 THEN 500
  • 350 S = RND(1)
  • 360 PRINT@20, SD
  • 370 SD = SD - 1
  • 380 GOTO 340

Even this would only mean a hundred different permutations of poetry, all down the line. Since there’s a keypress wait at the end of each poem, I also have it randomize a bit more based on the ones digit at that point.

Older posts