M-Code Routines

# A few M-Code Routines for the HP-41

Overview

1°)  Introduction

a)    A few M-Code Instructions
b)    Relative Jumps
c)    Absolute Jumps
d)    Useful Subroutines
e)    13-digit routines

2°)  Constants

a)   MAXR
b)   Euler's Constant

3°)  Stack Operations

a)   Y<>Z
b)   Y<>T
c)   Z<>T
d)   ST<>A
e)   DUP

4°)  14 Functions

a)   X+1 & X-1
b)   X/2
c)   Rounding to the nearest integer
d)   FLOOR & FRC2
e)   Cube
f)    Cube Root
g)   Y^X ( producing 0^0=1 )
h)   XROOT
i)   CNK
j)   Degree of a Polynomial
k)  X/E3 & X*E3

5°)  Random Number Generator ( Time Module required )

6°)  3D-Vectors

a)   Norm
b)   Dot Product
c)   Cross Product

7°)  Statistics

a)   Linear Regression
b)   Recalling the Statistics Registers

8°)  3 Tests

a)   X=1?
b)   X=N?
c)   X#Y??

9°)   Hyperbolic Functions  ( Sine, Cosine, Tangent and their Inverses )

10°)  5 Complex Functions

a)   Z*Z & Z/Z
b)   Z^2
c)   1/Z
d)   SQRTZ

11°)  Creating Extra-Registers

a)  Saving & Recalling X-Register
b)  Saving & Recalling all the Stack

12°)  Largest Element of a Block of Registers

Many of the following routines are elementary, but I hope they will be useful for beginners.

If your HP-41 "crashes", press and hold the ENTER^ key and press the ON key: this will stop infinite loops on "newer" HP-41s.
Otherwise, you'll have to remove the batteries for a while.

1°)  Introduction

a)  A few M-Code Instructions

-The CPU ( Central Pocessing Unit ) uses several registers.
-For mathematical purposes, the most important ones are C A B M N   ( CPU-registers M , N  are not the "synthetic" registers M , N )
-These 5 registers have 56 bits divided in 14 digits as shown below.

 13 12 11 10 9 8 7 6 5 4 3 2 1 0
 MS | -------------------------- Mantissa ------------------------- |---S&X--

MS  = Mantissa Sign
S&X = Sign of exponent & eXponent:  Sign = digit 2  &  exponent = digits 1-0

For example,

3.141592654 1071  is coded   0  3  1  4  1  5  9  2  6  5  4  0  7  1         MS = 0  for a positive number
-3.141592654 1071  is coded   9  3  1  4  1  5  9  2  6  5  4  0  7  1          MS = 9 for a negative number

3.141592654 10 -71  is coded   0  3  1  4  1  5  9  2  6  5  4  9  2  9        S&X = 1000 - | exponent |  for a negative exponent
-3.141592654 10 -71  is coded   9  3  1  4  1  5  9  2  6  5  4  9  2  9        929 = 1000 - 71

-I cannot list all the M-code instructions, but here are a few ones ( see reference [1] for a more complete list )

2DC  PT= 13  sets pointer to digit 13
35C  PT= 12  sets pointer to digit  12
21C  PT=  2   sets pointer to digit   2

010   LD@PT- 0   loads C-register digit at pointer with 0  and decrements pointer
050   LD@PT- 1   loads C-register digit at pointer with 1  and decrements pointer
090   LD@PT- 2   loads C-register digit at pointer with 2  and decrements pointer
0D0  LD@PT- 3   loads C-register digit at pointer with 3  and decrements pointer
110   LD@PT- 4  loads C-register digit at pointer with 4  and decrements pointer
150   LD@PT- 5  loads C-register digit at pointer with 5  and decrements pointer
190   LD@PT- 6  loads C-register digit at pointer with 6  and decrements pointer
1D0   LD@PT- 7 loads C-register digit at pointer with 7  and decrements pointer
210   LD@PT- 8  loads C-register digit at pointer with 8  and decrements pointer
250   LD@PT- 9  loads C-register digit at pointer with 9  and decrements pointer

130   LDI S&X  loads the word in the next address into the S&X field of C-register.

-For instance, the 2 words  130  LDI S&X   load  the exponent  41  in  C
041  041

-If you want to load  12000  in register C:

04E   C=0 ALL  ( see below )
35C   PT=12
050   LD@PT- 1
090   LD@PT- 2
130   LDI S&X
004   004

04E   C=0 ALL   clears register C
10E   A=C ALL  copies C-register to A-register
0AE  A<>C ALL  exchanges A & C registers
06E   A<>B ALL  ----------  A & B --------
0EE   C<>B ALL  ----------  C & B --------

198   C=M ALL  copies M-register to C-register
158   M=C ALL  ------  C----------- M--------
1D8  C<>M ALL exchanges C & M registers

0B0   C=N ALL  copies N-register to C-register
070    M=C ALL  ------  C----------- N-------
0F0   C<>M ALL exchanges C & N registers

-The most important register is register C: this is the only one that can exchange data with user memory.

028   WRIT 0(T)    writes register C into stack register T if  the RAM pointer is in the status register - this is always the default
068   WRIT 1(Z)    writes register C into stack register  Z if  .... etc ...
0A8  WRIT 2(Y)   ... and so on ...
0E8  WRIT 3(X)
128  WRIT 4(L)
168  WRIT 5(M)   here, M = synthetic register M
1A8  WRIT 6(N)   here, N = synthetic register N
1E8  WRIT 7(O)
228  WRIT 8(P)
268  WRIT 9(Q)

-Though it's not rigorous, I often write these instructions  T=C , Z=C , Y=C ... etc ...

078   READ 1(Z)   reads C into Z-register if  the RAM pointer is in the status register - this is always the default
0F8   READ 3(X)  ... and so on ...
278   READ 9(Q)        ( I often write these instructions  C=Z , C=Y ...etc... )

-Note that there is no  READ 0(T)  instruction. If you want to copy T into C, use the 3 words

046   C=0 S&X               places the RAM pointer
270   RAM SLCT            in the required area.

2F0   WRIT DATA          works in the same way   ( see reference [1] to access to data registers R00 , R01 , ..... )

-The CPU uses a special flag - the carry flag - which is set when there is an overflow or an underflow or when a special instruction sets it.
It is cleared by any instruction that doesn't set it.
-The CPU also uses 14 flags ( numbered 0 to 13 ). All these flags are different from the user flags.
-Here are a few instructions:

044   CLRF 4      clears flag 4
048   SETF 4       sets flag 4
04C  ?FSET 4     sets the carry flag if flag 4 is set

084   CLRF 5      clears flag 5
088   SETF 5       sets flag 5
08C  ?FSET 5     sets the carry flag if flag 5 is set

144   CLRF 6      clears flag 6
148   SETF 6       sets flag 6
14C  ?FSET 6     sets the carry flag if flag 6 is set

284   CLRF 7      clears flag 7
288   SETF 7       sets flag 7
28C  ?FSET 7     sets the carry flag if flag 7 is set

104   CLRF 8      clears flag 8
108   SETF 8       sets flag 8
10C  ?FSET 8     sets the carry flag if flag 8 is set

-The CPU can work in 2 different modes: hexadecimal ( this is the default ) and decimal:

260   SETHEX
2A0  SETDEC

-The end of M-code routines - or subroutines - is usually:

3E0   RTN
360   ?C RTN     return if carry
3A0  ?NC RTN  return if not carry

>>>  There is an exception because the CPU return-stack only has 4 subroutine-levels
and if you have used these 4 levels, 3E0  RTN  must be replaced by

3C1  ?NCGO
002   00F0

Otherwise ( with  3E0  RTN  instead ),  the display would freeze.
But it's not really a "crash": the HP-41 still responds to any keystroke.

-Among many other instructions, the 2 following ones are useful too:

160    ?LOWBAT   which set the carry flag when the batterry is low
3CC   ?KEY           ----------------------------- a key is pressed

b) Relative Jumps  ( X-Functions Module Required )

-There are 4 types of relative jumps:

JNC +nn   jumps nn instrunctions forward if carry clear   00 < nn < 64d
JC +nn     jumps nn instrunctions forward if carry set      00 < nn < 64d
JNC -nn   jumps nn instructions backwards if carry clear 00 < nn < 65d
JC -nn     jumps nn instructions backwards if carry set    00 < nn < 65d

-"RJUM" returns the code that corresponds to all possible relative jumps.

Formulae:

JNC+nn = 8 n + 3
JNC-nn = 1027 - 8 n

JC+nn = 8 n + 7
JC-nn = 1031 - 8 n

-These results are finally converted to hexadecimal numbers.
-XTOA is used.

Data Registers: /
Flag:  F01

CF 01 for jump if not carry
SF 01 for jump if carry

Subroutines: /

-The append character is denoted "~"

 01  LBL "RJUM"  02  INT  03  X=0?   04  LN  05  64  06  CHS  07  X<>Y  08  XY  12  X>Y?  13  SF 99  14  10  15  X<>Y            16  " J" 17  FC? 01  18  "~N"  19  "~C"  20  X>0?   21  "~+"  22  X<0?  23  "~-"  24  ABS  25  X

