【问题标题】:Bit-reverse a byte on 68HC12在 68HC12 上位反转一个字节
【发布时间】:2011-06-22 21:11:54
【问题描述】:

我正在学习微处理器课程,我们正在使用 Freescale CodeWarrior 中的汇编语言对 68HCS12 微控制器进行编程。我们本周的任务是反转一个字节,因此如果字节为 00000001,则输出将为 10000000,或 00101011 到 11010100。我们必须使用汇编语言,并被告知我们可以使用旋转和移位(但不限于! ) 来完成这项任务。我真的不知道该从哪里开始。

【问题讨论】:

    标签: assembly bit-manipulation reverse codewarrior 68hc12


    【解决方案1】:

    以下代码使用了旋转和移位。我用的是 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,因为它只占用一个字节,而 sub 占用 3 个字节。除此之外,编译器似乎总是通过选择 dec 而不是 sub 进行优化。

    编辑:还要注意rcl ax(3 个字节),而等效于adc ax, 0(2 个字节)后跟shl ax(2 个字节)的效率较低。 请参阅下面的 cmets,非常感谢 Peter Cordes 的见解。

    【讨论】:

    • 你不需要慢rcl;你可以使用adc ax, ax。两者都是 2 字节(在 16 位模式下),adc 在现代 CPU 上更快。您在 shl eax,1 和 adc al,0 方面的细分是错误的:它将是 shl ax,1adc ax,0,两者的操作数大小都与 rcl 相同,并且在 之前 添加进位。另外,dec cl 是 2 个字节;也许你在想dec cx?一字节的 inc/dec 操作码仅适用于 16 或 32 位操作数大小,而不是 8 位。 (如果你真的在优化速度而不是大小,你会使用慢速 loop 指令。但不要这样做)
    • 您也可以使用ror di, 1 而不是shr 使其成为非破坏性的。哦还有,你只设置了CX=8,但是DI和AX都是16位寄存器。
    • 顺便说一句,我对你关于慢循环的评论感到困惑,你能详细说明一下吗?我还检查了 ror 与 shr stackoverflow.com/questions/4976636/…,但是让它成为非破坏性的有什么意义呢?
    • Why is the loop instruction slow? Couldn't Intel have implemented it efficiently?。并且回复:非破坏性:如果您在循环中使用了 16 次 ror di,1,则 DI 的最终值将与初始值相同。但是您的 shr 循环会留下 DI=0(如果您进行了 16 次迭代)。因此,您可以选择哪个更有用:归零寄存器或原始值。
    • 谢谢你很有趣,我编辑了答案!同样在这种非常特殊的情况下,您是否认为有任何其他点可以超越 shr,因为我们并不真正关心保持 di 值?
    【解决方案2】:

    例如,如果你有一个字节数,最简单的方法是

    mov al, 10101110
    mov ecx, 8
    

    我们将 8 放入 ecx for 循环中

    mov ebx, 0 
    

    在bl中我们将得到结果,我们将制作ebx,只是为了看看会发生什么更好

    loop1:
    sal al, 1;           
    

    在进位标志中,现在你有左数的最后一位

    rcr bl, 1;           
    

    现在你把你在carry里的东西加进去

    loop loop1
    

    仅此而已

    【讨论】:

      【解决方案3】:

      我还必须为大学编程这个位反转(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 或类似 10010000 的内容,您必须将其移到右侧才能获得 01。 然后,将其放入 BH 并移动到所需的位置,即7for byte 06for byte 1等等。然后OR 到我们的最终结果和INCDEC 什么是必要的。不要忘记条件跳转并弹出AX 进行下一个循环:)

      结果将在 CH 中。

      【讨论】:

        【解决方案4】:

        这是一条评论,但我认为 WTH!

        为了节省 256 字节表的空间,您可以创建一个 16 字节表,一次包含四个位(半字节)的值。那么算法将是

        revval=(revdigit[inval&0x0f]<<4)|
                revdigit[inval>>4];
        

        如果我是教授,我当然会喜欢这两个部分,一个在索引中,另一个在索引之外。

        【讨论】:

          【解决方案5】:

          如果您可以节省 256 字节的额外代码大小,查找表可能是在 68HCS12 上反转字节的最有效方法。但我很确定这不是你的导师所期望的。

          对于“正常”解决方案,请单独考虑数据位。旋转和移位允许您移动位。对于第一个解决方案,隔离八个位(使用按位“与”操作),将它们移动到它们的目标位置(移位、旋转......),然后再次将它们组合在一起(使用按位“或”操作)。这不是最有效或最简单的实现,但您应该首先专注于获得正确的结果——优化可以等待。

          【讨论】:

          • 好的,我想出了如何使用移位和旋转来做到这一点。但是,我们可以因拥有最高效的代码而获得额外的荣誉。他不在乎我们怎么做。老实说,我不知道如何做一个查找表。之前在研究这个问题的答案时,我有点阅读它们,但我并不真正了解如何实现它们。
          • 假设您的程序在其生命周期中将进行无数次的位反转。当您启动程序时,您只需通过位循环执行所有 256 种可能性,并将结果存储在 256 字节的连续内存中,例如从 BASE 开始。现在,每当您需要翻转寄存器的位时,您只需查看 (BASE+value)。那是一个查找表(LUT)。如果您可以预先计算它们,您甚至可以将 LUT 作为 256 个常量的块硬编码到程序集中。然后就不需要初始化了。赢了。
          • 为了节省 256 字节表的空间,您可以有一个 16 字节表,一次包含四个位(半字节)的值。然后算法将是“revval=revdigit[inval&0x0f]>4]”。如果我是教授,我希望有两个部分,一个在索引中,另一个在外部。
          【解决方案6】:

          提示:如果进行移位,一位会被移出,而零(可能)会被移入。移出的位会去哪里?您需要将其移入目标寄存器或内存地址的另一端。

          我确信 25 年前我可以在没有汇编程序的情况下用 Z80 机器代码做到这一点 :)

          【讨论】:

          • 有一些更hacker的方法:graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious(在C中,但可以在汇编中完成......)
          • 实际上在汇编中比 C 更容易,因为你有rotate-through-carry。移入进位标志位,然后执行adc same,same 之类的操作,将进位标志位移入寄存器底部。
          【解决方案7】:

          当您进行右移时,最低有效位进入进位标志。

          执行循环时,进位标志用于填充结果的空位(LSB 表示 ROL,MSB 表示 ROR)。

          【讨论】:

            【解决方案8】:

            将两个寄存器视为位堆栈。如果您一次将一点从一个移到另一个会发生什么?

            【讨论】:

              【解决方案9】:

              首先要制定出算法来做你需要做的事情。将其表达为伪代码或 C 或纯英语或图表或任何你喜欢的东西。一旦您清除了这个概念障碍,实际的实现应该非常简单。

              您的 CPU 可能具有可让您移位和/或旋转寄存器的指令,可能包括进位标志作为附加位。这些说明将非常有用。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2014-04-15
                • 2014-11-30
                • 2023-04-08
                • 2017-08-09
                • 2018-03-24
                • 1970-01-01
                • 2022-05-04
                • 1970-01-01
                相关资源
                最近更新 更多