Está en la página 1de 17

Conversión de caracteres : Mayúsculas a minúsculas

Para convertir una letra de mayúscula a minúscula , notamos que el código ASCII para las letras mayúsculas de 'A'
a 'Z' forman una secuencia entre 65 a 90.
Las correspondientes letras minúsculas de 'a' a 'z' estan contenidas en la secuencia entre 97 y 122.
Nosotros decimos que el código Ascii forma una secuencia ordenada y usamos este hecho para ordenar
textualmente información.
Para convertir un carácter de mayúscula a su equivalente en minúscula, nosotros sumamos 32 al código Ascii de la
letra mayúscula.
Para convertir de mayúscula a minúscula, nosotros substraemos 32 del codigo Ascii de la
letra mayúscula.
El número 32 es obtenido substrayendo el código Ascii de 'A' del codigo Ascii de 'a'
(i.e. 'A'-'a' = 97-65=32).

Ejemplo 3,19: Escriba un programa que pida al usuario que ingrese una letra mayúscula, lea la letra y muestre a
pantalla su correspondiente letra minúscula. El programa debe convertir la letra a su minúscula correspondiente y mostrarla
en una nueva linea de pantalla.

; char.asm: character conversion: uppercase to lowercase


.model small
.stack 100h

CR equ 13d
LF equ 10d

.data
msg1 db ‘Enter an uppercase letter: $’
result db CR, LF, ‘The lowercase equivalent is: $’
.code

; main program
start:

; prompt for uppercase letter


mov ax, @data
mov ds, ax
mov dx, offset msg1
call puts

; read uppercase letter


call getc
mov bl, al ; save character in bl
add bl, 32d ; convert to lowercase
mov dx, offset result
call puts ; display result message
mov dl, bl
call putc ; display lowercase letter

;Salida del programa


mov ax, 4c00h
int 21h ; return to ms-dos

; user defined subprograms


puts: ; display a string terminated by $
; dx contains address of string
mov ah, 9h
int 21h ; output string
ret
putc: ; display character in dl
mov ah, 2h
int 21h
ret

getc: ; read character into al


mov ah, 1h
int 21h
ret

end start

Ejecutando este programa se obtendra la siguiente salida:

Enter an uppercase letter: G


The lowercase equivalent is: g

El string result esta definido para comenzar con el Return y el saldo de línea que harán que se muestre en una
nueva línea.
Una alternativa puede ser incluir los dos caracteres al final del string msg1, antes del carácter '$', por ejemplo:

msg1 db ‘Enter an uppercase letter: ’,CR, LF, ‘$’

Despues de mostrar msg1, como esta definido anteriormente, el próximo iten a ser mostrado aparecerá en una
nueva línea.

Ejercicios:

3.11Modifique el ejercicion anterior para que convierta letras minúsculas a sus respectivas mayúsculas.

3.12Escriba un programa para que convierta un número simples como un 5 a su carácter equivalente '5', y muestre
en pantalla.

Consistencia en los subprogramas de Entrada y Salida:


Hemos escrito hasta el momento tres subprogramas de entrada y salida: putc, getc, y puts.
Una dificultad con esos subprogramas es que usan diferentes registros por parámetros, basados en los requerimientos de los
subprogramas de entrada y salida del MS-Dos.

Esto significa que debemos ser cuidadosos en recordar a cual registro (al,dl,dx) nosotros pasamos el parámetro.
Un enfoque más consistente puede ser usar el mismo registro para pasar el parametro a todos los subprogramas de
entrada y salida, por ejemplo podemos usar el registro 'ax'.

Mientras nosotros no cambiemos el funcionamiento del MS-Dos, podemos hacer esto para modificar nuestros
subprogramas. Usaremos entonces 'al' para contener el carácter mostrado por putc y ax para contener la dirección del string
a ser mostrado por puts.
El subprograma getc retorna el carácter ingresado en 'al', entonces este no debe ser cambiado.

Ejemplo 3.20: Versión revisada de puts y putc

puts: ; display a string terminated by $


; ax contains address of string
mov dx, ax ; copy address to dx for ms-dos
mov ah, 9h
int 21h ; call ms-dos to output string
ret
putc: ; display character in al
mov dl, al ; copy al to dl for ms-dos
mov ah, 2h
int 21h
ret

