User Tools

Site Tools


6510/8502 Undocumented Commands

         -- A brief explanation about what may happen while
                using don't care states.

        ANE $8B         A = (A | #$EE) & X & #byte
                        same as
                        A = ((A & #$11 & X) | ( #$EE & X)) & #byte

                        In real 6510/8502 the internal parameter #$11
                        may occasionally be #$10, #$01 or even #$00.
                        This occurs when the video chip starts DMA
                        between the opcode fetch and the parameter fetch
                        of the instruction.  The value probably depends
                        on the data that was left on the bus by the VIC-II.

        LXA $AB         C=Lehti:   A = X = ANE
                        Alternate: A = X = (A & #byte)

                        TXA and TAX have to be responsible for these.

        SHA $93,$9F     Store (A & X & (ADDR_HI + 1))
        SHX $9E         Store (X & (ADDR_HI + 1))
        SHY $9C         Store (Y & (ADDR_HI + 1))
        SHS $9B         SHA and TXS, where X is replaced by (A & X).

                        Note: The value to be stored is copied also
                        to ADDR_HI if page boundary is crossed.

        SBX $CB         Carry and Decimal flags are ignored but the
                        Carry flag will be set in substraction. This
                        is due to the CMP command, which is executed
                        instead of the real SBC.

        ARR $6B         This instruction first performs an AND
                        between the accumulator and the immediate
                        parameter, then it shifts the accumulator to
                        the right. However, this is not the whole
                        truth. See the description below.

Many undocumented commands do not use AND between registers, the CPU
just throws the bytes to a bus simultaneously and lets the
open-collector drivers perform the AND. I.e. the command called 'SAX',
which is in the STORE section (opcodes $A0...$BF), stores the result
of (A & X) by this way.

More fortunate is its opposite, 'LAX' which just loads a byte
simultaneously into both A and X.

        $6B  ARR

This instruction seems to be a harmless combination of AND and ROR at
first sight, but it turns out that it affects the V flag and also has
a special kind of decimal mode. This is because the instruction has
inherited some properties of the ADC instruction ($69) in addition to
the ROR ($6A).

In Binary mode (D flag clear), the instruction effectively does an AND
between the accumulator and the immediate parameter, and then shifts
the accumulator to the right, copying the C flag to the 8th bit. It
sets the Negative and Zero flags just like the ROR would. The ADC code
shows up in the Carry and oVerflow flags. The C flag will be copied
from the bit 6 of the result (which doesn't seem too logical), and the
V flag is the result of an Exclusive OR operation between the bit 6
and the bit 5 of the result.  This makes sense, since the V flag will
be normally set by an Exclusive OR, too.

In Decimal mode (D flag set), the ARR instruction first performs the
AND and ROR, just like in Binary mode. The N flag will be copied from
the initial C flag, and the Z flag will be set according to the ROR
result, as expected. The V flag will be set if the bit 6 of the
accumulator changed its state between the AND and the ROR, cleared

Now comes the funny part. If the low nybble of the AND result,
incremented by its lowmost bit, is greater than 5, the low nybble in
the ROR result will be incremented by 6. The low nybble may overflow
as a consequence of this BCD fixup, but the high nybble won't be
adjusted. The high nybble will be BCD fixed in a similar way. If the
high nybble of the AND result, incremented by its lowmost bit, is
greater than 5, the high nybble in the ROR result will be incremented
by 6, and the Carry flag will be set. Otherwise the C flag will be

To help you understand this description, here is a C routine that
illustrates the ARR operation in Decimal mode:

           A,  /* Accumulator */
           AL, /* low nybble of accumulator */
           AH, /* high nybble of accumulator */

           C,  /* Carry flag */
           Z,  /* Zero flag */
           V,  /* oVerflow flag */
           N,  /* Negative flag */

           t,  /* temporary value */
           s;  /* value to be ARRed with Accumulator */

        t = A & s;                      /* Perform the AND. */

        AH = t >> 4;                    /* Separate the high */
        AL = t & 15;                    /* and low nybbles. */

        N = C;                          /* Set the N and */
        Z = !(A = (t >> 1) | (C << 7)); /* Z flags traditionally */
        V = (t ^ A) & 64;               /* and V flag in a weird way. */

        if (AL + (AL & 1) > 5)          /* BCD "fixup" for low nybble. */
          A = (A & 0xF0) | ((A + 6) & 0xF);

        if (C = AH + (AH & 1) > 5)      /* Set the Carry flag. */
          A = (A + 0x60) & 0xFF;        /* BCD "fixup" for high nybble. */

        $CB  SBX   X <- (A & X) - Immediate

The 'SBX' ($CB) may seem to be very complex operation, even though it
is a combination of the subtraction of accumulator and parameter, as
in the 'CMP' instruction, and the command 'DEX'. As a result, both A
and X are connected to ALU but only the subtraction takes place. Since
the comparison logic was used, the result of subtraction should be
normally ignored, but the 'DEX' now happily stores to X the value of
(A & X) - Immediate.  That is why this instruction does not have any
decimal mode, and it does not affect the V flag. Also Carry flag will
be ignored in the subtraction but set according to the result.


begin 644 vsbx


begin 644 sbx

These test programs show if your machine is compatible with ours
regarding the opcode $CB. The first test, vsbx, proves that SBX does
not affect the V flag. The latter one, sbx, proves the rest of our
theory. The vsbx test tests 33554432 SBX combinations (16777216
different A, X and Immediate combinations, and two different V flag
states), and the sbx test doubles that amount (16777216*4 D and C flag
combinations). Both tests have run successfully on a C64 and a Vic20.
They ought to run on C16, +4 and the PET series as well. The tests
stop with BRK, if the opcode $CB does not work as expected. Successful
operation ends in RTS. As the tests are very slow, they print dots on
the screen while running so that you know that the machine has not
jammed. On computers running at 1 MHz, the first test prints
approximately one dot every four seconds and a total of 2048 dots,
whereas the second one prints half that amount, one dot every seven

If the tests fail on your machine, please let us know your processor's
part number and revision. If possible, save the executable (after it
has stopped with BRK) under another name and send it to us so that we
know at which stage the program stopped.

The following program is a Commodore 64 executable that Marko M"akel"a
developed when trying to find out how the V flag is affected by SBX.
(It was believed that the SBX affects the flag in a weird way, and
this program shows how SBX sets the flag differently from SBC.)  You
may find the subroutine at $C150 useful when researching other
undocumented instructions' flags. Run the program in a machine
language monitor, as it makes use of the BRK instruction. The result
tables will be written on pages $C2 and $C3.

begin 644 sbx-c100

Other undocumented instructions usually cause two preceding opcodes
being executed. However 'NOP' seems to completely disappear from 'SBC'
code $EB.

The most difficult to comprehend are the rest of the instructions
located on the '$0B' line.

All the instructions located at the positive (left) side of this line
should rotate either memory or the accumulator, but the addressing
mode turns out to be immediate! No problem. Just read the operand, let
it be ANDed with the accumulator and finally use accumulator
addressing mode for the instructions above them.

/* This part of the document is not accurate.  You can
   read it as a fairy tale, but do not count on it when
   performing your own measurements. */

The rest two instructions on the same line, called 'ANE' and 'LXA'
($8B and $AB respectively) often give quite unpredictable results.
However, the most usual operation is to store ((A | #$ee) & X & #$nn)
to accumulator. Note that this does not work reliably in a real 64!
In the Commodore 128 the opcode $8B uses values 8C, CC, EE, and
occasionally 0C and 8E for the OR instead of EE,EF,FE and FF used in
the C64. With a C128 running at 2 MHz #$EE is always used.  Opcode $AB
does not cause this OR taking place on 8502 while 6510 always performs
it. Note that this behaviour depends on processor and/or video chip

Let's take a closer look at $8B (6510).

        A <- X & D & (A | VAL)

        where VAL comes from this table:

       X high   D high  D low   VAL
        even     even    ---    $EE (1)
        even     odd     ---    $EE
        odd      even    ---    $EE
        odd      odd      0     $EE
        odd      odd     not 0  $FE (2)

(1) If the bottom 2 bits of A are both 1, then the LSB of the result may
    be 0. The values of X and D are different every time I run the test.
    This appears to be very rare.
(2) VAL is $FE most of the time. Sometimes it is $EE - it seems to be random,
    not related to any of the data. This is much more common than (1).

  In decimal mode, VAL is usually $FE.

Two different functions have been discovered for LAX, opcode $AB. One
is A = X = ANE (see above) and the other, encountered with 6510 and
8502, is less complicated A = X = (A & #byte). However, according to
what is reported, the version altering only the lowest bits of each
nybble seems to be more common.

What happens, is that $AB loads a value into both A and X, ANDing the
low bit of each nybble with the corresponding bit of the old
A. However, there are exceptions. Sometimes the low bit is cleared
even when A contains a '1', and sometimes other bits are cleared. The
exceptions seem random (they change every time I run the test). Oops -
that was in decimal mode. Much the same with D=0.

What causes the randomness?  Probably it is that it is marginal logic
levels - when too much wired-anding goes on, some of the signals get
very close to the threshold. Perhaps we're seeing some of them step
over it. The low bit of each nybble is special, since it has to cope
with carry differently (remember decimal mode). We never see a '0'
turn into a '1'.

Since these instructions are unpredictable, they should not be used.

There is still very strange instruction left, the one named SHA/X/Y,
which is the only one with only indexed addressing modes. Actually,
the commands 'SHA', 'SHX' and 'SHY' are generated by the indexing

While using indexed addressing, effective address for page boundary
crossing is calculated as soon as possible so it does not slow down
operation. As a result, in the case of SHA/X/Y, the address and data
are processed at the same time making AND between them to take place.
Thus, the value to be stored by SAX, for example, is in fact (A & X &
(ADDR_HI + 1)).  On page boundary crossing the same value is copied
also to high byte of the effective address.

base/6510_8502_undocumented_commands.txt · Last modified: 2015-04-17 04:30 (external edit)