2016-04-03 9 views
1

ich konvertieren MIPS Assembly lernen, und ich bin versucht, die folgende C-Funktion in MIPS Assembly zu konvertieren:eine C-Funktion zu MIPS Montage

int count (int a[], int n, int x) 
{ 
int res = 0; 
int i = 0; 
int j = 0; 
int loc[]; 
for(i = 0; i != n; i++) 
if(a[i] == x) 
{ 
    res = res + 1; 
    loc [j] = i; 
    j = j + 1; 
} 
return res, loc; 
} 

Ich habe bei der Umwandlung die meisten davon, und es gelang mir Ich glaube, ich habe res erfolgreich zurückgegeben (ein Wert von 1), obwohl ich nicht sicher bin, ob ich loc zurückliege (ich bekomme auch einen Wert von 1, und ich glaube nicht, dass das korrekt ist). Allerdings habe ich Schwierigkeiten mit diesem Programm und bin mir nicht sicher, wie ich sicherstellen kann, dass loc den richtigen Wert zurückgibt oder wie man es selbst programmiert.

Hier ist meine Assembly Code:

.data 
a: .word 5,6,7,8,9,10 
n: .word 
x: .word 
res: .word 0 
i: .word 0 
jj: .word 0 
loc: .space 40 

.text 
main: 
la $s0, a 
lw $s1, res 
lw $s2, x 
lw $t0, i 
lw $t1, jj 
lw $t2, n 
la $s3, loc 
li $t4, 6 

start: 
sll $t3, $t0, 2 
add $t5, $t3, $s0 
lw $t4, 0($t5) 

beq $t0, $t4, start 
addi $t0, $t0, 1 
beq $t0, $t2, exit 

addi $s1, $s1, 1 
sll $t7, $t1, 2 
add $t6, $s3, $t7 
sw $t0, 0($t6) 
addi $t1, $t1, 1 

exit: 
li $v0, 1 
add $a0, $s1, $zero 
syscall 

li $v0, 1 
add $a1, $s3, $zero 
syscall 

Jede Hilfe, Hinweise oder Vorschläge sehr geschätzt werden würde.

EDIT: Ich habe meinen Code überarbeitet und jetzt 0 für die res Rückkehr und "268501028" für loc erhalten. Nicht sicher, wo diese Nummer herkommt.

.data 
a: .word 5,6,7,8,9,10 
n: .word #n 
x: .word #x 
res: .word 0 
i: .word 0 
jj: .word 0 
loc: .space 40 

.text 
main: 
la $s0, a 
lw $s1, res 
lw $s2, x 
lw $t0, i 
lw $t1, jj 
lw $t2, n 
la $s3, loc 
li $t4, 6 

start: 
beq $t0, $t2, exit #for(i = 0; i != n; i++) 
bne $s0, $s2, else #if(a[i] == x) 
j start 

else: 
addi $s1, $s1, 1 #res = res + 1; 
sw $t0, ($t1) #loc [j] = i; 
addi $t1, $t1, 1 #j = j+1 
addi $t0, $t0, 1 #Increment i 

addi $s3, $s3, 4 #Setting next element for loc 
addi $s0, $s0, 4 #Setting next element for a 
j start 

exit: 
li $v0, 1 
move $a0, $s1 
syscall 

li $v0, 1 
move $a0, $s3 
syscall 
+0

'int loc [];' im C-Code macht keinen Sinn. – EOF

+1

'return res, loc;' ist, nun, rein * technisch * ist es gültig C - aber es dient keinem klaren Zweck. Was soll diese Linie tun? – usr2564301

+1

@RadLexus In MIPS asm, selbst mit Standardkonvention, können Sie _zwei_ Werte (in 'v0' und' v1') zurückgeben. Mit internen Konventionen kann jeder FNC so viele "Rückgabe" -Werte in so viele Register setzen, wie man möchte. Das C hier ist mehr wie Pseudocode für die vorgeschlagene asm –

Antwort

4

Okay, es gab ein paar Bugs. Ich habe die Quelle kommentiert und "BUG:" hinzugefügt, um sie zu beleuchten. Ich habe dann eine aufgeräumt und korrigierte Version


