10-22-2018, 07:09 AM
Post: #1
 BlackMi Junior Member Posts: 14 Joined: Sep 2018
I read the programming in System RPL second Edition. no jump and lables.
Are there some methods to jump to lables and call a function in the code and return in sysRPL?
10-22-2018, 10:11 AM
Post: #2
 3298 Member Posts: 107 Joined: Oct 2014
Jumping to labels sounds like GOTO to me. This SysRPL command exists but is only usable when the containing program is always loaded at a specific address, which is generally not true for user programs. Even for those few commands in ROM that do use it, it might be problematic as it'll subtly break recall-modify-execute procedures applied to them. (The other commands in ROM don't do that, but a user might get that idea.)
I remember writing a relative GOTO (read this thread, there are some designs by others as well) designed to be used with the on-board SysRPL compiler. It's still pretty fragile because you still can't just have another program modify a relative-GOTO-using program between GOTO position and target (it would throw off the offset calculated at compile time), but at least you can move the program to any memory address without breaking it.

However, there are some terms in your request that confuse me. First: "code" - if you want to switch to machine code and later back to RPL, you don't need labels and GOTO, just embed a code object. Second: "call" and "return" - that's obviously something entirely different from jumping to labels. If you want to call subroutines, just embed them, or if you want to call the same one from multiple places: simply store it in some kind of variable (or even on the stack!) when your program launches, then recall and evaluate it when needed.
10-23-2018, 04:34 AM
Post: #3
 cyrille de brébisson Senior Member Posts: 866 Joined: Dec 2013
Hello,

SysRpl is a stack based language. Not only does it use a stack for data, but also for state (program blocks)...
GOTOs usually jump from program blocks to program blocks. Which means that they "break/pass" over program blocks boundaries... However, the GOTO code does not "know" that and does not know how to "tweak" the stack as needed... This result in "bad things" (like crossing the streams)...

Modern programming languages that do have goto go to great length to "fix" this, but it is a programming nightmare to do so... And SysRpl is not that "clever", especialy as it does not have a compiler...

Cyrille

Although I work for the HP calculator group, the views and opinions I post here are my own. I do not speak for HP.
10-24-2018, 02:07 AM
Post: #4
 BlackMi Junior Member Posts: 14 Joined: Sep 2018
(10-23-2018 04:34 AM)cyrille de brébisson Wrote:  Hello,

SysRpl is a stack based language. Not only does it use a stack for data, but also for state (program blocks)...
GOTOs usually jump from program blocks to program blocks. Which means that they "break/pass" over program blocks boundaries... However, the GOTO code does not "know" that and does not know how to "tweak" the stack as needed... This result in "bad things" (like crossing the streams)...

Modern programming languages that do have goto go to great length to "fix" this, but it is a programming nightmare to do so... And SysRpl is not that "clever", especialy as it does not have a compiler...

Cyrille
Thank you!
10-24-2018, 02:19 AM
Post: #5
 BlackMi Junior Member Posts: 14 Joined: Sep 2018
(10-22-2018 10:11 AM)3298 Wrote:  Jumping to labels sounds like GOTO to me. This SysRPL command exists but is only usable when the containing program is always loaded at a specific address, which is generally not true for user programs. Even for those few commands in ROM that do use it, it might be problematic as it'll subtly break recall-modify-execute procedures applied to them. (The other commands in ROM don't do that, but a user might get that idea.)
I remember writing a relative GOTO (read this thread, there are some designs by others as well) designed to be used with the on-board SysRPL compiler. It's still pretty fragile because you still can't just have another program modify a relative-GOTO-using program between GOTO position and target (it would throw off the offset calculated at compile time), but at least you can move the program to any memory address without breaking it.

However, there are some terms in your request that confuse me. First: "code" - if you want to switch to machine code and later back to RPL, you don't need labels and GOTO, just embed a code object. Second: "call" and "return" - that's obviously something entirely different from jumping to labels. If you want to call subroutines, just embed them, or if you want to call the same one from multiple places: simply store it in some kind of variable (or even on the stack!) when your program launches, then recall and evaluate it when needed.

