Table of Contents

Basics of Scripting Advanced Scripting

Basics of Scripting

Introduction to RPN-Code

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):

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 number123, 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.

Editing Scripts

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:

edit form
Script Editor Title
Tap here to access the Edit menu.
Save Button
Save changes to the script.
Cancel Button
Tap here to discard changes without loading the script for use.
Next Placeholder Button (>>)
Move cursor to the next place holder. The tab key or stroke also does this.
Code Area
The code for the script is edited here.
Token Popup - var
Insert available subroutine and variable names into the script.
Structure Popup - {x}
Insert various programming structures with placeholders for input.
Stack Popup - stack
Insert stack commands into script.
Query Popup - cond
Insert query commands into script.
Arithmatic Popup - arith
Insert basic math commands into script.
Math Popup - math
Insert math commands into script.

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.

Exporting Scripts

edit form

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.

Programming Tutorial

The following tutorial will walk through an example of creating a script. The steps for inputing the script are as follows:
  1. Start the script editor using the Edit... item in the Script Menu.
  2. Enter the script text (delete the starting text if needed).
  3. Tap the Save button.
Structure of a Script
The structure of a script is explained below using an example script:
resulting script
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

Basic Techniques

Button Layout

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.

Local Variables

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:

actionstack effectRPN-Code
create or set variable named "x"( tos - )=x
get value of variable named "x"( - x )x

Global Variables

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]).


