使用EXT,AND扩展数字。 ASR和LSL如何工作(68000汇编语言)

时间:2016-07-27 17:11:12

标签: assembly 68000

我正在学习汇编语言(对于68000微处理器),我遇到了以下问题:

  

编写一个68000汇编语言程序,执行5 * X.   + 6 * Y + [Y / 8] - > [D1.L],其中x是存储在最低字节中的无符号8位数   D0和Y是存储在D1的高16位中的16位有符号数。忽略其余部分   Y / 8。

这就是解决方案:

        ANDI.W      #$OOFF,DO    ;CONVERT X TO UNSIGNED 16-BIT
        MULU.W      #5,DO       ;COMPUTE UNSIGNED 5*X IN D0.L
        SWAP.W      D1          ;MOVE Y TO LOW 16 BITS IN D1
        M0VE.W      D1 ,D2      ;SAVE Y TO LOW 16 BITS OF D2
        MULS.W      #6,D1       ;COMPUTE SIGNED 6*Y IN D1.L
        ADD.L       DO,D1       ;ADD 5*X WITH 6*Y
        EXT.L       D2          ;SIGN EXTEND Y TO 32 BITS
        ASR.L       #3,D2       ;PERFORM Y/8;DISCARD REMAINDER
        ADD.L       D2,Dl       ;PERFORM 5*X+6*Y +Y/8
FINISH  JMP         FINISH

但是,我不了解第一行。我想我们必须将其转换为16位,因为Y是一个16位有符号数,但我不知道ANDI指令在这种情况下是如何工作的。

此外,在示例中,他们使用ASR.L #3,D2将D2除以8;所以,如果我使用ASR.L #2,D2,那么我将D2除以4?或者它是如何工作的?

如果我使用LSL.L #3,D2,那么我会将D2乘以8?

最后,他们为什么签名将Y扩展到32位?

感谢您的帮助!

2 个答案:

答案 0 :(得分:2)

我不知道68000 asm,但是我认为ANDI会立即与AND,所以result_16b = 0x00FF & X_8b;这将确保结果的高8位为零,低8位包含X值。 / p>

向左/向右移动实际上乘以/除以2的幂。

考虑二进制8b中的值10
0000 1010(8 + 2 = 10)
{1}}向左移位1位= 16 + 4 = 20(10 * 2)
0001 0100 10向右移动1个位置= 4 + 1 = 5(10/2)
0000 0101 10后向右移2个位置= 2(10/4截断)

所以在某些CPU上做X * 5可能会更快(比使用0000 0010):

MUL

它的原理是相同的,因为我们只需左右移动数字就可以得到* 10和/ 10的十进制数。如同45左移2个位置是45 * 100 = 4500.而45向右移动1个位置是4(45/10截断)。

他们可能会将最后的指令扩展到32b,因为结果应该是D1.L?我认为之前的MUL可能导致32b结果值?检查指令参考以获取详细信息。

但是我对问题描述中的那些x4 = (x<<2); // copy of x shifted left twice (*4) x = x + x4; // sum of original plus copy 感到困惑,在其他汇编程序中[]的使用通常表示&#34; x&#34;中的值的使用。作为从内存中获取值的地址,而不是直接使用值x。但是在你的问题描述中它没有多大意义,所以它可能只是关于价值......

关于AND和EXT。是的,这是查看它的一种方式,记住你用AND扩展无符号数,并用EXT签名,但是在学习汇编语言时,你应该总是努力争取第二个视角,即&#34;它实际上做了什么&#34;,并学习它们。

它的作用是在比特级上重要的大部分时间。

所以你有8位无符号数X,你想要它加到16位数tempY,什么是捕获?
如果你只是添加tempY_low8b + X,你会得到正确的tempY_low8b,但是如果low8b加上溢出,tempY_high8b可能会丢失+1。

如何解决这个问题?您可以将X值扩展为16b,也可以检测溢出并修复tempY_high8b。在68k上我认为操纵高8b的tempY是非常不切实际的(在x86上它可以归结为简单的[x],如果ADC ah,0寄存器用作tempY),那么它就是这样的更容易扩展X.

