Ayuda en ensamblador, no sale del ciclo

Iniciado por Juan Martinez, 15 Mayo 2017, 18:01 PM

0 Miembros y 1 Visitante están viendo este tema.

Juan Martinez

Tengo este programa, es para calcular el promedio de n números ingresados por el usuario,
Corre todo bien hasta el momento de ingresar los datos, nunca sale del ciclo en el que se encuentra.
Este es el código:


Código (asm) [Seleccionar]
.model small
.stack 64
.data
;.........................................
VAL1 DB ?
msj1 DB 0AH,0DH, Cuantos numeros deseas introducir? : , $
msj2 DB 0AH,0DH, Numero: , $
msj3 DB 0AH,0DH, Promedio: , $
buffer DB 3,4 DUP(?)
;.........................................
.code
empezar proc far
mov ax,@data
mov ds,ax
mov es,ax

LEA DX,msj1
MOV AH,09H
INT 21H

MOV AH,0AH
INT 21H
SUB AL,30H

MOV CL,AL
MOV BL,AL
MOV AL,00
MOV VAL1,AL
proc1:
LEA DX,msj2
MOV AH,09H
INT 21H

MOV AH,0AH
LEA DX,buffer
INT 21H
SUB AL,30H

ADD AL,VAL1
MOV VAL1,AL
LOOP proc1

proc2:
LEA DX,msj3
MOV AH,09H
INT 21H

MOV AX,00
MOV AL,VAL1
DIV BL
ADD AX,3030H
MOV DX,AX
MOV AH,09H
INT 21H


mov ax,4c00h ;fin del programa con interrupcion
int 21h
empezar endp

end empezar



Ojala y me puedan ayudar, gracias

MOD: Etiqueta GeSHi

Serapis

#1
Loop Proc1, te devuelve a Proc1, pero antes decrementa el registro CX en una unidad, cuando CX llega a 0, deja de hacer el salto a Proc1... Entonces se trata de saber que valor ingresas en el registro CX.

Si miramos el código, al registro CX, no se le ha asignado explícitamente ningún valor, luego contendrá la basurilla que reste de otro programa, si más adelante colocas un valor en CL, pero no sabemos cual hay en CH, luego...  :silbar: :silbar: :silbar:

Típicamente un bucle lleva la estructura:

Código (asm) [Seleccionar]
MOV CX, Contador
Inicio:
... codigo a repetir
LOOP Inicio

cuando CX valga 0
...codigo despues del bucle


Prueba a asignar a contador un valor arbitrario cada vez, por ejemplo 13, ejecuta paso a paso y verás como el bucle retorna una y otra vez al 'Inicio', y cólo cuando CX valga 0, ejecuta el código tras el bucle.

Dado que dices "n números ingresados", Contador debería valer ese 'n'. Si 'n' no es conocido, porque se debe ejecutar una y otra vez hasta que el usuario tome una decisión al respecto, entonces el bucle que debe usarse no va por Loop, mejor LOOPNE, o LOOPNZ por ejemplo haciendo que cuando el usuario quiera terminar pulse una determinada tecla, tu haces un TEST entre el valor de la letra esperada (Q) y el valor de la letra introducida por el usuario, y acto seguido saltas en función del resultado del test... a diferencia de otros lenguajes, el ensambalador permite una infinidad de opciones para hacer lo mismo. compruebas si la tecla pulsada es la deseada, en cuyo caso puedes hacer un XOR CX CX (que cambia su contenido a 0, y entonces usas LOOPNZ), cuando CX sea 0, saldrá del bucle... en este caso el salto está condicionado, por la pulsación de una tecla concreta (la 'Q' por ejemplo de "Quit")...
Y tu código quedaría controlado tal que así:

Código (asm) [Seleccionar]
MOV CX, 1 <-- poner un valor en el contador
Inicio:
.... tu codigo (entrada de datos por parte del usuario)
TEST teclaesperada "Q", contra valor entrado
JNE FinOpera <--- SALTAR si igual (o si no cero JNZ)
INC CX
.... tu código para sumar el nuevo valor entrado (distinto de Q)
FinOpera:  <--- punto de salto si usuario pulsa tecla Q (salir)
LOOPNZ Inicio

