1

there aren't a lot of GAS assembly tutorials so im terribly lost. this is just a simple program that computes the result after the user inputs a base and an exponent. but it won't work, i think there is something wrong with my compare statements. your kind help will be greatly appreciated! thanks! :)

.section .data
    input1: .ascii "Input base number: \0"
    input1Len: .long .-input1
    input2: .ascii "Input exponent: \0"
    input2Len: .long .-input2
    newline: .ascii "\n\0"
    newlineLen: .long .-newline
    output: .ascii "result = "
    outputLen: .long .-output

.section .bss
    .lcomm base, 1
    .lcomm exponent, 1
    .lcomm result, 1
    .lcomm one, 1


.section .text
.globl _start
_start:

#prompt 1st number
movl $4,%eax
movl $1,%ebx
movl $input1,%ecx
movl input1Len, %edx
int $0x80

#get 1st input number
movl $3, %eax
movl $1, %ebx
movl $base, %ecx
int $0x80

#write 1st input number
movl $4,%eax
movl $1,%ebx
movl $base,%ecx
int $0x80

#prompt 2nd number
movl $4,%eax
movl $1,%ebx
movl $input2,%ecx
movl input2Len, %edx
int $0x80   

#get 2nd input number
movl $3, %eax
movl $1, %ebx
movl $exponent, %ecx
int $0x80

#write 2nd input number
movl $4,%eax
movl $1,%ebx
movl $exponent,%ecx
int $0x80

#move base to result
movl (base), %eax
movl %eax, (result)

#check if exponent==1
movl (exponent), %ecx
subl $0x30, %ecx
cmpl $1, %ecx

while:

    movl (base), %eax
    movl (result), %ebx
    subl $0x30, %eax
    subl $0x30, %ebx

    #multiply result and base, then update result
    imull %eax, %ebx
    movl %ebx, (result)
    addl $0x30, %ebx
    movl %ebx, (result) 

    #subtract 1 from ecx, which is the exponent
    subl $1, %ecx

    #compare if ecx is greater than 1
    cmpl $1, %ecx
    jg while

print:

    #write output
    movl $4,%eax
    movl $1,%ebx
    movl $output,%ecx
    movl outputLen, %edx
    int $0x80

    movl $4,%eax
    movl $1,%ebx
    movl $result,%ecx
    movl $1, %edx
    int $0x80

    movl $4,%eax
    movl $1,%ebx
    movl $newline,%ecx
    movl newlineLen, %edx
    int $0x80

_exit:
    movl $1, %eax
    movl %ecx, %ebx
    int $0x80   
Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180
  • no, i'm really new in assembly, i don't know much. and there aren't many tutorials about it in the internet. – user2224027 Mar 29 '13 at 10:54
  • "won't work" _how_? As Alexey suggested, step through the code in a debugger so that you can see the intermediate results. Or output those results to a terminal. Also, what's stopping you from following a TASM/MASM or NASM tutorial and switching to GAS once you've grasped the x86 ISA? – Michael Mar 29 '13 at 11:04

1 Answers1

1

Some hints:

  # trailing \0 will write an unwanted \0 in the output 
  # you will give the length in the write call anyway
input1: .ascii "Input base number: \0"  

  # as calculated here
input1Len: .long .-input1  

  # you want to read those with movl (base), %eax ...
  # later on !! register size is 4 bytes
  # either resize or use movb (base), %al later on ...
.section .bss 
    .lcomm base, 1
    .lcomm exponent, 1
    .lcomm result, 1

    #get 1st input number
    movl $3, %eax
    movl $1, %ebx
    movl $base, %ecx
      # you should give length 2 in %edx here
      # ... otherwise the old value will be used
      # 2 seems the right choice as it will also read the \n following
    int $0x80 

    #write 1st input number
    movl $4,%eax
    movl $1,%ebx
    movl $base,%ecx
      # you should give length 1 in %edx here
      # ... otherwise the old value will be used 
    int $0x80

   # there's a lot of copying values back and forth
   # going on here ... 
   # better load registers once 
   # => here
