REXX Language implementation
The purpose of the cRexx assembler rxas is to translate a text file with
rxvm assembler instructions to a file with binary contents containing these
instructions in their binary, executable form. Its main use is to
translate an rxas file produced by the cRexx compiler
rxc to a binary .rxbin object module.
{height=100 width=500}
The assembler processing goes through a number of steps in a single pass: first, the Lexer / Scanner tokenises the RXAS code. After that, the Parser parses the structure into a series of instructions. The binary writer generates the binary code and constant pool for the program at hand. The backpatcher runs last and handles forward references.
The rxas assembler has a .rxas file as input and
produces an rxbin file as output, which can be considered an
object module, as it has unresolved addresses, which can be
resolved by the linkage editor component of the rxvm virtual
machine interpreter. It also produces a report to stdout (in case of
errors only) and can produce a trace file in Debug/verbose mode
(option -d).
The input file is assumed to be valid UTF8. The assembler, like the compiler, operates using two character sets. The first is for symbols in the assembler language statements. These are all composed of the ASCII subset of Unicode. The second character set is used for data; the contents of variables. Here the whole of Unicode can be used.
When the command line argument -h is specified the options are shown:
\fontspec{IBM Plex Mono} \begin{terminaloutput} \small \obeylines \splice{rxas -h | sed ‘s/\&/\\&/g’} \end{terminaloutput} \fontspec{TeX Gyre Pagella}
The assembler contains an optimizer; this is different from the
optimizer which is part of the compiler. This phase of the assembler
is running always, except when switched off by the -n
option. When there is any doubt whether any encountered problem is
caused by the optimizer, switching it off can help diagnosing the problem.
For machine instructions, see the cRexx VM
Specification publication. This section discusses instructions to the
assembler, which are called directives to clearly distinguish
them from virtual machine instructions. These are necessary to pass information into the compiled
.rxbin binary file, to enable execution by the rxvm
virtual machine. In the following itemized list, italic
descriptors are categories, while items in roman type are literal directives.
/* and */
indicators. The * (asterisk) can be used as a line
comment. The remainder of the line after a line comment is ignored.ID, is a target for branching-type instructions.Example:
loop:
fndnblnk r3,r1,r3 /* find first/next non blank offset */
ilt r5,r3,0 /* if <0, nothing found, end search */
brt break,r5
inc r6 /* else increase word count */
/* offset of word is in R3 */
copy r8,r3 /* save offset of word */
fndblnk r3,r1,r3 /* from offset find next blank offset */
ieq r7,r6,r2 /* is this the word we are looking for?*/
brt wordf,r7 /* go and fetch it */
ilt r5,r3,0 /* if <0, nothing found, end search */
brt break,r5 /* word not found */
bct loop,r4,r3 /* continue to look for next non blank char */
r0 ...*n are names of registers, indicated in the machine instructions documentation as REG.Defines \emph{int} global variable \emph{g0 … g\emph{n}}. These can be used within any procedure in the file.
The function header defines how many registers (called locals) the function can access - for practical purposes one can consider that any number of registers can be assigned to a function. In addition, each file defines a number of global registers that can be shared between procedures.
Here are several examples of how to use rxas to
assemble a program into an object module.
The cRexx compiler rxc enables1 inline assembly through the
assembler statement. When used in this way, a lot of
the complications of an assembly language program can be handled by the
cRexx compiler, like assigning registers to variables, and the
conversion of datatypes like integer for display as string.
/* crexx ipow test - inline assembler */
options levelb
number = 10
power = 2
result = 0
say 'test IPOW'
assembler do
ipow result,number,power
end
say "result =" result /* The compiler converts integer to string */
This is a simple and straightforward way to complement the low level assembler instructions with the power of the Rexx language. The following example intends to explain how this is implemented; it can be skipped without consequences.
In this example, the compiler generates the following assembler source:
\lstinputlisting[ language=rxas, basicstyle=\ttfamily\small, breaklines=true, columns=fullflexible ]{examples/pow.rxas}
The .srcstep directives (intended for trace, sourceline, panic reports,
and debuggers) indicate where the work is done. Related .traceevent
directives identify values that are actually available for trace display. The
variables are assigned, as integers, to the registers r1 and
r2. The line ipow, number, power becomes ipow
r3,r1,r2, and the display on the terminal is handled by the
itos,sconcat and say instructions.
This is an example, with the remark that in this case, the microcode
for ipow is always executed, the example in crexx on
page \pageref{fpowexample} shows that the cRexx optimizer of the
compiler can eliminate this code entirely.
The use of Assembler directives is not allowed in inline assembly, so (as an example) is it not possible to define procedures in an inline assembler block.
The assembler will give messages when there are problems in a source file. These are hopefully of enough clarity to resolve the immediate problem with syntactic issues or typos. When a program assembles correctly but its behaviour is unexpected, or its output is incorrect, a number of different strategies can be followed.
It is easy to add say statements to your program. Unlike
Rexx, there is no trace statement for assembler programs. It
is possible to disassemble (see page \pageref{rxdas—the-crexx-disassembler} an .rxbin module, and reassemble it
with added statements.
The rxdb debugger has a mode for assembler. This can be used to
set breakpoints and/or step through the code; here the registers can
be traced so variables in your program can be followed and the
comparisons and branches can be checked. For more information about the
debugger, see page \pageref{rxdb—the-crexx-debugger}.
When used with options level b ↩