# Complex numbers on the HP-41

Scientific calculator software to introduces complex number operations on the HP-41. Easy to use. Runs in extended memory. Complex numbers are commonly used to simplify the analysis of linear circuits in electrical and mechanical engineering. This program makes complex arithmetic as easy as working with normal numbers on HP-41CV and HP-41CX calculators.

## Features

• Ease of use
• Displays the whole complex number at once
• 15 mathematical, 12 trigonometric, 12 hyperbolic and 10 stack manipulation functions.
• Supports both rectangular and polar mode
• Stack mechanism identical to the normal stack
• Requires only 5 registers of main memory (assuming extended memory is used).

## History

In the early 1980’s, I started a complex arithmetic library in Oxford Pascal for the Commodore-64. After hacking up a RS-232 level adjuster for the Commodore-64, I transferred the library to the Pr1me computer on campus. From there, this program grew with me through college.

This program is a variation on Frans de Vries’ Complex Arithmetic program [1]. I moved the code to extended memory and made it more intuitive. The program now shows the result as a condensed complex number and makes the R/S button behave like a COMPLEX prefix to operations.

Twenty-five years later, I restored my faithful HP-41 and use the same program once more. This version is also verified to run under Windows using Warren Furlow’s V41 simulator when compiled with Leo Duran’s hp41uc.

## Entering the program

The program consist of two parts. The first part labeled CA fits in a mere 5 registers of main memory. It handles the initialization and then jumps to the second part labeled X. This second part should be placed in extended memory, but can also be used in main memory.

Start by freeing up some program memory (SIZE 014). Enter both parts of the program using a wand, magnetic cards, HP-IL or type it in. Pack it (GTO ..), and test the program with XEQ “CA”. This clears the complex stack, initializes relevant flags and angular mode. It then shows the value at the top of the complex stack and waits for input. Assuming all went well, move part 2 to extended memory (“X” SAVEP) and remove line 019 from part 1.

Start the program using XEQ “CA”. Again, this clears the complex stack and initializes flags and angular mode, but this time it will jump to extended memory. It displays the top of the complex stack (Z1), and wait at line 013 where. At this point, you have the option of entering an operand or calling a operation. At the end of each operation it will return to line 013.

Note that it is not possible to change the program while in extended memory. Once the program executes in extended memory, the bytes near the GTO and XEQ instructions will be changed to match to the corresponding LBL address. This would cause the check sum of the fill to mismatch. For more details on running a program in extended memory, refer to the book “Extend your HP-41” [6].

## Usage

The program makes complex number arithmetic very similar to working with normal numbers. Numbers are entered and returned through a mechanism very similar to the regular stack. The calculating environment consists of 6 complex stack registers, numbered Z1 thru Z6, and a complex LASTZ1 register. The large complex stack reduces the need for store and recall functions.

The keyboard overlay shown below, gives an overview of the complex number operations.

### Operations

Complex operations are called by pressing COMPLEX (the R/S key on the normal keyboard) followed by the desired operation key. In the overlay, the complex operations can be identified by the blue paint splash. In this article, these complex operations are shown in a blue box.

After each operations, the result of the operation is stored on the complex stack as Z1, and shown on the display in a condensed format. To see the full accuracy, press X<>Y to see the imaginary part (or phase in Polar mode), and X<>Y again to see the real part (or modulus in Polar mode).

Most operation require one or two operands from the stack. Exceptions to this rule are functions that take a real number argument (Zn, Z1/n, nZ, VIEWZn, Z1 Zn), the mode switches (RECT, POL) and prefixes (g, ARC, HYP).

The function Z1/n, is a bit different. It will return multiple answers: the nth roots of the complex number in Z1. The ALPHA annunciator indicates that more roots are available. Press COMPLEX to show the next root. If you leave ALPHA-mode to see the root in X and Y in full accuracy, be sure to restore the order of the stack before pressing the R/S key to see the next root. When all roots are displayed, the first root will be available as Z1 on the complex stack (and X,Y on the regular stack).

The use of the prefixes ARC and HYP is similar to the use of the shift key, with flag 03 and 04 acting as the annunciator for ARC and HYP respectively. They can be used in any combination to denote and execute the desired function. The state of these flags only affects trigonometric operations. To squeeze the program (866 bytes) into the x-memory, the operations ATAN, ATANH, ACOT and ACOTH had to be removed.

Note that operations can anywhere between 1 and take up to 6 seconds to execute. Also, BCUT is only implemented in “Complex Arithmetic with adjustable branch cut for HP-41cv/cx“.

### Display

After each operations, the result of the operation is stored on the complex stack as Z1, and shown on the display in a condensed format. To see the full accuracy, press X<>Y to see the imaginary part (or phase in Polar mode), and X<>Y again to see the real part (or modulus in Polar mode).

Both rectangular coordinates and polar form are supported for data entry and viewing. To switch between modes, use the RECT and POL operations. These operations also recall Z1 into the normal stack. Flag 0 shown on the LCD display serves as an annunciator for polar mode.

### Operands

A few operations, such as Zn, Z1/n, nZ, VIEWZn and Z1 Zn require a real number operand in the X register. Most other operations use a complex number operand.

Complex numbers can either be entered on the regular stack as an X,Y pair on the normal stack. In rectangular mode, the real part of the complex number in should be placed in X register and the imaginary part in Y. In polar mode, the magnitude should be in X and the phase in Y.

If no complex number is entered on the normal stack, the operation will take the value from the complex stack.

### Error handling

The program has no accommodation (apart from the SF 25 at line 040 in “X”) to avoid error halts. This implies that attempts to divide by zero, take the log of zero, etc. will usually cause a DATA ERROR halt. Pressing INIT lets the program return to its normal halting point at line 013 in “X”.

