ojoijoijij

wr
f
wrf

wrf

wrf
Merge branch 'main' of https://github.com/pkivolowitz/asm_book
This commit is contained in:
Perry Kivolowitz 2023-04-06 18:47:57 -05:00
commit 9435411155
5 changed files with 61 additions and 47 deletions

View file

@ -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.

View file

@ -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.

View file

@ -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