The 6502 uses the carry bit system (over a borrow bit). On such machines, the bitwise NOT is applied to the subtrahend before it is "subtracted" (really, added, taking advantage of two's complement) from the minuend. Since obviously when you're generating machine from assembly code, you have control over the bytes which are actually emitted, for 6502 variants like the NES's 2A03, was the presence of both ADC and SBC by immediate necessary? Couldn't the immediate just be NOTed at the assembly phase? Or are the flags reckoned differently for ADC and SBC?
4 Answers
No, ADC and SBC does exactly the same except SBC does a NOT operation on the argument before adding, and when it comes to immediate values, they are definitely redundant.
However, when it comes to other addressing modes, ADC and SBC instructions were both needed. And as the 6502 is pretty orthogonal when it comes to instructions and addressing modes, this is why both SBC #imm exists even though it could always be replaced with ADC #~imm.
EDIT: The overflow flag, which is "simply" the XOR of the 6th and 7th bit carry, is the same whenever ADC and SBC is used.
I'd also say that pseudo-instructions, while common in the ARM world for example, were unknown/uncommon during the 6502 days. Back then people expected instructions to be in memory exactly what they typed in the assembly code.
Conclusion: The decimal mode is the only way and ADC #~imm and SBC #imm will make a difference. However, while the decimal mode rarely used and marginal today in hindsight, especially considering it was removed for the NES/Famicom, you should not forget that it was a major marketing tool back in the late '70s, which was a selling point for this CPU.
-
1Is this true even of the overflow flag? – junius May 25 '20 at 16:17
-
1@KevinKeith Yes. (http://www.righto.com/2012/12/the-6502-overflow-flag-explained.html) – Bregalad May 26 '20 at 07:06
-
1"pseudo-instructions [..] were unknown/uncommon during the 6502 days" is not completely true. A lot of assemblers had BLE and BGT synonyms for BCS and BCC like the Lisa assembler on Apple II. But granted, it's really not very significant. – Patrick Schlüter May 26 '20 at 10:45
-
@PatrickSchlüter also BLE/BGT are 'only' aliases, not encodings o their own like an ADC #imm used to subtract. – Raffzahn May 26 '20 at 12:13
-
@PatrickSchlüter: I think the term "pseudo-instruction" refers to opcodes like $A7, $87 and $C7, sometimes called "LAX", "SAX", and "DCP" [the opcodes given are for zero-page direct mode, but other addressing modes are available]. LAX will read a byte from memory and copy it to A and X simultaneously (same timing as LDA) SAX will store the value (A and X) into a specified address without affecting flags (same timing as STA). DCP will decrement the value at the specified address and then compare the result to the accumulator, setting flags like CMP (but same timing as DEC). – supercat May 26 '20 at 15:15
-
@PatrickSchlüter: The LDA and LDX instructions use the same decoding for the top six bits of the opcode, and use the bottom two bits to enable the latches on both A and X registers. LAX uses the same upper bits, but sets both lower bits, so both registers. get loaded. STA and STX are similar, but the bottom bits control which registers get placed on the bus. SAX tries to both registers on the bus simultaneously, but at the electronic level values are copied to the bus by setting all the bits, and then having registers clear any bits that should be clear, so if both A and X try to output... – supercat May 26 '20 at 15:21
-
...the bus value will deterministically receive the bit-wise AND of A and X. DCP is processed like DEC, except for the "cleanup" phase [which occurs while fetching the next instruction]. The "cleanup" phase of DEC normally wouldn't do anything, but the cleanup phase of CMP subtracts the last processed byte from A and sets the flags accordingly, so opcode $C7 acts like "DEC" except that its cleanup phase behavior is that of CMP. – supercat May 26 '20 at 15:24
-
BCD mode was removed from RICOH 6502 clone for copyright reasons – Jean-François Fabre Sep 29 '22 at 12:43
was the presence of both ADC and SBC by immediate necessary?
Necessary is for instruction sets always a misleading question. After all, next to all instructions could be omitted from the hardware and put into code, essentially ending with a one instruction machine. The question is not about a minimizing the instruction set (including addressing variants) to an absolute minimum but rather to find a (*1) the sweet spot between bloated and unusable.
Couldn't the immediate just be NOTed at the assembly phase?
Sure, it could. But were to put it? But that would require either a(nother) temporary register to store the value after the 'NOTing' instruction to hold it for the 'ADDing' as the accumulator already holds the first operand. In addition the 'NOTing' instruction would have to have all addressing modes of the SBC instruction it replaces to be as useful. This means as many opcodes and decode-ROM entries with SBC - plus an additional one for 'Add Temp to A' (*2).
Now if this question is strictly only about dropping the SBC with an immediate value, then yes, it would have saved a single opcode, but no hardware at all, as the implied negation would still need to be there for all SBC with a memory operand. Maybe one line in the decode-ROM could be saved - but at horrible cost with potential buyers ... 'Wut? A CPU that can't subtract an immediate value? No way I'd buy that'
Bottom line: While removing SBC is a nice Gedankenexperiment, is will increase hardware requirement and bloat the instruction set when done general, or have no meaningful savings at all.
Next, the notion of 'at assembly phase' carries a lot of hindsight. Back when the 6500 was done, it's designated assembler was barely than a tool to replace a combination of mnemonic and operand markers into a single opcode. That beast was really dumb. Not to mention, that assembling from hand was considered a normal way to do it. Woz did write not only his monitor, but as well the first Integer BASIC exactly that way - no assembler involved.
P.S.: Instead the 6500 design saved on a not instruction, as it can simply be synthesized by an EOR #$FF instruction.
*1 - There isn't a single sweet spot, but many, depending on targets and resources.
*2 - Turning this into a Load-Temp instruction and a Neg-And-Add-Temp will lead to the same result.
- 222,541
- 22
- 631
- 918
-
2Just to clarify, I did indeed mean just removing the SBC with immediate addressing mode, not SBC entirely. – junius May 25 '20 at 16:13
-
1@KevinKeith Ok, but then,what it should be good for as all circuitry had to be there anyway? – Raffzahn May 25 '20 at 19:55
-
1
-
2
Wut? A CPU that can't subtract an immediate value? No way I'd buy thatReminds me the oft-repeated fallacy that the 6502 can't multiply and divide - just because there's no such instructions. – Bregalad May 26 '20 at 06:12
Many 6502 programs exploit the ability to patch program instructions at runtime. If a program will have to perform a subtraction within a loop, being able to say, e.g.
; Compute value in X somehow, then...
stx subPatch+1
; Now compute a different value in A and set carry
subPatch:
sbc #0
bcs subPatch
is more convenient than it would be if code had to XOR the value with $FF before storing it.
Note also that the processing "SBC #imm" involves two sets of logic: the immediate-operand-fetch logic which is shared with the six other valid opcodes sharing bit pattern (xxx0 1001) and the "subtract with carry instruction" logic which is shared with the seven other opcodes having the bit pattern (111x xx01). There are some instructions in the memory map which have their own "oddball" logic (for example, seven immediate-mode instructions have the bit pattern xxx01001 which is shared by no other instructions, three likewise have 1xx0 0000, two have 1x10 0100, and has xxxx 0010), making opcode E9 behave as "SBC #imm" requires less circuitry than would having it behave in any other consistent fashion.
- 35,993
- 3
- 63
- 159
The argument about orthogonality on its own is convincing, but there's one further consideration:
Does the carry flag get added to or subtracted from the resulting value?
The answers to https://stackoverflow.com/questions/48971814/i-dont-understand-whats-going-on-with-sbc seem to imply that whatever carry flag value represents 'no carry' from an "add" instruction actually represents 'carry' from a "subtract" instruction, and vice versa, which makes it possible to write ADC #255 and get the same effect as SBC #0 (the same results in both accumulator and carry flag).
However other contemporary CPUs (8080, Z80, M6800, PDP-11) encoded "carry or not" congruently between all instructions (not in the manner I've just described), so that makes the 6502 an outlier in the workings of its carry flag.
- 141
- 3
-
2Another way to look at this is that the carry is added to the result, regardless of whether it's an
ADCorSBCinstruction. That certainly makes sense from a design minimalism point of view. – Martin Kealey Sep 29 '22 at 07:50