MIPS 3×3 Matrix Calculator

Assembly Programming - September 26, 2024

MIPS Matrix Calculator Code

Overview

I developed a program in MIPS assembly language that performs multiplication operations on 3×3 matrices. This project required implementing fundamental matrix multiplication algorithms at the assembly level, dealing directly with memory management, register allocation, and low-level program flow control. The calculator efficiently handles the nine multiplication and addition operations required for each element in the resulting matrix.

Implementation

The program takes two 3×3 matrices as input and computes their product using nested loops. Each element in the result matrix is calculated by taking the dot product of the corresponding row from the first matrix and column from the second matrix. The implementation demonstrates efficient memory access patterns and register usage to optimize performance.

matrix_multiplier.asm

                            .data
                            prompt: .asciiz "In this format X,X,X X,X,X X,X,X X,X,X X,X,X X,X,X \n Please enter matrices values: " 
                            inputs: .space 1024 	# buffer for saving user input 
                            matrix_inputs: .space 36  # Space for two 3x3 matrices (18 halfwords * 2 bytes/halfword = 36 bytes)
                            matrix_result: .space 36  # Space for the result 3x3 matrix (9 words)
                            newline: .asciiz "\n"
                            comma: .asciiz ", "
                            result_msg: .asciiz "Result matrix:\n"
                            
                            .text
                            .globl main
                            
                            main:
                                # Display prompt string 
                                li $v0, 4 		# system call for print string 
                                la $a0, prompt 		# load prompt message 
                                syscall 
                            
                                # Getting user input 
                                li $v0, 8 		# getting user input 
                                la $a0, inputs		# buffer space 
                                la $a1, 1024 		# max number of characters 
                                syscall 
                            
                                # Initialization 
                                li $t0, 0 		# stores current character
                                li $t1, 0 		# stores current number 	
                                la $s0, matrix_inputs
                                la $a0, inputs 
                                
                            loop: 
                                lb $t0, 0($a0) 			# load first byte into t0 
                                beq $t0, 10, endstore		# check if t0 has newline character 
                                beq $t0, 44, storenumber 	# check if t0 has ',' comma 
                                beq $t0, 32, storenumber 	# check if t0 has ' ' empty space 
                                subi $t0, $t0, 48 		# convert from ascii to integer 
                                mul $t1, $t1, 10 		
                                add $t1, $t1, $t0		# add new digit to number 
                                
                                addi $a0, $a0, 1		# move to next character 
                                j loop
                            
                            storenumber: 			
                                sh $t1, 0($s0)			# store half word value into matrix_inputs 
                                addi $s0, $s0, 2		# increment offset 
                                li $t1, 0			# reset number 
                                li $t0, 0 			# reset number
                            
                                addi $a0, $a0, 1		# move to next character 
                                j loop
                            
                            endstore:
                                sh $t1, 0($s0)			# store last half word value into matrix_inputs
                                li $t0, 0 			# reset temp register  
                                li $t1, 0 			# reset temp register  
                            
                                # Load addresses of the input matrix and result matrix
                                la $a0, matrix_inputs
                                la $a2, matrix_result
                            
                                # Call the matrix multiplication function
                                jal matrix_multiply
                            
                                # After matrix multiplication, print the result
                                li $v0, 4
                                la $a0, result_msg
                                syscall
                            
                               la $s0, matrix_result  # Load the base address of the result matrix
                                li $s1, 9              # Counter for 9 elements
                                li $t0, 3              # Counter for elements per row (3 elements per row)
                            
                            print_result:
                                lw $a0, 0($s0)         # Load the next element of the matrix into $a0
                                li $v0, 1              # Print integer syscall
                                syscall
                            
                                addi $t0, $t0, -1      # Decrement row counter (3 elements per row)
                                bnez $t0, print_comma  # If not end of row, go to print_comma
                                j print_newline        # If end of row, go to print_newline
                            
                            print_comma:
                                li $v0, 4
                                la $a0, comma          # Print comma
                                syscall
                                j continue_printing    # Jump to continue printing
                            
                            print_newline:
                                li $v0, 4
                                la $a0, newline        # Print newline
                                syscall
                                li $t0, 3              # Reset row counter to 3 for next row
                            
                            continue_printing:
                                addi $s0, $s0, 4       # Move to the next element in the matrix
                                addi $s1, $s1, -1      # Decrement element counter
                                bnez $s1, print_result # Continue loop if more elements are left
                                
                                # Exit program
                                li $v0, 10
                                syscall
                            
                            matrix_multiply:
                            
                                            # Save values into the stack
                                            addi $sp, $sp, -32    # Allocate space for 8 words
                                            sw $s0, 28($sp)
                                            sw $s1, 24($sp)
                                            sw $s2, 20($sp)
                                            sw $s3, 16($sp)
                                            sw $s4, 12($sp)
                                            sw $s5, 8($sp)
                                            sw $s6, 4($sp)
                                            sw $s7, 0($sp)                           
                                            li $t7, 3 # just used for comparison               
                                            li $t0, 0 # loop counter
                                            outer_loop: # this loop figures out which row of the first matrix and which column of the second will be in use                              
                                                            # t2 represents an intermediate for the position of the first value
                                                            li $t2, 0
                            
                                                            # t5 represents an intermediate second value
                                                            li $t5, 0          
                            
                                                            li $t1, 0 # loop counter
                                                            inner_loop: # this loop figures out which element of the row/column is being multiplied
                                                          
                                                                            # operations on position of second value
                                                                            move $t6, $t5
                                                                            add $t6, $t6, $t0
                                                                            add $t6, $t6, $t0
                                                                            add $t6, $t6, $t0                           
                            
                                                                            # operations on position of first value
                                                                            move $t4, $t2
                                                                            add $t4, $t4, $t1 # this is to add the column position of the first value                                              
                                                                            li $t3, 0 # loop counter
                                                                            li $s7, 0
                                                                            iterate_both_positions: # reserved for the purposes of this loop: t0, t1, t2, t3, t4, t5, t6, t7
                                                                                           
                                                                                            # final calculation for position in first matrix
                                                                                            li $s1, 0
                                                                                            move $s1, $t6
                                                                                            add $s1, $s1, $t3
                                                                                            sll $s1, $s1, 1  #half word align result                                                           
                            
                                                                                            # final calculation for position in second matrix
                                                                                            li $s2, 0
                                                                                            move $s2, $t4
                                                                                            add $s2, $s2, $t3
                                                                                            add $s2, $s2, $t3
                                                                                            add $s2, $s2, $t3
                                                                                            addi $s2, $s2, 9
                                                                                            sll $s2, $s2, 1
                            
                                                                                            #actual multiplication
                                                                                            li $s4, 0
                                                                                            li $s5, 0
                                                                                            add $s6, $s1, $a0    #Calculate effective address
                                                                                            lh $s4, 0($s6)       # Load halfword from calculated address
                                                                                            add $s6, $s2, $a0    # Calculate effective address
                                                                                            lh $s5, 0($s6)       # Load halfword from calculated address  
                                                                                            
                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
                                                                                            mul $s3, $s4, $s5
                                                                                            add $s7, $s7, $s3                                                          
                            
                                                                                            #final products go to 3 * outer_loop + inner_loop shifted left 2
                                                                                            li $s0, 0
                                                                                            add $s0, $t1, $t0
                                                                                            add $s0, $s0, $t0
                                                                                            add $s0, $s0, $t0
                                                                                            sll $s0, $s0, 2     
                                                                                                       
                                                                                            # store s6 in stack because not enough registers
                                                                                            addi $sp, $sp, -4    # Adjust stack pointer
                                                                                            sw $s6, 0($sp)       # Store $s6 on stack                                               
                                                                                            add $s6, $s0, $a2    # Calculate effective address
                                                                                            sw $s7, 0($s6)       # Store word at calculated address
                                                                                            lw $s6, 0($sp)       # Load $s6 from stack
                                                                                            addi $sp, $sp, 4     # Restore stack pointer
                                                                                          
                                                                            addi $t3, $t3, 1
                                                                            bne $t3, $t7, iterate_both_positions # end this loop if runs = 3
                                                            # increment runs
                                                            addi $t1, $t1, 1
                                                            bne $t1, $t7, inner_loop # end this loop if runs = 3
                            
                                            # increment runs
                                            addi $t0, $t0, 1
                                            bne $t0, $t7, outer_loop # end this loop if runs = 3
                            
                            lw $s7, 0($sp)
                            lw $s6, 4($sp)
                            lw $s5, 8($sp)
                            lw $s4, 12($sp)
                            lw $s3, 16($sp)
                            lw $s2, 20($sp)
                            lw $s1, 24($sp)
                            lw $s0, 28($sp)
                            
                            addi $sp, $sp, 32    # Deallocate space for 8 registers
                            jr $ra
                            
                        

