Skip to content

Commit

Permalink
ojoijoijij
Browse files Browse the repository at this point in the history
wr
f
wrf

wrf

wrf
Merge branch 'main' of https://github.com/pkivolowitz/asm_book
  • Loading branch information
pkivolowitz committed Apr 6, 2023
2 parents 664e952 + e149b1a commit 9435411
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 50 deletions.
36 changes: 20 additions & 16 deletions section_2/float/fmov.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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:
Expand All @@ -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
Expand All @@ -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 modified section_2/float/fmov.pdf
Binary file not shown.
68 changes: 37 additions & 31 deletions section_2/float/literals.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

The above code is found [here](./t.s).

`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).
.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 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 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.
Expand Down Expand Up @@ -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:

Expand Down Expand Up @@ -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:

Expand Down
Binary file modified section_2/float/literals.pdf
Binary file not shown.
10 changes: 7 additions & 3 deletions section_2/float/t.s
Original file line number Diff line number Diff line change
@@ -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
Expand Down

0 comments on commit 9435411

Please sign in to comment.