Assembly 将数据从双字数组移动到字节数组

Assembly 将数据从双字数组移动到字节数组,assembly,x86-16,Assembly,X86 16,您好,我正在尝试将给定的双字数组除以必须为字节的数组 a dd 12345678h,1A2B3Ch,78h, ;given array 我只想加上不等于0的数字 正如您所看到的,第一个数字是ok的,第二个数字的末尾有两个零 001A2B3Ch,第三个有六个0 00000078h 我写了一个代码来实现这一点,对于第一个数字,它使用78,56,34,12,28,2B的ASCII码添加到数组字符中,对于最后两个数字,它必须看起来像(78,56,34,12,3C,2B,1A,78),这是不正确的。我不

您好,我正在尝试将给定的双字数组除以必须为字节的数组

a dd 12345678h,1A2B3Ch,78h, ;given array
我只想加上不等于0的数字 正如您所看到的,第一个数字是ok的,第二个数字的末尾有两个零 001A2B3Ch,第三个有六个0 00000078h

我写了一个代码来实现这一点,对于第一个数字,它使用78,56,34,12,28,2B的ASCII码添加到数组字符中,对于最后两个数字,它必须看起来像(78,56,34,12,3C,2B,1A,78),这是不正确的。我不知道为什么

assume cs:code, ds:data
data segment
a dd 12345678h,1A2B3Ch,78h ;given array
l equ $-a
l1 equ l/4
zece db 10
pat dw 4
n db l dup(?) ;distination array
data ends

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

    mov cl,l1
    mov ch,0
    mov si,0
    mov ax,0
    mov bx,0

    repeta:
        mov bx,si
        mul pat
        mov al,byte ptr a[si]
        mov n[bx],al
        mov al,byte ptr a[si]+1
        add bx,1
        mov n[bx],al
        mov al,byte ptr a[si]+2
        add bx,1
        mov n[bx],al
        mov al,byte ptr a[si]+3
        add bx,1
        mov n[bx],al
        inc si
    loop repeta

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

首先,要始终了解您的数据,x86内存可以通过字节寻址。无论您使用什么样的逻辑结构将数据写入内存,如果其他人正在查看内存内容,而他们不知道您的逻辑结构,他们只看到字节

a dd 12345678h,1A2B3Ch,78h
因此它编译为12(3*4)字节:

要通过删除零来压缩这样的数组,您甚至不需要使用双字,只需逐字节复制它(自愿放弃您原来认为它是双字数组的知识),跳过零值

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

    lea     si,[a]      ; original array offset
    lea     di,[n]      ; destination array offset
    mov     cx,l        ; byte (!) length of original array

    repeta:
        ; load single byte from original array
        mov     al,[si]
        inc     si
        ; skip zeroes
        test    al,al
        jz      skipping_zero
        ; store non-zero to destination
        mov     [di],al
        inc     di
    skipping_zero:
        loop    repeta

    ; fill remaining bytes of destination with zeroes - init
    xor     al,al
    lea     si,[n+l]    ; end() offset of "n"
    ; jump first to test, so filling is skipped when no zero
    jmp     fill_remaining_test

    fill_remaining_loop:
        ; clear one more byte in destination
        mov     [di],al
        inc     di
    fill_remaining_test:
        ; test if some more bytes are to be cleared
        cmp     di,si       ; current offset < end() offset
        jb      fill_remaining_loop

    ; exit back to DOS
    mov ax,4C00h
    int 21h

code ends
end start
如您所见,
mul
既不使用
bx
,也不使用
si
,而是将其结果分成32位值,分为
dx
(大写)和
ax
(小写)

要将
si
乘以
4
,您必须执行以下任一操作:

    mov     ax,si   ; ax = si
    mul     [pat]   ; dx:ax = ax * word(4)
或者简单地利用计算机处理位和整数值的二进制编码,因此要乘以4,只需将值中的位值向上移动两个位置(左)

但这会破坏原始的
si
(“索引”),因此人们通常不这样做,而是调整循环增量。您将从
si=0
开始,但您将执行
add si,4
而不是
inc si
。不再需要了


add bx,1
伤了我的眼睛,我更喜欢在人工汇编中使用
inc bx
(虽然在某些代的x86 CPU上,
add bx,1
速度更快,但在现代x86上,
inc
又很好)