## Examples

The following examples assume a ENG 3, SF 28 and CF 29 display mode.

### Calculate $$(2 + 3j)(7 – 6j) + (4 + 5j)$$

$$(2 + 3j)(7 – 6j) + (4 + 5j)$$
Key strokes Display
input display
XEQ “CA” 0.00E0+0.00E0J
3 ENTER 3.000
2 COMPLEX ENTER 2.00E0+3.00E0J
6 CHS
ENTER -6.000
7COMPLEX * 32.0E0+9.00E0J
5 ENTER 5.000
4 COMPLEX + 36.0E0+14.0E0J
X Y 36.000
X Y 14.000

The answer is $$36 + 14j$$

### Calculate $$(3 + 4.5j)^5$$

$$(3 + 4.5j)^5$$
Key strokes Display
input display
4.5 ENTER 4.500
3 COMPLEX ENTER 3.00E0+4.50E0J
5 COMPLEX ZN 926.E0-4.53E3J
X Y -4.533 03
X Y 926.4 00

The answer is $$926.4 – 4.533j$$

Show what is in the LASTZ1 complex register

LASTZ1
Key strokes Display
COMPLEX LASTZ1 3.00E0+4.50E0J

Note that the complex number is saved, not the power 5

### Calculate $$\frac{1}{3\ \angle 30^o}$$

$$\frac{1}{3\ \angle 30^o}$$
Key strokes Display
COMPLEX POL 5.41E0<56.3E0
40 ENTER 40.00 00
3 COMPLEX 1/Z 333.E-3<-40.0E0

The answer is $$0.333\ \angle{-40}^o$$

### Calculate $$asinh(8 – 5j)$$

$$asinh(8 – 5j)$$
Key strokes Display
COMPLEX RECT 255.E-3-214.E-3J
5 CHS
ENTER -5.000 00
8 COMPLEX ARC
HYP SIN 2.94E0-556.E-3J

The first answer is still there

 input display 5 COMPLEX Z1 Zn 94E0-556.E-3J

### Calculate $$\sqrt[3]{5 + 3j}$$

$$\sqrt[3]{5 + 3j}$$
Key strokes Display
3 ENTER 3.000 00
5 COMPLEX VIEW 5.00E0+3.00E0J
3 COMPLEX Z1/n 1.77E0+322.E-3J (alpha)
COMPLEX -1.16E0+1.37E0J (alpha)
COMPLEX -606.E-3-1.69E0J (alpha)
COMPLEX 1.77E0+322.E-3J

### Calculate $$csch(1 + 2j)$$

$$csch(1 + 2j)$$
Key strokes Display
2 ENTER 2.000 00
1 COMPLEX HYP CSC -489.E-3+1.40E0J

### Calculate $$\ln(4 + 5j) + (3 – 2j)^{(2 – 3j)}$$

$$\ln(4 + 5j) + (3 – 2j)^{(2 – 3j)}$$
Key strokes Display
5 ENTER 5.000 00
4 COMPLEX LN 1.86E0+896.E-3J
2 CHS
ENTER -2.000 00
3 COMPLEX ENTER 3.00E0-2.00E0J
3 CHS
ENTER -3.000 00
2 COMPLEX Z2Z1 -1.79E-3-3.19E-3J
COMPLEX + 1.85E0+893.E-3J

### Calculate $$2^{3+4j}$$

$$2^{3+4j}$$
Key strokes Display
4 ENTER 4.000 00
3 COMPLEX ENTER 3.00E0+4.00E0J
2 COMPLEX NZ -7.46E0+2.89E0J

The number $$N$$ must always be the last one that is entered.

## Tweaks

• The input/output angular mode for polar coordinates is set in line 004 of program “CA”, Default is DEG, change to RAD for radians.
• The size of the complex stack can be adjusted in line 008 of program “CA”.
• If flag 14 is set when calling “CA”, the stack will not be cleared, which may be useful if you left “CA” temporarily and want to continue. This however assumes that the data registers used for the complex stack are left undisturbed.
• The program uses flag 22 “numeric entry” to determine if there is a new entry in X,Y. If there is a numeric entry, the number is first pushed onto the complex stack, respecting stack lift status. This implies that you can clear flag 22 manually if you have made an accidental digit entry and still want the next operation to be performed on the current Z1.
• Operands that require a real number input, will always take that number from the X register, new entry or not. If stack lift is disabled, the stack will first be shifted down, so the function will be performed on Z2. This is done, so that you may have ENTER ‘ed the complex number on which you want the function to be executed, and without the shift down there would be left a superfluous copy of that number on the stack.

## Source code

Ángel Martin combined several of my focal programs from this site and compiled ROM and MOD images. They are available through GitHub. Note that this program consists of two parts.

1. Part 1 should be placed in main memory and is available as source code, raw for the V41 emulator and barcode for the HP Wand (HP82153A)
2. Part 2, can be placed in either main or extended memory. When placed in extended memory, the instruction GTO “X” should be removed. This part is available as source code, raw for the V41 emulator and barcode for the HP Wand.
3. Requires X-Functions module on the HP-41cv
4. The keyboard overlay is available as image and inverted. These are scaled down by 98% so when printed on 4×6″, it typically comes out as true-size.

Available through the repository

### part 1

; The program consists of two parts.  The part listed below should be in main
; memory.  The other part (listed in CA300xm.txt) can be placed in either main
; or extended memory.  When placed in extended memory, THE INSTRUCTION GTO "X"
; SHOULD BE REMOVED.
;
; Before placing the second part in XM, the program should be PACKed
; and run in RAM.  This should be done to compile all the GTO's and
; XEQ's.  If this is not done one will see CHKSUM ERR when trying to

