Assembly I';我试图在AAM指令之后使用AX中存储的值将其除以2,为什么它不';2位数字输出不能工作?

Assembly I';我试图在AAM指令之后使用AX中存储的值将其除以2,为什么它不';2位数字输出不能工作?,assembly,x86-16,tasm,dosbox,Assembly,X86 16,Tasm,Dosbox,英语不是我的母语;请原谅键入错误,我将在这里显示的代码是一个家庭作业 我真的需要了解发生了什么。我在DosBox 0.74和TASM汇编程序中使用Intel 8086语法 代码的问题是当我必须除以2时,三角形的区域。注意:该程序始终只能从键盘读取1位数字。看看代码(这里的一位用户建议显示我的所有代码,而不是按部分显示,我认为有点长,但我认为他是对的,更具可读性)[注意:我编辑了这个问题,这里只有代码中有问题的部分,我花了一些时间将评论和消息翻译成英语,这有点长,我没有太多时间来翻译所有内容]:

英语不是我的母语;请原谅键入错误,我将在这里显示的代码是一个家庭作业

我真的需要了解发生了什么。我在DosBox 0.74和TASM汇编程序中使用Intel 8086语法

代码的问题是当我必须除以2时,三角形的区域。注意:该程序始终只能从键盘读取1位数字。看看代码(这里的一位用户建议显示我的所有代码,而不是按部分显示,我认为有点长,但我认为他是对的,更具可读性)[注意:我编辑了这个问题,这里只有代码中有问题的部分,我花了一些时间将评论和消息翻译成英语,这有点长,我没有太多时间来翻译所有内容]:

;Segmento de Datos
DATA SEGMENT 'DATA'
    LADO1   DB ?                        ;con el ? se indica que la variable no tiene nada
    LADO2   DB ?
    LADO3   DB ?
    BASE    DB ?
    ALTURA  DB ?
    COUNTER DB 0                        ;contador para muestra de mensajes

    ;mensajes ESPECIFICOS
    MSG_PRESENTACION DB 10,13,"Este programa calcula los perimetros y areas de algunas figuras geometricas $"

    ;triangulo
    MSG_DIMENSIONES_TRIANGULO   DB 10,13,10,13,"Ingrese las dimensiones del triangulo (considere el lado 2 como la BASE)$"
    MSG_PERIMETRO_TRIANGULO     DB 10,13,"El perimetro del triangulo es: $"
    MSG_AREA_TRIANGULO          DB 10,13,"El area del triangulo es: $"
    ;triangulo

    ;cuadrado
    MSG_DIMENSIONES_CUADRADO    DB 10,13,10,13,"Ingrese las dimensiones del cuadrado$"
    MSG_PERIMETRO_CUADRADO      DB 10,13,"El perimetro del cuadrado es: $"
    MSG_AREA_CUADRADO           DB 10,13,"El area del cuadrado es: $"
    ;cuadrado

    ;rectangulo
    MSG_DIMENSIONES_RECTANGULO  DB 10,13,10,13,"Ingrese las dimensiones del rectangulo$"
    MSG_PERIMETRO_RECTANGULO    DB 10,13,"El perimetro del rectangulo es: $"
    MSG_AREA_RECTANGULO         DB 10,13,"El area del rectangulo es: $"
    ;rectangulo

    ;otros mensajes GENERALES
    MSG_ALTURA  DB 10,13,"Introduzca la altura: $"  ;sirve para solicitar la altura de varias figuras
    MSG_BASE    DB 10,13,"Introduzca la base: $"
    MSG_LADO    DB 10,13,"Ingrese el lado $"
    MSG_PUNTO   DB ": $"                ;sirve para colocar 2 puntos luego del lado
    MSG_PRUEBA  DB 10,13,10,13,"El valor de prueba es: $"
DATA ENDS

;Segmento de codigo
CODE SEGMENT 'CODE'
    ASSUME DS:DATA,CS:CODE              ;se le indica al Assembler que DATA es el nombre dado al DATA SEGMENT, y CODE el nombre de CODE SEGMENT

