Lab05 - aarch64

Objective: 

In this lab, I worked with 64-bit assembly language on Aarch64 platform to learn about CPU architecture and assembly instructions.

Getting Started with Code Example 

The first step is to retrieve the code example from the server. The file is compressed, so we'll extract it into the desired location (in my case, the home directory):

cd ~

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

Once extracted, you’ll see a folder named spo600, which contains all the necessary files..

AArch64:


Investigating the C Code

To explore the C version of the program, navigate to the folder containing the hello.c file and compile it using make:

make

AArch64:


 Disassemble the binary to view the assembly code:

objdump -d hello

AArch64: 

This will show us the assembly instructions, where we can observe that different CPU architectures (x86_64 vs Aarch64) generate different instructions, even for the same program.

Exploring the Aarch64 Platform

Now, let’s dive into the Aarch64-specific example:

  • Navigate to /spo600/examples/hello/assembler and compile it using make.
  • After compilation, you’ll have an executable file named hello.
  • Running it will print "Hello, World!" to the screen.

To see the assembly instructions, use the following: 

objdump -d hello

Fixing a Loop in Aarch64

We were provided with a loop in Aarch64, but it wasn’t doing anything. The task was to modify it to print something each time it loops.

Here's a simple version of the loop that prints "Loop" each time it runs:

.text

 .globl _start

min = 0

max = 6

_start:

    mov     x19, min

loop:

    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 syscall #64

    svc     0                   // Invoke syscall

    add     x19, x19, 1         // Increment the loop counter

    cmp     x19, max            // Check if max is reached

    b.ne    loop                // If not, continue the loop

    mov     x0, 0               // Set exit status to 0

    mov     x8, 93              // Exit syscall #93

    svc     0                   // Invoke syscall


.data

msg:     .ascii     "Loop\n"

len=     . - msg

This code prints "Loop" for each iteration of the loop. The challenge was getting the correct registers and system calls set up, as well as managing memory and buffers for the printed text.

Loop with Loop Number

We can further enhance the loop to print the loop number alongside "Loop". Here’s an updated version:

.text

.globl _start

min = 0                      // Loop starting value

end_value = 6                // Loop ending value


_start:

    mov     x20, min         // Set loop counter to min (0)


loop:

    // Print the "Loop: " message

    mov     x0, 1            // File descriptor 1 (stdout)

    adr     x1, message      // Message location (memory address)

    mov     x2, message_len  // Message length

    mov     x8, 64           // Syscall number for write

    svc     0                // Invoke syscall to print "Loop: "


    // Copy loop counter to buffer

    adr     x21, output_buf  // Load buffer address into x21

    mov     x22, x20         // Copy loop counter to x22

    add     x22, x22, 48     // Convert loop counter to ASCII ('0' is 48)

    strb    w22, [x21]       // Store ASCII character in buffer

    add     x21, x21, 1      // Move to next byte in buffer


    // Add newline character to buffer

    mov     x22, 10          // Newline character (ASCII 10)

    strb    w22, [x21]       // Store newline character in buffer


    // Write buffer to stdout

    mov     x0, 1            // File descriptor 1 (stdout)

    adr     x1, output_buf   // Buffer location

    mov     x2, 2            // Buffer length (2 bytes: ASCII + newline)

    mov     x8, 64           // Syscall number for write

    svc     0                // Invoke syscall to print loop count and newline


    // Increment loop counter and check if end_value is reached

    add     x20, x20, 1      // Increment loop counter

    cmp     x20, end_value   // Compare loop counter with end_value

    b.ne    loop             // If not equal, continue the loop


    // Exit program

    mov     x0, 0            // Exit status 0

    mov     x8, 93           // Syscall number for exit

    svc     0                // Invoke syscall to exit program


.data

message:     .ascii "Loop: "   // Message to print before the counter

message_len = . - message     // Calculate message length

output_buf:  .space 2          // Allocate space for 2-byte buffer (1 byte for counter, 1 byte for newline)

Result:
    

Loop with 2-digit Numbers (Up to 32)

.text

.globl _start

min = 0                      // Loop starting value
max = 33                     // Loop ending value

_start:
    mov     x20, min         // Set loop counter to min (0)

loop:

