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

汇编语言二维数组简介

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

汇编语言程序员看来,二维数组是一位数组的高级抽象。高级语言有两种方法在内存中存放数组的行和列:行主序和列主序,如下图所示。 

行主序和列主序

使用行主序(最常用)时,第一行存放在内存块开始的位置,第一行最后一个元素后面紧跟的是第二行的第一个元素。使用列主序时,第一列的元素存放在内存块开始的位置,第一列最后一个元素后面紧跟的是第二列的第一个元素。

用汇编语言实现二维数组时,可以选择其中的任意一种顺序。这里使用的是行主序。如果是为高级语言编写汇编子程序,那么应该使用高级语言文档中指定的顺序。

x86 指令集有两种操作数类型:基址-变址和基址-变址-位移量,这两种类型都适用于数组。下面将对它们进行研究并通过例子来说明如 何有效地使用它们。

基址-变址操作数

基址-变址操作数将两个寄存器(称为基址和变址)相加,生成一个偏移地址:

[base + index]

其中的方括号是必需的。32 位模式下,任一 32 位通用寄存器都可以用作基址和变址寄存器。(通常情况下避免使用 EBP,除非进行堆栈寻址。)下面的例子是 32 位模式中基址和变址操作数的各种组合:

  • .data
  • array WORD 1000h,2000h,3000h
  • .code
  • mov ebx,OFFSET array
  • mov esi, 2
  • mov ax,[ebx+esi] ; AX = 2000h
  • mov edi, OFFSET array
  • mov ecx,4
  • mov ax,[edi+ecx] ; AX = 3000h
  • mov ebp,OFFSET array
  • mov esi, 0
  • mov ax,[ebp+esi] ; AX = l000h

二维数组

按行访问一个二维数组时,行偏移量放在基址寄存器中,列偏移量放在变址寄存器中。例如,下表给出的数组为 3 行 5 列:

  • tableB BYTE 10h, 20h, 30h, 40h, 50h
  • Rowsize = ($ - tableB)
  • BYTE 60h, 70h, 80h, 90h, 0A0h
  • BYTE 0B0h, 0C0h, 0D0h, 0E0h, 0F0h

该表为行主序,汇编器计算的常数 Rowsize 是表中每行的字节数。如果想用行列坐标定位表中的某个表项,则假设坐标基点为 0,那么,位于行 1 列 2 的表项为 80h。

将 EBX 设置为该表的偏移量,加上(Rowsizerow_index),计算出行偏移量,将 ESI 设置为列索引:

  • row_index = 1
  • column_index = 2
  • mov ebx, OFFSET tableB ; 表偏移量
  • add ebx, RowSize * row_index ; 行偏移量
  • mov esi, column_index
  • mov al,[ebx + esi] ; AL = 80h

假设该数组位置的偏移量为 0150h,则其有效地址表示为 EBX+ESI,计算得 0157h。下图展示了如何通过 EBX 加上 ESI 生成 tableB[1, 2] 字节的偏移量。如果有效地址指向该程序数据区之外,那么就会产生一个运行时错误。 

用基址-变址操作数寻址数组

1) 计算数组行之和

基于变址的寻址简化了二维数组的很多操作。比如,用户可能想要计算一个整数矩阵中一行的和。下面的 32 位 calc_row_sum 程序就计算了一个 8 位整数矩阵中被选中行的和数:

  • ;------------------------------------------------------------
  • ; 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

BYTE PTR 是必需的,用于声明 MOVZX 指令中操作数的类型。

2) 比例因子

如果是为字数组编写代码,则需要将变址操作数乘以比例因子 2。下面的例子定位行 1 列 2 的元素值:

  • tablew WORD 10h, 20h, 30h, 40h, 50h
  • RowsizeW = ($ - tableW)
  • WORD 60h, 70h, 80h, 90h, 0A0h
  • WORD 0B0h, 0C0h, 0D0h, 0E0h, 0F0h
  • .code
  • row_index = 1
  • column_index = 2
  • mov ebx,OFFSET tableW ;表偏移量
  • add ebx,RowSizeW * row_index ;行偏移量
  • mov esi, column_index
  • mov ax,[ebx + esi*TYPE tableW] ;AX = 0080h

本例的比例因子 (TYPE tableW) 等于 2。同样,如果数组类型为双字,则比例因子为 4:

tableD DWORD 10h, 20h, . . .etc.
.code
mov eax,[ebx + esi*TYPE tableD]

基址-变址-偏移量操作数

基址-变址-偏移量操作数用一个偏移量、一个基址寄存器、一个变址寄存器和一个可选的比例因子来生成有效地址。格式如下:

[base + index + displacement]
displacement[base + index]

Displacement ( 偏移量 ) 可以是变量名或常量表达式。32 位模式下,任一 32 位通用寄存器都可以用作基址和变址寄存器。基址-变址-偏移量操作数非常适于处理二维数组。偏移量可以作为数组名,基址操作数为行偏移量,变址操作数为列偏移量。

双字数组示例

下面的二维数组包含了 3 行 5 列的双字:

  • tableD DWORD 10h, 20h, 30h, 40h, 50h
  • Rowsize = ($ - tableD)
  • DWORD 60h, 70h, 80h, 90h, 0A0h
  • DWORD 0B0h, 0C0h, 0D0h, 0E0h, 0F0h

Rowsize 等于 20 (14h) 。假设坐标基点为 0,那么位于行 1 列 2 的表项为 80h。为了访问到这个表项,将 EBX 设置为行索引,ESI 设置为列索引:

mov ebx, Rowsize              ;行索弓|
mov esi, 2                          ;列索引
mov eax, tableD[ebx + esi*TYPE tableD]

设 tableD 开始于偏移量 0150h 处,下图展示了 EBX 和 ESI 相对于该数组的位置。偏移量为十六进制。 

基址-变址-偏移量操作数示例

64 位模式下的基址-变址操作数

64 位模式中,若用寄存器索引操作数则必须为 64 位寄存器。基址-变址操作数和基址-变址-偏移量操作数都可以使用。

下面是一段小程序,它用 get_tableVal 过程在 64 位整数的二维数组中定位一个数值。如果将其与前面的 32 位代码进行比较,会发现 ESI 被替换为 RSI,EAX 和 EBX 也成了 RAX 和 RBX。

  • ;64 位模式下的二维数组 (TwoDimArrays.asm)
  • Crlf proto
  • WriteInt64 proto
  • ExitProcess proto
  • .data
  • table QWORD 1,2,3,4,5
  • RowSize = ($ - table)
  • QWORD 6,7,8,9,10
  • QWORD 11,12,13,14,15
  • .code
  • main proc
  • ; 基址-变址-偏移量操作数
  • mov rax,1 ; 行索引基点为0
  • mov rsi,4 ; 列索引基点为0
  • call get_tableVal ; RAX中为返回值
  • call WriteInt64 ; 显示返回值
  • call Crlf
  • mov ecx,0
  • call ExitProcess ; 程序结束
  • main endp
  • ;------------------------------------------------------
  • ; get_tableVal
  • ; 返回四字二维数组中给定行列值的元素
  • ; 接收: RAX = 行数, RSI = 列数
  • ; 返回: RAX中的数值
  • ;------------------------------------------------------
  • get_tableVal proc uses rbx
  • mov rbx,RowSize
  • mul rbx ; 乘积(低) = RAX
  • mov rax,table[rax + rsi*TYPE table]
  • ret
  • get_tableVal endp
  • end
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门