Rebcode Overview - r3n/rebol-wiki GitHub Wiki
|
Rebcode is a new Rebol dialect that implements a virtual machine (VM) allowing programmers to create high performance lower-level functions in a manner that is consistent with the design principles of Rebol.
For most types of programs, and especially scripts, Rebol's normal execution methods provide excellent performance and all of the code can be written with higher level functions. However, there are special cases and specific operations, such as looping mathematical computations and large series manipulation (e.g. generating images), that require greater performance. Those cases can benefit from an optimized execution method, even if the code is more difficult to write and maintain than normal Rebol.
For cases where high performance computation is necessary, Rebol provides a different method of evaluation based on the idea of a virtual machine (VM). It is called rebcode.
Rebcode is a dialect of Rebol (a block of words and values), using a concept similar to that of bytecode used in languages like Java, and long before that, Pascal and Lisp. The result is that specific functions can be written in an optimized manner to execute ten times faster, on average, and up to thirty times faster in special cases. Cases where rebcode is useful include: special graphics routines, math operations, unique search methods, and more.
The concept of rebcode is consistent with the design principles of Rebol. Rebcode is expressed in block format and is encapsulated by a function interface. Rebcode allows access to normal Rebol variables, including those bound to other contexts, objects, and globals. In addition, rebcode is machine independent and will run identically on all processors.
- High speed execution, on average ten times faster for specific integer, decimal, logic, series, and looping algorithms.
- Integrated with Rebol as a new functional datatype (rebcode!).
- Access to normal Rebol variables with proper scoping (binding as locals, functions, objects, globals).
- Built from normal Rebol blocks, allowing loading and molding, as well as dynamic construction of rebcode dialect code using parse, compose, reduce, and other techniques.
- Supports embedded comments and doc-strings, similar to other Rebol functions, compatible with the help function.
- Opcodes for executing integer, decimal (floating point), and series (strings, blocks, images, etc.) datatypes.
- Direct execution of standard math functions such as sine, cosine, tangent, log, and others.
- Able to evaluate any arbitrary Rebol expression within a block (using the do opcode).
- Provides block-based control flow for conditional (if, either) and looping (while, until, loop, repeat) functions.
- Assembler fixup of branch labels and support for special rewriting rules.
- Rebcode is designed for experts; it is not intended for beginners. Compared to normal Rebol, it is easy to make mistakes when writing rebcode.
- Rebcode was added to Rebol primarily to provide greater performance for algorithms that require such. Because of that, rebcode opcodes are lower-level and they remove most of the type checks and datatype polymorphism found in normal Rebol.
- Rebcode is much less readable than normal Rebol; programmers should not create rebcode functions until they know for certain that optimization is needed, and that it cannot be done with normal Rebol functions, even with the special use of refinements. For example, if you decide to write a string search or parser using rebcode, you should first exhaust the range of solutions provided by the parse function and its dialect, and possibly find/match.
- High level code, such as the outer blocks and functions of your program, should never be written in rebcode; doing so provides no advantage. Use rebcode only for optimizing specific, well defined, lower-level functions.
- In some cases, it is better to generate rebcode using higher level expressions that are compiled. You can use the internal rebcode rewriting assembler or create your own special dialects that are compiled with the parse function.
The current test versions of Rebol with Rebcode can be found in the Rebol Test Releases area.
The current Windows beta release is rebview1361031.exe". The current Linux beta release is rebview1350042.tar.gz".
We will be posting links to more information and examples here soon.
Rebcode is a dialect of Rebol. It is a sequence of words and values that is interpreted in an extremely efficient manner, similar to how instructions are executed on an actual processor. (This is the reason why rebcode is said to be processed by a "virtual machine".)
Rebcode is written in a block, just like normal Rebol code. However, unlike Rebol code that gets evaluated as a series of functions, rebcode consists of a sequence of opcodes. Each opcode performs a small, low-level action on a fixed set of arguments. For example, an opcode may do nothing more than add two integers. If you are familiar with the concept of assembly code, rebcode is very similar.
Here is an example rebcode block:
[
div.i val 2
add.i val num
mul.i val 100
div.i val 50
]The words div.i, add.i, and mul.i are opcodes; they are low level functions that perform integer math operations. Each opcode is followed by two arguments that are used in the operation, and the result is stored back into the first argument (val). Thus, rebcode is implemented in what is known as the "two-address" model of computation. This technique permits optimal performance within the virtual machine.
Rebcode is a statement-based language, not an expression-based language like Rebol. Opcodes perform actions, but do not return results.
There are many different opcodes supported by the rebcode virtual machine. A summary list is provided below, and a complete description of each opcode is provided in the RebCode Reference documentation.
All rebcode must be written within a special type of function, called a rebcode function. These functions are created like Rebol user-defined functions.
Here is a simple example that makes a rebcode function:
md32: rebcode [
"Returns two integers multiplied then divided by 32."
a [integer!]
b [integer!]
][
mul.i a b
div.i a 32
return a
]The rebcode function works just like the func function. It accepts an interface specification and a function body, and returns a rebcode! datatype. Like func, the rebcode function is shorthand for:
make rebcode! args bodyThe args block is the interface specification. It can contain embedded comments, formal argument words, and optional datatype restrictions.
The body block holds opcodes and their arguments. They must be written in specific order as defined by the rebcode dialect. In addition, before the rebcode block can be executed, it must be processed by an assembler that performs actions such as doing relative branch fixups and more. See below for details.
Once defined, a rebcode function can be evaluated like all other Rebol functions. You can evaluate it and set its result to a variable:
result: md32 5 30or pass the result to other functions:
print md32 3 60or use it along with other functions:
print md32 random 10 now/monthJust keep in mind that rebcode is not a normal function. It is evaluated by a special interpreter that is very fast, but also very strict about the format of the rebcode block.
The general form of rebcode is:
opcode argument1 argument2A rebcode block contains one or more such opcodes:
add.i val 10
mul.i val 100
randz valAs with all Rebol dialects, line breaks are not relevant to the meaning of the code.
Some opcodes may take only one argument, others may require more. Most opcodes require that the first argument be a Rebol variable--it can be a function local, global, or object variable-- and allow the second argument to be a variable or a literal (integer, decimal, string, file, etc.)
There are three types of opcodes:
- Compute
- performs an operation between the arguments and stores the result into the first argument.
- Compare
- performs a comparison operation between the arguments, and sets or clears the T flag.
- Control
- conditionally performs an operation depending on the state of the T flag.
set count 0
add.i count 1
sqrt num
length? len stringThe count, num, and len variables are all modified by these compute-type opcodes to hold the result.
The compare opcodes perform an operation but do not store the result into a variable. Instead they affect an internal flag called the T flag. If the comparison is true the T flag is set to true, otherwise it is false. The T flag is then tested by a number of other opcodes that can act on it. Here are examples of the opcodes:
eq.i count 100
gte.d num 5.8
head? agesTo be useful these opcodes must be followed by a control opcode that checks the T flag. Here are examples that show the typical combinations of compare and control opcodes:
gteq.i count 100
ift [set.i count 0]
gteq.d num 5.8
brat reset-num
until [
pick num ages 1
add.i total num
tail? ages
]Other opcodes can appear between the comparison and the control opcode, as long as they do not affect the T flag. For example:
geq.i count 100
add.i count 1
ift [set.i count 0]A complete list of rebcode opcodes and their interface specifications can be obtained directly from Rebol with the following line:
print system/internal/rebcodesPart of the power of rebcode comes from the fact that normal Rebol variables can be used directly. Variables obey the same binding (scoping) rules as they do throughout Rebol. The variable can be local to the function, part of another function or object, or global. However, because rebcode is highly optimized, there are a few important rules about variables that you should know.
Rule 1: Always initialize your local variables. When you define a local variable, it is set to none by default. You must set it to the correct datatype before using it.
In this code:
code: rebcode [arg /local sum] [
set sum 0
add.i sum arg
...
]The sum variable is set to an integer value before it is used with the add.i opcode - an integer operation. If you forget this step, the variable will become a corrupt datatype. It may act like an integer in rebcode, but it will print as none.
Rule 2: Force variables to be of the correct datatype in the function interface. The code above is better written like this:
code: rebcode [arg [integer!] /local sum] [
set sum 0
add.i sum arg
...
]Now the arg variable is guaranteed to be an integer when it is used with the add.i opcode.
Rule 3: Beware of opcodes that may modify the datatype of a variable. Some opcodes will set a variable's datatype as well as its value. The general rule is this: if an opcode provides an argument that is only for holding the result (not for passing values to the opcode), then its datatype will be set according to the results of the opcode.
Here is an example:
block: [123 "name" 1.2]
code: rebcode [arg series /local sum] [
set sum 0
add.i sum arg
...
pick arg block 2
]The pick opcode will modify the arg variable to make it a string datatype. It is no longer an integer, and should not be used as such. This type of reuse of variables is common for larger functions because it reduces the number of local variables that are needed within the function.
Rule 4: For high performance code, use opcodes that are datatype specific. For example:
code: rebcode [arg1 [integer!] arg2 [decimal!]] [
...
set.i arg1 2
add.i arg1 count
...
set.d arg2 1.0
add.d arg2 value
]Here the set.i, add.i, set.d, and add.d opcodes only modify the values of their variables. They do not set the datatype. You should only use these opcodes when you know that the datatype is correct. This rule applies to many of opcodes within rebcode.
You may have figured out that opcodes ending in .i are integer opcodes and .d are decimal opcodes. This is only a naming convention; it doesn't mean you can add .i or . to the end of any opcode name to make it type specific. There is no compiler behind the scenes for basid rebcode. What you see is what you execute.
You may also have noticed that logic opcodes and some math opcodes don't have any type suffix on them; that's because there shouldn't be any confusion about what datatypes they operate against.
Rebcode functions do not return a result by default. This behavior is different from normal Rebol functions. In rebcode a value can only be returned with the return opcode.
Here is an example of a normal Rebol function. The result is returned automatically:
f1: func [a [integer!] b [integer!]] [
max a b
]However, written in rebcode, the result must be returned explicitly:
f2: rebcode [a [integer!] b [integer!]] [
max.i a b
return a
]To exit a function without returning a result, use the exit opcode. This is the same as normal Rebol functions.
One way that rebcode differs from most other virtual machine designs is that it often uses nested blocks to implement flow-of-control opcodes.
For example, the ift and iff opcodes conditionally execute a block of rebcode depending on the state of the T flag (described earlier).
add.i a 10
gteq.i a 100
ift [set.i a 0]Other functions such as while and until use the same method:
set a 0
while [lt.i a 100] [
pick n vals 1
add.i a n
]Note that the binding of rebcode remains the same in these types of control blocks. The opcodes are bound to the VM context. There is currently only one exception to this rule, the do opcode. See below for more.
Branches are always local
All of the branch opcodes (bra, brat, etc.) expect their target labels to be within the same block of code. The branches are always relative to the current block. You cannot branch to a target label outside the block where the branch occurs; doing so will produce erroneous results.
The T flag is set and checked by various opcodes; it acts as a temporary flag, so you don't have to allocate a word to hold the result of comparisons.
If you've programmed in assembly language, you are no doubt familiar with CPU flags (e.g. the zero and carry flags). Rebcode doesn't require CPU emulation or need multiple flags or registers, but it does operate on similar principles; the T flag is one of those. The T in T flag stands for true.
The opcodes listed in the summary section under Compare Opcodes alter the T flag. In addition, the value? and sett opcodes affect it.
The following opcodes check the T flag and act accordingly.
braf
brat
breakf
breakt
either
iff
ift
until
whileThe state of the T flag can also be set from or to a variable. The sett sets the T flag from a variable, and gett gets the T flag and puts it in a variable. This is useful if you need to combine the results of multiple comparisons (and using conditional branches alone is not enough).
How to remember gett and sett. Read them as:
SET T flag from a variable
GET T flag and put it in a variableThese two opcodes will be a source of problems unless this rule is memorized. If you just remember SET T it will be enough. An easy way to remember it is this: SETT is the same as "SET T".
Note that SETT sets false flag also for zero integer!
Although the higher level control opcodes like ift, either, while, until, loop, repeat and others are easier to write, usually more readable, and often faster, there may be times when code can be optimized with the use of branch opcodes.
Four branch opcodes are supported:
bra unconditional branch
brat branch if T flag is true
braf branch if T flag is false
brab branch via an index into a block tableThe argument to the branch opcodes is an integer value, representing how much of an offset you want the branch to perform. Branch offsets are always relative to the location after the branch opcode, not the absolute offset within the block. Positive values branch forward; negative, backward. The branch target must always fall within the current code block.
To make it easier to write branch offsets, labels are allowed. A label is created with the label opcode. If the bra, brat, or braf opcodes are followed by a word, the word is assumed to be a label, and the assembler will compute the correct relative offset.
Here is an example of branches and labels:
label top
add.i n 1
gt.i n 100
brat done
...
bra top
label doneNotice that the label word is also an opcode; however, it performs no operation. The label is kept in the code block to support reflection (molding) of the block.
The brab opcode allows you to branch to an offset selected at runtime by an index. The first argument to the opcode is normally a block, and the second is a zero-based index into that block. The value at that position is fetched and assumed to be the integer offset for the branch.
Here is an example:
brab [4 6 8] n
print "default"
bra done
print 1
print 2
print 3
label doneNote that if the branch index is past the tail of the block, the brab will fall through to the next opcode. This allows you to create default cases without requiring prior testing of the index value.
The contents of the branch block can also be labels, which will be converted to integer offsets by the assembler, similar to normal branches.
brab [lab1 lab2 lab3] num
bra error-caseThere is also a special case of operation. If the block argument to brab is an integer (created from a label), then the branch is made to that relative location plus the value of the index argument.
Rebcode provides the apply opcode to call other functions. These include rebcode, natives, and user-defined functions.
Note: apply cannot evaluate action or op functions at this time.
The format of the apply opcode is:
Result then refers to the value returned from the function. Function is the name of the function, and the args block holds the values that are passed as arguments to the function.
The args block must be fully reduced to a block of values and/or variable words. It cannot contain opcode expressions.
Here is an example of rebcode that calls the checksum native:
apply num checksum [string]
mul.i num 10
...Rebcode functions can be called in the same way. For instance, if you define the function:
add-mul: rebcode [a [integer!] b [integer!] c [integer!]] [
add.i a b
mul.i a c
return a
]It can be called from rebcode with a line such as:
apply num add-mul [n m 10]If a function allows refinements, they can also be specified in the argument block. The position of the refinement arguments is that specified by the function interface. For example, if you ask for help on checksum, you see:
>> ? checksum
USAGE:
CHECKSUM data /tcp /secure /hash size /method word /key key-value
To invoke the checksum function with the /secure refinement, you would write:
apply num checksum [string none true]This specifies the /secure refinement as being enabled, but not /tcp refinement.
Note that all unsupplied arguments are set to none. In the examples above, the /hash, size, /method, word, and all other arguments will be set to none when the checksum function is called.
In normal Rebol code, you are allowed to change the position of refinements within the interface specification of functions. In normal Rebol, this will have no affect on the functions when they are called.
For instance, this function:
bub: func [val /normal /only] [...]can be changed to:
bub: func [val /only /normal] [...]Without affecting normal Rebol code. However, it will have an affect on rebcode that calls it, because the order of refinements in rebcode is specified by position, not by name.
The do opcode is used to invoke the normal Rebol interpreter. This allows your rebcode to evaluate any Rebol expression from within your rebcode function. This is a useful "escape" when your code needs to perform more complicated actions or access functions or objects that are not easy to use directly in rebcode.
do plat [reduce [system/version/4 system/version/3]]Note that do opcode does not bind the contents of its block to the VM context. This allows you to use normal Rebol code within the block.
The do opcode escapes to Rebol, meaning it is much slower than the rebcode VM. If you use do inside a rebcode function, particularly inside a loop, consider whether you need to use rebcode at all. Using do will greatly impact the performance of rebcode.
As described above, rebcode functions are created with rebcode function such as:
md32: rebcode [
"Returns two integers multiplied then divided by 100."
a [integer!]
b [integer!]
][
mul.i a b
div.i a 100
return a
]The value of variable md32 is of the rebcode! datatype. This is a functional datatype, the same as function!, native!, action!, and others.
Rebol datatype functions apply to rebcode. For example:
print type? :md32
rebcode!To check if a value is rebcode, you can write:
if rebcode? :md32 [...]Rebcode also satisfies the general function check:
if any-function? :md32 [...]Like other functions, help can provide usage information for rebcode functions:
help md32
USAGE:
md32 a b
DESCRIPTION:
Returns two integers multiplied then divided by 100.
md32 is a rebcode value.
ARGUMENTS:
a -- (Type: integer)
b -- (Type: integer)To obtain the context words for a rebcode function:
first :md32
[a b]To get the body block of a rebcode function:
second :md32
[
mul.i a b
div.i a 100
return a
]Note that the body may be different than that used for the creation of the function. The changes are the result of the rebcode assembly process.
To get the function interface specification:
third :md32
[
{Returns two integers multiplied then divided by 100.}
a [integer!]
b [integer!]
]The mold, save, and source functions also work on rebcode. Here is an example of source:
source md32
md32: rebcode [
{Returns two integers multiplied then divided by 100.}
a [integer!]
b [integer!]
][
mul.i a b
div.i a 100
return a
]The save/all function also creates a properly formed rebcode literal block.
The comment opcode lets you embed comments into your code. They differ from normal ";" comments because they remain within the body of the code and will appear if the code is printed, molded, or saved.
For example, to add a string comment to your code:
comment "This is a comment"You can also use a comment to temporarily remove sections of code by putting it within a block:
comment [
add.i n 1
eq.i n 10
ift [set n 0]
]It is more difficult to write rebcode than regular Rebol. Invalid expressions will crash the process; datatype mismatches may produce bad results without crashing.
To help you write and test code, a few debugging opcodes have been provided. These opcodes are similar to their related functions in Rebol. You can insert them into your code to view values during debugging.
- ?? variable : Print a variable name followed by its value.
- probe : Print a molded value or a molded block of values.
- print : Print a value or block of values.
- escape : Check if escape key has been pressed.
The rebcode assembler is invoked each time a rebcode function is made (normally by calling the rebcode function). The main purpose of the assembler is to bind the opcodes to the VM context, and to create branch offsets from their target labels.
The assembler may also include other features in the future. The current format and operation of these features is subject to change and may be modified in future test releases.
The source code for the assembler can be viewed with:
probe system/internalMore information about the rebcode assembler will be added in future updates to this documentation.
There are three types of errors that can occur in rebcode:
- syntax : These errors occur at load time, the same way they do with all Rebol expressions. They are normally the result of improperly written Rebol values.
- assembly : When you create a new rebcode function, it is parsed by the assembler (mentioned above). The opcodes and their datatypes will be verified during this operation; if they are invalid, an error message will be generated.
- runtime : For performance reasons very little checking is done within opcodes. This is different from most Rebol function code. It is possible for errors to exist in your rebcode that can crash the Rebol process. Such errors are permitted, and must be eliminated by the programmer. It is also possible for errors to have no effect at all, or to produce invalid results.
Produces the same result as the Rebol log-2 function.
log-2: rebcode [n [decimal!]] [
log-e n
mul.d n 1.44269504088896 ; 1.44... = 1 / log-e 2
return n
]Here is a factorial function written in rebcode as an example. It's about 10 times faster than plain Rebol, but if you really need speed for a function like this, you may be better of with a memoization approach if that can be done.
factorial: rebcode [
n [number!]
/local res d
] [
set res 1.0
set d 0.0
to-int n
loop n [
add.d d 1.0
mul.d res d
]
return res
]The Ackermann function is often used for benchmark tests. It is defined as:
ack: func [m n] [
either zero? :m [:n + 1] [
either zero? :n [ack (:m - 1) 1] [
ack (:m - 1) ack :m (:n - 1)
]
]
]Here it is written in rebcode:
ack-rc: rebcode [m [integer!] n [integer!] /local result] [
eq.i m 0
either [
set result n
add.i result 1
] [
eq.i n 0
either [
sub.i m 1
apply result ack-rc [m 1]
] [
sub.i n 1
apply result ack-rc [m n]
sub.i m 1
apply result ack-rc [m result]
]
]
return result
]power: rebcode [val [decimal!] exp [decimal!] "Exponent"] [
log-e val
mul.d val exp
exp val
return val
]root: rebcode [val [decimal!] exp [decimal!] "Exponent"] [
log-e val
div.d val exp
exp val
return val
]Let's say you need to perform two comparisons and act on the result. For example:
(a = 1) and (b = 2):This can be implemented by doing the comparisons in this manner:
eq.i a 1
gett c1
eq.i b 2
gett c2
and c1 c2
ift [...]If the comparison is used for a loop, a faster method is:
eq.i a 1
brat here
eq.i b 2
brat hereOr, to exit the loop:
eq.i a 1
breakt
eq.i b 2
breaktHere is a function that will convert separate RGBA components into a single RGBA integer value:
rgba-to-int: rebcode [
r [integer!] g [integer!] b [integer!] a [integer!]
] [
lsl a 24
lsl r 16
lsl g 8
or a r
or a g
or a b
return a
]Converts a string of binary digits, zeros or ones, to a signed integer value. The first digit represents the sign bit.
bin-str-to-int: rebcode [
s "string of binary digits; up to 32 chars"
/local len res dig bit
] [
tail? s
ift [return 0] ; bail if empty string
length? len s
gt.i len 32
ift [return none] ; bail if s more than 32 chars
set res 0 ; this is our accumulator
tail s ; we'll be walking the string backwards
set bit 1 ; which bit are we going to flip for 1's
until [
back s
pick dig s 1
eq.i dig 49 ; 49 = #"1"
braf not-a-one ; not a 1, skip bit flipping
or res bit ; it's a 1; flip the bit
label not-a-one
lsl bit 1 ; shift our bit so we flip the next one
; on each pass.
head? s ; done when we hit the head
]
return res
]Converts a signed integer value to a string of binary digits; zeros or ones; the first digit represents the sign bit.
int-to-bin-str: rebcode [
val [integer!]
/local res tmp bit
] [
copy res "00000000000000000000000000000000" -1
tail res ; we'll be walking the string backwards
set bit 1 ; which bit are we going to flip for 1's
set tmp 0 ; intialize datatype
until [
back res
set.i tmp val
and tmp bit
neq.i tmp 0
braf not-a-one ; not a 1, skip digit setting
poke res 1 49 ; 49 = #"1"
label not-a-one
lsl bit 1 ; shift our bit so we flip the next one
; on each pass.
head? res ; done when we hit the head
]
return res
]This section provides a summary of all rebcode opcodes. For more information about specific opcodes, see the Rebcode Reference document.
abs.i Changes the operand to its absolute value.
add.i Integer add; adds operand and value.
div.i Divides operand by value; the integral result goes in the operand.
max.i Sets the operand to the greater of the two values.
min.i Sets the operand to the lesser of the two values.
mul.i Multiplies operand by value.
neg.i Negate. Changes the sign of the operand.
randz Zero-based random number generator; sets operand to value from 0 to (value - 1).
rem.i Remainder. Divides operand by value; the integer remainder goes in the operand.
sub.i Subtracts value from operand.abs.d Changes the operand to its absolute value.
acos Arccosine.
add.d Decimal add; adds operand and value.
asin Arcsine.
atan Arctangent.
cos Cosine.
div.d Divides operand by value.
exp Exponential; raise a number to a power.
log-10 Log base 10.
log-e Natural log.
max.d Sets the operand to the greater of the two values.
min.d Sets the operand to the lesser of the two values.
mul.d Multiplies operand by value; result goes in the operand.
neg.d Change sign
sin Sine.
sqrt Square root.
sub.d Subtracts value from operand; result goes in the operand.
tan Tangent.and Bitwise AND of two integers; result goes in the operand.
bswap Byte swap. Converts integer endian-ness.
compl Bitwise complement.
ext8 Sign extend; 8-bits to integer.
ext16 Sign extend; 16-bits to integer.
lsl Logical shift left; toward MSB.
lsr Logical shift right; toward LSB.
rotl Rotate left; toward MSB.
rotr Rotate right; toward LSB.
not Logic datatype complement
or Bitwise OR of two integers; result goes in operand.
xor Bitwise XOR of two integers; result goes in operand.back Moves the current position of the series backward by one.
head Changes the current position of the series to its head.
next Moves the current position of the series forward by one.
skip Changes the current position of the series forward or backward.
tail Changes the current position of the series to its tail.change Changes count values at the specified position in a series.
clear Removes all values from current index to tail. Leaves reference at tail.
copy Copies count items from series. Operand modified.
insert Inserts one series into another and sets the operand to the series at the insert point.
pick Sets the operand to refer to the value at the specified position in a series.
pickz Zero-based pick. Operand modified.
poke Changes the value at the specified position in a series.
pokez Zero-based poke.
remove Remove count items from the series, at the current position.index? Sets the operand to the index number of the current position in the series.
length? Sets the operand to the length of the series from the current position.See Also: Compare Opcodes/Series Comparisons
apply Apply a function to arg block. Set operand to result.
comment Includes a comment in the code
do Escape to normal evaluation. Set operand to result.
gett Get the TRUE flag and store in a variable. Operand modified.
getw Get the value of a word (indirect). Result modified.
set Set a variable to any value. Operand modified.
set.d Set decimal variable only. Operand modified.
set.i Set integer variable only. Operand modified.
sett Set the TRUE flag from contents of a variable
setw Set the value of a word (indirect).
to-dec Convert integer to decimal. Operand modified.
to-int Convert decimal to integer. Operand modified.
type? Sets the operand to the value's datatype.
value? Set TRUE flag if the variable has a value.?? Works like ?? in Rebol.
escape? Check if user pressed escape key. If so, halt to console.
print Works like print in Rebol.
probe Works like probe in Rebol.eq.i Sets the TRUE flag if the values are equal.
glt.i Sets the TRUE flag if: value1 < operand < value2.
glte.i Sets the TRUE flag if: value1 <= operand <= value2.
gt.i Sets the TRUE flag if the first value is greater than the second value.
gteq.i Sets the TRUE flag if the first value is greater than or equal to the second value.
lt.i Sets the TRUE flag if the first value is less than the second value.
lteq.i Sets the TRUE flag if the first value is less than or equal to the second value.
neq.i Sets the TRUE flag if the values are not equal.eq.d Sets the TRUE flag if the values are equal.
glt.d Sets the TRUE flag if: value1 < operand < value2.
glte.d Sets the TRUE flag if: value1 <= operand <= value2.
gt.d Sets the TRUE flag if the first value is greater than the second value.
gteq.d Sets the TRUE flag if the first value is greater than or equal to the second value.
lt.d Sets the TRUE flag if the first value is less than the second value.
lteq.d Sets the TRUE flag if the first value is less than or equal to the second value.
neq.d Sets the TRUE flag if the values are not equal.head? Sets the TRUE flag if a series is at its head.
past? Sets the TRUE flag if the series is past its end.
tail? Sets the TRUE flag if a series is at its tail.See also: SETT, VALUE?
iff If the TRUE flag is not set, evaluate the block.
ift If the TRUE flag is set, evaluate the block.
either If the TRUE flag is set, evaluate the first block; otherwise evaluate the second block.breakf If the T flag is not set, break out of the currently executing block.
breakt If the T flag is set, break out of the currently executing block.
loop Evaluates the block a specified number of times.
repeat Evaluates a block a number of times or over a series.
repeatz Zero-based repeat (0 to n-1)
until Evaluate a block until the TRUE flag is set
while While the condition block sets the TRUE flag, evaluate the body block.bra Unconditional branch
brab Branch block table
braf Branch to target if the TRUE flag is not set.
brat Branch to target if the TRUE flag is set.
label Define target labelexit Exits a rebcode function, returning no value.
return Return value from rebcode function.Thanks to Brian Hawley for many insightful comments on this document, and the design and implementation of rebcode