Checking number of zeros in a MIPS array - arrays

I have a question as follows:
Given an array of 32-bit signed integers in the memory and its length in one of the registers, write a MIPS program that counts how many zeros the array contains. Assume that the array starts at Ox12345678, and the length of the array is already stored in $1. The number of zeros should be stored in $2, which mayor may not be initialised with zero at the beginning. Refer to Table 1 for MIPS assembly instructions.
Here is where I have got to in my own head but I have one main question:
1) I think that if I have subroutines, I need to be pushing and then popping data from a stack by using sw $ra, 4($sp) and addi $sp,$sp,-8and sw $fp, 0($sp). However, using my program, I have a break clause which only moves to a subroutine on a condition (if something is $0). So I don't jal to the subroutine, I beq to the subroutine. How can I modify my code to do this?
Here is my current code:
Add $3, $0, $0 #Set a counter to 0, the start of the array
Lui $4, 0x1234
Ori $4, $4, 0x5678 #Store register $4 to start of the array so you can offset
Add $2, $0, $0 #Sets $2 to $0 which is the total number of zeros
Start_for: Beq $3, $1, end_for #If counter is equal to the array length, go to end
Lw $5, 0x0($4) Load the current value of array into temp register $5
Addi $4, $4, 4 #Increment array pointer to next value
Beq $5, $0, increment #Increment the sum by 1 if the array[i] is zero
Addi $3, $3, 1 #Increment counter by 1
J start_for
increment:
Addi $2, $2, 1 #Increment the number of zeros by 1 and add to sum
Jr $ra
End_for: Lui $8, 0xffff
Ori $8, $8, 0xf004 Load the outtray into $8
Lw $8, 0x0($2) Store the number of zeros in the array to the outtray

Related