There is one main conditional structure in RPN-Code:
			( 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.


There is one main looping structure in RPN-Code:
			{ 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.

Script Format

The numbers to the left match the notes below and are not part of RPN-Code:
			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 ;
  1. The first five characters of any RPN script must be of the form RPN.X[.g[(+|-)w]] where X is the RPN-Code version number that the script requires. Currently the version for new script should always be '2' or '4'. The variable field of the header indicates the number of global variables and is discussed in the section on global variables. The width field of the header, w, indicates how to adjust the width of the script window. Valid widths are -8 to 8. The width of the window is adjusted by 10 pixels per increment, so RPN.2.a+5 makes the script area 40 pixels wider than its usual 80 pixels. The variable and width field are optional. If they both are present the variable field must come first.
  2. Subroutines are of the following form: [name] code ;
    Subroutines named with a single letter can be called by using the RPN-Code, C. Other subroutines are called by name if using RPN.4. Subroutines must come after the "RPN.X" and before the first label.
  3. Labels are formed by enclosing text in quotes. The first label is the title of the script being defined. '"' is not a valid label character.
  4. Defining a button is done by specifying a label followed by the button's RPN-Code. All button definitions must end in a semi-colon.
  5. Starting a function label with the '_' character allows values to be dragged to that button. The '_' character is not displayed and the function should have the following stack signature: (n --> ?). The ':' character signals the beginning of the button's help string. The ':' and following characters are not displayed. When help is turned on the entire button label (3 lines maximum) is shown in a RPN dialog (use '\' for a line feed);
  6. The '~' character is used to separate lines of buttons. There can be up to 4 lines of buttons defined by any script. All lines have the same height, and all buttons on a row are of the same width. This height and width is adjusted according to the number of lines and buttons.
  7. The '\' character is used to delimit comments.
  8. The end of a line also ends a comment.
Note: There must be an even number of ' and " characters in every valid script; otherwise, error checking will reject the script.


			       "+" +;
			       "-" -;
			       "*" *;
			       "/" /;
			       "%\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.

CommandStack Effect Bytecode
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
break( -- )B
==( y x -- f )=0
>=( y x -- f )g2g2>k3=0|
<=( y x -- f )g2g2<k3=0|
!=( y x -- f)=0!
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
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 /* )

Basic Bytecodes

The following RPN-Code list describes the basic operations which are most commonly used in scripting RPN. There is a complete list in the next section for all RPN-Code.
In the Stack column, the number of items removed from the stack and the number of items put onto the stack are listed separated by the »character.
0 » 1
push 0-9 onto stack
0 » 1convert string to number using base 10 and push the value onto the stack
0 » 0exit subroutine
1 » 0set local variable; this variable is unique to each subroutine (ie. each subroutine has its own value of v
0 » 1push the subroutine's local variable onto the stack
+ - * /
2 » 1add, subtract, multiply, divide; removes two values from the stack and return result
2 » 1returns nos**tos
1 » 1absolute value
1 » 1negate
1 » 2duplicate tos
1 » 0remove tos from the stack
2 » 2swap the top two stack items
0 » 1puts the stack depth on the stack (1 2 3 --> 1 2 3 3)
&| ^ >
boolean operations
2 » 1logical and bitwise binary operations
boolean not
1 » 1converts true to false, and false to true
2 » 1returns true if |nos-tos| '=0' does an exact comparison
1 » 0start conditional
0 » 0separate conditional code; mark end of code to execute when true and beginning of code to execute when false; else is not required
0 » 0close conditional
0 » 0begin loop
0 » 0repeat loop
0 » 0leave current loop
0 » 1show dialog and report button pressed;
D'Do you like\RPN?|yes|no|maybe|' creates dialog image
0 » 0call subroutine labeled [x]

Advanced Scripting

Advanced programming topics are covered here. If these details do not make sense, then you probably don't need them.

Migrating from RPN.3

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:

  1. Change the header to be RPN.4 rather than RPN.3.
  2. Wherever you used the old local.make or local.set syntax replace both =local. Currently .set and .make still work but they will be removed soon.
  3. RPN.3 allowed you to access globals by single character references. RPN.4 does not. Simply define a subroutine for each variable: [a]xa; and that will allow the same code to work, though you might consider naming your globals something better than a-z.
  4. Subroutines are now searched before locals, this is so that [=global]Xa; will be found instead of setting a local.
  5. Locals and subroutines have better defined naming limitations (see the Formats in the Introduction to RPN-Code).

Event Handlers

RPN includes special subroutine forms for intercepting and handling events. When event handlers are called RPN does not enter the user's input or update the display after the handler is done; the handler should use 'Ue' and 'Ud' to cause an enter and display action to occur. Handlers which override the a default behavior (such as a key or button action) of RPN should use 'Uh', handled, to signal that the event has been handled. If the handler does not execute 'Uh' then RPN will handle the event as usual. The handler subroutines have the form {x}ByteCodes; where x determines the event to handle as follows:
		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 scripts
For the button handler, {b}, buttons are numbered as follows:

Bytecode Reference

All the bytecodes available for RPN scripting are detailed here:
[ Literal | Boolean | Control | Variables | Stack | Math | Trig | Misc ]

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.


[ Literal | Boolean | Control | Variables | Stack | Math | Trig | Misc ]

NameStack DescriptionNotes
--> valuepush 0-9 onto stack
--> xxxconvert string to number using base 10; #'' gets a random, signed 32-bit number


[ Literal | Boolean | Control | Variables | Stack | Math | Trig | Misc ]

Boolean values [(-1,1)=false, other=true] are produced by comparison operations and can be combined using logical operators. Only the whole part of the number is used to determine its logical value, so the non-inclusive range (-1,1) is false. In general, boolean values should not be mixed with numerical values and should not be left on the stack; however, it may be of use to know that the logical operators (|^)are actually bitwise operators as long as their inputs fit into a unsigned 32-bit number. Negative numbers can be used as boolean values but only their absolute value matters; logical operations return unsigned numbers regardless of input signs. The not code, !, is logical rather than bitwise. If you find the above information confusing, please just think of the boolean codes as operating on the special values returned by comparison (ie. you don't need to think about what the numerical value of booleans are unless you combine them with numbers, display them, or use a number as a boolean).
NameStack DescriptionNotes
flag --> !flagnegate a boolean (logical negation)
flag1 flag2 --> flag1AND together two booleans (limited range bitwise)
flag1 flag2 --> flag1|flag2OR together two booleans (limited range bitwise)
flag1 flag2 --> flag1^flag2XOR together two booleans (limited range bitwise)
nos tos --> flagreturns true if |nos-tos| use '=@' to get X from the stack; '=0' does an exact comparison
nos tos --> flagis nos less than tos?
nos tos --> flagis nos greater than tos?


[ Literal | Boolean | Control | Variables | Stack | Math | Trig | Misc ]

RPN's control codes allow you to setup conditional and looping structures. Both can be nested (to greater than 100 levels).
NameStack DescriptionNotes
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
var break
-->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


[ Literal | Boolean | Control | Variables | Stack | Math | Trig | Misc ]

There are two kinds of variables in RPN which are accessed with low-level RPN-Code.

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).

NameStack DescriptionNotes
get var
--> valueget global variable
set var
value -->set global variable
get var
--> valueget local variable
set var
value -->set local variable


[ Literal | Boolean | Control | Variables | Stack | Math | Trig | Misc ]

NameStack DescriptionNotes
--> valueput error value on stack; same result as "10/"
--> Mode_ValueX indicates mode to get; r=radianFlag; v=rpnVersionNumber; other modes may be defined later
goto function set
-->goto (load) the function set named xxx
user interface action
see notes
ttrace--> toggle byte code tracing mode
ddisplay--> redraw the RPN stack and input display
eenter--> process the user inout line
bbasetos --> set the base to tos (1<tos>257)
hhandled--> signal that an event handler handled the event and RPN should not handle the event
TTimertos --> set the timer event to happen in tos/100 seconds
zzerotos --> the character used to diplay a given digit is given by c(d)=Z+d when Z!='0' and (d>9 ? 'A':'0')+d otherwise; RPN digits are case insensitive iff Z='0'=48; this byte code sets Z (so #'48'Uz is the normal mode)
--> timeget time in ticks, minutes, hours, Days, Months, or Years
--> buttonrun 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


[ Literal | Boolean | Control | Variables | Stack | Math | Trig | Misc ]

NameStack DescriptionNotes
nos tos --> nos+tosadd
nos tos --> nos-tossubtract
nos tos --> nos*tosmultiply
nos tos --> nos/tosdivide
nos tos --> nos%tosmod
nos tos --> nos**tosraise nos to the tos power
mul Int
tos --> tos*XYmultiply 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 --> -tosnegate
tos --> sqroot(tos)square root
tos --> 1/tosinvert
tos --> fp(tos)fractional part
tos --> wp(tos)whole part
tos --> tos/2much faster than "2/"


[ Literal | Boolean | Control | Variables | Stack | Math | Trig | Misc ]

All trig operations are performed in the user's current trig mode (radians or degrees). Use the "Mr" (see Misc section) to test the current mode.
NameStack DescriptionNotes
tos --> sin(tos)sin
tos --> cos(tos)cos
tos --> tan(tos)tan
tos --> asin(tos)asin
tos --> acos(tos)acos
tos --> atan(tos)atan


[ Literal | Boolean | Control | Variables | Stack | Math | Trig | Misc ]

RPN's stack codes all take a stack index an opcode. Indexes start at one (zero is not a valid stack index). Using an invalid stack index aborts the running function. The special opcode '@' (read 'd@' as "drop at") indicates that the stack index is on the stack. This stack index is an index to the stack after the index itself is removed from the stack (ie. d1 == 1d@).
NameStack DescriptionNotes
X+1 X...tos --> X+1 X...tos Xcopy 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...tosdrop the Xth stack item; d1=drop
X+1 X...tos --> X+1...tos Xmove Xth to top; r1=nop; r2=swap; r3=rot
X+1 X...tos --> X+1 tos X...nosmove top to Xth position; k1=nop; k2=swap; k3=tuck; tuck is the inverse of rotate (r3k3==nop if depth>=3)
--> depthputs the stack depth on the stack (1 2 3 --> 1 2 3 3)
... --> the whole stack temporarily (storage not persistent across relaunches of RPN and shared by all code); does not change the stack at all
... --> previous_stackrecall the last stored state of the stack (empty stack if nothing was stored)