David Heinemannhttps://dheinemann.com/assets/about.png2022-01-22T06:38:10+10:00David HeinemannThis is not the Web I've Known2022-01-09T00:00:00+10:002022-01-09T00:00:00+10:00https://dheinemann.com/posts/2022-01-09-this-is-not-the-web-ive-known <p> As I get older, I'm reflecting more and more on how different things are today compared to my childhood. One of the biggest changes I've observed is to the World Wide Web. What was once a Wild West of individuality and self-expression has been largely abandoned in favour of restrictive walled gardens like Facebook and Twitter, and the Web is worse off for it. </p> <p> The Web was very different when I was a kid. It was smaller, and more intimate. The social networks of today were basically non-existent. Instead, users congregated and exchanged ideas on message boards, Usenet, or IRC. If one had something more substantive to share, such as essays or artwork, one could create a personal website&mdash;often through a free host such as Geocities, since self-hosting in the era of dial-up Internet was impossible for most<sup>1</sup>. </p> <p> Back then, nobody really had a concept of what a good, well-designed, and accessible website looked like. Unencumbered by the shackles of modern design sensibilities, people let their imaginations run wild. Tacky GIFs, tables, and frames were abundant. It was <b>weird</b> and it was <b>awesome</b>. </p> <div style="float: left; width: 100%"> <a href="https://geocities.restorativland.org/SiliconValley/4324/"> <figure style="float: left;"> <img src="https://dheinemann.com/assets/thecritic.png" alt="The Critic's homepage" width="610" height="438"/> <figcaption>The Critic's homepage</figcaption> </figure> </a> <a href="https://geocities.restorativland.org/SiliconValley/4300/"> <figure style="float: left;"> <img src="https://dheinemann.com/assets/computerworld.png" alt="Home of Computer World" width="610" height="438"/> <figcaption>Home of Computer World</figcaption> </figure> </a> </div> <p> Even in the days of MySpace, users enjoyed a level of control over their personal pages not seen today, with the ability to change the CSS and background image to their liking. Unfortunately, those times are largely behind us now&mdash;lost somewhere in the mid-2000s. In the Facebook era, everybody shares the same sterile profile, with the only avenues for self-expression being the photos and comments that they post. Not to mention the rampant spying and privacy invading that goes on today&hellip; </p> <p> But the Web doesn't have to be this way. In fact, there's a growing number of people dissatisfied the state of the Modern Web and looking to the Old Web for inspration: <ul> <li> <a href="https://neustadt.fr/essays/the-small-web/">Rediscovering the Small Web</a> (and companion article <a href="https://neustadt.fr/essays/against-a-user-hostile-web/">Against an Increasingly User-Hostile Web</a>) </li> <li><a href="https://cheapskatesguide.org/articles/beauty-of-text.html">Rediscovering the Beauty of Text on the Internet</a></li> <li><a href="https://thewebisfucked.com">The Web is Fucked</a></li> <li><a href="https://sadgrl.online/cybercafe/manifesto.html">The Internet has Changed</a></li> </ul> </p> <p> Some of that Old Web magic can be reclaimed for yourself by creating a personal website and truly making it <i>your own</i> space. Go crazy and make something unique! <ul> <li> <b>Find a host.</b> <a href="https://neocities.org">Neocities</a> has filled in the vacuum left by Geocities' demise. It offers a generous 1 GB of storage and no ads whatsoever&mdash;all free of charge. If you need a bit more flexibility, <a href="https://nearlyfreespeech.net">Nearly Free Speech</a> offers a pay-as-you-go FreeBSD environment for pennies on the Dollar<sup>2</sup>. </li> <li> <b>Build your website.</b> Pick up some HTML and CSS, and let your imagination run wild. You will learn a lot, and the website you create will be wholly your own creation. Skip the JavaScript for now&mdash;you don't need a web framework to have fun. </li> <li> <b>Make friends and explore.</b> There are several communities of like-minded people out there, such as <a href="https://yesterweb.org">Yesterweb</a>, or Neocities itself. </li> </ul> </p> <p> My site is hand-coded HTML & CSS, with a bit of PHP to automate the index, Atom feed, and page templates. Give it a try for yourself and see what you come up with! </p> <p class="footnote"> <hr> <sup>1</sup> Dial-up Internet used the phone line. Unless you had two phone lines at home, you were unable to make or receive phone calls while connected to the Internet.<br> <sup>2</sup> I've been a happy customer of Nearly Free Speech for 10+ years. The cost of hosting this website is less than $10 USD/year. </p>Fixing Breville Bambino Plus Drip Tray Leaks2021-12-28T00:00:00+10:002021-12-28T00:00:00+10:00https://dheinemann.com/posts/2021-12-28-fixing-breville-bambino-plus-drip-tray-leaks <p> Earlier this year, my workplace got a Breville (Sage for those in the UK) Barista Express espresso machine. Making my own coffees has been fantastic. They're always just the way I want them, and it's significantly cheaper than buying from a cafe. </p> <p> This pursuaded me to get a Breville Bambino Plus at home. It makes great espresso for such a tiny machine. The only problem has been that it tends to leak from the drip tray. </p> <h3>The Leaks</h3> <p> The leaking happens in two locations: <ul> <li> At the pressure release pipe: water gets released from this pipe with enough force that it splashes off the bottom of the drip tray, and back up over the drip tray edges.<br> <img src="https://dheinemann.com/assets/bbpPressureReleaseOverflow.jpg" alt="Photo of pressure release pipe" class="autosize" style="max-width: 588px;"/> </li> <li> Around the milk jug thermal sensor: when the steam wand self-cleans, it sprays jets of water into the drip tray. The pressure of this spray forces water up and over the drip tray's edges around the thermal sensor.<br> <img src="https://dheinemann.com/assets/bbpSteamWandOverflow.jpg" alt="Photo of steam wand water overflow" class="autosize" style="max-width: 743px;"/> </li> </ul> </p> <p> The Bambino Plus drip tray doesn't have a rubber seal to prevent these leaks from occurring, which is an unfortunate shortcoming. The severity of the leaking seems to vary depending on how firmly the drip tray is attached&mdash;if your Bambino Plus is on a perfectly flat surface and you push the drip tray all the way into the machine, it seems to be fine. However, my kitchen table is somewhat uneven. If the drip tray is lower than the rest of the espresso machine, it doesn't seal very well and leaks occur. This has often resulted in a pool of water forming underneath the Bambino Plus, which is inconvenient to clean. </p> <p> The good news is that these leaks can be minimised or fixed for about $1.50. </p> <h3>The Solution</h3> <p> These leaks can be minimised or entirely fixed by adding your own seal to the espresso machine: <ol> <li> Acquire 30 cm of 3 mm thick self-adhesive EPDM (about $1.50). </li> <li> Cut a small section (about 50 mm), and apply behind the pressure release pipe.<br> <img src="https://dheinemann.com/assets/bbpPressureReleaseOverflowFix.jpg" alt="Photo of pressure release pipe fix" class="autosize" style="max-width: 500px"/> </li> <li> Cut a longer section (about 120 mm), and wrap it around the walls of the thermal sensor.<br> <img src="https://dheinemann.com/assets/bbpSteamWandOverflowFix.jpg" alt="Photo of steam wand water overflow fix" class="autosize" style="max-width: 400px"/> </li> <li> Insert the drip tray and ensure that it still fits. The fit may be tighter than it used to be. </li> <li> (Optional): Use the remaining EPDM to cut a few thin (1-2 mm) strips, and apply them to the lip of the drip tray, where the metal grill sits. This doesn't affect the leaking, but it reduces noise and vibration caused by the metal grill rattling when pulling a shot. </li> </ol> EPDM is somewhat porous, and will allow a little bit of water through. However, it's soft enough that the drip tray will form a good seal and prevent most of the water from leaking. For best results, align the EPDM as closely as possible to the top of the thermal sensor wall (where the lip is located) to prevent water from sitting on top of the EPDM. </p> <p> You may have better results if you can source something less porous, like solid rubber. The thinnest adhesive solid rubber I could find was 3 mm thick, which would likely prevent the drip tray from fitting. Adhesive rubber with thickness of 1 mm or less might work. </p>Retiring the ThinkPad X2202022-01-13T00:00:00+10:002021-12-24T00:00:00+10:00https://dheinemann.com/posts/2021-12-24-retiring-the-thinkpad-x220 <p> The day has finally come to retire my ThinkPad X220. This laptop has been absolutely bullet-proof&mdash;it has survived 9&frac12; years of almost daily use, and is still going strong! </p> <p> I purchased the X220 in July 2012 for $1,066 AUD, with these specs: <ul> <li>CPU: Intel Core i5-2520M Processor</li> <li>GPU: Intel HD Graphics 3000</li> <li>Display: 1366x768 IPS display</li> <li>RAM: 2&times;4 GB RAM</li> <li>Storage: 320GB HDD</li> </ul> </p> <p> And I steadily upgraded it over the years, for a total of $928 AUD: <ul> <li>250 GB SSD (2013)</li> <li>Ultrabase (2013)</li> <li>Replacement battery (2016)</li> <li>Replacement palmrest (2018)</li> <li>Replacement battery (2018)</li> <li>Bluetooth adapter (2018)</li> <li>2&times8 GB RAM (2020)</li> <li>1 TB SSD (2021)</li> </ul> </p> <p> 9&frac12; years of daily use for just shy of $2,000 AUD isn't a bad deal. The laptop is still going strong, and I could keep using it today. Unfortunately, the CPU isn't what it used to be. It can barely run a single VM these days without being thermal throttled, and although I could replace the thermal paste to extend its life a little longer, I think it's time to move on. </p> <p> I've spent a lot of time over the years thinking about what I would buy next. I always expected to get another X-series ThinkPad&mdash;but not any more. I'm not happy with the soldered RAM in newer models. Moreover, I want to play the odd video game, and I no longer need a portable computer. Buying a new laptop just doesn't make sense for me. However, I have a limited amount of space to work with, so I knew that if I were to upgrade to a desktop PC, it would have to be a <i>small one</i>. </p> <p> For that reason, I decided to go with a mini-ITX build. Although it would limit my options for components and cost more, it would also avoid using up too much desk space. After doing some research, I decided the Cooler Master NR200 would be perfect. It's <i>just</i> large enough to fit all the usual components, plus a full-size GPU, cooler, and extra case fans. </p> <p> That said, I decided not to use a dedicated GPU for the time being. I <i>do</i> have a spare Nvidia GTX 970 lying around, but I decided not to bother due to the potential for Linux compatibility issues (i.e. with Wayland). I settled on the Ryzen 5 5600G CPU instead, which has zero-hassle integrated graphics that are <i>almost</i> as good. When the GPU prices eventually (<i>hopefully</i>) return to normal, I'll buy a dedicated AMD GPU to complete the build. </p> <p> In the meantime, the build is as follows: <ul> <li>Case: <a href="https://www.coolermaster.com/catalog/cases/mini-itx/masterbox-nr200/">Cooler Master NR200</a></li> <li>CPU: <a href="https://www.amd.com/en/products/apu/amd-ryzen-5-5600g">AMD Ryzen 5 5600G</a></li> <li>Cooler: <a href="https://noctua.at/en/nh-c14s">Noctua NH-C14S</a></li> <li>Motherboard: <a href="https://rog.asus.com/motherboards/rog-strix/rog-strix-b550-i-gaming-model/">ASUS B550i</a></li> <li>RAM: <a href="https://www.crucial.com/memory/ddr4/bl2k16g32c16u4b">2&times;16 GB Crucial Ballistix</a></li> <li>Storage: 1 TB SSD</li> <li>PSU: <a href="https://www.bequiet.com/en/powersupply/1553">Be Quiet! SFX L Power 600W</a></li> </ul> Excluding the SSD (which I kept from the ThinkPad), the build cost $1,423 AUD. </p> <p> The ThinkPad X220 used to idle at about 60-75&deg;C (140-167&deg;F), and routinely reached 96&deg;C (205&deg;F) at load... before immediately being thermal throttled into oblivion. I'm happy to say that the new build idles under 40&deg;C (104&deg;F), and sits around 65&deg;C (149&deg;F) at high load. A significant improvement! Now I can spend my Christmas holiday enjoying Disco Elysium. </p> <p> Here's hoping I get another 9&frac12; years out of this PC. </p> <h3>Update 2022-01-13</h3> <p> The Ryzen 5 5600G performance has been great for Disco Elysium and most other games I've tried. However, I could only achieve a stable 30 FPS in the Witcher 3 by playing at 720p with all the graphics settings on Low. I was hoping for better, so I decided to install the old GTX 970 and try my luck. </p> <p> I was expecting to encounter issues, but it's been completely fine. I was able to install the drivers <a href="https://rpmfusion.org/Howto/NVIDIA">through RPM Fusion</a>. It seems to work well with Wayland (although Fedora 35 now selects X11 by default). I'm now getting 40-60 FPS in the Witcher 3 at 1080p on High. Much better! </p> <p> I'd still go with AMD over Nvidia if I were to buy a new GPU, but the GTX 970 holds up well and is working great. </p>Using an Eaton 3S 850 UPS with Unraid2021-10-09T00:00:00+10:002021-10-09T00:00:00+10:00https://dheinemann.com/posts/2021-10-09-eaton-3s-with-unraid <p> For anyone in the market for an Unraid-compatible UPS... </p> <p> Power outages are frequent where I live, especially as the storm season approaches. After <a href="https://dheinemann.com/posts/2021-08-22-resurrecting-the-microserver">reviving my home NAS</a>, I decided it was time to protect my investment with a UPS. </p> <p> I wanted to get an Eaton power supply, since I have had good experiences with them in the past. However, my NAS runs Unraid, and I had heard a lot of reports online that Eaton devices aren't compatible&mdash;at least, not without installing a third-party plugin. On the other hand, APC and CyberPower were well-supported and highly recommended. </p> <p> After some research, I found that the Eaton 3S 850 UPS supports a HID-compliant USB interface. This is the same kind of interface as the APC devices I had seen recommended, so I decided to take a shot and order it. After connecting it to my NAS and enabling it in the Unraid UPS settings, it worked flawlessly. Quick testing showed that Unraid was able to detect a mains power outage (simulated by yanking the UPS power cord out), and perform a graceful shutdown. </p> <p> So if you're looking for a UPS for Unraid, some Eaton devices certainly do work out-of-the-box. </p> <p> My UPS settings in Unraid: <table style="border-spacing: 5px;"> <tbody> <tr><td style="font-weight: bold;">Start APC UPS daemon:</td><td>Yes</td></tr> <tr><td style="font-weight: bold;">UPS cable:</td> <td>USB</td></tr> <tr><td style="font-weight: bold;">Custom UPS cable:</td> <td><i>(blank)</i></td></tr> <tr><td style="font-weight: bold;">UPS type:</td> <td>USB</td></tr> <tr><td style="font-weight: bold;">Device:</td> <td><i>(blank)</i></td></tr> </tbody> </table> </p>Resurrecting the MicroServer2021-08-22T00:00:00+10:002021-08-22T00:00:00+10:00https://dheinemann.com/posts/2021-08-22-resurrecting-the-microserver <img src="https://dheinemann.com/assets/n40l.jpg" alt="HP ProLiant MicroServer N40L" style="float: right; margin-left: 30px; margin-bottom: 30px; width:200px;"/> <p> Way back in February 2012, I bought an <a href="https://web.archive.org/web/20120123141804/http://h10010.www1.hp.com/wwpc/us/en/sm/WF25a/15351-15351-4237916-4237918-4237917-4248009.html?dnr=1">HP ProLiant MicroServer N40L</a> to store my backups and other files. I installed 8 GB of ECC RAM, and a set of 2 TB Seagate Barracuda drives. For the OS, I used FreeNAS so that I could take advantage of ZFS. The server was great, and ran flawlessly until about 2016 when some of the disks began to get SMART errors. I shut the server down until I could order some replacement disks, but never got around to it. The server has been gathering dust ever since. </p> <p> In the meantime, I've been making do with a <a href="https://www.backblaze.com/">Backblaze</a> subscription. It's convenient and the price is reasonable, but it's not available for Linux. Therefore, I've been running it on my old Windows gaming PC. I'd prefer to have less Windows in my life, and the Linux-friendly alternative services like <a href="https://www.backblaze.com/b2/cloud-storage.html">Backblaze B2</a> and <a href="https://aws.amazon.com/s3/">Amazon S3</a> are expensive for large volumes of data. I decided it was time to resurrect the MicroServer and manage my own backups again. </p> <p> The MicroServer had been stored well for the last 5 years, and was still in working order. I bought four 4 TB Seagate IronWolf drives to replace the old ones. At $170 AUD each, they're more expensive, byte for byte, than the equivalent Barracuda. However, they're "proper" NAS drives, they don't use SMR, and they come with a 3-year warranty. They should last a long time. If they make it past 4 years, they'll have saved me money compared to a Backblaze subscription. </p> <p> But which OS to run? I'm over FreeNAS. ZFS was a chore to set up for the first time, and I've long since forgotten how to configure it. I don't need most of its features, so I want something simpler. </p> <p> I initially planned to install an SSD in the MicroServer's optical drive bay and run Fedora Server with the disks configured in a RAID array. However, I discovered that the only available power option for the SSD would be through MOLEX, and they seem prone to failure. </p> <p> After looking for other options, I found <a href="https://unraid.net">Unraid</a>, a commercial Linux distribution for network storage, among other things. It runs off a USB stick, just like FreeNAS did - perfect for my server. Unraid achieves redundancy through parity disks, similar to RAID 3. Unlike FreeNAS, the disks are formatted with a standard Linux filesystem like XFS or BTRFS. If the server ever dies, it will be easy to recover my data from the disks. </p> <p> The Unraid installation and configuration was dead easy. The web interface is intuitive enough that I was able to configure nearly everything without having to refer to the documentation. Looks like a winner. My only real complaint is that the default settings are very insecure: a fresh install has no root password, and yet SSH, Telnet, and FTP are all enabled. Additionally, the contents of the Unraid USB stick is shared publicly over SMB! This can all be fixed by the administrator after installation, but it's very unusual. Luckily, the upcoming <a href="https://forums.unraid.net/bug-reports/prereleases/unraid-os-version-6100-rc1-available-r1514/"> 6.10.0 release</a> will correct these security shortcomings for new installations. </p> <p> For now, I've configured my array with 2 parity disks and 8 TB of storage. This should last a good while, and the extra redundancy won't hurt. When I outgrow 8 TB, I'll convert one of the parity disks to bring the total storage to 12 TB. And since Unraid doesn't require that all disks be the same size, I can eventually upgrade the array one disk at a time. </p> <p> Now I can cancel my Backblaze subscription&hellip; but I may start using Backblaze B2 as additional insurance for my most important files, just in case the whole house ever burns down. </p>Building Replicalc Part 3: Porting to MS-DOS2021-08-15T00:00:00+10:002021-08-15T00:00:00+10:00https://dheinemann.com/posts/2021-08-15-building-replicalc-part-3 <aside> <div class="blurb"> <p> In late May, I decided to sit down and finally learn C. This has been a goal of mine for some time, but in the past I hadn't gotten much further than trying a handful of exercises from K&R. This time I had a specific project in mind: <a href="https://dheinemann.com/replicalc">Replicalc</a>, a REPL-based calculator for Linux, MS-DOS, and Windows 3. This series is a short retrospective about the journey to the first Replicalc release, 0.1.0, which took a little over a month. </p> <p> Articles in this Series: <ul> <li><a href="https://dheinemann.com/posts/2021-08-01-building-replicalc-part-1">Part 1: The Plan</a></li> <li><a href="https://dheinemann.com/posts/2021-08-08-building-replicalc-part-2">Part 2: The Calculator</a></li> <li>Part 3: Porting to MS-DOS (you are here)</li> </ul> </p> </div> </aside> <p> With Replicalc running well under Linux, it was time to begin porting to MS-DOS. The first step was to make sure the existing code (not including ncurses-specifics) would compile. </p> <p> I fired up DOSBox, installed Open Watcom, removed the ncurses-specific code, and tried compiling. It didn't work. I discovered that although I had been compiling for C90 in GCC, GCC has a very liberal interpretation of what "C90" is. I had accidentally written C99 code, and GCC didn't warn me despite having enabled compiler warnings. </p> <p> First there were a handful of instances where I had declared new variables in the middle of a function - these were easily fixed. Then there were also a few cases of the vague <em>Error! E1054: Expression must be constant</em> error. I determined that this was because I had been using variable-length arrays, which was only introduced in C99. In C90, array sizes must be compile-time constants. With those fixed, the code was compiling under MS-DOS. Time to move onto the user interface. </p> <p> Since ncurses wasn't available for MS-DOS, my plan was to create a new user interface from scratch. By designing it with the same API as its Linux counterpart, I would be able to use preprocessor macros to choose one interface or the other during compilation based on the user's operating system - sort of like a compile-time interface. </p> <p> My plan was to get user input using <code>getch()</code> from conio.h, then process it using a behind-the-scenes buffer. This would allow me to track the cursor position in order to allow moving left and right, or using the backspace and delete keys: </p> <pre> struct Buffer { char* str; /* Buffer text. */ int top; /* Index of last character in the buffer. */ int index; /* Index of the cursor. */ }; </pre> <p> This was a bit of a challenge for two reasons. First, I initially couldn't get arrow keys to work at all under DOSBox. After much troubleshooting and reading, I found that I needed to add some additional lines to my DOSBox config: </p> <pre> [sdl] usescancodes=false </pre> <p> Then I realised that arrow keys are part of the extended keyset, and must be detected using two separate <code>getch()</code> calls. The first call returns <code>0</code> to indicate that an extended key was pressed, and the second call indicates the extended key number. </p> <p> It was at this point that I discovered <a href="https://pdcurses.org">PDCurses</a> and felt a bit silly. PDCurses is a public-domain curses implementation that supports both DOS and Windows, and for my purposes its API is 100% ncurses-compatible<sup>&dagger;</sup>. It took a little work to figure out how to integrate it into the build process, but once I did it worked like a charm, and the ncurses code I had written for Linux was suddenly working under MS-DOS. In the end, PDCurses practically did the porting for me, but I learned a lot along the way. </p> <p> There was something that bugged me. Often when exiting Replicalc in MS-DOS, an ominous error message would appear: <code>*** NULL assignment detected</code>. I determined this was due to some kind of memory corruption error, but I wasn't sure what. </p> <p> First I tried running Replicalc through Valgrind, learning how Valgrind works along the way. This enabled me to find and fix a memory leak due to not freeing stacks and their elements properly. However, this didn't fix the NULL assignment error in MS-DOS, and no other issues were reported. </p> <p> I decided I would have to troubleshoot this in MS-DOS, but I couldn't get the Open Watcom debugger to work. Instead, I began commenting out blocks of code and testing to see if the issue had gone, slowing paring the program down until I found the source of the bug. In the end, it was another memory issue due to mallocing a 2D array incorrectly: </p> <pre> /* Good */ malloc(sizeof(double[STACK_MAX_CAP])); /* Bad */ malloc(sizeof(double[STACK_MAX_CAP][EXPR_MAX_WIDTH])); </pre> <p> In both cases, these bugs were due to me misunderstanding how 2D arrays work: the array and its individual elements must be allocated separately and freed separately. </p> <p> With no major bugs remaining, I decided this would become the first Replicalc release, 0.1.0. It's not perfect - there are still other bugs, especially due to lacking input validation. However, it's reliable enough that I can now use it every day instead of my old TI-83. </p> <div class="window" style="max-width: 640px"> <div class="innerBorder"> <div class="titleBar"> <div class="windowButton menuIconSmall"></div> <div class="title">Replicalc</div> <div class="windowButton minMax"></div> </div> <div class="content" style="max-width: 640px; max-height: 400px;"> <img src="https://dheinemann.com/assets/rcalcdos.png" alt="Replicalc running in MS-DOS" class="autosize"/> </div> </div> <div class="corner topLeftCorner"></div> <div class="corner topRightCorner"></div> <div class="corner bottomLeftCorner"></div> <div class="corner bottomRightCorner"></div> </div> <hr> <p class="footnote"> <sup>&dagger;</sup> PDCurses also supports Linux, but I didn't see a need to switch from ncurses. </p> </main>Building Replicalc Part 2: The Calculator2021-08-08T00:00:00+10:002021-08-08T00:00:00+10:00https://dheinemann.com/posts/2021-08-08-building-replicalc-part-2 <aside> <div class="blurb"> <p> In late May, I decided to sit down and finally learn C. This has been a goal of mine for some time, but in the past I hadn't gotten much further than trying a handful of exercises from K&R. This time I had a specific project in mind: <a href="https://dheinemann.com/replicalc">Replicalc</a>, a REPL-based calculator for Linux, MS-DOS, and Windows 3. This series is a short retrospective about the journey to the first Replicalc release, 0.1.0, which took a little over a month. </p> <p> Articles in this Series: <ul> <li><a href="https://dheinemann.com/posts/2021-08-01-building-replicalc-part-1">Part 1: The Plan</a></li> <li>Part 2: The Calculator (you are here)</li> <li><a href="https://dheinemann.com/posts/2021-08-15-building-replicalc-part-3">Part 3: Porting to MS-DOS</a></li> </ul> </p> </div> </aside> <p> With my development environment set up, I began working on an initial version for Linux. </p> <p> The first step was to build a tokenizer, which takes an expression like <code>100 + 200</code> and identifies the individual tokens: <code>100</code>, <code>+</code>, and <code>200</code>. This will allow the calculator to distinguish between numbers and operators. </p> <p> I already had a good idea of how to tackle this using a stack of strings, with each element representing an individual token. By iterating over each character in the expression, we can check whether it's the same "type" of character as the previous. If they're the same (for example, if both are numeric), append the new character to the top-most element of the stack. If they are different, push the new character onto the stack, where it will form a new token. There might be better ways to tackle it, but this was intuitive. </p> <p> The only area that proved a bit tricky was handling negative numbers. Depending on context, the <code>-</code> character can be either an operator (subtract), or it can indicate a negative number. To determine whether the context makes it an operator or not, I simply check the characters adjacent to it: if the previous character was a symbol and the next character is a number, then the <code>-</code> character is part of the following number; otherwise it's the subtract operator. </p> <p> The next step was to convert expressions from infix (<code>3 + 5 * 4</code>) to postfix (<code>3 5 4 * +</code>). Postfix expressions can be evaluated from left-to-right using a stack, making it the ideal notation for a calculator. This is usually achieved through the <a href="https://en.wikipedia.org/wiki/Shunting-yard_algorithm">shunting yard algorithm</a>, which juggles ("shunts") the expression's numbers and operators between two stacks until they are re-arranged in postfix notation. This was straightforward to implement using the detailed outline from Wikipedia. </p> <p> Next, it was time to build the heart of the calculator: the evaluator, which calculates the result of postfix expressions. Once again, I had a pretty good idea of how to achieve this. By iterating over the expression's tokens, we can check whether each one is a number or an operator. If it's a number, we add it to a stack. If it's an operator, we pop the last two numbers from the stack and apply the operator to them. The whole expression can be processed from start to finish in this way. </p> <p> At this point, I had a working calculator for Linux. The final part was the user interface. So far, I had simply hardcoded a handful of test expressions, which were then evaluated and printed to the console so I could check the result - but a real calculator needs to be interactive. I considered the options and decided to use ncurses, since it makes it easy to support editing one's input using the backspace, left, and right keys. Although ncurses doesn't support MS-DOS, I figured I could develop a close enough alternative later using a combination of conio.h and Open Watcom's graph.h. </p> <p> And with that, the Linux version was done. The next step was to get the calculator running on MS-DOS. </p> <p> Continued in <a href="https://dheinemann.com/posts/2021-08-15-building-replicalc-part-3">Part 3</a>. </p>Building Replicalc Part 1: The Plan2021-08-01T00:00:00+10:002021-08-01T00:00:00+10:00https://dheinemann.com/posts/2021-08-01-building-replicalc-part-1 <p> In late May, I decided to sit down and finally learn C. This has been a goal of mine for some time, but in the past I hadn't gotten much further than trying a handful of exercises from K&R. This time I had a specific project in mind: <a href="https://dheinemann.com/replicalc">Replicalc</a>, a REPL-based calculator for Linux, MS-DOS, and Windows 3. This series is a short retrospective about the journey to the first Replicalc release, 0.1.0, which took a little over a month. </p> <div class="blurb"> Articles in this Series: <ul> <li>Part 1: The Plan (you are here)</li> <li><a href="https://dheinemann.com/posts/2021-08-08-building-replicalc-part-2">Part 2: The Calculator</a></li> <li><a href="https://dheinemann.com/posts/2021-08-15-building-replicalc-part-3">Part 3: Porting to MS-DOS</a></li> </ul> </div> <h2>But Why?</h2> <p> Lots of reasons! </p> <p> I often keep a TI-83 calculator by my desk. I don't <em>need</em> a graphing calculator, but I like its REPL-based interface. I can type a whole equation with multiple operators, and evaluate it only when I press the Enter button. I'd love to be able to do this on my computer. There are a couple of options out there, but I'd rather develop my own. Can't be too hard, right? </p> <p> So why support MS-DOS and Windows 3? I grew up with those operating systems, but was too young to be able to develop for them at the time. It's something I've always wanted to do, and it's still possible today even if the rest of the world has moved on. And thanks to Windows' backwards compatibility, it also allows me to target Windows 10 with little additional effort. </p> <p> And why C? There aren't many other options when you want to target MS-DOS, and C is both well-supported and well-documented across the platforms I want to use. Also, Charles Petzold's book, <em>Programming Windows 3.11</em>, is primarily written for C, and will be useful when I eventually add a Windows GUI in the future. </p> <h2>Getting Started</h2> <p> The first thing I needed to do was figure out the most recent version of C that I would be able to use. C99 has some nice quality-of-life features, however, none of the MS-DOS compilers support it fully. That left C90. </p> <p> Next, I needed compilers. My computer runs Fedora, which ships with GCC, so that's an easy choice. For MS-DOS, there are a few options. I went with Open Watcom since I already had some previous experience from Jim Hall's <a href="https://www.freedos.org/books/cprogramming/">C programming tutorial</a>. </p> <p> For an IDE, I like to use Visual Studio Code. It has decent C support, including debugging. The debugger obviously doesn't support MS-DOS, but it would allow me to iron out most of the teething issues during the initial Linux build before porting to MS-DOS later. </p> <p> Finally, I would need a way to test the code in DOS later on. I decided to use DOSBox rather than a VM. DOSBox is great for running games, but I heard it wasn't recommended for software. I decided to use it anyway because it would make it very easy to share and compile my source code inside the "guest". Otherwise I would need to set up SMB shares and configure networking for a VM. As it turns out, DOSBox did cause some issues during development (more on this later) but it still paid off in convenience. </p> <p> Continued in <a href="https://dheinemann.com/posts/2021-08-08-building-replicalc-part-2">Part 2</a>. </p>Announcing Replicalc2021-06-26T00:00:00+10:002021-06-26T00:00:00+10:00https://dheinemann.com/posts/2021-06-26-announcing-replicalc <p> Today marks the release of Replicalc v0.1.0, a REPL-based calculator for programmers. </p> <div class="window" style="max-width: 640px"> <div class="innerBorder"> <div class="titleBar"> <div class="windowButton menuIconSmall"></div> <div class="title">Screenshot</div> <div class="windowButton minMax"></div> </div> <div class="content" style="max-width: 640px; max-height: 400px;"> <img src="https://dheinemann.com/assets/rcalcdos.png" alt="Replicalc running in MS-DOS" class="autosize"/> </div> </div> <div class="corner topLeftCorner"></div> <div class="corner topRightCorner"></div> <div class="corner bottomLeftCorner"></div> <div class="corner bottomRightCorner"></div> </div> <p> I've been working on Replicalc for the last month or so, as a hobby project to learn the C programming language. I've learned a lot, and reached the point where Replicalc is ready for an initial release. It's going to be buggy, and will crash with unexpected input. If I've seriously screwed up somewhere, then the results may even be incorrect. However, it runs happily on both Linux, MS-DOS, and Windows 3 onward! </p> <p> In future I plan to add a Win16 UI for Windows, and GTK UI for Linux. </p> <p> Further details, Windows releases, and source code are available on <a href="https://dheinemann.com/replicalc">the Replicalc page</a>. </p>Win16 Programming Resources2021-06-23T00:00:00+10:002021-06-23T00:00:00+10:00https://dheinemann.com/posts/2021-06-23-win16-programming-resources <img src="https://dheinemann.com/assets/win3logo.png" alt="The Windows 3 logo" style="float: right; margin-left: 10px;"/> <p> 'Win16' is the colloqial name for the Windows API that shipped with Windows 3.11 and earlier. It's still supported to this day, even on Windows 10 - but only for 32-bit software and greater. Your 16-bit software is going to require DOSbox or a VM. </p> <p> I want to try my hand at Win16 programming some time, starting with a Win16 interface for <a href="https://git.sr.ht/~dheinemann/Replicalc">Replicalc</a>. However, there is very little surviving information online. This list represents everything I have been able to collate so far, primarily from a C perspective. </p> <strong>Compilers</strong> <ul> <li><a href="http://openwatcom.org">Open Watcom</a></li> <li><a href="https://digitalmars.com/features.html">Digital Mars</a> ($$$)</li> <li><a href="http://www.delorie.com/djgpp/">DJGPP</a></li> <li><a href="http://blog.ssokolow.com/archives/2017/10/19/useful-info-on-win16-targeting-compilers-and-a-list-of-resources/">Useful Info on Win16-Targeting Compilers... And a List of DOS/Win16 Resources</a></li> </ul> <strong>API Documentation</strong> <ul> <li><a href="https://www.cplusplus.com/reference/clibrary/">C Standard Library Documentation</a></li> </ul> <strong>General</strong> <ul> <li><a href="https://www.transmissionzero.co.uk/computing/win16-apps-in-c/">Building Win16 GUI Applications in C</a></li> <li><a href="https://chenhuijing.com/blog/building-a-win31-app-in-2019/">Building a Windows 3.1 Application in 2019</a></li> <li><a href="https://ecs.syr.edu/faculty/Fawcett/handouts/coretechnologies/WindowsProgramming/Win32Prog/Don_Hobson/Don_Hobson.htm">An Introduction to Windows Programming</a></li> <li><a href="http://www.blong.com/Conferences/BorConUK97/WinAPI/Api.htm">Using the Windows API in Delphi</a></li> <li><a href="https://cmpct.info/~calvin/Articles/Win16.htm">Win16 for Fun and Profit</a></li> </ul> <strong>Emulation</strong> <ul> <li><a href="https://medium.com/hackernoon/win3mu-part-1-why-im-writing-a-16-bit-windows-emulator-2eae946c935d#.afzijuf4b">Why I'm writing a Windows 3 Emulator</a></li> </ul> <strong>Reverse Engineering</strong> <ul> <li><a href="http://blog.ssokolow.com/archives/2018/12/02/resources-for-reverse-engineering-16-bit-applications/">Resources for Reverse-Engineering 16-bit Applications</a></li> </ul> <strong>Books</strong> <ul> <li><a href="https://www.amazon.com/Programming-Windows-3-1-Charles-Petzold/dp/1556153953/">Programming Windows 3.1 by Charles Petzold</a> ($$$)</li> </ul> <strong>Videos</strong> <ul> <li><a href="https://www.youtube.com/watch?v=JlZe2JwrJqM">Hello Windows! RetroCoding "Hello World" for Windows with Dave</a></li> </ul> <h2>Honourable Mentions</h2> <p>(Resources not directly related to Win16 programming)</p> <strong>DOS Programming</strong> <ul> <li><a href="https://github.com/balintkissdev/awesome-dos">Awesome DOS</a></li> </ul> <strong>Win32 Programming</strong> <ul> <li><a href="https://web.archive.org/web/20051231182002/http://www.iseran.com/Win32/FAQ/">Frequently Asked Questions About Win32 Programming</a></li> <li><a href="https://www.amazon.com/Programming-Windows®-Fifth-Developer-Reference/dp/157231995X/">Programming Windows by Charles Petzold</a> ($$$)</li> </ul> </main>