mirror of
https://github.com/pkivolowitz/asm_book.git
synced 2026-06-23 15:36:45 +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
|
The `fmov` instruction is used to move floating point values in and out
|
||||||
of floating point registers and to some degree, moving data between
|
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
|
## Loading Floating Point Numbers as Immediate Values
|
||||||
|
|
||||||
Just as we saw with integer
|
Just as we saw with integer registers, some values can be used as
|
||||||
registers, some values can be used as immediate values and some cannot.
|
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:
|
For example, this works:
|
||||||
|
|
||||||
`mov x0, 65536`
|
`mov x0, 65535`
|
||||||
|
|
||||||
but this does not:
|
but this does not:
|
||||||
|
|
||||||
`mov x0, 65537`
|
`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
|
The constraints placed on immediate values for `fmov` are much tighter
|
||||||
because floating point numbers are far more complex than integers.
|
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!
|
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
|
4 bits available for the mantissa. In fact, the only values that work
|
||||||
as immediate values will be those floating point values whose fractional
|
as immediate values will be those floating point values whose fractional
|
||||||
values are combinations of:
|
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
|
As far as exponents go, `fmov` can accommodate 3 bits. So, exponents of
|
||||||
plus or minus 2**7 can be used.
|
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
|
## Loading / Storing Floating Point Numbers in General
|
||||||
|
|
||||||
When in doubt, load fixed floating point numbers from memory. This is
|
When in doubt, load fixed floating point numbers from memory. This is
|
||||||
|
|
@ -64,11 +63,16 @@ covered [in this chapter](./literals.md).
|
||||||
## SIMD
|
## SIMD
|
||||||
|
|
||||||
`fmov` can also deal with the more complicated special cases induced by
|
`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
|
## Movement To / From Integer Registers
|
||||||
|
|
||||||
`fmov` can *bits* between the integer and floating point registers. We
|
`fmov` can copy *bits* between the integer and floating point registers.
|
||||||
emphasize the *bits*. No conversions are done using `fmov`. There exist
|
We emphasize the *bits*. No conversions are done using `fmov`. There
|
||||||
other instructions for that. See [this chapter](./rounding.md) for more
|
exist other instructions for that. See [this chapter](./rounding.md) for
|
||||||
information.
|
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:
|
as the following:
|
||||||
|
|
||||||
```asm
|
```asm
|
||||||
.text // 1
|
.text // 1
|
||||||
.global main // 2
|
.global main // 2
|
||||||
.align 2 // 3
|
.align 2 // 3
|
||||||
// 4
|
// 4
|
||||||
main: str x30, [sp, -16]! // 5
|
main: str x30, [sp, -16]! // 5
|
||||||
ldr s0, =0x3fc00000 // 6
|
ldr s0, =0x3fc00000 // 6
|
||||||
fcvt d0, s0 // 7
|
fcvt d0, s0 // 7
|
||||||
ldr x0, =fmt // 8
|
ldr x0, =fmt // 8
|
||||||
bl printf // 9
|
bl printf // 9
|
||||||
ldr x30, [sp], 16 // 10
|
ldr x30, [sp], 16 // 10
|
||||||
mov w0, wzr // 11
|
mov w0, wzr // 11
|
||||||
ret // 12
|
ret // 12
|
||||||
// 13
|
// 13
|
||||||
.data // 14
|
.data // 14
|
||||||
fmt: .asciz "%f\n" // 15
|
fmt: .asciz "%f\n" // 15
|
||||||
.end // 16
|
.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
|
`Line 6` puts the translated value of 1.5 into `s0` (since we are
|
||||||
is a `float` it goes in an `s` register). The assembler performs some
|
thinking of the value as a `float` it goes in an `s` register). The
|
||||||
magic getting a 32 bit value seemingly fit into a 32 bit instruction.
|
assembler performs some magic getting a 32 bit value seemingly fit into
|
||||||
See [below](./literals.md#fitting-32-bits-into-a-32-bit-bag).
|
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
|
`Line 7` converts the single precision number into a double precision
|
||||||
number for printing.
|
number for printing.
|
||||||
|
|
@ -136,6 +138,9 @@ Cool huh?
|
||||||
|
|
||||||
## Fitting 32 bits into a 32 bit bag
|
## 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
|
AARCH64 instructions are 32 bits in width. Yet, `line 6` from
|
||||||
[this](./t.s) program reads:
|
[this](./t.s) program reads:
|
||||||
|
|
||||||
|
|
@ -195,15 +200,16 @@ Scan downward to find `0x7a0`:
|
||||||
0x7a0 <main+32> .inst 0x3fc00000 ; undefined
|
0x7a0 <main+32> .inst 0x3fc00000 ; undefined
|
||||||
```
|
```
|
||||||
|
|
||||||
Hey look! Here's our literal float. The `.inst` is an ARM
|
Hey look! Here's our literal float. The `.inst` is an ARM specific GNU
|
||||||
specific GNU assembler directive what allows the programmer
|
assembler directive says: `¯\_(-)_/¯`.
|
||||||
to encode their own instruction. Note, the encoded instruction does not
|
|
||||||
have to make any sense - instead the compiler has emitted a make believe
|
Note, the encoded "instruction" does not have to make any sense -
|
||||||
instruction that happens to have the value of our literal.
|
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
|
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
|
distance to load the value of another location in memory where our
|
||||||
our constant.
|
constant is really found.
|
||||||
|
|
||||||
Let us take this explanation further. Notice we see:
|
Let us take this explanation further. Notice we see:
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,12 +1,16 @@
|
||||||
.text
|
.text
|
||||||
.global main
|
.global _main
|
||||||
.align 2
|
.align 2
|
||||||
|
|
||||||
main: str x30, [sp, -16]!
|
_main:
|
||||||
|
str x30, [sp, -16]!
|
||||||
|
mov x0, 0xFFFFFFFF
|
||||||
|
/*
|
||||||
ldr s0, =0x3fc00000
|
ldr s0, =0x3fc00000
|
||||||
fcvt d0, s0
|
fcvt d0, s0
|
||||||
ldr x0, =fmt
|
ldr x0, =fmt
|
||||||
bl printf
|
bl printf
|
||||||
|
*/
|
||||||
ldr x30, [sp], 16
|
ldr x30, [sp], 16
|
||||||
mov w0, wzr
|
mov w0, wzr
|
||||||
ret
|
ret
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue