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 = 0b00001100
。 21 = 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 如何反转寄存器中的一个字节的主要内容,如果未能解决你的问题,请参考以下文章