// Print the "Loop: " message mov x0, 1 // File descriptor 1 (stdout) adr x1, message // Message location (memory address) mov x2, message_len // Message length mov x8, 64 // Syscall number for write svc 0 // Invoke syscall to print "Loop: " // Copy loop counter to buffer adr x21, output_buf // Load buffer address into x21 mov x22, x20 // Copy loop counter to x22 add x22, x22, 48 // Convert loop counter to ASCII ('0' is 48) strb w22, [x21] // Store ASCII character in buffer add x21, x21, 1 // Move to next byte in buffer // Add newline character to buffer mov x22, 10 // Newline character (ASCII 10) strb w22, [x21] // Store newline character in buffer // Write buffer to stdout mov x0, 1 // File descriptor 1 (stdout) adr x1, output_buf // Buffer location mov x2, 2 // Buffer length (2 bytes: ASCII + newline) mov x8, 64 // Syscall number for write svc 0 // Invoke syscall to print loop count and newline // Increment loop counter and check if max is reached add x20, x20, 1 // Increment loop counter cmp x20, max // Compare loop counter with max b.ge done // If equal or greater, exit loop // Perform the additional conversion and store in buffer mov x3, x20 // Copy loop count to x3 for conversion mov x4, 10 // Store 10 in x4 (for decimal division) udiv x6, x3, x4 // x6 = x3 (loop count) / x4 (10) (tens place) msub x7, x6, x4, x3 // x7 = x3 (loop count) - (x6 * x4) (ones place) // Convert tens place to ASCII and store in buffer add x6, x6, 48 // Convert tens place to ASCII ('0' is 48) adr x5, buffer // Load buffer address into x5 strb w6, [x5] // Store tens place to buffer add x5, x5, 1 // Move to next byte in buffer // Convert ones place to ASCII and store in buffer add x7, x7, 48 // Convert ones place to ASCII ('0' is 48) strb w7, [x5] // Store ones place to buffer add x5, x5, 1 // Move to next byte in buffer // Add newline character to buffer mov x3, 10 // Newline character in ASCII strb w3, [x5] // Store newline character in buffer // Write buffer to stdout mov x0, 1 // File descriptor 1 (stdout) adr x1, buffer // Buffer location mov x2, 3 // Buffer length (2 digits + newline) mov x8, 64 // Syscall number for write svc 0 // Invoke syscall to print loop count and newline // Loop again b loop // Continue the loop done: // Exit program mov x0, 0 // Exit status 0 mov x8, 93 // Syscall number for exit svc 0 // Invoke syscall to exit program .data message: .ascii "Loop: " // Message to print before the counter message_len = . - message // Calculate message length output_buf: .space 2 // Allocate space for 2-byte buffer (1 byte for counter, 1 byte for newline) buffer: .space 3 // Allocate space for 2 digits + newline character
Result:

Loop with 2-digit Numbers (Up to 32) Suppressing the Leading 0s

.text


.globl _start


min = 0                      // Loop starting value

max = 33                     // Loop ending value


_start:

    mov     x20, min         // Set loop counter to min (0)


loop:


    // Print the "Loop: " message

    mov     x0, 1            // File descriptor 1 (stdout)

    adr     x1, message      // Message location (memory address)

    mov     x2, message_len  // Message length

    mov     x8, 64           // Syscall number for write

    svc     0                // Invoke syscall to print "Loop: "


    // Clear the buffer before each use

    adr     x21, output_buf  // Load buffer address into x21

    mov     x22, 0           // Clear the register for the buffer

    strb    w22, [x21]       // Clear first byte of buffer

    add     x21, x21, 1      // Move to next byte

    strb    w22, [x21]       // Clear second byte of buffer


    // Handle the loop counter (x20)

    mov     x22, x20         // Copy loop counter to x22

    cmp     x22, 10          // Check if counter is less than 10

    blt     single_digit     // If less, handle single-digit case


    // Handle multi-digit (10 and above) case

    mov     x3, x20          // Copy loop count to x3 for conversion

    mov     x4, 10           // Store 10 in x4 (for decimal division)

    udiv    x6, x3, x4       // x6 = x3 (loop count) / x4 (10) (tens place)

    msub    x7, x6, x4, x3   // x7 = x3 (loop count) - (x6 * x4) (ones place)


    // Convert tens place to ASCII and store in buffer

    add     x6, x6, 48       // Convert tens place to ASCII ('0' is 48)

    adr     x5, buffer       // Load buffer address into x5

    strb    w6, [x5]         // Store tens place to buffer

    add     x5, x5, 1        // Move to next byte in buffer


    // Convert ones place to ASCII and store in buffer

    add     x7, x7, 48       // Convert ones place to ASCII ('0' is 48)

    strb    w7, [x5]         // Store ones place to buffer

    add     x5, x5, 1        // Move to next byte in buffer


    // Jump to buffer writing

    b       write_buffer