( 131 bytes / SIZE 000 )

 STACK INPUT OUTPUT X +/-nn /

The alpha register is displayed at the end                               ---Execution time = 3seconds---

Examples:

•  CF 01   "JUMP IF NOT CARRY"

4  XEQ "RJUM"  >>>>   JNC+04=023
61        R/S           >>>>   JNC+61=1EB
63        R/S           >>>>   JNC+63=1FB

-4        R/S           >>>>   JNC-04=3E3
-61       R/S           >>>>   JNC-61=21B
-64       R/S           >>>>   JNC-64=203

•  SF 01   "JUMP IF CARRY"

4  XEQ "RJUM"  >>>>   JC+04=027
61        R/S           >>>>   JC+61=1EF
63        R/S           >>>>   JC+63=1FF

-4        R/S           >>>>   JC-04=3E7
-61       R/S           >>>>   JC-61=21F
-64       R/S           >>>>   JC-64=207

Notes:

-If you key in    0   XEQ "RJUM" you'll get  "DATA ERROR"  ( line 04 )
-If you key in  +nn  XEQ "RJUM" with n > 63,  you'll get "NONEXISTENT"  ( line 13 )
-If you key in  -nn  XEQ "RJUM" with -n < -64, you'll get "NONEXISTENT"  ( line 09 )

Warning:

-The n-values = the jump distances, must be expressed in decimal

-If - for instance - you want to code JNC+25 hexadecimal, first convert 25h to 2 x 16 + 5 = 37 decimal
and then:   CF 01  37  XEQ "RJUM"  yields  JNC+37=12B

-Contrariwise, the returned code is always an hexadecimal word.

c) Absolute Jumps

-There are 4 types of absolute jumps:

?NCXQ  if not carry execute
?CXQ    if carry execute
?NCGO  if not carry go to
?CGO    if carry go to

-Each absolute jump requires 2 words.
-The following program displays the required words for a given jump.
-This routine is only a slight modification of a program listed in the "Hepax Module Owner's Manual"

Data Register:   R00 :  temp
Flags: /
Subroutines: /

-The append character is denoted  ~
-To load lines 20-21  key in  XEQ "HEPAX"  you get  HEPAX  _ _ _  and press  003  or  C  ... etc ...

 01  LBL "JUMP" 02  INT 03  4 04  MOD 05  STO 00         06  "?NCXQ" 07  X#0? 08  "?CXQ" 09  DSE X 10  "?NCGO" 11  DSE X 12  "?CGO" 13  "~ "  14  4 15  HPROMPT 16  4 17  DECODYX 18  "~="  19  1 20  HEPAX         21  3 22  X<>Y 23  -2 24  HEPAX 25  11 26  HEPAX  27  9 28  1023 29  HEPAX         30  3 31  X<>Y 32  HEPAX 33  1 34  LASTX 35  X<>Y 36  3 37  DECODYX 38  "~ "  39  RDN 40  10 41  HEPAX         42  11 43  -2 44  HEPAX 45  11 46  RCL 00 47  HEPAX 48  3 49  HEPAX         50  9 51  3 52  DECODYX 53  AVIEW 54  CLST 55  END

( 115 bytes / SIZE 001 )

 STACK INPUT OUTPUT X k 0

where

k = 0  for  ?NCXQ
k = 1  for   ?CXQ
k = 2  for   ?NCGO
k = 3  for   ?CGO

Example:    You have to code  ?NCXQ
F123

0   XEQ "JUMP"  the HP-41 displays   ?NCXQ  _ _ _ _   key in  F  1  2  3

and a few seconds later, you read:   ?NCXQ  F123 =  08D  3C4

08D   ?NCXQ
3C4   F123

-Likewise   3  R/S   gives   ?CGO _ _ _ _   and if you press   0 0 D A   you'll get   ?CGO  00DA =  369  003  meaning:

369   ?CGO
003   00DA

Notes:

-"HPROMPT"  "DECODYX"  "HEPAX" are functions of an HEPAX Module.
-If you don't have an Hepax Module, use for instance Warren Furlow's excellent V41 Emulator.
-There are also "port dependent jumps" but they will be omitted here.

d) Useful Subroutines

Cheks:

361   ?NCXQ          checks register C for alpha data
050   14D8               and sets the CPU in decimal mode

355   ?NCXQ          checks registers A and C for alpha data, executes A<>C       ( more exactly it checks C then executes A<>C and checks C )
050   14D5               and executes SETDEC

Stack Operations:

3B5   ?NCXQ         executes a R^
050    14ED             ( roll up )

3A5   ?NCXQ         executes a RDN
050    14E9              ( roll down )

345    ?NCXQ         executes           synthetic registers
040    10D1              CLA                M N O P are cleared

Mathematics:      the CPU must be in Decimal mode

-Most of the maths subroutines can deal with numbers as great as 10200

01D    ?NCXQ        replaces  C by
060     1807                A+C

135    ?NCXQ        replaces C by
060    184D               A*C

2F9    ?NCXQ       replaces C by         this subroutines cheks if C<0
060    18BE             sqrt(C)                  and displays DATA ERROR in this case

22D    ?NCXQ      replaces C by         this subroutines cheks if C=0
060     188B              1/C                     and displays DATA ERROR in this case

261    ?NCXQ      replaces C by          this subroutines cheks if C=0
060     1898             A/C                      and displays DATA ERROR in this case

3C4    ST=0          replaces C
045    ?NCXQ            by                    provided   X = C
06C   1B11               Y^C

3B1   ?NCXQ       replaces C              DATA ERROR is displayed if C < 0 or if C is not an integer
060    18EC           by fact(C)               the result can be used if C < 100

Note:   the 7 subroutines above do not alter CPU-register N

044    CLRF 4
029    ?NCXQ      replaces C by
068    1A0A           exp(C)

048    SETF 4
029    ?NCXQ      replaces C by          ( actually the same subroutine )
068    1A0A          exp(C)-1

084    CLRF 5
1CD   ?NCXQ      replaces C by
06C    1B73           Ln1+C

088     SETF 5
1CD   ?NCXQ      replaces C by          ( actually the same subroutine )
06C    1B73          Log1+C

003    CLRF 3
084    CLRF 5
115    ?NCXQ      replaces C by
06C    1B45           Ln(C)

003    CLRF 3
088    SETF 5
115    ?NCXQ      replaces C by          ( the same subroutine )
06C    1B45          Log(C)

044    CLRF 4       replaces
070    N=C ALL        C
3E1    ?NCXQ          by
06C    1BF8           10^C

084    CLRF 5       replaces C
0ED   ?NCXQ            by
064    193B              frc(C)

088    SETF 5       replaces C
0ED   ?NCXQ            by                         ( the same subroutine )
064    193B              Int(C)

-Simply use  2BE  C=-C-1 MS  to change the sign of C      ( but this instruction usually sets the carry flag )
and  05E   C=0 MS       to replace C by its magnitude

044   CLRF 4
070   N=C ALL      replaces C by
171   ?NCXQ         A mod C                    ( positive arguments )
064   195C

-For negative arguments, however,  this will not work properly if X is different from C
-So, in this case, add  0E8  WRIT 3(X)  before  ?NCXQ  195C

0E8  WRIT 3(X)
044   CLRF 4
070   N=C ALL      replaces C by
171   ?NCXQ         A mod C                    ( all arguments )
064   195C

231   ?NCXQ        replaces C by              it performs
064   198C             180.C/(pi)                      R-D

070   N=C             replaces C
205   ?NCXQ             by                              D-R
064   1981              (pi).C/180

070   N=C             replaces C
265   ?NCXQ             by
044   1199              HMS(C)

070    N=C            replaces C
24D   ?NCXQ            by
044    1193              HR(C)

-The following lines rounds X ( not C ) to its nearest integer and places the result in C ( not X )
-However, the CPU is then set in hexadecimal mode.
-So, add  2A0  SETDEC  after these lines if you have to make other calculations...

130   LDI S&X
080   080
0C5  ?NCXQ
028   0A31

-The 6 following subroutines perform trigonometric functions in the current angular mode.

070   N=C            replaces C
221   ?NCXQ            by
048    1288              sin(C)

070   N=C            replaces C
261   ?NCXQ           by
040    1098             asin(C)

070   N=C            replaces C
1F1   ?NCXQ            by
048    127C              cos(C)

070    N=C            replaces C
1F5   ?NCXQ            by
040    107D             acos(C)

070    N=C            replaces C
209   ?NCXQ            by
048    1282              tan(C)

070    N=C            replaces C
2A9   ?NCXQ            by
040    10AA             atan(C)

Statistics:           the CPU must be in Decimal mode

041   ?NCXQ         calculates the sample standard deviation sx in C               moreover, this subroutine checks the statistics registers
074   1D10              and  the sample standard deviation sy in N                      and checks for alpha data.

3F9   ?NCXQ         calculates the mean µx in C                                              it also checks the statistics registers
070    1CFE            and the mean µy in N                                                      and checks for alpha data.

