Categories
goatee-stroking musing, or something

The Mandelbrot Set, before Mandelbrot

How many CPU hours did I burn in the early 1990s rendering bits of the Mandelbrot Set? A lot, mainly because I was doing it in BASIC on an unexpanded 8 MHz Commodore Amiga A500. The image below that Fraqtive rendered in almost no time would have taken me days:

the squashed bug that started it all: the Mandelbrot set

But it turns out that the first rendering of what we now call the Mandelbrot set wasn’t produced by Benoit Mandelbrot, but by Brooks & Matelski a year or two earlier:

text plot of the Mandelbrot set with points inside set marked with asterisks
figure 2 (original caption “The set of C’s such that f(z) = z² + C has a stable periodic orbit”) from Brooks, Robert, and J. Peter Matelski. “The dynamics of 2-generator subgroups of PSL (2, C).Riemann surfaces and related topics: Proceedings of the 1978 Stony Brook Conference, Ann. of Math. Stud. Vol. 97. 1981.

What I’ve tried to do — and come close, but not actually managed to exactly replicate — is create period-appropriate code to reproduce that graphic. Since the paper was presented in 1978, there’s a fair chance that the authors had access to a machine running FORTRAN-77 or a near equivalent. FORTRAN’s particularly good for this:

  • it has a built-in COMPLEX type that extends regular mathematical functions;
  • it has just good enough string handling to output a line of spaces/asterisks. I would not have wanted to write this in FORTRAN-66, as that language had no string manipulation facilities at all.

So here’s the code. It should compile on any Fortran compiler:

      PROGRAM BRKMAT
! GENERATE FIGURE FROM BROOKS-MATELSKI PAPER C.1978
!  THAT EVENTUALLY BECAME KNOWN AS THE MANDELBROT SET
! - SCRUSS, 2020-06
! REF: BROOKS, ROBERT, AND J. PETER MATELSKI.
!     "THE DYNAMICS OF 2-GENERATOR SUBGROUPS OF PSL (2, C)."
!      RIEMANN SURFACES AND RELATED TOPICS: PROCEEDINGS OF THE
!      1978 STONY BROOK CONFERENCE,
!      ANN. OF MATH. STUD. VOL. 97. 1981: FIG. 2, P. 81

         REAL MAP, CR, CI
         INTEGER I, J, K, M, ROWS, COLS, MAXIT
         COMPLEX C, Z
         PARAMETER (ROWS=31, COLS=70, MAXIT=36)
         CHARACTER*80 OUT
         CHARACTER CH*1

         DO J=1,ROWS
            CI=MAP(REAL(J), 1.0, REAL(ROWS), -0.89, 0.89)
            DO I=1,COLS
               CR=MAP(REAL(I), 1.0, REAL(COLS), -2.0, 0.45)
               C=CMPLX(CR, CI)
               Z=CMPLX(0.0, 0.0)
               CH='*'
               DO 100, K=1,MAXIT
                  Z = Z**2 + C
                  IF (ABS(Z) .GT. 2) THEN
                     CH=' '
                     GO TO 101
                  END IF
 100           CONTINUE
 101           OUT(I:I)=CH
            END DO
            WRITE(*,*)OUT
         END DO
      END

      REAL FUNCTION MAP(X, XMIN, XMAX, YMIN, YMAX)
         REAL X, XMIN, XMAX, YMIN, YMAX
         MAP = YMIN + (YMAX - YMIN) * ((X - XMIN) / (XMAX - XMIN))
      END

The results are close:

mandelbrot set, rendered in asterisks from BRKMAT.F
a few more holes in this than the original

but not quite right. Maybe Brooks & Matelski had access to an Apple II and wrote something in BASIC? I could be entirely period-accurate and write something in PDP-8 BASIC on my SBC6120, but not today.

It really is much easier using a language with complex number support when working with the Mandelbrot set. Here’s the same program in Python3, which bears more of a resemblance to FORTRAN-77 than it might admit:

#!/usr/bin/python3
# brkmat - Brooks-Matelski proto ASCII Mandelbrot set - scruss, 2020-06
# -*- coding: utf-8 -*-

def valmap(value, istart, istop, ostart, ostop):
    return ostart + (ostop - ostart) * ((value - istart) / (istop - istart))

rows = 31
cols = 70
maxit = 36

for y in range(rows):
    ci = valmap(float(y + 1), 1.0, float(rows), -0.89, 0.89)
    for x in range(cols):
        cr = valmap(float(x + 1), 1.0, float(cols), -2.0, 0.45)
        c = complex(cr, ci)
        z = complex(0.0, 0.0)
        ch = '*'
        for k in range(maxit):
            z = z**2 + c
            if (abs(z) > 2.0):
                ch = ' '
                break
        print(ch, end='')
    print()

I found out about the Brooks-Matelski paper from this article: Who Discovered the Mandelbrot Set? – Scientific American. It’s none too complimentary towards Benoit Mandelbrot.

Categories
goatee-stroking musing, or something

MicroPython on the BrainPad Classic/BP2

GHI Electronics BrainPad Classic/BP2 electronics project board on a tasteful faux-leopard background. Small OLED screen is showing "SCRUSS 2020"
GHI Electronics BrainPad Classic/BP2 in 3d printed case

I’ve extended the MicroPython examples for the BrainPad Classic so that all of the devices work: scruss/brainpad-micropython: Micropython examples for the BrainPad Classic (BP2) from GHI Electronics.

The ones that already worked in the original examples repo are:

  • buttons
  • accelerometer
  • LEDs
  • light sensor
  • OLED screen

I’ve added:

  • temperature sensor: although my calibration may be a bit off on the MCP9701a used on the board
  • timer blink example: STM32 Timers are cool and we should use them
  • PWM RGB LED example: floating-point silliness with HSV(ish) Colour Wheel in Python
  • buzzer: simple tones plus tunes (in RTTTL) via dhylands / upy-rtttl
  • servos: I may have forgotten to put the example in there, but the standard Servo(1) code should work.

