In the code below I have defined a symbol that multiplies two block-matrices (I can't use the builtin Dot since Dot assumes that the matrix coefficients commute):
Unprotect[Times] Times[c_,mat[0]]:=mat[0] Protect[Times] Unprotect[Plus] Plus[m_,mat[0]]:=m Plus[mat[0],m_]:=m Protect[Plus] SetAttributes[dot,{Flat,OneIdentity}] dot[m_]:=m dot[a___,mat[0],b___]:=mat[0] dot[a_,mat[id]]:=a dot[mat[id],a_]:=a dot[m1___,c_*m_mat,m2___]:=c*dot[m1,m,m2] dot[m1___,m2_+m3_,m4___]:=dot[m1,m2,m4]+dot[m1,m3,m4] dot[m1_,m2_]/;(MatrixQ[m1]&&MatrixQ[m2]&&(Length[First[m1]]==Length[m2])) := Table[ Table[ Apply[Plus,Table[dot[m1[[i,k]],m2[[k,j]]],{k,1,Length[m2]}]], {j,1,Length[First[m2]]} ],{i,1,Length[m1]} ] As you can see I'm using the head mat to signal that a variable is a matrix, e.g. mat[0] is the zero matrix, mat[id] is the identity matrix and mat[a] is an indeterminant matrix with name a. The result of a test looks as follows: 
So the computation works fine but why does Mathematica add Hold?
UnprotectTimesorPlus. It is better if you do:mat /: Times[c_, mat[0]] := mat[0]. $\endgroup$DotsinceDotassumes that the matrix coefficients commute…" - and that is why Mathematica providesInner[], which you can then use with operators likeNonCommutativeMultiply[](**). $\endgroup$