Ejemplo 3.21: Para ilustrar el uso de la nueva definicion de putc y puts, reescribiremos el programa 3.19, el cual convertía
una letra mayúscula a su equivalente minúscula:

; char2.asm: character conversion: uppercase to lowercase


.model small
.stack 100h
CR equ 13d
LF equ 10d
.data

msg1 db 'Enter an uppercase letter: $'


result db CR, LF, 'The lowercase equivalent is: $'

.code
; main program
start:
mov ax, @data
mov ds, ax
mov ax, offset msg1
call puts
call getc ; read uppercase letter
mov bl, al ; save character in bl
add bl, 32d ; convert to lowercase
mov ax, offset result
call puts ; display result message
mov al, bl
call putc ; display lowercase letter
mov ax, 4c00h
int 21h ; return to ms-dos

; user defined subprograms


puts: ; display a string terminated by $
; ax contains address of string
mov dx, ax
mov ah, 9h
int 21h ; call ms-dos to output string
ret
putc: ; display character in al
mov dl, al
mov ah, 2h
int 21h
ret
getc: ; read character into al
mov ah, 1h
int 21h
ret
end start

Guardando registros
Existe una desventaja en utilizar el metodo descripto anteriormente.
Ahora nosotros utilizamos dos registros en lugar de uno para almacenar la infomación deseada. Esto reduce el número de
registros disponibles para almacenamiento.
Otro punto importante, es en el subprograma puts, por ejemplo, el registro dx es modificado.
Si nosotros usamos este registro en un programa antes de la llamada a puts, entonces la información almacenada en dx se
perderá, a menos que la guardemos.
Esto puede causar sutiles pero serios errores en los programas que son difíciles de detectar. El siguiente fragmento de
código ilustra el problema:

mov dx, 12 ; dx = 12
mov ax, offset msg1 ; display message msg1
call puts ; dx gets modified
add dx, 2 ; dx will NOT contain 14

Este error se manifiesta mucho despues, en la ejecución del programa. Los principiantes tienen frecuentemente
este tipo de error en lenguaje ensamblador.

Cuando un programa se comporta extrañamente, es usualmente una buena idea utilizar técnicas de depuración para
chequear este tipo de situaciones, por ejemplo chequear que los subprogramas no modifiquen registros que son utilizados
para otros propósitos.

Esto es generalmente un problema con todos lo subprogramas que cambian el valor de registros. Entonces, si
nosotros estamos usando el registro 'ah' antes de llamar a un subprograma, debemos guardar su valor antes de ejecutar la
llamada.
Incluso, el subprograma del MS-Dos invocando al int pueden cambiar el valor de los registros. Por ejemplo el
subprograma número 2h (usado por getc) hace eso. Este modifica el registro 'al' para retornar el valor ingresado por el
teclado. Los subprogramas del MS-Dos tambien pueden cambiar otros valores de registros y debemos ser muy cuidadosos
de chequear por esto cuando usamos tales subprogramas.
Existe una solucion sencilla a este problema. Nosotros podemos y debemos escribir nuestros subprogramas de
manera que antes de modificar cualquier registro este primero guarde el valor de ese registro. Entonces, antes de retornar de
un subprograma, restauramos el registro a su valor original.
( En el cado de getc, sin embargo, nosotros no guardamos el valor del registro 'al', porque queremos que getc lo lea.)

El stack es típicamente usado para guardar y recuperar los valores de los registros usados en subprogramas.
El stack es una area de memoria (Ram) donde podemos almacenar temporariamente ítems. Solemos decir que
hacemos push dentro del stack para guardar el dato. También solemos decir que hacemos pop del stack para obtener un
dato.
El 8086 provee instrucciónes push y pop para almacenar y recuperar itens del stack. Ver capítulo 2 para más
detalles.

Ejemplo 3.22 : Nosotros reescribiremos los subprogramas getc, putc, y puts para guardar los valores de los registros y
restaurarlos apropiadamente. La siguiente versión de getc, putc y puts son por lo tanto seguras en el sentido que los registros
no cambian de valor sin que el programador no realice el cambio.

puts: ; display a string terminated by $


; dx contains address of string
push ax ; save ax
push bx ; save bx
push cx ; save cx
push dx ; save dx
mov dx, ax
mov ah, 9h
int 21h ; call ms-dos to output string
pop dx ; restore dx
pop cx ; restore cx
pop bx ; restore bx
pop ax ; restore ax
ret

