在汇编中为什么不能在不移动的情况下打印十六进制数?

时间:2016-01-09 13:26:25

标签: assembly emu8086

此代码取自在线示例。 让我们说我想要在我的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

为什么要轮班?它不能像小数一样打印吗? 另外,为什么在这里使用 SHR SHL

2 个答案:

答案 0 :(得分:4)

在base16(十六进制)中,你有16个可能的数字(0..F),所以它只需要4位代表一个十六进制数字(log2(16)== 4)。这里我谈的是数值意义上的数字(base10中的0..F0..15),而不是ASCII字符。

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

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

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

答案 1 :(得分: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个字节,而不是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 doesn't modify AH.

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