Lab 5 - 64-Bit Assembly Language - x86_64

 64-Bit Assembly Language

In this post, I will be conducting the same experiments I did in the aarch64 platform, but I will perform them on the x86_64 platform.

Get code example

The first step is to retrieve the code example from the server. The example is located in /public/spo600-assembler-lab-examples.tgz, which is a compressed file. To extract the file and store it in the desired location, navigate to that location. For me, it's the home directory:

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

After running the command, the files will be extracted to your current location. You will see a folder named spo600.


x86_64 platform

Same as what I did inAarch64, we have source code below that contains a loop, but the loop does not perform any actions. Now, I'm trying to modify the source code to make it print something.

.text
 .globl    _start
 min = 0                         /* starting value for the loop index; **note that this is a symbol (constant)**, not a variable */
 max = 5                        /* loop exits when the index hits this number (loop condition is i<max) */
 _start:
     mov     $min,%r15           /* loop index */
 loop:
     /* ... body of the loop ... do something useful here ... */
     inc     %r15                /* increment the loop index */
     cmp     $max,%r15           /* see if we've hit the max */
     jne     loop                /* if not, then continue the loop */
     
     mov     $0,%rdi             /* set exit status to 0 */
     mov     $60,%rax            /* exit is syscall #60  */
     syscall                     /* invoke syscall */

Print out the word "Loop" each time it loops:

.text
 .globl    _start
 min = 0                         /* starting value for the loop index; **note that this is a symbol (constant)**, not a variable */
 max = 5                        /* loop exits when the index hits this number (loop condition is i<max) */
 _start:
     mov     $min,%r15           /* loop index */
 loop:
     /* ... body of the loop ... do something useful here ... */
     /* ============== Start ============== */
     mov     $len,%rdx           /* message length */
     mov     $msg,%rsi           /* message location */
     mov     $1,%rdi             /* file descriptor stdout */
     mov     $1,%rax             /* syscall sys_write */
     syscall
     /* =============== End =============== */
     inc     %r15                /* increment the loop index */
     cmp     $max,%r15           /* see if we've hit the max */
     jne     loop                /* if not, then continue the loop */

     mov     $0,%rdi             /* set exit status to 0 */
     mov     $60,%rax            /* exit is syscall #60  */
     syscall                     /* invoke syscall */

.section .data

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

Result:


Print out the word "Loop" with the loop number each time it loops:

.text
 .globl    _start
 min = 0                         /* starting value for the loop index; **note that this is a symbol (constant)**, not a variable */
 max = 6                        /* loop exits when the index hits this number (loop condition is i<max) */
 _start:
     mov     $min,%r15           /* loop index */
 loop:
     /* ... body of the loop ... do something useful here ... */
     /* ============== Start ============== */
     mov     $len,%rdx           /* message length */
     mov     $msg,%rsi           /* message location */
     mov     $1,%rdi             /* file descriptor stdout */
     mov     $1,%rax             /* syscall sys_write */
     syscall

     lea     buffer(%rip),%r9    /* load the buffer addres to r9 */

     mov     %r15,%r8            /* store r15(loop count) to r8 register */
     add     $48,%r8             /* convert number to character */
     movb    %r8b,(%r9)          /* store character into buffer */
     add     $1,%r9              /* move to next byte in buffer */
     movb    $10,(%r9)           /* 10 in ASCII is newline character. Store newline character to buffer */

     mov     $2,%rdx             /* message length */
     mov     $buffer,%rsi           /* message location */
     mov     $1,%rdi             /* file descriptor stdout */
     mov     $1,%rax             /* syscall sys_write */
     syscall
     /* =============== End =============== */
     inc     %r15                /* increment the loop index */
     cmp     $max,%r15           /* see if we've hit the max */
     jne     loop                /* if not, then continue the loop */

     mov     $0,%rdi             /* set exit status to 0 */
     mov     $60,%rax            /* exit is syscall #60  */
     syscall                     /* invoke syscall */