1	LBL "CA"
2	CF 03		; clear prefix flags (ARC, HYP)
3	CF 04
4	DEG
5	FS? 14		; if the "do not clear stack" flag is set
7	SIZE?		;   else if needed extended the # of data registers (NEW: LINE REMOVED)
8	6
9	STO 00
10	ST+ X
11	3
12	+
13	X>Y?
14	PSIZE
15	CLX
16	STO 01		; clear LASTZ
17	STO 02
18	LBL 00
19	GTO "X"		 ; REMOVE THIS LINE when LBL "X" in X-memory !!
20	"\8D\BE"
21	RCL M
22	END

### part 2

• Stack and alpha registers:
• The program uses all registers
• Data registers:
• R00, depth of complex stack (default value is 6)
• R01, real part of LASTZ1
• R02, imaginary part of LASTZ1
• R03, real part of complex number Z1
• R04, imaginary part of complex number Z1
• :
• R13, real part of complex number Z6
• R14, imaginary part of complex number Z6
• Note that the internal format of the complex stack is always rectangular.
• Flags:
• flag 00, “POL”, when set indicates Polar notation (POL), otherwise rectangular notation (RECT)
• flag 02, “no stack lift”, when set disables the stack lift
• flag 03, “ARC”, when set indicates inverse trigonometric operation (ARC)
• flag 04, “HYP”, when set indicates hyperbolic operation (HYP)
• flag 10, “alt operation”, allows similar operations to reuse code
• flag 14, “clipped corner”, is used for all but magnetic cards. During initialization it indicates “do not clear stack”; during keyboard fetching it indicates more key strokes need to follow; and during trigonometry operations, indicates inverse operation.
• flag 22, “keyboard input”, set when numeric input is entered from the keyboard, cleared on power cycle. [7]
• flag 25, “ignore error”, when set, ignores improper operations (like div by zero) or range errors.
• flag 30, used for “always false” test. Used to negate prior test. [7]
• flag 47, “shift set”, used internally in shifted operations.
• flag 44-46, cleared as a side effect of showing the “shift” annunciator
• flag 48-55, cleared as a side effect of showing the “shift” annunciator
• Legend:
• (x + y.j), complex number held on the regular stack
• X register holds the real part (x)
• Y register holds the imaginary part (y)
• (M . e^j.phi), complex number in polar notation

  ; INITIALIZE AND MAIN LOOP
;
; An infinite loop to fetch a keystroke identifying the operation to be
; executed.  The "shift" key causes the "shift" status to be toggled, for all
; other keys the LBL that matches the key code will be called (or keycode+5 if
; "shift" was active).
;
; called      : from "CA" in main memory, or
;               in response to [CINIT] keycode (labeled [ON] on keyboard)
; on entry    : when flag 14 is set, the complex stack will not be cleared
; on exit     : value of Z1 shown.  Also available as X,Y.

1	LBL "X"
2	RDN
3	FC?C 14		; if the "do not clear stack" flag is not set
4	XEQ 48		;   then clear the complex stack

5	LBL 01		; [CINIT] operation
6	CF 10
7	CF 22
8	CF 25
9	FS? 00
10	R-P
11	XEQ 10		; display complex number (x + y.j)
12	STOP
13	ENTER^
14	LBL 02
15	CLX
16	GETKEY		; wait for an operation keycode
17	X=0?
18	GTO 02
19	31
20	X#Y?		; if not the "shift" key
21	GTO 00		;   then handle that operation
22	R^  		;   else update the shift annunciator
23	R^
24	"&#92;&#48;1&#92;&#48;0"
25	FS? 47
26	CLA
27	RCLFLAG
28	ASTO d
29	STOFLAG
30	AOFF
31	GTO 02

32	LBL 00	   	; handle operation associated with a keycode
31	CLX
34	5
35	FC? 47		; if "shift was active"
36	CLX 		;   then increment key code by 5
37	+
38	RDN
39	CLD
40	SF 25
41	XEQ IND T	; call the corresponding operation
42	FC?C 14
43	GTO 01
44	ENTER^
45	GTO 02

; TOGGLE [ARC] MODIFIER, for ASIN, ACOS, ATAN

46	LBL 11		; [ARC], key label [sigma+]
47	FC?C 03
48	SF 03
49	SF 14		; indicate "more key strokes to follow"
50	RTN

; TOGGLE [HYP] MODIFIER, for SINH, COSH and TANH

51	LBL 16		; [HYP], key label [sigma-]
52	FC?C 04
53	SF 04
54	SF 14		; indicate "more key strokes to follow"
55	RTN

; SWITCH BETWEEN POLAR AND RECTANGULAR NOTATION

56	LBL 68		; [RECT], key label [P>R]
57	CF 00
58	GTO 00
59	LBL 69		; [POL], key label [R>P]
60	SF 00
61	LBL 00
62	RCL 04		; get Z1 from the complex stack as (x + y.j)
63	RCL 03
64	RTN

; COMPLEX RECIPROCAL (1/Z)
;
; on entry : Z in X,Y registers in the form (x + y.j)
; on exit  : the result is stored as Z1 on the complex stack
;    	     the result is stored in X,Y in the form (x + y.j)
;	     LASTZ1 holds a copy of the operation operand Z

65	LBL 12	    	; [1/Z] operation

66	XEQ 09		; push (x + y.j) onto complex stack and update LASTZ1
67	XEQ 31		; compute (x + j.y) = 1 / (x + j.y)
68	GTO 03		; copy (x + y.j) to complex stack, and return

; COMPLEX ENTER^
;
; on entry : Z in X,Y registers in the form (x + y.j)
; on exit  : Z is pushed up the complex stack as Z1 and Z2
;    	     Z is X,Y in the form (x + y.j)
;    	     LASTZ1 is unchanged

69	LBL 41	        ; [CENTER^] operation

70	XEQ 04		; push (x + y.j) onto complex stack
71	XEQ 11		; move complex stack up, Z1 > Z2 > Z3 > Z4 > Z5 > Z6
72	SF 02		; "no stack lift"
73	RTN

; COMPLEX CLEAR STACK
;
; on entry : n/a
; on exit  : Z1..Z6 on the complex stack are set to (0 + 0j)
;    	     LASTZ1 is unchanged

74	LBL 48	        ; [CCLST] operation

75	RCL 00
76	ISG X
77	""
78	ST+ X
79	 E3
80	/
81	3
82	+
83	.
84	LBL 36
85	STO IND Y
86	ISG Y
87	GTO 36
88	CF 02		; no "no stack lift"
89	CLST
90	RTN

; COMPLEX CHANGE SIGN AND COMPLEX CONJUGATE (Complement)
;
;     -(x + y.j) = -x - y.j  (change sign)
;     (x + y.j)* =  x - y.j  (conjugate)
;
; on entry : Z in X,Y registers in the form (x + y.j)
; on exit  : the result is stored as Z1 on the complex stack
;    	     the result is stored in X,Y in the form (x + y.j)
;	     LASTZ1 holds a copy of the operation operand Z

91	LBL 42		; [CHSZ] operation
92	SF 10
93	LBL 47		; [COMPLZ] operation

94	XEQ 04		; push (x + y.j) onto complex stack
95	FS? 10
96	CHS
97	X<>Y
98	CHS
99	X<>Y
100	GTO 03		; copy (x + y.j) to complex stack, and return

; CLEAR Z1
;
; on entry : Z in X,Y registers in the form (x + y.j)
; on exit  : Z is pushed up the complex stack as Z1 and Z2
;    	     Z is X,Y in the form (x + y.j)
;    	     LASTZ1 is unchanged

101	LBL 49	        ; [CLZ1] operation

102	XEQ 04		; push (x + y.j) onto complex stack
103	CLST
104	SF 02		; "no stack lift"
105	GTO 03		; copy (x + y.j) to complex stack, and return

; LAST Z1
;
; on entry : n/a
; on exit  : the operand from the last numeric operation (except CHSZ)
;              is pushed onto the complex stack
;    	     the operand from the last numeric operation (except CHSZ)
;              is stored in X,Y in the form (x + y.j)

106	LBL 88 	  	; [LASTZ1] operation

107	FS? 02
108	FS? 22		; if "no stack lift" or "input from keyboard"
109	XEQ 41		;   then perform ENTER^
110	CF 02
111	RCL 02		; LASTZ1
112	RCL 01
113	GTO 03		; copy (x + y.j) to complex stack, and return

;
;     (z + t.j) + (x + y.j) = (x + z) + j.(y + t)
;     (z + t.j) - (x + y.j) = (x - z) + j.(y - t)
;
; on entry : if number was entered on the keyboard,
;              then (x + y.j) as entered in X,Y registers, and
;                   (z + t.j) from Z1 on the complex stack
;              else (x + y.j) from Z1 on the complex stack, and
;                   (z + t.j) from Z2 on the complex stack
; on exit  : the result is stored as Z1 on the complex stack
;    	     the result is stored in X,Y in the form (x + y.j)
;	     LASTZ1 holds a copy of (x + y.j)

114	LBL 51		; [C-] operation
115	SF 10
116	LBL 61		; [C+] operation

117	XEQ 07		; get two operands, as (x + j.y) and (z + j.t)
118	FS? 10
119	CHS
120	X<>Y
121	FS? 10
122	CHS
123	ST+ T
124	RDN
125	+
126	GTO 03		; copy (x + y.j) to complex stack, and return

; COMPLEX MULTIPLICATION AND DIVISION
;
;     Z2 * Z1 = (re1 + j.im1) * (re2 + j.im2) =
;             = (re1.re2 - im1.im2 ) + j.(im1.re1 + re1.im2)
;
;     Z2 / Z1 = Z2 * 1/Z1

127	LBL 81		; [C/] operation
128	SF 10
129	LBL 71		; [C*] operation

130	XEQ 07		; get two operands, as (x + j.y) and (z + j.t)
131	FS? 10		; if division
132	XEQ 31		;   then compute (x + j.y) = 1 / (x + j.y)
133	XEQ 00		; compute (x + j.y) * ( z + j.t)
134	GTO 03		; copy (x + y.j) to complex stack, and return

; COMPLEX POWER OF A COMPLEX NUMBER
;
;           (x+y.j)      z    -t.phi1    j.(z.phi1 + t.ln(r1))
;     (z+t.j)       =  r1  . e        . e
;
;   where:
;         r1   = sqrt(x^2+y^2)
;         phi1 = .... x + y.j ....????

135	LBL 17		; [Z2^Z1] operation

136	XEQ 07		; get two operands, as (x + j.y) and (z + j.t)
137	R^
138	R^
140	R-P
141	LN
142	XEQ 00		; compute (x + j.y) * ( z + j.t)
143	E^X
144	P-R
145	GTO 03		; copy (x + y.j) to complex stack, and return

; COMPLEX PARALLEL CIRCUIT, useful in network theory
;
;            Z1 . Z2
; Z1 // Z2 = -------    {for |Z1+Z2| <> 0}
;            Z1 + Z2

146	LBL 32		; [CPAR] operation
147	XEQ 07		; get two operands, as (x + j.y) and (z + j.t)
148	XEQ 31		; compute 1 / ( x + j.y)
149	R^
150	R^
151	XEQ 31		; compute 1 / ( x + j.y)
152	X<>Y
153	ST+ T
154	RDN
155	+
156	XEQ 31		; compute 1 / ( x + j.y)

