Artikel-Schlagworte: „Matrix“

Berechnung der Determinante einer NxN Matrix in MIPS Assembler

Samstag, 6. Februar 2010

Dieser Assemblercode kann die Determinante einer NxN Matrix berechnen. Dabei wird die NxN-Matrix mit Hilfe des Laplaceschen Entwicklungssatzes in N N-1xN-1-Matrixen zerlegt (eine 4×4-Matrix wird in 4 3×4-Matrixen zerlegt, usw).

Das Programm funktioniert leider nicht für “große” Matrixen (z.B. 10×10), weil das Programm dann aus dem Heap raus schreibt. Aber für Matrixen bis 5×5 funktioniert es definitiv, eventuell auch für Größere, das habe ich aber nicht getestet.

.data

dim:
#.word 2
#.word 3
#.word 4
.word 5
#.word 10

matrix:
#2x2-Martrix; solution = -2
#.word 3, 4, 5, 6
#3x3-Matrixx; solution = 4
#.word 3, 4, 5, 9, 4, 9, 1, 2, 2
#4x4-Matrix; solution = -19
#.word 3, 4, 5, 9, 4, 9, 1, 2, 2, 1, 3, 5, 7, 9, 3, 5
#solution = 0
.word 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25
#.word -2, 1, 1, -2, -1, -1, -1, 3, -2, -3, 0, -1, -1, -4, -5, -5, 3, 2, -4, 1, 1, 1, 0, 3, 2, 0, 3, 3, 2, 0, 2, -3, 0, -1, 4, -1, -2, -3, 0, -1, 2, -3, -3, -5, -3, -5, -3, -1, -5, 3, 0, -5, 0, -5, -5, -1,  2,  1, -4, -1, -5, -4,  1,  1, -4,  2,  3,  4, -3, -1, 1, -2,  0, -3,  1, -5,  4,  3, -1, -4, -5,  2, -5, -2, -3, -3,  3, -2,  1, -3, 1, -1, -3,  2,  0, -5,  3,  2,  3, -5

det:    .asciiz "Die Determinante ist: "
###############################################################################

.text

main:
###################################
###################################
#Hier soll das Programm stehen
#Das Ergebnis muss am Ende in $s6 stehen.

    addi    $s7, $0, 0x10040000 # initialize default heap pointer
 
    la      $a0, matrix
    lw      $a1, dim
    jal     calcDet
    addi    $s6, $v0, 0

    j       ende

###################################
# Calculates the determinante of a NxN matrix.
# expects:  adress of array with N^2 elements in $a0
#           dimension of matrix in $a1
# returns det. of given matrix in $v0
###################################
# Documentation
# $a0   -> matrix
# $a1   -> dim
# ----------------
# $s0   -> sum of det. of matrixes
calcDet:
    # if
    slti    $t0, $a1, 2         # if $a1(=dim) < 2
    beq     $0, $t0, calcDetIf  # jump to the if part
                                # if $t0 == $t1(=1) or
                                # if 2 < dim
    # else
    lw      $v0, 0($a0)
    jr      $ra                 # jump back to caller

calcDetIf:                      # The if part
    # reduce maxtrix and call recursive for each
    # reduced matrix
    addi    $sp, $sp, -4        # get some space on stack
    sw      $ra, 0($sp)         # store return adress in stack
   
    addi    $s0, $0, 0          # initialize $s0 (sum) with 0
    addi    $t0, $0, 0          # initialize $t0 (i) with 0
   
