Full Width [alt+shift+f] Shortcuts [alt+shift+k]
Sign Up [alt+shift+s] Log In [alt+shift+l]
1
<![CDATA[For Chrismtas 2024 I bought myself a lovely little Cardputer uLisp Machine, an M5Stack Cardputer that can run uLisp. The M5Stack Cardputer is a card-sized, microcontroller-based portable system for home automation, hobby, and industrial applications. Although not designed for Lisp the Cardputer can run uLisp, an implementation optimized for microcontrollers. This is my unit: Cardputer uLisp Machine card-sized microcontroller-based computer. The uLisp system provides a capable Lisp implementation, a rich anvironment, debugging and editing tools, and lots of libraries and examples. It's well maintained and has an active user community. Motivation Like many Lispers I always wanted to play with Lisp on the bare metal and the Cardputer uLisp Machine is a simple and inexpensive solution. uLisp runs on a wide variety of microcontrollers and boards. I picked the ESP32-S3 based Cardputer because it's compact, can run off rechargeable batteries or USB without an external power...
2 months ago

Improve your reading experience

Logged in users get linked directly to articles resulting in a better reading experience. Please login for free, it takes less than 1 minute.

More from Paolo Amoroso's Journal

Enjoying the stability of Linux

<![CDATA[I initially used Linux from the mid 1990s to 2015, first as dual boot with Windows 95 and then as my only desktop operating system. Back then my PC had an Nvidia graphics card and system updates frequently broke X11 on Linux, leaving me at the text console with no idea what to do. At some point I stopped applying the updates as I dreaded change. In 2015 I had enough and switched to ChromeOS. Although I still used Crostini Linux on ChromeOS, over the years I increasingly bumped into the limitations of this containerized approach. The rumors of Google considering for ChromeOS a feature like Windows Recall eventually made the cloud operating system a deal breaker. So I decided to migrate back to Linux for good, bought a System76 Merkaat mini PC with no Nvidia hardware, and installed Linux Mint Cinnamon. It's been seven months since my switch back to Linux in July of 2024 and, despite some early issues, my experience with Mint has been smooth and uneventful. Linux supports all my hardware, system updates install seamlessly, and everything works. The system fades into the background and I can focus on running the programs I need. In my early Linux years I often upgraded to every major and minor version of my distro. There were good reasons as Linux evolved rapidly, significant features came out regularly, online updates weren't a thing, and getting online was costly and impractical. It helped that I was younger and eager to play with Linux. In January of 2025 Linux Mint 22.1 was released, the first minor version since my current Mint 22. But this time I'll defer upgrading until at least the next major release, or possibly for a year or two. I feel no pressure as system updates flow regularly over the support period of Mint 22 that ends in 2029. Besides, upgrading involves some preparation and work I don't look forard to doing. If some features I really want do come out I may consider upgrading. But, for now, I want to savor this newfound Linux stability. Linux has really come a long way. Linux a href="https://remark.as/p/journal.paoloamoroso.com/enjoying-the-stability-of-linux"Discuss.../a Email | Reply @amoroso@fosstodon.org !--emailsub--]]>

a week ago 3 votes
Making uLisp more usable on the Cardputer

<![CDATA[It's a joy to use the Cardputer uLisp Machine, a nice little microcontroller system that runs uLisp. But after a short experience I had to put aside my Cardputer due to a showstopper issue that made it impractical to program the device. Since then a good workaround emerged and I learned how to improve the experience with the device. The showstopper is a buffer overflow when sending Lisp code from Emacs to the Cardputer over a serial USB line. If the receive buffer fills up too fast the device will crash and disconnect. Sending more than a few hundred bytes triggers the issue and makes it impractical to evaluate medium or large code blocks. This acknowledged Arduino issue reported in February of 2022 has not been addressed yet. Meanwhile, Dennis Draheim devised a workaround. He wrote some Emacs Lisp code to open a serial connection to the Cardputer and send an expressions or region for evaluation. The trick is to split the input into lines and send one line at a time, with a delay in between to keep the Cardputer's serial buffer from overflowing. Dennis' code works well and makes uLisp usable on the Cardputer. The only downside is the echoed input clutters the Emacs serial buffer. Our attempts at turning off echo failed as we don't know where Emacs handles this. The workaround enables running more substantial and interesting uLisp programs such as this nice surface of rotation graphics demo: 3D function plot on the display of a Cardputer uLisp Machine device. The Cardputer has a tiny built-in keyboard that is handy for short interactions. But it's prone to overtyping when entering a character that requires pressing two keys, such as shifted characters or the parentheses. I originally attempted to press at the same time the Aa shift key and the key with the desired symbol. But this often results in typing more than one character as hitting such tiny targets simultaneously is difficult. I later stumbled upon a way to consistently avoid overtyping: I press and hold Aa, then press the key with the desired character. #Cardputer #Lisp a href="https://remark.as/p/journal.paoloamoroso.com/making-ulisp-more-usable-on-the-cardputer"Discuss.../a Email | Reply @amoroso@fosstodon.org !--emailsub--]]>

2 weeks ago 2 votes
Switching Blot sync from Google Drive to Git

<![CDATA[In 2023 I migrated my personal website from Squarespace to Blot. Blot is a static site generator that publishes as a website a folder stored on Google Drive, Dropbox, or a Git repository. Back then I set up Blot to publish a Google Drive folder but I recently switched the synchronization to a Git repository. It was necessary as Blot has been unable to access Google Drive since Google eventually imposed onerous and expensive auditing processes to use their API. This forced Blot and other small developers to drop support for Google Drive. Blot's developer is implementing a workaround that will allow to continue using Google Drive. But I decided to go with Git anyway as it's a better fit for my current tools and workflows. Switching to Git instantaneously and seamlessly created a repository I can clone and update to change the site. #blogging #Google a href="https://remark.as/p/journal.paoloamoroso.com/switching-blot-sync-from-google-drive-to-git"Discuss.../a Email | Reply @amoroso@fosstodon.org !--emailsub--]]>

a month ago 2 votes
I accepted the Blog Question Challenge

<![CDATA[MattoF has tagged me for the Blog Question Challenge which asks bloggers questions about their background and motivations, tools, and workflows. It is a variation by Kev Quirk of a challenge originally created by Ava at Bear Blog. I have accepted this fun challenge and this is my entry. Why did you start blogging in the first place? I started in 2004 in the golden era of blogging. I enjoyed blogs and their community, wanted to experiment with the new medium and tools, and thought I had something to say. So I set up a blog to tap into and amplify this energy. What platform are you using to manage your blog, and why do you use it? For my blog Paolo Amoroso's Journal I use Write.as because it's a lightweight blogging platform with a clean design and excellent support for technical writing, which is what I mostly do. As a byproduct these features reduce friction in writing and publishing. I settled on Write.as after a long journey across blogging platforms. Have you blogged on other platforms before? Yes, and I used static site generators well before it was cool. My first platform was BlogMax, an Emacs package we would now describe as a static site generator. Back then I was a heavy Emacs user and it seemed natural to blog with Emacs. My first blog has been online since I started it in August of 2004, now an abandoned time capsule of what its name unambiguously hints has always been my obsessionpassion: Lisp Propulsion Laboratory log. For my blogs I later used Blogger, Micro.blog, and eventually Write.as. I also used the elephant in the room, WordPress, for contributing to other blogs. How do you write your posts? I just use the online Markdown editor of Write.as as I don't need anything fancier. When do you feel most inspired to write? I mostly do technical writing and my blog is a sort of lab log. It's usually clear what to write about and inspiration is rarely involved. In general I write when I complete a project, am stuck with a technical issue, or I just want to record the progress and experience with a project or something I do. Do you normally publish immediately after writing, or do you let it simmer a bit? I never publish immediately. I'm a slow writer and do lots of editing rounds, especially as a non native English speaker. Letting it simmer improves the final result. What’s your favorite post on your blog? My favorite is My encounter with Medley Interlisp, which captured my exhilaration at approaching and exploring Medley Interlisp. I was beginning to dive deep into this 1980s Lisp workstation environment I had always dreamed of, and blogging the experience seemd a natural way of sharing my excitement and explaining what's unique about Medley. This is apparently also the favorite post of my readers. It's the most visited of the blog and received over 38K views, most of which when it was shared on Hacker News. Any future plans for the blog? No, which is a feature. Everything is going so smoothly I just want to continue blogging without worrying about tools and platforms. Who's next? I nominate Fabrizio Ferri Benedetti, Kevin Tofel, and Rob Roland. I hope they will accept as they have interesting blogs and diverse stories. blogging a href="https://remark.as/p/journal.paoloamoroso.com/i-accepted-the-blog-question-challenge"Discuss.../a Email | Reply @amoroso@fosstodon.org !--emailsub--]]>

a month ago 2 votes
Interpinkie, a Finger client in Interlisp

<![CDATA[I wrote Interpinkie, a basic Finger client in Interlisp that runs on the Medley environment. This is the main window of the program: Main window of the Interpinkie Interlisp Finger client. It was a fun challenge considering I couldn't use Medley's TCP/IP stack. I always wanted to do some network programming with Medley. But the bitrot of its TCP/IP stack left it in a non working state since before the Medley Interlisp project, and the revival effort hasn't got around to the stack yet. The XNS stack still mostly works with the Dodo Services XNS implementation. But, as far as I know, XNS provides LAN services and I'm not sure whether or how it allows access to the outside Internet. So I cheated and reached for Medley's escape hatch. As part of the Medley modernization effort, the UNIXUTILS library module provides the ShellCommand function to run a Unix program on the host operating system and send the output to a stream. Since I can run arbitrary Unix commands from Medley, it's trivial to build a client that talks the Finger protocol via Netcat. For example, to run the Finger query amoroso@happynetbox.com I can just execute this from the Linux shell: $ echo "amoroso" | nc -C -w 3 happynetbox.com finger where -C sends \r\n as line-ending, -w 3 sets a timeout of 3 seconds, and finger is the Finger port number as per /etc/services. All Interpinkie does is to build and feed a query to ShellCommand, pipe the output through tr) to remove extra \rs, and redirect the output to a suitable destination. The Interlisp function QUERY.FINGER.SERVER wraps this functionality. By default the output goes to the primary output (Interlisp jargon for stdout), or optionally to a separate window with menu options for running another Finger query or quitting the program. Interpinkie builds upon user interface techniques I used in previous projects such as attaching a menu to a TEdit window. TEdit, the WYSIWYG the rich text editor of Medley, can be driven by other programs that send output to the editor buffer. This is handy as the programs get an automatically repainted scrollable window for free from TEdit. In addition, Interpinkie attaches a command menu to the TEdit window and uses TEdit's prompt window (Interlisp jargon for an edit field and status area) to request input and display errors. Unlike my previous Interlisp projects, however, Interpinkie doesn't call the TEdit API to insert text in the buffer. Instead, the program redirects the ShellCommand output to the Lisp stream associated with the TEdit window, causing the text to appear in the buffer. By default ordinary Lisp printing functions such as PRIN1 and PRINT send the output to the REPL or optionally to another Lisp I/O stream. Along with these output destinations, some Interlisp components and programs like TEdit have an associated stream. So it's easy to send output to TEdit by just printing to the stream returned by (TEXTSTREAM Window), where Window is a TEdit window, or redirecting to that stream the output of printing functions like PRIN1. In other words, ordinary stream I/O is the API. Since ShellCommand accepts a stream as an optional argument, QUERY.FINGER.SERVER passes TEdit's stream to ShellCommand thus causing the output to go to the editor buffer. The benefit is Interpinkie uses the same QUERY.FINGER.SERVER function to output to both the primary output and a TEdit window. If the destination is a window Interpinkie does some additional processing before and after sending the output. For example, before doing output the program updates the window title to reflect the Finger query, and sets the TEdit buffer to read-only once the output finishes. These days there are not many Finger servers and I was lucky to stumble upon happynetbox.com. It is managed by Happy Net Box, a nice public Finger server anyone can sign up to for for free and publish a status file. Happy Nex Box was essential for developing and testing Interpinkie, as well as fun — and addictive — as you can see by running the query random@happynetbox.com to look up a random user. #Interpinkie #Interlisp #Lisp a href="https://remark.as/p/journal.paoloamoroso.com/interpinkie-a-finger-client-in-interlisp"Discuss.../a Email | Reply @amoroso@fosstodon.org !--emailsub--]]>

a month ago 2 votes

More in programming

constantly divisionless random numbers

Last year I wrote about inlining just the fast path of Lemire’s algorithm for nearly-divisionless unbiased bounded random numbers. The idea was to reduce code bloat by eliminating lots of copies of the random number generator in the rarely-executed slow paths. However a simple split prevented the compiler from being able to optimize cases like pcg32_rand(1 << n), so a lot of the blog post was toying around with ways to mitigate this problem. On Monday while procrastinating a different blog post, I realised that it’s possible to do better: there’s a more general optimization which gives us the 1 << n special case for free. nearly divisionless Lemire’s algorithm has about 4 neat tricks: use multiplication instead of division to reduce the output of a random number generator modulo some limit eliminate the bias in (1) by (counterintuitively) looking at the lower digits fun modular arithmetic to calculate the reject threshold for (2) arrange the reject tests to avoid the slow division in (3) in most cases The nearly-divisionless logic in (4) leads to two copies of the random number generator, in the fast path and the slow path. Generally speaking, compilers don’t try do deduplicate code that was written by the programmer, so they can’t simplify the nearly-divisionless algorithm very much when the limit is constant. constantly divisionless Two points occurred to me: when the limit is constant, the reject threshold (3) can be calculated at compile time when the division is free, there’s no need to avoid it using (4) These observations suggested that when the limit is constant, the function for random numbers less than a limit should be written: static inline uint32_t pcg32_rand_const(pcg32_t *rng, uint32_t limit) { uint32_t reject = -limit % limit; uint64_t sample; do sample = (uint64_t)pcg32_random(rng) * (uint64_t)limit); while ((uint32_t)(sample) < reject); return ((uint32_t)(sample >> 32)); } This has only one call to pcg32_random(), saving space as I wanted, and the compiler is able to eliminate the loop automatically when the limit is a power of two. The loop is smaller than a call to an out-of-line slow path function, so it’s better all round than the code I wrote last year. algorithm selection As before it’s possible to automatically choose the constantly-divisionless or nearly-divisionless algorithms depending on whether the limit is a compile-time constant or run-time variable, using arcane C tricks or GNU C __builtin_constant_p(). I have been idly wondering how to do something similar in other languages. Rust isn’t very keen on automatic specialization, but it has a reasonable alternative. The thing to avoid is passing a runtime variable to the constantly-divisionless algorithm, because then it becomes never-divisionless. Rust has a much richer notion of compile-time constants than C, so it’s possible to write a method like the follwing, which can’t be misused: pub fn upto<const LIMIT: u32>(&mut self) -> u32 { let reject = LIMIT.wrapping_neg().wrapping_rem(LIMIT); loop { let (lo, hi) = self.get_u32().embiggening_mul(LIMIT); if lo < reject { continue; } else { return hi; } } } assert!(rng.upto::<42>() < 42); (embiggening_mul is my stable replacement for the unstable widening_mul API.) This is a nugatory optimization, but there are more interesting cases where it makes sense to choose a different implementation for constant or variable arguments – that it, the constant case isn’t simply a constant-folded or partially-evaluated version of the variable case. Regular expressions might be lex-style or pcre-style, for example. It’s a curious question of language design whether it should be possible to write a library that provides a uniform API that automatically chooses constant or variable implementations, or whether the user of the library must make the choice explicit. Maybe I should learn some Zig to see how its comptime works.

10 hours ago 3 votes
Air purifiers are a simple answer to allergies

I developed seasonal allergies relatively late in life. From my late twenties onward, I spent many miserable days in the throes of sneezing, headache, and runny eyes. I tried everything the doctors recommended for relief. About a million different types of medicine, several bouts of allergy vaccinations, and endless testing. But never once did an allergy doctor ask the basic question: What kind of air are you breathing? Turns out that's everything when you're allergic to pollen, grass, and dust mites! The air. That's what's carrying all this particulate matter, so if your idea of proper ventilation is merely to open a window, you're inviting in your nasal assailants. No wonder my symptoms kept escalating. For me, the answer was simply to stop breathing air full of everything I'm allergic to while working, sleeping, and generally just being inside. And the way to do that was to clean the air of all those allergens with air purifiers running HEPA-grade filters. That's it. That was the answer! After learning this, I outfitted everywhere we live with these machines of purifying wonder: One in the home office, one in the living area, one in the bedroom. All monitored for efficiency using Awair air sensors. Aiming to have the PM2.5 measure read a fat zero whenever possible. In America, I've used the Alen BreatheSmart series. They're great. And in Europe, I've used the Philips ones. Also good. It's been over a decade like this now. It's exceptionally rare that I have one of those bad allergy days now. It can still happen, of course — if I spend an entire day outside, breathing in allergens in vast quantities. But as with almost everything, the dose makes the poison. The difference between breathing in some allergens, some of the time, is entirely different from breathing all of it, all of the time. I think about this often when I see a doctor for something. Here was this entire profession of allergy specialists, and I saw at least a handful of them while I was trying to find a medical solution. None of them even thought about dealing with the environment. The cause of the allergy. Their entire field of view was restricted to dealing with mitigation rather than prevention. Not every problem, medical or otherwise, has a simple solution. But many problems do, and you have to be careful not to be so smart that you can't see it.

17 hours ago 2 votes
Let's Talk About The American Dream

A few months ago I wrote about what it means to stay gold — to hold on to the best parts of ourselves, our communities, and the American Dream itself. But staying gold isn’t passive. It takes work. It takes action. It takes hard conversations that ask

2 hours ago 1 votes
A Happy Day for Rust
yesterday 3 votes
March 2025
yesterday 2 votes