Yes, it would be nice to have a slick unified library like the BBC micro:bit does. For later, though.

Other resources:

Categories
goatee-stroking musing, or something

terminal colour silliness with Python

terminal text in rainbows

Using ansicolors:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# colourshen.py - stdin to rainbow stdout
# scruss, 2020-06

from colors import *            # see https://pypi.org/project/ansicolors/
import sys
wheel_pos = 0


def cos_wheel(pos):
    # Input a value 0 to 255 to get a colour value.
    # scruss (Stewart Russell) - 2019-03 - CC-BY-SA
    from math import cos, pi
    if pos < 0:
        return (0, 0, 0)
    pos %= 256
    pos /= 255.0
    return (int(255 * (1 + cos(pos * 2 * pi)) / 2),
            int(255 * (1 + cos((pos - 1 / 3.0) * 2 * pi)) / 2),
            int(255 * (1 + cos((pos - 2 / 3.0) * 2 * pi)) / 2))


def hex_wheel(pos):
    rgb = cos_wheel(pos)
    return('#%02x%02x%02x' % rgb)


def wheel_print(s):
    global wheel_pos
    incr = int(256/(1+len(s)))-1
    if incr < 1:
        incr = 1
    for c in s:
        print(color(c, fg=hex_wheel(wheel_pos)), end='')
        wheel_pos = (wheel_pos+incr) % 256
    print()


for txt in sys.stdin:
    wheel_print(txt.rstrip())

(fixed a very obvious ahem! in the code, hope no-one noticed …)

Categories
goatee-stroking musing, or something

mandala with chips

8-way symmetric constructed mandala on a black background with small unencapsulated silicon chips placed as jewels every 45°
graphite, pencil crayons, silicon chips
small rectangular silicon chip sitting on gold graphite background
detail at 600 dpi. I have lots of these.
Categories
goatee-stroking musing, or something

today’s collage

Categories
computers suck goatee-stroking musing, or something

Canaduino STM32 boards with MicroPython

Volker Forster at Universal Solder was kind enough to send me a couple of these boards for free when I asked about availability. By way of thanks, I’m writing this article about what’s neat about these micro-controller boards.

always neat packaging from Universal Solder

Can I just say how nicely packaged Universal Solder’s own or customized products are? They want it to get to you, and they want it to work.

I’d previously played around with Blue Pill and Black Pill boards with limited success. Yes, they’re cheap and powerful, but getting the toolchain to work reliably was so much work. So when I read about the WeAct STM32F411CEU6 board on the MicroPython forum, I knew they’d be a much better bet.

Canaduino Black Pill Carrier Board with STM32F411 (and battery) installed

Volker sent me two different things:

Let’s start with the STM32 Screw Terminal Adapter:

Canaduino Black Pill Carrier Board (front)

It’s a neat, solid board built on a black 1.6 mm thick PCB. Apart from the obvious screw terminals — essential for long-term industrial installations — it adds three handy features:

  • a real-time clock battery. If you’re using a micro-controller for data logging, an RTC battery helps you keep timestamps accurate even if the device loses power.
  • mounting holes! This may seem a small thing, but if you can mount your micro-controller solidly, your project will look much more professional and last longer too.
  • A 6–30 V DC regulator. Connect this voltage between Vin and GND and the regulator will keep the board happy. From the helpful graph on the back of the board, it doesn’t look as if things start getting efficient until around 12 V, but it’s really nice to have a choice.
Canaduino Black Pill Carrier Board (back)

I made a little slip-case for this board so it wouldn’t short out on the workbench. The project is here: Canaduino STM32 Screw Terminal board tray and you can download a snapshot here:

The boards themselves are pretty neat:

two STM32F411 Black Pill boards from Canaduino

Gone are the lumpy pin headers of the earlier Blue and Black Pill boards, replaced by tactile switches. The iffy micro USB connectors are replaced by much more solid USB C connectors. According to STM32-base, the STM32F411 has:

  • 100 MHz ARM Cortex-M4 core. This brings fast (single-precision) floating point so you don’t have to fret over integer maths
  • 512 K Flash, 128 K RAM. MicroPython runs in this, but more flash is always helpful
  • Lots of digital and analogue I/O, including a 12-bit ADC
  • A user LED and user input switch.

About the only advanced features it’s missing are a true RNG, a DAC for analogue outputs, and WiFi. But on top of all this, Volker added:

the all-important 128 Mbit flash chip (and capacitor) fitted by Universal Solder

128 Mbit of Flash! This gives the board roughly 16 MB of storage that, when used with MicroPython, appears as a small USB drive for your programs and data. I found I was able to read the ADC more than 22,000 times/second under MicroPython, so who needs slow-to-deploy compiled code?

Building and Installing MicroPython

This is surprisingly easy. You’ll need to install the gcc-arm-none-eabi compiler set before you start, but following the instructions at mcauser/WEACT_F411CEU6: MicroPython board definition for the WeAct STM32F411CEU6 board will get you there.

I had to run make a couple of times before it would build, but it built and installed quickly. This board doesn’t take UF2 image files that other boards use, so the installation is a little more complicated than other. But it works!

Once flashed, you should have a USB device with two important MicroPython files on it: boot.py and main.py. boot.py is best left alone, but main.py can be used for your program. I’m going into more details in a later article, but how about replacing the main.py program with the fanciest version if Blink you ever saw:

# main.py -- fancy Blink (scruss, 2020-05)

from pyb import LED
from machine import Timer
tim = Timer(-1)
tim.init(period=1000, mode=Timer.PERIODIC,
         callback=lambda t: LED(1).toggle())

None of that blocking delay() nonsense: we’re using a periodic timer to toggle the user LED every second!

debugging the mystery huge potentiometer using two ADC channels

I’m really impressed with the Universal Solder-modified board as an experimentation/discovery platform. MicroPython makes development and testing really quick and easy.