MAIN PROC                               ;incia el inicio del segmento de codigo
    MOV AX,DATA                         ;hacer disponible/accesible el contenido del DATA SEGMENT al CODE SEGMENT
    MOV DS,AX                           ;inicializar el data segment, debido a limitacion de MOV, se hace con la ayuda de AX

    LEA DX,MSG_PRESENTACION             ;Mensaje de presentacion / introduccion
    CALL printString

    ;triangulo********
    LEA DX,MSG_DIMENSIONES_TRIANGULO    ;carga a DX el mensaje 1 (Se le dice al usuario que introduzca los datos del triangulo)
    CALL printString
    ;SOLICITUD DE DATOS     ----//----
    LEA DX,MSG_ALTURA                   ;indica al usuario que introduzca la altura del triangulo
    CALL printString
    CALL readNumber                     ;se lee del teclado la entrada del usuario mediante este procedimiento (1 digito)
    MOV ALTURA,AL

    LEA DX,MSG_LADO                     ;se le dice al usuario que introduzca el lado 1
    CALL printString
    CALL numberInMessage                ;muestra el numero 1 despues del mensaje anterior y DOS PUNTOS para introducccion de datos (solo con fines de formato)
    CALL readNumber
    MOV LADO1,AL                        ;mueve valor capturado a la variable

    LEA DX,MSG_LADO                     ;se le dice al usuario que introduzca el lado 2 (la base)
    CALL printString
    CALL numberInMessage                ;muestra el numero 2 despues del mensade de LADO
    CALL readNumber
    MOV LADO2,AL                        ;mueve valor capturado a la variable

    LEA DX,MSG_LADO                     ;se le dice al usuario que introduzca el lado 3
    CALL printString
    CALL numberInMessage                ;muestra el numero 3
    CALL readNumber
    MOV LADO3,AL                        ;mueve valor capturado a la variable

    ;PERIMETRO DE TRIANGULO ----//----
    ADD AL,LADO2                        ;SUMA LADO3(AL) CON CON LADO2 y lo guarda en AL
    ADD AL,LADO1                        ;SUMA LO ANTERIOR CON LADO1
    MOV AH,0                            ;borra basura de AH
    AAA                                 ;significa ASCII Adjust after Addition
    MOV BX,AX                           ;los registros AH y AL se usan una y otra vez, por tanto para evitar perder sus datos se mueven a BH y BL, esto se hace moviendo AX a BX
    LEA DX,MSG_PERIMETRO_TRIANGULO      ;lee la direccion efectiva de memoria del mensaje (resultado de la suma) y la carga en DX
    CALL printString                    ;printDigits usa BX para la impresion de los datos
    CALL printDigits                    ;imprime resultado

    ;AREA DE TRIANGULO      ----//----
    MOV AL,LADO2                        ;multiplicar la base (LADO2) por la altura
    MUL ALTURA
    AAM
    MOV BL,2
    AAD
    DIV BL
    MOV BX,AX

    LEA DX,MSG_AREA_TRIANGULO           ;mensaje que indica la impresion del area del triangulo
    CALL printString
    CALL printDigits                    ;imprime numeros del area del triangulo
    ;FIN triangulo********

    ;cuadrado********
    LEA DX,MSG_DIMENSIONES_CUADRADO
    CALL printString
    ;SOLICITUD DE DATOS     ----//----
    LEA DX,MSG_LADO
    CALL printString
    LEA DX,MSG_PUNTO                    ;solo con fines de formato, muestra los 2 puntos antes la entrada del usuario
    CALL printString
    CALL readNumber                     ;recordemos que la lectura se almacena en AL
    MOV LADO1,AL

    ;PERIMETRO DEL CUADRADO ----//----
    MOV AH,4                            ;MUL no acepta valores constantes como argumentos, por tanto se mueve a un registro
    MUL AH                              ;multiplicamos la entrada del usuario por 4 y se almacena en AL
    MOV AH,0                            ;se limpia basura de AH
    AAM                                 ;ya explicado, se iran reduciendo comentarios debido a esto, no se omitiran valores y/o situaciones especificas y necesarias para entender
    MOV BX,AX                           ;se mueve porque se trabaja mucho con AX, y evitamos posible perdida de los datos
    LEA DX,MSG_PERIMETRO_CUADRADO
    CALL printString
    CALL printDigits                    ;imprime lo que esta en BX

    ;AREA DEL CUADRADO      ----//----
    MOV AL,LADO1
    MUL AL                              ;multiplicamos la entrada del usuario por el mismo, ya que se desea elevar al cuadrado
    MOV AH,0                            ;se limpia basura de AH
    AAM
    MOV BX,AX                           ;se mueve porque se trabaja mucho con AX, y evitamos posible perdida de los datos
    LEA DX,MSG_AREA_CUADRADO
    CALL printString
    CALL printDigits                    ;imprime lo que esta en BX
    ;FIN cuadrado********

    ;rectangulo********
    LEA DX,MSG_DIMENSIONES_RECTANGULO
    CALL printString
    ;SOLICITUD DE DATOS     ----//----
    LEA DX,MSG_BASE
    CALL printString
    CALL readNumber
    MOV BASE,AL

    LEA DX,MSG_ALTURA
    CALL printString
    CALL readNumber
    MOV ALTURA,AL

    ;PERIMETRO DEL RECTANGULO----//----
    MOV AH,2                            ;no se puede usar MUL con una consatnte, por tanto se mueve a un registro
    MUL AH                              ;AL contiene la ALTURA, y se multiplica por 2
    MOV AH,0                            ;se limpia la basura
    AAM
    MOV BX,AX                           ;se guarda en otro registro para evitar perdida de datos

    MOV AL,BASE
    MOV AH,2                            ;mismo procedimiento que arriba para multiplicar por un numero constante
    MUL AH
    MOV AH,0
    AAM

    ADD BX,AX                           ;se suma el primer resultado (guardado en BX), con lo ultimo conseguido (guardo en AX), el resultado queda e BX
    AAA

    LEA DX,MSG_PERIMETRO_RECTANGULO
    CALL printString
    CALL printDigits

    ;AREA DEL RECTANGULO    ----//----
    MOV AL,BASE
    MUL ALTURA
    MOV AH,0
    AAM
    MOV BX,AX
    LEA DX,MSG_AREA_RECTANGULO
    CALL printString
    CALL printDigits
    ;FIN rectangulo********
    MOV AH,4CH                          ;EXIT, termina el programa
    INT 21H                             ;ejecuta la interrupcion
