Lab 4 - 64-bit Assembly Language Lab - Part 3

Welcome to part 3 of lab 4. This lab goes over some basics of 64-bit assembly languages, specifically in the AArch64 and x86_64 platforms. This specific part will be going over the second half of the lab in the AArch64 system. Links for the other parts are listed here:

Part 1 - First half in AArch64

Part 2 - First half in x86_64

Part 4 - Second half in x86_64

I'll be outlining the steps in detail as I go along but the full instructions for this lab can be found here.


Okay this is where the lab gets a bit trickier. Our instructions now are to make our loop from previous parts loop from 00-30, printing a double digit on every line. Before we relied on a somewhat simplistic method to print the index, and now we'll have to get a bit fancier. To do this I used udiv and msub. udiv divides a register by another register, and it very specifically wants a register it seems, and not just a value. So I made register 28 equal 10, and used that for my equations. Then I used msub to calculate the remainder and honestly in this first 2 parts of this lab I started to feel like AArch64 was cumbersome, but now I'm really feeling it. I multiply register 20, the result from the division, by 10 (register 28) and subtract it from the index to get the remainder (if any) from the original division. And then I store it in register 21. Cumbersome. After that it's just a matter of converting these individual digits into characters and inserting them into their respective spots in msg. Honestly I'll admit my code is a bit messy and I could have probably reused some registers in some part to make the program use less registers but too late now. The edited code is provided below:

.text

.globl _start


min = 0    /* starting value for the loop index; note that this is a symbol (constant), not a variable */

max = 31   /* loop exits when the index hits this number (loop condition is i<max) */


_start:


        mov     x19, min       //index for looping

        mov     x28, 10        //value 10 for dividing


loop:

        

        udiv    x20, x19, x28       //calculate first digit

        msub    x21, x20, x28, x19  //calculate remainder (second digit)


        add     x24, x20, '0'  //Create first digit character      

        add     x25, x21, '0'  //Create second digit character


        adr     x26, msg+6     //Pointer for first digit location

        adr     x27, msg+7     //Pointer for second digit location


        strb    w24, [x26]     //Insert first digit

        strb    w25, [x27]     //Insert second digit


        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

And of course, the output is shown here.

Loop: 00

Loop: 01

Loop: 02

Loop: 03

Loop: 04

Loop: 05

Loop: 06

Loop: 07

Loop: 08

Loop: 09

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

For this next part I need to suppress that leading zero. Not entirely sure how that's meant to look but I interpreted that as making the first digit a space if it was zero. I'm going to provide a small sample of the code below of the important logic. First I'm just checking if the first digit is a zero, and if it is I branch to a label called suppressLeadZero. This skips over the section of code where I would have inserted the first digit, and instead I add a space to the first digits spot. If instead the first digit isn't zero, I insert the digit normally and than branch past suppressLeadZero and go straight to inserting the second digit. Definitely feel like my code is neater now that I'm using labels, but once again I should have reused some registers to reduce the overall amount of registers used. Like I could have just used the same registers in both firstDigit and secondDigit as overwriting them would have been pretty easy. Oh well.

        cmp     x20, 0              //check if first digit is zero

        b.eq    suppressLeadZero


firstDigit:

        add     x24, x20, '0'  //Create first digit character

        adr     x26, msg+6     //Pointer for first digit location      

        strb    w24, [x26]     //Insert first digit

        bl      secondDigit    //branch to secondDigit


suppressLeadZero:

        mov     x24, ' '

        adr     x26, msg+6     //Pointer for first digit location      

        strb    w24, [x26]     //Insert first digit

 

secondDigit:

        add     x25, x21, '0'  //Create second digit character

        adr     x27, msg+7     //Pointer for second digit location

        strb    w25, [x27]     //Insert second digit

Here's the full code by the way:

.text

.globl _start


min = 0    /* starting value for the loop index; note that this is a symbol (constant), not a variable */

max = 31   /* loop exits when the index hits this number (loop condition is i<max) */


_start:


        mov     x19, min       //index for looping

        mov     x28, 10        //value 10 for dividing


loop:

        

        udiv    x20, x19, x28       //calculate first digit

        msub    x21, x20, x28, x19  //calculate remainder (second digit)


        cmp     x20, 0              //check if first digit is zero

        b.eq    suppressLeadZero


firstDigit:

        add     x24, x20, '0'  //Create first digit character

        adr     x26, msg+6     //Pointer for first digit location      

        strb    w24, [x26]     //Insert first digit

        bl      secondDigit    //branch to secondDigit


suppressLeadZero:

        mov     x24, ' '

        adr     x26, msg+6     //Pointer for first digit location      

        strb    w24, [x26]     //Insert first digit

 

secondDigit:

        add     x25, x21, '0'  //Create second digit character

        adr     x27, msg+7     //Pointer for second digit location

        strb    w25, [x27]     //Insert second digit


        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

And of course, the 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

This is the end of the AArch64 portions, in the next and final part I will conclude by doing these same steps in the x86_64 platform. Click here for that.








Comments

Popular posts from this blog

SPO600 - Final Thoughts

SPO600 Project - Stage 1: Selection

Lab 4 - 64-bit Assembly Language Lab - Part 4