Lab 5 - 64-Bit Assembly Language - Aarch64

64-Bit Assembly Language

In this lab, I will be conducting experiments with assemblers on both the x86_64 and Aarch64 platforms.


Get code example

The first step is to retrieve the code example from the server. The example is located in /public/spo600-assembler-lab-examples.tgz, which is a compressed file. To extract the file and store it in the desired location, navigate to that location. For me, it's the home directory:

cd ~
tar xvf /public/spo600-assembler-lab-examples.tgz

After running the command, the files will be extracted to your current location. You will see a folder named spo600. 


Investigating C version code

We can use the above command to extract the same code example on both platform servers. Now, let's check if there are any differences between the x86_64 and Aarch64 platforms.

First, navigate to the folder containing the C version of the program and run the make command to compile it. Then, use the following command to disassemble the binary and view the assembly instructions:

objdump -d hello
x86_64 platform
Aarch64 platform
hello.c file
#include <stdio.h>

int main() {
   printf("Hello World!\n");
}
We can see that both platforms have the same number of instructions, but the instructions themselves are different. This is because each platform uses a different CPU architecture, and each architecture has its own unique instruction set.

Aarch64 platform

Next, let's explore the Aarch64 platform. There is a code example located in /spo600/examples/hello/assembler. Inside the assembler folder, there is a pre-configured Makefile. We simply need to run the make command, and it will compile the example based on the CPU architecture.

Since we are testing on the Aarch64 platform, we should navigate to the Aarch64 folder to proceed.

We can see there is an executable file named hello.
When we run the executable file, we will see it print out a String "Hello, world!"


Check the assembly language program source code by:
cat hello.s

Disassemble the binary and view the assembly instructions by:

objdump -d hello

hello.s (source code)
hello (disassembled binary file)
We can see that the instructions are the same, but the value has changed from decimal value to hexadecimal value.

Fixing a loop in Aarch64

We have source code below that contains a loop, but the loop does not perform any actions. Now, I'm trying to modify the source code to make it print something.
 .text
 .globl _start
 min = 0                          /* starting value for the loop index; **note that this is a symbol (constant)**, not a variable */
 max = 6                         /* loop exits when the index hits this number (loop condition is i<max) */
 _start:
     mov     x19, min
 loop:

     /* ... body of the loop ... do something useful here ... */

     add     x19, x19, 1     /* increment the loop counter */
     cmp     x19, max        /* see if we've hit the max */
     b.ne    loop            /* if not, then continue the loop */
     
     mov     x0, 0           /* set exit status to 0 */
     mov     x8, 93          /* exit is syscall #93 */
     svc     0               /* invoke syscall */

Print out the word "Loop" each time it loops:

.text
 .globl _start
 min = 0                          /* starting value for the loop index; **note that this is a symbol (constant)**, not a variable */
 max = 6                         /* loop exits when the index hits this number (loop condition is i<max) */
 _start:
     mov     x19, min
 loop:

     /* ... body of the loop ... do something useful here ... */
     /* ============== Start ============== */
     mov     x0, 1               // File descriptor: 1 is stdout
     adr     x1, msg             // message location (memory address)
     mov     x2, len             // message length (bytes)

     mov     x8, 64              // write is syscall #64
     svc     0                   // invoke syscall
     /* =============== End =============== */

     add     x19, x19, 1     /* increment the loop counter */
     cmp     x19, max        /* see if we've hit the max */
     b.ne    loop            /* if not, then continue the loop */

     mov     x0, 0           /* set exit status to 0 */
     mov     x8, 93          /* exit is syscall #93 */
     svc     0               /* invoke syscall */

.data
msg:     .ascii     "Loop\n"
len=     . - msg

Result:


Print out the word "Loop" with the loop number each time it loops:

