简体   繁体   English

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

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

This code is taken from an online example. 此代码取自在线示例。 Let's say I have the variable I want to print in my DL. 假设我有要在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

Why the shifts? 为什么要换班? Can't it be printed like a decimal? 不能像小数一样打印吗? Also, why is both SHR and SHL being used here? 另外,为什么在这里同时使用SHRSHL

In base16 (hex) you've got 16 possible digits ( 0..F ), so it takes exactly 4 bits to represent one hexadecimal digit (log2(16) == 4). 在base16(十六进制)中,您有16个可能的数字( 0..F ),因此恰好需要4位来表示一个十六进制数字(log2(16)== 4)。 Here I'm talking about digits in the sense of the values ( 0..F , or 0..15 in base10), not the ASCII characters. 在这里,我说的是数字的含义( 0..F或base10中的0..15 ),而不是ASCII字符。

So one byte can hold two hexadecimal digits. 因此,一个字节可以容纳两个十六进制数字。 Let's say that DL holds the following bits: XXXXYYYY (where each X and Y is either a binary 0 or 1). 假设DL拥有以下位: XXXXYYYY (其中每个XY都是二进制0或1)。

First the 16-bit register BX is shifted 4 bits to the left. 首先,将16位寄存器BX向左移4位。 BX consists of BL (least significant byte) and BH (most significant byte). BXBL (最低有效字节)和BH (最高有效字节)组成。 BH has been set to 0, and BL contains the input, so prior to the shift BX will contain the bits 00000000XXXXYYYY . BH已设置为0,并且BL包含输入,因此在移位之前BX包含位00000000XXXXYYYY And after the shift it will contain 0000XXXXYYYY0000 . 移位后将包含0000XXXXYYYY0000
Then the most significant byte of BX (ie BH , which now contains 0000XXXX ) is moved to DL , converted to a character, and printed. 然后, BX的最高有效字节(即BH ,现在包含0000XXXX )被移至DL ,转换为字符并打印。

For the second part BL , which now contains YYYY0000 is shifted 4 bits to the right, resulting in 0000YYYY . 对于第二部分,现在包含YYYY0000 BL YYYY0000右移4位,得到0000YYYY And then that value is converted to a character and printed. 然后将该值转换为字符并打印。

Your code is massively overcomplicated, no wonder it was confusing. 您的代码非常复杂,难怪它会令人困惑。 It's getting the high nibble of DL by left-shifting BX, so the two nibbles are split into BH and BL, but the nibble in BL is in the top 4 bytes. 它通过左移BX获得DL的高半字节,因此将两个半字节分为BH和BL,但BL中的半字节在前4个字节中。

You need one shift to get the high 4 bits down to the bottom of a register. 您需要进行一次移位才能将高4位降低到寄存器的底部。

Using AND to keep only the low 4 bits would be easier, and much faster on a real 8086 (where each count of a shift took a clock cycle, unlike modern CPUs with barrel-shifter ALUs that can do arbitrary shifts in 1 clock cycle). 在实数8086上,使用AND来仅保留低4位会更容易,并且速度快得多(在这种情况下,每个移位的计数需要一个时钟周期,这与带有桶形移位器ALU的现代CPU可以在1个时钟周期内进行任意移位的现代CPU不同) 。

This is a simpler and easier-to-understand implementation, which is also more compact and thus faster and better on a real 8086. 这是一个更简单易懂的实现,它也更紧凑,因此在实际的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

To save code size, mov dl, 0Fh / and dl, dh would be only 2 bytes per instruction, instead of 3 for and dh, 0Fh , and it takes the mov off the critical path for out-of-order execution. 为了节省代码大小, mov dl, 0Fh / and dl, dh每条指令仅2个字节,而不是for and dh, 0Fh的3个字节,它使mov脱离了乱序执行的关键路径。

The implementation of ONE_DIGIT also has needlessly-complex branching. ONE_DIGIT的实现还具有不必要的复杂分支。 (Often you'd implement nibble -> ASCII with a lookup table, but it only needs one branch, not two. Running an extra add is cheaper than an extra jmp .) (通常,您可以使用查找表实现半字节-> 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

We could have done ADD dx, '00' (and changed the cmp ) to operate on both nibbles at once (after isolating them each in DH and DL). 我们可以将ADD dx, '00' (并更改cmp )同时对两个半字节进行操作(在DH和DL中将它们分别隔离之后)。

We could also hoist the MOV AH,02H , because int 21h / ah=2 doesn't modify AH. 我们也可以提升MOV AH,02H ,因为int 21h / ah=2不会修改AH。 .

If we cared about performance, we'd create a 2-character string and use one int 21h system call to print both digits at once, though. 如果我们关心性能,我们将创建一个2个字符的字符串,并使用一个int 21h系统调用一次打印两个数字。

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

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