2017-02-20 1 views
1

안녕하세요 바이트해야 배열에 더블 워드를하고있는 지정된 배열을 분할하는 것을 시도하고,이동 데이터 배열

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

를 바이트하고 있지 않은지 난 단지 수를 추가 할 동일 0 당신은 첫 번째 숫자는 괜찮 보는 바와 같이 두 번째는 끝 001A2B3Ch에서 견인 0을 가지고 있으며, 세 번째는 그것이 추가 작동

내가 첫 번째 숫자에 대해이 작업을 수행하는 코드를 작성 00000078h 여섯 0을 가지고 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 메모리는 바이트 단위로 주소 지정이 가능합니다. 다른 사람이 메모리 내용을보고 있고 논리 구조에 대해 알지 못하는 경우 어떤 종류의 논리 구조를 사용하여 데이터를 메모리에 쓰는지는 중요하지 않으며 바이트 만 표시됩니다.

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 ptral이 데이터 너비를 이미 정의했기 때문에 제거 될 수 있습니다 (배열을 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 + simul이 예상 한대로 수행되지 않고 나머지 코드가 잘못된 색인에서 작동하고 있음을 조만간 알게 될 것입니다. 디버거없이 어셈블리에서 프로그래밍은 눈가리개가있는 로봇을 조립하려고하는 것과 같습니다.

+0

모든 조언을 주셔서 대단히 감사합니다. 저는이 프로그래밍 언어를 처음 사용하며 16b로만 작업합니다. 모든 숫자에 대해 결과를 역으로 변환하는 방법을 묻습니다. (12,34,56,78,1A, 2B, 3C, 78) – Esan

+0

잘 모르겠습니다. 가장 중요한 바이트의 순서를 의미합니까? 그런 다음 0이 아닌 값을 복사하는 것뿐만 아니라 각 4 바이트의 순서를 반대로 4 바이트의 구조로 작업해야합니다. Assembly의 모든 것과 마찬가지로, 코드를 작성하는 방법에는 여러 가지가 있습니다. – Ped7g

+0

@Esan : ad "여러 가지 가능한 방법": 데이터를 이해하고 달성하고자하는 계산이 있다면 간단한 단계가 될 때까지 간단하게 계산을 중단하고 CPU 지침과 유사하게 만듭니다. 그런 다음 코드를 작성합니다. 어셈블리는 어떤 명령어가 "역 문자열"/ etc ...인지 배우는 것이 아닙니다. 명령어가 레지스터와 메모리로 수행하는 작업을 배우게됩니다. 그리고 다양한 데이터를 저장하는 방법을 배워야합니다. 그리고 원하는 결과를 계산하는 수식을 계산하십시오. 그런 다음 CPU 명령어 = done에 해당 계산을 씁니다. – Ped7g