SPO600 - Lab 4 Part 2

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

This post is a follow-up to the first part of this lab covered here. The first part covered giving an assembly language loop something to print each loop, and then adding the loop index to the output. The task involved implementing this for both AArch64 and x86_64 assembly languages.

Loop v3

The next task for this lab includes extending the loops from 10 iterations to 31 total, printing values 00 to 30. This is slightly more complicated than simply adding 0-9 to the value of an ascii character and inserting it into the string to write. Dealing with two digits to print means converting each digit separately and inserting each byte individually into the string. The solution to this is dividing the loop index by ten and converting the quotient and remainder into the first and second digits, respectively.

AArch64 Loop v3

.text
.globl _start

min = 0
max = 31

_start:

        mov     x19, min

loop:

// Prepare message

        mov     x22, 10         /* store int value 10 into x22 */
        udiv    x23, x19, x22   /* calculate both digits of the index */
        msub    x24, x23, x22, x19

        add     x20, x23, 48    /* convert first digit */
        adr     x21, msg+6      /* set first digit placeholder address */
        strb    w20, [x21]      /* overwrite first digit placehokder */

        add     x20, x24, 48    /* convert 2nd digit */
        adr     x21, msg+7      /* set 2nd digit placeholder address */
        strb    w20, [x21]      /* overwrite 2nd digit placehokder */

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

The process for the AArch64 version of Loop v3 begins with storing the value 10 into register x22. Using that register, the loop index is divided by 10 and the quotient is stored into register x23. In order to calculate the remainder, an "msub" instruction is used with the quotient (x23), the divisor (x22 = 10), and the dividend (x19 = index) storing the result into register x24. Once this is complete both digits are available to use, and all that's left to do is convert to the ascii values and insert each into bytes 6 and 7 in the string.

x86_64 Loop v3

.text
.globl  _start

min = 0
max = 31

_start:
        mov     $min,%r12

loop:

        mov     %r12,%rax                       /* prepare div args */
        mov     $10,%r13
        mov     $0,%rdx

        div     %r13                            /* divide loop index by 10 */
        add     $'0',%rax                       /* convert quotient into char */
        add     $'0',%rdx                       /* convert remainder into char */

        movb    %al,msg+6                       /* overwrite first digit placeholder */
        movb    %dl,msg+7                       /* overwrite 2nd digit 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

The x86_64 version of Loop v3 is made slightly simpler due to the way the "div" instruction works on this architecture. For x86_64, div provides both quotient and remainder in one instruction. To set up for div, the loop index is stored into register "rax", register r13 is loaded with the number 10, and register rdx is loaded with the number 0 (this is simply a requirement for the div instruction). div is called with register r13 as the argument. Register rax is divided by r13, the quotient is stored into rax, and the remainder is stored into rdx. Once that is complete, once again, the rest is simply converting to the proper ascii characters and inserting into the string.

Loop v4 - The Finale

The next and final task is to simply remove the leading '0' from the output that Loop v3 produces while the loop index is still in the single digits. Luckily, the solution is remarkably simple and universal between the two architectures. The solution is comparing the converted quotient with the ascii value 48 or '0', if equal branching past inserting that digit, and making sure the placeholders for the two digits are now initially whitespace. If the character for the first digit is equal to '0' the branch will jump ahead to inserting the second digit, and the 6th byte in the string will remain empty, producing single digit output for the 0-9 loops.

AArch64 Loop v4

.text
.globl _start

min = 0
max = 31

_start:

        mov     x19, min

loop:

// Prepare message

        mov     x22, 10         /* store int value 10 into x22 */
        udiv    x23, x19, x22   /* calculate both digits of the index */
        msub    x24, x23, x22, x19

        add     x20, x23, 48    /* convert first digit */
        cmp     x20, 48         /* check 1st digit == '0' */
        b.eq    digit2			/* skip writing 1st digit if '0' */

        adr     x21, msg+6      /* set first digit placeholder address */
        strb    w20, [x21]      /* overwrite first digit placehokder */

digit2: add     x20, x24, 48    /* convert 2nd digit */
        adr     x21, msg+7      /* set 2nd digit placeholder address */
        strb    w20, [x21]      /* overwrite 2nd digit placehokder */

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

X86_64 Loop v4

.text
.globl  _start

min = 0
max = 31

_start:
        mov     $min,%r12

loop:

        mov     %r12,%rax                       /* prepare div args */
        mov     $10,%r13
        mov     $0,%rdx

        div     %r13                            /* divide loop index by 10 */
        add     $'0',%rax                       /* convert quotient into char */
        add     $'0',%rdx                       /* convert remainder into char */

        cmp     $'0',%rax                       /* check 1st digit == '0' */
        je      digit2                          /* skip writing 1st digit if '0' */
        movb    %al,msg+6                       /* overwrite first digit placeholder */

digit2: movb    %dl,msg+7                       /* overwrite 2nd digit 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

Comments