Assembly 如何在8086汇编中减去两个64位整数

Assembly 如何在8086汇编中减去两个64位整数,assembly,nasm,x86-16,dosbox,Assembly,Nasm,X86 16,Dosbox,编写一个名为SUB64的程序,从0x0160和0x0164中的64位整数减去内存位置0x0150和0x0154中的64位整数。将结果存储在内存位置0x0170和0x0174中 我理解将其分成更小的部分背后的逻辑,因为我们无法将64位放入寄存器。我知道我们先减去最不重要的部分。我正在努力编写代码。我们使用哪种GPIO重要吗?这是我的例子,也许你能看到我的想法。也许我离这里不远,但感觉就像这样 MOV AX, 0X0150 MOV BX, 0X0154 MOV CX, 0X0160 MOV DX,

编写一个名为SUB64的程序,从0x0160和0x0164中的64位整数减去内存位置0x0150和0x0154中的64位整数。将结果存储在内存位置0x0170和0x0174中

我理解将其分成更小的部分背后的逻辑,因为我们无法将64位放入寄存器。我知道我们先减去最不重要的部分。我正在努力编写代码。我们使用哪种GPIO重要吗?这是我的例子,也许你能看到我的想法。也许我离这里不远,但感觉就像这样

MOV AX, 0X0150
MOV BX, 0X0154
MOV CX, 0X0160
MOV DX, 0X0164
SUB BX, DX
SUB AX, CX
MOVE 0X0174, BX
MOVE 0X0170, AX
我将64位整数的每一半存储到寄存器中。然后我减去寄存器并将它们放入内存位置。十六进制格式可以吗?或者它需要像0174h这样的东西

即使这在某种程度上是正确的,我是否需要一个返回语句或一个头或任何东西来使它真正编译?(我正在使用NASM和DosBox)

这是我必须编写的12个类似程序中的第一个。所以任何朝着正确方向的努力都有希望让我走上正轨

1)

内存位置0x0150和0x154中的64位整数

这不是非常精确的任务描述,64b整数在内存中的位置从0x0150到0x0157(总共8个字节)。该描述听起来像是默认使用的内存单元
dword
(32位),在16b实模式下,默认数据大小不是很合理

2)
mov ax,0x0150
不会从内存中加载值,它会将该常量
0x0150
加载到
ax
中。如果要从内存加载实际值,必须取消对该内存地址的引用,例如:

mov si,0x0150  ; set si to contain address of input number1
mov ax,[si]    ; load least significant 16b word (LSW) of it into ax

; you can even use absolute addressing like:
mov  ax,[0x0150]  ; usually not practical, but possible
现在,如果您想放置所有字的所有地址(将使用3*4个内存字,num1、num2和result,它们都有4个字长),那么寄存器很快就会用完。但实际上,您可以在16b模式下使用相对寻址,因此这是可能的:

mov   si,0x0150  ; set si to contain address of input number1
mov   ax,[si]
mov   bx,[si+2]
mov   cx,[si+4]
mov   dx,[si+6]
; here dx:cx:bx:ax concatenated into single 64b number contains the number1
但是数字2也可以通过数字1的基址寻址,因此您也可以通过减法继续:

sub   ax,[si+0x10]   ; subtraction of LSW, ignoring CF (borrow)
sbb   bx,[si+0x12]   ; continuing by subtracting adjoining 16 bits
    ; but this time CF will affect the result, to make any "borrowing"
    ; happening while subtracting the LSW to propagate into upper 16 bits
... etc..
关于你在评论中的问题。。。由CF修改的结果已存储在
bx
中,旧CF丢失,CF包含
bx-[0x162]-旧\u借用的新“借用”。如果您愿意,您可以将CF存储在某个地方,CPU有指示来检测/存储/设置CF值,但是对于数字,一旦您计算出15-8是7,7就是最终值,您不需要记住您借用了“1”来从5中减去8

相对于num1地址,结果地址也很容易计算,所以您可以将结果存储回内存

mov   [si+0x20],ax  ; 0x0150 + 0x20 = 0x0170
mov   [si+0x22],bx
...

我基本上给出了整个解决方案:/。。。所以,让我们至少添加一些更多的信息,让您稍微了解一下

您可以在不使用4个16b寄存器来保存单个64b值的情况下执行相同操作,如:

mov   ax,[si]
sub   ax,[si+0x10]
mov   [si+0x20],ax
mov   ax,[si+2]
sbb   ax,[si+0x10+2]
mov   [si+0x20+2],ax
...
看了这本书后,你可能会有一个好主意,那么:

mov   ax,[si]
sub   ax,[si+0x10]
mov   [si+0x20],ax
add   si,2          ; adjust rather base pointer then changing all those offsets
mov   ax,[si]
sbb   ax,[si+0x10]
mov   [si+0x20],ax
...
它有用吗?不再是了。
add si,2
将修改CF,因此下一个
sbb
不会在减法中“继续”。这里要学到的教训是,您应该经常查看《说明参考指南》以了解详细的ins。说明,包括它影响的标志。对于x86-某些指令可能会让您感到惊讶,例如,上面的代码修改为:

mov   ax,[si]
sub   ax,[si+0x10]
mov   [si+0x20],ax
inc   si        ; adjust base pointer
inc   si
mov   ax,[si]
sbb   ax,[si+0x10]
mov   [si+0x20],ax
...
将起作用,因为
inc
不影响CF,只修改其他算术标志

另一种在不修改任何标志的情况下向有效寻址寄存器添加两个的方法是
lea-si[si+2]

顺便说一句,dosbox默认模拟386+,因此您甚至可以在16b实模式下使用:

mov   si,0x0150
mov   eax,[si]
mov   ebx,[si+4]    ; ebx:eax = 64b number1 (note +4 this time)
sub   eax,[si+0x10]
sbb   ebx,[si+0x14] ; ebx:eax = (number1 - number2)
mov   [si+0x20],eax
...

除非您必须仅使用8086(80286)指令集,否则32位寄存器是随80386 CPU引入的。

您需要注意从低半部到高半部的借用-查看
SBB
指令。谢谢。我可以简单地在进位标志激活的情况下存储该值吗?或者我必须对这些值做些什么,以便在读取数据时返回正确的值?如果这个问题没有意义,很抱歉。如果我们必须将64位字重新组合在一起,进位标志会影响这一点吗,还是会将所有的字组合在一起,对吗?记得小学时做过减法吗?您会一次减去一列,然后在需要时从下一列中“借用”吗?将64位值的每一半看作一列。如果从第一个
SUB
获得借阅,则需要使用
SBB
指令而不是
SUB
将其传播到高位字减法。谷歌的“x86 SBB”或类似的,你应该找到吨的例子…AX是一个16位寄存器。您需要执行一个sub和3个sbb来执行64位减法。64位整数需要8个字节或4个字的数据,这意味着如果64位整数的第一个(最低有效)字位于0x150,那么最后一个(最高有效)字位于0x156。@Penrose5833您的问题描述和代码都表明您仍在处理寄存器大小(eax 32位,ax 16位)和寻址模式(
mov-ax,0x0150
vs
mov-ax,[0x0150]
)也许这些东西值得一看。此外,8086是非常老式的(仍然有效),但如果您可以-并且被允许-您应该使用32位汇编读取“除以0x0150和0x154”。。。“从0x0160和0x164”我假设允许他使用32位寄存器(0x0170/4=0x0150/4-0x160/4)。0x0154是第一个值的后半部分,而不是2nd@Tommylee2k可能是的,但他在自己的代码中只使用了ax。。。也许他没有意识到
eax
ax
al
之间的细微差别,仍然让他们感到困惑?我确实在答案的末尾添加了32b寄存器的示例。但如果他能将其与纯16b版本进行比较,实际上并没有什么坏处,当您更改数据类型时,需要调整什么/如何调整