我不明白为什么我的汇编代码不起作用

时间:2017-11-12 11:51:06

标签: assembly

此代码的目标是扫描数据库,每次出现“no”字样时,它将替换为“**”,程序将停在char'$'

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

1 个答案:

答案 0 :(得分:1)

什么样的"数据库" db代表"定义字节",而不是数据库。

同样,当你将它与字符串文字结合使用时,实际上是在定义几个字节,而不只是一个字节。

jz exit ...比较之后,通常使用别名je作为"跳跃等于" 缩短 - 它&#39 ; s是相同的jz指令,但对于检查代码的任何人来说,与非零值进行比较会更好地读取。

mov bx,0这是获取数据偏移的脆弱方式,而是使用定义的标签:mov bx, OFFSET Stringlea bx,[String]

如果您要加载带有String-1偏移量的bx(第一个inc bx将其固定为+0地址),您也可以完全重用循环代码。因此,您可以在开头避免使用两个重复的cmp说明。

最后......检查列表文件或反汇编,比较cmp [word ptr bx], 'no'的值是什么。由于你没有提到你的汇编程序,所以不可能告诉你,你的汇编是如何编译的。

例如,NASM对字符串文字有例外,它将把它组合为两个字节' n'' o'在人类预期的字符串顺序" (字值0x6F6E),即使它被标记为word大小值(但您没有使用NASM,因为[word ptr bx]将是无效的语法)。

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

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

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

 16 0000  81 3F 6E6F         cmp [word ptr bx],'no'
 17 0004  81 3F 6F6E         cmp [word ptr bx],'n'+256*'o'

从指令操作码(81 3F 6E6F vs 81 3F 6F6E)可以看出,您的'no'正在"上搜索子字符串"

有趣的是,你必须完全理解little-endian和byte vs word细微之处,因为如果你将机器码检查为ASCII文本,那么第一个变体包含" no" string(将搜索" on"),第二个包含" on" string(将搜索" no")。

实际上我混淆了自己,在操作码中,单词值显示为单词值,即81 3F 6E6F以字节81 3F 6F 6E表示,当以ASCII格式查看时,它将包含错误的" on& #34;,它是它寻找的子字符串。

为什么* 256有效...

  • byte是8位。
  • word是16位。
  • x86机器是little-endian,内存可通过单个字节寻址。

这意味着,当您指示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文本)。

因此像' no, I am not no ? 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"由两个字节6E 6F组成。

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

现在通过编写'n' + 'o'*256,您手动决定将哪个字母作为第一个字节进行检查('n' ..这是从完整数学'n' * 256^0缩短的(通过^我的意思是这里的功率,不是xor)),它将被检查为第二个字节('o'*256,其中256是256的第一个幂)。

当256的幂出现时...每个字节是8位,因此它可以存储0到255的值(当被解释为无符号整数时),即恰好256 = 2 8 不同的值。因此,当您在单字节上用完值时,并且在第二个较高字节上执行+1时,您在256个边界处执行此操作...而第三个和第四个字节(对于32b值)则为256 2 值和256 3 值。

即。 32位值16,909,060将作为四个字节04 03 02 01存储在内存中。验证:4 * 256 0 + 3 * 256 1 + 2 * 256 2 + 1 * 256 3 = 16,909,060 .... yaaay ..

因此,如果你想同时检查两个字母,作为一个字值,你必须寻找字值06F6Eh(测试6Eh = 'n'的第一个字节和第二个字节{{1 }})。