Hier ist dein Original-Code - keine Fehlerbehebungen, nur Anmerkungen [bitte unentgeltlichen Stil Bereinigungs verzeihen]:

# int 
# count(int a[], int n, int x) 
# { 
# int res = 0; 
# int i = 0; 
# int j = 0; 
# int loc[n]; 
# 
# for (i = 0; i != n; i++) { 
#  if (a[i] == x) { 
#   res = res + 1; 
#   loc[j] = i; 
#   j = j + 1; 
#  } 
# } 
# 
# return res, loc; 
# } 

    .data 
a:   .word  5,6,7,8,9,10 
n:   .word 
x:   .word 
res:  .word  0 
i:   .word  0 
jj:   .word  0 
loc:  .space  40 
nl:   .asciiz  "\n" 

    .text 

    .globl main 

main: 
    la  $s0,a 
    lw  $s1,res 
    lw  $s2,x 
    lw  $t0,i 
    lw  $t1,jj 
    lw  $t2,n 
    la  $s3,loc 
    li  $t4,6     # BUG: extraneous (gets trashed below) 

start: 
    sll  $t3,$t0,2    # get i << 2 
    add  $t5,$t3,$s0    # get &a[i] 
    lw  $t4,0($t5)    # fetch it 

    # BUG: we're comparing a[i] against i but we want to compare against x 
    # _and_ we want to flip the sense of the branch 
    beq  $t0,$t4,start   # is it a match? if yes, loop 

    addi $t0,$t0,1    # increment i 
    beq  $t0,$t2,exit   # i == n? if no, loop. if yes, exit 

    # BUG: the indexing here is wrong 
    addi $s1,$s1,1    # j += 1 
    sll  $t7,$t1,2    # get jj << j 
    add  $t6,$s3,$t7    # &loc[jj << j] (BUG: we want &loc[j]) 
    sw  $t0,0($t6)    # set it to i 
    addi $t1,$t1,1    # jj += 1 

    # BUG: we should loop here and _not_ fall through 

exit: 
    # print j (with newline) 
    li  $v0,1 
    add  $a0,$s1,$zero 
    syscall 
    li  $v0,4 
    la  $a0,nl 
    syscall 

    # print _address_ of loc[0] 
    # BUG: if we care to print anything, we should print the _values_ of the 
    # whole array 
    li  $v0,1 
    # BUG: this should be a0 and _not_ a1 
    ###add  $a1,$s3,$zero 
    add  $a0,$s3,$zero 
    syscall 
    li  $v0,4 
    la  $a0,nl 
    syscall 

    li  $v0,10     # exit program 
    syscall 

Hier ist die gereinigte up und korrigierte Version. Ich musste ein wenig umstrukturieren und vereinfachen, damit es funktioniert, also könnte es auf den ersten Blick ein bisschen "fremd" erscheinen. Ich habe jedoch versucht, Ihre Registrierung so weit wie möglich zu behalten.

I erhöhte auch die Größe des a Array und eine Benutzereingabeaufforderung für die x Wert hinzugefügt:

# int 
# count(int a[], int n, int x) 
# { 
# int i = 0; 
# int j = 0; 
# int loc[n]; 
# 
# for (i = 0; i != n; i++) { 
#  if (a[i] == x) { 
#   loc[j] = i; 
#   j += 1; 
#  } 
# } 
# 
# return j, loc; 
# } 

    .data 
a:   .word  5,6,7,8,9,10 
    .word 5,6,7,8,9,10 
    .word 5,6,7,8,9,10 
    .word 5,6,7,8,9,10 
    .word 5,6,7,8,9,10 

ae: 

loc:  .space  1000 

prompt:  .asciiz  "Enter x value: " 
msgnl:  .asciiz  "\n" 
msgj:  .asciiz  "j: " 
msgloc:  .asciiz  "loc: " 

    .text 

# main -- main program 
# 
# RETURNS [sort of as this is a main program]: 
# s1 -- j value (count of elements in "loc") 
# loc -- filled in indexes into "a" array of matches to x 
# 
# registers: 
# s0 -- a (base address of "a" array) 
# t2 -- n (number of elements in "a" array) 
# 
# s2 -- x (value to match) 
# t0 -- i (current index into "a" array) 
# s3 -- loc (base address of "loc" array) 
# s1 -- j (current index into "loc" array) 
# 
# t6 -- quick temporary [reusable] 
# t7 -- used in array offset/index calculations [reusable] 
    .globl main 