.text
 .globl _start
 min = 0                          /* starting value for the loop index; **note that this is a symbol (constant)**, not a variable */
 max = 6                         /* loop exits when the index hits this number (loop condition is i<max) */
 _start:
     mov     x19, min
 loop:

     /* ... body of the loop ... do something useful here ... */
     /* ============== Start ============== */
     mov     x0, 1               // File descriptor: 1 is stdout
     adr     x1, msg             // message location (memory address)
     mov     x2, len             // message length (bytes)

     mov     x8, 64              // write is syscall #64
     svc     0                   // invoke syscall

     adr     x5, buffer          // Load buffer into x5 register

     mov     x3, x19             // copy loop count x19 to x3
     add     x3, x3, 48          // Convert number to character
     strb    w3, [x5]            // Store number character in buffer
     add     x5, x5, 1           // move to next byte in buffer
     mov     x3, 10              // 10 is newline character in ASCII
     strb    w3, [x5]            // store newline character in buffer

     mov     x0, 1               // File descriptor: 1 is stdout
     adr     x1, buffer          // buffer loaction (memory address)
     mov     x2, 2               // buffer length

     mov     x8, 64              // write is syscall #64
     svc     0                   // invoke syscall
     /* =============== End =============== */

     add     x19, x19, 1     /* increment the loop counter */
     cmp     x19, max        /* see if we've hit the max */
     b.ne    loop            /* if not, then continue the loop */

     mov     x0, 0           /* set exit status to 0 */
     mov     x8, 93          /* exit is syscall #93 */
     svc     0               /* invoke syscall */

.data
msg:     .ascii     "Loop: "
len=     . - msg
buffer:  .space     2

Result:


Print out the loop number with 2 digits until 32. For example, 0 is 00.

  .text
 .globl _start
 min = 0                          /* starting value for the loop index; **note that this is a symbol (constant)**, not a variable */
 max = 33                         /* loop exits when the index hits this number (loop condition is i<max) */
 _start:
     mov     x19, min
 loop:

     /* ... body of the loop ... do something useful here ... */
     /* ============== Start ============== */
     mov     x0, 1               // File descriptor: 1 is stdout
     adr     x1, msg             // message location (memory address)
     mov     x2, len             // message length (bytes)

     mov     x8, 64              // write is syscall #64
     svc     0                   // invoke syscall

     adr     x5, buffer          // Load buffer into x5 register

     mov     x3, x19             // copy loop count x19 to x3
     mov     x4, 10              // store #10 in x4
     udiv    x6, x3, x4          // x6 = x3(loop count) / x4(16)
     msub    x7, x6, x4, x3      // x7 = x3(loop count) - (x6 * x4)

     add     x6, x6, 48          // Convert number to character
     strb    w6, [x5]            // Store to buffer
     add     x5, x5, 1           // Move to next byte in buffer
     add     x7, x7, 48          // Convert number to character
     strb    w7, [x5]            // Store to buffer
     add     x5, x5, 1           // Move to next byte in buffer
     mov     x3, 10              // 10 is newline character in ASCII
     strb    w3, [x5]            // Store newline character in buffer

     mov     x0, 1               // File descriptor: 1 is stdout
     adr     x1, buffer          // buffer loaction (memory address)
     mov     x2, 3               // buffer length

     mov     x8, 64              // write is syscall #64
     svc     0                   // invoke syscall
     /* =============== End =============== */

     add     x19, x19, 1     /* increment the loop counter */
     cmp     x19, max        /* see if we've hit the max */
     b.ne    loop            /* if not, then continue the loop */

     mov     x0, 0           /* set exit status to 0 */
     mov     x8, 93          /* exit is syscall #93 */
     svc     0               /* invoke syscall */

.data
msg:     .ascii     "Loop: "
len=     . - msg
buffer:  .space     3

Result:


