System RPL - How to exit indefinite loop?
|
07-16-2021, 02:35 AM
Post: #1
|
|||
|
|||
System RPL - How to exit indefinite loop?
What is the best way to drop out of this indefinite loop, BEGIN … AGAIN? I think I would need to generate an error. But how? The complete code is from Joe Horn’s Full Reset Stack. A double tap of the ON key is supposed to drop out of the program but couldn’t see anything that actually does that. I figured I would need to do that on my own. My guess is to generate an error, do a STOF, and drop out. I’m sure I’m making this way too simplistic. But I’m looking for a place to start.
:: xRCLF InitSysUI :: INCOMPDROP PTR 2F3AA BEGIN … … … BEGIN … … … DUP#0= UNTIL … … ClrDAsOK GetKeyOb ERRSET DoKeyOb ERRTRAP SysErrorTrap AGAIN ; ERRTRAP VLM ; @ |
|||
07-16-2021, 03:49 AM
Post: #2
|
|||
|
|||
RE: System RPL - How to exit indefinite loop?
(07-16-2021 02:35 AM)Strike1 Wrote: The complete code is from Joe Horn’s Full Reset Stack. No idea what that is. Please give us either the original source code, or a link to it. <0|ɸ|0> -Joe- |
|||
07-16-2021, 12:38 PM
Post: #3
|
|||
|
|||
RE: System RPL - How to exit indefinite loop?
Link: https://groups.google.com/g/comp.sys.hp48/c/ubiYam5PQL4
Code: !NO CODE !RPL :: xRCLF InitSysUI :: INCOMPDROP STOALLFcont2_ BEGIN AtUserStack SysMenuCheck PTR 2A32D :: EditLExists? IT CODE 0001B 1321B82100317314C1308D94150 DispCommandLine ; ?DispStatus :: PTR 2A28C :: TOADISP BINT1 :: StackLineHeight EditLExists? ?SKIP #8+ CommandLineHeight CODE 00028 1331F0CE08147131808B2C034701331458D1B130 #=case 3DROP DUP4UNROLL #- SCANFONT BEGIN DUP NULL$ 4PICK #6+ DEPTH #>ITE 'NOP :: 4PICK #5+ PICK ; BINT74 SysITE BINT1 BINT0 BINT52 TestSysFlag ARRYREAL TestSysFlag IT :: 7PICK #1<> OR ; BINT95 TestSysFlag OBJ>R_ 3PICK 'NOP EQ ?SKIP :: 2LIST TestSysFlag ?SEMI { BINT2 BINT3 BINT4 BINT29 BINT5 BINT6 } 4PICK XEQTYPE COERCE SWAPDROP #=POSCOMP #0<> ?SEMI BINT95 SetSysFlag ; FPTR 2 95 BINT95 R>OBJ_ ITE SetSysFlag ClrSysFlag UNROTDUP 6ROLL SWAP 5ROLL EditLExists? CODE 000CB 8428F86A435508528F86A434606A708FD5F30068FB97601B2B22810007D5CA340400082480B83460310586280818FA7E21FD8608143C6CAF2CA131D9C6F2C9C98FC0760D4118CA8DC75308FD5F30100068FB97601BDF818142342B228C4C2F0C21341106D6F 4UNROLL #- SWAP#1+SWAP DUP#0= UNTIL 3DROP ; SetDA2aValid ; ; EditLExists? ?SKIP CODE 0001B 1321B8210031F314C1308D94150 PTR 2A346 :: BINT0 BINT1 GETDF DoLabel BINT22 BINT2 GETDF DoLabel BINT44 BINT3 GETDF DoLabel BINT66 BINT4 GETDF DoLabel BINT88 BINT5 GETDF DoLabel BINT110 BINT6 GETDF DoLabel CODE 00014 34A80E2136142134808C SetDA3Valid ; ClrDAsOK GetKeyOb ERRSET DoKeyOb ERRTRAP SysErrorTrap AGAIN ; ERRTRAP VLM ; @ BYTES: #24C2h 562 - GaaK - |
|||
07-16-2021, 05:36 PM
Post: #4
|
|||
|
|||
RE: System RPL - How to exit indefinite loop?
(07-16-2021 02:35 AM)Strike1 Wrote: ... The complete code is from Joe Horn’s Full Reset Stack. ... (07-16-2021 12:38 PM)Strike1 Wrote: Link: https://groups.google.com/g/comp.sys.hp48/c/ubiYam5PQL4 Aha, no wonder I didn't recognize that code: it's not my program. It was written by "GaaK" (Gustavo Portales). I hope somebody here can help you with it. -Joe- <0|ɸ|0> -Joe- |
|||
07-25-2021, 01:57 PM
Post: #5
|
|||
|
|||
RE: System RPL - How to exit indefinite loop?
As you are probably already aware, a System RPL BEGIN...AGAIN loop is inherently an infinite loop. It has no default mechanism for early exiting or aborting. It can be done, of course, it simply requires the programmer to directly manipulate both the runstream and the return stack as needed for the given situation.
This is definitely an advanced topic for System RPL programming, and I'm not going to attempt to explain all the underlying principles here. Suffice it to say that it can be a powerful tool, but great care needs to be taken when designing the program flow for these structures. I'll share one way of breaking out of a BEGIN...AGAIN loop that I typically deploy. Note that for this to work, there's a requirement that the AGAIN statement be immediately followed by a ";" (SEMI), which implies that the return stack already has an entry for the codeword that immediately follows the SEMI. An indefinite loop matching the above description can be exited through the effective use of case/COLA and runstream commands. I usually deploy something like the following: Code: <some computation resulting in a boolean value> Notes: - The somewhat odd-looking encapsulation of a single command inside a secondary may seem strange at first glance, but the secondary is definitely needed in this situation. It's also common to include other processing steps in that secondary, since an early exit at an arbitrary point may require additional clean-up to take place. That makes the secondary a more natural fit. - This construct must be at the root level of the loop as written. Encapsulating it into another secondary adds an additional return stack level and would require different steps to ensure that the proper level of code is aborted (eg. 2RDROP). - It's possible to save a tiny amount of space by using a different structure, but IMHO this version is actually more flexible if changes are needed and provides more clarity. I'll include some simple examples to show how this technique can be applied. Let's say I want to create a small app to show the internal keycodes used by the O/S. I want to loop this so that I can press an arbitrary number of keys to get their codes, and I want to do leave a string ("All done!") on the stack at completion. Example 1: The Lazy Approach This code sample is written as an infinite loop, and will just run indefinitely until you interrupt it with ON-C or a hard reset. This might be perfectly fine for a quick-and-dirty test, but I would never use this for something that would be distributed. Forcing the user to do a soft- or hard-reset isn't exactly a friendly way to design an app: Code: :: Note that as written above, the "All done!" string never sees the light of day. The BEGIN...AGAIN loop is never exited gracefully, so that follow-up code never gets executed. Example 2: I Hate It When I Do That OK, so I realize now that I need some trigger built into the look to force an exit. Let's use a somewhat intuitive mechanism: pressing the ON/CANCEL key (O/S key number 47). Armed with the awareness of my special "early exit" construct, I place a test between the key acquisition and value printing statements: Code: :: So I run the app, and it behaves as expected to show key codes until I press ON/CANCEL. But instead of leaving my "All done!" string on the stack, it simply exits with no further action taken. At this point I'm usually slapping my forehead when I realize that, once again, I forgot a fundamental element with this version. In particular, the encapsulating secondary that ends with the AGAIN statement. Not having that, the forced exit jumped to the next return stack entry as it was designed to do, which in this case was the end of the whole program (not the string to be left on the stack). Example 3: Final Working Version Armed with all of the above, this version does what I originally intended: Code: :: While I have given an example here of how to exit a BEGIN...AGAIN loop, I (intentionally) haven't tailored it to your specific situation. Creating or modifying a System Outer Loop app is a risky endeavor. I encourage you to play around with Gaak's program, but be aware that there are many, many things that can come up with this kind of program that makes them very fragile and prone to unexpected behaviors. Definitely don't load this kind of program on a real calc until you've done extensive testing. |
|||
07-25-2021, 04:11 PM
Post: #6
|
|||
|
|||
RE: System RPL - How to exit indefinite loop?
Wow. Really helpful! Thank you DavidM.
As I near retirement I decided to revisit programming my favorite calculator. Before your reply I had decided to change the BEGIN - AGAIN loop to a BEGIN - WHILE - REPEAT loop. Surprisingly it worked. It let me insert the test clause (I employed a User Flag) and it cleanly let me drop out of the loop. Now that you've given me something to study I will try to figure out the merits of a different approach to dropping out of either type loop. GAAK wrote two versions of this program. One with the menu and one without. They're both similar in structure and the one without the menu has more code. Once again employing a User Flag I had various code skipped or included depending on whether the flag was set or clear. And that works too. I really like that sense of accomplishment when it works. I assigned a toggle-the-flag program to the shift-right down arrow and can bring up the menu or clear it at will. And I see what you mean by "fragile". If not for Debug4x I might have wasted a good calculator several times over. |
|||
07-25-2021, 07:45 PM
Post: #7
|
|||
|
|||
RE: System RPL - How to exit indefinite loop?
(07-25-2021 04:11 PM)Strike1 Wrote: I had decided to change the BEGIN - AGAIN loop to a BEGIN - WHILE - REPEAT loop. Surprisingly it worked. It let me insert the test clause (I employed a User Flag) and it cleanly let me drop out of the loop. Now that you've given me something to study I will try to figure out the merits of a different approach to dropping out of either type loop. When there's a single decision point, BEGIN...WHILE...REPEAT is usually the better choice for clarity, but you can sometimes save some bytes by using alternate methods. There's also those times when you may already be doing something with the return stack (such as processing a list), and a hybrid approach may be needed. Since you mentioned using Debug4x, be aware that the compiler may complain about unmatched codewords if you start mixing up the looping commands. It also automatically includes the :: ; tokens in a BEGIN...WHILE...REPEAT sequence, so you shouldn't put those at the outermost level between WHILE and REPEAT in most cases. You may also have to put the codewords in [] to keep the compiler from getting confused by non-standard constructs. |
|||
« Next Oldest | Next Newest »
|
User(s) browsing this thread: 3 Guest(s)