.section .data

msg:    .ascii    "Loop: "
len=    .-msg
buffer: .space    2

Result:


Print out the loop number with 2 digits until 32. For example, 0 is 00.

.text
 .globl    _start
 min = 0                         /* starting value for the loop index; **note that this is a symbol (constant)**, not a variable */
 max = 33                        /* loop exits when the index hits this number (loop condition is i<max) */
 _start:
     mov     $min,%r15           /* loop index */
 loop:
     /* ... body of the loop ... do something useful here ... */
     /* ============== Start ============== */
     mov     $len,%rdx           /* message length */
     mov     $msg,%rsi           /* message location */
     mov     $1,%rdi             /* file descriptor stdout */
     mov     $1,%rax             /* syscall sys_write */
     syscall

     lea     buffer(%rip),%r9    /* load the buffer address to r9 */

     mov     %r15,%rax           /* store dividend to rax */
     xor     %rdx,%rdx           /* clear rdx value */
     mov     $10,%r11            /* store dividor to r11 */
     div     %r11                /* perform unsigned division */
     mov     %rax,%r8            /* store result to r8 */
     mov     %rdx,%r10           /* store remainder to r10 */

     add     $48,%r8             /* convert number to character */
     movb    %r8b,(%r9)          /* store character into buffer */
     add     $1,%r9              /* move to next byte in buffer */

     add     $48,%r10            /* convert number to character */
     movb    %r10b,(%r9)         /* store character into buffer */
     add     $1,%r9              /* move to next byte in buffer */

     movb    $10,(%r9)           /* 10 in ASCII is newline character. Store newline character to buffer */

     mov     $3,%rdx             /* message length */
     mov     $buffer,%rsi        /* message location */
     mov     $1,%rdi             /* file descriptor stdout */
     mov     $1,%rax             /* syscall sys_write */
     syscall
     /* =============== End =============== */
     inc     %r15                /* increment the loop index */
     cmp     $max,%r15           /* see if we've hit the max */
     jne     loop                /* if not, then continue the loop */

     mov     $0,%rdi             /* set exit status to 0 */
     mov     $60,%rax            /* exit is syscall #60  */
     syscall                     /* invoke syscall */

.section .data

msg:    .ascii    "Loop: "
len=    .-msg
buffer: .space    3

Result:



Print out the loop number with 1 digit when the number is less than 10.

.text
 .globl    _start
 min = 0                         /* starting value for the loop index; **note that this is a symbol (constant)**, not a variable */
 max = 33                        /* loop exits when the index hits this number (loop condition is i<max) */
 _start:
     mov     $min,%r15           /* loop index */
 loop:
     /* ... body of the loop ... do something useful here ... */
     /* ============== Start ============== */
     mov     $len,%rdx           /* message length */
     mov     $msg,%rsi           /* message location */
     mov     $1,%rdi             /* file descriptor stdout */
     mov     $1,%rax             /* syscall sys_write */
     syscall

     lea     buffer(%rip),%r9    /* load the buffer address to r9 */

     mov     %r15,%rax           /* store dividend to rax */
     xor     %rdx,%rdx           /* clear rdx value */
     mov     $10,%r11            /* store dividor to r11 */
     div     %r11                /* perform unsigned division */
     mov     %rax,%r8            /* store result (ten) to r8 */
     mov     %rdx,%r10           /* store remainder (unit) to r10 */

     cmp     $0,%r8              /* compare ten digit if it is 0 */
     je      units_digit         /* if it is 0, skip storing it and go to the unit digit */
     add     $48,%r8             /* convert number to character */
     movb    %r8b,(%r9)          /* store character into buffer */
     add     $1,%r9              /* move to next byte in buffer */
 units_digit:
     add     $48,%r10            /* convert number to character */
     movb    %r10b,(%r9)         /* store character into buffer */
     add     $1,%r9              /* move to next byte in buffer */

     movb    $10,(%r9)           /* 10 in ASCII is newline character. Store newline character to buffer */

     mov     $3,%rdx             /* message length */
     mov     $buffer,%rsi           /* message location */
     mov     $1,%rdi             /* file descriptor stdout */
     mov     $1,%rax             /* syscall sys_write */
     syscall
     /* =============== End =============== */
     inc     %r15                /* increment the loop index */
     cmp     $max,%r15           /* see if we've hit the max */
     jne     loop                /* if not, then continue the loop */

     mov     $0,%rdi             /* set exit status to 0 */
     mov     $60,%rax            /* exit is syscall #60  */
     syscall                     /* invoke syscall */

