如何在8086汇编

时间:2017-03-07 08:55:57

标签: 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, 0X0164
SUB BX, DX
SUB AX, CX
MOVE 0X0174, BX
MOVE 0X0170, AX

我将64位整数的每一半存储到寄存器中。然后我减去寄存器并将它们放入存储器位置。那个十六进制格式好吗?还是需要像0174h那样?

即使这在某种程度上是正确的,我是否需要一个return语句或一个标题或任何东西来实际编译? (我使用NASM和DosBox)

这是我必须编写的12个类似程序中的第一个。因此,任何朝着正确方向前进的方向都有希望让我继续前行!

1 个答案:

答案 0 :(得分:4)

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和结果,所有这些都是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] - old_borrow。如果你愿意,你可以 将CF存储在某个地方,CPU有指令来检测/存储/设置CF的值,但是一旦你计算出15 - 8是7,那么7是最终的,你不要&# 39;需要记住你确实借过了" 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,只修改其他算术标志。

在不修改任何标志的情况下将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引入的。