putc: ; display character in al


push ax ; save ax
push bx ; save bx
push cx ; save cx
push dx ; save dx
mov dl, al
mov ah, 2h
int 21h
pop dx ; restore dx
pop cx ; restore cx
pop bx ; restore bx
pop ax ; restore ax
ret

getc: ; read character into al


push bx ; save bx
push cx ; save cx
push dx ; save dx
mov ah, 1h
int 21h
pop dx ; restore dx
pop cx ; restore cx
pop bx ; restore bx
ret

Note que nosotros sacamos valores del stack en el orden inverso en que fue colocado debido a la naturaleza LIFO
del stack (Last Input First Output).

De ahora en adelante, cuando nos refiramos a getc, putc, y puts en este documento, las definiciones anteriores se
daran como entendidas.

OBS: Es vital, que cuando usemos el stack en subprogramas, saquemos todos los itens puestos en el stack por el
subprograma, antes de retornar del mismo.

Fallas de este tipo, dejando un item en el stack, será utilizado por la instrucción ret como dirección de retorno. Esto
causará que nuestro programa se comporte inadecuadamente. Si tenemos suerte fallará. En otro caso, este continuará su
ejecución desde cualquier punto en el programa, produciendo resultados incomprensibles.

Un punto que vale la pena repetir: cuando usamos el stack en un subprograma, debemos asegurarnos de remover
todos los items colocados en el, antes de retornar del subprograma.

Control de flujo: Instrucciones Jump.

Instrucciones de jump incondicionales:


Las instrucción incondicional jmp del 8086 causa el control de flujo (por ejemplo, cual es la próxima instrucción a
ser ejecutada) al transferir el control al punto indicado en el identificador pasado a la instrucción jmp.

Ejemplo 3.23: En este ejemplo ilustramos el uso de la instrucción jmp para implementar un bucle sin fin (no es algo que
normalmente haríamos).

again:
call getc ; read a character
call putc ; display character
jmp again ; jump to again
Este es un ejemplo de saldo de retroceso, el control del programa es transferido de un lugar a otro.
El fragmento de codigo causa repetidas instrucciones entre la instrucción jmp y su etiqueta.
Puedes colocar etiquetas en cualquier punto del programa y la etiqueta puede estar en la misma línea que la
instrucción, por ejemplo:

again: call getc ; read a character

el programa anterior ejecutará para siempre un bucle y deberás pararlo con una interrupción,
por ejemplo precionando ctrl/c o desconectando la máquina.

Ejemplo 3.24: El siguiente fragmento de código ilustra un forward jump, el control es transferido a una posición posterior
del programa:

call getc ; read a character


call putc ; display the character
jmp finish ; jump to label finish
<do other things>; Never gets done !!!

finish:
mov ax, 4c00h
int 21h

En este caso el código entre la instrucción y la etiqueta finish nunca será ejecutado porque el jmp causa que salte sobre el.

Instrucciones de jump condicionales:


El 8086 provee un número de instrucciones para jump condicionales (por ejemplo je,jne,ja). Esas instrucciones
únicamente transfieren el flujo del programa si alguna condición es satisfecha.
Por ejemplo, cuando una operación aritmética como la suma o resta arroja un resultado (carry out), el CPU setea o
limpia un flag (Z-flag) en el registrador de estado para guardar si el resultado de la operación fue cero, u otro flag si el
resultado fue negativo y asi sucesivamente.
Si el z-flag tiene valor 1, esto significa que el resultado de la última instrucción que ha afectado el z-flag fue 0.
Si el z-flag tiene valor 0, esto significa que el resultado de la última instrucción que ha afectado el z-flag no fue 0.
Para testear estos flags, individualmente o una combinación de ellos, las instrucciones condicionales de jump
pueden manejar varios condiciones (==,!=,<,>,<=,>=) que surgen cuando comparamos valores. Además, existen
instrucciones de jumps condicionales para testear condiciones como la ocurrencia de overflow o el cambio de signo.

La instrucción de jump condicional son algunas veces llamadas instrucciones con condiciones. Estas testean el
valor de los flags en el registro.
(El valor del registro 'cx' es usado en algunos de ellos). Una de las instrucciones condicionales de jump es jz la cual salta a
otra ubicación del programa igual que la instrucción jmp, excepto que lo hace si el z-flag es 1, por ejemplo si el resultado de
la última instrucción fue 0. (La instrucción jz puede ser entendida como “jump con condición cero ” o “jump en cero”).