.section .data

msg:    .ascii    "Loop: "
len=    .-msg
buffer: .space    3

Result:


Print out the loop number in hexadecimal instead of decimal number.

.text
 .globl    _start
 min = 0                         /* starting value for the loop index; **note that this is a symbol (constant)**, not a variable */
 max = 33                        /* loop exits when the index hits this number (loop condition is i<max) */
 _start:
     mov     $min,%r15           /* loop index */
 loop:
     /* ... body of the loop ... do something useful here ... */
     /* ============== Start ============== */
     mov     $len,%rdx           /* message length */
     mov     $msg,%rsi           /* message location */
     mov     $1,%rdi             /* file descriptor stdout */
     mov     $1,%rax             /* syscall sys_write */
     syscall

     lea     buffer(%rip),%r9    /* load the buffer address to r9 */

     mov     %r15,%rax           /* store dividend to rax */
     xor     %rdx,%rdx           /* clear rdx value */
     mov     $16,%r11            /* store dividor to r11 */
     div     %r11                /* perform unsigned division */
     mov     %rax,%r8            /* store result (ten) to r8 */
     mov     %rdx,%r10           /* store remainder (unit) to r10 */

     cmp     $0,%r8              /* compare ten digit if it is 0 */
     je      units_digit         /* if it is 0, skip storing it and go to the unit digit */
     add     $48,%r8             /* convert number to character */
     movb    %r8b,(%r9)          /* store character into buffer */
     add     $1,%r9              /* move to next byte in buffer */
 units_digit:
     add     $48,%r10            /* convert number to character */
     cmp     $58,%r10            /* compare if character value greater than 58. 0-9 is ASCII value 48-57 */
     jl      st_unit             /* if less than 58, go to store the value */
     add     $7,%r10             /* if greater or equal to, add 7 to A-E ASCII value */
 st_unit:
     movb    %r10b,(%r9)         /* store character into buffer */
     add     $1,%r9              /* move to next byte in buffer */

     movb    $10,(%r9)           /* 10 in ASCII is newline character. Store newline character to buffer */

     mov     $3,%rdx             /* message length */
     mov     $buffer,%rsi           /* message location */
     mov     $1,%rdi             /* file descriptor stdout */
     mov     $1,%rax             /* syscall sys_write */
     syscall
     /* =============== End =============== */
     inc     %r15                /* increment the loop index */
     cmp     $max,%r15           /* see if we've hit the max */
     jne     loop                /* if not, then continue the loop */

     mov     $0,%rdi             /* set exit status to 0 */
     mov     $60,%rax            /* exit is syscall #60  */
     syscall                     /* invoke syscall */

.section .data

msg:    .ascii    "Loop: "
len=    .-msg
buffer: .space    3

Result:


Reflection

Understanding assembler instructions and logic is tough for me. I even struggled to print the values in registers and create space to store a string. There isn’t enough documentation to help guide me through it. Assembler is low-level, so I have to think carefully about how things work step-by-step. It takes practice to get used to how data moves and how operations happen. Without high-level abstractions, it’s hard to see the big picture sometimes.

Comments

Popular posts from this blog

Project - Stage 1: Create a Basic GCC Pass

Lab 4 - GCC Build