Skip to main content

HP-41CX Emulator Tool - GLOBAL command

When working on the Decoder/Encoder, the so-called GLOBAL commands were the most tricky to understand. There is plenty of documentation, but none specifically for those who write an encoder (also known as a compiler).

Definition #

GLOBAL commands consist of a group comprising global LBL and the two END commands, as defined by documentation describing the internal workings of an HP-41C.

Global LBL #

Alphanumeric labels are GLOBAL—with a few exceptions. The single-character variants LBL A to LBL J and LBL a to LBL e are not actually global labels. This is probably for compatibility with the HP-67, the predecessor of the HP-41C. Those single-character labels are also automatically assigned a key on the USER mode keyboard.

Every program should start with a global label. This is not enforced, but it is good practice, as sub-programmes without a global label can become orphaned, and accessing orphaned programmes is quite difficult.

END #

There are two types of END: the intermediate END that separates programmes, and the final .END.. There must always be one final .END.. On the calculator itself, the FOCAL system ensures this. My encoder is not quite as sophisticated. Forget the final .END., and you will run into problems later.

An intermediate END can be added by the user—for example, with [XEQ] [ALPHA] [E] [N] [D] [ALPHA] on the calculator itself. An END should be followed by a global LBL, otherwise your programme might become orphaned.

There are also three more flags to further subdivide END commands: Packed, Compiled, and Private. Note that the encoder always sets those flags to false. However, when the --with-hex command-line option is used, the decoder shows them as comments.

How GLOBAL Commands Are Encoded #

All GLOBAL commands consist of 24 bits (3 bytes). Global LBL commands are then followed by the key assignment and label text. The data is bit-packed.

Bits Content
1 … 4 Always C16—marks the beginning of a GLOBAL command
5 … 7 Byte count for the linked list. Range is 0 … 6
8 … 16 Register count for the linked list
17 … 20 LBL: Start ALPHA; END: Stop & Private flag
21 … 24 LBL: ALPHA length; END: Packed and Compiled flag

Note: Most HP-41C documentation uses one as the base index, and I follow that. This includes the programme itself, as you can freely choose the base index in Ada.

Linked List #

The GLOBAL commands all form a linked list from the last element to the first. For easy access by the FOCAL interpreter, the last object must be right-justified in the last used register. If necessary, the final .END. is moved into a register of its own. The space between is padded with 0016.

Without proper placement of the final .END., the linked list cannot be traversed. The programme can still be loaded and executed, but CAT 1 and key assignments will not work.

The links of the list are relative and count the steps from the beginning of the current entry to the beginning of the previous entry. They are stored as 12 packed bits. Links are split into bytes and registers (7 bytes, 14 digits). The first three bits are the byte offset; the remaining 9 bits are the register offset.

Trivia: With just 9 bits for the register offset (capping offsets at 511 registers), the HP-41C could never fully exploit its theoretical 1024-register main memory. This elegant limitation birthed the extended memory “kludge” in the HP-41CX—a workaround that a simple 10-bit offset might have rendered obsolete.

LBL #

For LBL, the two bytes at bit positions 17 … 24 contain a standard string header consisting of the F16 marking the beginning of the string and the length byte. LBL commands are restricted to a maximum of 7 characters. One additional byte is added to the length for the optional key assignment byte, making the total range of valid values for the second byte 2 … 8.

END #

For END and .END., the two bytes at bit positions 17 … 24 contain four flags that describe the type of END. These flags are structured such that bits 17 and 20 are always 0 (ensuring bits 17 … 20 never form the hexadecimal F value used to mark the start of an Alpha string in LBL commands), while bits 21 and 24 are always 1 (ensuring bits 21 … 24 fall outside the valid length range of 2 … 8 for LBL strings). This distinct bit pattern occupies a different numerical range from the Alpha string header, allowing the FOCAL interpreter to reliably distinguish between END and LBL commands.

Bits Meaning Decoder
17 Always 02
18 Private P
19 Final .END. S
20 Always 02
21 Always 12
22 Packed Z
23 Compiled C
24 Always 12

Note: Bits 21 … 24 can also be all 02.

Final .END. #

