我有一个问题要解决,我到了我不知道还能尝试什么的地步。所以我有这个数据段:
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,我还能够将它作为双字保存在数据段中。
任何建议都将不胜感激。
答案 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, a
和lea 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,memcpy
从a
到b
(或者任何其他一次一个字节的副本没有字节交换)实际上会产生:
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
请注意,使用si
和di
的循环仅将它们递增1,但您加载/存储两个字节。未对齐的重叠加载/存储工作,但您正在做多余的工作。
对于您的情况,您需要复制5个字节。您可以将rep movsb
与cx=5
一起使用。 8086当然不支持movsd
或movsq
,rep
启动开销会使小副本效率低下。
在单核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。 (还有x86标签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)。如果我错了,请纠正我。