首先,總是理解你的數據,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],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字節字節(即扭轉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
不符合您的期望,而其餘代碼在錯誤的索引上運行。在沒有調試器的情況下進行程序集編程就像試圖組裝一個蒙上眼睛的機器人。
所以從'1A2B3Ch'要複製只有3個字節到目標數組,對不對? – Ped7g
是這將是3C,2B,1A – Esan