[and about the mystery huge potentiometer: it’s a Computer Instruments Corporation Model 206-IG multi-turn, multi-track potentiometer I picked up from the free table at a nerd event. I think it’s a 1950s (so Servo-control/Cybernetics age) analogue equivalent of a shaft encoder, looking at the patent. Best I can tell is that each pot (there are two, stacked, with precision bearings) appears to have two 120° 10k ohm sweep tracks offset 90° to one another. The four wipers are labelled -COS, -SIN, +COS and +SIN. If anyone knows more about the thing, let me know!]

Categories
goatee-stroking musing, or something

SimStapler© Simulator

ka-chunk … ka-chunk … ka-chunk … Splendid!

Long ago, the was a thing that called itself “SimStapler©”. It was a very early clicker game: every ten times you clicked on the virtual stapler, you got bonus audio. That was all it did.

It was a simpler time …

A couple of days ago, Lee posted a video with reminded me so much of that virtual stapler:

Since I have the hardware and for various reasons my social calendar isn’t what it was, I set out on the bold plan to make Sim-SimStapler© … or SimStapler© Simulator … or RealStaplerⒹ … or … look, I’ve spent more effort in trying to come up with a name for this than I did making the thing, so call it what you want.

You’ll need a Circuit Playground Express, a couple of alligator clip test leads, and a stapler. And maybe some tape and a paperclip, too

a Circuit Playground Express, a couple of alligator clip test leads, and a stapler
it doesn’t have to be in “I believe you have my stapler” red, but I feel it helps somehow

The important thing about a switch is that it has two electrically isolated parts that come together to close a circuit. And that’s exactly what the Swingline® 747® stapler doesn’t have: its entire metal body and mechanism is electrically conductive. So we have to rig something up …

stapler anvil with tape across it supporting a paperclip, taped down. A green alligator clip is attached to the free end of the alligator clip
Tape, paperclip and alligator clip make up one half of the switch
rear of stapler with white alligator clip protruding, clipped to staple dispenser
… while a handy ledge at the back of the staple dispenser provides a connection for alligator clip #2

Did I say take the staples out yet? No? Take the staples out of the stapler. Possibly even before doing anything else.

The code we’re going to run on the Circuit Playground Express is very simple:

  • Set up pin 1 (helpfully named A7 on the board) as an input. Turn off all the LEDs
  • If pin 1 is shorted to ground, increase a counter and light successive numbers of LEDs round the CPX’s face
  • If the counter reaches 10, play the sample, reset the counter and turn off all the LEDs
  • repeat from “If pin 1 is shorted to ground …” until too bored to continue.

Here’s the code:

# SIM-SimStapler / RealStapler - scruss, 2020-04
# circuitpython on CPX - stapler between D1 (A7) and GND

from adafruit_circuitplayground import cp
import board
from digitalio import DigitalInOut, Direction, Pull
import time

# set up stapler on pin D1 (port A7): goes LOW when pressed
stapler = DigitalInOut(board.D1)
stapler.direction = Direction.INPUT
stapler.pull = Pull.UP

# set up pixels - not too bright
cp.pixels.brightness = 0.1
# turn all pixels off
for i in range(10):
    cp.pixels[i] = (0, 0, 0)

count = 0
while True:
    # stapler pressed, so increase count
    if not stapler.value:
        count = count + 1
        # only count first press, not if held
        while not stapler.value:
            pass
        time.sleep(0.1)

    # light up pixels clockwise for each press
    for i in range(count):
        cp.pixels[9 - i] = (0, 255, 0)

    # get a bonus Penelope Keith every ten presses
    if count == 10:
        cp.play_file("splendid.wav")
        # turn all pixels off after bonus
        for i in range(count):
            cp.pixels[i] = (0, 0, 0)
        # and reset counter for next time
        count = 0

Here’s the code and sample ready to be copied to your CIRCUITPYTHON drive:

(The sample is a slightly tweaked version of Freeverse’s original Bonus.wav. I ran it through an equalizer to make it sound less awful through the CPX’s tinny little speaker. I was also today years old when I found out that the sample wasn’t Penelope Keith from To the Manor Born, but Jen Krasinski, a staffer at Freeverse.)

The connection (singular) is simple:

Alligator clips to A7 (in reality, D1) and GND

Have an appropriate amount of fun!

I suppose I could also have done this on the BrainPad, but I haven’t set it up with MicroPython yet, and I don’t have time to drag coding blocks around. Also, this is not any project to associate with the word “brain” …

If the video doesn’t work: local link.

Categories
goatee-stroking musing, or something

Rainbow Moose

Rainbow Moose to enhance your day
Rainbow Moose detail (as seen on ig)

Model: Moose Head for Materio3D by Morena Protti (modified)
Clear PLA with manual colouring using ShinHan Touch markers

Categories
goatee-stroking musing, or something

I maded it: TRex Solder 3rd Hand

TRex Solder 3rd Hand

https://www.thingiverse.com/thing:4241697

(since Thingiverse’s markdown parser seems to be broken)

Summary

A weighted, non-slip, non-wobble soldering third hand.

Huge thanks to Modular Hose for giving me the Loc-Line samples I used to make this!

Why is it called TRex? It’s got really short arms …

Despite what Thingiverse might say, this is not a Customizer project. Opening that link will disappoint.

Parts required

  • at least 12× ¼” Loc-Line segments
  • 2× Loc-Line ¼” NPT threaded connectors
  • 2× Loc-Line ¼” nozzles
  • 2× alligator clips
  • 3× adhesive round non-slip feet (up to 16 mm diameter)
  • appx. 60 g steel BB shot (or available equivalent) as ballast
  • hot glue to secure ballast and seal port in place
  • Polycaprolactone (PCL; trade names include InstaMorph) warm-melt granules to secure clips into nozzles
  • (optional) PTFE plumber’s tape.

Note that Modular Hose promotional Loc-Line keyrings each have 3 segments plus a threaded connector and nozzle, so four keyrings provide enough parts.