084   CLRF 5              replaces
35C  PT=12                C by sx
04D  ?NCXQ                and                        without checking the statistics registers
074   1D13                  N by sy

35C  PT=12            replaces C by µx
001   ?NCXQ                and                        without checking the statistics registers
074   1D00                  N by µy

-The 5 following subroutines recall the statistics registers in C, but the RAM pointer remains in the stat registers area.
-So, if - for instance - you want to copy the result in register X,

10E   A=C ALL
046   C=0 S&X               these 2 lines set the RAM pointer
270   RAM SLCT            in the status registers area
0AE  A<>C
0E8   WRIT 3(X)

after these instructions.

35C    PT=12
3BD   ?NCXQ        places Sum x in C
070    1CEF

19C    PT=11
3BD   ?NCXQ        places Sum x^2 in C
070    1CEF

0DC   PT=10
3BD   ?NCXQ        places Sum y in C
070    1CEF

25C    PT= 9
3BD   ?NCXQ        places Sum y^2 in C
070    1CEF

11C    PT= 8
3BD   ?NCXQ        places Sum x.y in C
070    1CEF

29C    PT= 7
3BD   ?NCXQ        places n ( number of data points ) in C
070    1CEF

Overflow & underflow:

331   ?NCGO           takes an argument in C    if there is an underflow,  X is saved in L-register and 0 is placed in X-register
002    00CC                                                    if there is an overflow, it works according to user flag F24

369   ?NCGO           checks for overflow/underflow,
002   00DA               saves X in L-register , copies C in X , Z in Y , T in Z

e)  13-digit Routines  ( cf reference [3] )

-In fact, almost all the routines return 10-digit results in CPU register C and  13-digit results in CPU registers A & B:
-Register A contains the sign & exponent and register B contains the 13-digit mantissa.
-The result in A & B may be used directly to perform several operations, for example:

001  ?NCXQ     calculates           ( with the 13-digit result in A & B
060   1800          AB+1                  and the 10-digit result in C )

009   ?NCXQ      C=                       we could also write
060   1802          AB-1                     AB=AB-1

239   ?NCXQ      C=
060   188E          1/AB

044    CLRF 4       C
035    ?NCXQ      =
068    1A0D        exp(AB)

048    SETF 4       C
035    ?NCXQ      =
068    1A0D        exp(AB)-1

003    CLRF 3
084    SETF 5        C
121   ?NCXQ        =
06C   1B48          ln(AB)

305    ?NCXQ       C=
060     18C1        sqrt(AB)

-Like 2F9  060  ( 10-digit sqrt ), this last routine must be used with care:  it correctly places 0 in C but incorrectly 0000000000001  in B ( like E-12 )
-So if the argument may be equal to zero and if it is not the last operation, these words should be followed by

2EE   ?C#0 ALL
017    JC+02
02E    B=0 ALL

-Simply adding  10E  A=C ALL  is a correct alternative but we would lose the 13-digit precision.

-The following subroutines takes a 13-digit result in A & B and a 10-digit argument in C:

025    ?NCXQ   C=
060    1809        AB+C

13D   ?NCXQ    C=
060    184F      AB*C

269    ?NCXQ   C=
060    189A       AB/C

-Several routines are available to store and recall the 13-digit results:

089  ?NCXQ    AB                    stores A & B in synthetic register Q
064  1922         STO Q+            and in the scratch part of the "append" register ( denoted + here )

0D1   ?NCXQ   RCL                 recalls Q+ in CPU registers  C ( 13-digit mantissa )
064    1934        Q+                   and M  ( sign & exponent )

-If data registers are "ramselected", we must first execute 0 RAMSELECT before storing in or recalling from Q+
-This is done directly by

081   ?NCXQ    0 ramslct
064   1920         AB STO Q+

0C9   ?NCXQ    0 ramslct
064    1932         RCL Q+

-And exchanging A & B with Q+ is performed by:

0A9   ?NCXQ
064    192A        AB<>Q+

-Finally, the following routines perform operations with two 13-digit arguments, the first one in A & B, the second one in C & M

031  ?NCXQ      C=
060   180C      AB+CM

149   ?NCXQ     C=
060   1852        AB*CM

275    ?NCXQ     C=
060    189D        AB/CM

-See reference [3]  for detailed examples.

Note:

-It's sometimes useful to transform the 10-digit number in register C into a 13-digit number in A & B
-This may be done by the 3 words:

02E   B=0 ALL
0FA  B<>C M
0AE  A<>C ALL

2°)  Constants

-Constants may of course be stored into X-register by standard means.
-However, digit entry lines are very slow and they alter synthetic register Q.

a)  MAXR

-This routine executes a ROLL UP - unless the RPN stack lift is disabled - and places 9.999999999 E99 in register X.
( the functions that disable stack lift are  CLX  ENTER^  SIGMA+  SIGMA- )
-CPU flag 11 is set when the RPN stack lift is enabled, so R^ is executed in this case only.
-If you want to execute R^ in all cases, replace

18C  ?FSET 11    by    3B5  ?NCXQ
3B5  ?CXQ                  050  14ED
051   14ED

-If you want to overwrite the previous content of register X, key in neither   18C   3B5   051  nor   3B5   050

092   "R"                            "R" is coded  012h  but we must always add  80h  for the last character of a M-code routine.
018   "X"
001   "A"                             A function name or a ROM name is always written in reverse order.
00D  "M"
18C   ?FSET 11                  if CPU flag 11 is set,
3B5   ?CXQ                        we execute
051    14ED                         R^
2A0   SETDEC
04E   C=0  ALL
27A  C=C-1 M
130   LDI S&X
099   099
0E8   WRIT 3(X)
3E0   RTN

b)  Euler's Constant

-"GAMMA" executes a ROLL UP - unless the RPN stack lift is disabled - and places 0.5772156649 in X-register.

081   "A"
00D  "M"
00D  "M"
001   "A"
007   "G"
18C   ?FSET 11
3B5   ?CXQ
051    14ED
04E   C=0  ALL
35C   PT=12
150   LD@PT- 5
1D0  LD@PT- 7
1D0  LD@PT- 7
090   LD@PT- 2
050   LD@PT- 1
150   LD@PT- 5
190   LD@PT- 6
190   LD@PT- 6
110   LD@PT- 4
250   LD@PT- 9
2A0  SETDEC
266   C=C-1  S&X
0E8   WRIT 3(X)
3E0   RTN

3°)  Stack Operations

a)  Y<>Z

-This routine swaps the contents of registers Y and Z

09A   "Z"
03E    ">"
03C   "<"
019   "Y"
10E   A=C ALL
0A8  WRIT 2(Y)
0AE  A<>C ALL
068   WRIT 1(Z)
3E0   RTN

b)  Y<>T

-Likewise, "Y<>T" swaps the contents of registers Y and T

094    "T"
03E    ">"
03C   "<"
019   "Y"
10E   A=C ALL
046   C=0 S&X
270   RAM SLCT
0A8  WRIT 2(Y)
0AE  A<>C ALL
028   WRIT 0(T)
3E0   RTN

c)  Z<>T

-And similarly, "Z<>T" exchanges the contents of registers Z and T

094    "T"
03E    ">"
03C   "<"
01A   "Z"
10E   A=C ALL
046   C=0 S&X
270   RAM SLCT
068   WRIT 1(Z)
0AE  A<>C ALL
028   WRIT 0(T)
3E0   RTN

-You can create other routines like  "L<>Y" , "L<>Z" , "L<>T"  in the same way.

d)  ST<>A

-"ST<>A"  swaps the contents of the stack registers X , Y , Z with the "synthetic" registers M , N , O respectively.
-This routine may be used to compute the dot product and the cross product below.

081   "A"
03E   ">"
03C   "<"
014   "T"
013   "S"
10E   A=C ALL
0E8   WRIT 3(X)
0AE  A<>C ALL
168   WRIT 5(M)
10E   A=C ALL
0A8   WRIT 2(Y)
0AE   A<>C ALL
1A8   WRIT 6(N)
10E   A=C ALL
068   WRIT 1(Z)
0AE  A<>C ALL
1E8   WRIT 7(O)
3E0   RTN

e)  DUP

-This routine copies the content of register X in registers Y , Z , T  ( like ENTER^  ENTER^  ENTER^ )

090   "P"
015   "U"
004   "D"
0A8  WRIT 2(Y)
068   WRIT 1(Z)
028   WRIT 0(T)
3E0   RTN

4°)  13 Functions

a)  X+1 & X-1

-It is often useful to add ( or subtract ) one to register X
-If X contains an integer, this can be done by  ISG X  TEXT0  or  DSE X  TEXT0
-Otherwise,  1  +  or  1  -  work but the stack is changed and synthetic register Q is altered

0B1   "1"
02B   "+"
018   "X"
2A0   SETDEC             if you want to check for alpha data, replace this line ( SETDEC ) by the 2 words  361  ?NCXQ
00E   A=0 ALL             A                                                                                                                         050  14D8
35C   PT=12                 =
162   A=A+1 @PT       1
01D  ?NCXQ               C=
060   1807                    A+C
0E8   WRIT 3(X)
3E0   RTN

