繁体   English   中英

在组装时为什么不能不移动就打印十六进制数?

[英]In assembly why can't a hexadecimal number be printed without shifting?

此代码取自在线示例。 假设我有要在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

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

因此,一个字节可以容纳两个十六进制数字。 假设DL拥有以下位: XXXXYYYY (其中每个XY都是二进制0或1)。

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

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

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

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

在实数8086上,使用AND来仅保留低4位会更容易,并且速度快得多(在这种情况下,每个移位的计数需要一个时钟周期,这与带有桶形移位器ALU的现代CPU可以在1个时钟周期内进行任意移位的现代CPU不同) 。

这是一个更简单易懂的实现,它也更紧凑,因此在实际的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个字节,而不是for and dh, 0Fh的3个字节,它使mov脱离了乱序执行的关键路径。

ONE_DIGIT的实现还具有不必要的复杂分支。 (通常,您可以使用查找表实现半字节-> ASCII,但它只需要一个分支,而不是两个分支。运行额外的add要比额外的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系统调用一次打印两个数字。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM