8086 如何反转寄存器中的一个字节

Posted

技术标签:

【中文标题】8086 如何反转寄存器中的一个字节【英文标题】:8086 how to reverse a byte in a register 【发布时间】:2018-12-21 13:37:01 【问题描述】:

如何在字节级别反转寄存器值?

mov al,12  -----> how can i reverse al value
to 21

这是我尝试过的:

mov bx,4321      ;i want to make bx 1234

mov cl,04          ;cl for rotation value

xchg bh,bl          ; now bx will 2143  

mov al,bh         ;moving 21 to al

rol al,cl             ; rotate al 4 times  to left now al is 12`

mov bh,al      ; bh is 12 setuped ,time for bl

;---------------------------
mov al,bl   ;moving lower byte  43 to al

rol al,cl     ; rotate 4 times to left now al will 34

mov bl,al      ; moving 34 to bl  

现在bx 必须包含一个颠倒的数字 1234;问题是数字是十六进制的,即 10e1h 或 4321。

当我反转它是 1e01h 但这个值不是表示 1234。

1234 是 04d2。我得到的值是 7681d。

【问题讨论】:

反转半字节和反转十进制数字不是一回事,你想做的任务是什么? 如果你想反转十进制,你将不得不使用十进制算术。 12 = 0b0000110021 = 0b00010101。位反转它们的二进制表示不会反转十进制数字,因为 10 不是 2 的幂。rol al,4 将交换十六进制数字,因为 16 = 2^4 并且一个字节包含两个半字节(十六进制数字)。 他应该使用mov cl,4; rol al,cl 而不是rol al,4,因为直接操作数大于1的移位和旋转操作实际上是在80286中引入的。 @vitsoft :EMU8086 OP 已标记为它确实支持 rol al,4 尽管它所做的是将其转换为 4 个连续的 rol al, 1 。在 80186 而不是 80286 上引入了立即位计数的移位操作。 【参考方案1】:

虽然其他答案为您的问题提供了直接的解决方案,但我想写一些有关该理论的内容,因为我认为它下次会对您有所帮助:

正如你已经写的,十进制数 1234 在十六进制中写为 4D2,而 4321 写为 10E1。

这意味着“还原一个数字”的操作在不同的数字系统中会导致不同的结果:

在十进制系统中“还原”1234 会导致 4321。在十六进制系统中,“还原”4D2 会导致 2D4。使用固定长度的 4 个十六进制数字(16 位寄存器!)但是“还原” 04D2 将导致 2D40...

如果某些操作只适用于某个base(*),你必须考虑以下几点:

使用处理字节的计算机,您可以轻松地在 base-256 中执行操作:xchg bh,bl 将“还原”base-256 系统中数字的两位数。

使用移位和旋转可以执行以 2^N 为底的运算(例如二进制、八进制或十六进制)。

但是其他基数(例如十进制)的运算需要计算个位数,执行运算并根据数字计算(二进制)数。

对于恢复十进制数,以下伪代码可能有效:

  A = input (here: 1234)
  B = 0
mainLoop:
  digit =  A mod 10  (get the right digit of A)
  A = A/10           (remove the right digit from A)
  B = 10*B + digit   (append the digit to B)
  if A>0: jump to mainLoop

在汇编程序中,代码可能如下所示:

    mov ax,1234   ; ax = "A" in the pseudo-code
    mov cx,0      ; cx = "B" in the pseudo-code
    mov bx,10     ; The base we are working in
mainLoop:
    xchg ax,cx    ; "mul" can only work with ax
    mul bx        ; Results: ax = "10*B"
                  ;          dx = overflow (typically 0)
    xchg ax,cx    ; Change ax and cx back: cx="10*B"
    mov dx,0      ; Prepare dx for "div"
    div bx        ; Perform division and modulo
                  ; Result:
                  ;   ax = "A/10"
                  ;   dx = "A MOD 10" = "digit"
    add cx,dx     ; "B = 10*B+digit"
    cmp ax,0
    ja mainLoop
                  ; Here cx will contain the "reverted" number

(*) 您要执行的操作不是“还原数字”而是“还原十进制数字”。

【讨论】:

【参考方案2】:

要反转字节中的位,请使用查找表。要反转一个字中的位,请使用查找表反转最低 8 位,然后使用rol ax,8,然后使用查找表反转其他 8 位。

要在一个字节中反转(4 位)半字节,请使用rol al,4。要反转单词中的半字节,请使用rol al,4; rol ax,8; rol al,4

用于反转字节或字中的十进制数字;不。相反,更改打印十进制数字的代码。原因是从整数(例如值 1234)到字符串(例如字符“1234”)的转换通常会以相反的顺序生成字符,并且必须做额外的工作来反转字符;所以“print_reversed_decimal()”会做更少的工作(并且以其他方式反转数字,然后在打印时再次反转它是白白工作的两倍!)。或者,您可以改用 BCD(每个半字节包含一个十进制数字)。在这种情况下,您可以使用“反向半字节”方法来反转十进制数字,然后打印数字变得更便宜(移位和掩码而不是除法和模数)。

请注意,在整数中反转十进制数字的数学类似于:

    k = 10000000;
    result = 0;
    while(value > 0) 
        digit = input % 10;
        result += digit * k;
        input /= 10;
        k /= 10;
    

但是,您必须首先确定 k 的正确值(这取决于是否忽略前导零或反转 - 例如,如果 012 变为 210 或 021)。另请注意,这很昂贵(循环内的除法、模数和乘法),这就是为什么您要尽一切可能避免这样做的原因。当然,如果数字范围足够小(例如,仅从 000 到 199 的值),那么您可以使用查找表快速完成此操作。

【讨论】:

您可以使用result = result * 10 + digit 累积result,就像其他答案一样。这是字符串->整数的正常算法,从 MSD 开始。因此,取value 的低位数字并将其视为result 的下一位数字,并且您无需知道会有多少个小数位就已经设置好了。也避免了昂贵的k/=10。 (顺便说一句,除以编译时常量比硬件div 指令便宜得多。Why does GCC use multiplication by a strange number in implementing integer division?) 但是是的,+1 仅用于从正常的 int->string 算法以 LSD-first 顺序打印/存储数字。 @PeterCordes:对于“忽略前导零”的情况,使用result = result * 10 + digit 会很好,但是对于“前导零反转”,它会让人头疼(至少,循环的终止条件完全不同)。为了好玩,理论上有无限数量的前导零(例如,1 只是...0001 的简写)所以也许pow(10, INFINITY)k 的正确初始值。 ;-) 如果你想假装你的寄存器保存一个固定宽度的十进制数,你可以设置一个固定的迭代计数。没有必要真正使用 k。我描述的算法将乘以 10 并为“额外”迭代添加零,将尾随零添加到结果中。是的,前导零是一个问题,但我看不出你的算法如何使它变得更好。 @PeterCordes:我的算法允许在一段示例代码后跟“你必须确定 k 的正确值”。使用 result = result * 10 + digit 没有这个属性 - 你必须有 2 段示例代码才能传达相同的基本思想(一个用于“忽略前导零”,另一个用于“前导零反转”),并且(给定没有人,可能包括 OP,可能一开始就想这样做)我不确定我是否能看到“两倍的例子”的理由。

以上是关于8086 如何反转寄存器中的一个字节的主要内容,如果未能解决你的问题,请参考以下文章

8086汇编如何打印寄存器内数值

第3章 寄存器(内存访问)小结

汇编语言第三章——寄存器(内存访问)

第三章知识总结

汇编语言第二章总结

汇编语言第二章总结