Lab 5: 64-bit Assembler - x86_64

Instruction
This posting will describe the process and the finding of lab 5. This posting only covers x86_64. The explanation for Aarch64 is described in the previous posting: Lab 5: 64-bit Assembler - Aarch64.  You can see the detailed instruction of the lab5 on the Seneca SPO600 website.

The Brief Instruction of the Lab 5 about Aarch64

There are four representative part of the Lab 5
  • Take a look at the code using 'objdump -d objectfile' using aarch64 assembly language programs
  • Take a look at the code using 'objdump -d objectfile' using x86_64 assembly language programs
  • Write code to loop a message with digit using Aarch64 assembly language programs
  • Write code to loop a message with digit using x86_64 assembly language programs
This posting only covers the x86_64 part.


'Objdump -d hello' using Aarch64 language programs
The <main> section in the output of 'objdump -d' command using x86_64 language programs is below.

hello 

hello2

hello3

Designing the Lab 5 using x86_64
Provided code
The provided code for this lab is written below.
=====================================================
.text
.globl  _start

_start:

        movq    $len,%rdx                       /* message length */
        movq    $msg,%rsi                       /* message location */


        movq    $1,%rdi                         /* file descriptor stdout */

        movq    $1,%rax                         /* syscall sys_write */
        syscall

        movq    $0,%rdi                         /* exit status */

        movq    $60,%rax                        /* syscall sys_exit */
        syscall


.section .data

msg:    .ascii      "Hello, world!: 0\n"
        len = . - msg
======================================================
When you assemble this, it prints "Hello, world!". The ascii string "Hello, world!\n" is stored in msg and the length of the message is stored in len. The message is printed at the point of 'syscall' after 'movq   $1, %rax'. The program is done at the point of 'syscall' after 'movq  $60, %rax'. 


Looping
To loop the message, it needs the loop index for starting point and the loop index for ending point. Also, It needs a subroutine to execute looping.
======================================================
/*
   This is a 'hello world' program in x86_64 assembler using the
   GNU assembler (gas) syntax. Note that this program runs in 64-bit
   mode.

   CTyler, Seneca College, 2014-01-20

   Licensed under GNU GPL v2+
*/

.text

.globl  _start

_start:

        movq    $min, %r10    /*store the min vlaue into r10 as a loop index*/
        movq    $division, %r9 /*store the division value(10) into r9*/

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             %r10    /*increment loop index*/

        cmp             $max, %r10 /*compare the max with r10*/
        jne             loop    /*if the max value and the r10(loop index) value is not equal,*/
                                /*   redo the loop subroutine*/

        movq    $0,%rdi                         /* exit status */

        movq    $60,%rax                        /* syscall sys_exit */
        syscall


.section .data

msg:    .ascii      "Hello, world!:   \n"
        len = . - msg
min = 0
max = 30

division = 10
======================================================
The shape of the code is similar to c language. The code to loop using C language is the following:
for(int i = 0; i < 30, i++){
     printf("Hello, world!\n");
}
The code to correspond to for is 'loop:'. This is the subroutine to loop the code. The register r10 works as an 'i' which is used to check the condition. Every time the message is printed, the value in register r10 is increment and the incremented value is used to compare with max value. If the value is the same, the looping finishes. It is not the same, it loops again.

Print the digits
As the code loops 30 times, there are two cases of printing the digits: printing 1-digit and printing 2-digits. Therefore, it needs two subroutines for each case. To print the digit depend on the each loop, the blank spaces '  ' is added in the message (msg: .ascii "Hello, world!:   \n"). 
======================================================
        msg:    .ascii      "Hello, world!:   \n"

======================================================

Print the character number using the loop index
The hexadecimal for character 0 starts at 30. Therefore, one way to print number using the loop index is adding 30 to index value and store it in a register.
======================================================
        movq    %r10, %r15   
        add     $30, %r15   
======================================================
 If r15 is printed and the value in r15 is 0, the character 0 will be printed.

However, there is an easier way to calculate number character.
======================================================
        movq    %r10, %r15   
        add     $'0', %r15     
======================================================
By adding character 0 with the value of r15 which stores the value of r10, r15 stores the value for the number character.

Distinguish the Digit: 1-digit or 2-digits
You need to distinguish whether you need to print 1-digit or 2-digits by checking the value you want to print. If the value is less than 10, it means the digit is 1-digit. If it is greater or equal than 10, it means, the digit is 2-digits.
======================================================
        cmp     %r9, %r10   
        jl      digit_1       
        jmp     digit_2       
======================================================
The subroutine for 1-digit names digit_1. The subroutine for 2-digits names digit_2.