Print out the loop number with 1 digit when the number is less than 10.

  .text
 .globl _start
 min = 0                          /* starting value for the loop index; **note that this is a symbol (constant)**, not a variable */
 max = 33                         /* loop exits when the index hits this number (loop condition is i<max) */
 _start:
     mov     x19, min
 loop:

     /* ... body of the loop ... do something useful here ... */
     /* ============== Start ============== */
     mov     x0, 1               // File descriptor: 1 is stdout
     adr     x1, msg             // message location (memory address)
     mov     x2, len             // message length (bytes)

     mov     x8, 64              // write is syscall #64
     svc     0                   // invoke syscall

     adr     x5, buffer          // Load buffer into x5 register

     mov     x3, x19             // copy loop count x19 to x3
     mov     x4, 10              // store #10 in x4
     udiv    x6, x3, x4          // x6 = x3(loop count) / x4(16)
     msub    x7, x6, x4, x3      // x7 = x3(loop count) - (x6 * x4)

     cmp     x6, 0               // Check if ten digit is 0
     b.eq    units_digit         // Skip ten digit if it is 0
     add     x6, x6, 48          // Convert number to character
     strb    w6, [x5]            // Store to buffer
     add     x5, x5, 1           // Move to next byte in buffer
 units_digit:
     add     x7, x7, 48          // Convert number to character
     strb    w7, [x5]            // Store to buffer
     add     x5, x5, 1           // Move to next byte in buffer
     mov     x3, 10              // 10 is newline character in ASCII
     strb    w3, [x5]            // Store newline character in buffer

     mov     x0, 1               // File descriptor: 1 is stdout
     adr     x1, buffer          // buffer loaction (memory address)
     mov     x2, 3               // buffer length

     mov     x8, 64              // write is syscall #64
     svc     0                   // invoke syscall
     /* =============== End =============== */

     add     x19, x19, 1     /* increment the loop counter */
     cmp     x19, max        /* see if we've hit the max */
     b.ne    loop            /* if not, then continue the loop */

     mov     x0, 0           /* set exit status to 0 */
     mov     x8, 93          /* exit is syscall #93 */
     svc     0               /* invoke syscall */

.data
msg:     .ascii     "Loop: "
len=     . - msg
buffer:  .space     3

Result:


Print out the loop number in hexadecimal instead of decimal number.

  .text
 .globl _start
 min = 0                          /* starting value for the loop index; **note that this is a symbol (constant)**, not a variable */
 max = 33                         /* loop exits when the index hits this number (loop condition is i<max) */
 _start:
     mov     x19, min
 loop:

     /* ... body of the loop ... do something useful here ... */
     /* ============== Start ============== */
     mov     x0, 1               // File descriptor: 1 is stdout
     adr     x1, msg             // message location (memory address)
     mov     x2, len             // message length (bytes)

     mov     x8, 64              // write is syscall #64
     svc     0                   // invoke syscall

     adr     x5, buffer          // Load buffer into x5 register

     mov     x3, x19             // copy loop count x19 to x3
     mov     x4, 16              // store #16 in x4
     udiv    x6, x3, x4          // x6 = x3(loop count) / x4(16)
     msub    x7, x6, x4, x3      // x7 = x3(loop count) - (x6 * x4)

     cmp     x6, 0                // Check if ten digit is 0
     b.eq    units_digit          // Skip ten digit if it is 0
     add     x6, x6, 48           // Convert number to character
     strb    w6, [x5]             // Store to buffer
     add     x5, x5, 1            // Move to next byte in buffer
 units_digit:
     add     x7, x7, 48           // Convert number to character
     cmp     x7, 58               // Compare if the character code is greater than 59. 0-9 is ASCII value 48-57
     b.lt    st_unit              // Go to store unit st_unit if it is less than 58.
     add     x7, x7, 7            // If it is greater than 58, add 7 to convert to A-E
 st_unit:
     strb    w7, [x5]             // Store to buffer
     add     x5, x5, 1            // Move to next byte in buffer
     mov     x3, 10               // 10 is newline character in ASCII
     strb    w3, [x5]             // Store newline character in buffer

     mov     x0, 1                // File descriptor: 1 is stdout
     adr     x1, buffer           // buffer loaction (memory address)
     mov     x2, 3                // buffer length

     mov     x8, 64               // write is syscall #64
     svc     0                    // invoke syscall
     /* =============== End =============== */

     add     x19, x19, 1     /* increment the loop counter */
     cmp     x19, max        /* see if we've hit the max */
     b.ne    loop            /* if not, then continue the loop */

     mov     x0, 0           /* set exit status to 0 */
     mov     x8, 93          /* exit is syscall #93 */
     svc     0               /* invoke syscall */

.data
msg:     .ascii     "Loop: "
len=     . - msg
buffer:  .space     3

Result:



Comments

Popular posts from this blog

Project - Stage 1: Create a Basic GCC Pass

Lab 4 - GCC Build