Assembly

  1. assemble the two Loc-Line arms with a threaded connector and at least six segments each
  2. secure the ends of the alligator clips in the nozzles using softened PCL or hot glue. Make sure that the ball joint connector surface is clear of material, as you’ll need this to fit the nozzle onto the arm
  3. fill the tool body with ballast, apply a plug of hot glue to stop it rattling and secure the end port in place
  4. Carefully thread the arms onto the tool body. NPT threads are tapered, so become gradually tighter as they go in. Use a little plumber’s tape if they’re too loose. Be careful not to overtighten, as this might crack the tool
  5. Apply the non-slip feet, applying appropriate pressure to activate the adhesive
  6. Get soldering!

Acknowledgements

Dan Kirshner’s Thread-drawing modules for OpenSCAD were used to make a nice ¼” NPT taper-profile thread. Thanks, Dan!

Foot and shot port
Side view

Data archive: TRexSolder3rdHand.tar.gz

Categories
goatee-stroking musing, or something

I feel so seen … from 1814

So it seems my lifelong nickname is/was a Piedmontese word for noise, crash/clatter/bang or scream/shout/squawk:

[french/piedmnotese dictionary text]
Scruss s.; Bruit, fracas, cri.
La porta a la fait un scruss;
La porte a crié, a fait un cri.
from Capello, Luigi. Dictionnaire portatif piémontais-français suivu d’un vocabulaire français des termes usités dans les Arts et Métiers.. Impr. V. Bianco, 1814.

An early 19th century dictionary written by an Italian count had my number all along.

Categories
goatee-stroking musing, or something

FreeCAD on Raspberry Pi 4

FreeCAD window displaying red and white traffic cone model
FreeCAD 0.18.4 running on a Raspberry Pi 4

FreeCAD and the Raspberry Pi haven’t always got on too well. For complex technical reasons the standard package would load and immediately crash on a Raspbian system. For user reasons, this was just another annoyance.

Recent releases seem to run fairly well on a Raspberry Pi 4, though, but only after building them from source. Here’s a method that got FreeCAD 0.18.4 running for me. It’s lightly modified from FreeCAD forum MartijnD‘s post:

sudo apt install cmake build-essential libtool lsb-release swig libboost-dev libboost-date-time-dev libboost-filesystem-dev libboost-graph-dev libboost-iostreams-dev libboost-program-options-dev libboost-python-dev libboost-regex-dev libboost-serialization-dev libboost-signals-dev libboost-thread-dev libcoin-dev libeigen3-dev libgts-bin libgts-dev libkdtree++-dev libmedc-dev libopencv-dev libproj-dev libvtk6-dev libx11-dev libxerces-c-dev libzipios++-dev qt4-dev-tools libqt4-dev libqt4-opengl-dev libqtwebkit-dev libshiboken-dev libpyside-dev pyside-tools python-dev python-matplotlib python-pivy python-ply python-pyside libocct*-dev occt-draw libsimage-dev doxygen libcoin-doc dh-exec libspnav-dev

wget https://github.com/FreeCAD/FreeCAD/archive/0.18.4.zip

unzip 0.18.4.zip

rm 0.18.4.zip

mkdir freecad-build

cd freecad-build

cmake -DPYTHON_EXECUTABLE=/usr/bin/python2.7 -DPYTHON_INCLUDE_DIR=/usr/include/python2.7 -DPYTHON_LIBRARY=/usr/lib/arm-linux-gnueabihf/libpython2.7.so  -DPYTHON_PACKAGES_PATH=/usr/local/lib/python2.7/dist-packages/  ../FreeCAD-0.18.4/

make -j4

Notes:

  • The only modifications I made to Martijn’s method were in the Python paths in the cmake command. Some of the paths given aren’t valid any more on an up-to-date Buster system
  • I built this on a Raspberry Pi 4 with 4 GB of RAM. It takes quite a bit of free storage: I wouldn’t attempt to build this with less than 4 GB free
  • make -j4 took 95 minutes, and even with a fan my Raspberry Pi 4 was at 70°C
  • Yes, it’s using Python 2.7, but it works
  • I’ve got no idea how to make it install properly, but it runs from the freecad-build/bin directory.

If you want to learn how to use it, look at the tutorials: even the Raspberry Pi Foundation have written some. The UK Traffic Cone model you can have: it’s what I made to learn a bit more about FreeCAD. Don’t worry, I’m still on Team OpenSCAD

Categories
goatee-stroking musing, or something

a bad scene, a worse infographic

CBC says that Alberta’s looming multibillion-dollar orphan wells problem prompts auditor general probe. I mean, I’d say it does: estimated costs to clean up abandoned petrochemical wells outstrip the industry cleanup fund by over 132×, so it’s gone way past looming and is well into omnishambles country. But I’m not here to talk about the environmental mismanagement (well, not much: lolRedwater …), but more to talk about CBC’s terrible infographic:

Total estimated liabilities: $30.1 billion; Total security held: $227 million. It doesn’t take Wilkins Micawber to tell you that the result is misery

The image is accurate, technically. The estimated liabilities ($30100000000) are 132.6× the total security held ($227000000), and the red square’s length is 11½× (= √132.6) the blue one’s. But people are generally terrible at comparing areas.

Here are the same numbers, but in bar chart form:

Alberta estimated oil and gas cleanup costs vs allocated funds: costs more than 132 times coverage
They’re not even on the same page, are they?
(Graph badly put together by me in netpbm. Yes, netpbm …)

And there’s the problem: it’s too big to comprehend. CBC’s comfortable little chart fits on a page; you can tweet it, even. But reality is a whole lot of scrolling down the page.

Even the manky old pie chart would be better than CBC’s squares-by area:

Pie chart of estimated liabilities vs security funds for AB abandoned oil wells. A small blue wedge of less than 3° of the total represents available securities.
Securities are a 2.69° wedge. Liabilities, the rest

At least pie charts used linear measure as a proportion of the full 360° pie. But comparing areas is hard; in the diagram below, the teal-coloured part is twice the area of the gold part.

