我正在编写一些非常严格的ASM代码。
请注意由NASM生成的这组操作码:
Intent intent = new Intent(getApplicationContext(), scoreKeeper.class);
intent.putExtra("score", score);
startActivity(intent);
类似的:
8AA4241C020000 mov ah,[esp+0x21c]
有没有办法与处理器通信,你打算将一个15位偏移应用于32位寄存器,让它为自己找出0填充?
我一直在梳理https://c9x.me/x86/html/file_module_x86_id_176.html以寻求指导。额外的2个字节在这里或那里真的会拯救我的生命!
也接受:
重写语句以使其变小的替代方法,最终我在这个例子中的目的是:
051C020000 add eax,0x21c ; 4 extra 0's!
8D84241C020000 lea eax,[esp+0x21c] ; Brutal!
如果有办法对其进行手工编码以使其超小,我很乐意看到这种技术。
答案 0 :(得分:0)
有没有办法与处理器通信,你打算将一个15位偏移应用于32位寄存器,让它为自己找出0填充?
没有。可用的指令编码记录在英特尔手册中(其在线版本可在线提供各种位置;请参阅x86标签wiki中的链接)。对于MOV
,偏移大小与寄存器大小匹配。当您MOV
进入16位寄存器时,处理器仅使用16位偏移。无法获得15位偏移量。
正如雷蒙德·陈所说,"它不像你可以只编造[你自己的自定义编码]"。
在某些模式下,某些指令有一个符号扩展位。
当然可以,但我不知道这对你有什么帮助。您的目标是减小指令的大小:添加额外的16位操作数大小前缀以更改偏移大小的解释并不会帮助您这样做。
通常,如果对指令编码的编码方式较短,则汇编器将为您发出该编码。 当然 NASM将使用multi-pass optimization option(默认启用)。
这里或那里额外的2个字节真的可以挽救我的生命!
这不是您可以有效保存的地方之一。
正如David Wohlferd已经建议的那样,如果您反复这样做,您可以通过预先清除寄存器(XOR reg, reg
; 2个字节)来稍微压缩代码大小,使用此作为reg寄存器MOV
的源寄存器(每个只有2个字节),然后将16位MOV
写入已经清除其高16位的寄存器中。
在处理具有大量寄存器的ISA时,相对常见的做法是在特定过程的上下文中将一个用于包含0。许多ISA通过具有专用的零寄存器来进一步实现这一点。您也可以使用x86执行此操作,但考虑到ISA的寄存器约束,它通常是一种悲观。但是,如果您的尺寸优先于其他所有尺寸,那么它有时可能会有意义。 (然后,它可能不会,因为它可能会迫使你溢出到内存中,并且每个存储和加载会使代码膨胀至少 2个字节。)
实际上,我打赌你的代码中有很多其他地方你可以通过指令大小进行挥霍,并且可以实现更大幅度的降低。如果您希望审核代码并着眼于缩小代码,请考虑在Code Review上发布一个问题(假设您当前有工作代码)。
我不确定在什么情况下你会编写代码,节省2个字节会很重要。也许您正在编写一个需要在512字节以内的引导加载程序?在这种情况下,大多数人所做的是编写一个多阶段引导加载程序,其中第一阶段,即仅限制为512字节的阶段,只调用第二阶段,在那里你没有这样的限制。
答案 1 :(得分:0)
如果你有一些归零的高位24位,那么例如(对于eax归零)可以关闭2个字节:
; additional 2 bytes ruining the saving, if you don't have zero reg.
; b0 87 mov al,0x87
; 5 byte fetch of value
b0 87 mov al,0x87
8a 24 84 mov ah,BYTE PTR [esp+eax*4]
或者,如果您知道某些其他寄存器中有一些低值104..540(其中只有一些适合),您可以稍微降低偏移量,例如假设你知道ebx == 104
:
8a 64 9c 7c mov ah,BYTE PTR [esp+ebx*4+(0x21C-104*4)]
如果这是真正的大小挑战,你必须发布整个代码,因为可能(并且经常是)疯狂的方式以非常意外和几乎难以想象的方式保存大小。