single_digit:

    // Handle single-digit numbers (0-9)

    add     x22, x20, 48     // Convert loop counter to ASCII ('0' is 48)

    adr     x5, buffer       // Load buffer address into x5

    strb    w22, [x5]        // Store the single digit to buffer

    add     x5, x5, 1        // Move to next byte in buffer


write_buffer:

    // Add newline character to buffer

    mov     x22, 10          // Newline character (ASCII 10)

    strb    w22, [x5]        // Store newline character in buffer


    // Write buffer to stdout

    mov     x0, 1            // File descriptor 1 (stdout)

    adr     x1, buffer       // Buffer location

    mov     x2, 3            // Buffer length (2 digits + newline, can vary)

    mov     x8, 64           // Syscall number for write

    svc     0                // Invoke syscall to print loop count and newline


    // Increment loop counter and check if max is reached

    add     x20, x20, 1      // Increment loop counter

    cmp     x20, max         // Compare loop counter with max

    b.ge    done             // If equal or greater, exit loop


    // Loop again

    b       loop             // Continue the loop


done:

    // Exit program

    mov     x0, 0            // Exit status 0

    mov     x8, 93           // Syscall number for exit

    svc     0                // Invoke syscall to exit program


.data

message:     .ascii "Loop: "   // Message to print before the counter

message_len = . - message     // Calculate message length

output_buf:  .space 2          // Allocate space for 2-byte buffer (1 byte for counter, 1 byte for newline)

buffer:      .space 3          // Allocate space for 2 digits + newline character


Result:

Output in hexadecimal (0-20)

.text .globl _start min = 0 // Loop starting value max = 33 // Loop ending value _start: mov x20, min // Set loop counter to min (0) loop: // Print the "Loop: " message mov x0, 1 // File descriptor 1 (stdout) adr x1, message // Message location (memory address) mov x2, message_len // Message length mov x8, 64 // Syscall number for write svc 0 // Invoke syscall to print "Loop: " // Clear the buffer before each use adr x21, output_buf // Load buffer address into x21 mov x22, 0 // Clear the register for the buffer strb w22, [x21] // Clear first byte of buffer add x21, x21, 1 // Move to next byte strb w22, [x21] // Clear second byte of buffer // Handle the loop counter (x20) in hexadecimal mov x22, x20 // Copy loop counter to x22 mov x3, 16 // Set base to 16 (for hexadecimal conversion) // Get the first hex digit (most significant) udiv x6, x22, x3 // x6 = x22 / 16 (tens place for hexadecimal) msub x7, x6, x3, x22 // x7 = x22 - (x6 * 16) (ones place for hexadecimal) // Convert the first hex digit (tens place) cmp x6, 9 // Check if the first digit is less than 10 ble first_digit // If less, it's a number add x6, x6, 87 // Convert to ASCII for 'A' to 'F' (A = 65, offset = 10) b store_first first_digit: add x6, x6, 48 // Convert to ASCII for '0' to '9' store_first: adr x5, buffer // Load buffer address into x5 strb w6, [x5] // Store the first hex digit to buffer add x5, x5, 1 // Move to next byte in buffer // Convert the second hex digit (ones place) cmp x7, 9 // Check if the second digit is less than 10 ble second_digit // If less, it's a number add x7, x7, 87 // Convert to ASCII for 'A' to 'F' b store_second second_digit: add x7, x7, 48 // Convert to ASCII for '0' to '9' store_second: strb w7, [x5] // Store the second hex digit to buffer add x5, x5, 1 // Move to next byte in buffer // Add newline character to buffer mov x22, 10 // Newline character (ASCII 10) strb w22, [x5] // Store newline character in buffer // Write buffer to stdout mov x0, 1 // File descriptor 1 (stdout) adr x1, buffer // Buffer location mov x2, 3 // Buffer length (2 hex digits + newline) mov x8, 64 // Syscall number for write svc 0 // Invoke syscall to print loop count and newline // Increment loop counter and check if max is reached add x20, x20, 1 // Increment loop counter cmp x20, max // Compare loop counter with max b.ge done // If equal or greater, exit loop // Loop again b loop // Continue the loop done: // Exit program mov x0, 0 // Exit status 0 mov x8, 93 // Syscall number for exit svc 0 // Invoke syscall to exit program .data message: .ascii "Loop: " // Message to print before the counter message_len = . - message // Calculate message length output_buf: .space 2 // Allocate space for 2-byte buffer (1 byte for counter, 1 byte for newline) buffer: .space 3 // Allocate space for 2 hex digits + newline character



Result:


Comments

Popular posts from this blog

Project Stage 1

Lab05 - x86_64

Lab 03 - 6502 Program Lab (Revised)