2

此代码取自在线示例。假设我有要在 DL 中打印的变量。

DISPLAY_HEX PROC NEAR
    MOV BL,DL   

    MOV BH,0    
    MOV CL,4    

    SHL BX,CL   
    MOV DL,BH   

    CALL ONE_DIGIT  

    MOV CL,4    
    SHR BL,CL   
    MOV DL,BL   

    CALL ONE_DIGIT  

    RET     
DISPLAY_HEX ENDP


ONE_DIGIT PROC NEAR

    CMP DL,9    
    JA LETTER   

    ADD DL,48
    JMP NEXT    

LETTER: ADD DL, 'A'-10  

NEXT:   MOV AH,02H  
    INT 21H 

END:    RET     
ONE_DIGIT ENDP

为什么要换班?不能像小数一样打印吗?另外,为什么这里同时使用SHRSHL

4

2 回答 2

5

在 base16(十六进制)中,您有 16 个可能的数字(0..F),因此恰好需要 4 位来表示一个十六进制数字(log2(16)== 4)。在这里,我说的是值(0..F0..15base10)意义上的数字,而不是 ASCII 字符。

所以一个字节可以容纳两个十六进制数字。假设它DL包含以下位:(XXXXYYYY其中每个XY是二进制 0 或 1)。

首先将 16 位寄存器BX左移 4 位。BXBL(最低有效字节)和BH(最高有效字节)组成。BH已设置为 0,并BL包含输入,因此在移位之前BX将包含位00000000XXXXYYYY。并且在班次之后它将包含0000XXXXYYYY0000. 然后(即,现在包含)
的最高有效字节被移动到,转换为字符,并打印。BXBH0000XXXXDL

BL对于现在包含的第二部分,YYYY0000右移 4 位,得到0000YYYY. 然后将该值转换为字符并打印。

于 2016-01-09T13:58:26.777 回答
1

您的代码非常复杂,难怪它令人困惑。它是通过左移 BX 得到 DL 的高半字节,所以这两个半字节分为 BH 和 BL,但 BL 中的半字节在前 4 个字节中。

您需要一次移位才能将高 4 位降低到寄存器的底部。

使用 AND 仅保留低 4 位会更容易,并且在真正的 8086 上更快(其中每个移位计数需要一个时钟周期,不像现代 CPU 具有桶形移位器 ALU 可以在 1 个时钟周期内进行任意移位) .

这是一个更简单、更容易理解的实现,它也更紧凑,因此在真正的 8086 上更快更好。

; Input in DL
; clobbers AX, CL, DX
DISPLAY_HEX PROC NEAR
    mov  dh, dl          ; save a copy for later

    mov  cl, 4
    shr  dl, cl          ; extract the high nibble into an 8-bit integer

    CALL ONE_DIGIT  

    and  dh, 0Fh         ; clear the high nibble, keeping only the low nibble
    mov  dl, dh          ; and pass it to our function
    CALL ONE_DIGIT  

    RET     

DISPLAY_HEX ENDP

为了节省代码大小, mov dl, 0Fh/and dl, dh每条指令将只有 2 个字节,而不是 3 个字节and dh, 0Fh,并且它mov消除了乱序执行的关键路径。

的实现ONE_DIGIT也有不必要的复杂分支。(通常你会用一个查找表来实现 nibble -> ASCII,但它只需要一个分支,而不是两个。运行一个 extraadd比一个 extra 便宜jmp。)

; input: DL = 0..15 integer
; output: AL = ASCII hex char written
; clobbers = AH    
ONE_DIGIT PROC NEAR
    CMP   DL,9    
    JBE   DIGIT   

    ADD   DL, 'A'-10  - '0'
DIGIT:
    ADD   DL, '0'

    MOV AH,02H  
    INT 21H      ; write char to stdout, return in AL.  Checks for ^c/break
    RET
ONE_DIGIT ENDP

我们本可以ADD dx, '00'(并更改cmp)同时对两个半字节进行操作(在 DH 和 DL 中分别隔离它们之后)。

我们也可以提升MOV AH,02H,因为int 21h/ah=2不会修改 AH。.

如果我们关心性能,我们会创建一个 2 个字符的字符串并使用一个int 21h系统调用来一次打印两个数字。

于 2018-06-05T03:24:21.373 回答