2025年4月7日 星期一 乙巳(蛇)年 正月初八 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > 汇编

汇编语言实例:矩阵行求和

时间:03-05来源:作者:点击数:73

前面《二维数组简介》一节中已经展示了如何计算字节矩阵中单个行的总和。尽管这个解决方案有些冗长,现在还是要看看能否用宏来简化任务。首先,我们来回顾一下 calc_row_sum 过程:

  • ;------------------------------------------------------------
  • ; calc_row_sum
  • ; 计算字节矩阵中一行的和数
  • ; 接收: EBX = 表偏移量, EAX = 行索引
  • ; ECX = 按字节计的行大小
  • ; 返回: EAX 为和数
  • ;------------------------------------------------------------
  • calc_row_sum PROC uses ebx ecx edx esi
  • mul ecx ; 行索引 * 行大小
  • add ebx,eax ; 行偏移量
  • mov eax,0 ; 累加器
  • mov esi,0 ; 列索引
  • L1: movzx edx,BYTE PTR[ebx + esi] ; 取一个字节
  • add eax,edx ; 与累加器相加
  • inc esi ; 行中的下一个字节
  • loop L1
  • ret
  • calc_row_sum ENDP

从把 PROC 改为 MACRO 开始,删除 RET 指令,把 ENDP 改为 ENDM。由于没有宏与 USES 伪指令功能相当,因此插入 PUSH 和 POP 指令:

  • mCalc_row_sum MACRO
  • push ebx ;保存被修改的寄存器
  • push ecx
  • push esi
  • mul ecx ;行索引x行大小
  • add ebx,eax ;行偏移量
  • mov eax,0 ;累加器
  • mov esi,0 ;列索引
  • L1: movzx edx,BYTE PTR[ebx + esi]
  • add eax, edx ;取一个字节
  • inc esi ;与累加器相加
  • loop L1 ;行内的下一个字节
  • pop esi ;恢复被修改的寄存器
  • pop ecx
  • pop ebx
  • ENDM

接着,用宏参数代替寄存器参数,并对宏内寄存器进行初始化:

  • mCalc_row_sum MACRO index, arrayoffset, rowSize
  • push ebx ;保存被修改的寄存器
  • push ecx
  • push esi
  • ;设置需要的寄存器
  • mov eax,index
  • mov ebx,arrayOffset
  • mov ecx,rowSize
  • mul ecx ;行索引x行大小
  • add ebx,eax ;行偏移量
  • mov eax,0 ;累加器
  • mov esi,0 ;列索引
  • L1: movzx edx, BYTE PTR[ebx + esi] ;取一个字节
  • add eax, edx ;与累力口器相力口
  • inc esi ;行内的下一个字节
  • loop L1
  • pop esi ;恢复被修改的寄存器
  • pop ecx
  • pop ebx
  • ENDM

然后,添加一个参数 eltType 指定数组类型 (BYTE、WORD 或 DWORD):

mCalc_row_sum MACRO index, arrayOffset, rowSize, eltType

复制到 ECX 的参数 rowSize 现在表示的是每行的字节数。如果要用其作为循环计数器,那么它就必须转换为每行的元素 (element) 个数。

因此,若为 16 位数组,就将 ECX 除以 2 ;若为双字数组,就将 ECX 除以 4。实现上述操作的快捷方式为:eltType 除以 2,把商作为移位计数器,再将 ECX 右移:

shr ecx,(TYPE eltType/2)          ; byte=0, word=1, dword=2

TYPE eltType 就成为 MOVZX 指令中基址-变址操作数的比例因子:

movzx edx,eltType PTR[ebx + esi*(TYPE eltType)]

若 MOVZX 右操作数为双字,那么指令不会汇编。所以,当 eltType 为 DWORD 时,需要用 IFIDNI 运算符另外编写一条 MOV 指令:

IFIDNI <eltType>,<DWORD>
    mov edx,eltType PTR[ebx + esi*(TYPE eltType)]
ELSE
    movzx edx, eltType PTR[ebx + esi*(TYPE eltType)]
ENDIF

最后必须结束宏,记住要把标号 L1 指定为 LOCAL:

  • ;-----------------------------------------------------
  • mCa1c_row_sum MACRO index, arrayOffset, rowSize, eltType
  • ;计算二维数组中一行的和数。
  • ;接收:行索引、数组偏移量、每行的字节数、数组类型 (BYTE、WORD、或 DWORD)。
  • ;返回:EAX= 和数。
  • ;-----------------------------------------------------
  • LOCAL L1
  • push ebx ;保存被修改的寄存器
  • push ecx
  • push esi
  • ;设置需要的寄存器
  • mov eax, index
  • mov ebx, arrayOffset
  • mov ecx, rowSize
  • ;计算行偏移量
  • mul ecx ;行索引x行大小
  • add ebx, eax ;行偏移量
  • ;初始化循环计数器
  • shr ecx,(TYPE eltType/2) ;byte=0, word=1, dword=2
  • ;初始化累加器和列索引
  • mov eax,0 ;累加器
  • mov esi,0 ;列索引
  • L1:
  • IFIDNI <eltType>, <DWORD>
  • mov edx,eltType PTR[ebx + esi*(TYPE eltType)]
  • ELSE
  • movzx edx,eltType PTR[ebx + esi*(TYPE eltType)]
  • ENDIF
  • add eax,edx ;与累加器相加
  • inc esi
  • loop L1
  • pop esi ;恢复被修改的寄存器
  • pop ecx
  • pop ebx
  • ENDM

下面用字节数组、字数组和双字数组对宏进行示例调用:

  • .data
  • tableB BYTE 10h, 20h, 30h, 40h, 50h
  • RowSizeB = ($ - tableB)
  • BYTE 60h, 70h, 80h, 90h, 0A0h
  • BYTE 0B0h, 0C0h, 0D0h, 0E0h, 0F0h
  • tableW WORD 10h, 20h, 30h, 40h, 50h
  • RowSizeW = ($ - tableW)
  • WORD 60h, 70h, 80h, 90h, 0A0h
  • WORD 0B0h, 0C0h, 0D0h, 0E0h, 0F0h
  • tableD DWORD 10h, 20h, 30h, 40h, 50h
  • RowSizeD = ($ - tableD)
  • DWORD 60h, 70h, 80h, 90h, 0A0h
  • DWORD 0B0h, 0C0h, 0D0h, 0E0h, 0F0h
  • index DWORD ?
  • .code
  • mCalc_row_sum index, OFFSET tableB, RowSizeB, BYTE
  • mCalc_row_sum index, OFFSET tableW, RowSizeW, WORD
  • mCalc_row_sum index, OFFSET tableD, RowSizeD, DWORD
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门