The Museum of HP Calculators

HP Articles Forum

[Return to the Index ]
[ Previous | Next ]


A Unix Geek Rediscovers the HP-41

Posted by Howard Owen on 5 June 2005, 8:08 p.m.

The Good Ol' Days

Back in 1982, I was attending Santa Barbara City College studying first year chemistry. I discoverd the HP-41C in the campus bookstore and fell in love with it. I scraped some tutoring money together to buy the basic model and started playing with the machine. I learned RPN and programming at the same time. I'm sure the experience warped my impressionable student's brain, because for several years afterwards, I couldn't really grasp a difficult algorithm or programming challenge without recoding it, at least in my head, into the HP-41C's register oriented, assembler-like programming language. As further evidence of the HP-41C's insidious influence, I switched majors to Computer Science within a year of obtaining the calculator. I attribute this partly to the fun and success I had programming the 41-C.

Way Too Much Time Passes

23 years later, in 2005, I had become a successful Unix/Linux geek with 20 years of experience. I was still in love with gadgets, as any self respecting geek must be, but the old 41-C was long gone, having been stolen from me while I napped under the shade of a campus tree back in the bucolic 1980s. My current gadget fascination was with the Zaurus line of Linux based handheld computers from Sharp. I was working on one of the many Linux distributions that had sprung up for those machines. This particular "distro", called pdaXrom, was based on the X11 windowing system used by larger Linux machines. Part of the attraction this held for me is that software that runs on the larger machines should be more easily ported to pdaXrom than to some of the other Zaurus systems that weren't so based. I was looking for something to port, and I noticed that the default calculator in pdaXrom was lame. (All calculators are lame, in one way or another, after you have gotten used to a top-of-line HP.) I started casting around for a better calculator with available source code, and quickly came across Eric Smith's Nonpareil. This software emulated many old HP machines, including the HP-41CV. (Eric now has the CX code working as well.) The software looked great while running, and it was based on recent Linux X11 toolkits, the very ones that pdaXrom implemented, as a matter of fact. This looked promising, and so it proved. It took me about two hours to whip up a script to build and package just the HP-41CV calculator for my first release to pdaXrom.

Fatal Attraction

Along the way, I felt that old, fatal attraction returning. Playing with Nonpareil's HP-41CV simulation was great fun, but frustrating as well. It lacked the tactile feedback of that great keyboard, for one thing. It was also slow tapping in keystrokes with a stylus or a mouse. I needed the real thing! Fortunately, there was this newfangled thing called eBay. Soon I had a real HP-41CX actually resting in my hands! I was delighted. I immediately set to work coding a card deck program on my old/new toy.

The Morning After

I quickly came to realize something that wasn't part of my rosy memory of the machine. Application programming on an HP-41 is hard. It really is more like assembler than any higher level language. Only, there's no macro facility or aliases for cryptic addresses like you get with a decent macro assembler. the 41-C's registers are numbered from zero to SIZE-1. In a large program, how do I remember which register I put the number of decks in? Was it in 12 or 22? Scrolling back through a long listing on the 41-C is painfully slow, at least for someone who has survived the last twenty years of growth in computer power, so searching for assignments that way was hard. Without a printer to view the whole listing, any program of non-trivial size quickly becomes difficult to manage on the calculator itself.

Searching for Solutions

The problem of connecting an HP-41C to a PC is an interesting one. But I'm still pursuing answers as I write this. More immediately, I came up with some ideas to get around the coding difficulty issue, assuming I was able to get text or compiled code exchanged between the calculator and my desktop. One idea is to define symbols for registers and labels using the standard syntax of the C preprocessor (cpp.) The symbols can then be stripped out by the C preprocessor before the text code is compiled for download to a real HP-41C or to an emulator. I like having the line numbers in my listings, because it makes it easier to work back and forth between the calculator and the listing. But when you are working in a text editor, there's no automatic renumbering when you delete and add lines. So I wrote a Perl script to do the renumbering. Finally, I wrote another Perl script to remove comments and line numbers from the output of the C preprocessor. I also added another one to add DOSish line endings to the output. And thereon hangs a tale.

Emulation: HP-41 on DOS on Linux

I use Jean-François Garnier's excellent EMU41 emulator in order to prepare the way for the day I plug in Cristoph Klug's clone of the HP 82973A HP-IL to PC interface. But JF's software is designed to run in MS-DOS. Not a problem! It works just fine using the great DOSEMU virtual machine for Linux, which includes the FreeDOS MS-DOS replacement software. As a bonus, the 41UC tools also work well in the emulated DOS environment.

An Example

The following program isn't intended to be the last word in card decks for the HP-41C. I wrote it to test out my preprocessing process before uploading it to EMU41. The first listing is the source form, complete with cpp definitions, comments and symbolic names for registers, labels and constants. The second is a portion of the file prepared for processing by 41UC.EXE into a form that EMU41 can load.

# Storage registers #define nSuits 00 #define nCards 01 #define Suit 02