0B1   "1"
02D   "-"
018   "X"
2A0   SETDEC             if you want to check for alpha data, replace this line by the 2 words  361  ?NCXQ
02E   B=0 ALL                                                                                                                    050  14D8
0FA  B<>C M
0AE  A<>C ALL
009   C=
060   AB-1
0E8   WRIT 3(X)
3E0   RTN

b)  X/2

-You can use  ST+ X  to double the content of register X without disturbing the stack or status register Q.
-Likewise, "X/2"  divides the content of X-register by 2.

0B2   "2"
02F   "/"
018   "X"
2A0   SETDEC             if you want to check for alpha data, replace this line by the 2 words  361  ?NCXQ
10E   A=C ALL                                                                                                                    050  14D8
04E   C=0 ALL            C
35C  PT=12                 =
090   LD@PT- 2          2
261   ?NCXQ              C=
060   1898                   A/C
0E8   WRIT 3(X)
3E0   RTN

c)  Rounding to the nearest integer

-"RND0" is equivalent to  FIX 0    RND  except that the display setting is unchanged.

0B0   "0"
004   "D"
00E   "N"
012   "R"
2A0   SETDEC             if you want to check for alpha data, replace this line by the 2 words  361  ?NCXQ
128   WRIT 4(L)                                                                                                                   050  14D8
130   LDI S&X
080   080
0C5  ?NCXQ
028   0A31
0E8   WRIT 3(X)
3E0   RTN

d)  FLOOR & FRC2

FLOOR(x) is the greatest integer less than or equal to x.  FLOOR(x) = INT(x)  for positive arguments but not always for negative x
FRC2(x) = x - FLOOR(x)      FRC2(x) and FRC(x)  are usually different if x < 0

-These functions are useful for Calendar routines.
-FRC2 is a periodic function and is non-negative.

092    "R"
00F   "O"
00F   "O"
00C   "L"
006    "F"
108    SETF 8                       we use a CPU-flag ( here flag 8 ) to avoid calling FRC2 as a subroutine
033    JNC +06
0B2   "2"
003   "C"
012   "R"
006   "F"
104    CLRF 8
2A0   SETDEC                     if you want to check for alpha data, replace SETDEC by the 2 words  361  ?NCXQ
128    WRIT 4(L)                                                                                                                             050  14D8
084    CLRF 5                     C
0ED   ?NCXQ                     =
064    193B                      frc(C)
2FE   ?C#0 MS
033    JNC +06
00E    A=0 ALL                  A
35C   PT=12                       =
162   A=A+1 @PT             1
01D  ?NCXQ                     C=
060   1807                         A+C
0E8   WRIT 3(X)
10C   ?FSET 8
3A0   ?NC RTN
2BE   C=-C-1 MS              C=-C
10E    A=C ALL
01D  ?NCXQ                     C=
060   1807                         A+C
0E8   WRIT 3(X)
3E0   RTN

-For instance,

-1.4   XEQ "FLOOR"  gives   -2
-1.4   XEQ "FRC2"     gives   0.6

e)  Cube

"X^3" saves x in L-register and calculates x3 without disturbing registers Y Z T
It's faster than  3  Y^X
This routine doesn't check for overflow/underflow

0B3    "3"
01E    "^"
018    "X"
2A0   SETDEC                  if you want to check for alpha data, replace SETDEC by the 2 words  361  ?NCXQ
128    WRIT 4(L)                                                                                                                          050  14D8
10E    A=C ALL
135   ?NCXQ                    C=
060   184D                        A*C
13D   ?NCXQ                    C=
060   184F                        AB*C
0E8   WRIT 3(X)
3E0   RTN

f)  Cube Root

-"CBRT" computes the cube root of a real number.
-Unlike  3   1/X   Y^X  ,  it works for negative numbers too.
-Moreover, if  n is an integer and if n3 doesn't exceed 1010 ,  CBRT(n3) gives exactly n.

094   "T"
012   "R"
002   "B"
003   "C"
2A0  SETDEC                     if you want to check for alpha data, replace SETDEC by the 2 words  361  ?NCXQ
128   WRIT 4(L)                                                                                                                              050  14D8
2EE  ?C#0 ALL
3A0  ?NC RTN
05E  C=|C|
084  C
115  =
06C  ln(C)
04E  C
35C  =
0D0  3
269  C=
060  AB/C
044  C
035  =
068  exp(AB)
10E  A=C ALL
0F8  C=X
11E  A=C MS
0AE  A<>C ALL
0E8  X=C
3E0  RTN

-For example,   -64  XEQ "CBRT"  yields exactly  -4

g)  Y^X

-The built-in function Y^X produces a DATA ERROR if X = Y = 0
-It's often preferable to get 0^0 = 1, for instance to compute Bessel's functions.

098   "X"
01E   "^"
019   "Y"
10E   A=C  ALL
355   ?NCXQ              this subroutine checks A and C for alphadata
050   14D5                  executes A<>C  and  SETDEC
128   WRIT4(L)          saves X in L-register
2EE   ?C#0?  ALL
027   JC+ 04
35C   PT= 12             C =
050    LD@PT-1         1
023   JNC+04
3C4   ST=0                 C
045   ?NCXQ              =
06C   1B11                Y^C
0E8   WRIT3(X)
0A8   WRIT2(Y)
046   C=0 S&X
270   RAMSLCT
068   WRIT1(Z)
3E0   RTN

XEQ "Y^X"  will work like the built-in function Y^X but it yields 00 = 1

-However, this M-Code routine does not check for overflows and underflows.

h)  XROOT

-This function calculates the x-th root of y if y is non-negative.
-Otherwise, it computes sign(y) [abs(y)^(1/x)]
-So, it's not an orthodox XROOT !

-It will return correctly cube root ( -64 ) = -4   exactly
-But it will also give square root ( -9 ) = -3 !!!

-This function is actually a continuation of  y^(1/x)  by a symmetry with respect to the origine O.

-There is no check for alpha data or under/overflow.

094  "T"
00F  "O"
00F  "O"
012  "R"
018  "X"
2A0  SETDEC
0B8  C=Y
2EE  ?C#0 ALL
05B  JNC+11d
05E  C=|C|
084  C
115  =
06C  LN(C)
0F8  C=X
269  C=
060  AB/C
044  C
035  =
068  exp(AB)
10E  A=C ALL
0B8  C=Y
11E  A=C MS
0F8  C=X
128  L=C
0AE  A<>C ALL
0E8  X=C
078  C=Z
0A8  Y=C
046  C
270  =
038  T
068  Z=C
3E0  RTN

-For instance,  32  CHS  ENTER^   5   XEQ "XROOT"  gives  -2

-X is saved in L-register.
-If Y= 0 , the final result will be 0 too !
-Of course, you can improve this routine to get DATA ERROR if y < 0 and x mod 1 # 0

i)  CNK

-Since the suroutine  3B1   ?NCXQ   calculates fact(C) up to C = 99 , we can use it to calculate the binomial coefficients Cnk = n!/[k!(n-k)!]
060    18EC

08B   "K"
00E   "N"
003   "C"
10E   A=C  ALL
355   ?NCXQ                 ckecks C and A for alpha data
050   14D5                      and executes SETDEC
3B1  ?NCXQ                       C=
060   18EC                        fact(C)
070   N=C ALL
2BE   C=-C-1 MS              C=-C
10E   A=C ALL
01D  ?NCXQ                      C=
060   1807                         A+C
3B1  ?NCXQ                       C=
060   18EC                        fact(C)
10E   A=C ALL
0B0   C=N ALL
135   ?NCXQ                    C=
060   184D                        A*C
070   N=C ALL
3B1  ?NCXQ                       C=
060   18EC                        fact(C)
10E   A=C ALL
0B0   C=N ALL
261   ?NCXQ                   C =
060   1898                        A/C
369   ?NCGO                saves X in L-register, copies C in X
002   00DA                    Z in Y , T in Z

-For example,   99  ENTER^
12  XEQ "CNK"  yields  9.243705252 E14

-The results will be meaningless if n is a positive integer greater than 99

j)  Degree of a Polynomial

-When performing multiplications of polynomials - whose control numbers are bbb.eee - we often need the degree of these polynomials  ( deg f = eee - bbb )
and the address of the next free register ( eee + 1 )
-"DEG F" takes bbb.eee in X-register and returns deg f  in X-register and eee+1 in L-register