Assembly in c language [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I have code in assembly form and I want to transform it to ะก code. So far I am aware of the fact that A decoder is needed. I have download the rtdec but I can not understand how to use it in order to transform the code.
The code is :
.data
A:
.word 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
.word 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17
.word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.word 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
B:
.word 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17
.word 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18
.word 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
.word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
C:
.word
.text
li $8, 0
li $9, 0
li $15, 16
li $16, 4
la $10, A
la $11, B
la $12, C
again:
lw $13, 0($10)
lw $14, 0($11)
add $13, $13, $14
sw $13, 0($12)
addi $10, $10,4
addi $11, $11,4
addi $12, $12,4
addi $9, $9,1
blt $9, $15, again
li $9,0
addi $8, $8,1
blt $8, $16, again
li $v0, 10
syscall
The code uses three registers - $10, $11, and $12 - as the current position pointers in the three arrays A, B, and C. Here's the init sequence:
la $10, A
la $11, B
la $12, C
So $10 follows A, $11 follows B, and $12 follows C. Let's see what else happens to those registers:
lw $13, 0($10) loads the word that $10 is pointing at (the one from A)
lw $14, 0($11) loads the word that $11 is pointing at (the one from B)
sw $13, 0($12) stores a register into C
Finally, all three registers are increased by the word size, which is 4:
addi $10, $10,4
addi $11, $11,4
addi $12, $12,4
That is "move on to the next array element in A, B, and C" logic.

How do I force GAS to generate output identical to input?

I want to write code that has a public C interface, but is otherwise implemented entirely in MIPS, mainly as a learning exercise. However, I'm stuck fighting GAS because it seems to assume it knows better than I do.
To illustrate, let's say I want to implement the following in MIPS:
int bar(void)
{
return (4 / 2);
}
As I mentioned above, I want to be able to call ASM routines from C, so we'll need a C code file, bar.c, as well as the MIPS assembly in bar.S.
bar.c:
extern int bar(void);
void _start()
{
int foo = bar();
}
bar.S:
.global bar .text
bar:
addi $2, $0, 4
addi $3, $0, 2
div $2, $3
mflo $2
jr $31
addu $3, $0, $0
mipsel-none-elf-gcc bar.c bar.S -o bar.elf -ffreestanding -nostdinc -nostdlib successfully compiles this, but mipsel-none-elf-objdump -d bar.elf shows that GAS is messing with my bar() code:
00400050 <bar>:
400050: 20020004 addi v0,zero,4
400054: 20030002 addi v1,zero,2
400058: 14600002 bnez v1,400064 <bar+0x14>
40005c: 0043001a div zero,v0,v1
400060: 0007000d break 0x7
400064: 2401ffff li at,-1
400068: 14610004 bne v1,at,40007c <bar+0x2c>
40006c: 3c018000 lui at,0x8000
400070: 14410002 bne v0,at,40007c <bar+0x2c>
400074: 00000000 nop
400078: 0006000d break 0x6
40007c: 00001012 mflo v0
400080: 00001012 mflo v0
400084: 03e00008 jr ra
400088: 00000000 nop
40008c: 00001821 move v1,zero
I don't want the division checks, or delay slot fixups, or the assembler being "helpful" in any way here: I'm perfectly capable of handling those things myself if necessary. How do I tell GAS to just be a dumb assembler?
This one was solved in the comments. GAS accepts a 3-operand version of div where division checks are disabled if the first operand is $0. As for the delay slot, a .set noreorder directive prevents reordering of instructions (duh). Changing the assembly to:
.set noreorder
.global bar .text
bar:
addi $2, $0, 4
addi $3, $0, 2
div $0, $2, $3
mflo $2
jr $31
addu $3, $0, $0
produces the correct output:
00400050 <bar>:
400050: 20020004 addi v0,zero,4
400054: 20030002 addi v1,zero,2
400058: 0043001a div zero,v0,v1
40005c: 00001012 mflo v0
400060: 03e00008 jr ra
400064: 00001821 move v1,zero

While loop through array, MIPS assembly

I want to convert while (w[i] == x) i += j; into MIPS assembly code.
Assuming INTEGERS i,j, and x are in $3,$4 and $5. Also assume i=0 initially before the while loop.
w => array of integers and its base address is stored in $6. So far I have this.
Loop:
sll $10, $3, 2 # $10 = i* 4
add $10, $6, $10 # $10 has address of w[i]
lw $11, 0($10) # $11 = w[i]
bne $11, $5, Exit # exit from loop if w[i]!= x
add $3, $3, $4 # i= i+ j
j Loop
Exit:
Is it possible to optimize this code by moving the base address itself by j*4 and also get rid of the multiple branch instructions? Cause I have no clue on how to do that.
Thanks in advance!
To get rid of the multiple branch instructions, this trick can be used:
WARNING: NOT exactly equivalent to your code
Loop:
sll $10, $3, 2 # $10 = i* 4
add $10, $6, $10 # $10 has address of w[i]
lw $11, 0($10) # $11 = w[i]
add $3, $3, $4 # i = i + j
beq $11, $5, Loop # keep looping if w[i] == x
Exit:
sub $3, $3, $4 # i = i - j
The trick is to perform i += j before testing for keep looping or not.
This do introduce a problem sometimes: it may trigger an additional integer overflow when your code doesn't.
EDITED:
It's something like rewriting this:
while (some_condition())
do_something();
into this:
do
do_something();
while (some_condition());
undo_something();
EDITED:
Well, let me try to "move the pointer from the base address itself by j*4" this time :)
Start:
sll $11, $3, 2 # $11 = i * 4
add $10, $11, $6 # Let $10 be a "cursor" pointing to w[i]
Loop:
lw $11, 0($10) # $11 = w[i]
sll $12, $4, 2 # $12 = j * 4
add $10, $10, $12 # update $10 by 4 * j
add $3, $3, $4 # update i by j
beq $11, $5, Loop # keep looping if w[i] == x
Exit:
sub $3, $3, $4 # i = i - j
However it's not more optimized than the version I gave above: both of them uses 5 instructions inside the loop body.
To make the comparison easier, I've written a small dummy function:
#include <stdint.h>
#include <stdlib.h>
uint32_t fun1(uint32_t const *in, uint32_t cmp, size_t j)
{
size_t i = 0;
while (in[i] == cmp)
{
i += j;
}
return in[i];
}
this can be compiled, and the output can be compared to an equivalent function:
uint32_t fun2(uint32_t const *in, uint32_t cmp, size_t j)
{
while (*in == cmp)
{
in += j;
}
return *in;
}
For both functions, gcc (4.8 on x86-64) generates a loop with only 4 instructions.
For the second function it essentially goes:
temp1 = (in)
compare temp1, cmp
if not equal, return temp1
temp2 = j*sizeof(uint32_t)
loop:
in += temp2 #\
temp1 = (in) # \
compare temp1, cmp # - 4 instructions loop
if equal, goto loop # /
return temp1
This pseudo-assembly can probably be implemented for MIPS like so:
lw $v0, 0($a0)
beq $a1, $v0, end
sll $t1, $a2, 2
loop:
add $a0, $a0, $t1 #\
lw $v0, 0($a0) # - only 3 instructions in loop, due to test-and-branch
bne $a1, $v0, loop #/
end:
jr $ra

Dynamic allocation for arrays in MIPS

Our team is trying to create a compiler that's fed code and produces MIPS assembly from it.
To tackle array declaration at the global scope, we create a label for the array in .text and reserve 4 bytes to hold the address pointing to the start of the array in memory.
.text
arr: .space 4
.data
...
li $t0, N # (where N = number of elements in arr)
li $v0, 9 # Load system instruction to allocate dynamic memory
li $t1, 4 # 4 bytes per element
mult $t0, $t1 # Calculates how big the allocated memory has to be in bytes.
mflo $a0 # Loads this value into $a0
syscall # Allocates memory and returns address into $v0
la $s0, arr # load address of arr into $s0
sw $v0, ($s0) # Save allocated memory address into the space reserved in .text
However, the last instruction doesn't seem to be working properly for us.
This image shows exactly where the error occurs and the state of the registers at that time. I'm unsure why it's causing an error.
Edit: some more information on the error produced, updated to encompass the modified instructions at the end
[00400000] 8fa40000 lw $4, 0($29) ; 183: lw $a0 0($sp) # argc
[00400004] 27a50004 addiu $5, $29, 4 ; 184: addiu $a1 $sp 4 # argv
[00400008] 24a60004 addiu $6, $5, 4 ; 185: addiu $a2 $a1 4 # envp
[0040000c] 00041080 sll $2, $4, 2 ; 186: sll $v0 $a0 2
[00400010] 00c23021 addu $6, $6, $2 ; 187: addu $a2 $a2 $v0
[00400014] 0c100009 jal 0x00400024 [main] ; 188: jal main
[00400018] 00000000 nop ; 189: nop
[0040001c] 3402000a ori $2, $0, 10 ; 191: li $v0 10
[00400020] 0000000c syscall ; 192: syscall # syscall 10 (exit)
[00400024] 34080003 ori $8, $0, 3 ; 9: li $t0, 3 # Load immediate value into register $t0
[00400028] 34020009 ori $2, $0, 9 ; 10: li $v0, 9
[0040002c] 34090004 ori $9, $0, 4 ; 11: li $t1, 4
[00400030] 01090018 mult $8, $9 ; 12: mult $t0, $t1
[00400034] 00002012 mflo $4 ; 13: mflo $a0
[00400038] 0000000c syscall ; 14: syscall
[0040003c] 3c101001 lui $16, 4097 [arr0] ; 15: la $s0, arr0
[00400040] ae020000 sw $2, 0($16) ; 16: sw $v0, ($s0)
PC = 400040
EPC = 40003c
Cause = 1c
BadVAddr = 1004002f
Status = 3000ff12
The last instruction sw $v0, arr0($0) = MEM[$0 + arr0] = $v0 which isn't correct. However you'll be given the amount of memory requested and it is needed to save that memory pointer in a register until scope of program. The assignment might look like this,
arr: .space 4
syscall # Allocates memory and returns address into $v0
la $s0, arr # arr is pointer address
st $v0, 0($s0) # start base address of array
Now arr is base address of 4 bytes of memory holding the address of memory content. For good practice it is important to de-allocate the memory assigned back to OS as,
li $v0,10 # return back to OS
syscall

Trying to write a basic main program using a function

I am trying to write a function to copy words from source memory to destination memory.
I have written the function but I am having difficulty executing the code.
It is giving me execption 4 as an error
.data
.text
main:
.setnoreorder
top: beq $a2,0,done
lw $t1,($a0)
sw $t1,($a1)
add $a0,$a0,4
add $a1,$a1,4
j top
sub $a2,$a2,1
done:
jr $ra #return to the main program
add $0, $0, $0 #nop
I want to write a main program which calls this function to copy 800 words from
address 0x50000 to 0x90000 in memory. But when I add the values in $a0-$a2 and run the code it doesnt work.
Does anyone know how to fix it. (I am converting C code to MIPS which is why I have included a C tag
Cheers
.text # code starts here
main: # required label
la $a0,dest # point to destination
la $a1,srce # point to source
li $a2,1000 # move this many words
jal block_copy # call the routine
nop
li $v0,10
syscall
###############################################################################
# block copy - moves $a3 words from $a1 to $a0
#
# register usage:
# $a0 address of destination
# $a1 address of source
# $a2 block size (number of words to move)
# $v0 return code (0 means no troubles)
#
block_copy:
move $v0,$a2 # counter/rc
bc_loop:
lw $t0,0($a1) # no DMA here
sw $t0,0($a0) # we have to move a word at a time
addiu $a0,$a0,4 # point to next word in destination
addiu $a1,$a1,4 # point to next word in source
addiu $v0,$v0,-1 # decrement counter
bgtz $v0,bc_loop # keep on moving if counter is positive
jr $ra # return to caller
###############################################################################
.data
dest:
.word 9:1000 # destination 1000 words (all zeroes)
srce:
.word 0:1000 # source 1000 words (all nines)
Shouldn't that be:
sub $a2,$a2,1
j top
You show a delay slot in two places, here
j top
sub $a2,$a2,1
and here
done:
jr $ra #return to the main program
add $0, $0, $0 #nop
But apparently not here:
top: beq $a2,0,done
lw $t1,($a0)
Maybe the problem is that the load following the beq is, in fact, a delay slot and is being executed even when $a2 is zero (and the branch is taken) - you are loading from memory at ($a0) even when the count is zero - perhaps accessing invalid memory and causing the exception.

Resources