# How can ADD be a 1 byte instruction?

I am learning 8051 coding. I came across the instructions of these forms ADD R7 and ADD A, R5. Now, since ADD is a mathematical operation, which should always take place in the accumulator, isn't the ADD A, R5 command redundant?

And secondly, how can these be a 1 byte instruction? I mean, there should be 2 bytes, one for the opcode which says that it is an add operation, and the second byte will have the byte value that is to be added to the accumulator. How can both be squeezed into one byte?

• you may wish to consider accepting one of the answers. It's an important part of the process. Commented Jan 8, 2014 at 16:24
• Sorry mate, forgot. Done now.. Commented Jan 9, 2014 at 20:25

You are correct that ADD A, R5 is redundant. This is probably done for clarity in the code, to make it slightly more human-readable.

ADD is either a one-byte or a two-byte instruction, depending on what's getting added to what. If you're adding a constant, or the value at an arbitrary address, then ADD is a two-byte instruction. It's only a one-byte instruction if there are enough bits in the opcode besides those indicating ADD to indicate something else useful. In this case, there are two variations: add one of eight registers, or add what's found at a particular address (with that address stored in one of two registers).

• Yeah, I did find that ADD A, #5f is a 2 byte instruction, then why isn't ADD A, R5 a 2 byte instruction as well? Commented Sep 11, 2013 at 21:22
• @Cupidvogel Because only five bits are necessary to indicate "ADD", not an entire byte. "5" (indicating register five) can be represented by the remaining three bits in the byte. This does mean there are limits on what registers can be used for what purposes; R12 couldn't be used, because 12 can't be represented in three bits. Commented Sep 11, 2013 at 21:23
• Umm, why? What is the opcode for ADD? And where is 5 coming from in this question? Commented Sep 11, 2013 at 21:25
• @Cupidvogel The opcode for ADD A, Rn is 00101, as shown in the link. That's only five bits. 5 comes from your example ADD A, R5. Commented Sep 11, 2013 at 21:27
• Okay, so basically it is 101, and 5 bits remain. Since there are 8 registers, numbered 0 to 7, there are specified by the corresponding BCD, which takes up at most 3 bits. Remaining 5 digits are covered by ADD (padded by 0s to make up 5), right? Commented Sep 11, 2013 at 21:32

The 8x51 instruction set has 16 instructions of the form "do something with Rn" where n=[0..7]; every combination of operation and register number is assigned a different opcode; a total of 128 opcodes are used for such purposes. Some of the operations operate only on Rn, and some only on Rn and the accumulator. In either case, the opcode specifies everything that needs to be said about the instruction. Some other operations require more information (e.g. "load Rn with some constant", "load Rn with data from some directly-specified address", "decrement Rn and branch forward or backward some distance if not zero", or "compare Rn with data at some specified address and branch forward or backward some distance if not equal"). In those cases, one or two additional bytes will be required following the opcode.

• So when a single byte code is generated, how will the system know which part is the opcode and which part is the operand (in this case, the number 5, the serial number of the register whose value is to be added to A)? I mean, if there are two single byte instructions, the one like here: [7..3][2..0], where the first 5 bits are the opcode, and the last 3 are the operand. Let's imagine another one: [7..4][3..0]. Both are single byte instructions, how will the system know in which case the opcode is 4 bits and in which case opcode is 5 bits? Commented Sep 11, 2013 at 22:19
• All opcodes whose MSB is set are of the form 1nnn oooo (where nnn identifies one of eight registers, and oooo one of 16 operations). Commented Sep 11, 2013 at 22:36
• So whenever the machine find an instruction of the type 1nnn oooo, it will do a prescribed operation based on the value of oooo, with the value stored in the register specified by nnn, right? And among those 16 operations, one is add the value of the said register with A and store it there? Commented Sep 11, 2013 at 22:41
• @Cupidvogel: Bingo, except I described the exact bit ordering wrong [oops]. See keil.com/support/man/docs/is51/is51_opcodes.htm for a list of opcodes. Commented Sep 11, 2013 at 22:47

If you check the op-codes for ADD on an 8051 here

You will see that ADD R5 has opcode 0x2D.

It adds the value in R5 to the Accumulator and stores the result in the Accumulator. Only one byte is needed to code this

Whereas ADD #xx where xx is immediate data has opcodes 0x24 xx needing two bytes.

See Atmel's 8051 family hardware manual at http://www.atmel.ca/Images/doc4316.pdf . It includes a detailed listing of the bit patterns for all 8051 instructions.

ADD A, R5 is not an instruction; it is a fragment of assembly language. Assembly language is not machine language. It contains syntax that does not correspond to anything in machine language directly, such as pseudo-operations, alternative register names and such.

Even if some instruction always uses a fixed register as a source or destination operand, or both, those can still appear explicitly in the assembly language syntax.

As for "how can something be a one-byte operation"? You can use as little as one bit to encode a symbol. Take a look at a compression method called Huffman Coding. Huffman coding lets us take an alphabet (or, more generally, a dictionary of some symbols, which are bit strings, or whatever) and assign them to table of variable-length codes, the shortest of which is just one bit!

