ARM assembler in Raspberry Pi – Chapter 7
ARM architecture has been for long targeted at embedded systems. Embedded systems usually end being used in massively manufactured products (dishwashers, mobile phones, TV sets, etc). In this context margins are very tight so a designer will always try to spare as much components as possible (a cent saved in hundreds of thousands or even millions of appliances may pay off). One relatively expensive component is memory although every day memory is less and less expensive. Anyway, in constrained memory environments being able to save memory is good and ARM instruction set was designed with this goal in mind. It will take us several chapters to learn all of these techniques, today we will start with one feature usually named shifted operand.
Indexing modes
We have seen that, except for load (ldr
), store (str
) and branches (b
and bXX
), ARM instructions take as operands either registers or immediate values. We have also seen that the first operand is usually the destination register (being str
a notable exception as there it plays the role of source because the destination is now the memory). Instruction mov
has another operand, a register or an immediate value. Arithmetic instructions like add
and and
(and many others) have two more source registers, the first of which is always a register and the second can be a register or an immediate value.
These sets of allowed operands in instructions are collectively called indexing modes. Today this concept will look a bit off since we will not index anything. The name indexing makes sense in memory operands but ARM instructions, except load and store, do not have memory operands. This is the nomenclature you will find in ARM documentation so it seems sensible to use theirs.
We can summarize the syntax of most of the ARM instructions in the following pattern
There are some exceptions, mainly move (mov
), branches, load and stores. In fact move is not so different actually.
Both Rdest
and Rsource1
must be registers. In the next section we will talk about source2
.
We will discuss the indexing modes of load and store instructions in a future chapter. Branches, on the other hand, are surprisingly simple and their single operand is just a label of our program, so there is little to discuss on indexing modes for branches.
Shifted operand
What is this mysterious source2
in the instruction patterns above? If you recall the previous chapters we have used registers or immediate values. So at least that source2
is this: register or immediate value. You can use an immediate or a register where a source2
is expected. Some examples follow, but we have already used them in the examples of previous chapters.
But source2
can be much more than just a simple register or an immediate. In fact, when it is a register we can combine it with a shift operation. We already saw one of these shift operations in chapter 6. Not it is time to unveil all of them.
LSL #n
Logical Shift Left. Shifts bitsn
times left. Then
leftmost bits are lost and then
rightmost are set to zero.LSL Rsource3
Like the previous one but instead of an immediate the lower byte of a register specifies the amount of shifting.LSR #n
Logical Shift Right. Shifts bitsn
times right. Then
rightmost bits are lost and then
leftmost bits are set to zero,LSR Rsource3
Like the previous one but instead of an immediate the lower byte of a register specifies the amount of shifting.ASR #n
Arithmetic Shift Right. Like LSR but the leftmost bit before shifting is used instead of zero in then
leftmost ones.ASR Rsource3
Like the previous one but using a the lower byte of a register instead of an immediate.ROR #n
Rotate Right. Like LSR but then
rightmost bits are not lost but pushed onto then
leftmost bitsROR Rsource3
Like the previous one but using a the lower byte of a register instead of an immediate.
In the listing above, n
is an immediate from 1 to 31. These extra operations may be applied to the value in the second source register (to the value, not to the register itself) so we can perform some more operations in a single instruction. For instance, ARM does not have any shift right or left instruction. You just use the mov
instruction.
You may be wondering why one would want to shift left or right the value of a register. If you recall chapter 6 we saw that shifting left (LSL
) a value gives a value that the same as multiplying it by 2. Conversely, shifting it right (ASR
if we use two's complement, LSR
otherwise) is the same as dividing by 2. Since a shift of n
is the same as doing n
shifts of 1, shifts actually multiply or divide a value by 2n.
We can combine it with add
to get some useful cases.
You can do something similar with sub
.
ARM comes with a handy rsb
(Reverse Substract) instruction which computes Rdest ← source2 - Rsource1
(compare it to sub
which computes Rdest ← Rsource1 - source2
).
Another example, a bit more contrived.
You are probably wondering why would we want to use shifts to perform multiplications. Well, the generic multiplication instruction always work but it is usually much harder to compute by our ARM processor so it may take more time. There are times where there is no other option but for many small constant values a single instruction may be more efficient.
Rotations are less useful than shifts in everyday use. They are usually used in cryptography, to reorder bits and "scramble" them. ARM does not provide a way to rotate left but we can do a n
rotate left doing a 32-n
rotate right.
That's all for today.