用汇编添加两个矩阵
adding two matrices with assembly
我正在编写一个汇编程序,它将采用两个 3X6 矩阵,将它们相加,然后将结果放入一个新矩阵中。我 运行 遇到了一些问题。
问题是它只为矩阵 1 输出 2-16,为矩阵 2 输出 20-34。我似乎无法弄清楚如何让它使用整个范围。
%include "io.mac"
.STACK 100H
.DATA
NO_ROWS EQU 3
NO_COLUMNS EQU 5
SIZE_OF_ROW EQU 5
SIZE_OF_ENTRY EQU 2
matrix1
dw 1,2,3,4,5,6
dw 7,8,9,10,11,12
dw 13,14,15,16,17,18
matrix2
dw 19,20,21,22,23,24
dw 25,26,27,28,29,30
dw 31,32,33,34,35,36
matrix3 TIMES 40 DW 0
.CODE
.STARTUP
mov CX, NO_ROWS ; set outer loop count
L1: ; begin the outer loop
push CX ; save outer loop count
mov BX, CX ;move outer loop count into EAX
sub bx, 1
mov CX, 5 ; set inner loop count
L2:
; use formula arrayName + (elements_in_a_row*row_number + element) *size_of_entry
mov si, 0
mov di, 0
mov dx, 0
mov si, matrix1
mov di, matrix2
mov ax, SIZE_OF_ROW
mul bx ;multiply ax by which row you're on.
add ax, cx ;then add column count, for which column you're on.
shl ax, 1 ;then multiply it by the size of each entry.
add si, ax ; so that the index points to the element.
add di, ax
mov ax, [si]
add ax, [di]
mov [matrix3], ax
PutInt [matrix3]
nwln
add word [matrix3], 2
loop L2 ; repeat inner loop
pop CX ;restore outer loop
loop L1 ;repeat outer loop
done:
.EXIT
内部循环 L2
将只为 cx={5, 4, 3, 2, 1}
执行 5 次。外循环将通过 bx={2, 1, 0}
(ok)。因此总共只使用 15 个值。另外 SIZE_OF_ROW
应该是 6,如果矩阵是 3x6(也是 NO_COLUMNS
实际上为什么你需要两个常量覆盖相同的逻辑)。
但总的来说,你只是走错了方向。当您需要特定的矩阵元素("random" 访问)时,需要进行索引计算。
要将两个相同大小的矩阵相加,顺序访问就足够了,因为您需要处理所有元素。
你甚至可以忘记矩阵有多少维度,因为所有三个(源矩阵 A 和 B,以及结果矩阵 C)都具有相同的大小 => 它们具有相同的总元素数。
在你的例子中,矩阵是 3x6 = 18 个元素。每个元素的大小为 word
(2 个字节)。所以每个矩阵将占用 36 个字节(或 18 个字)。您以连续的方式定义了它们(下一行在上一个 = 好的设计结束后立即开始)。
这样的过程将进行矩阵加法:对于相同大小的矩阵,C = A + B:
; Same-size matrices addition (of 16b word elements): C = A + B
; ds:si = A address, ds:bx = B address
; ds:di = C address, cx = total amount of elements
; modifies: all input registers and ax
matrices_add:
mov ax,[si]
add ax,[bx] ; ax = A[i] + B[i]
mov [di],ax ; C[i] = ax
; ++i (actually advancing all three pointers instead of using index)
add si,2
add bx,2
add di,2
; loop until all elements are added
dec cx
jnz matrices_add
ret
现在在你的情况下你可以用这些参数调用它:
NO_ROWS EQU 3
NO_COLUMNS EQU 6 ; fixed
...
mov si,matrix1
mov bx,matrix2
mov di,matrix3
mov cx,(NO_ROWS*NO_COLUMNS)
call matrices_add
; here memory at address matrix3 will contain matrix1+matrix2 elements
...
停止使用控制台输出进行调试,并从 MS 获取一些 DOS 调试器,如 Turbo Debugger 或 CV.EXE(代码视图)。或者使用内置调试器的虚拟机 BOCHS,这是更强大的解决方案,甚至可以在 DOS 运行.
之前调试引导程序。
如果你想显示矩阵,而不是使用单独的代码,以避免在加法过程中产生一些不必要的影响并使调试更容易(例如你可以先尝试显示 matrix1
然后退出回到 DOS,只是为了验证输出例程是否正确:
...
mov si,matrix3 ; pointer to next element
; output 3x6 16b matrix from "si" address
mov dx,NO_ROWS
output_matrix_row:
mov cx,NO_COLUMNS
output_matrix_line:
PutInt [si] ; display next element
PutCh ' ' ; make space between elements
add si,2 ; adjust pointer to point to next element
dec cx
jnz output_matrix_line
nwln ; new line
dec dx
jnz output_matrix_row
...
如你所见,我再次避免了元素计算的 "random-access" 索引,以连续的方式再次访问它们,只需为下一个元素做基本的 add si,2
,不慢 mul
甚至一些复杂的计算。
这就是用汇编编写的代码速度快的原因,您可以优化算法以不执行无用的指令,如果您将通过执行 offset = (y*ROW_SIZE + x)*ELEMENT_SIZE
继续计算每个元素的完整索引,您将以较慢的代码结束比用任何半正经的高级语言(C、Java、C# ...循环遍历连续的偏移量)。
所以在编写任何代码之前,您应该首先确保您完全了解您要计算的内容和原因,并尽可能简化它以避免任何冗余操作。
除非您在调试器中看到单步执行单指令,否则您将很难捕捉到代码的所有问题,正如您在最后 ~4 个问题中很好地演示的那样。我很欣赏你的固执,但你看起来没有足够的天赋在没有调试器的情况下继续进行下去,最终得到一些,并学会使用它并多次检查你的旧问题+其他人的提示,看看你会做多少这次能够复制+完全理解。
我正在编写一个汇编程序,它将采用两个 3X6 矩阵,将它们相加,然后将结果放入一个新矩阵中。我 运行 遇到了一些问题。
问题是它只为矩阵 1 输出 2-16,为矩阵 2 输出 20-34。我似乎无法弄清楚如何让它使用整个范围。
%include "io.mac"
.STACK 100H
.DATA
NO_ROWS EQU 3
NO_COLUMNS EQU 5
SIZE_OF_ROW EQU 5
SIZE_OF_ENTRY EQU 2
matrix1
dw 1,2,3,4,5,6
dw 7,8,9,10,11,12
dw 13,14,15,16,17,18
matrix2
dw 19,20,21,22,23,24
dw 25,26,27,28,29,30
dw 31,32,33,34,35,36
matrix3 TIMES 40 DW 0
.CODE
.STARTUP
mov CX, NO_ROWS ; set outer loop count
L1: ; begin the outer loop
push CX ; save outer loop count
mov BX, CX ;move outer loop count into EAX
sub bx, 1
mov CX, 5 ; set inner loop count
L2:
; use formula arrayName + (elements_in_a_row*row_number + element) *size_of_entry
mov si, 0
mov di, 0
mov dx, 0
mov si, matrix1
mov di, matrix2
mov ax, SIZE_OF_ROW
mul bx ;multiply ax by which row you're on.
add ax, cx ;then add column count, for which column you're on.
shl ax, 1 ;then multiply it by the size of each entry.
add si, ax ; so that the index points to the element.
add di, ax
mov ax, [si]
add ax, [di]
mov [matrix3], ax
PutInt [matrix3]
nwln
add word [matrix3], 2
loop L2 ; repeat inner loop
pop CX ;restore outer loop
loop L1 ;repeat outer loop
done:
.EXIT
内部循环 L2
将只为 cx={5, 4, 3, 2, 1}
执行 5 次。外循环将通过 bx={2, 1, 0}
(ok)。因此总共只使用 15 个值。另外 SIZE_OF_ROW
应该是 6,如果矩阵是 3x6(也是 NO_COLUMNS
实际上为什么你需要两个常量覆盖相同的逻辑)。
但总的来说,你只是走错了方向。当您需要特定的矩阵元素("random" 访问)时,需要进行索引计算。
要将两个相同大小的矩阵相加,顺序访问就足够了,因为您需要处理所有元素。
你甚至可以忘记矩阵有多少维度,因为所有三个(源矩阵 A 和 B,以及结果矩阵 C)都具有相同的大小 => 它们具有相同的总元素数。
在你的例子中,矩阵是 3x6 = 18 个元素。每个元素的大小为 word
(2 个字节)。所以每个矩阵将占用 36 个字节(或 18 个字)。您以连续的方式定义了它们(下一行在上一个 = 好的设计结束后立即开始)。
这样的过程将进行矩阵加法:对于相同大小的矩阵,C = A + B:
; Same-size matrices addition (of 16b word elements): C = A + B
; ds:si = A address, ds:bx = B address
; ds:di = C address, cx = total amount of elements
; modifies: all input registers and ax
matrices_add:
mov ax,[si]
add ax,[bx] ; ax = A[i] + B[i]
mov [di],ax ; C[i] = ax
; ++i (actually advancing all three pointers instead of using index)
add si,2
add bx,2
add di,2
; loop until all elements are added
dec cx
jnz matrices_add
ret
现在在你的情况下你可以用这些参数调用它:
NO_ROWS EQU 3
NO_COLUMNS EQU 6 ; fixed
...
mov si,matrix1
mov bx,matrix2
mov di,matrix3
mov cx,(NO_ROWS*NO_COLUMNS)
call matrices_add
; here memory at address matrix3 will contain matrix1+matrix2 elements
...
停止使用控制台输出进行调试,并从 MS 获取一些 DOS 调试器,如 Turbo Debugger 或 CV.EXE(代码视图)。或者使用内置调试器的虚拟机 BOCHS,这是更强大的解决方案,甚至可以在 DOS 运行.
之前调试引导程序。如果你想显示矩阵,而不是使用单独的代码,以避免在加法过程中产生一些不必要的影响并使调试更容易(例如你可以先尝试显示 matrix1
然后退出回到 DOS,只是为了验证输出例程是否正确:
...
mov si,matrix3 ; pointer to next element
; output 3x6 16b matrix from "si" address
mov dx,NO_ROWS
output_matrix_row:
mov cx,NO_COLUMNS
output_matrix_line:
PutInt [si] ; display next element
PutCh ' ' ; make space between elements
add si,2 ; adjust pointer to point to next element
dec cx
jnz output_matrix_line
nwln ; new line
dec dx
jnz output_matrix_row
...
如你所见,我再次避免了元素计算的 "random-access" 索引,以连续的方式再次访问它们,只需为下一个元素做基本的 add si,2
,不慢 mul
甚至一些复杂的计算。
这就是用汇编编写的代码速度快的原因,您可以优化算法以不执行无用的指令,如果您将通过执行 offset = (y*ROW_SIZE + x)*ELEMENT_SIZE
继续计算每个元素的完整索引,您将以较慢的代码结束比用任何半正经的高级语言(C、Java、C# ...循环遍历连续的偏移量)。
所以在编写任何代码之前,您应该首先确保您完全了解您要计算的内容和原因,并尽可能简化它以避免任何冗余操作。
除非您在调试器中看到单步执行单指令,否则您将很难捕捉到代码的所有问题,正如您在最后 ~4 个问题中很好地演示的那样。我很欣赏你的固执,但你看起来没有足够的天赋在没有调试器的情况下继续进行下去,最终得到一些,并学会使用它并多次检查你的旧问题+其他人的提示,看看你会做多少这次能够复制+完全理解。