如何在8086汇编中减去两个64位整数
How to subtract two 64 bit integers in 8086 assembly
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?
即使这在某种程度上是正确的,我是否需要 return 语句或 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 包含新的"borrow" of bx - [0x162] - old_borrow
。如果你愿意,你 可以 将 CF 存储在某个地方,CPU 有关于 detect/store/set 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
不会在减法中"continue"。这里要吸取的教训是,您应该经常查看说明参考指南以获取详细信息。描述,包括它影响的标志。对于 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,只修改其他算术标志。
在不修改任何标志的情况下将 2 添加到有效寻址寄存器的另一种方法是 lea si,[si+2]
。
BTW 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.
引入的
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?
即使这在某种程度上是正确的,我是否需要 return 语句或 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 包含新的"borrow" of bx - [0x162] - old_borrow
。如果你愿意,你 可以 将 CF 存储在某个地方,CPU 有关于 detect/store/set 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
不会在减法中"continue"。这里要吸取的教训是,您应该经常查看说明参考指南以获取详细信息。描述,包括它影响的标志。对于 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,只修改其他算术标志。
在不修改任何标志的情况下将 2 添加到有效寻址寄存器的另一种方法是 lea si,[si+2]
。
BTW 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.
引入的