GAS汇编基础知识,获得指数

时间:2013-03-29 10:51:17

标签: assembly x86 gas

没有很多GAS汇编教程,所以我非常失败。 这只是一个简单的程序,用于在用户输入基数和指数后计算结果。但它不起作用,我认为我的比较声明有问题。 非常感谢您的帮助!谢谢! :)

.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   

1 个答案:

答案 0 :(得分:1)

一些提示:

  # 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 !!

其他提示:检查1的指数并使用result初始化base可能不是最佳选择。如果其0 result应为1

编辑:你还问过如何通过适当地除以10来调整代码以获得比9更大的结果我决定添加第二部分。

输出更大的数字并除以10

我不会完全详细说明,因为在stackoverflow上已有一个很好的条目(参见:Assembly Language - How to Do Modulo?),而是试着给你一些简短的提示。

正如您已经发现输出更大数字的主要问题是正确计算小数位数。这些可以通过继续除以10来计算,使用余数来填充相应的小数位。例如:

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

一旦分割结果达到0,此算法就会停止。数字的十进制表示只是剩余部分的串联。向上阅读你得到“1”。 “2”。 “3”=“123”这正是您要找的代表。

如上所述,x86上的除法可以由div指令执行。 32位版本将读取来自寄存器%edx%eax的输入,解释为64位数字%edx:%eax和另一个参数。前两个寄存器%eax%edx的使用是隐含的,无法更改。

结果将作为%eax中的商和余下的%edx再次给出此分配。

将此用于上述算法似乎很简单,因为如果没有小的并发症,这正是所需的两个值。这种复杂的形式是以相反的顺序给出数字 - 因此,只需直接打印它们就可以像"321"而不是"123"一样打印上面的例子。

因此,在能够输出单个数字之前,您必须首先撤销其订单。我在尝试时选择的解决方案是首先将堆栈上的数字按顺序写入字符串缓冲区,然后再次从堆栈中弹出它们。

实施例

我已注释掉您已在您的示例中实现的任何部分。唯一重复的部分是计算过程,因为我不确定您是否使用了相同的寄存器分配。

    .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