1

** 为澄清和“更清洁”的代码而编辑。

我正在尝试从键盘接受一个字符(任何字符)并将其 ASCII 值转换为十六进制,然后显示它。

我知道如何从基数 10 转换为十六进制,但只是为了确保我没有使用不正确的术语:

如果我输入“c”作为我的 ASCII 值,它的十进制值为 63。63 除以 16(十六进制为 16 进制)= 3.9375。保存 3 的商以备后用。余数 * 基数 (.9375 * 16) = 15。15 是十六进制字符“F”。

商除以基数 (3 / 16) = 0.1875。商为零,所以这是转换的最后一步。余数 * 基数 (.1875 * 16) = 3

从后到前读取它们(“先进后出”,如果考虑堆栈),我得到“3F”作为十进制数“63”的十六进制值(这是“c”的ASCII)

这是正确的,是吗?

我希望它读起来很简单。我只是从键盘(AL)中获取字符,将 BX 设置为除数(以 16 为基数),然后将 AL 除以 BX,将余数存储在堆栈中,然后循环遍历堆栈并尝试显示它.. .

我认为我的问题是我的乘法和我的 INT 21H/02H 有问题。我不确定是否需要添加 30h 才能显示字符...

最重要的是,我什至不确定我是否需要在那一点(显示),因为我还没有弄清楚如何将 10-15 转换为十六进制的 AF。

我试着和我的老师说话,在等了 30 分钟让他完成和我班上另一群学生的谈话之后(关于他的另一个班的东西,而不是这个……这让我很恼火),我得到的最多的是“重新开始”。

当被问及“使用 SHR 和 SHL 怎么样?” 正如有人向我指出的那样,但有人告诉我,没有它也可以完成,而且我不能使用这些命令(课堂上没有介绍它们)。

