acme/docs/AddrModes.txt
2020-06-21 19:06:12 +00:00

172 lines
6.0 KiB
Plaintext

ACME
...the ACME Crossassembler for Multiple Environments
--- addressing modes ---
If an instruction can be used with different addressing modes, ACME
has to decide which one to use. Several instructions of the 6502 CPU
can be used with either "absolute" addressing or "zeropage-absolute"
addressing. The former one means there's a 16-bit argument, the latter
one means there's an 8-bit argument.
And the 65816 CPU even has some instructions with 24-bit addressing...
So how does ACME know which addressing mode to use?
The simple approach is to always use the smallest possible argument,
of course: If the argument fits in a byte, use zeropage addressing. If
it doesn't, use absolute addressing. If it needs more than two bytes
and the 65816 CPU is chosen, use 24-bit addressing.
In most cases this works - with two exceptions. The remainder of this
text may sound somewhat confusing now, so if you don't have any
problems with addressing modes, then don't bother trying to understand
everything this texts says. :)
The two exceptions are:
*** 1) Symbols are defined too late
If ACME cannot figure out the argument value in the first pass, it
assumes that the instruction uses 16-bit addressing.
If it later finds out that the argument only needs 8 bits, ACME gives
a warning ("using oversized addressing mode") and continues. However,
if it finds out that the argument needs 24 bits, it gives an error.
These problems can be solved by defining the symbols *before* using
them, so that the value can be figured out in the first pass. If this
is not possible, you can use the postfix method, effectively exactly
defining what addressing mode to use. The postfix method is described
in a separate section below.
*** 2) You *want* to use an oversized addressing mode
On the 65816 CPU, "zeropage addressing" is called "direct page
addressing". The difference is that the position of the "direct page"
can be changed. Then, "lda $fa" does not necessarily access the same
memory location as "lda $00fa" anymore. The same goes for 16- and 24-
bit addressing: "lda $fabc" does not necessarily access the same
memory location as "lda $00fabc", because the default bank can be set
to something other than zero.
But even on the plain 6502 CPU you might want to force ACME to use an
oversized addressing mode, for example because of timing issues.
Again there are two ways to solve the problem: You can define the
target location using leading zeros. ACME will then use an addressing
mode that is big enough even if the leading zeros would have been
other digits:
symbol1 = $fb
symbol2 = $00fd
symbol3 = $0000ff
lda $fa
sta $00fc
lda $0000fe
sta symbol1
lda symbol2
sta symbol3
will be assembled to
a5 fa ; lda $fa
8d fc 00 ; sta $00fc
af fe 00 00 ; lda $0000fe
85 fb ; sta $fb
ad fd 00 ; lda $00fd
8f ff 00 00 ; sta $0000ff
This feature can be disabled using the "--ignore-zeroes" CLI switch.
The other possibility is to use the postfix method (described in the
next section).
*** The postfix method
Warning: This may sound very complicated at first, but I think that
once you get used to it you'll agree it's useful. If you don't want to
use this, stick to the "leading zeros" method and don't bother about
postfixes.
Still with me? Okay:
You can force ACME to use a specific addressing mode by adding "+1",
"+2" or "+3" to the assembler mnemonic. Each one of these postfixes
sets the relevant "Force Bit" in ACME's result. If Force Bit 3 is set,
ACME will use 24-bit addressing. Force Bit 2 means 16-bit addressing
and Force Bit 1 means 8-bit addressing. Higher Force Bits have higher
priorities.
Here's an (overly complicated) example:
symbol1 = $fb
symbol2 = $fd
symbol3+3 = $ff ; set Force Bit 3 and store in symbol's flags
ldx $fa
ldy+2 $fc ; set Force Bit 2 (16-bit addressing)
lda+3 $fe ; set Force Bit 3 (24-bit addressing)
stx symbol1
sty+2 symbol2 ; set Force Bit 2 (16-bit addressing)
sta symbol3 ; no need to set Force Bit 3 as it is
; already set in "symbol3".
will be assembled to
a6 fa ; ldx $fa
ac fc 00 ; ldy $00fc
af fe 00 00 ; lda $0000fe
86 fb ; stx $fb
8c fd 00 ; sty $00fd
8f ff 00 00 ; sta $0000ff
Postfixes added directly to the mnemonic have higher priority than
those given to the argument. As you can see, you can add the postfix
to the symbol definition as well (equivalent to leading zeros).
Applying the byte extraction operators ("<" gives the low byte, ">"
gives the high byte and "^" gives the bank byte of a value) to any
value will clear the argument's Force Bits 2 and 3 and set Force
Bit 1 instead. So "lda <symbol" will use 8-bit addressing, regardless
of the symbol's Force Bits. Of course, you can change this by
postfixing the instruction again... :)
*** The algorithm to find the right addressing mode
You don't need to read this paragraph just to use ACME, I only
included it for completeness' sake. This is a description of ACME's
strategy for finding the addressing mode to use:
First, ACME checks whether the instruction has any postfix. If it has,
ACME acts upon it. So postfixes have the highest priority.
Otherwise, ACME checks whether the argument has any Force Bits set
(because of leading zeros or byte extraction operators, for example)
or references symbols that have. If any Force Bit is set, ACME acts
upon it. This is the next priority level: Leading zeros or postfixes
added to the symbol definition.
Otherwise, ACME checks whether the argument could be figured out in
the first pass. If it couldn't, ACME tries to use a default addressing
mode (normally this will be 16-bit addressing).
In case the value could be figured out even in the first pass, then
everything's cool and froody: ACME just looks at the argument's value
and uses the smallest addressing mode that matches.
I admit that this algorithm sounds complicated, but it hasn't failed
yet. :) And in everyday usage, it does exactly what one expects.
If you want to take a closer look at the algorithm, examine the
"calc_arg_size" function in the "src/mnemo.c" file.