▼
Posts: 1,253
Threads: 117
Joined: Nov 2005
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 twolevel 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 4level stack lift and drop has been implemented into all nonmonadic 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...
Best,
ÁM
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.
▼
Posts: 1,619
Threads: 147
Joined: May 2006
Excellent news. I look forward to testing it.
Posts: 564
Threads: 72
Joined: Sep 2005
Awesome Angel, you are clearly on a roll!! Looking forward to playing with it!
Cheers
Peter
PS: thanks for the kind words, pretty sure I don't deserve them but they are very much appreciated just the same.
▼
Posts: 1,253
Threads: 117
Joined: Nov 2005
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 4levels 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 nonmonadic functions, with duplication of the 4th. complexlevel 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: 44j
L3: 33j
L2: 22j
L1: 11j
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 realstack 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 (workaround 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.
Best, ÁM
Edited: 17 Aug 2009, 7:59 a.m. after one or more responses were posted
▼
Posts: 1,830
Threads: 113
Joined: Aug 2005
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.
Regards,
Howard
Posts: 564
Threads: 72
Joined: Sep 2005
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
Peter
▼
Posts: 1,619
Threads: 147
Joined: May 2006
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.
▼
Posts: 1,253
Threads: 117
Joined: Nov 2005
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 12foot 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 15Cway. Not looking forward to rewriting the whole thing!
As per the data input process, I've used SHIFTSHIFT 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.
Posts: 1,253
Threads: 117
Joined: Nov 2005
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 rewrite 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.
▼
Posts: 1,830
Threads: 113
Joined: Aug 2005
Quote:
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 rewrite 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.
Quote:
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.] " :)
Regards, Howard
Posts: 115
Threads: 10
Joined: Aug 2008
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...
▼
Posts: 1,253
Threads: 117
Joined: Nov 2005
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?
Best,
ÁM
Posts: 564
Threads: 72
Joined: Sep 2005
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 znumber' 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...
Cheers
Peter
Posts: 1,253
Threads: 117
Joined: Nov 2005
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 ZStack
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 ZStack Roll Up
ZRCL Stack Complex RCL
ZRDN Stack ZStack 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 MultiValue Complex functions
▼
Posts: 1,253
Threads: 117
Joined: Nov 2005
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:
A. ZENTER^.
1. writes (X,Y) into L1 (b3,b4)
2. buffer normal lift: L3>L4; L2>L3; L1>L2
3. synch: writes [b3b6] 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 desynchronize.
B. LASTZ.
1. buffer long lift: L3>L4; L2>L3; L1>L2; L0>L1
2. synch: writes [b3b6] to stack [X,Y,Z,T]
C. ZRDN.
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 [b3b6] 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 [b3b6] 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 [b3b6] 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 [b3b6] 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 quickaccess)
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 [b3b6] 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??
Best,
ÁM
Edited: 19 Aug 2009, 5:45 p.m.
▼
Posts: 564
Threads: 72
Joined: Sep 2005
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 helpfunctions 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)!
Cheers
Peter
▼
Posts: 887
Threads: 9
Joined: Jul 2007
Quote: 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 reassigned 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.
▼
Posts: 1,619
Threads: 147
Joined: May 2006
I like the comma, but its hard to get to. I recommended shift4 (BEEP) above. Who uses BEEP in interactive mode?
▼
Posts: 887
Threads: 9
Joined: Jul 2007
Quote: I like the comma, but its hard to get to. I recommended shift4 (BEEP) above. Who uses BEEP in interactive mode?
Anyone who used the 41 regularly is bound to have half the keys reassigned, 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.
▼
Posts: 1,253
Threads: 117
Joined: Nov 2005
my preferred way would be the same as the 35S:
Re(z)
"i"
Im(z)
ENTER
If I were a real MCODE programmer (instead of a patternrecognition 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:
Im(z)
ENTER
RE(z)
ZENTER
But first things first  I'll get to these enhancements after the base module is codecomplete... getting very close!
Best,
ÄM
Posts: 1,253
Threads: 117
Joined: Nov 2005
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)
Best,
ÁM
Edited: 21 Aug 2009, 5:29 p.m.
Posts: 1,253
Threads: 117
Joined: Nov 2005
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:
Im(z)
ENTER
Re(Z)
ZENTER^
x
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.
Best,
ÁM
Posts: 1,830
Threads: 113
Joined: Aug 2005
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. :)
Regards, Howard
▼
Posts: 1,253
Threads: 117
Joined: Nov 2005
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 4level complex stack (buffered!)
Best, ÁM
Edited: 21 Aug 2009, 5:02 p.m.
Posts: 1,368
Threads: 212
Joined: Dec 2006
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?
Les
▼
Posts: 1,619
Threads: 147
Joined: May 2006
Quote:
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.
Posts: 1,253
Threads: 117
Joined: Nov 2005
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(.
Best,
AM
