Introduction

Hello World

Printing variables

User input

Testing printf

Mathematical operations

Functions

Arrays

Loops

Projects:

Estimating sin(x)

Calculating cube roots

Sieve of Eratosthenes

Integer and floating point maths


Integer maths

Integer maths is easily done using the x registers. The ARM64 instruction set provides "add", "sub", "mul", and "div" to perform basic integer maths. Note that while adding, subtracting and multiplying integers always results in an integer result, division can also result in a floating point result.
That is, 8 ÷ 4 = 2 while 8 ÷ 3 = 2.25 which would be truncated to 2.

This can be tested with the following code:

    
    // maths_operations.s

    .global main
    .extern printf

    .data
    add_out:
	    .asciz "%d + %d = %d\n"
    subt_out:
	    .asciz "%d - %d = %d\n"
    mult_out:
	    .asciz "%d x %d = %d\n"
    div_out:
	    .asciz "%d / %d = %d\n"
    num1:
	    .quad 8
    num2:
	    .quad 3

    .text
    main:
	    // Prolog
	    stp x29, x30, [sp, -16]!
	    mov x29, sp

	    // Main code
	    ldr x0, =add_out	// Load x0 with output string
	    ldr	x1, =num1		// Load x1 with addr of num1
	    ldr x1, [x1]		// Load x1 with value of num1
	    ldr x2, =num2		// Load x2 with addr of num2
	    ldr x2, [x2]		// load x2 with value of num2
	    add x3, x1, x2		// x3 = x1 + x2
	    bl printf

	    ldr x0, =subt_out
	    ldr x1, =num1
	    ldr x1, [x1]
	    ldr x2, =num2
	    ldr x2, [x2]
	    sub x3, x1, x2		// x3 = x1 - x2
	    bl printf

	    ldr x0, =mult_out
	    ldr x1, =num1
	    ldr x1, [x1]
	    ldr x2, =num2
	    ldr x2, [x2]
	    mul x3, x1, x2		// x3 = x1 * x2
	    bl printf

	    ldr x0, =div_out
	    ldr x1, =num1
	    ldr x1, [x1]
	    ldr x2, =num2
	    ldr x2, [x2]
	    sdiv x3, x1, x2		// x3 = x1 / x2, both operands are signed
	    bl printf

	    ldr x0, =div_out
	    ldr x1, =num1
	    ldr x1, [x1]
	    ldr x2, =num2
	    ldr x2, [x2]
	    udiv x3, x1, x2		// x3 = x1 / x2, both operands are unsigned
	    bl printf

	    // Cleanup
	    mov x0, #0
	    ldp x29, x30, [sp], 16
	    RET
    
    

The only thing to note is the two different divisions used at the end.
They are "sdiv" and "udiv". Sdiv is signed division where the signs of the operands are considered, udiv is unsigned division where the operands are treated as unsigned integers.
In the code shown we expect both to produce the same output.

    
    andrew@master:~/assm2 $ ./maths_operations_2
    8 + 3 = 11
    8 - 3 = 5
    8 x 3 = 24
    8 / 3 = 2
    8 / 3 = 2
    
    

The output is as expected.
In the next example, 8 will be replaced with -8. Observe the output of the final division.

    
    andrew@master:~/assm2 $ ./maths_operations_2
    -8 + 3 = -5
    -8 - 3 = -11
    -8 x 3 = -24
    -8 / 3 = -2
    -8 / 3 = 1431655762
    
    

The result for the final calculation is explained as the processor interpreting -8 as an unsigned 64 bit integer. There is an issue here in that 8 as a 64 bit signed integer would be

0 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 000

Therefore the 64 bit signed representation of -8 would be:

1 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1110 111

As an unsigned 64 bit integer this would be 18,446,744,073,709,551,613.
If this is divided by 3 the result is 6,148,914,691,236,516,864 which is not what is shown in the output. The output shown is too small and I don't know why.

Floating point maths

The only operation that needs to be demonstrated is division but "everything in pairs" so multiplicaiton will also be demonstrated.

    
    // maths_with_floats

    .global main
    .extern printf

    .section .data
    output_mult:
	    .asciz "%f x %f = %f\n"

    output_div:
	    .asciz "%f / %f = %f\n"

    num1:
	    .double 3.125

    num2:
	    .double 2.667

    .section .text
    main:
	    // Prolog
	    stp x29, x30, [sp, -16]!
	    mov x29, sp

	    // Main code
	    ldr x0, =output_mult	// Output string in x0
	    ldr x1, =num1			// Addr of num1
	    ldr d0, [x1]			// value at addr into d0
	    ldr x1, =num2			// Addr of num2
	    ldr d1, [x1]			// Value at addr into d1
	    fmul d2, d1, d0			// d2 = d0 * d1
	    bl printf

	    ldr x0, =output_div
	    ldr x1, =num1
	    ldr d0, [x1]
	    ldr x1, =num2
	    ldr d1, [x1]
	    fdiv d2, d0, d1			// d2 = d0 / d1
	    bl printf

	    // Cleanup
	    mov x0, #0
	    ldp x29, x30, [sp], 16
	    RET
    
    

Note the use of the d or double precision registers. The addresses are loaded into standard 64 bit registers and the d registers are loaded with the values stored at those addresses.