mirror of
https://github.com/michaelcmartin/Ophis.git
synced 2024-12-21 12:29:46 +00:00
Update documentation and examples for version 2.2
This commit is contained in:
parent
94d0b9d0c4
commit
f957df94f2
Binary file not shown.
@ -20,7 +20,7 @@
|
||||
<bookinfo>
|
||||
<title>Programming with Ophis</title>
|
||||
<author><firstname>Michael</firstname><surname>Martin</surname></author>
|
||||
<copyright><year>2006-2014</year><holder>Michael Martin</holder></copyright>
|
||||
<copyright><year>2006-2024</year><holder>Michael Martin</holder></copyright>
|
||||
</bookinfo>
|
||||
&pre1;
|
||||
<part label="I">
|
||||
|
@ -6,7 +6,7 @@
|
||||
consoles. Its primary design goals are code readability and output
|
||||
flexibility - Ophis has successfully been used to create programs
|
||||
for the Nintendo Entertainment System, the Atari 2600, and various
|
||||
8-bit Commodore machines.
|
||||
8-bit Commodore and Apple machines.
|
||||
</para>
|
||||
<para>
|
||||
Ophis's syntax is noticably different from the formats
|
||||
@ -39,20 +39,20 @@
|
||||
cross-assembler for the 6502 chip the C64 used in both.
|
||||
</para>
|
||||
<para>
|
||||
The Perl one—uncreatively
|
||||
dubbed <quote>Perl65</quote>—was quickly abandoned, but
|
||||
the Python one saw more work. When it came time to name it, one
|
||||
of the things I had been hoping to do with the assembler was to
|
||||
The Perl one—uncreatively dubbed
|
||||
<quote>Perl65</quote>—was quickly abandoned, but the
|
||||
Python one saw more work. When it came time to name it, 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.<footnote><para>Ironically, cross-platform development
|
||||
for the Apple II is extremely difficult, and while Ophis has
|
||||
been very successfully used to develop code for the Commodore
|
||||
64, Nintendo Entertainment System, and Atari 2600, it has yet to
|
||||
actually be deployed on any of the Apples which inspired its
|
||||
for the Apple II is much less straightforward than for the
|
||||
Commodore 8-bits or ROM-based consoles, and it took many years
|
||||
after its release before it was actually used to write code
|
||||
deployed on any of the Apples which inspired its
|
||||
name.</para></footnote>
|
||||
</para>
|
||||
<para>
|
||||
@ -68,18 +68,38 @@
|
||||
</para>
|
||||
<para>
|
||||
After its release Ophis 2 was picked up by a number of
|
||||
developers work with actual hardware from the period, including
|
||||
prototype machines that never saw production. Some of their
|
||||
contributions have refined the code generators for version 2.1.
|
||||
developers working with actual hardware from the period,
|
||||
including prototype machines that never saw production. Some
|
||||
of their contributions have refined the code generators for
|
||||
version 2.1.
|
||||
</para>
|
||||
<para>
|
||||
This is an updated edition of <emphasis>Programming With
|
||||
Ophis</emphasis>, including documentation for all new features
|
||||
introduced and expanding the examples to include simple
|
||||
demonstration programs for platforms besides the Commodore
|
||||
64. It also includes updated versions of the <emphasis>To HLL
|
||||
and Back</emphasis> essays I wrote using Ophis and Perl65 as
|
||||
example languages.
|
||||
At that point, the program was basically done, and very little
|
||||
changes for about five years. The world, however, moved on, and
|
||||
Python 2, my implementation language, was deprecated and
|
||||
rendered obsolete. That didn't change much about the 2.1
|
||||
release—Python 2 was still installed on non-Windows
|
||||
machines by default, and the Windows distribution was as a
|
||||
bundled .EXE file—but it threatened the viability of the
|
||||
program overall. In 2019, then, I converted the source base to
|
||||
the backwards-incompatible Python 3, in the hopes of
|
||||
future-proofing the system. Five years after
|
||||
<emphasis>that</emphasis>, enough bug reports and bug fixes had
|
||||
trickled in to justify a fresh release, and 2.2 was published in
|
||||
2024—a lightly polished update that now fit more neatly
|
||||
into the Python toolchains of the 2020s.
|
||||
</para>
|
||||
<para>
|
||||
In the twenty years since I first started this project, I've
|
||||
gained quite a bit more experience with programming the computer
|
||||
systems of the 1970s and 1980s. I have left this manual largely
|
||||
as it was in its 2014 edition, including its versions of the
|
||||
<emphasis>To HLL and Back</emphasis> essays I wrote using Ophis
|
||||
and Perl65 as example languages. I don't think I stand behind my
|
||||
design decisions back then as firmly as I did when I wrote those
|
||||
essays, but there's noting <emphasis>wrong</emphasis> with them
|
||||
either so I'm happy to leave them as a testament to my younger,
|
||||
brasher self.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
@ -708,7 +708,7 @@ _done: rts
|
||||
.checkpc $D000
|
||||
|
||||
.data zp
|
||||
.checkpc $80
|
||||
.checkpc $90
|
||||
</programlisting>
|
||||
</section>
|
||||
<section id="structure-src">
|
||||
|
@ -58,11 +58,11 @@
|
||||
|
||||
<para>
|
||||
We <userinput>SAVE</userinput> this program to a file, then
|
||||
study it in a debugger. It's 15 bytes long:
|
||||
study it with a hex dumper. It's 15 bytes long:
|
||||
</para>
|
||||
|
||||
<screen>
|
||||
1070:0100 01 08 0C 08 0A 00 9E 20-32 30 36 34 00 00 00
|
||||
00000000 01 08 0c 08 0a 00 9e 20 32 30 36 34 00 00 00 |....... 2064...|
|
||||
</screen>
|
||||
|
||||
<para>
|
||||
@ -72,29 +72,32 @@
|
||||
|
||||
<table frame="all">
|
||||
<title>BASIC program breakdown</title>
|
||||
<tgroup cols='2'>
|
||||
<tgroup cols='3'>
|
||||
<thead>
|
||||
<row>
|
||||
<entry align="center">File Offsets</entry>
|
||||
<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>
|
||||
<row><entry>0-1</entry><entry>Nowhere</entry><entry>2-byte pointer to where in memory to load the rest of the file ($0801).</entry></row>
|
||||
<row><entry>2-3</entry><entry>$0801-$0802</entry><entry>2-byte pointer to the next line of BASIC code ($080C).</entry></row>
|
||||
<row><entry>4-5</entry><entry>$0803-$0804</entry><entry>2-byte line number ($000A = 10).</entry></row>
|
||||
<row><entry>6</entry><entry>$0805</entry><entry>Byte code for the <userinput>SYS</userinput> command.</entry></row>
|
||||
<row><entry>7-11</entry><entry>$0806-$080A</entry><entry>The rest of the line, which is just the string <quote> 2064</quote>.</entry></row>
|
||||
<row><entry>12</entry><entry>$080B</entry><entry>Null byte, terminating the line.</entry></row>
|
||||
<row><entry>13-14</entry><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.
|
||||
That's 15 bytes, of which 13 are actually loaded into memory.
|
||||
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>
|
||||
@ -176,6 +179,16 @@ next: .word 0 ; End of program
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
We can do better still, though. That initial starting address
|
||||
of 2064 was only ever a guess; now that we know that we overshot
|
||||
by two bytes, we can simply change the starting address to 2062
|
||||
and omit the <literal>.advance</literal> directive entirely. In
|
||||
fact, we can even remove the space before the number and make it
|
||||
2061 instead—BASIC doesn't need that space in its
|
||||
instruction and it's arguably a wasted byte.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
@ -25,9 +25,9 @@
|
||||
</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.
|
||||
We can rewrite our header data using temporary labels, allowing
|
||||
the main program to have a label named <literal>next</literal>
|
||||
if it wants.
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
|
@ -24,7 +24,8 @@
|
||||
<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.
|
||||
is useful for ensuring a library is present somewhere in the
|
||||
final code.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -132,11 +133,6 @@ _done:
|
||||
<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
|
||||
|
@ -104,4 +104,12 @@ target10: .byte "Universe", 0
|
||||
provided with the sample programs
|
||||
as <filename>petscii.map</filename>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Versions of Ophis prior to 2.2 have a bug where only the first
|
||||
argument to <literal>.byte</literal> would be translated. That's
|
||||
fine for our example code here, with only one string per line, but
|
||||
a more text-heavy title that relied on this should confirm their
|
||||
version before getting too far in.
|
||||
</para>
|
||||
</chapter>
|
||||
|
@ -91,7 +91,11 @@ delay: sta _tmp ; save argument (rdtim destroys it)
|
||||
using are <literal>.org</literal> and <literal>.space</literal>
|
||||
commands. Ophis will not complain if you
|
||||
use <literal>.space</literal> inside a <literal>.text</literal>
|
||||
segment, but this is nearly always wrong.
|
||||
segment, but this is nearly always wrong. Remember,
|
||||
both <literal>.org</literal> and <literal>.space</literal> only
|
||||
ever alter the way that Ophis computes labels. They do not output
|
||||
any bytes, nor do they change where in the output file the bytes
|
||||
are actually written.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
|
@ -110,8 +110,8 @@ _done: rts
|
||||
</table>
|
||||
<para>
|
||||
Note that brackets, not parentheses, are used to group arithmetic
|
||||
operations. This is because parentheses are used for the indirect
|
||||
addressing modes, and it makes parsing much easier.
|
||||
operations. Parentheses are reserved for the indirect addressing
|
||||
modes.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
|
@ -130,7 +130,7 @@ _done: rts
|
||||
|
||||
<programlisting>
|
||||
.data zp
|
||||
.checkpc $80
|
||||
.checkpc $90
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
|
114
doc/tutor8.sgm
114
doc/tutor8.sgm
@ -1,5 +1,5 @@
|
||||
<chapter>
|
||||
<title>Platform-Specific Techniques</title>
|
||||
<title>Included Platform Support</title>
|
||||
|
||||
<para>
|
||||
Ophis is intended to produce cross-assembled binaries that will
|
||||
@ -18,9 +18,7 @@
|
||||
In a real sense, the Commodore 64 is the "native"
|
||||
target platform for Ophis. It was the first platform targeted
|
||||
and it's the one that has received the most additional
|
||||
support. It's also one where the developer needs to take the
|
||||
most care about exactly what kind of program they are
|
||||
writing.
|
||||
support.
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
@ -166,27 +164,33 @@
|
||||
<title>The Nintendo Entertainment System</title>
|
||||
|
||||
<para>
|
||||
The NES development community is somewhat more fragmented than
|
||||
the others. A skeletal <literal>nes.oph</literal> file is
|
||||
provided, but memory locations are not as consistently
|
||||
named. Much sample code doesn't provide aliases for control
|
||||
registers at all.
|
||||
The NES development community in 2024 has standardized on the
|
||||
sophisticated <literal>ca65</literal> assembler for major
|
||||
homebrew projects, but Ophis's simpler output model has
|
||||
advantages of its own. A skeletal <literal>nes.oph</literal>
|
||||
file is provided in the platform support directory, but most
|
||||
NES code you'll find in the wild doesn't use aliases for control
|
||||
registers at all—it just sticks with the register numbers.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Conveniently creating runnable NES programs is somewhat
|
||||
involved. Any given product was generally burned onto several
|
||||
chips that were affixed to one of a large number of circuit
|
||||
boards. These are often referred to as "mappers" by
|
||||
developers because their effect is to implement various
|
||||
bankswitching schemes. The result is a program built out of
|
||||
parts, each with its own origin. A "Hello World"
|
||||
sample program ships with Ophis. It does not use a bankswitcher,
|
||||
but it does split its contents into a program chip and a
|
||||
graphics chip, with one of two wrapper files to knit them
|
||||
together into a file that other software will recognize. Samples
|
||||
are given for the common iNES format and the defunct UNIF
|
||||
format.
|
||||
Creating output files that emulators or other tools will
|
||||
recognize as complete NES programs is somewhat involved.
|
||||
Any given product was generally one of a large selection of
|
||||
circuit boards with several ROM or support-logic chips
|
||||
affixed to it. These circuit board configurations are generally
|
||||
referred to as "mappers" by developers because their
|
||||
effect is to implement various bankswitching schemes. The result
|
||||
is a program built out of parts, each with its own origin.
|
||||
A simple <quote>Hello World</quote> sample program ships with
|
||||
Ophis. It is configured to use "Mapper Zero", or a simulation of
|
||||
the <quote>NROM</quote> circuit board, which had no special
|
||||
bankswitching logic and simply wired the program chip and the
|
||||
graphics chip directly into the address bus. The sample code
|
||||
includes one source file for each chip, and then two wrapper
|
||||
files to knit them together into a file that other software will
|
||||
recognize. As of 2024, the UNIF format is entirely abandoned in
|
||||
favor of the backwards-compatible iNES 2.0 format.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
@ -194,18 +198,66 @@
|
||||
<title>The Atari 2600 VCS</title>
|
||||
|
||||
<para>
|
||||
Of all the 8-bit development communities, the Atari developers
|
||||
seem to be the most cohesive. The development documents
|
||||
available are universal, and analysts and developers alike all
|
||||
use the register names in the <emphasis>Stella Developer's
|
||||
Guide</emphasis>. Ophis follows their lead, providing these
|
||||
names in the header <literal>stella.oph</literal>.
|
||||
Ophis provides a <literal>stella.oph</literal> header that names
|
||||
the system's registers to match the documentation in
|
||||
the <emphasis>Stella Programmer's Guide</emphasis>. It also
|
||||
replicates two macros that were widely shared on mailing lists
|
||||
and other tutorial documents at the time Ophis was first
|
||||
released. See the file itself for details.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <literal>stella.oph</literal> header also replicates two
|
||||
macros that appear in the header files distributed to budding
|
||||
VCS developers. They are documented in the file.
|
||||
Atari 2600 ROM images are simple ROM dumps and do not require
|
||||
any more sophisticated organization in the Ophis source files
|
||||
than an <literal>.advance</literal> directive to pad the output
|
||||
to the appropriate size.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Two sample programs ship with Ophis 2.2; a tiny hello-world
|
||||
program, and a more sophisticated interactive program that
|
||||
explores the system's color palette.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Other Atari 8-bits</title>
|
||||
|
||||
<para>
|
||||
The Atari 2600's successor, the Atari 5200, shares much of its
|
||||
architecture with the Atari 400/800/1200/XL/XE line. Atari DOS
|
||||
had an executable format that divided itself up into chunks that
|
||||
were independently loaded, with some chunks being special and
|
||||
identifying program entry points or intervening processing to be
|
||||
done mid-load.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A simple Hello World program compatible with Atari DOS is
|
||||
included in the examples directory. The output file may be
|
||||
loaded and run directly in many emulators, or may be copied
|
||||
into a disk image with a tool like <literal>atr</literal> or
|
||||
Altirra and executed from the DOS prompt.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>The Apple II series</title>
|
||||
|
||||
<para>
|
||||
For most of its lifespan, Apple II systems ran either a
|
||||
primitive system named "DOS 3.3" or more sophisticated one
|
||||
named ProDOS. ProDOS 8 is as of 2024 still under active
|
||||
development, and its superior support for machine-language
|
||||
interfacing with the disk drive makes it the preferable
|
||||
choice for Ophis-based development.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A simple Hello World program is included in the examples
|
||||
directory. To actually run the resulting binary, it must be
|
||||
added to a ProDOS-formatted disk using a tool such as CADIUS
|
||||
or CiderPress.
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
||||
|
@ -94,4 +94,4 @@ _done: rts
|
||||
.checkpc $D000
|
||||
|
||||
.data zp
|
||||
.checkpc $80
|
||||
.checkpc $90
|
||||
|
34
examples/hello_a800.oph
Normal file
34
examples/hello_a800.oph
Normal file
@ -0,0 +1,34 @@
|
||||
.outfile "hello.obj"
|
||||
.word $ffff ; Binary file
|
||||
.word start ; start of code
|
||||
.word end-1 ; end of code
|
||||
|
||||
.org $0600 ; Load into page 6
|
||||
|
||||
;; `iostob OFFSET, VALUE
|
||||
;; `iostow OFFSET, VALUE
|
||||
;; Store value in OFFSET in I/O control block X>>4.
|
||||
.macro iostob
|
||||
lda #_2
|
||||
sta $340+_1,x
|
||||
.macend
|
||||
|
||||
.macro iostow
|
||||
`iostob _1,<_2
|
||||
`iostob _1+1,>_2
|
||||
.macend
|
||||
|
||||
start: ldx #$00 ; Channel 0 (E:)
|
||||
|
||||
;; Write message with one I/O call, and exit
|
||||
`iostob 2,11 ; WRITE command
|
||||
`iostow 4,msg ; buffer pointer
|
||||
`iostow 8,msgend-msg ; buffer length
|
||||
jmp $e456 ; Do I/O call and quit
|
||||
|
||||
msg: .byte "Hello, world!",$9b
|
||||
msgend: ; End of message
|
||||
end: ; End of code block
|
||||
|
||||
;; Autostart at start
|
||||
.word $02e0,$02e1,start
|
31
examples/hello_apple2.oph
Normal file
31
examples/hello_apple2.oph
Normal file
@ -0,0 +1,31 @@
|
||||
;;; ----------------------------------------------------------------------
|
||||
;;; HELLO WORLD for the Apple II
|
||||
;;; This is a ProDOS 8 program. Its output should be importable by
|
||||
;;; CiderPress without incident.
|
||||
;;; ----------------------------------------------------------------------
|
||||
|
||||
.outfile "HI.SYSTEM#ff2000"
|
||||
.org $2000
|
||||
|
||||
;; Write message
|
||||
ldx #$00
|
||||
* lda msg,x
|
||||
beq wait
|
||||
ora #$80 ; Disable inverse
|
||||
jsr $fded ; CHROUT
|
||||
inx
|
||||
bne -
|
||||
|
||||
;; Wait for keypress
|
||||
wait: bit $c000 ; Check keypress bit
|
||||
bpl wait
|
||||
bit $c010 ; Acknowledge keypress
|
||||
|
||||
;; Return to ProDOS
|
||||
jsr $bf00
|
||||
.byte $65
|
||||
.word +
|
||||
brk ; Unreachable
|
||||
* .byte 4, 0, 0, 0, 0, 0, 0
|
||||
|
||||
msg: .byte "HELLO, WORLD!",13,"PRESS ANY KEY TO EXIT",0
|
@ -1,9 +1,9 @@
|
||||
"Hi Stella" is a simple "Hello World" program for the "Stella" chip,
|
||||
more famously known as the Atari 2600. Simply running
|
||||
"Hi Stella" is a simple "Hello World" program for the "Stella"
|
||||
system, more famously known as the Atari 2600. Simply running
|
||||
|
||||
ophis hi_stella.oph
|
||||
|
||||
should produce hi_stella.bin, a 256-byte file that prints "HI" on
|
||||
should produce hi_stella.bin, a simple demo that prints "HI" on
|
||||
the screen with some rolling color bars.
|
||||
|
||||
A more sophisticated program is colortest, which lets the user
|
||||
|
@ -36,7 +36,7 @@ def parse_args(raw_args):
|
||||
|
||||
parser = optparse.OptionParser(
|
||||
usage="Usage: %prog [options] srcfile [srcfile ...]",
|
||||
version="Ophis 6502 cross-assembler, version 2.1")
|
||||
version="Ophis 6502 cross-assembler, version 2.2")
|
||||
|
||||
parser.add_option("-o", default=None, dest="outfile",
|
||||
help="Output filename (default 'ophis.bin')")
|
||||
|
Loading…
Reference in New Issue
Block a user