The mmult
Subprogram
The mmult
subprogram should have two parameters: the
addresses of two matrix descriptors.
It should return the address of a new matrix descriptor that refers to
the matrix product of the two parameter matrices.
The first thing to do in the subprogram is check the matrix descriptors
to see if the two parameter matrices are compatible.
If they are not, you can just return null (0).
If the parameter matrices are compatible then you need to call
mcreate
create a new matrix descriptor and matrix array for
the result of the multiplication.
Then you need to implement the assembly language equivalent of the
following multiplication loop.
In this pseudocode, M1 and M2 are the matrices to be multiplied and R
is the result matrix created by mcreate
.
for (int r = 0; r < M1 row count; r++) {
for (int c = 0; c < M2 column count; c++) {
R[r][c] = 0
for (int k = 0; k < M1 column count; k++) {
R[r][c] += M1[r][k]*M2[k][c]
}
}
}
The loop control variables r and c are row and
column indices in R and M1 or M2.
The innermost loop control variable is used as a column index in M1 and
as a row index in M2.
Entry Access
For accessing entries in the matrices, you can do address computations
using the following formula.
address of M[r][c] = r*(row size of M) + c*(entry size of M)
+ base address of the entries array for M
The row size of M is just the product of the column count of M and the
entry size of M.
For 4 byte entries the above formula becomes
address of M[r][c] = (r*(column count of M) + c)*4
+ base address of the entries array for M
If you have r, c, the column count, and the base
address in registers then you can compute the entry address using a
single additional register.
You multiply r and the column count, putting the result into
the additional register.
Then add c into the register, and so on.
The additional register is being used as a scratch register.
This way of using a register is appropriate when you do only need the
register values in a short stretch of code.
It also makes sense to use the same scratch register in two different
stretches of code as long as it is not carrying data from one stretch to
another.
Scratch registers can be very useful when you are running short of
registers.
This is an important consideration in this assignment.
Register Usage
I suggest that you make this assignment easier on yourself: carefully
plan and document the use of registers before writing the code.
Since you have to use registers to do arithmetic, it makes sense to
designate a register to be used as a temporary copy of the R[r][c]
entry.
To take advantage of sequential access into R you will also want a
register for the entry address.
Set the temporary copy register to 0 before the innermost loop and
increment it by the product of the appropriate M1 and M2 entries inside
the innermost loop.
You only store its contents to R[r][c] (and increment the address
register) after the innermost loop completes.
It is probably best to copy the data in the M1 and M2 descriptor structs
into registers early in the mmult
code rather than
accessing the data through the structs each time you need it.
The reason for this is that you will need to overwrite the $a0 and $a1
registers when you call mcreate
.
After deciding what values you need in registers you will find that
there are not enough $t registers to get the job done.
The best strategy is to use some $s registers.
They should be saved on the stack before they are used and restored
before the subprogram returns.
The main Program
You can use the same main program as the previous assignment with a
little bit of extra code.
The extra code just calls mmult
with the addresses of the
static matrices matrix2x3
and matrix3x2
as
parameters, then uses mwrite
to print the product matrix.
You should see the following result:
10 13
28 40