8086 - 使用 CX 作为索引的环路阵列

8086 - loop over array using CX as index

提问人:Umberto Fontanazza 提问时间:11/8/2023 最后编辑:Sep RolandUmberto Fontanazza 更新时间:11/12/2023 访问量:60

问:

我正在使用 emu8086 为 8086 编写 .asm。

我想使用指令遍历一个字节数组。在循环中,我想按顺序访问数组中的数据(从最低的内存地址到最高的内存地址)。loop

我可以使用以下代码来完成:

array               DB 9,1,4,7,2
length              DW 5

                    mov cx, length  ;cx <-- desired iterations
                    lea si, array   ;si <-- address of first array byte
my_loop:            mov al, [si]    ;load from array
                    
                    ;perform operations
                    ;on AL here
                    
                    inc si          ;si points to the next array element
                    loop my_loop

我想知道是否有更好的方法可以只利用一个寄存器来做到这一点,因为 CX 和 SI 基本上都在做相同的工作(第一个数组元素的偏移量自然源于迭代计数)。

在此示例中,每次迭代时,CX 和 SI 的值将如下所示:

CX: [5, 4, 3, 2, 1]
SI: [array + 0, array + 1, array + 2, array + 3, array + 4]

现在,在每次迭代中,SI 的值由公式给出,其中 和 在编译时是已知的。si = addr(array) + length - cxarraylength

在循环中,这看起来像(这个语法解释了我想做什么,但很可能是错误的):

mov al, offset(array) + length - cx

我是寻址模式的新手。有没有办法完全避免使用 SI,或者您能解释为什么不可避免地使用它吗?

数组 循环 程序集 x86-16 emu8086 8086

评论


答:

4赞 Sep Roland 11/12/2023 #1
array               DB 9,1,4,7,2
length              DW 5

array并且在编译时是已知的。length

有充分的理由不将长度定义为基于内存的变量,而是通过编写以下内容来将其等同起来。length equ $ - array

我想使用指令遍历一个字节数组。loop

如果您坚持使用 ,那么您将不得不使用额外的寄存器。在 上,CX 寄存器永远不能在寻址模式下使用。loop

我是寻址模式的新手,有没有办法完全避免使用 SI,或者您能解释为什么不可避免地使用它吗?

SI 寄存器并非不可避免,因为在 上,您可以用 BX、DI 甚至 BP 替换它(BP 很特殊,因为它默认使用 SS 段寄存器,所以不要将其用作首选!

我想知道是否有更好的方法可以只利用一个寄存器

您可以使用单个地址寄存器(BX、SI、DI 或 BP 之一)轻松编写环路。以下是一些方法:

  xor  bx, bx           ; 0
my_loop:
  mov  al, [array + bx]
  ...
  inc  bx               ; Increment the 'offset' BX
  cmp  bx, length       ; 5
  jb   my_loop          ; Loop for as long as BX below 5

和:

  mov  bx, OFFSET array
my_loop:
  mov  al, [bx]
  ...
  inc  bx               ; Increment the 'address' BX
  cmp  bx, OFFSET array + length
  jb   my_loop          ; Loop for as long as BX below (array + 5)

正如 @ecm 和 @PeterCordes 所建议的那样(删除指令):

  mov  bx, -length
my_loop:
  mov  al, [array + length + bx]
  ...
  inc  bx               ; Increment the 'offset' BX
  jnz  my_loop          ; Loop for as long as BX negative [-5,-1]

在循环中,我想按顺序访问数组中的数据(从最低的内存地址到最高的内存地址)。

这很遗憾(但不是那么多),因为如果手头的任务允许,您可以反转方向并删除指令:

  mov  bx, length - 1   ; 5 - 1
my_loop:
  mov  al, [array + bx]
  ...
  dec  bx               ; Decrement the 'offset' BX
  jge  my_loop          ; Loop for as long as BX positive [0,4]

评论

2赞 ecm 11/12/2023
最后一个示例可以与 : then 和 finally \ 中的负索引一起使用(希望我没有偏离一个,如果您将其添加为示例,您应该对其进行审查。bxmov bx, - lengthmov al, [array + length + bx]inc bxjnz my_loop
2赞 Peter Cordes 11/12/2023
您可以像循环之前一样将索引向上计数为零,和 / / 在 [-5, -1] 范围内循环 BX,相对于数组末尾(数组 + 长度)进行索引。寻址模式下的地址只是折叠到绝对地址中,因此不会产生额外费用。计算 BX 从中不那么明显,但可以用 / / / 来完成。我对此进行了测试,它有效,但是非常不明显的是,设置的 FLAGS 对 有用。mov bx, -lengthmov al, [array + length + bx]inc bxjnz my_loop+length[-4, 0]mov bx, -(length-1)mov al, [array+length-1 + bx]inc bxjle topincjle
0赞 Peter Cordes 11/12/2023
@ecm:伟大的思想家的思维方式是一样的:P
2赞 Peter Cordes 11/12/2023
要计算 BX,包括零,更喜欢 /,这样它就可以在 Sandybridge 系列上宏熔断。(x86_64 - 装配 - 循环条件和无序)。/ 不能融合成一个 UOP,但 / 可以。它在 8086 上运行相同,但在那里没有缺点,因此由于现代 CPU,它可以说是一个更好的成语。(包括Skylake;我只是检查了一下,确保没有改变。dec bxjgedecjnsdecjgedec/jge
2赞 Peter Cordes 11/13/2023
如果您想要其他类型的循环,请不要使用该指令;无论如何,在最近的 CPU 上它很慢。(8086 很好。大多数 ISA 根本没有关于循环的特殊指令,使用/或等效是完全正常的。当您可以只使用底部的 compare 和分支时,或者没有 if dec、sub、inc 或 add 可以以有用的方式设置 FLAGS,则没有理由跳过箍来使用循环指令。loopdecjnzloopcmp