组件8086-将两个矩阵相乘

时间:2018-11-01 18:08:36

标签: assembly matrix multiplying emu8086

这段代码应将两个矩阵相乘以保持对应元素的符号

N EQU 3
M EQU 4
P EQU 2

.MODEL small
.STACK
.DATA

matA DB  4,-3,5,1,3,-5,0,11,-5,12,4,-5
matB DB  -2,3,5,-1,4,3,9,-7
matC DW  N*P DUP(?)


.CODE
.STARTUP

XOR  AX,AX
XOR  BX,BX
XOR  CX,CX
XOR  DX,DX
XOR  SI,SI
XOR  DI,DI
XOR  BP,BP

MOV  CX,N
decN:
PUSH CX
PUSH BX     
            MOV  CX,P
            decP:
            PUSH CX
            PUSH BP

                        MOV  CX,M
                        MOV  DI,0
                        decM:
                        PUSH CX
                        XOR  AH,AH
                        MOV  AL,matA[BX][DI]
                        PUSH BX
                        MOV  BX,BP
                        MOV  DL,matB[DI][BX]
                        POP  BX
                        IMUL DL
                        MOV  SI,BP
                        ADD  matC[BX][SI],AX
                        INC  DI
                        POP  CX
                        LOOP decM
            INC  BP                   
            POP  CX

            LOOP decP
INC  BX                            
POP  CX

LOOP decN

.EXIT
END

它不会终止,因为在第一个周期之后,当CX的值为0时,它会递减,因此它将FFFF压入堆栈,这会产生混乱。第二个问题是,在第一个循环之后,它无法从矩阵中选择正确的值。在decM:下,由于仿真器计算出错误的偏移,我不得不将BP值移到BX中。

1 个答案:

答案 0 :(得分:1)

matB[DI][BX]不是2D索引,而只是matB[DI + BX]。我不知道为什么emu8086支持这种令人困惑的语法,但是我不建议这样做。

8086机器码仅在寻址模式下支持简单加法,因此您需要add DI, N之类的东西来跨越列向量。

  

decM下:我必须将BP值移至BX,因为仿真器计算出的偏移量错误。

这很奇怪。起初,我想知道您的偏移量是否正确,但段基数是否错误(因为BP表示SS,而BX表示DS),但是.model small gives you DS=SS

但是我认为您需要自己初始化DS。如果我理解正确,则DOS .exe的开头为DS base = PSP的开头。 Why doesn't MS-DOS initialize the DS and ES registers?

因此,您可能具有正确的偏移量,但段基数错误,从而导致线性地址错误。如果BP有效,那么在seg:off寻址模式下错的不是偏移量。

修复此问题后,您应该能够使用调试器调试代码。分段可能会造成混淆,但是其余的分段应该很容易通过单步调试,并在手册中查找任何令人惊讶的说明。

[BP+BX]不是有效的寻址模式,因此,是的,在MOV SI,BP之前确实需要ADD matC[BX][SI],AX。或add bx,bp或其他可以破坏寄存器的东西。

您可能只需使用incadd reg,N / sub reg,P-1或其他任何方法递增3个指针(目标,列源和行源)尝试将ijk索引保留在寄存器中,并在每次2D访问之前对其进行缩放。


不均衡的推送/弹出

您的内部循环有2次推送和2次弹出。但是您的外部2个循环(decNdecP)每个都有2个推入和1个弹出。这似乎注定要失败,并且会用更大的N溢出堆栈。

我还没有尝试确切地弄清楚应该将哪个值发送到哪里,并且如果您要尝试始终将一个值弹出到它来自的寄存器中,或者利用保存/恢复作为将其弹出到其他寄存器的机会。您的代码中没有注释...


使用所有寄存器代替内存很好,但是 需要使用一些内存。如果将BP用作帧指针,则可能会花费更短的时间(并且代码效率更高),因此可以随机访问堆栈。限制自己进行推送/弹出以进行保存/还原意味着您必须在内部循环中保存/恢复,而不是仅在内存中保留一些外部循环的东西。

您可以在内存中保留一个循环计数器,例如dec byte [BP-2] / jnz decP,而不是保存/恢复CX以将其用作所有3个循环的循环计数器。

或者在寄存器中使用指针,您可以检查它们,而不用将单独的寄存器或内存位置用作循环计数器。像

top_of_loop:
      ...
    ; bottom of a loop
    cmp DI, OFFSET matC + N*P * 2
    jb  top_of_loop
      ; fall through when DI has 

 ; or OFFSET matC + SIZEOF matC, or put a label at the end of matC

然后,您可以将CX用作临时文件(例如使用add cx, ax来累积行/列点乘积,该乘积将成为结果矩阵的元素),或者如果您想支持运行时,则可以保留循环绑定可变的矩阵大小。

您也不需要像现在一样使用DX。 imul byte ptr matB[DI][BX]可以工作,而无需单独加载到寄存器中。 (或使用指针imul byte ptr [BX]。)这使您可以自由使用DX。