3.1 实验内容
C语言编写多重循环程序(大于3重),查看其反汇编码,分析各条语句功能(分析情况需要写入实验报告),并采用汇编语言重写相同功能程序。
3.2实验环境
Microsoft Visual Studio 2017+masm 32
3.3实验步骤
3.3.1 编写c语言代码
此处我选择的程序为实现两个矩阵相乘,是一个3重循环的程序。
3.3.2 查看反汇编代码并分析
1: #include<stdio.h>
2:
3: int Result[10][10];
4: int MatrixA[10][2] = { 1,2,3,4,5,6,7,8,9,23,24,46,68,23,45,66,32,65,45,24 };
5: int MatrixB[2][10] = { 23,45,23,12,43,2,5,6,43,24,12,45,33,22,64,55,23,45,24,23 };
6:
7: int main()
8: {
00B91810 push ebp
//将前栈底指针压入栈中
00B91811 mov ebp,esp
//ebp存入当前esp的值
00B91813 sub esp,0FCh
//更新esp的值,预留出0FCh个单位的地址空间
00B91819 push ebx
//将原始内存偏移指针压栈
00B9181A push esi
//将源地址指针压栈
00B9181B push edi
//将目的地址指针压栈
00B9181C lea edi,[ebp-0FCh]
//将栈顶指针赋给edi
00B91822 mov ecx,3Fh
//ecx存储重复次数3Fh次
00B91827 mov eax,0CCCCCCCCh
//将栈中从[ebp-0FCh]开始位置向高地址方向赋值0xCCCCCCCCh
00B9182C rep stos dword ptr es:[edi]
//rep指示重复ecx中值的次数,stos指令的作用是将eax中的值拷贝到es:[edi]指向的位置
00B9182E mov ecx,offset _D5DA6D57_main@cpp (0B9C003h)
00B91833 call @__CheckForDebuggerJustMyCode@4 (0B91212h)
9:for (int i = 0; i < 10; i++)
;-----------------------------------------
00B91838 mov dword ptr [ebp-8],0
00B9183F jmp main+3Ah (0B9184Ah)
//将i赋值为0,由于i是局部变量,所以存储在栈中。
//无条件跳转到判断模块,原因是第一次不需要增加后再判断
;-------------------------------------------
00B91841 mov eax,dword ptr [ebp-8]
00B91844 add eax,1
00B91847 mov dword ptr [ebp-8],eax
//将i赋给eax,执行加1操作,再赋给i
;---------------------------------------------
00B9184A cmp dword ptr [ebp-8],0Ah
00B9184E jge main+0B1h (0B918C1h)
//判断i是否大于等于10,大于等于10则跳转到循环结束处。
10:{
11: for (int j = 0; j < 10; j++)
;------------------------------------------
00B91850 mov dword ptr [ebp-14h],0
00B91857 jmp main+52h (0B91862h)
;-------------------------------------------
00B91859 mov eax,dword ptr [ebp-14h]
00B9185C add eax,1
00B9185F mov dword ptr [ebp-14h],eax
;--------------------------------------------
00B91862 cmp dword ptr [ebp-14h],0Ah
00B91866 jge main+0AFh (0B918BFh)
//与i相同
12:{
13: for (int k = 0; k < 2; k++)
;-------------------------------------------
00B91868 mov dword ptr [ebp-20h],0
00B9186F jmp main+6Ah (0B9187Ah)
;---------------------------------------------
00B91871 mov eax,dword ptr [ebp-20h]
00B91874 add eax,1
00B91877 mov dword ptr [ebp-20h],eax
;---------------------------------------------
00B9187A cmp dword ptr [ebp-20h],2
00B9187E jge main+0ADh (0B918BDh)
;---------------------------------------------
//与i相同
14: {
15: Result[i][j] += MatrixA[i][k] * MatrixB[k][j];
00B91880 imul eax,dword ptr [ebp-8],28h
//eax=i*40,因为是dword数组,所以4个偏移量代表一个数据,i*40代表Result的第i行开始的地址
00B91884 mov ecx,dword ptr [ebp-20h]
//ecx=k
00B91887 shl ecx,2
//ecx=4k,4个偏移量代表一个数据
00B9188A imul edx,dword ptr [ebp-20h],28h
//edx=k*40,同i类似,表示MatrixB的第k行地址
00B9188E mov esi,dword ptr [ebp-8]
//esi=i
00B91891 mov edi,dword ptr [ebp-14h]
//edi=j
00B91894 mov ecx,dword ptr MatrixA (0B9A000h)[ecx+esi*8]
//ecx=MatrixA[i][k],esi*8是因为MatrixA一行有两个数据,一个数据为4个偏移量,表示MatrixA的第i行,ecx=4k,表示第k个数据,4也为偏移量
00B9189B imul ecx,dword ptr MatrixB (0B9A050h)[edx+edi*4]
//ecx=ecx*Matrix[k][j],edx=k*40表示MatrixB第k行的起始地址,edi=j,4j表示第j个数据。
00B918A3 mov edx,dword ptr [ebp-14h]
//edx=j
00B918A6 add ecx,dword ptr Result (0B9A1F8h)[eax+edx*4]
//ecx=ecx+Result[i][j],eax为Result的第i行其实地址,edx*4表示第j个数据
00B918AD imul eax,dword ptr [ebp-8],28h
//eax=i*40
00B918B1 mov edx,dword ptr [ebp-14h]
//edx=j
00B918B4 mov dword ptr Result (0B9A1F8h)[eax+edx*4],ecx
//Result[i][j]=ecx,存储结果
16: }
00B918BB jmp main+61h (0B91871h)
//第3重循环结束,跳转到k++
17: }
00B918BD jmp main+49h (0B91859h)
//第2重循环结束,跳转到j++
18: }
00B918BF jmp main+31h (0B91841h)
//第1重循环结束,跳转到i++
19: for (int i = 0; i < 10; i++)
;-----------------------------------------------------
00B918C1 mov dword ptr [ebp-2Ch],0
00B918C8 jmp main+0C3h (0B918D3h)
//将i赋值为0,由于i是局部变量,所以存储在栈中。
//无条件跳转到判断模块,原因是第一次不需要增加后再判断
;-----------------------------------------------------
00B918CA mov eax,dword ptr [ebp-2Ch]
00B918CD add eax,1
00B918D0 mov dword ptr [ebp-2Ch],eax
//将i赋给eax,执行加1操作,再赋给i
;-----------------------------------------------------
00B918D3 cmp dword ptr [ebp-2Ch],0Ah
00B918D7 jge main+10Eh (0B9191Eh)
//判断i是否大于等于10,大于等于10则跳转到循环结束处。
20: {
21: for (int j = 0; j < 10; j++)
;-------------------------------------------
00B918D9 mov dword ptr [ebp-38h],0
00B918E0 jmp main+0DBh (0B918EBh)
;-----------------------------------------------
00B918E2 mov eax,dword ptr [ebp-38h]
00B918E5 add eax,1
00B918E8 mov dword ptr [ebp-38h],eax
;---------------------------------------------
00B918EB cmp dword ptr [ebp-38h],0Ah
00B918EF jge main+0FFh (0B9190Fh)
//同i
22: {
23: printf("%d ", Result[i][j]);
00B918F1 imul eax,dword ptr [ebp-2Ch],28h
//eax=i*40
00B918F5 mov ecx,dword ptr [ebp-38h]
//ecx=j
00B918F8 mov edx,dword ptr Result (0B9A1F8h)[eax+ecx*4]
//edx=Result[i][j]
00B918FF push edx
//将edx压栈
00B91900 push offset string "%d " (0B97B30h)
//将"%d "压栈
00B91905 call _printf (0B9104Bh)
//调用_printf,输出edx
00B9190A add esp,8
//栈顶指针加8,刚好压入两个变量,所以esp+8清栈
24: }
00B9190D jmp main+0D2h (0B918E2h)
//内层循环结束,跳转j++
25: printf("\n");
00B9190F push offset string "\n" (0B97B34h)
00B91914 call _printf (0B9104Bh)
00B91919 add esp,4
//同上_printf
26: }
00B9191C jmp main+0BAh (0B918CAh)
//第一层循环结束,跳转i++
27: return 0;
00B9191E xor eax,eax
//eax清0,return 0
28: }
00B91920 pop edi
//恢复原来调用的edi
00B91921 pop esi
//恢复原来调用的esi
00B91922 pop ebx
//恢复原来调用的ebx
00B91923 add esp,0FCh
//栈顶指针回退0FCh个地址单元
00B91929 cmp ebp,esp
00B9192B call __RTC_CheckEsp (0B9121Ch)
00B91930 mov esp,ebp
//将当前ebp赋给esp
00B91932 pop ebp
//恢复原来的ebp
00B91933 ret
3.3.3 汇编代码实现矩阵的乘法
;定义所需的变量
.data
Result dword 100 dup(0)
MatrixA dword 20 dup(1,2,3,4,5,6,7,8,9,23,24,46,68,23,45,66,32,65,45,24)
MatrixB dword 20 dup(23,45,23,12,43,2,5,6,43,24,12,45,33,22,64,55,23,45,24,23)
output byte "%d ",0
endl byte " ",0AH,0
.code
main Proc
;声明局部变量i,j,k
local i,j,k
;i=0
mov i,0
;无条件跳转L2,原因是第一次不需要增加后再判断
jmp L2
;L1为i++
L1:
mov eax,i
add eax,1
mov i,eax
;L2为比较i是否大于等于10,大于等于跳转
L2:
cmp i,10
jge L10
;L3为将令j=0,且无条件跳转L5,同i
L3:
mov j,0
jmp L5
;L4为j++
L4:
mov eax,j
add eax,1
mov j,eax
;L5为比较j是否大于等于10,大于等于跳转到i++
L5:
cmp j,10
jge L1
;L6为令k=0,无条件跳转L8,同i
L6:
mov k,0
jmp L8
;L7为k++
L7:
mov eax,k
add eax,1
mov k,eax
;L8为比较k是否大于等于2,大于等于跳转到j++
L8:
cmp k,2
jge L4
L9:
;eax=i*40,因为是dword数组,所以4个偏移量代表一个数据,i*40代表Result的第i行开始的地址
imul eax,i,40
;将第i行Result的开始地址赋给ecx
lea ecx,dword ptr Result[eax]
;edx=i*8,表示MatrixA的第i行
imul edx,i,8
;将MatrixA第i行的起始地址赋给eax
lea eax,dword ptr MatrixA[edx]
;edx=k*40,表示MatrixB的第k行数据
imul edx,k,40
;将MatrixB第k行的起始地址赋给edx
lea edx,dword ptr MatrixB[edx]
;esi=k
mov esi,k
;edi=j
mov edi,j
;eax=MatrixA[i][k],比例变址寻址
mov eax,dword ptr[eax+esi*4]
;eax=eax*MatrixB[k][j]
imul eax,dword ptr[edx+edi*4]
;edx=j
mov edx,j
;eax=eax+Result[i][j]
add eax,dword ptr[ecx+edx*4]
;Result[i][j]=eax
mov dword ptr[ecx+edx*4],eax
;最内层循环结束,跳转k++
jmp L7
;L10为令i=0,并且无条件跳转到判断处
L10:
mov i,0
jmp L12
;L11为输出换行并且i++
L11:
invoke printf,offset endl
mov eax,i
add eax,1
mov i,eax
;L12为比较i是否大于等于10,大于等于跳转到循环结束处
L12:
cmp i,10
jge L17
;L13为令j=0,并且无条件跳转到判断
L13:
mov j,0
jmp L15
;L14为j++
L14:
mov eax,j
add eax,1
mov j,eax
;L15为比较j是否大于等于10,大于等于则循环结束,跳转到外层i++
L15:
cmp j,10
jge L11
L16:
;eax=i*40,表示Result第i行数据
imul eax,i,40
;将Result第i行起始地址赋给ecx
lea ecx,dword ptr Result[eax]
;edx=j
mov edx,j
;ebx=Result[i][j]
mov ebx,dword ptr[ecx+edx*4]
;调用输出函数,输出Result[i][j]
invoke printf,addr output,ebx
;一次循环结束,跳转j++
jmp L14
;L17为return 0并且返回
L17:
xor eax,eax
ret
3.4实验结果
c语言运行结果
汇编语言运行结果
3.5完整代码
Chris_William/BIT-X86-Experiment (gitee.com)https://github.com/chris-william0829/bit-x86-experiment