MAIN ENDP                               ;acaba la rutina principal

;PROCEDIMIENTOS -----------------------------------------------------------------------------------------------------------------------------
printDigits PROC
    ADD BX,3030H                        ;como AH & AL (BH & BL) estan en BCD, se le suman 30 para obtener el valor en ASCII
    ;primer digito
    MOV AH,2                            ;se le indica a la interrupcion que haremos una impresion de un CARACTER
    MOV DL,BH                           ;como BH contiene el primer digito, se imprime este primero pasando el valor a DL
    INT 21H                             ;se llama a la interrupcion, que ejecuta lo especificado por AH, tomando como entrada DL
    ;segundo digito
    MOV AH,2                            ;nuevamente se le indica una impresion (STANDARD OUTPUT)
    MOV DL,BL                           ;esta vez se pasa el segundo digito
    INT 21H                             ;y se imprime
    RET
printDigits ENDP

printString PROC
    MOV AH,9                            ;se pasa a AH el 9 para indicar que se hara una salida a STANDARD OUTPUT
    INT 21H                             ;interrupcion del DOS, que funciona con el valor del registro AH, el 9 indica STANDARD OUTPUT
    RET
printString ENDP

readNumber PROC
    MOV AH,1                            ;1 en AH indica STANDARD INPUT
    INT 21H                             ;Se usa la interrupcion para tomar entrada del teclado (por instruccion 1 en AH), se retorna lectura a AL
    SUB AL,30H                          ;se convierte de ASCII a numero normal para realizar las operaciones deseadas, el 30 es porque se recibe el numero en BCD desempacado
    RET
readNumber ENDP

