Initial import of the Ophis 1.0 distribution and supplemental material

This commit is contained in:
Michael C. Martin 2011-08-20 16:33:25 -07:00
parent b78f3c614a
commit 2c8dba2450
99 changed files with 16145 additions and 0 deletions

BIN
doc/a2blink.map Normal file

Binary file not shown.

BIN
doc/a2inverse.map Normal file

Binary file not shown.

BIN
doc/a2normal.map Normal file

Binary file not shown.

12
doc/c64-1.oph Normal file
View File

@ -0,0 +1,12 @@
.word $0801
.org $0801
.scope
.word _next, 10 ; Next line and current line number
.byte $9e," 2064",0 ; SYS 2064
_next: .word 0 ; End of program
.scend
.advance 2064
.require "kernal.oph"

40
doc/c64-2.oph Normal file
View File

@ -0,0 +1,40 @@
.word $0801
.org $0801
.scope
.word _next, 10 ; Next line and current line number
.byte $9e," 2064",0 ; SYS 2064
_next: .word 0 ; End of program
.scend
.advance $0810
.require "kernal.oph"
.data zp
.org $0002
.text
.scope
; Cache BASIC's zero page at top of available RAM.
ldx #$7E
* lda $01, x
sta $CF81, x
dex
bne -
jsr _main
; Restore BASIC's zero page and return control.
ldx #$7E
* lda $CF81, x
sta $01, x
dex
bne -
rts
_main:
; Program follows...
.scend

478
doc/docbook/cmdref.sgm Normal file
View File