Al llegar a Loop, si la tecla introducida es distinta de la esperada, se incrementa CX, entonces CX=2, luego Loop decrementa CX, y CX vuelve a valer 1.
En cambio, si el númnero recibido es igual al valor ASCII de Q (81) el test devuelve 'True', y por tanto saltamos el incremento de CX, como vale 1, cuando llegue a LOOP decrementa 1, vale 0 y por tanto sale ya del bucle.

En resumen:
- Fíjate que tu no tienes claro (yo por lo menos leyendo tu código), el valor de CX, así que no hay forma de saber cuando llega a cero...
- LOOP decrementa CX en 1 cada vez que se ejecuta y si CX vale 0, sale fuera del bucle.
- Si el condicional de salida depende de algún factor más, puedes o bien controlar directamente CX (como te muestro en el ejemplo), o bien usar otras variantes de LOOP, LOOPE, LOOPZ, LOOPNE, LOOPNZ, o incluso alguna instrucción condicional de salto... (por ejemplo JNE, podría saltar fuera del bucle directamente, es buena práctica al inicio, tener una única salida de un procedimiento, cuando tengas buen control del código, no te importará que se salga desde más de una parte)...

Juan Martinez

#2
Muchas gracias por tu ayuda

entonces quedaria asi:
Código (asm) [Seleccionar]

MOV CX,1
proc1:
       LEA DX,msj2
       MOV AH,09H
       INT 21H

       MOV AH,0AH  
   LEA DX,buffer
   INT 21H
   SUB AL,30H

TEST
JNE FinOpera
INC CX

       ADD AL,VAL1
 MOV VAL1,AL
FinOpera:
       LOOPNZ proc1


o que mas es lo que tengo que modificar?


· Los códigos deben ir en etiquetas GeSHi (segunda advertencia)
>aquí las reglas del foro
-Engel Lex

Serapis

#3
Antes de nada: Date cuenta que te han corregido el código para encerrarlo entre (USA) etiquetas CODE, hazlo tú en lo sucesivo... para delimitar el código y que resulte más legible.
En los botoncitos del editor (de mensajes), a la derecha pone: "Código Geshi", despliega el combo y elige el lenguaje pertinente, al caso ASM. Es la 2ª advertencia que te dan, no se que pasará a la 3ª  :silbar:

Bueno, lo que veo es que o no conoces la instrucción Test, o se te ha pasado (piensa que yo te he puesto algo intermedio entre ensamblador y pseudocódigo, me gusta que la gente soluciones su tema, pero pensando un poco no solo copiando y pegando)...
La instrucción TEST, es muy similar a la instrucción CMP  y como ella, lleva dos operandos (en el mensaje previo, he expresado ambos operandos en forma de pseudocódigo), Destino y fuente, técnicamente es un AND (y por tanto igual de rápida) y a diferencia de AND y CMP, no almacena el resultado en destino, solo altera (si así sucede), los banderines de estados (los mismos que AND, ya que el hardware implementa esta instrucción partiendo de AND, técnicamente es un AND (si no es un procesador RISC), sin la microoperación copiar el resultado en destino).

Así en TEST, uno de sus operandos puede contener la mascara con la que se compara. Si al caso como yo decía la letra Q (ASCII=81), si es un valor inmediato entonces debe ser el operando origen, en el otro operando puedes poner un registro, o una dirección de memoria, donde yace el valor de la letra con la que se evalúa.

Ejemplo:
TEST Al, 51H - 30H
<--- Igual que a los números 0-9 (ASCII 48-57, les restamos 48
Para que sean  en el rango 0-9, al valor inmediato también le tenemos que restar, para que coincida que el valor leído (y al que se le descontó, 48 (30H))... obviamente, la resta puedes ponerla ya como un resultado, pero si nu ncomentario, más adelabnte puede que te preguntes de dónde viene compararlo con ese valor... hasta que tengas costumbre, los comentarios 'tontos', te serán necesarios, ponlos...

p.d.: Una última cosa... no es preciso, que me envíes un mensaje privado para que responda en tu tema, ya cuando entro procuro mirar temas que entienda 'tener a medias'... siempre que mi tiempo libre me lo permita.