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

标签 assembly nasm x86-16 dosbox

Write a program called SUB64 to subtract the 64-bit integer in memory locations 0x0150 and 0x0154 from the 64-bit integer in 0x0160 and 0x0164. Store the result in memory location 0x0170 and 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 的东西?

即使这在某种程度上是正确的,我是否需要返回语句或 header 或任何东西才能使其实际编译? (我正在使用 NASM 和 DosBox)

这是我必须编写的 12 个类似程序中的第一个。因此,任何朝正确方向的插入都希望能让我走上正轨!

最佳答案

1)

the 64-bit integer in memory locations 0x0150 and 0x154

这不是非常准确的任务描述,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 和结果,所有这些都是 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

但是 number2 也可以通过 number1 的基地址来寻址,因此您也可以通过减法继续:

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] - 旧借用。如果您愿意,您可以将 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 不会“继续”减法。这里要吸取的教训是,您应该经常查看说明引用指南以获取详细信息。描述,包括它确实影响哪些标志。对于 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) 指令集,否则 80386 CPU 会引入 32 位寄存器。

关于assembly - 如何在8086汇编中减去两个64位整数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42643922/

相关文章:

assembly - x86_64 程序集 - 尝试在 x64 程序集中编辑数组内的字节时出现段错误

windows - 如何在 64 位 Windows 计算机上链接 32 位 Nasm 汇编目标代码

assembly - 如何在nasm汇编语言中从Z循环回到A?

linux - 在适用于 Linux 的 Windows 子系统上的 Ubuntu 上使用 INT 0x80 汇编编译的可执行文件不产生输出

assembly - “MOV AH, 4CH”用汇编语言表示什么意思?

assembly - “rdtsc”之前的“cpuid”

c - 最少三个值的最佳汇编或编译

assembly - 合法数据段构建

string - 如何在 .COM 可执行文件中以相反顺序打印字符串?

multithreading - 在 x86 上,如果 [mem] 不是 32 位对齐, "lock inc [mem]"还能正常工作吗?