while:
    movl (base), %eax
    movl (result), %ebx

   # perform your operations ...

    jg while

   # ... and reassign them
   # => here

print:
   # you'll be in big trouble here 
   # if anything else but
   # --------------------
   #  base=any  exponent=0
   #  base=0    exponent=any
   #  base=1    exponent=any
   #  base=2    exponent=0, 1, 2, 3
   #  base=3    exponent=0, 1, 2
   # --- is given
   #  you'll have to calculate the decimal
   #  representation first ... simply adding 0x30 won't do !!

Additional hint: checking exponent for 1 and initializing result with base might not be the best idea. If its 0 the result should be 1.

EDIT: As you also asked about how to adjust the code for bigger results than 9 by properly dividing by 10 I decided to add this 2nd part.

Output of bigger numbers and division by 10

I won't go fully into details, as there already is a nice entry about that on stackoverflow (see: Assembly Language - How to Do Modulo?) but rather try to give you some brief hints.

As you already seem to have found out the main problem in outputting bigger numbers is to get the calculation of the decimal places right. Those can be calculated by a continued division by 10 using the remainder to fill in the corresponding decimal place. Example:

123 / 10 = 12 remainder 3
 12 / 10 =  1 remainder 2
  1 / 10 =  0 remainder 1

This algorithm stops once the result of the division reaches 0. The decimal representation of the number is just the concatenation of the remainders. Reading upwards you get "1" . "2" . "3" = "123" which is exactly the representation you were looking for.

As explained division on the x86 can be performed by the div instruction. The 32-bit version will read input from registers %edx and %eax interpreted as the 64-bit number %edx:%eax and an additional argument. Usage of the first two registers %eax and %edx is implicit and can't be changed.

The result will be given as quotient in %eax and remainder ind %edx again this assignment is fixed.

Putting this to use in the algorithm described above seems straight forward as this are exactly the two values needed if there weren't a small complication. This complication coming in the form of giving the digits in reversed order - so by just printing them directly the would be printed like "321" instead of "123" for the example above.

So before being able to output the single digits you'll have to reverse their order first. The solution I chose in my attempt was to push the numbers on the stack first to write them to a string buffer in correct order while poping them from the stack again.

Example

I commented out any parts you already managed to implement in your example. The only duplicated part is the calculation process, as I'm not sure if you used the same register assigment.

    .section .bss
.lcomm base, 1  
.lcomm exponent, 1  
.lcomm len, 4
.lcomm string, 10

    # ... perform all the read operations of your example here

calculate:
    # ebx: base
    # ecx: exponent
    # eax: result

    # base
    xor %ebx, %ebx
    movb (base), %bl
    subl $0x30, %ebx

    # exponent
    xor %ecx, %ecx
    movb (exponent), %cl
    subl $0x30, %ecx

    # result
    xor %eax, %eax  # initilize = 0
    # base == 0 ?
    cmpl $0, %ebx
    je prepare
    movl $1, %eax  # initilize = 1

while:
    # exponent == 0 ?
    cmpl $0, %ecx
    je prepare

    # multiply
    imull %ebx, %eax

    subl $1, %ecx   
    jmp while

prepare:
    # eax: result
    # edx: remainder
    # ecx: string
    # ebx: divisor

    movl $0x000a, %ebx
    movl %esp, %ebp
divide:
    xor %edx, %edx
    divl %ebx   # edx:eax / ebx =>  q: eax  mod: edx

    addl $0x30, %edx

    push %edx

    cmpl $0, %eax
    jne divide

    # eax: current digit
    # ebx: string buffer
    # ecx: length  
    mov $string, %ebx
    mov $0, %ecx

reverse:
    pop %eax
    movb %al, (%ebx, %ecx)
    inc %ecx
    cmp %ebp, %esp
    jne reverse

    mov %ecx, (len)

print:
    # ... print all the other stuff of your example here

    # write result
    movl $4,%eax
    movl $1,%ebx
    movl $string, %ecx   
    movl (len), %edx
    int $0x80
Community
  • 1
  • 1
mikyra
  • 10,077
  • 1
  • 40
  • 41