Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
String 8086中的字符串比较_String_Assembly_X86_X86 16_Strcmp - Fatal编程技术网

String 8086中的字符串比较

String 8086中的字符串比较,string,assembly,x86,x86-16,strcmp,String,Assembly,X86,X86 16,Strcmp,我对这个问题有意见。我不知道它想从我这里得到什么 问题:编写一个过程,将DS:SI处的源字符串与ES:DI处的目标字符串进行比较,并相应地设置标志。如果源小于目标,则设置进位标志。如果字符串相等,则设置零标志。如果源大于目标,则清除零和进位标志 我的答覆是: MOV ESI , STRING1 MOV EDI, STRING2 MOV ECX, COUNT CLD REPE CMPSB 我还是不确定。这是真的还是我应该试试别的 p、 s:我不明白为什么人们投票否决这个问题。我的问题怎么了?我想

我对这个问题有意见。我不知道它想从我这里得到什么

问题:编写一个过程,将DS:SI处的源字符串与ES:DI处的目标字符串进行比较,并相应地设置标志。如果源小于目标,则设置进位标志。如果字符串相等,则设置零标志。如果源大于目标,则清除零和进位标志

我的答覆是:

MOV ESI , STRING1
MOV EDI, STRING2
MOV ECX, COUNT
CLD
REPE CMPSB
我还是不确定。这是真的还是我应该试试别的


p、 s:我不明白为什么人们投票否决这个问题。我的问题怎么了?我想我们都是来学习的。还是不?错过了什么吗?

如果问题陈述说当你被调用时指针已经在
SI
DI
中,你不应该碰它们

对于所有函数,16位代码通常不遵循单一调用约定,在寄存器中传递(前几个)参数通常是好的(指令更少,并避免存储/重新加载)。32位x86调用约定通常使用堆栈参数,但这已经过时。Windows x64和Linux/Mac x86-64 System V ABIs/调用约定都使用寄存器参数


然而,问题陈述没有提到计数因此,您正在为以零字节结尾的字符串实现
strcmp
,而不是为已知长度的内存块实现
memcmp
您不能使用单个
rep
指令,因为您需要检查字符串的结尾是否不相等如果您只是传递一些较大的字符串,并且字符串相等,
repe cmpsb
将继续通过终止符。

repe cmpsb
如果知道任一字符串的长度,则可用。e、 g.在CX中取一个长度参数,以避免在两个字符串中都超过终止符的问题

但就性能而言,
repe cmpsb
无论如何都不快(比如Skylake vs.Ryzen上每次比较2到3个周期,或者推土机系列上每次比较4个周期)。只有
rep-mov
rep-sto
在现代CPU上是有效的,优化的微码一次复制或存储16(或32或64)字节

内存中有2种主要的字符串:显式长度字符串< /String >(指针+长度),如C++ +Cudio:St::String < /C>,和<强>隐式长度字符串,在这里你只有一个指针,字符串的结尾用前哨/终止符标记。(如使用

0
字节的C
char*
,或使用
'$'
作为终止符的DOS字符串打印函数。)


一个有用的观察结果是,您只需要检查其中一个字符串中的终止符。如果另一个字符串有终止符,而这个字符串没有,则将不匹配

因此,您希望将一个字节从一个字符串加载到寄存器中,并检查它是否为teminator,检查另一个字符串是否为内存

如果您需要实际使用ES:DI而不是使用默认DS段基的DI,您可以使用
CMPAL[ES:bx+DI]
(NASM语法,根据需要进行调整,如
CMPAL,ES:[bx+DI]
).可能是您打算使用
lodsb
scasb
的问题,因为。)

用法:将指针放在SI/DI中,
调用strcmp
/
je match
,因为匹配/不匹配状态为标志。如果要将条件转换为整数,386和更高版本的CPU允许
sete al
根据
e
quals条件(ZF==1)在al中创建0或1

使用
sub-al[mem]
而不是
cmp-al[mem]
,我们将得到
al=str1[i]-str2[i]
,仅当字符串匹配时才给我们一个0。如果字符串仅包含0..127之间的ASCII值,则不会导致有符号溢出,因此可以将其用作有符号返回值,实际告诉您哪个字符串在另一个字符串之前/之后排序。(但是,如果字符串中可能有高ASCII 128..255字节,我们需要先将零或符号扩展到16位,以避免(无符号)5-(无符号)254=(有符号)+7之类的大小写由于8位环绕而出现有符号溢出

当然,通过我们的FLAGS返回值,调用者已经可以使用
ja
jb
(无符号比较结果)
,或者
jg
/
jl
,如果他们想将字符串视为包含
有符号字符的字符串。无论输入字节的范围如何,这都是有效的

或内联此循环,因此
jne不匹配
直接跳转到有用的地方

,但BX可以是基,SI和DI都可以是索引。我使用了索引增量,而不是
inc SI
inc DI
。使用
lodsb
也是一个选项,甚至可能
scasb
将其与其他字符串进行比较。(然后检查终止符。)


表演 在一些现代x86 CPU上,索引寻址模式可能会较慢,但这确实可以将指令保存在循环中(因此对于真正的8086来说,在代码大小很重要的情况下,这是很好的)。虽然要真正调整8086,我认为
lodsb
/
scasb
将是最好的选择,取代
mov
加载和
cmpal[mem]
,还有
inc bx
。如果您的呼叫约定不能保证这一点,请记住在循环外使用
cld

如果您关心现代x86,请使用
movzx eax,byte[si+bx]
为不单独重命名部分寄存器的CPU断开对旧值eax的错误依赖。(如果使用
sub al[str2],断开错误的dep尤其重要)
因为这将使它变成一个通过EAX的2周期循环的依赖链,在CPU上,而不是通过Sandybridge.IvyBridge和更高版本将AL与EAX分开重命名,所以
mov AL,[me]
;; inputs:  str1 pointer in DI, str2 pointer in SI
;; outputs: BX = mismatch index, or one-past-the-terminators.
;;          FLAGS:  ZF=1 for equal strings (je),  ZF=0 for mismatch (jne)
;; clobbers: AL (holds str1's terminator or mismatching byte on return)
strcmp:
    xor    bx, bx
.innerloop:                 ; do { 
    mov    al, [si + bx]        ; load a source byte
    cmp    al, [di + bx]        ; check it against the other string
    jne   .mismatch             ; if (str1[i] != str2[i]) break;

    inc    bx                   ; index++

    test   al, al               ; check for 0.  Use cmp al, '$' for a $ terminator
    jnz   .innerloop        ; }while(str1[i] != terminator);

    ; fall through (ZF=1) means we found the terminator
    ; in str1 *and* str2 at the same position, thus they match

.mismatch:               ; we jump here with ZF=0 on mismatch
    ; sete  al           ; optionally create an integer in AL from FLAGS
    ret
strcmp:
         mov    ecx,DWORD PTR [esp+0x4]
         mov    edx,DWORD PTR [esp+0x8]     # load pointer args

L(oop): mov     al,BYTE PTR [ecx]          # movzx eax, byte ptr [ecx] would be avoid a false dep
        cmp     al,BYTE PTR [edx]
        jne     L(neq)
        inc     ecx
        inc     edx
        test    al, al
        jnz     L(oop)

        xorl    eax, eax
        /* when strings are equal, pointers rest one beyond
           the end of the NUL terminators.  */
        ret

L(neq): mov    eax,  1
        mov    ecx, -1
        cmovb  eax, ecx
        ret