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:
- It shifts left, instead of right (behaves like ASL)
- It shifts a zero in, instead of C (behaves like ASL)
- 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.
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…).
You have a bug in Program 2a. It should be CLC not SEC.
Looks like you can replace your single ROR command with 8 ROL commands to resolve this issue.
> 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)
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%.
Some of this information seems to have come out recently: TubeTime has a video showing the microphotographs of an early 6502. “The 6502 Rotate Right Myth” https://www.youtube.com/watch?v=Uk_QC1eU0Fg
I note that the Apple-1 ROM contains zero ROR instructions.
The Apple ][ ROM contains about 25 of them, mostly in floating point arithmetic routines, plus a couple in graphics.
I tested the ROR in my Apple I rebuild. My CPU is from week 21, 1976 and you can see my test here:
https://www.youtube.com/watch?v=q5irJP5DcNc&t=2s
Memory operations produce something different then ASL. But I did only test one number so far. 0x44 becomes 0x43.