RPN is an unusual language in that much of the code you write is actually the bytecode that is executed by RPN. All RPN bytecode is made up of typeable ASCII characters (for instance the bytecode for addition is '+').
All scripts contain structure such as the header, title, and labels, which is not RPN-Code. The RPN-Code is all the executable code in a script, and there are two supported modes of writing RPN-Code. These modes are selected by the script header which should start with either "RPN.2" or "RPN.4". RPN.3 has been dropped because it is incompatible with and harder to use than RPN.4. If you need to migrate code from RPN.3 format to RPN.4 see Migrating from RPN.3
In RPN.2 form, all code your write is bytecode and spaces are stripped out of your code.
In RPN.4 form, code is broken into words based solely on whitespace, so "4+2" is one word with a length of 3; while, " 4 + 2 " is 3 words each with a length of one. These words are then executed as described below (the first match from the list is used):
| Name | Action | Format | |
| 1. | local | ( -- value ) get local | [a-z|A-Z|0-9|_] not starting with a digit. Like C identifiers. |
| 2. | subroutine | ( ? -- ? ) call subroutine | any characters except: "()'[]`{}; |
| 3. | =local | ( value -- ) set local | =[local format] |
| 4. | number | ( -- value ) push number | 123, 2e-4, .3, ... |
| 5. | command | ( ? -- ? ) execute command | fixed |
| 6. | bcode | ( ? -- ? ) run string as bytecode | any |
RPN has a simple trace utility to aid in script debugging. Executing the code, 'Ut', toggles the trace state. Pressing the Record button while a function is running will also activate the trace utility and allow the function to be traced, aborted, or continued. The stack gauge (between the stack and function display) is updated periodically while functions are running.
When an illegal action is taken in RPN-Code the function which is running aborts, and a message is displayed to the user indicating the reason for the error.
To edit the currently visible script choose Edit... from the Script menu (to make the menus visible tap the upper left corner of the screen or use your device's built-in menu button or key). If the RPN-Code for the script is available, the editor will be opened and the code loaded. Scripts installed and migrated from before RPN 3.50 will not have RPN-Code installed and you will need to paste their source in from its original source. The edit form and its parts are detailed below:
![]() |
|
Make any changes you wish to the script and then press Save to load the script into RPN for use. If you wish to create a new script from an old one, simply change the title in the RPN-Code and a new script will be created when you press Save, leaving the old script unmodified.
![]() |
You can use the Export menu item to create an exportable database of your script. After selecting Export, you will be asked which export database to place the script into. This allows you to make a package containing several scripts that are to be distributed together. |
Once the export is created and after hotsync, you will find a database in your backup folder named "Yrpn_MyExportName.pdb". You can rename this file however you wish provided you leave the ".pdb" extension.
The best place to distribute your exported scripts is at the RPN Coweb.
| |
| 0 |
RPN.4.a |
| 1 |
[dialog] D'hello world' ; |
| 2 |
[a]xa; [=a]Xa; |
| 3 |
"Example" |
| 4 |
"Hello" dialog ; |
| 5 |
~ |
| 6 |
"X" a ; \ recall 'a' to tos |
| 7 |
"_set(X)" =a ; |
| 8 |
~ |
| 9 |
"_+X" a + ; |
| 10 |
"abs(X)" a 0<( a neg : a ) ; |
| 0 | Script header: RPN.2 or RPN.4 is required for all scripts; '.a' creates one global variable. |
| 1 | Subroutine: just below the script header any number of subroutines can be defined; each is labeled with a name inside brackets. This subroutine opens a dialog displaying 'a subroutine'. |
| 2 | Subroutines: Define subroutines to access the global variable. |
| 3 | Title: the script name which will be displayed in the script popup menu. The title is the first label in double quotes in the script. |
| 4 | Button: buttons are defined from left to right and top to bottom in the script button area; the code after the label will be executed when the user taps the button. |
| 5 | New line: buttons defined after the ~ will be on the line below previous buttons in the script area. |
| 6 | Button: this button will put the variable 'a' on the stack. |
| 7 | Button: this button will set the variable 'a'; the leading underscore character allows the user to drag values to the button and should be used for all buttons taking exactly one value off the stack. The underscore is not displayed as part of the name. |
| 8 | New line: start the third line of buttons in the script. |
| 9 | Button: this button puts the variable 'a' on the stack and adds. |
| 10 |
Button: a slightly more complex button script:
if a < 0 then neg(a) else a end |
Each script presents a collection of buttons for the user to press. See the Programming Tutorial above for an example.
Subroutines are an important part of writing readable code. After the header of each script you can define a list of subroutines for that script and then call the subroutines by name in any part of your RPN-Code.
Each subroutine and button has its own local variables which are not persistent. The is one variable aways available; it is read and written by the RPN-Code bytecodes: 'v' and 'V'.
In addition names locals can be allocated and used in RPN.4 code. These use a higher level word based syntax to encourage their use:
| action | stack effect | RPN-Code |
|---|---|---|
| create or set variable named "x" | ( tos - ) | =x |
| get value of variable named "x" | ( - x ) | x |
Each script has 0-255 global variables. The number defined is determined by the script header. For instance, starting a script with "RPN.4.c" defines three global variables ('a' to 'c'). These variables can be accessed from anywhere in the script and are presistent and even backed-up when you sync your device.
To read the 'b' variable onto the stack, you use the RPN-Code 'xb'. To write from the stack to the variable, use the RPN-Code: 'Xb'.
To define more than 26 globals you have to use RPN.4 form and define them in groups of 10 using a capital letter in header. You can access them using the X@ and x@ bcodes. So RPN.4.C will define 30 variables (the first 26 can still be accessed with x[a-z]).
( true_code : false_code )When such a conditional is executed a boolean value should be on the top of the stack. Either the true or the false section of code is executed but never both. Conditionals can of course be nested, and the false code section including the ':' is optional. Example (which negates tos if it is negative and adds 4 otherwise):
dup 0 < (n : 4+)The other conditional related RPN-Code can be found in the RPN-Code Reference.
{ loop_code }
All loops are infinite loops and must contain code to break out of the
loop. The basic break code is 'B'. Example (which simply breaks out of
the loop using the break code):
{ 1 2 < (B) }
Another useful construct is a count down loop:
9V{ code _v} / loop 10 times 9 -> 0
The other looping related RPN-Code can be found in the RPN-Code
Reference.
1. RPN.version[.variables[+|-width]] 2. subroutines 3. "title" 4. "label1" code ; 5. "_label2: help" code ; 6. ~ 7. \comment\ 8. \whole line comment 9. "label3" code ; "label4" code ; ...
RPN.1 [p]#'3.1415'; "Example" ~ "+" +; "-" -; "*" *; "/" /; "%\mod" %; ~ "_sin" i; "_cos" o; "_tan" a; ~ "_exp" e; "_ln" l; "2pi" 2Cp*;
RPN-Code can contain special commands words rather than bytecode. While making your code longer (usually) these commands make your code more readable. Unlike bytecodes, all of these commands need to be delimited by whitespace on both sides.
| Command | Stack Effect | Bytecode | |
| Stack | |||
| dup | ( x -- x x ) | g1 | |
| drop | ( x -- ) | d1 | |
| swap | ( y x -- x y ) | r2 | |
| rot | ( z y x -- y x z ) | r3 | |
| nip | ( y x -- x ) | r2d1 | |
| tuck | ( z y x -- x z y ) | k3 | |
| over | ( y x -- y x y ) | g2 | |
| store | ( stack -- stack ) | Z | |
| recall | ( -- previous_stack ) | z | |
| Control | |||
| break | ( -- ) | B | |
| == | ( y x -- f ) | =0 | |
| >= | ( y x -- f ) | g2g2>k3=0| | |
| <= | ( y x -- f ) | g2g2<k3=0| | |
| != | ( y x -- f) | =0! | |
| Math | |||
| err | ( -- numerical_error) | E | |
| pi | ( -- pi ) | #'3.1415926535897932' | |
| abs | ( x -- abs(x) ) | b | |
| neg | ( x -- -x ) | n | |
| fp | ( x -- fractional_part(x) ) | f | |
| wp | ( x -- whole_part(x) ) | w | |
| inv | ( x -- 1/x ) | t | |
| pow | ( y x -- y^x ) | P | |
| exp | ( x -- e^x ) | e | |
| ln | ( x -- ln(x) ) | l | |
| log | ( x -- log10(x) ) | L | |
| sqrt | ( x -- squareroot(x) ) | s | |
| Trig | |||
| sin | ( x -- sin(x) ) | i | |
| cos | ( x -- cos(x) ) | o | |
| tan | ( x -- tan(x) ) | a | |
| asin | ( x -- asin(x) ) | I | |
| acos | ( x -- acos(x) ) | O | |
| atan | ( x -- atan(x) ) | A | |
| rad | ( x_in_rad -- trig_mode(x_in_rad) ) | Mr!( 180 pi /* ) | |
| deg | ( x_in_deg -- trig_mode(x_in_deg) ) | Mr( pi 180 /* ) |
|
|
|
|
|
|---|---|---|---|
|
|
|
| push 0-9 onto stack |
|
|
| 0 » 1 | convert string to number using base 10 and push the value onto the stack |
|
|
| 0 » 0 | exit subroutine |
|
|
| 1 » 0 | set local variable; this variable is unique to each subroutine (ie. each subroutine has its own value of v |
|
|
| 0 » 1 | push the subroutine's local variable onto the stack |
|
|
| 2 » 1 | add, subtract, multiply, divide; removes two values from the stack and return result |
|
|
| 2 » 1 | returns nos**tos |
|
|
| 1 » 1 | absolute value |
|
|
| 1 » 1 | negate |
|
|
| 1 » 2 | duplicate tos |
|
|
| 1 » 0 | remove tos from the stack |
|
|
| 2 » 2 | swap the top two stack items |
|
|
| 0 » 1 | puts the stack depth on the stack (1 2 3 --> 1 2 3 3) |
|
|
| 2 » 1 | logical and bitwise binary operations |
|
|
| 1 » 1 | converts true to false, and false to true |
|
|
| 2 » 1 | returns true if |nos-tos| '=0' does an exact comparison |
|
|
| 1 » 0 | start conditional |
|
|
| 0 » 0 | separate conditional code; mark end of code to execute when true and beginning of code to execute when false; else is not required |
|
|
| 0 » 0 | close conditional |
|
|
| 0 » 0 | begin loop |
|
|
| 0 » 0 | repeat loop |
|
|
| 0 » 0 | leave current loop |
|
|
| 0 » 1 | show dialog and report button pressed;
D'Do you like\RPN?|yes|no|maybe|' creates ![]() |
|
|
| 0 » 0 | call subroutine labeled [x] |
Unfortunately, with the introduction of RPN.4 form of RPN-Code, the RPN.3 form had to be dropped. Many of your RPN.3 scripts will work without modification. Below is the list of changes you will need to make sure your RPN.3 code can be used in future versions of RPN:
x=k --> key (ascii --> ?) x=b --> button (number --> ?) x=t --> timed ( --> ?) x=o --> open ( --> ?) should not interfere with scrolling through scripts x=c --> close ( --> ?) should not interfere with scrolling through scriptsFor the button handler, {b}, buttons are numbered as follows:
Stack Notation:
Each byte code is described in stack notation having the form,
"before --> after." This notation documents the required arguments and
the results of the byte code. All byte codes consume their arguments,
so "x y --> x+y" means x and y are popped off the stack and x+y pushed
on to the stack. The shorthand tos, nos, and flag are used for
TopOfStack, NextOnStack, and boolean values. The table of byte codes
also includes links to further information on some byte codes.
|
| Name | Stack Description | Notes |
|---|---|---|---|
|
|
| --> value | push 0-9 onto stack |
|
|
| --> xxx | convert string to number using base 10; #'' gets a random, signed 32-bit number |
|
| Name | Stack Description | Notes |
|---|---|---|---|
|
|
| flag --> !flag | negate a boolean (logical negation) |
|
|
| flag1 flag2 --> flag1 | AND together two booleans (limited range bitwise) |
|
|
| flag1 flag2 --> flag1|flag2 | OR together two booleans (limited range bitwise) |
|
|
| flag1 flag2 --> flag1^flag2 | XOR together two booleans (limited range bitwise) |
|
|
| nos tos --> flag | returns true if |nos-tos| use '=@' to get X from the stack; '=0' does an exact comparison |
|
|
| nos tos --> flag | is nos less than tos? |
|
|
| nos tos --> flag | is nos greater than tos? |
|
| Name | Stack Description | Notes |
|---|---|---|---|
|
|
| flag --> | start conditional |
|
|
| --> | separate conditional code; mark end of code to execute when true and beginning of code to execute when false; else is not required |
|
|
| --> | close conditional |
|
|
| --> | begin loop |
|
|
| --> | repeat loop |
|
|
| --> | leave current loop |
|
|
| --> | goto the beginning of the current loop |
|
|
| --> | decrement indicated variable (see section on variables) and break if the variable is negative after decrementing (note: '_x@' is not valid) |
|
|
| --> | restart the current routine |
|
|
| --> | call subroutine labeled [x] |
|
|
| --> | exit function |
|
|
| --> | silently abort the current program |
|
|
| tos --> | a switch statement; format: "c(0-:1:2:3:4+)" where the numbers indicate the code that tos selects; note the last block is executed by by large inputs and the first block is executed by inputs less than or equal to zero; the skip command can be used to implement ranges: "c(0-:1:,:2|3:4+)" |
|
|
| --> | skip the next byte; "1,23+" == 4 |
One local variable is available to all buttons and subroutines. This variable is private to each routine and is accessed using the codes 'v' and 'V' (they are volatile hence the naming).
The local variable is always available and should be used before globals unless the value needs to be persistent. Using it can speed your code, use less system resources, simplify your global variable space, and make your code less affected by later changes in RPN.
Global persistent variables are also provided by RPN. Global variables are stored with your function set and and cannot be accessed outside your set (but they are global within the set). Globals are declared in the RPN-Code header. Specifying RPN.3.g, indicates that this code needs global variables a-g to be created and managed by RPN. Function sets can be created with no globals by leaving off the global specifier (ie. RPN.3). These globals are accessed using the 'x' and 'X' codes (see below). Accessing an undefined variable will abort your function. When the Functions|Replace menu command is used to overwrite a set having the same name as the set being installed, the global variables defined in both sets are copied from the old to the new set. If you replace a script, global variables are copied over from the previous installation.
Globals can also be accessed dynamically using 'x@' and 'X@' where an index to the globals is on the stack. This is a one-based array (ie. 1x@ == xa).
|
| Name | Stack Description | Notes |
|---|---|---|---|
|
|
| --> value | get global variable |
|
|
| value --> | set global variable |
|
|
| --> value | get local variable |
|
|
| value --> | set local variable |
|
| Name | Stack Description | Notes | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
| --> value | put error value on stack; same result as "10/" | ||||||||||||||||||||||||
|
|
| --> Mode_Value | X indicates mode to get; r=radianFlag; v=rpnVersionNumber; other modes may be defined later | ||||||||||||||||||||||||
|
|
| --> | goto (load) the function set named xxx | ||||||||||||||||||||||||
|
|
| see notes |
| ||||||||||||||||||||||||
|
|
| --> time | get time in ticks, minutes, hours, Days, Months, or Years | ||||||||||||||||||||||||
|
|
| --> button | run dialog and report button pressed; example D'Do you like\RPN?|yes|no|maybe|' illustrates the current features of dialogs; the |buttons| are optional: D'Hello' is legal; if you start a button name with \ then it will be the default button which is selected if the user does something other than choose a button. | ||||||||||||||||||||||||
|
|
| --> | require X arguments to proceed; use '?@' to get X from the stack | ||||||||||||||||||||||||
|
|
| freq durAmp--> | freq in Hz; durAmp=10*duration (in 100ths of a second) + amp[0-8] | ||||||||||||||||||||||||
|
|
| --> | simulate the user entering the character, X; if 'K' starts a function then a user Enter is not processed before running the function |
|
| Name | Stack Description | Notes |
|---|---|---|---|
|
|
| nos tos --> nos+tos | add |
|
|
| nos tos --> nos-tos | subtract |
|
|
| nos tos --> nos*tos | multiply |
|
|
| nos tos --> nos/tos | divide |
|
|
| nos tos --> nos%tos | mod |
|
|
| nos tos --> nos**tos | raise nos to the tos power |
|
|
| tos --> tos*XY | multiply by two digit integer XY |
|
|
| tos --> abs(tos) | absolute value |
|
|
| tos --> exp(tos) | e**tos |
|
|
| tos --> ln(tos) | natural log |
|
|
| tos --> log10(tos) | log base 10 |
|
|
| tos --> -tos | negate |
|
|
| tos --> sqroot(tos) | square root |
|
|
| tos --> 1/tos | invert |
|
|
| tos --> fp(tos) | fractional part |
|
|
| tos --> wp(tos) | whole part |
|
|
| tos --> tos/2 | much faster than "2/" |
|
| Name | Stack Description | Notes |
|---|---|---|---|
|
|
| tos --> sin(tos) | sin |
|
|
| tos --> cos(tos) | cos |
|
|
| tos --> tan(tos) | tan |
|
|
| tos --> asin(tos) | asin |
|
|
| tos --> acos(tos) | acos |
|
|
| tos --> atan(tos) | atan |
|
| Name | Stack Description | Notes |
|---|---|---|---|
|
|
| X+1 X...tos --> X+1 X...tos X | copy the Xth stack item to the top; g1=dup; g2=over |
|
|
| X+1 X X-1...tos --> X+1 tos X-1... | put TOS into Xth slot on the stack; p1=drop; p2=swap drop |
|
|
| X+1 X...tos --> X+1...tos | drop the Xth stack item; d1=drop |
|
|
| X+1 X...tos --> X+1...tos X | move Xth to top; r1=nop; r2=swap; r3=rot |
|
|
| X+1 X...tos --> X+1 tos X...nos | move top to Xth position; k1=nop; k2=swap; k3=tuck; tuck is the inverse of rotate (r3k3==nop if depth>=3) |
|
|
| --> depth | puts the stack depth on the stack (1 2 3 --> 1 2 3 3) |
|
|
| ... --> ... | store the whole stack temporarily (storage not persistent across relaunches of RPN and shared by all code); does not change the stack at all |
|
|
| ... --> previous_stack | recall the last stored state of the stack (empty stack if nothing was stored) |