main: 
    # prompt for x value 
    li  $v0,4     # syscall: print string 
    la  $a0,prompt 
    syscall 

    # read in x value 
    li  $v0,5     # syscall: read integer 
    syscall 
    move $s2,$v0 

    # get address of "a" array and compute length 
    la  $s0,a     # get &a[0] 
    la  $t2,ae     # get address of &a[n] 
    sub  $t2,$t2,$s0    # get number of bytes in a 
    srl  $t2,$t2,2    # get number of words in a (i.e. n) 

    li  $t0,0     # i = 0 
    li  $s1,0     # j = 0 
    la  $s3,loc     # base address of loc array 

# main matching loop 
loop: 
    sll  $t7,$t0,2    # get i << 2 
    add  $t7,$t7,$s0    # get &a[i] 
    lw  $t6,0($t7)    # fetch from it 
    bne  $t6,$s2,next   # a[i] == x? if no, advance to next element 

    # add new "i" value to loc array 
    sll  $t7,$s1,2    # get j << 2 
    add  $t7,$s3,$t7    # &loc[j << 2] 
    sw  $t0,0($t7)    # store i into loc 
    addi $s1,$s1,1    # j += 1 

next: 
    addi $t0,$t0,1    # i += 1 
    blt  $t0,$t2,loop   # i < n? if yes, loop (or, we're done) 

# done with calculation/fill loop 
done: 
    la  $s6,msgj    # get prefix string 
    move $s7,$s1     # get j 
    jal  prtnum     # pretty print the number 

    blez $s1,exit    # bug out if _no_ values in loc 

    # prepare to print all values of loc 
    la  $t6,loc     # base address of "loc" 
    li  $t7,0     # initial index 

# loop and print all values of loc 
prtlocloop: 
    la  $s6,msgloc    # prefix string 
    lw  $s7,($t6)    # get loc[...] 
    jal  prtnum     # pretty print the number 

    add  $t6,$t6,4    # increment address 
    add  $t7,$t7,1    # increment index 
    blt  $t7,$s1,prtlocloop  # done? if no, loop 

exit: 
    li  $v0,10     # exit program 
    syscall 

# prtnum -- print a number with a prefix string on a single line 
# 
# arguments: 
# s6 -- prefix string 
# s7 -- value to print 
# 
# registers: 
# v0 -- syscall number [trashed] 
# a0 -- syscall argument [trashed] 
prtnum: 
    li  $v0,4     # syscall: print string 
    move $a0,$s6     # string to print 
    syscall 

    li  $v0,1     # syscall: print integer 
    move $a0,$s7     # value to print 
    syscall 

    li  $v0,4     # syscall: print string 
    la  $a0,msgnl 
    syscall 

    jr  $ra      # return 

UPDATE:

Was genau ist der Unterschied zwischen print und prtnum?

print ist die Bezeichnung für den Anfang der Schleife, die die Werte in loc druckt. prtnum ist Unterprogramm/Funktion, die den Druck einer einzigen Nummer ausführt.

Ich habe prtnum hinzugefügt, um die Verwendung einer Funktion zu demonstrieren und unnötige Replikation von Code zu vermeiden.

Können sie nicht richtig zusammengeführt werden?

Sicher, mit einigen Vorbehalten. Ich habe einen leichten/kosmetischen Schnitt gemacht, um die Dinge klarer zu machen. Insbesondere habe ich print: in prtlocloop: umbenannt, um seine Rolle klarer zu machen.

The syscall(1) für "print integer" druckt nur die ganze Zahl, aber tut sie nicht alle Leerzeichen oder Newline hinzufügen zu trennen (das heißt, es ist genau wie printf("%d",a0)). Also, wir brauchen etwas.

Ursprünglich hatte ich gerade die syscall(print_integer). Damit erhalten wir eine "sehr lange" Nummer. Dann fügte ich syscall(4) hinzu, um eine neue Zeile zu drucken. Das war in Ordnung, außer dass die Ausgabe ein wenig verwirrend war, welcher Wert j war und welche die loc Werte waren.

