2013-04-10 116 views
3

首先,不要打扰“您是否使用AT & T或Intel汇编?”或类似的东西。汇编[ASM]如何在没有“ADD”指令的情况下将两个寄存器合并在一起

我想知道是否可以在不使用“ADD”或“ADC”指令的情况下将两个寄存器的内容(如AX和BX)一起添加到。我想只使用此指令集做:

MOV 
CMP 
JMP 
JE 
JNE 

我想看到一个简单的例子,即使是刚刚算法没有任何真正的代码,或一个很好的教程这一点。

如果您想知道我为什么要问这个问题,那是因为我正在创建一台非常基本的计算机,只提供那些当时的说明,并且我想知道它是否已经能够将两个寄存器一起添加。

非常感谢!

+3

如果你有递减/递增指令,你可以这样做。 – 2013-04-10 08:57:20

+3

假设'MOV'可以从内存中移动,那么如果你在大型查找表中有大量内存需要烧录,那么这是可能的。 – harold 2013-04-10 09:04:58

+1

@harold或者它可能是一个巨大的状态机的内存吨,其中指令指针值是状态,假设CMP可以与常量进行比较。 – 2013-04-10 09:22:10

回答

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 
... 

在任何实际意义上说,回答你的问题是“不”。

+0

我的想法确切,+1。 – 2013-04-10 09:23:05

+0

好,如果我明白了,我应该测试......所有可能的值并给出结果... 不够实用,但谢谢你的想法:)。当从高级编程语言到assambly时,算法看起来很愚蠢...... – 2013-04-10 09:28:11

+0

@GuillaumeVoiron:鉴于我们并不真正了解你在这里实现的目标,所以很难评论你应该做什么。 – NPE 2013-04-10 09:29:01

3

编辑:任何加法或减法可以做一个258字节的查表并只使用movcmpjne。根本不需要巨型查找表。使用相同的查找表分别更新低8位和高8位。

下面是仅使用一个258字节的查表只有movcmpjne总结axbx代码:

 
[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 

编辑:的答案涉及其他的解决方案,其余那需要更多的记忆。

编辑:一维128 KiB查找表就足以用于任何加法或减法的16位操作数。不需要巨大的查找表。 编辑:修正了导致通常设置进位标志产生不正确结果的附加错误。

下面是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 tebibtete),请参阅下面的原因。查找表的每个单元格包含相应的X坐标的总和(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 

在86和x86-64 mov可以通过用于乘法256^N,即:256,65536,16777216,...

乘以256 mov容易,计算ax = 256 * bl

mov ax,0 
mov ah,bl 

要添加例如, al & bl,我们需要得到正确的偏移量,它是256 * al + bl256 * bl + al(因为查找表是一个对称矩阵,并且它是对称的,因为加法是交换操作)。

在x86/x86-64中乘以65536和更大的数字只使用mov需要使用内存,因为无法直接寻址32位通用寄存器(如eax)的高16位或高32位64位通用寄存器的位(如rax)。

为了计算eax = 65536 * bx只有mov使用:

mov [temp], dword 0 
mov [temp+2], bx 
mov eax, [temp] 

... 

temp dd 0 

但随着16位值,真正的问题是,在86/x86-64的内存使用字节寻址偏置,不与字/双字/四字偏移,我们只能乘以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单元,每个单元为双字,所以每一行需要2个* 65536字节= 131072个字节。有65536行,所以它是一个65536 * 65536矩阵

由于x86程序集允许比例因子为1,2,4和8,因此字大小的单元对于X(水平索引或任一加数)不是问题。

编辑:更正阵列大小的文本,它实际上比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中是可以的。

下面是使用只mov256 * 256查表(与YASM测试,应与NASM组装太)为8位加法代码:

 
[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 
+0

你知道,你可以对待... – 2013-04-11 23:53:57

+1

我喜欢这个答案如何使用需要添加的x86寻址模式,这是对OP的愚蠢尝试以避免需要添加电路的嘲弄。使用我认为可能需要的不太可怕的代码来完成这项工作的好主意!我喜欢存储/重新加载偏移想法来处理低于2的字节,它们没有单独的部分寄存器名称。 – 2016-08-25 03:54:51

1

一个非常基本的计算机应该至少有一个位操作指令。只需一个NAND或NOR即可满足所有的逻辑计算。并且,你也可以做一些算术运算。

相关问题