如何在 Assembly 的特定位置将字符串插入其他字符串?

How to insert string into other string at certain position in Assembly?

对于我的作业,我正在尝试编写类似于 C++ insert 的 Insert 函数。 我的函数应该在某个位置将一个字符串插入到另一个字符串中。

输入:

destination : Be my friend today
source      : good
position    : 6

输出:

Be my good friend today

How should I make room in the middle of the string ?

到目前为止,这是我的代码。

Insert PROC PROC uses edi esi ebx ecx,
                  destination:DWORD ,
                  source:DWORD,
                  position:DWORD

    mov  esi,destination          ;add esi address of str
    add  esi,position
    add  esi,4                     ;lenght of source
    mov  edi,destination           
    add  edi,position

    ;I don't know what should I do in here

    ;How should I make room in the middle of destination ?
    ;How should I insert at certain position + source length :-(
    ;Also, I shouldn't use loop and lea. 


    rep  movsb

    Insert ENDP

您应该为新的 "string" 保留内存,其长度为原始字符串的长度 + 要插入的字符串的长度。接下来从源字符串复制字符,直到指定的位置(根据字符宽度使用 LODSB 或 LODSW)。将 eds 指向新的输出字符串并将 edi 指向源数组。将 CX 设置为位置中的值。这会将字符从源内存复制到新的目标内存。然后将 edi 指向要插入的字符串,并将 cx 设置为要插入的字符串的长度。 运行 LODSW 最后一次将 edi 指向源字符串的位置,并将 cx 设置为源字符串的长度减去位置,最后一次 运行 lodsw。

到新字符串:

从原来的字符复制,然后复制新的字符,然后复制原来的尾部。


就地:

复制字符串的尾部,留出一个空隙来存放新的字符串。这就像 memmove:因为复制的源和目标重叠,你应该从最后一个字符开始,所以你不会覆盖你还没有复制的字符。它也与 InsertionSort's copying to make room for a new element.

相同

您可能想要使用两个寄存器来跟踪源位置和目标位置。

我将避免提供任何代码,因为我无意为您编写作业,但我会尝试解释您将如何完成它。

我将做出以下假设:

1) destination 在其地址处有足够的内存来保存完整的字符串而不会覆盖任何其他变量。

2) 您实际上想要插入 "good "(尾随 space),而不是 "good",因为如果您插入 "good",您将结束使用 "Be my goodfriend today"(好友和朋友之间没有 space)。

3) 您实际上打算指定位置 5,而不是位置 6,因为 C++ insert 使用从零开始的位置(位置 0 是第一个字符,位置 1 是第二个,等等)。

您的第一步是复制字符串的右侧部分以腾出空间。您将从插入的起始位置复制字符,到起始位置加上要插入的字符串的大小,最后得到以下内容:

destination : Be my frienfriend today

这里有一个问题:您必须从 destination 的末尾向后 复制 以避免覆盖尚未复制的字符 - 如果您从偏移量 6 开始并将其复制到偏移量 11,您将覆盖需要先复制到偏移量 16 的偏移量 11 的值,而您最终会得到如下内容:

destination : Be my frienfrienfrienfri

通过向后复制,您可以避免这个问题。使用适当的 decsub 运算符的简单条件跳转将正常工作。

在为上面指定的源字符串腾出空间后,只需正确加载寄存器并使用 rep movsb 将源字符串复制到指定位置的目标位置,覆盖您已经拥有的字符即可已复制。

因此,destination 将包含 "Be my good friend today"

你的问题可以用两个函数来解决:第一个函数将字符串的字符从 SI 位置开始向右移动一个位置,第二个函数调用第一个函数 CX 次。这些函数是为插入新字符串腾出空间所必需的(我想您可以将它们合并为一个函数)。之后,很容易用源字符串覆盖目标字符串。我选择“$”来完成字符串,你可以用零或其他东西来改变它:

shift_right_once

;---------------------------------------------------
;SHIFTS ONE POSITION TO THE RIGHT ALL THE CHARACTERS
;OF A STRING STARTING AT POSITION POINTED BY "SI".
;STRING MUST FINISH WITH "$".
;EXAMPLE: SI = 6
;      INPUT = 'Be my friend today$$$$$$$$$$$$$$$$'
;     OUTPUT = 'Be my ffriend today$$$$$$$$$$$$$$$'
;MODIFIED REGISTERS : AX, SI.

shift_right_once proc   
    mov  al, [ si ]    ;FIRST CHAR TO START PROCESS.
shifting:
    cmp  al, '$'       
    je   end_shifting  ;IF ( END OF STRING )
    inc  si
    mov  ah, [ si ]
    mov  [ si ], al
    xchg al, ah
    jmp  shifting
end_shifting:    
    ret
shift_right_once endp

shift_right_cx

;---------------------------------------------------
;SHIFTS "CX" POSITIONS TO THE RIGHT ALL THE CHARACTERS
;OF A STRING STARTING AT POSITION POINTED BY "SI".
;STRING MUST FINISH WITH "$".
;EXAMPLE: SI = 6
;         CX = 3
;      INPUT = 'Be my friend today$$$$$$$$$$$$$$$$'
;     OUTPUT = 'Be my ffffriend today$$$$$$$$$$$$$'
;MODIFIED REGISTERS : AX, CX.

shift_right_cx proc
    push si
    call shift_right_once      ;CALL PREVIOUS FUNCTION.
    pop  si
    loop shift_right_cx    
    ret
shift_right_cx endp

下一个小程序(用 EMU8086 制作)展示了如何使用它们:

.stack 100h

.data

destination db 'Be my friend today$$$$$$$$$$$$$$$$'
source      db 'good $'
length      dw 5             ;LENGTH OF "SOURCE".
position    dw 6             ;POSITION TO INSERT.

.code
    mov  ax, @data
    mov  ds, ax    
    mov  es, ax    

;SHIFT CHARS TO THE RIGHT TO MAKE ROOM FOR NEW STRING.
    mov  cx, length           ;HOW MANY CHARS TO PUSH.
    mov  si, offset destination
    add  si, position
    call shift_right_cx

;OVERWRITE OLD CHARS WITH THE NEW ONES.
    mov  cx, length           ;HOW MANY NEW CHARS.
    mov  di, si               ;DI = DESTINATION.
    mov  si, offset source    ;SI = SOURCE.
    rep  movsb                ;DO { [DI]=[SI] } WHILE CX>0.

;WAIT FOR A KEY.
    mov  ah, 0
    int  16h        

;FINISH PROGRAM.    
    mov  ax, 4c00h
    int  21h        

;---------------------------------------------------
;SHIFTS "CX" POSITIONS TO THE RIGHT ALL THE CHARACTERS
;OF A STRING STARTING AT POSITION POINTED BY "SI".
;STRING MUST FINISH WITH "$".
;EXAMPLE: SI = 6      |
;         CX = 3
;      INPUT = 'Be my friend today$$$$$$$$$$$$$$$$'
;     OUTPUT = 'Be my ffffriend today$$$$$$$$$$$$$'
;MODIFIED REGISTERS : AX, CX.

shift_right_cx proc
    push si
    call shift_right_once ;SHIFT RIGHT CHARACTERS ONCE.
    pop  si
    loop shift_right_cx    
    ret
shift_right_cx endp

;---------------------------------------------------
;SHIFTS ONE POSITION TO THE RIGHT ALL THE CHARACTERS
;OF A STRING STARTING AT POSITION POINTED BY "SI".
;STRING MUST FINISH WITH "$".
;EXAMPLE: SI = 6      |
;      INPUT = 'Be my friend today$$$$$$$$$$$$$$$$'
;     OUTPUT = 'Be my ffriend today$$$$$$$$$$$$$$$'
;MODIFIED REGISTERS : AX, SI.

shift_right_once proc   
    mov  al, [ si ]    ;FIRST CHAR TO START PROCESS.
shifting:
    cmp  al, '$'       
    je   end_shifting  ;IF ( END OF STRING )
    inc  si
    mov  ah, [ si ]
    mov  [ si ], al
    xchg al, ah
    jmp  shifting
end_shifting:    
    ret
shift_right_once endp

希望对您有所帮助。