086  "F"
020  " "
007  "G"
005  "E"
004  "D"
2A0  SETDEC                     if you want to check for alpha data, replace SETDEC by the 2 words  361  ?NCXQ
088   SETF 5                       C                                                                                                         050  14D8
0ED   ?NCXQ                     =
064    193B                      int(C)
2BE   C=-C-1 MS           C=-C
070    N=C  ALL
084   CLRF 5                      C
0ED   ?NCXQ                    =
064    193B                      frc(C)
226    C=C+1 S&X            C
226    C=C+1 S&X            =
226    C=C+1 S&X        1000 C
10E   A=C  ALL
0F0   C<>N  ALL
01D   ?NCXQ                    C=
060    1807                        A+C
0E8   WRIT3(X)
00E   A=0  ALL                  A
35C  PT=12                        =
162   A=A+1 @PT             1
0B0  C=N  ALL
01D   ?NCXQ                    C=
060    1807                        A+C
128   WRIT4(L)
3E0   RTN

k)  X/E3 & X*E3

-These short routines divide ( respectively multiply ) X-register by 1000
without disturbing registers Y , Z , T , L.
-Unlike  E3  /   or  E3  *  , synthetic register Q is unchanged.

0B3   "3"
005   "E"
02F   "/"
018   "X"
2A0   SETDEC             if you want to check for alpha data, replace this line ( SETDEC ) by the 2 words  361  ?NCXQ
2EE   ?C#0 ALL                                                                                                                                      050  14D8
3A0   ?NC RTN
266    C=C-1 S&X
266    C=C-1 S&X
266    C=C-1 S&X
0E8   WRIT 3(X)
3E0   RTN

0B3   "3"
005   "E"
02A   "*"
018   "X"
2A0   SETDEC             if you want to check for alpha data, replace this line ( SETDEC ) by the 2 words  361  ?NCXQ
2EE   ?C#0 ALL                                                                                                                                      050  14D8
3A0   ?NC RTN
226    C=C+1 S&X
226    C=C+1 S&X
226    C=C+1 S&X
0E8   WRIT 3(X)
3E0   RTN

5°)  Random Number Generator

-"RAND" uses the timer to produce pseudo-random numbers r between 0 and 1 ( 0 < r < 1 ). I don't know if r = 0 may happen.
-The content of register X is saved in L-register

084   "D"
00E   "N"
001   "A"
012   "R"
128   WRIT 4(L)
130   LDI S&X
010   010                         The user memory must be deselected by selecting
270   RAM SLCT            a non-existent RAM ( at 010h )
130   LDI S&X                then the Timer
0FB  0FB                         is
3F0   PRPH SLCT           selected
3E8   WRIT 15(e)            Set the A/B pointer to A
1BC  RCR 11                  Rotates C-register 11 digits right
046   C=0 S&X               The user memory
270   RAM SLCT            is selected again
05E   C=0 MS
130   LDI S&X
041   041
2A0  SETDEC
10E   A=C ALL
04E   C=0 ALL
35C   PT=12
1D0   LD@PT- 7
210   LD@PT- 8
110   LD@PT- 4
050   LD@PT- 1
090   LD@PT- 2
250   LD@PT- 9
190   LD@PT- 6
1D0  LD@PT- 7
210   LD@PT- 8
1D0  LD@PT- 7
044   CLRF 4                   C
070   N=C ALL                 =
171   ?NCXQ                   A
064   195C                       mod C
10E   A=C ALL
0B0   C=N ALL
261   ?NCXQ                   C =
060   1898                        A/C
0E8   WRIT 3(X)
3E0   RTN

6°)  3D-Vectors

a)  Norm

-"NORM"  save X-register in L-register and returns  ( x2 + y2 + z2 ) 1/2  in X-register
-The other stack registers are undisturbed.

08D   "M"
012    "R"
00F   "O"
00E   "N"
2A0   SETDEC
10E    A=C ALL
135    ?NCXQ                    C=
060    184D                        A*C
128    WRIT 4(L)
10E    A=C ALL
135    ?NCXQ                   C=
060    184D                        A*C
025   ?NCXQ                    C=
060    1809                       AB+C
070    N=C ALL
128    WRIT 4(L)
10E    A=C ALL
135    ?NCXQ                    C=
060    184D                        A*C
0B0    C=N ALL
025   ?NCXQ                    C=
060    1809                       AB+C
305    ?NCXQ                   C=
060    18C1                    sqrt(AB)
0E8    WRIT 3(X)
3E0    RTN

-This routine does not check for alpha data.

b)  Dot Product

-Here, we assume that X- Y- Z- registers contain the 3 components of a vector U(x,y,z)
and that the synthetic registers M , N , O contain the 3 components of another vector V(x',y',z')

"DOT" saves x in L-registers and returns the dot product  U.V = xx' + yy' + zz'  in X-register
without disturbing the other stack registers or the "alpha registers".

094    "T"
00F    "O"
004    "D"
2A0   SETDEC
128    WRIT 4(L)
10E    A=C ALL
135    ?NCXQ                    C=
060    184D                        A*C
070    N=C ALL
10E    A=C ALL
135    ?NCXQ                    C=
060    184D                        A*C
0B0   C=N ALL
025   ?NCXQ                    C=
060    1809                       AB+C
070    N=C ALL
10E    A=C ALL
135    ?NCXQ                    C=
060    184D                        A*C
0B0   C=N ALL
025   ?NCXQ                    C=
060    1809                       AB+C
0E8    WRIT 3(X)
3E0    RTN

-This routine does not check for alpha data.
-Even if you add  355  ?NCXQ   at the proper places, the HP-41 may not always identify non-numeric data in the synthetic registers!
050  14D8

-So, this routine should be used carefully...

c)  Cross Product

-Like "DOT", "CROSS" assumes that X- Y- Z- registers contain the 3 components of a vector U(x,y,z)
and that the synthetic registers M , N , O contain the 3 components of another vector V(x',y',z')

"CROSS" saves x in L-registers and returns the cross product  UxV (x",y",z")  in registers X , Y , Z respectively
without disturbing the other stack registers or the "alpha registers".

093    "S"
013    "S"
00F    "O"
012    "R"
003    "C"
2A0    SETDEC
10E    A=C ALL
135    ?NCXQ                    C=
060    184D                        A*C
2BE   C=-C-1 MS              C=-C
128    WRIT 4(L)
10E    A=C ALL
135    ?NCXQ                    C=
060    184D                        A*C
025   ?NCXQ                    C=
060    1809                       AB+C
128    WRIT 4(L)               here, L-register = z"
10E    A=C ALL
135    ?NCXQ                    C=
060    184D                        A*C
2BE   C=-C-1 MS              C=-C
070    N=C ALL
10E    A=C ALL
135    ?NCXQ                    C=
060    184D                        A*C
0B0    C=N ALL
025   ?NCXQ                    C=
060    1809                       AB+C
070    N=C ALL                here, the CPU register N = x"
10E    A=C ALL
135    ?NCXQ                    C=
060    184D                        A*C
2BE   C=-C-1 MS              C=-C
0A8   WRIT 2(Y)
10E    A=C ALL
135    ?NCXQ                    C=
060    184D                        A*C
025   ?NCXQ                    C=
060    1809                       AB+C
0A8   WRIT 2(Y)              Y now contains y"
068    WRIT 1(Z)
128    WRIT 4(L)
0B0    C=N ALL
0E8    WRIT 3(X)
3E0    RTN

-This routine does not check for alpha data.
-Even if you add  355  ?NCXQ   at the proper places, the HP-41 may not always identify non-numeric data in the synthetic registers!
050  14D8
-So, this routine should be used carefully...

-The 3 routines ST<>A  CROSS and DOT may be used to calculate a determinant of order 3.
-For instance, if

a , a' , a" are stored in R01 , R02 , R03
b , b' , b" are stored in R05 , R06 , R07
c , c' , c" are stored in R12 , R13 , R14   respectively,

the short program:

 01  LBL "D3"  02  RCL 14  03  RCL 13  04  RCL 12  05  ST<>A  06  RCL 07  07  RCL 06  08  RCL 05  09  CROSS  10  ST<>A  11  RCL 03  12  RCL 02   13  RCL 01  14  DOT  15  CLA   16  END

will calculate

| a   b   c |
D =  | a'  b'  c' |
| a" b" c" |

7°)  Statistics

a)  Linear Regression

-The following routines ( COV  CORR  LR  LRY )
calculates the sample covariance, the coefficient of correlation, the least-squares line and the linear estimate.
-All work like built-in functions: saving X in L-register, without disturbing the stack.
-However, scratch register Q is altered ( like MEAN and SDEV do ) and there is no check for OVERFLOW or UNDERFLOW
-You'll get ALPHA DATA if one of the statistics registers contains an alpha string and NON EXISTENT if one of the statistics registers doesn't exist.

-If the least-squares line is  y = a.x + b

"LR"   gives  a   in X-register
and  b   in Y-register  ( the previous content of Y-register is lost )

-These routines are interconnected but CPU-flags 6 7 8 are used to avoid subroutine calls.

099   "Y"
012   "R"
00C  "L"
288   SETF 7
023   JNC +04
092   "R"
00C  "L"
284   CLRF 7
104   CLRF 8
033   JNC +06
092   "R"
012   "R"
00F   "O"
003   "C"
108   SETF 8
144   CLRF 6
02B   JNC +05
096   "V"
00F   "O"
003   "C"
148   SETF 6
3F9   ?NCXQ
070   1CFE
10E   A=C ALL
128   WRIT 4(L)
0B0   C=N ALL
135    ?NCXQ                    C=
060    184D                        A*C
2BE   C=-C-1 MS              C=-C
268   WRIT 9(Q)
11C   PT=8                         C
3BD  ?NCXQ                     =
070   1CEF                         Sum xy
070   N=C ALL
29C  PT=7                          C
3BD  ?NCXQ                     =
070   1CEF                         n
10E   A=C ALL
046   C=0 S&X
270   RAM SLCT
0AE  A<>C ALL
268   WRIT 9(Q)
135    ?NCXQ                    C=
060    184D                        A*C
10E   A=C ALL
0B0   C=N ALL
01D   ?NCXQ                    C=
060    1807                        A+C
070   N=C ALL
00E   A=0 ALL                  A
1BE   A=A-1 MS               =
35C   PT=12                       -
162   A=A+1 @PT             1
01D   ?NCXQ                    C=
060    1807                        A+C
10E   A=C ALL
0B0   C=N ALL
0AE  A<>C ALL
261   ?NCXQ                   C =
060   1898                        A/C
0E8   WRIT 3(X)
14C   ?FSET 6
360   ?C RTN                   end of  "COV"
084   CLRF 5                    C
35C  PT=12                      = sx
04D  ?NCXQ                   N
074   1D13                       = sy
10E   A=C ALL
10C   ?FSET 8
013    JNC +02
0B0   C=N
135    ?NCXQ                    C=
060    184D                        A*C
10E    A=C ALL
0AE   A<>C
261   ?NCXQ                   C =
060   1898                        A/C
0E8   WRIT 3(X)
10C   ?FSET 8
360    ?C RTN                  end of  "CORR"
35C   PT=12                     C
001   ?NCXQ                   = x bar
074   1D00                    N = y bar
10E   A=C ALL
135    ?NCXQ                    C=
060    184D                        A*C
2BE   C=-C-1 MS              C=-C
10E    A=C ALL
0B0   C=N ALL
01D   ?NCXQ                    C=
060    1807                        A+C
28C   ?FSET 7
01F    JC +03
0A8   WRIT 2(Y)
3E0    RTN                        end of "LR"
268    WRIT 9(Q)
10E    A=C ALL
135    ?NCXQ                    C=
060    184D                        A*C
10E    A=C ALL
01D   ?NCXQ                    C=
060    1807                        A+C
0E8    WRIT 3(X)
3E0    RTN                         end of "LRY"

b)  Recalling the Statistics Registers

RCL S  ( RCL SIGMA ) saves the content of X-register in L-register, cheks that the statistics registers do exist and returns:

Sum x   in X-erister
Sum y   in Y-register                     Sum xy  in synthetic register M
Sum x2 in Z-register          and            n      in synthetic register N
Sum y2 in T-register

0CE   "S"
020    " "                        ( one space )
00C   "L"
003   "C"
012   "R"
260   SETHEX
128   WRIT 4(L)
1BC  RCR 11
070   N=C ALL
10E   A=C ALL
130   LDI S&X
1FB  507d                          this value is correct for an HP-41CV or CX or an HP-41C with a Quad memory module.
306   ?A<C S&X
381   ?NCGO
00A  02E0                         NON EXISTENT is displayed if the statistics registers do not exist
130   LDI S&X
004   004
0EE   C<>B ALL
130   LDI S&X
005   005
10E   A=C ALL
0B0   C=N ALL
206   C=C+A S&X
270   RAM SLCT
158   M=C ALL
0AE  A<>C ALL
10E   A=C ALL
326   ?A<B S&X
017   JC +02
226   C=C+1 S&X
270   RAM SLCT
198   C=M ALL
2F0   WRIT DATA
1A6   A=A-1 S&X
393   JNC -0E
04E   C=0 ALL
270   RAM SLCT
10E   A=C ALL
028   WRIT 0(T)
0AE  A<>C ALL
0E8   WRIT 3(X)
3E0   RTN

8°)  3 Tests

a)  X=1?

-In a program,  "X=1?"  skips the following line if X-register is different from 1.
-If the next line is an END or "the" .END. this line is not skipped and the program pointer is unchanged.
-Executed from the keyboard, it has the same effects and the HP-41 does not display YES or NO.

0BF    "?"
031    "1"
03D    "="
018    "X"
00E   A=0 ALL                 A
35C   PT=12                     =
162   A=A+1 @PT           1
36E   ?A#C ALL
3A0   ?NC RTN
141    ?NCXQ                      These 6 words  ( 141  0A4  3E5  0A8  0BD  08C )  skip one program line.
0A4    2950                           Repeat this sequence if you want to skip 2 lines or more.
3E5    ?NCXQ                      Be sure that the CPU is set in Hexadecimal mode ( 260  SETHEX ). It's not necessary here since it's always the default.
0A8    2AF9
0BD   ?NCXQ                      Here, the last 3 words may be replaced by only 2 words:      0BD   ?NCGO
08C    232F                                                                                                                       08E    232F
3E0    RTN

b)  X=N?

-In a program,  "X=N?"  skips the following line if X-register is not an integer.
-It is equivalent to  FRC  X=0?  except that the stack is unchanged.

0BF    "?"
00E    "N"
03D    "="
018    "X"
2A0   SETDEC                     if you want to check for alpha data, replace  2A0  SETDEC by the 2 words  361  ?NCXQ
084    CLRF 5                      C                                                                                                                  050  14D8
0ED   ?NCXQ                      =
064    193B                        frc(C)
2EE   ?C#0 ALL
3A0   ?NC RTN
260    SETHEX
141    ?NCXQ
0A4    2950
3E5    ?NCXQ
0A8    2AF9
0BD   ?NCGO
08E    232F

c)  X#Y??

-"X#Y??"  allows to stop a loop when 2 successive approximations ( in registers X & Y ) are almost equal.
-So, it can replace  "X#Y?"  to avoid infinite loops when using Newton's method ... and so on ...

0BF   "?"
03F   "?"
019   "Y"
04D  "#"
018   "X"
10E   A=C ALL
36E   ?A#C ALL
0DB   JNC +1Bh                 if you want to check for alpha data, replace the 2 words  0DB   by  the 3 words   0E3   JNC +1Ch
2A0   SETDEC                                                                                                           2A0                              355   ?NCXQ
2EE   ?C#0 ALL                                                                                                                                               050   14D5
017    JC +02
0AE   A<>C ALL
2BE   C=-C-1 MS              C=-C
070    N=C ALL
01D   ?NCXQ                    C=
060    1807                        A+C
10E    A=C ALL
0B0    C=N ALL
261   ?NCXQ                    C =
060   1898                         A/C
05E   C=0 MS                   C=|C|
10E   A=C ALL
04E   C=0 ALL                  C=
2BE   C=-C-1 MS
35C   PT=12
090    LD@PT- 2
21C   PT=2
250    LD@PT- 9
250    LD@PT- 9
050    LD@PT- 1             -2 E-9
01D   ?NCXQ                    C=
060    1807                        A+C
2FE   ?C#0 MS
3A0   ?NC RTN
260    SETHEX
141    ?NCXQ
0A4    2950
3E5    ?NCXQ
0A8    2AF9
0BD   ?NCGO
08E    232F

9°)  Hyperbolic Functions

-The following routines compute hyperbolic sine, cosine, tangent and their inverses:  SINH  COSH  TANH   ASINH  ACOSH  ATANH
-They use the same formulas as the programs listed in "Hyperbolic Functions for the HP-41"
-As usual, x is saved in L-register and Y Z T are undisturbed.
-However, these M-Code routines do not check for overflow/underflow.

Hyperbolic Sine & Cosine

088   "H"
013   "S"
00F   "O"
003   "C"
044    CLRF 4
033    JNC +06
088    "H"
00E   "N"
009    "I"
013   "S"
048    SETF 4
2A0   SETDEC                  if you want to check for alpha data, replace SETDEC by the 2 words  361  ?NCXQ
128    WRIT 4(L)                                                                                                                          050  14D8
2BE   C=-C-1 MS
029    ?CXQ                   C=
069    1A0A            exp(C) or exp(C)-1  according to CPU flag 4
04C   ?FSET 4
013    JNC +02
2BE   C=-C-1 MS            C=-C
0E8    WRIT 3(X)
029    ?NCXQ                   C=
068    1A0A            exp(C) or exp(C)-1  according to CPU flag 4
025   ?NCXQ                    C=
060    1809                       AB+C
04E   C=0 ALL                   C
35C  PT=12                        =
090   LD@PT- 2                 2
269   ?NCXQ                   C=
060   189A                      AB/C
0E8   WRIT 3(X)
3E0   RTN

Hyperbolic Tangent

