繁体   English   中英

大量转移-组装

[英]Shifting a huge number - assembly

我有大量加载到堆栈上的文件,我使用eax访问。 它不能存储在寄存器中。 我使用eax只是指向它的地址(数字是自然类型,这意味着前4个字节包含符号,接下来的4个字节包含长度,其他则为实际值)。

我必须改变它的edx时间。 我正在考虑从LSB一位开始移位(最多8次/字节),然后将这些位复制到下一个字节中。 为此,我必须首先移动下一个字节,依此类推,直到MSB位置+ 1(最坏的情况),或者直到完成所有移位并且没有进位标志为止。 PS我显然是在这种特殊情况下谈论shl但是shr情况几乎相同。

有没有更简单的解决方案?

经典的8位时代思想是使用由DEC counter + JNZ交错的RCL(带进位向左旋转)-您可以暂停一秒钟,最后欣赏一下,为什么x86 DEC/INC指令仅影响零标志,但不影响进位(谜底已解决) )。

因此,代码将遵循以下几行:

    mov   edi,address_of_last_byte
    mov   edx,count_of_bytes
    mov   cl,1
    clc   ; clear CF
loop_1_bit_left:
    rcl   byte [edi],cl    ; CF -> LSB, MSB -> CF
    dec   edi    ; preserves CF! Goes from last byte to first one
    dec   edx    ; preserves CF! Decrement counter
    jnz   loop_1_bit_left  ; till whole buffer is shifted
    ; CF has last bit, will be thrown away unless you do something about it

现在这有很多不足之处...

如何保存缓冲区的MSB? 我将首先计算移位后所需的缓冲区大小(new_length = arg_length +(shift + 7)/ 8)。 并将输入复制到其中,然后不移位arg_length个字节,而是移位new_length个字节,从而解决了MSB截断的问题。

但是还有另一个问题,性能。 不幸的是,现代x86 CPU上的rcl速度很慢,因此以这种方式进行315位移位是非常糟糕的主意。 但是您不必。 您可以仅通过将已经减少39个字节的输入数(朝开始处)复制到new_length缓冲区中,来进行312位移位,然后通过上面的循环将剩下的3位进行一次移位。

另外,如果您将足够填充输出缓冲区,则可以使用dword / qword rcl变体(32b / 64b代码)来同时处理更多字节。 (实际上,根据您的描述,不清楚您的代码由谁负责分配,如果您的代码将以某种方式将其返回到堆栈上(??我不确定根据移位量动态增长的缓冲区在哪种ABI可能实现),或者将其分配在堆上,再在顶部再添加几个字节,因此您可以在值的最后一个常规字节之后修改几个字节,并且可以改为使用dword / qword以及超过4 / 8B对齐的(!)地址。


编辑: word / dword引用的变种rcl / rcr在阵列整个大数量以下的x86小端的方式,只有当将正常工作,且环路下面的正确++ / -方向(位b0-7在字节数组中的偏移量为+0,例如b80-b87的位在偏移量为+10时,右移将从MSB(+10)b87移向LSB(+0)b0)。 我的第一个byte [edi]示例期望它采用大端方式,其中MSB从偏移量+0开始,而LSB以+结尾,因此可以按人的顺序查看这些位b87 .. b0,little endian具有每个字节组(b7 .. b0 b15 .. b8 ... ... ... b87 ... b80)在视觉上“反转”了……至少我是这样认为的,现在我开始变得如此困惑。 只需以一种方式编写代码,为简单的极端情况创建单元测试,并验证结果并对其进行修复以产生您所期望的结果。 :D


只要确保您在这种情况下不要通过sub edi,4sub rdi,8 )更新edi ,因为那样会破坏CF内容,因此改用lea edi[edi-4]通过寻址模式进行简单计算的方法。 并将计数器调整为正确的/4 || /8 /4 || /8值。

为了获得最佳性能,可能仍然值得一口气将1-7位移位:对于左1位,您可以保留rcl版本,对于2-7位移位,某些掩蔽/运算值的变体将按目标量单次移位例如,使用32b寄存器来处理16b的缓冲区读/写操作,并将移出的位保留在上半部分。 或者,如果您走得那么远,也许它与shl/and/or 1位变体是否兼容,是否不比rcl快。 由于编译器未使用rcl ,因此特定的CPU可能会比单个rcl更喜欢一些shl/and/or指令。


有趣的事实:我完全独自编写的第一个Z80汇编代码就是这样做的,将一个巨大的内存区域向左(向右)移动了一点。 由于该巨大的存储区实际上是ZX Spectrum计算机的视频内存,因此可以有效地左右移动图像1个像素(ZX每个像素使用1位)。

而且我没有意识到可以从一个旋转到另一个旋转使用CF,因此我通过分别屏蔽位,将其复制到另一个寄存器,然后从那里恢复到新的字节等方式来做到这一点。

所以我写了它,运行它(由于错误而重置了ZX),修复了该错误,运行了它,并观察了图像的移动方式……比我预期的慢了10倍(每秒约3帧) “全能的快速汇编代码”。 然后我的一个朋友确实向我展示了如何旋转它,这使代码朝着20 FPS的方向运行(这仍然使我意识到,即使“快速汇编”也不是无限的,我必须花很多时间来编写代码,在ZX的屏幕上看到任何看起来不错的东西)。

我宁愿ROL或ROR值,切掉翻转的位,并将它们应用于下一个字节(对它应用完全相同的过程之后)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM