как суммировать две матрицы элемент за элементом? - PullRequest
1 голос
/ 11 мая 2019

Я новичок в сборке, и я был бы признателен, если бы вы помогли мне с фрагментом кода о том, как добавить две матрицы и переместить результат в другую матрицу на языке ассемблера x86-32bit. Матрицы объявлены как 1d массивы.

n dd 9
A dd 1,2,3,4,5,6,7,8,9
B dd 2,0,4,5,6,7,0,1,3
sum dd dup 9(0)

Я попробовал приведенный ниже код, но он работает только для матриц, объявленных таким образом, и мне понадобится один для работы с матрицами, объявленными как массив 1s.

A db 1,2,3
   db 4,5,6
B db 7,8,9
   db 10,11,12
.code
start:
mov eax , 0 
mov esi, 0 
mov ebx, 0 

add al, A[ebx][esi]
add al, B[ebx][esi]
mov A[ebx][esi], al
mov al, 0
inc esi
add al, A[ebx][esi]
add al, B[ebx][esi]
mov A[ebx][esi], al
mov al, 0
inc esi
add al, A[ebx][esi]
add al, B[ebx][esi]
mov A[ebx][esi], al

mov al, 0
mov esi, 0
add ebx, 3 
add al, A[ebx][esi]
add al, B[ebx][esi]
mov A[ebx][esi], al
mov al, 0
inc esi
add al, A[ebx][esi]
add al, B[ebx][esi]
mov A[ebx][esi], al
mov al, 0
inc esi
add al, A[ebx][esi]
add al, B[ebx][esi]
mov A[ebx][esi], al
 push 0
call exit
end start

Ответы [ 2 ]

3 голосов
/ 11 мая 2019

Матрицы, которые являются смежными в памяти (например, C 2D-массивы), эквивалентны 1D-массивам, просто rows * cols элементов в строке в памяти, независимо от того, какой синтаксис asm вы используете для их размещения там. Единственное, что делает их двумерной матрицей, это то, как вы их индексируете, например,
flat_index row * width + col.

(И для цикла по нему вы, конечно, можете сделать row_offset += width; это add ebx, 3 в вашем матричном коде 2x3 байта.)

При добавлении матриц для каждого элемента вообще не нужно заботиться об их размерах, это та же проблема, что и при добавлении массива для каждого элемента . Так что просто зациклите индекс или указатель на каждый массив и добавьте.

Тогда вам не нужны два отдельных индекса для строки и столбца, которые просто усложнят ваш код, или (для таких небольших измерений) почти стоит развернуть, как вы делали во второй раз.

(Или, если ваш процессор поддерживает SSE2, вы можете сделать это 4 dwords за раз с paddd.)


Это не особенное:

A db 1,2,3
   db 4,5,6

Объявление таким образом с двумя отдельными db строками для отдельных строк эквивалентно одному длинному массиву. Для MASM это может изменить SIZEOF A (вы, вероятно, получите только первую строку, которая на самом деле находится на той же строке, что и метка A), но больше ничего не изменится.

Причина, по которой код, который идет с ним, не будет работать для вашего случая, заключается в том, что он использует байтовые элементы и имеет другой размер матрицы (9 элементов вместо 6). Ничего общего с тем, как объявлено.

Вы могли бы полностью развернуть цикл и выполнить несколько сложных операций перемещения и добавления целочисленных регистров, если хотите, но в этом нет никакого смысла.


A[ebx][esi] неверный синтаксис в большинстве (?) Ассемблеров. Если он собирается, я предполагаю, что это означает A[ebx + esi]. Это был бы нормальный способ написать это.

Он не выполняет матричную индексацию для вас, поэтому вам все еще нужно использовать смещения байтов для перехода к следующей строке.

Вы можете использовать такие вещи, как A[ebx*4 + esi], если число столбцов является постоянной величиной времени сборки 2 (в частности, 1, 2, 4 или 8; режимы адресации x86 имеют 2-битный счетчик сдвига для индекса) .

