mirror of
https://github.com/pkivolowitz/asm_book.git
synced 2026-06-21 04:26:47 +08:00
218 lines
8.1 KiB
Markdown
218 lines
8.1 KiB
Markdown
# Section 3 / Bit Fields / Review of Newly Described Instructions
|
|
|
|
## Overview
|
|
|
|
Our discussion of implementing ourselves what the C / C++ compiler gives
|
|
us led us to use six new instructions. This chapter reviews those
|
|
instructions. In addition to describing what an instruction does, we
|
|
will try to indicate what the instruction is "good for."
|
|
|
|
## `and`
|
|
|
|
The `and` instruction is pretty much what you would expect. It
|
|
implements the `&` operator from C and C++. That is, the bitwise and
|
|
operator.
|
|
|
|
The `and` instruction comes in a number of flavors including for
|
|
use with immediate values.
|
|
|
|
### `and` Immediate
|
|
|
|
This is the form of the instruction we used.
|
|
|
|
```asm
|
|
and rd, rs, imm
|
|
```
|
|
|
|
This performs a bitwise and of the `imm` value with the source register
|
|
`rs` placing the result in the destination register `rd`.
|
|
|
|
There are limits to the bit width of `imm` because it has to fit within
|
|
the `and` instruction. If you exceed the allowable width of `imm`, the
|
|
assembler will be glad to insult you.
|
|
|
|
It is possible that a `mov` instruction will allow your immediate value.
|
|
You'd follow up with an `and` using a register than an immediate value.
|
|
|
|
If your immediate value is too large for a `mov` then put the value in
|
|
RAM and `ldr` it into a register and proceed.
|
|
|
|
We would love to tell you what the rules are for an immediate value in
|
|
the `and` instruction, but they are not obvious and in fact are very
|
|
complex. Our advice, try the immediate value you have in mind and if it
|
|
works, great. Otherwise, see above.
|
|
|
|
A slight variation on this `and` instruction uses a register where the
|
|
preceding one uses an immediate value.
|
|
|
|
```asm
|
|
and rd, rs, rm
|
|
```
|
|
|
|
This ands `rm` to `rs` and places the result in `rd`.
|
|
|
|
This instruction has a variation:
|
|
|
|
```asm
|
|
and rd, rs, rm, *shift* num_bits
|
|
```
|
|
|
|
`*shift*` can be one of `lsl`, `lsr`, `asr` or `ror`.
|
|
|
|
These mean:
|
|
|
|
| `*shift*` | Meaning | Meaning of the Meaning |
|
|
| --------- | ------- | ---------------------- |
|
|
| `lsl` | logical shift left | shifts left introducing zeros on the right |
|
|
| `lsr` | logical shift right | shifts right introducing zeros on the left |
|
|
| `asr` | arithmetic shift right | shifts right introducing duplicates of the previous most significant bit |
|
|
| `ror` | rotate right | shifts right introducing the bits shifted out back in from the left |
|
|
|
|
There are other two similar `and` instructions. These are the immediate
|
|
and the register versions of `ands`. `ands` is the same as `and` with
|
|
the addition that the CPU's condition bits are updated by the
|
|
instruction permitting a conditional branch to follow the instruction.
|
|
|
|
*`and` is the basis of bit bashing and is used to clear bits.
|
|
Put `and` together with the or instructions and a couple of other basic
|
|
logic instructions, out pops a computer.*
|
|
|
|
A shorthand way of clearing specific bits is the `bic` instruction.
|
|
Use of `bic` can avoid having to negate the bits in a mask prior to
|
|
and'ing.
|
|
|
|
## `bfi`
|
|
|
|
This instruction mnemonic stands for Bit Field Insert. The word *Insert*
|
|
should really be copy. The official ARM
|
|
[documentation](https://developer.arm.com/documentation/dui0801/g/A64-General-Instructions/BFI)
|
|
explains this instruction very well so we'll repeat it here:
|
|
|
|
*Bit Field Insert copies any number of low-order bits from a source
|
|
register into the same number of adjacent bits at any position in the
|
|
destination register, leaving other bits unchanged.*
|
|
|
|
The instruction has the following format:
|
|
|
|
```asm
|
|
bfi rd, rs, lsb, width
|
|
```
|
|
|
|
Starting at bit 0 of `rs`, `width` bits are copied to `rd` starting at
|
|
bit `lsb`.
|
|
|
|
The `bfi` instruction replaces as many as three instructions: likely a
|
|
shift, an `and` and an `orr`.
|
|
|
|
*The preceding has described what `bfi` does but what is it for? Suppose
|
|
you have a multiple bit field in a device register. Using `bfi` makes
|
|
it easy to copy the right number of bits from a source directly into
|
|
the bits in the middle of the destination without need of other
|
|
bit-bashing.*
|
|
|
|
The *opposite* of `bfi` is `bfx`.
|
|
|
|
`ubfiz` first zeros the destination register then copies in the
|
|
specified bits from the source. The `u` means don't consider the
|
|
sign bit as special. The `z` is what causes the zero fill first and
|
|
sets it apart from `bfi`.
|
|
|
|
See bottom for an example.
|
|
|
|
## `mvn`
|
|
|
|
This instruction takes only takes two operands (but permits an optional
|
|
shift to be explained below).
|
|
|
|
The basic syntax is:
|
|
|
|
```asm
|
|
mvn rd, rs
|
|
```
|
|
|
|
This flips all the bits in the source and copies them to the
|
|
destination.
|
|
|
|
In addition to the basic instruction, there is also:
|
|
|
|
```asm
|
|
mvn rd, rs, *shift* num_bits
|
|
```
|
|
|
|
The *shift* and `num_bits` function the same way as described above
|
|
including the option to use `*shift*` as one of `lsl`, `lsr`, `asr` or
|
|
`ror`.
|
|
|
|
*Note the `mvn` is different from `neg` in that `mvn` is a bitwise
|
|
operation while `neg` is aware of what makes a negative integer value.
|
|
That is, if your goal is to turn a positive integer into a negative
|
|
integer, use `neg`. If your goal is to negate bits for bit bashing
|
|
purposes, use `mvn`.*
|
|
|
|
See bottom for an example.
|
|
|
|
## `lsl`
|
|
|
|
**Logical Shift Left** moves bits to the left shifting 0 into any
|
|
vacated positions.
|
|
|
|
```asm
|
|
lsl rd, rs, *number of bits*
|
|
```
|
|
|
|
*`lsl` is most often used for bit bashing as well as for a fast
|
|
multiplication by a power of 2.*
|
|
|
|
The instruction `asl` is a synonym for `lsl`. The `a` is `asl` means
|
|
"arithmetic". There is no difference between a logical and
|
|
an arithmetic shift in the **leftward** direction. There is, however,
|
|
a difference in the **rightward** direction in that `asr` replicates
|
|
the sign bit where `lsr` still moves in zeros in any bit positions
|
|
vacated.
|
|
|
|
## `orr`
|
|
|
|
This is the plain vanilla *or* instruction. It is used extensively
|
|
for bit bashing as it is how bits can be set to 1.
|
|
|
|
```asm
|
|
orr rd, rs, rm # rm is another register
|
|
orr rd, rs, imm
|
|
```
|
|
|
|
## Example
|
|
|
|
[Here](./ubfiz.s) is the code to an example:
|
|
|
|
```asm
|
|
.global main // 1
|
|
.text // 2
|
|
.align 2 // 3
|
|
/* This demo should be run from gdb. `layout regs` would be helpful. // 4
|
|
This demo shows: // 5
|
|
bfi - bit field insert // 6
|
|
*/ // 7
|
|
main: mov w1, wzr // // 8
|
|
mvn w1, w1 // set w1 to 0xFFFFFFFF // 9
|
|
mov w3, w1 // save for reuse // 10
|
|
mov w2, 3 // set bits 0 and 1 // 11
|
|
bfi w1, w2, 4, 4 // 12
|
|
// Look at w1. You will note that the bottom // 13
|
|
// 4 bits of w2 (0011) have been copied into the second // 14
|
|
// 4 bits of w1 including the zeros. // 15
|
|
// // 16
|
|
// NEXT DEMO // 17
|
|
mov w1, w3 // reset w1 // 18
|
|
mov w2, 3 // set bits 0 and 1 // 19
|
|
ubfiz w1, w2, 4, 2 // 20
|
|
// Look at w1. You will note that all the bits were // 21
|
|
// zeroed and then 2 bits from w2 are copied into w1 // 22
|
|
// starting at bit 4. Compare this to bfi. // 23
|
|
// // 24
|
|
// NEXT DEMO // 25
|
|
bfxil w3, w1, 4, 4 // 26
|
|
// Look at w3. You will note that 4 bits (0011) from // 27
|
|
// w1 were put into w3 starting at bit 0. // 28
|
|
mov w0, wzr // 29
|
|
ret // 30
|
|
```
|