two sets of concentric circles: on the left, the outer area is 2× the area of the centre. On the right, the centre circle is 2x that of the centre
Confusing, isn’t it?
Categories
goatee-stroking musing, or something

this is why I did wind power instead

07:32 - This is a Province of
Ontario emergency bulletin
which applies to people
within ten (10) kilometres
of the Pickering Nuclear
Generating Station. An
incident was reported
at the Pickering Nuclear
Generating Station. There
has been NO abnormal

Update, a few panicked minutes later: false alarm. Still no news release from OPG or province-wide phone alert to stand down, but there is this:

Just as a reminder, you can get potassium iodide pills for free if you live within 50 km of an Ontario nuclear installation. Go to preparetobesafe.ca

50 km buffer from GTA nuclear stations
50 km buffer from Pickering and Darlington nuclear stations. Includes all of Toronto and much of the GTA
50 km buffer from Bruce station
50 km buffer from Bruce station

I don’t know if I should be including Chalk River in this.

Further update: yup, they retracted it. But a scary wee-waa-wee-waa siren while the text is read in a robot voice is not very reassuring

EMERGENCY ALERT /
ALERTE D’URGENCE

There is NO active nuclear
situation taking place

at the Pickering Nuclear
Generating Station. The

previous alert was issued in
error. There is no danger to
the public or environment.
No further action is
required.
Issued 09:11
Categories
goatee-stroking musing, or something

wrinkle #plottertwitter

Generative art output on a Roland pen plotter
Categories
goatee-stroking musing, or something

For completeness: Level 9 “Snowball” bitmap font

The Snowball font on the Amstrad CPC 464