numberInMessage PROC
    ADD COUNTER,1                       ;añade 1 a al contador, para nuevos usos es necesario limpiar COUNTER antes de la llamada
    MOV BL,COUNTER                      ;se mueve porque se desea imprimir el contador, y printDigits trabaja con BX
    MOV BH,0
    CALL printDigits
    LEA DX,MSG_PUNTO                    ;contiene el mensaje de los dos puntos para formato solamente
    CALL printString
    RET
numberInMessage ENDP

CODE ENDS                               ;final del segmento de codigo
END MAIN
注:“三角区”是唯一存在问题的部分

这不适用于所有数字(那些有2位数字的输出),我认为这里的问题是AAM和AAD指令,AAD要求AX有一个2位压缩BCD号,所以我决定删除AAM指令,但这也不起作用。所以我想知道我在这里缺少了什么,我这样做得到的输出是(使用5和6,5*6=30/2=15):

我认为AAD指令是问题的一部分,如果我使用其他数字,如4和4,这将是输出:

The area of the triangle is: 08
所以,我决定删除AAD指令,但行为是一样的,你能告诉我为什么吗


提前感谢您的时间和帮助!(如果您认为在我的代码中有其他方法可以执行某些操作,而且因为任何原因都更好,请告诉我,我想学习)

您需要了解AAM和AAD的实际功能。AAM指令将AX中00-99范围内的二进制数转换为AH和AL中的两个未打包的十进制数字。AAD执行与此相反的操作,将AH和AL中的两个未打包的十进制数字转换为AX中00-99范围内的二进制数。这就是它们所做的全部工作,de尽管它们的名字表明它们只能分别在乘法之后和除法之前使用

用于计算三角形面积的代码从0到9范围内的两个独立二进制数开始,然后将它们相乘,得到0到81范围内的二进制数。然后使用AAM将AX中的二进制结果转换为AH和AL中的两个未压缩数字。然后几乎立即使用AAD转换un将AH an AL中的数字打包回AX中的二进制数

希望您能看到,使用AAM后跟AAD并没有任何用处。您从AX中的一个二进制数开始,然后在AX中得到相同的二进制数。您要做的是将两个维度相乘,然后除以2,最后才将结果转换为单独的数字进行打印。那么,您应该使用doing是这样的:

MOV AL,LADO2
MUL ALTURA
MOV BL,2
DIV BL
AAM
请注意,AAM不遵循乘法指令并不重要。不管怎样,它都会执行相同的转换

请注意,您可以通过利用二进制右移与无符号除以2是相同的事实来简化此代码:

MOV AL,LADO2
MUL ALTURA
SHR AX, 1
AAM

最后,我要指出,现代代码不使用AAM和AAD这样的指令,因为它们比使用更通用指令的等效操作慢。

太多的文本,如果你让它更简洁,你会得到更多的关注编辑你的答案,让它显示你的程序的完整源代码,你实际上就是我们ing。不要将其分割成单独的部分,然后扰乱顺序。这甚至不接近使用AAM或任何东西的测试用例。只需制作一个测试用例,将一些值移动到寄存器中,并执行一些操作。然后解释您在寄存器中实际得到的内容(例如,使用调试器检查它们),并解释您想要/期望的内容。非常类似于
DIV r8
,但有一个立即数操作数,商/余数位于与DIV相反的寄存器中。如果您的程序不工作,请单步执行该程序,并查阅insn set参考,查找任何不符合您期望的指令。顺便说一句,使用DIV to divide乘2是可怕的。使用
shr ax,1
有趣的事实:AAM比Haswell上的
DIV r8
快1个周期。当然,模乘逆比两者都快,而且始终是一个选项,因为AAM只支持
imm8
常量除数(不必是10;)@PeterCordes我实际上查看了英特尔优化手册,但没有找到它。你在Agner Fog的表格中找到了吗?是的,这是最方便的指令计时来源。在Haswell上:AAM是8 uops,21c延迟,每8c吞吐量一个。DIV r8是9 uops,22-25c延迟,每9c吞吐量一个。哇,谢谢,很多!你很好哈!你的解释很简单,但完整明了!!我认为你的答案很有用@Ross Ridge
MOV AL,LADO2
MUL ALTURA
SHR AX, 1
AAM