먼저 데이터를 이해하면 x86 메모리는 바이트 단위로 주소 지정이 가능합니다. 다른 사람이 메모리 내용을보고 있고 논리 구조에 대해 알지 못하는 경우 어떤 종류의 논리 구조를 사용하여 데이터를 메모리에 쓰는지는 중요하지 않으며 바이트 만 표시됩니다.
78 67 34 12 3C 2B 1A 00 78 00 00 00
단지로 그것을 바이트를 복사, 당신도 더블 워드로 작업 할 필요가 없습니다 제로를 제거하여 이러한 배열을 응축 : 12 (3 * 4) 바이트로
a dd 12345678h,1A2B3Ch,78h
그래서이 컴파일 바이트 (자의적으로 더블 워드 배열을 의미한다는 사실을 자발적으로 알리지 않음)로 0 값을 건너 뜁니다.
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 증식,
mov ax,si ; ax = si
mul [pat] ; dx:ax = ax * word(4)
또는 단순히를 컴퓨터 비트와 함께 작업하는 것을 이용하고, 정수 값의 바이너리 인코딩 :
당신이 가진 것 중 하나를 할 4
에 의해 si
를 곱하려면 두 개의 "위"(왼쪽) 위치 값에있는 비트 값.
shl si,2 ; si *= 4 (truncated to 16 bit value)
하지만 원래 si
("인덱스")를 파괴하므로 대신 사람들이 일반적으로 루프 증가를 조절하고의. 대신 add si,4
을 입력하면 si = 0
으로 시작합니다. 더 이상 곱셈은 필요 없습니다.
add bx,1
은 (86 CPU를 일부 세대에 add bx,1
빠른 있었지만, 그러나 현대 86에 inc
다시 괜찮습니다) 내 눈을, 나는 인간의 조립에 inc bx
를 선호 아파요.
mov al,byte ptr a[si]+1
은 매우 이상한 구문입니다. "Intel과 같은"것들을 간단하게 유지하는 것을 선호합니다. mov al,byte ptr [si + a + 1]
. 그것은 C 배열이 아니며, 대괄호 안의 주소로부터 메모리의 값을로드하고 있습니다. 아마도 C-array 구문을 모방하면 시간이 지남에 따라 혼란 스러울 것입니다. 또한 byte ptr
은 al
이 데이터 너비를 이미 정의했기 때문에 제거 될 수 있습니다 (배열을 dd
에 적용하는 일부 MASM을 사용하지 않는 한 10 피트 극으로 해당 마이크로 소프트 물건을 터치하고 싶지는 않습니다).
동일한 내용은 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
= 별도로 증식 할 필요가 없습니다.
EDIT : 사용 가능한 16b 모드에서 축척이 없습니다 ("색인"부분의 1/2/4/8을 곱함) 가능한 경우 [si*4]
이 작동하지 않습니다.
(즉, 86 DWORD의 작은 엔디안 방식을 반전.) 가장 중요한 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
방식으로 작성된 것은 짧고 간단하지가 움직이지 않도록 퍼포먼스를 위해 (그리고 나는 당신이 언어에 정말로 편안해질 때까지 같은 것을 목표로 강하게 권합니다. 전문가의 속임수없이 간단한 방법으로 작성하더라도 초보자에게는 충분히 어렵습니다.)
그런데, 당신을 위해 작동하는 일부 디버거를 찾아야합니다. 따라서 명령에 따라 명령을 수행하고 "n"의 결과 값이 추가되는 방식과 그 이유를 관찰 할 수 있습니다. 또는 bx
+ si
대 mul
이 예상 한대로 수행되지 않고 나머지 코드가 잘못된 색인에서 작동하고 있음을 조만간 알게 될 것입니다. 디버거없이 어셈블리에서 프로그래밍은 눈가리개가있는 로봇을 조립하려고하는 것과 같습니다.
그래서'1A2B3Ch'에서 3 바이트만을 대상 배열에 복사하고 싶습니까? – Ped7g
예 3C, 2B, 1A – Esan