Oooh blecch — did I really like this when I was a young ‘un? Following on from the same process as in The coolest font (when I was 15, that is) here’s the bitmap font from Level 9’s Snowball.

 10 REM    *** L9SFONT.BAS ***
 15 REM bitmap font from Level 9's
 20 REM Snowball text adventure
 30 REM  on the Amstrad CPC 464
 40 REM (it was so cool at the time…)
 50 REM Dug up by scruss, 2020-01
 60 REM ==============================
 100 SYMBOL AFTER 32
 110 MODE 1
 120 GOSUB 1000
 130 PRINT" *** It's the Level 9 font ***"
 140 PRINT" ***    from  Snowball!    ***"
 150 PRINT"   Dug up by scruss, 2020-01"
 160 PRINT
 170 PEN 2
 180 PRINT"Lorem ipsum dolor sit amet, consectetur"
 190 PRINT"adipiscing elit, sed do eiusmod tempor"
 200 PRINT"incididunt ut labore et dolore magna"
 210 PRINT"aliqua. Ut enim ad minim veniam, quis"
 220 PRINT"nostrud exercitation ullamco laboris"
 230 PRINT"nisi ut aliquip ex ea commodo consequat"
 240 PRINT"arfle barfle gloop? | | |"
 250 PRINT
 260 PEN 1
 270 FOR i%=32 TO 127
 280 PRINT CHR$(i%); " ";
 290 NEXT i%
 300 PRINT
 310 PRINT
 990 END
 1000 SYMBOL 33,&10,&28,&28,&28,&10,0,&38,0
 1010 SYMBOL 34,&66,&66,&44,&88,0,0,0,0
 1020 SYMBOL 35,0,&24,&7e,&24,&24,&7e,&24,0
 1030 SYMBOL 36,&10,&7c,&40,&7c,&04,&7c,&10,0
 1040 SYMBOL 37,&e4,&a4,&e8,&10,&2e,&4a,&4e,0
 1050 SYMBOL 38,&70,&88,&88,&72,&84,&88,&76,0
 1060 SYMBOL 39,&0c,&0c,&08,&10,0,0,0,0
 1070 SYMBOL 40,&0c,&10,&30,&30,&20,&14,&0c,0
 1080 SYMBOL 41,&30,&28,&04,&0c,&0c,&08,&30,0
 1090 SYMBOL 42,&10,&54,&38,&fe,&38,&54,&10,0
 1100 SYMBOL 43,0,&10,&10,&7c,&10,&10,0,0
 1110 SYMBOL 44,0,0,0,0,&18,&18,&10,&20
 1120 SYMBOL 45,0,0,0,&fc,0,0,0,0
 1130 SYMBOL 46,0,0,0,0,&1c,&14,&1c,0
 1140 SYMBOL 47,0,&06,&0e,&08,&10,&70,&60,0
 1150 SYMBOL 48,&fc,&8c,&8c,&84,&c4,&c4,&fc,0
 1160 SYMBOL 49,&30,&10,&10,&10,&18,&18,&38,0
 1170 SYMBOL 50,&f8,&98,&08,&f8,&80,&c8,&f8,0
 1180 SYMBOL 51,&7c,&64,&04,&3c,&34,&04,&7c,0
 1190 SYMBOL 52,&c8,&88,&88,&f8,&08,&18,&18,0
 1200 SYMBOL 53,&f8,&98,&80,&f0,&08,&c8,&f0,0
 1210 SYMBOL 54,&78,&98,&80,&f8,&98,&c8,&f8,0
 1220 SYMBOL 55,&fc,&c4,&04,&08,&08,&18,&18,0
 1230 SYMBOL 56,&fc,&c4,&8c,&fc,&84,&9c,&fc,0
 1240 SYMBOL 57,&fc,&e4,&84,&fc,&04,&0c,&0c,0
 1250 SYMBOL 58,&38,&28,&38,0,&38,&28,&38,0
 1260 SYMBOL 59,&18,&18,0,&18,&18,&10,&20,0
 1270 SYMBOL 60,&04,&08,&10,&20,&10,&08,&04,0
 1280 SYMBOL 61,0,0,&7e,0,&7e,0,0,0
 1290 SYMBOL 62,&80,&40,&20,&10,&20,&40,&80,0
 1300 SYMBOL 63,&7c,&04,&1c,&10,&10,0,&10,0
 1310 SYMBOL 64,&3e,&22,&2e,&2a,&2e,&20,&3e,0
 1320 SYMBOL 65,&fc,&c4,&8c,&fc,&84,&c4,&c4,0
 1330 SYMBOL 66,&f8,&84,&e4,&f8,&84,&9c,&f8,0
 1340 SYMBOL 67,&fc,&8c,&80,&80,&c0,&c4,&fc,0
 1350 SYMBOL 68,&f8,&64,&64,&44,&4c,&4c,&f8,0
 1360 SYMBOL 69,&fc,&8c,&c0,&f8,&c0,&8c,&fc,0
 1370 SYMBOL 70,&fc,&9c,&80,&f8,&80,&c0,&c0,0
 1380 SYMBOL 71,&fc,&9c,&80,&9c,&c4,&c4,&fc,0
 1390 SYMBOL 72,&c4,&c4,&84,&fc,&84,&8c,&8c,0
 1400 SYMBOL 73,&7c,&14,&10,&10,&10,&50,&7c,0
 1410 SYMBOL 74,&7c,&54,&10,&18,&18,&90,&60,0
 1420 SYMBOL 75,&c8,&90,&a0,&d0,&88,&8c,&8c,0
 1430 SYMBOL 76,&c0,&c0,&c0,&80,&80,&98,&f8,0
 1440 SYMBOL 77,&82,&ee,&92,&92,&ba,&82,&c6,0
 1450 SYMBOL 78,&8c,&c4,&c4,&b4,&8c,&8c,&84,0
 1460 SYMBOL 79,&fc,&c4,&c4,&84,&8c,&8c,&fc,0
 1470 SYMBOL 80,&fc,&c4,&8c,&fc,&80,&c0,&c0,0
 1480 SYMBOL 81,&fc,&c4,&c4,&84,&94,&88,&f4,0
 1490 SYMBOL 82,&fc,&c4,&8c,&fc,&90,&c8,&cc,0
 1500 SYMBOL 83,&fc,&84,&80,&78,&04,&84,&fc,0
 1510 SYMBOL 84,&7c,&10,&10,&10,&30,&30,&30,0
 1520 SYMBOL 85,&c4,&c4,&84,&8c,&8c,&8c,&fc,0
 1530 SYMBOL 86,&c4,&84,&84,&48,&48,&30,&30,0
 1540 SYMBOL 87,&84,&84,&84,&b4,&b4,&cc,&84,0
 1550 SYMBOL 88,&cc,&cc,&48,&30,&48,&cc,&cc,0
 1560 SYMBOL 89,&86,&86,&44,&28,&10,&18,&18,0
 1570 SYMBOL 90,&fc,&84,&08,&30,&40,&84,&fc,0
 1580 SYMBOL 91,&7c,&60,&60,&40,&40,&4c,&7c,0
 1590 SYMBOL 92,0,&60,&30,&10,&08,&0c,&06,0
 1600 SYMBOL 93,&3e,&32,&02,&02,&06,&06,&3e,0
 1610 SYMBOL 94,&18,&24,&42,&42,0,0,0,0
 1620 SYMBOL 95,0,0,0,0,0,0,&ee,&bb
 1630 SYMBOL 96,&3c,&22,&78,&20,&78,&20,&7e,0
 1640 SYMBOL 97,0,0,&f8,&98,&88,&cc,&fc,0
 1650 SYMBOL 98,&80,&80,&f8,&98,&88,&c8,&f8,0
 1660 SYMBOL 99,0,0,&f8,&88,&c0,&c8,&f8,0
 1670 SYMBOL 100,&08,&08,&f8,&98,&88,&c8,&f8,0
 1680 SYMBOL 101,0,0,&f8,&80,&e0,&80,&f8,0
 1690 SYMBOL 102,0,&1c,&10,&38,&38,&10,&70,0
 1700 SYMBOL 103,0,0,&f8,&98,&88,&f8,&08,&78
 1710 SYMBOL 104,&c0,&c0,&80,&f8,&88,&cc,&cc,0
 1720 SYMBOL 105,0,&60,0,&60,&60,&70,&70,0
 1730 SYMBOL 106,&08,0,&18,&18,&08,&08,&68,&78
 1740 SYMBOL 107,&c0,&c0,&cc,&d8,&f0,&d8,&cc,0
 1750 SYMBOL 108,&30,&30,&30,&30,&30,&30,&30,0
 1760 SYMBOL 109,0,0,&cc,&b4,&b4,&84,&84,0
 1770 SYMBOL 110,0,0,&f8,&98,&88,&cc,&cc,0
 1780 SYMBOL 111,0,0,&f8,&98,&88,&c8,&f8,0
 1790 SYMBOL 112,0,0,&f8,&98,&88,&f8,&80,&80
 1800 SYMBOL 113,0,0,&f8,&88,&c8,&f8,&0c,&0c
 1810 SYMBOL 114,0,0,&f8,&98,&80,&c0,&c0,0
 1820 SYMBOL 115,0,0,&f8,&80,&70,&08,&f8,0
 1830 SYMBOL 116,&60,&60,&20,&78,&20,&28,&38,0
 1840 SYMBOL 117,0,0,&c8,&c8,&98,&98,&f8,0
 1850 SYMBOL 118,0,0,&cc,&cc,&88,&70,&20,0
 1860 SYMBOL 119,0,0,&84,&84,&b4,&b4,&cc,0
 1870 SYMBOL 120,0,0,&cc,&48,&30,&48,&cc,0
 1880 SYMBOL 121,0,0,&88,&c8,&f8,&08,&08,&78
 1890 SYMBOL 122,0,0,&f8,&90,&20,&48,&f8,0
 1900 SYMBOL 123,&0c,&30,&10,&60,&10,&30,&0c,0
 1910 SYMBOL 124,&df,&db,&db,&9f,&83,&9b,&fb,0
 1920 SYMBOL 125,&30,&0c,&08,&06,&08,&0c,&30,0
 1930 SYMBOL 126,&7c,&82,&ba,&a2,&ba,&82,&7c,0
 1940 SYMBOL 127,&FF,&FF,&FF,&FF,&FF,&FF,&FF,&FF
 1950 RETURN

