Several questions dealing with functions in IA32 assembly language

349 views Asked by At

I am working on code in assembly language that calculates snowfall. It asks a user for the amount (in inches) of snow that has fallen in a do-while loop, until the user enters 0 which breaks the loop. Also within the loop, the amounts are added up with each other. Once 0 is entered, the program is then supposed to print the total amount of snowfall in feet and inches.

My program has 3 functions that were given to me: printStr, readUInt, and printUInt along with my main. I understand how printStr and readUInt work but I do not understand how printUInt works, so I was hoping someone could explain that to me.

Also, when I have to print "Total amount of snowfall: # feet and # inches", I am having trouble figuring out how I would print the two numbers within the string, some advice on that would be helpful also.

I have been working on this for hours, and I would not be here if I wasn't entirely stumped.

printStr (edi = address of null-terminated string to be printed)

    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 = unsigned integer to print):

    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 (returns the read unsigned int in %eax)

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 for the main:

    .data


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


.text

main:

    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

There are 1 answers

0
Tony On BEST ANSWER

The printUInt routine works like this:

  1. Take an integer (initially in %edi, but put in %eax)
  2. Repeatedly divide this by 10 and find the remainder (found in %edx after division). This remainder is the last digit of the number being divided, or the right-most digit.
  3. Add the ASCII code for "0" to this right-most digit to get the ASCII code for the digit.
  4. Store the resulting value in memory where %ecx is pointing, and decrement %ecx (the string is put in memory from right to left).
  5. Repeat until the quotient (%eax) is zero, which implies all the digits are printed.
  6. Call the print routine, pointing at the first digit in the string in memory.

For example:

  1. Start with %edx:%eax = 0:113
  2. Divide by 10: %eax = 11, %edx = 3
  3. Add 48 to 3: 51 (or ASCII "3")
  4. Store 51 in the memory location where the string is (now " 3").
  5. Divide by 10: %eax = 1, %edx = 1
  6. Add 48 to 1: 49 (ASCII "1")
  7. Store 49 in string (now " 13")
  8. Divide by 10: %eax = 0, %edx = 1
  9. Add 48 to 1: 49 (ASCII "1")
  10. Store 49 in string (now " 113")
  11. Stop because %eax = 0
  12. Print the string.

To print your answer in feet and inches, I suggest breaking the task up into 4 print instructions, having obtained the feet and inches components and put these on the stack:

  1. Print "Total snowfall: " using the printStr routine
  2. Print feet (retrieve feet value from stack and put in %edi before calling printUInt)
  3. Print " and " using the printStr routine
  4. Print inches (retrieve inches value from stack and put in %edi...)

Something like this should work (but could be cleaner):

.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