以位为单位扩展X意味着将X复制到能够容纳16位以上的位,并将高位8位复位为零(无符号8b值将使所有扩展位为零,扩展时为&#34;显而易见&# 34;二进制数如何工作)。由于你在ax = ah:al中有X,它有16位以上,你不需要复制,只需清除高位。这是D0有利于什么,通过不在&#34;掩码中提供它来清除特定位置&#34;值,所以当你做AND X,掩码时,结果中的每个位,掩码有0位,也将变为零。 X的其他位(掩码有1)将保持其值。所以AND正在这样做,用掩码ANDI.W #$OOFF,DO掩盖D0 16b,清除高8b,保持低8b。

如果您考虑装配中的某些问题,并且您已经意识到需要将位i,j和k重置为零,那么您现在可以回想起那里有0000 0000 1111 1111个单一AND做到这一点(使用适当的掩码,i,j和k归零)。

现在Y是签名号码,所以扩展这个号码更加棘手。如果Y的最高位设置为0,则表示该数字为正数,您可以通过在其前面添加零来扩展它。但是如果最高位为1,则为负,并且将相同的负值扩展到更多位是在其前面添加一个。 0010 = +2 in signed 4b -> extended to 8b -> 0000 0010 = +2 in signed 8b
1110 = -2 in signed 4b -> extended to 8b -> 1111 1110 = -2 in signed 8b

没有具有这种功能的基本位操作符,以及&#34;将一些位复制到所有其他高位&#34;,这就是为什么这有专门指令的原因{ {1}}。

如果您必须坚持基本的位操作,您可以通过首先将窄位移位,在更宽的版本中将最高位置于最高位位置来执行相同操作,然后使用&#34;算法&#34 ;向右移回原来的位置。算术右移将使用最高位的副本填充新位 EXT
0010 to 8b: shift left 4 = 0010 0000 -> a.shift right 4 -> 0000 0010

这就是为什么大多数处理器都有两种移位位右指令变体,一种是将0置为新位,另一种是保持最高位。左移,有些CPU确实有两个变量,两者都相同,将低位设置为0.

因此,如果您确实知道您将无符号数字延长1110 to 8b: shift left 4 = 1110 0000 -> a.shift right 4 -> 1111 1110并由AND签名,那么它就可以了,但如果您还了解内部发生了什么,那么您可以有时会找出不同的可能性。

例如:

EXT

EOR.W D1,D1 MOVE.B D0,D1 16b无符号数放入D1中的8b无符号数。我会让你自己想一个这个,作为练习。 :)

所以不要盲目地学习&#34;如何做到这一点和那个&#34;而是总是检查你完全理解操作及其目的。这样您就可以更快地构建自己的解决方案。毕竟,CPU实际上很少有基本操作。

通常所有CPU都有(并非总是所有这些,但从基本的那些足以模拟其余部分):

  • 位操作:旋转,移位,AND,OR,XOR(EOR),NOT和复制(移动/加载/存储变体)(x86测试)。
  • 几个算术片段如:NEG,ADD,SUB,MUL,DIV,INC,DEC(还有一些比较)
  • jump / call / ret和其他流量控制操作
  • 堆栈实用程序功能:push / pop / ...(可以通过move / add / sub模拟)
  • 其他一切通常是对CPU /平台的专门控制,或基础的专业/复杂变体,或更高的数学函数,如sin / cos /...

因此,如果你学习了前三组,并习惯于在位级别上考虑它们,那么在对它的语法进行简短的学习之后,你可以在任何其他CPU上得到很大的帮助。&#34;不同的& #34;汇编。这些是非常普遍的。这也有助于在更高级语言编程时,因此更好地了解什么是CPU的廉价本机操作,以及什么是更复杂的计算,因此您可以选择更简单的问题解决方案。

答案 1 :(得分:1)

原始帖子中的答案效率很低。与“正常”的4到12个周期的指令时间相比,应尽可能避免使用MULU和DIV指令,因为它们分别花费70/140个周期。

首先,您可以更改公式,以便在编码时更好地工作

5 * X + 6 * Y = (X + Y) * 4 + X + 2 * Y
              = (X + Y) << 2 + X + Y + Y

         ORG $1000
         ; Compute 5*X + 6*Y + [Y/8] -> [D1.L], 
         ; X = D0.B (unsigned). Y = top 16 bits D1 (signed) 
START
         MOVEQ     #0,D2                   ; MOVEQ faster than CLR
         MOVE.B    D0,D2                   ; D2 now 32 bit X

         SWAP      D1                      ; move Y to bottom 16 bits
         EXT.L     D1                      ; extend to long word.
         MOVE.L    D1,D3                   ;
         ADD.L     D2,D3                   ; add X
         LSL.L     #2,D3                   ; multiply by 4 = 4(X+Y)
         ADD.L     D2,D3                   ; add another X
         ADD.L     D1,D3
         ADD.L     D1,D3                   ; add another 2 Y
         ASR.L     #3,D1                   ; Y/8
         ADD.L     D3,D1                   ; add (5X+6Y)
FINISH
         END      START

要回答您的问题:

EXT执行带符号的扩展,因为值的高位本质上是指示它是正还是负的指示符。清除值的高位将执行无符号扩展。在“二进制/二进制补码”中,正值的高位为零。在值上使用AND会将不属于“ MASK”的所有位都设置为零,因此可以用来清除这些位。

每次将值移位1时,即乘以(左)或除以(右)2。也可以通过重复加法乘(小),这就是上面的代码。

编写这样的自定义代码有一些缺点,因为它是针对特定问题的优化解决方案,并且进行较小的更改将需要重新编码。