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.