Category: computers suck

  • for Paul Carter, after 15 years

    All the Colours We Have (for Paul Carter) — 2021 re-encoding

    It’s hard to believe that Paul Carter has been gone fifteen years. Like all things digital, my ten year memorial for him was a bit dusty, so I did a re-encoding that will look better on today’s devices. The info page is unchanged.

    Still miss you, buddy. It gets further but no easier.

  • Tetris for Applesoft BASIC

    a tetris game tableau, paused, with a completed line of bricks about to be removed
    a very paused game

    Paleotronic’s Tetris for Applesoft BASIC is a surprisingly decent version for something that’s written in an interpreted language. It’s not what anyone would call zippy, but it’s not so slow that you want to give up.

    Paleotronic want you to type it in, but life’s too short for that. You can play it in your browser on the Internet Archive: Tetris for Applesoft BASIC by Mark Stock

    Or download an auto-booting Apple II disk image:

    Keys:

    • , — move left
    • . — move right
    • A — rotate left
    • R — rotate right
    • Z — drop
    • P — pause
    • Q — quit

    Life’s also too short for correcting OCR errors in BASIC code. Tesseract is hilariously bad at recognizing source code, so I had to go through this several times. AppleCommander’s BASIC Tools was very handy for catching the last of the errors with its variable dump: caught the cases of the TO keyword converted to the variable T0 … and frankly, I am no fan of SmartQuotes when applied to source code, either.

    Here’s the source, straight from the interpreter with all its weird spacing:

    10  GOSUB 1000
    100 W = W +1: IF W >LV  THEN W = 0: GOSUB 350
    110 K =  PEEK(KB): IF K > = H  THEN  POKE KC,H:K = K -H: GOSUB 300
    190  GOTO 100
    200 PY = PY *A2: VLIN PY,PY +A1 AT PX: RETURN 
    225 PY = PY *A2: HLIN X1,X2 AT PY: HLIN X1,X2 AT PY +A1: RETURN 
    300  ON E(K) GOTO 30000,330,340,350,360,30100
    310  RETURN 
    330 X = X -1: GOTO 400
    340 X = X +1: GOTO 400
    350 DN = 1:Y = Y +1: GOSUB 400:DN = 0: RETURN 
    360 S = S +1: IF S/4 =  INT(S/4)  THEN S = S -4
    400  GOSUB 500
    410  GOSUB 800: IF F = 0  THEN X = XX:Y = YY:S = SS: GOSUB 420: IF DN  THEN  GOSUB 900
    420  COLOR= CF: FOR PP = 1 TO 4:PX = X +X(S,PP):PY = Y +Y(S,PP): GOSUB 200: NEXT PP:XX = X:YY = Y:SS = S:D = 0: RETURN 
    500  IF DD  THEN  RETURN 
    510  COLOR= CB: FOR PP = 1 TO 4:PX = XX +X(SS,PP):PY = YY +Y(SS,PP): GOSUB 200: NEXT PP:DD = 0: RETURN 
    800 F = 1: FOR PP = 1 TO 4:PY = Y +Y(SS,PP): ON ( FN PC(X +X(S,PP)) >0) GOTO 805: NEXT PP: RETURN 
    805 F = 0: RETURN 
    850 F = 1: RETURN 
    900 P = 10: GOSUB 30300
    905 RN = 0:Y = YM
    910 X = XL
    920 PY = Y: IF  FN PC(X) = CB  THEN 950
    930 X = X +1: IF X < = XR  THEN 920
    940 R(RN) = Y:RN = RN +1
    950 Y = Y -1: IF Y > = 0  THEN 910
    960  IF RN  THEN  GOSUB 30400
    970 Y = 0
    980 X =  INT((XR -XL)/2) +XL
    985 S =  INT( RND(1) *NS):CF = C(S):S = S *4
    990  GOSUB 800: IF F  THEN  RETURN 
    995  GOTO 31000
    1000  DIM E(127),X(27,4),Y(27,4),R(40)
    1010  TEXT : HOME : GR 
    1011  PRINT "WELCOME..."
    1014 LM = 10
    1015 XM = 10:YM = 15
    1016 XL =  INT((40 -XM)/2)
    1017 XR = XL +XM -1
    1021 A1 = 1
    1022 A2 = 2
    1030  DEF  FN PC(X) =  SCRN( X,PY *A2)
    1040 CB = 0
    1050 XX = 20:YY = 0:SS = 0
    1100 KB =  -16384
    1110 KC =  -16368
    1120 H = 128
    1129  REM KEYBOARD ACTIONS
    1130  REM QUIT
    1131 E( ASC("Q")) = 1
    1132 E( ASC("Q") -64) = 1
    1140  REM MOVE LEFT
    1141 E(8) = 2
    1142 E( ASC(",")) = 2
    1150  REM MOVE RIGHT
    1151 E(21) = 3
    1152 E( ASC(".")) = 3
    1160  REM MOVE DOWN
    1161 E(32) = 4
    1162 E( ASC("Z")) = 4
    1170  REM ROTATE
    1171 E( ASC("R")) = 5
    1172 E(13) = 5
    1173 E( ASC("A")) = 5
    1179  REM PAUSE GAME
    1180 E( ASC("P")) = 6
    1181 E( ASC("P") -64) = 6
    1185  GOSUB 2000
    1186  GOSUB 1300
    1190  PRINT "PRESS ANY KEY TO START..."
    1191  PRINT 
    1192  PRINT "PRESS Q TO QUIT."
    1193  GOTO 31020
    1299  REM DRAW THE GAME
    1300  COLOR= 4: FOR I = 0 TO 19:X1 = 0:X2 = 39:PY = I: GOSUB 225: NEXT 
    1320  COLOR= CB: FOR I = 0 TO YM:X1 = XL:X2 = XR:PY = I: GOSUB 225: NEXT 
    1350  RETURN 
    1400  DATA 1
    1401  DATA 0,0,1,0,0,1,1,1
    1402  DATA 0,0,1,0,0,1,1,1
    1403  DATA 0,0,1,0,0,1,1,1
    1404  DATA 0,0,1,0,0,1,1,1
    1410  DATA 2
    1411  DATA 0,1,1,1,2,1,3,1
    1412  DATA 1,0,1,1,1,2,1,3
    1413  DATA 0,1,1,1,2,1,3,1
    1414  DATA 1,0,1,1,1,2,1,3
    1420  DATA 12
    1421  DATA 1,1,0,1,1,0,2,1
    1422  DATA 1,1,0,1,1,0,1,2
    1423  DATA 1,1,0,1,2,1,1,2
    1424  DATA 1,1,1,0,2,1,1,2
    1430  DATA 13
    1431  DATA 1,1,0,1,2,1,0,2
    1432  DATA 1,1,1,0,1,2,2,2
    1433  DATA 1,1,0,1,2,1,2,0
    1434  DATA 1,1,1,0,1,2,0,0
    1440  DATA 9
    1441  DATA 1,1,0,1,2,1,2,2
    1442  DATA 1,1,1,0,1,2,2,0
    1443  DATA 1,1,0,1,2,1,0,0
    1444  DATA 1,1,1,0,1,2,0,2
    1450  DATA 3
    1451  DATA 1,1,1,0,0,0,2,1
    1452  DATA 1,1,1,0,0,1,0,2
    1453  DATA 1,1,1,0,0,0,2,1
    1454  DATA 1,1,1,0,0,1,0,2
    1460  DATA 6
    1461  DATA 1,1,0,1,1,0,2,0
    1462  DATA 1,1,0,1,0,0,1,2
    1463  DATA 1,1,0,1,1,0,2,0
    1464  DATA 1,1,0,1,0,0,1,2
    1990  DATA  -1
    2000 X = 0:Y = 0
    2010 NS = 0
    2020  READ C: IF C < > -1  THEN C(NS) = C: FOR J = 0 TO 3: FOR I = 1 TO 4: READ X(NS *4 +J,I): READ Y(NS *4 +J,I): NEXT I: NEXT J:NS = NS +1: GOTO 2020
    2030  RETURN 
    21210 P = 1: RETURN 
    30000  TEXT : HOME : END 
    30100  HOME 
    30110  PRINT "GAME PAUSED. PRESS P TO CONTINUE..."
    30120 P = 1
    30130 K =  PEEK(KB): IF K > = H  THEN  POKE KC,H:K = K -H: GOSUB 30200
    30140  IF P  THEN 30130
    30150  HOME 
    30160  PRINT "SCORE ";SC; TAB( 21);"LEVEL ";LM -LV +1
    30170  RETURN 
    30200  ON E(K) GOTO 30000,30210,30210,30210,30210,30220
    30210  RETURN 
    30220 P = 0
    30230  RETURN 
    30300 SC = SC +P
    30310  VTAB 21: HTAB 7
    30320  PRINT SC;
    30330  RETURN 
    30400 RN = RN -1
    30410  FOR C = 0 TO 32
    30415  COLOR= C
    30420  FOR I = 0 TO RN:X1 = XL:X2 = XR:PY = R(I): GOSUB 225: NEXT I
    30430  FOR I = 0 TO 2: NEXT I
    30440  NEXT C
    30450  FOR I = 0 TO RN
    30460 Y = R(I) +I
    30470 YP = Y -1: FOR X = XL TO XR:PY = YP: COLOR=  FN PC(X):PX = X:PY = Y: GOSUB 200: NEXT X:Y = Y -1: IF Y >0  THEN 30470
    30480 P = 100: GOSUB 30300
    30490  NEXT I
    30495  RETURN 
    31000  VTAB 22: PRINT 
    31010  PRINT "              GAME OVER"
    31020 P = 1
    31030 K =  PEEK(KB): IF K > = H  THEN  POKE KC,H:K = K -H: GOSUB 31200
    31040  IF P  THEN 31030
    31050 D = 1
    31060 SC = 0:LV = LM
    31070  GOSUB 30150
    31080  GOSUB 1300
    31090  GOTO 905
    31200  ON E(K) GOTO 30000
    31210 P = 0: RETURN 
    32000  REM END OF LISTING
    

    If only AppleSoft had a RENUM command, the code might not look so messy

  • digging the BlueSCSI

    I like old (as in 68K old) Apple Macintoshes, but I don’t like their hard drives. Apple used SCSI drives, which were super-cool at the time (multiple drives on one bus! extra devices like SCSI scanners, too!) but now seem an absolute pain. There may be a lot to complain about with USB storage, but compared to SCSI, it just works.

    While the 40 MB Quantum SCSI drive in my Mac Classic II still works, it gets really full really fast and that old spinning rust won’t spin forever. One of the newer ways to replace a SCSI drive is the BlueSCSI, an open-everything design based on a cheap Blue Pill micro-controller board, a Micro-SD card slot and some passive components. The whole kit is very affordable, and a local maker sells them, so it was worth a try.

    I’ve had nothing but miserable failure with Blue Pill boards, and very quickly moved on to STM32F4 board that actually work. It didn’t fill me with much hope that the board I got with my kit looked like this, end on:

    end-on view of a blue micro controller PCB which has - mystifyingly - two micro-USB connectors, one on either face of the PCB
    There are two USB connectors on this board.
    There are two of ____.

    Despite that oddness, it all soldered up fine (the surface-mount card slot was a little fiddly) and it fits inside the Classic II’s cavernously empty shell wherever I choose to stick it down.

    I thought I knew what I was doing in making filesystem images (hence my recent nonsense anent HFS utilities), but clearly I was wrong. The 2 GB images I made in Basilisk II weren’t recognized at all. For now, I’m booting from one of the RaSCSI canned boot images plus a couple of the blank formatted drive images to put my own custom system on.

    I’m trying to get as much as possible set up on the Micro-SD card while I can still access it, as the Classic II’s case is not something you can pop open on a whim. It needs a special long-thin Torx T15 driver to even get the case partially open, then you have to ease/fight the case the rest of the way. Aside from the faint ponk of dodgy analogue board caps (I’ll fix ’em one day, I promise!), you have to remember to tiptoe around the life-ending voltages lurking at the back of the CRT when you’re working there. Retrotechnology: it smells bad and it can kill you!

    A feature I really appreciate with the BlueSCSI is that it dumps a status log on the card every time it boots. It took me a while to get the hang of naming images correctly, but I’d have been absolutely lost without logs with this level of detail:

    BlueSCSI <-> SD - https://github.com/erichelgeson/BlueSCSI
     VERSION: 1.0-20210410
     DEBUG:0 SCSI_SELECT:0 SDFAT_FILE_TYPE:3
     SdFat version: 2.0.6
     SdFat Max FileName Length: 32
     Initialized SD Card - lets go!
     Not an image: LOG.txt
     Imagefile: HD10_512 753.hda / 41943040bytes / 40960KiB / 40MiB
     Imagefile: HD20_512 BS.hda / 41943040bytes / 40960KiB / 40MiB
     Imagefile: HD40 MacHD-1000MB.hda / 1048576000bytes / 1024000KiB / 1000MiB
     Imagefile: HD30_512 MacHD-500MB.hda / 524288000bytes / 512000KiB / 500MiB
     ID:LUN0:LUN1:
      0:----:----:
      1: 512:----:
      2: 512:----:
      3: 512:----:
      4: 512:----:
      5:----:----:
      6:----:----:
     Finished initialization of SCSI Devices - Entering main loop.

    So I’ve got four SCSI drives pretending to live on this card, and even if one of them’s not quite named correctly (HD40 MacHD-1000MB.hda should be called HD40_512 MacHD-1000MB.hda), it’s been found okay.

  • Powering a Cambridge Z88 with a MT3068 module

    keyboard of a small computer with a USB cabled gadget on top of it
    my Z88 – USB power cable, with slightly stoory Z88 underneath

    I got a Cambridge Computer Z88 again, after perhaps 30 years of not having one. They are very nice little portable computers, remarkable for packing a multi-tasking OS into an 8-bit machine that runs for weeks on 4x AA batteries.

    But the Z88 is a Clive Sinclair joint, so there’s going to be at least one deeply weird and/or ill-advised detail about it. For me, it’s the power adapter. The original had a 6 V centre-positive barrel connector, but in true Sinclair fashion, that wasn’t quite ideal.

    The DC power comes through a diode, which drops it down to ~5.2 V. The batteries supply up to 6 V and there’s no protection circuit, so the DC adapter won’t do anything until the internal batteries discharge down to that level. This is far from ideal.

    The clever way of dealing with this was Rakewell’s Z88 PSU. This was a variable voltage PSU set to deliver 7 V, so that even when dropped through the power diode, it wouldn’t deplete the batteries.

    Unfortunately, Rakewell stopped selling these a while ago. But I remembered I had a some MT3608 (datasheet) boards bought at a hamfest some years back. MT3608s are boost DC-DC converters, so they’ll convert from a lower DC voltage to a higher one, typically at least +2 V higher than the input. With a hacked-up USB cable, a small length of DC cable with a connector and a 3d printed case, I built one that works quite well, at least in the short time I’ve tested it.

    • I used the MT3608 step up DC boost box design from Thingiverse
    • Newer MT3608 boards sometimes feature a micro-USB connector, which is much more convenient than hacking up a USB cable
    • It’s really important to know how to adjust a MT3608 DC-DC Booster before connecting the final voltage, as it’s very easy to burn the board out
    • You can’t use a computer USB port to power an MT3608, as the cable’s not smart enough to negotiate a decent amount of current. A good phone charger should be enough.
    • I set the voltage output to be 7.05 V, with no load

  • niche 68K Mac emulation needs

    black and white screenshot of Macintosh System 7 desktop showing Disk First Aid icon
    It amazes me how you manage to live in anything that small“: actual size Mac Classic II screenshot

    So I’m refurbishing the Mac Classic II I got in 2016 now that I’ve found that BlueSCSI is a fairly cheap way of providing large replacement storage. The 40 MB (yes, 40 MB) drive from 1992 can finally be replaced.

    Hit a snag, though: DiskCopy won’t copy a disk with errors, which this drive seemed to have. DiskCopy also won’t repair the boot disk, so you have to find a bootable floppy or some other way to work around this limitation. The only readily available bootable disk image I could find was the System 7.1 Disk Tools floppy — which reported no errors on my drive! Later versions of Disk First Aid would fix it, but weren’t provided on a bootable image.

    Here’s the 7.1 Disk Tools floppy with all of the apps replaced by Disk First Aid 7.2. Both of these were found on the Internet Archive’s download.info.apple.com mirror and combined:

    Disk duly repaired, DiskCopy was happy, backup complete. Eventually: the Classic II is not a fast machine at all.

    I love the old Mac icons, how they packed so much into so few pixels:

    enlarged icon: stylized ambulance with flashing light and driver, side is a floppy disk, there are motion lines at back and the whole icon is slanted to indicate speed/rush
    Disk First Aid icon

    It’s a stylized ambulance with flashing light and driver, the side is a floppy disk, it’s got motion lines and the whole icon is slanted to indicate speed/rush. Nice!


    Vaguely related: most old Mac software is stored as Stuffit! archives. These don’t really work too well on other systems, as they use a compression scheme all their own and are specialized to handle the forked filesystem that old Macs use. Most emulators won’t know what to do with them unless you jump through hoops in the transfer stage.

    Fortunately, the ancient Linux package hfsutils knows the ways of these hoops, if only you tell it when to jump. The script below takes a Stuffit! file as its sole argument, and creates a slightly larger HFS image containing the archive, with all the attributes set for Stuffit! Expander or what-have-you to unpack it.

    #!/bin/bash
    # sitdsk - make an HFS image containing a properly typed sit file
    # requires hfsutils
    # scruss, 2021-07
    
    if
        [ $# -ne 1 ]
    then
        echo Usage: $0 file.sit
        exit 1
    fi
    
    base="${1%.sit}"
    # hformat won't touch anything smaller than 800 KB,
    #  so min image size will be 800 * 1.25 = 1000 KB
    size=$(du -k "$1" | awk '{print ($1 >= 800)?$1:800}')
    dsk="${base}.dsk"
    dd status=none of="$dsk" if=/dev/zero bs=1K count=$((size * 125 / 100))
    hformat -l "$base" "$dsk"
    # note that hformat does an implicit 'hmount'
    hcopy "$1" ':'
    # and hcopy silently changes underscores to spaces
    hattrib -t 'SIT!' -c 'SITx' ":$(echo ${1} | tr '_' ' ')"
    hls -l
    humount
    
    

    What this does:

    1. creates a blank image file roughly 25% larger than the archive (or 1000 KB, whichever is the larger) using dd;
    2. ‘formats’ the image file with an HFS filesystem using hformat;
    3. copies the archive to the image using hcopy;
    4. attempts to set the file type and creator using hattrib;
    5. lists the archive contents using hls;
    6. disconnects/unmounts the image from the hfsutils system using humount.

    Notice I said it “attempts” to set the file type. hfsutils does some file renaming on the fly, and all I know about is that it changes underscores to spaces. So if there are other changes made by hfsutils, this step may fail. The package also gets really confused by images smaller than 800 KB, so small archives are rounded up in size to keep hformat happy.

  • Colourful Alligator Clips

    Everything looks like buds when you have a bud vase.

  • CardKB mini keyboard with MicroPython

    small computer screen with text
*** APPALLING TYPEWRITER ***
** Type stuff, Esc to end **

then further down: "hello I am smol keeb"
    it really is the size of a credit card
    (running with a SeeedStudio Wio Terminal)

    I got one of these CardKB Mini Keyboards to see if I could use it for small interactives with MicroPython devices. It’s very small, and objectively not great as a mass data entry tool. “Better than a Pocket C.H.I.P. keyboard” is how I’d describe the feel. It’s also pretty reliable.

    It’s got an I²C Grove connector, and its brains are an ATMega chip just like an Arduino. It’s strictly an ASCII keyboard: that is, it sends the 8-bit ASCII code of the key combination you pressed. It doesn’t send scan codes like a PC keyboard. The driver source is in the CardKB m5-docs, so if you really felt ambitious you could write a scan code-like firmware for yourself.

    The device appears at I²C peripheral address 95, and returns a single byte when polled. That byte’s either 0 if no key was pressed, or the character code of what was pressed. The Esc key returns chr(27), and Enter returns CR. If you poll the keyboard too fast it seems to lose the plot a little, so a tiny delay seems to help

    Here’s a small demo for MicroPython that acts as the world’s worst typewriter:

    # M5Stack CardKB tiny keyboard - scruss, 2021-06
    # MicroPython - Raspberry Pi Pico
    
    from machine import Pin, I2C
    from time import sleep_ms
    
    i2c = I2C(1, scl=Pin(7), sda=Pin(6))
    cardkb = i2c.scan()[0]  # should return 95
    if cardkb != 95:
        print("!!! Check I2C config: " + str(i2c))
        print("!!! CardKB not found. I2C device", cardkb,
              "found instead.")
        exit(1)
    
    ESC = chr(27)
    NUL = '\x00'
    CR = "\r"
    LF = "\n"
    c = ''
    
    print("*** APPALLING TYPEWRITER ***")
    print("** Type stuff, Esc to end **")
    
    while (c != ESC):
        # returns NUL char if no character read
        c = i2c.readfrom(cardkb, 1).decode()
        if c == CR:
            # convert CR return key to LF
            c = LF
        if c != NUL or c != ESC:
            print(c, end='')
        sleep_ms(5)
    

    And here’s the CircuitPython version. It has annoying tiny differences. It won’t let me use the I²C Grove connector on the Wio Terminal for some reason, but it does work much the same:

    # M5Stack CardKB tiny keyboard - scruss, 2021-06
    # CircuitPython - SeeedStudio Wio Terminal
    # NB: can't use Grove connector under CPY because CPY
    
    import time
    import board
    import busio
    
    i2c = busio.I2C(board.SCL, board.SDA)
    
    while not i2c.try_lock():
        pass
    
    cardkb = i2c.scan()[0]  # should return 95
    if cardkb != 95:
        print("!!! Check I2C config: " + str(i2c))
        print("!!! CardKB not found. I2C device", cardkb,
              "found instead.")
        exit(1)
    
    ESC = chr(27)
    NUL = '\x00'
    CR = "\r"
    LF = "\n"
    c = ''
    b = bytearray(1)
    
    # can't really clear screen, so this will do
    for i in range(12):
        print()
    print("*** APPALLING TYPEWRITER ***")
    print("** Type stuff, Esc to end **")
    for i in range(8):
        print()
    
    while (c != ESC):
        # returns NUL char if no character read
        i2c.readfrom_into(cardkb, b)
        c = b.decode()
        if c == CR:
            # convert CR return key to LF
            c = LF
        if c != NUL or c != ESC:
            print(c, end='')
        time.sleep(0.005)
    
    # be nice, clean up
    i2c.unlock()
    

  • An hour of Pink Noise

    cover made by netpbm, of course
    an hour of soothing 2-channel noise

    Direct download: 01-pink_noise.mp3

    There are a million variations on the simple “use sox to play masking pink noise“, such as:

    play -n synth pinknoise gain -3

    This will play synthesized pink noise until you hit Ctrl-C.

    But it you want two independent noise channels rather than mono, that’s a little more complex. It’s probably easier to download/play the MP3 file above than show you the command line.

    Note that MP3s really aren’t designed to encode such random data, and it’s likely that your player will cause the audio to clip in a couple of places. I’m not quite sure why it does this, but it does it repeatably.

    If you want to create this for yourself (and create a bonus lossless FLAC, which was far too large to upload here), here’s what I did to make this:

    #!/bin/bash
    
    duration='60:00'
    fade='1'
    outfile='pinknoise.wav'
    
    # make the track
    sox --combine merge "|sox --norm=-3 -c 1 -b 16 -r 44100 -n -p synth $duration pinknoise" "|sox --norm=-3 -c 1 -b 16 -r 44100 -n -p synth $duration pinknoise" -c 2 -b 16 -r 44100 $outfile fade $fade fade 0 $duration $fade gain -n -3
    
    # make the cover
    # 1 - text - 500 x 500 px
    pnmcat -white -tb <(pbmmake -white 500 114) <(pbmtextps -font HelveticaBold -fontsize 64 -resolution 180 "PINK" | pnmcrop) <(pbmmake -white 32 32) <(pbmtextps -font HelveticaBold -fontsize 64 -resolution 180 "NOISE" | pnmcrop) <(pbmmake -white 500 114) > cover-text.pbm
    # 2 - make the noise bg
    pgmnoise 500 500 > cover-noise.pgm
    # 3 - make the magenta text
    ppmchange black magenta cover-text.pbm > cover-text-magenta.ppm
    # 4 - overlay with transparency
    pnmcomp -alpha=<(pnminvert cover-text.pbm | pbmtopgm 35 35 ) cover-text-magenta.ppm cover-noise.pgm | cjpeg -qual 50 -opt -baseline -dct float > cover.jpg
    # delete the temporary image files, leaving cover.jpg
    rm -f cover-text.pbm cover-noise.pgm cover-text-magenta.ppm
    
    # make the mp3
    lame -V 2 --noreplaygain -m s --tt 'Pink Noise' --ta 'Pink Noise' --tl 'Pink Noise' --ty $(date +%Y) --tc "scruss, 2021-05" --tn 1/1 --tg Ambient --ti cover.jpg "$outfile" 01-pink_noise.mp3
    
    # make the flac (and delete wav file)
    flac --best --output-name=01-pink_noise.flac --delete-input-file --picture=cover.jpg --tag="TITLE=Pink Noise" --tag="ARTIST=Pink Noise" --tag="ALBUM=Pink Noise" --tag="DATE=$(date +%Y)" --tag="COMMENT=scruss, 2021-05" --tag="GENRE=Ambient" --tag="TRACKNUMBER=1" --tag="TRACKTOTAL=1" "$outfile"
    
    

    You’ll likely need these packages installed:

    sudo apt install sox libsox-fmt-all ghostscript gsfonts-x11 netpbm lame flac libjpeg-progs
  • The cheapest Micro SD card interface in the world

    a micro-sd adapter with 7 0.1"-pitch header pins  soldered onto its contacts
    micro-SD adapter + pins + solder = working SD interface

    It’s only a serial SPI interface, but you can’t beat the price. It should only be used with 3.3 V micro-controllers like the Raspberry Pi Pico, since micro-SD cards don’t like 5 V directly at all.

    You might want to pre-tin the pins and apply some extra flux on both surfaces, because these pads are thin and you don’t want to melt them. I used my standard SnAgCu lead-free solder without trouble, though.

    label sticker image for 7 pins, from left to right DO, GND, CLK, 3V3, GND, DI, CS
    got a label maker? This label’s the same length as an SD card is wide, as shown above.
    Made entirely with netpbm

    You only need to use one of the Ground connections for the card to work.

  • cp2up.sh — fits the important part of Canada Post print labels two per sheet

    blurred (for privacy) 2-up landscape page of Canada Post Tracked Package (to USA) shipping labels made by this script
    no you will not read my 2-up shipping labels

    If you need to ship things, you’re probably not too keen on queuing at the post office right now. Canada Post’s Ship Online service is pretty handy if you have a printer. The PDFs it produces are okay to print on plain paper, but if you’re using full-sheet labels like Avery 5165 you’re going to waste half a sheet of expensive labels.

    If you’ve got two parcels to mail, this shell script will extract the right side of each page and create a single 2-up PDF with both your labels on the same page. You will need:

    On my Ubuntu system, you can get good-enough¹ versions by doing this:

    sudo apt install poppler-utils netpbm img2pdf

    The code:

    #!/bin/bash
    # cp2up.sh - fits the important part of Canada Post print labels 2 per sheet
    # scruss, 2021-05 - CC-BY-SA
    # hard-coded input name (document.pdf)
    # hard-coded output name (labels-2up.pdf)
    # accepts exactly two labels (sorry)
    
    dpi=600
    width_in=11
    height_in=8.5
    # png intermediate format uses pixels per metre
    dpm=$(echo "scale=3; $dpi * 1000 / 25.4" | bc)
    # calculated pixel sizes, truncated to integer
    half_width_px=$(echo "$width_in * $dpi / 2" | bc | sed 's/\..*$//')
    height_px=$(echo "$height_in * $dpi" | bc | sed 's/\..*$//')
    
    pdftoppm -mono -r "$dpi" -x "$half_width_px" -y 0 \
    	 -W  "$half_width_px" -H "$height_px" document.pdf labels
    pnmcat -lr labels-1.pbm labels-2.pbm |\
        pnmtopng -compression 9 -phys "$dpm" "$dpm" 1 > labels.png \
        && rm labels-1.pbm labels-2.pbm
    # fix PDF time stamps
    now=$(date --utc --iso-8601=seconds)
    img2pdf -o labels-2up.pdf --creationdate "$now" --moddate "$now" \
    	--pagesize "Letter^T" labels.png \
        && rm labels.png 
    
    # saved from:
    # history | tail | awk '{$1=""; print}' |\ 
    #           perl -pwle 'chomp;s/^\s+//;' > cp2up.sh
    

    It’s got a few hard-coded assumptions:

    • input name (document.pdf);
    • output name (labels-2up.pdf);
    • accepts exactly two labels (sorry).

    Clever people could write code to work around these. Really clever people could modify this to feed a dedicated label printer.

    Yes, I could probably have done all this with one ImageMagick command. When ImageMagick’s command line syntax begins to make sense, however, it’s probably time to retire to that remote mountain cabin and write that million-word thesis on a manual typewriter. Also, ImageMagick’s PDF generation is best described as pish.

    One of the issues that this script avoids is aliasing in the bar-codes. For reasons known only to the anonymous PDF rendering library used by Canada Post, their shipping bar-codes are stored as smallish (780 × 54 px) bitmaps that are scaled up to a 59 × 19 mm print size. Most PDF viewers (and Adobe Viewer is one of these) will anti-alias scaled images, making them slightly soft. If you’re really unlucky, your printer driver will output these as fuzzy lines that no bar-code scanner could ever read. Rendering them to high resolution mono images may still render the edges a little roughly, but they’ll be crisply rough, and scanners don’t seem to mind that.

    split image of simulated printed barcode: top image is five indistinct black-grey bars merging into a white background, bottom image is the same vertical lines, rendered crisply but showing some slightly rough edges
    fuzzy vs crisply rough: scaled image (top) vs direct-rendered (bottom), at simulated 600 dpi laser print resolution

    ¹: Debian/Ubuntu’s netpbm package is roughly 20 years out of date for reasons that only a very few nerds care about, and the much better package is blocked by Debian’s baroque and gatekeepery packaging protocol. I usually build it from source for those times I need the new features.

  • Applied Futility: Re-creating RAND’s ‘A Million Random Digits’

    Sometimes, one must obey the inscrutable exhortations of one’s soul and travel deep into the inexplicable. The planet Why? has been left far behind, the chatter of its querulous denizens nothing more than a faint wisp of static. Where I’m going, pure patternlessness is all there is.

    Page 53 from “A Million Random Digits …”, showing the five-digit sequential line number at left, followed by ten columns of five random digits each
    A page from “A Million Random Digits …”, showing the five-digit sequential line number at left, followed by ten columns of five random digits each

    In 1955, military-industrial complex stalwarts RAND Corporation published a huge book of just pages and pages of … digits. The digits were deliberately as random as possible, and were intended to help the fledgling practice of data science carry out truly random simulations. The book was called “A Million Random Digits with 100,000 Normal Deviates”. It was also made available on punched cards, with 50 digits to a card adding up to ten boxes of 2000 cards. A single box of punched cards was about 370 × 200 × 95 mm and weighed roughly 5 kg, so these digits had heft.

    I thought it might be fun (or perhaps fun?: pronounced with a rising, questioning tone to stress the might-ness of any enjoyment arising) to dig into how RAND carried out this work, create some electronics to produce a similar random stream, and see how my random digits compare for randomness with RAND’s. For a final trick, I might even typeset the whole giant table into a book that no-one wants.

    As I research and progress with this project, I’ll add in links here

    I must stress: there is no reason for me to do this. A $5 micro-controller board can generate tens to hundreds of thousands of truly random digits per second. Any results I produce will have no use beyond my own amusement.


    A Million Random Digits with 100,000 Normal Deviates is © Copyright 2001 RAND. Apart from a couple of page images and quotations from supporting material, none of that work is reproduced here. RAND does not support or endorse my futile efforts in any way.

  • Delicate tracings from a terrible PRNG

    The two orbits of 16-bit RANDU, plotted as X-Y coordinates

    I’d previously mentioned RANDU — IBM’s standard scientific PRNG in the 1970s that was somewhat lacking, to say the least — some time ago, but I found a new wrinkle.

    For their 1130 small computer system, IBM published the Scientific Subroutine Package [PDF], which included a cut-down version of RANDU for this 16-bit machine. The code, on page 64 of the manual, doesn’t inspire confidence:

    c     RANDU - from IBM Scientific Subroutine 
    c     Package Programmer's Manual
    c     1130-CM-02X - 5th ed, June 1970
    c     http://media.ibm1130.org/1130-106-ocr.pdf
    
          SUBROUTINE RANDU(IX, IY, YFL)
          IY = IX * 899
          IF (IY) 5, 6, 6
     5    IY = IY + 32767 + 1
     6    YFL = IY
          YFL = YFL / 32727.
          RETURN
          END
    

    (If you’re not hip to ye olde Fortran lingo, that bizarre-looking IF statement means IF (IY < 0) GO TO 5; IF (IY == 0) GO TO 6; IF (IY > 0) GO TO 6)

    It accepts an odd number < 32768 as a seed, and always outputs an odd number. Yes, it basically multiplies by 899, and changes the sign if it rolls over. Umm

    There are two possible orbits for this PRNG, each 8192 entries long:

    1. Starting seed 1 (899, 21769, 7835, …, 18745, 9003, 1, 899)
    2. Starting seed 5 (4495, 10541, 6407, …, 28189, 12247, 5, 4495).

    The plot above is the first series as X and the second as Y. I used INTEGER*2 types to simulate the 1130’s narrow integers.

  • Niche Knowledge: Z80 parallel port SD card on Zeta2

    green circuit board with secure digital card slot on left and 40-pin parallel interface connectors on the right
    Mini PPISD board: a slow SD card mass-storage system for 8-bit computers

    Almost no-one will need this knowledge, but I might need to remember it. In order to add Mini PPISD support to a RomWBW 3.01-supported system, you need to create a file called something like Source/HBIOS/Config/ZETA2_ppisd.asm (for yes, I’m using a Zeta SBC V2) containing:

    #include "cfg_zeta2.asm"
    UARTCFG		.SET	UARTCFG | SER_RTS
    CRTACT		.SET	TRUE		
    PPIDEENABLE	.SET	FALSE		
    SDENABLE	.SET	TRUE		
    PPPENABLE	.SET	FALSE		
    PPISD		.EQU	TRUE

    Running make from the top-level directory should create a ROM image called Binary/ZETA2_ppisd.rom for you to write to flash. Since my floppy drive isn’t feeling too happy, I had to resort to buying a TL866II Plus programmer to write the chip.

    And it worked!

     RomWBW HBIOS v3.0.1, 2021-03-12
    
     ZETA V2 Z80 @ 8.000MHz
     0 MEM W/S, 1 I/O W/S, INT MODE 2
     512KB ROM, 512KB RAM
    
     CTC: MODE=Z2 IO=0x20
     UART0: IO=0x68 16550A MODE=38400,8,N,1
     DSRTC: MODE=STD IO=0x70 Sun 2021-03-14 17:47:13 CHARGE=OFF
     MD: UNITS=2 ROMDISK=384KB RAMDISK=384KB
     FD: IO=0x30 UNITS=2
     SD: MODE=PPI IO=0x60 DEVICES=1
     SD0: SDSC NAME=SD    BLOCKS=0x003C7800 SIZE=1935MB
    
     Unit        Device      Type              Capacity/Mode
     ----------  ----------  ----------------  --------------------
     Char 0      UART0:      RS-232            38400,8,N,1
     Disk 0      MD1:        RAM Disk          384KB,LBA
     Disk 1      MD0:        ROM Disk          384KB,LBA
     Disk 2      FD0:        Floppy Disk       3.5",DS/HD,CHS
     Disk 3      FD1:        Floppy Disk       3.5",DS/HD,CHS
     Disk 4      SD0:        SD Card           1935MB,LBA
    
     ZETA V2 Boot Loader
    
     ROM: (M)onitor (C)P/M (Z)-System (F)orth (B)ASIC (T)-BASIC (P)LAY (U)SER ROM  
     Disk: (0)MD1 (1)MD0 (2)FD0 (3)FD1 (4)SD0 
    
     Boot Selection? 

    I was pleasantly surprised how easy it is to use a TL866 programmer under Linux. minipro does all the work, though. To write and verify the whole 512K Flash ROM, it’s:

    minipro -p SST39SF040 -w ZETA2_ppisd.rom

    The programmer supports over 16000 devices, of which around 10000 are variants (form factor, programming voltage, speed, OTP, etc). It’ll also verify over 100 different 74-series logic chips. It’s not a super cheap device (mine was a little over $80, from Simcoe Diy) but it does a lot for that price.

    Next stop: try rebuilding BBC BASIC with RomWBW’s timer support included ..

  • Seeeduino XIAO simple USB volume control with CircuitPython

    round computer device with USB cable exiting at left. Small microcontroller at centreshowing wiring to LED ring and rotary encoder
    Slightly blurry image of the underside of the device, showing the Seeeduino XIAO and the glow from the NeoPixel ring. And yes, the XIAO is really that small

    Tod Kurt’s QTPy-knob: Simple USB knob w/ CircuitPython is a fairly simple USB input project that relies on the pin spacing of an Adafruit QT Py development board being the same as that on a Bourns Rotary Encoder. If you want to get fancy (and who wouldn’t?) you can add a NeoPixel Ring to get an RGB glow.

    The QT Py is based on the Seeeduino XIAO, which is a slightly simpler device than the Adafruit derivative. It still runs CircuitPython, though, and is about the least expensive way of doing so. The XIAO is drop-in replacement for the Qt Py in this project, and it works really well! Everything you need for the project is described here: todbot/qtpy-knob: QT Py Media Knob using rotary encoder & neopixel ring

    I found a couple of tiny glitches in the 3d printed parts, though:

    1. The diffuser ring for the LED ring is too thick for the encoder lock nut to fasten. It’s 2 mm thick, and there’s exactly 2 mm of thread left on the encoder.
    2. The D-shaft cutout in the top is too deep to allow the encoder shaft switch to trigger.

    I bodged these by putting an indent in the middle of the diffuser, and filling the top D-shaft cutout with just enough Blu Tack.

    Tod’s got a bunch of other projects for the Qt Py that I’m sure would work well with the XIAO: QT Py Tricks. And yes, there’s an “Output Farty Noises to DAC” one that, regrettably, does just that.

    Maybe I’ll add some mass to the dial to make it scroll more smoothly like those buttery shuttle dials from old video editing consoles. The base could use a bit more weight to stop it skiting about the desk, so maybe I’ll use Vik’s trick of embedding BB gun shot into hot glue. For now, I’ve put some rubber feet on it, and it mostly stays put.


    Hey! Unlike my last Seeed Studio device post, I paid for all the bits mentioned here.

  • Nyan Cat, except it gets faster — RTTTL on the Raspberry Pi Pico

    Raspberry Pi Pico with small piezo speaker connected to pins 23 (ground) and 26 (GPIO 20)
    piezo between pins 26 and 23. Sounds a little like this

    It was inevitable:

    1. A Raspberry Pi Pico; plus
    2. a tiny piezo PC beeper; plus
    3. MicroPython; plus
    4. dhylands / upy-rtttl; plus
    5. nyancat.rtttl; plus
    6. my unfailing sense of knowing when to stop, then ignoring it

    brings you this wonderful creation, which plays the Nyan Cat theme forever, except it gets 20% faster each time. This is weapons-grade annoying (thank’ee kindly), so I’m not going to include a recording here. If you must hear an approximation, paste the RTTTL into Play RTTTL Online and enjoy.

    # Raspberry Pi Pico RTTTL example
    # nyan cat, but it gets faster
    # scruss - 2021-02: sorry, not sorry ...
    
    # Uses rtttl.py from
    #  github.com/dhylands/upy-rtttl
    # Nyan Cat RTTTL from
    #  github.com/KohaSuomi/emb-rtttl/blob/master/rtttl/nyancat.rtttl
    
    from rtttl import RTTTL
    from time import sleep_ms
    from machine import Pin, PWM
    
    b = 90    # bpm variable
    
    # pin 26 - GP20; just the right distance from GND at pin 23
    #  to use one of those PC beepers with the 4-pin headers
    pwm = PWM(Pin(20))
    led = Pin('LED', Pin.OUT)
    
    
    def play_tone(freq, msec):
        # play RTTL notes, also flash onboard LED
        print('freq = {:6.1f} msec = {:6.1f}'.format(freq, msec))
        if freq > 0:
            pwm.freq(int(freq))       # Set frequency
            pwm.duty_u16(32767)       # 50% duty cycle
        led.on()
        sleep_ms(int(0.9 * msec))     # Play for a number of msec
        pwm.duty_u16(0)               # Stop playing for gap between notes
        led.off()
        sleep_ms(int(0.1 * msec))     # Pause for a number of msec
    
    
    while True:
        nyan = 'nyancat:d=4,o=5,b=' + str(b) + ':16d#6,16e6,8f#6,8b6,16d#6,16e6,16f#6,16b6,16c#7,16d#7,16c#7,16a#6,8b6,8f#6,16d#6,16e6,8f#6,8b6,16c#7,16a#6,16b6,16c#7,16e7,16d#7,16e7,16c#7,8f#6,8g#6,16d#6,16d#6,16p,16b,16d6,16c#6,16b,16p,8b,8c#6,8d6,16d6,16c#6,16b,16c#6,16d#6,16f#6,16g#6,16d#6,16f#6,16c#6,16d#6,16b,16c#6,16b,8d#6,8f#6,16g#6,16d#6,16f#6,16c#6,16d#6,16b,16d6,16d#6,16d6,16c#6,16b,16c#6,8d6,16b,16c#6,16d#6,16f#6,16c#6,16d#6,16c#6,16b,8c#6,8b,8c#6,8f#6,8g#6,16d#6,16d#6,16p,16b,16d6,16c#6,16b,16p,8b,8c#6,8d6,16d6,16c#6,16b,16c#6,16d#6,16f#6,16g#6,16d#6,16f#6,16c#6,16d#6,16b,16c#6,16b,8d#6,8f#6,16g#6,16d#6,16f#6,16c#6,16d#6,16b,16d6,16d#6,16d6,16c#6,16b,16c#6,8d6,16b,16c#6,16d#6,16f#6,16c#6,16d#6,16c#6,16b,8c#6,8b,8c#6,8b,16f#,16g#,8b,16f#,16g#,16b,16c#6,16d#6,16b,16e6,16d#6,16e6,16f#6,8b,8b,16f#,16g#,16b,16f#,16e6,16d#6,16c#6,16b,16f#,16d#,16e,16f#,8b,16f#,16g#,8b,16f#,16g#,16b,16b,16c#6,16d#6,16b,16f#,16g#,16f#,8b,16b,16a#,16b,16f#,16g#,16b,16e6,16d#6,16e6,16f#6,8b,8a#,8b,16f#,16g#,8b,16f#,16g#,16b,16c#6,16d#6,16b,16e6,16d#6,16e6,16f#6,8b,8b,16f#,16g#,16b,16f#,16e6,16d#6,16c#6,16b,16f#,16d#,16e,16f#,8b,16f#,16g#,8b,16f#,16g#,16b,16b,16c#6,16d#6,16b,16f#,16g#,16f#,8b,16b,16a#,16b,16f#,16g#,16b,16e6,16d#6,16e6,16f#6,8b,8c#6'
        tune = RTTTL(nyan)
        print('bpm: ', b)
        for freq, msec in tune.notes():
            play_tone(freq, msec)
        b = int(b * 1.2)
    
  • Visualizing PWM

    my desk is not usually this tidy

    I built a DS0150 — successfully, on my second try — and wanted to measure something. My demo MicroPython program from MicroPython on the terrible old ESP8266-12 Development Board has been running since May 2020, and the RGB LED’s red channel is conveniently broken out to a header. Since the DSO150 has precisely one channel, it let me see the how the PWM duty cycle affects the voltage (and brightness) of the LED.

    I can’t think of oscilloscopes without being reminded of a scene from one of my favourite sci-fi books, The Reproductive System by John Sladek. Cal, the hapless new hire in the Wompler toy factory-turned-military research lab, is showing the boss some equipment while making up more and more extravagant names for them for the clueless owner:

    At each exhibit, Grandison [Wompler] would pause while Cal named the piece of equipment. Then he would repeat the name softly, with a kind of wonder, nod sagely, and move on. Cal was strongly reminded of the way some people look at modern art exhibitions, where the labels become more important to them than the objects. He found himself making up elaborate names.
    “And this, you’ll note, is the Mondriaan Modular Mnemonicon.”
    “—onicon, yes.”
    “And the Empyrean diffractosphere.”
    “—sphere. Mn. I see.”
    Nothing surprised Grandison, for he was looking at nothing. Cal became wilder. Pointing to Hita’s desk, he said, “The chiarascuro thermocouple.”
    “Couple? Looks like only one, to me. Interesting, though.”
    A briar pipe became a “zygotic pipette,” the glass ashtray a “Piltdown retort,” and the lamp a “phase-conditioned Aeolian.” Paperclips became “nuances.”
    “Nuances, I see. Very fine. What’s that thing, now?”
    He pointed to an oscilloscope. Cal took a deep breath.
    “Its full name,” he said, “is the Praetorian eschatalogical morphomorphic tangram, Endymion-type, but we usually just call it a ramification.”
    The old man fixed him with a stern black eye. “Are you trying to be funny or something? I mean, I may not be a smart-aleck scientist, but I sure as hell know a television when I see one.”
    Cal assured him it was not a television, and proved it by switching it on. “See,” he said, pointing to a pattern of square waves, “there are the little anapests.”

    — The Reproductive system, by John Sladek (text copypasta from Grey Goo in the 1960s)

    So we were displaying roughly 500 anapests/s there. Not bad, not bad at all …