我正在上微处理器课,我们正在使用 Freescale CodeWarrior 中的汇编语言对 68HCS12 微控制器进行编程。本周我们的作业是反转一个字节,因此如果该字节是 00000001,则输出将为 10000000,或 00101011 到 11010100。我们必须使用汇编语言,并被告知我们可以使用旋转和移位(但不限于! )来完成这个任务。我真的不知道该从哪里开始。
提示:如果进行移位,一位会被移出,而零(可能)会被移入。移出的位会去哪里?您需要将其移入目标寄存器或内存地址的另一端。
我确信 25 年前我可以在 Z80 机器代码中做到这一点,无需汇编器:)
如果您可以节省 256 字节的额外代码大小,查找表可能是在 68HCS12 上反转字节的最有效方法。但我很确定这不是你的老师所期望的。
对于“正常”解决方案,单独考虑数据位。旋转和移位允许您移动位。对于第一个解决方案,隔离八个位(使用按位“与”运算),将它们移动到目标位置(移位、旋转...),然后再次将它们组合在一起(使用按位“或”运算)。这不是最有效或最简单的实现,但您应该首先集中精力获得正确的结果——优化可以等待。
将两个寄存器视为位堆栈。如果一次一点地从一个移动到另一个会发生什么?
当您进行右移时,最低有效位将进入进位标志。
进行循环时,进位标志用于填充结果的空出位(ROL 为 LSB,ROR 为 MSB)。
首先制定出算法来完成你需要做的事情。将其表达为伪代码、C 语言、简单英语、图表或任何您喜欢的形式。一旦清除了这个概念障碍,实际的实施应该非常简单。
您的 CPU 可能具有可让您移位和/或旋转寄存器的指令,可能包括进位标志作为附加位。这些说明将非常有用。
例如,如果您有所有字节数,最简单的方法是
mov al, 10101110
mov ecx, 8
我们将 8 放入 ecx for 循环中
mov ebx, 0
在 bl 中我们将得到结果,我们将制作 ebx,只是为了看看会发生什么更好
loop1:
sal al, 1;
在进位标志中,现在您拥有左起最后一位
rcr bl, 1;
现在你在 BL 中添加你携带的东西
loop loop1
仅此而已
这是一条评论,但我想WTH!
为了节省 256 字节表的空间,您可以使用一个 16 字节表,一次包含四位(半字节)的值。那么算法就是
revval=(revdigit[inval&0x0f]<<4)|
revdigit[inval>>4];
如果我是一名教授,我肯定会喜欢这两个部分,其中一个轮班在索引中,另一个轮班在索引之外。
我还必须为大学编写这个位反向(8 位)。这是我的做法:
MOV AL, 10001011B ;set the value to test
MOV CL, 7
MOV DH, 1
MOV DL, 0
loop1: PUSH AX
AND AL, DH
PUSH CX
MOV CL, DL
SHR AL, CL
POP CX
MOV BH, AL
SHL BH,CL
OR CH,BH
DEC CL
INC DL
SHL DH, 1
POP AX
CMP DL, 8
JE END
JMP LOOP1
END:
我没有评论它,所以它是如何工作的: DH 是一个
1
,像第一次一样以字节形式传输:00000001
;第二次00000010
等等。当你用 AL 制作 AND
时,你会得到 0
或类似 100
或 10000
的东西,你必须将其向右移动才能得到 0
或 1
。
然后,将其放入 BH 并移动到所需的位置,即 7
表示字节 0
,6
表示字节 1
,依此类推。然后OR
我们的最终结果以及INC
和DEC
什么是必要的。不要忘记条件跳转并弹出 AX
进行下一个循环:)
结果将在 CH 中。
以下代码利用了旋转和移位。我使用Intel x86语法,请参阅右侧说明:
mov cx, 8 ; we will reverse the 8 bits contained in one byte
loop: ; while loop
ror di ; rotate `di` (containing value of the first argument of callee function) to the Right in a non-destructive manner
adc ax, ax ; shift `ax` left and add the carry, the carry is equal to 1 if one bit was rotated from 0b1 to MSB from previous operation
dec cx ; Decrement cx
jnz short loop ; Jump if cx register Not equal to Zero else end loop and return ax
我使用 dec 指令而不是 sub,因为它只占用 1 个字节,而 sub 占用 3 个字节。最重要的是,编译器似乎总是通过选择 dec 而不是 sub 来优化。
编辑:另请注意,
rcl ax
(3 个字节),而相当于 adc ax, 0
(2 个字节)后跟 shl ax
(2 个字节)效率较低。
请参阅下面的评论,非常感谢 Peter Cordes 的见解。
一个可能的通用解决方案(不在任何特定的汇编器中,可能很快但很大 - 我注意到这可以放入带有移位的循环中,但会更慢) 用 0 加载字节 reg a 用测试值加载字节reg b
位测试 128 与 a 如果为 0 则跳过下一个 一个<- a+1
位测试 64 与 a 如果为 0 则跳过下一个 一个<- a+2
位测试 32 与 a 如果为 0 则跳过下一个 一个<- a+2
位测试 16 与 a 如果为 0 则跳过下一个 一个<- a+4
最后一个存在
等位测试 1 与 a 如果为 0 则跳过下一个 一个<- a+128 result in a