calcDetLoop:                    # loop over all sub-matrixes
    beq     $t0, $a1, calcDetLoopDone
                                # while $t0(=i) != $a1(=dim)

    addi    $sp, $sp, -16       # get some space on stack
    sw      $s0, 0($sp)         # store sum
    sw      $t0, 4($sp)         # store $t0 (i)
    sw      $a0, 8($sp)         # store $a0
    sw      $a1, 12($sp)        # store $a1

                                # dont need to copy any adress
                                # in $a0 because its already there
                                # dont need to copy any dimension
                                # of matrix in $a1 because its already there
    addi    $a2, $t0, 0         # copy i into $a2
    jal     reduceMatrix        # jump to reduceMatrix
   
    addi    $a0, $v0, 0         # copy result adress of reduceMatrix
                                # into argument register for recursive call
    sub     $a1, $a1, 1         # $a1 = dim - 1
    jal     calcDet             # call myself recursively
   
    lw      $a1, 12($sp)        # load $a1
    lw      $a0, 8($sp)         # load $a0
    lw      $t0, 4($sp)         # load $t0 (i)
    lw      $s0, 0($sp)         # load sum
    addi    $sp, $sp, 16        # clean up the stack

    addi    $t2, $0, 1          # initialise pow(er) with 1
    and     $t1, $t0, 1         # $t1 = $t0 & 0x00000001
    beq     $t1, $0, calcDetLoopJump
                                # skip one line, if $t0 % 2 = 1
    addi    $t2, $0, -1         # set power to -1
calcDetLoopJump:               

    addi    $t1, $0, 4          # $t1 = 4 (because one word = 4 bytes)
    mult    $t0, $t1            # i * $t1(=4)
    mflo    $t3                 # get result in $t3
    add     $t3, $a0, $t3       # add calulated offset to start adress of matrix
    lw      $t3, 0($t3)         # load value at calulated adress

    mult    $t2, $t3            # power * (i-th element in matrx (always in first row))
    mflo    $t4                 # get result in $t4
    mult    $t4, $v0            # multiply calcDet-result with calculated stuff
    mflo    $t4                 # get result in $t4
    add     $s0, $s0, $t4       # add to sum

    addi    $t0, $t0, 1         # $t0++ or i++

    j       calcDetLoop


calcDetLoopDone:                # loop finished, calculated all matrixes
    addi    $v0, $s0, 0         # copy sum into return register

    lw      $ra, 0($sp)         # load return adress from stack
    addi    $sp, $sp, 4         # get some space on stack

    jr      $ra
###################################


###################################
# Extracts _one_ (N-1) matrix from a
# NxN matrix. The argument
# $a2 says which (N-1) matrix should
# be returned.
# expects:  adress of array with N^2 elements in $a0
#           dimension of matrix in $a1
#           index of the smaller matrix (0-based) in $a2
# returns adress of (N-1) matrix in $v0
###################################
# Documentation
# $a0   -> matrix
# $a1   -> dim
# $a2   -> index of matrix that should be returned
# ----------------
# $s0   -> dim - 1
# $s1   -> counter for the current position in the SOURCE matrix
# $s2   -> counter for the current position in the TARGET matrix
reduceMatrix:
    addi    $s0, $a1, -1        # $s0 = dim - 1
    mult    $s0, $s0            # $s0*$s0 or
                                # (N-1)^2 or (dim-1)^2
                                # is also the size of the
                                # new matrix
    mflo    $t1                 # get result into $t1
                                # $t1 contains now the number of words required
    addi    $s1, $a0, 0         # copy start adress of source matrix in $s1
    addi    $s2, $s7, 0         # save current position of heap pointer in $s2
    addi    $v0, $s7, 0         # copy target array adress in return register
    addi    $t0, $0, 4          # set $t0 = 4
    mult    $t1, $t0            # multiply $t0(=4) * $t1(=(dim-1)^2)
                                # -> amount of necessary bytes
    mflo    $t0                 # copy result in $t0
    add     $s7, $s7, $t0       # get (N-1)^2 space of heap

    addi    $t0, $0, 4          # set $t0 = 4
    mult    $a1, $t0            # $a1(=dim)*$t0(=4)
                                # number of bytes that must be skipped to
                                # skip the first row of source matrix
    mflo    $t0
    add     $s1, $s1, $t0       # skip dim-elements or
                                # skip the first row of source matrix
    addi    $t0, $s0, 0         # initialize counter for loop 1; used as i
    add     $t2, $t0, $a2       # $t2 = $t0 + $a2 or
                                # $t2 = i + index
