mirror of
https://github.com/pkivolowitz/asm_book.git
synced 2026-06-21 01:56:47 +08:00
ojoijoijij
wr f wrf wrf wrf Merge branch 'main' of https://github.com/pkivolowitz/asm_book
This commit is contained in:
commit
9435411155
5 changed files with 61 additions and 47 deletions
|
|
@ -2,27 +2,23 @@
|
|||
|
||||
The `fmov` instruction is used to move floating point values in and out
|
||||
of floating point registers and to some degree, moving data between
|
||||
integer and floating point registers.
|
||||
integer and floating point registers.
|
||||
|
||||
## Loading Floating Point Numbers as Immediate Values
|
||||
|
||||
Just as we saw with integer
|
||||
registers, some values can be used as immediate values and some cannot.
|
||||
Just as we saw with integer registers, some values can be used as
|
||||
immediate values and some cannot. It comes down to how many bits are
|
||||
necessary to encode the value. Too many bits... not enough room to fit
|
||||
in a 4 byte instruction plus the opcode.
|
||||
|
||||
For example, this works:
|
||||
|
||||
`mov x0, 65536`
|
||||
`mov x0, 65535`
|
||||
|
||||
but this does not:
|
||||
|
||||
`mov x0, 65537`
|
||||
|
||||
The reason is that all AARCH64 instructions must fit within a 32 bit
|
||||
instruction that must hold the instruction's op code, its flags and
|
||||
other bits and bobs plus any immediate value. In the above example we
|
||||
can see that the `mov` instruction provides up to 16 bits for an
|
||||
immediate value.
|
||||
|
||||
The constraints placed on immediate values for `fmov` are much tighter
|
||||
because floating point numbers are far more complex than integers.
|
||||
|
||||
|
|
@ -40,7 +36,7 @@ Let's take a look at some code:
|
|||
fmov d0, 1.96875 // Zoinks!
|
||||
```
|
||||
|
||||
From this we can see that an immediate value for an `fmov` seems to have
|
||||
From this we can see that an immediate value for an `fmov` has
|
||||
4 bits available for the mantissa. In fact, the only values that work
|
||||
as immediate values will be those floating point values whose fractional
|
||||
values are combinations of:
|
||||
|
|
@ -56,6 +52,9 @@ values are combinations of:
|
|||
As far as exponents go, `fmov` can accommodate 3 bits. So, exponents of
|
||||
plus or minus 2**7 can be used.
|
||||
|
||||
A sign bit makes the total number of bits available for immediate moves
|
||||
to be 8.
|
||||
|
||||
## Loading / Storing Floating Point Numbers in General
|
||||
|
||||
When in doubt, load fixed floating point numbers from memory. This is
|
||||
|
|
@ -64,11 +63,16 @@ covered [in this chapter](./literals.md).
|
|||
## SIMD
|
||||
|
||||
`fmov` can also deal with the more complicated special cases induced by
|
||||
SIMD instructions.
|
||||
SIMD instructions. `fmov` is able to move values between the various
|
||||
register widths such as single precision to double precision. **However,
|
||||
no conversion of value is performed - `fmov` just copies bits.**
|
||||
|
||||
If you need to change the precision of a floating point value, the
|
||||
`fcvt` family of instructions must be used instead.
|
||||
|
||||
## Movement To / From Integer Registers
|
||||
|
||||
`fmov` can *bits* between the integer and floating point registers. We
|
||||
emphasize the *bits*. No conversions are done using `fmov`. There exist
|
||||
other instructions for that. See [this chapter](./rounding.md) for more
|
||||
information.
|
||||
`fmov` can copy *bits* between the integer and floating point registers.
|
||||
We emphasize the *bits*. No conversions are done using `fmov`. There
|
||||
exist other instructions for that. See [this chapter](./rounding.md) for
|
||||
more information.
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -20,30 +20,32 @@ To load a `float`, you could translate the value to binary and do
|
|||
as the following:
|
||||
|
||||
```asm
|
||||
.text // 1
|
||||
.global main // 2
|
||||
.align 2 // 3
|
||||
// 4
|
||||
main: str x30, [sp, -16]! // 5
|
||||
ldr s0, =0x3fc00000 // 6
|
||||
fcvt d0, s0 // 7
|
||||
ldr x0, =fmt // 8
|
||||
bl printf // 9
|
||||
ldr x30, [sp], 16 // 10
|
||||
mov w0, wzr // 11
|
||||
ret // 12
|
||||
// 13
|
||||
.data // 14
|
||||
fmt: .asciz "%f\n" // 15
|
||||
.end // 16
|
||||
.text // 1
|
||||
.global main // 2
|
||||
.align 2 // 3
|
||||
// 4
|
||||
main: str x30, [sp, -16]! // 5
|
||||
ldr s0, =0x3fc00000 // 6
|
||||
fcvt d0, s0 // 7
|
||||
ldr x0, =fmt // 8
|
||||
bl printf // 9
|
||||
ldr x30, [sp], 16 // 10
|
||||
mov w0, wzr // 11
|
||||
ret // 12
|
||||
// 13
|
||||
.data // 14
|
||||
fmt: .asciz "%f\n" // 15
|
||||
.end // 16
|
||||
```
|
||||
|
||||
The above code is found [here](./t.s).
|
||||
The above code is kind of found [here](./t.s) - the file is used
|
||||
for miscellaneous testing.
|
||||
|
||||
`Line 6` puts the translated value of 1.5 into `s0` (since the value
|
||||
is a `float` it goes in an `s` register). The assembler performs some
|
||||
magic getting a 32 bit value seemingly fit into a 32 bit instruction.
|
||||
See [below](./literals.md#fitting-32-bits-into-a-32-bit-bag).
|
||||
`Line 6` puts the translated value of 1.5 into `s0` (since we are
|
||||
thinking of the value as a `float` it goes in an `s` register). The
|
||||
assembler performs some magic getting a 32 bit value seemingly fit into
|
||||
a 32 bit instruction. See
|
||||
[below](./literals.md#fitting-32-bits-into-a-32-bit-bag).
|
||||
|
||||
`Line 7` converts the single precision number into a double precision
|
||||
number for printing.
|
||||
|
|
@ -136,6 +138,9 @@ Cool huh?
|
|||
|
||||
## Fitting 32 bits into a 32 bit bag
|
||||
|
||||
**This section is currently LINUX-centric - in the future it will
|
||||
address both native Apple and Linux equally.***
|
||||
|
||||
AARCH64 instructions are 32 bits in width. Yet, `line 6` from
|
||||
[this](./t.s) program reads:
|
||||
|
||||
|
|
@ -195,15 +200,16 @@ Scan downward to find `0x7a0`:
|
|||
0x7a0 <main+32> .inst 0x3fc00000 ; undefined
|
||||
```
|
||||
|
||||
Hey look! Here's our literal float. The `.inst` is an ARM
|
||||
specific GNU assembler directive what allows the programmer
|
||||
to encode their own instruction. Note, the encoded instruction does not
|
||||
have to make any sense - instead the compiler has emitted a make believe
|
||||
instruction that happens to have the value of our literal.
|
||||
Hey look! Here's our literal float. The `.inst` is an ARM specific GNU
|
||||
assembler directive says: `¯\_(-)_/¯`.
|
||||
|
||||
Note, the encoded "instruction" does not have to make any sense -
|
||||
instead the compiler has emitted a make believe instruction that happens
|
||||
to have the value of our literal.
|
||||
|
||||
What we're seeing the actual `line 6` doing is reaching ahead a short
|
||||
distance to load the value of another "instruction" when really it is
|
||||
our constant.
|
||||
distance to load the value of another location in memory where our
|
||||
constant is really found.
|
||||
|
||||
Let us take this explanation further. Notice we see:
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,12 +1,16 @@
|
|||
.text
|
||||
.global main
|
||||
.align 2
|
||||
.global _main
|
||||
.align 2
|
||||
|
||||
main: str x30, [sp, -16]!
|
||||
_main:
|
||||
str x30, [sp, -16]!
|
||||
mov x0, 0xFFFFFFFF
|
||||
/*
|
||||
ldr s0, =0x3fc00000
|
||||
fcvt d0, s0
|
||||
ldr x0, =fmt
|
||||
bl printf
|
||||
*/
|
||||
ldr x30, [sp], 16
|
||||
mov w0, wzr
|
||||
ret
|
||||
|
|
|
|||
Loading…
Reference in a new issue