typedef struct node {
int data;
struct node *next;
} nodeL;
Assuming I want to translate the above declaration in MIPS assembly language, how am I supposed to do it? Apart from allocating memory (using syscall 9), which is done in the .text segment, what about the .data segment?
Also, what about alignment?
Before even mentioning code, you need to clarify whether the struct you want to create will be static data (data segment), local data (stack), or dynamically allocated data (heap). There are different ways to allocate each.
But before discussing that, the very first thing you need to do is determine the layout of the struct instance. At the very least it could be:
------------------
| data - 32-bits |
------------------
| next - 32-bits |
------------------
To create an instance statically, it's simply:
.data
.align 2
anInstance: .word 0,0
And on the heap:
.text
Allocator.newNode:
li $a0, 8 #allocate 8 bytes
li $v0, 9
syscall #returns word-aligned ptr
jr $ra
If placing on the stack, simply allocate 8 bytes for it.
A cleaner way is to use a prototype-based method.
Your object layout becomes:
------------------
| size - 32-bits |
------------------
| atr 1 - 32-bits|
------------------
| atr 2 - 32-bits|
------------------
.
.
.
------------------
| atr n - 32-bits|
------------------
For each struct, you create a prototype which the allocation routine will use to create the instance.
.data
ListProto: .word 8,0 #size, head ptr
NodeProto: .word 12,0,0 #size, data, next ptr
.text
main:
la $a0, ListProto
jal Allocator.newObject #create new list instance
la $a0, NodeProto
jal Allocator.newObject #create new node instance
Allocator.newObject:
lw $a0, 0($a0) #a0 = object size
li $v0, 9
syscall
jr $ra
Whether you want the instance to actually keep a size field is up to you. With that approach you can simply add prototypes and that's it.
Related
So I am learning C++ at the moment and got some really weird results when I tried printing out element of a list of integers, that are actually out of range. (Don't ask why, I stumbled on that by accident)
This is my Code:
#include <iostream>
using namespace std;
int main() {
int nums[0] = {};
for (int i = 0; i <= 10; i++) {
cout << nums[i] << endl;
}
return 0;
}
Results of when I ran the code the first time:
0
17110752
0
4199367
0
0
0
65
0
4225392
0
Results of when I ran the code a second time:
0
1906400
0
4199367
0
0
0
65
0
4225392
0
Results of when I ran the code a third time:
0
15603424
0
4199367
0
0
0
65
0
4225392
0
Every time I run this programm, the second number seems to be diffrent.
I know why I get unexpected results, but I'm curious where these values come from.
Edit: "I know why I get unexpected results" is just poorly worded. What I meant was "I know that I get unexpected result, because I am calling a element that is not part of the list".
Are they values from the RAM?
Why are some value repeating and some not?
The result of compiling your code to (my invented) machine code is something like this.
1 main:
2 reserve basePtr, 2 ; reserve 2 slots on the stack, and store in basePtr
3 mov nums, basePtr ; num points at the first local variable
4 mov [nums], 0 ; int num[1] = {0} - set num to zero
5 mov i, basePtr + 1 ; i points at second slot for local variables
6 mov [i], 0 ; int i = 0;
7 for_loop:
8 cmp [i], 10 ;
9 jmp_above_or_equal fini ; exit loop
10 mov tmp, [nums+i] ;
11 call cout, tmp
12 call cout, std::endl ;
13 inc [i]
14 jmp for_loop
15 fini:
16 return 0
So we reserve 2 slots for variables, and then read beyond the reserved space.
Imagine the call works like this :-
Initial stack
+-----------+
| ret_main | // crt code which calls main
+-----------+
| nums[0] |
+-----------+
| i |
SP-> +-----------+
push param (e.g. tmp) creates a stack like so...
+-----------+
| ret_main | // crt code which calls main
+-----------+
| nums[0] |
+-----------+
| i |
+-----------+
| tmp |
SP-> +-----------+
With the call itself....
+-----------+
| ret_main | // crt code which calls main
+-----------+
| nums[0] |
+-----------+
| i |
+-----------+
| tmp |
+-----------+
| line_12 |
SP-> +-----------+
With this stack growing down through calls, your further requests are showing the memory values of the return address into your code, and the local variables of the previous call to cout.
If the stack grows the other way (more normal), a stack may look as so...
SP-> +-----------+
| line_12 |
+-----------+
| tmp |
+-----------+
| i |
+-----------+
| nums[0] |
+-----------+
| ret_main | // crt code which calls main
+-----------+
This is the normal direction for stacks in modern processors. In this case when you look further into the nums array, you are reading values of the return address for the function, and then the local variables which are from the function calling main.
In practice i is probably optimized into a register, and entry into a function would save some registry values based on the platform's calling conventions.
So the values you are reading, is likely to be in this order...
Registers saved by main
Values to detect against buffer overrun (which is happening here)
Return address to main
arguments to main
local variables in the function which calls main.
Why are they repeating. If the function calling main has copies of argv, then it would be likely the push to store argv are matched by the callers copy in local variables.
Why do some change. Since Address Space Layout Randomization (ASLR), processes normally launch with a random base address, and random stack location, making it harder to exploit a buffer overflow.
So I am new to MIPS and I wanted to implement simple modulo arithmetic functions. I am unable to see why the program is not jumping according to the jump statements in the code. Any help would be appreciated.
.data
text_enquiry : .asciiz "Enter operation code (1-add, 2-subtract, 3-multiply, 4-exponentiation, 5-inversion, 6-exit): "
text_a : .asciiz "Enter a: "
text_b : .asciiz "Enter b: "
text_m : .asciiz "Enter m: "
text_result : .asciiz "Result = "
new_line : .asciiz "\n"
.text
main:
## t0 = code, t1 = a, t2 = b, t3 = m
#print operation message
la $a0, text_enquiry
li $v0, 4
syscall
#read code
li $v0, 5
syscall
move $t0, $v0
#if code == 6
beq $t0, 6, exit
j rest
exit:
li $v0, 10
syscall
#else, continue procedure
#print 'a, b, m' messages and read them
rest:
la $a1, text_a
li $v0, 4
syscall
li $v0, 5
syscall
move $t1, $v0
la $a1, text_b
li $v0, 4
syscall
li $v0, 5
syscall
move $t2, $v0
la $a1, text_m
li $v0, 4
syscall
li $v0, 5
syscall
move $t3, $v0
j mod
## t0 = code, t1 = a, t2 = b, t3 = m, t4 = un-modded result, t5 = modded result, t6 =
mod:
beq $t0, 1, func_add
beq $t0, 2, func_sub
beq $t0, 3, func_mul
# beq $t0, 4, func_exp
func_add:
add $t4, $t1, $t2
div $t4, $t3
mfhi $t5
#print accordingly
la $a3, text_result
li $v0, 4
syscall
move $a3, $t5
li $v0, 1
syscall
la $a3, new_line
li $v0, 4
syscall
j main
func_sub:
sub $t4, $t1, $t2
div $t4, $t3
mfhi $t5
#print accordingly
la $a3, text_result
li $v0, 4
syscall
move $a3, $t5
li $v0, 1
syscall
j main
func_mul:
mult $t1, $t2
mflo $t4
div $t4, $t3
mfhi $t5
#print accordingly
la $a3, text_result
li $v0, 4
syscall
move $a3, $t5
li $v0, 1
syscall
j main
As you can see, the code works fine when the code is '6' but doesn't work for any other code value.
I am unable to see why the program is not jumping according to the jump statements in the code. Any help would be appreciated.
Your program is jumping according to the input.
What you are unable to see is what it really does and why your further text output fails, and you are unable to see that, because you didn't even look (in debugger, single-stepping over every instruction).
Would you take a look, you would clearly see that for example for input "3", the code will reach instruction after label rest:, so the branching is correct.
That code later is not doing what you expect, because it doesn't print the other helper prompts, because it is calling syscall with wrong arguments (the code sets a1 with string address, instead of a0, and in a0 is still the original operation-prompt address), but it is executed as planned.
Generally deciding on assembly code correctness only by checking input/output is very bad practice, as you can often have correct output "by accident", while the code is already doing also something different than planned, which may just bite you later, if you keep extending your code, or if you use different inputs.
To decide if your assembly code is correct, you should rather spend quite some time in debugger, verifying different inputs, and thinking how the code works and if really works as planned/assumed, while you were writing it - on per-instruction basis!
The final code should consist only of instructions which you know exactly what they are doing, and why they are part of the code, and why they are placed where they are. If anything works "by accident" without you truly understand it, you must fix it (either by understanding how it works, or by rewriting it in a way you understand how it works), it's still bug, even if the output is correct.
I know it might be and obvious question, but I've decided to learn a little bit of low level programming. I began with c and the gdb.
First problem:
`(gdb) x/10xb $rip
0x4005a5 <main+4>: 0xb9 0x04 0x00 0x00 0x00 0xba 0x03 0x00
0x4005ad <main+12>: 0x00 0x00
(gdb) x/10xh $rip
0x4005a5 <main+4>: 0x04b9 0x0000 0xba00 0x0003 0x0000 0x02be 0x0000 0xbf00
0x4005b5 <main+20>: 0x0001 0x0000
(gdb) x/10xw $rip
0x4005a5 <main+4>: 0x000004b9 0x0003ba00 0x02be0000 0xbf000000
0x4005b5 <main+20>: 0x00000001 0xffff9fe8 0x0000b8ff 0xc35d0000
`
Question: Why when I use unit size b the next address is 0x4005ad but when I use h or w the next address is 0x4005b5?
Second problem:
`(gdb) x/4xw $rip + 0
0x4005a5 <main+4>: 0x000004b9 0x0003ba00 0x02be0000 0xbf000000
(gdb) x/4xw $rip + 1
0x4005a6 <main+5>: 0x00000004 0x000003ba 0x0002be00 0x01bf0000
(gdb) x/4xw $rip + 2
0x4005a7 <main+6>: 0xBA000000 0x00000003 0x000002be 0x0001bf00
(gdb) x/4xw $rip + 3
0x4005a8 <main+7>: 0x03BA0000 0xbe000000 0x00000002 0x000001bf
(gdb) x/4xw $rip + 4
0x4005a9 <main+8>: 0x0003BA00 0x02be0000 0xbf000000 0x00000001
(gdb) x/4xw $rip + 5
0x4005aa <main+9>: 0x000003BA 0x0002be00 0x01bf0000 0xe8000000
(gdb) x/4xw $rip + 6
0x4005ab <main+10>: 0x00000003 0x000002be 0x0001bf00 0x9fe80000
(gdb) x/4xw $rip + 7
0x4005ac <main+11>: 0xBE000000 0x00000002 0x000001bf 0xff9fe800
(gdb) x/4xw $rip + 8
0x4005ad <main+12>: 0x02BE0000 0xbf000000 0x00000001 0xffff9fe8`
Question: Why the same value is repeated(Capital letters) for example: in the first column but move to the right, like from $rip + 2 to $rip + 5 where BA is first at the beginning, then at the middle and finally at the end?
When you ask any low-level debugger to display values from memory startingat a given address it will get some number of bytes from successive locations and display them. (Each address refers to a particular byte in memory)
In your first problem you're asking it to display ten bytes and it displays each byte as a two-digit hexadecimal value, eight bytes per line, and the address goes from 0x4005a5 to (0x4005a55 + 8) or 0x4005ad
Then you ask to display ten half words, eight half words per line, and because each half word is two bytes, the address goes from 0x4005a5 to (0x4005b5 + 16) or 0x4005b5
Your second problem is a little more complicated. Remember that when you ask it to display the contents of memory starting at a location it just fetches your four words STARTING at that location. When you pick one higher address then you're mostly getting the same memory values, just shifted by one.
So why do the values in the words seem to be shifting around in the wrong direction? That has to do with the fact that you're asking for words and x86 CPUs fetch words in a somewhat unintuitive order, from least significant byte to most.
This should help:
https://en.wikipedia.org/wiki/Endianness
I'm trying to do a mips interactive exercise (BubbleSort). I have to read 20 integers from the user and store them into an array,then sort the array with the method of BUBBLESORT.
I have a problem with storing the integers into the array.
Here is a bit of my code:
.data
prompt:.asciiz "enter the integer one by one \n "
.text
addi $sp,$sp,-88 #space on the stack
lui $s0, 0x0040 #s0=adress of array
sw $s0,-84($sp) #store s0 on the stack
sw $ra,-80($sp) #store ra on the stack
readInt:
#prompt the user
li $v0, 4 #op code to print string
la $a0, prompt
syscall
#get the array's integer
addi $t0,$zero,0 #t0=compteur
addi $a1,$zero,80 #a1=size of array*4 (20 int)
move $a0,$s0 #a0= adress of array
loop:
beq $t0,$a1,BubbleSort #if t0 = size of array ==>BubbleSort
li $v0,5 #opcode to read int
syscall
**sw $v0,0($a0)** ERROR HERE !!!! :(
addi $a0,$a0,4 #increment a0 (next int)
addi $t0,$t0,4 #incrementer t0 (counter +=4)
j loop
hello everyone i need some help converting the following C code into MIPS:
main()
{
int i;
for (i=0;i<5;++i)
power(2,1)
factorial(i)
return 0;
}
int power(int base,int n)
{
int i,p;
p=0;
for(i=1;i<=n;++i)
p=p*base;
return p;
}
int factorial(int a)
{
if(a==1)
return 1;
else
{
a *=factorial(a-1);
return a;
}
}
i have only done the factorial part so far but i'm stuck at a*=factorial(a-1), i under stand that multi is used to multiply two registers together but how can it take the input of a recursive loop? also is it possible to multi to multiply with a constant instead of another register?
factorial:
addi $sp, $sp, -8 #adjust stack for 2 items
sw $ra, 4($sp) #save return address
sw $a0, 0($sp) #save argument
bne $a0, 1, Else #if !(a==1), go to else address
addi $v0, $zero, 1 #result is 1
addi $sp, $sp, 8 #pop 2 items from stack
jr $ra #return
Else: addi $a0, $a0, -1 #decrement s-1
jal factorial #recursive call
lw $a0, 0($sp) #restore original a
lw $ra, 4($sp) #and return address
addi $sp, $sp, 8 #pop two items from stack
Here is a code that I wrote, I think it will fit your purpose.
.text
factorial:
li $t0,13 # load 13 to check for overflow
blt $a0,$t0,no
li $v1,1
no:
bgtz $a0,find # if $a0>0 goto generic case
li $v0, 1 # base case, 0! = 1
jr $ra
find: sub $sp,8 # make room for $s0 and $ra
sw $s0,($sp) # store argument $s0=n
sw $ra,4($sp) # store return address
move $s0, $a0 # save argument
sub $a0, 1 # factorial(n-1)
jal factorial # v0 = (n-1)!
mul $v0,$s0,$v0 # n*(n-1)!
lw $s0,($sp) # restore $s0=n
lw $ra,4($sp) # restore $ra
add $sp,8 # reduce stack size
jr $ra # return
main:
la $a0,input
li $v0,4
syscall
li $v0,5
syscall
move $a0,$v0
jal factorial
move $t0,$v1
bnez $t0,overflow1
move $a0,$v0
li $v0,1
syscall
b end
overflow1:
la $a0,overflow
li $v0,4
syscall
end:
li $v0,10
syscall
.data
input:.asciiz"Input a number:"
overflow:.asciiz"There is an overflow!!"
It's complete with comments and explanations so there shouldn't be problems.