MASM移动具有嵌套循环的数组元素

时间:2017-11-09 06:22:31

标签: arrays loops assembly x86 masm

使用您的姓氏初始化BYTE数组,而不是0终止的字符串。使用嵌套循环和索引寻址,编写一个程序,使外部循环的每次迭代将数组成员向前旋转一个位置。数组开头的值必须回绕到最后一个位置。例如,在外循环的第一次迭代之后,' S''' i' i'' t',' 39; H'应该转变为' m',''''''' S'使用作者的库函数之一在每次旋转后显示数组。使用正确的外循环迭代次数将名称返回给它的原始拼写。内循环应该执行旋转。

如果有人可以提供任何帮助,我不能为我的生活弄清楚这一点我会很感激。我对组装非常新,并且不太了解嵌套循环。我也无法弄清楚如何移动字母。这就是我到目前为止所拥有的

INCLUDE Irvine32.inc
WriteString PROTO

.data
    source BYTE "S", "m", "i", "t", "h"
.code
main PROC
    mov esi, 0
    mov ecx, SIZEOF source
L1 :
    mov al, source[esi]
    inc esi
loop L1
    mov edx, offset source 
    call WriteString
exit
main ENDP
END main

有人能引导我朝着正确的方向前进吗?

1 个答案:

答案 0 :(得分:1)

  

嵌套循环

请记住整个"循环"是程序员的逻辑概念,而不是CPU的一个特性(嗯,x86确实有一个名为loop的指令,但不要使用它:Why is the loop instruction slow? Couldn't Intel have implemented it efficiently?)。

什么使循环循环?你有一些被认为是" body"循环,你运行它很多次,那就是"循环"。

现在,嵌套循环正在调用一种情况,其中循环体(外部循环)包含另一个循环(内部循环),该循环将执行例如多次M次。然后内循环体将总共执行NxM次,因为每次外循环循环时,内循环将再次运行。

简单的x86示例:

    ; init outer loop counter (will loop till zero)
    mov     ebp,7
outer_loop_body:
    ; some outer loop body code
    ; init inner loop counter (will loop till zero)
    mov     ecx,5
inner_loop_body:
    ; some inner (nested) loop body code
    ; this inner body will execute 7x5 = 35 many times
    dec     ecx
    jnz     inner_loop_body ; repeat until counter is zero
    ; some other outer loop code
    dec     ebp
    jnz     outer_loop_body ; repeat until counter is zero
    ; this code is outside of loops, executed after them

当然,您必须保留您的计数器(如果您在寄存器上运行不足,请使用一些内存来存储它们,如果您不太使用它们的话)。

在您的任务中,它被称为"索引寻址" ,并且示例中的倒计时计数器经历值N到1,因此它们几乎可用作索引(只需要 - 1),如果你将从source数组的末尾开始。但这有利于"前进一个位置"具有就地存储器覆盖的数据移动,对于向后移动,最好构造具有向上计数计数器的循环,如:

    ; init loop index to go from zero to N-1
    xor     ecx,ecx     ; index = 0
loop_body:
    ; some loop body code, using ECX as index
    ; advance to next index
    inc     ecx
    cmp     ecx,N
    ; loop until value N in index is reached
    jb      loop_body
    ; this code is outside of loop, executed after it
  

我也无法弄清楚如何移动这些字母。

我几乎要付钱给你看看你大脑的尝试,让我真的很好奇你如何看待这类问题,对于经验丰富的程序员来说这很明显,但看起来并非如此。

我个人首先想象数据(忽略代码和算法),在你的情况下:

source BYTE "S", "m", "i", "t", "h"

这将在内存中组合为5个连续字节,其值为S,m,i,t,h(对于特定数值,请检查ASCII表或反汇编)。

现在我想象在第一次向前移动迭代后内存应该如何看待(我会做前进移动):

source BYTE "h", "S", "m", "i", "t"

" h"离开原始数组放在第一个位置,以使整个内容循环。

现在我想象一些算法来实现这种数据转换(即从输入数据中计算出所需的结果):

fetch last element and keep it safe for later
loop (from last position down to second position) do:
    read position-1 element, and store it at position
store the original "last element" to first position

我在脑海中快速运行以验证它是否进行了我想要的计算,修复了任何错误。我做了一些疯狂的快速猜测,实现特定步骤需要多少指令,如果超过cca 5,我将其分解为更简单的步骤。在这种情况下,这个感觉就像每个步骤大约是2-3个指令(这通常意味着最后的x2),所以这个算法的细分对我来说没问题。

然后我将其用作基本注释,并使用asm指令实现它。

对我来说似乎很明显,我无法想象是什么让你陷入困境。

最后举例说明如何覆盖"字节数组的一些内存"以索引方式:

; assume ESI already contains address of array
; initialized earlier by something like: mov esi, OFFSET source
; and ECX contains index (0..4 value)
mov     al,[esi+ecx-1]    ; loads element from position ECX-1
    ; ^^ will go out of bounds for index 0!
mov     [esi+ecx],al      ; store that element to position ECX

或者你可以使用另一个MASM语法选项,这次直接将数组地址指定为编译时常量:

; ECX contains index (0..4 value)
mov     al,source[ecx-1]    ; loads element from position ECX-1
    ; ^^ will go out of bounds for index 0
mov     source[ecx],al      ; store that element to position ECX

在寄存器中使用基址的第一个选项让您可以选择在任何存储器阵列上重复使用相同的功能,而不仅仅是source,所以我更喜欢它而不是第二个变体,但是它们在功能。

简而言之,"移动字母"你只需用新内容覆盖原始内存内容,这些内容恰好是原始值,但存储到不同的地址(按+ -1,取决于你理解的方式"转发"有问题的措辞,虽然我相信这个例子是完全错误的,"转发"是另一种方式。)

如果你在相同的记忆中就地这样做,请注意你必须选择正确的覆盖顺序(从最后一个位置到#34;前进"或者从&#形成第一个到最后一个位置34;向后"),否则你将一个元素值复制到剩余的位置。

现在,我很高兴听到您无法弄清楚这一点,以及现在是否清楚。

编辑:再说一遍......

当然要使用asm指令实现任何计算,您需要首先了解一下,可用的指令以及它们的功能。它有助于阅读指令参考指南几次,以了解可能的计算类型。

对于x86,我将从80386 ISA开始,因为它相当短(不包括FP指令和SIMD扩展),但已包含所有常见的基本x86指令,例如谷歌发现了一些来自英特尔的原始PDF {{3相关的第3,4,5和17章(或网络版http://css.csail.mit.edu/6.858/2013/readings/i386.pdf)。

并确保您了解什么是CPU寄存器,它们有多少,它们的尺寸是什么"比特,这对于最小/最大值意味着什么,以及什么是计算机内存以及它是如何运作的。即关于x86计算机体系结构的基本知识。

然后步骤"用指令实施该评论"应该是可以管理的(即使不是那么难,也会有成长经验)。