The Museum of HP Calculators
Some of the HP-41C programs in the Software Library use "Synthetic Programming". Synthetic programming is a way of entering program codes that can't be entered directly from the keyboard.
Many of these instructions were intentionally left out because used incorrectly they can confuse the calculator and cause either a lockup or a "Memory Lost" state. (If you get into the former state, you may need to remove the batteries to reset the calculator.)
You should backup anything important before first engaging in Synthetic Programming.
HP-41 instructions are one or more bytes long. The calculator will only allow certain sequences, but with the tool below, you'll be able create new sequences by entering code that the calculator allows and then grabbing bytes out of this code to create different instructions. These new sequences allow access to additional characters, additional sounds, more alpha editing commands, easy control of the calculator through direct access to system registers, etc. They make it possible to do new things and to reduce the size and execution time of programs. The byte grabber described below will allow you to remove individual bytes from programs you create so the bytes that remain are interpreted differently.
You can use the following steps to create a Byte Grabber. Make sure to follow the steps exactly. If it doesn't work the first time, try again. Note that the first step is a master clear so save anything important to cards, tape, or disk now!
The byte grabber should now be assigned to the LN key. Press AND HOLD the LN key which should display XROM 28,63. Keep holding the key until the calculator displays NULL because you don't want to execute the byte grabber now. This would be a good time to save the calculator's status on a card by pressing XEQ ALPHA W S T S ALPHA in case you accidentally destroy the byte grabber later.
Be careful in using the byte grabber. You may get a "MEMORY LOST" or lock the calculator if you use it incorrectly. If the latter happens, remove the batteries for a few seconds and return them. If that doesn't work, try turning the calculator on a few times with the batteries out or leave them out for several hours.
The byte grabber is used in program mode to capture a byte into a text string which may then be deleted. The byte grabber grabs the first byte of the line FOLLOWING the line displayed. For example, key in the following program fragment in PRGM mode:
ENTER ; Press the ENTER key 1E-6 ; press EEX CHS 6
Even though you pressed just EEX CHS 6, the calculator insisted on prepending a 1 to normalize the number.
Now pack the program by pressing XEQ ALPHA P A C K ALPHA while still in program mode. Press BST to back up to the ENTER line. Make sure the calculator is in both PRGM and USER mode and press the LN key which is assigned to the byte grabber. Line 2 will become a strange looking text line. Press SST to see E-6 all alone.
The strange text line is not needed so press BST to return to it and press the back arrow to delete it. The program now consists of:
Which is functionally the same but one byte shorter and a little faster than the unaltered form.
The only purpose of the ENTER above and in the examples below is to provide a place to run the byte grabber since it always grabs a byte from after the line shown. (This is because the HP-41C inserts after the line shown.) In most real programs, you'll just use whatever instruction happens to be ahead of the byte you want to grab.
You can do more than just make programs a little shorter. You can create new TONEs, access internal registers and control calculator features, create characters that you otherwise couldn't etc.
The museum provides two tables to aid synthetic programmers. The first is a graphic byte code table (~40K) similar to those published in many books. In each cell, the first line shows how a byte is interpreted as a prefix and the second line shows how the same byte would be interpreted as a suffix. By grabbing the first byte of a multi-byte instruction, the suffix byte then becomes interpreted as a prefix byte. The bottom left part of each cell shows the byte in decimal. The graphic table also shows what bytes look like as characters on the printer and the display. The latter is omitted for bytes 80-FF because they are all starburst (all segments on) for these characters and the space is needed to show the suffixes.
The format each cell in the graphic byte table is shown below:
Alternately, you may prefer this HTML byte code table To keep things simple, it doesn't show the display or printer displays for each byte but it does have the advantage that you can use your browser's search feature to find prefixes and suffixes quickly.
If you wanted to create a new TONE 90 (decimal) instruction, you would find TONE on either byte code table at 9F and then you would find decimal 90 at 5A hex which corresponds to the COS instruction. Thus, you need to start with a program that includes IND 31 (byte 9F interpreted as a suffix) followed by COS. So, go into PRGM mode and enter:
ENTER ; just need some instruction to to run the grabber from STO IND 31 ; press STO 31 COS
The STO was chosen as a convenient way of entering the IND 31 which was what we really wanted. Now BST back to the ENTER and press LN to activate the byte grabber. Now press backspace the delete the strange text string left by the byte grabber.
With the STO prefix byte removed, the following 9F byte is no longer interpreted by the calculator as the suffix byte IND 31 but rather as the prefix byte TONE and since TONE needs a suffix, the following byte 5A is interpreted as a suffix 90 rather than a prefix COS. Pressing SST, you'd expect to see TONE 90 but because the calculator's firmware wasn't written to display multiple character TONE #'s, you'll see just TONE 0.
You really have a synthetic TONE 90 however. To prove it, exit program mode by pressing PRGM and press SST to execute the TONE. Now press XEQ ALPHA T O N E ALPHA 0 to hear a true TONE 0.
When you encounter an instruction in one of the library programs that can't be entered in the normal way, proceed as you did with the TONE instruction above.
Register d contains all 56 system and user flags. Being able to RCL and then STO it allows a program to change calculator modes for its uses and then return them easily to the user's preferences at the end.
To key in a RCL d, find RCL in the graphic byte code table or HTML byte code table at position 90. Note that the suffix interpretation of this byte is IND 16 so we'll enter a STO to allow us to enter the IND 16 and delete the STO later.
Now locate d as a suffix (2nd line of each cell) in the byte code table at position 7E. The Prefix code for this is AVIEW so we'll enter that after the IND 16. In PRGM and USER mode, enter:
ENTER ; This is just a code to execute the byte grabber from STO IND 16 ; STO 16 AVIEW ; ALPHA VIEW ALPHA
Now BST to the ENTER, press LN to execute the byte grabber and press the backspace to delete the resulting text string. The program now consists of:
ENTER ; you can delete this too if you like with backspace RCL d
Removing the STO code caused IND 17 to be reinterpreted as RCL and AVIEW to be reinterpreted as d. To STO d later, you would do use IND 17 instead of IND 16.
The value returned from the flags register can be safely stored on the stack but should not be stored in a numeric register. This is because the flags register is 56 independent bits that may not represent a valid number or string. Data stored in numeric registers is subject to normalization which forces it to be a valid number or string.
Register b holds the return stack and program counter. You can make a very small and fast infinite loop by entering:
ENTER ; just gives a place to start the byte grabber STO IND 16 MEAN ; XEQ ALPHA M E A N ALPHA STO IND 17 MEAN
Now BST to the Enter, press LN (byte grabber), and press backspace to remove the text string. SST to see RCL b. Now press LN an again remove the text instruction with backspace. The program now consists of:
RCL b STO b
Now BST back to the RCL b and exit program mode by pressing PRGM. Now press R/S. The program will run forever but the program indicator doesn't move because the calculator only moves it when labels are executed. Press R/S to stop it. (The contents of b will be in the X register which will give you a taste of what non-normalized data can look like.)
Now press SST to step through the program. Notice that the line number keeps growing even though you're looping back. The reason is the display code always increments the number unless it sees an instruction that would branch to a different location. Since the code doesn't expect you to be stepping through synthetic code, it does the wrong thing. Be prepared for interesting things like this when you use synthetic instructions. (Note also, that backstepping from these non-existent steps may confuse the calculator.)
You can use the byte grabber to synthesize characters that you can't enter from the keyboard including additional punctuation and lower-case characters.
Text instructions start with a byte of F0-F15. The first 4 bits specifies that what follows is a string and the 2nd 4 bits specifies the length. The F0-F15 bytes are prefixes and they are followed by 0-15 suffix bytes respectively. Removing a F# byte has the effect of causing the next # characters to turn into instructions.
The characters for the byte codes are shown in the right side of each cell in the graphical byte code table. Note that some characters print differently than they display--another result of HP not expecting users to discover this stuff.
To add synthetic characters to a text instruction, you create the text string in the required length, grab the leading F# byte which converts the string into instructions. Do not backspace over the grabbed text string in this case. Then you edit the instructions, to correspond to the characters you need referring to the byte code table.
To convert the edited instructions back to text, you use the byte grabber again. This time you position the program counter just before the text string created above. Using the grabber here causes it to grab the F7 text prefix off the old grabbed string. This causes the grabbed string to convert back to instructions and the last instruction is the text prefix from your original string. Thus, the instructions you edited turn back into a string with the characters you want.
One additional complication: The ? (byte code 3F) in the grabbed string turns into an extra STO 15. Backspace over this instruction to delete it and backspace over the text string created in the last grab. The dash instructions turned into NULLs that won't hurt anything but consume some extra space until the next time you do a PACK.
To create the text "INPUT #1" enter the following program
ENTER "INPUT X1" ; ALPHA I N P U T SPACE X 1 ALPHA
BST to the Enter and use the byte grabber (LN) but leave the text string that is created. SST through the instructions to find the X which will have become an E^X-1. Press backspace and then enter RCL 03 in its place. Now BST back to the ENTER and byte grab (LN) again. Now you can delete the new grabbed text line and the extra STO 15 and your program will look like:
ENTER "INPUT #1"
To better understand synthetic instructions, you need to know a little about the HP-41C's memory map which is shown below:
The calculator is designed to process and store Binary-Coded Decimal (BCD) numbers. Each BCD digit requires 4 bits to store. Each floating-point number in the HP-41C has 10 mantissa digits, 2 exponent digits and two signs. For simplicity, the signs are also given 4 bits of storage each. Thus, the calculator is designed around an array of 7 Byte (14 digit) registers. The numbers on the left side are register (not byte) addresses.
Don't confuse the absolute register numbers with the register numbers used in STO and RCL operations. The base address of the user registers is determined when the SIZE command is executed.
Notice that higher-numbered user registers are in higher addressed memory, but programs start from the top of their partition and execute "downward". Many synthetic instructions you'll see refer to the bottom of memory--the HP-41C's status registers which are shown in more detail below:
These registers are accessed using the addresses on the left. The bottom row shows how the registers will be interpreted when they are displayed. Remember that the data contained may not format as a normal number. The bottom 5 registers, are the user stack. Working upwards:
Consider the following synthetic program fragment:
X<>c ; exchange X with c setting the R00 base to X X<>Y ; get value to store in Y STO 00 ; store at location now pointed to by c X<>Y ; get the old value of c back into X STO c ; restore c before the calculator panics
The above program expects a value to store in Y and an absolute memory register address in X. It then swaps X with c which preserves c and sets the R00 base to X. (This is this easy because the R00 pointer happens to start at the base of the mantissa.) Note, however, that .END. pointer, sigma base register and Cold Start Constant have all been (temporarily) corrupted!
Next it gets the value to store in X and stores it at register 00 (which is now pointing wherever you want - such as a status register, program memory, key assignment area etc.) and then quickly restores the value of c before the calculator notices how messed up it is. (If you single-step this program, you'll get a "MEMORY LOST" after the first instruction.)
If you want to enter this powerful and dangerous program, you should know enough to do it. You'll want to start with a STO IND 78 to get the X<> and a following SDEV will turn into c when the STO is grabbed. For a more complete program for loading bytes anywhere is shown below.
The byte grabber is really just a string assigned to the LN key. When you entered the byte grabber, you made use of a bug in the HP-41 firmware--when you pressed the backspace in step 8 the calculator backed into the system area. When you pressed GTO .005, you positioned the calculator in the key assignment area. Because the calculator thought it was still somewhere in program space, you used programing instructions to replace the "+" key assignment to the LN key with 7 bytes that are inserted each time you press the byte grabber key (LN).
The byte grabber inserts one register (7 bytes) into the program which contains a 7-byte string of the form F7,00,3f,00,00,00,00. The F7 indicates a string with 7 characters (See the byte code table) but only 6 characters follow. As a result, the calculator interprets the first byte that follows as the last character of the string.
The 3f is the "?" you entered when assigning the key. You could use a different key if you like but it's best to select a byte that corresponds to a single byte instruction. ("?" corresponds to STO 15.) The "A"s you entered relates to the LN key so don't change them unless you wish to use a different key for the byte grabber and know the keycode mapping.
Do not use the byte grabber on the .END. instruction or in empty program memory.
The byte grabber is an amazing tool and great for times when you need a quick synthetic instruction or two. There are other times, however, when you'd like to enter many synthetic instructions and might prefer a more direct approach. Clifford Stern's byte grabber shown below will allow you to enter decimal byte codes directly into program memory. You're probably not surprised to find that this is a synthetic program so you'll need to use the byte grabber to create it.
The program is listed below, but to create it, start by entering the following non-synthetic instructions:
01 ENTER 02 STO IND 16 ; STO 16 03 MEAN ; XEQ ALPHA M E A N ALPHA 04 STO IND 17 05 RDN 06 STO IND L ; STO . L 07 CLD ; XEQ ALPHA C L D ALPHA 08 ENTER 09 ENTER 10 LBL 01 11 STO IND 78 12 RDN 13 STO IND 78 14 AVIEW ; ALPHA view ALPHA 15 STO IND 78 16 AVIEW 17 STO IND 17 18 RDN 19 STO IND 78 20 AVIEW 21 STO IND 78 22 AVIEW 23 STO IND 78 24 RDN 25 STO IND 17 26 LASTX 27 STO IND 78 28 LASTX 29 STO IND 78 30 SDEV 31 STO IND 17 32 SDEV 33 STO IND Y ; STO . Y 34 CLD 35 ENTER 36 STO IND 78 37 SDEV 38 STO IND 16 39 RDN 40 STO IND 17 41 SDEV
Now perform the following steps:
Now you've created all the synthetic lines you need. You should have:
01 RCL b 02 STO M 03 "" 04 X<> M 05 X<> d 06 X<> d 07 STO M 08 X<> d 09 X<> d 10 X<> M 11 STO N 12 X<> N 13 X<> c 14 STO c 15 "" 16 X<> c 17 RCL M 18 STO c
(The only exception is E4 which you should have no trouble creating or you can just leave it as 1E4.) Add additional non-synthetic lines to form:
01◆LBL 01 02 CLST 03 BEEP 04 STOP 05 GTO "++" 06◆LBL "LB" 07 FS? 50 08 GTO 02 09 1 10 ENTER↑ 11 ENTER↑ 12 CLA 13 CF 21 14 AVIEW 15 -10 16 GTO "++" 17◆LBL 02 18 7 19 / 20 INT 21 FIX 0 22 CF 29 23 ARCL X 24 " REGS." ; text append space R E G S . 25 TONE 8 26 AVIEW 27 PSE 28 RCL b 29 STO M ; STO [ when printed 30 "" ; no spaces in this string, "◆◆x" when printed 31 X<> M ; X<> [ when printed 32 X<> d 33 CF 04 34 CF 05 35 CF 06 36 FS?C 07 37 SF 05 38 FS?C 08 39 SF 06 40 FS?C 09 41 SF 07 42 FS?C 10 43 SF 09 44 FS?C 11 45 SF 10 46 FS?C 12 47 SF 11 48 X<> d 49 INT 50 DEC 51 1 52 + 53 .1 54 % 55 + 56 + 57◆LBL 03 58 1.007 59 ENTER 60◆LBL 04 61 " " ; just a space 62 ARCL Y 63 "?" ; no spaces in this string 64 AVIEW 65 STO M ; STO [ when printed 66 RDN 67 STOP 68 FC?C 22 69 GTO 05 70 OCT 71 E4 72 + 73 X<> d 74 FS?C 19 75 SF 20 76 FS?C 18 77 SF 19 78 FS?C 17 79 SF 18 80 FS? 15 81 SF 17 82 FS? 14 83 SF 16 84 X<> d 85 X<> M ; X<> [ when printed 86 "**" ; no spaces in this string 87 STO N ; STO \ when printed 88 ARCL Y 89 X<> N ; X<> \ when printed 90 ISG Y 91 GTO 04 92 SIGN 93 X<> c 94 LASTX 95 STO IND T 96 X<>Y 97 STO c 98 R↑ 99 DSE X 100 GTO 03 101 GTO 01 102◆LBL 05 103 "" ; no spaces in this string, "◆" when printed 104 ISG X 105 GTO 05 106 X<> c 107 RCL M ; RCL [ 108 STO IND Z 109 X<>Y 110 STO c 111 GTO 01 112 END
To allow you to load any byte code in any place, this program changes register c which is a very delicate operation. Check your entries carefully, and back up again. Writing this program to a card before trying it out is a good idea.
To use LB go to the location where you want to enter synthetic instructions and enter the sequence:
LBL "++" + + + ... + + + XEQ "LB"
The number of "+" lines should be 16 plus the number of bytes you are going to create. (The "+"s are used both to reserve space to be used by LB and they are executed to for counting so don't use a different key.)
If you have some spare cards, create a long sequence as above and record it. This will make using LB a lot faster. (Tapes, disks can serve too. Extended memory modules are very convenient but are subject to loss when you crash your calculator.)
Now press PRGM to switch out of program mode. If you left the program at the XEQ "LB" statement, you can just press R/S. Otherwise press XEQ ALPHA L B ALPHA.
LB will display the number of registers available for loading (there are 7 bytes per register.) LB then starts prompting with the byte number within the current register. Key in the decimal numbers from either the graphic byte code table or HTML byte code table and press R/S. Note that this program keeps critical data on the stack while prompting you for bytes, so don't disturb the stack by pressing ENTER or doing calculations.
When you are done, just press R/S without making an entry. If you run out of space for insertion, LB will stop prompting you.
For example, to enter a program containing RCL d, ST* M, and X<> c, enter the "++" sequence above with at least 22 "+"s, exit program mode and execute LB: Your entries are shown below:
1 REGS. ; no entry here - just tells you have one reg (assuming 22 +s) 1? 144 R/S 2? 126 R/S 3? 148 R/S 4? 117 R/S 5? 206 R/S 6? 125 R/S 7? R/S
After the LB program stops, you can press SST and switch to PRGM mode to see the LBL "++" followed by some remaining "+"s and your instructions. Now you can remove any extraneous "+"s (and the LBL "++") with backspace or DEL and insert any non-synthetic instructions that are needed to complete the program. (Don't execute this code fragment by itself.)
Looking at either table you'll notice some interesting features of the instruction set. There are general forms of instructions like RCL and STO at hex positions 90 and 91. Either of these is followed by a byte that contains the register number (BCD-encoded). However, there are also short forms of RCL and STO for registers 0-15 at positions 20-3F. These short forms for common references reduce program size. This is also true for the LBL instruction.
The GTO, XEQ and W instructions at 1D-1F are followed by text strings representing (GLOBAL) alpha labels. For example, GTO "TEST" would be encoded (in hex) as 1D, F4, 54, 45, 53, 54.
CO-CD are GLOBAL label instructions and ENDs and each takes three or more bytes. The first nibble identifies the instruction as a global and the next three digits indicate the distance to the next global. (Globals are chained together in this way so they may be quickly found.) If the third byte is a text prefix (F0-F15) then the global is a label, otherwise it's an END. In the former case, the key assignment byte (or zero if unassigned) and the label characters follow. (The text length includes the key assignment byte.)
Instructions F0-FF are prefixes for byte strings with the number of characters to follow specified in the second nibble (e.g., F4 would be followed by 4 characters.)
XROM has been shortened to XR in the graphic byte table. XROM instructions refer to functions in plug-in ROM modules. When a program refers to a function in a ROM that is not plugged in the reference will display as XROM i, f where i is the ROM identity number and f is the function number. (A few modules have two IDs.) The XROM is encoded as the nibble A followed by two 6 bit numbers which encode the numbers in binary (not BCD). The ID is restricted to 0-31 but the function number uses the entire 0-63 range. Giving ROMS IDs made them slot-independent.
B1-BF and D0-DF encode two- and three- byte GTOs respectively. For all of these, only the first nibble is required to identify it as a GTO instruction. Two-byte GTOs are available for labels 00 to 14. The first byte specifies that this is a GTO and the label. A second byte follows (initially zero) that the calculator will later fill in with the distance to the target. This speeds execution later. (If the target is not within the distance that can be encoded in the byte, execution is slower.)
Three-byte GTOs start with a BYTE of D0 (208 decimal), a byte of zero, and a byte containing the label. In this form of GTO, the second nibble of the first byte and the entire second byte are available for holding the distance to the target. Because the second nibble is filled in by the calculator, three-byte GTOs are shown as taking byte codes D0-DF. The three-byte XEQs (E0-EF) work in the same way and are entered as E0, a byte a zero, and a byte containing the label.
Prefix AE is unusual in that the suffix actually determines the meaning of the prefix. If the suffix's highest order bit is zero, then the function is interpreted as a GTO IND suffix. Otherwise, the function is interpreted as XEQ IND suffix (but with the high bit zeroed since you couldn't use a register above 100 decimal anyway.)
The instructions above assume you have just the basic calculator. To make these tasks a little easier, you should find either a PPC ROM, CCD ROM or a ZENROM and read their user's manuals. These ROMs include synthetic programs that can make the entry of synthetic programs more convenient.
This text is not designed to fully explain synthetic programming or the internal architecture of the HP-41C. This is just enough to help you to enter programs that use synthetic programming and to give you an idea of what they're doing.
If you do wish to pursue synthetic programming further, there are several books on the subject including:
The first five were used as references for this page.
This text drew heavily on books listed above which in turn drew on the works of: Jack Baldrige, Erwin Gosteli, Roger Hill, Clifford Stern, Phi Trinh, Bill Wickes and others.
The load bytes program was written by Clifford Stern, published in "HP-41 Synthetic Programming Made Easy" and is used here by permission.