; REPLACE Z1 with (x + y.j) ON COMPLEX STACK

157	LBL 03	    	; [PRGM] keycode
158	DEG		; switch to DEG mode
159	STO 03
160	X<>Y
161	STO 04
162	X<>Y
163	RTN

; MULTIPLY TWO COMPLEX NUMBERS subroutine
;
;     (x + y.j) * (z + t.j) = (x + j.y) * (z + j.im2) =
;                           = (x.z - y.t ) + j.(y.x + x.t)

164	LBL 00
165	STO L
166	R^
167	ST* L
168	X<> Z
169	ST* Z
170	R^
171	ST* Y
172	ST* Z
173	X<> L
174	+
175	X<> Z
176	-
177	RTN

; COMPLEX COMMON (base 10) and NATURAL (base e) LOGARITHM
;
;     ln(x + y.j) = ln(r) + j.phi
;
;     Z1
;       log(Z2) = ln(Z2) / ln(Z1)

178	LBL 14		; [LOG(Z)] operation
179	XEQ 08		; get operand, as (x + j.y) and update LASTZ
180	LN  		; x=ln(M1), y=phi1
181	GTO 00

182	LBL 15		; [LN(Z)] operation
183	XEQ 09		; push (x + y.j) onto complex stack and update LASTZ
184	 E		; x=1

185	LBL 00
186	RDN
188	R-P
189	LN
190	R^
191	ST/ Z
192	/
193	GTO 03		; copy (x + y.j) to complex stack, and return

; COMPLEX COMMON (base 10) and NATURAL (base e) EXPONENTIAL
;
;     (x + j.y)    x              x
;    e          = e .sin(y) + j.e .cos(y)

194	LBL 19		; [n^Z] operation
195	XEQ 08		; get operand, as (x + j.y) and update LASTZ
196	LN
197	GTO 00		; reuse part of  [E^Z] operation

198	LBL 20		; [E^Z] operation
199	XEQ 09		; push (x + y.j) onto complex stack and update LASTZ
200	 E

201	LBL 00
202	ST* Z
203	*
204	E^X
206	P-R
207	GTO 03		; copy (x + y.j) to complex stack, and return

; COMPLEX EXPONENTIATION WITH REAL EXPONENT n

208	LBL 18		; [Z^n] operation

209	XEQ 08		; get operand, as (x + j.y) and update LASTZ
210	RDN
211	R-P
212	R^
213	ST* Z
214	Y^X
215	P-R
216	GTO 03		; copy (x + y.j) to complex stack, and return

; COMPLEX ROOT OF REAL NUMBER n
;        __
;     Z1/         1/Z1
;     \/ Z2   = Z2
;        __
;     n /        1/n   j.(phi/n)
;     \/ Z    = M   . e

217	LBL 13		; [Z^1/n] operation

218	XEQ 08		; get operand, as (x + j.y) and update LASTZ
219	RDN
220	R-P
221	R^
222	1/X
223	Y^X
224	360
225	R^
226	ST/ T
227	ST/ Y
228	R^
229	R^
230	LBL 05
231	FC? 00
232	P-R
233	XEQ 10		; display complex number (x + y.j)
234	AON
235	STOP
236	FC? 00
237	R-P
238	R^
239	ST+ Z
240	RDN
241	DSE Z
242	GTO 05		; loop back to LBL 05
243	P-R
244	AOFF
245	GTO 03		; copy (x + y.j) to complex stack, and return

; COMPLEX SINE, COSECANT, COSINE AND SECANT
;
;     sin( x + j.y) = sin(x).cosh(y) + j.cos(x).sinh(y)
;     cos( x + j.y) = cos(x).cosh(y) - j.sin(x).sinh(y)
;     sinh(x + j.y) = cos(y).sinh(x) + j.sin(y).cosh(x)
;     cosh(x + j.y) = cos(y).cosh(x) + j.sin(y).sinh(x)
;     csc(x + j.y)  = 1 / sin( x + j.y)
;     sec(x + j.y)  = 1 / cos( x + j.y)
;     csch(x + j.y) = 1 / sinh(x + j.y)
;     sech(x + j.y) = 1 / cosh(x + j.y)
;
; Flags used:
;     flag 04, indicates [HYP]
;     flag 10, indicates [SIN], otherwise [COS]
;     flag 14, indicates inverse operation (CSC and COS, aka SIN^-1 and COS^-1)
;
; Reference:
;     https://en.wikipedia.org/wiki/Complex_number#Complex_analysis

246	LBL 28		; [CSC(Z)] operation
247	SF 14

248	LBL 23		; [SIN(Z)] operation
249	SF 10

250	LBL 29		; [SEC(Z)] operation
251	FC? 10
252	SF 14

253	LBL 24		; [COS(Z)] operation

254	XEQ 09		; push (x + y.j) onto complex stack and update LASTZ
255	FS?C 03		; ARC?
256	GTO 13
257	XEQ 00		; calculate cos/sin/cosh/sinh
258	ST* T
259	RDN
260	*
261	CHS
262	FC? 04		; HYP?
263	FS? 10		; SIN?
264	CHS
265	FC?C 04		; HYP?
266	FC? 10		; COS?
267	X<>Y
268	FS?C 14		; inverse operation?
269	XEQ 31		;   then compute Z1 = 1 / Z1
270	GTO 03		; copy (x + y.j) to complex stack, and return

; COMPLEX TANGENT AND COTANGENT, doesn't support ARC or HYP variations
;
;     tan(x + j.y) = sin(2.x)  / ( cosh(2.y) + cos(2.x) ) +
;                    sinh(2.y) / ( cosh(2.y) + cos(2.x) ) . j
;     cot(Z) = 1 / tan(Z)
;
; Flags used:
;     F03 indicates [ARC]
;     F04 indicates [HYP]
;     F14 indicates inverse operation (COT aka TAN^-1]

