mirror of
https://github.com/michaelcmartin/Ophis.git
synced 2024-06-16 08:29:35 +00:00
Compare commits
105 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
99f074da27 | ||
|
41bf01d035 | ||
|
971fafd918 | ||
|
f6990095c1 | ||
|
778fbf7e2c | ||
|
c3d48da59d | ||
|
92f91aeeee | ||
|
0fd4d5f36a | ||
|
3b41dde751 | ||
|
f656a69a90 | ||
|
33b9e9acac | ||
|
afe510735e | ||
|
12f0dc05d4 | ||
|
d3772587da | ||
|
8f53f2d213 | ||
|
1bd1424e33 | ||
|
bfbe169364 | ||
|
2f7007ac1b | ||
|
31bff0e414 | ||
|
7ad52695d2 | ||
|
f48071add9 | ||
|
72d86ea06d | ||
|
60f03a34af | ||
|
5b64c9701e | ||
|
bac908bff5 | ||
|
70f93b22eb | ||
|
1ab61cd3be | ||
|
c0bf2e98b7 | ||
|
83b8433b77 | ||
|
97ea228fb7 | ||
|
d407791aa9 | ||
|
a7994f9e85 | ||
|
42f01f7cd6 | ||
|
3f13609932 | ||
|
88c7214950 | ||
|
adf965fc9d | ||
|
bcfd08c750 | ||
|
dcc37f5751 | ||
|
591fc2fe35 | ||
|
5c162d2407 | ||
|
8152590946 | ||
|
6856da1bbf | ||
|
5c4b23cbee | ||
|
dec3106744 | ||
|
c4be540f49 | ||
|
ccef1b663f | ||
|
7686b21396 | ||
|
45e79d5583 | ||
|
364b39edfb | ||
|
e5ac21f0f9 | ||
|
1c7174e696 | ||
|
c25047ca66 | ||
|
5fc504c6c1 | ||
|
0b020a827b | ||
|
45784e9b95 | ||
|
4ad16be245 | ||
|
ae59cbf3c4 | ||
|
5362a635c8 | ||
|
9ef2b91e9e | ||
|
55d7344cc7 | ||
|
0faae3f5b4 | ||
|
51583ce5e0 | ||
|
7f650e787d | ||
|
10c3b46996 | ||
|
926eef2287 | ||
|
382a6a218b | ||
|
ffd96a8c2f | ||
|
07f807d680 | ||
|
cc9acf3ce4 | ||
|
47be777884 | ||
|
6e30cc4153 | ||
|
e44ad61af9 | ||
|
23700276a6 | ||
|
4891849e4a | ||
|
7e503df96f | ||
|
9323067e91 | ||
|
9ea0962e52 | ||
|
86e58efce8 | ||
|
cf0df92fb1 | ||
|
17f68399ef | ||
|
809bf51239 | ||
|
a9f406489d | ||
|
feba267ee7 | ||
|
14a37ca879 | ||
|
f83379287f | ||
|
d955fe00a1 | ||
|
1bbb2f1f1b | ||
|
e47073bc1d | ||
|
eae4ea7dcd | ||
|
e58d5ccaac | ||
|
57e663cf29 | ||
|
af50326e39 | ||
|
196cb47f05 | ||
|
741390e955 | ||
|
f8bc917601 | ||
|
3184b22e41 | ||
|
c1c102291c | ||
|
b843ba9ba9 | ||
|
1df8ad465d | ||
|
d5ec7bdacd | ||
|
579747fc43 | ||
|
8c94910440 | ||
|
00f8586be9 | ||
|
a1cc6db760 | ||
|
5392f4c2d4 |
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
*.pdf binary
|
||||
*.bin binary
|
||||
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
*.pyc
|
||||
*~
|
||||
src/build
|
||||
src/dist
|
7
README
7
README
|
@ -1,6 +1,7 @@
|
|||
Ophis is a cross-assembler for the 65xx series of chips. It supports
|
||||
the stock 6502 opcodes, the 65c02 extensions, and syntax for the
|
||||
"undocumented opcodes" in the 6510 chip used on the Commodore
|
||||
the stock 6502 opcodes, the 65c02 extensions, experimental support
|
||||
for the 4502/4510 used in the Commodore 65 prototypes, and syntax for
|
||||
the "undocumented opcodes" in the 6510 chip used on the Commodore
|
||||
64. (Syntax for these opcodes matches those given in the VICE team's
|
||||
documentation.)
|
||||
|
||||
|
@ -10,7 +11,7 @@ It is provided under the MIT license, reproduced below:
|
|||
|
||||
---
|
||||
|
||||
Ophis, Copyright (C) 2002-2010 Michael Martin and contributors
|
||||
Ophis, Copyright (C) 2002-2014 Michael Martin and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
10
bin/ophis
Executable file
10
bin/ophis
Executable file
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from os.path import realpath, dirname, join
|
||||
from sys import argv, exit, path
|
||||
|
||||
path.insert(0, join(dirname(realpath(argv[0])), '..', 'src'))
|
||||
|
||||
import Ophis.Main
|
||||
|
||||
exit(Ophis.Main.run_ophis(argv[1:]))
|
|
@ -1,40 +0,0 @@
|
|||
.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
|
|
@ -117,7 +117,7 @@
|
|||
<listitem><para><literal><$D000+32</literal> evaluates to $20</para></listitem>
|
||||
<listitem><para><literal>>$D000+32</literal> evaluates to $F0</para></listitem>
|
||||
<listitem><para><literal>>[$D000+32]</literal> evaluates to $D0</para></listitem>
|
||||
<listitem><para><literal>>$D000-275</literal> evaluates to $CE</para></listitem>
|
||||
<listitem><para><literal>>[$D000-275]</literal> evaluates to $CE</para></listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
<section>
|
||||
|
@ -361,118 +361,240 @@
|
|||
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>
|
||||
<listitem>
|
||||
<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.
|
||||
<literal>.outfile</literal> <emphasis>filename</emphasis>:
|
||||
Sets the filename for the output binary if one has not
|
||||
already been set. If no name is ever set, the output will
|
||||
be written to <literal>ophis.bin</literal>.
|
||||
</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>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>.advance</literal> <emphasis>address</emphasis>
|
||||
[, <emphasis>filler</emphasis>]: Forces the program
|
||||
counter to be <emphasis>address</emphasis>. Unlike
|
||||
the <literal>.org</literal>
|
||||
directive, <literal>.advance</literal> outputs bytes (the
|
||||
value of <emphasis>filler</emphasis>, or zeroes if it is
|
||||
unspecified) 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>.cbmfloat</literal> <emphasis>string</emphasis>
|
||||
[ , <emphasis>string</emphasis>, ... ]: Specifies a
|
||||
series of strings, which are interpreted as floating
|
||||
point constants, and then included in the 5-byte floating
|
||||
point format used by the Commodore BASICs. This format is
|
||||
8 bits of exponent, followed by a sign bit and a 31-bit
|
||||
big-endian mantissa fraction. (The 1 in front of the
|
||||
binary point is presumed to be present.) An exponent of 0
|
||||
specifies a constant of 0, and the exponent is shifted up
|
||||
by 129 before being stored.
|
||||
</para>
|
||||
<para>
|
||||
Because IEEE-754 doesn't perfectly match the Commodore's
|
||||
system, if you wish to precisely replicate individual
|
||||
constants that cannot be represented exactly you may have
|
||||
better luck with the following program, which will run on
|
||||
both the Commodore 64 and VIC-20:
|
||||
</para>
|
||||
<programlisting>
|
||||
10 CLR:V=0:PV=PEEK(45)+256*PEEK(46)+2
|
||||
20 INPUT "NUMBER (0 TO QUIT)";V
|
||||
30 IF V=0 THEN END
|
||||
40 PRINT ".BYTE";
|
||||
50 FOR I=0 TO 4
|
||||
60 IF I>0 THEN PRINT CHR$(157);",";
|
||||
70 PRINT PEEK(PV+I);:NEXT I:PRINT:GOTO 20
|
||||
</programlisting>
|
||||
<para>This program will print out
|
||||
a <literal>.byte</literal> directive for you to include in
|
||||
your program to represent that number.</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>
|
||||
[, <emphasis>offset</emphasis>
|
||||
[, <emphasis>length</emphasis>]]: Inserts the contents of
|
||||
the file specified as binary data. Use it to include
|
||||
graphics information, precompiled code, or other
|
||||
non-assembler data. You may also optionally specify an
|
||||
index to start including from, or a length to only
|
||||
include a subset.
|
||||
</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, if
|
||||
you aren't doing it via the command line.
|
||||
</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>
|
||||
</section>
|
||||
</appendix>
|
|
@ -1,29 +0,0 @@
|
|||
<!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>
|
|
@ -1,74 +0,0 @@
|
|||
<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—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>
|
|
@ -1,749 +0,0 @@
|
|||
<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>
|
185
doc/hll1.sgm
Normal file
185
doc/hll1.sgm
Normal file
|
@ -0,0 +1,185 @@
|
|||
<chapter id="hll-1">
|
||||
<title>The Second Step</title>
|
||||
|
||||
<para>
|
||||
This essay discusses how to do 16-or-more bit addition and
|
||||
subtraction on the 6502, and how to do unsigned comparisons
|
||||
properly, thus making 16-bit arithmetic less necessary.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>The problem</title>
|
||||
<para>
|
||||
The <literal>ADC</literal>, <literal>SBC</literal>, <literal>INX</literal>,
|
||||
and <literal>INY</literal> instructions are the only real
|
||||
arithmetic instructions the 6502 chip has. In and of themselves,
|
||||
they aren't too useful for general applications: the accumulator
|
||||
can only hold 8 bits, and thus can't store any value over 255.
|
||||
Matters get even worse when we're branching based on
|
||||
values; <literal>BMI</literal> and <literal>BPL</literal> hinge on
|
||||
the seventh (sign) bit of the result, so we can't represent any
|
||||
value above 127.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>The solution</title>
|
||||
|
||||
<para>
|
||||
We have two solutions available to us. First, we can use
|
||||
the <quote>unsigned</quote> discipline, which involves checking
|
||||
different flags, but lets us deal with values between 0 and 255
|
||||
instead of -128 to 127. Second, we can trade speed and register
|
||||
persistence for multiple precision arithmetic, using 16-bit
|
||||
integers (-32768 to 32767, or 0-65535), 24-bit, or more.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Multiplication, division, and floating point arithmetic are beyond
|
||||
the scope of this essay. The best way to deal with those is to
|
||||
find a math library on the web (I
|
||||
recommend <ulink url="http://www.6502.org/"></ulink>) and use the
|
||||
routines there.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Unsigned arithmetic</title>
|
||||
<para>
|
||||
When writing control code that hinges on numbers, we should always
|
||||
strive to have our comparison be with zero; that way, no explicit
|
||||
compare is necessary, and we can branch simply
|
||||
with <literal>BEQ/BNE</literal>, which test the zero flag.
|
||||
Otherwise, we use <literal>CMP</literal>.
|
||||
The <literal>CMP</literal> command subtracts its argument from the
|
||||
accumulator (without borrow), updates the flags, but throws away
|
||||
the result. If the value is equal, the result is zero.
|
||||
(<literal>CMP</literal> followed by <literal>BEQ</literal>
|
||||
branches if the argument is equal to the accumulator; this is
|
||||
probably why it's called <literal>BEQ</literal> and not something
|
||||
like <literal>BZS</literal>.)
|
||||
</para>
|
||||
<para>
|
||||
Intuitively, then, to check if the accumulator is <emphasis>less
|
||||
than</emphasis> some value, we <literal>CMP</literal> against that
|
||||
value and <literal>BMI</literal>. The <literal>BMI</literal>
|
||||
command branches based on the Negative Flag, which is equal to the
|
||||
seventh bit of <literal>CMP</literal>'s subtract. That's exactly
|
||||
what we need, for signed arithmetic. However, this produces
|
||||
problems if you're writing a boundary detector on your screen or
|
||||
something and find that 192 < 4. 192 is outside of a signed
|
||||
byte's range, and is interpreted as if it were -64. This will not
|
||||
do for most graphics applications, where your values will be
|
||||
ranging from 0-319 or 0-199 or 0-255.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Instead, we take advantage of the implied subtraction
|
||||
that <literal>CMP</literal> does. When subtracting, the result's
|
||||
carry bit starts at 1, and gets borrowed from if necessary. Let
|
||||
us consider some four-bit subtractions.
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
C|3210 C|3210
|
||||
------ ------
|
||||
1|1001 9 1|1001 9
|
||||
|0100 - 4 |1100 -12
|
||||
------ --- ------ ---
|
||||
1|0101 5 0|1101 -3
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
The <literal>CMP</literal> command properly modifies the carry bit
|
||||
to reflect this. When computing A-B, the carry bit is set if A
|
||||
>= B, and it's clear if A < B. Consider the following two
|
||||
code sequences.
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
(1) (2)
|
||||
CMP #$C0 CMP #$C0
|
||||
BMI label BCC label
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
The code in the first column treats the value in the accumulator
|
||||
as a signed value, and branches if the value is less than -64.
|
||||
(Because of overflow issues, it will actually branch for
|
||||
accumulator values between $40 and $BF, even though it *should*
|
||||
only be doing it for values between $80 and $BF. To see why,
|
||||
compare $40 to $C0 and look at the result.) The second column
|
||||
code treats the accumulator as holding an unsigned value, and
|
||||
branches if the value is less than 192. It will branch for
|
||||
accumulator values $00-$BF.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>16-bit addition and subtraction</title>
|
||||
|
||||
<para>
|
||||
Time to use the carry bit for what it was meant to do. Adding two
|
||||
8 bit numbers can produce a 9-bit result. That 9th bit is stored
|
||||
in the carry flag. The <literal>ADC</literal> command adds the
|
||||
carry value to its result, as well. Thus, carries work just as
|
||||
we'd expect them to. Suppose we're storing two 16-bit values, low
|
||||
byte first, in $C100-1 and $C102-3. To add them together and
|
||||
store them in $C104-5, this is very easy:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
CLC
|
||||
LDA $C100
|
||||
ADC $C102
|
||||
STA $C104
|
||||
LDA $C101
|
||||
ADC $C103
|
||||
STA $C105
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
Subtraction is identical, but you set the carry bit first
|
||||
with <literal>SEC</literal> (because borrow is the complement of
|
||||
carry—think about how the unsigned compare works if this
|
||||
puzzles you) and, of course, using the <literal>SBC</literal>
|
||||
instruction instead of <literal>ADC</literal>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The carry/borrow bit is set appropriately to let you continue,
|
||||
too. As long as you just keep working your way up to bytes of
|
||||
ever-higher significance, this generalizes to 24 (do it three
|
||||
times instead of two) or 32 (four, etc.) bit integers.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>16-bit comparisons</title>
|
||||
|
||||
<para>
|
||||
Doing comparisons on extended precision values is about the same
|
||||
as doing them on 8-bit values, but you have to have the value you
|
||||
test in memory, since it won't fit in the accumulator all at once.
|
||||
You don't have to store the values back anywhere, either, since
|
||||
all you care about is the final state of the flags. For example,
|
||||
here's a signed comparison, branching to <literal>label</literal>
|
||||
if the value in $C100-1 is less than 1000 ($03E8):
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
SEC
|
||||
LDA $C100
|
||||
SBC #$E8
|
||||
LDA $C101 ; We only need the carry bit from that subtract
|
||||
SBC #$03
|
||||
BMI label
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
All the commentary on signed and unsigned compares holds for
|
||||
16-bit (or higher) integers just as it does for the 8-bit
|
||||
ones.
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
880
doc/hll2.sgm
Normal file
880
doc/hll2.sgm
Normal file
|
@ -0,0 +1,880 @@
|
|||
<chapter id="hll2">
|
||||
<title>Structured Programming</title>
|
||||
|
||||
<para>
|
||||
This essay discusses the machine language equivalents of the
|
||||
basic <quote>structured programming</quote> concepts that are part
|
||||
of the <quote>imperative</quote> family of programming languages:
|
||||
if/then/else, for/next, while loops, and procedures. It also
|
||||
discusses basic use of variables, as well as arrays, multi-byte data
|
||||
types (records), and sub-byte data types (bitfields). It closes by
|
||||
hand-compiling pseudo-code for an insertion sort on linked lists
|
||||
into assembler. A complete Commodore 64 application is included as
|
||||
a sample with this essay.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>Control constructs</title>
|
||||
<section>
|
||||
<title>Branches: <literal>if x then y else z</literal></title>
|
||||
|
||||
<para>
|
||||
This is almost the most basic control construct.
|
||||
The <emphasis>most</emphasis> basic is <literal>if x then
|
||||
y</literal>, which is a simple branch instruction
|
||||
(bcc/bcs/beq/bmi/bne/bpl/bvc/bvs) past the <quote>then</quote>
|
||||
clause if the conditional is false:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
iny
|
||||
bne no'overflow
|
||||
inx
|
||||
no'overflow:
|
||||
;; rest of code
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
This increments the value of the y register, and if it just
|
||||
wrapped back around to zero, it increments the x register too.
|
||||
It is basically equivalent to the C statement <literal>if
|
||||
((++y)==0) ++x;</literal>. We need a few more labels to handle
|
||||
else clauses as well.
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
;; Computation of the conditional expression.
|
||||
;; We assume for the sake of the example that
|
||||
;; we want to execute the THEN clause if the
|
||||
;; zero bit is set, otherwise the ELSE
|
||||
;; clause. This will happen after a CMP,
|
||||
;; which is the most common kind of 'if'
|
||||
;; statement anyway.
|
||||
|
||||
BNE else'clause
|
||||
|
||||
;; THEN clause code goes here.
|
||||
|
||||
JMP end'of'if'stmt
|
||||
else'clause:
|
||||
|
||||
;; ELSE clause code goes here.
|
||||
|
||||
end'of'if'stmt:
|
||||
;; ... rest of code.
|
||||
</programlisting>
|
||||
</section>
|
||||
<section>
|
||||
<title>Free loops: <literal>while x do y</literal></title>
|
||||
|
||||
<para>
|
||||
A <emphasis>free loop</emphasis> is one that might execute any
|
||||
number of times. These are basically just a combination
|
||||
of <literal>if</literal> and <literal>goto</literal>. For
|
||||
a <quote>while x do y</quote> loop, that executes zero or more
|
||||
times, you'd have code like this...
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
loop'begin:
|
||||
;; ... computation of condition, setting zero
|
||||
;; bit if loop is finished...
|
||||
beq loop'done
|
||||
;; ... loop body goes here
|
||||
jmp loop'begin
|
||||
loop'done:
|
||||
;; ... rest of program.
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
If you want to ensure that the loop body executes at least once
|
||||
(do y while x), just move the test to the end.
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
loop'begin:
|
||||
;; ... loop body goes here
|
||||
;; ... computation of condition, setting zero
|
||||
;; bit if loop is finished...
|
||||
bne loop'begin
|
||||
;; ... rest of program.
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
The choice of zero bit is kind of arbitrary here. If the
|
||||
condition involves the carry bit, or overflow, or negative, then
|
||||
replace the beq with bcs/bvs/bmi appropriately.
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Bounded loops: <literal>for i = x to y do z</literal></title>
|
||||
|
||||
<para>
|
||||
A special case of loops is one where you know exactly how many
|
||||
times you're going through it—this is called
|
||||
a <emphasis>bounded</emphasis> loop. Suppose you're copying 16
|
||||
bytes from $C000 to $D000. The C code for that would look
|
||||
something like this:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
int *a = 0xC000;
|
||||
int *b = 0xD000;
|
||||
int i;
|
||||
for (i = 0; i < 16; i++) { a[i] = b[i]; }
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
C doesn't directly support bounded loops;
|
||||
its <literal>for</literal> statement is just <quote>syntactic
|
||||
sugar</quote> for a while statement. However, we can take
|
||||
advantage of special purpose machine instructions to get very
|
||||
straightforward code:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
ldx #$00
|
||||
loop:
|
||||
lda $c000, x
|
||||
sta $d000, x
|
||||
inx
|
||||
cpx #$10
|
||||
bmi loop
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
However, remember that every arithmetic operation,
|
||||
including <literal>inx</literal> and <literal>dex</literal>,
|
||||
sets the various flags, including the Zero bit. That means that
|
||||
if we can make our computation <emphasis>end</emphasis> when the
|
||||
counter hits zero, we can shave off some bytes:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
ldx #$10
|
||||
loop:
|
||||
lda #$bfff, x
|
||||
sta #$cfff, x
|
||||
dex
|
||||
bne loop
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
Notice that we had to change the addresses we're indexing from,
|
||||
because x takes a slightly different range of values. The space
|
||||
savings is small here, and it's become slightly more unclear.
|
||||
(It also hasn't actually saved any time, because the lda and sta
|
||||
instructions are crossing a page boundary where they weren't
|
||||
before—but if the start or end arrays began at $b020 or
|
||||
something this wouldn't be an issue.) This tends to work better
|
||||
when the precise value of the counter isn't used in the
|
||||
computation—so let us consider the NES, which uses memory
|
||||
location $2007 as a port to its video memory. Suppose we wish
|
||||
to jam 4,096 copies of the hex value $20 into the video memory.
|
||||
We can write this <emphasis>very</emphasis> cleanly, using the X
|
||||
and Y registers as indices in a nested loop.
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
ldx #$10
|
||||
ldy #$00
|
||||
lda #$20
|
||||
loop:
|
||||
sta $2007
|
||||
iny
|
||||
bne loop
|
||||
dex
|
||||
bne loop
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
Work through this code. Convince yourself that
|
||||
the <literal>sta</literal> is executed exactly 16*256 = 4096
|
||||
times.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This is an example of a <emphasis>nested</emphasis> loop: a loop
|
||||
inside a loop. Since our internal loop didn't need the X or Y
|
||||
registers, we got to use both of them, which is nice, because
|
||||
they have special incrementing and decrementing instructions.
|
||||
The accumulator lacks these instructions, so it is a poor choice
|
||||
to use for index variables. If you have a bounded loop and
|
||||
don't have access to registers, use memory locations
|
||||
instead:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
lda #$10
|
||||
sta counter ; loop 16 times
|
||||
loop:
|
||||
;; Do stuff that trashes all the registers
|
||||
dec counter
|
||||
bne loop
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
That's it! These are the basic control constructs for using
|
||||
inside of procedures. Before talking about how to organize
|
||||
procedures, I'll briefly cover the way the 6502 handles its
|
||||
stack, because stacks and procedures are very tightly
|
||||
intertwined.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>The stack</title>
|
||||
|
||||
<para>
|
||||
The 6502 has an onboard stack in page 1. You can modify the stack
|
||||
pointer by storing values in X register and
|
||||
using <literal>txs</literal>; an <quote>empty</quote> stack is
|
||||
value $FF. Going into a procedure pushes the address of the next
|
||||
instruction onto the stack, and RTS pops that value off and jumps
|
||||
there. (Well, not precisely. JSR actually pushes a value that's
|
||||
one instruction short, and RTS loads the value, increases it by
|
||||
one, and THEN jumps there. But that's only an issue if you're
|
||||
using RTS to implement jump tables.) On an interrupt, the next
|
||||
instruction's address is pushed on the stack, then the process
|
||||
flags, and it jumps to the handler. The return from interrupt
|
||||
restores the flags and the PC, just as if nothing had
|
||||
happened.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The stack only has 256 possible entries; since addresses take two
|
||||
bytes to store, that means that if you call something that calls
|
||||
something that calls something that (etc., etc., 129 times), your
|
||||
computation will fail. This can happen faster if you save
|
||||
registers or memory values on the stack (see below).
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Procedures and register saving</title>
|
||||
|
||||
<para>
|
||||
All programming languages are designed around the concept of
|
||||
procedures.<footnote><para>Yes, all of them. Functional languages
|
||||
just let you do more things with them, logic programming has
|
||||
implicit calls to query procedures, and
|
||||
object-oriented <quote>methods</quote> are just normal procedures
|
||||
that take one extra argument in secret.</para></footnote>
|
||||
Procedures let you break a computation up into different parts,
|
||||
then use them independently. However, compilers do a lot of work
|
||||
for you behind the scenes to let you think this. Consider the
|
||||
following assembler code. How many times does the loop
|
||||
execute?
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
loop: ldx #$10 jsr do'stuff dex bne loop
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
The correct answer is <quote>I don't know, but
|
||||
it <emphasis>should</emphasis> be 16.</quote> The reason we don't
|
||||
know is because we're assuming here that
|
||||
the <literal>do'stuff</literal> routine doesn't change the value
|
||||
of the X register. If it does, than all sorts of chaos could
|
||||
result. For major routines that aren't called often but are
|
||||
called in places where the register state is important, you should
|
||||
store the old registers on the stack with code like this:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
do'stuff:
|
||||
pha
|
||||
txa
|
||||
pha
|
||||
tya
|
||||
pha
|
||||
|
||||
;; Rest of do'stuff goes here
|
||||
|
||||
pla
|
||||
tay
|
||||
pla
|
||||
tax
|
||||
pla
|
||||
rts
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
(Remember, the last item pushed onto the stack is the first one
|
||||
pulled off, so you have to restore them in reverse order.) That's
|
||||
three more bytes on the stack, so you don't want to do this if you
|
||||
don't absolutely have to. If <literal>do'stuff</literal>
|
||||
actually <emphasis>doesn't</emphasis> touch X, there's no need to
|
||||
save and restore the value. This technique is
|
||||
called <emphasis>callee-save</emphasis>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The reverse technique is called <emphasis>caller-save</emphasis>
|
||||
and pushes important registers onto the stack before the routine
|
||||
is called, then restores them afterwards. Each technique has its
|
||||
advantages and disadvantages. The best way to handle it in your
|
||||
own code is to mark at the top of each routine which registers
|
||||
need to be saved by the caller. (It's also useful to note things
|
||||
like how it takes arguments and how it returns values.)
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Variables</title>
|
||||
|
||||
<para>
|
||||
Variables come in several flavors.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>Global variables</title>
|
||||
|
||||
<para>
|
||||
Global variables are variables that can be reached from any
|
||||
point in the program. Since the 6502 has no memory protection,
|
||||
these are easy to declare. Take some random chunk of unused
|
||||
memory and declare it to be the global variables area. All
|
||||
reasonable assemblers have commands that let you give a symbolic
|
||||
name to a memory location—you can use this to give your
|
||||
globals names.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Local variables</title>
|
||||
|
||||
<para>
|
||||
All modern languages have some concept of <quote>local
|
||||
variables</quote>, which are data values unique to that
|
||||
invocation of that procedure. In modern architecures, this data
|
||||
is stored into and read directly off of the stack. The 6502
|
||||
doesn't really let you do this cleanly; I'll discuss ways of
|
||||
handling it in a later essay. If you're implementing a system
|
||||
from scratch, you can design your memory model to not require
|
||||
such extreme measures. There are three basic techniques.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>Treat local variables like registers</title>
|
||||
<para>
|
||||
This means that any memory location you use, you save on the
|
||||
stack and restore afterwards. This
|
||||
can <emphasis>really</emphasis> eat up stack space, and it's
|
||||
really slow, it's often pointless, and it has a tendency to
|
||||
overflow the stack. I can't recommend it. But it does let
|
||||
you do recursion right, if you don't need to save much memory
|
||||
and you aren't recursing very deep.
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Procedure-based memory allocation</title>
|
||||
<para>
|
||||
With this technique, you give each procedure its own little
|
||||
chunk of memory for use with its data. All the variables are
|
||||
still, technically, globals; a
|
||||
routine <emphasis>could</emphasis> interfere with another's,
|
||||
but the discipline of <quote>only mess with real globals, and
|
||||
your own locals</quote> is very, very easy to maintain.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This has many advantages. It's <emphasis>very</emphasis>
|
||||
fast, both to write and to run, because loading a variable is
|
||||
an Absolute or Zero Page instruction. Also, any procedure may
|
||||
call any other procedure, as long as it doesn't wind up
|
||||
calling itself at some point.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
It has two major disadvantages. First, if many routines need
|
||||
a lot of space, it can consume more memory than it should.
|
||||
Also, this technique can require significant assembler
|
||||
support—you must ensure that no procedure's local
|
||||
variables are defined in the same place as any other
|
||||
procedure, and it essentially requires a full symbolic linker
|
||||
to do right. Ophis includes commands for <emphasis>memory
|
||||
segmentation simulation</emphasis> that automate most of this
|
||||
task, and make writing general libraries feasible.
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Partition-based memory allocation</title>
|
||||
|
||||
<para>
|
||||
It's not <emphasis>really</emphasis> necessary that no
|
||||
procedure overwrite memory used by any other procedure. It's
|
||||
only required that procedures don't write on the memory that
|
||||
their <emphasis>callers</emphasis> use. Suppose that your
|
||||
program is organized into a bunch of procedures, and each fall
|
||||
into one of three sets:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para>Procedures in set A don't call anyone.</para></listitem>
|
||||
<listitem><para>Procedures in set B only call procedures in set A.</para></listitem>
|
||||
<listitem><para>Procedures in set C only call procedures in sets A or B.</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
Now, each <emphasis>set</emphasis> can be given its own chunk
|
||||
of memory, and we can be absolutely sure that no procedures
|
||||
overwrite each other. Even if every procedure in set C uses
|
||||
the <emphasis>same</emphasis> memory location, they'll never
|
||||
step on each other, because there's no way to get to any other
|
||||
routine in set C <emphasis>from</emphasis> any routine in set
|
||||
C.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This has the same time efficiencies as procedure-based memory
|
||||
allocation, and, given a thoughtful design aimed at using this
|
||||
technique, also can use significantly less memory at run time.
|
||||
It's also requires much less assembler support, as addresses
|
||||
for variables may be assigned by hand without having to worry
|
||||
about those addresses already being used. However, it does
|
||||
impose a very tight discipline on the design of the overall
|
||||
system, so you'll have to do a lot more work before you start
|
||||
actually writing code.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
<section>
|
||||
<title>Constants</title>
|
||||
|
||||
<para>
|
||||
Constants are <quote>variables</quote> that don't change. If
|
||||
you know that the value you're using is not going to change, you
|
||||
should fold it into the code, either as an Immediate operand
|
||||
wherever it's used, or (if it's more complicated than that)
|
||||
as <literal>.byte</literal> commands in between the procedures.
|
||||
This is especially important for ROM-based systems such as the
|
||||
NES; the NES has very little RAM available, so constants should
|
||||
be kept in the more plentiful ROM wherever possible.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Data structures</title>
|
||||
|
||||
<para>
|
||||
So far, we've been treating data as a bunch of one-byte values.
|
||||
There really isn't a lot you can do just with bytes. This section
|
||||
talks about how to deal with larger and smaller elements.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>Arrays</title>
|
||||
|
||||
<para>
|
||||
An <emphasis>array</emphasis> is a bunch of data elements in a
|
||||
row. An array of bytes is very easy to handle with the 6502
|
||||
chip, because the various indexed addressing modes handle it for
|
||||
you. Just load the index into the X or Y register and do an
|
||||
absolute indexed load. In general, these are going to be
|
||||
zero-indexed (that is, a 32-byte array is indexed from 0 to 31.)
|
||||
This code would initialize a byte array with 32 entries to
|
||||
0:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
lda #$00
|
||||
tax
|
||||
loop:
|
||||
sta array,x
|
||||
inx
|
||||
cpx #$20
|
||||
bne loop
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
(If you count down to save instructions, remember to adjust the
|
||||
base address so that it's still writing the same memory
|
||||
location.)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This approach to arrays has some limits. Primary among them is
|
||||
that we can't have arrays of size larger than 256; we can't fit
|
||||
our index into the index register. In order to address larger
|
||||
arrays, we need to use the indirect indexed addressing mode. We
|
||||
use 16-bit addition to add the offset to the base pointer, then
|
||||
set the Y register to 0 and then load the value
|
||||
with <literal>lda (ptr),y</literal>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Well, actually, we can do better than that. Suppose we want to
|
||||
clear out 8K of ram, from $2000 to $4000. We can use the Y
|
||||
register to hold the low byte of our offset, and only update the
|
||||
high bit when necessary. That produces the following
|
||||
loop:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
lda #$00 ; Set pointer value to base ($2000)
|
||||
sta ptr
|
||||
lda #$20
|
||||
sta ptr+1
|
||||
lda #$00 ; Storing a zero
|
||||
ldx #$20 ; 8,192 ($2000) iterations: high byte
|
||||
ldy #$00 ; low byte.
|
||||
loop:
|
||||
sta (ptr),y
|
||||
iny
|
||||
bne loop ; If we haven't wrapped around, go back
|
||||
inc ptr+1 ; Otherwise update high byte
|
||||
dex ; bump counter
|
||||
bne loop ; and continue if we aren't done
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
This code could be optimized further; the loop prelude in
|
||||
particular loads a lot of redundant values that could be
|
||||
compressed down further:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
lda #$00
|
||||
tay
|
||||
ldx #$20
|
||||
sta ptr
|
||||
stx ptr+1
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
That's not directly relevant to arrays, but these sorts of
|
||||
things are good things to keep in mind when writing your code.
|
||||
Done well, they can make it much smaller and faster; done
|
||||
carelessly, they can force a lot of bizarre dependencies on your
|
||||
code and make it impossible to modify later.
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Records</title>
|
||||
<para>
|
||||
A <emphasis>record</emphasis> is a collection of values all
|
||||
referred to as one variable. This has no immediate
|
||||
representation in assembler. If you have a global variable
|
||||
that's two bytes and a code pointer, this is exactly equivalent
|
||||
to three seperate variables. You can just put one label in
|
||||
front of it, and refer to the first byte
|
||||
as <literal>label</literal>, the second
|
||||
as <literal>label+1</literal>, and the code pointer
|
||||
a <literal>label+2</literal>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This really applies to all data structures that take up more
|
||||
than one byte. When dealing with the pointer, a 16-bit value,
|
||||
we refer to the low byte as <literal>ptr</literal>
|
||||
(or <literal>label+2</literal>, in the example above), and the
|
||||
high byte as <literal>ptr+1</literal>
|
||||
(or <literal>label+3</literal>).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Arrays of records are more interesting. There are two
|
||||
possibilities for these. The way most high level languages
|
||||
treat it is by keeping the records contiguous. If you have an
|
||||
array of two sixteen bit integers, then the records are stored
|
||||
in order, one at a time. The first is in location $1000, the
|
||||
next in $1004, the next in $1008, and so on. You can do this
|
||||
with the 6502, but you'll probably have to use the indirect
|
||||
indexed mode if you want to be able to iterate
|
||||
conveniently.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Another, more unusual, but more efficient approach is to keep
|
||||
each byte as a seperate array, just like in the arrays example
|
||||
above. To illustrate, here's a little bit of code to go through
|
||||
a contiguous array of 16 bit integers, adding their values to
|
||||
some <literal>total</literal> variable:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
ldx #$10 ; Number of elements in the array
|
||||
ldy #$00 ; Byte index from array start
|
||||
loop:
|
||||
clc
|
||||
lda array, y ; Low byte
|
||||
adc total
|
||||
sta total
|
||||
lda array+1, y ; High byte
|
||||
adc total+1
|
||||
sta total+1
|
||||
iny ; Jump ahead to next entry
|
||||
iny
|
||||
dex ; Check for loop termination
|
||||
bne loop
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
And here's the same loop, keeping the high and low bytes in
|
||||
seperate arrays:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
ldx #$00
|
||||
loop:
|
||||
clc
|
||||
lda lowbyte,x
|
||||
adc total
|
||||
sta total
|
||||
lda highbyte,x
|
||||
adc total+1
|
||||
sta total+1
|
||||
inx
|
||||
cpx #$10
|
||||
bne loop
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
Which approach is the right one depends on what you're doing.
|
||||
For large arrays, the first approach is better, as you only need
|
||||
to maintain one base pointer. For smaller arrays, the easier
|
||||
indexing makes the second approach more convenient.
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Bitfields</title>
|
||||
|
||||
<para>
|
||||
To store values that are smaller than a byte, you can save space
|
||||
by putting multiple values in a byte. To extract a sub-byte
|
||||
value, use the bitmasking commands:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para>To set bits, use the <literal>ORA</literal> command. <literal>ORA #$0F</literal> sets the lower four bits to 1 and leaves the rest unchanged.</para></listitem>
|
||||
<listitem><para>To clear bits, use the <literal>AND</literal> command. <literal>AND #$F0</literal> sets the lower four bits to 0 and leaves the rest unchanged.</para></listitem>
|
||||
<listitem><para>To reverse bits, use the <literal>EOR</literal> command. <literal>EOR #$0F</literal> reverses the lower four bits and leaves the rest unchanged.</para></listitem>
|
||||
<listitem><para>To test if a bit is 0, AND away everything but that bit, then see if the Zero bit was set. If the bit is in the top two bits of a memory location, you can use the BIT command instead (which stores bit 7 in the Negative bit, and bit 6 in the Overflow bit).</para></listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>A modest example: Insertion sort on linked lists</title>
|
||||
|
||||
<para>
|
||||
To demonstrate these techniques, we will now produce code to
|
||||
perform insertion sort on a linked list. We'll start by defining
|
||||
our data structure, then defining the routines we want to write,
|
||||
then producing actual code for those routines. A downloadable
|
||||
version that will run unmodified on a Commodore 64 closes the
|
||||
chapter.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>The data structure</title>
|
||||
|
||||
<para>
|
||||
We don't really want to have to deal with pointers if we can
|
||||
possibly avoid it, but it's hard to do a linked list without
|
||||
them. Instead of pointers, we will
|
||||
use <emphasis>cursors</emphasis>: small integers that represent
|
||||
the index into the array of values. This lets us use the
|
||||
many-small-byte-arrays technique for our data. Furthermore, our
|
||||
random data that we're sorting never has to move, so we may
|
||||
declare it as a constant and only bother with changing the
|
||||
values of <literal>head</literal> and
|
||||
the <literal>next</literal> arrays. The data record definition
|
||||
looks like this:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
head : byte;
|
||||
data : const int[16] = [838, 618, 205, 984, 724, 301, 249, 946,
|
||||
925, 43, 114, 697, 985, 633, 312, 86];
|
||||
next : byte[16];
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
Exactly how this gets represented will vary from assembler to
|
||||
assembler. Ophis does it like this:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
.data
|
||||
.space head 1
|
||||
.space next 16
|
||||
|
||||
.text
|
||||
lb: .byte <$838,<$618,<$205,<$984,<$724,<$301,<$249,<$946
|
||||
.byte <$925,<$043,<$114,<$697,<$985,<$633,<$312,<$086
|
||||
hb: .byte >$838,>$618,>$205,>$984,>$724,>$301,>$249,>$946
|
||||
.byte >$925,>$043,>$114,>$697,>$985,>$633,>$312,>$086
|
||||
</programlisting>
|
||||
</section>
|
||||
<section>
|
||||
<title>Doing an insertion sort</title>
|
||||
|
||||
<para>
|
||||
To do an insertion sort, we clear the list by setting the 'head'
|
||||
value to -1, and then insert each element into the list one at a
|
||||
time, placing each element in its proper order in the list. We
|
||||
can consider the lb/hb structure alone as an array of 16
|
||||
integers, and just insert each one into the list one at a
|
||||
time.
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
procedure insertion_sort
|
||||
head := -1;
|
||||
for i := 0 to 15 do
|
||||
insert_elt i
|
||||
end
|
||||
end
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
This translates pretty directly. We'll have insert_elt take its
|
||||
argument in the X register, and loop with that. However, given
|
||||
that insert_elt is going to be a complex procedure, we'll save
|
||||
the value first. The assembler code becomes:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; insertion'sort: Sorts the list defined by head, next, hb, lb.
|
||||
; Arguments: None.
|
||||
; Modifies: All registers destroyed, head and next array sorted.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
insertion'sort:
|
||||
lda #$FF ; Clear list by storing the terminator in 'head'
|
||||
sta head
|
||||
ldx #$0 ; Loop through the lb/hb array, adding each
|
||||
insertion'sort'loop: ; element one at a time
|
||||
txa
|
||||
pha
|
||||
jsr insert_elt
|
||||
pla
|
||||
tax
|
||||
inx
|
||||
cpx #$10
|
||||
bne insertion'sort'loop
|
||||
rts
|
||||
</programlisting>
|
||||
</section>
|
||||
<section>
|
||||
<title>Inserting an element</title>
|
||||
|
||||
<para>
|
||||
The pseudocode for inserting an element is a bit more
|
||||
complicated. If the list is empty, or the value we're inserting
|
||||
goes at the front, then we have to update the value
|
||||
of <literal>head</literal>. Otherwise, we can iterate through
|
||||
the list until we find the element that our value fits in after
|
||||
(so, the first element whose successor is larger than our
|
||||
value). Then we update the next pointers directly and exit.
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
procedure insert_elt i
|
||||
begin
|
||||
if head = -1 then begin
|
||||
head := i;
|
||||
next[i] := -1;
|
||||
return;
|
||||
end;
|
||||
val := data[i];
|
||||
if val < data[i] then begin
|
||||
next[i] := head;
|
||||
head := i;
|
||||
return;
|
||||
end;
|
||||
current := head;
|
||||
while (next[current] <> -1 and val < data[next[current]]) do
|
||||
current := next[current];
|
||||
end;
|
||||
next[i] := next[current];
|
||||
next[current] := i;
|
||||
end;
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
This produces the following rather hefty chunk of code:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; insert_elt: Insert an element into the linked list. Maintains the
|
||||
; list in sorted, ascending order. Used by
|
||||
; insertion'sort.
|
||||
; Arguments: X register holds the index of the element to add.
|
||||
; Modifies: All registers destroyed; head and next arrays updated
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
.data
|
||||
.space lbtoinsert 1
|
||||
.space hbtoinsert 1
|
||||
.space indextoinsert 1
|
||||
|
||||
.text
|
||||
|
||||
insert_elt:
|
||||
ldy head ; If the list is empty, make
|
||||
cpy #$FF ; head point at it, and return.
|
||||
bne insert_elt'list'not'empty
|
||||
stx head
|
||||
tya
|
||||
sta next,x
|
||||
rts
|
||||
insert_elt'list'not'empty:
|
||||
lda lb,x ; Cache the data we're inserting
|
||||
sta lbtoinsert
|
||||
lda hb,x
|
||||
sta hbtoinsert
|
||||
stx indextoinsert
|
||||
ldy head ; Compare the first value with
|
||||
sec ; the data. If the data must
|
||||
lda lb,y ; be inserted at the front...
|
||||
sbc lbtoinsert
|
||||
lda hb,y
|
||||
sbc hbtoinsert
|
||||
bmi insert_elt'not'smallest
|
||||
tya ; Set its next pointer to the
|
||||
sta next,x ; old head, update the head
|
||||
stx head ; pointer, and return.
|
||||
rts
|
||||
insert_elt'not'smallest:
|
||||
ldx head
|
||||
insert_elt'loop: ; At this point, we know that
|
||||
lda next,x ; argument > data[X].
|
||||
tay
|
||||
cpy #$FF ; if next[X] = #$FF, insert arg at end.
|
||||
beq insert_elt'insert'after'current
|
||||
lda lb,y ; Otherwise, compare arg to
|
||||
sec ; data[next[X]]. If we insert
|
||||
sbc lbtoinsert ; before that...
|
||||
lda hb,y
|
||||
sbc hbtoinsert
|
||||
bmi insert_elt'goto'next
|
||||
insert_elt'insert'after'current: ; Fix up all the next links
|
||||
tya
|
||||
ldy indextoinsert
|
||||
sta next,y
|
||||
tya
|
||||
sta next,x
|
||||
rts ; and return.
|
||||
insert_elt'goto'next: ; Otherwise, let X = next[X]
|
||||
tya ; and go looping again.
|
||||
tax
|
||||
jmp insert_elt'loop
|
||||
</programlisting>
|
||||
</section>
|
||||
<section>
|
||||
<title>The complete application</title>
|
||||
|
||||
<para>
|
||||
The full application, which deals with interfacing with CBM
|
||||
BASIC and handles console I/O and such, is
|
||||
in <xref linkend="structure-src" endterm="structure-fname">.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
</chapter>
|
297
doc/hll3.sgm
Normal file
297
doc/hll3.sgm
Normal file
|
@ -0,0 +1,297 @@
|
|||
<chapter id="hll3">
|
||||
<title>Pointers and Indirection</title>
|
||||
|
||||
<para>
|
||||
The basics of pointers versus cursors (or, at the 6502 assembler
|
||||
level, the indirect indexed addressing mode versus the absolute
|
||||
indexed ones) were covered in <xref linkend="hll2"> This essay seeks
|
||||
to explain the uses of the indirect modes, and how to implement
|
||||
pointer operations with them. It does <emphasis>not</emphasis> seek to explain
|
||||
why you'd want to use pointers for something to begin with; for a
|
||||
tutorial on proper pointer usage, consult any decent C textbook.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>The absolute basics</title>
|
||||
|
||||
<para>
|
||||
A pointer is a variable holding the address of a memory location.
|
||||
Memory locations take 16 bits to represent on the 6502: thus, we
|
||||
need two bytes to hold it. Any decent assembler will have ways of
|
||||
taking the high and low bytes of an address; use these to acquire
|
||||
the raw values you need. The 6502 chip does not have any
|
||||
simple <quote>pure</quote> indirect modes (except
|
||||
for <literal>JMP</literal>, which is a matter for a later essay);
|
||||
all are indexed, and they're indexed different ways depending on
|
||||
which index register you use.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>The simplest example</title>
|
||||
|
||||
<para>
|
||||
When doing a simple, direct dereference (that is, something
|
||||
equivalent to the C code <literal>c=*b;</literal>) the code
|
||||
looks like this:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
ldy #0
|
||||
lda (b), y
|
||||
sta c
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
Even with this simple example, there are several important
|
||||
things to notice.
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
The variable <literal>b</literal> <emphasis>must be on the
|
||||
zero page</emphasis>, and furthermore, it <emphasis>cannot
|
||||
be $FF.</emphasis> All your pointer values need to be
|
||||
either stored on the zero page to begin with or copied
|
||||
there before use.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
The <literal>y</literal> in the <literal>lda</literal>
|
||||
statement must be y. It cannot be x (that's a different
|
||||
form of indirection), and it cannot be a constant. If
|
||||
you're doing a lot of indirection, be sure to keep your Y
|
||||
register free to handle the indexing on the
|
||||
pointers.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
The <literal>b</literal> variable is used alone. Statements
|
||||
like <literal>lda (b+2), y</literal> are syntactically valid
|
||||
and sometimes even correct: it dereferences the value next
|
||||
to <literal>b</literal> after adding y to the value therein.
|
||||
However, it is almost guaranteed that what you *really*
|
||||
wanted to do was compute <literal>*(b+2)</literal> (that is,
|
||||
take the address of b, add 2 to <emphasis>that</emphasis>,
|
||||
and dereference that value); see the next section for how to
|
||||
do this properly.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
In nearly all cases, it is the Y-register's version (Indirect
|
||||
Indexed) that you want to use when you're dealing with pointers.
|
||||
Even though either version could be used for this example, we
|
||||
use the Y register to establish this habit.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
<section>
|
||||
<title>Pointer arithmetic</title>
|
||||
|
||||
<para>
|
||||
Pointer arithmetic is an obscenely powerful and dangerous
|
||||
technique. However, it's the most straightforward way to deal
|
||||
with enormous arrays, structs, indexable stacks, and nearly
|
||||
everything you do in C. (C has no native array or string types
|
||||
primarily because it allows arbitrary pointer arithmetic, which is
|
||||
strong enough to handle all of those without complaint and at
|
||||
blazing speed. It also allows for all kinds of buffer overrun
|
||||
security holes, but let's face it, who's going to be cracking root
|
||||
on your Apple II?) There are a number of ways to implement this
|
||||
on the 6502. We'll deal with them in increasing order of design
|
||||
complexity.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>The straightforward, slow way</title>
|
||||
|
||||
<para>
|
||||
When computing a pointer value, you simply treat the pointer as
|
||||
if it were a 16-bit integer. Do all the math you need, then
|
||||
when the time comes to dereference it, simply do a direct
|
||||
dereference as above. This is definitely doable, and it's not
|
||||
difficult. However, it is costly in both space and time.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When dealing with arbitrary indices large enough that they won't
|
||||
fit in the Y register, or when creating values that you don't
|
||||
intend to dereference (such as subtracting two pointers to find
|
||||
the length of a string), this is also the only truly usable
|
||||
technique.
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>The clever fast way</title>
|
||||
|
||||
<para>
|
||||
But wait, you say. Often when we compute a value, at least one
|
||||
of the operations is going to be an addition, and we're almost
|
||||
certain to have that value be less than 256! Surely we may save
|
||||
ourselves an operation by loading that value into the Y register
|
||||
and having the load operation itself perform the final
|
||||
addition!
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Very good. This is the fastest technique, and sometimes it's
|
||||
even the most readable. These cases usually involve repeated
|
||||
reading of various fields from a structure or record. The base
|
||||
pointer always points to the base of the structure (or the top
|
||||
of the local variable list, or what have you) and the Y register
|
||||
takes values that index into that structure. This lets you keep
|
||||
the pointer variable in memory largely static and requires no
|
||||
explicit arithmetic instructions at all.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
However, this technique is highly opaque and should always be
|
||||
well documented, indicating exactly what you think you're
|
||||
pointing at. Then, when you get garbage results, you can
|
||||
compare your comments and the resulting Y values with the actual
|
||||
definition of the structure to see who's screwing up.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For a case where we still need to do arithmetic, consider the
|
||||
classic case of needing to clear out a large chunk of memory.
|
||||
The following code fills the 4KB of memory between $C000 and
|
||||
$D000 with zeroes:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
lda #$C0 ; Store #$C000 in mem (low byte first)
|
||||
sta mem+1
|
||||
lda #$00
|
||||
sta mem
|
||||
ldx #$04 ; x holds number of times to execute outer loop
|
||||
tay ; accumulator and y are both 0
|
||||
loop: sta (mem), y
|
||||
iny
|
||||
bne loop ; Inner loop ends when y wraps around to 0
|
||||
inc mem+1 ; "Carry" from the iny to the core pointer
|
||||
dex ; Decrement outer loop count, quit if done
|
||||
bne loop
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
Used carefully, proper use of the Y register can make your code
|
||||
smaller, faster, <emphasis>and</emphasis> more readable. Used
|
||||
carelessly it can make your code an unreadable, unmaintainable
|
||||
mess. Use it wisely, and with care, and it will be your
|
||||
greatest ally in writing flexible code.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
<section>
|
||||
<title>What about Indexed Indirect?</title>
|
||||
|
||||
<para>
|
||||
This essay has concerned itself almost exclusively with the
|
||||
Indirect Indexed—or (Indirect), Y—mode. What about Indexed
|
||||
Indirect—(Indirect, X)? This is a <emphasis>much</emphasis>
|
||||
less useful mode than the Y register's version. While the Y
|
||||
register indirection lets you implement pointers and arrays in
|
||||
full generality, the X register is useful for pretty much only one
|
||||
application: lookup tables for single byte values.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Even coming up with a motivating example for this is difficult,
|
||||
but here goes. Suppose you have multiple, widely disparate
|
||||
sections of memory that you're watching for signals. The
|
||||
following routine takes a resource index in the accumulator and
|
||||
returns the status byte for the corresponding resource.
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
; This data is sitting on the zero page somewhere
|
||||
resource_status_table: .word resource0_status, resource1_status,
|
||||
.word resource2_status, resource3_status,
|
||||
; etc. etc. etc.
|
||||
|
||||
; This is the actual program code
|
||||
.text
|
||||
getstatus:
|
||||
clc ; Multiply argument by 2 before putting it in X, so that it
|
||||
asl ; produces a value that's properly word-indexed
|
||||
tax
|
||||
lda (resource_status_table, x)
|
||||
rts
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
Why having a routine such as this is better than just having the
|
||||
calling routine access resourceN_status itself as an absolute
|
||||
memory load is left as an exercise for the reader. That aside,
|
||||
this code fragment does serve as a reminder that when indexing an
|
||||
array of anything other than bytes, you must multiply your index
|
||||
by the size of the objects you want to index. C does this
|
||||
automatically—assembler does not. Stay sharp.
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Comparison with the other indexed forms</title>
|
||||
|
||||
<para>
|
||||
Pointers are slow. It sounds odd saying this, when C is the
|
||||
fastest language around on modern machines precisely because of
|
||||
its powerful and extensive use of pointers. However, modern
|
||||
architectures are designed to be optimized for C-style code (as an
|
||||
example, the x86 architecture allows statements like <literal>mov
|
||||
eax, [bs+bx+4*di]</literal> as a single instruction), while the
|
||||
6502 is not. An (Indirect, Y) operation can take up to 6 cycles
|
||||
to complete just on its own, while the preparation of that command
|
||||
costs additional time <emphasis>and</emphasis> scribbles over a
|
||||
bunch of registers, meaning memory operations to save the values
|
||||
and yet more time spent. The simple code given at the beginning
|
||||
of this essay—loading <literal>*b</literal> into the
|
||||
accumulator—takes 7 cycles, not counting the 6 it takes to
|
||||
load b with the appropriate value to begin with. If b is known to
|
||||
contain a specific value, we can write a single Absolute mode
|
||||
instruction to load its value, which takes only 4 cycles and also
|
||||
preserves the value in the Y register. Clearly, Absolute mode
|
||||
should be used whenever possible.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
One might be tempted to use self-modifying code to solve this
|
||||
problem. This actually doesn't pay off near enough for the hassle
|
||||
it generates; for self-modifying code, the address must be
|
||||
generated, then stored in the instruction, and then the data must
|
||||
be loaded. Cost: 16 cycles for 2 immediate loads, 2 absolute
|
||||
stores, and 1 absolute load. For the straight pointer
|
||||
dereference, we generate the address, store it in the pointer,
|
||||
clear the index, then dereference that. Cost: 17 cycles for 3
|
||||
immediate loads, 2 zero page stores, and 1 indexed indirect load.
|
||||
Furthermore, unlike in the self-modifying case, loops where simple
|
||||
arithmetic is being continuously performed only require repeating
|
||||
the final load instruction, which allows for much greater time
|
||||
savings over an equivalent self-modifying loop.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
(This point is also completely moot for NES programmers or anyone
|
||||
else whose programs are sitting in ROM, because programs stored on
|
||||
a ROM cannot modify themselves.)
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Conclusion</title>
|
||||
|
||||
<para>
|
||||
That's pretty much it for pointers. Though they tend to make
|
||||
programs hairy, and learning how to properly deal with pointers is
|
||||
what separates real C programmers from the novices, the basic
|
||||
mechanics of them are not complex. With pointers you can do
|
||||
efficient passing of large structures, pass-by-reference,
|
||||
complicated return values, and dynamic memory management—and
|
||||
now these wondrous toys may be added to your assembler programs,
|
||||
too (assuming you have that kind of space to play with).
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
270
doc/hll4.sgm
Normal file
270
doc/hll4.sgm
Normal file
|
@ -0,0 +1,270 @@
|
|||
<chapter>
|
||||
<title>Functionals</title>
|
||||
|
||||
<para>
|
||||
This essay deals with indirect calls. These are the core of an
|
||||
enormous number of high level languages: LISP's closures, C's
|
||||
function pointers, C++ and Java's virtual method calls, and some
|
||||
implementations of the <literal>switch</literal> statement.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
These techniques vary in complexity, and most will not be
|
||||
appropriate for large-scale assembler projects. Of them, however,
|
||||
the Data-Directed approach is the most likely to lead to organized
|
||||
and maintainable code.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>Function Pointers</title>
|
||||
|
||||
<para>
|
||||
Because assembly language is totally untyped, function pointers
|
||||
are the same as any other sixteen-bit integer. This makes
|
||||
representing them really quite easy; most assemblers should permit
|
||||
routines to be declared simply by naming the routine as
|
||||
a <literal>.word</literal> directly.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To actually invoke these methods, copy them to some sixteen-bit
|
||||
location (say, <literal>target</literal>) and then invoking the
|
||||
method is a simple matter of the using an indirect jump:
|
||||
the <literal>JMP (target)</literal> instruction.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
There's really only one subtlety here, and it's that the indirect
|
||||
jump is an indirect <emphasis>jump</emphasis>, not an
|
||||
indirect <emphasis>function call</emphasis>. Thus, if some
|
||||
function <literal>A</literal> makes in indirect jump to some
|
||||
routine, when that routine returns, it returns to whoever
|
||||
called <literal>A</literal>, not <literal>A</literal>
|
||||
itself.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
There are several ways of dealing with this, but only one correct
|
||||
way, which is to structure your procedures so that any call
|
||||
to <literal>JMP (xxxx)</literal> occurs at the very
|
||||
end.
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>A quick digression on how subroutines work</title>
|
||||
<para>
|
||||
Ordinarily, subroutines are called with <literal>JSR</literal> and
|
||||
finished with <literal>RTS</literal>. The <literal>JSR</literal>
|
||||
instruction takes its own address, adds 2 to it, and pushes this
|
||||
16-bit value on the stack, high byte first, then low byte (so that
|
||||
the low byte will be popped off first).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
But wait, you may object. All <literal>JSR</literal> instructions
|
||||
are three bytes long. This <quote>return address</quote> is in
|
||||
the middle of the instruction. And you would be quite right;
|
||||
the <literal>RTS</literal> instruction pops off the 16-bit
|
||||
address, adds one to it, and <emphasis>then</emphasis> sets the
|
||||
program counter to that value.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
So it <emphasis>is</emphasis> possible to set up
|
||||
a <quote><literal>JSR</literal> indirect</quote> kind of operation
|
||||
by adding two to the indirect jump's address and then pushing that
|
||||
value onto the stack before making the jump; however, you wouldn't
|
||||
want to do this. It takes six bytes and trashes your accumulator,
|
||||
and you can get the same functionality with half the space and
|
||||
with no register corruption by simply defining the indirect jump
|
||||
to be a one-instruction routine and <literal>JSR</literal>-ing to
|
||||
it directly. As an added bonus, that way if you have multiple
|
||||
indirect jumps through the same pointer, you don't need to
|
||||
duplicate the jump instruction.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Does this mean that abusing <literal>JSR</literal>
|
||||
and <literal>RTS</literal> is a dead-end, though? Not at all...
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Dispatch-on-type and Data-Directed Assembler</title>
|
||||
|
||||
<para>
|
||||
Most of the time, you care about function pointers because you've
|
||||
arranged them in some kind of table. You hand it an index
|
||||
representing the type of your argument, or which method it is
|
||||
you're calling, or some other determinator, and then you index
|
||||
into an array of routines and execute the right one.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Writing a generic routine to do this is kind of a pain. First you
|
||||
have to pass a 16-bit pointer in, then you have to dereference it
|
||||
to figure out where your table is, then you have to do an indexed
|
||||
dereference on <emphasis>that</emphasis> to get the routine you
|
||||
want to run, then you need to copy it out to somewhere fixed so
|
||||
that you can write your jump instruction. And making this
|
||||
non-generic doesn't help a whole lot, since that only saves you
|
||||
the first two steps, but now you have to write them out in every
|
||||
single indexed jump instruction. If only there were some way to
|
||||
easily and quickly pass in a local pointer directly...
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Something, say, like the <literal>JSR</literal> instruction, only not for
|
||||
program code.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Or we could just use the <literal>JSR</literal> statement itself,
|
||||
but only call this routine at the ends of other routines, much
|
||||
like we were organizing for indirect jumps to begin with. This
|
||||
lets us set up routines that look like this:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
jump'table'alpha:
|
||||
jsr do'jump'table
|
||||
.word alpha'0, alpha'1, alpha'2
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
Where the <literal>alpha'x</literal> routines are the ones to be
|
||||
called when the index has that value. This leaves the
|
||||
implementation of do'jump'table, which in this case uses the Y
|
||||
register to hold the index:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
do'jump'table:
|
||||
sta _scratch
|
||||
pla
|
||||
sta _jmpptr
|
||||
pla
|
||||
sta _jmpptr+1
|
||||
tya
|
||||
asl
|
||||
tay
|
||||
iny
|
||||
lda (_jmpptr), y
|
||||
sta _target
|
||||
iny
|
||||
lda (_jmpptr), y
|
||||
sta _target+1
|
||||
lda _scratch
|
||||
jmp (_target)
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
The <literal>TYA:ASL:TAY:INY</literal> sequence can actually be
|
||||
omitted if you don't mind having your Y indices be 1, 3, 5, 7, 9,
|
||||
etc., instead of 0, 1, 2, 3, 4, etc. Likewise, the instructions
|
||||
dealing with <literal>_scratch</literal> can be omitted if you
|
||||
don't mind trashing the accumulator. Keeping the accumulator and
|
||||
X register pristine for the target call comes in handy, though,
|
||||
because it means we can pass in a pointer argument purely in
|
||||
registers. This will come in handy soon...
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>VTables and Object-Oriented Assembler</title>
|
||||
|
||||
<para>
|
||||
The usual technique for getting something that looks
|
||||
object-oriented in non-object-oriented languages is to fill a
|
||||
structure with function pointers, and have those functions take
|
||||
the structure itself as an argument. This works just fine in
|
||||
assembler, of course (and doesn't really require anything more
|
||||
than your traditional jump-indirects), but it's also possible to
|
||||
use a lot of the standard optimizations that languages such as C++
|
||||
provide.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The most important of these is the <emphasis>vtable</emphasis>.
|
||||
Each object type has its own vtable, and it's a list of function
|
||||
pointers for all the methods that type provides. This is a space
|
||||
savings over the traditional structs-with-function-pointers
|
||||
approach because when you have many objects of the same class, you
|
||||
only have to represent the vtable once. So that all objects may
|
||||
be treated identically, the vtable location is traditionally fixed
|
||||
as being the first entry in the corresponding structure.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Virtual method invocation takes an object pointer (traditionally
|
||||
called <literal>self</literal> or <literal>this</literal>) and a
|
||||
method index and invokes the approprate method on that object.
|
||||
Gee, where have we seen that before?
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
sprite'vtable:
|
||||
jsr do'jump'table
|
||||
.word sprite'init, sprite'update, sprite'render
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
We mentioned before that vtables are generally the first entries
|
||||
in objects. We can play another nasty trick here, paying an
|
||||
additional byte per object to have the vtable be not merely a
|
||||
pointer to its vtable routine, but an actual jump instruction to
|
||||
it. (That is, if an object is at location X, then location X is
|
||||
the byte value <literal>$4C</literal>,
|
||||
representing <literal>JMP</literal>, location X+1 is the low byte
|
||||
of the vtable, and location X+2 is the high byte of the vtable.)
|
||||
Given that, our <literal>invokevirtual</literal> function becomes
|
||||
very simple indeed:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
invokevirtual:
|
||||
sta this
|
||||
stx this+1
|
||||
jmp (this)
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
Which, combined with all our previous work here, takes
|
||||
the <literal>this</literal> pointer in <literal>.AX</literal> and
|
||||
a method identifier in <literal>.Y</literal> and invokes that
|
||||
method on that object. Arguments besides <literal>this</literal>
|
||||
need to be set up before the call
|
||||
to <literal>invokevirtual</literal>, probably in some global
|
||||
argument array somewhere as discussed back in <xref linkend="hll2">.
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>A final reminder</title>
|
||||
|
||||
<para>
|
||||
We've been talking about all these routines as if they could be
|
||||
copy-pasted or hand-compiled from C++ or Java code. This isn't
|
||||
really the case, primarily because <quote>local variables</quote>
|
||||
in your average assembler routines aren't really local, so
|
||||
multiple calls to the same method will tend to trash the program
|
||||
state. And since a lot of the machinery described here shares a
|
||||
lot of memory (in particular, every single method invocation
|
||||
everywhere shares a <literal>this</literal>), attempting to shift
|
||||
over standard OO code into this format is likely to fail
|
||||
miserably.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You can get an awful lot of flexibility out of even just one layer
|
||||
of method-calls, though, given a thoughtful
|
||||
design. The <literal>do'jump'table</literal> routine, or one very
|
||||
like it, was extremely common in NES games in the mid-1980s and
|
||||
later, usually as the beginning of the frame-update loop.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you find you really need multiple layers of method calls,
|
||||
though, then you really are going to need a full-on program stack,
|
||||
and that's going to be several kinds of mess. That's the topic
|
||||
for the final chapter.
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
218
doc/hll5.sgm
Normal file
218
doc/hll5.sgm
Normal file
|
@ -0,0 +1,218 @@
|
|||
<chapter>
|
||||
<title>Call Stacks</title>
|
||||
|
||||
<para>
|
||||
All our previous work has been assuming FORTRAN-style calling
|
||||
conventions. In this, all procedure-local variables are actually
|
||||
secretly globals. This means that a function that calls itself will
|
||||
end up stomping on its previous values, and everything will be
|
||||
hideously scrambled. Various workarounds for this are covered
|
||||
in <xref linkend="hll2">. Here, we solve the problem fully.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>Recursion</title>
|
||||
|
||||
<para>
|
||||
A procedure in C or other similar languages declares a chunk of
|
||||
storage that's unique to that invocation. This chunk is just
|
||||
large enough to hold the return address and all the local
|
||||
variables, and is called the <emphasis>stack frame</emphasis>.
|
||||
Stack frames are arranged on a <emphasis>call stack</emphasis>;
|
||||
when a function is called, the stack grows with the new frame, and
|
||||
when that function returns, its frame is destroyed. Once the main
|
||||
function returns, the stack is empty.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Most modern architectures are designed to let you implement
|
||||
variable access like this directly, without touching the registers
|
||||
at all. The x86 architecture even dedicates a register to
|
||||
function explicitly as the <emphasis>stack pointer</emphasis>, and
|
||||
then one could read, say, the fifth 16-bit variable into the
|
||||
register AX with the command <literal>MOV AX, [SP+10]</literal>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
As we saw in <xref linkend="hll3">, the 6502 isn't nearly as
|
||||
convenient. We'd need to keep the stack pointer somewhere on the
|
||||
zero page, then load the Y register with 10, then load the
|
||||
accumulator with an indexed-indirect call. This is verbose, keeps
|
||||
trashing our registers, and it's very, very slow.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
So, in the spirit of programmers everywhere, we'll cheat.
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Our Goals</title>
|
||||
|
||||
<para>
|
||||
The system we develop should have all of the following
|
||||
characteristics.
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para>It should be <emphasis>intuitive to program for</emphasis>. The procedure bodies should be easily readable and writable by humans, even in assembler form.</para></listitem>
|
||||
<listitem><para>It should be <emphasis>efficient</emphasis>. Variable accesses are very common, so procedures shouldn't cost much to run.</para></listitem>
|
||||
<listitem><para>It should allow <emphasis>multiple arity</emphasis> in both arguments and return values. We won't require that an unlimited amount of information be passable, but it should allow more than the three bytes the registers give us.</para></listitem>
|
||||
<listitem><para>It should permit <emphasis>tail call elimination</emphasis>, an optimization that will allow certain forms of recursion to actually not grow the stack.</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
Here is a system that meets all these properties.
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para>Reserve two bytes of the zero page for a stack pointer. At the beginning of the program, set it to the top of memory.</para></listitem>
|
||||
<listitem><para>Divide the remainder of Zero Page into two parts:
|
||||
<itemizedlist>
|
||||
<listitem><para>The <emphasis>scratch space</emphasis>, which is where arguments and return values go, and which may be scrambled by any function call, and</para></listitem>
|
||||
<listitem><para>The <emphasis>local area</emphasis>, which all functions must restore to their initial state once finished.</para></listitem>
|
||||
</itemizedlist>
|
||||
</para></listitem>
|
||||
<listitem><para>Assign to each procedure a <emphasis>frame size</emphasis> S, which is a maximum size on the amount of the local area the procedure can use. The procedure's variables will sit in the first S bytes of the local area.</para></listitem>
|
||||
<listitem><para>Upon entering the procedure, push the first S bytes of the local area onto the stack; upon exit, pop hose S bytes back on top of the local area.</para></listitem>
|
||||
<listitem><para>While the procedure is running, only touch the local area and the scratch space.</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>This meets our design criteria neatly:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para>It's as intuitive as such a system will get. You have to call <literal>init'stack</literal> at the beginning, and you need to ensure that <literal>save'stack</literal> and <literal>restore'stack</literal> are called right. The procedure's program text can pretend that it's just referring to its own variables, just like with the old style. If a procedure doesn't call <emphasis>anyone</emphasis>, then it can just do all its work in the scratch space.</para></listitem>
|
||||
<listitem><para>It's efficient; the inside of the procedure is likely to be faster and smaller than its FORTRAN-style counterpart, because all variable references are on the Zero Page.</para></listitem>
|
||||
<listitem><para>Both arguments and return values can be as large as the scratch space. It's not infinite, but it's probably good enough.</para></listitem>
|
||||
<listitem><para>Tail call elimination is possible; just restore the stack before making the JMP to the tail call target.</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
The necessary support code is pretty straightforward. The stack
|
||||
modification routines take the size of the frame in the
|
||||
accumulator, and while saving the local area, it copies over the
|
||||
corresponding values from the scratch space. (This is because
|
||||
most functions will be wanting to keep their arguments around
|
||||
across calls.)
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
.scope
|
||||
; Stack routines
|
||||
.data zp
|
||||
.space _sp $02
|
||||
.space _counter $01
|
||||
.space fun'args $10
|
||||
.space fun'vars $40
|
||||
|
||||
.text
|
||||
init'stack:
|
||||
lda #$00
|
||||
sta _sp
|
||||
lda #$A0
|
||||
sta _sp+1
|
||||
rts
|
||||
|
||||
save'stack:
|
||||
sta _counter
|
||||
sec
|
||||
lda _sp
|
||||
sbc _counter
|
||||
sta _sp
|
||||
lda _sp+1
|
||||
sbc #$00
|
||||
sta _sp+1
|
||||
ldy #$00
|
||||
* lda fun'vars, y
|
||||
sta (_sp), y
|
||||
lda fun'args, y
|
||||
sta fun'vars, y
|
||||
iny
|
||||
dec _counter
|
||||
bne -
|
||||
rts
|
||||
|
||||
restore'stack:
|
||||
pha
|
||||
sta _counter
|
||||
ldy #$00
|
||||
* lda (_sp), y
|
||||
sta fun'vars, y
|
||||
iny
|
||||
dec _counter
|
||||
bne -
|
||||
pla
|
||||
clc
|
||||
adc _sp
|
||||
sta _sp
|
||||
lda _sp+1
|
||||
adc #$00
|
||||
sta _sp+1
|
||||
rts
|
||||
.scend
|
||||
</programlisting>
|
||||
</section>
|
||||
<section>
|
||||
<title>Example: Fibonnacci Numbers</title>
|
||||
|
||||
<para>
|
||||
About the simplest <quote>interesting</quote> recursive function
|
||||
is the Fibonacci numbers. The function fib(x) is defined as being
|
||||
1 if x is 0 or 1, and being fib(x-2)+fib(x-1) otherwise.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Actually expressing it like that directly produces a very
|
||||
inefficient implementation, but it's a simple demonstration of the
|
||||
system. Here's code for expressing the fib function:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
.scope
|
||||
; Uint16 fib (Uint8 x): compute Xth fibonnaci number.
|
||||
; fib(0) = fib(1) = 1.
|
||||
; Stack usage: 3.
|
||||
|
||||
fib: lda #$03
|
||||
jsr save'stack
|
||||
lda fun'vars
|
||||
cmp #$02
|
||||
bcc _base
|
||||
|
||||
dec fun'args
|
||||
jsr fib
|
||||
lda fun'args
|
||||
sta fun'vars+1
|
||||
lda fun'args+1
|
||||
sta fun'vars+2
|
||||
lda fun'vars
|
||||
sec
|
||||
sbc #$02
|
||||
sta fun'args
|
||||
jsr fib
|
||||
clc
|
||||
lda fun'args
|
||||
adc fun'vars+1
|
||||
sta fun'args
|
||||
lda fun'args+1
|
||||
adc fun'vars+2
|
||||
sta fun'args+1
|
||||
jmp _done
|
||||
|
||||
_base: ldy #$01
|
||||
sty fun'args
|
||||
dey
|
||||
sty fun'args+1
|
||||
|
||||
_done: lda #$03
|
||||
jsr restore'stack
|
||||
rts
|
||||
.scend
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
The full application, which deals with interfacing with CBM BASIC
|
||||
and handles console I/O and such, is in <xref linkend="fib-src"
|
||||
endterm="fib-fname">.
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
|
@ -1,67 +0,0 @@
|
|||
; 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
|
Binary file not shown.
70
doc/ophismanual.sgm
Normal file
70
doc/ophismanual.sgm
Normal file
|
@ -0,0 +1,70 @@
|
|||
<!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 part8 SYSTEM "tutor8.sgm">
|
||||
<!ENTITY samplecode SYSTEM "samplecode.sgm">
|
||||
<!ENTITY pre1 SYSTEM "preface.sgm">
|
||||
<!ENTITY cmdref SYSTEM "cmdref.sgm">
|
||||
<!ENTITY hll1 SYSTEM "hll1.sgm">
|
||||
<!ENTITY hll2 SYSTEM "hll2.sgm">
|
||||
<!ENTITY hll3 SYSTEM "hll3.sgm">
|
||||
<!ENTITY hll4 SYSTEM "hll4.sgm">
|
||||
<!ENTITY hll5 SYSTEM "hll5.sgm">
|
||||
]>
|
||||
<book>
|
||||
<bookinfo>
|
||||
<title>Programming with Ophis</title>
|
||||
<author><firstname>Michael</firstname><surname>Martin</surname></author>
|
||||
<copyright><year>2006-2014</year><holder>Michael Martin</holder></copyright>
|
||||
</bookinfo>
|
||||
&pre1;
|
||||
<part label="I">
|
||||
<title>Using the Ophis Assembler</title>
|
||||
<partintro>
|
||||
<para>
|
||||
The chapters in Part 1 are a tutorial guiding you through the
|
||||
features and programming model of the Ophis assembler. It uses
|
||||
the Commodore 64 as its target platform.
|
||||
</para>
|
||||
<para>
|
||||
This is not a tutorial on 6502 assembly language; those are
|
||||
available elsewhere.
|
||||
</para>
|
||||
</partintro>
|
||||
&part1;
|
||||
&part2;
|
||||
&part3;
|
||||
&part4;
|
||||
&part5;
|
||||
&part6;
|
||||
&part7;
|
||||
&part8;
|
||||
</part>
|
||||
<part label="II">
|
||||
<title>To HLL and Back</title>
|
||||
<partintro>
|
||||
<para>
|
||||
This is a compilation of an essay series I wrote from
|
||||
2002-2005 explaining how to apply HLL constructs from
|
||||
high-level languages in your assembly language projects.
|
||||
</para>
|
||||
<para>
|
||||
The examples have been updated and modernized for Ophis 2, and
|
||||
while the examples all target the Commodore 64, they are more
|
||||
generally applicable.
|
||||
</para>
|
||||
</partintro>
|
||||
&hll1;
|
||||
&hll2;
|
||||
&hll3;
|
||||
&hll4;
|
||||
&hll5;
|
||||
</part>
|
||||
&samplecode;
|
||||
&cmdref;
|
||||
</book>
|
146
doc/preface.sgm
Normal file
146
doc/preface.sgm
Normal file
|
@ -0,0 +1,146 @@
|
|||
<preface>
|
||||
<title>Preface</title>
|
||||
<para>
|
||||
Ophis is an assembler for the 6502 microprocessor - the famous
|
||||
chip used in the vast majority of the classic 8-bit computers and
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
Ophis's syntax is noticably different from the formats
|
||||
traditionally used for these chips; it draws its syntactic
|
||||
inspiration primarily from the assemblers for more modern chips,
|
||||
where the role of tokens is determined more by what they're made
|
||||
of and their grammatical location on a line rather than their
|
||||
absolute position on a line. It also borrows the sophisticated
|
||||
methods of tracking the location of labels when writing relinkable
|
||||
code—Ophis expects that the final output it produces will have
|
||||
only a vague resemblance to the memory image when loaded. Most of
|
||||
the alternatives when Ophis was first designed would place
|
||||
instructions and data into a memory map and then dump that map.
|
||||
</para>
|
||||
<para>
|
||||
That said, there remain many actively used 6502 assemblers out
|
||||
there. If you're already a seasoned 6502 assembly programmer, or
|
||||
want to get your old sources built again, Ophis is likely not for
|
||||
you—however, if you are writing new code, or are new to the
|
||||
chip while still having other experience, then Ophis is a tool
|
||||
built with you in mind.
|
||||
</para>
|
||||
<section>
|
||||
<title>History of the project</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 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
|
||||
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
|
||||
name.</para></footnote>
|
||||
</para>
|
||||
<para>
|
||||
Ophis 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 Ophis 1.0 was formally released.
|
||||
However, Ophis was written for Python 2.1 and this became more
|
||||
and more untenable as time has gone by. As I started receiving
|
||||
patches for parts of Ophis, and as I used it for some projects
|
||||
of my own, it became clear that Ophis needed to be modernized
|
||||
and to become better able to interoperate with other
|
||||
toolchains. It was this process that led to Ophis 2.
|
||||
</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.
|
||||
</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.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Getting a copy of Ophis</title>
|
||||
<para>
|
||||
As of this writing, the Ophis assembler is hosted at Github. The
|
||||
latest downloads and documentation will be available
|
||||
at <ulink url="http://github.com/michaelcmartin/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>
|
||||
For Windows users, 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>
|
||||
<para>
|
||||
If you are working on a system with Python installed but to
|
||||
which you do not wish to install software, there is also a
|
||||
standalone pure-Python edition with an ophis.py script. This may
|
||||
be placed anywhere and running ophis.py will temporarily set the
|
||||
library path to point to your directory.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>About the examples</title>
|
||||
<para>
|
||||
Versions of the examples in this book are available from the
|
||||
Ophis site. Windows users will find them packaged with the
|
||||
distribution; all other users can get them as a separate
|
||||
download or pull them directly from github.
|
||||
</para>
|
||||
<para>
|
||||
The code in this book is available in
|
||||
the <literal>examples/</literal> subdirectory, while extra
|
||||
examples will be in subdirectories of their own with brief
|
||||
descriptions. They are largely all simple <quote>Hello
|
||||
world</quote> applications, designed mainly to demonstrate how
|
||||
to package assembled binaries into forms that emulators or ROM
|
||||
loaders can use. They are not primarily intended as tutorials
|
||||
for writing for the platforms themselves.
|
||||
</para>
|
||||
<para>
|
||||
Most examples will require use of <emphasis>platform
|
||||
headers</emphasis>—standardized header files that set
|
||||
useful constants for the target system and, if needed, contain
|
||||
small programs to allow the program to be loaded and run. These
|
||||
are stored in the <literal>platform/</literal> subdirectory.
|
||||
</para>
|
||||
</section>
|
||||
</preface>
|
1169
doc/samplecode.sgm
Normal file
1169
doc/samplecode.sgm
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -144,14 +144,15 @@ next: .word 0 ; End of program
|
|||
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>.
|
||||
<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
|
||||
|
@ -163,7 +164,10 @@ next: .word 0 ; End of program
|
|||
used <literal>.advance</literal>, which outputs zeros until
|
||||
the specified address is reached. Attempting
|
||||
to <literal>.advance</literal> backwards produces an
|
||||
assemble-time error.
|
||||
assemble-time error. (If we wanted to output something
|
||||
besides zeros, we could add it as a second
|
||||
argument: <literal>.advance 2064,$FF</literal>, for
|
||||
instance.)
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
It has comments that explain what the data are for. The
|
||||
|
@ -256,6 +260,31 @@ hello: .byte "HELLO, WORLD!", 0
|
|||
summary of available command line options.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Ophis takes a list of source files and produces an output file
|
||||
based on assembling each file you give it, in order. You can add
|
||||
a line to your program like this to name the output file:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
.outfile "hello.prg"
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
Alternately, you can use the <option>-o</option> option on the
|
||||
command line. This will override any <literal>.outfile</literal>
|
||||
directives. If you don't specify any name, it will put the
|
||||
output into a file named <filename>ophis.bin</filename>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you are using Ophis as part of some larger toolchain, you can
|
||||
also make it run in <emphasis>pipe mode</emphasis>. If you give
|
||||
a dash <option>-</option> as an input file or as the output
|
||||
target, Ophis will use standard input or output for
|
||||
communication.
|
||||
</para>
|
||||
|
||||
<table frame="all">
|
||||
<title>Ophis Options</title>
|
||||
<tgroup cols='2'>
|
||||
|
@ -266,13 +295,14 @@ hello: .byte "HELLO, WORLD!", 0
|
|||
</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>
|
||||
<row><entry><option>-o FILE</option></entry><entry>Overrides the default filename for output.</entry></row>
|
||||
<row><entry><option>-l FILE</option></entry><entry>Specifies an optional listing file that gives the emitted binary in human-readable form, with disassembly.</entry></row>
|
||||
<row><entry><option>-m FILE</option></entry><entry>Specifies an optional map file that gives the in-source names for every label used in the program.</entry></row>
|
||||
<row><entry><option>-u</option></entry><entry>Allows the 6510 undocumented opcodes as listed in the VICE documentation.</entry></row>
|
||||
<row><entry><option>-c</option></entry><entry>Allows opcodes and addressing modes added by the 65C02.</entry></row>
|
||||
<row><entry><option>-4</option></entry><entry>Allows opcodes and addressing modes added by the 4502. (Experimental.)</entry></row>
|
||||
<row><entry><option>-q</option></entry><entry>Quiet operation. Only reports warnings and errors.</entry></row>
|
||||
<row><entry><option>-v</option></entry><entry>Verbose operation. Reports files as they are loaded.</entry></row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
@ -283,33 +313,21 @@ hello: .byte "HELLO, WORLD!", 0
|
|||
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
|
||||
localhost$ ophis -v hello1.oph
|
||||
Loading hello1.oph
|
||||
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.
|
||||
This will produce a file
|
||||
named <filename>hello.prg</filename>. 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. If you have access to a
|
||||
device like the 1541 Ultimate II, you can even load the file
|
||||
directly into the actual hardware.
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
|
@ -43,6 +43,13 @@ _next: .word 0 ; End of program
|
|||
.advance 2064
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
It's possible to have multiple temporary labels with the same
|
||||
name in different parts of the code. If you create a label map
|
||||
in those cases, you will have to look at the sourcefile location
|
||||
to distinguish them.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
<section>
|
||||
<title>Anonymous labels</title>
|
|
@ -34,6 +34,13 @@
|
|||
for linking in pre-created graphics or sound data.
|
||||
</para>
|
||||
|
||||
<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
|
||||
|
@ -53,7 +60,10 @@
|
|||
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"
|
||||
endterm="kernal-fname">.)
|
||||
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>
|
||||
|
@ -90,7 +100,9 @@
|
|||
<para>
|
||||
No global or anonymous labels may be defined inside a macro:
|
||||
temporary labels only persist in the macro expansion itself.
|
||||
(Each macro body has its own scope.)
|
||||
(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>
|
|
@ -64,11 +64,11 @@ target10: .byte "Universe", 0
|
|||
and lowercase are reversed, so we have messages
|
||||
like <computeroutput>hELLO, sOLAR sYSTEM!</computeroutput>. For
|
||||
the specific case of PETSCII, we can just fix our strings, but
|
||||
that's less of an option if we're writing for the Apple II's
|
||||
character set, or targeting a game console that puts its letters
|
||||
in arbitrary locations. We need to remap how strings are turned
|
||||
into byte values. The <literal>.charmap</literal>
|
||||
and <literal>.charmapbin</literal> directives do what we need.
|
||||
that's less of an option if we're writing for a game console that
|
||||
puts its letters in arbitrary locations. We need to remap how
|
||||
strings are turned into byte values.
|
||||
The <literal>.charmap</literal> and <literal>.charmapbin</literal>
|
||||
directives do what we need.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
|
@ -102,9 +102,6 @@ target10: .byte "Universe", 0
|
|||
specifies an external file, 256 bytes long, that is loaded in at
|
||||
that point. A binary character map for the Commodore 64 is
|
||||
provided with the sample programs
|
||||
as <filename>petscii.map</filename>. There are also three
|
||||
files, <filename>a2normal.map</filename>, <filename>a2inverse.map</filename>,
|
||||
and <filename>a2blink.map</filename> that handle the Apple II's
|
||||
very nonstandard character encodings.
|
||||
as <filename>petscii.map</filename>.
|
||||
</para>
|
||||
</chapter>
|
|
@ -79,7 +79,10 @@ _done: rts
|
|||
|
||||
<para>
|
||||
Note that we only have to name <literal>cache</literal> once, but
|
||||
can use addition to refer to any offset from it.
|
||||
can use addition to refer to any offset from it.<footnote><para>We
|
||||
could spare ourselves some trouble here and use $fb instead of
|
||||
$10, which BASIC does <emphasis>not</emphasis> use, but the
|
||||
example is more thorough this way.</para></footnote>
|
||||
</para>
|
||||
|
||||
<para>
|
|
@ -2,11 +2,11 @@
|
|||
<title>Advanced Memory Segments</title>
|
||||
|
||||
<para>
|
||||
This is the last section of the Ophis tutorial. By now we've
|
||||
covered the basics of every command in the assembler; in this
|
||||
final installment we show the full capabilities of
|
||||
By now we've covered the basics of every command in the assembler;
|
||||
in this final installment we show the full capabilities of
|
||||
the <literal>.text</literal> and <literal>.data</literal> commands
|
||||
as we produce a final set of Commodore 64 header files.
|
||||
as we produce a more sophisticated set of Commodore 64 header
|
||||
files.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
|
@ -45,30 +45,49 @@
|
|||
|
||||
<para>
|
||||
Now, actually, the rest of the zero page is reserved too:
|
||||
locations $02-$7F are used by the BASIC interpreter, and
|
||||
locations $80-$FF are used by the KERNAL. We don't need the
|
||||
BASIC interpreter, though, so we can back up all of $02-$7F at
|
||||
the start of our program and restore it all when we're done:
|
||||
locations $02-$8F are used by the BASIC interpreter, and
|
||||
locations $90-$FF are used by the KERNAL. We don't need the
|
||||
BASIC interpreter, though, so we can back up all of $02-$8F at
|
||||
the start of our program and restore it all when we're done.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In fact, since we're disablng BASIC, we can actually also swap
|
||||
out its ROM entirely and get a contiguous block of RAM from
|
||||
$0002 to $CFFF:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
.scope
|
||||
; Cache BASIC's zero page at top of available RAM.
|
||||
ldx #$7E
|
||||
; Cache BASIC zero page at top of available RAM
|
||||
ldx #$8e
|
||||
* lda $01, x
|
||||
sta $CF81, x
|
||||
sta $cf81, x
|
||||
dex
|
||||
bne -
|
||||
|
||||
; Swap out the BASIC ROM for RAM
|
||||
lda $01
|
||||
and #$fe
|
||||
ora #$06
|
||||
sta $01
|
||||
|
||||
; Run the real program
|
||||
jsr _main
|
||||
|
||||
; Restore BASIC's zero page and return control.
|
||||
; Restore BASIC ROM
|
||||
lda $01
|
||||
ora #$07
|
||||
sta $01
|
||||
|
||||
ldx #$7E
|
||||
* lda $CF81, x
|
||||
; Restore BASIC zero page
|
||||
ldx #$8e
|
||||
* lda $cf81, x
|
||||
sta $01, x
|
||||
dex
|
||||
bne -
|
||||
|
||||
; Back to BASIC
|
||||
rts
|
||||
|
||||
_main:
|
||||
|
@ -77,11 +96,6 @@ _main:
|
|||
.scend
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
The new, improved header file is <xref linkend="c64-2-src"
|
||||
endterm="c64-2-fname">.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Our <literal>print'str</literal> routine is then rewritten to
|
||||
declare and use a zero-page variable, like so:
|
||||
|
@ -120,24 +134,8 @@ _done: rts
|
|||
</programlisting>
|
||||
|
||||
<para>
|
||||
That concludes our tour. The final source file
|
||||
is <xref linkend="tutor7-src" endterm="tutor7-fname">.
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Where to go from here</title>
|
||||
<para>
|
||||
This tutorial has touched on everything that the assembler can
|
||||
do, but it's not really well organized as a
|
||||
reference. <xref linkend="ref-link"> is a better place to look
|
||||
up matters of syntax or consult lists of available commands.
|
||||
</para>
|
||||
<para>
|
||||
If you're looking for projects to undertake, the Commodore 64
|
||||
and Atari 2600 development communities are both very strong, and
|
||||
the Apple II and NES development communities are still alive and
|
||||
well as well. There's an annual Minigame Competition that's
|
||||
always looking for new entries.
|
||||
The final source file is <xref linkend="tutor7-src"
|
||||
endterm="tutor7-fname">.
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
211
doc/tutor8.sgm
Normal file
211
doc/tutor8.sgm
Normal file
|
@ -0,0 +1,211 @@
|
|||
<chapter>
|
||||
<title>Platform-Specific Techniques</title>
|
||||
|
||||
<para>
|
||||
Ophis is intended to produce cross-assembled binaries that will
|
||||
run in a variety of contexts. The expectation is that most users
|
||||
will be writing for emulated versions of hardware from when the
|
||||
6502 chip was current, and producing files either for those
|
||||
emulators or for devices that will transfer the results to real
|
||||
hardware. This chapter describes the support routines and examples
|
||||
to make those tasks easier.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>The Commodore 64 and VIC-20</title>
|
||||
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>c64kernal.oph</literal> actually defines no
|
||||
code. It merely sets up the customary names for the KERNAL
|
||||
jump table routines so that you may refer to routines
|
||||
like <literal>chrout</literal> and <literal>rdtim</literal>
|
||||
by name.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>c64header.oph</literal> is an absolutely minimal
|
||||
C64 header program; it contains the one-line BASIC program
|
||||
and nothing else. Smaller programs that do not require more
|
||||
than four bytes of zero page do not need to do any
|
||||
bankswitching or zero page caching and don't need any more
|
||||
than this. The aliases provided
|
||||
in <literal>c64kernal.oph</literal> may be useful, but are
|
||||
not included in this header.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>c64_0.oph</literal> is suitable for larger and more
|
||||
sophisticated programs. It is an enhancement of the header
|
||||
file developed in the previous chapter. It stores the saved
|
||||
zero page values in the RAM shadowed by the KERNAL ROM, and
|
||||
it also uses a different mechanism for returning to BASIC
|
||||
when done that is more robust in the face of self-modifying
|
||||
programs such as those produced by self-extracting
|
||||
compressed executables or onefiled multipart programs. It is
|
||||
used like the other header files—just include it at
|
||||
the top of your source file and use <literal>RTS</literal>
|
||||
to end your program—but programs that use this header
|
||||
file will have all of the zero page from $02-$8F and a
|
||||
contiguous chunk of program RAM from $0800-$CFFF.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>libbasic64.oph</literal> is an experimental set of
|
||||
macros and routines to permit the assembly programmer to
|
||||
make use of the software floating point routines provided by
|
||||
BASIC. It is, for obvious reasons, not compatible
|
||||
with <literal>c64_0.oph</literal>, because it needs to make
|
||||
use of BASIC's workspace and the ROM itself. If you wish to
|
||||
use this file you should include it near the end of your
|
||||
program.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>vic20.oph</literal> is a header that will work for
|
||||
the <emphasis>unexpanded</emphasis> VIC-20. Memory expansion
|
||||
slots change where BASIC programs load, and since these
|
||||
headers load in the machine language program in as the
|
||||
suffix to a BASIC program, that also changes where they are
|
||||
themselves loaded. There is no trickery with bankswitching
|
||||
ROMs in and out—the VIC-20 does not have enough RAM to
|
||||
gain anything from these techniques.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>vic20x.oph</literal> does the same, but for a
|
||||
VIC-20 with one or more memory expansions.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<section>
|
||||
<title>Using LIBBASIC64</title>
|
||||
|
||||
<para>
|
||||
The 6502's arithmetic capabilities are rather limited. To
|
||||
counteract this, BASICs of the era did floating point in
|
||||
software and gave BASIC programmers the full suite of
|
||||
arithmetic operations. These operations are largely
|
||||
unavailable to machine language programmers.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <literal>libbasic64.oph</literal> library is an attempt to
|
||||
address this. It is currently considered highly experimental,
|
||||
but initial results are very promising.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
BASIC stores floating point numbers in a five-byte format, but
|
||||
translates them into a seven-byte format to do actual work in
|
||||
two Floating Point Accumulators (FAC1 and FAC2). Ophis will
|
||||
let you specify 5-byte constants with
|
||||
the <literal>.cbmfloat</literal> directive, which takes a
|
||||
string and produces the requisite five-byte value.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The floating point functions in BASIC all operate on FAC1 and
|
||||
are relatively reliable. The
|
||||
functions <literal>abs_fac1</literal>, <literal>atn_fac1</literal>, <literal>cos_fac1</literal>, <literal>exp_fac1</literal>, <literal>int_fac1</literal>, <literal>log_fac1</literal>, <literal>rnd_fac1</literal>, <literal>sgn_fac1</literal>, <literal>sin_fac1</literal>,
|
||||
and <literal>tan_fac1</literal> are all provided. Routines
|
||||
that touch the FACs tend to be extremely finicky. This system
|
||||
defines a set of macros and routines to manage that for you:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para><literal>`f_move</literal> <emphasis>dest, source</emphasis>: Copy a five-byte floating point value from <emphasis>source</emphasis> to <emphasis>dest</emphasis>.</para></listitem>
|
||||
<listitem><para><literal>`fp_load</literal> <emphasis>src</emphasis>: Loads FAC1 with the floating point constant specified by <emphasis>src</emphasis>.</para></listitem>
|
||||
<listitem><para><literal>`fp_store</literal> <emphasis>dest</emphasis>: Saves the value of FAC1 to the named memory location.</para></listitem>
|
||||
<listitem><para><literal>`fp_print</literal> <emphasis>src</emphasis>: Prints out the value of FAC1 to the screen. You may want to call <literal>int_fac1</literal> first to round it. Unlike BASIC's <literal>PRINT</literal> statement, this routine will not bracket the number with blanks.</para></listitem>
|
||||
<listitem><para><literal>`fp_read</literal> <emphasis>ptr</emphasis>: Attempts to convert a string to a floating point value in FAC1, in a manner similar to BASIC's <literal>VAL</literal> function.</para></listitem>
|
||||
<listitem><para><literal>`fp_add</literal> <emphasis>operand</emphasis>: Adds the operand to FAC1.</para></listitem>
|
||||
<listitem><para><literal>`fp_subtract</literal> <emphasis>operand</emphasis>: Subtracts the operand from FAC1.</para></listitem>
|
||||
<listitem><para><literal>`fp_multiply</literal> <emphasis>operand</emphasis>: Multiplies the operand by FAC1.</para></listitem>
|
||||
<listitem><para><literal>`fp_divide</literal> <emphasis>operand</emphasis>: Divides FAC1 by the operand.</para></listitem>
|
||||
<listitem><para><literal>`fp_pow</literal> <emphasis>operand</emphasis>: Raises FAC1 to the operand's power.</para></listitem>
|
||||
<listitem><para><literal>`fp_and</literal> <emphasis>operand</emphasis>: Juggles floating point-to-integer conversions to do a bitwise AND.</para></listitem>
|
||||
<listitem><para><literal>`fp_or</literal> <emphasis>operand</emphasis>: Likewise, but for OR.</para></listitem>
|
||||
<listitem><para><literal>jsr randomize</literal>: Calls RND(-TI) and leaves the (useless) result in FAC1. This seeds BASIC's random number generator with the number of clock ticks since poweron.</para></listitem>
|
||||
<listitem><para><literal>jsr rnd</literal>: Calls RND(1) and leaves the result in FAC1, providing a random number between 0 and 1.</para></listitem>
|
||||
<listitem><para><literal>jsr fac1_sign</literal>: Loads the SGN(FAC1) into the accumulator. This will be $01 if the accumulator is positive, $00 if it is zero, and $FF if it is negative. This routine is useful for branching based on the result of a floating point computation.</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
Other functions are available, but their preconditions are
|
||||
hazier. The source file is commented with the current state of
|
||||
knowledge.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To see some of these functions in action,
|
||||
the <literal>examples</literal> directory includes a
|
||||
program <literal>kinematics.oph</literal>, which reads numbers
|
||||
in from input and computes trajectories based on them.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<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.
|
||||
</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.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<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>.
|
||||
</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.
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
|
@ -3,10 +3,10 @@
|
|||
|
||||
.scope
|
||||
.word _next, 10 ; Next line and current line number
|
||||
.byte $9e," 2064",0 ; SYS 2064
|
||||
.byte $9e," 2062",0 ; SYS 2064
|
||||
_next: .word 0 ; End of program
|
||||
.scend
|
||||
|
||||
.advance 2064
|
||||
.advance 2062
|
||||
|
||||
.require "kernal.oph"
|
||||
.require "../platform/c64kernal.oph"
|
213
examples/fibonacci.oph
Normal file
213
examples/fibonacci.oph
Normal file
|
@ -0,0 +1,213 @@
|
|||
.include "../platform/c64_0.oph"
|
||||
.require "../platform/c64kernal.oph"
|
||||
.outfile "fibonacci.prg"
|
||||
|
||||
lda #<opening ; Print opening text
|
||||
sta fun'args
|
||||
lda #>opening
|
||||
sta fun'args+1
|
||||
jsr print'string
|
||||
|
||||
lda #$00
|
||||
sta fun'vars ; Count num from 0 to 19
|
||||
* lda fun'vars ; Main loop: print num, with leading space if <10
|
||||
cmp #$09
|
||||
bcs +
|
||||
lda #$20
|
||||
jsr chrout
|
||||
lda fun'vars
|
||||
* sta fun'args ; Copy num to args, print it, plus ": "
|
||||
inc fun'args
|
||||
lda #$00
|
||||
sta fun'args+1
|
||||
jsr print'dec
|
||||
lda #$3A
|
||||
jsr chrout
|
||||
lda #$20
|
||||
jsr chrout
|
||||
lda fun'vars ; Copy num to args, call fib, print result
|
||||
sta fun'args
|
||||
jsr fib
|
||||
jsr print'dec
|
||||
lda #$0D ; Newline
|
||||
jsr chrout
|
||||
inc fun'vars ; Increment num; if it's 20, we're done.
|
||||
lda fun'vars
|
||||
cmp #20
|
||||
bne -- ; Otherwise, loop.
|
||||
rts
|
||||
|
||||
opening:
|
||||
.byte 147, " FIBONACCI SEQUENCE",13,13,0
|
||||
|
||||
.scope
|
||||
; Uint16 fib (Uint8 x): compute Xth fibonnaci number.
|
||||
; fib(0) = fib(1) = 1.
|
||||
; Stack usage: 3.
|
||||
|
||||
fib: lda #$03
|
||||
jsr save'stack
|
||||
|
||||
lda fun'vars ; If x < 2, goto _base.
|
||||
cmp #$02
|
||||
bcc _base
|
||||
|
||||
dec fun'args ; Otherwise, call fib(x-1)...
|
||||
jsr fib
|
||||
lda fun'args ; Copy the result to local variable...
|
||||
sta fun'vars+1
|
||||
lda fun'args+1
|
||||
sta fun'vars+2
|
||||
lda fun'vars ; Call fib(x-2)...
|
||||
sec
|
||||
sbc #$02
|
||||
sta fun'args
|
||||
jsr fib
|
||||
clc ; And add the old result to it, leaving it
|
||||
lda fun'args ; in the 'result' location.
|
||||
adc fun'vars+1
|
||||
sta fun'args
|
||||
lda fun'args+1
|
||||
adc fun'vars+2
|
||||
sta fun'args+1
|
||||
jmp _done ; and then we're done.
|
||||
|
||||
_base: ldy #$01 ; In the base case, just copy 1 to the
|
||||
sty fun'args ; result.
|
||||
dey
|
||||
sty fun'args+1
|
||||
|
||||
_done: lda #$03
|
||||
jsr restore'stack
|
||||
rts
|
||||
.scend
|
||||
|
||||
.scope
|
||||
; Stack routines: init'stack, save'stack, restore'stack
|
||||
.data zp
|
||||
.space _sp $02
|
||||
.space _counter $01
|
||||
.space fun'args $10
|
||||
.space fun'vars $40
|
||||
|
||||
.text
|
||||
init'stack:
|
||||
lda #$00
|
||||
sta _sp
|
||||
lda #$A0
|
||||
sta _sp+1
|
||||
rts
|
||||
|
||||
save'stack:
|
||||
sta _counter
|
||||
sec
|
||||
lda _sp
|
||||
sbc _counter
|
||||
sta _sp
|
||||
lda _sp+1
|
||||
sbc #$00
|
||||
sta _sp+1
|
||||
ldy #$00
|
||||
* lda fun'vars, y
|
||||
sta (_sp), y
|
||||
lda fun'args, y
|
||||
sta fun'vars, y
|
||||
iny
|
||||
dec _counter
|
||||
bne -
|
||||
rts
|
||||
|
||||
restore'stack:
|
||||
pha
|
||||
sta _counter
|
||||
ldy #$00
|
||||
* lda (_sp), y
|
||||
sta fun'vars, y
|
||||
iny
|
||||
dec _counter
|
||||
bne -
|
||||
pla
|
||||
clc
|
||||
adc _sp
|
||||
sta _sp
|
||||
lda _sp+1
|
||||
adc #$00
|
||||
sta _sp+1
|
||||
rts
|
||||
.scend
|
||||
|
||||
|
||||
; Utility functions. print'dec prints an unsigned 16-bit integer.
|
||||
; It's ugly and long, mainly because we don't bother with niceties
|
||||
; like "division". print'string prints a zero-terminated string.
|
||||
|
||||
.scope
|
||||
.data
|
||||
.org fun'args
|
||||
.space _val 2
|
||||
.space _step 2
|
||||
.space _res 1
|
||||
.space _allowzero 1
|
||||
.text
|
||||
print'dec:
|
||||
lda #$00
|
||||
sta _allowzero
|
||||
lda #<10000
|
||||
sta _step
|
||||
lda #>10000
|
||||
sta _step+1
|
||||
jsr repsub'16
|
||||
lda #<1000
|
||||
sta _step
|
||||
lda #>1000
|
||||
sta _step+1
|
||||
jsr repsub'16
|
||||
lda #0
|
||||
sta _step+1
|
||||
lda #100
|
||||
sta _step
|
||||
jsr repsub'16
|
||||
lda #10
|
||||
sta _step
|
||||
jsr repsub'16
|
||||
lda _val
|
||||
jsr _print
|
||||
rts
|
||||
|
||||
repsub'16:
|
||||
lda #$00
|
||||
sta _res
|
||||
* lda _val
|
||||
sec
|
||||
sbc _step
|
||||
lda _val+1
|
||||
sbc _step+1
|
||||
bcc _done
|
||||
lda _val
|
||||
sec
|
||||
sbc _step
|
||||
sta _val
|
||||
lda _val+1
|
||||
sbc _step+1
|
||||
sta _val+1
|
||||
inc _res
|
||||
jmp -
|
||||
_done: lda _res
|
||||
ora _allowzero
|
||||
beq _ret
|
||||
sta _allowzero
|
||||
lda _res
|
||||
_print: clc
|
||||
adc #'0
|
||||
jsr chrout
|
||||
_ret: rts
|
||||
.scend
|
||||
|
||||
print'string:
|
||||
ldy #$00
|
||||
* lda (fun'args), y
|
||||
beq +
|
||||
jsr chrout
|
||||
iny
|
||||
jmp -
|
||||
* rts
|
|
@ -1,5 +1,6 @@
|
|||
.word $0801
|
||||
.org $0801
|
||||
.outfile "hello.prg"
|
||||
|
||||
.word next, 10 ; Next line and current line number
|
||||
.byte $9e," 2064",0 ; SYS 2064
|
|
@ -1,5 +1,6 @@
|
|||
.word $0801
|
||||
.org $0801
|
||||
.outfile "hello.prg"
|
||||
|
||||
.scope
|
||||
.word _next, 10 ; Next line and current line number
|
|
@ -1,4 +1,5 @@
|
|||
.include "c64-1.oph"
|
||||
.outfile "hello.prg"
|
||||
|
||||
.macro print
|
||||
ldx #0
|
|
@ -1,4 +1,5 @@
|
|||
.include "c64-1.oph"
|
||||
.outfile "hello.prg"
|
||||
|
||||
.macro print
|
||||
ldx #0
|
|
@ -1,4 +1,5 @@
|
|||
.include "c64-1.oph"
|
||||
.outfile "hello.prg"
|
||||
|
||||
.macro print
|
||||
ldx #0
|
|
@ -1,4 +1,5 @@
|
|||
.include "c64-1.oph"
|
||||
.outfile "hello.prg"
|
||||
|
||||
.macro print
|
||||
ldx #0
|
|
@ -1,4 +1,5 @@
|
|||
.include "c64-1.oph"
|
||||
.outfile "hello.prg"
|
||||
|
||||
.data
|
||||
.org $C000
|
|
@ -1,4 +1,5 @@
|
|||
.include "c64-1.oph"
|
||||
.outfile "hello.prg"
|
||||
|
||||
.data
|
||||
.org $C000
|
|
@ -1,4 +1,5 @@
|
|||
.include "c64-2.oph"
|
||||
.include "../platform/c64_0.oph"
|
||||
.require "../platform/c64kernal.oph"
|
||||
|
||||
.data
|
||||
.org $C000
|
26
examples/hello_nes/README.txt
Normal file
26
examples/hello_nes/README.txt
Normal file
|
@ -0,0 +1,26 @@
|
|||
This is a "Hello World" program for the Nintendo Entertainment System,
|
||||
which uses the sprite system to display and color-cycle the letters.
|
||||
|
||||
Since NES cartridges tended to have sophisticated circuitry built into them
|
||||
that controlled memory addressing, several standards have arisen to represent
|
||||
this information. The program code for "Hello, NES" is split into two halves;
|
||||
a hello_prg.oph containing the executable code (PRG-ROM), and a hello_chr.oph
|
||||
containing the graphics tile information (CHR-ROM). These can then be packaged
|
||||
one of two ways - the popular iNES format (hello_ines.oph) or the
|
||||
mostly-defunct UNIF format (hello_unif.oph). Simply running
|
||||
|
||||
ophis hello_ines.oph
|
||||
|
||||
or
|
||||
|
||||
ophis hello_unif.oph
|
||||
|
||||
should produce hello.nes and hello.unf, respectively. Although UNIF is not a
|
||||
common format, its "chunk" system is not rare. The hello_unif.oph file
|
||||
demonstrates some techniques for automatically computing chunk sizes in Ophis.
|
||||
|
||||
Be warned that as these techniques use the program counter, attempting to use
|
||||
labels to compute chunk size of assembled code is likely to backfire
|
||||
spectacularly - this technique should really only be used for inline strings
|
||||
and data.
|
||||
|
20
examples/hello_nes/hello_chr.oph
Normal file
20
examples/hello_nes/hello_chr.oph
Normal file
|
@ -0,0 +1,20 @@
|
|||
;; NES "Hello World" character tables
|
||||
|
||||
;; We spell out "Hello World" and we make sure that the colors are
|
||||
;; different so our palette shift works.
|
||||
|
||||
.text
|
||||
.org $0000
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $c6,$c6,$c6,$fe,$c6,$c6,$c6,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; H (color 1) #1
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$fe,$c0,$c0,$fc,$c0,$c0,$fe,$00 ; E (color 2) #2
|
||||
.byte $60,$60,$60,$60,$60,$60,$7e,$00,$60,$60,$60,$60,$60,$60,$7e,$00 ; L (color 3) #3
|
||||
.byte $60,$60,$60,$60,$60,$60,$7e,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; L (color 1) #4
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$7c,$c6,$c6,$c6,$c6,$c6,$7c,$00 ; O (color 2) #5
|
||||
.byte $c6,$c6,$d6,$fe,$fe,$ee,$c6,$00,$c6,$c6,$d6,$fe,$fe,$ee,$c6,$00 ; W (color 3) #6
|
||||
.byte $7c,$c6,$c6,$c6,$c6,$c6,$7c,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; O (color 1) #7
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$fc,$c6,$c6,$fc,$d8,$cc,$c6,$00 ; R (color 2) #8
|
||||
; L (color 3) #3
|
||||
.byte $f8,$cc,$c6,$c6,$c6,$cc,$f8,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; D (color 1) #9
|
||||
|
||||
.advance $2000 ; Fill in the rest of the CHR-ROM chip.
|
7
examples/hello_nes/hello_ines.oph
Normal file
7
examples/hello_nes/hello_ines.oph
Normal file
|
@ -0,0 +1,7 @@
|
|||
; iNES header
|
||||
.byte $4e,$45,$53,$1a,$01,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
|
||||
.outfile "hello.nes"
|
||||
|
||||
.include "hello_prg.oph"
|
||||
.include "hello_chr.oph"
|
162
examples/hello_nes/hello_prg.oph
Normal file
162
examples/hello_nes/hello_prg.oph
Normal file
|
@ -0,0 +1,162 @@
|
|||
;; PRG-ROM for our NES demo program.
|
||||
|
||||
.alias sprites $200 ; Keep our OAM DMA here.
|
||||
|
||||
.data
|
||||
.org $0000
|
||||
.space count 1
|
||||
.space palette 32
|
||||
|
||||
.text
|
||||
.org $C000
|
||||
|
||||
reset: sei
|
||||
cld
|
||||
; Wait two VBLANKs.
|
||||
* lda $2002
|
||||
bpl -
|
||||
* lda $2002
|
||||
bpl -
|
||||
|
||||
; Disable all graphics.
|
||||
lda #$00
|
||||
sta $2000
|
||||
sta $2001
|
||||
|
||||
; Mask out sound IRQs.
|
||||
lda #$40
|
||||
sta $4017
|
||||
|
||||
; Clear out RAM.
|
||||
lda #$00
|
||||
ldx #$00
|
||||
* sta $000,x
|
||||
sta $100,x
|
||||
sta $200,x
|
||||
sta $300,x
|
||||
sta $400,x
|
||||
sta $500,x
|
||||
sta $600,x
|
||||
sta $700,x
|
||||
inx
|
||||
bne -
|
||||
|
||||
; Reset the stack pointer.
|
||||
ldx #$FF
|
||||
txs
|
||||
|
||||
; Clear the name tables.
|
||||
lda #$24
|
||||
sta $2006
|
||||
ldy #$00
|
||||
sty $2006
|
||||
ldx #$08
|
||||
lda #0
|
||||
ldy #0
|
||||
* sta $2007
|
||||
iny
|
||||
bne -
|
||||
dex
|
||||
bne -
|
||||
|
||||
; Load the palette from later on in the ROM.
|
||||
lda #$3F
|
||||
ldx #$00
|
||||
sta $2006
|
||||
stx $2006
|
||||
* lda palette'rom,x
|
||||
sta palette,x
|
||||
sta $2007
|
||||
inx
|
||||
cpx #$20
|
||||
bne -
|
||||
|
||||
; Load the proper sprite data into place.
|
||||
ldx #$00
|
||||
* lda graphics,x
|
||||
sta sprites,x
|
||||
inx
|
||||
bne -
|
||||
|
||||
lda #$1E
|
||||
sta count ; Update twice a second
|
||||
|
||||
; Un-scroll everything, just to be safe.
|
||||
lda #$00
|
||||
sta $2006
|
||||
sta $2006
|
||||
sta $2005
|
||||
sta $2005
|
||||
; Re-enable the displays.
|
||||
lda #$80
|
||||
sta $2000
|
||||
lda #$1E
|
||||
sta $2001
|
||||
|
||||
; FINALLY. We've set up the system. From here on it's all up to
|
||||
; the NMI interrupt to handle things.
|
||||
cli
|
||||
* jmp -
|
||||
|
||||
vblank: ; Refresh the SPR-RAM.
|
||||
lda #$02
|
||||
sta $4014
|
||||
|
||||
; Time to update the palette?
|
||||
dec count
|
||||
bne irq ; If not, done.
|
||||
|
||||
lda #$1E
|
||||
sta count ; Update twice a second
|
||||
|
||||
; Update those parts of the palette that aren't transparent.
|
||||
ldx #$11
|
||||
* txa
|
||||
and #$03
|
||||
beq + ; Don't update transparents
|
||||
jsr bump'palette
|
||||
* inx
|
||||
cpx #$20
|
||||
bne --
|
||||
|
||||
; Now re-blast that palette to VRAM.
|
||||
lda #$3F
|
||||
ldx #$00
|
||||
sta $2006
|
||||
stx $2006
|
||||
* lda palette,x
|
||||
sta $2007
|
||||
inx
|
||||
cpx #$20
|
||||
bne -
|
||||
|
||||
; And reset the name tables.
|
||||
lda #$00
|
||||
sta $2006
|
||||
sta $2006
|
||||
sta $2005
|
||||
sta $2005
|
||||
|
||||
irq: rti
|
||||
|
||||
bump'palette:
|
||||
inc palette,x
|
||||
lda palette,x
|
||||
and #$0F
|
||||
sec
|
||||
sbc #$0D
|
||||
bpl bump'palette
|
||||
rts
|
||||
|
||||
palette'rom:
|
||||
.byte $0d,$0d,$0d,$0d,$0d,$0d,$0d,$0d,$0d,$0d,$0d,$0d,$0d,$0d,$0d,$0d
|
||||
.byte $0d,$01,$02,$03,$0d,$04,$05,$06,$0d,$07,$08,$09,$0d,$0a,$0b,$0c
|
||||
|
||||
graphics:
|
||||
.byte $70,$01,$00,$6c,$70,$02,$00,$74,$70,$03,$00,$7c,$70,$04,$01,$84
|
||||
.byte $70,$05,$01,$8c
|
||||
.byte $78,$06,$01,$6c,$78,$07,$02,$74,$78,$08,$02,$7c,$78,$03,$02,$84
|
||||
.byte $78,$09,$03,$8c
|
||||
|
||||
.advance $FFFA
|
||||
.word vblank, reset, irq
|
25
examples/hello_nes/hello_unif.oph
Normal file
25
examples/hello_nes/hello_unif.oph
Normal file
|
@ -0,0 +1,25 @@
|
|||
.outfile "hello.unf"
|
||||
.byte "UNIF"
|
||||
.dword 7
|
||||
.advance $20
|
||||
.byte "MAPR"
|
||||
.dword ++-+
|
||||
*
|
||||
.byte "NES-NROM-256",0
|
||||
*
|
||||
.byte "NAME"
|
||||
.dword ++-+
|
||||
*
|
||||
.byte "Ophis Hello World Demo",0
|
||||
*
|
||||
.byte "PRG0"
|
||||
.dword $4000
|
||||
.include "hello_prg.oph"
|
||||
|
||||
.byte "MIRR"
|
||||
.dword 1
|
||||
.byte 0
|
||||
|
||||
.byte "CHR0"
|
||||
.dword $2000
|
||||
.include "hello_chr.oph"
|
159
examples/kinematics.oph
Normal file
159
examples/kinematics.oph
Normal file
|
@ -0,0 +1,159 @@
|
|||
.include "../platform/c64header.oph"
|
||||
.include "../platform/c64kernal.oph"
|
||||
|
||||
* `print_str angle_prompt
|
||||
jsr get_num
|
||||
`fp_store theta
|
||||
|
||||
;; Range-check result: 1-90
|
||||
`fp_subtract f_1
|
||||
jsr fac1_sign
|
||||
cmp #$ff
|
||||
beq -
|
||||
|
||||
`fp_load theta
|
||||
`fp_subtract f_90
|
||||
jsr fac1_sign
|
||||
cmp #$01
|
||||
beq -
|
||||
|
||||
;; Range check passes, convert to radians
|
||||
`fp_load theta
|
||||
`fp_multiply f_pi
|
||||
`fp_divide f_180
|
||||
`fp_store theta
|
||||
|
||||
* `print_str speed_prompt
|
||||
jsr get_num
|
||||
`fp_store speed
|
||||
|
||||
;; Range-check result: 1-100
|
||||
`fp_subtract f_1
|
||||
jsr fac1_sign
|
||||
cmp #$ff
|
||||
beq -
|
||||
|
||||
`fp_load speed
|
||||
`fp_subtract f_100
|
||||
jsr fac1_sign
|
||||
cmp #$01
|
||||
beq -
|
||||
|
||||
`fp_load theta
|
||||
jsr sin_fac1
|
||||
`fp_multiply speed
|
||||
`fp_store v_y
|
||||
|
||||
`fp_load theta
|
||||
jsr cos_fac1
|
||||
`fp_multiply speed
|
||||
`fp_store v_x
|
||||
|
||||
;; Compute impact time
|
||||
`fp_load v_y
|
||||
`print_str impact_time_1
|
||||
`fp_divide f_0_5
|
||||
`fp_divide f_9_8
|
||||
`fp_store time
|
||||
jsr fac1out
|
||||
`print_str impact_time_2
|
||||
|
||||
`print_str impact_point_1
|
||||
`fp_load time
|
||||
`fp_multiply v_x
|
||||
jsr fac1out
|
||||
`print_str impact_point_2
|
||||
|
||||
`print_str height_1
|
||||
`fp_load f_0_5
|
||||
`fp_multiply v_y
|
||||
`fp_multiply v_y
|
||||
`fp_divide f_9_8
|
||||
jsr fac1out
|
||||
`print_str impact_point_2
|
||||
|
||||
rts
|
||||
|
||||
angle_prompt:
|
||||
.byte "CHOOSE FIRING ANGLE (1-90): ",0
|
||||
|
||||
speed_prompt:
|
||||
.byte "CHOOSE FIRING SPEED (1-100): ",0
|
||||
|
||||
impact_time_1:
|
||||
.byte "IMPACT AT ",0
|
||||
|
||||
impact_time_2:
|
||||
.byte " SECONDS",13,0
|
||||
|
||||
impact_point_1:
|
||||
.byte "IMPACT AT ",0
|
||||
|
||||
impact_point_2:
|
||||
.byte " METERS",13,0
|
||||
|
||||
height_1:
|
||||
.byte "MAXIMUM HEIGHT OF ",0
|
||||
|
||||
f_0_125: .cbmfloat "0.125"
|
||||
f_9_8: .cbmfloat "9.8"
|
||||
f_90: .cbmfloat "90"
|
||||
f_100: .cbmfloat "100"
|
||||
f_180: .cbmfloat "180"
|
||||
|
||||
get_num:
|
||||
.scope
|
||||
lda #$00 ; Turn on blinky cursor
|
||||
sta $cc
|
||||
sta numindx
|
||||
_lp: jsr getin
|
||||
cmp #$14 ; DEL?
|
||||
bne +
|
||||
ldx numindx
|
||||
beq _lp
|
||||
dex
|
||||
stx numindx
|
||||
jsr $ffd2
|
||||
jmp _lp
|
||||
* cmp #$0d ; RETURN?
|
||||
bne +
|
||||
ldx numindx
|
||||
beq _lp
|
||||
bne _got
|
||||
* cmp #'0 ; digit?
|
||||
bcc _lp
|
||||
cmp #'9+1
|
||||
bcs _lp
|
||||
ldx numindx ; Room for character?
|
||||
cpx #$0f
|
||||
beq _lp
|
||||
sta numbuf,x
|
||||
inx
|
||||
stx numindx
|
||||
jsr $ffd2
|
||||
jmp _lp
|
||||
_got: ldx numindx
|
||||
lda #$00
|
||||
sta numbuf,x
|
||||
lda #$01 ; Disable blinky cursor again
|
||||
sta $cc
|
||||
lda #$20
|
||||
jsr $ffd2
|
||||
lda #$0d
|
||||
jsr $ffd2
|
||||
lda #<numbuf
|
||||
ldy #>numbuf
|
||||
jmp ld_fac1_string
|
||||
.scend
|
||||
|
||||
.include "../platform/libbasic64.oph"
|
||||
|
||||
;;; Post-program data space
|
||||
|
||||
.space numindx 1
|
||||
.space numbuf 16
|
||||
.space speed 5
|
||||
.space theta 5
|
||||
.space v_x 5
|
||||
.space v_y 5
|
||||
.space time 5
|
13
examples/stella/README.txt
Normal file
13
examples/stella/README.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
"Hi Stella" is a simple "Hello World" program for the "Stella" chip,
|
||||
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
|
||||
the screen with some rolling color bars.
|
||||
|
||||
A more sophisticated program is colortest, which lets the user
|
||||
explore the 128 colors provided by the system. Use up and down
|
||||
to move the color value by 2, and left and right to move it
|
||||
by 16. (The lowest bit in the color value byte is ignored, for
|
||||
a total of 128 colors available.)
|
391
examples/stella/colortest.oph
Normal file
391
examples/stella/colortest.oph
Normal file
|
@ -0,0 +1,391 @@
|
|||
;;; ---------- COLOR TEST ----------
|
||||
;;; Michael Martin, 2014
|
||||
;;;
|
||||
;;; This is a sample program for the Atari 2600 VCS that lets you
|
||||
;;; explore the 128-color palette the system provides. This is
|
||||
;;; presented mainly as a more sophisticated example program to
|
||||
;;; supplement "hi_stella".
|
||||
;;;
|
||||
;;; It makes use of every graphical element but the Ball, and also
|
||||
;;; makes use of multicolor asymmetric playfields.
|
||||
|
||||
.require "../../platform/stella.oph"
|
||||
.outfile "colortest.bin"
|
||||
|
||||
.data
|
||||
.org $0080
|
||||
.space startcol 1 ; Starting color for the striped playfield
|
||||
.space subrow 1 ; Counting lines per "tall pixel"
|
||||
.space curcol 1 ; The color number we are focusing on at the moment
|
||||
.space high_nybble 2 ; Pointer to graphic data for 16s hexit
|
||||
.space low_nybble 2 ; Pointer to graphic data for ones hexit
|
||||
.space input_allowed 1 ; Flag for whether or not to ignore input.
|
||||
|
||||
.text
|
||||
;; Start at $f800 - 2KB ROMs are the smallest size available.
|
||||
.org $f800
|
||||
|
||||
reset: `clean'start
|
||||
|
||||
;; We offer 1c as the initial color. It's a nice yellow shade.
|
||||
lda #$1c
|
||||
sta curcol
|
||||
|
||||
|
||||
frame: `vertical'sync ; Beginning of the frame. Set up the timer
|
||||
lda #43 ; to count out the length of VBLANK while
|
||||
sta TIM64T ; we do the processing for the display.
|
||||
|
||||
;; Place the player and missile graphics appropriately. We
|
||||
;; count cycles and write the missile and player reset registers
|
||||
;; at the closest times we can manage. Due to the way the TIA
|
||||
;; timing works, the formula for the pixel they will show up at
|
||||
;; is N*3-55+P, where N is the number of cycles from the end of
|
||||
;; the latest STA WSYNC and the end of the STA RES* instruction,
|
||||
;; and P is 1 for player sprites and 0 for missiles and the ball.
|
||||
;;
|
||||
;; The line after that we can strobe HMOVE to adjust them the
|
||||
;; rest of the way into place.
|
||||
;;
|
||||
;; We will be using the missiles to draw the left and right
|
||||
;; sides of a largish square and the player sprites to display a
|
||||
;; byte value (the current color) as two hex digits (one per
|
||||
;; player). All the rest of our graphics will be done via the
|
||||
;; playfield registers.
|
||||
;;
|
||||
;; The missile graphics are being targeted to pixels 40 and 116
|
||||
;; and will be 4 pixels wide each. The player graphics will be
|
||||
;; 8 pixels wide and are targeting pixels 72 and 80.
|
||||
sta WSYNC
|
||||
sta WSYNC ; = 0
|
||||
ldy #$06 ; +2 = 2
|
||||
* dey ; +2 = 4- 9-14-19-24-29
|
||||
bne - ; +3 = 7-12-17-22-27-31
|
||||
sta RESM0 ; +3 = 34 (31*3-55 = 38. Needs to move 2 pixels right.)
|
||||
lda #$E0 ; +2 = 36
|
||||
sta HMCLR ; +3 = 39 - reset the fine-move registers
|
||||
sta HMM0 ; +3 = 42 - set M0 to move 2 right
|
||||
sta RESP0 ; +3 = 45 (42*3-54 = 72. Placed perfectly.)
|
||||
sta RESP1 ; +3 = 48 (45*3-54 = 81. Needs to move 1 pixel left.)
|
||||
lda #$10 ; +2 = 50
|
||||
sta HMP1 ; +3 = 53 - and set P1 to move 1 left.
|
||||
nop ; +2 = 55
|
||||
nop ; +2 = 57
|
||||
sta RESM1 ; +3 = 60 (57*3-55 = 116. Placed perfectly.)
|
||||
sta WSYNC
|
||||
sta HMOVE ; Next scanline, execute the fine moves.
|
||||
|
||||
lda #$20
|
||||
sta NUSIZ0 ; Quad-size missiles, single copy of single player
|
||||
sta NUSIZ1 ; M1 and P1 are the same
|
||||
|
||||
;; Read the input
|
||||
lda #$00
|
||||
sta SWACNT
|
||||
lda SWCHA
|
||||
bit input_allowed
|
||||
bmi true_input_read
|
||||
;; Wait for neutral stick so we can re-enable input.
|
||||
and #$f0
|
||||
cmp #$f0
|
||||
bne input_done
|
||||
;; Bits are set if the direction isn't active, so we only get
|
||||
;; here if the stick was neutral. this also means the accumulator
|
||||
;; has #$f0 in it now, which means we can store it directly and
|
||||
;; the BIT/BMI above will start succeeding next frame.
|
||||
sta input_allowed
|
||||
beq input_done
|
||||
true_input_read:
|
||||
;; Now we rotate it through the carry bit to see what
|
||||
;; direction was pushed. We advance the color 2 or 16 at a time,
|
||||
;; depending. (The least significant bit in the color register is
|
||||
;; the one ignored, so we are not missing anything here.)
|
||||
ror ; Skip P2 input
|
||||
ror
|
||||
ror
|
||||
ror
|
||||
ror ; Carry clear if up
|
||||
bcs +
|
||||
inc curcol ; If up, increase color by 2
|
||||
inc curcol
|
||||
jmp input_found
|
||||
* ror ; Carry clear if down
|
||||
bcs +
|
||||
dec curcol ; If down, decrease color by 2
|
||||
dec curcol
|
||||
jmp input_found
|
||||
* ror ; Carry clear if left
|
||||
bcs +
|
||||
lda curcol
|
||||
sec
|
||||
sbc #$10 ; Left decreases color by 16
|
||||
sta curcol
|
||||
jmp input_found
|
||||
* ror ; Carry clear if right
|
||||
bcs input_done
|
||||
lda curcol
|
||||
adc #$10 ; Right increases color by 16
|
||||
sta curcol
|
||||
input_found:
|
||||
lda #$00
|
||||
sta input_allowed
|
||||
|
||||
input_done:
|
||||
;; Clear the playfield while we wait, and make it asymmetric
|
||||
lda #$00
|
||||
sta PF0
|
||||
sta PF1
|
||||
sta PF2
|
||||
sta CTRLPF
|
||||
|
||||
;; alter playfield color so we get a rotating effect
|
||||
dec startcol
|
||||
|
||||
;; prepare numeric sprite values
|
||||
lda curcol
|
||||
lsr
|
||||
lsr
|
||||
lsr
|
||||
lsr
|
||||
tay
|
||||
lda digits_low, y
|
||||
sta high_nybble
|
||||
lda curcol
|
||||
and #$0f
|
||||
tay
|
||||
lda digits_low, y
|
||||
sta low_nybble
|
||||
lda #$ff
|
||||
sta low_nybble+1
|
||||
sta high_nybble+1
|
||||
|
||||
;; Wait for VBLANK to finish, then turn off the VBLANK signal.
|
||||
* lda INTIM
|
||||
bne -
|
||||
sta WSYNC
|
||||
sta VBLANK
|
||||
|
||||
;; Display kernel.
|
||||
;; Top blank: 4 lines
|
||||
ldx #4
|
||||
stx subrow
|
||||
* sta WSYNC
|
||||
dex
|
||||
bne -
|
||||
|
||||
;; Header graphics: 20 lines
|
||||
ldy #5
|
||||
ldx startcol
|
||||
header_loop:
|
||||
sta WSYNC
|
||||
stx COLUPF ; +3 = 3
|
||||
lda pf0_left-1,y ; +4 = 7
|
||||
sta PF0 ; +3 = 10
|
||||
lda pf1_left-1,y ; +4 = 14
|
||||
sta PF1 ; +3 = 17
|
||||
lda pf2_left-1,y ; +4 = 21
|
||||
sta PF2 ; +3 = 24
|
||||
cmp $80 ; +3 = 27 (3-cycle no-op)
|
||||
lda pf0_right-1,y ; +4 = 31
|
||||
sta PF0 ; +3 = 34
|
||||
lda pf1_right-1,y ; +4 = 38
|
||||
sta PF1 ; +3 = 41
|
||||
lda pf2_right-1,y ; +4 = 45
|
||||
sta PF2 ; +3 = 48 ** MUST STORE PF2 2ND TIME ON EXACTLY CYCLE 48 **
|
||||
inx ; +2 = 50
|
||||
inx ; +2 = 52
|
||||
dec subrow ; +5 = 57
|
||||
bne header_loop ; +2 = 59
|
||||
dey ; +2 = 61
|
||||
beq header_done ; +2 = 63
|
||||
lda #4 ; +2 = 65
|
||||
sta subrow ; +3 = 68
|
||||
bne header_loop ; +3 = 71
|
||||
;; We've cut it very fine here! We only have 76 cycles per
|
||||
;; scanline and we use nearly all of them.
|
||||
header_done:
|
||||
;; Ruled split between title and data (8 lines)
|
||||
ldy #$00 ; Clear playfield now that we're done (+2 = 72)
|
||||
ldx #$0c ; Default status color is light grey (+2 = 74)
|
||||
sta WSYNC ; Rest of previous line
|
||||
sty PF0
|
||||
sty PF1
|
||||
sty PF2
|
||||
stx COLUPF
|
||||
stx COLUP0
|
||||
stx COLUP1
|
||||
dey
|
||||
ldx #$f0
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
stx PF0 ; Fill playfield completely
|
||||
sty PF1
|
||||
sty PF2
|
||||
|
||||
ldy #$01
|
||||
sty CTRLPF ; Symmetric PF
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
dey
|
||||
sty PF0 ; Clear playfield again
|
||||
sty PF1
|
||||
sty PF2
|
||||
|
||||
ldy #$08 ; 32 lines (for letters; 8, 16, 8)
|
||||
* sta WSYNC
|
||||
dey
|
||||
bne -
|
||||
ldy #$05
|
||||
* lda (high_nybble), y
|
||||
sta GRP0
|
||||
lda (low_nybble), y
|
||||
sta GRP1
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
dey
|
||||
bpl -
|
||||
iny
|
||||
sty GRP0
|
||||
sty GRP1
|
||||
ldy #$0A
|
||||
* sta WSYNC
|
||||
dey
|
||||
bne -
|
||||
|
||||
;; Top border (12 lines)
|
||||
lda #$03
|
||||
sta PF1
|
||||
lda #$ff
|
||||
sta PF2
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
ldx #$00
|
||||
stx PF1
|
||||
stx PF2
|
||||
ldx #$02 ; Turn on walls (the missiles)
|
||||
stx ENAM0
|
||||
stx ENAM1
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta PF2
|
||||
lda curcol
|
||||
sta COLUPF
|
||||
|
||||
;; Color blob (96 lines)
|
||||
ldx #96
|
||||
* sta WSYNC
|
||||
dex
|
||||
bne -
|
||||
|
||||
;; Bottom border (12 lines)
|
||||
stx PF2
|
||||
lda #$0c
|
||||
sta COLUPF
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
stx ENAM0 ; Turn off walls (the missles)
|
||||
stx ENAM1
|
||||
lda #$03
|
||||
sta PF1
|
||||
lda #$ff
|
||||
sta PF2
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
stx PF1
|
||||
stx PF2
|
||||
ldx #$0a
|
||||
* sta WSYNC
|
||||
dex
|
||||
bne -
|
||||
|
||||
; Turn on VBLANK, do 30 lines of Overscan
|
||||
lda #$02
|
||||
sta VBLANK
|
||||
ldy #30
|
||||
* sta WSYNC
|
||||
dey
|
||||
bne -
|
||||
jmp frame ; And the frame is done, back to VSYNC.
|
||||
|
||||
;;; Graphical data. Notice that we have to start not on a page
|
||||
;;; boundary, but with all graphics in each group on one page.
|
||||
.advance $FF01
|
||||
pf0_left:
|
||||
.byte $e0,$20,$20,$20,$e0
|
||||
|
||||
pf1_left:
|
||||
.byte $77,$54,$54,$54,$74
|
||||
|
||||
pf2_left:
|
||||
.byte $ae,$6a,$ea,$aa,$ee
|
||||
|
||||
pf0_right:
|
||||
.byte $00,$00,$00,$00,$00
|
||||
|
||||
pf1_right:
|
||||
.byte $4e,$48,$4c,$48,$ee
|
||||
|
||||
pf2_right:
|
||||
.byte $27,$24,$27,$21,$77
|
||||
|
||||
;; We don't need a digits_high. It's always $FF!
|
||||
digits_low:
|
||||
.byte <digit_0, <digit_1, <digit_2, <digit_3
|
||||
.byte <digit_4, <digit_5, <digit_6, <digit_7
|
||||
.byte <digit_8, <digit_9, <digit_a, <digit_b
|
||||
.byte <digit_c, <digit_d, <digit_e, <digit_f
|
||||
|
||||
digit_0:
|
||||
.byte $3c,$66,$76,$6e,$66,$3c
|
||||
digit_1:
|
||||
.byte $7e,$18,$18,$18,$38,$18
|
||||
digit_2:
|
||||
.byte $7e,$30,$18,$0c,$66,$3c
|
||||
digit_3:
|
||||
.byte $3c,$66,$0c,$18,$0c,$7e
|
||||
digit_4:
|
||||
.byte $0c,$7e,$6c,$3c,$1c,$0c
|
||||
digit_5:
|
||||
.byte $3c,$66,$06,$7c,$60,$7e
|
||||
digit_6:
|
||||
.byte $3c,$66,$66,$7c,$60,$3c
|
||||
digit_7:
|
||||
.byte $30,$30,$18,$0c,$06,$7e
|
||||
digit_8:
|
||||
.byte $3c,$66,$66,$3c,$66,$3c
|
||||
digit_9:
|
||||
.byte $38,$0c,$06,$3e,$66,$3c
|
||||
digit_a:
|
||||
.byte $66,$7e,$66,$66,$3c,$18
|
||||
digit_b:
|
||||
.byte $7c,$66,$66,$7c,$66,$7c
|
||||
digit_c:
|
||||
.byte $3c,$66,$60,$60,$66,$3c
|
||||
digit_d:
|
||||
.byte $78,$6c,$66,$66,$6c,$78
|
||||
digit_e:
|
||||
.byte $7e,$60,$60,$7c,$60,$7e
|
||||
digit_f:
|
||||
.byte $60,$60,$60,$7c,$60,$7e
|
||||
;;; Interrupt vectors.
|
||||
.advance $FFFA
|
||||
.word reset, reset, reset
|
151
examples/stella/hi_stella.oph
Normal file
151
examples/stella/hi_stella.oph
Normal file
|
@ -0,0 +1,151 @@
|
|||
.require "../../platform/stella.oph"
|
||||
.outfile "hi_stella.bin"
|
||||
|
||||
.data
|
||||
.org $0080
|
||||
.space col'0 1
|
||||
.space col'1 1
|
||||
.space temp 1
|
||||
.space counter 1
|
||||
|
||||
.text
|
||||
.org $F800
|
||||
|
||||
reset: `clean'start
|
||||
|
||||
; Initialize the player sprites.
|
||||
|
||||
; We're going to use quad-sized players for our
|
||||
; letters, and each one is 5 notional pixels wide.
|
||||
; When we write RESP*, the start cycle after WSYNC
|
||||
; determines the pixel it appears at with the
|
||||
; formula 3N - 54 (minimum 1). Cycles 36 and 44
|
||||
; get us close. We end up 4 pixels too far left.
|
||||
|
||||
; While we wait, we set up our sprites to be
|
||||
; quad-sized and initialize the independent
|
||||
; color-counters for each sprite.
|
||||
|
||||
sta WSYNC
|
||||
lda #$07 ; +2= 2
|
||||
sta NUSIZ0 ; +3= 5
|
||||
sta NUSIZ1 ; +3= 8
|
||||
lda #$20 ; +2=10
|
||||
ldy #5 ; +2=12
|
||||
* dey
|
||||
bne - ; 17-22-27-32-36
|
||||
sta RESP0 ; +3=39 (P0 at pixel 54)
|
||||
sta col'0 ; +3=42
|
||||
eor #$80 ; +2=44
|
||||
sta RESP1 ; (P1 at pixel 78)
|
||||
sta col'1
|
||||
sta temp
|
||||
lda #$C0 ; HMOVE us 4 right
|
||||
sta HMP0
|
||||
sta HMP1
|
||||
sta WSYNC
|
||||
sta HMOVE
|
||||
|
||||
frame: `vertical'sync
|
||||
lda #43
|
||||
sta TIM64T
|
||||
|
||||
; Advance the color bars once every fourth frame.
|
||||
inc counter
|
||||
lda #$03
|
||||
bit counter
|
||||
bne +
|
||||
inc col'0
|
||||
dec col'1
|
||||
|
||||
; Wait for VBLANK to finish, then turn off the VBLANK signal.
|
||||
* lda INTIM
|
||||
bne -
|
||||
sta WSYNC
|
||||
sta VBLANK
|
||||
|
||||
; Kernel. 78 blank lines on each side of
|
||||
; a 36-line letter. The 'letter kernel' is a 4-line
|
||||
; kernel at the top level, and expects .A and .X
|
||||
; to have the player 0 and 1 colors at the start.
|
||||
; We set those during the top wait, because why not.
|
||||
lda col'0
|
||||
ldx col'1
|
||||
|
||||
; Wait 78 lines for vertical placement...
|
||||
ldy #78
|
||||
* sta WSYNC
|
||||
dey
|
||||
bne -
|
||||
|
||||
; And draw the letter. This is a 4-line kernel, but
|
||||
; the colors update every line. To keep that working
|
||||
; we need to juggle the registers a bit. The accumulator
|
||||
; _starts_ with P0's color, but it's needed to load the
|
||||
; graphics, so we stash it in TEMP first.
|
||||
ldy #9
|
||||
* sta COLUP0
|
||||
stx COLUP1
|
||||
sta temp
|
||||
lda hgr-1, y
|
||||
sta GRP0
|
||||
lda igr-1, y
|
||||
sta GRP1
|
||||
|
||||
; Now, to make the lines update cleanly, we want a fast
|
||||
; incrementer, but with two counters, we need to stash
|
||||
; away .Y. Fortunately, the accumulator was just trashed
|
||||
; by the graphics loads and so is available for this.
|
||||
tya
|
||||
ldy temp
|
||||
inx
|
||||
iny
|
||||
sta WSYNC ; Go through three more lines,
|
||||
sty COLUP0 ; updating the colors and bumping
|
||||
stx COLUP1 ; the counters.
|
||||
inx
|
||||
iny
|
||||
sta WSYNC
|
||||
sty COLUP0
|
||||
stx COLUP1
|
||||
inx
|
||||
iny
|
||||
sta WSYNC
|
||||
sty COLUP0
|
||||
stx COLUP1
|
||||
inx ; .X is only touched here, so we
|
||||
iny ; can keep it around, but .Y is
|
||||
sty temp ; our line count. Use temp again
|
||||
tay ; to trade back .A and .Y, which
|
||||
lda temp ; also preps .A for the color write
|
||||
sta WSYNC ; at the top of the whole 4-line
|
||||
dey ; loop.
|
||||
bne -
|
||||
|
||||
; Clear out the player graphics...
|
||||
lda #$00
|
||||
sta GRP0
|
||||
sta GRP1
|
||||
|
||||
; Wait 78 lines for the rest of the screen...
|
||||
ldy #78
|
||||
* sta WSYNC
|
||||
dey
|
||||
bne -
|
||||
|
||||
; Turn on VBLANK, do 30 lines of Overscan
|
||||
lda #$02
|
||||
sta VBLANK
|
||||
ldy #30
|
||||
* sta WSYNC
|
||||
dey
|
||||
bne -
|
||||
jmp frame ; And the frame is done, back to VSYNC.
|
||||
|
||||
; Graphics for the letters.
|
||||
hgr: .byte $88,$88,$88,$88,$f8,$88,$88,$88,$88
|
||||
igr: .byte $f8,$20,$20,$20,$20,$20,$20,$20,$f8
|
||||
|
||||
; Interrupt vectors.
|
||||
.advance $FFFA
|
||||
.word reset, reset, reset
|
232
examples/structuredemo.oph
Normal file
232
examples/structuredemo.oph
Normal file
|
@ -0,0 +1,232 @@
|
|||
.include "../platform/c64_0.oph"
|
||||
.require "../platform/c64kernal.oph"
|
||||
.outfile "structuredemo.prg"
|
||||
|
||||
jsr print'unsorted
|
||||
jsr insertion'sort
|
||||
jsr print'list
|
||||
rts
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Linked list data: head, next, lb, hb.
|
||||
; lb/hb: Low/high bytes of the data array. These are immutable and
|
||||
; kept with the program text.
|
||||
; head: Array index of the first element in the list, or #$FF if the
|
||||
; list is empty
|
||||
; next: Array of successor indices. If you've just read element X,
|
||||
; the value of memory location next+X is the index of the
|
||||
; next element. If next is #$FF, you've reached the end of
|
||||
; the list.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
.data
|
||||
.org $C000
|
||||
.space head 1
|
||||
.space next 16
|
||||
|
||||
.text
|
||||
lb: .byte <$838,<$618,<$205,<$984,<$724,<$301,<$249,<$946
|
||||
.byte <$925,<$043,<$114,<$697,<$985,<$633,<$312,<$086
|
||||
hb: .byte >$838,>$618,>$205,>$984,>$724,>$301,>$249,>$946
|
||||
.byte >$925,>$043,>$114,>$697,>$985,>$633,>$312,>$086
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; insertion'sort: Sorts the list defined by head, next, hb, lb.
|
||||
; Arguments: None.
|
||||
; Modifies: All registers destroyed, head and next array sorted.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
insertion'sort:
|
||||
lda #$FF ; Clear list by storing the terminator in 'head'
|
||||
sta head
|
||||
ldx #$0 ; Loop through the lb/hb array, adding each
|
||||
insertion'sort'loop: ; element one at a time
|
||||
txa
|
||||
pha
|
||||
jsr insert_elt
|
||||
pla
|
||||
tax
|
||||
inx
|
||||
cpx #$10
|
||||
bne insertion'sort'loop
|
||||
rts
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; insert_elt: Insert an element into the linked list. Maintains the
|
||||
; list in sorted, ascending order. Used by
|
||||
; insertion'sort.
|
||||
; Arguments: X register holds the index of the element to add.
|
||||
; Modifies: All registers destroyed; head and next arrays updated
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
.data
|
||||
.space lbtoinsert 1
|
||||
.space hbtoinsert 1
|
||||
.space indextoinsert 1
|
||||
|
||||
.text
|
||||
|
||||
insert_elt:
|
||||
ldy head ; If the list is empty, make
|
||||
cpy #$FF ; head point at it, and return.
|
||||
bne insert_elt'list'not'empty
|
||||
stx head
|
||||
tya
|
||||
sta next,x
|
||||
rts
|
||||
insert_elt'list'not'empty:
|
||||
lda lb,x ; Cache the data we're inserting
|
||||
sta lbtoinsert
|
||||
lda hb,x
|
||||
sta hbtoinsert
|
||||
stx indextoinsert
|
||||
ldy head ; Compare the first value with
|
||||
sec ; the data. If the data must
|
||||
lda lb,y ; be inserted at the front...
|
||||
sbc lbtoinsert
|
||||
lda hb,y
|
||||
sbc hbtoinsert
|
||||
bmi insert_elt'not'smallest
|
||||
tya ; Set its next pointer to the
|
||||
sta next,x ; old head, update the head
|
||||
stx head ; pointer, and return.
|
||||
rts
|
||||
insert_elt'not'smallest:
|
||||
ldx head
|
||||
insert_elt'loop: ; At this point, we know that
|
||||
lda next,x ; argument > data[X].
|
||||
tay
|
||||
cpy #$FF ; if next[X] = #$FF, insert arg at end.
|
||||
beq insert_elt'insert'after'current
|
||||
lda lb,y ; Otherwise, compare arg to
|
||||
sec ; data[next[X]]. If we insert
|
||||
sbc lbtoinsert ; before that...
|
||||
lda hb,y
|
||||
sbc hbtoinsert
|
||||
bmi insert_elt'goto'next
|
||||
insert_elt'insert'after'current: ; Fix up all the next links
|
||||
tya
|
||||
ldy indextoinsert
|
||||
sta next,y
|
||||
tya
|
||||
sta next,x
|
||||
rts ; and return.
|
||||
insert_elt'goto'next: ; Otherwise, let X = next[X]
|
||||
tya ; and go looping again.
|
||||
tax
|
||||
jmp insert_elt'loop
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; print'unsorted: Steps through the data array and prints each value.
|
||||
; Standalone procedure.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
print'unsorted:
|
||||
lda #<unsorted'hdr
|
||||
ldx #>unsorted'hdr
|
||||
jsr put'string
|
||||
ldy #$00
|
||||
print'unsorted'loop:
|
||||
lda hb, Y
|
||||
jsr print'hex
|
||||
lda lb, y
|
||||
jsr print'hex
|
||||
lda #$20
|
||||
jsr chrout
|
||||
iny
|
||||
cpy #$10
|
||||
bne print'unsorted'loop
|
||||
lda #$0D
|
||||
jsr chrout
|
||||
rts
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; print'list: Starts at head, and prints out every value in the
|
||||
; linked list.
|
||||
; Standalone procedure.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
print'list:
|
||||
lda #<sorted'hdr
|
||||
ldx #>sorted'hdr
|
||||
jsr put'string
|
||||
ldy head
|
||||
print'list'loop:
|
||||
cpy #$FF
|
||||
beq print'list'done
|
||||
lda hb, y
|
||||
jsr print'hex
|
||||
lda lb, y
|
||||
jsr print'hex
|
||||
lda #$20
|
||||
jsr chrout
|
||||
lda next, Y
|
||||
tay
|
||||
jmp print'list'loop
|
||||
print'list'done:
|
||||
lda #$0d
|
||||
jsr chrout
|
||||
rts
|
||||
|
||||
;; String data for the above routines.
|
||||
|
||||
unsorted'hdr:
|
||||
.byte 147 ; Clear screen first!
|
||||
.byte "UNSORTED DATA:",13,0
|
||||
|
||||
sorted'hdr:
|
||||
.byte "SORTED DATA:",13,0
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; print'hex: outputs a two-character hex representation of a one-
|
||||
; byte value.
|
||||
; Arguments: Byte to print in accumulator
|
||||
; Modifies: .A and .X
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
print'hex:
|
||||
pha
|
||||
clc
|
||||
lsr
|
||||
lsr
|
||||
lsr
|
||||
lsr
|
||||
tax
|
||||
lda hexstr,x
|
||||
jsr chrout
|
||||
pla
|
||||
and #$0F
|
||||
tax
|
||||
lda hexstr,X
|
||||
jsr chrout
|
||||
rts
|
||||
|
||||
; Character data array for print'hex.
|
||||
hexstr: .byte "0123456789ABCDEF"
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; put'string: outputs a C-style null terminated string with length
|
||||
; less than 256 to the screen. If 256 bytes are written
|
||||
; without finding a terminator, the routine ends quietly.
|
||||
; Arguments: Low byte of string address in .A, high byte in .X
|
||||
; Modifies: .A and .Y
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
.data zp
|
||||
.space put'string'addr 2
|
||||
|
||||
.text
|
||||
put'string:
|
||||
sta put'string'addr
|
||||
stx put'string'addr+1
|
||||
ldy #$00
|
||||
put'string'loop:
|
||||
lda (put'string'addr),y
|
||||
beq put'string'done
|
||||
jsr chrout
|
||||
iny
|
||||
bne put'string'loop
|
||||
put'string'done:
|
||||
rts
|
||||
|
42
platform/README.txt
Normal file
42
platform/README.txt
Normal file
|
@ -0,0 +1,42 @@
|
|||
This directory holds files likely to be of use to you in developing your own
|
||||
programs. The contents of each file is summarized below.
|
||||
|
||||
|
||||
c64_0.oph: A Commodore 64 equivalent to a modern compiler's "crt0.s" - it
|
||||
contains a .PRG file header, a short BASIC program that launches
|
||||
the machine language program, and a prologue and epilogue that
|
||||
prepare memory for your use and then clean it up again when you
|
||||
are done. Memory locations $02 through $8F on the zero page are
|
||||
available for your use, and the program lives at the beginning
|
||||
a contiguous block of RAM from $0800 through $CFFF. The BASIC
|
||||
ROM is swapped out of memory (leaving $A000-$BFFF as RAM) for
|
||||
the duration of your program. BASIC's working storage on the
|
||||
zero page is backed up in the RAM underneath the KERNAL ROM
|
||||
while your program runs.
|
||||
|
||||
c64kernal.oph: A collection of standard aliases for the KERNAL routines on the
|
||||
Commodore 64. Names for these routines have been chosen to match
|
||||
the Commodore 64 Programmer's Reference Guide. Additional useful
|
||||
constants are defined for the character codes for color changes
|
||||
and case-changing.
|
||||
|
||||
libbasic64.oph:A still-experimental set of macros and routines for exploiting
|
||||
the software floating point routines in the Commodore 64
|
||||
BASIC ROM.
|
||||
|
||||
c64header.oph: A much simpler Commodore 64 header that does nothing but jump
|
||||
directly to your code. Useful for small programs or those that
|
||||
intend to interface with BASIC.
|
||||
|
||||
vic20.oph: A simple header for the unexpanded VIC-20. Equivalent in
|
||||
behavior to c64header.oph.
|
||||
|
||||
vic20x.oph: A simple header like the two above, but for expanded VIC-20.
|
||||
|
||||
nes.oph: A somewhat skeletal collection of aliases for the PPU registers
|
||||
on the Nintendo Entertainment System. These names were chosen
|
||||
to match the constant names given on the NESdev Wiki.
|
||||
|
||||
stella.oph: A collection of aliases for the registers of the Atari 2600.
|
||||
These names were taken from the "Stella Programmer's Guide" and
|
||||
are in wide use amongst developers and code analysts alike.
|
78
platform/c64_0.oph
Normal file
78
platform/c64_0.oph
Normal file
|
@ -0,0 +1,78 @@
|
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;
|
||||
;; Commodore 64 Basic Runtime File
|
||||
;;
|
||||
;; Include this at the TOP of your C64 program, and it will handle
|
||||
;; hiding away the BASIC ROM and data and restoring it at the end.
|
||||
;;
|
||||
;; You will have a contiguous block of RAM from $0800 to $CFFF, and
|
||||
;; Zero Page access from $02 to $8F in the segment "zp".
|
||||
|
||||
.include "c64header.oph"
|
||||
|
||||
.data zp ; Zero Page memory segment.
|
||||
.org $0002
|
||||
|
||||
.text
|
||||
|
||||
.scope
|
||||
; Cache BASIC zero page underneath the KERNAL, while also
|
||||
; making RAM copies of the NMI routines
|
||||
ldx #$00
|
||||
* lda $00, x
|
||||
sta $e000, x
|
||||
lda $fe00, x
|
||||
sta $fe00, x
|
||||
lda $ff00, x
|
||||
sta $ff00, x
|
||||
inx
|
||||
bne -
|
||||
|
||||
; Swap out the BASIC ROM for RAM
|
||||
lda $01
|
||||
and #$fe
|
||||
ora #$06
|
||||
sta $01
|
||||
|
||||
; Run the real program
|
||||
jsr _main
|
||||
|
||||
; Swap out KERNAL to expose cached BASIC ZP values
|
||||
; Block IRQs during this period. NMIs cannot be blocked,
|
||||
; but we copied enough of the processing code into the
|
||||
; RAM under the KERNAL that we can disable NMI processing
|
||||
; during this period
|
||||
sei ; Disable IRQs
|
||||
lda #$c1 ; Defang NMIs
|
||||
sta $318
|
||||
|
||||
lda $01 ; Swap out KERNAL
|
||||
and #$fd
|
||||
sta $01
|
||||
|
||||
; Restore BASIC zero page
|
||||
ldx #$8E
|
||||
* lda $e001, x
|
||||
sta $01, x
|
||||
dex
|
||||
bne -
|
||||
|
||||
; Restore BASIC ROM, KERNAL, and interrupts
|
||||
lda $01
|
||||
ora #$07
|
||||
sta $01
|
||||
lda #$47 ; Restore NMI vector
|
||||
sta $318
|
||||
cli ; Re-enable interrupts
|
||||
|
||||
; Back to BASIC. We do this by clearing the keyboard
|
||||
; buffer and then jumping through the warm start
|
||||
; vector. This will more cleanly handle case where
|
||||
; the program has somehow modified BASIC's state,
|
||||
; such as running through PUCRUNCH or a onefiler.
|
||||
stx $c6 ; .X is zero from previous loop
|
||||
jmp ($a002)
|
||||
|
||||
_main:
|
||||
; Program follows...
|
||||
.scend
|
10
platform/c64header.oph
Normal file
10
platform/c64header.oph
Normal file
|
@ -0,0 +1,10 @@
|
|||
.word $0801
|
||||
.org $0801
|
||||
|
||||
; BASIC program that just calls our machine language code
|
||||
.scope
|
||||
.word _next, 10 ; Next line and current line number
|
||||
.byte $9e," 2062",0 ; SYS 2062
|
||||
_next: .word 0 ; End of program
|
||||
.scend
|
||||
; Program follows...
|
67
platform/c64kernal.oph
Normal file
67
platform/c64kernal.oph
Normal file
|
@ -0,0 +1,67 @@
|
|||
; 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
|
287
platform/libbasic64.oph
Normal file
287
platform/libbasic64.oph
Normal file
|
@ -0,0 +1,287 @@
|
|||
;;; LIBBASIC64.OPH
|
||||
|
||||
;;; This is a collection of routines inside the BASIC ROM that can
|
||||
;;; be repurposed to do floating-point math inside your machine
|
||||
;;; language programs. It is currently VERY EXPERIMENTAL. The documentation
|
||||
;;; available for this is spotty at best and disassembly confirms that
|
||||
;;; a lot of hidden invariants may lurk.
|
||||
|
||||
;;; BASIC function equivalents. These operate on FAC1 and are pretty
|
||||
;;; clean overall. They take their input in FAC1 and put their output
|
||||
;;; there too. While it is not *guaranteed* it is probably best to
|
||||
;;; assume that these functions trash the value in FAC2.
|
||||
.alias abs_fac1 $bc58
|
||||
.alias atn_fac1 $e30e
|
||||
.alias cos_fac1 $e264
|
||||
.alias exp_fac1 $bfed
|
||||
.alias int_fac1 $bccc
|
||||
.alias log_fac1 $b9ea
|
||||
.alias rnd_fac1 $e097
|
||||
.alias sgn_fac1 $bc39
|
||||
.alias sin_fac1 $e26b
|
||||
.alias tan_fac1 $e2b4
|
||||
|
||||
;;; Getting useful information into the FACs
|
||||
|
||||
;; Treat the accumulator as a signed byte, load that value
|
||||
;; into FAC1
|
||||
.alias ld_fac1_a $bc3c
|
||||
|
||||
;; Load the signed 16-bit value with A as the *high* byte and
|
||||
;; Y as the *low* byte into FAC1. This is backwards from pretty
|
||||
;; much everything else.
|
||||
.alias ld_fac1_s16 $b391
|
||||
|
||||
;; Load a 5-byte value from memory into FAC1.
|
||||
.alias ld_fac1_mem $bba2
|
||||
|
||||
;; Copy FAC2 into FAC1.
|
||||
.alias ld_fac1_fac2 $bbfc
|
||||
|
||||
;; Translate FAC1 into a string that is at $0100.
|
||||
.alias fac1_to_string $bddd
|
||||
|
||||
;; Convert FAC1 into a 32-bit *big-endian* signed integer at
|
||||
;; $62-$65 (where the mantissa usually goes in FAC1).
|
||||
.alias fac1_to_s32 $bc9b
|
||||
|
||||
;; Store out FAC1 to $57-$5B, converting it back into the five-byte
|
||||
;; floating-point format.
|
||||
.alias fac1_to_57 $bbca
|
||||
|
||||
;; Do the same but at $5c-$60.
|
||||
.alias fac1_to_5c $bbc7
|
||||
|
||||
;; Load a 5-byte value into FAC2.
|
||||
.alias ld_fac2_mem $ba8c
|
||||
|
||||
;; Copy FAC1 to FAC2. FAC1 has some extra precision that is
|
||||
;; rounded away when you do this.
|
||||
.alias ld_fac2_fac1 $bc0c
|
||||
|
||||
;;; Comparison operator.
|
||||
;; Like sgn_fac1, but returns the -1/0/1 in the accumulator as
|
||||
;; an integer.
|
||||
.alias fac1_sign $bc2b
|
||||
|
||||
;;; FP operators. These are all FAC2 OP FAC1 with the result in FAC1.
|
||||
;;; PRECONDITIONS: All of these operations but AND and OR require you to
|
||||
;;; have the contents of $61 in the accumulator. calling one of the ld_fac*
|
||||
;;; routines will do that for you automatically. f_add_op also requires that
|
||||
;;; $6F be set properly; only ld_fac2_mem does this.
|
||||
.alias f_add_op $b86a
|
||||
.alias f_subtract_op $b853
|
||||
.alias f_multiply_op $ba2b
|
||||
.alias f_divide_op $bb12
|
||||
.alias f_pow_op $bf7b
|
||||
.alias f_and_op $afe9
|
||||
.alias f_or_op $afe6
|
||||
|
||||
;;; Memory-based FP operations. All are MEM OP FAC1. These are usually safer
|
||||
;;; than the *_op routines.
|
||||
.alias f_add_mem $b867
|
||||
.alias f_subtract_mem $b850
|
||||
.alias f_multiply_mem $ba28
|
||||
.alias f_divide_mem $bb0f
|
||||
|
||||
;;; Useful FP constants that live in the ROM. It's plausible that ld_fac1_a
|
||||
;;; or ld_fac1_s16 would be more convenient than ld_fac1_mem with f_1 or f_10,
|
||||
;;; but when doing memory-based generic stuff, these will still be useful.
|
||||
.alias f_0_5 $bf11 ; 0.5
|
||||
.alias f_1 $b9bc ; 1.0
|
||||
.alias f_pi $aea8 ; 3.1415926
|
||||
.alias f_10 $baf9 ; 10.0
|
||||
|
||||
;;; Macros for using these routines more safely.
|
||||
|
||||
;; Copy 5-byte values around in memory without touching the FACs.
|
||||
.macro f_move
|
||||
ldx #$00
|
||||
_fmvlp: lda _2,x
|
||||
sta _1,x
|
||||
inx
|
||||
cpx #$05
|
||||
bne _fmvlp
|
||||
.macend
|
||||
|
||||
;;; These next few macros really exist just to save us the trouble of loading
|
||||
;;; addresses into registers
|
||||
.macro print_str
|
||||
lda #<_1
|
||||
ldy #>_1
|
||||
jsr strout
|
||||
.macend
|
||||
|
||||
.macro ld_fac1
|
||||
lda #<_1
|
||||
ldy #>_1
|
||||
jsr ld_fac1_mem
|
||||
.macend
|
||||
|
||||
.macro ld_fac2
|
||||
lda #<_1
|
||||
ldy #>_1
|
||||
jsr ld_fac2_mem
|
||||
.macend
|
||||
|
||||
.macro st_fac1
|
||||
lda #<_1
|
||||
ldy #>_1
|
||||
jsr fac1_to_mem
|
||||
.macend
|
||||
|
||||
.macro fp_load
|
||||
`ld_fac1 _1
|
||||
.macend
|
||||
|
||||
.macro fp_store
|
||||
`st_fac1 _1
|
||||
.macend
|
||||
|
||||
.macro fp_print
|
||||
`ld_fac1 _1
|
||||
jsr fac1out
|
||||
.macend
|
||||
|
||||
.macro fp_read
|
||||
lda #<_1
|
||||
ldy #>_1
|
||||
jsr ld_fac1_string
|
||||
.macend
|
||||
|
||||
;;; Arithmetic macros. These serve mainly to make the operations work left-
|
||||
;;; to-right as one generally would prefer. They also guarantee the obscure
|
||||
;;; preconditions hold.
|
||||
|
||||
.macro fp_add
|
||||
lda #<_1
|
||||
ldy #>_1
|
||||
jsr f_add_mem
|
||||
.macend
|
||||
|
||||
.macro fp_subtract
|
||||
jsr ld_fac2_fac1
|
||||
`ld_fac1 _1
|
||||
jsr f_subtract_op
|
||||
.macend
|
||||
|
||||
.macro fp_multiply
|
||||
lda #<_1
|
||||
ldy #>_1
|
||||
jsr f_multiply_mem
|
||||
.macend
|
||||
|
||||
.macro fp_divide
|
||||
jsr ld_fac2_fac1
|
||||
`ld_fac1 _1
|
||||
jsr f_divide_op
|
||||
.macend
|
||||
|
||||
.macro fp_pow
|
||||
jsr ld_fac2_fac1
|
||||
`ld_fac1 _1
|
||||
jsr f_pow_op
|
||||
.macend
|
||||
|
||||
.macro fp_and
|
||||
`ld_fac2 _1
|
||||
jsr f_and_op
|
||||
.macend
|
||||
|
||||
.macro fp_or
|
||||
`ld_fac2 _1
|
||||
jsr f_or_op
|
||||
.macend
|
||||
|
||||
;;; Utility routine for converting the system clock to a floating point
|
||||
;;; value.
|
||||
ld_fac1_ti:
|
||||
jsr $ffde ; RDTIM
|
||||
sty $63
|
||||
stx $64
|
||||
sta $65
|
||||
;; Once the requirements on .Y and $68 are better
|
||||
;; understood, this might be exportable as
|
||||
;; ld_fac1_s32, but there are still some dragons
|
||||
;; lurking
|
||||
ldy #$00 ; Clear out intermediary values
|
||||
sta $62
|
||||
sta $68
|
||||
jmp $bcd5
|
||||
|
||||
;;; FAC1 can only be stored out to two locations. We'd prefer to be able
|
||||
;;; to store anywhere. This routine is a support routine that allows that.
|
||||
;;; It will normally only be called by the fp_store macro.
|
||||
fac1_to_mem:
|
||||
sta $fd
|
||||
sty $fe
|
||||
jsr fac1_to_5c
|
||||
ldy #$00
|
||||
* lda $5c,y
|
||||
sta ($fd),y
|
||||
iny
|
||||
cpy #$05
|
||||
bne -
|
||||
rts
|
||||
|
||||
;;; The VAL function uses the CHRGET routine copied to the zero page to read
|
||||
;;; strings in. That's a fragile operation if we don't want to confuse BASIC
|
||||
;;; later, so this routine juggles the values we need to preserve. It will
|
||||
;;; normally only be called by the fp_read macro.
|
||||
ld_fac1_string:
|
||||
ldx $7a
|
||||
sta $7a
|
||||
txa
|
||||
pha
|
||||
lda $7b
|
||||
pha
|
||||
sty $7b
|
||||
jsr $79
|
||||
jsr $bcf3
|
||||
pla
|
||||
sta $7b
|
||||
pla
|
||||
sta $7a
|
||||
rts
|
||||
|
||||
;;; Print out the contents of FAC1.
|
||||
fac1out:
|
||||
ldy #$00 ; Clean out overflow
|
||||
sty $68
|
||||
sty $70
|
||||
jsr fac1_to_string
|
||||
ldy #$01
|
||||
;; Skip the first character if it's not "-"
|
||||
lda $100
|
||||
sec
|
||||
sbc #$2d
|
||||
beq strout
|
||||
lda #$01
|
||||
;; Fall through to strout
|
||||
|
||||
;;; The BASIC ROM already has a STROUT routine - $ab1e - but
|
||||
;;; it makes use of BASIC's own temporary string handling. We
|
||||
;;; don't want it to ever touch its notion of temporary strings
|
||||
;;; here, so we provide our own short routine to do this.
|
||||
strout: sta $fd
|
||||
sty $fe
|
||||
ldy #$00
|
||||
* lda ($fd),y
|
||||
beq +
|
||||
jsr $ffd2 ; CHROUT
|
||||
iny
|
||||
bne -
|
||||
* rts
|
||||
|
||||
;;; Execute RND(-TI), seeding the random number generator the traditional way.
|
||||
randomize:
|
||||
jsr ld_fac1_ti
|
||||
lda #$ff
|
||||
sta $66 ; Force sign negative
|
||||
jmp rnd_fac1 ; RND(-TI)
|
||||
|
||||
|
||||
;;; Return RND(1), a fresh random number between 0 and 1.
|
||||
rnd: lda #$01
|
||||
jsr ld_fac1_a
|
||||
jmp rnd_fac1
|
15
platform/nes.oph
Normal file
15
platform/nes.oph
Normal file
|
@ -0,0 +1,15 @@
|
|||
; NES-related headers. Unlike the C64 and Stella developers, there is
|
||||
; no standard nomenclature for these registers. It's not uncommon to
|
||||
; see them hardcoded.
|
||||
|
||||
; PPU registers have reasonably standard names, at least.
|
||||
|
||||
.alias PPUCTRL $2000 ; PPU Control Register #1
|
||||
.alias PPUMASK $2001 ; PPU Control Register #2
|
||||
.alias PPUSTATUS $2002 ; PPU Status Register
|
||||
.alias OAMADDR $2003 ; SPR-RAM Address Register
|
||||
.alias OAMDATA $2004 ; SPR-RAM I/O Register
|
||||
.alias PPUSCROLL $2005 ; VRAM Address Register #1 (Panning control)
|
||||
.alias PPUADDR $2006 ; VRAM Address Register #2 (Direct Address control)
|
||||
.alias PPUDATA $2007 ; VRAM I/O Register
|
||||
.alias OAMDMA $4014 ; Sprite DMA Register
|
127
platform/stella.oph
Normal file
127
platform/stella.oph
Normal file
|
@ -0,0 +1,127 @@
|
|||
; Register mnemonics for the Atari 2600 VCS
|
||||
;
|
||||
; Taken from the "Stella Programming Guide", at
|
||||
; http://www.alienbill.com/2600/101/docs/stella.html
|
||||
|
||||
; Writable TIA addresses
|
||||
|
||||
.alias VSYNC $00 ; vertical sync set-clear
|
||||
.alias VBLANK $01 ; vertical blank set-clear
|
||||
.alias WSYNC $02 ; wait for leading edge of horizontal blank
|
||||
.alias RSYNC $03 ; reset horizontal sync counter
|
||||
.alias NUSIZ0 $04 ; number-size player-missile 0
|
||||
.alias NUSIZ1 $05 ; number-size player-missile 1
|
||||
.alias COLUP0 $06 ; color-lum player 0
|
||||
.alias COLUP1 $07 ; color-lum player 1
|
||||
.alias COLUPF $08 ; color-lum playfield
|
||||
.alias COLUBK $09 ; color-lum background
|
||||
.alias CTRLPF $0A ; control playfield ball size and collisions
|
||||
.alias REFP0 $0B ; reflect player 0
|
||||
.alias REFP1 $0C ; reflect player 1
|
||||
.alias PF0 $0D ; playfield register byte 0
|
||||
.alias PF1 $0E ; playfield register byte 1
|
||||
.alias PF2 $0F ; playfield register byte 2
|
||||
.alias RESP0 $10 ; reset player 0
|
||||
.alias RESP1 $11 ; reset player 1
|
||||
.alias RESM0 $12 ; reset missile 0
|
||||
.alias RESM1 $13 ; reset missile 1
|
||||
.alias RESBL $14 ; reset ball
|
||||
.alias AUDC0 $15 ; audio control 0
|
||||
.alias AUDC1 $16 ; audio control 1
|
||||
.alias AUDF0 $17 ; audio frequency 0
|
||||
.alias AUDF1 $18 ; audio frequency 1
|
||||
.alias AUDV0 $19 ; audio volume 0
|
||||
.alias AUDV1 $1A ; audio volume 1
|
||||
.alias GRP0 $1B ; Graphics player 0
|
||||
.alias GRP1 $1C ; Graphics player 1
|
||||
.alias ENAM0 $1D ; Graphics enable missile 0
|
||||
.alias ENAM1 $1E ; Graphics enable missile 1
|
||||
.alias ENABL $1F ; Graphics enable ball
|
||||
.alias HMP0 $20 ; horizontal motion player 0
|
||||
.alias HMP1 $21 ; horizontal motion player 1
|
||||
.alias HMM0 $22 ; horizontal motion missile 0
|
||||
.alias HMM1 $23 ; horizontal motion missile 1
|
||||
.alias HMBL $24 ; horizontal motion ball
|
||||
.alias VDELP0 $25 ; vertical delay player 0
|
||||
.alias VDELP1 $26 ; vertical delay player 1
|
||||
.alias VDELBL $27 ; vertical delay ball
|
||||
.alias RESMP0 $28 ; reset missile 0 to player 0
|
||||
.alias RESMP1 $29 ; reset missile 1 to player 1
|
||||
.alias HMOVE $2A ; apply horizontal motion
|
||||
.alias HMCLR $2B ; clear horizontal motion registers
|
||||
.alias CXCLR $2C ; clear collision latches
|
||||
|
||||
; Readable TIA addresses
|
||||
|
||||
.alias CXM0P $00 ; read collision missile 0 players
|
||||
.alias CXM1P $01 ; read collision missile 1 players
|
||||
.alias CXP0FB $02 ; read collision player 0 playfield/ball
|
||||
.alias CXP1FB $03 ; read collision player 1 playfield/ball
|
||||
.alias CXM0FB $04 ; read collision missile 0 playfield/ball
|
||||
.alias CXM1FB $05 ; read collision missile 1 playfield/ball
|
||||
.alias CXBLPF $06 ; read collision ball playfield
|
||||
.alias CXPPMM $07 ; read collision player/player missile/missile
|
||||
.alias INPT0 $08 ; read pot port
|
||||
.alias INPT1 $09 ; read pot port
|
||||
.alias INPT2 $0A ; read pot port
|
||||
.alias INPT3 $0B ; read pot port
|
||||
.alias INPT4 $0C ; read input
|
||||
.alias INPT5 $0D ; read input
|
||||
|
||||
; PIA addresses
|
||||
|
||||
.alias SWCHA $280 ; Port A data register (read/write)
|
||||
.alias SWACNT $281 ; Port A data direction register (0=input, 1=output)
|
||||
.alias SWCHB $282 ; Port B - console switches (read-only)
|
||||
.alias SWBCNT $283 ; Port B data direction register (hardwired to 0)
|
||||
.alias INTIM $284 ; Timer output (read only)
|
||||
.alias TIM1T $294 ; Set 1-clock interval (838 nsec/interval)
|
||||
.alias TIM8T $295 ; Set 8-clock interval (6.7 usec/interval)
|
||||
.alias TIM64T $296 ; Set 64-clock interval (53.6 usec/interval
|
||||
.alias T1024T $297 ; Set 1025-clock interval (858.2 usec/interval)
|
||||
|
||||
; These macros are adapted from DASM's old macro.h. Credit and description are
|
||||
; replicated from there.
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; VERTICAL_SYNC
|
||||
; Original author: Manuel Polik
|
||||
; Inserts the code required for a proper 3 scanline
|
||||
; vertical sync sequence
|
||||
;
|
||||
; Note: Alters the accumulator
|
||||
;
|
||||
; IN:
|
||||
; OUT: A = 1
|
||||
|
||||
.macro vertical'sync
|
||||
lda #$02
|
||||
sta WSYNC
|
||||
sta VSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
lsr
|
||||
sta WSYNC
|
||||
sta VSYNC
|
||||
.macend
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; CLEAN_START
|
||||
; Original author: Andrew Davie
|
||||
; Standardised start-up code, clears stack, all TIA registers and RAM to 0
|
||||
; Sets stack pointer to $FF, and all registers to 0
|
||||
; Sets decimal mode off, sets interrupt flag (kind of un-necessary)
|
||||
; Use as very first section of code on boot (ie: at reset)
|
||||
; Code written to minimise total ROM usage - uses weird 6502 knowledge :)
|
||||
.macro clean'start
|
||||
sei
|
||||
cld
|
||||
ldx #$00
|
||||
txa
|
||||
tay
|
||||
_clear'stack:
|
||||
dex
|
||||
txs
|
||||
pha
|
||||
bne _clear'stack
|
||||
.macend
|
5
platform/vic20.oph
Normal file
5
platform/vic20.oph
Normal file
|
@ -0,0 +1,5 @@
|
|||
;;; Minimal header file for unexpanded VIC-20.
|
||||
;;; It translates to 10 SYS4109.
|
||||
.word $1001
|
||||
.org $1001
|
||||
.byte $0b,$10,$0a,$00,$9e,$34,$31,$30,$39,$00,$00,$00
|
5
platform/vic20x.oph
Normal file
5
platform/vic20x.oph
Normal file
|
@ -0,0 +1,5 @@
|
|||
;;; Minimal header file for expanded VIC-20.
|
||||
;;; It translates to 10 SYS4621.
|
||||
.word $1201
|
||||
.org $1201
|
||||
.byte $0b,$12,$0a,$00,$9e,$34,$36,$32,$31,$00,$00,$00
|
|
@ -1,32 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html
|
||||
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<head><title>The Ophis Assembler</title></head>
|
||||
<body>
|
||||
<h1>The Ophis Assembler</h1>
|
||||
|
||||
<p>Ophis is a cross-assembler for the 65xx series of chips. It supports the stock 6502 opcodes, the 65c02 extensions, and syntax for the "undocumented opcodes" in the 6510 chip used on the Commodore 64. (Syntax for these opcodes matches those given in the <a href="http://www.viceteam.org">VICE team's documentation</a>.)</p>
|
||||
|
||||
<p>Ophis is written in pure Python and should be highly portable.</p>
|
||||
|
||||
<p>If you have questions or comments, email me at <i>mcmartin AT gmail DOT com</i>.</p>
|
||||
|
||||
<h2>Downloads</h2>
|
||||
<ul>
|
||||
<li><a href="Ophis-1.0.tar.gz">Source distribution</a>. For Unix and Mac. Untar, then run "python setup.py install" as root to install. Documentation and sample code is in the tarball but won't be placed anywhere special.</li>
|
||||
<li><a href="http://hkn.eecs.berkeley.edu/~mcmartin/ophis/Ophis-1.0-win32-installer.exe">Win32 installer</a>. Installs a standalone executable and support libraries. You will need to put the install directory into your PATH to run it conveniently, as it is a commandline program.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Documentation</h2>
|
||||
|
||||
<p>The manual <i>Programming with Ophis</i> is distributed with each download. You can also get it alone.</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="ophismanual.pdf">Download the PDF version of <i>Programming with Ophis</i></a></li>
|
||||
<li><a href="manual/book1.html">Browse <i>Programming with Ophis</i> online</a></li>
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,184 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Example Programs</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Where to go from here"
|
||||
HREF="x449.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="tutor2.oph"
|
||||
HREF="x461.html"></HEAD
|
||||
><BODY
|
||||
CLASS="APPENDIX"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x449.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x461.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="APPENDIX"
|
||||
><H1
|
||||
><A
|
||||
NAME="AEN454"
|
||||
></A
|
||||
>Example Programs</H1
|
||||
><P
|
||||
> This Appendix collects all the programs referred to in the course
|
||||
of this manual.
|
||||
</P
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="TUTOR1-SRC"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor1.oph</TT
|
||||
></A
|
||||
></H1
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="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</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x449.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x461.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Where to go from here</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor2.oph</TT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,311 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Ophis Command Reference</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="tutor7.oph"
|
||||
HREF="x501.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Basic arguments"
|
||||
HREF="x572.html"></HEAD
|
||||
><BODY
|
||||
CLASS="APPENDIX"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x501.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x572.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="APPENDIX"
|
||||
><H1
|
||||
><A
|
||||
NAME="REF-LINK"
|
||||
></A
|
||||
>Ophis Command Reference</H1
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN507"
|
||||
>Command Modes</A
|
||||
></H1
|
||||
><P
|
||||
> These mostly follow the <I
|
||||
CLASS="EMPHASIS"
|
||||
>MOS Technology 6500
|
||||
Microprocessor Family Programming Manual</I
|
||||
>, except
|
||||
for the Accumulator mode. Accumulator instructions are written
|
||||
and interpreted identically to Implied mode instructions.
|
||||
</P
|
||||
><P
|
||||
></P
|
||||
><UL
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>Implied:</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>RTS</TT
|
||||
></P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>Accumulator:</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>LSR</TT
|
||||
></P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>Immediate:</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>LDA #$06</TT
|
||||
></P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>Zero Page:</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>LDA $7C</TT
|
||||
></P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>Zero Page, X:</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>LDA $7C,X</TT
|
||||
></P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>Zero Page, Y:</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>LDA $7C,Y</TT
|
||||
></P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>Absolute:</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>LDA $D020</TT
|
||||
></P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>Absolute, X:</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>LDA $D000,X</TT
|
||||
></P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>Absolute, Y:</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>LDA $D000,Y</TT
|
||||
></P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>(Zero Page Indirect, X):</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>LDA ($80, X)</TT
|
||||
></P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>(Zero Page Indirect), Y:</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>LDA ($80), Y</TT
|
||||
></P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>(Absolute Indirect):</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>JMP ($A000)</TT
|
||||
></P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>Relative:</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>BNE loop</TT
|
||||
></P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>(Absolute Indirect, X):</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>JMP ($A000, X)</TT
|
||||
> — Only available with 65C02 extensions</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>(Zero Page Indirect):</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>LDX ($80)</TT
|
||||
> — Only available with 65C02 extensions</P
|
||||
></LI
|
||||
></UL
|
||||
></DIV
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x501.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x572.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor7.oph</TT
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Basic arguments</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,467 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Programming with Ophis</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Preface"
|
||||
HREF="f10.html"></HEAD
|
||||
><BODY
|
||||
CLASS="BOOK"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="BOOK"
|
||||
><A
|
||||
NAME="AEN1"
|
||||
></A
|
||||
><DIV
|
||||
CLASS="TITLEPAGE"
|
||||
><H1
|
||||
CLASS="TITLE"
|
||||
><A
|
||||
NAME="AEN2"
|
||||
>Programming with Ophis</A
|
||||
></H1
|
||||
><H3
|
||||
CLASS="AUTHOR"
|
||||
><A
|
||||
NAME="AEN4"
|
||||
></A
|
||||
>Michael Martin</H3
|
||||
><P
|
||||
CLASS="COPYRIGHT"
|
||||
>Copyright © 2006-7 Michael Martin</P
|
||||
><HR></DIV
|
||||
><DIV
|
||||
CLASS="TOC"
|
||||
><DL
|
||||
><DT
|
||||
><B
|
||||
>Table of Contents</B
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="f10.html"
|
||||
>Preface</A
|
||||
></DT
|
||||
><DD
|
||||
><DL
|
||||
><DT
|
||||
><A
|
||||
HREF="f10.html#AEN15"
|
||||
>Why <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"Ophis"</SPAN
|
||||
>?</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x22.html"
|
||||
>Getting a copy of Ophis</A
|
||||
></DT
|
||||
></DL
|
||||
></DD
|
||||
><DT
|
||||
><A
|
||||
HREF="c35.html"
|
||||
>The basics</A
|
||||
></DT
|
||||
><DD
|
||||
><DL
|
||||
><DT
|
||||
><A
|
||||
HREF="c35.html#AEN48"
|
||||
>A note on numeric notation</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x51.html"
|
||||
>Producing Commodore 64 programs</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x119.html"
|
||||
>Related commands and options</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x140.html"
|
||||
>Writing the actual code</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x149.html"
|
||||
>Assembling the code</A
|
||||
></DT
|
||||
></DL
|
||||
></DD
|
||||
><DT
|
||||
><A
|
||||
HREF="c200.html"
|
||||
>Labels and aliases</A
|
||||
></DT
|
||||
><DD
|
||||
><DL
|
||||
><DT
|
||||
><A
|
||||
HREF="c200.html#AEN206"
|
||||
>Temporary labels</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x214.html"
|
||||
>Anonymous labels</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x225.html"
|
||||
>Aliasing</A
|
||||
></DT
|
||||
></DL
|
||||
></DD
|
||||
><DT
|
||||
><A
|
||||
HREF="c236.html"
|
||||
>Headers, Libraries, and Macros</A
|
||||
></DT
|
||||
><DD
|
||||
><DL
|
||||
><DT
|
||||
><A
|
||||
HREF="c236.html#AEN240"
|
||||
>Header files and libraries</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x257.html"
|
||||
>Macros</A
|
||||
></DT
|
||||
><DD
|
||||
><DL
|
||||
><DT
|
||||
><A
|
||||
HREF="x257.html#AEN265"
|
||||
>Macro definitions</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x257.html#AEN278"
|
||||
>Macro invocations</A
|
||||
></DT
|
||||
></DL
|
||||
></DD
|
||||
><DT
|
||||
><A
|
||||
HREF="x287.html"
|
||||
>Example code</A
|
||||
></DT
|
||||
></DL
|
||||
></DD
|
||||
><DT
|
||||
><A
|
||||
HREF="c292.html"
|
||||
>Character maps</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="c329.html"
|
||||
>Local variables and memory segments</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="c371.html"
|
||||
>Expressions</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="c419.html"
|
||||
>Advanced Memory Segments</A
|
||||
></DT
|
||||
><DD
|
||||
><DL
|
||||
><DT
|
||||
><A
|
||||
HREF="c419.html#AEN424"
|
||||
>The Problem</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x430.html"
|
||||
>The Solution</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x449.html"
|
||||
>Where to go from here</A
|
||||
></DT
|
||||
></DL
|
||||
></DD
|
||||
><DT
|
||||
><A
|
||||
HREF="a454.html"
|
||||
>Example Programs</A
|
||||
></DT
|
||||
><DD
|
||||
><DL
|
||||
><DT
|
||||
><A
|
||||
HREF="a454.html#TUTOR1-SRC"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor1.oph</TT
|
||||
></A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x461.html"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor2.oph</TT
|
||||
></A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x465.html"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>c64-1.oph</TT
|
||||
></A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x469.html"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>kernal.oph</TT
|
||||
></A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x473.html"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor3.oph</TT
|
||||
></A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x477.html"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor4a.oph</TT
|
||||
></A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x481.html"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor4b.oph</TT
|
||||
></A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x485.html"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor4c.oph</TT
|
||||
></A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x489.html"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor5.oph</TT
|
||||
></A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x493.html"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor6.oph</TT
|
||||
></A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x497.html"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>c64-2.oph</TT
|
||||
></A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x501.html"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor7.oph</TT
|
||||
></A
|
||||
></DT
|
||||
></DL
|
||||
></DD
|
||||
><DT
|
||||
><A
|
||||
HREF="a505.html"
|
||||
>Ophis Command Reference</A
|
||||
></DT
|
||||
><DD
|
||||
><DL
|
||||
><DT
|
||||
><A
|
||||
HREF="a505.html#AEN507"
|
||||
>Command Modes</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x572.html"
|
||||
>Basic arguments</A
|
||||
></DT
|
||||
><DD
|
||||
><DL
|
||||
><DT
|
||||
><A
|
||||
HREF="x572.html#AEN575"
|
||||
>Numeric types</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x572.html#AEN598"
|
||||
>Label types</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x572.html#AEN611"
|
||||
>String types</A
|
||||
></DT
|
||||
></DL
|
||||
></DD
|
||||
><DT
|
||||
><A
|
||||
HREF="x620.html"
|
||||
>Compound Arguments</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x647.html"
|
||||
>Memory Model</A
|
||||
></DT
|
||||
><DD
|
||||
><DL
|
||||
><DT
|
||||
><A
|
||||
HREF="x647.html#AEN650"
|
||||
>Basic PC tracking</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x647.html#AEN659"
|
||||
>Basic Segmentation simulation</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x647.html#AEN683"
|
||||
>General Segmentation Simulation</A
|
||||
></DT
|
||||
></DL
|
||||
></DD
|
||||
><DT
|
||||
><A
|
||||
HREF="x692.html"
|
||||
>Macros</A
|
||||
></DT
|
||||
><DD
|
||||
><DL
|
||||
><DT
|
||||
><A
|
||||
HREF="x692.html#AEN696"
|
||||
>Defining Macros</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x692.html#AEN702"
|
||||
>Invoking Macros</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x692.html#AEN710"
|
||||
>Passing Arguments to Macros</A
|
||||
></DT
|
||||
><DT
|
||||
><A
|
||||
HREF="x692.html#AEN720"
|
||||
>Features and Restrictions of the Ophis Macro Model</A
|
||||
></DT
|
||||
></DL
|
||||
></DD
|
||||
><DT
|
||||
><A
|
||||
HREF="x732.html"
|
||||
>Assembler directives</A
|
||||
></DT
|
||||
></DL
|
||||
></DD
|
||||
></DL
|
||||
></DIV
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="f10.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Preface</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,208 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Labels and aliases</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Assembling the code"
|
||||
HREF="x149.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Anonymous labels"
|
||||
HREF="x214.html"></HEAD
|
||||
><BODY
|
||||
CLASS="CHAPTER"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x149.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x214.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="CHAPTER"
|
||||
><H1
|
||||
><A
|
||||
NAME="AEN200"
|
||||
></A
|
||||
>Labels and aliases</H1
|
||||
><P
|
||||
> Labels are an important part of your code. However, since each
|
||||
label must normally be unique, this can lead to <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"namespace
|
||||
pollution,"</SPAN
|
||||
> and you'll find yourself going through ever
|
||||
more contorted constructions to generate unique label names.
|
||||
Ophis offers two solutions to this: <I
|
||||
CLASS="EMPHASIS"
|
||||
>anonymous
|
||||
labels</I
|
||||
> and <I
|
||||
CLASS="EMPHASIS"
|
||||
>temporary labels</I
|
||||
>. This
|
||||
tutorial will cover both of these facilities, and also introduce
|
||||
the aliasing mechanism.
|
||||
</P
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN206"
|
||||
>Temporary labels</A
|
||||
></H1
|
||||
><P
|
||||
> 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 <TT
|
||||
CLASS="LITERAL"
|
||||
>.scope</TT
|
||||
> statement is encountered. This
|
||||
produces a new, inner scope if there is another scope in use.
|
||||
The <TT
|
||||
CLASS="LITERAL"
|
||||
>.scend</TT
|
||||
> command ends the innermost
|
||||
currently active scope.
|
||||
</P
|
||||
><P
|
||||
> We can thus rewrite our header data using temporary labels, thus
|
||||
allowing the main program to have a label
|
||||
named <TT
|
||||
CLASS="LITERAL"
|
||||
>next</TT
|
||||
> if it wants.
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="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</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x149.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x214.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Assembling the code</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Anonymous labels</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,226 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Headers, Libraries, and Macros</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Aliasing"
|
||||
HREF="x225.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Macros"
|
||||
HREF="x257.html"></HEAD
|
||||
><BODY
|
||||
CLASS="CHAPTER"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x225.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x257.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="CHAPTER"
|
||||
><H1
|
||||
><A
|
||||
NAME="CH3-LINK"
|
||||
></A
|
||||
>Headers, Libraries, and Macros</H1
|
||||
><P
|
||||
> In this chapter we will split away parts of our <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"Hello
|
||||
World"</SPAN
|
||||
> 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.
|
||||
</P
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN240"
|
||||
>Header files and libraries</A
|
||||
></H1
|
||||
><P
|
||||
> The prelude to our program—the <TT
|
||||
CLASS="FILENAME"
|
||||
>PRG</TT
|
||||
>
|
||||
information and the BASIC program—are going to be the same
|
||||
in many, many programs. Thus, we should put them into a header
|
||||
file to be included later. The <TT
|
||||
CLASS="LITERAL"
|
||||
>.include</TT
|
||||
>
|
||||
directive will load a file and insert it as source at the
|
||||
designated point.
|
||||
</P
|
||||
><P
|
||||
> A related directive, <TT
|
||||
CLASS="LITERAL"
|
||||
>.require</TT
|
||||
>, will include
|
||||
the file as long as it hasn't been included yet elsewhere. It
|
||||
is useful for ensuring a library is linked in.
|
||||
</P
|
||||
><P
|
||||
> For pre-assembled code or raw binary data,
|
||||
the <TT
|
||||
CLASS="LITERAL"
|
||||
>.incbin</TT
|
||||
> 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.
|
||||
</P
|
||||
><P
|
||||
> As a sample library, we will expand the definition of
|
||||
the <TT
|
||||
CLASS="LITERAL"
|
||||
>chrout</TT
|
||||
> routine to include the standard
|
||||
names for every KERNAL routine. Our header file will
|
||||
then <TT
|
||||
CLASS="LITERAL"
|
||||
>.require</TT
|
||||
> it.
|
||||
</P
|
||||
><P
|
||||
> 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 <TT
|
||||
CLASS="LITERAL"
|
||||
>chrout</TT
|
||||
> routine to get their effects.
|
||||
</P
|
||||
><P
|
||||
> 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 <A
|
||||
HREF="x465.html"
|
||||
><I
|
||||
><I
|
||||
>c64-1.oph</I
|
||||
></I
|
||||
></A
|
||||
> and <A
|
||||
HREF="x469.html"
|
||||
><I
|
||||
><I
|
||||
>kernal.oph</I
|
||||
></I
|
||||
></A
|
||||
>.)
|
||||
</P
|
||||
></DIV
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x225.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x257.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Aliasing</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Macros</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,344 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Character maps</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Example code"
|
||||
HREF="x287.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Local variables and memory segments"
|
||||
HREF="c329.html"></HEAD
|
||||
><BODY
|
||||
CLASS="CHAPTER"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x287.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="c329.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="CHAPTER"
|
||||
><H1
|
||||
><A
|
||||
NAME="CH4-LINK"
|
||||
></A
|
||||
>Character maps</H1
|
||||
><P
|
||||
> Now we will close the gap between the Commodore's
|
||||
version of ASCII and the real one. We'll also add a time-delay
|
||||
routine to slow down the output. This routine isn't really of
|
||||
interest to us right now, so we'll add a subroutine
|
||||
called <TT
|
||||
CLASS="LITERAL"
|
||||
>delay</TT
|
||||
> that executes 2,560*(accumulator)
|
||||
<KBD
|
||||
CLASS="USERINPUT"
|
||||
>NOP</KBD
|
||||
>s. By the time the program is finished,
|
||||
we'll have executed 768,000 no-ops.
|
||||
</P
|
||||
><P
|
||||
> There actually are better ways of getting a time-delay on the
|
||||
Commodore 64; we'll deal with those in <A
|
||||
HREF="c329.html"
|
||||
>the Chapter called <I
|
||||
>Local variables and memory segments</I
|
||||
></A
|
||||
>.
|
||||
As a result, there isn't really a lot to discuss here. The later
|
||||
tutorials will be building off of <A
|
||||
HREF="x477.html"
|
||||
><I
|
||||
><I
|
||||
>tutor4a.oph</I
|
||||
></I
|
||||
></A
|
||||
>, so you may want to get familiar with
|
||||
that. Note also the change to the body of
|
||||
the <TT
|
||||
CLASS="LITERAL"
|
||||
>greet</TT
|
||||
> macro.
|
||||
</P
|
||||
><P
|
||||
> On to the topic at hand. Let's change the code to use mixed case.
|
||||
We defined the <TT
|
||||
CLASS="LITERAL"
|
||||
>upper'case</TT
|
||||
>
|
||||
and <TT
|
||||
CLASS="LITERAL"
|
||||
>lower'case</TT
|
||||
> aliases back
|
||||
in <A
|
||||
HREF="c236.html"
|
||||
>the Chapter called <I
|
||||
>Headers, Libraries, and Macros</I
|
||||
></A
|
||||
> as part of the
|
||||
standard <A
|
||||
HREF="x469.html"
|
||||
><I
|
||||
><I
|
||||
>kernal.oph</I
|
||||
></I
|
||||
></A
|
||||
>
|
||||
header, so we can add this before our invocations of
|
||||
the <TT
|
||||
CLASS="LITERAL"
|
||||
>greet</TT
|
||||
> macro:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> lda #lower'case
|
||||
jsr chrout</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> And that will put us into mixed case mode. So, now we just need
|
||||
to redefine the data so that it uses the mixed-case:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>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</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> The code that does this is in <A
|
||||
HREF="x481.html"
|
||||
><I
|
||||
><I
|
||||
>tutor4b.oph</I
|
||||
></I
|
||||
></A
|
||||
>. If you assemble and run it, you will
|
||||
notice that the output is not what we want. In particular, upper
|
||||
and lowercase are reversed, so we have messages
|
||||
like <SAMP
|
||||
CLASS="COMPUTEROUTPUT"
|
||||
>hELLO, sOLAR sYSTEM!</SAMP
|
||||
>. For
|
||||
the specific case of PETSCII, we can just fix our strings, but
|
||||
that's less of an option if we're writing for the Apple II's
|
||||
character set, or targeting a game console that puts its letters
|
||||
in arbitrary locations. We need to remap how strings are turned
|
||||
into byte values. The <TT
|
||||
CLASS="LITERAL"
|
||||
>.charmap</TT
|
||||
>
|
||||
and <TT
|
||||
CLASS="LITERAL"
|
||||
>.charmapbin</TT
|
||||
> directives do what we need.
|
||||
</P
|
||||
><P
|
||||
> The <TT
|
||||
CLASS="LITERAL"
|
||||
>.charmap</TT
|
||||
> directive usually takes two
|
||||
arguments; a byte (usually in character form) indicating the ASCII
|
||||
value to start remapping from, and then a string giving the new
|
||||
values. To do our case-swapping, we write two directives before
|
||||
defining any string constants:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>.charmap 'A, "abcdefghijklmnopqrstuvwxyz"
|
||||
.charmap 'a, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> Note that the <TT
|
||||
CLASS="LITERAL"
|
||||
>'a</TT
|
||||
> constant in the second
|
||||
directive refers to the <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"a"</SPAN
|
||||
> character in the source,
|
||||
not in the current map.
|
||||
</P
|
||||
><P
|
||||
> The fixed code is in <A
|
||||
HREF="x485.html"
|
||||
><I
|
||||
><I
|
||||
>tutor4c.oph</I
|
||||
></I
|
||||
></A
|
||||
>, and will produce the expected results
|
||||
when run.
|
||||
</P
|
||||
><P
|
||||
> An alternative is to use a <TT
|
||||
CLASS="LITERAL"
|
||||
>.charmapbin</TT
|
||||
>
|
||||
directive to replace the entire character map directly. This
|
||||
specifies an external file, 256 bytes long, that is loaded in at
|
||||
that point. A binary character map for the Commodore 64 is
|
||||
provided with the sample programs
|
||||
as <TT
|
||||
CLASS="FILENAME"
|
||||
>petscii.map</TT
|
||||
>. There are also three
|
||||
files, <TT
|
||||
CLASS="FILENAME"
|
||||
>a2normal.map</TT
|
||||
>, <TT
|
||||
CLASS="FILENAME"
|
||||
>a2inverse.map</TT
|
||||
>,
|
||||
and <TT
|
||||
CLASS="FILENAME"
|
||||
>a2blink.map</TT
|
||||
> that handle the Apple II's
|
||||
very nonstandard character encodings.
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x287.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c329.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Example code</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Local variables and memory segments</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,402 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Local variables and memory segments</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Character maps"
|
||||
HREF="c292.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Expressions"
|
||||
HREF="c371.html"></HEAD
|
||||
><BODY
|
||||
CLASS="CHAPTER"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="c292.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="c371.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="CHAPTER"
|
||||
><H1
|
||||
><A
|
||||
NAME="CH5-LINK"
|
||||
></A
|
||||
>Local variables and memory segments</H1
|
||||
><P
|
||||
> As mentioned in <A
|
||||
HREF="c292.html"
|
||||
>the Chapter called <I
|
||||
>Character maps</I
|
||||
></A
|
||||
>, there are better ways
|
||||
to handle waiting than just executing vast numbers of NOPs. The
|
||||
Commodore 64 KERNAL library includes a <TT
|
||||
CLASS="LITERAL"
|
||||
>rdtim</TT
|
||||
>
|
||||
routine that returns the uptime of the machine, in
|
||||
60<SUP
|
||||
>th</SUP
|
||||
>s of a second, as a 24-bit integer.
|
||||
The Commodore 64 programmer's guide available online actually has
|
||||
a bug in it, reversing the significance of the A and Y registers.
|
||||
The accumulator holds the <I
|
||||
CLASS="EMPHASIS"
|
||||
>least</I
|
||||
> significant
|
||||
byte, not the most.
|
||||
</P
|
||||
><P
|
||||
> Here's a first shot at a better delay routine:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>.scope
|
||||
; data used by the delay routine
|
||||
_tmp: .byte 0
|
||||
_target: .byte 0
|
||||
|
||||
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</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> This works, but it eats up two bytes of file space that don't
|
||||
really need to be specified. Also, it's modifying data inside a
|
||||
program text area, which isn't good if you're assembling to a ROM
|
||||
chip. (Since the Commodore 64 stores its programs in RAM, it's
|
||||
not an issue for us here.) A slightly better solution is to
|
||||
use <TT
|
||||
CLASS="LITERAL"
|
||||
>.alias</TT
|
||||
> to assign the names to chunks of RAM
|
||||
somewhere. There's a 4K chunk of RAM from $C000 through $CFFF
|
||||
between the BASIC ROM and the I/O ROM that should serve our
|
||||
purposes nicely. We can replace the definitions
|
||||
of <TT
|
||||
CLASS="LITERAL"
|
||||
>_tmp</TT
|
||||
> and <TT
|
||||
CLASS="LITERAL"
|
||||
>_target</TT
|
||||
> with:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> ; data used by the delay routine
|
||||
.alias _tmp $C000
|
||||
.alias _target $C001</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> This works better, but now we've just added a major bookkeeping
|
||||
burden upon ourselves—we must ensure that no routines step on
|
||||
each other. What we'd really like are two separate program
|
||||
counters—one for the program text, and one for our variable
|
||||
space.
|
||||
</P
|
||||
><P
|
||||
> Ophis lets us do this with the <TT
|
||||
CLASS="LITERAL"
|
||||
>.text</TT
|
||||
>
|
||||
and <TT
|
||||
CLASS="LITERAL"
|
||||
>.data</TT
|
||||
> commands.
|
||||
The <TT
|
||||
CLASS="LITERAL"
|
||||
>.text</TT
|
||||
> command switches to the program-text
|
||||
counter, and the <TT
|
||||
CLASS="LITERAL"
|
||||
>.data</TT
|
||||
> command switches to the
|
||||
variable-data counter. When Ophis first starts assembling a file,
|
||||
it starts in <TT
|
||||
CLASS="LITERAL"
|
||||
>.text</TT
|
||||
> mode.
|
||||
</P
|
||||
><P
|
||||
> To reserve space for a variable, use the .space command. This
|
||||
takes the form:
|
||||
|
||||
<TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>.space varname size</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
>
|
||||
|
||||
which assigns the name <TT
|
||||
CLASS="LITERAL"
|
||||
>varname</TT
|
||||
> to the current
|
||||
program counter, then advances the program counter by the amount
|
||||
specified in <TT
|
||||
CLASS="LITERAL"
|
||||
>size</TT
|
||||
>. Nothing is output to the
|
||||
final binary as a result of the <TT
|
||||
CLASS="LITERAL"
|
||||
>.space</TT
|
||||
> command.
|
||||
</P
|
||||
><P
|
||||
> You may not put in any commands that produce output into
|
||||
a <TT
|
||||
CLASS="LITERAL"
|
||||
>.data</TT
|
||||
> segment. Generally, all you will be
|
||||
using are <TT
|
||||
CLASS="LITERAL"
|
||||
>.org</TT
|
||||
> and <TT
|
||||
CLASS="LITERAL"
|
||||
>.space</TT
|
||||
>
|
||||
commands. Ophis will not complain if you
|
||||
use <TT
|
||||
CLASS="LITERAL"
|
||||
>.space</TT
|
||||
> inside a <TT
|
||||
CLASS="LITERAL"
|
||||
>.text</TT
|
||||
>
|
||||
segment, but this is nearly always wrong.
|
||||
</P
|
||||
><P
|
||||
> The final version of <TT
|
||||
CLASS="LITERAL"
|
||||
>delay</TT
|
||||
> looks like this:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>; 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</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> We're not quite done yet, however, because we have to tell the
|
||||
data segment where to begin. (If we don't, it starts at 0, which
|
||||
is usually wrong.) We add a very brief data segment to the top of
|
||||
our code:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>.data
|
||||
.org $C000
|
||||
.text</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> This will run. However, we also ought to make sure that we aren't
|
||||
overstepping any boundaries. Our program text shouldn't run into
|
||||
the BASIC chip at $A000, and our data shouldn't run into the I/O
|
||||
region at $D000. The <TT
|
||||
CLASS="LITERAL"
|
||||
>.checkpc</TT
|
||||
> command lets us
|
||||
assert that the program counter hasn't reached a specific point
|
||||
yet. We put, at the end of our code:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>.checkpc $A000
|
||||
.data
|
||||
.checkpc $D000</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> The final program is available as <A
|
||||
HREF="x489.html"
|
||||
><I
|
||||
><I
|
||||
>tutor5.oph</I
|
||||
></I
|
||||
></A
|
||||
>. Note that we based this on the
|
||||
all-uppercase version from the last section, not any of the
|
||||
charmapped versions.
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c292.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c371.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Character maps</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Expressions</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,180 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>The basics</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Getting a copy of Ophis"
|
||||
HREF="x22.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Producing Commodore 64 programs"
|
||||
HREF="x51.html"></HEAD
|
||||
><BODY
|
||||
CLASS="CHAPTER"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x22.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x51.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="CHAPTER"
|
||||
><H1
|
||||
><A
|
||||
NAME="PART1"
|
||||
></A
|
||||
>The basics</H1
|
||||
><P
|
||||
> In this first part of the tutorial we will create a
|
||||
simple <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"Hello World"</SPAN
|
||||
> program to run on the Commodore
|
||||
64. This will cover:
|
||||
|
||||
<P
|
||||
></P
|
||||
><UL
|
||||
><LI
|
||||
><P
|
||||
>How to make programs run on a Commodore 64</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>Writing simple code with labels</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>Numeric and string data</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>Invoking the assembler</P
|
||||
></LI
|
||||
></UL
|
||||
>
|
||||
</P
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN48"
|
||||
>A note on numeric notation</A
|
||||
></H1
|
||||
><P
|
||||
> 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.
|
||||
</P
|
||||
></DIV
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x22.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x51.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Getting a copy of Ophis</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Producing Commodore 64 programs</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,348 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Expressions</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Local variables and memory segments"
|
||||
HREF="c329.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Advanced Memory Segments"
|
||||
HREF="c419.html"></HEAD
|
||||
><BODY
|
||||
CLASS="CHAPTER"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="c329.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="c419.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="CHAPTER"
|
||||
><H1
|
||||
><A
|
||||
NAME="AEN371"
|
||||
></A
|
||||
>Expressions</H1
|
||||
><P
|
||||
> Ophis permits a reasonably rich set of arithmetic operations to be
|
||||
done at assemble time. So far, all of our arguments and values
|
||||
have either been constants or label names. In this chapter, we
|
||||
will modify the <TT
|
||||
CLASS="LITERAL"
|
||||
>print</TT
|
||||
> macro so that it calls a
|
||||
subroutine to do the actual printing. This will shrink the final
|
||||
code size a fair bit.
|
||||
</P
|
||||
><P
|
||||
> Here's our printing routine. It's fairly straightforward.
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>; 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</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> However, now we are faced with the problem of what to do with
|
||||
the <TT
|
||||
CLASS="LITERAL"
|
||||
>print</TT
|
||||
> macro. We need to take a 16-bit
|
||||
value and store it in two 8-bit registers. We can use
|
||||
the <TT
|
||||
CLASS="LITERAL"
|
||||
><</TT
|
||||
> and <TT
|
||||
CLASS="LITERAL"
|
||||
>></TT
|
||||
> operators
|
||||
to take the low or high byte of a word, respectively.
|
||||
The <TT
|
||||
CLASS="LITERAL"
|
||||
>print</TT
|
||||
> macro becomes:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>.macro print
|
||||
lda #<_1
|
||||
ldx #>_1
|
||||
jsr printstr
|
||||
.macend</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> Also, since BASIC uses the locations $10 and $11, we should really
|
||||
cache them at the start of the program and restore them at the
|
||||
end:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>.data
|
||||
.org $C000
|
||||
.space cache 2
|
||||
.text
|
||||
|
||||
; Save the zero page locations that printstr uses.
|
||||
lda $10
|
||||
sta cache
|
||||
lda $11
|
||||
sta cache+1
|
||||
|
||||
; ... main program goes here ...
|
||||
|
||||
; Restore the zero page values printstr uses.
|
||||
lda cache
|
||||
sta $10
|
||||
lda cache+1
|
||||
sta $11</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> Note that we only have to name <TT
|
||||
CLASS="LITERAL"
|
||||
>cache</TT
|
||||
> once, but
|
||||
can use addition to refer to any offset from it.
|
||||
</P
|
||||
><P
|
||||
> Ophis supports following operations, with the following precedence
|
||||
levels (higher entries bind more tightly):
|
||||
</P
|
||||
><DIV
|
||||
CLASS="TABLE"
|
||||
><A
|
||||
NAME="AEN388"
|
||||
></A
|
||||
><P
|
||||
><B
|
||||
>Table 1. Ophis Operators</B
|
||||
></P
|
||||
><TABLE
|
||||
BORDER="1"
|
||||
BGCOLOR="#E0E0E0"
|
||||
CELLSPACING="0"
|
||||
CELLPADDING="4"
|
||||
CLASS="CALSTABLE"
|
||||
><THEAD
|
||||
><TR
|
||||
><TH
|
||||
ALIGN="CENTER"
|
||||
>Operators</TH
|
||||
><TH
|
||||
ALIGN="CENTER"
|
||||
>Description</TH
|
||||
></TR
|
||||
></THEAD
|
||||
><TBODY
|
||||
><TR
|
||||
><TD
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>[ ]</TT
|
||||
></TD
|
||||
><TD
|
||||
>Parenthesized expressions</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>< ></TT
|
||||
></TD
|
||||
><TD
|
||||
>Byte selection (low, high)</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>* /</TT
|
||||
></TD
|
||||
><TD
|
||||
>Multiply, divide</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>+ -</TT
|
||||
></TD
|
||||
><TD
|
||||
>Add, subtract</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>| & ^</TT
|
||||
></TD
|
||||
><TD
|
||||
>Bitwise OR, AND, XOR</TD
|
||||
></TR
|
||||
></TBODY
|
||||
></TABLE
|
||||
></DIV
|
||||
><P
|
||||
> 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.
|
||||
</P
|
||||
><P
|
||||
> The code for this version of the code is
|
||||
in <A
|
||||
HREF="x493.html"
|
||||
><I
|
||||
><I
|
||||
>tutor6.oph</I
|
||||
></I
|
||||
></A
|
||||
>.
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c329.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c419.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Local variables and memory segments</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Advanced Memory Segments</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,184 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Advanced Memory Segments</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Expressions"
|
||||
HREF="c371.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="The Solution"
|
||||
HREF="x430.html"></HEAD
|
||||
><BODY
|
||||
CLASS="CHAPTER"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="c371.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x430.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="CHAPTER"
|
||||
><H1
|
||||
><A
|
||||
NAME="AEN419"
|
||||
></A
|
||||
>Advanced Memory Segments</H1
|
||||
><P
|
||||
> This is the last section of the Ophis tutorial. By now we've
|
||||
covered the basics of every command in the assembler; in this
|
||||
final installment we show the full capabilities of
|
||||
the <TT
|
||||
CLASS="LITERAL"
|
||||
>.text</TT
|
||||
> and <TT
|
||||
CLASS="LITERAL"
|
||||
>.data</TT
|
||||
> commands
|
||||
as we produce a final set of Commodore 64 header files.
|
||||
</P
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN424"
|
||||
>The Problem</A
|
||||
></H1
|
||||
><P
|
||||
> Our <TT
|
||||
CLASS="LITERAL"
|
||||
>print'str</TT
|
||||
> routine
|
||||
in <A
|
||||
HREF="x493.html"
|
||||
><I
|
||||
><I
|
||||
>tutor6.oph</I
|
||||
></I
|
||||
></A
|
||||
> accesses
|
||||
memory locations $10 and $11 directly. We'd prefer to have
|
||||
symbolic names for them. This reprises our concerns back in
|
||||
<A
|
||||
HREF="c329.html"
|
||||
>the Chapter called <I
|
||||
>Local variables and memory segments</I
|
||||
></A
|
||||
> when we concluded that we wanted two
|
||||
separate program counters. Now we realize that we really need
|
||||
three; one for the text, one for the data, and one for the zero
|
||||
page data. And if we're going to allow three, we really should
|
||||
allow any number.
|
||||
</P
|
||||
></DIV
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c371.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x430.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Expressions</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>The Solution</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,186 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Preface</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Getting a copy of Ophis"
|
||||
HREF="x22.html"></HEAD
|
||||
><BODY
|
||||
CLASS="PREFACE"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x22.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="PREFACE"
|
||||
><H1
|
||||
><A
|
||||
NAME="AEN10"
|
||||
></A
|
||||
>Preface</H1
|
||||
><P
|
||||
> 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.
|
||||
</P
|
||||
><P
|
||||
> 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.
|
||||
</P
|
||||
><P
|
||||
> Part of that process has been formatting the various little
|
||||
tutorials and references I'd created into a single, unified
|
||||
document—the one you are now reading.
|
||||
</P
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN15"
|
||||
>Why <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"Ophis"</SPAN
|
||||
>?</A
|
||||
></H1
|
||||
><P
|
||||
> 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. <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"Ophis"</SPAN
|
||||
> is Greek
|
||||
for <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"snake"</SPAN
|
||||
>, and a number of traditions also use it
|
||||
as the actual <I
|
||||
CLASS="EMPHASIS"
|
||||
>name</I
|
||||
> of the serpent in the
|
||||
Garden of Eden. So, Pythons, snakes, and stories involving
|
||||
really old Apples all combined to name the assembler.
|
||||
</P
|
||||
></DIV
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x22.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Programming with Ophis</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Getting a copy of Ophis</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,206 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Related commands and options</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="The basics"
|
||||
HREF="c35.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Producing Commodore 64 programs"
|
||||
HREF="x51.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Writing the actual code"
|
||||
HREF="x140.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x51.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>The basics</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x140.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN119"
|
||||
>Related commands and options</A
|
||||
></H1
|
||||
><P
|
||||
> 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.
|
||||
|
||||
<P
|
||||
></P
|
||||
><UL
|
||||
><LI
|
||||
><P
|
||||
>To specify decimal constants, simply write the number.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>To specify hexadecimal constants, put a $ in front.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>To specify octal constants, put a 0 (zero) in front.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>To specify binary constants, put a % in front.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>To specify ASCII constants, put an apostrophe in front.</P
|
||||
></LI
|
||||
></UL
|
||||
>
|
||||
|
||||
Example: 65 = $41 = 0101 = %1000001 = 'A
|
||||
</P
|
||||
><P
|
||||
> There are other commands besides <TT
|
||||
CLASS="LITERAL"
|
||||
>.byte</TT
|
||||
>
|
||||
and <TT
|
||||
CLASS="LITERAL"
|
||||
>.word</TT
|
||||
> to specify data. In particular,
|
||||
the <TT
|
||||
CLASS="LITERAL"
|
||||
>.dword</TT
|
||||
> command specifies four-byte values
|
||||
which some applications will find useful. Also, some linking
|
||||
formats (such as the <TT
|
||||
CLASS="FILENAME"
|
||||
>SID</TT
|
||||
> format) have
|
||||
header data in big-endian (high byte first) format.
|
||||
The <TT
|
||||
CLASS="LITERAL"
|
||||
>.wordbe</TT
|
||||
> and <TT
|
||||
CLASS="LITERAL"
|
||||
>.dwordbe</TT
|
||||
>
|
||||
directives provide a way to specify multibyte constants in
|
||||
big-endian formats cleanly.
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x51.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x140.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Producing Commodore 64 programs</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c35.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Writing the actual code</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,197 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Writing the actual code</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="The basics"
|
||||
HREF="c35.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Related commands and options"
|
||||
HREF="x119.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Assembling the code"
|
||||
HREF="x149.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x119.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>The basics</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x149.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN140"
|
||||
>Writing the actual code</A
|
||||
></H1
|
||||
><P
|
||||
> Now that we have our header information, let's actually write
|
||||
the <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"Hello world"</SPAN
|
||||
> program. It's pretty
|
||||
short—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 <TT
|
||||
CLASS="LITERAL"
|
||||
>RTS</TT
|
||||
> statement.
|
||||
</P
|
||||
><P
|
||||
> 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.
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> ldx #0
|
||||
loop: lda hello, x
|
||||
beq done
|
||||
jsr $ffd2
|
||||
inx
|
||||
bne loop
|
||||
done: rts
|
||||
|
||||
hello: .byte "HELLO, WORLD!", 0
|
||||
</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> The complete, final source is available in
|
||||
the <A
|
||||
HREF="a454.html#TUTOR1-SRC"
|
||||
><I
|
||||
><I
|
||||
>tutor1.oph</I
|
||||
></I
|
||||
></A
|
||||
> file.
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x119.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x149.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Related commands and options</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c35.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Assembling the code</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,317 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Assembling the code</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="The basics"
|
||||
HREF="c35.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Writing the actual code"
|
||||
HREF="x140.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Labels and aliases"
|
||||
HREF="c200.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x140.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>The basics</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="c200.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN149"
|
||||
>Assembling the code</A
|
||||
></H1
|
||||
><P
|
||||
> 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 <B
|
||||
CLASS="COMMAND"
|
||||
>ophis.exe</B
|
||||
>; on other platforms, the
|
||||
Ophis modules should be in the library and
|
||||
the <B
|
||||
CLASS="COMMAND"
|
||||
>ophis</B
|
||||
> script should be in your path.
|
||||
Typing <B
|
||||
CLASS="COMMAND"
|
||||
>ophis</B
|
||||
> with no arguments should give a
|
||||
summary of available command line options.
|
||||
</P
|
||||
><DIV
|
||||
CLASS="TABLE"
|
||||
><A
|
||||
NAME="AEN155"
|
||||
></A
|
||||
><P
|
||||
><B
|
||||
>Table 2. Ophis Options</B
|
||||
></P
|
||||
><TABLE
|
||||
BORDER="1"
|
||||
BGCOLOR="#E0E0E0"
|
||||
CELLSPACING="0"
|
||||
CELLPADDING="4"
|
||||
CLASS="CALSTABLE"
|
||||
><THEAD
|
||||
><TR
|
||||
><TH
|
||||
ALIGN="CENTER"
|
||||
>Option</TH
|
||||
><TH
|
||||
ALIGN="CENTER"
|
||||
>Effect</TH
|
||||
></TR
|
||||
></THEAD
|
||||
><TBODY
|
||||
><TR
|
||||
><TD
|
||||
><CODE
|
||||
CLASS="OPTION"
|
||||
>-6510</CODE
|
||||
></TD
|
||||
><TD
|
||||
>Allows the 6510 undocumented opcodes as listed in the VICE documentation.</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
><CODE
|
||||
CLASS="OPTION"
|
||||
>-65c02</CODE
|
||||
></TD
|
||||
><TD
|
||||
>Allows opcodes and addressing modes added by the 65C02.</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
><CODE
|
||||
CLASS="OPTION"
|
||||
>-v 0</CODE
|
||||
></TD
|
||||
><TD
|
||||
>Quiet operation. Only reports errors.</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
><CODE
|
||||
CLASS="OPTION"
|
||||
>-v 1</CODE
|
||||
></TD
|
||||
><TD
|
||||
>Default operation. Reports files as they are loaded, and gives statistics on the final output.</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
><CODE
|
||||
CLASS="OPTION"
|
||||
>-v 2</CODE
|
||||
></TD
|
||||
><TD
|
||||
>Verbose operation. Names each assembler pass as it runs.</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
><CODE
|
||||
CLASS="OPTION"
|
||||
>-v 3</CODE
|
||||
></TD
|
||||
><TD
|
||||
>Debug operation: Dumps the entire IR after each pass.</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
><CODE
|
||||
CLASS="OPTION"
|
||||
>-v 4</CODE
|
||||
></TD
|
||||
><TD
|
||||
>Full debug operation: Dumps the entire IR and symbol table after each pass.</TD
|
||||
></TR
|
||||
></TBODY
|
||||
></TABLE
|
||||
></DIV
|
||||
><P
|
||||
> The only options Ophis demands are an input file and an output
|
||||
file. Here's a sample session, assembling the tutorial file
|
||||
here:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="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)
|
||||
</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> If your emulator can run <TT
|
||||
CLASS="FILENAME"
|
||||
>PRG</TT
|
||||
> files
|
||||
directly, this file will now run (and
|
||||
print <SAMP
|
||||
CLASS="COMPUTEROUTPUT"
|
||||
>HELLO, WORLD!</SAMP
|
||||
>) as many
|
||||
times as you type <KBD
|
||||
CLASS="USERINPUT"
|
||||
>RUN</KBD
|
||||
>. Otherwise, use
|
||||
a <TT
|
||||
CLASS="FILENAME"
|
||||
>D64</TT
|
||||
> management utility to put
|
||||
the <TT
|
||||
CLASS="FILENAME"
|
||||
>PRG</TT
|
||||
> on a <TT
|
||||
CLASS="FILENAME"
|
||||
>D64</TT
|
||||
>, then
|
||||
load and run the file off that.
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x140.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c200.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Writing the actual code</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c35.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Labels and aliases</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,198 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Anonymous labels</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Labels and aliases"
|
||||
HREF="c200.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Labels and aliases"
|
||||
HREF="c200.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Aliasing"
|
||||
HREF="x225.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="c200.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Labels and aliases</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x225.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN214"
|
||||
>Anonymous labels</A
|
||||
></H1
|
||||
><P
|
||||
> 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 <TT
|
||||
CLASS="LITERAL"
|
||||
>+</TT
|
||||
>
|
||||
or <TT
|
||||
CLASS="LITERAL"
|
||||
>-</TT
|
||||
> signs. <TT
|
||||
CLASS="LITERAL"
|
||||
>+</TT
|
||||
> refers to
|
||||
the next anonymous label, <TT
|
||||
CLASS="LITERAL"
|
||||
>++</TT
|
||||
> the label
|
||||
after that, etc. Likewise, <TT
|
||||
CLASS="LITERAL"
|
||||
>-</TT
|
||||
> is the most
|
||||
recently defined label, <TT
|
||||
CLASS="LITERAL"
|
||||
>--</TT
|
||||
> the one before
|
||||
that, and so on. The main body of the Hello World program
|
||||
with anonymous labels would be:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> ldx #0
|
||||
* lda hello, x
|
||||
beq +
|
||||
jsr $ffd2
|
||||
inx
|
||||
bne -
|
||||
* rts</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> It is worth noting that anonymous labels are globally available.
|
||||
They are not temporary labels, and they ignore scoping
|
||||
restrictions.
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c200.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x225.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Labels and aliases</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c200.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Aliasing</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,197 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Getting a copy of Ophis</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Preface"
|
||||
HREF="f10.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Preface"
|
||||
HREF="f10.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="The basics"
|
||||
HREF="c35.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="f10.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Preface</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="c35.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN22"
|
||||
>Getting a copy of Ophis</A
|
||||
></H1
|
||||
><P
|
||||
> 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 <A
|
||||
HREF="http://hkn.eecs.berkeley.edu/~mcmartin/ophis/"
|
||||
TARGET="_top"
|
||||
>http://hkn.eecs.berkeley.edu/~mcmartin/ophis/</A
|
||||
>. If
|
||||
this is out-of-date, a Web search on <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"Ophis 6502
|
||||
assembler"</SPAN
|
||||
> (without the quotation marks) should yield its
|
||||
page.
|
||||
</P
|
||||
><P
|
||||
> 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 <TT
|
||||
CLASS="LITERAL"
|
||||
>Ophis</TT
|
||||
> package somewhere in your Python
|
||||
package path, and then put the <B
|
||||
CLASS="COMMAND"
|
||||
>ophis</B
|
||||
> script
|
||||
somewhere in your path.
|
||||
</P
|
||||
><P
|
||||
> Windows users that have Python installed can use the same source
|
||||
distributions that the other operating systems
|
||||
use; <B
|
||||
CLASS="COMMAND"
|
||||
>ophis.bat</B
|
||||
> will arrange the environment
|
||||
variables accordingly and invoke the main script.
|
||||
</P
|
||||
><P
|
||||
> If you are on Windows and do not have Python installed, a
|
||||
prepackaged system made with <B
|
||||
CLASS="COMMAND"
|
||||
>py2exe</B
|
||||
> is also
|
||||
available. The default Windows installer will use this. In
|
||||
this case, all you need to do is
|
||||
have <B
|
||||
CLASS="COMMAND"
|
||||
>ophis.exe</B
|
||||
> in your path.
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="f10.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c35.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Preface</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="f10.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>The basics</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,203 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Aliasing</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Labels and aliases"
|
||||
HREF="c200.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Anonymous labels"
|
||||
HREF="x214.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Headers, Libraries, and Macros"
|
||||
HREF="c236.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x214.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Labels and aliases</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="c236.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN225"
|
||||
>Aliasing</A
|
||||
></H1
|
||||
><P
|
||||
> Rather the reverse of anonymous labels, aliases are names
|
||||
given to specific memory locations. These make it easier to
|
||||
keep track of important constants or locations. The KERNAL
|
||||
routines are a good example of constants that deserve names.
|
||||
To assign the traditional name <TT
|
||||
CLASS="LITERAL"
|
||||
>chrout</TT
|
||||
> to
|
||||
the routine at $FFD2, simply give the directive:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>.alias chrout $ffd2</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
>And change the <KBD
|
||||
CLASS="USERINPUT"
|
||||
>jsr</KBD
|
||||
> command
|
||||
to:</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
> jsr chrout</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> The final version of the code is in <A
|
||||
HREF="x461.html"
|
||||
><I
|
||||
><I
|
||||
>tutor2.oph</I
|
||||
></I
|
||||
></A
|
||||
>. It should
|
||||
assemble to exactly the same program as <A
|
||||
HREF="a454.html#TUTOR1-SRC"
|
||||
><I
|
||||
><I
|
||||
>tutor1.oph</I
|
||||
></I
|
||||
></A
|
||||
>.
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x214.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c236.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Anonymous labels</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c200.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Headers, Libraries, and Macros</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,289 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Macros</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Headers, Libraries, and Macros"
|
||||
HREF="c236.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Headers, Libraries, and Macros"
|
||||
HREF="c236.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Example code"
|
||||
HREF="x287.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="c236.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Headers, Libraries, and Macros</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x287.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN257"
|
||||
>Macros</A
|
||||
></H1
|
||||
><P
|
||||
> 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 <TT
|
||||
CLASS="LITERAL"
|
||||
>#define</TT
|
||||
> mechanic
|
||||
use <I
|
||||
CLASS="EMPHASIS"
|
||||
>textual replacement</I
|
||||
>: a macro is
|
||||
expanded before any evaluation or even parsing occurs.
|
||||
</P
|
||||
><P
|
||||
> In contrast, Ophis's macro system uses a <I
|
||||
CLASS="EMPHASIS"
|
||||
>call by
|
||||
value</I
|
||||
> 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 <A
|
||||
HREF="a505.html"
|
||||
>the Appendix called <I
|
||||
>Ophis Command Reference</I
|
||||
></A
|
||||
>.
|
||||
</P
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H2
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN265"
|
||||
>Macro definitions</A
|
||||
></H2
|
||||
><P
|
||||
> A macro definition is a set of statements between
|
||||
a <TT
|
||||
CLASS="LITERAL"
|
||||
>.macro</TT
|
||||
> statement and
|
||||
a <TT
|
||||
CLASS="LITERAL"
|
||||
>.macend</TT
|
||||
> statement.
|
||||
The <TT
|
||||
CLASS="LITERAL"
|
||||
>.macro</TT
|
||||
> statement also names the macro
|
||||
being defined.
|
||||
</P
|
||||
><P
|
||||
> No global or anonymous labels may be defined inside a macro:
|
||||
temporary labels only persist in the macro expansion itself.
|
||||
(Each macro body has its own scope.)
|
||||
</P
|
||||
><P
|
||||
> Arguments to macros are referred to by number: the first is
|
||||
<TT
|
||||
CLASS="LITERAL"
|
||||
>_1</TT
|
||||
>, the second <TT
|
||||
CLASS="LITERAL"
|
||||
>_2</TT
|
||||
>, and so on.
|
||||
</P
|
||||
><P
|
||||
> Here's a macro that encapsulates the printing routine in our
|
||||
<SPAN
|
||||
CLASS="QUOTE"
|
||||
>"Hello World"</SPAN
|
||||
> program, with an argument being the
|
||||
address of the string to print:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>.macro print
|
||||
ldx #0
|
||||
_loop: lda _1, x
|
||||
beq _done
|
||||
jsr chrout
|
||||
inx
|
||||
bne _loop
|
||||
_done:
|
||||
.macend</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H2
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN278"
|
||||
>Macro invocations</A
|
||||
></H2
|
||||
><P
|
||||
> Macros may be invoked in two ways: one that looks like a
|
||||
directive, and one that looks like an instruction.
|
||||
</P
|
||||
><P
|
||||
> The most common way to invoke a macro is to backquote the name
|
||||
of the macro. It is also possible to use
|
||||
the <TT
|
||||
CLASS="LITERAL"
|
||||
>.invoke</TT
|
||||
> command. These commands look
|
||||
like this:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>`print msg
|
||||
.invoke print msg</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> Arguments are passed to the macro as a comma-separated list.
|
||||
They must all be expressions that evaluate to byte or word
|
||||
values—a mechanism similar to <TT
|
||||
CLASS="LITERAL"
|
||||
>.alias</TT
|
||||
>
|
||||
is used to assign their values to the <TT
|
||||
CLASS="LITERAL"
|
||||
>_n</TT
|
||||
>
|
||||
names.
|
||||
</P
|
||||
></DIV
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c236.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x287.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Headers, Libraries, and Macros</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c236.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Example code</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,161 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Example code</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Headers, Libraries, and Macros"
|
||||
HREF="c236.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Macros"
|
||||
HREF="x257.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Character maps"
|
||||
HREF="c292.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x257.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Headers, Libraries, and Macros</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="c292.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN287"
|
||||
>Example code</A
|
||||
></H1
|
||||
><P
|
||||
> <A
|
||||
HREF="x473.html"
|
||||
><I
|
||||
><I
|
||||
>tutor3.oph</I
|
||||
></I
|
||||
></A
|
||||
> expands our
|
||||
running example, including the code above and also defining a
|
||||
new macro <TT
|
||||
CLASS="LITERAL"
|
||||
>greet</TT
|
||||
> that takes a string argument
|
||||
and prints a greeting to it. It then greets far too many
|
||||
targets.
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x257.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c292.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Macros</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c236.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Character maps</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,295 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>The Solution</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Advanced Memory Segments"
|
||||
HREF="c419.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Advanced Memory Segments"
|
||||
HREF="c419.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Where to go from here"
|
||||
HREF="x449.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="c419.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Advanced Memory Segments</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x449.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN430"
|
||||
>The Solution</A
|
||||
></H1
|
||||
><P
|
||||
> The <TT
|
||||
CLASS="LITERAL"
|
||||
>.data</TT
|
||||
> and <TT
|
||||
CLASS="LITERAL"
|
||||
>.text</TT
|
||||
>
|
||||
commands can take a label name after them—this names a new
|
||||
segment. We'll define a new segment
|
||||
called <TT
|
||||
CLASS="LITERAL"
|
||||
>zp</TT
|
||||
> (for <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"zero page"</SPAN
|
||||
>) and
|
||||
have our zero-page variables be placed there. We can't actually
|
||||
use the default origin of $0000 here either, though, because the
|
||||
Commodore 64 reserves memory locations 0 and 1 to control its
|
||||
memory mappers:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>.data zp
|
||||
.org $0002</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> Now, actually, the rest of the zero page is reserved too:
|
||||
locations $02-$7F are used by the BASIC interpreter, and
|
||||
locations $80-$FF are used by the KERNAL. We don't need the
|
||||
BASIC interpreter, though, so we can back up all of $02-$7F at
|
||||
the start of our program and restore it all when we're done:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>.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:
|
||||
; _main points at the start of the real program,
|
||||
; which is actually outside of this scope
|
||||
.scend</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> The new, improved header file is <A
|
||||
HREF="x497.html"
|
||||
><I
|
||||
><I
|
||||
>c64-2.oph</I
|
||||
></I
|
||||
></A
|
||||
>.
|
||||
</P
|
||||
><P
|
||||
> Our <TT
|
||||
CLASS="LITERAL"
|
||||
>print'str</TT
|
||||
> routine is then rewritten to
|
||||
declare and use a zero-page variable, like so:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>; 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</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> Also, we ought to put in an extra check to make sure our
|
||||
zero-page allocations don't overflow, either:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>.data zp
|
||||
.checkpc $80</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> That concludes our tour. The final source file
|
||||
is <A
|
||||
HREF="x501.html"
|
||||
><I
|
||||
><I
|
||||
>tutor7.oph</I
|
||||
></I
|
||||
></A
|
||||
>.
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c419.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x449.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Advanced Memory Segments</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c419.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Where to go from here</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,162 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Where to go from here</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Advanced Memory Segments"
|
||||
HREF="c419.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="The Solution"
|
||||
HREF="x430.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Example Programs"
|
||||
HREF="a454.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x430.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Advanced Memory Segments</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="a454.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN449"
|
||||
>Where to go from here</A
|
||||
></H1
|
||||
><P
|
||||
> This tutorial has touched on everything that the assembler can
|
||||
do, but it's not really well organized as a
|
||||
reference. <A
|
||||
HREF="a505.html"
|
||||
>the Appendix called <I
|
||||
>Ophis Command Reference</I
|
||||
></A
|
||||
> is a better place to look
|
||||
up matters of syntax or consult lists of available commands.
|
||||
</P
|
||||
><P
|
||||
> If you're looking for projects to undertake, the Commodore 64
|
||||
and Atari 2600 development communities are both very strong, and
|
||||
the Apple II and NES development communities are still alive and
|
||||
well as well. There's an annual Minigame Competition that's
|
||||
always looking for new entries.
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x430.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a454.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>The Solution</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c419.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Example Programs</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,183 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>tutor2.oph</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Example Programs"
|
||||
HREF="a454.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Example Programs"
|
||||
HREF="a454.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="c64-1.oph"
|
||||
HREF="x465.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="a454.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Example Programs</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x465.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="TUTOR2-SRC"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor2.oph</TT
|
||||
></A
|
||||
></H1
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="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</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a454.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x465.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Example Programs</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a454.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>c64-1.oph</TT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,176 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>c64-1.oph</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Example Programs"
|
||||
HREF="a454.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="tutor2.oph"
|
||||
HREF="x461.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="kernal.oph"
|
||||
HREF="x469.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x461.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Example Programs</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x469.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="C64-1-SRC"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>c64-1.oph</TT
|
||||
></A
|
||||
></H1
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="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"</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x461.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x469.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor2.oph</TT
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a454.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>kernal.oph</TT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,231 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>kernal.oph</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Example Programs"
|
||||
HREF="a454.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="c64-1.oph"
|
||||
HREF="x465.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="tutor3.oph"
|
||||
HREF="x473.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x465.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Example Programs</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x473.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="KERNAL-SRC"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>kernal.oph</TT
|
||||
></A
|
||||
></H1
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="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</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x465.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x473.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>c64-1.oph</TT
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a454.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor3.oph</TT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,209 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>tutor3.oph</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Example Programs"
|
||||
HREF="a454.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="kernal.oph"
|
||||
HREF="x469.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="tutor4a.oph"
|
||||
HREF="x477.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x469.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Example Programs</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x477.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="TUTOR3-SRC"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor3.oph</TT
|
||||
></A
|
||||
></H1
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="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</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x469.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x477.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>kernal.oph</TT
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a454.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor4a.oph</TT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,230 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>tutor4a.oph</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Example Programs"
|
||||
HREF="a454.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="tutor3.oph"
|
||||
HREF="x473.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="tutor4b.oph"
|
||||
HREF="x481.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x473.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Example Programs</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x481.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="TUTOR4A-SRC"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor4a.oph</TT
|
||||
></A
|
||||
></H1
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="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</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x473.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x481.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor3.oph</TT
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a454.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor4b.oph</TT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,232 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>tutor4b.oph</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Example Programs"
|
||||
HREF="a454.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="tutor4a.oph"
|
||||
HREF="x477.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="tutor4c.oph"
|
||||
HREF="x485.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x477.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Example Programs</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x485.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="TUTOR4B-SRC"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor4b.oph</TT
|
||||
></A
|
||||
></H1
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="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</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x477.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x485.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor4a.oph</TT
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a454.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor4c.oph</TT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,235 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>tutor4c.oph</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Example Programs"
|
||||
HREF="a454.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="tutor4b.oph"
|
||||
HREF="x481.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="tutor5.oph"
|
||||
HREF="x489.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x481.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Example Programs</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x489.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="TUTOR4C-SRC"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor4c.oph</TT
|
||||
></A
|
||||
></H1
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="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</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x481.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x489.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor4b.oph</TT
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a454.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor5.oph</TT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,239 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>tutor5.oph</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Example Programs"
|
||||
HREF="a454.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="tutor4c.oph"
|
||||
HREF="x485.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="tutor6.oph"
|
||||
HREF="x493.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x485.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Example Programs</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x493.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="TUTOR5-SRC"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor5.oph</TT
|
||||
></A
|
||||
></H1
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="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</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x485.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x493.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor4c.oph</TT
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a454.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor6.oph</TT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,266 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>tutor6.oph</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Example Programs"
|
||||
HREF="a454.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="tutor5.oph"
|
||||
HREF="x489.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="c64-2.oph"
|
||||
HREF="x497.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x489.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Example Programs</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x497.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="TUTOR6-SRC"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor6.oph</TT
|
||||
></A
|
||||
></H1
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="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</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x489.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x497.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor5.oph</TT
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a454.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>c64-2.oph</TT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,204 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>c64-2.oph</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Example Programs"
|
||||
HREF="a454.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="tutor6.oph"
|
||||
HREF="x493.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="tutor7.oph"
|
||||
HREF="x501.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x493.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Example Programs</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x501.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="C64-2-SRC"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>c64-2.oph</TT
|
||||
></A
|
||||
></H1
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="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</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x493.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x501.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor6.oph</TT
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a454.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor7.oph</TT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,257 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>tutor7.oph</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Example Programs"
|
||||
HREF="a454.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="c64-2.oph"
|
||||
HREF="x497.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Ophis Command Reference"
|
||||
HREF="a505.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x497.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Example Programs</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="a505.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="TUTOR7-SRC"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>tutor7.oph</TT
|
||||
></A
|
||||
></H1
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="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</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x497.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a505.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><TT
|
||||
CLASS="FILENAME"
|
||||
>c64-2.oph</TT
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a454.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Ophis Command Reference</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,428 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Producing Commodore 64 programs</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="The basics"
|
||||
HREF="c35.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="The basics"
|
||||
HREF="c35.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Related commands and options"
|
||||
HREF="x119.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="c35.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>The basics</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x119.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN51"
|
||||
>Producing Commodore 64 programs</A
|
||||
></H1
|
||||
><P
|
||||
> Commodore 64 programs are stored in
|
||||
the <TT
|
||||
CLASS="FILENAME"
|
||||
>PRG</TT
|
||||
> format on disk. Some emulators
|
||||
(such as CCS64 or VICE) can run <TT
|
||||
CLASS="FILENAME"
|
||||
>PRG</TT
|
||||
>
|
||||
programs directly; others need them to be transferred to
|
||||
a <TT
|
||||
CLASS="FILENAME"
|
||||
>D64</TT
|
||||
> image first.
|
||||
</P
|
||||
><P
|
||||
> The <TT
|
||||
CLASS="FILENAME"
|
||||
>PRG</TT
|
||||
> 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.
|
||||
</P
|
||||
><P
|
||||
> 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:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>10 SYS 2064
|
||||
</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> We <KBD
|
||||
CLASS="USERINPUT"
|
||||
>SAVE</KBD
|
||||
> this program to a file, then
|
||||
study it in a debugger. It's 15 bytes long:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="SCREEN"
|
||||
>1070:0100 01 08 0C 08 0A 00 9E 20-32 30 36 34 00 00 00
|
||||
</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> The first two bytes are the memory location: $0801. The rest of
|
||||
the data breaks down as follows:
|
||||
</P
|
||||
><DIV
|
||||
CLASS="TABLE"
|
||||
><A
|
||||
NAME="AEN65"
|
||||
></A
|
||||
><P
|
||||
><B
|
||||
>Table 1. BASIC program breakdown</B
|
||||
></P
|
||||
><TABLE
|
||||
BORDER="1"
|
||||
BGCOLOR="#E0E0E0"
|
||||
CELLSPACING="0"
|
||||
CELLPADDING="4"
|
||||
CLASS="CALSTABLE"
|
||||
><THEAD
|
||||
><TR
|
||||
><TH
|
||||
ALIGN="CENTER"
|
||||
>Memory Locations</TH
|
||||
><TH
|
||||
ALIGN="CENTER"
|
||||
>Value</TH
|
||||
></TR
|
||||
></THEAD
|
||||
><TBODY
|
||||
><TR
|
||||
><TD
|
||||
>$0801-$0802</TD
|
||||
><TD
|
||||
>2-byte pointer to the next line of BASIC code ($080C).</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
>$0803-$0804</TD
|
||||
><TD
|
||||
>2-byte line number ($000A = 10).</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
>$0805</TD
|
||||
><TD
|
||||
>Byte code for the <KBD
|
||||
CLASS="USERINPUT"
|
||||
>SYS</KBD
|
||||
> command.</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
>$0806-$080A</TD
|
||||
><TD
|
||||
>The rest of the line, which is just the string <SPAN
|
||||
CLASS="QUOTE"
|
||||
>" 2064"</SPAN
|
||||
>.</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
>$080B</TD
|
||||
><TD
|
||||
>Null byte, terminating the line.</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
>$080C-$080D</TD
|
||||
><TD
|
||||
>2-byte pointer to the next line of BASIC code ($0000 = end of program).</TD
|
||||
></TR
|
||||
></TBODY
|
||||
></TABLE
|
||||
></DIV
|
||||
><P
|
||||
> 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.
|
||||
</P
|
||||
><P
|
||||
> These are just bytes—indistinguishable from any other sort of
|
||||
data. In Ophis, bytes of data are specified with
|
||||
the <TT
|
||||
CLASS="LITERAL"
|
||||
>.byte</TT
|
||||
> 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 <TT
|
||||
CLASS="LITERAL"
|
||||
>.org</TT
|
||||
>
|
||||
(origin) command tells Ophis this. Thus, the Ophis code for our
|
||||
header and linking info is:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>.byte $01, $08, $0C, $08, $0A, $00, $9E, $20
|
||||
.byte $32, $30, $36, $34, $00, $00, $00, $00
|
||||
.byte $00, $00
|
||||
.org $0810
|
||||
</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> This gets the job done, but it's completely incomprehensible,
|
||||
and it only uses two directives—not very good for a
|
||||
tutorial. Here's a more complicated, but much clearer, way of
|
||||
saying the same thing.
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="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
|
||||
</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> This code has many advantages over the first.
|
||||
|
||||
<P
|
||||
></P
|
||||
><UL
|
||||
><LI
|
||||
><P
|
||||
> It describes better what is actually
|
||||
happening. The <TT
|
||||
CLASS="LITERAL"
|
||||
>.word</TT
|
||||
> directive at the
|
||||
beginning indicates a 16-bit value stored in the typical
|
||||
65xx way (small byte first). This is followed by
|
||||
an <TT
|
||||
CLASS="LITERAL"
|
||||
>.org</TT
|
||||
> statement, so we let the
|
||||
assembler know right away where everything is supposed to
|
||||
be.
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> 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 <TT
|
||||
CLASS="LITERAL"
|
||||
>next</TT
|
||||
> and put that value in as data.
|
||||
We also describe the line number in decimal since BASIC
|
||||
line numbers generally <I
|
||||
CLASS="EMPHASIS"
|
||||
>are</I
|
||||
> in decimal.
|
||||
Labels are defined by putting their name, then a colon, as
|
||||
seen in the definition of <TT
|
||||
CLASS="LITERAL"
|
||||
>next</TT
|
||||
>.
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>
|
||||
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.
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>
|
||||
Instead of adding the buffer ourselves, we
|
||||
used <TT
|
||||
CLASS="LITERAL"
|
||||
>.advance</TT
|
||||
>, which outputs zeros until
|
||||
the specified address is reached. Attempting
|
||||
to <TT
|
||||
CLASS="LITERAL"
|
||||
>.advance</TT
|
||||
> backwards produces an
|
||||
assemble-time error.
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>
|
||||
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.
|
||||
</P
|
||||
></LI
|
||||
></UL
|
||||
>
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c35.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x119.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>The basics</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="c35.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Related commands and options</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,309 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Basic arguments</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Ophis Command Reference"
|
||||
HREF="a505.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Ophis Command Reference"
|
||||
HREF="a505.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Compound Arguments"
|
||||
HREF="x620.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="a505.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Ophis Command Reference</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x620.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN572"
|
||||
>Basic arguments</A
|
||||
></H1
|
||||
><P
|
||||
> Most arguments are just a number or label. The formats for
|
||||
these are below.
|
||||
</P
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H2
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN575"
|
||||
>Numeric types</A
|
||||
></H2
|
||||
><P
|
||||
></P
|
||||
><UL
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>Hex:</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>$41</TT
|
||||
> (Prefixed with $)</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>Decimal:</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>65</TT
|
||||
> (No markings)</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>Octal:</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>0101</TT
|
||||
> (Prefixed with zero)</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>Binary:</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>%01000001</TT
|
||||
> (Prefixed with %)</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><I
|
||||
CLASS="EMPHASIS"
|
||||
>Character:</I
|
||||
> <TT
|
||||
CLASS="LITERAL"
|
||||
>'A</TT
|
||||
> (Prefixed with single quote)</P
|
||||
></LI
|
||||
></UL
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H2
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN598"
|
||||
>Label types</A
|
||||
></H2
|
||||
><P
|
||||
> Normal labels are simply referred to by name. Anonymous
|
||||
labels may be referenced with strings of - or + signs (the
|
||||
label <TT
|
||||
CLASS="LITERAL"
|
||||
>-</TT
|
||||
> refers to the immediate
|
||||
previous anonymous label, <TT
|
||||
CLASS="LITERAL"
|
||||
>--</TT
|
||||
> the
|
||||
one before that, etc., while <TT
|
||||
CLASS="LITERAL"
|
||||
>+</TT
|
||||
>
|
||||
refers to the next anonymous label), and the special
|
||||
label <TT
|
||||
CLASS="LITERAL"
|
||||
>^</TT
|
||||
> refers to the program
|
||||
counter at the start of the current instruction or directive.
|
||||
</P
|
||||
><P
|
||||
> Normal labels are <I
|
||||
CLASS="EMPHASIS"
|
||||
>defined</I
|
||||
> by
|
||||
prefixing a line with the label name and then a colon
|
||||
(e.g., <TT
|
||||
CLASS="LITERAL"
|
||||
>label:</TT
|
||||
>). Anonymous labels
|
||||
are defined by prefixing a line with an asterisk
|
||||
(e.g., <TT
|
||||
CLASS="LITERAL"
|
||||
>*</TT
|
||||
>).
|
||||
</P
|
||||
><P
|
||||
> Temporary labels are only reachable from inside the
|
||||
innermost enclosing <TT
|
||||
CLASS="LITERAL"
|
||||
>.scope</TT
|
||||
>
|
||||
statement. They are identical to normal labels in every
|
||||
way, except that they start with an underscore.
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H2
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN611"
|
||||
>String types</A
|
||||
></H2
|
||||
><P
|
||||
> Strings are enclosed in double quotation marks. Backslashed
|
||||
characters (including backslashes and double quotes) are
|
||||
treated literally, so the string <TT
|
||||
CLASS="LITERAL"
|
||||
>"The man said,
|
||||
\"The \\ character is the backslash.\""</TT
|
||||
> produces
|
||||
the ASCII sequence for <TT
|
||||
CLASS="LITERAL"
|
||||
>The man said, "The \
|
||||
character is the backslash."</TT
|
||||
>
|
||||
</P
|
||||
><P
|
||||
> Strings are generally only used as arguments to assembler
|
||||
directives—usually for filenames
|
||||
(e.g., <TT
|
||||
CLASS="LITERAL"
|
||||
>.include</TT
|
||||
>) but also for string
|
||||
data (in association with <TT
|
||||
CLASS="LITERAL"
|
||||
>.byte</TT
|
||||
>).
|
||||
</P
|
||||
><P
|
||||
> 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.
|
||||
</P
|
||||
></DIV
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a505.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x620.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Ophis Command Reference</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a505.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Compound Arguments</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,212 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Compound Arguments</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Ophis Command Reference"
|
||||
HREF="a505.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Basic arguments"
|
||||
HREF="x572.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Memory Model"
|
||||
HREF="x647.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x572.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Ophis Command Reference</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x647.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN620"
|
||||
>Compound Arguments</A
|
||||
></H1
|
||||
><P
|
||||
> Compound arguments may be built up from simple ones, using the
|
||||
standard +, -, *, and / operators, which carry the usual
|
||||
precedence. Also, the unary operators > and <, which
|
||||
bind more tightly than anything else, provide the high and low
|
||||
bytes of 16-bit values, respectively.
|
||||
</P
|
||||
><P
|
||||
> Use brackets [ ] instead of parentheses ( ) when grouping
|
||||
arithmetic operations, as the parentheses are needed for the
|
||||
indirect addressing modes.
|
||||
</P
|
||||
><P
|
||||
> Examples:
|
||||
</P
|
||||
><P
|
||||
></P
|
||||
><UL
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>$D000</TT
|
||||
> evaluates to $D000</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>$D000+32</TT
|
||||
> evaluates to $D020</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>$D000+$20</TT
|
||||
> also evaluates to $D020</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
><$D000+32</TT
|
||||
> evaluates to $20</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>>$D000+32</TT
|
||||
> evaluates to $F0</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>>[$D000+32]</TT
|
||||
> evaluates to $D0</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>>$D000-275</TT
|
||||
> evaluates to $CE</P
|
||||
></LI
|
||||
></UL
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x572.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x647.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Basic arguments</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a505.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Memory Model</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,373 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Memory Model</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Ophis Command Reference"
|
||||
HREF="a505.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Compound Arguments"
|
||||
HREF="x620.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Macros"
|
||||
HREF="x692.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x620.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Ophis Command Reference</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x692.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN647"
|
||||
>Memory Model</A
|
||||
></H1
|
||||
><P
|
||||
> 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.
|
||||
</P
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H2
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN650"
|
||||
>Basic PC tracking</A
|
||||
></H2
|
||||
><P
|
||||
> The primary technique Ophis uses is <I
|
||||
CLASS="EMPHASIS"
|
||||
>program counter
|
||||
tracking</I
|
||||
>. As it assembles the code, it keeps
|
||||
track of a virtual program counter, and uses that to
|
||||
determine where the labels should go.
|
||||
</P
|
||||
><P
|
||||
> In the absence of an <TT
|
||||
CLASS="LITERAL"
|
||||
>.org</TT
|
||||
> directive, it
|
||||
assumes a starting PC of zero. <TT
|
||||
CLASS="LITERAL"
|
||||
>.org</TT
|
||||
>
|
||||
is a simple directive, setting the PC to the value
|
||||
that <TT
|
||||
CLASS="LITERAL"
|
||||
>.org</TT
|
||||
> specifies. In the simplest
|
||||
case, one <TT
|
||||
CLASS="LITERAL"
|
||||
>.org</TT
|
||||
> directive appears at the
|
||||
beginning of the code and sets the location for the rest of
|
||||
the code, which is one contiguous block.
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H2
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN659"
|
||||
>Basic Segmentation simulation</A
|
||||
></H2
|
||||
><P
|
||||
> 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.
|
||||
</P
|
||||
><P
|
||||
> It is possible, but inconvenient, to do this
|
||||
with <TT
|
||||
CLASS="LITERAL"
|
||||
>.alias</TT
|
||||
>, assigning a specific
|
||||
memory location to each variable. This requires careful
|
||||
coordination through your code, and makes creating reusable
|
||||
libraries all but impossible.
|
||||
</P
|
||||
><P
|
||||
> A better approach is to reserve a section at the beginning
|
||||
or end of your program, put an <TT
|
||||
CLASS="LITERAL"
|
||||
>.org</TT
|
||||
>
|
||||
directive in, then use the <TT
|
||||
CLASS="LITERAL"
|
||||
>.space</TT
|
||||
>
|
||||
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.
|
||||
</P
|
||||
><P
|
||||
> The <TT
|
||||
CLASS="LITERAL"
|
||||
>.text</TT
|
||||
>
|
||||
and <TT
|
||||
CLASS="LITERAL"
|
||||
>.data</TT
|
||||
> 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 <TT
|
||||
CLASS="LITERAL"
|
||||
>.data</TT
|
||||
> section (filled
|
||||
with <TT
|
||||
CLASS="LITERAL"
|
||||
>.space</TT
|
||||
> commands) and
|
||||
a <TT
|
||||
CLASS="LITERAL"
|
||||
>.text</TT
|
||||
> 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 <TT
|
||||
CLASS="LITERAL"
|
||||
>.included</TT
|
||||
> by multiple
|
||||
projects without getting in anything's way.
|
||||
</P
|
||||
><P
|
||||
> However, any given program may have its own ideas about
|
||||
where data and code go, and it's good to ensure with
|
||||
a <TT
|
||||
CLASS="LITERAL"
|
||||
>.checkpc</TT
|
||||
> at the end of your code
|
||||
that you haven't accidentally overwritten code with data or
|
||||
vice versa. If your <TT
|
||||
CLASS="LITERAL"
|
||||
>.data</TT
|
||||
>
|
||||
segment <I
|
||||
CLASS="EMPHASIS"
|
||||
>did</I
|
||||
> 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).
|
||||
</P
|
||||
><P
|
||||
> If you write code with no segment-defining statements in
|
||||
it, the default segment
|
||||
is <TT
|
||||
CLASS="LITERAL"
|
||||
>text</TT
|
||||
>.
|
||||
</P
|
||||
><P
|
||||
> The <TT
|
||||
CLASS="LITERAL"
|
||||
>data</TT
|
||||
> segment is designed only
|
||||
for organizing labels. As such, errors will be flagged if
|
||||
you attempt to actually output information into
|
||||
a <TT
|
||||
CLASS="LITERAL"
|
||||
>data</TT
|
||||
> segment.
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H2
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN683"
|
||||
>General Segmentation Simulation</A
|
||||
></H2
|
||||
><P
|
||||
> 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 <TT
|
||||
CLASS="LITERAL"
|
||||
>.text</TT
|
||||
>
|
||||
or <TT
|
||||
CLASS="LITERAL"
|
||||
>.data</TT
|
||||
> produces a new segment with
|
||||
the specified name.
|
||||
</P
|
||||
><P
|
||||
> 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:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>.data
|
||||
.org $200
|
||||
.data zp
|
||||
.org $2
|
||||
.text
|
||||
.org $800</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> And, to be safe, we would probably want to end our code
|
||||
with checks to make sure we aren't overwriting anything:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>.data
|
||||
.checkpc $800
|
||||
.data zp
|
||||
.checkpc $100</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x620.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x692.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Compound Arguments</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a505.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Macros</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,358 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Macros</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Ophis Command Reference"
|
||||
HREF="a505.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Memory Model"
|
||||
HREF="x647.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Assembler directives"
|
||||
HREF="x732.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x647.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Ophis Command Reference</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x732.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN692"
|
||||
>Macros</A
|
||||
></H1
|
||||
><P
|
||||
> Assembly language is a powerful tool—however, there are
|
||||
many tasks that need to be done repeatedly, and with
|
||||
mind-numbing minor modifications. Ophis includes a facility
|
||||
for <I
|
||||
CLASS="EMPHASIS"
|
||||
>macros</I
|
||||
> to allow this. Ophis macros
|
||||
are very similar in form to function calls in higher level
|
||||
languages.
|
||||
</P
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H2
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN696"
|
||||
>Defining Macros</A
|
||||
></H2
|
||||
><P
|
||||
> Macros are defined with the <TT
|
||||
CLASS="LITERAL"
|
||||
>.macro</TT
|
||||
>
|
||||
and <TT
|
||||
CLASS="LITERAL"
|
||||
>.macend</TT
|
||||
> commands. Here's a
|
||||
simple one that will clear the screen on a Commodore
|
||||
64:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>.macro clr'screen
|
||||
lda #147
|
||||
jsr $FFD2
|
||||
.macend</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H2
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN702"
|
||||
>Invoking Macros</A
|
||||
></H2
|
||||
><P
|
||||
> To invoke a macro, either use
|
||||
the <TT
|
||||
CLASS="LITERAL"
|
||||
>.invoke</TT
|
||||
> 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:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>.invoke clr'screen</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
>or</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>`clr'screen</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
>will work equally well.</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H2
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN710"
|
||||
>Passing Arguments to Macros</A
|
||||
></H2
|
||||
><P
|
||||
> Macros may take arguments. The arguments to a macro are
|
||||
all of the <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"word"</SPAN
|
||||
> type, though byte values may
|
||||
be passed and used as bytes as well. The first argument in
|
||||
an invocation is bound to the label
|
||||
<TT
|
||||
CLASS="LITERAL"
|
||||
>_1</TT
|
||||
>, the second
|
||||
to <TT
|
||||
CLASS="LITERAL"
|
||||
>_2</TT
|
||||
>, and so on. Here's a macro
|
||||
for storing a 16-bit value into a word pointer:
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><PRE
|
||||
CLASS="PROGRAMLISTING"
|
||||
>.macro store16 ; `store16 dest, src
|
||||
lda #<_2
|
||||
sta _1
|
||||
lda #>_2
|
||||
sta _1+1
|
||||
.macend</PRE
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> Macro arguments behave, for the most part, as if they were
|
||||
defined by <TT
|
||||
CLASS="LITERAL"
|
||||
>.alias</TT
|
||||
>
|
||||
commands <I
|
||||
CLASS="EMPHASIS"
|
||||
>in the calling context</I
|
||||
>.
|
||||
(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.)
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H2
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN720"
|
||||
>Features and Restrictions of the Ophis Macro Model</A
|
||||
></H2
|
||||
><P
|
||||
> 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.
|
||||
</P
|
||||
><P
|
||||
> The primary benefit of this <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"expand-via-binding"</SPAN
|
||||
>
|
||||
discipline is that there are no surprises in the semantics.
|
||||
The expression <TT
|
||||
CLASS="LITERAL"
|
||||
>_1+1</TT
|
||||
> 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.
|
||||
</P
|
||||
><P
|
||||
> 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 <TT
|
||||
CLASS="LITERAL"
|
||||
>LDA _1</TT
|
||||
> and
|
||||
accept as arguments both <TT
|
||||
CLASS="LITERAL"
|
||||
>$C000</TT
|
||||
>
|
||||
(which would put the value of memory location $C000 into
|
||||
the accumulator) and <TT
|
||||
CLASS="LITERAL"
|
||||
>#$40</TT
|
||||
> (which
|
||||
would put the immediate value $40 into the accumulator).
|
||||
If you <I
|
||||
CLASS="EMPHASIS"
|
||||
>really</I
|
||||
> need this kind of
|
||||
behavior, a run a C preprocessor over your Ophis source,
|
||||
and use <TT
|
||||
CLASS="LITERAL"
|
||||
>#define</TT
|
||||
> to your heart's
|
||||
content.
|
||||
</P
|
||||
></DIV
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x647.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x732.html"
|
||||
ACCESSKEY="N"
|
||||
>Next >>></A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Memory Model</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a505.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Assembler directives</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
|
@ -1,595 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Assembler directives</TITLE
|
||||
><META
|
||||
NAME="GENERATOR"
|
||||
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
||||
REL="HOME"
|
||||
TITLE="Programming with Ophis"
|
||||
HREF="book1.html"><LINK
|
||||
REL="UP"
|
||||
TITLE="Ophis Command Reference"
|
||||
HREF="a505.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Macros"
|
||||
HREF="x692.html"></HEAD
|
||||
><BODY
|
||||
CLASS="SECTION"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#0000FF"
|
||||
VLINK="#840084"
|
||||
ALINK="#0000FF"
|
||||
><DIV
|
||||
CLASS="NAVHEADER"
|
||||
><TABLE
|
||||
SUMMARY="Header navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TH
|
||||
COLSPAN="3"
|
||||
ALIGN="center"
|
||||
>Programming with Ophis</TH
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="left"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="x692.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Ophis Command Reference</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
> </TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="SECTION"
|
||||
><H1
|
||||
CLASS="SECTION"
|
||||
><A
|
||||
NAME="AEN732"
|
||||
>Assembler directives</A
|
||||
></H1
|
||||
><P
|
||||
> Assembler directives are all instructions to the assembler
|
||||
that are not actual instructions. Ophis's set of directives
|
||||
follow.
|
||||
</P
|
||||
><P
|
||||
></P
|
||||
><UL
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.advance</TT
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>address</I
|
||||
>:
|
||||
Forces the program counter to
|
||||
be <I
|
||||
CLASS="EMPHASIS"
|
||||
>address</I
|
||||
>. Unlike
|
||||
the <TT
|
||||
CLASS="LITERAL"
|
||||
>.org</TT
|
||||
>
|
||||
directive, <TT
|
||||
CLASS="LITERAL"
|
||||
>.advance</TT
|
||||
> outputs zeroes until the
|
||||
program counter reaches a specified address. Attempting
|
||||
to <TT
|
||||
CLASS="LITERAL"
|
||||
>.advance</TT
|
||||
> to a point behind the current
|
||||
program counter is an assemble-time error.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.alias</TT
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>label</I
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>value</I
|
||||
>: 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).</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.byte</TT
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>arg</I
|
||||
> [ , <I
|
||||
CLASS="EMPHASIS"
|
||||
>arg</I
|
||||
>, ... ]:
|
||||
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.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.checkpc</TT
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>address</I
|
||||
>: Ensures that the
|
||||
program counter is less than or equal to the address
|
||||
specified, and emits an assemble-time error if it is not.
|
||||
<I
|
||||
CLASS="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.</I
|
||||
></P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.data</TT
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>[label]</I
|
||||
>: Sets the segment to
|
||||
the segment name specified and disallows output. If no label
|
||||
is given, switches to the default data segment.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.incbin</TT
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>filename</I
|
||||
>: Inserts the
|
||||
contents of the file specified as binary data. Use it to
|
||||
include graphics information, precompiled code, or other
|
||||
non-assembler data.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.include</TT
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>filename</I
|
||||
>: Includes the
|
||||
entirety of the file specified at that point in the program.
|
||||
Use this to order your final sources.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.org</TT
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>address</I
|
||||
>: Sets the program
|
||||
counter to the address specified. <I
|
||||
CLASS="EMPHASIS"
|
||||
>This does not emit any
|
||||
code in and of itself, nor does it overwrite anything that
|
||||
previously existed.</I
|
||||
> If you wish to jump ahead in memory,
|
||||
use <TT
|
||||
CLASS="LITERAL"
|
||||
>.advance</TT
|
||||
>.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.require</TT
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>filename</I
|
||||
>: Includes the entirety
|
||||
of the file specified at that point in the program. Unlike <TT
|
||||
CLASS="LITERAL"
|
||||
>.include</TT
|
||||
>,
|
||||
however, code included with <TT
|
||||
CLASS="LITERAL"
|
||||
>.require</TT
|
||||
> will only be inserted once.
|
||||
The <TT
|
||||
CLASS="LITERAL"
|
||||
>.require</TT
|
||||
> 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.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.space</TT
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>label</I
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>size</I
|
||||
>: 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 <I
|
||||
CLASS="EMPHASIS"
|
||||
>size</I
|
||||
>
|
||||
steps ahead. No actual code is produced. This is equivalent
|
||||
to <TT
|
||||
CLASS="LITERAL"
|
||||
>label: .org ^+size</TT
|
||||
>.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.text</TT
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>[label]</I
|
||||
>: Sets the segment to
|
||||
the segment name specified and allows output. If no label is
|
||||
given, switches to the default text segment.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.word</TT
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>arg</I
|
||||
> [ , <I
|
||||
CLASS="EMPHASIS"
|
||||
>arg</I
|
||||
>, ... ]:
|
||||
Like <TT
|
||||
CLASS="LITERAL"
|
||||
>.byte</TT
|
||||
>, 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.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.dword</TT
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>arg</I
|
||||
> [ , <I
|
||||
CLASS="EMPHASIS"
|
||||
>arg</I
|
||||
>, ...]:
|
||||
Like <TT
|
||||
CLASS="LITERAL"
|
||||
>.word</TT
|
||||
>, but for 32-bit values.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.wordbe</TT
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>arg</I
|
||||
> [ , <I
|
||||
CLASS="EMPHASIS"
|
||||
>arg</I
|
||||
>, ...]:
|
||||
Like <TT
|
||||
CLASS="LITERAL"
|
||||
>.word</TT
|
||||
>, but stores the value in a big-endian format (high byte first).</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.dwordbe</TT
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>arg</I
|
||||
> [ , <I
|
||||
CLASS="EMPHASIS"
|
||||
>arg</I
|
||||
>, ...]:
|
||||
Like <TT
|
||||
CLASS="LITERAL"
|
||||
>.dword</TT
|
||||
>, but stores the value high byte first.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.scope</TT
|
||||
>: Starts a new scope block. Labels
|
||||
that begin with an underscore are only reachable from within
|
||||
their innermost enclosing <TT
|
||||
CLASS="LITERAL"
|
||||
>.scope</TT
|
||||
> statement.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.scend</TT
|
||||
>: Ends a scope block. Makes the
|
||||
temporary labels defined since the last <TT
|
||||
CLASS="LITERAL"
|
||||
>.scope</TT
|
||||
>
|
||||
statement unreachable, and permits them to be redefined in a
|
||||
new scope.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.macro</TT
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>name</I
|
||||
>: Begins a macro
|
||||
definition block. This is a scope block that can be inlined
|
||||
at arbitrary points with <TT
|
||||
CLASS="LITERAL"
|
||||
>.invoke</TT
|
||||
>. Arguments to the
|
||||
macro will be bound to temporary labels with names like
|
||||
<TT
|
||||
CLASS="LITERAL"
|
||||
>_1</TT
|
||||
>, <TT
|
||||
CLASS="LITERAL"
|
||||
>_2</TT
|
||||
>, etc.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.macend</TT
|
||||
>: Ends a macro definition
|
||||
block.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.invoke</TT
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>label</I
|
||||
> [<I
|
||||
CLASS="EMPHASIS"
|
||||
>argument</I
|
||||
> [,
|
||||
<I
|
||||
CLASS="EMPHASIS"
|
||||
>argument</I
|
||||
> ...]]: invokes (inlines) the specified
|
||||
macro, binding the values of the arguments to the ones the
|
||||
macro definition intends to read. A shorthand for <TT
|
||||
CLASS="LITERAL"
|
||||
>.invoke</TT
|
||||
>
|
||||
is the name of the macro to invoke, backquoted.</P
|
||||
></LI
|
||||
></UL
|
||||
><P
|
||||
> The following directives are deprecated, added for
|
||||
compatibility with the old Perl
|
||||
assembler <B
|
||||
CLASS="COMMAND"
|
||||
>P65</B
|
||||
>. Use
|
||||
the <TT
|
||||
CLASS="LITERAL"
|
||||
>-d</TT
|
||||
> option to Ophis to enable
|
||||
them.
|
||||
</P
|
||||
><P
|
||||
></P
|
||||
><UL
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.ascii</TT
|
||||
>: Equivalent to <TT
|
||||
CLASS="LITERAL"
|
||||
>.byte</TT
|
||||
>,
|
||||
which didn't used to be able to handle strings.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.code</TT
|
||||
>: Equivalent to <TT
|
||||
CLASS="LITERAL"
|
||||
>.text</TT
|
||||
>.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.segment</TT
|
||||
>: Equivalent to <TT
|
||||
CLASS="LITERAL"
|
||||
>.text</TT
|
||||
>,
|
||||
from when there was no distinction between <TT
|
||||
CLASS="LITERAL"
|
||||
>.text</TT
|
||||
> and
|
||||
<TT
|
||||
CLASS="LITERAL"
|
||||
>.data</TT
|
||||
> segments.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.address</TT
|
||||
>: Equivalent to
|
||||
<TT
|
||||
CLASS="LITERAL"
|
||||
>.word</TT
|
||||
>.</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
><TT
|
||||
CLASS="LITERAL"
|
||||
>.link</TT
|
||||
> <I
|
||||
CLASS="EMPHASIS"
|
||||
>filename address</I
|
||||
>: Assembles
|
||||
the file specified as if it began at the address specified.
|
||||
This is generally for use in <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"top-level"</SPAN
|
||||
> files, where there
|
||||
is not necessarily a one-to-one correspondence between file
|
||||
position and memory position. This is equivalent to an
|
||||
<TT
|
||||
CLASS="LITERAL"
|
||||
>.org</TT
|
||||
> directive followed by an <TT
|
||||
CLASS="LITERAL"
|
||||
>.include</TT
|
||||
>.
|
||||
With the introduction of the <TT
|
||||
CLASS="LITERAL"
|
||||
>.org</TT
|
||||
> directive this one is
|
||||
less useful (and in most cases, any <TT
|
||||
CLASS="LITERAL"
|
||||
>.org</TT
|
||||
> statement
|
||||
you use will actually be at the top of the <TT
|
||||
CLASS="LITERAL"
|
||||
>.include</TT
|
||||
>d
|
||||
file).</P
|
||||
></LI
|
||||
></UL
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="NAVFOOTER"
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"><TABLE
|
||||
SUMMARY="Footer navigation table"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
CELLPADDING="0"
|
||||
CELLSPACING="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="x692.html"
|
||||
ACCESSKEY="P"
|
||||
><<< Previous</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="book1.html"
|
||||
ACCESSKEY="H"
|
||||
>Home</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Macros</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="a505.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
Binary file not shown.
|
@ -1,17 +1,99 @@
|
|||
"""Command line options data.
|
||||
"""Command line options data."""
|
||||
|
||||
verbose:
|
||||
0: Only report errors
|
||||
1: Announce each file as it is read, and data count (default)
|
||||
2: As above, but also announce each pass.
|
||||
3: As above, but print the IR after each pass.
|
||||
4: As above, but print the labels after each pass.
|
||||
import optparse
|
||||
|
||||
6510 compatibility and deprecation are handled in Ophis.Main."""
|
||||
# Copyright 2002-2014 Michael C. Martin and additional contributors.
|
||||
# You may use, modify, and distribute this file under the MIT
|
||||
# license: See README for details.
|
||||
|
||||
# Copyright 2002 Michael C. Martin.
|
||||
# You may use, modify, and distribute this file under the BSD
|
||||
# license: See LICENSE.txt for details.
|
||||
enable_branch_extend = True
|
||||
enable_undoc_ops = False
|
||||
enable_65c02_exts = False
|
||||
enable_4502_exts = False
|
||||
|
||||
verbose = 1;
|
||||
warn_on_branch_extend = True
|
||||
|
||||
print_summary = True
|
||||
print_loaded_files = False
|
||||
print_pass = False
|
||||
print_ir = False
|
||||
print_labels = False
|
||||
|
||||
infiles = None
|
||||
outfile = None
|
||||
listfile = None
|
||||
mapfile = None
|
||||
|
||||
|
||||
def parse_args(raw_args):
|
||||
"Populate the module's globals based on the command-line options given."
|
||||
global enable_collapse, enable_branch_extend
|
||||
global enable_undoc_ops, enable_65c02_exts, enable_4502_exts
|
||||
global warn_on_branch_extend
|
||||
global print_summary, print_loaded_files
|
||||
global print_pass, print_ir, print_labels
|
||||
global infiles, outfile, listfile, mapfile
|
||||
|
||||
parser = optparse.OptionParser(
|
||||
usage="Usage: %prog [options] srcfile [srcfile ...]",
|
||||
version="Ophis 6502 cross-assembler, version 2.1")
|
||||
|
||||
parser.add_option("-o", default=None, dest="outfile",
|
||||
help="Output filename (default 'ophis.bin')")
|
||||
parser.add_option("-l", default=None, dest="listfile",
|
||||
help="Listing filename (not created by default)")
|
||||
parser.add_option("-m", default=None, dest="mapfile",
|
||||
help="Label-address map filename (not created by default)")
|
||||
|
||||
ingrp = optparse.OptionGroup(parser, "Input options")
|
||||
ingrp.add_option("-u", "--undoc", action="store_true", default=False,
|
||||
help="Enable 6502 undocumented opcodes")
|
||||
ingrp.add_option("-c", "--65c02", action="store_true", default=False,
|
||||
dest="c02", help="Enable 65c02 extended instruction set")
|
||||
ingrp.add_option("-4", "--4502", action="store_true", default=False,
|
||||
dest="csg4502", help="Enable 4502 extended instruction set")
|
||||
|
||||
outgrp = optparse.OptionGroup(parser, "Console output options")
|
||||
outgrp.add_option("-v", "--verbose", action="store_const", const=2,
|
||||
help="Verbose mode", default=1)
|
||||
outgrp.add_option("-q", "--quiet", action="store_const", help="Quiet mode",
|
||||
dest="verbose", const=0)
|
||||
outgrp.add_option("-d", "--debug", action="count", dest="verbose",
|
||||
help=optparse.SUPPRESS_HELP)
|
||||
outgrp.add_option("--no-warn", action="store_false", dest="warn",
|
||||
default=True, help="Do not print warnings")
|
||||
|
||||
bingrp = optparse.OptionGroup(parser, "Compilation options")
|
||||
bingrp.add_option("--no-branch-extend", action="store_false",
|
||||
dest="enable_branch_extend", default="True",
|
||||
help="Disable branch-extension pass")
|
||||
|
||||
parser.add_option_group(ingrp)
|
||||
parser.add_option_group(outgrp)
|
||||
parser.add_option_group(bingrp)
|
||||
|
||||
(options, args) = parser.parse_args(raw_args)
|
||||
|
||||
if len(args) == 0:
|
||||
parser.error("No input files specified")
|
||||
if options.c02 and options.undoc:
|
||||
parser.error("--undoc and --65c02 are mutually exclusive")
|
||||
if options.c02 and options.csg4502:
|
||||
parser.error("--65c02 and --4502 are mutually exclusive")
|
||||
if options.csg4502 and options.undoc:
|
||||
parser.error("--undoc and --4502 are mutually exclusive")
|
||||
|
||||
infiles = args
|
||||
outfile = options.outfile
|
||||
listfile = options.listfile
|
||||
mapfile = options.mapfile
|
||||
enable_branch_extend = options.enable_branch_extend
|
||||
enable_undoc_ops = options.undoc
|
||||
enable_65c02_exts = options.c02
|
||||
enable_4502_exts = options.csg4502
|
||||
warn_on_branch_extend = options.warn
|
||||
print_summary = options.verbose > 0 # no options set
|
||||
print_loaded_files = options.verbose > 1 # v
|
||||
print_pass = options.verbose > 2 # dd
|
||||
print_ir = options.verbose > 3 # ddd
|
||||
print_labels = options.verbose > 4 # dddd
|
||||
|
|
|
@ -1,96 +1,177 @@
|
|||
"""Core pragmas
|
||||
|
||||
Provides the core assembler directives. It does not guarantee
|
||||
compatibility with older versions of P65-Perl."""
|
||||
Provides the core assembler directives."""
|
||||
|
||||
# Copyright 2002 Michael C. Martin.
|
||||
# You may use, modify, and distribute this file under the BSD
|
||||
# license: See LICENSE.txt for details.
|
||||
|
||||
from __future__ import nested_scopes
|
||||
# Copyright 2002-2014 Michael C. Martin and additional contributors.
|
||||
# You may use, modify, and distribute this file under the MIT
|
||||
# license: See README for details.
|
||||
|
||||
import Ophis.CmdLine
|
||||
import Ophis.IR as IR
|
||||
import Ophis.Frontend as FE
|
||||
import Ophis.Errors as Err
|
||||
import math
|
||||
import os.path
|
||||
|
||||
loadedfiles={}
|
||||
basecharmap = "".join([chr(x) for x in range(256)])
|
||||
currentcharmap = basecharmap
|
||||
|
||||
|
||||
def reset():
|
||||
global loadedfiles, currentcharmap, basecharmap
|
||||
loadedfiles={}
|
||||
global currentcharmap, basecharmap
|
||||
FE.loadedfiles = {}
|
||||
currentcharmap = basecharmap
|
||||
|
||||
|
||||
def pragmaOutfile(ppt, line, result):
|
||||
"Sets the output file if it hasn't already been set"
|
||||
filename = line.expect("STRING").value
|
||||
line.expect("EOL")
|
||||
if type(filename) == str and Ophis.CmdLine.outfile is None:
|
||||
Ophis.CmdLine.outfile = filename
|
||||
|
||||
|
||||
def pragmaListfile(ppt, line, result):
|
||||
"Sets the listing file if it hasn't already been set"
|
||||
filename = line.expect("STRING").value
|
||||
line.expect("EOL")
|
||||
if type(filename) == str and Ophis.CmdLine.listfile is None:
|
||||
Ophis.CmdLine.listfile = filename
|
||||
|
||||
|
||||
def pragmaInclude(ppt, line, result):
|
||||
"Includes a source file"
|
||||
filename = line.expect("STRING").value
|
||||
line.expect("EOL")
|
||||
if type(filename)==str: result.append(FE.parse_file(ppt, filename))
|
||||
if type(filename) == str:
|
||||
result.append(FE.parse_file(ppt, filename))
|
||||
|
||||
|
||||
def pragmaRequire(ppt, line, result):
|
||||
"Includes a source file at most one time"
|
||||
filename = line.expect("STRING").value
|
||||
line.expect("EOL")
|
||||
if type(filename) == str:
|
||||
global loadedfiles
|
||||
if filename not in loadedfiles:
|
||||
loadedfiles[filename]=1
|
||||
result.append(FE.parse_file(ppt, filename))
|
||||
result.append(FE.parse_file(ppt, filename, True))
|
||||
|
||||
|
||||
def pragmaIncbin(ppt, line, result):
|
||||
"Includes a binary file"
|
||||
filename = line.expect("STRING").value
|
||||
offset = IR.ConstantExpr(0)
|
||||
size = None
|
||||
if str(line.lookahead(0)) == ",":
|
||||
line.pop()
|
||||
offset = FE.parse_expr(line)
|
||||
if str(line.lookahead(0)) == ",":
|
||||
line.pop()
|
||||
size = FE.parse_expr(line)
|
||||
line.expect("EOL")
|
||||
if type(filename) == str:
|
||||
f = file(filename, "rb")
|
||||
bytes = f.read()
|
||||
try:
|
||||
f = open(os.path.join(FE.context_directory, filename), "rb")
|
||||
if offset.hardcoded and (size is None or size.hardcoded):
|
||||
# We know how big it will be, we can just use the values.
|
||||
# First check to make sure they're sane
|
||||
if offset.value() < 0:
|
||||
Err.log("Offset may not be negative")
|
||||
f.close()
|
||||
bytes = [IR.ConstantExpr(ord(x)) for x in bytes]
|
||||
return
|
||||
f.seek(0, 2) # Seek to end of file
|
||||
if offset.value() > f.tell():
|
||||
Err.log("Offset runs past end of file")
|
||||
f.close()
|
||||
return
|
||||
if size is not None:
|
||||
if size.value() < 0:
|
||||
Err.log("Length may not be negative")
|
||||
f.close()
|
||||
return
|
||||
if offset.value() + size.value() > f.tell():
|
||||
Err.log(".incbin length too long")
|
||||
f.close()
|
||||
return
|
||||
if size is None:
|
||||
size = IR.ConstantExpr(-1)
|
||||
f.seek(offset.value())
|
||||
bytes = f.read(size.value())
|
||||
bytes = [IR.ConstantExpr(x) for x in bytes]
|
||||
result.append(IR.Node(ppt, "Byte", *bytes))
|
||||
else:
|
||||
# offset or length could change based on label placement.
|
||||
# This seems like an unbelievably bad idea, but since we
|
||||
# don't have constant prop it will happen for any symbolic
|
||||
# alias. Don't use symbolic aliases when extracting tiny
|
||||
# pieces out of humongous files, I guess.
|
||||
bytes = f.read()
|
||||
bytes = [IR.ConstantExpr(x) for x in bytes]
|
||||
if size is None:
|
||||
size = IR.SequenceExpr([IR.ConstantExpr(len(bytes)),
|
||||
"-",
|
||||
offset])
|
||||
result.append(IR.Node(ppt, "ByteRange", offset, size, *bytes))
|
||||
f.close()
|
||||
except IOError:
|
||||
Err.log("Could not read " + filename)
|
||||
return
|
||||
|
||||
|
||||
def pragmaCharmap(ppt, line, result):
|
||||
"Modify the character map."
|
||||
global currentcharmap, basecharmap
|
||||
bytes = readData(line)
|
||||
if len(bytes) == 0:
|
||||
if str(line.lookahead(0)) == "EOL":
|
||||
currentcharmap = basecharmap
|
||||
else:
|
||||
bytes = readData(line)
|
||||
try:
|
||||
base = bytes[0].data
|
||||
newsubstr = "".join([chr(x.data) for x in bytes[1:]])
|
||||
currentcharmap = currentcharmap[:base] + newsubstr + currentcharmap[base+len(newsubstr):]
|
||||
currentcharmap = currentcharmap[:base] + newsubstr + \
|
||||
currentcharmap[base + len(newsubstr):]
|
||||
if len(currentcharmap) != 256 or base < 0 or base > 255:
|
||||
Err.log("Charmap replacement out of range")
|
||||
currentcharmap = currentcharmap[:256]
|
||||
except ValueError:
|
||||
Err.log("Illegal character in .charmap directive")
|
||||
|
||||
|
||||
def pragmaCharmapbin(ppt, line, result):
|
||||
"Load a new character map from a file"
|
||||
global currentcharmap
|
||||
filename = line.expect("STRING").value
|
||||
line.expect("EOL")
|
||||
if type(filename) == str:
|
||||
f = file(filename, "rb")
|
||||
try:
|
||||
f = open(os.path.join(FE.context_directory, filename), "rb")
|
||||
bytes = f.read()
|
||||
f.close()
|
||||
except IOError:
|
||||
Err.log("Could not read " + filename)
|
||||
return
|
||||
if len(bytes) == 256:
|
||||
currentcharmap = bytes
|
||||
else:
|
||||
Err.log("Character map " + filename + " not 256 bytes long")
|
||||
|
||||
|
||||
def pragmaOrg(ppt, line, result):
|
||||
"Relocates the PC with no output"
|
||||
newPC = FE.parse_expr(line)
|
||||
line.expect("EOL")
|
||||
result.append(IR.Node(ppt, "SetPC", newPC))
|
||||
|
||||
|
||||
def pragmaAdvance(ppt, line, result):
|
||||
"Outputs filler until reaching the target PC"
|
||||
newPC = FE.parse_expr(line)
|
||||
if str(line.lookahead(0)) == ",":
|
||||
line.pop()
|
||||
fillexpr = FE.parse_expr(line)
|
||||
else:
|
||||
fillexpr = IR.ConstantExpr(0)
|
||||
line.expect("EOL")
|
||||
result.append(IR.Node(ppt, "Advance", newPC))
|
||||
result.append(IR.Node(ppt, "Advance", newPC, fillexpr))
|
||||
|
||||
|
||||
def pragmaCheckpc(ppt, line, result):
|
||||
"Enforces that the PC has not exceeded a certain point"
|
||||
|
@ -98,19 +179,24 @@ def pragmaCheckpc(ppt, line, result):
|
|||
line.expect("EOL")
|
||||
result.append(IR.Node(ppt, "CheckPC", target))
|
||||
|
||||
|
||||
def pragmaAlias(ppt, line, result):
|
||||
"Assigns an arbitrary label"
|
||||
lbl = line.expect("LABEL").value
|
||||
target = FE.parse_expr(line)
|
||||
result.append(IR.Node(ppt, "Label", lbl, target))
|
||||
|
||||
|
||||
def pragmaSpace(ppt, line, result):
|
||||
"Reserves space in a data segment for a variable"
|
||||
lbl = line.expect("LABEL").value
|
||||
size = line.expect("NUM").value
|
||||
line.expect("EOL")
|
||||
result.append(IR.Node(ppt, "Label", lbl, IR.PCExpr()))
|
||||
result.append(IR.Node(ppt, "SetPC", IR.SequenceExpr([IR.PCExpr(), "+", IR.ConstantExpr(size)])))
|
||||
result.append(IR.Node(ppt, "SetPC",
|
||||
IR.SequenceExpr([IR.PCExpr(), "+",
|
||||
IR.ConstantExpr(size)])))
|
||||
|
||||
|
||||
def pragmaText(ppt, line, result):
|
||||
"Switches to a text segment"
|
||||
|
@ -122,6 +208,7 @@ def pragmaText(ppt, line, result):
|
|||
segment = "*text-default*"
|
||||
result.append(IR.Node(ppt, "TextSegment", segment))
|
||||
|
||||
|
||||
def pragmaData(ppt, line, result):
|
||||
"Switches to a data segment (no output allowed)"
|
||||
next = line.expect("LABEL", "EOL")
|
||||
|
@ -132,67 +219,114 @@ def pragmaData(ppt, line, result):
|
|||
segment = "*data-default*"
|
||||
result.append(IR.Node(ppt, "DataSegment", segment))
|
||||
|
||||
|
||||
def pragmaCbmfloat(ppt, line, result):
|
||||
"Parses a string into a CBM BASIC format floating point number"
|
||||
data = []
|
||||
while True:
|
||||
try:
|
||||
v_str = line.expect("STRING").value
|
||||
v = float(v_str)
|
||||
if v == 0.0:
|
||||
data.extend([0,0,0,0,0])
|
||||
else:
|
||||
if v < 0.0:
|
||||
sign = 128
|
||||
v = -v
|
||||
else:
|
||||
sign = 0
|
||||
expt = math.floor(math.log(v, 2))
|
||||
if expt >= -128 and expt <= 126:
|
||||
mantissa = v / (2**expt)
|
||||
m1 = (mantissa - 1.0) * 128 + sign
|
||||
m2 = m1 * 256
|
||||
m3 = m2 * 256
|
||||
m4 = m3 * 256
|
||||
data.extend([int(x) % 256 for x in [expt+129,m1,m2,m3,m4]])
|
||||
else:
|
||||
Err.log("Floating point constant out of range")
|
||||
except ValueError:
|
||||
Err.log("Expected: floating point")
|
||||
next = line.expect(',', 'EOL').type
|
||||
if next == 'EOL':
|
||||
break
|
||||
bytes = [IR.ConstantExpr(x) for x in data]
|
||||
result.append(IR.Node(ppt, "Byte", *bytes))
|
||||
|
||||
|
||||
def readData(line):
|
||||
"Read raw data from a comma-separated list"
|
||||
if line.lookahead(0).type == "STRING":
|
||||
data = [IR.ConstantExpr(ord(x)) for x in line.expect("STRING").value.translate(currentcharmap)]
|
||||
data = [IR.ConstantExpr(ord(x))
|
||||
for x in line.expect("STRING").value.translate(currentcharmap)]
|
||||
else:
|
||||
data = [FE.parse_expr(line)]
|
||||
next = line.expect(',', 'EOL').type
|
||||
while next == ',':
|
||||
if line.lookahead(0).type == "STRING":
|
||||
data.extend([IR.ConstantExpr(ord(x)) for x in line.expect("STRING").value])
|
||||
data.extend([IR.ConstantExpr(ord(x))
|
||||
for x in line.expect("STRING").value])
|
||||
else:
|
||||
data.append(FE.parse_expr(line))
|
||||
next = line.expect(',', 'EOL').type
|
||||
return data
|
||||
|
||||
|
||||
def pragmaByte(ppt, line, result):
|
||||
"Raw data, a byte at a time"
|
||||
bytes = readData(line)
|
||||
result.append(IR.Node(ppt, "Byte", *bytes))
|
||||
|
||||
|
||||
def pragmaWord(ppt, line, result):
|
||||
"Raw data, a word at a time, little-endian"
|
||||
words = readData(line)
|
||||
result.append(IR.Node(ppt, "Word", *words))
|
||||
|
||||
|
||||
def pragmaDword(ppt, line, result):
|
||||
"Raw data, a double-word at a time, little-endian"
|
||||
dwords = readData(line)
|
||||
result.append(IR.Node(ppt, "Dword", *dwords))
|
||||
|
||||
|
||||
def pragmaWordbe(ppt, line, result):
|
||||
"Raw data, a word at a time, big-endian"
|
||||
words = readData(line)
|
||||
result.append(IR.Node(ppt, "WordBE", *words))
|
||||
|
||||
|
||||
def pragmaDwordbe(ppt, line, result):
|
||||
"Raw data, a dword at a time, big-endian"
|
||||
dwords = readData(line)
|
||||
result.append(IR.Node(ppt, "DwordBE", *dwords))
|
||||
|
||||
|
||||
def pragmaScope(ppt, line, result):
|
||||
"Create a new lexical scoping block"
|
||||
line.expect("EOL")
|
||||
result.append(IR.Node(ppt, "ScopeBegin"))
|
||||
|
||||
|
||||
def pragmaScend(ppt, line, result):
|
||||
"End the innermost lexical scoping block"
|
||||
line.expect("EOL")
|
||||
result.append(IR.Node(ppt, "ScopeEnd"))
|
||||
|
||||
|
||||
def pragmaMacro(ppt, line, result):
|
||||
"Begin a macro definition"
|
||||
lbl = line.expect("LABEL").value
|
||||
line.expect("EOL")
|
||||
result.append(IR.Node(ppt, "MacroBegin", lbl))
|
||||
|
||||
|
||||
def pragmaMacend(ppt, line, result):
|
||||
"End a macro definition"
|
||||
line.expect("EOL")
|
||||
result.append(IR.Node(ppt, "MacroEnd"))
|
||||
|
||||
|
||||
def pragmaInvoke(ppt, line, result):
|
||||
macro = line.expect("LABEL").value
|
||||
if line.lookahead(0).type == "EOL":
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user