# Labels #define rankLoop 00 #define suitLoop 01 #define Ace 02 #define tenJQK 03 #define Ten 04 #define Jack 05 #define Queen 06 #define Clubs 07 #define Hearts 08 #define Diamonds 09 #define stoSuit 10 #define addCard 11

# Character constants #define charZero 48 #define charOne 49 #define charA 65 #define charC 67 #define charD 68 #define charH 72 #define charJ 74 #define charK 75 #define charS 83

# Other variables #define deckRegisterOffset 15

01 LBL "INIDECK" 05 4 06 STO nSuits # number of suits 07 LBL rankLoop 08 13 09 STO nCards 10 LBL suitLoop 11 RCL nSuits # alpha code the suit 12 1 13 X=Y? 14 GTO Clubs # Clubs 15 RDN 16 2 17 X=Y? 18 GTO Hearts 19 RDN 20 3 21 X=Y? 22 GTO Diamonds 23 charS # ASCII S, Spades, by default 24 GTO stoSuit 25 LBL Clubs 26 charC # ASCII C 27 GTO stoSuit 28 LBL Hearts 29 charH # ASCII H 30 GTO stoSuit 31 LBL Diamonds 32 charD # ASCII D 33 LBL stoSuit 34 STO Suit # ASCII suit 35 RCL nCards # Encode the rank 36 1 37 X=Y? # Card is an ace 38 GTO Ace 39 RDN 40 9 41 X<Y? # card is 10, J , Q or K 42 GTO tenJQK 43 RDN # Card is 2-9 44 charZero # we can add 48 45 + # to get ASCII 46 GTO addCard

47 LBL Ace # Card is an ace 48 65 # ASCII A 49 GTO addCard

50 LBL tenJQK # 10, J, Q or K # Figure out which 51 RDN 52 10 53 X=Y? 54 GTO Ten 55 RDN 56 11 # Jack 57 X=Y? 58 GTO Jack 59 RDN 60 12 # Queen 61 X=Y? 62 GTO Queen 63 charK # Must be a King 64 GTO addCard

65 LBL Ten # 66 charOne # two characters 67 XTOA 68 charZero 69 GTO addCard

70 LBL Jack 71 charJ 72 GTO addCard 73 LBL Queen # Q 74 charQ

75 LBL addCard # Add the card to Alpha 76 XTOA # First add the rank in X 77 RCL Suit # Then the suit 78 XTOA

# Compute the storage register this card will go in.

79 RCL nSuits # Suits left 80 INT # It's a loop counter 80 1 # Subtract 1 for 81 - # the current suit 82 13 # this many cards in each remaining suit 83 * 84 RCL nCards # Cards left in this suit 85 INT # It's a loop counter also 85 + 86 deckRegisterOffset # Add this to the calculated address 87 + 88 VIEW X # Keep track of progess 89 ASTO IND X 90 CLA 91 DSE nSuits 92 GTO suitLoop 93 DSE nCards 94 GTO rankLoop

Here is the result of running the following pipeline on Linux with the above file as input. This example uses the bash shell.
% perl 41pp.pl < deck.txt  | cpp -P |  perl crlf.pl | head -10
LBL "INIDECK"
4
STO 00
LBL 00
13
STO 01
LBL 01
RCL 00
1
X=Y?

The first command in the pipeline is the perl script whose source is given below. It strips out all comments except for the #define lines that the c preprocessor will use. The script also removes line numbers and leading spaces from the pipeline

The next command in the pipeline is the C preprocessor. We give it the -P option, which causes it to not emit certain control information that it normally does for the gcc compiler. The cpp processes the #defines replacing the first given text with the second. This strips the symbols from the source, replacing them with the correct numeric equivilants.

Next we run the data through a filter that ensures that the line endings are in DOS format. Then we finish up by piping the output through the head command, to give us a look at the first ten lines. Normally we would redirect the output to a file, which would then be ready to feed to HP41UC.EXE for conversion to whatever format we wanted.

Here are the two Perl filters from the above pipeline:

#!/usr/bin/perl
# 41pp.pl
# This script is Free Software. You may redistribute it under the terms 
# of the GNU GPLv2 as found at http://www.fsf.org/licensing/licenses/gpl.txt

while(<>){ chomp; # remove the newline s/\r$//; # and the CR too. print "$_\n" if (/^\#\s*define/); # special case these comments next if (/^\s*$/ or /^#/);# Skip blank or comment lines s/^\d+//; # Strip line numbers s/^\s+//; # Strip any leading space s/\s*#.*//; # Strip trailing comments print "$_\n"; }

#!/usr/bin/perl # crlf.pl # This script is Free Software. You may redistribute it under the terms # of the GNU GPLv2 as found at http://www.fsf.org/licensing/licenses/gpl.txt

while(<>){ chomp; # remove any newline s/\r$//; # and any CR too. print "$_\r\n"; # Put them back the way we want }

Password:

[ Return to the Message Index ]

Go back to the main exhibit hall