Ejemplo 3.25: Usando la instrucción jz.

mov ax, 2 ; ax = 2
sub ax, bx ; ax = 2 - bx
jz nextl ; jump if (ax-bx) == 0

inc ax ; ax = ax + 1
nextl:
inc bx

Lo anterior es equivalente a:

ax = 2;
if ( ax != bx )
{
ax = ax + 1 ;
}
bx = bx + 1 ;

En este ejemplo, el z-flag estara seteado (a 1) unicamente si bx contiene 2. Si esto ocurre, entonces la instrucción jz
causará el salto.

Nosotros estamos comparando efectivamente ax con bx y saltando (jumping) si son iguales.

El 8086 provee de la instrucción cmp para comparar. Esta trabaja exactamente como la instrucción sub excepto que
el operador no es afectado, por ejemplo, este substrae el codigo del destino, pero descarta el resultado dejando el operador
de destino sin cambios. Sin embargo, este modifica el stado del registro. Todos los flags que son seteados o reseteados por
sub son seteado o reseteados por cmp. De esta manera, si tu quieres comparar dos valores, es mas razonable usar la
instrucción cmp.

Ejemplo 3.26 : El ejemplo anterior reescrito usando cmp:

mov ax, 2 ; ax becomes 2


cmp ax, bx ; set flags according to (ax - bx)
jz equals ; jump if (ax == bx)
inc ax ; executed only if bx != ax
equals:
inc bx

Nota: El cmp compara el operando de destino con el operando del codigo. El orden es obviamente importante porque por
ejemplo, una instrucción como jng dest, source causara bifurcación únicamente si dest <= source.

Algunas instrucciones condicionales pueden tener más de un nombre, por ejemplo jz (Jump on zero) es tambien llamada je
(jump on equal). De esta manera el codigo anterior puede ser escritor como :

cmp ax, bx
je equals ; jump if ax == bx

Este nombre para la instrucción hace el código mas entendible en situaciones donde estamos interesado en la
igualdad de dos valores.

Los jumps con condiciones pueden ser usado para saltos hacia adelante (como en los ejemplos anteriores) o hacia
atrás y en este caso implementar loops.

Existen 16 jumps con condicionales los cuales testean cuales flags o combinación de flags estan seteado o fueron
limpiados.

Sin embargo, más que concentrarse en el seteo de flags, es mas fácil entender la comparación de números
(signados y sin signo separadamente) como igual, no igual, menor que, mayor que , mayor o igual, o menor o igual que.

Tabla 3.1 lista las instrucciones con condiciones. Estas tienen nombres alternativos además.

Nombre Jump si Flag testeado

je / jz equal/zero zf = 1

jne / jnz not equal/not zero zf = 0


Operando con números sin signo

ja / jnbe above/not below or equal (cf or zf) = 0

jae / jnb above or equal/not below cf = 0

jb / jnae / jc below/not above or equal/carry cf = 1

jbe / jna below or equal/not above (cf or zf) = 1

Operando sobre números con signos

jg / jnle greater/not less than nor equal zf=0 and sf = of

jge / jnl greater or equal/not less sf = of

jl / jnge less /not greater nor equal sf <> of

jle / jng less or equal/not greater (zf=1) or (sf!=of)

jo overflow of = 1

jno not overflow of = 0

jp / jpe parity/parity even pf = 1

jnp / jpo no parity/odd parity pf = 0

js sign sf = 1

jns no sign sf = 0

Tabla 3.1: Instrucciones condicionales para jump.

Notas:
 cf,of,zf,pg, y sf son flags de estado de carry,overflow, cero, paridad, y signo.

 (cf o zf) = 1 significa que el jump es en cualquier caso, cf o zf esta seteado a 1.

En las instrucciones anteriores, la letra “a” puede ser tomada como “sobre”(above), y la letra “b” significa
“abajo”(below). Las instrucciones que usan esas letras (por ej. ja, jb, etc) operan con números sin signo.
La letra “g” puede ser tomada como “mayor que” (greater tan) y la letra “l” puede ser utilizada como “menor que”
(less than). Instrucciones que usan esas letras (por ej. jg, jl, etc.) operan sobre números con signos.
Es responsabilidad del programador usar la instrucción correcta dependiendo si va a manipular numeros signados o
no.

