41Z Breakthrough: 4-level Complex Stack


Well, maybe not a *real* breakthrough but nevertheless important enough for some :)

Get ready for an enhanced revision of the 41Z module, with FOUR levels of COMPLEX Stack... made possible by ustilizing a buffer that holds the 3rd. and 4th. levels (the standard stack keeps track of the first two, of course).

The "Complex" buffer (id#=1) is created upon calc being turned on, and maintained by the module. Totally transparent to the user. If it somehow fails, a two-level stack is always available regardless. Maybe not a perfect solution but surely the PERFECT compromise!

The module functions work more or less as before, but the 4-level stack lift and drop has been implemented into all non-monadic functions of course.

My thanks go to Peter Platzer for encouragement (his MPL module proved it was possible) and to Häkan Thörngren, who wrote the base routines for buffer creation and handling tat I've built upon.

So stay tuned... I'm in programmer's paradise this weekend (which of course includes lots of Pizza), should come up with the final release pretty soon...



PS. you said four? hmmm, no reason to go over that, provided that enough free registers exist to increase the complex buffer size...

Edited: 16 Aug 2009, 7:33 a.m.


Excellent news. I look forward to testing it.


Awesome Angel, you are clearly on a roll!! Looking forward to playing with it!

PS: thanks for the kind words, pretty sure I don't deserve them but they are very much appreciated just the same.


Glad to see you're interested, Egan & Peter. I'm done with the changes and was about to upload the new module on TOS, but there's something that is bothering me about the whole data input process.

I'll explain. Yes the new complex stack is 4-levels deep, and has the expected functionality built into it:

- Levels 1&2 are held into the standard "real' stack.

- Levels 3&4 are held into a dedicated memory buffer. They are "created" upon CALC_On event and survive power cycles and Key assignments.

- There are three new functions to manage it: ZENTER, ZRDN, ZR^, and ZCLST.

- supports Complex stack drop upon execution of non-monadic functions, with duplication of the 4th. complex-level into the 3rd. one.

- supports complex stack lift upon data recall (from a complex storage register or LastZ).

For example, suppose we want the following complex numbers input into the stack:

L4: 4-4j

L3: 3-3j

L2: 2-2j

L1: 1-1j

Either one of the following sequences will accomplish the task:

4, CHS, ENTER^, 4, ZENTER ... (or: 4, CHS, ENTER^, CHS, ZENTER)

3, CHS, ENTER^, 3, ZENTER ... (or: 3, CHS, ENTER^, CHS, ZENTER)

2, CHS, ENTER^, 2, ZENTER ... (or: 2, CHS, ENTER^, CHS, ZENTER)

1, CHS, ENTER^, 1 ..................... (or: 1, CHS, ENTER^, CHS)

The issue is the automatic stack lift. Say you have your complex numbers stored into the stack, and then you input any real number (into the X register). Then the real-stack would lift, and as a result the second complex level will "spill over" - since T is gone - partially losing its value.

I checked on the 42 and it appears to me that the same problem exists as well - only that it'll be the fourth stack level instead of the second partially losing its content.

So what gives? Do we have 4 complex stack levels that cannot be kept without losing some information upon other events? One solution (work-around rather) is using ZSTO and ZRCL to fill the stack - which BTW are prompting functions, thus will allow an easy manipulation of memory but...)

I'd like to hear your suggestions on how to deal witht this sticky glitch - pls. let me know what you think.


Edited: 17 Aug 2009, 7:59 a.m. after one or more responses were posted


You could "shadow" the entire complex stack in a buffer or two. When a destructive real stack lift occurred, you could then tidy things back up from the shadow levels 1&2. Another way to look at it would be to regard your buffers as the genuine complex stack with the 41C's stack merely displaying the first two levels.



Just a thought: maybe set a flag to put the calc in complex mode. When in complex mode all keys have their complex function (+,-,*,/,enter, etc). This way all complex numbers will stay together all the time.
This 'flag' can be a real user flag, a higher number flag or (probably preferred for compatibility reasons) just uses a byte in the header register of the buffer. A little bit trickier might be the complete reassignement. One way is to simply write a routine to assign the new functions to all keys (similar to my assugnement function in the m pl which out of lazyness I wrote in focal). The other, far more involved way would be to use the interrupt points and send any normal function to your complex equivalent if the complex mode is set (similar to the ccd modules extension of the cat and 'sto'/rcl functions.

Just a thought



I like this idea. Sorta like the 15C and flag 8. I'd also add that I think the stack should work that way as well (like 15C). The stack would contain the reals and the buffers would contain the imaginary.

As for complex input, how about number i number were i = shift 4. Seriously, who uses BEEP in interactive mode. I know nothing about MCODE or the 41 internals, so this may be impossible or impracticable.


Great minds think alike, they say...

Sure enough I've looked at the 15C implementation, with its parallel real and imaginary stacks - the imaginary stack must also be a buffer, but the difference is that the 15C OS is written to respect its needs. For instance, when you input a number in X, both the real and the imaginary stack lift, allowing for consistent operation. The only way to do this would be (again) using the dreadful IO interrupt approach, which I won't touch with a 12-foot pole so far.

Then it's the fact that all math in the 41Z module is written using the real stack as L1 and L2 complex levels, and that would have to go away if changing to the 15C-way. Not looking forward to re-writing the whole thing!

As per the data input process, I've used SHIFT-SHIFT for complex input, but this doesn't bring any benefits (when compared to the real ENTER^ for instance) because the stack lift continues to be active regardless of how you fill the complex stack.

The way it works now is: Im(z), ENTER^, Re(z), ZENTER.

It's hard to explain without seeing it work, let me send you the module and you see for yourselves.


Great to see you guys engaged on this, let's see if we can pull it off together.

Howard's "shadow" buffer is an excellent route, but I've got the feeling the required overhead for its management and synchronization with the real stack could be too heavy. Worth trying a proof of concept for sure. I was keeping the real stack part because all math happens using the real stack (things like [TOPOL], [TOREC], and other mainframe routines - this is how I first wrote the 41Z module, so it'd take a complete re-write from the ground up... ufff}

The "Complex flag" is what I have implicitely done, using the USER mode as the Complex mode (alas flag 27 then). As Peter, I have mapped the complex functions to the calculator keyboard (using a MCODE routine in my case, which in turn I lifted from one HP module - forget which one). This works beautifully while you stay within the assigned functions, and it is mostly compatible with the "normal" operation - save the infamous "stack lift" issue.

Since there's no way one can assign everything to user keys, the better approach is of course the interrupt vectors (a la CCD or ZenRom). This would either prevent or enhance the "normal" behavior of the calculator, respecting the complex stack all the time.

I always regarded this as equivalent to subclassing a process and replacing its code upon certain events in the windoze world (which I've used many times), but in the MCODE world however, such approach is beyond my programming skiils, I'm afraid. I remember experimenting with the IO interrupt, what a mess...

Here's the rub: just entering a number (i.e. pressing a numeric key) could damage things, sending the contents of T to limbo, and thus your L2 complex stack is also gone. How do you "prevent" for this, such a common occurrence?

And remember, the same thing happens on the 42S - albeit losing L4 is not as bad as losing L2, I agree...

If you're game I'll send you the current incarnation (including the commented source code), I'd like to get your comments once you've seen it work for yourselves, it's hard otherwise. Pls. send me your email addresses if not available.


I was keeping the real stack part because all math happens using the real stack (things like [TOPOL], [TOREC], and other mainframe routines - this is how I first wrote the 41Z module, so it'd take a complete re-write from the ground up... ufff}

If you don't use a buffer as the authoritative stack, then it seems to me you'd have to do stack lift detection. You could then make the real stack a genuine stack, and shadow it in the buffer. If you can detect stack lift, then you ought to be able to fix things up with the backup copy in the buffer. I'm not sure if the stack lift is directly detectable in mcode though. If not, you might be able to track it with heuristics, but that would probably be ugly in assembler. (It would be in a high level language too.) But the advantage would be you wouldn't have to rewrite all your routines that operate on the standard stack.

If you're game I'll send you the current incarnation (including the commented source code), I'd like to get your comments once you've seen it work for yourselves, it's hard otherwise. Pls. send me your email addresses if not available.

I'm back where I can program my Nov64, so I could try it out.

My address is "egbokalaka at gmail dot [the most common top level.] " :)



Maybe the entire Z stack could go into the buffer, along with its own stack lift flag. It would take 11 registers with some care and perhaps you want to (temporarily?) take some extra scratch registers in the buffer area if needed? You could even keep an open mind to allow for a larger stack when writing the code.

Another alternative would be to use an XMem file. That memory is of course not always available, but when it is, people will probably find it easier to part with some of it rather than the buffer area.

However, as you do not take that many registers, and it works on all machines, I think using the buffer area is the prettier solution.

Then provide some command(s) for retrieving numbers from the Z stack to the RPN stack.

P.S. I was taken a bit by surprise being mentioned. You are of course very welcome to use whatever code I wrote from long time ago. I am not even sure if I am worth the credit, or if I more or less borrowed the code from HP. It was a long time ago...


Greetings Håkan, glad you showed up!

There's much to thank you for - regardless of whether the code you used being "inspired" by HP's the fact is you managed to put it together in new and creative ways, and at a time when it wasn't really a done deal.

This particular buffer stuff I took from your SEED/RNDM articles published on PPCCJ back in 1986 if memory serves. More impressive were your other projects on RAMED, ARCLCHR, RSTCHK and HEXIN. All of them a source of inspiration and MCODE learning...

Thanks for your ideas on the XMem file - I agree it's probably better to stick with the buffer approach for the reasons you mentioned.

So how about dusting off those MCODE skiils of yours and rolling up your sleeves on this IO interrupt thing?




After some more pondering of the use of the io interrupt I must say I like this idea of all z numbers in the buffer. This would allow easy switching between complex and normal operation via the user key. I guess the recoding would be messy for all functins but not necessarily more so than trying the io route. (there would be quite a number of situations to be considered....) it might even bring some code simplificTion as stack drops now happen in continuous memory and there is no need to capture the break between z2 (in normal stack) and z3 (in buffer) one thing I used in the mpl is to store useful information in the header reg as this is the reg that gets 'found' (eg the absolute adress, number of stack levels, etc)
maybe we can write some generic 'get z-number' routine that could replace the read x and read y etc functions.
Anyway, I look forward to the mod and it's code, maybe we can all pitch in a little...




Function	Group   	Description
-HP 41Z Shows revision number
LASTZ Stack Complex LASTX
NXTLOG Transc. Next Ln(z)
NXTRTN Transc. Next Complex Root
W^Z Transc. Complex Y^X
X^Z Transc. Hybrid Y^X
Z- Math Complex substraction
Z* Math Complex multiplication
Z/ Math Complex division
Z^2 Transc. Complex X^2
Z^X Transc. Hybrid Y^X
Z+ Math Complex addition
Z<> Stack Exchange Z and any register
Z<>W Stack Exchange Z and W
Z=0? Comparison Is Z=0?
Z=I? Comparison Is Z=I?
Z=W? Comparison Is Z=W?
ZACOS Trig. Complex ACOS
ZACOSH Trig. Complex Hyp. ACOS
ZALOG Transc. Complex 10^X
ZARG Geometry Argument of Z
ZASIN Trig. Complex ASIN
ZASINH Trig. Complex Hyp. ASIN
ZATAN Trig. Complex ATAN
ZATANH Trig. Complex Hyp. ATAN
ZAVIEW Stack Shows Complex Z
ZCLST Stack Clears Z-Stack
ZCONJ Geometry Changes sign of Im(Z)
ZCOS Trig. Complex COS
ZCOSH Trig. Complex Hyp. COS
ZDBL Math Calculates 2*Z (Doubles it)
ZENTER Stack Lifts Complex Stack
ZEXP Transc. Complex e^X
ZHALF Math Calculates Z/2 (halves it)
ZIN? Comparison Is Z inside the unit circle?
ZINV Math Complex Inversion
ZKEYS Block Key Assignments
ZLN Transc. Complex LN
ZLOG Transc. Complex LOG
ZMOD Geometry Module of Z
ZNEG Geometry Complex CHS
ZNORM Geometry Norm of Z (I.e. square of Module)
ZOUT? Comparison Is Z outside the unit circle?
ZPOL Geometry Converts to Polar notation
ZR^ Stack Z-Stack Roll Up
ZRCL Stack Complex RCL
ZRDN Stack Z-Stack Roll Down
ZREC Geometry Convers to Rectangular notation
ZSIGN Geometry Complex SIGN
ZSIN Trig. Complex SIN
ZSINH Trig. Complex Hyp. SIN
ZSQRT Transc. Complex SQRT
ZSTO Stack Complex STO
ZTAN Trig. Complex TAN
ZTANH Trig. Complex Hyp. TAN
ZTRP Stack Exchanges Re(Z) and Im(Z)
ZUNIT? Comparison Is Z on the unit circle?
ZVIEW Stack Shows Complex number
ZWANG Geometry Angle between Z and W
ZWCROSS Geometry Cross product of Z and W
ZWDET Geometry Determinant of Z and W
ZWDIST Geometry Distance between Z and W
ZWDOT Geometry Dot product of Z and W
ZWLINE Geometry Line equation defined by Z and W
ZPROOT Focal Roots of complex polynomials
ZQUAD Focal Complex Quadratic Eq. Roots
ZGAMMA Focal Complex Gamma
ZMTV Focal Multi-Value Complex functions


I too am leaning towards the full complex stack solution. I *think* I have figured out all the situations where it would come to place, see if you find something missing:

Design Criteria:

1. The complex Buffer has 5 levels, from L0 (for lastZ) to L4 - plus its header. Each level holds 2 registers, thus 11 regs in total, called b0 (header) to b10 (imaginary part of L4).

2. The Mapping with the real stack is as follows:
X=b3, Y=b4, Z=b5, T=b6. L=b1

3. All functions take their input from X,Y (if monadic) or [X,Y,Z,T] if dual. The output is placed also on the stack, no interaction with the buffer during the calculation phase.

4. It's upon the function to save the operand into L0 (LastZ) prior to executing the math, and to synchronize the stack with the complex buffer upon completion.

Once these criteria are defined, we need to properly structure the "Special events", as follows:


1. writes (X,Y) into L1 (b3,b4)

2. buffer normal lift: L3->L4; L2->L3; L1->L2

3. synch: writes [b3-b6] to stack [X,Y,Z,T]
(this is for consistency with other processes, as it only requires writing (X,Y) into (T,Z).

This is the key point that solves the previous issue: now it doesn't matter what happens to the real stack (like T spilling over) because the complex buffer L2 remains untouched upon execution of ZENTER^. Naturally one must be disciplined and ALWAYS execute ZENTER^ upon complex data input, or otherwise the real stack and the complex buffer will de-synchronize.


1. buffer long lift: L3->L4; L2->L3; L1->L2; L0->L1

2. synch: writes [b3-b6] to stack [X,Y,Z,T]


1. saves L1 (b3,b4) into scratch (M,N), keeps Alpha untouched

2. buffer drop: L2->L1; L3->L2; L4->L3

3. writes scratch into L4 (b9,b10)

4. synch: writes [b3-b6] into stack [X,Y,Z,T]

D.- ZR^.

1. saves L4 (b9,b10) into scratch (M,N)

2. buffer normal lift: L3->L4; L2->L3;, L1->L2

3. writes scratch into L1 (b3,b4)

4. synch: writes [b3-b6] into stack [X,Y,Z,T]

E.- ZRCL__.

1. buffer normal lift: L3->L4; L2->L3; L1->L2

2. writes X,Y into L1

3. synch: writes [b3-b6] into stack [X,Y,Z,T]

(done like this for consistency sake, other sequences are also possible)

And here's how these are being used during the execution flow:

I. Monadic Function:

0. Save (X,Y) into LastZ [L0]- (b1,b2)

1. Execute function (all within stack)

2. write X,Y result into buffer L1

3. synch: writes [b3-b6] to stack [X,Y,Z,T]
(this is for consistency with other processes, as it's NOT really required if the function was executed right after ZENTER^, but provides more flexibility for quick-access)

II. Dual Function:

0. Save (X,Y) into LastZ [L0] - (b1,b2)

1. Execute function (all within the stack)

2. write X,Y result into buffer L1

3. perform buffer drop: L3->L2; L4->L3

4. synch: writes [b3-b6] to stack [X,Y,Z,T]
(this is for consistency with other processes, as it only requires writing L2 (b5,b6) into (Z,T)

So things are getting complex (no pun intended?). Whilst not an easy scenario it's doable just the same - if tedious sometimes. My head is spinning with RAM SELECT and READ/WRIT DATA statements, I'm going to print this out and memorize before continuing with the code writing!

Do you see any other situation missing on the above lists??



Edited: 19 Aug 2009, 5:45 p.m.


Angel, looks good to me. Couple thoughts, for what its worth: I think the need to do ZENTER for complex number input is totally fine. Maybe assign it to the shift enter key? (as the user needs the enter key to get the two parts into x and y)

Related to this are your help-functions that work between a real number and a complex number (x^z and z^x for example). One might consider doing away with them as the user would still have to do real part of x, enter, o, zenter to enter the real number x. Personally I think this is acceptable for the user. One last question - I personally think of complex numbers as real part, complex part. So the natural way to enter them would be real part, enter, complex part, zenter. Is that how you have defined the stack? (I could not glean it from the description below) One function you might or might not have that RPN people love to use is 'X<>Y' so one might want to create a function Zx <> Zy. Depending on space availability, one could extend this (similar as to your RPN extensions from the Sandbox) to Zx <> Zz and Zx<>Zt and maybe even a RCLZ_y, RclZ_z, though I agree that some of those are very rarely used. I only mention them as given the code that has to be created anyway, they might come quite 'cheap'.

Looking forward to the module (and source if you can)!




So the natural way to enter them would be real part, enter, complex part, zenter
or real_part, comma, imaginary_part, enter. If you don't do the imaginary part, it's automatically 0. The comma is on the same key LASTX and "." are on. "." has to stay; but I use LASTX less than CATALOG, such that I have LASTX re-assigned to PACK, but I use that seldom enough too that making it go to the imaginary part of an entry would be fine.

To take care of various aspects like STO & RCL (including IND) and I/O functions will be an amazing accomplishment.


I like the comma, but its hard to get to. I recommended shift-4 (BEEP) above. Who uses BEEP in interactive mode?


I like the comma, but its hard to get to. I recommended shift-4 (BEEP) above. Who uses BEEP in interactive mode?
Anyone who used the 41 regularly is bound to have half the keys re-assigned, so I think any solution will require going in and out of USER keyboard a lot. I'm not complaining, just saying that's the way it will have to be. My Catalog 6 (USER key assignments) is a list of 34 keys. In place of BEEP I have my alarmclock program assigned.


my preferred way would be the same as the 35S:


If I were a real MCODE programmer (instead of a pattern-recognition expert... but that's a nother story ) that'd be the way I'd programm it. Unfortunately the "partial key data entry" ML routines are a complete mystery to me so I'll have to live with this:


But first things first - I'll get to these enhancements after the base module is code-complete... getting very close!



ZSTO__, ZRCL__, Z<>__ and ZVIEW__ are programmable prompting functions (uses a trick written by Doug Wilder, similar to the HEPAX implementation).

Thanks to them complex memory management is easy and convenient, just input the Complex register (pair) number at the function prompt (or add a second line in a program to take it from).

It still does not support IND__ or ST_, but maybe one day ...

Besides, ZTRP does exactly the Re<>Im swap (in XY *and* L1)


Edited: 21 Aug 2009, 5:29 p.m.


the "hybrid" functions (Z^X, x^Z, etc...) don't require any imaginary part for the "real" number. It's like a shortcut, as follows:






Z^X (or other hybrid function)[n]

Upon completion, the following actions take place:

Re(z), Im(Z) are stored into L0 (lastZ level of rhe buffer)

Result is stored into both L1 and (X,Y)

L2 is copied back into T,Z for sncronization purposes.



Oops. I see you've gone a long way beyond the point where I made my last reply just now. What you have there sounds good to me too.

One thing: can't you always guarantee that the standard stack is in sync with the complex stack by rewriting the former with the latter at appropriate points? Would that allow you to use the regular enter key? That would only be an issue if you were supporting complex operations while not in user mode. You could reassign ENTER otherwise. If you couldn't do that, I'm just imagining how hard it would be to retrain my fingers. :)



The logic is more involved that what I first thought, I must confess I was tempted to give it up many times but I think I finally managed to get in under control.

Here's how it works:- the real stack is like a scratch pad to work out the complex data input. You can use XYZT as required, and when ZENTER^ is executed it performs an update of the L1 level in the complex buffer level (copying from XY) *and* a refresh of T,Z using L2 from the complex buffer.

So it doesn't matter if T spills over (or even Z for that matter). And thus it is now a full 4-level complex stack (buffered!)

Best, ÁM

Edited: 21 Aug 2009, 5:02 p.m.


Angel, is the 41Z module that can be downloaded from TOS as part of the default setup to i41cx+ for iPhone your work?

If so, I should make myself more familiar with it. I downloaded the existing version to my module list, but I haven't played with.

I assume other users around here burn the MOD file to Clonix or something akin to it?



is the 41Z module that can be downloaded from TOS as part of the default setup to i41cx+ for iPhone your work?

Yes. 41Z.MOD (circa 2005) is there. I have use it on my iPhone.

Don't know about the iPhone stuff but yes, the original 41Z module has been available at TOS for a few years. The posted version has only a two/level stack implememtation, however. You can read the manual to get familiar with it *also posted there(.


Possibly Related Threads...
Thread Author Replies Views Last Post
  Four-Level RPN and the Real World Matt Agajanian 14 4,130 12-13-2013, 03:57 PM
Last Post: Manolo Sobrino
  Last call for 41Z/SandMath Overlays... plus new ones Ángel Martin 0 1,042 12-12-2013, 10:27 AM
Last Post: Ángel Martin
  HP Prime: complex numbers in CAS. Alberto Candel 1 1,272 12-06-2013, 02:36 PM
Last Post: parisse
  [HP Prime] Plots containing complex numbers bug? Chris Pem10 7 2,564 12-05-2013, 07:40 AM
Last Post: cyrille de Brébisson
  HP 50g - select characters on the stack, copy/paste Sean Freeman 7 1,895 11-20-2013, 07:11 AM
Last Post: Sean Freeman
  Prime: Placing more than 1 item on the RPN stack in a single program? John Colvin 4 1,550 11-19-2013, 08:59 AM
Last Post: Miguel Toro
  Complex Number Entry on Prime Jeff O. 19 3,489 11-16-2013, 12:34 PM
Last Post: Jeff O.
  emu48 - copy stack doesn't work (as expected) Thomas Radtke 2 1,463 11-11-2013, 02:19 PM
Last Post: Thomas Radtke
  HP Prime Stack operations from within a program John Colvin 1 985 11-08-2013, 09:45 PM
Last Post: Helge Gabert
  Sandmath/41Z overlays Bernd Grubert 1 1,000 10-29-2013, 03:32 PM
Last Post: 'Angel Martin

Forum Jump: