首页 > 解决方案 > 原始计算器(+、-、*、/)。添加以十六进制表示法工作的能力

问题描述

我写了一个计算器,您可以在其中执行十进制和二进制系统的运算。我还尝试添加以十六进制表示法工作的能力,但认为它是错误的。请帮我弄清楚如何解决这个问题。

Calc
Used
B - 二进制
D - 十进制
+, -, *, / - 运算
= 或 ENTER - 显示结果并退出

表达式: (HEX)A+ (HEX)B = (DEC)5
按任意键退出...

这是程序代码:

LOCALS @@
.model tiny
 
.code
 .386
        org     100h
main    proc
 
        jmp     start
 
        OpMul           equ     '*'
        OpDiv           equ     '/'
        OpAdd           equ     '+'
        OpSub           equ     '-'
        
        CrLf            db      0Dh, 0Ah, '$'
        msgAbout        db      'Calc', 0Dh, 0Ah
                        db      'Used', 0Dh, 0Ah
                        db      'B - binary base', 0Dh, 0Ah
                        db      'D - decimal base', 0Dh, 0Ah
                        db      'H - hex base', 0Dh, 0Ah
                        db      '+, -, *, / - operations', 0Dh, 0Ah
                        db      '= or ENTER - show result and quit', 0Dh, 0Ah, '$'
        msgChangeBase2  db      08h, 08h, 08h, 08h, 08h
        msgBase2        db      ' (BIN)', '$'
        msgChangeBase10 db      08h, 08h, 08h, 08h, 08h
        msgBase10       db      ' (DEC)', '$'
        msgChangeBase16 db      08h, 08h, 08h, 08h, 08h
        msgBase16       db      ' (HEX)', '$'
        msgPromptExpr   db      0Dh, 0Ah, 'Expression: ', '$'
        msgResult       db      'Result: ', '$'
        msgPressAnyKey  db      0Dh, 0Ah, 'Press any key to exit...', '$'
        Base            dw      ?  
        EnBaseChange    dw      ? 
        Operand1        dw      ?  
        Operand2        dw      ? 
        Operation       db      ?
        OperationNext   db      ? 
start:
        mov     ah,     09h
        lea     dx,     [msgAbout]
        int     21h
        
        mov     ah,     09h
        lea     dx,     [msgPromptExpr]
        int     21h
 
        mov     [Operand1],     0
        mov     [Operand1],     0
        mov     [Operation],    OpAdd
        mov     [EnBaseChange], 1
        mov     [Base], 10
        mov     ah,     09h
        lea     dx,     [msgBase10]
        int     21h
        @@GetCmd:
                mov     ah,     00h
                int     16h
        @@IsDigit:
                cmp     al,     '0'
                jb      @@IsOperation
                
                ;;;;;;;;;;;;
                ;to enter letters from A to F for hexadecimal notation
                cmp     al,     '9'+1
                jb      @@1
                cmp     al,     'A'
                jb      @@IsOperation
                cmp     al,     'F'
                ja      @@IsOperation
                mov     dl,     al
                mov     ah,     0
                sub     al,     37h
                jmp @@2
                ;;;;;;;;;;;;
                
            @@1:mov     dl,     al
                mov     ah,     0
                sub     al,     '0'
            @@2:cmp     ax,     [Base]
                jae     @@GetCmd
                push    ax
                mov     ah,     02h
                int     21h
                pop     ax
                mov     [EnBaseChange], 0 
                mov     bx,     0
                mov     bl,     al
                mov     ax,     [Operand2]
                mul     [Base]
                add     ax,     bx
                mov     [Operand2],   ax
                jmp     @@GetCmd
        @@IsOperation:
                cmp     al,     'a'
                jb      @@case
                cmp     al,     'z'
                ja      @@case
                add     al,     'A'-'a'
        @@case:
                cmp     al,     0Dh
                jne     @@IsBase2
                mov     al,     '='
 
        @@IsBase2:
                cmp     al,     'B'
                jne     @@IsBase16
                cmp     [EnBaseChange], 1
                jne     @@GetCmd
 
                mov     [Base], 2
                mov     ah,     09h
                lea     dx,     [msgChangeBase2]
                int     21h
                jmp     @@GetCmd
                
                
        ;;;;;;;;;;;;;;;;;;;
        @@IsBase16:
                cmp     al,     'H'
                jne     @@IsBase10
                cmp     [EnBaseChange], 1
                jne     @@GetCmd
 
                mov     [Base], 16
                mov     ah,     09h
                lea     dx,     [msgChangeBase16]
                int     21h
                jmp     @@GetCmd
        ;;;;;;;;;;;;;;;;;;;
        @@IsBase10:
                cmp     al,     'D'
                jne     @@IsOp
                cmp     [EnBaseChange], 1
                jne     @@GetCmd
 
                mov     [Base], 10
                mov     ah,     09h
                lea     dx,     [msgChangeBase10]
                int     21h
                jmp     @@GetCmd
        @@IsOp:
                cmp     al,     OpAdd
                je      @@DoOperation
                cmp     al,     OpSub
                je      @@DoOperation
                cmp     al,     OpMul
                je      @@DoOperation
                cmp     al,     OpDiv
                je      @@DoOperation
                cmp     al,     '='
                je      @@DoOperation
                jmp     @@GetCmd
        @@DoOperation:
                mov     [OperationNext],        al
                mov     ax,     [Operand1]
                cwd
                mov     bx,     [Operand2]
                cmp     [Operation],    OpAdd
                jne     @@Sub
                add     ax,     bx
                jmp     @@Calc
        @@Sub:
                cmp     [Operation],    OpSub
                jne     @@Mul
                sub     ax,     bx
                jmp     @@Calc
        @@Mul:
                cmp     [Operation],    OpMul
                jne     @@Div
                imul    bx
        @@Div:
                cmp     [Operation],    OpDiv
                jne     @@Calc
                idiv    bx
        @@Calc:
                mov     [EnBaseChange], 1
                mov     [Operand1],     ax
                mov     [Operand2],     0
                mov     al,     [OperationNext]
                mov     [Operation],    al
                int     29h
 
                mov     ah,     09h
                lea     dx,     [msgBase2]
                cmp     [Base], 2
                je      @@ShowBase
                lea     dx,     [msgBase10]
        @@ShowBase:
                int     21h
                mov     al,     [Operation]
                cmp     al,     '='
                je      @@Break
        jmp     @@GetCmd
@@Break:
        mov     ax,     [Operand1]
        mov     bx,     [Base]
        call    ShowUInt16
        mov     ah,     09h
        lea     dx,     [msgPressAnyKey]
        int     21h
 
        mov     ah,     00h
        int     16h
 
        int     20h
main    endp
 
; Display on the screen a whole 16-bit unsigned number
; at the entrance:
; ax is an integer 16 bit unsigned number
ShowUInt16       proc
        push    ax
        push    bx
        push    cx
        push    dx
        ;mov     bx,     10              ; divider (base of the number system)
        mov     cx,     0               ; number of displayed digits
        @@div:
                xor     dx,     dx      ; divide (dx: ax) by bx
                div     bx
                add     dl,     '0'     ; convert the remainder of the division into a digit symbol
                push    dx              ; and save it on the stack
                inc     cx              ; increase the number counter
                test    ax,     ax      ; are there still numbers in the number?
        jnz     @@div                   ; yes - repeat the cycle of highlighting the number
        @@show:
                mov     ah,     02h     ; function ah = 02h int 21h - display a character from dl on the screen
                pop     dx              ; extract the next digit from the stack
                int     21h             ; and display it on the screen
        loop    @@show                  ; and so do as many times as the numbers found in the number (cx)
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
ShowUInt16       endp
 
end     main

标签: assemblyx86-16masmtasm

解决方案


您的评论准确地描述了问题:

例如:7D00 * 2 = FA00,但程序会给出?:00即不是字母F,而是符号? , 而不是字母 A 将有一个符号:

为什么是这样?

ShowUInt16 proc 最初是为处理 base-10 数字而编写的。一旦你开始将它用于以 16 为基数的数字,你必须准备好在更广泛的范围 [0,15] 中获得余数。

这是需要的更改:

  • 对于余数 [0,9],加 48 以得到字符“0”-“9”
  • 对于余数 [10,15],加 48再加上 7以得到字符“A”-“F”
; Display on the screen a whole 16-bit unsigned number
; at the entrance:
; ax is an integer 16 bit unsigned number
ShowUInt16       proc
        push    ax
        push    bx
        push    cx
        push    dx
        mov     bx, [Base]         ; divider (base of the number system) {2,10,16}
        xor     cx, cx             ; number of displayed digits
    @@div:
        xor     dx, dx
        div     bx
        add     dl, '0'            <<<<<<<<<
        cmp     dl, '9'            <<<<<<<<<
        jbe     @@ok               <<<<<<<<<
        add     dl, 7              <<<<<<<<<
    @@ok:                          <<<<<<<<<
        push    dx
        inc     cx
        test    ax, ax
        jnz     @@div
    @@show:
        pop     dx
        mov     ah, 02h            ; DOS.PrintChar
        int     21h
        loop    @@show
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
ShowUInt16       endp

您的代码中有一个遗漏,在显示结果之前不显示(HEX)消息。您可能希望使用以下代码改进程序:

    mov     al, [Base]
    lea     dx, [msgBase2]
    cmp     al, 2
    je      @@ShowBase
    lea     dx, [msgBase10]
    cmp     al, 10                 <<<<<<<<<
    je      @@ShowBase             <<<<<<<<<
    lea     dx, [msgBase16]        <<<<<<<<<
@@ShowBase:
    mov     ah, 09h
    int     21h

    mov     al, [Operation]
    cmp     al, '='
    je      @@Break
    jmp     @@GetCmd

mov     [Operand1],     0
mov     [Operand1],     0

最后,这可能只是一个错字,您的代码将Operand1 归零两次,从而忘记了用于构建输入数字的重要Operand2 。这可以解释奇怪的数值异常......


为什么不添加一些完美呢?

随着您选择只接受大写字符“A”-“F”的十六进制表示法的引入,用户现在必须恢复使用小字符“b”、“d”和“h”来选择号码系统。您可能希望在您的msgAbout消息中反映此限制。


推荐阅读