

***************************
Assembly language interface
***************************

Assembly language provides symbolic access to target machine
instructions. You might need this control for specific instructions
unrepresentable in C, precise hardware interaction, exception stack
frame manipulation, exact timing sequences, or performance-critical
routines.

Intrinsic functions
===================

The compiler provides *intrinsic functions* (declared in the
calypsi/intrinsics65816.h file) that resemble ordinary functions. Instead of a
function call, an intrinsic generates a specific instruction sequence.
For example, ``__disable_interrupts()`` emits machine instructions to
disable normal interrupts. See :ref:`intrinsic-functions` for details.

Assembly functions
==================

You can implement functions in assembly language and call them from C
like any other function. An assembly function must adhere to the C
calling convention, which dictates how values are passed to the called
function and where the return value is placed.

.. note::

   To simplify your assembly routine interface, consider using the
   ``simple_call`` calling convention.

You can choose between a separate assembly source file or inline
assembly; each has pros and cons. Assembly functions offer better
separation between C and assembly, aiding portability. However, function
call overhead and adherence to calling conventions may be undesirable.

Minimal boilerplate assembly code is required to place the routine in
a suitable section and declare public symbols. This assembly code resides
in a separate file, which must be added to the build system.

Assembly language files typically use the ``.s`` or ``.asm`` extension;
however, this varies due to the lack of standardization in assembly
language itself.

The assembler provided by the Calypsi C compiler tool chain, similar to UNIX assemblers, uses
directives starting with a dot. This avoids name clashes with instructions,
whose naming conventions vary widely across targets, ensuring consistent
directive names.

As a minimum, you must declare the section and export your function
name using the ``.public`` directive:

.. literalinclude:: example/simpleFunction.s
   :language: ca65

To call this function correctly, you must provide a prototype in C:

.. literalinclude:: example/callSimpleFunction.c
   :language: C

Generate skeleton code
-----------------------

The easiest way to generate an assembly source file is to have the
compiler create it using the ``--assembly-source`` command-line option.
A simplified C source file containing the desired functions and
declarations can be used for this purpose.

You can provide desired function definitions with simple parameter uses
to study how they are passed:

.. literalinclude:: ../common/example/skeleton.c
   :language: C

.. code-block:: console

   $ cc65816 skeleton.c --assembly-source=skeleton.s



.. literalinclude:: skeleton.s
   :language: ca65



Calling convention
==================

The default calling convention is complex in detail, but straightforward
in most common scenarios.
An alternative calling convention ``simple_call`` is also provided.

If parameters are passed on the stack, the caller is responsible for
cleanup. The called function may use any register resource but must
preserve certain registers, saving and restoring them before returning.

.. index:: calling convention

Simple calling convention
-------------------------

Use ``__attribute__((simple_call))`` or ``__simple_call``
on a function declaration to enable the simple calling convention.

In this calling convention the first parameter is passed in register
if it fits, the remaining parameters are passed on the stack.

Registers ``A``, ``X``, ``Y`` and pseudo registers ``_Dp[0-7]`` are
destroyed by a function call. Pseudo registers ``_Dp[8-15]`` must be
preserved.

.. note::

   The pseudo registers are clobbered for consistency with the normal
   calling convention. If you implement an assembly or foreign
   language interface that do not use the ``_Dp`` style pseudo
   registers you can simply ignore this detail as it does not matter.

.. table:: first parameter register location
 :widths: 2 1 4
 :column-dividers: none single single none
 :column-alignment: left left left

 +----------------+-----+-------------------------------------------+
 |Register        |Size | Types                                     |
 +================+=====+===========================================+
 | ``A``          |8    | ``char``                                  |
 +----------------+-----+-------------------------------------------+
 | ``C``          |16   | ``int``, ``short``,                       |
 |                |     | ``tiny`` and ``near`` pointers            |
 +----------------+-----+-------------------------------------------+
 | ``X`` and ``C``|32   |``long``, ``float``, ``far`` and ``huge``  |
 |                |     |pointers                                   |
 +----------------+-----+-------------------------------------------+