Thank you very much!
10-24-2018, 03:03 AM
Post: #6
 DavidM Senior Member Posts: 748 Joined: Dec 2013

The "next address" referred to in that documentation is the next 5 nibbles after the GOTO command reference in the current run stream.

IMHO, SysRPL GOTO is really only useful in a general sense to developers of ROM images, as they are likely to be writing code that will be in known (or predictable) positions in memory. SysRPL programs in the general sense are simply not well-suited to GOTOs as a result of the delicate stack structures/error frames/lambda environments that could easily be broken by attempting to jump between code blocks.

You can't ever assume that SysRPL code you write will be in a fixed location. As such, you can't know in advance the address to specify that would be appropriate to jump to in your own code. If you're wanting to jump into a specific ROM address, you would need to know exactly what that code does before returning control to the SOL (system outer loop). Also, it has to be structured as a SysRPL stream (ie. not straight machine code). If you're at that level, you may as well be coding in Saturn assembly anyway.

Shorter version: forget about SysRPL GOTO. It isn't there for standard (relocatable) SysRPL code. Use standard SysRPL structures instead (if-then-else, case, begin-while-repeat, do-loop, etc.). SysRPL also offers certain runstream alterations that can achieve similar behaviors to break and continue if used carefully.
10-24-2018, 05:34 AM
Post: #7
 cyrille de brébisson Senior Member Posts: 866 Joined: Dec 2013
Hello,

