Several questions dealing with functions in IA32 assembly language - gcc

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

The printUInt routine works like this:
Take an integer (initially in %edi, but put in %eax)
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.
Add the ASCII code for "0" to this right-most digit to get the ASCII code for the digit.
Store the resulting value in memory where %ecx is pointing, and decrement %ecx (the string is put in memory from right to left).
Repeat until the quotient (%eax) is zero, which implies all the digits are printed.
Call the print routine, pointing at the first digit in the string in memory.
For example:
Start with %edx:%eax = 0:113
Divide by 10: %eax = 11, %edx = 3
Add 48 to 3: 51 (or ASCII "3")
Store 51 in the memory location where the string is (now " 3").
Divide by 10: %eax = 1, %edx = 1
Add 48 to 1: 49 (ASCII "1")
Store 49 in string (now " 13")
Divide by 10: %eax = 0, %edx = 1
Add 48 to 1: 49 (ASCII "1")
Store 49 in string (now " 113")
Stop because %eax = 0
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:
Print "Total snowfall: " using the printStr routine
Print feet (retrieve feet value from stack and put in %edi before calling printUInt)
Print " and " using the printStr routine
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

Related

How to print out the prompt to the user using MIPS

Write a MIPS assembly language program that prompts the user to input 3 integers and then prints out the average of the 3 numbers (integer division is OK for this problem). You do not need to validate the user input.
Can't seem to get the prompt for the user to show up in the console when running.
.data
prompt: .asciiz "Enter three numbers: "
sum: .asciiz "The Sum is: "
avg: .asciiz "The Avergae is: "
.text
# Read Integer
li $v0, 5
la $a0, prompt # Print String
syscall
#
add $s0, $0, $v0
li $v0, 5
syscall
#
add $s1, $0, $v0
li $v0, 5
syscall
#
add $s2,$0,$v0
add $s3, $s1, $s0
add $s3, $s2, $s3
li $v0, 4
la $a0, sum
syscall
#
li $v0,4
la $a0, avg
syscall
#
li $v0, 1
addi $t1,$0, 3
div $a0, $s3, $t1
syscall
#
li $v0, 10
syscall
This here says that the for the "output string" function, the value of $v0 before the syscall should be 4, not 5. Code 5 stands for "read integer".

How register offset affects printing

So I have this Assembly program where I'm trying to output some data using a call to printf, but it wont print the whole thing that I'm trying to print.
.section .data
output:
.asciz "The processor Vendor ID is ‘%s’\n"
.section .bss
.lcomm buffer, 64
.section .text
.globl _start
_start:
pushq %rbx
movq $0, %rax
cpuid
movq $buffer, %rsi
movq $output, %rdi
mov %ebx, 28(%rdi)
mov %ecx, 32(%rdi)
#mov %edx, 35(%rdi)
movq $0, %rax
call printf
movq $60, %rax
movq $0, %rdi
popq %rbx
syscall
This version prints absolutely nothing, however; this version:
.section .data
output:
.asciz "The processor Vendor ID is ‘%s’\n"
.section .bss
.lcomm buffer, 64
.section .text
.globl _start
_start:
pushq %rbx
movq $0, %rax
cpuid
movq $buffer, %rsi
movq $output, %rdi
mov %ebx, 27(%rdi)
mov %ecx, 31(%rdi)
#mov %edx, 35(%rdi)
movq $0, %rax
call printf
movq $60, %rax
movq $0, %rdi
popq %rbx
syscall
This version prints out The processor Vendor ID is Genuntel even though the offset only changed by one. If I include the commented line, the program doesn't print anything no matter what (I am aware that the offset is wrong, in the first example, but even if it is 36 it won't print). As such, I'm curious as to how the offset affects it so that it wont print at all for the first one but it prints for the second one.
Keep in mind that this is a school assignment, but I have been trying for days now and I can't seem to get it to work.
This code looks like it's set up for you to store the string bytes into buffer, relative to RSI, so the %s conversion in the format string will print it. But with the string in .data instead of .rodata where you should put read-only data, yes you can overwrite bytes of the format string at runtime.
When you overwrite the \n, printf doesn't flush the output buffer because stdout is line buffered. You exit with sys_exit (direct system call) instead of call exit or returning from main, leaving the data un-printed. See Using printf in assembly leads to an empty ouput
You can use ltrace ./my_program to see the library function calls it makes.

replace the matched text with the contents of another file using sed inside a shell script