Process was the same:

  1. Load the disk image into an emulator
  2. Load the game
  3. Save the game as a v2 snapshot – these are uncompressed memory dumps
  4. Dump the snapshot to a png using phooky’s PROM/bin2png.py
  5. Crop that image down to just the character data and save as a plain PBM
  6. For each column, create the hex dump using shell code a bit like this:
sc=32; pnminvert snowball_font.pbm | pnmnoraw | tail +3 | cut --character="1-8"|while read f; do echo "printf '&%02x, \n' "\"\$((2#${f}))\"; done | bash | fmt -w43 | sed 's/,$//;' | while read g; do echo SYMBOL $sc", "$g; sc=$((sc + 1)); done | awk '{print 10 * (NR + 0) " " $0;}' > schars032to063.bas
Categories
goatee-stroking musing, or something

Adding the date to Sony Mavica FD-91 images

20 years ago, this was one state-of-the-art brick:

Sony Mavica FD-91: a camera with a floppy drive, or a Sovet brutalist monument? Discuss (briefly)

The only way to get pictures from it is via the floppy disk drive on the side. Then you’ve got a bunch of images with 8.3 filenames and the only metadata being the date saved from the camera’s clock. I’d written about it before, but I had an event that I wanted to record with a suitable camera, so adding some metadata to the pictures became important.

#!/bin/bash
# mavicafix - add some basic exif info to MVC-FD91 images
# scruss - 2019-12

jhead -mkexif  "$@"
jhead -ta+5:00 "$@"
exiftool -Make='Sony' -Model='MVC-FD91' -Orientation=1 -n "$@"
jhead -ft "$@"
rename '$_=lc($_);' "$@"

What it does:

  1. uses jhead to add a minimal EXIF header containing the file modification time
  2. corrects that time (I think the Mavica expects it in UTC) to my local time
  3. adds basic make/model/which-way-up data using exiftool
  4. Corrects the file modification time back to the stored EXIF value
  5. renames the files to all lower case.

Categories
goatee-stroking musing, or something

Save money, buy misery: cheap STM32 boards

(I’m still writing this. It will change over time.)

Use an STM32 Blue Pill or Black Pill micro-controller board”, they said. “So cheap, so powerful”, they said. “You’ll love it”, they said.

Dear Reader, none of the above turned out to be true.

For some time now I’ve been looking for a cheap, USB HID micro-controller board that is somewhat more flexible than the ATMega32U4 (Arduinos Leonardo, Micro and Pro Micro; also the impossibly smol Atto) and yet not quite as flexible as the let’s-accidentally-overwrite-our-accessibility-code-with-the-holiday-snaps CircuitPython boards from Adafruit. And for a while it looked like the STM32 boards might do it: they’ve got a 72 MHz ARM Cortex-M3 with at least 64 KB of Flash and 20 KB of SRAM and they’re under $5. Yay?

Not quite. There are three main problems with the STM32 boards that get in the way of inexpensive electronic nerdery.

1: They may not actually be STM32 chips

Slightly grotty photos follow. One day I’ll get a better USB microscope.

First, the chip from a “Black Pill” board bought recently:

flux blobs aside, this is clearly marked STM32 F103C8T6

Compare with a “Blue Pill” bought last year:

the very tentatively marked CS32F 103C8T6 and its possibly fake CKS (“China Key System”) logo

Knock-offs are rife in the cheap end of the market, and at least this chip is honest enough to say that it’s not from STMicroelectronics. While it may be possible to program these things with some heroic faffing about, consider balancing the effort required versus the time cost of doing so.

2: They may not have working USB

moar later (about the Blue Pill’s incorrect resistor)

3: The documentation is everywhere and nowhere and Google is not your friend

even moar later (about a very dedicated amateur’s hosting of the project documentation becoming too successful for him to afford)

(Huge thanks to Andrew Klaassen who provided me his notes for getting some of these boards at least able to run Blink under Linux.)

Categories
goatee-stroking musing, or something

Schotter on a Tek 4010

Georg Nees’ Schotter, reproduced on a simulated Tek4010 display

Georg Nees (1926–2016) was a pioneer of generative art. His piece “Schotter” (gravel) from 1968 displayed a rectangular array of squares with the position of each square becoming more random as the piece progressed.

The BASIC code for this video is based on Processing code by Jim Plaxco, http://www.artsnova.com/

The Tektronix 4010 display simulator is by Rene Richarz, https://github.com/rricharz/Tek4010

The BASIC interpreter with Tektronix graphics support is Matrix Brandy BASIC maintained by Michael McConnell, https://github.com/stardot/MatrixBrandy

(Higher-quality video at Georg Nees’ “Schotter”, reproduced on a simulated Tek4010 display at YouTube.)

  100 REM Georg Nees "Schotter" reproduction - scruss, 2019-11
  110 REM based on Processing 3 code by Jim Plaxco, www.artsnova.com
  120 :
  130 scrn_wd% = 2048: REM fixed screen size, unlike Processing
  140 scrn_ht% = 1560
  150 columns% = 12
  160 rows% = 22
  170 sqsize% = FN_min(scrn_wd% / (columns% + 4), scrn_ht% / (rows% + 4))
  180 padding% = 2 * sqsize%
  190 randstep = 0.22
  200 randsum = 0.0
  210 randval = 0.0
  220 dampen = 0.45
  230 :
  240 SYS "Brandy_TekEnabled", 1
  250 A$=GET$: REM PLOT 4, 0, 0: PLOT 5, 0, 0
  260 FOR y% = 1 TO rows%
  270   randsum = randsum + (y% * randstep)
  280   FOR x% = 1 TO columns%
  290     randval = RND(1) * (2 * randsum) - randsum
  300     PROC_square((scrn_wd% / 2) - (sqsize% * columns% / 2) + (x% * sqsize%) - (sqsize% / 2) + INT(randval * dampen), scrn_ht% - (padding% + (y% * sqsize%) - (sqsize% / 2) + INT(randval * dampen)), sqsize%, randval)
  310   NEXT x%
  320 NEXT y%
  330 PLOT 4, 0, 0: PLOT 5, 0, 0
  340 END
  350 :
  360 DEF PROC_square(cx%, cy%, side, angle)
  370 LOCAL r, i%, pm%
  380 pm% = 4: REM plot mode - move = 4, draw = 5
  390 r = (side / 2) / (SQR(2) / 2)
  400 FOR i% = 0 TO 4
  410   PLOT pm%, cx% + r * COS(RAD(angle + i% * 90 + 45)), cy% + r * SIN(RAD(angle + i% * 90 + 45))
  420   pm% = 5
  430 NEXT i%
  440 ENDPROC
  450 :
  460 DEF FN_min(a%, b%) IF a% < b% THEN =a% ELSE =b%
