Прежде всего, всегда понимайте свои данные, память 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
не выполняют то, что вы ожидаете, а оставшийся код работает с неправильными индексами. Программирование в сборке без отладчика похоже на попытку собрать робота с завязанными глазами.
Итак, из '1A2B3Ch' вы хотите скопировать только 3 байта в целевой массив, не так ли? – Ped7g
да, это будет 3C, 2B, 1A – Esan