我正在尝试制作一个执行此任务的混合程序(C ++和ASM),但我的ASM模块并没有像预期的那样工作。文本和字符加载在程序的C ++部分中。我甚至不确定我在哪里犯错误。
编辑:我使用的是Borland C ++和TASM编译器(DosBOX)。该程序显示错误的出现次数(尽管实际出现次数,但结果是相同的,但是随着我对程序的看似无关的改变而改变),或者喷出奇怪的字符(如向下箭头符号或笑脸)在数字应该在哪里 - 这是由在C ++中设置不正确的变量类型引起的。实际问题肯定是由一些寄存器的值改变造成的,这些寄存器显然必须在程序之前和之后保持不变(正如David Wohlferd和许多其他人指出的那样 - 谢谢大家)。我查看了我们的导师给我们的文件,根据它们,这些寄存器是(对于C / C ++):DS,SS,SP,BP,SI,DI和标志寄存器,如果在过程中修改了方向标志。
以下是有效的更正代码:https://pastebin.com/JPxMxzmK
.MODEL SMALL, C
.STACK 400h
.DATA
.CODE
public CountChar
CountChar PROC
push bp
mov bp, sp
xor bx, bx
mov si, [bp+4]
mov ah, [bp+6]
Check:
mov dl, [si]
cmp dl, 0
je EndOfP
cmp dl, ah
je Increasing
inc si
jmp Check
Increasing:
inc bx
inc si
jmp Check
EndOfP:
mov ax, bx
pop bp
ret
CountChar ENDP
END
答案 0 :(得分:2)
你实际上并没有说过出了什么问题。这使得很难确定“答案”可能是什么。但是我会对它采取一些措施(嘿,我需要业力)。
阅读你的代码,代码似乎没有任何“错误”(尽管我会做一些不同的事情)。但是,如果要与C交互,汇编程序必须遵循一些规则。最重要的一个规则是,如果更改某些寄存器,则负责按照找到它们的方式将它们放回去。你的代码违反了这条规则。
作为一个新手,这对你来说似乎有点混乱。毕竟,它不像你的C代码使用寄存器,对吧?除了您的C代码 使用寄存器。实际上,这基本上是C编译器的全部目的:将C代码(不使用寄存器)转换为汇编代码(确实如此)。
如果我们可以看到为调用CountChar的代码生成汇编程序代码,我们会看到2个push
语句(将参数放在堆栈上),然后是call CountChar
。但调用代码(可能)使用一些其他寄存器(如si)来保存其他值。你的CountChar例程不能踩到那些值,否则当CountChar退出时会发生奇怪的事情。
您可能会问:为什么调用例程在调用代码之前不会保存所有寄存器的值?它可能。但是每次调用函数时保存所有寄存器(并恢复所有寄存器)都会减慢速度。而且你所调用的例程完全有可能甚至没有使用所有寄存器,这会浪费时间而没有任何好处。
相反,决定这些事情的人会做出妥协:当调用函数时,调用者将假定某些寄存器在函数返回时保持不变。根据函数的定义方式,哪些寄存器可以改变一点点。您可能还没有碰到它,但有几组规则代码经常使用(cdecl,stdcall,pascal,fastcall等)。
正如Raymond所说,对于16位代码,cdecl说bp,si和di(以及DS,但我们不会去那里)必须由被调用者保留。当你编写C代码时,这一切都是为你完成的。但是当你编写汇编程序时,你必须知道(并遵循)这些规则。
这并不意味着你不能使用这些寄存器。只是如果你这样做,你必须保存旧值(例如使用push si
)并在函数退出之前将其恢复(例如使用pop si
)。当然,推送/弹出不是免费的,因此您可能希望在使用其中一个必须保存/恢复之前先使用所有其他寄存器。
由于这听起来像是家庭作业,我不会发布重新编写的代码(我还没有运行它的环境),但我会给你一些建议考虑:
si
(必须保留),而是使用cx
(不支持)。bx
来保存计数(然后将值移至ax
),而只需使用ax
来保存计数。您可以使用bx
来保留要搜索的字符。test dl, dl
比使用cmp dl, 0
(稍微)快一些。查看这段代码:
cmp dl, ah
je Increasing
inc si
jmp Check
Increasing:
inc bx
inc si
jmp Check
如果您在inc si
指令之前移动了cmp
,会发生什么?然后你不必在两个地方拥有它:
inc si
cmp dl, ah
je Increasing
jmp Check
Increasing:
inc bx
jmp Check
但看看发生了什么。现在我们有2个跳转指令紧挨着。这看起来有点不必要吗?如果不是跳到Increasing
上的je
,而是Check
,那么你会跳到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个人今天使用你的代码),它会让生活变得更好更轻松。甚至(尤其是?)如果代码错误,则将注释显示为您的意图/期望。
这是我用你提供的信息做出的最佳答案。