mov al,byte ptr a[si]+1
是一种非常奇怪的语法,我更喜欢保持“英特尔式”的简单,即
mov al,byte ptr[si+a+1]
。它不是C数组,它实际上是从括号内的地址从内存中加载值。随着时间的推移,模仿C-array语法可能会让你感到困惑。另外,
byte ptr
也可以从中删除,因为
al
已经定义了数据宽度(除非您使用的是一些MASM,它在
dd
数组上强制执行此操作,但我不想用十英尺长的杆接触微软的东西)

这同样适用于
mov n[bx],al
=
mov[n+bx],al
mov[bx+n],al
,以代码中更有意义的为准

但总的来说,在循环内使用索引有点不寻常,通常您希望在init部分中在循环之前将所有索引转换为地址,并在循环内使用最终指针而不进行任何计算(按元素大小递增,即对于双字,
add si,4
)。那么你就不需要做任何索引乘法了

特别是在16位模式下,寻址模式非常有限,在32/64b模式下,您至少可以将一个寄存器与通用大小(1、2、4、8)相乘,即,
mov[n+ebx*4],eax
=无需单独相乘

编辑:在16b模式下没有刻度(乘以“索引”部分的1/2/4/8),可能的示例
[si*4]
不起作用


从最重要的dword字节存储字节的新变体(即,颠倒x86 dword的小端方案):

用一种简洁的方式来写,而不是为了表现(我强烈建议你也要这样做,直到你对这门语言感到非常舒服为止,对于初学者来说,即使是用简单的方式写,没有任何专家的诡计,也已经够难的了)


顺便说一句,您应该找到一些适合您的调试器,这样您就可以一条一条地逐条执行指令,并观察“n”中的结果值是如何添加的,以及为什么添加。或者您可能会很快注意到,
bx
+
si
vs
mul
没有按照您的期望执行操作,剩余的代码在错误的索引上运行。在没有调试器的汇编程序中编程就像试图蒙住眼睛组装机器人。

因此,从
1A2B3Ch
开始,您只想将3个字节复制到目标数组中,对吗?是的,它将是3C、2B、1A非常感谢您的所有建议,我对这种编程语言不熟悉,我们只使用16b,请允许我问你如何颠倒每个数字的结果,就像它必须是(12,34,56,78,1A,2B,3C,78)一样。我不确定我是否理解你的意思。你是说从最高有效字节开始的顺序?然后,您必须使用4字节的结构,不仅要复制非零值,还要反转每个4字节的顺序。与汇编中的所有内容一样,它有许多可能的编码方式。@Esan:ad“许多可能的方式”:如果您了解您的数据以及您想要实现的计算,您只需将该计算分解为更简单的步骤,直到这些步骤如此简单,以至于它们类似于CPU指令。然后编写代码来实现这一点。汇编不是学习哪条指令“反转字符串”等。。。您只需了解指令对寄存器和内存的作用。然后,您必须了解如何存储各种数据。并找出计算您期望结果的公式。然后你就用CPU指令=完成来写计算。@Esan:另外,如果你在学习汇编,我最近确实很好奇地回答了某人的问题,我认为这个解决方案足够简单,你可以理解它(通过调试器和指令参考指南来验证它们是如何工作的),但你可能会感兴趣地看到这个问题是如何构建的(OP没有停止寻找第一个解决方案)
    mov     bx,si   ; bx = si (index into array?)
    mul     pat     ; dx:ax = ax * word(4)
    mov     ax,si   ; ax = si
    mul     [pat]   ; dx:ax = ax * word(4)
    shl     si,2    ; si *= 4 (truncated to 16 bit value)
code segment
start:
    mov     ax,data
    mov     ds,ax
    lea     si,[a]      ; original array offset
    lea     di,[n]      ; destination array offset
    mov     cx,l1       ; element-length of original array

    repeta:
        ; load four bytes in MSB-first order from original array
        ; and store only non-zero bytes to destination
        mov     al,[si+3]
        call    storeNonZeroAL
        mov     al,[si+2]
        call    storeNonZeroAL
        mov     al,[si+1]
        call    storeNonZeroAL
        mov     al,[si]
        call    storeNonZeroAL
        ; advance source pointer to next dword in array
        add     si,4
        loop    repeta

    ; Different ending variant, does NOT zero remaining bytes
    ; but calculates how many bytes in "n" are set => into CX:
    lea       cx,[n]      ; destination begin() offset
    sub       cx,di
    neg       cx          ; cx = number of written bytes into "n"

    ; exit back to DOS
    mov ax,4C00h
    int 21h

; helper function to store non-zero AL into [di] array
storeNonZeroAL:
    test    al,al
    jz      ignoreZeroAL
    mov     [di],al
    inc     di
ignoreZeroAL:
    ret

code ends
end start