Assembly 计算字符串中特定字符的出现次数

Assembly 计算字符串中特定字符的出现次数,assembly,x86-16,tasm,Assembly,X86 16,Tasm,我正试图制作一个混合程序(C++和ASM)来完成这个任务,但我的ASM模块并没有按预期工作。文本和字符被加载到程序的C++部分中。我甚至不知道我在哪里犯了错误 编辑:我使用Borland C++和TASM编译器(DOSBOX)。该程序要么显示错误的出现次数(结果与实际发生次数相同,但与我对该程序所做的看似无关的更改有关),要么输出奇怪的字符(如向下箭头符号或笑脸)在一个数字应该出现的地方,这是因为在C++中设置了不正确的变量类型。 实际问题最明显的原因是某些寄存器的值发生了变化,这些寄存器显然必

我正试图制作一个混合程序(C++和ASM)来完成这个任务,但我的ASM模块并没有按预期工作。文本和字符被加载到程序的C++部分中。我甚至不知道我在哪里犯了错误

<>编辑:我使用Borland C++和TASM编译器(DOSBOX)。该程序要么显示错误的出现次数(结果与实际发生次数相同,但与我对该程序所做的看似无关的更改有关),要么输出奇怪的字符(如向下箭头符号或笑脸)在一个数字应该出现的地方,这是因为在C++中设置了不正确的变量类型。 实际问题最明显的原因是某些寄存器的值发生了变化,这些寄存器显然必须在过程前后保持不变(正如David Wohlferd和其他许多人向我指出的那样,谢谢大家)。我查看了导师给我们的文件,根据这些文件,这些寄存器是(对于C/C++):DS、SS、SP、BP、SI、DI,如果在过程中修改了方向标志,则为标志寄存器

以下是正确的代码:


你还没说到底出了什么问题。这使得很难确定“答案”可能是什么。但是我要尝试一下(嘿,我需要因果报应)

阅读您的代码时,代码似乎没有任何实际的“错误”(尽管有一些事情我会做得不同)。然而,如果汇编程序要与C进行交互,它必须遵循一些规则。最重要的规则之一是,如果您更改某些寄存器,您有责任将它们放回您找到它们的方式。你的代码违反了这条规则

作为一个新手,这可能会让你有点困惑。毕竟,它不像你的C代码使用寄存器,对吗?除了你的C代码使用寄存器。事实上,这基本上就是C编译器的全部目的:将C代码(不使用寄存器)转换为汇编代码(使用寄存器)

如果我们可以看到为调用CountChar的代码生成的汇编代码,我们将看到2个
push
语句(将参数放在堆栈上),然后是
callcountchar
。但是调用代码(可能)使用其他一些寄存器(如si)来保存其他值。您的CountChar例程不能践踏这些值,否则当CountChar退出时会发生奇怪的事情

您可能会问:为什么调用例程在调用代码之前不保存所有寄存器的值?可以。但是,每次调用函数时保存所有寄存器(并恢复它们)确实会减慢速度。而且完全有可能您正在调用的例程甚至没有使用所有的寄存器,这将使它毫无益处地浪费时间

相反,决定这些事情的人做出了妥协:调用函数时,调用方会假设函数返回时某些寄存器保持不变。确切地说,哪些寄存器可以根据函数的定义稍微改变。您可能还没有碰到它,但代码通常会使用几组规则(cdecl、stdcall、pascal、fastcall等)

正如Raymond所说,对于16位代码,cdecl说bp、si和di(以及DS,但我们不会去那里)必须由被调用方保留。当您编写C代码时,这一切都已为您完成。但当您编写汇编程序时,您必须知道(并遵循)这些规则

这并不意味着你不能使用这些寄存器。如果这样做,则必须在函数退出之前保存旧值(例如使用
push si
)并将其还原(例如使用
pop si
)。当然,执行push/pop并不是免费的,所以在使用其中一个必须保存/恢复的寄存器之前,您可能希望先使用所有其他寄存器

因为这听起来像是一个家庭作业,所以我不会发布重新编写的代码(我没有运行它的环境),但我会给你一些建议供你考虑:

  • 不要使用
    si
    (必须保留),而是使用
    cx
    (不保留)
  • 不要使用
    bx
    保存计数(然后将值移动到
    ax
    ),只需使用
    ax
    将计数保留在第一位即可。您可以使用
    bx
    保存要搜索的字符
  • 当测试寄存器是否为零时,使用
    testdl,dl
    比使用
    cmpdl,0
    要(稍微)快
  • 查看这段代码:

    cmp         dl, ah
    je          Increasing
    inc         si
    jmp         Check
    
    Increasing:
    inc         bx
    inc         si
    jmp         Check
    
    如果在
    cmp
    指令之前将
    inc si
    向上移动,会发生什么情况?这样,您就不必在两个位置安装它:

    inc         si
    cmp         dl, ah
    je          Increasing
    jmp         Check
    
    Increasing:
    inc         bx
    jmp         Check
    
    但看看发生了什么。现在我们有两个跳转指令紧挨着彼此。这不是有点不必要吗?如果您不是在
    je
    上跳到
    增加
    ,而是跳到
    检查
    如果
    jne
    ?现在,您的代码如下所示:

    inc         si
    cmp         dl, ah
    jne         Check
    
    inc         bx
    jmp         Check
    
  • 对汇编代码的任何审查都必须说明注释。这是一小段代码,只是一个练习。但你还是应该养成这样的习惯:

    inc         si      ; Position to next byte
    cmp         dl, ah  ; Is this the byte we are counting?
    jne         Check
    
    inc         bx      ; Found one
    jmp         Check
    
    即使像这样微不足道的注释也会使代码更容易理解

    当您在几个月(或几年)后重新使用此代码时,或者当其他人必须拿起您的代码并尝试理解它时(就像今天至少有3个人使用您的代码一样),这将使您的生活更加轻松。即使(特别是?)代码是错误的,发表评论也表明了你的意图/期望


  • 根据您提供的信息,这是我能给出的最好答案。

    可能是因为您正在更改C希望保留的寄存器的值。我不记得16位的规则是什么,但你正在改变
    inc         si      ; Position to next byte
    cmp         dl, ah  ; Is this the byte we are counting?
    jne         Check
    
    inc         bx      ; Found one
    jmp         Check