将数据移动到字节数组

问题描述:

你好我想给定的数组,它是双字到数组必须是字节划分,将数据移动到字节数组

a dd 12345678h,1A2B3Ch,78h, ;given array 

,我想只添加的数量,是不是等于0 正如你看到的第一个数字是确定的第二端部有 001A2B3Ch拖零,而第三个具有六个零00000078h

我写了一个代码来做到这一点,对于第一个数字它的工作原理把它添加排列ASCII码为78,56,34,12,28,2B的字符,最后两个数字不正确(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 
+0

所以从'1A2B3Ch'要复制只有3个字节到目标数组,对不对? – Ped7g

+0

是这将是3C,2B,1A – Esan

首先,总是理解你的数据,x86内存可以按字节寻址。使用什么样的逻辑结构将数据写入内存,如果其他人正在观看内存内容,并且他们不知道自己的逻辑结构,他们只能看到字节。

a dd 12345678h,1A2B3Ch,78h 

所以编译如下12(3 * 4)字节:

78 67 34 12 3C 2B 1A 00 78 00 00 00 

要通过消除零凝聚这样的数组,你甚至都不需要用双字去上班,就通过复制字节字节(自愿放弃你最初的双字数组意思),跳过零值。

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,特别是约两个值的力量放大:

mov  bx,si ; bx = si (index into array?) 
    mul  pat  ; dx:ax = ax * word(4) 

正如你所看到的,mul时未使用bx,或si,并且它导致成32位的值,分裂分成dx(上位字)和ax(下位字)。

通过4要乘以si你要么做:

mov  ax,si ; ax = si 
    mul  [pat] ; dx:ax = ax * word(4) 

或者干脆利用该计算机与位工作和整数值的二进制编码,所以由4乘你只需要移位值由两个位置“上”(左)。

shl  si,2 ; si *= 4 (truncated to 16 bit value) 

但是,这会破坏原来的si(“指数”),所以不是这样的人通常调整循环增量。您将从si = 0开始,而不是inc si,您应该做add si,4。没有多余的需要了。


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语言的语法可能会随着时间的推移而迷惑你。另外,byte ptr可以从被删除,因为al定义数据宽度已经(除非你使用的是一些MASM其强制执行此在dd数组,但我不想触摸到了微软的东西有十英尺极)。

同样适用于mov n[bx],al = mov [n + bx],almov [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字节字节(即扭转86 DWORD的小端方案):书面的方式

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 

保持它简短,不为了表现(我强烈建议你的目标是相同的,直到你对这门语言感到非常满意为止,即使没有任何专家诡计的简单写法,初学者也很难。

BTW,你应该找到一些调试器,它为你的作品,那么将有可能为你的指令一步的指示,看如何“N”被添加在产生的值,以及为什么。或者您可能会很快注意到bx + si vs mul不符合您的期望,而其余代码在错误的索引上运行。在没有调试器的情况下进行程序集编程就像试图组装一个蒙上眼睛的机器人。

+0

非常感谢您为您的所有意见,我是新与此编程语言,我们只是16b的工作,我可以问你如何扭转其结果为每一个数字,如它必须是(12,34,56,78,1A,2B,3C,78) – Esan

+0

我不知道我理解你。你的意思是从最重要的字节开始的顺序?然后你将不得不使用4字节的结构,不仅要复制非零值,还要颠倒每个4字节的顺序。与Assembly中的所有内容一样,它有许多可能的编码方式。 – Ped7g

+0

@Esan:ad“许多可能的方式”:如果您了解数据以及要实现什么计算,只需简单地将计算过程分解为简单的步骤,直到步骤非常简单,以至于它们与CPU指令相似。然后你写这些代码。汇编不是要了解哪个指令“反转字符串”/等等......你只要了解指令对寄存器和内存的作用。然后你必须学习如何存储各种数据。并找出计算你想要的结果的公式。然后你只需在CPU指令= done中写入该计算。 – Ped7g