2016-07-19 13 views
2

Ich nehme gerade einen Kurs in Assembly und habe Probleme mit der folgenden Aufgabe.MIPS Fragen zum Schreiben von Assembly zum Aufruf von Funktionen auf einem Array

Schreiben Sie ein Programm, das (mit einer entsprechenden Eingabeaufforderung) eine Folge von 20 Ganzzahlen liest und sie in einem Array speichert und dann die folgenden drei Funktionen aufruft und die Ergebnisse in einem lesbaren Format ausgibt.

Die drei Funktionen sind: smallestLargest: der kleinsten und der größten Werte im Array berechnet. teilbar: berechnet die Anzahl der Ganzzahlen im Array, die durch 4 teilbar sind SumProduct: berechnet die Summe und das Produkt der ganzen Zahlen.

Ich schrieb Assembler-Code (unten), um zu versuchen, dieses Problem zu lösen, aber ich kann nicht die richtige Ausgabe mit Ausnahme der größten Zahl im Array erhalten. Alles andere gibt mir ein Problem. Ich habe keine Ahnung, was los ist, und ich habe in den letzten anderthalb Wochen daran gearbeitet, daher würde jede Hilfe sehr geschätzt werden.

.data 
array: .space 80 
newLine:.asciiz "\n" # I will use it to start a new line 
space: .asciiz " "  # I will use it to have a space 
Prompt: .asciiz "\n Enter an integer: " 
Result1:.asciiz "The smallest value in the array is " 
Result2:.asciiz "The largest value in the array is " 
Result3:.asciiz "The number of integers divisible by 4 is " 
Result4:.asciiz "The sum of the integers is " 
Result5:.asciiz "The product of the integers is " 
     .globl main 
     .text 
main: 
     li $t0,20  # $t0 keeps track of the number of integers to be read 
     la $t1,array # loading the starting address of an array 
loopQ: 
     la $a0,Prompt 
     li $v0,4 
     syscall 
     li $v0,5  # reading an integer 
     syscall 
     sw $v0,0($t1) # storing the integer entered 
     add $t0,$t0,-1 # decrement the number of integers by one 
     add $t1,$t1,4 # load the address of the next integer 
     bgtz $t0,loopQ # branch to read and store the next integer 
     li $t0,20 
     la $t1,array 
smallestLargest: 
     lw $v0,0($t1) # $v0 = Mem($t1) 
     move $v1,$v0 # $v1 = $v0 
     addi $t0,$t0,-1 # decrement $t0 
     blez $t0,ret1 # while ($t0 > 0) 
loopR: 
     addi $t1,$t1,4 
     lw $t2,0($t1) # load $t1 to $t2 
     bge $t2,$v0,next# if ($t2 < $v0) 
     move $v0,$t2 # $v0 = $t2 
     b chk 
next: 
     ble $t2,$v1,chk # if ($t2 > $v1) 
     move $v1,$t2 # $v1 = $t2 
chk: 
     addi $t0,$t0,-1 # decrement t0 
     bgtz $t0,loopR 
ret1: 
     li $v0,4  # system call code for print_str 
     la $a0,Result1 # load address of Result1 into $a0 
     syscall 
     move $a0,$v0 # move value to be printed to $a0 
     li $v0,1  # system call code for print_int 
     syscall 
     la $a0,newLine # start a new line 
     li $v0,4 
     syscall 
     li $v0,4  # system call code for print_str 
     la $a0,Result2 # load address of Result2 into $a0 
     syscall 
     move $a0,$v1 # move value to be printed to $a0 
     li $v0,1  # system call code for print_int 
     syscall 
     la $a0,newLine # start a new line 
     li $v0,4 
     syscall 
     li $t0,20  # initialize length parameter 
     la $t1,array # initialize address parameter 
Div4: 
     li $v0,0  # $v0 = 0 
     li $t3,3  # $t3 = 3 
     b skip 
loopS: 
     lw $t2,0($t1) # $t2 = Mem($t1)  
     addi $t1,$t1,4 # $t1 = $t1 + 4 
     and $t4,$t2,$t3 # $t4 = $t2 & $t3 
     bnez $t4,skip # if ($t4 == 0) 
     addi $v0,$v0,1 # $v0 = $v0 + 1 
skip: 
     addi $t0,$t0,-1 # $t0 = $t0 - 1 
     bgez $t0,loopS # if $t0 > 0 
ret2: 
     li $v0,4  # system call code for print_str 
     la $a0,Result3 # load address of Result3 into $a0 
     syscall 
     move $a0,$v0 # move value to be printed to $a0 
     li $v0,1  # system call code for print_int 
     syscall 
SumAMult: 
     la $a0,newLine # start a new line 
     li $v0,4 
     syscall 
     li $v0,4  # system call code for print_str 
     la $a0,Result4 # load address of Result4 into $a0 
     syscall 
     li $t0,20  # initialize length parameter 
     la $t1,array # initialize address parameter 

     jal sum   # call sum 

     move $t1,$v0 # move value to be printed to $t1 
     li $v0,1  # system call code for print_int 
     syscall 
     la $a0,newLine # start a new line 
     li $v0,4 
     syscall 
     li $v0,4  # system call code for print_str 
     la $a0,Result5 # load address of Result5 into $a0 
     syscall 
     li $v0,1  # system call code for print_int 
     move $t1,$v1 # move value to be printed from $v1 
     syscall 
     li $v0,10 
     syscall 
sum:  
     li $v0,0  # $v0 will hold the sum 
     li $v1,0  # $v1 will hold the product 
loopT: 
     blez $t0, retzz # If t0 <= branch 
     addi $t0,$t0,-1 # decrement loop count 
     lw $t5,0($t1) # get a value from the array 
     addi $t1,$t1,4 # increment array pointer 
word: 
     add $v0,$v0,$t5 # add to sum 
     # mult $v1,$t5 # multiply to product 
     b loopT   # branch to loopT  
retzz: 
     jr $ra 

Unten ist die Ausgabe ich nach der Eingabe ganzer Zahlen bin immer 1-20

The smallest value in the array is 4 
The largest value in the array is 20 
The number of integers divisible by 4 is 4 
The sum of the integers is 268501210 
The product of the integers is 2685
+1

Verwenden Sie Ihren Debugger/Simulator zu einzelnen Schritt der Code und sehen, wo es schief geht. Hinweis: Bei 'ret2' überschreiben Sie den Wert in' $ v0', den Sie später drucken wollen ... PS: Sie wissen, dass Sie keine Funktionen geschrieben haben, oder? (Außer für "Summe") – Jester

+0

@Jester - danke für die schnelle Antwort und Hilfe. Wie kommt es, dass mein Code keine Funktionen erfüllt? Ich bin immer noch sehr neu in der Montage und stecken geblieben, also jede Hilfe würde geschätzt werden! –

+0

Sie lesen ganze Zahlen in "main", dann folgen Sie sofort durch Berechnen von min/max. Wenn ein anderer Teil Ihrer App sie erneut verwenden möchte, kann sie nicht aufgerufen werden. Um es zu einer Funktion zu machen, verschieben Sie den Code außerhalb von main und rufen Sie ihn mit 'jal' auf. (und einen Weg definieren, wie die Funktionsparameter, d. h. die Array-Länge, der Array-Zeiger und wie sie Min und Max zurückgibt, angegeben werden. Auf diese Weise können Sie dann die Min/Max-Funktion von jedem Teil der App aus auf jedem beliebigen Array aufrufen. – Ped7g

Antwort

3

ich Ihren Code festgelegt haben. Ich habe versucht, ihm möglichst treu zu bleiben, musste aber erhebliche Umstrukturierungen vornehmen.

Ein paar Dinge zu beachten.

Ich habe Funktionen erstellt (nach der Problembeschreibung). Das heißt, anstelle von einem langen main, teile ich Dinge auf, genau wie in C. Ich habe Blockkommentare für jede Funktion hinzugefügt.

In main, ich die Array-Anzahl in $s0 und die Array-Basisadresse in $s1 geladen. Die Berechnungsfunktionen richten ihre Werte hieraus ein, anstatt den Code zu replizieren. (d. h.) Die Array-Adresse und der Zählwert können in einen Ort eingestellt/geändert werden, falls gewünscht.

Ich habe einige Kommentare in der Seitenleiste geändert, um die Absicht besser zu beschreiben, anstatt lediglich die Mechanik der Anweisungen neu zu formulieren.

Ich habe auch die Etiketten so war es einfacher, sie zu den Funktionen entsprechen sie in waren (zum Beispiel alle Etiketten in Funktion foo sind foo_blah)

I statische Testdaten erstellt Tests zu beschleunigen. Notieren Sie den auskommentierten jal dataread, um den Benutzer tatsächlich aufzufordern.


Hier ist der korrigierte Code:

.data 
array:  .space  80 

array2:  .word  3, 3, 3, 17, 3 
      .word  3, 24, 3, 3, 4 
      .word  -4, -8, 97, 3, 2 
      .word  3, 3, 3, 3, 3 

newLine: .asciiz  "\n"   # I will use it to start a new line 
space:  .asciiz  " "    # I will use it to have a space 

Prompt:  .asciiz  "\n Enter an integer: " 
msg_min: .asciiz  "The smallest value in the array is " 
msg_max: .asciiz  "The largest value in the array is " 
msg_div4: .asciiz  "The number of integers divisible by 4 is " 
msg_sum: .asciiz  "The sum of the integers is " 
msg_prod: .asciiz  "The product of the integers is " 

    .globl main 
    .text 

main: 
    li  $s0,20     # set array count 
    la  $s1,array2    # set array address 

    # NOTE: uncomment this to really prompt user (vs. testing) 
    ###jal  dataread   # prompt user for data 

    jal  minmax     # compute minimum/maximum 
    jal  div4     # count number divisible by 4 
    jal  sumprod     # compute sum and product 

    li  $v0,10 
    syscall 

# dataread -- prompt user for data 
# 
# registers: 
# t0 -- remaining count 
# t1 -- array address pointer 
dataread: 
    move $t0,$s0     # initialize array count 
    move $t1,$s1     # initialize array pointer 

dataread_loop: 
    la  $a0,Prompt 
    li  $v0,4 
    syscall 

    li  $v0,5     # reading an integer 
    syscall 
    sw  $v0,0($t1)    # storing the integer entered 

    add  $t0,$t0,-1    # decrement the number of integers by one 
    add  $t1,$t1,4    # load the address of the next integer 
    bgtz $t0,dataread_loop  # branch to read and store the next integer 

    jr  $ra      # return 

# minmax -- compute min/max 
# 
# registers: 
# t0 -- remaining count 
# t1 -- array address pointer 
# t2 -- minimum value 
# t3 -- maximum value 
# t4 -- current array value 
minmax: 
    move $t0,$s0     # initialize array count 
    move $t1,$s1     # initialize array pointer 

    lw  $t2,0($t1)    # initialize smallest 
    move $t3,$t2     # initialize largest 

    add  $t1,$t1,4    # load the address of the next integer 
    addi $t0,$t0,-1    # decrement remaining count 

minmax_loop: 
    blez $t0,minmax_done   # at end of array? if yes, fly 

    lw  $t4,0($t1)    # fetch current array element 
    add  $t1,$t1,4    # load the address of the next integer 
    addi $t0,$t0,-1    # decrement remaining count 

    bge  $t4,$t2,minmax_notlt # new minimum? if no, fly 
    move $t2,$t4     # yes, set it 

minmax_notlt: 
    ble  $t4,$t3,minmax_loop  # new maximum? if no, loop 
    move $t3,$t4     # yes, set it 
    b  minmax_loop 

minmax_done: 
    li  $v0,4     # system call code for print_str 
    la  $a0,msg_min    # message to print 
    syscall 

    move $a0,$t2     # move value to be printed to $a0 
    li  $v0,1     # system call code for print_int 
    syscall 

    la  $a0,newLine    # start a new line 
    li  $v0,4 
    syscall 

    li  $v0,4     # system call code for print_str 
    la  $a0,msg_max    # message to print 
    syscall 

    move $a0,$t3     # move value to be printed to $a0 
    li  $v0,1     # system call code for print_int 
    syscall 

    la  $a0,newLine    # start a new line 
    li  $v0,4 
    syscall 

    jr  $ra      # return 

# div4 -- get number of integers divisible by 4 
# 
# registers: 
# t0 -- remaining count 
# t1 -- array address pointer 
# t2 -- count of array elements divisible by 4 
# t4 -- current array value 
div4: 
    move $t0,$s0     # initialize array count 
    move $t1,$s1     # initialize array pointer 
    li  $t2,0     # initialize count 

div4_loop: 
    blez $t0,div4_done   # at end of array? if yes, fly 

    lw  $t4,0($t1)    # fetch current array value 
    add  $t1,$t1,4    # load the address of the next integer 
    addi $t0,$t0,-1    # decrement remaining count 

    andi $t4,$t4,0x03   # divisible by 4? 
    bnez $t4,div4_loop   # no, loop 
    addi $t2,$t2,1    # yes, increment count 
    b  div4_loop    # loop 

div4_done: 
    li  $v0,4     # system call code for print_str 
    la  $a0,msg_div4   # message to print 
    syscall 

    move $a0,$t2     # move value to be printed to $a0 
    li  $v0,1     # system call code for print_int 
    syscall 

    la  $a0,newLine    # start a new line 
    li  $v0,4 
    syscall 

    jr  $ra 

# sumprod -- compute sum and product 
# 
# registers: 
# t0 -- remaining count 
# t1 -- array address pointer 
# t2 -- summation value 
# t3 -- product value 
# t4 -- current array value 
sumprod: 
    move $t0,$s0     # initialize array count 
    move $t1,$s1     # initialize array pointer 

    li  $t2,0     # initialize sum 
    li  $t3,1     # initialize product 

sumprod_loop: 
    blez $t0,sumprod_done  # at end of array? if yes, fly 

    lw  $t4,0($t1)    # fetch current array value 
    add  $t1,$t1,4    # load the address of the next integer 
    addi $t0,$t0,-1    # decrement remaining count 

    add  $t2,$t2,$t4    # compute the sum 
    mul  $t3,$t3,$t4    # compute the product 
    b  sumprod_loop 

sumprod_done: 
    li  $v0,4     # system call code for print_str 
    la  $a0,msg_sum    # message to print 
    syscall 

    move $a0,$t2     # move value to be printed to $a0 
    li  $v0,1     # system call code for print_int 
    syscall 

    la  $a0,newLine    # start a new line 
    li  $v0,4 
    syscall 

    li  $v0,4     # system call code for print_str 
    la  $a0,msg_prod   # message to print 
    syscall 

    move $a0,$t3     # move value to be printed to $a0 
    li  $v0,1     # system call code for print_int 
    syscall 

    la  $a0,newLine    # start a new line 
    li  $v0,4 
    syscall 

    jr  $ra      # return 

Hier ist eine kompaktere Variante, die einen Trick von Sorten verwendet. Es verwendet das Äquivalent von "Tail Call Optimization", um eine gemeinsame Druckroutine zu haben, anstatt den Druckcode in jeder Berechnungsfunktion zu replizieren.

Das heißt, der „Trick“ ist die Berechnungsfunktionen Argumente für den Druck einrichten und dann Sprung, um es über j [stattdessen eine zweite Ebene zu tun jal] und die Druckfunktion wird die jr $ra, die normalerweise wären getan durch die Berechnungsfunktionen.

Wie auch immer, hier ist der Code:

.data 
array:  .space  80 

array2:  .word  3, 3, 3, 17, 3 
      .word  3, 24, 3, 3, 4 
      .word  -4, -8, 97, 3, 2 
      .word  3, 3, 3, 3, 3 

newLine: .asciiz  "\n"   # I will use it to start a new line 
space:  .asciiz  " "    # I will use it to have a space 

Prompt:  .asciiz  "\n Enter an integer: " 
msg_min: .asciiz  "The smallest value in the array is " 
msg_max: .asciiz  "The largest value in the array is " 
msg_div4: .asciiz  "The number of integers divisible by 4 is " 
msg_sum: .asciiz  "The sum of the integers is " 
msg_prod: .asciiz  "The product of the integers is " 

    .globl main 
    .text 

main: 
    li  $s0,20     # set array count 
    la  $s1,array2    # set array address 

    # NOTE: uncomment this to really prompt user (vs. testing) 
    ###jal  dataread   # prompt user for data 

    jal  minmax     # compute minimum/maximum 
    jal  div4     # count number divisible by 4 
    jal  sumprod     # compute sum and product 

    li  $v0,10 
    syscall 

# dataread -- prompt user for data 
# 
# registers: 
# t0 -- remaining count 
# t1 -- array address pointer 
dataread: 
    move $t0,$s0     # initialize array count 
    move $t1,$s1     # initialize array pointer 

dataread_loop: 
    la  $a0,Prompt 
    li  $v0,4 
    syscall 

    li  $v0,5     # reading an integer 
    syscall 
    sw  $v0,0($t1)    # storing the integer entered 

    add  $t0,$t0,-1    # decrement the number of integers by one 
    add  $t1,$t1,4    # load the address of the next integer 
    bgtz $t0,dataread_loop  # branch to read and store the next integer 

    jr  $ra      # return 

# minmax -- compute min/max 
# 
# registers: 
# t0 -- remaining count 
# t1 -- array address pointer 
# t2 -- minimum value 
# t3 -- maximum value 
# t4 -- current array value 
minmax: 
    move $t0,$s0     # initialize array count 
    move $t1,$s1     # initialize array pointer 

    lw  $t2,0($t1)    # initialize smallest 
    move $t3,$t2     # initialize largest 

    add  $t1,$t1,4    # load the address of the next integer 
    addi $t0,$t0,-1    # decrement remaining count 

minmax_loop: 
    blez $t0,minmax_done   # at end of array? if yes, fly 

    lw  $t4,0($t1)    # $v0 = Mem($t1) 
    add  $t1,$t1,4    # load the address of the next integer 
    addi $t0,$t0,-1    # decrement remaining count 

    bge  $t4,$t2,minmax_notlt # new minimum? if no, fly 
    move $t2,$t4     # yes, set it 

minmax_notlt: 
    ble  $t4,$t3,minmax_loop  # new maximum? if no, loop 
    move $t3,$t4     # yes, set it 
    b  minmax_loop 

minmax_done: 
    la  $a2,msg_min    # first message 
    la  $a3,msg_max    # second message 
    j  print 

# div4 -- get number of integers divisible by 4 
# 
# registers: 
# t0 -- remaining count 
# t1 -- array address pointer 
# t2 -- count of array elements divisible by 4 
# t4 -- current array value 
div4: 
    move $t0,$s0     # initialize array count 
    move $t1,$s1     # initialize array pointer 
    li  $t2,0     # initialize count 

div4_loop: 
    blez $t0,div4_done   # at end of array? if yes, fly 

    lw  $t4,0($t1)    # fetch current array value 
    add  $t1,$t1,4    # load the address of the next integer 
    addi $t0,$t0,-1    # decrement remaining count 

    andi $t4,$t4,0x03   # divisible by 4? 
    bnez $t4,div4_loop   # no, loop 
    addi $t2,$t2,1    # yes, increment count 
    b  div4_loop    # loop 

div4_done: 
    la  $a2,msg_div4   # first message 
    li  $a3,0     # _no_ second message 
    j  print 

# sumprod -- compute sum and product 
# 
# registers: 
# t0 -- remaining count 
# t1 -- array address pointer 
# t2 -- summation value 
# t3 -- product value 
# t4 -- current array value 
sumprod: 
    move $t0,$s0     # initialize array count 
    move $t1,$s1     # initialize array pointer 

    li  $t2,0     # initialize sum 
    li  $t3,1     # initialize product 

sumprod_loop: 
    blez $t0,sumprod_done  # at end of array? if yes, fly 

    lw  $t4,0($t1)    # fetch current array value 
    add  $t1,$t1,4    # load the address of the next integer 
    addi $t0,$t0,-1    # decrement remaining count 

    add  $t2,$t2,$t4    # compute the sum 
    mul  $t3,$t3,$t4    # compute the product 
    b  sumprod_loop 

sumprod_done: 
    la  $a2,msg_sum    # first message 
    la  $a3,msg_prod   # second message 
    j  print 

# print -- common print function 
# 
# arguments: 
# a2 -- first message 
# t2 -- first value 
# a3 -- second message 
# t3 -- second value 
print: 
    beqz $a2,print_skip1   # skip if no first argument 
    li  $v0,4     # system call code for print_str 
    move $a0,$a2     # get address of first message 
    syscall 

    move $a0,$t2     # move value to be printed to $a0 
    li  $v0,1     # system call code for print_int 
    syscall 

    la  $a0,newLine    # start a new line 
    li  $v0,4 
    syscall 

print_skip1: 
    beqz $a3,print_skip2   # skip if no second argument 
    li  $v0,4     # system call code for print_str 
    move $a0,$a3     # get address of second message 
    syscall 

    move $a0,$t3     # move value to be printed to $a0 
    li  $v0,1     # system call code for print_int 
    syscall 

    la  $a0,newLine    # start a new line 
    li  $v0,4 
    syscall 

print_skip2: 
    jr  $ra      # return 

Hier ist ein C-Programm, das ich für den Vergleich Tests und Validierung verwendet:

// mipsmmdsp/mipsmmdsp -- check validity of mask for divisibility 

#include <stdio.h> 
#include <stdlib.h> 

int array2[20] = { 
    3,3,3,17,3, 
    3,24,3,3,4, 
    -4,-8,97,3,2, 
    3,3,3,3,3 
}; 

// sumprod -- calculate sum and product 
void 
sumprod(void) 
{ 
    int idx; 
    int val; 
    int sum; 
    int prod; 
    int div4; 
    int min; 
    int max; 

    sum = 0; 
    prod = 1; 
    div4 = 0; 

    min = array2[0]; 
    max = array2[0]; 

    for (idx = 0; idx < 20; ++idx) { 
     val = array2[idx]; 

     if (val < min) 
      min = val; 
     if (val > max) 
      max = val; 

     if ((val % 4) == 0) 
      ++div4; 

     sum += val; 
     prod *= val; 
    } 

    printf("min=%d max=%d div4=%d sum=%d prod=%d\n",min,max,div4,sum,prod); 
} 

// modcheck -- check validity of mod mask 
void 
modcheck(void) 
{ 
    int lo; 
    int hi; 
    int val; 
    int mskflg; 
    int modflg; 
    long long cnt; 

    lo = -100; 
    hi = 100; 

    lo = -2000000000; 
    hi = 2000000000; 

    cnt = 0; 
    for (val = lo; val <= hi; ++val, ++cnt) { 
     mskflg = ((val & 0x03) == 0); 
     modflg = ((val % 4) == 0); 

#if 0 
     printf("modcheck: %4d/%8.8X: mskflg=%d modflg=%d\n", 
      $val,$val,$mskflg,$modflg); 
#endif 

     if (mskflg != modflg) { 
      printf("modcheck: FAIL %4d/%8.8X: mskflg=%d modflg=%d\n", 
       val,val,mskflg,modflg); 
      exit(1); 
     } 
    } 

    printf("modcheck: cnt=%lld\n",cnt); 
} 

// main -- main program 
int 
main(void) 
{ 

    modcheck(); 
    sumprod(); 

    return 0; 
} 

Randbemerkung:

Neben dem spim Simulator gibt es den mars Simulator. Es ist hier zu finden: http://courses.missouristate.edu/KenVollmar/MARS/

ich beide und es vorziehen, in den meisten Fällen mars verwendet haben - YMMV

+0

Das ist wirklich erstaunlich. Vielen Dank für deine Hilfe. Ich sollte beim Testen Dummy-Daten eingeben, so dass ich nicht jedes Mal, wenn ich die Programme ausführte, den Prozess der Eingabe von 20 Ganzzahlen durchlaufen müsste. Ich schätze all deine Hilfe. –

+0

Gern geschehen. Die Dummy-Daten waren ein Erlebnis-Aufruf. Wenn dies ein C-Programm wäre, das dazu aufgefordert wird, hätte ich wahrscheinlich eine Textdatei mit "benutzerähnlicher" Eingabe und umgeleiteter "stdin" erstellt. Aber es gibt kein Äquivalent für 'spim'. Es ist möglich, einen 'syscall' zu verwenden, um eine Datei zu öffnen und zu lesen, aber es gibt _no_ Äquivalent von' syscall 5' für _file_ input, also müsste ein 'atoi'-Äquivalent geschrieben werden [zu viel Arbeit, hier]. Also, das verließ den '.word' Ansatz, und nur ein Dummy-Array wurde benötigt. Mit mehr hätte ich nach einer Nummer gefragt: 0 = Daten manuell eingeben, 1 = Testfeld 1, 2 = Testfeld 2, ... –