首先,不要打扰“你使用AT& T或Intel汇编吗?”或类似的东西。
我想知道是否可以将两个寄存器(例如AX和BX)的内容一起添加到而不使用ADD
或ADC
指令。我想只使用这套指令来做到这一点:
MOV
CMP
JMP
JE
JNE
我想看一个简单的例子,即使它只是算法而没有任何实际代码,或者是一个很好的教程。
如果你想知道为什么我这么问,那是因为我正在创建一个非常基本的计算机,暂时只提供那些指令,我想知道它是否已经能够将两个寄存器组合在一起。
答案 0 :(得分:3)
理论上,是的。可以使用您的指令集表达以下非常棒的程序,并执行添加:
if ax = 0 and bx = 0:
result = 0
else if ax = 0 and bx = 1:
result = 1
...
else if ax = 42 and bx = 24:
result = 66
...
在任何实际意义上,你的问题的答案都是“不”。
答案 1 :(得分:3)
修改:可以使用一个258字节的查找表进行任何加法或减法,并且仅使用mov
,cmp
和jne
。对于巨型查找表完全没有必要。使用相同的查找表分别更新低8位和高8位。
以下代码仅使用一个258字节的查找表,仅使用ax
,bx
和mov
来汇总cmp
和jne
: / p>
[bits 64] ; valid instuctions: mov, cmp, jmp, je, jne ; used instuctions: mov, cmp, jne section .text global _start ; this code sums ax & bx _start: ; define the values to be summed (in ax & bx). mov ax,12853 ; example summand 1. mov bx,33276 ; example summand 2. ; summing is easy: just decrement each summand until it becomes zero, ; and for each decrement, increment the sum (in ax). cmp bx,0 jne start_summing ; normally this would be je ready and ; next 2 instructions would be deleted. cmp bx,1 ; this shows that jne is sufficient. jne ready ; this conditional jump branches always. start_summing: mov ecx,0 summing_loop: mov cl,bl mov bl,[rcx+(number_line-1)] ; decrement bl. cmp bl,255 jne bl_not_carry mov cl,bh mov bh,[rcx+(number_line-1)] ; decrement bh. bl_not_carry: mov cl,al mov al,[rcx+(number_line+1)] ; increment al. cmp al,0 jne al_not_carry mov cl,ah mov ah,[rcx+(number_line+1)] ; increment ah. al_not_carry: cmp bx,0 jne summing_loop ready: ; sum is now in eax. section .data max_value equ 255 max_value_plus_1 equ (max_value + 1) db max_value ; 0 - 1 = 255 number_line: %assign myValue 0 %rep max_value_plus_1 db myValue %assign myValue (myValue + 1) %endrep db 0
编辑:其余答案涉及需要更多内存的其他解决方案。
编辑:对于16位操作数的任何加法或减法,一维128 KiB查找表就足够了。无需巨型查找表。 编辑:修正了导致通常设置进位标志的添加产生错误结果的错误。
这是x86-64汇编中的代码,与YASM汇编,也可能与NASM汇编。实现add ax,bx
,仅使用mov
,cmp
& jne
。
[bits 64] ; valid commands: mov, cmp, jmp, je, jne ; used commands: mov, cmp, jne section .text global _start ; this code sums ax & bx _start: ; define the values to be summed (in ax & bx). mov ax,12853 ; example summand 1. mov bx,33276 ; example summand 2. ; summing is easy: just decrement each summand until it becomes zero, ; and for each decrement, increment the sum (in ax). mov edx,0 mov dx,ax mov eax,edx ; eax = ax mov ecx,0 mov cx,bx ; ecx = bx summing_loop: mov cx,[2*rcx+(number_line-2)] ; decrement ecx. mov ax,[2*rax+(number_line+2)] ; increment eax. cmp ecx,0 jne summing_loop ; sum is now in eax. section .data max_value equ 65535 dw max_value ; 0 - 1 = 65535 number_line: %assign myValue 0 %rep max_value dw myValue %assign myValue (myValue + 1) %endrep dw 0
编辑:其余的答案涉及我最初提出的更有限的解决方案。
可以使用二维查找表来完成。
对于8位寄存器,例如al
& bl
,这很简单。对于16位寄存器,它可以完成,但查找表将是巨大的(几乎1 tebibyte),请参阅下面的原因。查找表的每个单元格包含相应X&的总和。 Y坐标(X和Y坐标是加数)。
对于 8位和,查找表(256 * 256矩阵)是这样的:
db 0, 1, 2, ... , 253, 254, 255
db 1, 2, 3, ... , 254, 255, 0
db 2, 3, 4, ... , 255, 0, 1
. . . . . . .
. . . . . . .
. . . . . . .
db 253, 254, 255, ... , 250, 251, 252
db 254, 255, 0, ... , 251, 252, 253
db 255, 0, 1, ... , 252, 253, 254
在x86和x86-64中, mov
可用于乘以256 ^ n ,即:256,65536,16777216,......
使用mov
乘以256非常简单,可以计算ax = 256 * bl
:
mov ax,0
mov ah,bl
添加例如。 al
& bl
,我们需要获得正确的偏移量,它是256 * al + bl
或256 * bl + al
(因为查找表是对称矩阵,并且它是对称的,因为加法是可交换的操作)。
在x86 / x86-64中仅使用mov
乘以65536和更大的数字需要使用内存,因为无法直接寻址32位通用寄存器的高16位(例如eax)或64位通用寄存器的高32位(例如rax)。
仅使用eax = 65536 * bx
计算mov
:
mov [temp], dword 0
mov [temp+2], bx
mov eax, [temp]
...
temp dd 0
但是16位值的真正问题是在x86 / x86-64内存中使用字节偏移来寻址,而不是使用字/双字/ qword偏移,我们只能乘以 256 ^ n 。但是,如果我们在乘法和字节偏移量寻址方面没有这个问题,那么我们首先看一下查找表的外观。查找表可以是这样的:
dw 0, 1, 2, ... , 65533, 65534, 65535
dw 1, 2, 3, ... , 65534, 65535, 0
dw 2, 3, 4, ... , 65535, 0, 1
. . . . . . .
. . . . . . .
. . . . . . .
dw 65533, 65534, 65535, ... , 65530, 65531, 65532
dw 65534, 65535, 0, ... , 65531, 65532, 65533
dw 65535, 0, 1, ... , 65532, 65533, 65534
这里,每行有65536个单元,每个单元都是dword,因此每行需要2 * 65536个字节= 131072个字节。有65536行,所以它是 65536 * 65536矩阵。
字大小的单元格对于X(水平索引,任何一个加法)都不是问题,因为x86程序集允许比例因子为1,2,4和8 。
编辑:修正了数组大小的文字,实际上比1 TiB小一点。
这里的问题是,仅使用mov
不能将Y(垂直索引,另一个加数)乘以131072。因此,查找表的每一行必须重复32768次,或者更准确地说,在任何实际数据行之间必须有32767个未使用的填充行。为什么32767? 因为mov
只能用于乘以256,65536,16777216 ...所以我们需要将Y(垂直索引,另一个加数)乘以16777216。每行需要131072字节,为了使每个16777216字节的新数据行开始,每个数据行之后必须有32767个未使用的填充行(每个占用131072个字节)。在最后一个数据行之后不需要填充行,因此总的来说数组大小为:
65535 * 16777216 + 131072 = 10.99 * 10 ^ 12字节=几乎1 tebibyte(1 TiB)。
不幸的是,我的计算机中没有那么多内存,但是在x86-64中它是可能的。
以下是仅使用mov
和 256 * 256查找表的8位加法代码(使用YASM进行测试,也应与NASM一起组装):
[bits 64] ; valid instructions: mov, cmp, jmp, je, jne ; used instructions: mov section .text global _start ; al & bl must be preserved ; this code sums al & bl _start: ; define the values to be summed (in al & bl). mov al,47 ; example first summand mov bl,55 ; example second summand ; the summing code starts here. mov ecx,0 mov cl,al ; ecx = al mov ch,bl ; ecx = 256 * bl + al mov al,[rcx+sum_look_up_table] ; fetch the sum from look-up table. ; for 32-bit code, rcx -> ecx ; the sum is now in al. section .data y_times equ 256 x_times equ 256 sum_look_up_table: %assign myY 0 %rep y_times %assign myX 0 %rep x_times %assign myValue (myX + myY) %rep y_times %if myValue >= 256 %assign myValue (myValue - 256) %endif %endrep db myValue %assign myX (myX + 1) %endrep %assign myY (myY + 1) %endrep
答案 2 :(得分:2)
因为我正在创建一台非常基本的计算机,暂时只提供这些指令,我想知道它是否已经能够将两个寄存器组合在一起。
在这种情况下,您应该阅读What is the minimum instruction set required for any Assembly language to be considered useful?
简而言之,最小可能的架构有only one instruction。但是要更强大有用 非常基本的计算机应该至少有一位操作指令。 NAND
只需NOR
或MOV
就足够了all logic calculations。有了这个,你也可以做算术,但不如单独的ADD / SUB有效。此外,你需要另一个条件跳转。这总计为3条指令。但是,如果您可以有5个这样的指令,那么您可以在另一个问题上阅读更多更好的指令选择
也就是说,可以在x86和8051之类的许多其他架构中只执行static void alu_add8(char* s, char* x, char* y, char* c, int off)
{
/* requires dword carry initialized */
print("# alu_add8\n");
debug_id();
print("movl $0, %%eax\n");
print("movl $0, %%ebx\n");
print("movl $0, %%ecx\n");
print("movl $0, %%edx\n");
print("movb (%s+%d), %%al\n", x, off);
print("movb (%s+%d), %%cl\n", y, off);
print("movl (%s), %%ebx\n", c);
print("movb alu_add8l(%%eax,%%ecx), %%dl\n");
print("movb alu_add8h(%%eax,%%ecx), %%dh\n");
print("movb alu_add8l(%%edx,%%ebx), %%al\n");
print("movb %%al, (%s+%d)\n", s, off);
print("movb alu_add8h(%%edx,%%ebx), %%al\n");
print("movb %%al, (%s)\n", c);
print("# end alu_add8\n");
}
,因为它是proved to be Turing-complete。甚至还有一个编译器可以将有效的C代码编译成只有MOV的程序(或者只有XOR,SUB,ADD,XADD,ADC,SBB,AND / OR,PUSH / POP,1位移位或CMPXCHG / XCHG)名为movfuscator。有关编译器如何工作的详细信息,请参阅Break Me00 The MoVfuscator Turning mov into a soul crushing RE nightmare Christopher Domas,如果没有时间,则只需read the slide。基本上它使用查找表来实现大多数目的。以下是有关如何实现8-bit addition的示例
{{1}}
进一步阅读