从数据段8086检索/保存DWORD

时间:2015-02-06 16:10:37

标签: assembly cpu masm x86-16 tasm

我有一个问题要解决,我到了我不知道还能尝试什么的地步。所以我有这个数据段:

data segment
    a db 12h, 34h, 56h, 78h, 9Ah
    b dd 2 dup(?)
data ends

我想要做的是以这样的方式对其进行编码:b将具有与其具有完全相同的值,意思是:

b dd 12345678h, 9A000000h

现在,我陷入困境的部分。 这适用于8086 CPU,使用little endian技术存储/检索数据。例如,数据段的高字节保存在寄存器AX的高字节中,DS的低字节保存在寄存器AX的低字节中。

我尝试了什么:

lea si, a    ; load offset of a into si
lea di, d    ; load offset of d into di
mov cx, 2    ; initialize counter with 2

repeta:
mov dx, [si] ; copy the data starting the offset si to dx
             ; would be cool to get the dword at offset si
             ; into DX:AX, but looks like it doesn't

mov [di], dx ; reverse it into the other variable d
             ; and save DX:AX to offset di
inc si
inc di
loop repeta  ; cx!=0 go to repeta

首先,我不确定代码是否需要在途中进行更改,但是我可以通过将值放入DL中来获取偏移量si,小端和大端的数据。 ,DH 8位寄存器(这应该不是问题)。

问题在于我不知道如何从偏移量a开始获取双字,例如寄存器(DX:AX),因为它们通常用于此类事情。我目前的代码,一直只能识别DX,但AX保持不变。

所以问题是,如何以这种方式从数据段中检索到两个寄存器中的dword,我还能够将它作为双字保存在数据段中。

任何建议都将不胜感激。

5 个答案:

答案 0 :(得分:1)

您需要使用其他说明来完成此任务。

mov ax,[si+2]

答案 1 :(得分:1)

  

所以问题是,如何以这种方式从数据段中检索两个寄存器dword,我也能够将它作为双字保存在数据段中。

lea     si, a
mov     ax,[si]
mov     dx,[si+2]

此后DX:AX包含双字。

  

另外,如果有人知道,请发表评论:与此问题相关,   我想你不能把dword直接加载到像我这样的两个寄存器中   用其他词来思考(lea dx, a永远不会把dword带入   DX:AX或其他lea ax, a永远不会将dword带入AX:DX)。如果   我错了,请纠正我。

你说lea dx, a永远不会把dword带入DX:AX是正确的,但原因是LEA不会将存储在地址中的数据视为地址本身。
lea dx, alea ax, a都将 a 标签的地址放在寄存器中。

  

我猜你不能把dword直接加载到像我这样的两个寄存器中   认为

在8086程序集中实际上存在2个指令就是这样! LDS和LES。我将使用后者向您展示如何使用它。

lea     si, a
push    es
les     ax,[si]
mov     dx,es
pop     es

此后DX:AX包含dword。

这显然比两个单独的负载慢得多,但对于同一CPU上的中断而言是原子的。 (即如果您正在读取可被中断处理程序修改的双字,则两个单独的mov加载之间的中断可能会导致“撕裂”,但les可能发生在中断之前或之后)

答案 2 :(得分:0)

此代码显示如何将值放入' b'基于' a'中的值, 预计得到b dd 12345678h,9A000000h

 .model small
.stack  100h
.data
a   db 12h, 34h, 56h, 78h, 9Ah
b   dd 2 dup(?)

.code
main    proc
mov  ax, @data
mov ds, ax

mov cx, 4
mov si, offset a
mov di, offset b+3

m1: mov al, [si]
mov [di], al
inc si
dec di
loop    m1

mov si, offset a+4
mov di, offset b+7
mov al, [si]
mov [di], al
dec di
mov byte ptr [di], 0
dec di
mov byte ptr [di], 0
dec di
mov byte ptr [di], 0

mov ax, 4c00h
int 21h
main    endp
end main

答案 3 :(得分:0)

首先,您最大的问题是您在b中寻找的价值与a不同。 x86是little-endian,memcpyab(或者任何其他一次一个字节的副本没有字节交换)实际上会产生:

a   db 12h, 34h, 56h, 78h,   9Ah, 0,0,0  ; added padding 

b   dd 78563412h,            0000009Ah

你的b dd 12345678h, 9A000000h有第一个双字尾交换,a的第5个字节是b中第二个双字的MSB,而不是LSB。