关于我做错了什么的任何意见?谢谢!

    CHAR2HEX PROC                   ; Accept a character, print it's ascii value in hex.

        MOV DX, OFFSET AskChar      ; Display prompt
        MOV AH, 09H
        INT 21H

        MOV AX, 0                   ; Clear AX

        MOV AH, 07H                 ; Get keyboard input w/ no echo (AL)
        INT 21H

        MOV BX, 16                  ; Set up the divisor (base 16)
        MOV CX, 0                   ; Initialize the counter
        MOV DX, 0                   ; Clear DX

        Divide:                         
                                    ; Dividend (what's being divided) in DX/AX pair, Quotient in AX, Remainder in DX.
            DIV BX                  ; Divide (will be word sized).
            PUSH AX                 ; Save DX (the remainder) to stack.

            ADD CX, 1               ; Add one to counter

            MOV DX, 0               ; Clear Remainder (DX)
            CMP AX, 0               ; Compare Quotient (AX) to zero
            JNE Divide              ; If AX not 0, go to "Divide:"

        Multiply:
                                    ; Multiply remainder (from stack) by 16 to get hex value.               
            MOV DX, 0               ; Clear DX           
            POP AX                  ; Get remainder from stack into AX.

            MUL BX                  ; Multiply AX * BX.  (DX= high order bits, AX = low order bits)
            MOV DX, AX

            SUB DL, 30h             ; ADD 30h to DL 
            MOV AH, 02h             ; 02h to display AH (DL)
            INT 21H                 ; Send to DOS

            LOOP Multiply           ; If more to do, Multiply again
                                    ; LOOP subtracts 1 from CX. If non-zero, loop.
            RET
    CHAR2HEX ENDP
END START

已编辑**

我终于明白了!我能够让程序返回在键盘上按下的 ascii 字符的十六进制值,但它仅在每个余数为 0 到 9 时才有效。它不会显示 A 到 F,而是使用冒号,分号等...

我在网上查看了一个 Ascii/Deicmal/Hex 图表,注意到字符 0 到 9 是 30h 到 39h。但是字符 A(十六进制 10)直到 40 小时才开始。所以我改变了程序,如果该值大于 39h,它将 7h 添加到 DL 并显示它。

    CHAR2HEX PROC                   ; Accept a character, print it's ascii value in hex.

        MOV DX, OFFSET AskChar      ; Display prompt
        MOV AH, 09H
        INT 21H

        MOV AH, 07H                 ; Get keyboard input w/ no echo (AL)
        INT 21H

        MOV CL, AL                  ; Copy user input (AL) to CL
        MOV AX, 0                   ; Clear AX (get rid of HO bits)
        MOV AL, CL                  ; Copy user input back into AL

        MOV BX, 16                  ; Set up the divisor (base 16)
        MOV CX, 0                   ; Initialize the counter
        MOV DX, 0                   ; Clear DX

        Div2:                         
                                    ; Dividend (what's being divided) in DX/AX pair, Quotient in AX, Remainder in DX.
            DIV BX                  ; Divide (will be word sized).
            PUSH DX                 ; Save DX (the remainder) to stack.

            ADD CX, 1               ; Add one to counter
            MOV DX, 0               ; Clear Remainder (DX)
            CMP AX, 0               ; Compare Quotient (AX) to zero
            JNE Div2              ; If AX not 0, go to "Div2:"

        getHex2:
            MOV DX, 0               ; Clear DX.
            POP DX                  ; Put top of stack into DX.
            ADD DL, 30h             ; Conv to character.

            CMP DL, 39h
            JG MoreHex2

        HexRet2:        

            MOV AH, 02h             ; 02h to display AH (DL)
            INT 21H                 ; Send to DOS

            LOOP getHex2            ; If more to do, getHex2 again
                                    ; LOOP subtracts 1 from CX. If non-zero, loop.
            JMP Skip2
        MoreHex2:
            ADD DL, 7h
            JMP HexRet2             ; Return to where it left off before adding 7h.
        Skip2:
            RET
    CHAR2HEX ENDP
4

2 回答 2

1

首先用 C 或其他语言编写程序。不要使用语言库,例如不要使用 printf("%x\n",number) 来显示十六进制,而是按照您的建议将数字重复除以 10,将它们保存在某处。现在记住除以十的余数(模数)介于 0-9 之间,以便在 ascii 中的某处显示,您需要添加 0x30,如您所提到的。123变成12余数3,然后12变成1余数2,1变成0余数1,1+0x30 = 0x31显示那个2+0x30变成0x32显示那个,3+0x30变成0x33显示那个。

十六进制也不例外,基数是 16 而不是 10。正如 vhallac 所提到的,尽管您可以屏蔽和移位而不是使用除法运算符。

一旦你让“程序”在 C 或其他语言中工作,在低级别使用基本操作(加、减、除、移位和或等),然后用汇编语言重新编写该代码。

最终你可能会达到不需要用其他语言证明你的程序并从 asm 开始的地步。

于 2012-04-23T21:20:31.453 回答
1

您的以 10 为底的转换逻辑看起来不错:除以 10 的每个余数都会给您一个数字,并且它们将从最低有效位到最高有效位排序,因此您需要在打印之前反转获得的数字。将它们推入堆栈并在完成后弹出它们将以一种很好且紧凑的方式进行反转。

转换为十六进制,操作相同:除以 16,取余数,反转,打印。但是,有几点需要注意:除以 16 是右移 4 位。余数是value AND 0fH,因此您实际上不需要任何算术运算。位移和AND操作就足够了。此外,您甚至不需要涉及堆栈。您可以从最重要的半字节(四位)到最不重要的半字节执行此操作,并在计算时打印。您似乎正在努力解决的第二件事是将半字节转换为十六进制数字。仅仅添加30h是不够的。对于小数来说已经足够了,但对于十六进制值,你也可以得到 10 到 15 的数字。这些需要添加到41h(ASCII 中的“A”)减去 10。或者,您可以将数字 '0' 到 '9' 和 'A' 到 'F' 放在一个表中,并按要打印的半字节的值对它们进行索引。

您可能还想编写一个从输入中读取数字的例程。读取每个数字,直到您读取换行符,将字符转换为十进制值,并更新将保留您已读取的数字的累加器。您需要将它用于十六进制和十进制输出函数。

于 2012-04-23T21:00:04.340 回答