Assembly ASMx86:如何在字符串中选择字母?

Assembly ASMx86:如何在字符串中选择字母?,assembly,x86,irvine32,Assembly,X86,Irvine32,我对字符在汇编中的工作方式感到困惑。既然所有字符都是位,那么这是否意味着寄存器中的所有字符基本上都是十六进制版本/ascii值 例如,如果我想把“我喜欢馅饼”放在寄存器中,它会显示为“48h 4C484B45h 504945h”吗?如果我想得到所有的“I”字母,我需要使用如下命令 ; "command to get string input" getI: cmp BYTE PTR [edx],48h je L1 L1: add [edx],1 loop

我对字符在汇编中的工作方式感到困惑。既然所有字符都是位,那么这是否意味着寄存器中的所有字符基本上都是十六进制版本/ascii值

例如,如果我想把“我喜欢馅饼”放在寄存器中,它会显示为“48h 4C484B45h 504945h”吗?如果我想得到所有的“I”字母,我需要使用如下命令

;   "command to get string input"  
getI:
    cmp BYTE PTR [edx],48h 
    je L1
L1: 
    add [edx],1
    loop getI  
我基本上是想找到一种从输入的字符串中分离字符的方法

这是否意味着寄存器中的所有字符基本上都是十六进制版本/ascii值

是的,就像在C中一样,字符串只是
char
元素的数组
char
只是一种窄整数类型,类似于
uint8\t
。(C标准没有定义
char
是否有符号,因此如果您实际使用C并且想要无符号变量,请使用
uint8\t
。)

无论如何,是的,在asm中,您应该将ASCII字符串中的字符视为8位整数,其中。e、 g.如果
ax
具有
[0..9]
中的值,则可以使用以下公式将其转换为十进制数字:

add   al, '0'    ; '0' is a nice way to write 0x30
将较大的整数转换为多位数十进制字符串显然更为复杂,需要除以或乘以10


如果寄存器中有字母ASCII字符,可以将其强制为小写、大写或使用

or    al, 0x20    ; tolower
and   al, ~0x20   ; toupper
xor   al, 0x20    ; opposite
'a'-'a'
是0x20,并且范围不跨越编码空间内的0x20边界。(即,所有小写字母都设置了该位,而大写字母都没有)。要检查ascii字符是否为字母,请参阅


如果我把“我喜欢馅饼”放在登记簿里

在寄存器中一次处理多个字符是一件棘手的事情,但对于高性能来说是必不可少的。编写只保留一个指向字符串中某个位置的指针并一次处理一个字节的代码更容易

NASM和MASM都有,所以你可以做

mov   eax, 'abcd'    ; put those bytes into eax, like if you'd loaded from  db 'a', 'b', 'c', 'd'
除非您在组装时知道字符串长度,否则您将不知道保存整个内容需要多少寄存器。通常情况下,您会一次将4或8个字符加载到寄存器中,并使用一些SWAR bithack来执行类似的操作,例如,如果您正在实现
strlen()

如果您知道所有字符都是ASCII字母,可以使用

mov  eax, [rsi]
or   eax, 0x20202020    ; packed tolower on all 4 chars

你的循环 你想实施什么

另外,
add[edx],1
不会进行汇编,因为操作数大小不明确。它可以是
addbyte[edx],1
,word,或
adddword[edx],1
。您的意思可能是
inc-edx

如果知道字符存在,则无需检查长度(或C样式隐式长度字符串中的终止零字节)

e、 g


你的方法几乎奏效了:

getI:
    cmp BYTE PTR [edx],'i'  ; your assembler will know what that is
    jne L1                  ; skip the code for 'i' handling, if it's NOT an 'i'
    ; here you do what
    ; ever you want to 
    ; with your 'i's here
L1: 
    add edx,1               ; better: 'inc edx'
    loop getI               ; loop only works, if you have CX loaded with the length of your "string"
                            ; either use it, or check for 0 chars INSIDE the loop
你的CPU不关心你的“字符串”,它只是内存中的字节数
您的“我喜欢饼”是字节“69h,20h,6ch,69h,6bh,65h,20h,70h,69h,65h,0”的ascii表示形式 当您将其中一个加载到寄存器中时,它只是一个字节值。。。字符串或字符没有什么特别之处


顺便说一句:并不是所有的寄存器都是相等的,有些寄存器有特殊的用途。您已选择edx作为“字符串指针”。。。x86 CPU有索引寄存器,有专门的指令来使用它们,这会更好地为您服务。但那是另一回事;-)

注意,空格也是字符。在ASCII编码中,它们具有ASCII代码32(0x20)。此外,通常不会将实际字符串内容放入寄存器中。在您的示例中,它肯定不起作用,因为
“我喜欢馅饼”
是10个字符(即10个字节),太大了,无法放入寄存器(如果我们不考虑SSE/AVX寄存器)。因此,您要做的是将字符串数据放在数据段中,或者如果它是动态分配的字符串,则放在堆或堆栈中,并且您放在寄存器中的是该字符串数据的地址。此外,ASCII中的48h是
'H'
<代码>'i'是69小时,而
'i'
是49小时。非常感谢,我现在能更好地理解它,但对于“添加[edx],1”,它应该是“添加edx,1”。那么,如果它只是一个单引号,它指的是ASCII表?假设我用“1”代替“I”,它会寻找ASCII版本的1而不是数字吗?你对edx是正确的,我会修正它:)(没有检查我从你那里复制的内容:P)再次:你的CPU不关心ASCII。因此,如果要检查ascii“1”,请使用
cmp“1”
,检查二进制1是
cmp 1
unsafe_strchr:
   ; ... get the char you want in al, and the pointer in esi
.search:
    inc    esi
    cmp    byte [esi-1], al
    jne  .search
; esi holds to the location
getI:
    cmp BYTE PTR [edx],'i'  ; your assembler will know what that is
    jne L1                  ; skip the code for 'i' handling, if it's NOT an 'i'
    ; here you do what
    ; ever you want to 
    ; with your 'i's here
L1: 
    add edx,1               ; better: 'inc edx'
    loop getI               ; loop only works, if you have CX loaded with the length of your "string"
                            ; either use it, or check for 0 chars INSIDE the loop