基于ASM x86(Intel语法)的计算器,为什么减法不起作用?

编程语言 2026-07-11

我在学习x86汇编(Intel语法),在FreeDOS下把一个简单的计算器写成一个.COM程序。程序从键盘读取两个数字和一个运算符,然后执行运算。

加法工作正常,但减法给出错误的结果(表现得像加法,或得到错误的值)。

示例: 输入:2-1= 期望输出:1 实际输出:3(或错误的数值)


程序流程:

  1. BX初始化为5000
  2. 每次按下一个键通过 int 16h 读取
  3. 输入通过 grd 例程存储到内存中
  4. 为了简化内存处理,我将值存放在固定的内存地址:
    5000 -> 第一个操作数
    5002 -> 运算符
    5004 -> 第二个操作数
  5. 当BX到达5004时,执行跳转到 cn
  6. cn 中,将操作数装入CL与 DL
  7. 从内存中读取运算符并与 '+'(43)和 '-'(45)进行比较
  8. 根据结果跳转到加法或减法
  9. 通过int 10h打印结果

限制与设计取舍:

  • 仅支持单个数字操作数(0–9)
  • 仅实现了加法与减法
  • 输入逐字符读取
  • 未进行严格的输入验证
  • 结果以单个数字输出

我在每次输入后将BX增加2。选择这种做法,是为了避免数值重叠,并在学习底层内存管理时让内存布局更易于理解。

我怀疑问题可能与ASCII值的处理方式、或运算符的存放方式有关。

下面是代码:

org 100h
mov bx, 5000

cun:
xor ah, ah
int 16h
call grd
add al, 48
cmp al, 81
je fin
mov ah, 0Eh
int 10h
jmp cun

cn:
pop bx
mov bx, 5000
mov cl, [bx]
add bx, 4
mov dl, [bx]
sub bx, 2
cmp byte [bx], 43
je sum
cmp byte [bx], 45
je res

sum:
add cl, dl
mov ah, 0Eh
mov al, cl
add al, 48
int 10h
jmp cun

res:
sub cl, dl
mov ah, 0Eh
mov al, cl
add al, 48
int 10h
jmp cun

grd:
sub al, 48
mov [bx], al
cmp bx, 5004
jge cn
add bx, 2
ret

fin:
int 20h

解决方案

看到你能对程序做出如此详细的解释,我感到很欣慰;但同时你的源代码完全缺少(尾部)注释!这绝对是需要改进的地方……

你的grd子程序有点特殊!

你对每个输入的字符代码都减去48,无论它是否代表一个数字;并且在收到三个输入后就不再返回。后者虽然在某些情况下能工作,但在学习语言时你大概不应该这么做。 对每个输入都减去48也是导致程序结束测试(add al, 48 cmp al, 81)显得尴尬的原因之一。顺便说一句,你可以把这写成 cmp al, 'Q',要清晰得多。

我不会使用你写的那种子例程,而是把所有内容放在一个直接的循环里:

org 100h
mov di, 5000   ; Teletype uses BH (BX) for its display page

cun:
xor ah, ah     ; BIOS.ReadKeystroke
int 16h        ; -> AX
cmp al, 'Q'    ; Test before anything else (no echo needed)
je  fin
mov bx, 0007h  ; Teletype uses BH (BX) for its display page
mov ah, 0Eh    ; Do the echo BEFORE you convert anything!
int 10h
sub al, '0'    ; Convert AND check for decimal digit
cmp al, 10
jb  Gotit
add al, '0'    ; Restore original ASCII
Gotit:
stosb          ; AL={[0,9],"+","-"}
cmp di, 5003
jb  cun

mov bx, 5000
mov cl, [bx]   ; First operand
mov dl, [bx+2] ; Second operand
cmp byte [bx+1], "+"
je  sum
cmp byte [bx+1], "-"
je  res
jmp cun        ; Redo
  • 如果运算符是除了 "+" 或 "-" 之外的任意字符,你不应该在sum中继续执行,而应该从头重新开始(或退出)。
  • 请注意:我把存储空间缩减为每项数据一个字节。
  • 请注意,你并不局限于仅使用 [bx]。在NASM中你还可以像在 [bx+1] 那样添加偏移量。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章