Matrices: two indices, one address
A matrix is a grid of numbers arranged in rows and columns. A row is one horizontal line of the grid, read from left to right. A column is one vertical line of the grid, read from top to bottom. Memory has no concept of rows or columns. It is one long line of bytes, the same kind of vector covered in the previous lesson. To store a matrix, its rows and columns must be placed one after another in that single line. The usual choice is row-major order: store every number of the first row, then every number of the second row, and continue until every row has been placed.
Two indices, one address
An element's position in a matrix is given by two indices: the row index, which row it belongs to, and the column index, which column it belongs to. Counting for both starts at 0, the same way indices worked in the vectors lesson. Memory holds a single flat sequence of numbers, so the two indices must first be combined into one flattened index: multiply the row index by the number of columns in the matrix, then add the column index. From there the flattened index becomes a byte address the same way a vector's index does: address = base + index * 4.
mul t4, t1, t3 # t4 = row index * number of columnsadd t4, t4, t2 # t4 = that + column index (the flattened index)slli t4, t4, 2 # t4 = byte offset = index * 4add t5, t0, t4 # t5 = address of the element at this row and columnlw t5, 0(t5) # t5 = value at that address
multiplies the row index by the number of columns. In the vectors lesson the multiplication could be done with a shift because 4 is a power of two, but the number of columns in a matrix usually is not, so is used here instead of . The column index is added, and the result is scaled by 4 with another (4 is a power of two, so a shift still works for this part). That produces the byte offset of the element at the given row and column.
Row 0, column 0 maps to index 0.
.data layout and your own reading of it impose on that single line, using the , , and arithmetic shown above. Once the grid and the flattened row are understood as one thing shown twice, the same pattern covers any grid of data: an image's rows of pixels, a game board, or a spreadsheet. Each is one array, reached with one extra multiplication.Summing a matrix
.datam: .word 1, 2, 3, 4, 5, 6, 7, 8, 9 # 3x3 matrix, stored row by row.textla t0, m # base addressli t1, 3 # number of rowsli t2, 3 # number of columnsli t3, 0 # current row index, starts at 0li s0, 0 # running totalrow_loop:beq t3, t1, row_doneli t4, 0 # current column index, starts at 0col_loop:beq t4, t2, col_donemul t5, t3, t2 # row index * number of columnsadd t5, t5, t4 # + column index = flattened indexslli t5, t5, 2 # byte offset = index * 4add t6, t0, t5 # address of the element at this row and columnlw t6, 0(t6) # value at that addressadd s0, s0, t6 # total += elementaddi t4, t4, 1 # move to the next columnj col_loopcol_done:addi t3, t3, 1 # move to the next rowj row_looprow_done:mv a0, s0 # print total: 1+2+...+9 = 45li a7, 1ecallli a7, 10ecall
Two nested loops index the matrix. The outer loop selects a row and the inner loop selects a column within that row. multiplies the row index by the number of columns. Adding the column index and scaling by 4 gives the address of that element. The value stored at that address is loaded and added to the running total. When the inner loop finishes a row, the outer loop moves to the next row, until all 3 rows have been processed.