Categories
goatee-stroking musing, or something

All of the Argos Catalogues

The 1974 Argos catalogue has slide rules

A little slice of UK consumer history: The Argos Book of Dreams, scanned Argos catalogues from 1974 to present. (Via b3ta)

It seems that all (?) of these are also up at the Internet Archive. They’re a little harder to find, and some don’t have previews. A decent search for one uploader’s history is “retromash”, though “argos catalogue” finds more. The Book of Dreams site seems only to have autumn/winter catalogues, while the Archive has the spring/summer ones too.

Categories
goatee-stroking musing, or something

Taxman – a BASIC game from 1973

Back in 1973, the future definitely wasn’t equally distributed. While in Scotland we had power cuts, the looming three-day week and Miners’ Strike I, in California, the People’s Computer Company (PCC) was giving distributed computer access, teaching programming and publishing computer magazines. I don’t think we got that kind of access until (coincidentally) Miners’ Strike II a little over 10 years later.

taxman drawn image from People's Computer Company magazine (1973) , with "1 for you 19 for me" quote from The Beatles song "Taxman"
flares? platforms? centre parting? bow tie? It was 1973 after all

But the People’s Computer Company magazine archive is a sunny thing, overfilled with joyful amateur enthusiasm and thousands of lines of code fit to make Edsger Dijkstra explode. Of course it was written for the local few who had access to mainframes and terminals, but it hardly seems to come from the same world as the dark evenings in Scotland spent cursing the smug neighbours’ house with all the lights on, their diesel generator putt-putting into the night.

Lots of these games from the PCC era are forgettable now. The raw challenge of guessing a number on a text screen has paled somewhat in the face of 4K photo-realistic rendering. One game I found is still a little challenging, at least until you work out the trick of it: Taxman (or as the authors tried to rename it later, Factor Monster). Here’s a tiny sample game transcript:

Hi, I'm the taxman
Do you want the regulations?
(1=Yes, 0=No)? 0

How many numbers do you want
in the list? 6

The list is: 1  2  3  4  5  6 

You take? 5
Your total is  5 
I get  1 
My total is  1 

New list:  2  3  4  6 

You take? 6
Your total is  11 
I get  2  3 
My total is  6 

New list:  4 
I get  4 
because no factors of any number
are left.
My total is  10 

You  11  Taxman  10 
You win !!!

Again (1=yes, 0=no)?

Seems I sneaked a lucky win there, but it’s harder than it looks. The rules are simple:

  • Start with a list of consecutive numbers
  • You choose a number, but it has to have some factors in the list
  • The taxman (or the factor monster, a concept I much prefer as it doesn’t reinforce the Helmsley Doctrine) takes all the remaining factors of your number from the list
  • You get to choose a number from the list, which is now missing your previous choice and all of its factors, and repeat
  • Once the list has no multiples of any other number, the taxman/FM takes the rest
  • The winner is whoever has the largest sum.

For such a simple game (or perhaps, such a simple me) the computer wins surprisingly often. Since I find it fun to play, I thought I’d share the 1973 love as much as possible by porting to all of the BASIC dialects that I knew.

Plain text BASICtaxman.bas —runs under interpreters such as bas. Almost verbatim from the 1973 publication. May not allow you to play again on some interpreters; you might want to try my slightly rearranged 40 column version that should run on systems that don’t allow a variable to be dimensioned twice.

taxman on Amstrad CPC: starting with numbers 1-6, player has taken 4, so taxman takes 1 & 2, leaving 3, 5 and 6
taxman on Amstrad CPC: how BASIC programs look to me, yellow on blue 4 lyfe

Amstrad CPC Locomotive BASICtaxman.dsk — or as I call it, BASIC. 40 columns yellow on blue is how BASIC should look.

taxman on BBC Micro, showing games tart for 1-6. Adjacent numbers are a full column apart
taxman on BBC, Mode 7: dig the weird spacing

BBC BASICtaxman.ssd — for all the boopBeep fans out there. You can actually play this one in your browser, too. Yes, the number formatting is weird, but BBC BASIC was always its own master.

taxman: Commodore 64 showing the instructions
taxman on C64

Commodore 64taxman.prg — very very upper case for this dinosaur of a BASIC.

taxman running on Apple II: loaded from disk, started with 6 numbers
taxman running on Apple II

Apple II AppleSoft BASICTAXMAN.DSK — lots of fiddling with import tools and dialect weirdness because Apple.

taxman: end of game on ZX spectrum
taxman: end of game on ZX spectrum

ZX Spectrum (Sinclair BASIC)taxman.tap — 32 columns plus a very special dialect (no END, GOTO and GOSUB are GO TO and GO SUB, …) meant this took a while, but it was quite rewarding to get going.

taxman - BASIC program listing on ZX-81 running under sz81 emulator, Linux window borders visible
Taxman on ZX81: more SCROLLs than the Dead Sea

Sinclair ZX81 (16 K) — taxman.p — this one was a fight. The ZX81 didn’t scroll automatically, so you have to invoke SCROLL before every newline-generating PRINT or else your program will stop. For some reason this version gets unbearably slow near the end of long games, but it does complete.