x86_64 Assembly Language
Tags: opensource, assembly, assemblylanguage, x8664
Published: 2022-03-22 02:32:51

In my last post we went through writing a program for printing a message with a 2 digit incrementing value in AArch64 assembly. This time, we're going to tackle the same thing on an x86_64 architecture system. 

Starting with the original code we're given:

```
.text
.globl    _start

min = 0                         /* starting value for the loop index; note that this is a symbol (constant), not a variable */
max = 10                        /* 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 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
```

First we need to add logic to print a message, like so:

```
.text
.globl    _start

min = 0                         /* starting value for the loop index; note that this is a symbol (constant), not a variable */
max = 10                        /* loop exits when the index hits this number (loop condition is i<max) */

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

loop:

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

    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

.section .data

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

By adding the provided text output code as above we get the following output:

```
Loop
Loop
Loop
Loop
Loop
Loop
Loop
Loop
Loop
Loop
```

Now by adding logic to iterate an index and add it to the `msg` string, we get the following:

```
.text
.globl    _start

min = 0                         /* starting value for the loop index; note that this is a symbol (constant), not a variable */
max = 10                        /* loop exits when the index hits this number (loop condition is i<max) */

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

loop:

    mov     %r15,%r14           
    add     $'0',%r14     
    movb    %r14b,msg+6        

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

    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

.section .data

msg:    .ascii      "Loop: #\n"
```

With the resulting output:

```
Loop: 0
Loop: 1
Loop: 2
Loop: 3
Loop: 4
Loop: 5
Loop: 6
Loop: 7
Loop: 8
Loop: 9
```

Finally, we'll move to printing a 2 digit index along with our loop by making the following changes:

```
.text
.globl    _start

min = 0                         /* starting value for the loop index; note that this is a symbol (constant), not a variable */
max = 15                        /* loop exits when the index hits this number (loop condition is i<max) */

_start:
    mov     $min,%r15        
    mov     $10,%r13          

loop:

    mov     %r15,%rax         
    mov     $0,%rdx          
    div     %r13          
    cmp     $0,%rax
    je     secondDigit

    add     $'0',%rax          
    mov     %rax,%r12
    movb    %r12b,msg+6       

secondDigit:

    add     $'0',%rdx          
    mov     %rdx,%r12
    movb    %r12b,msg+7        

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

    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

.section .data

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

Which gets us the appropriate output:

```
Loop: #0
Loop: #1
Loop: #2
Loop: #3
Loop: #4
Loop: #5
Loop: #6
Loop: #7
Loop: #8
Loop: #9
Loop: #10
Loop: #11
Loop: #12
Loop: #13
Loop: #14
Loop: #15
Loop: #16
Loop: #17
Loop: #18
Loop: #19
Loop: #20
Loop: #21
Loop: #22
Loop: #23
Loop: #24
Loop: #25
Loop: #26
Loop: #27
Loop: #28
Loop: #29
Loop: #30
```

Again this looks like the output we're looking for, so I'll break down how we got here and leave some parting thoughts on writing assembly programs for x86_64 vs my experience writing for AArch64. 

```
_start:
    mov     $min,%r15        
    mov     $10,%r13       
```

Starting off with the start section, we move the value of min to `r15` and set `r13` to 10, which we'll use to divide and split our 2 digits. Remember in this syntax the destination register is placed on the right, contrary to how it was arranged in the AArch64 program. 

```
loop:

    mov     %r15,%rax         
    mov     $0,%rdx          
    div     %r13          
    cmp     $0,%rax
    je     secondDigit
```

Next we place the value to be divided into `rax` and clear `rdx` to accept the remainder, before using the `div` instruction to divide what's in the `rax` register. We compare the value placed in `rax`, our "tens" column, to zero and branch to the secondDigit section if there's no tens column. 


```
    add     $'0',%rax          
    mov     %rax,%r12
    movb    %r12b,msg+6   
```

In the first line here we add an ascii 0 to the result of the division, after which we move that to `r12` and finally move a byte of that with `movb` to the address of the pound sign in `msg`.


```
secondDigit:

    add     $'0',%rdx          
    mov     %rdx,%r12
    movb    %r12b,msg+7 
```

Again here we add an ascii 0, this time to the remainder from the previous division, and then move that to `r12` to have a byte moved to the pound sign address in `msg`. Like the last program, I'll end my breakdown here as the rest is pretty self evident and discuss my experience with x86_64. 

This was pretty similar to assembly in AArch64 in a lot of ways, there were some minor syntactical differences like the precedence of operands listed above, and $ and % symbols being used to denote immediate values and registers, respectively. I'd be hard pressed to pick one I prefer, but if I had to I'd lean toward the AArch64 for its syntax, which I find slightly more readable. The difference is pretty negligible though. I also like the philosophy of improvement and not being weighed down by legacy features and nomenclature that comes with x86_64, but that hasn't affected my coding on either to any great extent thus far. 

Overall this was a good challenge and I'm looking forward to diving deeper into these architectures.