In this topic, we'll look a the bit-level format and design of the ARM ISA.
We've referred indirectly to the bit-level machine language in prior topics, when we mentioned, for instance, that a ldr offset is limited by the number of bits reserved for it in the instruction. And in the intro topic we looked at the layout of a sample add instruction. But now we'll go through the machine language design more thoroughly. It's not necessary to understand every instruction in it, but we will at least cover those instructions we've used so far, except for the floating point coprocessor instructions, which we'll get to in the next topic.
Look up, and consult as we go, a bit-level chart of the ISA, like the one on p 4-2 of the ARM Instruction Set reference from the course Bibliography. Or use the image in the notes, quoted from https://www.cs.uaf.edu/courses/cs301/2014-fall/notes/arm-asm/ at the University of Alaska. In-lecture questions will frequently ask you to look up information in the first reference, or comparable ones.
The chart shows the 32-bit layout of each type of ARM instruction. As you can see, there are a number of different configurations, for different types of instructions. Bits are numbered right to left, from 0 to 31. Some bits are fixed, e.g. the 00 in bits 26/27 in the Data Processing category. But most form bit fields indicating things like source or target registers (4 bits each), operands (12 bits), or just single option flags of one bit, like the S and I bits in Data Processing. A lot is being crammed into just 32 bits, and one of the interesting points of ISA design is the way in which we make the most out of the limited bits in an instruction.
Let's look more closely at the Data Processing format on the first row. Instructions in this category include mov, add, sub, mvn, rsb, and, orr, cmp and 8 others we haven't used yet.
Like all the formats, it starts with a 4 bit cond field in the leftmost bits. This field specifies whether the operation is conditional on prior results. If an assembly instruction has a condition, like addeq or sublt, the two-letter condition is indicated by a 4-bit pattern in the cond field. One pattern (1110 in fact) is reserved to indicate "always" or unconditional execution, which is the most common case. So, a lot of ARM instructions begin with 1110, if they're not conditionally executed.
We've seen before that conditional operations allow the CPU to avoid pipeline-disrupting branches. It is interesting that this feature is so important that every machine language instruction can be conditional, and 4 out of 32 bits in every instruction are reserved to indicate the condition.
Question 1
Use the references to determine what 4-bit pattern would be at the start of an moveq instruction, and of a addlt instruction.
Answer 1
The next two bits are fixed at 00, and mark the instruction as a Data Processing instruction. Every category of instruction has its own unique set of fixed bits, which distinguishes it from any other category. We'll look more closely at the fixed bits later.
Following the 00 fixed bits is a single bitfield labelled I (That's an I, not a 1.) This bit affects how the final 12 bits for operand2 are interpreted. We'll discuss it when we get to operand2.
The next four bits labelled opcode indicate which type of data processing instruction to perform: add, sub and so on.
Question 2
Check the references again. What four-bit opcode pattern indicates an add? Which an rsb? Find and describe the eight data processing instructions we haven't seen in our example code thus far.
Answer 2
Following opcode is the S bit. While we typically set condition codes with a cmp (or tst, teq or cmn), any data processing instruction will set the condition codes if you set the S bit to 1. So we might write adds r1, r2, r3 (the s suffix sets the s bit) and then do a branch on the result of the add, e.g. beq label.
The Rd field in bits 12-15 (skipping the Rn for now -- some diagrams may label it Rs) gives the target register, as a four-bit binary number from 0 to 15. So, for instance, an add R1, R2, R3 has Rd of 0001, indicating register 1 as the target. The Rn field gives the first operand, so that same instruction has an Rn of 0010. Remember that the bits for the target register come after those for the first operand register.
The second operand occupies 12 bits. We've seen a variety of possible second operands. Examples include:
#42 constant 42 (or anything up to 255) R3 or any other register R4, lsr 3 R4 logic right-shifted by 3 or any value up to 31 R4, asr R2 R4 arithmetic right-shifted by R2's value.
And something alluded to earlier: certain constants over 255 are permitted, e.g. 200, 512, or even 16,711,680. But, interestingly, not 257, or 513, or many others. We'll see why in a minute.
Getting all that out of 12 bits is a remarkable accomplishment, and the operand2 bits are among the most cleverly used in the ARM design, or in any ISA. They are used in two different ways, as determined by the aforementioned I bit. Let's go over each of those two ways.
If the I bit is 0, the operand2 bits represent a register and optional shift.
The first 8 bits describe the shift (S bits) and the last 4 the register (R bits), so the format is:
SSSSSSSSRRRR
The shift bits can represent either a constant shift, e.g. asr #12, or a shift by the value a register, e.g lsr r3. See page 4-12 in our reference doc for details.
If that last S bit is 0, it indicates a constant shift. The first 5 S bits give a 0 to 31 integer value indicating the amount to shift, and the next 2 bits give the shift type, e.g. 10 for asr. So the 8 shift bits for asr #12 are 01100 10 0. 011000 is 12. 10 is asr. 0 is constant shift. Putting it all together, a 12-bit operand2 representing r6, asr #12 is:
01100 10 0 0110
with the last four bits giving the register to shift: r6.
If the last S bit is 1, then we have a register-determined shift, e.g. asr r4. The first four bits give the register containing the shift amount, the fifth bit is a fixed 0, and the next two give the shift type as for constant shift. So asr R4 is 0100 0 10 1. And the 12 bits for R6, asr R4 would be
0100 0 10 1 0110
Question 3
All of this is documented in the references. Use the discussion above, and the references, to determine the 12-bit operand2 patterns for:
R9, lsl #30 R3, asr R7 fp, ror #11 @ fp rotate-right-shifted by 11
Answer 3
R9, lsl #30 is 11110 00 0 1001 R3, asr R7 is 0111 0 10 1 0011 fp, ror #11 is 01011 11 0 1011 since fp is R11.
The ror is new; we haven't used it before. It specifies a rotating or circular shift to the right. (Recall this concept from the intermediate binary lecture.)
Question 4
There is no left circular shift. Why is one not needed?
Answer 4
Question 5
Explain why a register-and-shift operand2 cannot have the pattern XXXX1XX1XXXX, where X is any bit value.
Answer 5
The illegality of that 12-bit pattern turns out to be important later.
All of the preceding discussion was for the register-and-shift operand2. If the I bit is 1, then operand2's 12 bits represent a constant value instead of a possibly-shifted register, and the rules are completely different.
The easy way to use those 12 bits would be to let them represent a constant integer from 0 to 4095, which is the max unsigned integer value in 12 bits. A more conventional or older ISA might do just that. But the ARM designers decided to stretch those 12 bits in a clever way.
They realized that not all high integer values were equally likely to be needed, so they reserved just the bottom 8 bits for the integer constant, giving the range of 0 to 255 we've been assuming thus far. But, those 8 bits are viewed as the bottom byte in a 32 bit value (with the top 24 bits 0.) And, the top 4 bits of operand2 specify an amount to circular or rotate shift those bottom 8 bits, so that they can be moved about to get various larger constants.
Specifically, the top 4 bits give a value between 0 and 15. This is doubled to get possible shift values 0,2,4,....30. Odd-valued shifts are not possible. The shift is to the right, not the expected left. But, it's a circular right shift. So, for instance, a shift of 2 pushes the bottom two bits "around the bend" into the top two positions.
00000000000000000000000010100111 >> 2 gives 11000000000000000000000000101001
If you want to shift left by say, 4 bits, you specify a circular right shift of 28 bits, which is the same thing. Here are some examples of constant operand2 values:
1110 10100011 circular right shift of 14*2 = 28 => 00000000000000000000101000110000 0101 11000011 circular right shift of 5*2 = 10 => 00110000110000000000000000000000
Question 6
What 32-bit constants do these operand2 constants represent: 100010100101, 110011000011, 000011011011.
Answer 6
1000 10100101 => 00000000101001010000000000000000 (shift by 16) 1100 11000011 => 00000000000000001100001100000000 (shift by 24) 0000 11011011 => 00000000000000000000000011011011 (no shift)
Question 7
Try it the other way. For each of the values 4064, 32768, and 1028, construct an operand2 bit pattern that results in that value. Or, as will be the case for one of them, explain why it's not possible. Can any of them be represented in more than one way?
Answer 7
Fortunately, you don't have to do all this by hand when coding in assembly. You simply give the constant you want in the instruction, and the assembler determines how to represent it, or gives an error if it cannot do so. So, add r1, r2, #32768 simply works. And mov r1, #1028 gets an error.
You now know enough to decode a data processing machine language instruction or two.
Question 8
Decode this instruction, giving the assembly language equivalent. You'll need to first translate hex to binary so you can see the bits, then break apart the fields, and if needed look up the significance of each by reviewing the lecture or the reference docs. Then write an English description of the instruction, and finally write the assembly for it.
E1874203
Answer 8
In hex: E1874203 In binary 11100001100001110100001000000011 By fields: 1110 00 0 1100 0 0111 0100 00100 00 0 0011 w/meanings always dp reg/shift orr noS r7 r4 sft 4 lsl reg r3 In English, that's always-execute, a dp-format instruction, with register-and-shift operand2 format, an orr operation, don't set condition codes, target register r4, operand 1 register r7, operand2 is a constant shift of 4, logical shift left, on register r3 In assembly: orr r4, r7, r3, lsl #4
Question 9
Now go the other way, translating this instruction from assembly, to an English description, to a labelled fields representation, to binary and finally hex:
rsblt r0, r1, #3360
Answer 9
In assembly: rsblt r0, r1, #3360 In English: On condition lt, do a data processing rsb, with a constant operand2. Target is r0, operand1 is r1, and operand2 is 3360, or 210*16, or 210 right-circular rotated by 32-4 = 28 By fields: 1101 00 1 0011 0 0001 0000 1110 11010010 w/meanings lt dp const op2 rsb nS r1 r0 ror 28 210 In binary: 1101 0010 0110 0001 0000 1110 1101 0010 In hex: B2610ED2
In the next lecture segment we'll look at the ldr/str and related formats.