.. table:: Return values
 :widths: 2 1 4
 :column-dividers: none single single none
 :column-alignment: left left left

 +----------------+-----+--------------------------------------+
 |Register        |Size | Types                                |
 +================+=====+======================================+
 | ``A``          |8    | ``char``, ``tiny`` pointer           |
 +----------------+-----+--------------------------------------+
 | ``C``          |16   | ``int``, ``short``, ``tiny`` pointer |
 +----------------+-----+--------------------------------------+
 | ``X`` and ``C``|24   | ``far`` pointer, ``farfunc`` pointer |
 +----------------+-----+--------------------------------------+
 | ``X`` and ``C``|32   | ``long``, ``float``                  |
 +----------------+-----+--------------------------------------+


Normal calling convention
-------------------------

Parameters are passed in the ``A`` accumulator, index register ``X``
and pseudo registers ``_Dp[0-7]``.

Registers ``A``, ``X``, ``Y`` and pseudo registers ``_Dp[0-7]`` are
destroyed by a function call. Pseudo registers ``_Dp[8-15]`` must be
preserved.

.. note::

   The ``A`` register is the 8 bits accumulator. The same register in
   16 bits version is called ``C``. As the 65816 shares mnemonics with
   the 6502 and can switch modes, the term register name ``A`` is
   often used for the 16 bits version as well. Technically it should
   be named ``C`` when being the 16 bits version.

.. table:: Parameter registers
 :widths: 2 1 4
 :column-dividers: none single single none
 :column-alignment: left left left

 +----------------+-----+-------------------------------------------+
 |Register        |Size | Types                                     |
 +================+=====+===========================================+
 | ``A``          |8    | ``char``                                  |
 +----------------+-----+-------------------------------------------+
 | ``C``          |16   | ``int``, ``short``,                       |
 |                |     | ``tiny`` and ``near`` pointers            |
 +----------------+-----+-------------------------------------------+
 | ``_Dp[0-1]``   |16   | ``int``, ``short``,                       |
 |                |     | ``tiny`` and ``near`` pointers            |
 +----------------+-----+-------------------------------------------+
 | ``_Dp[2-3]``   |16   | ``int``, ``short``,                       |
 |                |     | ``tiny`` and ``near`` pointers            |
 +----------------+-----+-------------------------------------------+
 | ``_Dp[4-5]``   |16   | ``int``, ``short``,                       |
 |                |     | ``tiny`` and ``near`` pointers            |
 +----------------+-----+-------------------------------------------+
 | ``_Dp[0-3]``   |32   | ``long``, ``float``,                      |
 |                |     | ``far``, ``far24`` and ``huge`` pointers  |
 +----------------+-----+-------------------------------------------+
 | ``_Dp[4-7]``   |32   | ``long``, ``float``,                      |
 |                |     | ``far``, ``far24`` and ``huge`` pointers  |
 +----------------+-----+-------------------------------------------+
 | ``X`` and ``C``|32   | ``long``, ``float``                       |
 +----------------+-----+-------------------------------------------+

Parameters are bound to register left to right on a first fit
basis. If a parameter register has to be skipped over, it will be
considered again for later parameters. Parameters that cannot be fit
into registers are passed on the stack.

.. note::

   The 16 bit register ``_Dp[2-3]`` is only used in the small data
   model. In the other data models this parameter is passed in
   ``_Dp[4-5]`` instead. The reason for this is that in the small data
   model it is possible to use plain 16 bits pointers.  In the
   other data models the 16 bit register may need to be converted to
   a 24 bit register value.

.. note::

   24 bit values are passed in three bytes, but the actual pseudo
   register covered is four bytes. The last byte is ignored and can be
   regarded as unused garbage. This is done in order to avoid data
   size shifts in the code.

