thomasokken / free42

Free42 : An HP-42S Calculator Simulator
https://thomasokken.com/free42/
GNU General Public License v2.0
280 stars 54 forks source link

FUNC's unexpected behaviour in NSTK mode #32

Closed Slasy closed 3 years ago

Slasy commented 3 years ago

Hi, I really like big stack mode, but FUNC is not working in the same way as in 4STK mode. I'm not sure if this is a bug or some kind of limitation.

Here are some test programs, all should have same result in 4STK: Y: 10 X: 25 and respectively in NSTK: 2: 10 1: 25 But only first one (tstN) is behaving same in both modes, all others are unable to restore stack to expected state.

LBL "tstN"
CLST
10
25
FUNC 00
30
40
+
END
LBL "tstX1"
CLST
10
25
FUNC 00
50
+
END
LBL "tstX2"
CLST
10
25
FUNC 00
CLST
END
LBL "tstX3"
CLST
10
25
FUNC 00
+
END
LBL "tstX4"
CLST
10
25
FUNC 00
SQRT
END
thomasokken commented 3 years ago

FUNC is working as intended in NSTK mode, but I should update the documentation to explain what exactly it does in that mode, since it is a bit different from what it does in 4STK mode.

The problem with NSTK mode is that it isn't practical for FUNC to back up the entire stack. The stack can become arbitrarily deep, and in recursive functions, backing up the entire stack would lead to O(n^2) memory usage just for the stack.

What it does instead in NSTK mode is to back up only the parameters. That is, in the case of FUNC mn, it backs up levels 1 through m. The idea is that a function that takes m parameters will not modify or delete levels m+1 and higher, and so, those levels don't need to be backed up. Your test functions tstX1, tstX2, tstX3, and tstX4 all violate that assumption. Generally speaking, CLST, R↑, R↓, and anything that consumes any stack level above m, will cause FUNC to fail.

As long as levels m+1 and higher are left intact, FUNC will work correctly. Note that it is not necessary to avoid causing clutter on the stack: when a function with FUNC mn returns, having been called with original stack depth d and finishing with stack depth d', RTN will assume that levels 1 through n contain the results, levels m+1+d'-d through d' contain the original stack contents above the parameters, and levels n+1 through m+d'-d contain garbage and should be discarded.

4STK mode is completely different. Because of the fixed stack depth, FUNC cannot assume that stack levels m+1 and higher will be left alone by the function, since they may be lost off the top, and so it has to save the entire stack.

Slasy commented 3 years ago

Thank you for detailed explanation. It makes sense that it could blow through memory really quickly if it would have been saving entire stack as in 4STK mode. So in the end I was just confused by the documentation or rather by the lack of this piece of information.

thomasokken commented 3 years ago

That's understandable!

I'm writing up a diagram to explain exactly what FUNC and RTN/RTNERR do in 4STK and NSTK modes, and will add that to the FUNC documentation on my web site as soon as it's done, probably tomorrow.

thomasokken commented 3 years ago

https://thomasokken.com/free42/images/FUNC.pdf

Let me know what you think... I haven't linked this from the documentation page yet.

thomasokken commented 3 years ago

Things get even more complicated when FUNC is combined with LNSTK or L4STK. I guess I should create diagrams for those scenarios as well... 🤔

Slasy commented 3 years ago

I think this is understandable diagram and explanation.

thomasokken commented 3 years ago

Great! Thank you for checking. I'll create versions for FUNC+LNSTK and FUNC+L4STK as well and then link to those from the main page.

thomasokken commented 3 years ago

I updated the main Free42 page with a link to the diagram under the description of FUNC.

I decided not to cover the cases combining FUNC with LNSTK and L4STK; what's complicated about those is all behind the scenes, because of the way the effects of FUNC and L[4N]STK are combined and then rolled back together, but conceptually, what they do is pretty straightforward and intuitive, so I don't think it's useful to document that separately.

thomasokken commented 3 years ago

I changed my mind and added a paragraph about FUNC+LNSTK and FUNC+L4STK in the Big Stack section, and created a diagram covering those cases: https://thomasokken.com/free42/images/FUNC_L.pdf

Slasy commented 3 years ago

Awesome, it looks great.