问题描述
我对汇编语言很陌生,所以这里没有介绍,但是我有这个程序应该返回一个或多个字符的反数,例如A-> Z,B-> Y等。代码可以正常工作并正确转换字符,但程序不应该在屏幕上显示它或在应有的时候循环显示它,而是返回int 21h
的控制权。
.8086
.model small
.data
; Variables
prompt_in db 0Dh,"Input: $"
prompt_out db 0Dh,0Ah,"Output: $"
.code
main proc
; Load Variables
mov ax,@data
mov ds,ax
; display Input Prompt
mov dx,offset prompt_in
mov ah,09h
int 21h
mov cl,00
mov ah,01h
; Read input and push to BX until User presses Return (Enter)
read:
int 21h
mov bl,al
push bx
inc cx
cmp al,0Dh
jz return
jmp read
; display Output Prompt
return:
mov dx,offset prompt_out
mov ah,09h
int 21h
; Calculate Inverse Letter and display
calc:
mov ah,00
pop bx
cmp bl,0Dh
jz calc
mov al,bl
sub al,41h
mov cl,19h
mov ch,2
mul ch
sub cl,al
add bl,cl
mov dl,bl
int 21h ; Stops Code Here without displaying anything more
loop calc ; Doesn't Loop Here
mov ah,4Ch
int 21H
main endp
end main
解决方法
将字符写入标准输出的DOS服务是int 21h
with ah = 02h
。但这不是你在做什么:
-
仅在
calc
标签之后,您就加载ah
而不是00
的值。 -
几行后,您有了一条
02h
指令。请记住,x86上的mul ch
正在扩大; 8位乘法会在mul
中产生16位结果,尤其是会覆盖ax
。现在,在您的程序中,结果可能适合8位,因此ah
再次设置为零。
当您使用ah
调用int 21h
时,会调用terminate program service(ah = 0
的不推荐使用的前身),这就是程序此时终止的原因。
快速的解决方案是在int 21h / ah = 4Ch
之前插入mov ah,02h
,并删除较早的int 21h
,这是错误的,也是多余的。
您可以通过注意到乘以2与向左移1相同来进一步改进程序,这是更快的并且不需要太多寄存器。如果结果溢出8位(结果的大部分丢失,而不是放入mov ah,00
中),结果将完全不同,但是您无论如何都不希望处理这种情况。因此,您的ah
可以简单地替换为mov ch,2 / mul ch
。而且,此操作根本不会触及shl al,1
。
Nate Eldredge's answer彻底解释了为什么您的程序在显示任何输出之前就早退出的原因。希望您现在已纠正此问题。然后您会看到程序崩溃!
-
您正在使事情复杂化。将A-Z转换为Z-A的任务很简单,不涉及乘法和移位。
sub al,"A" sub al,"Z"-"A" neg al add al,"A"
更短:
sub al,"A" neg al add al,"Z"
-
该程序将永远循环并最终崩溃,因为您使用的是依赖于
LOOP
寄存器的CX
指令,并且您的代码正在使用大的非零值重新加载此计数器寄存器值。mov ch,2 <-- reloads with value 512+ ... loop calc <-- jumps forever
这将起作用:
pop dx ; First pop is the carriage return for sure!
; Why do you even push it in the first place?
calc:
pop dx
sub dl,"A"
neg dl
add dl,"Z"
mov ah,02h ; DOS.PrintCharacter
int 21h
loop calc
请注意,在输入开始时,您应该将整个CX
寄存器清零,而不仅仅是写入mov cl,0
。依靠您使用的模拟器的特定行为(大约为零寄存器)是一个坏习惯。