(1) Also, ich habe die "Präfix" Zeichenfolge hinzugefügt. Also, das wurden drei syscalls für jede Nummer.

(2) Dies wurde in zwei Stellen verwendet: Zum Drucken jund die loc Werte zu drucken.

Gleicher Code an zwei oder mehr Stellen. Das ist das Standardkriterium für "split out code to function" in beliebigen Sprache. Es ist eine Design-/Stilwahl [also gibt es keine absolute Antwort].

Also, mit (1) und (2), ich habe es auf die prtnum Funktion verschoben. Eigentlich schrieb ich die prtnum Funktion zuerst, weil ich bereits die Struktur kannte, und fügte das Präfix Argument nach der Ausgabe "sah hässlich" [zu mir].

Als ich es zuerst codiert habe, habe ich "j: " für j verwendet und ein " " Präfix für loc verwendet. Es sah immer noch ein bisschen funky aus. Also habe ich das Präfix auf "loc: " geändert, um konsistent zu sein.

Könnte es inline sein? Sicher. Neben dem Ausdruck der Zahl selbst müssen wir noch einen Separator hinzufügen. Also, wir brauchen zwei syscalls pro Nummer, um es zu tun.

Der Separator könnte ein Leerzeichen sein, wenn wir alle Zahlen auf die gleiche Zeile schreiben wollen. Gut für kurze Vektoren. Dies würde eine geringfügige Änderung des Codes erfordern, so wie er jetzt existiert, und wir müssten eine Endausgabe von Newline hinzufügen, um die Zeile zu schließen. Bei längeren Arrays [die möglicherweise nicht in eine einzige Zeile passen] ist einer pro Zeile [wahrscheinlich] aufgeräumter.

Wir mussten nur j und loc drucken. Wenn das Problem besagt, dass wir a, dann j und dann loc drucken mussten, wäre ich in die andere Richtung gegangen.

Ich hätte prtlocloop in eine andere Funktion (z. B. prtarray) geändert, die auf dem gegebenen Array Schleife und prtnum für jedes Element aufrufen würde.

Der erste Schritt war, die Berechnungsschleife korrekt zu bekommen. Die zweite war das Drucken. Aber manchmal müssen sie zusammen gemacht werden. (d. h.) Wie können Sie etwas debuggen, das Sie nicht sehen können?

Mit korrekter Berechnung können Sie den Ausgabedruck beliebig umkodieren. Die prtnum war nur meine Weg. Aber es ist durch keine bedeutet die einzige Möglichkeit.


über die grundlegenden Mechanismen der Zusammenarbeit mit den asm Anweisungen arbeiten, sind die Wahlen wie in einer anderen Sprache, [insbesondere C]. Kommentar gut, wählen Sie die einfachste und effektivste Möglichkeit, den Code zu entwerfen/zu teilen, verwenden Sie beschreibende Variablennamen usw. Kommentare sollten "Absicht", das "was/warum" zeigen. Die asm-Anweisungen sind das "Wie".

Randbemerkung: Einige OPs haben ernsthafte Schwierigkeiten hatte, zu verstehen, wie sll [die Sie bereits verstehen] funktioniert. Sie haben einfach nicht "verstanden", dass eine Linksverschiebung um 2 wie eine Multiplikation mit 4 war und konvertiert einen Indexwert in Byte/Adressoffset. So, Sie können bereits dem Spiel voraus sein ...

Gestern gab ich eine Antwort für eine Mips-Frage, wo ich den anderen Weg ging und empfahl, zwei Funktionen inlining. Das Problem bestand darin, sin(x) unter Verwendung einer Taylor-Reihenentwicklung [Summierung der Terme] des Formulars zu berechnen: x**(2n)/factorial(2n-1).

Mit Inlining war es möglich, Teilergebnisse aus dem vorherigen Begriff in der Reihe ohne wieder zu verwenden, müssen jeden Begriff von Grund auf neu berechnen. Dies wäre mit mehreren Funktionen nicht möglich gewesen.

Ich habe den Mips-Code nicht geschrieben, aber ich schrieb den C/Pseudo-Code: mips program to calculate sin(x) Der resultierende Mips-Code wäre [wahrscheinlich] einfacher gewesen und würde definitiv schneller laufen.