@ -0,0 +1,478 @@
<appendix id="ref-link">
<title>Ophis Command Reference</title>
<section>
<title>Command Modes</title>
<para>
These mostly follow the <emphasis>MOS Technology 6500
Microprocessor Family Programming Manual</emphasis>, except
for the Accumulator mode. Accumulator instructions are written
and interpreted identically to Implied mode instructions.
</para>
<itemizedlist>
<listitem><para><emphasis>Implied:</emphasis> <literal>RTS</literal></para></listitem>
<listitem><para><emphasis>Accumulator:</emphasis> <literal>LSR</literal></para></listitem>
<listitem><para><emphasis>Immediate:</emphasis> <literal>LDA #$06</literal></para></listitem>
<listitem><para><emphasis>Zero Page:</emphasis> <literal>LDA $7C</literal></para></listitem>
<listitem><para><emphasis>Zero Page, X:</emphasis> <literal>LDA $7C,X</literal></para></listitem>
<listitem><para><emphasis>Zero Page, Y:</emphasis> <literal>LDA $7C,Y</literal></para></listitem>
<listitem><para><emphasis>Absolute:</emphasis> <literal>LDA $D020</literal></para></listitem>
<listitem><para><emphasis>Absolute, X:</emphasis> <literal>LDA $D000,X</literal></para></listitem>
<listitem><para><emphasis>Absolute, Y:</emphasis> <literal>LDA $D000,Y</literal></para></listitem>
<listitem><para><emphasis>(Zero Page Indirect, X):</emphasis> <literal>LDA ($80, X)</literal></para></listitem>
<listitem><para><emphasis>(Zero Page Indirect), Y:</emphasis> <literal>LDA ($80), Y</literal></para></listitem>
<listitem><para><emphasis>(Absolute Indirect):</emphasis> <literal>JMP ($A000)</literal></para></listitem>
<listitem><para><emphasis>Relative:</emphasis> <literal>BNE loop</literal></para></listitem>
<listitem><para><emphasis>(Absolute Indirect, X):</emphasis> <literal>JMP ($A000, X)</literal> &mdash; Only available with 65C02 extensions</para></listitem>
<listitem><para><emphasis>(Zero Page Indirect):</emphasis> <literal>LDX ($80)</literal> &mdash; Only available with 65C02 extensions</para></listitem>
</itemizedlist>
</section>
<section>
<title>Basic arguments</title>
<para>
Most arguments are just a number or label. The formats for
these are below.
</para>
<section>
<title>Numeric types</title>
<itemizedlist>
<listitem><para><emphasis>Hex:</emphasis> <literal>$41</literal> (Prefixed with $)</para></listitem>
<listitem><para><emphasis>Decimal:</emphasis> <literal>65</literal> (No markings)</para></listitem>
<listitem><para><emphasis>Octal:</emphasis> <literal>0101</literal> (Prefixed with zero)</para></listitem>
<listitem><para><emphasis>Binary:</emphasis> <literal>%01000001</literal> (Prefixed with %)</para></listitem>
<listitem><para><emphasis>Character:</emphasis> <literal>'A</literal> (Prefixed with single quote)</para></listitem>
</itemizedlist>
</section>
<section>
<title>Label types</title>
<para>
Normal labels are simply referred to by name. Anonymous
labels may be referenced with strings of - or + signs (the
label <literal>-</literal> refers to the immediate
previous anonymous label, <literal>--</literal> the
one before that, etc., while <literal>+</literal>
refers to the next anonymous label), and the special
label <literal>^</literal> refers to the program
counter at the start of the current instruction or directive.
</para>
<para>
Normal labels are <emphasis>defined</emphasis> by
prefixing a line with the label name and then a colon
(e.g., <literal>label:</literal>). Anonymous labels
are defined by prefixing a line with an asterisk
(e.g., <literal>*</literal>).
</para>
<para>
Temporary labels are only reachable from inside the
innermost enclosing <literal>.scope</literal>
statement. They are identical to normal labels in every
way, except that they start with an underscore.
</para>
</section>
<section>
<title>String types</title>
<para>
Strings are enclosed in double quotation marks. Backslashed
characters (including backslashes and double quotes) are
treated literally, so the string <literal>"The man said,
\"The \\ character is the backslash.\""</literal> produces
the ASCII sequence for <literal>The man said, "The \
character is the backslash."</literal>
</para>
<para>
Strings are generally only used as arguments to assembler
directives&mdash;usually for filenames
(e.g., <literal>.include</literal>) but also for string
data (in association with <literal>.byte</literal>).
</para>
<para>
It is legal, though unusual, to attempt to pass a string to
the other data statements. This will produces a series of
words/dwords where all bytes that aren't least-significant
are zero. Endianness and size will match what the directive
itself indicated.
</para>
</section>
</section>
<section>
<title>Compound Arguments</title>
<para>
Compound arguments may be built up from simple ones, using the
standard +, -, *, and / operators, which carry the usual
precedence. Also, the unary operators &gt; and &lt;, which
bind more tightly than anything else, provide the high and low
bytes of 16-bit values, respectively.
</para>
<para>
Use brackets [ ] instead of parentheses ( ) when grouping
arithmetic operations, as the parentheses are needed for the
indirect addressing modes.
</para>
<para>
Examples:
</para>
<itemizedlist>
<listitem><para><literal>$D000</literal> evaluates to $D000</para></listitem>
<listitem><para><literal>$D000+32</literal> evaluates to $D020</para></listitem>
<listitem><para><literal>$D000+$20</literal> also evaluates to $D020</para></listitem>
<listitem><para><literal>&lt;$D000+32</literal> evaluates to $20</para></listitem>
<listitem><para><literal>&gt;$D000+32</literal> evaluates to $F0</para></listitem>
<listitem><para><literal>&gt;[$D000+32]</literal> evaluates to $D0</para></listitem>
<listitem><para><literal>&gt;$D000-275</literal> evaluates to $CE</para></listitem>
</itemizedlist>
</section>
<section>
<title>Memory Model</title>
<para>
In order to properly compute the locations of labels and the
like, Ophis must keep track of where assembled code will
actually be sitting in memory, and it strives to do this in a
way that is independent both of the target file and of the
target machine.
</para>
<section>
<title>Basic PC tracking</title>
<para>
The primary technique Ophis uses is <emphasis>program counter
tracking</emphasis>. As it assembles the code, it keeps
track of a virtual program counter, and uses that to
determine where the labels should go.
</para>
<para>
In the absence of an <literal>.org</literal> directive, it
assumes a starting PC of zero. <literal>.org</literal>
is a simple directive, setting the PC to the value
that <literal>.org</literal> specifies. In the simplest
case, one <literal>.org</literal> directive appears at the
beginning of the code and sets the location for the rest of
the code, which is one contiguous block.
</para>
</section>
<section>
<title>Basic Segmentation simulation</title>
<para>
However, this isn't always practical. Often one wishes to
have a region of memory reserved for data without actually
mapping that memory to the file. On some systems (typically
cartridge-based systems where ROM and RAM are seperate, and
the target file only specifies the ROM image) this is
mandatory. In order to access these variables symbolically,
it's necessary to put the values into the label lookup
table.
</para>
<para>
It is possible, but inconvenient, to do this
with <literal>.alias</literal>, assigning a specific
memory location to each variable. This requires careful
coordination through your code, and makes creating reusable
libraries all but impossible.
</para>
<para>
A better approach is to reserve a section at the beginning
or end of your program, put an <literal>.org</literal>
directive in, then use the <literal>.space</literal>
directive to divide up the data area. This is still a bit
inconvenient, though, because all variables must be
assigned all at once. What we'd really like is to keep
multiple PC counters, one for data and one for code.
</para>
<para>
The <literal>.text</literal>
and <literal>.data</literal> directives do this. Each
has its own PC that starts at zero, and you can switch
between the two at any point without corrupting the other's
counter. In this way each function can have
a <literal>.data</literal> section (filled
with <literal>.space</literal> commands) and
a <literal>.text</literal> section (that contains the
actual code). This lets our library routines be almost
completely self-contained - we can have one source file
that could be <literal>.included</literal> by multiple
projects without getting in anything's way.
</para>
<para>
However, any given program may have its own ideas about
where data and code go, and it's good to ensure with
a <literal>.checkpc</literal> at the end of your code
that you haven't accidentally overwritten code with data or
vice versa. If your <literal>.data</literal>
segment <emphasis>did</emphasis> start at zero, it's
probably wise to make sure you aren't smashing the stack,
too (which is sitting in the region from $0100 to
$01FF).
</para>
<para>
If you write code with no segment-defining statements in
it, the default segment
is <literal>text</literal>.
</para>
<para>
The <literal>data</literal> segment is designed only
for organizing labels. As such, errors will be flagged if
you attempt to actually output information into
a <literal>data</literal> segment.
</para>
</section>
<section>
<title>General Segmentation Simulation</title>
<para>
One text and data segment each is usually sufficient, but
for the cases where it is not, Ophis allows for user-defined
segments. Putting a label
after <literal>.text</literal>
or <literal>.data</literal> produces a new segment with
the specified name.
</para>
<para>
Say, for example, that we have access to the RAM at the low
end of the address space, but want to reserve the zero page
for truly critical variables, and use the rest of RAM for
everything else. Let's also assume that this is a 6510
chip, and locations $00 and $01 are reserved for the I/O
port. We could start our program off with:
</para>
<programlisting>
.data
.org $200
.data zp
.org $2
.text
.org $800
</programlisting>
<para>
And, to be safe, we would probably want to end our code
with checks to make sure we aren't overwriting anything:
</para>
<programlisting>
.data
.checkpc $800
.data zp
.checkpc $100
</programlisting>
</section>
</section>
<section>
<title>Macros</title>
<para>
Assembly language is a powerful tool&mdash;however, there are
many tasks that need to be done repeatedly, and with
mind-numbing minor modifications. Ophis includes a facility
for <emphasis>macros</emphasis> to allow this. Ophis macros
are very similar in form to function calls in higher level
languages.
</para>
<section>
<title>Defining Macros</title>
<para>
Macros are defined with the <literal>.macro</literal>
and <literal>.macend</literal> commands. Here's a
simple one that will clear the screen on a Commodore
64:
</para>
<programlisting>
.macro clr'screen
lda #147
jsr $FFD2
.macend
</programlisting>
</section>
<section>
<title>Invoking Macros</title>
<para>
To invoke a macro, either use
the <literal>.invoke</literal> command or backquote the
name of the routine. The previous macro may be expanded
out in either of two ways, at any point in the
source:
</para>
<programlisting>.invoke clr'screen</programlisting>
<para>or</para>
<programlisting>`clr'screen</programlisting>
<para>will work equally well.</para>
</section>
<section>
<title>Passing Arguments to Macros</title>
<para>
Macros may take arguments. The arguments to a macro are
all of the <quote>word</quote> type, though byte values may
be passed and used as bytes as well. The first argument in
an invocation is bound to the label
<literal>_1</literal>, the second
to <literal>_2</literal>, and so on. Here's a macro
for storing a 16-bit value into a word pointer:
</para>
<programlisting>
.macro store16 ; `store16 dest, src
lda #&lt;_2
sta _1
lda #&gt;_2
sta _1+1
.macend
</programlisting>
<para>
Macro arguments behave, for the most part, as if they were
defined by <literal>.alias</literal>
commands <emphasis>in the calling context</emphasis>.
(They differ in that they will not produce duplicate-label
errors if those names already exist in the calling scope,
and in that they disappear after the call is
completed.)
</para>
</section>
<section>
<title>Features and Restrictions of the Ophis Macro Model</title>
<para>
Unlike most macro systems (which do textual replacement),
Ophis macros evaluate their arguments and bind them into the
symbol table as temporary labels. This produces some
benefits, but it also puts some restrictions on what kinds of
macros may be defined.
</para>
<para>
The primary benefit of this <quote>expand-via-binding</quote>
discipline is that there are no surprises in the semantics.
The expression <literal>_1+1</literal> in the macro above
will always evaluate to one more than the value that was
passed as the first argument, even if that first argument is
some immensely complex expression that an
expand-via-substitution method may accidentally
mangle.
</para>
<para>
The primary disadvantage of the expand-via-binding
discipline is that only fixed numbers of words and bytes
may be passed. A substitution-based system could define a
macro including the line <literal>LDA _1</literal> and
accept as arguments both <literal>$C000</literal>
(which would put the value of memory location $C000 into
the accumulator) and <literal>#$40</literal> (which
would put the immediate value $40 into the accumulator).
If you <emphasis>really</emphasis> need this kind of
behavior, a run a C preprocessor over your Ophis source,
and use <literal>#define</literal> to your heart's
content.
</para>
</section>
</section>
<section>
<title>Assembler directives</title>
<para>
Assembler directives are all instructions to the assembler
that are not actual instructions. Ophis's set of directives
follow.
</para>
<itemizedlist>
<listitem><para><literal>.advance</literal> <emphasis>address</emphasis>:
Forces the program counter to
be <emphasis>address</emphasis>. Unlike
the <literal>.org</literal>
directive, <literal>.advance</literal> outputs zeroes until the
program counter reaches a specified address. Attempting
to <literal>.advance</literal> to a point behind the current
program counter is an assemble-time error.</para></listitem>
<listitem><para><literal>.alias</literal> <emphasis>label</emphasis> <emphasis>value</emphasis>: The
.alias directive assigns an arbitrary value to a label. This
value may be an arbitrary argument, but cannot reference any
label that has not already been defined (this prevents
recursive label dependencies).</para></listitem>
<listitem><para><literal>.byte</literal> <emphasis>arg</emphasis> [ , <emphasis>arg</emphasis>, ... ]:
Specifies a series of arguments, which are evaluated, and
strings, which are included as raw ASCII data. The final
results of these arguments must be one byte in size. Seperate
constants are seperated by comments.</para></listitem>
<listitem><para><literal>.checkpc</literal> <emphasis>address</emphasis>: Ensures that the
program counter is less than or equal to the address
specified, and emits an assemble-time error if it is not.
<emphasis>This produces no code in the final binary - it is there to
ensure that linking a large amount of data together does not
overstep memory boundaries.</emphasis></para></listitem>
<listitem><para><literal>.data</literal> <emphasis>[label]</emphasis>: Sets the segment to
the segment name specified and disallows output. If no label
is given, switches to the default data segment.</para></listitem>
<listitem><para><literal>.incbin</literal> <emphasis>filename</emphasis>: Inserts the
contents of the file specified as binary data. Use it to
include graphics information, precompiled code, or other
non-assembler data.</para></listitem>
<listitem><para><literal>.include</literal> <emphasis>filename</emphasis>: Includes the
entirety of the file specified at that point in the program.
Use this to order your final sources.</para></listitem>
<listitem><para><literal>.org</literal> <emphasis>address</emphasis>: Sets the program
counter to the address specified. <emphasis>This does not emit any
code in and of itself, nor does it overwrite anything that
previously existed.</emphasis> If you wish to jump ahead in memory,
use <literal>.advance</literal>.</para></listitem>
<listitem><para><literal>.require</literal> <emphasis>filename</emphasis>: Includes the entirety
of the file specified at that point in the program. Unlike <literal>.include</literal>,
however, code included with <literal>.require</literal> will only be inserted once.
The <literal>.require</literal> directive is useful for ensuring that certain code libraries
are somewhere in the final binary. They are also very useful for guaranteeing that
macro libraries are available.</para></listitem>
<listitem><para><literal>.space</literal> <emphasis>label</emphasis> <emphasis>size</emphasis>: This
directive is used to organize global variables. It defines the
label specified to be at the current location of the program
counter, and then advances the program counter <emphasis>size</emphasis>
steps ahead. No actual code is produced. This is equivalent
to <literal>label: .org ^+size</literal>.</para></listitem>
<listitem><para><literal>.text</literal> <emphasis>[label]</emphasis>: Sets the segment to
the segment name specified and allows output. If no label is
given, switches to the default text segment.</para></listitem>
<listitem><para><literal>.word</literal> <emphasis>arg</emphasis> [ , <emphasis>arg</emphasis>, ... ]:
Like <literal>.byte</literal>, but values are all treated as two-byte
values and stored low-end first (as is the 6502's wont). Use
this to create jump tables (an unadorned label will evaluate
to that label's location) or otherwise store 16-bit
data.</para></listitem>
<listitem><para><literal>.dword</literal> <emphasis>arg</emphasis> [ , <emphasis>arg</emphasis>, ...]:
Like <literal>.word</literal>, but for 32-bit values.</para></listitem>
<listitem><para><literal>.wordbe</literal> <emphasis>arg</emphasis> [ , <emphasis>arg</emphasis>, ...]:
Like <literal>.word</literal>, but stores the value in a big-endian format (high byte first).</para></listitem>
<listitem><para><literal>.dwordbe</literal> <emphasis>arg</emphasis> [ , <emphasis>arg</emphasis>, ...]:
Like <literal>.dword</literal>, but stores the value high byte first.</para></listitem>
<listitem><para><literal>.scope</literal>: Starts a new scope block. Labels
that begin with an underscore are only reachable from within
their innermost enclosing <literal>.scope</literal> statement.</para></listitem>
<listitem><para><literal>.scend</literal>: Ends a scope block. Makes the
temporary labels defined since the last <literal>.scope</literal>
statement unreachable, and permits them to be redefined in a
new scope.</para></listitem>
<listitem><para><literal>.macro</literal> <emphasis>name</emphasis>: Begins a macro
definition block. This is a scope block that can be inlined
at arbitrary points with <literal>.invoke</literal>. Arguments to the
macro will be bound to temporary labels with names like
<literal>_1</literal>, <literal>_2</literal>, etc.</para></listitem>
<listitem><para><literal>.macend</literal>: Ends a macro definition
block.</para></listitem>
<listitem><para><literal>.invoke</literal> <emphasis>label</emphasis> [<emphasis>argument</emphasis> [,
<emphasis>argument</emphasis> ...]]: invokes (inlines) the specified
macro, binding the values of the arguments to the ones the
macro definition intends to read. A shorthand for <literal>.invoke</literal>
is the name of the macro to invoke, backquoted.</para></listitem>
</itemizedlist>
<para>
The following directives are deprecated, added for
compatibility with the old Perl
assembler <command>P65</command>. Use
the <literal>-d</literal> option to Ophis to enable
them.
</para>
<itemizedlist>
<listitem><para><literal>.ascii</literal>: Equivalent to <literal>.byte</literal>,
which didn't used to be able to handle strings.</para></listitem>
<listitem><para><literal>.code</literal>: Equivalent to <literal>.text</literal>.</para></listitem>
<listitem><para><literal>.segment</literal>: Equivalent to <literal>.text</literal>,
from when there was no distinction between <literal>.text</literal> and
<literal>.data</literal> segments.</para></listitem>
<listitem><para><literal>.address</literal>: Equivalent to
<literal>.word</literal>.</para></listitem>
<listitem><para><literal>.link</literal> <emphasis>filename address</emphasis>: Assembles
the file specified as if it began at the address specified.
This is generally for use in <quote>top-level</quote> files, where there
is not necessarily a one-to-one correspondence between file
position and memory position. This is equivalent to an
<literal>.org</literal> directive followed by an <literal>.include</literal>.
With the introduction of the <literal>.org</literal> directive this one is
less useful (and in most cases, any <literal>.org</literal> statement
you use will actually be at the top of the <literal>.include</literal>d
file).</para></listitem>
</itemizedlist>
</section>
</appendix>

View File

@ -0,0 +1,29 @@
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"
[<!ENTITY part1 SYSTEM "tutor1.sgm">
<!ENTITY part2 SYSTEM "tutor2.sgm">
<!ENTITY part3 SYSTEM "tutor3.sgm">
<!ENTITY part4 SYSTEM "tutor4.sgm">
<!ENTITY part5 SYSTEM "tutor5.sgm">
<!ENTITY part6 SYSTEM "tutor6.sgm">
<!ENTITY part7 SYSTEM "tutor7.sgm">
<!ENTITY samplecode SYSTEM "samplecode.sgm">
<!ENTITY pre1 SYSTEM "preface.sgm">
<!ENTITY cmdref SYSTEM "cmdref.sgm">
]>
<book>
<bookinfo>
<title>Programming with Ophis</title>
<author><firstname>Michael</firstname><surname>Martin</surname></author>
<copyright><year>2006-7</year><holder>Michael Martin</holder></copyright>
</bookinfo>
&pre1;
&part1;
&part2;
&part3;
&part4;
&part5;
&part6;
&part7;
&samplecode;
&cmdref;
</book>

74
doc/docbook/preface.sgm Normal file
View File

@ -0,0 +1,74 @@
<preface>
<title>Preface</title>
<para>
The Ophis project started on a lark back in 2001. My graduate
studies required me to learn Perl and Python, and I'd been playing
around with Commodore 64 emulators in my spare time, so I decided
to learn both languages by writing a simple cross-assembler for
the 6502 chip the C-64 used in both.
</para>
<para>
The Perl version was quickly abandoned, but the Python one slowly
grew in scope and power over the years, and by 2005 was a very
powerful, flexible macro assembler that saw more use than I'd
expect. In 2007 I finally got around to implementing the last few
features I really wanted and polishing it up for general release.
</para>
<para>
Part of that process has been formatting the various little
tutorials and references I'd created into a single, unified
document&mdash;the one you are now reading.
</para>
<section>
<title>Why <quote>Ophis</quote>?</title>
<para>
It's actually a kind of a horrific pun. See, I was using Python
at the time, and one of the things I had been hoping to do with
the assembler was to produce working Apple II
programs. <quote>Ophis</quote> is Greek
for <quote>snake</quote>, and a number of traditions also use it
as the actual <emphasis>name</emphasis> of the serpent in the
Garden of Eden. So, Pythons, snakes, and stories involving
really old Apples all combined to name the assembler.
</para>
</section>
<section>
<title>Getting a copy of Ophis</title>
<para>
If you're reading this as part of the Ophis install, you clearly
already have it. If not, as of this writing the homepage for
the Ophis assembler
is <ulink url="http://hkn.eecs.berkeley.edu/~mcmartin/ophis/"></ulink>. If
this is out-of-date, a Web search on <quote>Ophis 6502
assembler</quote> (without the quotation marks) should yield its
page.
</para>
<para>
Ophis is written entirely in Python and packaged using the
distutils. The default installation script on Unix and Mac OS X
systems should put the files where they need to go. If you are
running it locally, you will need to install
the <literal>Ophis</literal> package somewhere in your Python
package path, and then put the <command>ophis</command> script
somewhere in your path.
</para>
<para>
Windows users that have Python installed can use the same source
distributions that the other operating systems
use; <command>ophis.bat</command> will arrange the environment
variables accordingly and invoke the main script.
</para>
<para>
If you are on Windows and do not have Python installed, a
prepackaged system made with <command>py2exe</command> is also
available. The default Windows installer will use this. In
this case, all you need to do is
have <command>ophis.exe</command> in your path.
</para>
</section>
</preface>

749
doc/docbook/samplecode.sgm Normal file
View File

@ -0,0 +1,749 @@
<appendix>
<title>Example Programs</title>
<para>
This Appendix collects all the programs referred to in the course
of this manual.
</para>
<section id="tutor1-src">
<title id="tutor1-fname"><filename>tutor1.oph</filename></title>
<programlisting>
.word $0801
.org $0801
.word next, 10 ; Next line and current line number
.byte $9e," 2064",0 ; SYS 2064
next: .word 0 ; End of program
.advance 2064
ldx #0
loop: lda hello, x
beq done
jsr $ffd2
inx
bne loop
done: rts
hello: .byte "HELLO, WORLD!", 0
</programlisting>
</section>
<section id="tutor2-src">
<title id="tutor2-fname"><filename>tutor2.oph</filename></title>
<programlisting>
.word $0801
.org $0801
.scope
.word _next, 10 ; Next line and current line number
.byte $9e," 2064",0 ; SYS 2064
_next: .word 0 ; End of program
.scend
.advance 2064
.alias chrout $ffd2
ldx #0
* lda hello, x
beq +
jsr chrout
inx
bne -
* rts
hello: .byte "HELLO, WORLD!", 0
</programlisting>
</section>
<section id="c64-1-src">
<title id="c64-1-fname"><filename>c64-1.oph</filename></title>
<programlisting>
.word $0801
.org $0801
.scope
.word _next, 10 ; Next line and current line number
.byte $9e," 2064",0 ; SYS 2064
_next: .word 0 ; End of program
.scend
.advance 2064
.require "kernal.oph"
</programlisting>
</section>
<section id="kernal-src">
<title id="kernal-fname"><filename>kernal.oph</filename></title>
<programlisting>
; KERNAL routine aliases (C64)
.alias acptr $ffa5
.alias chkin $ffc6
.alias chkout $ffc9
.alias chrin $ffcf
.alias chrout $ffd2
.alias ciout $ffa8
.alias cint $ff81
.alias clall $ffe7
.alias close $ffc3
.alias clrchn $ffcc
.alias getin $ffe4
.alias iobase $fff3
.alias ioinit $ff84
.alias listen $ffb1
.alias load $ffd5
.alias membot $ff9c
.alias memtop $ff99
.alias open $ffc0
.alias plot $fff0
.alias ramtas $ff87
.alias rdtim $ffde
.alias readst $ffb7
.alias restor $ff8a
.alias save $ffd8
.alias scnkey $ff9f
.alias screen $ffed
.alias second $ff93
.alias setlfs $ffba
.alias setmsg $ff90
.alias setnam $ffbd
.alias settim $ffdb
.alias settmo $ffa2
.alias stop $ffe1
.alias talk $ffb4
.alias tksa $ff96
.alias udtim $ffea
.alias unlsn $ffae
.alias untlk $ffab
.alias vector $ff8d
; Character codes for the colors.
.alias color'0 144
.alias color'1 5
.alias color'2 28
.alias color'3 159
.alias color'4 156
.alias color'5 30
.alias color'6 31
.alias color'7 158
.alias color'8 129
.alias color'9 149
.alias color'10 150
.alias color'11 151
.alias color'12 152
.alias color'13 153
.alias color'14 154
.alias color'15 155
; ...and reverse video
.alias reverse'on 18
.alias reverse'off 146
; ...and character set
.alias upper'case 142
.alias lower'case 14
</programlisting>
</section>
<section id="tutor3-src">
<title id="tutor3-fname"><filename>tutor3.oph</filename></title>
<programlisting>
.include "c64-1.oph"
.macro print
ldx #0
_loop: lda _1, x
beq _done
jsr chrout
inx
bne _loop
_done:
.macend
.macro greet
`print hello1
`print _1
`print hello2
.macend
lda #147
jsr chrout
`greet target1
`greet target2
`greet target3
`greet target4
`greet target5
`greet target6
`greet target7
`greet target8
`greet target9
`greet target10
rts
hello1: .byte "HELLO, ",0
hello2: .byte "!", 13, 0
target1: .byte "PROGRAMMER", 0
target2: .byte "ROOM", 0
target3: .byte "BUILDING", 0
target4: .byte "NEIGHBORHOOD", 0
target5: .byte "CITY", 0
target6: .byte "NATION", 0
target7: .byte "WORLD", 0
target8: .byte "SOLAR SYSTEM", 0
target9: .byte "GALAXY", 0
target10: .byte "UNIVERSE", 0
</programlisting>
</section>
<section id="tutor4a-src">
<title id="tutor4a-fname"><filename>tutor4a.oph</filename></title>
<programlisting>
.include "c64-1.oph"
.macro print
ldx #0
_loop: lda _1, x
beq _done
jsr chrout
inx
bne _loop
_done:
.macend
.macro greet
lda #30
jsr delay
`print hello1
`print _1
`print hello2
.macend
lda #147
jsr chrout
`greet target1
`greet target2
`greet target3
`greet target4
`greet target5
`greet target6
`greet target7
`greet target8
`greet target9
`greet target10
rts
hello1: .byte "HELLO, ",0
hello2: .byte "!", 13, 0
target1: .byte "PROGRAMMER", 0
target2: .byte "ROOM", 0
target3: .byte "BUILDING", 0
target4: .byte "NEIGHBORHOOD", 0
target5: .byte "CITY", 0
target6: .byte "NATION", 0
target7: .byte "WORLD", 0
target8: .byte "SOLAR SYSTEM", 0
target9: .byte "GALAXY", 0
target10: .byte "UNIVERSE", 0
; DELAY routine. Executes 2,560*(A) NOP statements.
delay: tax
ldy #00
* nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
iny
bne -
dex
bne -
rts
</programlisting>
</section>
<section id="tutor4b-src">
<title id="tutor4b-fname"><filename>tutor4b.oph</filename></title>
<programlisting>
.include "c64-1.oph"
.macro print
ldx #0
_loop: lda _1, x
beq _done
jsr chrout
inx
bne _loop
_done:
.macend
.macro greet
lda #30
jsr delay
`print hello1
`print _1
`print hello2
.macend
lda #147
jsr chrout
lda #lower'case
jsr chrout
`greet target1
`greet target2
`greet target3
`greet target4
`greet target5
`greet target6
`greet target7
`greet target8
`greet target9
`greet target10
rts
hello1: .byte "Hello, ",0
hello2: .byte "!", 13, 0
target1: .byte "programmer", 0
target2: .byte "room", 0
target3: .byte "building", 0
target4: .byte "neighborhood", 0
target5: .byte "city", 0
target6: .byte "nation", 0
target7: .byte "world", 0
target8: .byte "Solar System", 0
target9: .byte "Galaxy", 0
target10: .byte "Universe", 0
; DELAY routine. Executes 2,560*(A) NOP statements.
delay: tax
ldy #00
* nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
iny
bne -
dex
bne -
rts
</programlisting>
</section>
<section id="tutor4c-src">
<title id="tutor4c-fname"><filename>tutor4c.oph</filename></title>
<programlisting>
.include "c64-1.oph"
.macro print
ldx #0
_loop: lda _1, x
beq _done
jsr chrout
inx
bne _loop
_done:
.macend
.macro greet
lda #30
jsr delay
`print hello1
`print _1
`print hello2
.macend
lda #147
jsr chrout
lda #lower'case
jsr chrout
`greet target1
`greet target2
`greet target3
`greet target4
`greet target5
`greet target6
`greet target7
`greet target8
`greet target9
`greet target10
rts
.charmap 'A, "abcdefghijklmnopqrstuvwxyz"
.charmap 'a, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
hello1: .byte "Hello, ",0
hello2: .byte "!", 13, 0
target1: .byte "programmer", 0
target2: .byte "room", 0
target3: .byte "building", 0
target4: .byte "neighborhood", 0
target5: .byte "city", 0
target6: .byte "nation", 0
target7: .byte "world", 0
target8: .byte "Solar System", 0
target9: .byte "Galaxy", 0
target10: .byte "Universe", 0
; DELAY routine. Executes 2,560*(A) NOP statements.
delay: tax
ldy #00
* nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
iny
bne -
dex
bne -
rts
</programlisting>
</section>
<section id="tutor5-src">
<title id="tutor5-fname"><filename>tutor5.oph</filename></title>
<programlisting>
.include "c64-1.oph"
.data
.org $C000
.text
.macro print
ldx #0
_loop: lda _1, x
beq _done
jsr chrout
inx
bne _loop
_done:
.macend
.macro greet
lda #30
jsr delay
`print hello1
`print _1
`print hello2
.macend
lda #147
jsr chrout
`greet target1
`greet target2
`greet target3
`greet target4
`greet target5
`greet target6
`greet target7
`greet target8
`greet target9
`greet target10
rts
hello1: .byte "HELLO, ",0
hello2: .byte "!", 13, 0
target1: .byte "PROGRAMMER", 0
target2: .byte "ROOM", 0
target3: .byte "BUILDING", 0
target4: .byte "NEIGHBORHOOD", 0
target5: .byte "CITY", 0
target6: .byte "NATION", 0
target7: .byte "WORLD", 0
target8: .byte "SOLAR SYSTEM", 0
target9: .byte "GALAXY", 0
target10: .byte "UNIVERSE", 0
; DELAY routine. Takes values from the Accumulator and pauses
; for that many jiffies (1/60th of a second).
.scope
.data
.space _tmp 1
.space _target 1
.text
delay: sta _tmp ; save argument (rdtim destroys it)
jsr rdtim
clc
adc _tmp ; add current time to get target
sta _target
* jsr rdtim
cmp _target
bmi - ; Buzz until target reached
rts
.scend
.checkpc $A000
.data
.checkpc $D000
</programlisting>
</section>
<section id="tutor6-src">
<title id="tutor6-fname"><filename>tutor6.oph</filename></title>
<programlisting>
.include "c64-1.oph"
.data
.org $C000
.space cache 2
.text
.macro print
lda #<_1
ldx #>_1
jsr printstr
.macend
.macro greet
lda #30
jsr delay
`print hello1
`print _1
`print hello2
.macend
; Save the zero page locations that PRINTSTR uses.
lda $10
sta cache
lda $11
sta cache+1
lda #147
jsr chrout
`greet target1
`greet target2
`greet target3
`greet target4
`greet target5
`greet target6
`greet target7
`greet target8
`greet target9
`greet target10
; Restore the zero page values printstr uses.
lda cache
sta $10
lda cache+1
sta $11
rts
hello1: .byte "HELLO, ",0
hello2: .byte "!", 13, 0
target1: .byte "PROGRAMMER", 0
target2: .byte "ROOM", 0
target3: .byte "BUILDING", 0
target4: .byte "NEIGHBORHOOD", 0
target5: .byte "CITY", 0
target6: .byte "NATION", 0
target7: .byte "WORLD", 0
target8: .byte "SOLAR SYSTEM", 0
target9: .byte "GALAXY", 0
target10: .byte "UNIVERSE", 0
; DELAY routine. Takes values from the Accumulator and pauses
; for that many jiffies (1/60th of a second).
.scope
.data
.space _tmp 1
.space _target 1
.text
delay: sta _tmp ; save argument (rdtim destroys it)
jsr rdtim
clc
adc _tmp ; add current time to get target
sta _target
* jsr rdtim
cmp _target
bmi - ; Buzz until target reached
rts
.scend
; PRINTSTR routine. Accumulator stores the low byte of the address,
; X register stores the high byte. Destroys the values of $10 and
; $11.
.scope
printstr:
sta $10
stx $11
ldy #$00
_lp: lda ($10),y
beq _done
jsr chrout
iny
bne _lp
_done: rts
.scend
.checkpc $A000
.data
.checkpc $D000
</programlisting>
</section>
<section id="c64-2-src">
<title id="c64-2-fname"><filename>c64-2.oph</filename></title>
<programlisting>
.word $0801
.org $0801
.scope
.word _next, 10 ; Next line and current line number
.byte $9e," 2064",0 ; SYS 2064
_next: .word 0 ; End of program
.scend
.advance $0810
.require "kernal.oph"
.data zp
.org $0002
.text
.scope
; Cache BASIC's zero page at top of available RAM.
ldx #$7E
* lda $01, x
sta $CF81, x
dex
bne -
jsr _main
; Restore BASIC's zero page and return control.
ldx #$7E
* lda $CF81, x
sta $01, x
dex
bne -
rts
_main:
; Program follows...
.scend
</programlisting>
</section>
<section id="tutor7-src">
<title id="tutor7-fname"><filename>tutor7.oph</filename></title>
<programlisting>
.include "c64-2.oph"
.data
.org $C000
.text
.macro print
lda #<_1
ldx #>_1
jsr printstr
.macend
.macro greet
lda #30
jsr delay
`print hello1
`print _1
`print hello2
.macend
lda #147
jsr chrout
`greet target1
`greet target2
`greet target3
`greet target4
`greet target5
`greet target6
`greet target7
`greet target8
`greet target9
`greet target10
rts
hello1: .byte "HELLO, ",0
hello2: .byte "!", 13, 0
target1: .byte "PROGRAMMER", 0
target2: .byte "ROOM", 0
target3: .byte "BUILDING", 0
target4: .byte "NEIGHBORHOOD", 0
target5: .byte "CITY", 0
target6: .byte "NATION", 0
target7: .byte "WORLD", 0
target8: .byte "SOLAR SYSTEM", 0
target9: .byte "GALAXY", 0
target10: .byte "UNIVERSE", 0
; DELAY routine. Takes values from the Accumulator and pauses
; for that many jiffies (1/60th of a second).
.scope
.data
.space _tmp 1
.space _target 1
.text
delay: sta _tmp ; save argument (rdtim destroys it)
jsr rdtim
clc
adc _tmp ; add current time to get target
sta _target
* jsr rdtim
cmp _target
bmi - ; Buzz until target reached
rts
.scend
; PRINTSTR routine. Accumulator stores the low byte of the address,
; X register stores the high byte. Destroys the values of $10 and
; $11.
.scope
.data zp
.space _ptr 2
.text
printstr:
sta _ptr
stx _ptr+1
ldy #$00
_lp: lda (_ptr),y
beq _done
jsr chrout
iny
bne _lp
_done: rts
.scend
.checkpc $A000
.data
.checkpc $D000
.data zp
.checkpc $80
</programlisting>
</section>
</appendix>

315
doc/docbook/tutor1.sgm Normal file
View File

@ -0,0 +1,315 @@
<chapter id="part1">
<title>The basics</title>
<para>
In this first part of the tutorial we will create a
simple <quote>Hello World</quote> program to run on the Commodore
64. This will cover:
<itemizedlist>
<listitem><para>How to make programs run on a Commodore 64</para></listitem>
<listitem><para>Writing simple code with labels</para></listitem>
<listitem><para>Numeric and string data</para></listitem>
<listitem><para>Invoking the assembler</para></listitem>
</itemizedlist>
</para>
<section>
<title>A note on numeric notation</title>
<para>
Throughout these tutorials, I will be using a lot of both
decimal and hexadecimal notation. Hex numbers will have a
dollar sign in front of them. Thus, 100 = $64, and $100 = 256.
</para>
</section>
<section>
<title>Producing Commodore 64 programs</title>
<para>
Commodore 64 programs are stored in
the <filename>PRG</filename> format on disk. Some emulators
(such as CCS64 or VICE) can run <filename>PRG</filename>
programs directly; others need them to be transferred to
a <filename>D64</filename> image first.
</para>
<para>
The <filename>PRG</filename> format is ludicrously simple. It
has two bytes of header data: This is a little-endian number
indicating the starting address. The rest of the file is a
single continuous chunk of data loaded into memory, starting at
that address. BASIC memory starts at memory location 2048, and
that's probably where we'll want to start.
</para>
<para>
Well, not quite. We want our program to be callable from BASIC,
so we should have a BASIC program at the start. We guess the
size of a simple one line BASIC program to be about 16 bytes.
Thus, we start our program at memory location 2064 ($0810), and
the BASIC program looks like this:
</para>
<programlisting>
10 SYS 2064
</programlisting>
<para>
We <userinput>SAVE</userinput> this program to a file, then
study it in a debugger. It's 15 bytes long:
</para>
<screen>
1070:0100 01 08 0C 08 0A 00 9E 20-32 30 36 34 00 00 00
</screen>
<para>
The first two bytes are the memory location: $0801. The rest of
the data breaks down as follows:
</para>
<table frame="all">
<title>BASIC program breakdown</title>
<tgroup cols='2'>
<thead>
<row>
<entry align="center">Memory Locations</entry>
<entry align="center">Value</entry>
</row>
</thead>
<tbody>
<row><entry>$0801-$0802</entry><entry>2-byte pointer to the next line of BASIC code ($080C).</entry></row>
<row><entry>$0803-$0804</entry><entry>2-byte line number ($000A = 10).</entry></row>
<row><entry>$0805</entry><entry>Byte code for the <userinput>SYS</userinput> command.</entry></row>
<row><entry>$0806-$080A</entry><entry>The rest of the line, which is just the string <quote> 2064</quote>.</entry></row>
<row><entry>$080B</entry><entry>Null byte, terminating the line.</entry></row>
<row><entry>$080C-$080D</entry><entry>2-byte pointer to the next line of BASIC code ($0000 = end of program).</entry></row>
</tbody>
</tgroup>
</table>
<para>
That's 13 bytes. We started at 2049, so we need 2 more bytes of
filler to make our code actually start at location 2064. These
17 bytes will give us the file format and the BASIC code we need
to have our machine language program run.
</para>
<para>
These are just bytes&mdash;indistinguishable from any other sort of
data. In Ophis, bytes of data are specified with
the <literal>.byte</literal> command. We'll also have to tell
Ophis what the program counter should be, so that it knows what
values to assign to our labels. The <literal>.org</literal>
(origin) command tells Ophis this. Thus, the Ophis code for our
header and linking info is:
</para>
<programlisting>
.byte $01, $08, $0C, $08, $0A, $00, $9E, $20
.byte $32, $30, $36, $34, $00, $00, $00, $00
.byte $00, $00
.org $0810
</programlisting>
<para>
This gets the job done, but it's completely incomprehensible,
and it only uses two directives&mdash;not very good for a
tutorial. Here's a more complicated, but much clearer, way of
saying the same thing.
</para>
<programlisting>
.word $0801
.org $0801
.word next, 10 ; Next line and current line number
.byte $9e," 2064",0 ; SYS 2064
next: .word 0 ; End of program
.advance 2064
</programlisting>
<para>
This code has many advantages over the first.
<itemizedlist>
<listitem><para> It describes better what is actually
happening. The <literal>.word</literal> directive at the
beginning indicates a 16-bit value stored in the typical
65xx way (small byte first). This is followed by
an <literal>.org</literal> statement, so we let the
assembler know right away where everything is supposed to
be.
</para></listitem>
<listitem><para> Instead of hardcoding in the value $080C, we
instead use a label to identify the location it's pointing
to. Ophis will compute the address
of <literal>next</literal> and put that value in as data.
We also describe the line number in decimal since BASIC
line numbers generally <emphasis>are</emphasis> in decimal.
Labels are defined by putting their name, then a colon, as
seen in the definition of <literal>next</literal>.
</para></listitem>
<listitem><para>
Instead of putting in the hex codes for the string part of
the BASIC code, we included the string directly. Each
character in the string becomes one byte.
</para></listitem>
<listitem><para>
Instead of adding the buffer ourselves, we
used <literal>.advance</literal>, which outputs zeros until
the specified address is reached. Attempting
to <literal>.advance</literal> backwards produces an
assemble-time error.
</para></listitem>
<listitem><para>
It has comments that explain what the data are for. The
semicolon is the comment marker; everything from a semicolon
to the end of the line is ignored.
</para></listitem>
</itemizedlist>
</para>
</section>
<section>
<title>Related commands and options</title>
<para>
This code includes constants that are both in decimal and in
hex. It is also possible to specify constants in octal, binary,
or with an ASCII character.
<itemizedlist>
<listitem><para>To specify decimal constants, simply write the number.</para></listitem>
<listitem><para>To specify hexadecimal constants, put a $ in front.</para></listitem>
<listitem><para>To specify octal constants, put a 0 (zero) in front.</para></listitem>
<listitem><para>To specify binary constants, put a % in front.</para></listitem>
<listitem><para>To specify ASCII constants, put an apostrophe in front.</para></listitem>
</itemizedlist>
Example: 65 = $41 = 0101 = %1000001 = 'A
</para>
<para>
There are other commands besides <literal>.byte</literal>
and <literal>.word</literal> to specify data. In particular,
the <literal>.dword</literal> command specifies four-byte values
which some applications will find useful. Also, some linking
formats (such as the <filename>SID</filename> format) have
header data in big-endian (high byte first) format.
The <literal>.wordbe</literal> and <literal>.dwordbe</literal>
directives provide a way to specify multibyte constants in
big-endian formats cleanly.
</para>
</section>
<section>
<title>Writing the actual code</title>
<para>
Now that we have our header information, let's actually write
the <quote>Hello world</quote> program. It's pretty
short&mdash;a simple loop that steps through a hardcoded array
until it reaches a 0 or outputs 256 characters. It then returns
control to BASIC with an <literal>RTS</literal> statement.
</para>
<para>
Each character in the array is passed as an argument to a
subroutine at memory location $FFD2. This is part of the
Commodore 64's BIOS software, which its development
documentation calls the KERNAL. Location $FFD2 prints out the
character corresponding to the character code in the
accumulator.
</para>
<programlisting>
ldx #0
loop: lda hello, x
beq done
jsr $ffd2
inx
bne loop
done: rts
hello: .byte "HELLO, WORLD!", 0
</programlisting>
<para>
The complete, final source is available in
the <xref linkend="tutor1-src" endterm="tutor1-fname"> file.
</para>
</section>
<section>
<title>Assembling the code</title>
<para>
The Ophis assembler is a collection of Python modules,
controlled by a master script. On Windows, this should all
have been combined into an executable
file <command>ophis.exe</command>; on other platforms, the
Ophis modules should be in the library and
the <command>ophis</command> script should be in your path.
Typing <command>ophis</command> with no arguments should give a
summary of available command line options.
</para>
<table frame="all">
<title>Ophis Options</title>
<tgroup cols='2'>
<thead>
<row>
<entry align="center">Option</entry>
<entry align="center">Effect</entry>
</row>
</thead>
<tbody>
<row><entry><option>-6510</option></entry><entry>Allows the 6510 undocumented opcodes as listed in the VICE documentation.</entry></row>
<row><entry><option>-65c02</option></entry><entry>Allows opcodes and addressing modes added by the 65C02.</entry></row>
<row><entry><option>-v 0</option></entry><entry>Quiet operation. Only reports errors.</entry></row>
<row><entry><option>-v 1</option></entry><entry>Default operation. Reports files as they are loaded, and gives statistics on the final output.</entry></row>
<row><entry><option>-v 2</option></entry><entry>Verbose operation. Names each assembler pass as it runs.</entry></row>
<row><entry><option>-v 3</option></entry><entry>Debug operation: Dumps the entire IR after each pass.</entry></row>
<row><entry><option>-v 4</option></entry><entry>Full debug operation: Dumps the entire IR and symbol table after each pass.</entry></row>
</tbody>
</tgroup>
</table>
<para>
The only options Ophis demands are an input file and an output
file. Here's a sample session, assembling the tutorial file
here:
</para>
<screen>
localhost$ ophis tutor1.oph tutor1.prg -v 2
Loading tutor1.oph
Running: Macro definition pass
Running: Macro expansion pass
Running: Label initialization pass
Fixpoint failed, looping back
Running: Label initialization pass
Running: Circularity check pass
Running: Expression checking pass
Running: Easy addressing modes pass
Running: Label Update Pass
Fixpoint failed, looping back
Running: Label Update Pass
Running: Instruction Collapse Pass
Running: Mode Normalization pass
Running: Label Update Pass
Running: Assembler
Assembly complete: 45 bytes output (14 code, 29 data, 2 filler)
</screen>
<para>
If your emulator can run <filename>PRG</filename> files
directly, this file will now run (and
print <computeroutput>HELLO, WORLD!</computeroutput>) as many
times as you type <userinput>RUN</userinput>. Otherwise, use
a <filename>D64</filename> management utility to put
the <filename>PRG</filename> on a <filename>D64</filename>, then
load and run the file off that.
</para>
</section>
</chapter>

107
doc/docbook/tutor2.sgm Normal file
View File

@ -0,0 +1,107 @@
<chapter>
<title>Labels and aliases</title>
<para>
Labels are an important part of your code. However, since each
label must normally be unique, this can lead to <quote>namespace
pollution,</quote> and you'll find yourself going through ever
more contorted constructions to generate unique label names.
Ophis offers two solutions to this: <emphasis>anonymous
labels</emphasis> and <emphasis>temporary labels</emphasis>. This
tutorial will cover both of these facilities, and also introduce
the aliasing mechanism.
</para>
<section>
<title>Temporary labels</title>
<para>
Temporary labels are the easiest to use. If a label begins with
an underscore, it will only be reachable from inside the
innermost enclosing scope. Scopes begin when
a <literal>.scope</literal> statement is encountered. This
produces a new, inner scope if there is another scope in use.
The <literal>.scend</literal> command ends the innermost
currently active scope.
</para>
<para>
We can thus rewrite our header data using temporary labels, thus
allowing the main program to have a label
named <literal>next</literal> if it wants.
</para>
<programlisting>
.word $0801
.org $0801
.scope
.word _next, 10 ; Next line and current line number
.byte $9e," 2064",0 ; SYS 2064
_next: .word 0 ; End of program
.scend
.advance 2064
</programlisting>
</section>
<section>
<title>Anonymous labels</title>
<para>
Anonymous labels are a way to handle short-ranged branches
without having to come up with names for the then and else
branches, for brief loops, and other such purposes. To define
an anonymous label, use an asterisk. To refer to an anonymous
label, use a series of <literal>+</literal>
or <literal>-</literal> signs. <literal>+</literal> refers to
the next anonymous label, <literal>++</literal> the label
after that, etc. Likewise, <literal>-</literal> is the most
recently defined label, <literal>--</literal> the one before
that, and so on. The main body of the Hello World program
with anonymous labels would be:
</para>
<programlisting>
ldx #0
* lda hello, x
beq +
jsr $ffd2
inx
bne -
* rts
</programlisting>
<para>
It is worth noting that anonymous labels are globally available.
They are not temporary labels, and they ignore scoping
restrictions.
</para>
</section>