Existen tambien cuatro instrucciones envolviendo el registro 'cx': jcxz, loop, loope, loopne. Por ejemplo, la
instrucción jcxz causa un jump si el contenido del registro cx es cero.

Implementación de la estructura de control if-then:


if (condition)
{
/* action statements */
}
<rest of program>

Esto consiste de una condición para ser evaluada y una acción a ser tomada si la condición es verdadera.

Ejemplo 3.27:

C versión:

if ( i == 10 )
{
i=i+5;
j=j+5;
}
/* Rest of program */

Existen dos formas de escribir esto en Assembler. Un método es testear si la condición (i == 10) es verdadera. Esto
bifurca la salida del carry out si la condición es verdadera. Si la condición es falsa, existe una segunda salida del programa.

Esto es escrito como:

8086 version 1:

cmp i, 10
je label1 ; if i == 10 goto label1
jmp rest ; otherwise goto rest
label1: add i, 5
add j, 5
rest: ; rest of program

El segundo metodo es testear si la condición (i != 10) es verdadera, bifurcando el carry out al resto del programa en
este caso. Si este no es el caso, entonces las instrucciones son ejecutadas:

8086 versión 2:

cmp i, 10
jne rest ; if i != 10 goto rest
add i, 5 ; otherwise do action part
add j, 5
rest: ; rest of program

El segundo método solo requiere un pequeño cambio en la instrucción.

De esta manera, en general, para implementar un if-then en el lenguaje Assembler, nosotros debemos testear la
inversa de la condición que deseamos usar en el lenguaje de alto nivel, asi como se demostro en la versión 2 anteriormente.

Implementación de la estructura de control if-then-else:


La forma general de la estructura de control en C es:

if ( condition )
{
/* action1 statements */
}
else
{
/* action2 statements */
}

Ejemplo 3.28: Escribe un fragmento de codigo para leer un carácter ingresado por el usuario y compara este al carácter 'A'.
Muestra un mensaje apropiado si el usuario ingresa una 'A'. Este fragmento de código es básico para un juego de
adivinanzas.

Version en C:

printf(“Guessing game: Enter a letter (A to Z): “);


c = getchar() ;
if ( c == ‘A’ )
printf(“You guessed correctly !! “);
else
printf(“Sorry incorrect guess “) ;

Versión 8086:

mov ax, offset prompt ; prompt user


call puts
call getc ; read character

cmp al, ‘A’ ; compare it to ‘A’


jne is_not_an_a ; jump if not ‘A’
mov ax, offset yes_msg ; if action
call puts ; display correct guess
jmp end_else ; skip else action

is_not_an_A: ; else action


mov ax, offset no_msg
call puts ; display wrong guess

end_else:

Si el valor leído es la letra 'A', entonces el jne no sera ejecutado, yes_msg sera mostrado y el control se transferirá
a end_else. Si el valor ingresado no es 'A', entonces el jne es ejecutado y el control es transferido a is_not_an_A.

Ejemplo 3.29: el código completo para jugar un juego de adivinanzas basado en el fragmento de código anterior es:

; guess.asm: Guessing game program.


;User is asked to guess which letter the program ‘knows’
; Author: Joe Carthy
; Date: March 1994
.model small
.stack 100h
CR equ 13d
LF equ 10d

.data
prompt db “Guessing game: Enter a letter (A to Z): $“
yes_msg db CR, LF,“You guessed correctly !! $“
no_msg db CR, LF,“Sorry incorrect guess $“

.code
start:
mov ax, @data
mov ds, ax
mov ax, offset prompt
call puts ; prompt for input
call getc ; read character
cmp al, ‘A’
jne is_not_an_a ; if (al != ‘A’) skip action
mov ax, offset yes_msg ; if action
call puts ; display correct guess
jmp end_else1 ; skip else action
is_not_an_A: ; else action
mov ax, offset no_msg
call puts ; display wrong guess
end_else1:

finish: mov ax, 4c00h


int 21h
; User defined subprograms
; < puts getc defined here>
end start

Nota: En este programa nosotros usamos la etiqueta end_else1 para indicar el fin de la estructura if-then-else.