Print 1-digit (Subroutine digit_1)
Printing 1 digit is simple. After the system calculates the value for the character number, store the value at the position for digit (for example, position # in the message).
======================================================
         movq    $msg+15, %r11  
        movb    %r15b, (%r11) 
======================================================
The position is loaded in r11. As the character number value is stored in r11, it uses r15b to write one byte. It stores the r15b value in the position of r11.

Print 2-digits (Subroutine digit_2)
Printing 2 digits needs more steps because it needs division. As x86_64 language program can print one byte, if you want to print 2 digits, you need to divide the value by 10 so you can store each value in a separate register to print. There are a command for division 'div'. The rax value is divided by the register value you use. It keep the quotient into rax and the remainder into rdx. Before you use this command, you should initialize rdx. Also, you should keep the value you want to print out into rax.
======================================================
        movq    $0, %rdx       
        movq    %r10, %rax   
        div     %r9      

        movq    %rax, %r14  

        movq    %rdx, %r15 
======================================================
The register r14 and r15 is for storing the value. The r14 is for the left digit and the r15 is for the right digit. For example, if you want to print 30, 3 will be stored in r14 and 0 will be stored in r15. The r10 is the value you want to output. For example, it you want to print 30, r10 stores the value 30. The x9 stores 10 for division.

The whole code to implement the Lab 5
.text
.globl  _start

_start:

        movq    $min, %r10    /*store the min vlaue into r10 as a loop index*/
        movq    $division, %r9 /*store the division value(10) into r9*/

loop:

        cmp     %r9, %r10    /*compare r10(loop index) with 10*/
        jl      digit_1        /*if r10 is less than 10, go to the subroutine digit_1*/
        jmp     digit_2        /*if r10 is greater or equal to 10, go to the subroutine digit_2*/


digit_1:

        movq    %r10, %r15    /*store the r10 value into r15*/
        add     $'0', %r15    /*add '0' to r15 so the value will be ascii number character value*/

        movq    $msg+15, %r11  /*the digit location within string*/

        movb    %r15b, (%r11)  /*store the digit at the location*/

        jmp     print          /*go to the subroutine print*/


digit_2:

        movq    $0, %rdx        /*initialize rdx to 0 for division*/
        movq    %r10, %rax    /*store the r10 value into rax for division*/
        div     %r9            /*divide rax value by 10(r9)*/

        movq    %rax, %r14  /*store the rax value(quotient) into r14*/

        movq    %rdx, %r15  /*store the rdx value(remainder) int r15*/

        add     $'0', %r14      /*add '0' to r14 so the value will be ascii number character value*/

        add     $'0', %r15      /*add '0' to r15 so the value will be ascii number character value*/

        movq    $msg+14, %r11  /*the digit location within string*/

        movb    %r14b, (%r11)  /*store the digit at the location*/

        movq    $msg+15, %r12  /*the digit loctaion within string*/

        movb    %r15b, (%r12)  /*store the digit at the location*/

        jmp     print /*go to the subroutine print*/



print:

        movq    $len,%rdx                       /* message length */
        movq    $msg,%rsi                       /* message location */


        movq    $1,%rdi                         /* file descriptor stdout */

        movq    $1,%rax                         /* syscall sys_write */
        syscall
        inc             %r12b

        inc             %r10    /*increment loop index*/

        cmp             $max, %r10 /*compare the max with r10*/
        jne             loop    /*if the max value and the r10(loop index) value is not equal,*/
                                /*   redo the loop subroutine*/

        movq    $0,%rdi                         /* exit status */

        movq    $60,%rax                        /* syscall sys_exit */
        syscall


.section .data

msg:    .ascii      "Hello, world!:   \n"
        len = . - msg
min = 0
max = 30
division = 10


Struggles to Implement the Code
Login problem
As I sent the ssh key twice, the recent ssh key was not stored in the server. It makes the login problem. It has solved when the professor copies the recent ssh key which stores in the Aarch64 server.

The number of the register in the x86_64 language program
As I started coding using Aarch64 before I started using x86_64, there was confusion at the beginning of the lab5 using x86_64. In Aarch64,  there are 30 registers for each. For example, r0 through r30 refers generally to the registers, x0 through x30 is for 64-bit wide access, and w0 through w30 are for 32-bit wide access. However, rax, rbx, rcx, rdx, rbp, rsp, rsi, rdi, and r8-r15 are the registers in the x86_64 language problem. I accidentally used r20 in the x86_64 language program and it makes an error. I solved this error to check the x86_64 Register and Instruction Quick Start website.


Comparison between Aarch64 and x86_64
I found Aarch64 is easier to use than x86_64 because of the three reasons I will describe below.
The number of registers
The Aarch64 has more registers I can use than x86_64. Also, the wording is easier to understand.
In the x86_64 language program, the registers named rax, rbx, rcx, rdx, rbp, rsp, rsi, rdi, and r8-r15. As some registers such as rax and rdx are used for calculation, I felt the r8-r15 is safe to use.
In the Aarch64 language program, the registers named r0-r30. The registers from r9 to r15 are for temporary values and r19-r28 are safe to use in my program.
The Aarch 64 is easier for me to use because the name is unified except number.

Calculation

In the Aarch 64, the calculation is intuitive to understand. For example, the command for add is the following:
======================================================
add r0, r1, r2 // load r0 with r1+r2
======================================================
The format is similar to the format I used in math: r0 = r1 + r2

In the x86_64, the command for add is the following:

======================================================
add     %r14, %r15 //  add r14 and r15, put result in r15
======================================================
If I want to keep the original value before adding, I will write one more line.

What I have learned from Lab 5
I have learned how to implement the code using x86_64 and the difference between Aarch64 and x86_64. Also, I got some ideas about how the ssh key works while I got a struggle to log in. 
Both Aarch64 and x86_64 languages are interesting but I felt the Aarch64 is better than the x86_64. The x86_64 command is similar to Aarch64's so once I have finished the Aarch64 part, it is easy to complete the x86_64 part. However, the syntax for Aarch64 is more familiar and similar to math. 

Comments

Popular posts from this blog

Open Source Project: Lab2 (Pull Request)

Release 4.0 - Part 3: Release

Lab 8: Automated Testing and Continuous Integration