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..
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
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:
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
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:.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)
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:
// 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 characterLoop 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:
.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
.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
Comments
Post a Comment