ARM barrel shifter
The ARM processor incorporates a barrel shifter that can be used with the
mov/mvn and
data processing instructions.
You can also use the barrel shifter to affect the index value in
ldr/str operations.
Looking in more detail at the right hand 12 bits (which specific one of these something is, being defined elsewhere):
The ROTATE contains a value 0..15 for right rotates 0..30 in steps of 2.
The LIT8 value is zero-extended to 32 bits before being rotated by that amount.
Therefore you can encode any even-aligned 8 bits or any (odd-aligned) 7 bits.
The compiler manages most of that automatically, complaining when you ask for the impossible.
In particular it will split a mov (or mvn) instruction, but of course cannot do the same for (say) an add.
Suppose we wish to load #1635427C, a simplistic brute force approach might be:
[Noting that neither #2C/10 nor #B0/12 would constitute any kind of error, however weird they might at first seem, nor would it
be wrong or particularly unusual to see an orr of 1..3 after that last instruction, to fill in the last 2 bits the C couldn’t, though
for our collective sanity the compiler will prefer to emit same #instructions with non-overlapping nibbles if it can.]
Traditionally, ARM assemblers reject many things Phix/#ilASM{} figures out for you, especially variable addresses.
(It is of course mostly variable addresses that need this stuff, and luckily their locations should all be set in stone (or be frame-relative)
long before we need to emit even the very first machine instruction.)
Naturally the compiler is also at liberty to use shorter pc-relative (etc) operations when it can, and even
locate the address of some variable j as being some already-loaded-variable-i-address-relative.
One thing I would rather avoid is the "data holes in the code" approach, since that makes disassembly much trickier, plus
denying myself that "cop-out" plays right into my plans for making much better use of the available registers anyway.
As warned other instructions cannot be so split (obviously use eg mov r2,#102 then add r0,r2):
The ror 0 coding is actually rrx which rotates the 33-bit Rm & carry (just/only) one bit to the right.
The asr 0 coding is instead asr 32, giving a result of all ones or all zeros (ie the sign bit as 0/-1).
If no shift is required the SHFT, Z, and SHIFT# bits should all be 0 (aka "lsl 0").
Be advised that Z=1 and V!=0 is likely not this at all but a multiply or undefined instruction.
There is also an asl mnemonic which stands for arithmetic shift left, which behaves identically to lsl.
Note the compiler allows pseudo-instructions such as lsl r0,3 as shorthand for mov r0, r0 lsl 3, and/or asr r0,r2,3 for mov r0,r2 asr 3.
Rs may not be pc aka r15.
Please don’t ask or expect me to explain what happens when such a shift operation affects the carry flag,
and that is also (subsequently,?) used/set by the opcode (eg adc).
Likewise the precise effects of shifts>=32 holds no interest for me whatsoever.
Various other examples:
Looking in more detail at the right hand 12 bits (which specific one of these something is, being defined elsewhere):
| ... | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| ... | ROTATE | LIT8 | ||||||||||
| ... | RS | V | SHFT | Z | RM | |||||||
| ... | shift# | shft | 0 | Rm | ||||||||
| ... | Rs | 0 | shft | 1 | Rm | |||||||
The ROTATE contains a value 0..15 for right rotates 0..30 in steps of 2.
The LIT8 value is zero-extended to 32 bits before being rotated by that amount.
Therefore you can encode any even-aligned 8 bits or any (odd-aligned) 7 bits.
The compiler manages most of that automatically, complaining when you ask for the impossible.
In particular it will split a mov (or mvn) instruction, but of course cannot do the same for (say) an add.
Suppose we wish to load #1635427C, a simplistic brute force approach might be:
mov r0,#16000000 ; (#16 ror 8)
orr r0,#00350000 ; (#35 ror 16)
orr r0,#00004200 ; (#42 ror 24)
orr r0,#0000007C ; (#7C ror 0)
However the compiler should (for that very carefully chosen constant) be smart enough to emit:
mov r0,#16000000 ; (#16 ror 8 [or #2C ror 10, or #B0 ror 12])
orr r0,#00354000 ; (#D5 ror 18)
orr r0,#0000027C ; (#9F ror 30)
You may and should be rather grateful the compiler does most of that sort of thing obediently and silently![Noting that neither #2C/10 nor #B0/12 would constitute any kind of error, however weird they might at first seem, nor would it
be wrong or particularly unusual to see an orr of 1..3 after that last instruction, to fill in the last 2 bits the C couldn’t, though
for our collective sanity the compiler will prefer to emit same #instructions with non-overlapping nibbles if it can.]
Traditionally, ARM assemblers reject many things Phix/#ilASM{} figures out for you, especially variable addresses.
(It is of course mostly variable addresses that need this stuff, and luckily their locations should all be set in stone (or be frame-relative)
long before we need to emit even the very first machine instruction.)
Naturally the compiler is also at liberty to use shorter pc-relative (etc) operations when it can, and even
locate the address of some variable j as being some already-loaded-variable-i-address-relative.
One thing I would rather avoid is the "data holes in the code" approach, since that makes disassembly much trickier, plus
denying myself that "cop-out" plays right into my plans for making much better use of the available registers anyway.
As warned other instructions cannot be so split (obviously use eg mov r2,#102 then add r0,r2):
; add r0,#102 ; invalid (only just: 8 bits but odd rotate)
add r0,#104 ; valid (7 bits that /can/ be evenly rotated)
add r0,#204 ; valid (8 bits that /can/ be evenly rotated)
Unlike ROTATE, the lower 5 bits of Rs or the shift# immediate can contain 0..31, which acts on Rm, with SHFT as follows:
| SHFT | Mnemonic | Description |
| 0 0 | lsl | logical shift left (zero-fill) [===asl/shl] |
| 0 1 | lsr | logical shift right (zero-fill) |
| 1 0 | asr | arithmetic shift right (sign-extend) |
| 1 1 | ror | rotate right |
| 1 1 | rrx | rotate right extended (1 bit, see below) |
The ror 0 coding is actually rrx which rotates the 33-bit Rm & carry (just/only) one bit to the right.
The asr 0 coding is instead asr 32, giving a result of all ones or all zeros (ie the sign bit as 0/-1).
If no shift is required the SHFT, Z, and SHIFT# bits should all be 0 (aka "lsl 0").
Be advised that Z=1 and V!=0 is likely not this at all but a multiply or undefined instruction.
There is also an asl mnemonic which stands for arithmetic shift left, which behaves identically to lsl.
Note the compiler allows pseudo-instructions such as lsl r0,3 as shorthand for mov r0, r0 lsl 3, and/or asr r0,r2,3 for mov r0,r2 asr 3.
Rs may not be pc aka r15.
Please don’t ask or expect me to explain what happens when such a shift operation affects the carry flag,
and that is also (subsequently,?) used/set by the opcode (eg adc).
Likewise the precise effects of shifts>=32 holds no interest for me whatsoever.
Various other examples:
| Instruction | Description |
| mov r0, r1 lsl 2 | r0 := r1*4 |
| lsl r0, 2 | r0 *= 4 |
| lsr r0, 2 | r0 := floor(r0/4) |
[to be cleaned up/merged...] 4.5.1 CPSR flags The data processing operations may be classified as logical or arithmetic. The logical operations (AND, EOR, TST, TEQ, ORR, MOV, BIC, MVN) perform the logical action on all corresponding bits of the operand or operands to produce the result. If the S bit is set (and Rd is not R15, see below) the V flag in the CPSR will be unaffected, the C flag will be set to the carry out from the barrel shifter (or preserved when the shift operation is LSL #0), the Z flag will be set if and only if the result is all zeros, and the N flag will be set to the logical value of bit 31 of the result. The arithmetic operations (SUB, RSB, ADD, ADC, SBC, RSC, cmp, cmn) treat each operand as a 32 bit integer (either unsigned or 2’s complement signed, the two are equivalent). If the S bit is set (and Rd is not R15) the V flag in the CPSR will be set if an overflow occurs into bit 31 of the result; this may be ignored if the operands were considered unsigned, but warns of a possible error if the operands were 2’s Assembler Mnemonic OpCode Action AND 0000 operand1 AND operand2 EOR 0001 operand1 EOR operand2 SUB 0010 operand1 - operand2 RSB 0011 operand2 - operand1 ADD 0100 operand1 + operand2 ADC 0101 operand1 + operand2 + carry SBC 0110 operand1 - operand2 + carry - 1 RSC 0111 operand2 - operand1 + carry - 1 TST 1000 as AND, but result is not written TEQ 1001 as EOR, but result is not written cmp 1010 as SUB, but result is not written cmn 1011 as ADD, but result is not written ORR 1100 operand1 OR operand2 MOV 1101 operand2(operand1 is ignored) BIC 1110 operand1 AND NOT operand2(Bit clear) MVN 1111 NOT operand2(operand1 is ignored) Table 4-3: ARM Data processing instructions complement signed. The C flag will be set to the carry out of bit 31 of the ALU, the Z flag will be set if and only if the result was zero, and the N flag will be set to the value of bit 31 of the result (indicating a negative result if the operands are considered to be 2’s complement signed).