6

As everyone knows, the 6502 has many many undefined opcodes. One of these is RRA, which essentially performs both ROR and ADC.

From what I understand of hardware (really not much) and the explanation in this document, the example program:

rra $02

should:

  1. read from the location $02,
  2. write its contents back while it does the shift and addition.
  3. write the rotated value, while latching the result of the addition into the accumulator

But here's the example run in Visual6502.

0   0000    67  1       0000    aa  00  00  fd  nv‑BdIZc
0   0000    67  1       0000    aa  00  00  fd  nv‑BdIZc
1   0001    02  1       0001    aa  00  00  fd  nv‑BdIZc
1   0001    02  1       0001    aa  00  00  fd  nv‑BdIZc
2   0002    20  1       0002    aa  00  00  fd  nv‑BdIZc
2   0002    20  1       0002    aa  00  00  fd  nv‑BdIZc
3   0002    20  0       0002    aa  00  00  fd  nv‑BdIZc
3   0002    20  0       0002    aa  00  00  fd  nv‑BdIZc
4   0002    20  0       0002    aa  00  00  fd  nv‑BdIzc
4   0002    10  0       0002    aa  00  00  fd  nv‑BdIzc

So my concern is that the accumulator did not change. Trying a few different values shows that the Carry flag changes as though a ROR happened.

I'm inclined to trust the Visual6502 implementation, but on the other hand, all the documents, software, emulators and everything that attest to the behaviour of the RRA opcode can't all be wrong, can they? So the document I linked to up there is quite C64-centric. Have I discovered that the Visual6502 reproduces a variant that is different from the NMOS thing in the C64 and NES?

1
  • 3
    Keep the overlapping nature of fetch and execution in mind. Thus one needs to take the (whole) first cycle of the next instruction as well into account.
    – Raffzahn
    Apr 12 at 14:04
12

Look further down in the print-out. The A register shows as updated after the first byte fetch of that next instruction.

Starting with the bytes 67 02 20 10, which seem to be what you had:

cycle   ab      db  rw  Fetch   pc      a   x   y   s   p
0       0000    67  1   unknown 0000    aa  00  00  fd  nv‑BdIZc
0       0000    67  1   unknown 0000    aa  00  00  fd  nv‑BdIZc
1       0001    02  1           0001    aa  00  00  fd  nv‑BdIZc
1       0001    02  1           0001    aa  00  00  fd  nv‑BdIZc
2       0002    20  1           0002    aa  00  00  fd  nv‑BdIZc
2       0002    20  1           0002    aa  00  00  fd  nv‑BdIZc
3       0002    20  0           0002    aa  00  00  fd  nv‑BdIZc
3       0002    20  0           0002    aa  00  00  fd  nv‑BdIZc
4       0002    20  0           0002    aa  00  00  fd  nv‑BdIzc
4       0002    10  0           0002    aa  00  00  fd  nv‑BdIzc
5       0002    10  1   BPL     0002    aa  00  00  fd  nv‑BdIzc
5       0002    10  1   BPL     0002    aa  00  00  fd  nv‑BdIzc
6       0003    10  1           0003    ba  00  00  fd  Nv‑BdIzc
6       0003    10  1           0003    ba  00  00  fd  Nv‑BdIzc

Note that that's also the point where the N flag becomes set, even though the BPL has already started fetching. Since this is an effect from the previous instruction, the branch is not taken.


These results are as seen for separate ROR and ADC, with input 66 04 65 04 20 10 (offsets adjusted to point at the 20):

cycle   ab      db  rw  Fetch   pc      a   x   y   s   p
0       0000    66  1   ROR zp  0000    aa  00  00  fd  nv‑BdIZc
0       0000    66  1   ROR zp  0000    aa  00  00  fd  nv‑BdIZc
1       0001    04  1           0001    aa  00  00  fd  nv‑BdIZc
1       0001    04  1           0001    aa  00  00  fd  nv‑BdIZc
2       0004    20  1           0002    aa  00  00  fd  nv‑BdIZc
2       0004    20  1           0002    aa  00  00  fd  nv‑BdIZc
3       0004    20  0           0002    aa  00  00  fd  nv‑BdIZc
3       0004    20  0           0002    aa  00  00  fd  nv‑BdIZc
4       0004    20  0           0002    aa  00  00  fd  nv‑BdIzc
4       0004    10  0           0002    aa  00  00  fd  nv‑BdIzc
5       0002    65  1   ADC zp  0002    aa  00  00  fd  nv‑BdIzc
5       0002    65  1   ADC zp  0002    aa  00  00  fd  nv‑BdIzc
6       0003    04  1           0003    aa  00  00  fd  nv‑BdIzc
6       0003    04  1           0003    aa  00  00  fd  nv‑BdIzc
7       0004    10  1           0004    aa  00  00  fd  nv‑BdIzc
7       0004    10  1           0004    aa  00  00  fd  nv‑BdIzc
8       0004    10  1   BPL     0004    aa  00  00  fd  nv‑BdIzc
8       0004    10  1   BPL     0004    aa  00  00  fd  nv‑BdIzc
9       0005    10  1           0005    ba  00  00  fd  Nv‑BdIzc
9       0005    10  1           0005    ba  00  00  fd  Nv‑BdIzc
5

Although it's often convenient to think of each instruction as starting with an opcode fetch, it's more accurate to think of each instruction's execution as starting on the cycle after its opcode is fetched, and extending through the fetch of the following opcode. If one considers an instruction like ADC #12, there's no way the processor can even begin to perform the addition until after it has fetched the operand byte, and there's almost no time between that and the start of the next instruction fetch. Thus, instructions whose last step is to update a register will generally perform that last step during the fetch of the following opcode byte.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.