SPO600 - Lab 4 Part 1

This blog post is for my SPO600 class I am participating at Seneca College, this is related to Lab 4 detailed here.

In this lab we are tasked with modifying some simple loop programs, one made with AArch64 assembly language and another made with x86_64 assembly language.

The programs are as follows:

AArch64 version:

.text
.globl _start

min = 0
max = 30

_start:

    mov     x19, min

loop:
    add     x19, x19, 1
    cmp     x19, max
    b.ne    loop

    mov     x0, 0           /* status -> 0 */
    mov     x8, 93          /* exit is syscall #93 */
    svc     0               /* invoke syscall */

x86_64 GNU Assembler syntax version:

.text
.globl    _start

min = 0
max = 10

_start:
    mov     $min,%r15           /* loop index */

loop:
    inc     %r15                /* increment index */
    cmp     $max,%r15           /* see if we're done */
    jne     loop                /* loop if we're not */

    mov     $0,%rdi             /* exit status */
    mov     $60,%rax            /* syscall sys_exit */
    syscall

The first goal for the modification of these programs is simple, to begin, we are to make the loop do something! As is it stands now, these loops do nothing, and we must add logic to print a word each time the loop completes.

Loop v1

So where do I start? Conveniently on the AArch64 and x86_64 systems we are working with, there are provided "Hello World" example programs! Inside each of these programs are examples of setting up a string and executing a system call to print the string to the screen. That's helpful! Once I investigated this code it was simple to use for my modification in the loop.

AArch64 Loop v1

.text
.globl _start

min = 0
max = 10

_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 is syscall #64 */
        svc     0               /* invoke syscall */

        add     x19, x19, 1
        cmp     x19, max
        b.ne    loop

        mov     x0, 0           /* status -> 0 */
        mov     x8, 93          /* exit is syscall #93 */
        svc     0               /* invoke syscall */

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

To quickly explain what's going on here, beginning with the ".data" section there is a label "msg" pointing to the first byte of my string "Loop\n". There is also a label "len" being set to the value of the expression calculating the length of the string pointed to by "msg". Using each of these values, they are loaded into the registers responsible for arguments inside the body of the example loop.There are three arguments added into registers x0-x2 to prepare for a "write" system call.

Arg#1 into register x0, the file descriptor for stdout.

Arg#2 into register x1, the address of the beginning of the string to be printed.

Arg#3 into register x2, the length of the message in bytes.

Once that is complete all that's left to do is call "write" by setting register x8 to the integer value 64 and executing. This is done inside the body of the example loop, so this is done a total of 10 times for this example.

x86_64 Loop v1

The procedure for the x86_64 version of these modifications are identical, however the syntax and some of the values differ slightly. For example, the value for the "write" syscall in x86_64 is 1 instead of 64. Here is what the finished product looks like:

.text
.globl  _start

min = 0
max = 10

_start:
        mov     $min,%r12

loop:
        movq    $len,%rdx                       /* message length */
        movq    $msg,%rsi                       /* message location */
        movq    $1,%rdi                         /* file descriptor stdout */
        movq    $1,%rax                         /* syscall sys_write */
        syscall

        inc     %r12
        cmp     $max,%r12
        jne     loop

        movq    $0,%rdi                         /* exit status */
        movq    $60,%rax                        /* syscall sys_exit */
        syscall

.section .data

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

With the first objective complete, I have a fully functional "Loop v1" printing out a string ten times.

Loop v2

With v1 wrapped up we are provided another task, add the loop index to the output of the string. This is accomplished with some brief modifications to Loop v1. This will be explained once again in relation to the AArch64 version, and then the solution for the x86_64 version will be provided as well.

AArch64 Loop v2

.text
.globl _start

min = 0
max = 10

_start:

        mov     x19, min

loop:

// Prepare message

        add     x20, x19, 48   	/* add index to ascii '0' */
        adr     x21, msg+6		/* store address of placeholder byte */
        strb    w20, [x21]      /* overwrite placeholder byte */

// Print

        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 */

// Continue the loop

        add     x19, x19, 1
        cmp     x19, max
        b.ne    loop

// Exit

        mov     x0, 0           /* status -> 0 */
        mov     x8, 93          /* exit is syscall #93 */
        svc     0               /* invoke syscall */

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

First is modifying the string to have a placeholder for the loop index digit, the string becomes "Loop: #\n", the # being the placeholder. An important detail is that the placeholder is the 6th byte of the string. Next is converting the integer value of our current index to the accompanying ascii character. The "add" instruction is used for this purpose, adding the current index as an offset to the acsii value for the character '0'. Since the digits on the ascii table are all in order, adding the current index will simply give us the corresponding character for the current index. For example, the character '0' is represented by the integer value 48, adding a loop index of 1 will produce 49, the integer value representing the character '1'. After this addition is complete and stored in register x20, a pointer to the 6th byte in the string is created in register x21. Finally, the converted character value in x20 is stored into register x21, effectively overwriting the placeholder character in the 6th byte of the string. The loop can continue like normal and print the string to the screen.

x86_64 Loop v2

.text
.globl  _start

min = 0
max = 10

_start:
        mov     $min,%r12

loop:
        mov     %r12,%r13                       /* preserve the index value */
        add     $'0',%r13                       /* add the value of ascii '0' */
        movb    %r13b,msg+6                     /* overwrite placeholder */

        movq    $len,%rdx                       /* message length */
        movq    $msg,%rsi                       /* message location */
        movq    $1,%rdi                         /* file descriptor stdout */
        movq    $1,%rax                         /* syscall sys_write */
        syscall

        inc     %r12
        cmp     $max,%r12
        jne     loop

        movq    $0,%rdi                         /* exit status */
        movq    $60,%rax                        /* syscall sys_exit */
        syscall

.section .data

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

For this x86_64 version of Loop v2 the sequence of events are slightly different based on how the instructions function compared to their AArch64 counterparts. In this version the add instruction only takes two arguments, storing the result in the 2nd. To account for this, the current index is first stored in r13 and the value of ascii '0' is added to that. The instruction to replace the placeholder character "movb" accepts the label "msg" plus the offset of 6. This I can skip setting up a pointer to the placeholder memory location, and instead place the character directly into msg+6.

To Be Continued...

The toughest part of this lab has yet to come, there will be Loop v3 and finally Loop v4 extending the loops to 30 iterations and modifying the output yet again. These modifications I will cover in a part 2 post since this post feels cluttered enough running through Loop v1 and v2.

Comments