关于IA32汇编语言中函数的几个问题

时间:2014-11-12 05:13:11

标签: gcc assembly att ia-32

我正在使用汇编语言编写计算降雪的代码。它询问用户在do-while循环中下降的雪量(以英寸为单位),直到用户输入0来打破循环。同样在循环内,金额相互累加。输入0后,程序就会打印以英尺和英寸为单位的总降雪量。

我的程序有3个函数给我:printStr,readUInt和printUInt以及我的main。我理解printStr和readUInt是如何工作的,但我不明白printUInt是如何工作的,所以我希望有人可以向我解释一下。

另外,当我必须打印"总降雪量:#feet和#inches"时,我无法弄清楚如何打印字符串中的两个数字,对此有一些建议也有帮助。

我一直在研究这个问题几个小时,如果我不是完全难过,我就不会在这里。

printStr(edi =要打印的以空字符结尾的字符串的地址)

    printStr:
  pushq %rbp
  movq %rsp,%rbp
  subq $24,%rsp
  movl %ebx, -4(%rbp)

  movl %edi, %ecx   # Copy the "Start"

printStr_loop: 
  movb (%ecx),%al
  cmpb $0,%al
  jz   printStr_end 

  # Syscall to print a character
  movl $4, %eax     # Print (write) syscall
  movl $1, %ebx     # Screen (file)
#  movl $Hello, %ecx
  movl $1, %edx     # One character
  int $0x80

  addl $1, %ecx    
  jmp printStr_loop

printStr_end:
  movl $-1,%eax
  movl $-1,%ecx
  movl $-1,%edx
  movl -4(%rbp), %ebx
  leave
  ret

.data
printUIntBuffer: .asciz "          "
printUIntBufferEnd=.-2

.text

printUInt(edi =要打印的无符号整数):

    printUInt:
  pushq %rbp
  movq %rsp,%rbp
  subq $24,%rsp
  movl %ebx, -4(%rbp)
  movl %edi, -8(%rbp)
  movl $10, -12(%rbp)  # Constant 10 used for division/modulus  


  movl %edi, %eax   # eax = digits left to convert
  movl $printUIntBufferEnd,%ecx  # %ecx is the insert point
  # Convert each digit into a characters  
printUInt_loop:
     movl $0, %edx  # Reset high portion for division
     divl -12(%rbp)  # Divide edx:eax by 10; edx=Remainder / eax = quotient
     addb $'0',%dl
     movb %dl,0(%ecx)
     subl $1,%ecx
     testl %eax,%eax
     jnz   printUInt_loop 
# Done with loop, print the buffer
   movl %ecx,%edi
   addl $1,%edi
   call printStr

printUInt_end:
  movl $-1,%eax
  movl $-1,%ecx
  movl $-1,%edx
  movl -8(%rbp), %edi
  movl -4(%rbp), %ebx
  leave
  ret

.data
readUInt_bufferStart = .
readUInt_buffer: .ascii " "

.text

readUInt(返回%eax中的读取unsigned int)

readUInt:
  pushq   %rbp         # Save the old rpb
  movq    %rsp, %rbp   # Setup this frames start

  movl %ebx,-4(%rbp)


  movl $0,%eax   # initialize accumulator

readUInt_next_char:
  # Read a character
  movl %eax,-8(%rbp)
  movl $3, %eax   # issue a read
  movl $1,%ebx   # File descriptor 1 (stdin)
  movl $1,%edx   # sizet = 1 character
  movl $readUInt_bufferStart,%ecx
  int  $0x80    # Syscall
  movl -8(%rbp),%eax

  # Get the character
  movb readUInt_bufferStart,%bl
  cmpb   $'0',%bl
  jb     readUInt_end
  cmpb   $'9',%bl
  ja     readUInt_end

  movl   $10,%edx
  mul    %edx
  subb   $'0',%bl
  addl   %ebx,%eax
  jmp    readUInt_next_char

readUInt_end:
  movl $-1,%ecx
  movl $-1,%edx
  movl -4(%rbp),%ebx
  leave
  ret

主要数据:

    .data


AskSF: .asciz "How many inches of snow to add (0 when done): "
TotalSF: .asciz "Total snowfall: %d feet and inches "


.text

主:

    do_while:
movl $AskSF, %edi 
call printStr #asking for amount of snowfall
call readUInt
addl %eax,%edx  #adding amounts of snowfall together
movl %eax,%ecx  #moving entered amount to compare with 0
cmpl $0,%ecx    # checking if amount is 0 to see if loop should exit
jne do_while

#below here I was just experimenting looking for solutions

movl $TotalSF,%edi
call printStr
movl %edx,%edi
call printUInt

1 个答案:

答案 0 :(得分:0)

printUInt例程的工作原理如下:

  1. 取整数(最初在%edi中,但放入%eax)
  2. 反复将其除以10并找到余数(在除法后的%edx中找到)。这个余数是被分割数字的最后一位数,或者是最右边的数字。
  3. 将“0”的ASCII码添加到此最右边的数字,以获取该数字的ASCII码。
  4. 将结果值存储在%ecx指向的内存中,并减少%ecx(字符串从右向左放入内存中)。
  5. 重复直到商(%eax)为零,这意味着打印所有数字。
  6. 调用打印例程,指向内存中字符串中的第一个数字。
  7. 例如:

    1. 以%edx开头:%eax = 0:113
    2. 除以10:%eax = 11,%edx = 3
    3. 添加48到3:51(或ASCII“3”)
    4. 将51存储在字符串所在的内存位置(现在为“3”)。
    5. 除以10:%eax = 1,%edx = 1
    6. 添加48到1:49(ASCII“1”)
    7. 将49存储在字符串中(现为“13”)
    8. 除以10:%eax = 0,%edx = 1
    9. 添加48到1:49(ASCII“1”)
    10. 将49存储在字符串(现在为“113”)
    11. 因为%eax = 0
    12. 而停止
    13. 打印字符串。
    14. 要以英尺和英寸打印您的答案,我建议将任务分为4个打印说明,获得英尺和英寸组件并将其放入堆叠中:

      1. 使用printStr例程打印“总降雪量:”
      2. 打印脚(从堆栈中检索脚值并在调用printUInt之前放入%edi)
      3. 使用printStr例程打印“和”
      4. 打印英寸(从堆栈中检索英寸值并放入%edi ...)
      5. 这样的事情应该有效(但可能更清洁):

        .data
        
        TotalSF1: .asciz "Total snowfall: "
        TotalSF2: .asciz " feet and "
        TotalSF3: .asciz " inches\n"
        
        .text
        
        do_while:
            movl $AskSF, %edi
            call printStr
            call readUInt
            addl %eax, %ebx # %ebx doesn't get clobbered 
                            # by function calls, so use it for sum
            movl %eax, %ecx
            cmpl $0, %ecx
            jne do_while
        
        movl $TotalSF1, %edi # print the first bit of the answer
        call printStr
        xor %edx, %edx       # zero out %edx in prep for division
        movl $12, %ecx       # number of inches in a foot (imperialist!)
        movl %ebx,%eax       # put total snowfall in %eax
        divl %ecx            # divide %edx:%eax by 12 to get ft + in
        push %edx            # put inches on the stack to keep it safe
        movl %eax, %edi      # print out feet
        call printUInt
        movl $TotalSF2, %edi # print out the middle bit of the answer
        call printStr
        pop %edi             # print out inches
        call printUInt
        movl $TotalSF3, %edi # print closing bit of answer
        call printStr
        
        movl $1, %eax        # exit nicely
        movl $0, %ebx
        int $0x80