Ophis/doc/tutor3.sgm

173 lines
5.8 KiB
Plaintext
Raw Normal View History

<chapter id="ch3-link">
<title>Headers, Libraries, and Macros</title>
<para>
In this chapter we will split away parts of our <quote>Hello
World</quote> program into reusable header files and libraries.
We will also abstract away our string printing technique into a
macro which may be invoked at will, on arbitrary strings. We will
then multiply the output of our program tenfold.
</para>
<section>
<title>Header files and libraries</title>
<para>
The prelude to our program&mdash;the <filename>PRG</filename>
information and the BASIC program&mdash;are going to be the same
in many, many programs. Thus, we should put them into a header
file to be included later. The <literal>.include</literal>
directive will load a file and insert it as source at the
designated point.
</para>
<para>
A related directive, <literal>.require</literal>, will include
the file as long as it hasn't been included yet elsewhere. It
is useful for ensuring a library is linked in.
</para>
<para>
For pre-assembled code or raw binary data,
the <literal>.incbin</literal> directive lets you include the
contents of a binary file directly in the output. This is handy
for linking in pre-created graphics or sound data.
</para>
2012-06-13 06:13:55 +00:00
<para>
If you only wish to include part of a binary
file, <literal>.incbin</literal> takes up to two optional
arguments, naming the file offset at which to start reading and
the number of characters to read.
</para>
<para>
As a sample library, we will expand the definition of
the <literal>chrout</literal> routine to include the standard
names for every KERNAL routine. Our header file will
then <literal>.require</literal> it.
</para>
<para>
We'll also add some convenience aliases for things like reverse
video, color changes, and shifting between upper case/graphics
and mixed case text. We'd feed those to
the <literal>chrout</literal> routine to get their effects.
</para>
<para>
Since there have been no interesting changes to the prelude, and
the KERNAL values are standard, we do not reproduce them here.
(The files in question are <xref linkend="c64-1-src"
endterm="c64-1-fname"> and <xref linkend="kernal-src"
2012-06-09 08:06:25 +00:00
endterm="kernal-fname">.) The <filename>c64kernal.oph</filename>
header is likely to be useful in your own projects, and it is
available in the <literal>platform/</literal> directory for easy
inclusion.
</para>
</section>
<section>
<title>Macros</title>
<para>
A macro is a way of expressing a lot of code or data with a
simple shorthand. It's also usually configurable. Traditional
macro systems such as C's <literal>#define</literal> mechanic
use <emphasis>textual replacement</emphasis>: a macro is
expanded before any evaluation or even parsing occurs.
</para>
<para>
In contrast, Ophis's macro system uses a <emphasis>call by
value</emphasis> approach where the arguments to macros are
evaluated to bytes or words before being inserted into the macro
body. This produces effects much closer to those of a
traditional function call. A more detailed discussion of the
tradeoffs may be found in <xref linkend="ref-link">.
</para>
<section>
<title>Macro definitions</title>
<para>
A macro definition is a set of statements between
a <literal>.macro</literal> statement and
a <literal>.macend</literal> statement.
The <literal>.macro</literal> statement also names the macro
being defined.
</para>
<para>
No global or anonymous labels may be defined inside a macro:
temporary labels only persist in the macro expansion itself.
2014-05-24 12:36:17 +00:00
(Each macro body has its own scope. A label map will trace
back through macro expansions to describe were a label inside
a macro body came from.)
</para>
<para>
Arguments to macros are referred to by number: the first is
<literal>_1</literal>, the second <literal>_2</literal>, and so on.
</para>
<para>
Here's a macro that encapsulates the printing routine in our
<quote>Hello World</quote> program, with an argument being the
address of the string to print:
</para>
<programlisting>
.macro print
ldx #0
_loop: lda _1, x
beq _done
jsr chrout
inx
bne _loop
_done:
.macend
</programlisting>
</section>
<section>
<title>Macro invocations</title>
<para>
Macros may be invoked in two ways: one that looks like a
directive, and one that looks like an instruction.
</para>
<para>
The most common way to invoke a macro is to backquote the name
of the macro. It is also possible to use
the <literal>.invoke</literal> command. These commands look
like this:
</para>
<programlisting>
`print msg
.invoke print msg
</programlisting>
<para>
Arguments are passed to the macro as a comma-separated list.
They must all be expressions that evaluate to byte or word
values&mdash;a mechanism similar to <literal>.alias</literal>
is used to assign their values to the <literal>_n</literal>
names.
</para>
</section>
</section>
<section>
<title>Example code</title>
<para>
<xref linkend="tutor3-src" endterm="tutor3-fname"> expands our
running example, including the code above and also defining a
new macro <literal>greet</literal> that takes a string argument
and prints a greeting to it. It then greets far too many
targets.
</para>
</section>
</chapter>