088    "H"
00E   "N"
001    "A"
014    "T"
048    SETF 4
2A0   SETDEC                  if you want to check for alpha data, replace SETDEC by the 2 words  361  ?NCXQ
128    WRIT 4(L)                                                                                                                          050  14D8
10E   A=C ALL
01D   ?NCXQ                    C=
060    1807                        A+C
035    ?NCXQ                   C=
068    1A0D                  exp(AB)-1
0E8    WRIT 3(X)
001  ?NCXQ                       C=
060   1800                         AB+1
001  ?NCXQ                       C=
060   1800                         AB+1
10E   A=C ALL
0AE   A<>C ALL
261   ?NCXQ                   C=
060   1898                        A/C
0E8   WRIT 3(X)
3E0   RTN

Inverse Hyperbolic Sine

088   "H"
00E   "N"
009    "I"
013   "S"
001   "A"
2A0   SETDEC                  if you want to check for alpha data, replace SETDEC by the 2 words  361  ?NCXQ
128    WRIT 4(L)                                                                                                                          050  14D8
10E   A=C ALL
135    ?NCXQ                 C=
060    184D                    A*C
0E8   WRIT 3(X)
001  ?NCXQ                   C=
060   1800                     AB+1
305    ?NCXQ                 C=
060    18C1                    sqrt(AB)
001  ?NCXQ                   C=
060   1800                     AB+1
10E   A=C ALL
0AE   A<>C ALL
261   ?NCXQ                   C=
060   1898                        A/C
05E   C=0 MS              C= | C |
025  ?NCXQ                 C=
060   1809                    AB+C
084    CLRF 5                  C
1CD   ?NCXQ                 =
06C    1B73                 Ln1+C
10E   A=C ALL
11E   A=C MS
0AE  A<>C ALL
0E8   WRIT 3(X)
3E0   RTN

Inverse Hyperbolic Cosine

088   "H"
013   "S"
00F   "O"
003   "C"
001   "A"
2A0   SETDEC                  if you want to check for alpha data, replace SETDEC by the 2 words  361  ?NCXQ
128    WRIT 4(L)                                                                                                                          050  14D8
02E   B=0 ALL
0FA  B<>C M
0AE  A<>C ALL
009   ?NCXQ                  C=
060   1802                      AB-1
0E8   WRIT 3(X)
04E   C=0 ALL                 C
35C   PT=12                     =
090   LD@PT-2               2
025  ?NCXQ                 C=
060   1809                    AB+C
13D    ?NCXQ                 C=
060    184F                    AB*C
305    ?NCXQ                 C=
060    18C1                 sqrt(AB)
2EE   ?C#0 ALL
017    JC+02
02E    B=0 ALL
025   ?NCXQ                 C=
060    1809                   AB+C
084    CLRF 5                  C
1CD   ?NCXQ                 =
06C    1B73                 Ln1+C
0E8   WRIT 3(X)
3E0   RTN

Inverse Hyperbolic Tangent

088    "H"
00E   "N"
001    "A"
014    "T"
001    "A"
2A0   SETDEC                  if you want to check for alpha data, replace SETDEC by the 2 words  361  ?NCXQ
128    WRIT 4(L)                                                                                                                          050  14D8
05E   C=0 MS              C= | C |
2BE   C=-C-1 MS         C=-C
00E   A=0 ALL                 A
35C   PT=12                     =
162   A=A+1 @PT           1
01D  ?NCXQ                 C=
060   1807                     A+C
0E8   WRIT 3(X)
05E   C=0 MS              C= | C |
10E   A=C ALL
01D  ?NCXQ                 C=
060   1807                     A+C
269   ?NCXQ                   C=
060   189A                     AB/C
084    CLRF 5                  C
1CD   ?NCXQ                 =
06C    1B73                 Ln1+C
10E    A=C ALL
11E    A=C MS
04E   C=0 ALL                   C
35C  PT=12                        =
090   LD@PT- 2                 2
261   ?NCXQ                   C=
060   1898                        A/C
0E8   WRIT 3(X)
3E0   RTN

10°)  5 Complex Functions

-These routines compute the product, quotient, square, inverse and square root of complex numbers.
-One can use R-P and P-R instead, but the following functions are usually faster and more accurate.
-However, they do not check for alpha data, overflow/underflow.
-Otherwise, they work like built-in functions:  x is saved in L-register and Z & T registers are undisturbed.
-ENTER the imaginary part first ( in Y-register ), then the real part in X-register.

a)  Z*Z & Z/Z

09A   "Z"
02A   "*"
01A   "Z"
104    CLRF 8
02B    JNC +05
09A   "Z"
02F    "/"
01A   "Z"
108    SETF 8
2A0   SETDEC
128    WRIT 4(L)
10E    A=C ALL
135    ?NCXQ                 C=
060    184D                    A*C
070    N=C ALL
10E   A=C ALL
046   C=0 S&X               C
270   RAM SLCT            =
135    ?NCXQ                 C=
060    184D                    A*C
10C   ?FSET 8
017    JC +02
2BE   C=-C-1 MS        C=-C
10E   A=C ALL
0B0   C=N ALL
01D  ?NCXQ                 C=
060   1807                     A+C
0E8   WRIT 3(X)
10E   A=C ALL
135    ?NCXQ                 C=
060    184D                    A*C
10C   ?FSET 8
013    JNC +02
2BE   C=-C-1 MS         C=-C
070    N=C ALL
10E   A=C ALL
046   C=0 S&X               C
270   RAM SLCT            =
135    ?NCXQ                 C=
060    184D                    A*C
0B0   C=N ALL
025  ?NCXQ                 C=
060   1809                    AB+C
10E   A=C ALL
0AE   A<>C ALL
0A8   WRIT 2(Y)
10C   ?FSET 8
3A0  ?NC RTN            end of "Z*Z"
0AE   A<>C ALL
10E   A=C ALL
135    ?NCXQ                 C=
060    184D                    A*C
070    N=C ALL
10E   A=C ALL
135    ?NCXQ                 C=
060    184D                    A*C
0B0   C=N ALL
025  ?NCXQ                 C=
060   1809                    AB+C
070    N=C ALL
10E   A=C ALL
0B0   C=N ALL
261   ?NCXQ                   C=
060   1898                        A/C
0E8   WRIT 3(X)
10E   A=C ALL
0B0   C=N ALL
261   ?NCXQ                   C=
060   1898                        A/C
0A8   WRIT 2(Y)
3E0   RTN                  end of "Z/Z"

b)  Z^2

0B2    "2"
01E    "^"
01A   "Z"
2A0   SETDEC
128    WRIT 4(L)
10E   A=C ALL
135    ?NCXQ                 C=
060    184D                    A*C
2BE   C=-C-1 MS        C=-C
070    N=C ALL
10E   A=C ALL
135    ?NCXQ                 C=
060    184D                    A*C
0B0   C=N ALL
025  ?NCXQ                 C=
060   1809                   AB+C
2BE   C=-C-1 MS        C=-C
0E8   WRIT 3(X)
10E   A=C ALL
01D  ?NCXQ                 C=
060   1807                     A+C
13D    ?NCXQ                 C=
060    184F                    AB*C
0A8   WRIT 2(Y)
3E0   RTN

c)  1/Z

09A   "Z"
02F    "/"
031    "1"
2A0   SETDEC
128    WRIT 4(L)
10E   A=C ALL
135    ?NCXQ                 C=
060    184D                    A*C
070    N=C ALL
10E   A=C ALL
135    ?NCXQ                 C=
060    184D                    A*C
0B0   C=N ALL
025  ?NCXQ                 C=
060   1809                    AB+C
070    N=C ALL
10E   A=C ALL
0B0   C=N ALL
261   ?NCXQ                   C=
060   1898                        A/C
0E8   WRIT 3(X)
2BE   C=-C-1 MS        C=-C
10E   A=C ALL
0B0   C=N ALL
261  ?NCXQ                 C=
060   1898                     A/C
0A8   WRIT 2(Y)
3E0   RTN

d)  SQRTZ

09A   "Z"
014    "T"
012    "R"
011    "Q"
013    "S"
2A0   SETDEC
128    WRIT 4(L)
2EE   ?C#0 ALL
097    JC +18d=+12h
05E   C=0 MS          C = | C |
10E   A=C ALL
04E   C=0 ALL            C
35C  PT=12                 =
090   LD@PT- 2          2
261   ?NCXQ              C=
060   1898                   A/C
305   ?NCXQ             C=
060   18C1              sqrt(AB)
0E8   WRIT 3(X)
10E   A=C ALL
11E   A=C MS
0AE   A<>C ALL
0A8   WRIT 2(Y)
3E0   RTN                     end of the routine
10E   A=C ALL
135    ?NCXQ                 C=
060    184D                    A*C
070    N=C ALL
10E   A=C ALL
135    ?NCXQ                 C=
060    184D                    A*C
0B0   C=N ALL
025  ?NCXQ                  C=
060   1809                    AB+C
305    ?NCXQ                 C=
060    18C1                  sqrt(AB)
2EE   ?C#0 ALL
017    JC+02
02E    B=0 ALL
05E   C=0 MS            C = | C |
025   ?NCXQ                 C=
060    1809                   AB+C
070    N=C ALL
04E   C=0 ALL               C
35C  PT=12                    =
090   LD@PT- 2             2
269   ?NCXQ                C=
060   189A                   AB/C
305    ?NCXQ              C=
060    18C1               sqrt(AB)
0F0   C<>N ALL
10E   A=C ALL
01D   ?NCXQ                 C=
060    1807                     A+C
305    ?NCXQ              C=
060    18C1               sqrt(AB)
10E   A=C ALL
0AE  A<>C ALL
261   ?NCXQ                C=
060   1898                     A/C
0AE   A<>C ALL
2FE   ?C#0 MS
027   JC +04
0B0   C=N ALL
0E8   WRIT 3(X)
283   JNC -48d=-30h
0AE  A<>C ALL
05E   C=0 MS             C = | C |
0E8   WRIT 3(X)
0B0   C=N ALL
243   JNC -56d=-38h

