汇编 MASM 字符串比较

Posted

技术标签:

【中文标题】汇编 MASM 字符串比较【英文标题】:Assembly MASM string comparison 【发布时间】:2017-10-26 22:47:53 【问题描述】:

我正在准备汇编语言考试,我正在做我们老师给的例子。可悲的是我遇到了一个我不明白的问题。我的任务是编写一个简短的汇编程序,其中: 有两个字符串仅由拉丁字母的小写字母组成,以 ASCII 编码作为两个字节字符串放置在内存中。两个字符串都以值为 0 的字节结尾,字符串的位置是寄存器 ESI 和 EDI。比较两个字符串并以给定的方式设置标志 CF 和 ZF:

如果 ESI 字符串应该放在字典中的 EDI 字符串之前,则 CF=0 和 ZF=0

如果 ESI 字符串应该放在字典中的 EDI 字符串之后,则 CF=0 和 ZF=1

如果两个字符串相同,CF=0 和 ZF=1

在我的代码中它工作正常,直到我给出相同的字符串。我不知道为什么,但是当我给出相同的字符串时,程序说我的字符串不相同。我尝试过调试,所以我知道当我比较这些字符串的第三个元素时会发生这种情况,但我不明白为什么假设它们不同,因为它们是相同的。我的条件似乎工作正常,因为如果我写 cmp [esi+counter] 和 [esi+counter] 它可以工作并输出数字 3,但是对于 cmp [esi+counter] 和 [edi+counter] 它说它们是不同的(在我的案例输出数字 1)。我不知道问题出在哪里。有人可以解释一下发生了什么吗?我必须与 MASM 合作。我用的是VS17,程序是32位的。

这是我的代码:

.686
.model flat
public _main
extern _ExitProcess@4 : PROC
extern _MessageBoxA@16 : PROC 
.data
window_title db 'Example 42', 0
string1 db 'bies', 0
size1 = $ - string1
string2 db 'bies', 0
size2 = $ - string2
output db 80 dup(?), 0 ; variable to output result
.code
_main PROC
    mov esi, OFFSET string1
    mov edi, OFFSET string2
    mov al, OFFSET size1
    mov dl, OFFSET size2
    xor ebx, ebx ; set ebx = 0
    cmp al, dl ; compare sizes i want shorter to be counter for comparsion loop
    jbe second_as_counter ; case when bl is shorter - bl is counter
    movzx ecx, al 
    jmp compare

second_as_counter:
    movzx ecx, dl

compare:
    cmp ecx, ebx ; im checking if strings are the same size, ebx starts as 0
    je same ; if none of the others condition were fullfilled it means all the letter were the same
    push ecx ; remember the counter value, cause i have no more free registers
    mov ecx, dword ptr [edi+ebx] ; compare x element of both strings
    cmp dword ptr [esi+ebx], ecx
    ja after ; if letter in esi is bigger than letter in edi, it will be placed after edi in dictionary
    jb before ; else  edi is first 
    pop ecx ; return ecx previous value
    inc ebx ; increment counter, to check next elemnt of string
    jmp compare

after:
    stc ; set carry flag
    mov ecx, 2
    dec ecx ; clear zero flag
    mov byte ptr output, byte ptr '1' ; just an output to have a proof it works
    jmp koniec

before:
    clc ; clear carry flag
    mov ecx, 2
    dec ecx ; clear zero flag
    mov byte ptr output, byte ptr '2'
    jmp koniec

same:
    cmp al, dl ; check if lenghts are the same
    je identical 
    ja after
    jmp before

identical:
    clc ; clear carry flag 
    mov ecx, 1
    dec ecx ; set zero flag
    mov byte ptr output, byte ptr '3'


koniec:
    push 0; MB_OK
    push OFFSET window_title
    push OFFSET output 
    push 0
    call _MessageBoxA@16 
    push 0
    call _ExitProcess@4 
_main ENDP
END
; 1 - after(CF=1, ZF=0), 2 - before(CF=0, ZF=0), 3 - identical(CF=0, ZF=1)

编辑:我添加了一些评论

【问题讨论】:

出于某种原因,您正在比较双字而不是字节。你的代码也很难理解,你应该评论它并描述你的算法。 【参考方案1】:

如果 ESI 字符串应该放在字典中的 EDI 字符串之前,则 CF=0 和 ZF=0

如果 ESI 字符串应该放在字典中的 EDI 字符串之后,则 CF=0 和 ZF=1

如果两个字符串相同,CF=0 和 ZF=1

这不等同于你在程序最后一行提到的!

; 1 - after(CF=1, ZF=0), 2 - before(CF=0, ZF=0), 3 - identical(CF=0, ZF=1)

让我们假设程序中的行是正确的。无论如何,这是人们对这种操作所期望的更合乎逻辑的标志设置。 下一个 sn-p 中的代码将自动设置所有请求的标志。不需要STCCLCMOV ECX, 2DEC ECX 的所有恶作剧。

mov al, [edi + ebx]
cmp al, [esi + ebx]
ja  before            ;CF=0 ZF=0
jb  after             ;CF=1 ZF=0
; On equal you continu the loop that compares characters
; if any are left...

请注意,在处理字符串的字符时,您处理的是 bytes,而不是您所写的 dwords


cmp al, dl ; compare sizes i want shorter to be counter for comparsion loop
jbe second_as_counter ; case when bl is shorter - bl is counter
movzx ecx, al 
jmp compare
second_as_counter:
movzx ecx, dl
compare:

你没有为你的计数器计算最短字符串的长度!你在做相反的事情。当AL 小于或等于DL 时,会采用jbe,这意味着您应该将AL 放入ECX。 这是我设置计数器的版本:

    movzx   ecx, al  ; Optimistically considering 1st text to be shortest
    cmp     al, dl
    jbe     compare  ; Correct hunch
    movzx   ecx, dl  ; So who's perfect?
compare:

push ecx
mov  ecx, dword ptr [edi+ebx]
cmp  dword ptr [esi+ebx], ecx
ja   after
jb   before
pop  ecx

在上面的 sn-p 中,您应该将 pop ecx 放在分支出来的行的上方。这样你就可以保持堆栈平衡。在这个简单的程序中,它会被忽视,但在以后的程序中,这种错误会给你带来很多担忧。

push ecx
mov  ecx, dword ptr [edi+ebx]
cmp  dword ptr [esi+ebx], ecx
pop  ecx                       ; Keep a balanced stack
ja   after
jb   before

string1 db 'bies', 0
size1 = $ - string1
string2 db 'bies', 0
size2 = $ - string2

使用这些定义,您可以在长度中包含零终止符。您的代码不需要这个。写得更好:

string1 db 'bies', 0
size1 = $ - string1 - 1
string2 db 'bies', 0
size2 = $ - string2 - 1

push ecx ; remember the counter value, cause **i have no more free registers**

如果您将第一个字符串的长度放入DL 并将第二个字符串的长度放入DH,您将拥有AL 来存储字符串中的每个字符。

【讨论】:

以上是关于汇编 MASM 字符串比较的主要内容,如果未能解决你的问题,请参考以下文章

在汇编 (MASM) 中显示字符串的偶数字符

需要帮助使用 masm 以 80x86 汇编语言连接两个字符串

在汇编代码 (MASM) 中的字符串中查找子字符串的更好形式?

我用的是win7 32位系统,使用汇编语言编写程序的时候,输入输出字符串的汇编程序可以顺利执行

汇编语言学习笔记

MASM6.15汇编程序