Обычно в asm-синтаксисе вы пишете [base + index*scale], но ассемблерам Intel-синтаксиса на самом деле все равно, в каком порядке появляются компоненты режима адресации. Так что, если вам нравится думать в C, где левый индекс проходит по целому строк для выбора столбца, а затем запись его в виде [A + ebx*4 + esi] имеет смысл, если у вас была матрица uint8_t [2][4], поэтому шаг от элемента к следующей строке вниз равен 4.

Для элемента dword (как в вашем первом примере) вместо байтовых элементов (например, вашего 2-го) вам нужно масштабировать свои индексы или уже на 4 (например, A[ebx*4] или сделать их смещение байтов с помощью add esi, 4 вместо inc esi.

1 голос
/ 11 мая 2019

, если вы хотите суммировать любые 2 массива A и B, записав вывод в массив C, который вы можете указать как входным параметром вашей функции является размер матрицы A, который равен размеру матрицы B и матрицы C: MAT_SIZE. Для вычисления MAT_SIZE вам просто нужно умножить количество строк на число столбцов (одно- и двухмерные матрицы). Если количество индексов> 2, то MAT_SIZE будет произведением всех (максимум индексов + 1) (при условии, что 0 указывает на первый элемент и n-1 указывает на последний). Полагаю, матрицы содержат ячейки размером 1 байт.

;Indexes Max-Index+1 MAT_Size 
;      3           8    8*8*8

;ROUTINE @@MAT

;INPUT: EAX: First matrix pointer. Unaltered
;       EDX: Second matrix pointer. Unaltered.
;       EBX: Target matrix pointer. Unaltered.
;       ECX: MAT_Size. Unaltered.

@@MAt:PUSH  EBP           ; Save EBP.
      MOV   EBP,EBX       ; Copy EBX into EBP.

      JECXZ @@00          ; If matrix is empty, terminate sub-routine.

      PUSH  ECX           ; Save ECX.

 @@01:Mov   BL,[EAX]      ; Load in BL first byte.
      Add   BL,[EDX]      ; Add to BL second byte.
      MOV   [EBP],BL      ; Save result in [EBP].

      INC   EAX           ; Increase First matrix pointer.
      INC   EDX           ; Increase Second matrix pointer.
      INC   EBP           ; Increase Target matrix pointer.

      LOOP  @@01          ; If target matrix is full, end.

      POP   ECX           ; Resume MAT_SIZE

      SUB   EAX,ECX       ; Adjust First matrix pointer.
      SUB   EDX,ECX       ; Adjust Second matrix pointer.

      MOV   EBX,EBP
      SUB   EBX,ECX       ; Adjust Target matrix pointer.

 @@00:POP   EBP           ; Resume EBP.

      RET                 ; Return from sub-routine.

В реальном режиме x86, предположим, что матрица находится в сегменте данных, вы можете аналогичным образом написать:

;Indexes Max-Index+1 MAT_Size 
;      3           8    8*8*8

;ROUTINE @@MT2

;INPUT: SI: First matrix offset. Unaltered
;       DI: Second matrix offset. Unaltered.
;       BX: Target matrix offset. Unaltered.
;       CX: MAT_Size. Unaltered.

@@MT2:PUSH  BP            ; Save BP.
      MOV   BP,BX         ; Copy BX into BP.

      JCXZ  @@00          ; If matrix is empty, terminate sub-routine.

      PUSH  CX            ; Save ECX.

 @@01:Mov   BL,DS:[SI]    ; Load in BL first byte.
      Add   BL,DS:[DI]    ; Add to BL second byte.
      MOV   DS:[BP],BL    ; Save result in [BP].

      INC   SI            ; Increase First matrix offset.
      INC   DI            ; Increase Second matrix offset.
      INC   BP            ; Increase Target matrix offset.

      LOOP  @@01          ; If target matrix is full, end.

      POP   CX            ; Resume MAT_SIZE

      SUB   SI,CX         ; Adjust First matrix offset.
      SUB   DI,CX         ; Adjust Second matrix offset.

      MOV   BX,BP
      SUB   BX,CX         ; Adjust Target matrix pointer.

 @@00:POP   BP            ; Resume BP.

      RET                 ; Return from sub-routine.
...