I have a Makefiel which call a shell script. Inside that shell script I have the following sed command:
sed -e '/.section\t.text/{' -e 'r anotherfile.s' -e 'd' -e '}' input.s > output.s
which does not work.
while it works fine when I run this into terminal directly.
I want to search a line ".section .text" in a file and replace it with another file.
What is wrong here?
input.s
.section .data
.addressing Word
_A:
.data.32 0
.section .text
.addressing Word
_main:
LW %GPR27, _C(%GPR0)
NOP
anotherfile.s
.addr_space 32 ; address space is 2^32
.addressing Byte ; byte addressing (default)
.bits_per_byte 8 ; 1 byte consists of 8 bit (default)
.endian Big ; Big endian (default)
.section .text
.org 0x00000000
output.s (should be like this)
.section .data
.addressing Word
_A:
.data.32 0
.addr_space 32 ; address space is 2^32
.addressing Byte ; byte addressing (default)
.bits_per_byte 8 ; 1 byte consists of 8 bit (default)
.endian Big ; Big endian (default)
.section .text
.org 0x00000000
.addressing Word
_main:
LW %GPR27, _C(%GPR0)
NOP
sed is for doing s/old/new, that is all. That's not what you're doing so you shouldn't be using sed so rather than trying to debug something you shouldn't be doing anyway, just use awk:
awk '
NR==FNR { rep = (rep=="" ? "" : rep ORS) $0; next }
/\.section\t\.text/ { $0 = rep }
{ print }
' anotherfile.s input.s > output.s

Using QtSpim on OSX, MIPS error: "unknown character" for simple ascii declaration

I am taking my first ever Architecture class and I was given the snippet below of code to test and learn.
Unfortunately, when I run the darn thing I get this error message:
spim: (parser) Unknown character on line 2 of file /Users/X/Desktop/example_mips.asm
.asciiz “Enter in an Integer:”
^
Now given that this is an in-class example I am a little frustrated it won't run.
I am however using my own computer, a Mac running OS X 10.10.1 on QtSpim Version 9.1.16.
The school computers we initially tested this on are running windows. Could this make a difference?
Any noticeable errors? I am assuming this is all valid code:
.data
prompt: .asciiz “Enter in an Integer:”
str1: .asciiz “The answer is:”
newline:.asciiz “\n”
bye: .asciiz “Goodbye!\n”
.globl main
.text
main:
#init
li $s0, 10
#prompt for input
li $v0, 4
la $a0, prompt
syscall
#read in the value
li $v0, 5
syscall
move $s0, $v0
loop:
#print str1
li $v0, 4
la $a0, str1
syscall
#print loop value
li $v0, 1
move $a0, $s0
syscall
#print newline
li $v0, 4
la $a0, newline
syscall
#decrement loop value and branch if not negative
sub $s0, $s0, 1
bgez $a0, loop
#print goodbye message
li $v0, 4
la $a0, bye
syscall
#exit
li $v0, 10
syscall
spim: (parser) Unknown character on line 2
Something appears to have gone wrong when you copy-pasted the code. Replace the “ characters with the normal ASCII quotation mark character ".

making system call from 32-bit assembly in mac os x failed

I try to write a short 32-bit assembly program to test system calls in mac os x.
I write the first version which shows below, calling the write() function to print a string on the screen. it does work.
.data
str:
.ascii "Hello World\n"
.text
.globl _main
_main:
pushl $12
pushl $str
pushl $1
movl $4, %eax
subl $4, %esp
int $0x80
addl $16, %esp
pushl $0
movl $1, %eax
subl $12, %esp
int $0x80
After this, I decide to write another version uses mkdir() to make a new directory in "/tmp", but I failed. The system call number of mkdir() is 0x88 and my code shows below. Can anyone tell me where i am wrong. thanks very much.
.data
path:
.ascii "/tmp/new_dir"
.text
.globl _main
_main:
nop
pushl $path
pushl $0x1ff
movl $0x88, %eax
subl $8, %esp
int $0x80
addl $16, %esp
pushl $0
movl $1, %eax
subl $12, %esp
int $0x80
Here I found a simple guide for syscalls on MacOS. From what I read I assume that value of %eax register is the only one passed to syscall by register. The rest of registers must be pushed on the stack starting with EDX.
+----------+------------------------+--------------------------------+
| Register | Use | Location |
+----------+------------------------+--------------------------------+
| EAX | Unused, just a padding | [ESP] |
| EBX | Name of directory | [ESP+4] |
| ECX | Access | [ESP+8] |
| EDX | Unused | Unused, but let's say [ESP+12] |
+----------+------------------------+--------------------------------+
According to this table, your mkdir call should look like this:
pushl $0 /* for EDX, unused */
pushl $0x1FF /* for ECX */
pushl $path /* for EBX */
movl $0x88, %eax
pushl %eax /* for EAX but unused */
int $0x80
addl $16, %esp /* clean up */
Adding on, your exit call doesn't seem to be OK either (I'm not sure, I'm not very familiar with this way of syscall-ing)
subl %esp, $8 /* unused EDX and ECX */
pushl $0 /* for EBX */
movl $1, %eax
pushl %eax /* unused EAX */
int 0x80
EDIT:
After digging up some materials on the topic of BSD syscalls, I also found a source recommending having the stack 16-byte aligned. This means that when you call a syscall with less then 4 parameters, you should push additional doublewords, and when you call a syscall with 5 or full 6 parameters, you should also push some doublewords for (if free) ESI, EBP and ESP.

Resources