Esto es importante, si usas esta estructura un número de veces en el programa, emplear diferentes etiquetas cada
vez que la estructura es usada. De esta manera una etiqueta como end_else2 puede ser usada para la segunda ocurrencia de
la contrucción aunque para esto es preferido algo mas significativo como is_not_an_A.

Ejemplo 3.30: Modificar el programa 3.19, el cual convierte una letra mayúscula a minúscula, para testear la letra ingresada.
Testear si la letra es mayúscula, nosotros necesitamos testear si su código ASCII esta en el rango entre 65 a 90 ('A' a Z'').
En C sería:

if ( c >= ‘A‘ && c <= ‘Z‘ )


/* it is uppercase letter */

Lo contrario sería:

if ( c < ‘A‘ || c > ‘Z‘ )


/* it is not uppercase letter */

La variable c contiene el código ASCII del carácter ingresado. Este es inicialmente comparado con el código ASCII de 'A' y
'Z'.

La notacion && usada en la primera condición, entiéndase como AND, si el valor de c es mayor o igual que 'A' y es menor
o igual que 'Z', entonces c contiene una letra mayúscula.

La notación | | usada en la segunda condición, entiéndase como OR, en otras palabras, si el valor de c es menor que
'A' o si éste es mayor que 'Z', no es una letra mayúscula. Nosotros usamos la primera condición en el programa para 8086
abajo.
Versión C:

main() /* char.c: convert letter to lowercase */


{
char c;
printf(“\nEnter an uppercase letter: “);
c = getchar();
if ( c >= ‘A‘ && c <= ‘Z‘ )
{
c = c + ( ‘a’ - ‘A’ ) ;
/* convert to lowercase */
printf(“\nThe lowercase equivalent is: %c “,
c);
}
else
printf(“\nNot an uppercase letter %c “, c );
}
}

; char3.asm: character conversion: uppercase to lowercase


; Author: Joe Carthy
; Date: March 1994
.model small
.stack 100h
CR equ 13d
LF equ 10d

.data
msg1 db CR, LF,‘Enter an uppercase letter: $’
result db CR, LF,‘The lowercase equivalent is: $’
bad_msg db CR, LF,‘Not an uppercase letter: $’

.code ; main program

start:
mov ax, @data
mov ds, ax
mov ax, offset msg1
call puts
call getc ; read uppercase letter
mov bl, al ; save character in bl
cmp bl, ‘A‘
jl invalid ; if bl < ‘A‘ goto invalid
cmp bl, ‘Z‘ ; if bl > ‘Z‘ goto invalid
jg invalid
; otherwise its valid
add bl, 32d ; convert to lowercase
mov ax, offset result
call puts ; display result message
mov al, bl
call putc ; display lowercase letter
jmp finish

invalid:
mov ax, offset bad_msg ; not uppercase
call puts ; display bad_msg
mov al, bl
call putc ; display character entered

finish:
mov ax, 4c00h
int 21h ; return to ms-dos
; subprograms getc, putc and puts should be defined here
end start

Este programa produce como salida, asumiendo que el digito 8 es ingresado:

Enter an uppercase letter: 8


Not an uppercase letter: 8

Este produce como salida, asumiendo que la letra Y es ingresada:

Enter an uppercase letter: Y


The lowercase equivalent is: y

Ejercicios:

3.13Escribe un programa para leer un dígito y mostrar un mensaje de error si no es un dígito el ingresado

3.14 En el fragmento de código a continuación donde será ejecutado desde <jump-on-condition> es reemplazado por (a) je
lab1; (b) jg lab1; (c ) jle lab1; (d) jz lab1

(i) mov ax, 10h


cmp ax, 9h
<jump-on-condition>
; rest of program
...........
...........
lab1:
(ii) mov cx, 0h
cmp cx, 0d
<jump-on-condition>
; rest of program
...........
...........
lab1:

3.15Escribe un programa para testear un carácter ingresado por teclado y que transfiera el control a la etiqueta ok_here, si
el carácter es:

(I) un letra minúscula válida ('a' <= carácter <= 'z')


(II) cualquier letra mayúscula o minúscula ('A' <= carácter <='Z' o 'a' <= carácter <= 'z')
(III) que no sea una letra minúscula, por ejemplo un carácter < 'a' o > 'z'.
El programa debe mostrar mensajes acordes al ingreso, indicando que carácter satisfizo el test.