271	LBL 30		; [COT(Z)] operation
272	SF 14
273	LBL 25		; [TAN(Z)] operation
274	FS?C 03		; ARC?
275	GTO 02		;   UNSUPPORTED, jump back to main routine
276	FS?C 04		; HYP?
277	GTO 02		;   UNSUPPORTED, jump back to main routine
278	XEQ 09		; push (x + y.j) onto complex stack and update LASTZ
279	2		; multiply x and y by 2
280	ST* Z
281	*
282	XEQ 00		; calculate cos/sin/cosh/sinh (F04=0, F10=0)
283	R^
284	+
285	ST/ Z
286	/   		; answers is now as (x + y.j)
287	FS?C 14		; inverse operation?
288	XEQ 31		;   then compute Z1 = 1 / Z1
289	GTO 03		; copy (x + y.j) to complex stack, and return

; TRIGONOMIC OPERATIONS HELPER subroutine
;
; Call with:
;   complex number on the stack as (x + y.j).
;   F04 indicates [HYP]
;   F10 indicates [SIN], otherwise [COS]
;
; This operation returns:
;
;           | [HYP]    [HYP]
;	    | [SIN]    [COS]	[SIN]	[COS]
;     ------+----------------------------------
; re  y-reg | sin(y)   sin(y)   sin(x)  sin(x)
; re  z-reg | cosh(x)  sinh(x)  cosh(y) sinh(y)
;     ------+----------------------------------
; im  x-reg | cos(y)   cos(y)   cos(x)  cos(x)
; im  t-reg | sinh(x)  cosh(x)  sinh(y) cosh(y)
;
;            x     -x      2.x
;           e  -  e       e    -  1                    1
; sinh(x) = ---------  =  ---------  ,    csch(x) = -------
;                              x                    sinh(x)
;              2            2.e
;
;            x     -x      2.x
;           e  +  e       e    +  1                   1
; cosh(x) = ---------  =  ---------  ,    sech(x) = -------
;                              x                    cosh(x)
;               2           2.e
; Reference:
;     https://en.wikipedia.org/wiki/Hyperbolic_trig_operations

290	LBL 00
291	FS? 04		; HYP?
292	X<>Y		;
293	2
294	RCL Z
295	ST+ X
296	E^X-1
297	ST+ Y
298	R^
299	E^X
300	ST+ X
301	ST/ Z
302	/
303	FS? 10		; SIN? (not COS)
304	X<>Y		;
305	R^
307	SIN
308	R^
309	COS
310	RTN

