lab04
This is the blog for SPO600 lab 4. In this lab, we mainly focus on writing the loop to output some things like the number in 6502, x86_64, aarch64 assembler. This is quite new to me since nowadays we use modern programming language like Python, C and Javascript. But for this lab, we will go into the assembler language to see how the programs work.
This lab has two part, one is class part, which means that we will be seperated into group and there was a driver to share his screen and watchers would watch him to code. The program that out group has generated in the class was:
.text
.globl _start
min = 0 /* starting value of the loop idx. this is a symbol */
max = 10 /* ending value of the loop idx. this is a symbol */
_start:
mov x19, min /* store min to x19, which will be our idx register */
loop:
mov x0, 1 /* file descriptor: 1 is stdout */
adr x1, msg /* message location (memory address) */
mov x2, len /* message length (bytes) */
mov x4, x19 /* copy idx(x19) to x4 */
add x4, x4, 48 /* add 48 to make it ASCII */
strb w4, [x1, 6] /* store 1 byte into the memory loc of msg + 6 */
mov x8, 64 /* write is syscall #64 */
svc 0 /* invoke syscall */
add x19, x19, 1 /* add 1 to idx(x19) */
cmp x19, max /* compare x19 to max */
b.ne loop /* branch if not equal to loop */
mov x0, 0 /* status -> 0 */
mov x8, 93 /* exit is syscall #93 */
svc 0 /* invoke syscall */
.data
msg: .ascii "Loop: 0\n"
len= . - msg
These codes will generate a loop to output the number from 0-9. And that is all the things that our group has researcher in the class due to time limit. But the lab hasm;t been finished.
The next task in the lab is Extend the AArch64 code to loop from 00-30, printing each value as a 2-digit decimal number. And I tried to generate this program after class. The code:
.text
.globl _start
min = 0 /* starting value of the loop index */
max = 31 /* ending value (loop until 30) */
_start:
mov x19, min /* store min to x19 (loop index register) */
loop:
/* Calculate quotient (first digit) and remainder (second digit) */
mov x0, x19 /* copy index to x0 */
mov x1, 10 /* divisor: 10 */
udiv x2, x0, x1 /* x2 = quotient (first digit) */
msub x3, x2, x1, x0 /* x3 = remainder (second digit), msub: x3 = x0 - (x2 * x1) */
/* Convert quotient and remainder to ASCII */
add x2, x2, 48 /* add 48 to quotient to convert to ASCII */
add x3, x3, 48 /* add 48 to remainder to convert to ASCII */
/* Store the digits in the message */
adr x1, msg /* address of the message */
strb w2, [x1, 6] /* store first digit (quotient) at msg + 6 */
strb w3, [x1, 7] /* store second digit (remainder) at msg + 7 */
/* Write the message to stdout */
mov x0, 1 /* file descriptor: 1 is stdout */
mov x2, len /* message length */
mov x8, 64 /* write is syscall #64 */
svc 0 /* invoke syscall */
/* Increment loop index */
add x19, x19, 1 /* add 1 to index */
cmp x19, max /* compare index to max (31) */
b.ne loop /* if not equal, continue looping */
/* Exit the program */
mov x0, 0 /* status -> 0 */
mov x8, 93 /* exit syscall number 93 */
svc 0 /* invoke syscall */
.data
msg: .ascii "Loop: 00\n" /* message template: 00 will be replaced by digits */
len = . - msg /* message length */
This update code will generate the loop from 00 to 30. The new part in this the code is the mov x0, x19 /* copy index to x0 */
mov x1, 10 /* divisor: 10 */
udiv x2, x0, x1 /* x2 = quotient (first digit) */
msub x3, x2, x1, x0 /* x3 = remainder (second digit), msub: x3 = x0 - (x2 * x1) */.
This part of code basically take a number stored in x19 and divided it by 10, and then we store the result in x2, this is the first digit. The reminder is stored in the x3. And we will display the first and second digit
The next task is Change the code as needed to suppress the leading zero (printing 0-30 instead of 00-30). To do this, you'll need to add a conditional into your code (the equivalent of an “if” statement, implemented as a compare followed by a conditional branch or conditional jump).
My code is
.text
.globl _start
min = 0 /* starting value of the loop index */
max = 31 /* ending value (loop until 30) */
_start:
mov x19, min /* store min to x19 (loop index register) */
loop:
/* Calculate quotient (first digit) and remainder (second digit) */
mov x0, x19 /* copy index to x0 */
mov x1, 10 /* divisor: 10 */
udiv x2, x0, x1 /* x2 = quotient (first digit) */
msub x3, x2, x1, x0 /* x3 = remainder (second digit), msub: x3 = x0 - (x2 * x1) */
/* Convert quotient and remainder to ASCII */
add x2, x2, 48 /* add 48 to quotient to convert to ASCII */
add x3, x3, 48 /* add 48 to remainder to convert to ASCII */
/* Check if the number is less than 10 */
cmp x2, 48 /* compare first digit with '0' */
b.ne two_digits /* if not equal, print two digits */
/* For single digit numbers */
adr x1, msg /* address of the message */
strb w3, [x1, 6] /* store single digit at msg + 6 */
mov x4, ' ' /* space character */
strb w4, [x1, 7] /* store space at msg + 7 */
b print /* branch to print */
two_digits:
/* For two digit numbers */
adr x1, msg /* address of the message */
strb w2, [x1, 6] /* store first digit at msg + 6 */
strb w3, [x1, 7] /* store second digit at msg + 7 */
print:
/* Write the message to stdout */
mov x0, 1 /* file descriptor: 1 is stdout */
mov x2, len /* message length (always 9 now) */
mov x8, 64 /* write is syscall #64 */
svc 0 /* invoke syscall */
/* Increment loop index */
add x19, x19, 1 /* add 1 to index */
cmp x19, max /* compare index to max (31) */
b.ne loop /* if not equal, continue looping */
/* Exit the program */
mov x0, 0 /* status -> 0 */
mov x8, 93 /* exit syscall number 93 */
svc 0 /* invoke syscall */
.data
msg: .ascii "Loop: 00\n" /* message template: 00 will be replaced by digits */
len = . - msg /* message length */
The update part for this code is /* Check if the number is less than 10 */
cmp x2, 48 /* compare first digit with '0' */
b.ne two_digits /* if not equal, print two digits */
/* For single digit numbers */
adr x1, msg /* address of the message */
strb w3, [x1, 6] /* store single digit at msg + 6 */
mov x4, ' ' /* space character */
strb w4, [x1, 7] /* store space at msg + 7 */
b print /* branch to print */
two_digits:
/* For two digit numbers */
adr x1, msg /* address of the message */
strb w2, [x1, 6] /* store first digit at msg + 6 */
strb w3, [x1, 7] /* store second digit at msg + 7 */.
This will check if the number that we are going to output is single number or not, if this is the single number it will not print the 0 as the first digit. If it detect that the number is second digit, it will directly jump to two_digits store the two digit for output.
The last part for this lab is that we need to repeat the whole process in x86_64. For this part I have chosen nasm to finish the job. The code for outputing the loop from 0 to 30 is
section .text
global _start
_start:
xor r12, r12 ; Initialize loop counter
loop:
; Print "Loop: "
mov rax, 1 ; sys_write
mov rdi, 1 ; stdout
mov rsi, prefix
mov rdx, 6 ; length of "Loop: "
syscall
; Convert the number to ASCII
mov rax, r12
mov rsi, buffer
call int_to_string
; Print the number
mov rax, 1 ; sys_write
mov rdi, 1 ; stdout
mov rsi, buffer
mov rdx, 3 ; Max 3 digits (including null terminator)
syscall
; Print newline
mov rax, 1 ; sys_write
mov rdi, 1 ; stdout
mov rsi, newline
mov rdx, 1 ; length of newline
syscall
; Increment and continue loop
inc r12
cmp r12, 31
jl loop
; Exit
mov rax, 60 ; sys_exit
xor rdi, rdi ; status 0
syscall
int_to_string:
add rsi, 2 ; Point to end of buffer
mov byte [rsi], 0 ; Null terminator
mov rbx, 10 ; Divisor
.divide_loop:
xor rdx, rdx ; Clear RDX for division
div rbx ; Divide RAX by 10
add dl, '0' ; Convert remainder to ASCII
dec rsi ; Move back in buffer
mov [rsi], dl ; Store digit
test rax, rax ; Check if quotient is zero
jnz .divide_loop ; If not, continue loop
ret
section .data
prefix db 'Loop: '
newline db 10 ; Newline character
section .bss
buffer resb 4 ; Buffer for number conversion (3 digits + null terminator)
Baed on my experience, the main difference between x86_64 and aarch64 are the instruction sets. For x86_64 it typically use Intel syntax in NASM, but for aarch64, it use the ARM's syntax and x86 use condition code and conditional jump instructions. But for aarch64, it can conditionally execute more instructions using prediction. That is the things I have discover for the difference between aarch64 and x86. I don't have specific preference for these two architectures. I found both of them are really complex and inconvenient to use for daily program.
评论
发表评论