Assembly 我不知道';我不明白为什么我的汇编代码没有';行不通

Assembly 我不知道';我不明白为什么我的汇编代码没有';行不通,assembly,Assembly,这段代码的目标是扫描数据库,每次出现“no”一词时,它都会将其替换为“**”,程序将在字符“$”处停止 IDEAL MODEL small STACK 100h DATASEG ; -------------------------- ; Your variables here ; -------------------------- String db ' no, I am not no ? yes no reality $' CODESEG start: mov ax, @data

这段代码的目标是扫描数据库,每次出现“no”一词时,它都会将其替换为“**”,程序将在字符“$”处停止

IDEAL
MODEL small
STACK 100h
DATASEG
; --------------------------
; Your variables here
; --------------------------
String db ' no, I am not no ? yes no reality $'
CODESEG
start:
    mov ax, @data
    mov ds, ax
; --------------------------
; Your code here
; --------------------------
    mov bx, 0 
    cmp [byte ptr bx], '$'
    jz exit
    cmp [word ptr bx], 'no'
    jz switch

first:
    inc bx
    cmp [byte ptr bx], '$'
    jz exit
    cmp [word ptr bx], 'no'
    jnz first
switch: 
    mov [word ptr bx], '**'
    jmp first
exit:
    mov ax, 4c00h
    int 21h
END start
什么样的“数据库”
db
代表“定义字节”,而不是数据库

另外,当您将其与字符串文字结合使用时,实际上定义了几个字节,而不仅仅是一个字节

jz退出
。。。比较之后,更常见的做法是使用别名
je
作为“跳转相等”缩短-这与
jz
指令相同,但与非零值进行比较会更好地阅读代码

mov bx,0
这是获取数据偏移量的脆弱方法,而不是使用定义的标签:
mov bx,offset String
lea bx,[String]

此外,如果要加载带有
String-1
偏移量的bx(首先
inc bx
将其固定为+0地址),则可以完全重用循环代码。因此,您可以避免在开始时出现两条重复的
cmp
指令

最后。。。检查清单文件或反汇编,比较什么样的值
cmp[word ptr bx],“no”
。因为您没有提到您的汇编程序,所以很难说您的汇编程序是如何编译的

例如,NASM对字符串文本有例外,它将按照人类期望的“字符串顺序”(word value
0x6F6E
)将其组合为两个字节“n”,“o”,即使它标记为
word
size value(但您没有使用NASM,因为
[word ptr bx]
在那里的语法无效)

在MASM/TASM中,我想汇编器会将
word
值视为真正的16位值,即
'n'*256+'o'=0x6E*256+0x6F=0x6E6F
,即它会检测内存中的“on”子字符串,因为x86是小尾端,所以
word 0x6E6F
被分解为单个字节,如
6F 6E='o',n'

而是使用
cmp[word ptr bx],('n'+'o'*256)
以确保字母顺序正确(以小尾端方式)

编辑:我用TASM 4.1进行了尝试(编写了这两个变体,并使用
/l
命令行开关生成列表文件):

从指令操作码(
81 3F 6E6F
vs
81 3F 6F6E
)中可以看到,
的“否”
正在搜索子字符串“开”

有趣的是,您必须完全理解小尾端和字节与单词之间的微妙关系,因为如果您将机器代码检查为ASCII文本,第一个变量包含“no”字符串(将搜索“on”),第二个变量包含“on”字符串(将搜索“no”)

实际上我自己也弄糊涂了,在操作码中,单词值显示为单词值,即
81 3F 6E6F
以字节
81 3F 6F 6E
为单位,当被视为ASCII时,它将包含错误的“on”,这是它寻找的子字符串


为什么*256有效

  • 字节
    为8位
  • word
    是16位
  • x86机器是小端机,内存可以通过单个字节寻址
这意味着,当您指示CPU写入
word
值(如1500)时,它将首先存储最低有效字节,然后存储最高有效字节。对于1500=这是两个字节,220和5。因为5*256+220=1500。在十六进制格式中(这使得经验丰富的asm程序员非常喜欢它),更容易看到:1500=
05DCh
,存储的字节首先是
0DCh
,然后是
05h

现在,TASM中所谓的初学者“字符串”是ASCII编码的,单字母=单字节(现代UTF-8编码确实使用每个字母的可变字节长度,尽管值小于128的代码与ASCII兼容,因此任何7位ASCII文本也是有效的UTF-8文本)

所以像
“不,我不是不?yes no reality$'
被组装成字节:

20 6E 6F 2C 20 49 20 61 6D 20 6E 6F 74 20 6E 6F 20 3F
20 79 65 73 20 6E 6F 20 72 65 61 6C 69 74 79 20 24
请注意,“no”由两个字节组成
6e6f

如果将其读取为字值,如
mov ax,[String+1]
,CPU将使用字值的小端编码,
ax
将等于
06F6Eh
(第一个字节为低位8位,第二个字节为高位8位)

现在,通过编写
'n'+'o'*256
,您可以手动确定哪个字母将被检查为第一个字节(
'n'
。这是从完整数学
'n'*256^0
(这里我指的是幂,而不是异或)),哪个字母将被检查为第二个字节(
'o'*256
,其中256是256的一次幂)

256的力量来自哪里。。。每个字节为8位,因此它可以存储0到255之间的值(当解释为无符号整数时),即正好256=28个不同的值。所以,当单字节上的值用完时,如果执行+1到第二个更高的字节,则在256个边界处执行此操作。。。第三和第四字节(32b值)的值分别为2562和2563

即,32位值16909060将作为四个字节存储在内存中
04 03 02 01
。要验证:4*2560+3*2561+2*2562+1*2563=16909060。。。。哎呀


因此,如果要同时检查两个字母作为单词值,必须查找单词值
06F6Eh
(根据
6Eh='n'
和第二个字节
6Fh='o'
测试第一个字节).

您应该详细说明“不起作用”。您是否可以说:代码实际上做了什么?您希望代码做什么?请提供并在给出的示例中查看@fc,但这是毫无疑问的!由于修改后不会实际打印出字符串,因此判断该字符串未被修改的唯一方法是在调试器中运行代码(单步执行或设置断点)。而且,如果您能够做到这一点,那么您应该非常熟悉在每个指令之后检查数据,以确保它
20 6E 6F 2C 20 49 20 61 6D 20 6E 6F 74 20 6E 6F 20 3F
20 79 65 73 20 6E 6F 20 72 65 61 6C 69 74 79 20 24