2017-02-20 28 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 (нижнее слово).

Чтобы умножить si на 4 вам придется либо сделать:

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 процессоров add bx,1 был быстрее, но на современных x86 inc снова отлично).

mov al,byte ptr a[si]+1 очень странный синтаксис, я предпочитаю хранить вещи «как Intel», например, простые. mov al,byte ptr [si + a + 1]. Это не массив C, он действительно загружает значение из памяти из адреса внутри скобок. Мимик синтаксис C-массива, вероятно, просто смутит вас с течением времени. Также можно удалить byte ptr, так как al уже определяет ширину данных (если вы не используете какой-либо MASM, который применяет это к массиву dd, но я не хочу прикоснуться к этому материалу Microsoft с десятифутовым полюсом).

То же самое касается 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: нет шкалы (умножить на 1/2/4/8 части «индекс») в режиме 16b, возможный пример [si*4] не будет работать.


Нового вариант хранение байт из наиболее значимых двойных слова байт (т.е. реверсирование мало-младшему схемы x86 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 против mul не выполняют то, что вы ожидаете, а оставшийся код работает с неправильными индексами. Программирование в сборке без отладчика похоже на попытку собрать робота с завязанными глазами.

+0

Большое спасибо за все ваши советы, я новичок в этом языке программирования, и мы работаем только с 16b, могу ли я спросить вас, как отменить результат для каждого номера, как это должно быть (12,34,56,78,1A, 2B, 3C, 78) – Esan

+0

Я не уверен, что понимаю вас. Вы имеете в виду порядок от большинства значительных байтов? Затем вам придется работать со структурой из 4 байтов, а не только для копирования ненулевых значений, но и отменить порядок каждого из 4 байтов. Как и все в Assembly, у него есть много способов, как закодировать его. – Ped7g

+0

@Esan: объявление «много возможных способов»: если вы понимаете свои данные и какие вычисления вы хотите достичь, вы просто разбиваете это вычисление на более простые шаги, пока шаги не будут настолько простыми, что они будут напоминать инструкции CPU. Затем вы пишете код, который делает это. Ассамблея не об обучении, какая инструкция выполняет «обратную строку»/etc ... Вы просто узнаете, что делает инструкция с регистрами и памятью. И тогда вам нужно узнать, как можно хранить различные данные. И выяснить формулу, которая рассчитывает ваш желаемый результат. Тогда вы просто напишите, что вычисление в инструкциях CPU = выполнено. – Ped7g