diff --git a/section_1/float/literals.md b/section_1/float/literals.md new file mode 100644 index 0000000..6c7d394 --- /dev/null +++ b/section_1/float/literals.md @@ -0,0 +1,135 @@ +# Section 1 / Floating Point Literals + +Recall that all AARCH64 instructions are 4 bytes long. Recall also that +this means that there are constraints on what can be specified as a +literal since the literal must be encoded into the 4 byte instruction. +If the literal is too large, an assembler error will result. + +Given that floating point values are always at least 4 bytes long +themselves, using floating point literals is extremely constrained. +For example: + +```asm + fmov d0, 1 // 1 + fmov d0, 1.1 // 2 +``` + +`Line 1` will pass muster but `Line 2` will cause an error. + +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 the +magic of treating the literal as if it were an address and puts it into +`s0`. + +`Line 7` converts the single precision number into a double precision +number for printing. + +*`printf()` only knows how to print double precision values. When you +specify a `float`, it will convert it to a `double` before emitting it.* + +Translating `floats` and `doubles` by hand isn't a common practice for +humans, though compilers are happy to do so. + +Instead for us humans, the assembler directives `.float` and `.double` +are used more frequently to specify `float` and `double` values putting +them into RAM. + +The following example prints an array of floats and doubles: + +```asm + .global main // 1 + .text // 2 + .align 2 // 3 + // 4 +counter .req x20 // 5 +dptr .req x21 // 6 +fptr .req x22 // 7 + .equ max, 4 // 8 + // 9 +main: stp counter, x30, [sp, -16]! // 10 + stp dptr, fptr, [sp, -16]! // 11 + ldr dptr, =d // 12 + ldr fptr, =f // 13 + mov counter, xzr // 14 + // 15 +1: cmp counter, max // 16 + beq 2f // 17 + // 18 + ldr d0, [dptr, counter, lsl 3] // 19 + ldr s1, [fptr, counter, lsl 2] // 20 + fcvt d1, s1 // 21 + ldr x0, =fmt // 22 + add counter, counter, 1 // 23 + mov x1, counter // 24 + bl printf // 25 + b 1b // 26 + // 27 +2: ldp dptr, fptr, [sp], 16 // 28 + ldp counter, x30, [sp], 16 // 29 + mov w0, wzr // 30 + ret // 31 + // 32 + .data // 33 +fmt: .asciz "%d %f %f\n" // 34 +d: .double 1.111111, 2.222222, 3.333333, 4.444444 // 35 +f: .float 1.111111, 2.222222, 3.333333, 4.444444 // 36 + // 37 + .end // 38 +``` + +The above code is found [here](./literals.s). + +A number of interesting things in this source code: + +* We use `.req` to give symbolic names to various registers. This can +help you in remembering which register is being used for what purpose. + +* We use `.equ` to encode a small integer literal value to give it a +symbolic name, eliminating the use of a "magic number." + +* `Lines 19` and `20` use address arithmetic to march through an +array of doubles (8 bytes each) and an array of floats (4 bytes each). + +`Line 19` is equivalent to: + +```c++ +// ldr d0, [dptr, counter, lsl 3] +d0 = dptr[counter]; +``` + +`counter` is multiplied by 8 then added to `dptr`. + +`Line 20` is equivalent to: + +`counter` is multiplied byb 4 then added to `fptr`. + +```c++ +// ldr s1, [fptr, counter, lsl 2] +s1 = fptr[counter]; +``` + +Cool huh? diff --git a/section_1/float/literals.s b/section_1/float/literals.s new file mode 100644 index 0000000..c537670 --- /dev/null +++ b/section_1/float/literals.s @@ -0,0 +1,38 @@ + .global main + .text + .align 2 + +counter .req x20 +dptr .req x21 +fptr .req x22 + .equ max, 4 + +main: stp counter, x30, [sp, -16]! + stp dptr, fptr, [sp, -16]! + ldr dptr, =d + ldr fptr, =f + mov counter, xzr + +1: cmp counter, max + beq 2f + + ldr d0, [dptr, counter, lsl 3] + ldr s1, [fptr, counter, lsl 2] + fcvt d1, s1 + ldr x0, =fmt + add counter, counter, 1 + mov x1, counter + bl printf + b 1b + +2: ldp dptr, fptr, [sp], 16 + ldp counter, x30, [sp], 16 + mov w0, wzr + ret + + .data +fmt: .asciz "%d %f %f\n" +d: .double 1.111111, 2.222222, 3.333333, 4.444444 +f: .float 1.111111, 2.222222, 3.333333, 4.444444 + + .end diff --git a/section_1/float/t.s b/section_1/float/t.s new file mode 100644 index 0000000..ae1b890 --- /dev/null +++ b/section_1/float/t.s @@ -0,0 +1,16 @@ + .text + .global main + .align 2 + +main: str x30, [sp, -16]! + ldr s0, =0x3fc00000 + fcvt d0, s0 + ldr x0, =fmt + bl printf + ldr x30, [sp], 16 + mov w0, wzr + ret + + .data +fmt: .asciz "%f\n" + .end