Program Output

When executed, the program displays both input matrices and the resulting multiplication matrix:

Matrix A: 1 2 3 4 5 6 7 8 9 Matrix B: 9 8 7 6 5 4 3 2 1 Result Matrix (A × B): 30 24 18 84 69 54 138 114 90

Technical Challenges

Memory Management

Working with multi-dimensional arrays in MIPS assembly required careful address calculation to access the correct elements.

Solution: I implemented index calculations using multiplication and addition operations to convert 2D coordinates into linear memory offsets.

Register Allocation

MIPS has a limited number of registers, making it challenging to keep track of loop variables, addresses, and intermediate results.

Solution: I used a combination of temporary ($t) registers for short-lived values and saved ($s) registers for loop counters, properly saving and restoring context during function calls.

Nested Loop Implementation

Matrix multiplication requires three nested loops, which can be complex to implement in assembly language.

Solution: I structured the code with clear labels and branch instructions to maintain the loop hierarchy, making the control flow more readable and maintainable.

What I Learned

This project deepened my understanding of computer architecture principles, memory addressing, and low-level programming concepts. I gained valuable experience with:

  • MIPS assembly language syntax and conventions
  • Register allocation strategies
  • Memory addressing techniques
  • Implementation of mathematical algorithms at the assembly level
  • Low-level optimization techniques