.. table:: Return values
 :widths: 2 1 4
 :column-dividers: none single single none
 :column-alignment: left left left

 +----------------+-----+--------------------------------------+
 |Register        |Size | Types                                |
 +================+=====+======================================+
 | ``A``          |8    | ``char``, ``tiny`` pointer           |
 +----------------+-----+--------------------------------------+
 | ``C``          |16   | ``int``, ``short``, ``tiny`` pointer |
 +----------------+-----+--------------------------------------+
 | ``X`` and ``C``|24   | ``far`` pointer, ``farfunc`` pointer |
 +----------------+-----+--------------------------------------+
 | ``X`` and ``C``|32   | ``long``, ``float``                  |
 +----------------+-----+--------------------------------------+

.. index:: data size, index register; size, size; data access, size; index register

Index and data size
-------------------

The 65816 is used in native mode with 16 bit registers. In some
situations the runtime needs to switch to 8 bit register mode to
implement the correct behavior. This is done automatically and the
compiler will then switch back to 16 bits mode.

There are two flags in the CPU that control the width of registers,
one for index registers and the other for the accumulator and data
memory access. The index width is *always* kept at 16 bits by the
runtime, only the accumulator/memory width is changed as needed.

A function call expects the width to be set to 16 bits and must
return with the same setting. This means that if you write a function
in assembly language, it will get called from a C function with 16
bit register width and you need to return that way. Inside your own
routine you are free to switch register width as you may see fit.

.. note::

   The compiler may generate code that reads 8 and 24 bit objects
   using 16 bit access, ignoring the extra byte. Writes are always
   done obeying the object width. If you are reading from hardware
   registers that requires reads to follow the width, you must use the
   ``volatile`` keyword.

   It can be a good idea to group multiple 8 bit accesses next to each
   others as the compiler is able to reduce mode shifts back and forth
   if it detects it. However, you need to enable the optimizer for
   this to happen.

64 bit values
--------------

The above tables do not mention 64 bit values. A 64 bit value such
as ``long long`` and ``long double`` is passed by reference, that is,
as a pointer to the value. The size of this pointer is the same as the
default pointer size and is either 16 bits or 24 bits depending on the
data model used.

If a function returns a 64 bit value, the caller is responsible for
allocating space. An extra 'invisible' parameter, a pointer to this
space, is added to the function. The called function is expected to
return this pointer.

Structure passing
-----------------

Structure parameters are passed on the stack. Structure return values
are handled like 64 bit values. The caller allocates space
and adds an extra 'invisible' parameter (a pointer to that space)
to the function call. The called function is expected to return this
pointer.

.. index:: inline assembler

Inline assembler
================

The inline assembler allows you to insert and interface assembly code
slices within a C function. This avoids call overhead and can improve
parameter adaptation. However, the optimizer must be more cautious,
which may affect the performance of the surrounding C code.

Basic inline assembly
---------------------

You can insert a slice of assembly code using an ``__asm`` block:

.. literalinclude:: example/basicInlineAssembly.c
   :language: C

Each line without a label requires at least one leading space and must
be terminated by a newline character (``\n``).

The inline assembler supports the full assembly instruction set,
including literal bytes, volatile operations, local labels, and
register allocation for parameters and return values.

Goto labels and most assembler directives are currently not supported
by the inline assembler. If you need better control with placement, use
a separate assembly source file instead.

When inline assembly is inserted, the compiler adapts it to fit the
generated C code. Variables can be passed as parameters, and a single
result variable is supported. The compiler reasonably understands the
inserted assembly, mixing it with C-generated code. Inline assembly
is subject to low-level optimizations when the optimizer is enabled.

.. index:: inline assembler; volatile, volatile; inline assembler

Volatile
--------

An assembly block can be marked as ``volatile``:

.. code-block:: C

   __asm volatile { ... }

This has the effect that all memory accesses in the assembly slice are
treated as side effects. Otherwise the optimizer may remove reads from
memory when the value read is not used.

