Jeff Epler's blog

30 July 2026, 14:39 UTC

More junkdrawer features


I've continued enhancing the static site generator for my junkdrawer (github gist / pastebin replacement). The source code for the static site generator lives right in the same repo.

One thing I missed was some kind of ability to preview PDFs that I have uploaded to the junkdrawer, such as this m6800 reference card that disappeared earlier this year from its original site.

Initially, I tried to directly show the PDF as an embed or object tag. However, getting the embedded viewer properly sized was beyond what I wanted to tangle with.

Instead, I took the same approach github does: Convert the first pages of the pdf to images and display them with the img tag. This then gets the lightbox treatment from my mkdocs setup, so you can comfortably zoom the page image to its full 150dpi majesty.

I have made the decision to not worry too much about specially-crafted PDF files that might exploit bugs in poppler's pdftoppm: I'll have already locally viewed any PDF I'm putting in the junk drawer, so the Bad Thing will already have occurred. Anyway, the site generation process installs the latest packaged pdftoppm so I'm also likely to have patches for known poppler flaws.

The main downside I've identified is that the image is just an image and is therefore not searchable with the browser's search feature. Alas.

[permalink]

23 July 2026, 0:09 UTC

junkdrawer gets a grebedoc page


I added machinery to my junkdrawer repo so that it also builds a static website that's served by grebedoc.

The code to do it is in the junkdrawer's "site" directory and it functions just great using the free codeberg-tiny runner for CI.

[permalink]

20 July 2026, 14:52 UTC

Calling a Python function with a timeout


In Python 3.13+ with pyrepl, my rpn calculator can show the result of evaluating some code while the user is entering it at the prompt.

Most code is trivial (e.g., 3 4 5 * * to multiply some numbers together) but complex code including recursion is possible. This can give code that runs for quite a long time! It's one thing if this happens when you hit enter, but another when there's the possibility that the repl freezes in the middle of entering some code.

There are various recipes for running Python code with timeout, but I wrote my own. Then, just for extra fun, I made it properly type check with ty.

I didn't integrate it into pydc yet but that part shouldn't be hard.

See the docstring of the function for some additional caveats.

(Embedded not available - View kvghgghs/timeout.py on codeberg.org or download raw)

[permalink]

19 May 2026, 3:41 UTC

The oldest bug I ever fixed? (no)


Correction

Update, 2026-05-24: It was an emulator bug. My friend tested on his hardware and the original comparison code works correctly.

Original post

I've been playing with Tom Pittman's Tiny BASIC for 6800 computers. It's a very simple, small, and early BASIC implementation. It is also almost as old as me, being announced by February 1976.

In this BASIC, all numbers are 16-bit signed integers with a range of -32768 to 32767 inclusive.

Anyway, while working in Tiny BASIC, I got some weird results and boiled it down to the following:

IF 32513 < 0 THEN PRINT "WAT"

This prints "WAT", even though we usually consider 32513 to be greater than 0.

After spending an evening and a morning with it, and comparing the implementation to Tom Pittman's Tiny BASIC for 6502 computers, I believe that this bug is in the original implementation, and not the emulator I'm using.

The original version performs a 16-bit subtraction from two separate 8-bit subtractions; then, from the final flags value tries to determine whether the full result of the subtraction is greater to, less than, or equal to zero.

This works for many inputs. However, for some particular input values, the CPU flags—which are only aware of the results of the most significant byte of the subtraction—don't match up with the usual notions of comparison.

The fix, which I've implemented in my version of Tiny BASIC, is to instead compare the MSBs first as signed numbers, then if they are equal, to compare the LSBs as unsigned numbers.

I also wrote a test harness which performs about 60,000 comparisons, checking them against Python's idea of integer ordering. Now, all those comparisons (as well as my original test case) pass.

From what I can determine, the listing I have is derived from "TINY BASIC 6800 V.3R (C)1976 BY TOM PITTMAN (TB683R)". The "3" indicates it's the 4th release (0-based version numbering); TB682R was mentioned in ddj in October, 1976.

I'm a bit tickled to think I have fixed a bug that someone wrote when I was less than 1 year old cough 50 years ago cough. It's a bit much to claim I might be the first person who ever noticed or fixed the bug, but I looked through a range of old dr dobb's journal issues and never found anyone who remarked on the bug.

This is quite possibly the earliest-written bug I will ever fix.

[permalink]

6 October 2024, 14:15 UTC

Talking directly to in-process tcl/tk


I recently saw a post on a blog about using wish as a subprocess of Python, as a way to access tk without the complexity of tkinter.