; INVERSE TRIGONOMIC OPERATIONS, ARC and HYP-ARC
;
;     arcsin(x + y.j) = arcsin(b) + j.sign(y).ln(a + sqrt(a^2-1)
;     arccos(x + y.j) = arccos(b) - j.sign(y).ln(a + sqrt(a^2-1))
;     arccsc(Z)  =    arcsin(1/Z)
;     arcsec(Z)  =    arccos(1/Z)
;     arcsinh(Z) = -j.arcsin(j.Z)
;     arccosh(Z) =  j.arccos(Z)
;     arccsch(Z) =  j.arccsc(j.Z)
;     arcsech(Z) =  j.arcsec(Z)
;   where
;     a = ( sqrt( (x+1)^2 + y^2 ) + sqrt( (x-1)^2 + y^2) ) / 2
;     b = ( sqrt( (x+1)^2 + y^2 ) - sqrt( (x-1)^2 + y^2) ) / 2
;     sign(y) returns 1 when y>=0, otherwise returns -1
;
; Flags used:
;     F04 indicates [HYP]
;     F10 indicates [SIN], otherwise [COS]
;     F14 indicates inverse operation (CSC and COS, aka SIN^-1 and COS^-1]
;
; Reference:
;     https://en.wikipedia.org/wiki/Inverse_trigonometric_operation

311	LBL 13
312	FS?C 14		; inverse operation?
313	XEQ 31		; compute Z1 = 1 / Z1
314	FS? 04		; HYP flag
315	FC? 10
316	GTO 00
317	X<>Y
318	CHS
319	LBL 00		; entered with Z1 as (x + y.j)
320	RCL X
321	 E
322	ST- Z
323	+
324	X^2
325	X<>Y
326	X^2
327	X<> Z
328	X^2
329	ST+ Z
330	+
331	SQRT
332	STO Z
333	X<>Y
334	SQRT
335	ST- Z
336	+
337	2
338	ST/ Z
339	/   		; X holds a;  Y holds b; Z holds y
340	ENTER^
341	X^2
342	SIGN
343	ST- L
344	X<> L
345	SQRT
346	+
347	LN
348	R^
349	SIGN
350	*
351	FC? 10
352	CHS
353	X<>Y
355	FS? 10
356	ASIN
357	FC? 10
358	ACOS		; Z1 (x + y.j) now holds the answer to simple ARCSIN or ARCCOS
359	FC?C 04
360	GTO 03		; we're done for non-HYP operations;
361	FS? 10		; proceed for for HYP or inverse-HYP
362	CHS
363	X<>Y
364	FC? 10
365	CHS
366	GTO 03		; copy (x + y.j) to complex stack, and return

; VIEW COMPLEX NUMBER Zn

367	LBL 89		; [VIEWZn] operation
368	RCL 00
369	X<Y?		; if n > complex stack depth, recall Z1 and return
370	GTO 14		; get (x + y.j) from complex stack, and return
371	SIGN
372	+
373	ST+ X
374	SIGN
375	CLX
376	RCL IND L	; recall imaginary part of Zn
377	DSE L
378	RCL IND L	; recall real part of Zn
379	FS? 00		; if notation selected
380	R-P 		;   then convert to polar notation
381	XEQ 10		; display complex number (x + y.j)
382	PSE 		; pause, but allow number input
383	GTO 14		; get (x + y.j) from complex stack, and return

; EXCHANGE COMPLEX STACK REGISTERS

384	LBL 21		; [Z1<>Z2] operation
385	XEQ 04		; push (x + y.j) onto complex stack
386	2
387	LBL 26		; [Z1<>Zn] operation
388	RCL 00
389	X<Y?
390	GTO 14		; get (x + y.j) from complex stack, and return
391	X<>Y
392	ST+ X
393	1.003002	; X register holds 1.003002; Y register holds 2.n,
394	CF 02		; no "no stack lift"
395	GTO 00		; perform register swap and return

; COMPLEX STACK ROLL, up or down
;
; Does not roll around
; Uses block rotate trick form PPC Journal V10N3p15a

396	LBL 22		; [CR^] operation
397	SF 10
398	LBL 27		; [CRDN] operation

399	XEQ 04		; push (x + y.j) onto complex stack
400	3
401	ENTER^
402	5
403	FS? 10		; CR^?
404	X<>Y
405	RCL 00		; complex stack depth (csdepth)
406	DSE X
407	ST+ X
408	 E3
409	ST/ Z
410	X^2
411	/
412	+		; for CRDN
413	LBL 00
414	+
415	REGSWAP		; register swap for sss.dddnnn

; GET (x + y.j) FROM COMPLEX STACK

416	LBL 14
417	RCL 04		; imaginary part of Z1
418	RCL 03		; real part of Z1
419	RTN

; COMPLEX 1/Z1
;
; Formula:
;      1             x                 y
;    -------  =  ---------  -  j . ---------
;    x + y.j     x^2 + y^2         x^2 + y^2
;
; doesn't disturb Z and T
420	LBL 31
421	X^2
422	X<>Y
423	STO M
424	ST* X
425	ST+ Y
426	X<> M
427	CHS
428	X<>Y
429	ST/ Y
430	ST/ L
431	X<> L
432	RTN

; GET TWO OPERANDS as (x + j.y) and (z + j.t)
; 1st operand is from keyboard, otherwise from Z1
; stack management subroutine for operations with two complex number operands

433	LBL 07
434	XEQ 06		; get one operand (x + y.j) from keyboard input
;  otherwise from Z1 on the complex stack
435	FC?C 02
436	FC? 22		; if "no stack lift" or no "input from keyboard"
437	XEQ 12		;   then move complex stack down
438	RCL 04		; get operand (z + t.j) from Z1 on the complex stack
439	RCL 03
440	R^
441	R^
442	GTO 00

; GET OPERAND, as (x + j.y) and UPDATE LASTZ
; stack management subroutine for operations with one complex and one real number operand

443	LBL 08	   	; called with n in register X
444	FS?C 02		; if "no stack lift"
445	XEQ 12		;   then move complex stack down, Z1 < Z2 < Z3 < Z4 < Z5 < Z6
446	RCL 04		; copy Z1 to LASTZ
447	STO 02
448	RCL 03
449	STO 01
450	RCL Z		; n in register X, complex operand as (y + z.j)
451	RTN

; PUSH (x + y.j) ONTO COMPLEX STACK and UPDATE LASTZ
; stack management subroutine for operations with one complex number operand

452	LBL 09
453	XEQ 04		; push (x + y.j) onto complex stack
454	LBL 00
455	STO 01		; copy to LASTZ1
456	X<>Y
457	STO 02
458	X<>Y
459	RTN

; COMPLEX ALPHA/ALPHA ROUTINE

460	LBL 04	      	; [CVIEW] key code [ALPHA]
461	FC?C 02
462	FC? 22
463	FS? 30		; if both "no stack lift" and "keyboard input"
464	XEQ 11		;   then move complex stack up, Z1 > Z2 > Z3 > Z4 > Z5 > Z6

465	XEQ 06		; get one operand
466	STO 03
467	X<>Y
468	STO 04
469	X<>Y
470	RTN

; GET ONE OPERAND (x + y.j)
; from keyboard input, otherwise from Z1 on the complex stack

471	LBL 06
472	FS? 00		; if in polar mode, then convert it to Rect
473	FC? 22
474	FS? 30
475	P-R
476	FS? 22		; keyboard input?
477	RTN
478	RCL 04
479	RCL 03
480	RTN

; DISPLAY
; in rectangular mode "x + y.j", or in polar mode "x <y" with the
; angle in degrees
; subroutine that views both parts of the complex number in X and Y in
; condensed format in the display, without disturbing Z, T or the display
; mode.  ENG 2 was chosen because, to display complex numbers in analog
; electronics.

481	LBL 10		; Z1 = x + j.y
482	SIGN		; save X in LASTX
483	RDN
484	CLA
485	RCLFLAG		; save flags
486	ENG 2
487	ARCL L
488	RDN
489	FS? 00		; in Rectangular notation append real part,
490	GTO 00		; and '+' sign if imaginary part is positive
491	X<0?
492	GTO 00
493	>"+"
494	LBL 00
495	R^
496	FS? 00		; for Polar notation, append angle ('<') sign
497	>" <"
498	ARCL Y
499	FC? 00
500	>"J"		; in Rectangular notation append 'J' char
501	AVIEW
502	STOFLAG		; restore flags
503	X<> L		; restore X from LASTX
504	RTN

; ROLL THE COMPLEX STACK, by one position up or down
; subroutine to shift the stack up or down by one complex register
;
; Does not roll around like RDN or R^
; Does not enter or retrieve data.
;
; Example:
;                  | stack lift | stack drop
;    --------------+------------+------------
;    Z6    6 + 6j  |   5 + 5j   |   6 + 6j
;    Z5    5 + 5j  |   4 + 4j   |   6 + 6j
;    Z4    4 + 4j  |   3 + 3j   |   5 + 5j
;    Z3    3 + 3j  |   2 + 2j   |   4 + 4j
;    Z2    2 + 2j  |   1 + 1j   |   3 + 3j
;    Z1    1 + 1j  |   1 + 1j   |   2 + 2j

505	LBL 11		; stack lift, Z1 > Z2 > Z3 > Z4 > Z5 > Z6
506	3.005		;   typically when a new number is moved into Z1
507	GTO 00
508	LBL 12		; stack drop, Z1 < Z2 < Z3 < Z4 < Z5 < Z6
509	5.003		;   typically when a operation combines Z1 and Z2
510	LBL 00
511	SIGN
512	RCL 00		; complex stack depth (csdepth)
513	X<>Y
514	ST- Y
515	RDN
516	ST+ X
517	 E6
518	ST/ Y
519	X<> L
520	+		; register X is in sss.dddnnn format
521	REGMOVE		; copies 2*(csdepth-1) registers from sss to ddd
522	RDN
523	END

## Operations and stack

The table shows the key assignments with their effect on the complex stack.

 operation normal key description stack COMPLEX R/S Prefix for Complex operations. N INIT ON Reinitialize, but do not clear stack. + + Adds Z1 to Z2. E,L,↓ / * Multiplies Z2 by Z1. E,L,↓ / / Divides Z2 by Z1. E,L,↓ // XEQ Take Z1 and Z2 parallel (network theory). E,L,↓ CHS CHS Negate Z1. E CON ISG Complement/conjugate of Z1. E 1/Z 1/X Reciprocal of Z1. E Z2Z1 yx Z2 to the power Z1. E,L,↓ LOG LOG Base 10 logarithm of Z1. E,L LN LN Natural logarithm of Z1. E,L eZ ex e to the power Z1. E,L Zn x2 Z1 to the real number power n. E,L Z1/n √x All nth roots of Z1. E,L nZ 10x Real number N to the power Z1. E,L RECT P→R Sets the input/output mode to rectangular. N POL R→P Sets the input/output mode to polar. N ENTER ENTER Lifts the stack and disables stack left. D,↑,(↑) VIEW ALPHA Enters a new complex number on the stack without lifting the complex stack (compare to the use of ALPHA/ALPHA to terminate digit entry without using ENTER . Recalls Z1 if flag 22 is clear. E,(↑) CLZ1 CLX Clears Z1 and disables stack lift. D CLST RTN Clears the complex stack and the normal stack (LASTZ1 is preserved). E LASTZ1 LASTX Recalls LASTZ1 onto the stack. E,(↑) VIEWZn VIEW Views the contents of Zn. Specify 0 to view LASTZ1. N R RDN Rolls the stack down by one complex register. E R R Rolls the stack up by one complex register. E Z1 Z2 X Y Exchanges Z1 with Z2. E Z1 Zn CL∑ Exchanges Z1 with Zn. Specify 0 for LASTZ1 E ARC ∑+ ARC prefix. N HYP ∑- HYP prefix. N SIN SIN Sine of Z1. E,L COS COS Cosine of Z1. E,L TAN TAN Tangent of Z1. E,L CSC SIN-1 Cosecant of Z1 E,L SEC COS-1 Secant of Z1 E,L COT TAN-1 Cotangent of Z1 E,L

The prefixes ARC and HYP provide a rich set of trigonometric and hyperbolic functions.

 N = neutral stack lift status ↑ = stack lifts D = disables stack lift ↓ = stack drops E = enables stack lift (↑) = stack lift if enabled L = Z1 saved in LASTZ1

## References

 [1] COMPLEX ARITHMETIC Frans de Vries, May 1985 PPC Journal V12N5, page 4-9 [2] SCIENTIFIC ANALYSIS IN THE POCKET CALCULATOR, chapter 3-8, appendix 4 Jon M. Smith, 1975 New York, John Wiley & sons [3] LAPLACE-, FOURIER- EN Z-TRANSFORMATIE, chapter 1 Ir. A. van der Knaap, 1983 HTS Dordrecht, the Netherlands [4] COMPLEXE REKENWIJZE OP DE STANDAARD HP41CV Coert Vonk, 1986 HP Users Nieuws #8 June 1986 pg. 24-29 [5] MATHEMATICAL TABLES AND FORMULAS, formula 26 pg. 242 R.D. Carmichael and E.R. Smith Dover Publications Inc, New York [6] EXTEND YOUR HP-41C, page 558 Wlodek Mier-Jedrzejowicz, 1986 Synthetix [7] HP-41C FLAGS, PART 1 HP Key Notes V4N3, 1980 [8] COMPLEX FUNCTIONS FOR THE HP-41 Jean-Marc Baillard, 2004 see article [9] RUNNING PRGMS IN X-MEMORY Philip Karras, April 1982 PPC Journal, V9N3, pg.26

## 4 Replies to “Complex numbers on the HP-41”

1. Ángel Martin says:

Hi Coert, very nice little collection you have posted here – and world-class documentation! Thanks for sharing it…

I thought it’d be a nice idea to put all these programs in a ROM image, so you may want to use it on V41 or other hardware (Clonix, 41cl, MLDL, eproms, etc). I cannot attach it to this message bur will be glad to send it to your email address if you want.

Best wishes,
Ángel Martin

2. Coert says:

Thanks Ángel,
I posted the files through GitHub. For the URL refer to the Sources section.

3. Steve Tucker says:

There seems to be a problem with example 5.1

Calculate(2+3j)(7–6j)+(4+5j)