Bucles (Loops):
Nosotros ya hemos visto como podemos implementar bucles utilizando la instrucción jmp para hacer jumps hacia
atrás en un programa. Sin embargo, notamos que jmp es un jump incondicional, se pueden generar loops infinitos. La
solución a esto es usar jumps condicionales. Por ejemplo, un while loop para mostrar por pantalla el carácter '*' 60 veces
puede ser implementado como en el ejemplo 3.31.

version en C:
count = 1 ;
while ( count <= 60 )
{
putchar(‘*’) ;
count = count + 1 ;
}

versión 8086 :

mov cx, 1d ; cx = 1
mov al, ‘*’ ; al = ‘*’
disp_char:
cmp cx, 60d
jnle end_disp ; if cx > 60 goto end_disp
call putc ; display ‘*’
inc cx ; cx = cx + 1
jmp disp_char ; repeat loop test
end_disp:

La instrucción jnle (jump si no es menor o igual) puede ser escrita tambien como jg (jump si es mayor que). Nosotros
usamos una técnica similar en la implementación de la estructura if – then en la que testeamos la inversa de la condición
usada en el fragmento de código C (count <= 60). Esto nos permite a nosotros escribir código en Assembler más
entendible.

Ejemplo 3.32: Escriba un fragmento de código que muestre el carácter de la 'a' a la 'z' en la pantalla usando el conocimiento
que el código ASCII tiene una secuencia específica. Esto significa que el código para 'b' es más grande que que el código
para 'a' y el código para 'c' es más grande que el código para 'b' y asi sucecisavamente.

Versión C

c = ‘a‘ ; /* c = 97 (ASCII for ‘a‘)


while ( c <= ‘z‘ )
{
putchar( c );
c=c+1;
}

Versión 8086 :

mov al, ‘a’


startloop:
cmp al, ‘z’
jnle endloop ; while al <= ‘z’
call putc ; display character
inc al ; al = al + 1
jmp startloop ; repeat test
endloop:

Este programa produce como salida:

abcdefghijklmnopqrstuvwxyz

En los próximos dos ejemplos, nosotros especificaremos cuantas veces el loop hará iteraciones.

Frecuentemente encontramos casos donde nosotros no conocemos cuantas veces el bucle será ejecutado. Por ejemplo,
en cada iteración preguntamos al usuario si el bucle debe ser repetido y continuamos o no en base a la respuesta del usuario.
Ejemplo 3.33: El programa 3.19 lee una letra mayúscula, la convierte a minúscula y muestra a pantalla. Nosotros
modificaremos esto, para que el usuario pueda repetir este proceso tantas veces como desee. Al usuario le preguntamos si
quiere continuar con la tecla 'y' despues de cada iteración.

Versión C:

main()
{
char c, reply;
reply = ‘y‘;
while ( reply == ‘y‘ )
{
printf(“\nEnter an uppercase letter: “);
c = getchar();
c = c + ( ‘a’ - ‘A’ ) ; /* convert to lowercase */
printf(“\nThe lowercase equivalent is: %c “, c);
printf(“\nEnter y to continue: “);
reply = getchar();
}
}

Versión 8086:

; char4.asm: character conversion: upper to lowercase


.model small
.stack 100h
CR equ 13d
LF equ 10d

.data
reply db ‘y’
msg0 db CR, LF, ‘Enter y to continue: $’
msg1 db CR, LF, ‘Enter an uppercase letter: $’
result db CR, LF, ‘The lowercase equivalent is: $’

.code
; main program
start:
mov ax, @data
mov ds, ax
readloop:
cmp reply, ‘y’ ; while (reply == ‘y‘)
jne finish ; do loop body
mov ax, offset msg1
call puts ; prompt for letter
call getc ; read character
mov bl, al ; save character in bl
add bl, 32d ; convert to lowercase
mov ax, offset result
call puts ; display result message
mov al, bl
call putc ; display lowercase letter
mov ax, offset msg0
call puts ; prompt to continue
call getc ; read reply
mov reply, al ; save character in reply
jmp readloop ; repeat loop test
finish:
mov ax, 4c00h
int 21h ; return to ms-dos
; user defined subprograms should be defined here
end start

Ejecutando este programa obtenemos como resultado, asumiendo que el usuario ingresa el carater C, X y n:

