[英]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?
另外,为什么在这里同时使用SHR和SHL ?
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
(其中每个X
和Y
都是二进制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). BX
由BL
(最低有效字节)和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.