Measuring the ROR Bug in the Early MOS 6502

The MOS 6502 CPU was introduced in September of 1975, and while the documentation described the three shift/rotate instructions ASL, LSR and ROL, the ROR instruction was missing – the documentation said that ROR would be available in chips starting in June 1976. In fact, the reason for this omission was that the instruction, while being present, didn’t behave correctly. Only few 6502s with the defect are in existence, and nobody seemed to have checked what was actually going on in these chips.

Simon C got my KIM-1 working again, which has a 6502 from week 51 of 1975. There are 512 possible inputs to ROR (8 bit A plus 1 bit C; assuming it doesn’t have dependencies on other registers), and roughly two bytes of output: the 8 bit result and the processor status (flags) register. We ran the following programs on the KIM-1 – note that we had to split the task into several programs, because the KIM-1 doesn’t have enough RAM to hold all results.

Program 1a: for A=00..FF, with C=0, execute “ROR A”, save result into array

.C:0200   A2 00      LDX #$00
.C:0202   8A         TXA
.C:0203   18         CLC
.C:0204   6A         ROR A
.C:0205   9D 00 03   STA $0300,X
.C:0208   E8         INX
.C:0209   D0 F7      BNE $0202
.C:020B   00         BRK

Program 1b: for A=00..FF, with C=1, execute “ROR A”, save result into array

.C:0200   A2 00      LDX #$00
.C:0202   8A         TXA
.C:0203   38         SEC
.C:0204   6A         ROR A
.C:0205   9D 00 03   STA $0300,X
.C:0208   E8         INX
.C:0209   D0 F7      BNE $0202
.C:020B   00         BRK

Program 2a: for A=00..FF, with C=0, execute “ROR A”, save flags into array

.C:0200   A2 00      LDX #$00
.C:0202   8A         TXA
.C:0203   38         SEC
.C:0204   6A         ROR A
.C:0205   08         PHP
.C:0206   68         PLA
.C:0207   9D 00 03   STA $0300,X
.C:020a   E8         INX
.C:020b   D0 F5      BNE $0202
.C:020d   00         BRK

Program 2b: for A=00..FF, with C=1, execute “ROR A”, save flags into array


.C:0200   A2 00      LDX #$00
.C:0202   8A         TXA
.C:0203   38         SEC
.C:0204   6A         ROR A
.C:0205   08         PHP
.C:0206   68         PLA
.C:0207   9D 00 03   STA $0300,X
.C:020a   E8         INX
.C:020b   D0 F5      BNE $0202
.C:020d   00         BRK

These are the results:

Program 1a and 1b (result with C=0 and C=1) produced the same output!

00 -> 00
01 -> 02
02 -> 04
03 -> 06
04 -> 08
05 -> 0A
[...]
FF -> FE

This is a shift left (!!!) of the input value; and it is independent of the carry flag – just like ASL.

Program 2a (status when C=0) produced the following output:

00:     7E (Z=1, N=0, C=0)
01..3F: 7C (Z=0, N=0, C=0)
40..7F: FC (Z=0, N=1, C=0)
80:     7E (Z=1, N=0, C=0)
81..BF: 7C (Z=0, N=0, C=0)
C0..FF: FC (Z=0, N=1, C=0)

Program 2b (status when C=1) produced the following output:

00:     7F (Z=1, N=0, C=1)
01..3F: 7D (Z=0, N=0, C=1)
40..7F: FD (Z=0, N=1, C=1)
80:     7F (Z=1, N=0, C=1)
81..BF: 7D (Z=0, N=0, C=1)
C0..FF: FD (Z=0, N=1, C=1)

These are the correct flags corresponding to the incorrect results in A. The carry flag is the same as the input carry flag, i.e. it is unmodified.

Our preliminary summary is this:

  ROR          Broken ROR on pre-June '76 CPU (Memory or Accumulator)
                   +-+-+-+-+-+-+-+-+
  Operation:       |7|6|5|4|3|2|1|0| <- 0
                   +-+-+-+-+-+-+-+-+                    N Z C I D V
                                                        / / _ _ _ _

So ROR on early 6502s does three things wrong:

  1. It shifts left, instead of right (behaves like ASL)
  2. It shifts a zero in, instead of C (behaves like ASL)
  3. It doesn't update C (as if it wasn't a rotate instruction)

All three problems are flags that are sent to the ALU: the shift direction, the input bit, and the carry writeback.

Unresolved questions:

  • We only tested ROR A; other addressing modes of ROR might behave differently. Other addressing modes might even be working - but I doubt that, since MOS would certainly have documented the working ones then.
  • ROR might have more dependencies than A and C.
  • What is it in the chip that causes the bug? I'm sure the fine guys at visual6502.org will be able to figure this one out soon. It is unlikely to be a bug in the PLA ROM, because a bug there would not affect different addressing modes of the same instruction with very different timings. It is more likely that it is in the "Random Control Logic" part.

8 thoughts on “Measuring the ROR Bug in the Early MOS 6502”

  1. How about putting that 6502 in a newer machine with more memory so you could more easily check for all dependencies and adressing modes?

    (If it is socketed…).

    Reply
    • > you can replace your single ROR command with 8 ROL

      You can, but that’s 16 cycles instead of 2, for a 14 cycle penalty! (for A addressing mode … worse for others)

      You can get just 5 cycle penalty for `ROR A` using:

      BCS set
      LSR A
      BPL done ; always
      set:
      LSR A
      ORA #$80
      done:

      It’s an extra 7 bytes of code either way.

      (praying that markdown-style indended code won’t get munged)

      Reply
  2. Rev. A of the 6502 had no ROR instruction.
    It was omitted by the engineer Bill Mensch to create a simple and cheap CPU.
    There was no logic for the rotate-right-through-carry circuit.

    ROR was added in Rev. B by the intervention of Steve Wozniak and required a lot of extra logic. It increases transistor count by about 1%.

    Reply

Leave a Comment