Not that Huffman coding would commonly be used for instruction sets, but the principle is there: there are ways to use variable bit lengths in order to encode important symbols in less space, at the cost of lengthening the codes for others.

For instance, if you have an 8 byte opcode field, you could reserve, say two bits of it to classify opcodes into four categories. The remaining 6 bits are then category specific. Three of the categories (say) could use the remaining 6 bits to encode various instruction kinds so that, for instance, anything which starts with 00, 01, or 10 is some kind of regular opcode. But one of the four categories, say 11, could just stand for "add immediate to accumulator". The remaining six bits (and their 64 possibilities) would encode the value to be added, between 0 and 63. The value 0 (add 0 to accumulator) could serve as a no-op instruction, so that in the assembly language ADD #0, A and NOOP could be two spellings for exactly the same bit pattern (bringing us back to earlier points). Or the value 0 could be taken to designate 65, extending the addition range (and so NOOP would be something else, and ADD #0, A would be rejected by the assembler as out of range).

Effectively, you could regard this situation as having 64 different "add immediate to accumulator" instruction flavors, with one opcode for each value to be added, or you can regard that as a special two-bit opcode within the opcode byte, with an immediate field.

• So essentially, in my case, a part of the opcode denotes the BCD of the register whose value needs to be added to A, while the remaining 5 bits take up the opcode itself, right? Commented Sep 11, 2013 at 22:31
• Your first paragraph is rubbish. ADD A, R5 is an instruction too. And there's a 1-on-1 correspondence between assembly language and machine code. Commented Sep 12, 2013 at 10:51
• I guess he meant that the system does not interpret it in that manner, it is first converted into a machine-readbale opcode. Commented Sep 12, 2013 at 17:19
• The text ADD A, R5 is not a machine language instruction, but a source-level assembly representation of one. The claim that assembly is 1:1 with machine code is easily refuted. There are pseudo-ops and macros in assembly languages that do not correspond to an instruction, and there can be instruction sequences generated by one line of assembly. Assemblers can also analyze code and reorganize it, and do things like automatically fill branch delay slots (specific to a CPU model) and so on.
– Kaz
Commented Sep 12, 2013 at 18:19
• Another important way in which assembly is not 1:1 to machine language is the use of symbols for addresses and such. We can refer to a data or code address as FOO. When this is assembled, FOO changes to a concrete number. But that number will change as we reorganize the program's data and code over time. So FOO potentially maps to many numbers (not 1:1). Symbolic assembly languages, even rudimentary ones, are considerably higher level than machine code.
– Kaz
Commented Sep 12, 2013 at 19:27

It's easier on the CPU designer, the ones tasked with explaining the 8051/8052, the learner, and the authors of support tools like compilers, assemblers, simulators and reverse-assemblers to make the operations as orthogonal as possible; and it facilitates static analysis and translation. That means: fewer actual operator names, and explicitly-stated operands, instead of compound-word renaming of operator name + operand.

So, Intel had a tendency to move toward this by the late 1970's.

The only drawback occurs where all the operands are pointers, in which case you might need to include a size indicator with the operator name or operands, as is the case with many of the 80x86 operators. But, the issue doesn't arise with the 8051/8052.

Actually, the operations tend to form groups and the operator is contained in the opcode, itself. In hexadecimal, the opcode hex-"digits" 0xPQ... denote the operator by "P" and operands by "Q" (and whatever comes after), with the following group

    P       Operator        Q ⋯     Operand
─       ────────        ──      ───────
0x2     add A, ⋯        0x4 b   #b      (b is a 8-bit data value b)
0x3     addc A, ⋯       0x5 D   D       (D is an 8-bit address D for the internal register space 0x00-0x7f and SFRs 0x80-0xff)
0x4     orl A, ⋯        0x6+i   @Ri     (0 ≤ i < 2, Ri points to internal register space 0x00-0x7f and "stack" space 0x80-0xff)
0x5     anl A, ⋯        0x8+n   Rn      (0 ≤ n < 8)
0x6     xrl A, ⋯
0x9     subb A, ⋯
0xc     xch A, ⋯
0xe     mov A, ⋯


(the original 8051 did not support addresses 0x80-0xff for the stack space, while the 8052 did and newer 8051's do; registers R0-R7 are meant to be used as thread-local registers, that can map to 0x00-0x07, 0x08-0x0f, 0x10-0x17 or 0x18-0x1f, with the base address being given by PSW&0x18)

which applies in the following places (× = yes, · = no):

    P/Q     │       0x4     0x5     0x6-0x7 0x8-0xf
────────┼──────────────────────────────────────
0x2     │       ×       ×       ×       ×
0x3     │       ×       ×       ×       ×
0x4     │       ×       ×       ×       ×
0x5     │       ×       ×       ×       ×
0x6     │       ×       ×       ×       ×
0x9     │       ×       ×       ×       ×
0xc     │       ·       ×       ×       ×
0xe     │       ·       ×       ×       ×


So the modes Q ∈ {0x4,0x5} have an additional byte following the opcode for the extra argument, while the modes Q ∈ {0x6,⋯,0xf} contain the extra argument in the opcode hex-"digit" Q. The mode "mov A, #b" is mapped to 0x74, instead of to 0xe4, while the mode "xch A, #b" is excluded by the semantics of the operation.