In Saturn ASM, GOTO can do "relative" jumps, meaning it will add/substract n from the current program counter (with a limit of 16 bits = +/-16Kb on the saturn) and the saturn also has a "full" address space fixed address destination (GOTOVL? I do not even remember! That was over 10 years of my life :-( sniff... am I confusing with the GOSUB version (GOSBVL))

It is similar with Intel or ARM CPU. They have versions of GOTOs that are relatives and others that are absolute....

In all these "low level cases", you can jump ANYWHERE in memory...

When working in non Asm languages that HAVE GOTOs, GOTOs are always limited to jumping in the current function because of the aforementionned stack management... (some non stacked based languages such as Basic 1.0 being outsiders of course)

Cyrille

Although I work for the HP calculator group, the views and opinions I post here are my own. I do not speak for HP.
10-24-2018, 04:23 PM
Post: #8
 David Hayden Member Posts: 272 Joined: Dec 2013
What are you actually trying to accomplish? You may find that words like RDROP or CASE can fill your needs.
10-24-2018, 11:10 PM
Post: #9
 RMollov Member Posts: 240 Joined: Dec 2013
(10-22-2018 07:09 AM)BlackMi Wrote:  I read the programming in System RPL second Edition. no jump and lables.
Are there some methods to jump to lables and call a function in the code and return in sysRPL?
What lables?
10-29-2018, 11:26 AM
Post: #10
 BlackMi Junior Member Posts: 14 Joined: Sep 2018
(10-24-2018 04:23 PM)David Hayden Wrote:  What are you actually trying to accomplish? You may find that words like RDROP or CASE can fill your needs.

The problem is I don't know how "data stack" "return stack" and "virtual stack" work to control the runstream.
10-29-2018, 01:34 PM
Post: #11
 DavidM Senior Member Posts: 748 Joined: Dec 2013
(10-29-2018 11:26 AM)BlackMi Wrote:  The problem is I don't know how "data stack" "return stack" and "virtual stack" work to control the runstream.

If you're truly wanting to understand these concepts better, take a look at Debug4x. It has the ability to show the status of all of these entities as you step through SysRPL programs, which I think is one of the best ways to understand the operation (and manipulation) of all of these stacks in SysRPL code.

The return stack is the most important one to understand regarding runstream control, as it is an integral part of the implementation of all of the flow control statements in SysRPL. The other two are only tangentially related to runstream control, in that it is possible to manipulate the return stack by moving entries back and forth between the return stack and the data stack. The virtual stack has no direct connection to the runstream, but rather is an extension/encapsulation of data stack manipulation.
10-29-2018, 01:36 PM
Post: #12
 3298 Member Posts: 107 Joined: Oct 2014
That doesn't answer what you're trying to do.

The runstream consists of the remaining objects or commands in the currently executed secondary. At a low level, it's represented by a simple pointer into the secondary. When another secondary is entered, the runstream is pushed to the return stack (basically a stack of runstreams) and replaced by the contents of the new secondary, and when it's exited again, the return stack is popped back into the runstream. The normal way to exit from a secondary is the SEMI at the end, but some commands do this earlier, e.g. ?SEMI (which returns when the value it takes from the data stack is TRUE, otherwise it does nothing). This is documented as "dropping the rest of the runstream" or simply "dropping the runstream".
The runstream can also be "popped", which means the command or object after the command doing it is taken, the runstream pointer is advanced past it, and then something is done with the object or command (the simplest case: pushing it onto the data stack, which is what ' does).

Apart from saving where secondaries were interrupted for calling another secondary, the return stack is sometimes also used for temporary storage (e.g. OBJ>R_ and R>OBJ>_), or for certain control flow commands (e.g. BEGIN, AGAIN, and friends; but not DO and LOOP, those use yet another stack to save their loop environments). In both cases, make sure the return stack level won't get used as a runstream, or bad things will happen.
A particularly creative use of the return stack is the combination of >R and a loop containing ticR (possibly surrounded by two instances of RSWAP if using indefinite loops) to efficiently iterate through a list. >R pushes a composite object from the data stack (secondary, list, or similar) onto the return stack in such a way that its contents would get executed between returning from the current secondary and continuing whatever called it; ticR tries to pop one object off the newest runstream on the return stack (not the current runstream) and reports success or failure as TRUE or FALSE.

The data stack is what you see in the display. It doesn't have a direct influence on runstream and return stack, but many commands manipulating the runstream or the return stack take input from the data stack, or they leave output there.
The virtual stack is completely irrelevant here, it's a stack of data stacks. I know of no commands dealing with both the virtual stack and return stack / runstream. Same for the stack of temporary environments and the stack of do-loop environments. (SEMILOOP may be filed under the DO-LOOP category, but I think that command isn't even a SysRPL entry point; if it's an ASM one, the "loop" part would refer to the "return to RPL" procedure instead of definite loops in SysRPL.)
10-30-2018, 02:31 AM (This post was last modified: 10-30-2018 10:44 AM by BlackMi.)
Post: #13
 BlackMi Junior Member Posts: 14 Joined: Sep 2018
(10-29-2018 01:36 PM)3298 Wrote:  That doesn't answer what you're trying to do.

The runstream consists of the remaining objects or commands in the currently executed secondary. At a low level, it's represented by a simple pointer into the secondary. When another secondary is entered, the runstream is pushed to the return stack (basically a stack of runstreams) and replaced by the contents of the new secondary, and when it's exited again, the return stack is popped back into the runstream. The normal way to exit from a secondary is the SEMI at the end, but some commands do this earlier, e.g. ?SEMI (which returns when the value it takes from the data stack is TRUE, otherwise it does nothing). This is documented as "dropping the rest of the runstream" or simply "dropping the runstream".
The runstream can also be "popped", which means the command or object after the command doing it is taken, the runstream pointer is advanced past it, and then something is done with the object or command (the simplest case: pushing it onto the data stack, which is what ' does).

Apart from saving where secondaries were interrupted for calling another secondary, the return stack is sometimes also used for temporary storage (e.g. OBJ>R_ and R>OBJ>_), or for certain control flow commands (e.g. BEGIN, AGAIN, and friends; but not DO and LOOP, those use yet another stack to save their loop environments). In both cases, make sure the return stack level won't get used as a runstream, or bad things will happen.
A particularly creative use of the return stack is the combination of >R and a loop containing ticR (possibly surrounded by two instances of RSWAP if using indefinite loops) to efficiently iterate through a list. >R pushes a composite object from the data stack (secondary, list, or similar) onto the return stack in such a way that its contents would get executed between returning from the current secondary and continuing whatever called it; ticR tries to pop one object off the newest runstream on the return stack (not the current runstream) and reports success or failure as TRUE or FALSE.

The data stack is what you see in the display. It doesn't have a direct influence on runstream and return stack, but many commands manipulating the runstream or the return stack take input from the data stack, or they leave output there.
The virtual stack is completely irrelevant here, it's a stack of data stacks. I know of no commands dealing with both the virtual stack and return stack / runstream. Same for the stack of temporary environments and the stack of do-loop environments. (SEMILOOP may be filed under the DO-LOOP category, but I think that command isn't even a SysRPL entry point; if it's an ASM one, the "loop" part would refer to the "return to RPL" procedure instead of definite loops in SysRPL.)
This is a loop
Code:
 BEGIN     condition     WHILE     code   REPEAT

but
Code:
 ::     BEGIN       condition     NOTcase AGAIN       code     AGAIN   ;   RDROP
does the same thing.
What happened in the process? Should not the first AGAIN loop to the BEGIN running the condition over and over again?
10-30-2018, 09:37 AM
Post: #14
 Raymond Del Tondo Member Posts: 229 Joined: Dec 2013
Consider the following:
Code:
 LOCALLABEL L_C1238      DUP ONE SUB$1# NumericChr? NOTcase 2DROPFALSE CDR$ DUPNULL\$? *          NOT?GOTO L_C1238 CODE     GOSBVL    =popflag     GOC    +        * Skip lbl .     A=PC     LC(5)    (L_C1238)-(*)    offset to label     C=C+A    A     D0=C +    LOOP ENDCODE
The above was a (conditional) absolute GOTO converted to a (conditional) relative GOTO within the same RPL secondary. This way the code is relocateable.

Good RPL programming style? Certainly not. But that's not the point;-)

-- Ray
10-30-2018, 10:49 AM
Post: #15
 3298 Member Posts: 107 Joined: Oct 2014
(10-30-2018 02:31 AM)BlackMi Wrote:  This is a loop
BEGIN
condition
WHILE
code
REPEAT

but
::
BEGIN
condition
NOTcase AGAIN
code
AGAIN
;
RDROP
does the same thing.
What happened in the process? Should not the first AGAIN loop to the BEGIN running the condition over and over again?
I think I have seen that code before. You're looking at the programming tricks from Nosy's Readme, right? That's some advanced runstream juggling, so if you don't understand it, start with the simple stuff like merely going up more than one level of secondary nesting (straightforward solution: RDROP at the end of one secondary, so the ; after it pops an older entry off the return stack).

Anyway, the command "case" (and by extension NOTcase which just swaps the effects of the boolean input on the data stack) pops one object off the runstream (here: the command AGAIN), pops one boolean off the data stack, and depending on the value of the boolean it either just finishes (causing whatever is after the object to get executed; here that's whatever "code" gets substituted for), or it pops the topmost pointer off the return stack into the current runstream and then executes the object. The important bit is the return stack popping: that removes the value BEGIN has put there, so AGAIN will use a different one. That will be the runstream pushed by the :: before the BEGIN command (which searched for its corresponding ; and pushed a pointer to what comes after that).
The next question to ask is what AGAIN actually does with the value on the return stack. In an ordinary loop BEGIN simply pushes the current runstream onto the return stack, and AGAIN copies it back into the current runstream without popping it - this is how they work together to create a potentially infinite loop. In this piece of code NOTcase has removed the runstream pushed by BEGIN from the return stack and made it the current one, but before anything from that gets executed, AGAIN gets its chance - it will immediately replace the current runstream again, this time with the runstream of the secondary that called the :: ; embedded in this snippet. The first object in that is RDROP, which gets rid of the runstream AGAIN used but left on the return stack.
In case you wonder why AGAIN and RDROP aren't combined into a single command: That command may exist (it's called SEMI), but because it's an alias for ; it has the side effect of marking the end of the secondary. If the compiler wouldn't complain about the extra ; a few lines below, then using SEMI in place of the first AGAIN and omitting the RDROP would cause the code run on encountering :: to advance the runstream it pushes to the return stack only past that, not past the ; near the end of the snippet.
10-30-2018, 10:51 AM
Post: #16
 BlackMi Junior Member Posts: 14 Joined: Sep 2018
(10-24-2018 11:10 PM)RMollov Wrote:
(10-22-2018 07:09 AM)BlackMi Wrote:  I read the programming in System RPL second Edition. no jump and lables.
Are there some methods to jump to lables and call a function in the code and return in sysRPL?
What lables?

Therer is a lable in Raymond Del Tondo's code.
Usually some lables or line number should be in the code. People can jump or goto there to loop some commands.
10-30-2018, 11:32 AM
Post: #17
 BlackMi Junior Member Posts: 14 Joined: Sep 2018
(10-30-2018 10:49 AM)3298 Wrote:
(10-30-2018 02:31 AM)BlackMi Wrote:  This is a loop
BEGIN
condition
WHILE
code
REPEAT

but
::
BEGIN
condition
NOTcase AGAIN
code
AGAIN
;
RDROP
does the same thing.
What happened in the process? Should not the first AGAIN loop to the BEGIN running the condition over and over again?
I think I have seen that code before. You're looking at the programming tricks from Nosy's Readme, right? That's some advanced runstream juggling, so if you don't understand it, start with the simple stuff like merely going up more than one level of secondary nesting (straightforward solution: RDROP at the end of one secondary, so the ; after it pops an older entry off the return stack).

Anyway, the command "case" (and by extension NOTcase which just swaps the effects of the boolean input on the data stack) pops one object off the runstream (here: the command AGAIN), pops one boolean off the data stack, and depending on the value of the boolean it either just finishes (causing whatever is after the object to get executed; here that's whatever "code" gets substituted for), or it pops the topmost pointer off the return stack into the current runstream and then executes the object. The important bit is the return stack popping: that removes the value BEGIN has put there, so AGAIN will use a different one. That will be the runstream pushed by the :: before the BEGIN command (which searched for its corresponding ; and pushed a pointer to what comes after that).
The next question to ask is what AGAIN actually does with the value on the return stack. In an ordinary loop BEGIN simply pushes the current runstream onto the return stack, and AGAIN copies it back into the current runstream without popping it - this is how they work together to create a potentially infinite loop. In this piece of code NOTcase has removed the runstream pushed by BEGIN from the return stack and made it the current one, but before anything from that gets executed, AGAIN gets its chance - it will immediately replace the current runstream again, this time with the runstream of the secondary that called the :: ; embedded in this snippet. The first object in that is RDROP, which gets rid of the runstream AGAIN used but left on the return stack.
In case you wonder why AGAIN and RDROP aren't combined into a single command: That command may exist (it's called SEMI), but because it's an alias for ; it has the side effect of marking the end of the secondary. If the compiler wouldn't complain about the extra ; a few lines below, then using SEMI in place of the first AGAIN and omitting the RDROP would cause the code run on encountering :: to advance the runstream it pushes to the return stack only past that, not past the ; near the end of the snippet.

Thank you very much!
Yes, I am reading Nosy's Readme. It gives details of some RPL objects. How System RPL executing them is still not clear.
It takes time for me to understand the whole thing.
10-30-2018, 02:27 PM
Post: #18
 David Hayden Member Posts: 272 Joined: Dec 2013
Another important runstream command to look at is COLA. COLA executes the next object in the runstream just as if the innerloop had run it, except that it pops the return stack into the instruction pointer before executing the object. In other words, it executes the object as though it appeared in the calling secondary. Since it pops the return stack, it has the side effect of dropping the rest of the current secondary.

Regarding returning early from a secondary, I wish RPL had a RTN or BREAK command that functioned the same as SEMI when executed, but didn't also mark the end of the composite.
10-30-2018, 02:46 PM
Post: #19
 DavidM Senior Member Posts: 748 Joined: Dec 2013
(10-30-2018 02:27 PM)David Hayden Wrote:  Regarding returning early from a secondary, I wish RPL had a RTN or BREAK command that functioned the same as SEMI when executed, but didn't also mark the end of the composite.

I haven't tested this thoroughly, but here's a thought:

TRUE ?SEMI

I realize it's not applicable in situations where a single command is needed, but it should still work for some applications.
10-30-2018, 03:36 PM
Post: #20
 3298 Member Posts: 107 Joined: Oct 2014