11°)  Creating Extra-Registers

a)  Saving & Recalling X-Register

-I wrote these routines thanks to fruitful advices given by Dan Grelinger & Peter Platzer in the MoHPC forum.
-"STO W" saves the content of register X into 7 words at the addresses FD4A to FD50
-The first of these addresses are written in red below.
-FD4A to FD50 must remain unused by any routine:
-For example, start the following word at FD51

097  "W"
020    " "                        ( one space )
00F  "O"
014  "T"
013  "S"
0EE  C<>B ALL
130  LDI S&X
006  006
106  A=C S&X
15C  PT=6
3D0  LD@PT- F        change these 4 words according to the addresses you've choosen in you own ROM.
350  LD@PT- D
110  LD@PT- 4
290  LD@PT- A
0C6  C=B S&X          loop
042  C=0 @PT
040  WRIT S&X        only works if there is a MLDL type module at the address
3AE  RSHFB ALL
3AE  RSHFB ALL
23A  C=C+1 M
1A6  A=A-1 S&X
3CB  JNC -07
3E0  RTN

-After storing X-register, we need of course a routine to recall it in register X.

097  "W"
020    " "                        ( one space )
00C  "L"
003  "C"
012  "R"
130  LDI S&X
006  006
106  A=C S&X
15C  PT=6
3D0  LD@PT- F
350  LD@PT- D
110  LD@PT- 4
290  LD@PT- A
31C  PT=1
330  FETCH S&X        loop
0EA  C<>B PT<-
0EE  C<>B ALL
23C  RCR 2
0EE  C<>B ALL
23A  C=C+1 M
1A6  A=A-1 S&X
3CB  JNC -07
0EE  C<>B ALL
0E8  WRIT 3(X)
3E0  RTN

-"RCL W" simply overwrites X-register
-So, it would be better to replace the last 2 words by

028  WRIT 0(T)
3B5  ?NCGO  goto
052   14ED    roll up

b)  Saving & Recalling all the Stack

-You can create as many extra-registers as you want with routines similar to "STO W" & "RCL W"
-The following ones save and recall the 4 registers X Y Z T in a space of 4x7 = 28 words
-In my ROM, the addresses are FDA4 to FD65 ( I've deleted "STO W" & "RCL W" )
-So these programs may start at FD66 if the code begins just after the reserved space:

094  "T"
013  "S"
00F  "O"
014  "T"
013  "S"
284   CLRF 7                    using a flag to distinguish "STOST" & "RCLST" saves several words
03B  JNC+07
094  "T"
013  "S"
00C  "L"
003  "C"
012  "R"
288   SETF 7
130   LDI S&X
004   004                           if you want to save L-register too, replace this line by 005 and reserve a space of 35 words for the storage.
070   N=C ALL
15C  PT=6
3D0  LD@PT- F               change these 4 words according to the addresses you've choosen in you own ROM.
350  LD@PT- D
110  LD@PT- 4
290  LD@PT- A
0EE  C<>B ALL
0B0  C=N ALL
2E6  ?C#0 S&X
3A0  ?NC RTN                 both routines stop here
266   C=C-1 S&X
070   N=C ALL
28C  ?FSET 7
01F   JC+03
270   RAMSLCT
0EE  C<>B ALL
130   LDI S&X
006   006
106   A=C S&X
28C  ?FSET 7
08B   JNC+11h=+17d
31C  PT=1
330   FETCH S&X
0EA  C<>B PT <-
0EE   C<>B ALL
23C   RCR 2
0EE   C<>B ALL
23A   C=C+1 M
1A6   A=A-1 S&X
3CB  JNC-07
10E   A=C ALL
0B0   C=N ALL
270   RAMSLCT
0EE   C<>B ALL
2F0   WRIT DATA
06E   A<>B ALL
313   JNC-1Eh=-30d
21C  PT=2
0C6  C=B S&X
042   C=0 @PT
040   WRIT S&X
3AE  RSHFB ALL
3AE  RSHFB ALL
23A  C=C+1 M
1A6  A=A-1 S&X
3CB  JNC-07
2BB  JNC-29h=-41d

-"STOST" stores the 4 registers X Y Z T
-"RCLST" recalls X Y Z T ( the previous stack is lost. L-register is unchanged )

12°)  Largest Element of a Block of Registers

-"MAXAD" takes the control number of a block of contiguous registers
and returns the maximum element ( in absolute value ) in X and the corresponding address in Y.
-The block is defined by a control number of the form  bbb.eee ( like with ISG ) or eee.bbb'  with bbb' = bbb-1  ( like with DSE )
-Rbb is the first register, Ree is the last one

>>> The routine does not chek for alpha data.

-Z-register is saved in T and Y-register is saved in Z

084   "D"
001   "A"
018   "X"
001   "A"
00D  "M"
3B5   ?NCXQ         roll up
050    14ED
128   WRIT 4(L)
2A0   SETDEC
088    SETF 5            C
0ED   ?NCXQ           =
064    193B             int(C)
070    N=C ALL
084    CLRF 5           C
0ED   ?NCXQ           =
064    193B             frc(C)
226    C=C+1 S&X
226    C=C+1 S&X
226    C=C+1 S&X
088    SETF 5            C
0ED   ?NCXQ           =
064    193B             int(C)
260    SETHEX
38D   ?NCXQ
008    02E3
226    C=C+1 S&X
0F0   C<>N ALL
38D   ?NCXQ
008    02E3
106    A=C S&X
0B0   C=N ALL
306   ?A<C S&X
01F   JC+03
0A6   A<>C S&X
226    C=C+1 S&X
1BC   RCR 11
11A   A=C M
15A   A=A+C M
03C   RCR 3
146    A=A+C S&X
0AE   A<>C ALL
070    N=C ALL
03C   RCR 3
106   A=C S&X
130   LDI S&X
201   201h                          this value is correct for an HP-41CV or CX or an HP-41C with a Quad memory module.
306   ?A<C S&X
381   ?NCGO
00A  02E0                         NON EXISTENT is displayed if the last register of the block does not exist
04E   C=0 ALL
0E8   WRIT 3(X)
0B0   C=N ALL
0A8   WRIT 2(Y)
070   N=C ALL
106   A=C S&X
03C   RCR 3
306   ?A<C S&X
0CB  JNC+19h=+25d
0A6   A<>C S&X
270    RAMSLCT
05E    C=0 MS                C= | C |
10E    A=C ALL
046    C=0 S&X
270    RAMSLCT
2A0   SETDEC
0AE  A<>C ALL
268   WRIT 9(Q)
2BE   C=-C-1 MS          C=-C
01D   ?CXQ                    C=
061    1807                     A+C
2FE   ?C#0 MS
02B   JNC+05
0E8   WRIT 3(X)
0B0   C=N ALL
0A8   WRIT 2(Y)
260    SETHEX
0B0   C=N ALL
226   C=C+1 S&X
323   JNC-1Ch=-28d
106   A=C S&X
03C   RCR 3
1C6   A=A-C S&X              The following lines are actually given by Ken Emery in reference [1]
130    LDI S&X                    to convert an hexadecimal number in A S&X into a decimal number in C
010   010h
270   RAMSLCT
2DC  PT=13
110   LD@PT- 4
11E   A=C MS
3A1   ?NCXQ
014    05E8
0AE   A<>C ALL
11C   PT=8
04A   C=0 PT<-
270    RAMSLCT
39C   PT=0
0D0   LD@PT- 3
010    LD@PT- 0
0AE   A<>C ALL
342    ?A#0 @PT
027    JC+04
3FA   LSHFA M
1A6   A=A-1 S&X
3E3    JNC-04
0AE   A<>C ALL
2FA   ?C#0 M
017    JC+02
04E    C=0 ALL
0A8   WRIT 2(Y)
3E0    RTN

-For example,

if registers  R01  R02  R03  R04  R05
contain    1       4      6     -7       2     respectively

will return 7  in X-register  and  4  in Y-register

Notes:

-The control number is saved in L-register.
-Execution time = 2 seconds for a block of 100 registers.
-Synthetic register Q is used.

References:

[1]  Ken Emery - "HP-41 M-Code for Beginners" - Synthetix - ISBN 0-9612174-7-2
[2]  VASM listings
[3]  Ángel Martin - "HP-41 OS 13-Digit Math Routines"