I actually think that it was a good decision to provide only the ADC/SBC instructions. As Garth has indicated from his review of a large program, the overhead associated with using the CLC/SEC instructions is fairly low.
These instructions are the foundation of multi-precision arithmetic. With only these two instructions, since the CLC/SEC are required for other reasons, multi-precision arithmetic can be easily implemented. As anyone whose worked with the instruction set of processors like the 8080 (or Z80) which has both instructions, forgetting to use the ADC instruction for the second (or higher) addition in a multi-precision operation is just as bad as failing to initialize the carry flag before the first 6502 ADC instruction. Similarly, the RISC-like 16-bit Inmos Transputer did not have a carry flag (or processor status register), and doing higher precision (32-bit or more) arithmetic was a bit difficult.
I also think that Garth is right on point about the complexity issue. For example, I started implementing a
serial processor several years ago, and I opted to implement the add/sub operations like those of the 6502. I wanted to have a minimal number of instructions, 32/33, that would provide the capability to perform multi-precision arithmetic. Providing two kinds of add/sub instructions just didn't fit into my instruction set objectives. (Note: a great example of a minimal instruction set that is well matched to its application is that provided by James Bowman's
J1 processor or Arlet Ottens'
RISC processor.)
Finally, it's not that hard to implement arithmetic and logical shifts. In my 6502-compatible core, I implement these instructions with two simple structures:
Code:
// Carry In Multiplexer
always @(*)
begin
case(CSel)
2'b00 : Ci <= C; // ADC/SBC/ROL/ROR
2'b01 : Ci <= Q[7]; // ASR (Reserved for future use)
2'b10 : Ci <= 0; // DEC/ASL/LSR
2'b11 : Ci <= 1; // INC/CMP
endcase
end
and
Code:
always @(*)
begin
if(En)
{OV, Out} <= ((Op) ? {^SU[7:6], {SU[0], {Ci, SU[7:1]}}} // LSR/ROR
: {^SU[7:6], {SU[7], {SU[6:0], Ci}}}); // ASL/ROL
else
{OV, Out} <= 0;
end
Although these structures appear to be fairly simple, I wonder how difficult it would be for a manually placed and routed device like the 6502, with a limited number of metal layers, to accommodate the multiplexers and XOR gates described in my code snippets. I also suspect that many decisions regarding the instruction set were driven by size as others have indicated above.
Extending this thought, I think that the 6502's PLA-based control logic was used to great advantage in creating a processor well matched to memory and providing much faster execution rates limited the layout options as well. A PLA-based control logic is more compact than the control logic of a microprogrammed processor. A microprogrammed processor like the 8080 could use multiple cycles to implement the large number of related instructions like ADD/ADC, SUB/SBB, ROL/RLC, ROR/RRC, etc. by breaking the operations down to units less than 8-bits, i.e. 4-bit nibbles. The lack of complex addressing modes in the 8080 unlike the 6502, simplifies the microprogram, provides room in the IC area and in the instruction set for its large number of similar instructions.
When all things are considered, the instruction set of the 6502 is very efficient, and does not lack any significant functionality; programs which have good performance can be compactly written. The indexed and indirect addressing modes are difficult to emulate with the simple addressing modes of the 8080: immediate, absolute, and 8-bit relative (for branches only).