To be clear the original post also calls out the situation where the tkinter part of python is not installed by default, and my technique would not be applicable there.

So what do you do if you like Tk but don't care for the high level abstraction provided by Tkinter? Well, you can import _tkinter and create a tkapp object with _tkinter.create().

The _tkinter module and the tkapp object is largely undocumented, but in Python 3.11 here are some useful methods:

  • tkapp.createcommand: Create a callback into Python code. Takes a string and a callable. Creates a Tcl command with that name, that calls back into Python code: app.createcomand("cb", lambda *args: print("cb", args))
  • tkapp.eval: Takes a command and evaluates it. Call it with a single string: app.eval("button .b -text HI -command {cb arg1 arg2}")
  • tkapp.call: Takes a series of arguments, does proper Tcl quoting, and evaluates it: app.call("pack", ".b")
Now, go forth and program your graphical app without all that Python object nonsense!

Python 3.11.2 (main, Aug 26 2024, 07:20:54) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import _tkinter
>>> app = _tkinter.create()
>>> app.createcommand("cb", lambda *args: print("cb", args))
>>> app.eval("button .b -text HI -command {cb arg1 arg2}")
'.b'
>>> app.call("pack", ".b")
''
>>> # Now I click the button several times ...
>>> cb ('arg1', 'arg2')
cb ('arg1', 'arg2')
cb ('arg1', 'arg2')

[permalink]

1 September 2015, 14:39 UTC

DragonBoard™ 410c with rt-preempt failure


I was excited to obtain a DragonBoard, an affordable 64-bit ARM single-board computer, but unfortunately it hasn't lived up to my hopes.

First of all, it shipped with one terrible problem, making it useless for headless development: The onboard wireless doesn't receive multicast traffic, including ARP requests! And their kernel doesn't provide many modular drivers, so adding any old USB wireless dongle doesn't work until you rebuild the kernel. As of this writing, there is no useful workaround.

And that's when I hit the second show-stopper problem. I planned to build kernels for this thing anyway, because the goal is to run LinuxCNC on it with rt-preempt realtime. So I stuck in a large capacity micro-sd card, obtained the kernel source, built it, and rebooted with my new kernel.

(incidentally, testing kernels is a PITA. You have two choices: one, flash the one and only boot area with your new and untested kernel and hope for the best; two, tether to a PC (losing USB keyboard and mouse on the dragonboard!) and use fastboot every time you boot. ugh)

.. but the new kernel just didn't work. It scrolled kernel messages, but got stuck partway through booting. dmesg implicated the wireless card. This one at least has a workaround: cherry-pick a certain commit from their kernel git. As I understand it, this bug is triggered by using the compiler on the dragonboard, and not encountered with the linaro cross-compiler running on a traditional x86 desktop.

Anyway, having found out about this I was briefly celebratory. I booted with my own kernel and had a working USB dongle. Unfortunately, that celebration was short-lived. The USB dongle hasn't been terribly reliable either, particularly under high CPU load.

I soldiered on and patched the linaro 4.0 kernel with the 4.0-rt5 patchset. There were a few minor conflicts which I believe I resolved correctly. Unfortunately, the LinuxCNC latency-test readily encounters latencies above 15ms (15000us). LinuxCNC really would like sub-100us max latencies.

I briefly enabled tracers, and have traces that seem to implicate a variety of subsystems: networking, usb, and video *all* figure. More experienced developers than I will have to be the ones to sort out RT on 64-bit ARM.

[permalink]

5 August 2015, 13:13 UTC

Proven Delights (and what are proofs anyway)


I won a copy of the book Hacker's Delight from John Regehr for my entry in his nibble sort contest earlier this year.

In that thread I had heard about the CBMC Bounded Model Checking software, and it gave me the idea to combine the two: a project to take implementations of the algorithms from Hacker's Delight and prove the algorithms' properties with CBMC.

I have a modest start on github, which I am calling "Proven Delights":

read more…

24 June 2014, 12:35 UTC

Better pasting for irssi in X


While it's otherwise an excellent irc client, I've been frustrated by pasting in irssi. There are two main problems:

read more…

2 December 2013, 19:30 UTC

mailpie moves to github

30 September 2013, 14:19 UTC

GNU 30th—Celebrating 30 years

19 September 2013, 1:34 UTC

Debugging realtime components in UBC3

16 July 2013, 16:32 UTC

Rotary Delta Kinematics

6 March 2013, 13:59 UTC

Adventures in localization

19 February 2013, 22:42 UTC

Online Anagrammer with AJAX

All older entries
Website Copyright © 2004-2024 Jeff Epler