Enter an uppercase letter: C


The lowercase equivalent is: c
Enter y to continue: y
Enter an uppercase letter: X
The lowercase equivalent is: x
Enter y to continue: n

Ejercicios:

3.16 Modifique el programa del ejemplo 3.33 para testear que la letra ingresada es una letra minúscula válida. Si no lo es
muestre un mensaje de error y el programa debe continuar ejecutando tanto tiempo como el usuario desee.

3.17 Modifique el juego de adivinanzas (Programa 3.29) para que permita al usuario ingresar tres adivinanzas, terminando
si ninguna es correcta.

3.18 Modifique el juego de adivinanzas para que permita al usuario ingresar las adivinanzas cuantas veces desee,
terminando si ninguna es correcta.

3.19 Modifique el juego de adivinanzas para que el bucle itere hasta que la respuesta correcta sea ingresada.

Bucles con contador:


Bucles con contador, son aquellos que sabemos cuantas veces van a repetir el cuerpo del loop, ocurre
frecuentemente en la programación y como resultado la mayoría de los lenguajes de alto nivel tienen una construcción
especial llamada “bucle for” para implementar ésto.
En el programa 3.31, para mostrar el carácter '*' 60 veces, nosotros contamos incrementando de 1 a 60 , testeando
cada vez en el loop para ver si hemos llegado a 60. En Assembler, es común contar decrecientemente, por ejemplo de 60 a
0.
Debido a que este tipo de situación ocurre frecuentemente en la programación, ha sido implementada usando la
instrucción loop en Assembler tambien.
La instrucción loop combina el testeo del registro cx con cero y el decremento del registro cx en una simples
instrucción, por ejemplo, la instrucción loop decrementa cx en 1 y testea si cx es igual a cero.
Esto causa un jump si cx no es igual a 0. Esto únicamente puede ser usado en conjunción con el registro cx
(conocido como el registro contador o count register), por ejemplo, el registro cx es inicializado con el numero de veces que
el loop debe ser repetido. El Programa 3.31 puede ser reescrito para usar la instrucción loop como se indica a continuación:

Ejemplo 3.36: Usando la instrucción loop.

mov al, ‘*’ ; al = ‘*’


mov cx, 60d ; cx = 60 ; loop count
disp_char:
call putc ; display ‘*’
loop disp_char ; cx = cx - 1, if (cx != 0)
goto disp_char

Aquí, el registro cx es inicializado a 60, el numero de iteraciones requeridas.


La instrucción loop disp_char primero decrementa cx y entonces testea si cx no es igual a 0, bifurcando a disp_char
únicamente si cx no es igual a cero.
Formato general de la instrucción loop:

mov cx, count ; count = # of times to repeat


loop
start_loop: ; use any label name
<loop body> ; while cx > 0
; repeat loop body
instructions
loop start_loop

Para usar la instrucción loop, simplemente almacene el número de iteraciones requeridas en el registro cx y
contruya un cuerpo para el loop. La última instrucción del cuerpo del loop es la instrucción loop.

Nota 1: El cuerpo del loop siempre será ejecutado por última vez, desde la instrucción loop testea el valor del registro cx
despues de ejecutar el cuerpo del loop.

Nota 2: ¿Qué pasa si cx es inicializado a cero? La instrucción loop decrementa cx testeando antes la condición (cx != 0).
De esta manera nosotros continuamos iterando el loop, con cx comenzando por los negativos. Esto se repetirá 65.536 veces.
Porqué? La razón es por que nosotros substraemos 1 del registro cx hasta que encontremos 0. Eventualmente, al hacer cx
mas negativo, el mayor número negativo que cx puede contener será alcanzado. Cx es un registrador de 16 bits, y nosotros
sabemos del apendice 2, que este número es -32768d, lo cual representa en un numero de 16 bits 1000 0000 0000 0000.
Sustrayendo 1 a este campo de 16 bits obtenemos 0111 1111 1111 1111 o 32767d.
Nosotros podemos subtraer 1 de este número 32767 veces antes de alcanzar 0, con lo cual termina la instrucción
loop. De esta manera el total de veces que itera es 32768 + 32767 + 1 equivalente a 65535 + ( el 1 extra es porque cx
empieza de cero y es decrementado en 1 depues del test).

También podría gustarte