reduceMatrixLoop1:              # loop 1
    beq     $t2, $t0, reduceMatrixLoop1Done
                                # while index != i

    lw      $t1, 0($s1)         # load next entry from source matrix in $t1
    sw      $t1, 0($s2)         # store the entry in the target matrix
    addi    $s1, $s1, 4         # increase pointer $s1 = $s1 + 4
    addi    $s2, $s2, 4         # increase pointer $s2 = $s2 + 4

    addi    $t0, $t0, 1         # $t0 = $t0 + 1 or
                                # $t0++ (i++)
    j       reduceMatrixLoop1   # jump up to the beginning of the loop
reduceMatrixLoop1Done:          # endloop 1
reduceMatrixLoop2:              # loop 2
    mult    $a1, $a1
    mflo    $t1
    beq     $t0, $t1, reduceMatrixLoop2Done
                                # while i < dim^2

    addi    $s1, $s1, 4         # skip one element in SOURCE matrix
    addi    $t0, $t0, 1         # $t0 = $t0 + 1 or
                                # $t0++ or i++
   
    mult    $a1, $a1            # $a1^2 or dim^2
    mflo    $t2                 # get the result in $t2
    sub     $t2, $t2, 1         # sub 1 from $t2, because I want to compare
                                # it with $t0 (0-based) or
                                # (dim^2)-1
    sub     $t2, $t2, $t0       # $t2-$t0 or (dim^2)-1-i
    # if
    slt     $t3, $t2, $s0       # (dim^2)-1-i < dim-1 or
                                # if $t3 == 1, you reached the last
                                # row and already skipped
    beq     $t3, $0, reduceMatrixLoop2Else
                                # jumps, if (dim-1)^2 - i < dim - 1 is false
                                # in human words: if the programm
                                # is NOT in the last row of the source matrix
    # if case
    # copy the last elements of source into target and exit function! :)
    mult    $a1, $a1            # dim^2
    mflo    $t2                 # get result in $t2
reduceMatrixLoop2Loop1:
    beq     $t2, $t0, reduceMatrixLoop2Done
                                # jump if dim^2-1 == i or jump if
                                # end of source matrix is reached
   
    lw      $t1, 0($s1)         # load next entry from source matrix in $t1
    sw      $t1, 0($s2)         # store the entry in the target matrix
    addi    $s1, $s1, 4         # increase pointer $s1 = $s1 + 4
    addi    $s2, $s2, 4         # increase pointer $s2 = $s2 + 4

    addi    $t0, $t0, 1         # $t0 = $t0 + 1 or
                                # $t0++ or i++
    j       reduceMatrixLoop2Loop1
                                # jump up again, because its a loop!

    j       reduceMatrixLoop2Endif
                                # leave if case
                                # never used because if loop is finished
                                # it will jump to the end of the function

reduceMatrixLoop2Else:          # else case
    # copy dim - 1 elements
    add     $t2, $t0, $s0       # $t2 = $t0(=i) + $s0(=dim-1)
reduceMatrixLoop2ElseLoop:
    beq     $t2, $t0, reduceMatrixLoop2
                                # if $t2 = $t0 jump to main loop

    lw      $t1, 0($s1)         # load next entry from source matrix in $t1
    sw      $t1, 0($s2)         # store the entry in the target matrix
    addi    $s1, $s1, 4         # increase pointer $s1 = $s1 + 4
    addi    $s2, $s2, 4         # increase pointer $s2 = $s2 + 4
   
    addi    $t0, $t0, 1         # $t0 = $t0 + 1 or
                                # $t0++ or i++
    j       reduceMatrixLoop2ElseLoop
                                # jump up to the beginning of the loop

reduceMatrixLoop2Endif:         # endif


    addi    $t0, $t0, 1         # $t0 = $t0 + 1 or
                                # $t0++ or i++
    j       reduceMatrixLoop2   # jump up to the beginning of the loop
reduceMatrixLoop2Done:          # endloop 2
    jr      $ra                 # jump to caller function
                                # return value ($v0) is set at beginning
                                # of this function
###################################

###################################
###################################

ende:
#Ausgabe

la $a0 det
li $v0 4
syscall

move $a0, $s6
li $v0, 1
syscall

###################################
#Ende
li $v0, 10
syscall