.. index:: RPN

RPN compiler
============

The RPN compiler makes it possible to compile HP-41 RPN programs into
the same output format as the Nut assembler. That is, it will output a
relocatable object file and optionally a list file. The relocatable
object file is suitable to be linked into a HP-41 module.


.. running:: "RPN compiler" rpncomp
.. literalinclude:: gen/CompilerSignOn.text
   :language: none

The input source file can either be a source file or a raw binary RPN
image (using ``.raw`` file extension).

The default behavior is to convert ``XEQ`` to a global alpha label, to be
its corresponding ROM based ``XROM`` instruction. The ``XROM`` variant
will always call the same function and uses less memory, see
:ref:`sec-xrom-encoding`.

Normally you do not need to use any particular option, unless you want
to preserve an ``XEQ`` to a global alpha label which may be used as a
mechanism to call a user written function stored in RAM. A more
flexible way to make such call, is to store the global alpha label name in a
register and call it indirectly.

In addition to creating relocatable files, the RPN compiler can
convert ``.raw`` files to readable ``.rpn`` source files. Use the
``--rpn-output`` for this, see :ref:`sec-rpn-output` for more
details.


.. index:: language;RPN, RPN;language
.. _sec-rpn-language:

RPN language
------------

The language used is similar to how it appears in the HP-41 with some
minor differences due to special characters.

.. index:: Unicode;UTF-8

The RPN compiler expects the input file to be encoded in UTF-8, which
means that ordinary ASCII characters are encoded as (7-bit) ASCII
characters, while non-ASCII characters are encoded in sequences of
multiple bytes (with the highest bit set, making them different from
all ASCII characters). Thus, it is possible to type an instruction
such as ``ΣREG`` in the way it appears in the HP-41, which is
impossible to do in ASCII.


.. index:: RPN;synonyms

In order to help typing programs, some synonyms are also accepted:

.. table:: RPN synonyms
 :widths: 1 2
 :column-dividers: none single none

 +----------------+-----------------------+
 |RPN             |Synonyms               |
 +================+=======================+
 |``CLΣ``         |``CLSIGMA``            |
 +----------------+-----------------------+
 |``ΣREG``        |``SIGMAREG``           |
 +----------------+-----------------------+
 |``Σ+``          |``SIGMA+``             |
 +----------------+-----------------------+
 |``Σ-``          |``SIGMA-``             |
 +----------------+-----------------------+
 |``÷``           |``/``                  |
 +----------------+-----------------------+
 |``X≤Y?``        |``X<=Y?``              |
 +----------------+-----------------------+
 |``X≤0?``        |``X<=0?``              |
 +----------------+-----------------------+
 |``X≠Y?``        |``X#Y?``, ``X!=Y?``    |
 +----------------+-----------------------+
 |``X≠0?``        |``X#0?``, ``X!=0?``    |
 +----------------+-----------------------+
 |``ENTER^``      |``ENTER``              |
 +----------------+-----------------------+


Preprocessor
------------

The RPN compiler uses a full featured C preprocessor to handle the input
source file. The preprocessor provides the normal features you will
find in a C preprocessor, the ability to include header files, macro
expansions, conditional compilation and use of C style comments.

Wikipedia is a good place to look for an introduction with many
examples on how to use the
`C preprocessor <http://en.wikipedia.org/wiki/C_preprocessor>`_.

When used in the RPN compiler, the following macros are predefined:


.. index:: __CALYPSI_NUT__ (predefined symbol), predefined symbol; __CALYPSI_NUT__
.. index:: __CALYPSI_RPN_COMPILER__ (predefined symbol), predefined symbol; __CALYPSI_RPN_COMPILER__
.. index:: __CALYPSI_MODULE_USE__ (predefined symbol), predefined symbol; __CALYPSI_MODULE_USE__
.. index:: __CALYPSI_RAM_USE__ (predefined symbol), predefined symbol; __CALYPSI_RAM_USE__


.. table:: Predefined processor symbols
 :widths: 3 7
 :column-dividers: none single none

 +---------------------------------+----------------------------------+
 |Preprocessor symbol              |Description                       |
 +=================================+==================================+
 |``__CALYPSI_NUT__``              |An integer that is 1 when the Nut |
 |                                 |target (including NEWT variant)   |
 |                                 |is used.                          |
 +---------------------------------+----------------------------------+
 |``__CALYPSI_RPN_COMPILER__``     |An integer that is 1 when the RPN |
 |                                 |compiler is used.                 |
 +---------------------------------+----------------------------------+
 |``__CALYPSI_MODULE_USE__``       |An integer that is 1 when         |
 |                                 |generating code for module use.   |
 +---------------------------------+----------------------------------+
 |``__CALYPSI_RAM_USE__``          |An integer that is 1 when         |
 |                                 |generating code for RAM use.      |
 |                                 |(``--raw-output``)                |
 +---------------------------------+----------------------------------+


.. index:: comments;RPN, RPN;comments

Comments
^^^^^^^^

Comments start with a semi-colon and remains active for the rest of
the line. Using C style comments are also possible thanks to the
preprocessor.


.. index:: line numbers;RPN, RPN;line numbers

Line numbers
^^^^^^^^^^^^

HP-41 RPN programs have line numbers as listed. The RPN compiler will
accept programs as input either with or without line numbers. Whether
the program have line numbers is sensed automatically by looking at
what appears to be the first instruction in the program. If that is a
number, it is assumed that there will be a line number before each
instruction. [#line-no]_

Line numbers are just read and ignored and there is no requirement
that they make any sense, such as being in a strict increasing order.

.. index::
   single: RPN;indentation


Indentation
^^^^^^^^^^^

Spaces can be used to indent code, which can be useful to increase
readability. Using empty lines and comments blocks may also be useful
to make the program easier to read.


.. index:: RPN;alpha string literals, Unicode
.. _sec-unicode:

Alpha strings
^^^^^^^^^^^^^

As the input file is assumed to be in UTF-8, it allows you to use
certain special characters not available in ASCII and type them as
they look. Refer to the Unicode table in :ref:`sec-hp41-unicode-chars`.

Append alpha
""""""""""""

If the append character appears first in an alpha literal, it means
that the string literal will be appended to the alpha register rather
than replacing it.

The append character is 127 which correspond to the delete character
in ASCII. While it is possible to type it using the ``├``  Unicode
character, or a hexadecimal or octal  escape sequence (see below), the
easiest way is to precede the string literal with the greater than
sign::

 >"FOO"   ; Append FOO


Control characters
""""""""""""""""""

Control or individual special characters inside an otherwise normal
string literal, can be inserted using escape sequences in the same way
as in the C language::

  "ABC\r"    ; followed by CR
  "ABC\x0d"  ; also followed by CR (hex 0D)
  "FOO\\BAR" ; \ itself must be escaped


Synthetic alpha strings
"""""""""""""""""""""""

If you want to enter an string literal where the actual numeric
character codes are more important than readable characters, the
alternative syntax with a bracketed list of character codes can be
used::

[128,255,0,3]   ; 4 characters in decimal form



Numeric constants
^^^^^^^^^^^^^^^^^
Numeric constants are written just as they appears in an HP-41
program. You need to separate the exponent field from the number with
a space. Synthetic exponent constants are also allowed::

 -1.303 E-13
 E
 E3
 -E


.. index:: numbers;NULL separation

NULL separation of numeric constants
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If the source program contains two numeric constants immediately after
each other, an invisible NULL will be inserted between them. This is
done automatically. Such NULL instruction will not get an RPN line
number, as it is invisible when examining the program in the
HP-41. The NULL instruction will however be visible in the list file,
without any line number will be assigned to it.


A basic example
---------------

A simple RPN program to calculate the area of a circle can be
implemented as follows:

.. literalinclude:: area.rpn
   :language: none

Running the RPN compiler on it produces a list file with the following
contents:

.. literalinclude:: area.lst
   :language: none

As can be seen from the list file, there are two sections
generated. The first one (FAT) is meant to go into the function
address table. We are after all going to produce a module and the
global alpha label needs to be included there in order to be accessible. The
actual word values to be put into the image cannot be determined at
this point, as we do not know where the program is going to be located
in the module.

The second section (RPN) is the actual program. It starts with two
words that describe some properties of the program meant to be used by
the ``COPY`` instruction, which ensures that the program can be
properly copied to RAM memory. The rest are the actual RPN
instructions encoded for ROM use.

.. index:: RPN;line numbers

Also note that RPN line numbers are generated before each RPN instruction.
The leftmost column of hex numbers are the offset for the instruction
within its section.


.. index:: RPN;branch length, branch length;RPN

Branches and labels
-------------------

Relative branches are automatically compiled for ROM use. The HP-41
can make use of short and long branches and the smallest one that can
reach the destination is used. [#short-branch]_


.. index:: labels;global alpha, global alpha labels, RPN;labels, labels;RPN, XROM;function
.. index:: suppressing XROM calls, XROM;defining

.. _sec-xrom-encoding:

Global alpha labels
-------------------

Global alpha labels are automatically found and a suitable information is
generated for populating the FAT for each label. By default you will
get a small FAT section fragment with this program's contribution to
the final FAT. You may want to control this by hand, see
:ref:`sec-fat-order`.

A call to a global alpha label using ``XEQ`` is assumed to be within the
current module. It is converted to a 2-word XROM instruction, where
the generated words depends on the final location in the FAT. The
actual values of these words will be determined at link time.

In some cases you may actually want to call a global alpha label and avoid
the automatic translation to XROM. You can do this using the
``--xeq-from-rom`` command line switch:


.. literalinclude:: call.rpn
   :language: none

To prevent ``FN`` from being converted to an XROM call, use the
following command line:

.. code-block:: none

   $ rpncomp foo.rpn --xeq-from-rom=FN

In the resulting list file, ``FN`` has been preserved as an ``XEQ`` to
a global alpha label. This will cause the operating system to search for it
starting in catalog 1.

.. literalinclude:: call.lst
   :language: none

If you want to prevent multiple routines from being converted to XROM
calls, either use the ``--xeq-from-rom`` option multiple times, or
specify a double quoted space separated alpha label list:

.. code-block:: none

   $ rpncomp foo.rpn --xeq-from-rom="FN AREA"

.. note::
   The default behavior when calling a global alpha label with ``XEQ`` is to
   convert it into an XROM call instruction for the module being
   built. If that global alpha label is not defined in the same RPN program,
   the RPN compiler will silently create an external dependency on
   it. The linker will later take care of fixing the XROM call,
   provided that the global alpha label exists in another RPN program linked
   together in the same module.


.. index:: plug-in modules, import;instructions, import;functions,
           HP-41CX instructions, instructions;HP-41CX, XROM;calling
           XROM;instruction, XROM;function

Using other plug-in modules
---------------------------

An RPN program can use instructions and functions defined in other
plug-in modules. Such modules are not known to the RPN compiler from
start, but they can be made available using an ``.import`` directive
in the source file.

In this example the optional HP-41CX instructions and the Math Pac
module is imported. The ``CLRGX`` instruction is part of the HP-41CX
and the RPN program ``ACOSH`` is defined in Math Pac:

.. literalinclude:: import.rpn
   :language: none

Note that instructions are just typed as any instruction, while the
function ``ACOSH`` in Math Pac is an RPN program so it needs to be
preceded by ``XROM``, just as it appears when the HP-41 displays the
program steps.
Compiling this program produces the following list file output:

.. literalinclude:: import.lst
   :language: none

As the XROM codes for these optional functions are known at compile
time (the modules already exists, it is nothing we are generating
now), the actual words are resolved at compile time.

The imported modules have to be supplied as separate module export
files. How the imports can be generated is described in the
:ref:`chap-module-tool` chapter.

The installation contains a set of already generated module export
files in the ``module-export`` directory. If the desired module is
available there, you can just use it as the RPN provides contains this
directory in its predefined search path.

The search path also includes the current directory. If the module
export file is located elsewhere, you need to use the ``-I`` command
line option to add that directory to the search path.


.. index:: XROM;instruction from RPN, instructions;local MCODE,
           XROM;instruction

.. _sec:rpn-local-directive:

Invoking local MCODE instructions
---------------------------------

A special case is if you are building a mixed RPN and MCODE
module. There are a couple of ways of doing this. In general it boils
down to whether:

#. you decide up front on the XROM numbers for your MCODE instructions, or
#. you want to let the tool help you in a similar way to local RPN functions.

If you want to have fixed numbers, you can define a module exports
file and let the RPN functions import it. Then you just need to ensure
that you link the code in such order that the MCODE instructions end
up at the specified location in the FAT.

The alternative is to let the tools figure it out using a technique
that piggybacks on the same mechanism that allows automatic calls of
RPN functions in the same module.

To do this you need to tell the RPN compiler that there are local
MCODE instructions using the ``.local`` directive. [#local]_ It looks
syntactically similar to the ``.import`` directive, but instead of
reading a module exports file, it specifies individual MCODE
instruction names. In the Poker game that is part of the supplied
example games module (see :ref:`sec-games`), there are three MCODE
support instructions called ``CARD``, ``MV`` and ``TRI``. The start of
the source file may then be as follows::

  .import 41cx

  #ifdef __CALYPSI_MODULE_USE__
  .local CARD, MV, TRI
  #else
  .import exgames
  #endif

  01  LBL "POKER+"
  02  10
     ...
  54  CARD

.. note::
   Using ``.local`` is only needed when importing MCODE instruction
   names. It is not needed for RPN programs. The reason is that RPN
   programs are wth an ``XROM`` prefix, so the compiler have no
   problem understanding what is going on.

This makes the MCODE instructions available to the RPN program. The
actual XROM code used is unknown at compile time and the mechanism
works as follows. It is expected that for each such MCODE instruction,
there is a special named public label at each FAT entry. In the MCODE
source file you will need to do something like: [#RPNCompiler]_

.. literalinclude:: fat-entry.s
   :language: ca65

The FAT labels are expected to be named like ```FAT entry: XX``` where
``XX`` is the name of the MCODE instruction as given to the ``.local``
directive. Since this label contains non-alphanumeric characters, it
needs to be surrounded by back quotes.

If you expect your functions and instructions to be used from other
programs or modules, you may feel that you need control the order in
which the entries go into the FAT.
This is possible by achieve by using different section names and by
the order in which the object files are specified to the linker on the
command line.


.. _sec-rpn-output:

Converting ``.raw`` files
-------------------------

Since ``.raw`` files are just raw binary data, they are not human
readable. Sometimes it makes sense to convert such programs into
readable text, for inspection or editing. You can perform such
conversion using the ``--rpn-output`` option. In addition to this
option, you need to specify a ``.raw`` file. The output file is
written to the current directory, using the same base name as the
``.raw`` file, but with a ``.rpn`` file extension instead.

Calls to external ROMs currently shows up as ``XROM`` instructions
with numbers.

If you have many ``.raw`` files you want to convert, here is a way
using ``xargs`` which is available if you are using a POSIX.2
compliant system:

.. code-block:: none

  $ ls path-to-raw-files/*.raw | xargs -n1 -J % rpncomp --rpn-output %


.. rubric:: Footnotes

.. [#line-no]
   This means that a program which does not have line numbers but
   where the first instruction is a numeric constant, cannot be
   handled properly. In reality, this is a minor limitation as most
   programs start with a global alpha label.
.. [#short-branch]
   Short jump reachability differs slightly in ROM and RAM on the
   HP-41. In a ROM the distance covered by a short branch is slightly
   longer compared to RAM.
   This means that while a short branch may be compiled properly in
   ROM, it may not be compilable when copied to RAM. This will only
   affect the execution speed of the program, it will otherwise work
   in the same way.
.. [#local]
   The ``.local`` directive only works when building a module, as
   there is no FAT in RAM memory. In order to make the RPN program
   work in both RAM and a module, use conditional preprocessor
   ``#ifdef`` on the ``__CALYPSI_MODULE_USE__`` symbol around it and
   provide a module exports file for RAM use, see the example.
.. [#RPNCompiler]
   This manual work is not needed when using RPN functions, as the
   compiler will create the FAT entries and public labels automatically.


.. index:: XROM; with operands, postfix operands; for XROMs

Merged XROM instructions
------------------------

Some modules allow XROM instructions to have postfix operands. Such
combined instructions are not supported by the HP-41 operating system,
but is possible using some special techniques. There are a couple of
variants available, and they rely on that the XROM instruction
consumes the following step (which is an ordinary stand alone RPN
instruction) and use it as an argument of some kind.

Current support for this is limited and only supports the style used
by the Ladybug module.

The ``.postfix`` directive tells the RPN compiler that you have an
XROM with some kind of postfix operand. It takes a list of
instructions it applies to, followed by the operand style they use
inside parentheses:

.. literalinclude:: ladybug-postfix.rpn
   :language: none

As can be seen, you still need to import the module to being in the
available instructions. The ``.postfix`` directive is used to dress
them up as instructions that actually take operands.

.. note::
   The integer literal instruction (``#LIT``) starts with a hash
   symbol which will cause an error from the C preprocessor if used
   first on a line, as the preprocessor will try to interpret it as a
   preprocessor directive.
   To work around it, the ``#define`` defines a synonym ``integer``
   which expands to the ``#LIT`` instruction.

Following the postfix style, the default argument can optionally be
specified. If it is specified, the compiler can avoid generating an
explicit postfix instruction when the default will perform the same
operation. ``WSIZE`` uses an argument that is the same as the default
in this example program.

In the example ``VIEWI`` is a secondary function and gets an extra
byte that represents its function code due to this.

Dual argument functions as ``<>I`` are entered with the function name
first followed by its two operands. This is also how they are entered
from the keyboard on the HP-41.

The following list file shows the result:

.. literalinclude:: ladybug-postfix.lst
   :language: none

In the generated listing, such XROM instructions are generated as two
separate instructions. The postfix byte is held in alpha string
literals following the actual instruction. To make it easier to read
and save you from having to look it up from some table, instructions
are shown together with their operands. However, close studying of the
generated opcodes and line numbers reveals that they are actually two
instructions from a program list point of view.
