Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Assembly 在汇编中为什么可以';十六进制数字不能不移位地打印吗?_Assembly_Emu8086 - Fatal编程技术网

Assembly 在汇编中为什么可以';十六进制数字不能不移位地打印吗?

Assembly 在汇编中为什么可以';十六进制数字不能不移位地打印吗?,assembly,emu8086,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 D

此代码取自一个在线示例。 假设我有一个要在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
(其中每个
X
Y
都是二进制0或1)

首先,16位寄存器BX向左移位4位
BX
BL
(最低有效字节)和
BH
(最高有效字节)组成
BH
已设置为0,并且
BL
包含输入,因此在移位之前
BX
将包含位
00000000 xxxxyyyy
。移位后,它将包含
000xxxxyyyy0000

然后将
BX
(即
BH
,现在包含
0000XXXX
)的最高有效字节移动到
DL
,转换为字符并打印


对于现在包含
YYYY0000
的第二部分
BL
,将其向右移位4位,从而产生
0000yyyyy
。然后将该值转换为字符并打印

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

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

在真正的8086上,仅使用和保持低位4位会更容易,速度也会更快(在8086上,移位的每个计数都需要一个时钟周期,不像现代CPU中的桶形移位器ALU可以在一个时钟周期内进行任意移位)

这是一个更简单、更容易理解的实现,它也更紧凑,因此在真正的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
/
和dl,dh
每条指令只有2个字节,而不是
和dh,0Fh
的3个字节,并且它使
mov
脱离了无序执行的关键路径

一位
的实现也有不必要的复杂分支。(通常您会使用查找表实现半字节->ASCII,但它只需要一个分支,而不是两个分支。运行额外的
add
比额外的
jmp
便宜)

我们本可以将dx,'00'(并更改
cmp
)添加到一次操作两个半字节(在DH和DL中分别隔离后)

我们还可以提升
MOV-AH,02H
,因为


如果我们关心性能,我们会创建一个两个字符的字符串,并使用一个
int21h
系统调用同时打印两个数字。

您也不能直接打印小数。您需要转换为文本。恰好每4位是一个十六进制数字,所以可以使用移位而不是通常使用的除法。快速一看,代码是错误的,因为它将同一个数字打印两次。但是我还没有喝咖啡;)啊,好的,它可以很好地打印两位数字,
shr
是撤销
shl
,这样低4位就回到了开始的位置。谢谢你,杰斯特,我想我明白了。哇!非常感谢你花时间解释,太棒了!
; 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