从a到b复制5个字节会使b的最后3个字节保持未初始化状态。 (在Unix中,.bss空间是零初始化的。我假设这发生在MASM / TASM中的dup(?)空间,但如果没有,那么之前的垃圾仍然存在。)

如果从a到b复制8个字节,9A之后的三个字节将从b的开头读取,如果它们最终位于同一部分(而不是b进入bss。也许这就是你在答案中用org指令分隔它们的原因。

如果您没有任何特殊原因想要同时复制dword,那么在8086代码中,您应该只使用rep movsw或普通mov指令,例如

mov   ax, [a]          ; If your addresses are static, might as well just use
mov   dx, [a+2]        ; absolute addressing, esp in 16bit code where it's only 2B


mov   [b], ax
mov   [b+2], dx

请注意,使用sidi的循环仅将它们递增1,但您加载/存储两个字节。未对齐的重叠加载/存储工作,但您正在做多余的工作。

对于您的情况,您需要复制5个字节。您可以将rep movsbcx=5一起使用。 8086当然不支持movsdmovsqrep启动开销会使小副本效率低下。

如果您确实关心同时进行两次加载,例如来自中断处理程序可以修改的dword:

在单核CPU上,我们不必担心其他并发线程会修改内存。但是,中断(可能触发上下文切换到另一个线程)可能会在任意两条指令之间到达,但不是在单条指令的中间。 (这是单核原子性和多核之间的巨大差异:在多核上)。

所以, if 你正在加载一个可以异步修改的dword(例如通过中断处理程序),并且你想要同时加载它的两半,你需要得到两半用一条指令。

如果您只编写没有中断处理程序的普通单线程程序,请不要使用它。

一种方法是使用Sep Roland的les技巧(参见他的回答),但是ES暂时设置为奇怪的东西,这可能是一个问题,具体取决于你的中断处理程序。

另一种方法是使用x87 FPU(不保证在8086上存在),但您可以使用它来复制32位或64位块。 e.g。

fild   dword ptr [a]    ; load 32bits as an integer
fistp  dword ptr [d]    ; store as the same integer
; also works with qword ptr

; or store to the stack and then load into dx:ax with two mov instructions
; your own stack memory is private, so you don't need atomic ops there

x87的内部80位FP格式可以精确地表示每个64位整数,因此这适用于任何可能的位模式。 (fld / fstp不会,因为fld需要有效的IEEE双精度浮点表示,与fild不同。)

即使在8086,它对于中断也是原子的。 fild dword对于486及更高版本的硬件上的对齐加载是原子的。

gcc actually uses this以32位模式实现C ++ 11 std::atomic<uint64_t>加载/存储(因为ISA保证naturally-aligned loads/stores of 64-bit and smaller values are atomic, on P5 and later)。

当SSE2不可用时,gcc习惯于bounce std::atomic<double> values around with fild/fstp,但在我报告之后,这已得到修复。 (我在回答Deoptimizing a program for the pipeline in Intel Sandybridge-family CPUs

时注意到了这个问题

有关其他有用的技巧,请参阅Agner Fog's Optimizing Assembly guide。 (还有标签wiki)。

答案 4 :(得分:-1)

我想我找到了自己的答案。

这是我的代码:

assume cs:code, ds:data
data segment
    a db 12h, 34h, 56h, 78h, 9Ah, 0BCh
    org 20h                             ; make sure I'm not overwrittin
    d dd 2 dup(11111111h)               ; this will be overwritten
data ends

code segment
start:
    mov ax, data
    mov ds, ax


    lea si, a
    lea di, d
    mov cx, 4

    repeta:
    mov dx, [si]
    inc si
    inc si

    mov [di], dx
    inc di
    inc di

    loop repeta


    mov ax, 4C00h
    int 21h
code ends
end start

这很有效,您可以在数据段中清楚地看到,DS中将覆盖值为11111111h的双字,使其看起来像这样(十六进制):12 34 56 78 9A 0BC 0BC 00 00

要注意:如果数据已经安排在DS中,那么您不必担心小端,因为当您拿到它时,您可以将其带入&#34;反向订单&#34;,但一旦回复,它就会回到原来的形式(12 34而不是34 12)。有了这个说法,你可以轻松地做到 mov dx,[si]

此外,如果有人知道,请注意:与此问题相关,我猜你不能像我想的那样将dword直接加载到两个寄存器中,换句话说(lea dx,a永远不会把dword带入DX:AX,或其他lea ax,a永远不会把dword带入AX:DX)。如果我错了,请纠正我。