Local labels
------------

Local labels can be used and their names will not clash with C
identifiers. When an inline assembly slice is inserted, local labels are converted
to internal C labels, preventing name clashes with C identifiers.

External symbols
----------------

Inline assembly can refer to symbols defined outside its code slice, provided
such symbols are visible at the C level within the same compilation unit.

.. index:: inline assembler; constraints, constraints; inline assembler

Constraints
-----------

An inline assembly code slice can refer to C variables and return a
value. The inline assembly construct optionally accepts three lists:

#.  Output variable: Specifies a C variable to represent the returned
    value and a register class where the assembly code block places it.
    The result is prefixed by ``=`` in its single-value list.

#.  Input expressions: Typically variables. The compiler evaluates the
    expression and places it in the specified register class.

#.  Clobbered registers: Any register resource clobbered by the inline
    assembly must be specified here.

Multiple entries in a list are comma-separated.

Register classes
^^^^^^^^^^^^^^^^^

A register class is a register resource that can represent either a single
register or a set of equivalent registers. They are used for allocating
parameters and determining the return value location.

The following register classes are defined:

.. table:: Register classes
 :widths: 1 5
 :column-dividers: none single none
 :column-alignment: right left

 +----------------+---------------------------------------------------+
 |Register class  |Description                                        |
 +================+===================================================+
 | ``a``          | Accumulator A, 8 bit                              |
 +----------------+---------------------------------------------------+
 | ``c``          | Accumulator C (B and A combined), 16 bit          |
 +----------------+---------------------------------------------------+
 | ``x``          | X index register                                  |
 +----------------+---------------------------------------------------+
 | ``y``          | Y index register                                  |
 +----------------+---------------------------------------------------+
 | ``dp8``        | An 8-bit direct page pseudo register              |
 +----------------+---------------------------------------------------+
 | ``dp16``       | A 16-bit direct page pseudo register              |
 +----------------+---------------------------------------------------+
 | ``dp32``       | A 32-bit direct page pseudo register              |
 +----------------+---------------------------------------------------+

.. note::

   Register classes are used internally during code generation. Internal
   code generator rules ensure safe register allocation by adhering to
   specific rules and invariants. Rather than attempting to diagnose
   inline assembly constraints or impose conservative limitations,
   the compiler trusts you. Overusing resources or violating internal
   invariants may lead to a register allocation error. In such cases,
   ease the register resources to find a working allocation.


Registers and constraints
^^^^^^^^^^^^^^^^^^^^^^^^^

The following code shows how constraints for an inline assembly code
slice are defined:

.. literalinclude:: example/constraintInlineAssembly.c
   :language: C

Currently, the only supported constraint is 'K', followed by a register class.

An empty list can be entered by using a colon character followed by
nothing.

The first list is the optional output parameter, describing the C variable
where the output is visible after the inline assembly slice executes.
The inline assembly slice must leave the result in the specified register
class; the compiler will automatically insert code to store this value
in the C variable.

The second constraint specifies input variables and their register
classes. The compiler ensures these variables are available in the
specified register classes before passing control to the inline
assembly code slice.

The third list specifies the actual registers clobbered by the inline
assembly code slice. These must be register classes describing a single
register.

.. index:: inline assembler; substitutions, substitutions; inline assembler

Substitutions
^^^^^^^^^^^^^

A register class may specify a register resource with multiple alternatives.
The register allocator selects the actual register used. You can refer to
the register resource using substitutions, given by the ``%N`` syntax,
where N is ``0`` for the result (if present), ``1`` for the first input,
``2`` for the second, and so on:

.. literalinclude:: example/substituteInlineAssembly.c
   :language: C

.. note::

   If the output is empty then the input list starts with ``%0``.

If you find substitutions using numbers to be unreadable, you can
specify a symbol for each substitution:

.. literalinclude:: example/symbolSubstituteInlineAssembly.c
   :language: C