As mentioned placement of the final .END. is crucial. Most program step are byte packed. While editing programs the calculator add null operations 0016. These are completely ignored executed by the FOCAL interpreter. This is unlike the zero character 00F0 NOP which has side effects. You can pack the code with `GTO.." which remove the 0016 bytes.

However, the final .END. is always placed in the least significant 3 bytes of the final program fregister pointed to be ’.END. ABS.ADR.’ (3 least signifcant nibble of regiser 00D16). This way the interpreter can find the final .END. without time consuming scan operations.

The most signifcant bytes can contain other programm steps or be padded with 0016. You can see this in the DF41 memory dump of the GRAVITY game:

DM41
08  4b000000000000  00000000000000  00000000000000  00000000000000
0c  0000000000019c  1a70016919c175  0000002c048000  00000000000000
174  00000000000000  00000000c8262d  93021036ba0000  402143668e2142
178  225140668e5222  8e042126421242  0010362266ba00  452e2e2e2163b4
17c  45502053504143  06ba0003fd4445  12437120424092  40ce0220512442
180  aa001330244222  00431371457502  40542467b200a9  a80066b2005222
184  42124222517141  42134321413426  2345759303207a  2e2e7e897f3061
188  77f75a45524f2e  4e452e2e2e7e89  2e2e2e7e89f64f  2e7e89f654574f
18c  54485245452e2e  7e892367b300f8  3d9b06f37f2046  537e894585f241
190  9b71f57f20462f  45f27f46f27f3d  226e261a15f156  454c3d9b037e89
194  899c000af54655  9c02a91d9b017e  10322533f2473d  101010361c1510
198  10104035cf6615  61181010421112  56495459a81b31  c000f800475241
A: c000f800475241  B: 0000002c0480fd  C: 0000002c0480fd
S: 00001100000000
M: 00011cd5ff73cb  N: 000000000000c0  G: 00

The final .END. (c8262d16) is in memory location 17516. The most significant bytes of 17516 as well as the two least significant bytes of 17516 are padded with 0016.

On the HP41C and it’s emulators this is done automatically.

Example #

The following example is taken from the PX-41CX and decoded with --with-hex, which adds flags, the offsets for the linked list, and the hex code as comments to the output. This programme has no useful function. It only shows the use of LBL and END.

; STACK =  1.000000000E+00  1.000000000E+00  0.000000000E+00  0.000000000E+00  0.000000000E+00
; ALPHA = ⊤END⊤
; SIZE =  100
; ΣREG =  11
; FLAGS =  0000 0000 0000 0000 0000 0000 0010 1100 0000 0100 1000 0000 0000 0000
; A: 09999999999000  B: 0000002c0480fd  C: 000000002c00fd
; S: 00100010000000
; M: 000018a5ff83d3  N: 000000000c14c0  G: 00
LBL ⊤GLOBAL1⊤           ; Begin                 000: c000f800474c4f42414c31
123                     ;                       016: 111213
ENTER↑                  ;                       01c: 83
STO 01                  ;                       01e: 31
RTN                     ;                       020: 85
LBL ⊤LOCAL1⊤            ; (R:-2,S:-3)           022: c602f7004c4f43414c31
1                       ;                       036: 11
ENTER↑                  ;                       038: 83
2                       ;                       03a: 12
ENTER↑                  ;                       03c: 83
3                       ;                       03e: 13
*                       ;                       040: 42
+                       ;                       042: 40
RTN                     ;                       044: 85
END                     ; –––– (R:-2,S:-4)      046: c80209
LBL ⊤GLOBAL2⊤           ; (R: 0,S:-3)           04c: c600f800474c4f42414c32
1                       ;                       062: 11
ENTER↑                  ;                       064: 83
LBL 01                  ;                       066: 02
2                       ;                       068: 12
ENTER↑                  ;                       06a: 83
LBL A                   ;                       06c: cf66
5                       ;                       070: 15
ENTER↑                  ;                       072: 83
GTO 01                  ;                       074: b200
XEQ A                   ;                       078: e00066
8                       ;                       07e: 18
ENTER↑                  ;                       080: 83
LBL ⊤LOCAL2⊤            ; (R:-3,S:-6)           082: cc03f7004c4f43414c32
⊤ABCDE⊤                 ;                       096: f54142434445
GTO 01                  ;                       0a2: b200
RTN                     ;                       0a6: 85
END                     ; –––– (R:-2,S:-5)      0a8: ca0209
.END.                   ; S––– (R: 0,S:-4)      0b0: c80020

Note: The double END at the end appeared after packing the programme with GTO... You should not actually type them in, and if you recompile the programme with the encoder, I suggest deleting the extra END.

Terms #

Term Description
Bit Single binary digit containing 02 or 12
Nibble String of 4 bits. Same as a decimal or hex digit.
Byte String of 8 bits.
Digits A single number. Size depends on the number base. If base is not specified on the HP-41, it is 4-bit decimal (BCD).
BCD Binary coded decimal. A way to encode a decimal number in a nibble.
Binary digit Same as Bit.
Hex digit Same as Nibble.
Step A physical programme step consists of 8 bits / 1 byte.