2017-02-20 113 views
1

你好我想給定的數組,它是雙字到數組必須是字節劃分,將數據移動到字節數組

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

回答

3

首先,總是理解你的數據,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