简体   繁体   中英

I'm trying to use the stored value in AX after the AAM instruction to divide it by 2, Why it doesn't work with 2 digits numbers output?

English is not my native language; please excuse typing errors, the code I will show here is a homework.

I really need to understand what's going on. I'm using Intel 8086 syntax, in DosBox 0.74 and TASM assembler.

The problem with the code is in the Area of the Triangle when I have to divide by 2. Note: the program always read only 1 digit from the keyboard. Look at the code (a user here suggested to show all my code instead of show it by part, I think is a bit long, but I think he is right, is more readable) [note: I edited this question, only the part of the code with problems was here, and I took some time to translate comments and messages to English, this is a bit longer, and I don't have too much time to translate all]:

;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

NOTE: "AREA DE TRIANGULO" is the only part with problems.

This won't work with all numbers (those outputs with 2 digits), and I think the problem here is the AAM and AAD instruction, AAD requires AX to have a 2 digits packed BCD number, so I decided to delete AAM instruction, and that doesn't work too. So I want to know what I'm missing here, the output I get by doing this is (using 5 and 6, 5*6=30/2=15):

The area of the triangle is: 0?

I think AAD instruction is part of the problem, if I use other numbers, like 4 and 4, this will be the output:

The area of the triangle is: 08

So, I decided to delete the AAD instruction, but the behavior is the same, can you tell me why?

Thanks in advance for your time and help! (if you think that there is another way to do something in my code, and it's better because any reason, tell me, I want to learn)

You need to understand what AAM and AAD actually do. The AAM instruction converts a binary number in AX in the range 00-99 to two unpacked decimal digits in AH and AL. The AAD performs the reverse of this operation, it converts two unpacked decimal digits in AH and AL to a binary number in AX in the range 00-99. This is all that they do, despite their names suggesting that they should be only used after multiplication and before division respectively.

The code you're using to calculate the area of the triangle starts with two independent binary numbers in the range 0 to 9 and then multiplies them together to get a binary number in the range 0 to 81. It then uses AAM to convert the binary result in AX into two unpacked digits in AH and AL. It then almost immediately uses AAD to convert the unpacked digits in AH an AL back to a binary number in AX.

Hopefully you can see that using AAM followed by AAD doesn't do anything useful. You start with a binary number in AX and you end up with the same binary number in AX. What you want to do is multiply the two dimensions together, then divide by two, and only then finally convert the result to separate digits for printing. So what you should be doing is something like this:

MOV AL,LADO2
MUL ALTURA
MOV BL,2
DIV BL
AAM

Note that it doesn't matter that AAM doesn't follow a multiply instruction. It does the same conversion regardless.

Note that you can simplify this code by taking advantage of the fact that a binary shift right is the same thing as an unsigned division by two:

MOV AL,LADO2
MUL ALTURA
SHR AX, 1
AAM

Finally, I would point out modern code doesn't use instructions like AAM and AAD because they're slower than the equivalent operations using more general purpose instructions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM