técnicas de programación

49
INDICE INDICE INDICE INDICE Contenido Pág. Contenido Pág. Contenido Pág. Contenido Pág. TÉCNICAS DE PROGRAMACIÓN TÉCNICAS DE PROGRAMACIÓN TÉCNICAS DE PROGRAMACIÓN TÉCNICAS DE PROGRAMACIÓN ORGANIZACIÓN EN BASE DE RUTINAS ORGANIZACIÓN EN BASE DE RUTINAS ORGANIZACIÓN EN BASE DE RUTINAS ORGANIZACIÓN EN BASE DE RUTINAS PROCEDIMIENTOS…………………………………………………………… Técnicas de invocación de subrutinas………………………………………. Ejecución De Subrutinas………………………………………………………. Ventajas De La Ejecución De Subrutinas…………………………………….. Las Instrucciones De Llamada Y Retorno De Una Subrutina………………. Call……………………………………………………………………… Call Cercana……………………………………………………….. Call Lejana………………………………………………………….. Instrucciones Call Con Operandos Registro………………………… Instrucciones Call A Direcciones Indirectas En La Memoria……….. Ret……………………………………………………………………… Paso De Parámetros Y Devolución De Resultados………………… Paso De Parámetros A Través De Registro…………………. Paso De Parámetros A Través De Memoria………………... Paso De Parámetros A Través De La Pila…………………… Almacenamiento De Variables Locales A Una Subrutina….. Gestión Del Bloque De Activación…………………………………………. ACCESOS CONDICIONALES ACCESOS CONDICIONALES ACCESOS CONDICIONALES ACCESOS CONDICIONALES………………………………………………… ………………………………………………… ………………………………………………… ………………………………………………… Grupo De Instrucciones De Salto…………………………………………… MANEJO DE TABLAS MANEJO DE TABLAS MANEJO DE TABLAS MANEJO DE TABLAS………………………………………………………… ………………………………………………………… ………………………………………………………… ………………………………………………………… Conversión de binario a ASCII………………………………………………. Conversión de ASCII a binario……………………………………………….. Empleo De Tablas Para Conversiones De Datos……………………………. Empleo de una tabla para accesar a datos ASCII……………………………

Upload: edgarnoelr

Post on 07-Jun-2015

5.551 views

Category:

Documents


0 download

DESCRIPTION

Técnicas de Programación

TRANSCRIPT

Page 1: Técnicas de Programación

INDICEINDICEINDICEINDICE

Contenido Pág.Contenido Pág.Contenido Pág.Contenido Pág.

TÉCNICAS DE PROGRAMACIÓNTÉCNICAS DE PROGRAMACIÓNTÉCNICAS DE PROGRAMACIÓNTÉCNICAS DE PROGRAMACIÓN

ORGANIZACIÓN EN BASE DE RUTINASORGANIZACIÓN EN BASE DE RUTINASORGANIZACIÓN EN BASE DE RUTINASORGANIZACIÓN EN BASE DE RUTINAS

PROCEDIMIENTOS……………………………………………………………

Técnicas de invocación de subrutinas……………………………………….

Ejecución De Subrutinas………………………………………………………. Ventajas De La Ejecución De Subrutinas…………………………………….. Las Instrucciones De Llamada Y Retorno De Una Subrutina……………….

Call……………………………………………………………………… Call Cercana……………………………………………………….. Call Lejana…………………………………………………………..

Instrucciones Call Con Operandos Registro………………………… Instrucciones Call A Direcciones Indirectas En La Memoria……….. Ret……………………………………………………………………… Paso De Parámetros Y Devolución De Resultados…………………

Paso De Parámetros A Través De Registro…………………. Paso De Parámetros A Través De Memoria………………... Paso De Parámetros A Través De La Pila…………………… Almacenamiento De Variables Locales A Una Subrutina…..

Gestión Del Bloque De Activación………………………………………….

ACCESOS CONDICIONALESACCESOS CONDICIONALESACCESOS CONDICIONALESACCESOS CONDICIONALES…………………………………………………………………………………………………………………………………………………………………………………………………………

Grupo De Instrucciones De Salto……………………………………………

MANEJO DE TABLASMANEJO DE TABLASMANEJO DE TABLASMANEJO DE TABLAS…………………………………………………………………………………………………………………………………………………………………………………………………………………………………………

Conversión de binario a ASCII……………………………………………….

Conversión de ASCII a binario………………………………………………..

Empleo De Tablas Para Conversiones De Datos…………………………….

Empleo de una tabla para accesar a datos ASCII……………………………

Page 2: Técnicas de Programación

DOCUMENTACIÓN DEL SOFTWAREDOCUMENTACIÓN DEL SOFTWAREDOCUMENTACIÓN DEL SOFTWAREDOCUMENTACIÓN DEL SOFTWARE…………………………………….…………………………………….…………………………………….…………………………………….

Como documentar un sistema de software………………………………

Objetivo………………………………………………………………

Esquema………………………………………………………………

1. Requisitos…………………………………………………

2. Diseño…………………………………………………….

3. Pruebas…………………………………………………….

4. Reflexión…………………………………………………..

5. Apéndice…………………………………………………..

ELABORACIÓN DE COMENTARIOSELABORACIÓN DE COMENTARIOSELABORACIÓN DE COMENTARIOSELABORACIÓN DE COMENTARIOS……………………………………….……………………………………….……………………………………….……………………………………….

Documentación del código…………………………………….

Comentarios a nivel de especificación……………………………

Comentarios a nivel de implementación…………………………

PRÁCTICA #7PRÁCTICA #7PRÁCTICA #7PRÁCTICA #7…………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………

Estudio Del Sistema De Audio Del Computador……………………….. Síntesis De Sonido………………………………………………………….. Control Directo Del Altavoz……………………………………………… Control Del Altavoz Por El Temporizador………………………………

PRÁCTICA #8PRÁCTICA #8PRÁCTICA #8PRÁCTICA #8…………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………

Desarrollo De Una Aplicación Con El Sistema De Audio Del Computador

CONCLUSIÓNCONCLUSIÓNCONCLUSIÓNCONCLUSIÓN…………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………

BIBLIOGRAFÍA……………………………………………………BIBLIOGRAFÍA……………………………………………………BIBLIOGRAFÍA……………………………………………………BIBLIOGRAFÍA…………………………………………………………………………………………………………

ANEXOSANEXOSANEXOSANEXOS………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………..…………..…………..…………..

Page 3: Técnicas de Programación

INTRODUCCIÓNINTRODUCCIÓNINTRODUCCIÓNINTRODUCCIÓN

Las técnicas de desarrollo y diseño de programas que se utilizan en la programación convencional tienen inconvenientes, sobre todo a la hora de verificar y modificar un programa. En la actualidad están adquiriendo gran importancia las técnicas de programación, cuyo objetivo principal es el de facilitar la comprensión del programa, y además permiten, de forma rápida, las ampliaciones y modificaciones que surjan en la fase de explotación del ciclo de vida de un programa o una aplicación informática

Algunas técnicas de programación, con el empleo de programas como el macro ensamblador, emu8686, turbo C, serán ampliadas en este texto, las llamadas a funciones saltos así como empleo de rutinas y subrutinas en conjunto sirven de n enorme ayuda. Algunas técnicas de programación incluyen secuencias de macros, manejo de teclado y el monitor, módulos de programas, archivos de bibliotecas, interrupciones y otras importantes técnicas de programación.

Esta sección es una introducción a la programación, incluyendo técnicas valiosas de para programación que son de enorme ayuda para poder desarrollar programas para computadora personal.

El programa ensamblador convierte un modulo simbólico fuente (archivo) en un archivo objeto hexadecimal. Las instrucciones para control del programa dirigen su flujo y permiten cambiarlo. A menudo ocurre un cambio de fujo después que las decisiones están tomadas con una instrucción CMP o TEST van regidas de una instrucción para brinco condicional.

Un procedimiento es un grupo de instrucciones que, por lo general, desempeñan una tarea. Un procedimiento es una sección de un programa que se puede volver a utilizar y que se almacene una vez en la memoria, pero se emplea tan a menudo como se necesite. Esto ahorra espacio en la memoria y facilita el desarrollo de la programación. La única desventaja de un procedimiento es que la computadora requiere un poco de tiempo para ligarse con el procedimiento y retornar desde el. La instrucción CALL liga el procedimiento y la instrucción RET retorna del procedimiento.

La dirección para el retorno se almacena en la pila siempre que se llama a un procedimiento durante la ejecución de un programa. La instrucción CALL salva la dirección de la instrucción, que va después de ella, en la pila. La instrucción RET recupera una dirección de la pila a fin de que el programa retorne a la instrucción que sigue al CALL.

Page 4: Técnicas de Programación

RUTINASRUTINASRUTINASRUTINAS

Para crear un programa ejecutable a partir de un código objeto se

requiere que se resuelvan las llamadas a otros programas y a los servicios del

sistema operativo, y agregar las rutinas o información de runtime para que el

programa pueda ser cargado a memoria y ejecutado. Las rutinas de runtime

son necesarias, puesto que el sistema operativo requiere tener control sobre el

programa en cualquier momento, además de que la asignación de recursos y su

acceso deben hacerse solamente a través del sistema operativo.

Las rutinas son uno de los recursos más valiosos cuando se trabaja en

programación; ellas permiten que los programas sean más simples, debido a que

el programa principal se disminuye cuando algunas tareas se escriben en forma

de programas pequeños e independientes, en otras palabras se define como un

procedimiento independiente (un bloque), que realiza una labor específica y a la

cual se puede llamar desde cualquier parte del programa principal dado que cada

rutina realiza una labor en particular, como encender un LED o establecer una

base de tiempo de un segundo, por ejemplo, el programador, cuando está

seguro de su funcionamiento, puede mantenerla almacenada y disponible en un

«banco» o «librería», para utilizarla en cualquier programa, sin volver a escribir las

líneas que realicen lo deseado: sólo necesitará copiar el bloque de la rutina o

tener el archivo disponible para cuando se realice la compilación del programa.

Posteriormente, deberá hacer el llamado al procedimiento adecuado, en el

instante preciso.

Para la elaboración de cualquier rutina, es necesario conocer más a fondo

las instrucciones que la forman y cuales son las condiciones o estados que se

modifican ante cada una de ellas. Los estados, banderas o flags son bits que se

afectan por algunas operaciones lógicas o aritméticas y la consulta de estos se

utiliza para tomar decisiones dentro del programa.

Page 5: Técnicas de Programación

Además, una interrupción es una operación que invoca la ejecución de

una rutina específica que suspende la ejecución del programa que la llamó, de tal

manera que el sistema toma control del computador colocando en el stack el

contenido de los registros CS e IP. El programa suspendido vuelve a activarse

cuando termina la ejecución de la interrupción y son restablecidos los registros

salvados.

PROCEDIMIENTOSPROCEDIMIENTOSPROCEDIMIENTOSPROCEDIMIENTOS

El procedimiento o subrutina es parte importante de la arquitectura de

cualquier sistema de computadora. Un procedimiento es un grupo de instrucciones que, por lo general, desempeñan una tarea. Un procedimiento es una sección de un programa que se puede volver a utilizar y que se almacena una vez en la memoria, pero se emplea tan a menudo como se necesite. Esto ahorra espacio en la memoria y facilita el desarrollo de la programación. La única desventaja de un procedimiento es que la computadora requiere un poco de tiempo para ligarse con el procedimiento y retornar desde el. La instrucción CALL liga el procedimiento y la instrucción RET retorna del procedimiento.

La dirección para el retorno se almacena en la pila siempre que se llama a

un procedimiento durante la ejecución de un programa. La instrucción CALL salva la dirección de la instrucción, que va después de ella, en la pila. La instrucción RET recupera una instrucción de la pila a fin de que el programa retorne a la instrucción que sigue al CALL.

La mayor parte de los procedimientos que se utilizan con todos los programas (globales) se deben redactar como procedimientos lejanos. Los procedimientos que se utilizan para una tarea dada (locales) se suelen definir como procedimientos cercanos.

EJECUCIÓN DE SUBRUTINASEJECUCIÓN DE SUBRUTINASEJECUCIÓN DE SUBRUTINASEJECUCIÓN DE SUBRUTINAS

La estructura global de un programa es una combinación de bloques para los cuales existe una traducción sistemática. Esta y la codificación de los datos son las dos principales tareas de un compilador para obtener un ejecutable.

El mecanismo que merece un estudio aparte es el de llamada a subrutinas.

El desarrollo de programas modulares se basa en la posibilidad de ejecutar un bloque de código múltiples veces con diferentes valores de un conjunto de

Page 6: Técnicas de Programación

variables denominadas ‘parámetros’ que produce un resultado. Este mecanismo, con diferentes matices, es lo que se denomina como procedimientos, funciones, subprogramas o métodos y están presentes en prácticamente todos los lenguajes de programación de alto nivel.

En el contexto del lenguaje ensamblador se define una subrutina como

una porción de código que realiza una operación en base a un conjunto de valores dados como parámetros de forma independiente al resto del programa y que puede ser invocado desde cualquier lugar del código, incluso desde dentro de ella misma.

La ejecución de subrutinas tiene las siguientes ventajas:La ejecución de subrutinas tiene las siguientes ventajas:La ejecución de subrutinas tiene las siguientes ventajas:La ejecución de subrutinas tiene las siguientes ventajas:

Evita código redundante.Evita código redundante.Evita código redundante.Evita código redundante. Durante el diseño de un programa suelen existir ciertos cálculos que deben realizarse en diferentes lugares del código. La alternativa a replicar las instrucciones es encapsularlas en una subrutina e invocar esta cada vez que sea necesario lo cual se traduce en código más compacto.

FacFacFacFacilita la descomposición de tareasilita la descomposición de tareasilita la descomposición de tareasilita la descomposición de tareas. La descomposición de tareas complejas en secuencias de subtareas más simples facilita enormemente el desarrollo de programas. Esta técnica se suele aplicar de forma sucesiva en lo que se denomina ‘diseño descendente’ de programas. Cada subtarea se implementa como una rutina.

Facilita el encapsulado de códigoFacilita el encapsulado de códigoFacilita el encapsulado de códigoFacilita el encapsulado de código. El agrupar una operación y sus datos en una subrutina y comunicarse con el resto de un programa a través de sus parámetros y resultados, hace que si en algún momento se cambia su implementación interna, el resto del programa no requiera cambio alguno.

Además de estas ventajas, el encapsulado de código también facilita la reutilización de su funcionalidad en más de un programa mediante el uso de ‘bibliotecas’. Una biblioteca de funciones es un conjunto de subrutinas que realizan cálculos muy comunes en la ejecución de programas y que pueden ser utilizados por éstos. Java es un ejemplo de lenguaje que dispone de bibliotecas de clases que en su interior ofrecen multitud de métodos.

La desventaja de las subrutinas es que es necesario establecer un protocolo que defina dónde y cómo se realiza esta transferencia de datos para la que se requieren múltiples instrucciones máquina adicionales.

Tecnicas de programación de una sTecnicas de programación de una sTecnicas de programación de una sTecnicas de programación de una subrutinaubrutinaubrutinaubrutina

La politica general que se recomienda para la escritura de subrutina es:

Page 7: Técnicas de Programación

1.1.1.1.---- Si la subrutina va a llamar a otras subrutinas, almacenar en pila la dirección de retorno, para permitir el anidamiento de subrutinas.

2.2.2.2.---- Almacenar en la pila el contenido de los registros que utilice para cálculos temporales.

El punto numero dos, es crucial en la programación en esamblador, debido a que si una subrutina cambia el contenido de registros que el programa principal utiliza, el resultado puede ser impredecible.

Para evitar que esto suceda, existen dos técnicas que se denominan:

a) El invocador guarda los registros (caller save)

b) El invocado guarda los registros (callee save)

En la primera tecnica, el programa que invoca a la subrutina tiene la responsabilidad de guardar en pila los registros que le son necesarios, para que las rutinas las modifique a su antojo.

En la segunda técnica, la subrutina invocada es la responsable de dejar el contenido de los registros tal y como estaban en el momento en el que fue invocada. Para ello los salva inicialmente en la pila y restaura su valor justo antes del retorno.

LAS INSTRUCCIONES DE LLAMADA Y RETORNO DE UNA SUBRUTINALAS INSTRUCCIONES DE LLAMADA Y RETORNO DE UNA SUBRUTINALAS INSTRUCCIONES DE LLAMADA Y RETORNO DE UNA SUBRUTINALAS INSTRUCCIONES DE LLAMADA Y RETORNO DE UNA SUBRUTINA

En ensamblador la llamada a una subrutina se realiza mediante la

instrucción CALL cuyo único operando es la dirección de memoria, generalmente una etiqueta, en la que comienza su código. Tras ejecutar esta instrucción el procesador continua ejecutando la primera instrucción de la subrutina hasta que encuentra la instrucción RET que no tiene operandos y transfiere la ejecución a la instrucción siguiente al CALL que inició el proceso. La siguiente figura ilustra esta secuencia.

CALLCALLCALLCALL

La instrucción CALL transfiere el flujo del programa al procedimiento. Esta instrucción difiere de las instrucciones para brinco (salto) porque CALL salva una dirección para retorno en la pila. La dirección de retorno devuelve el control a la instrucción que sigue a CALL en un programa, cuando se ejecuta una instrucción RET.

� CALL cercanaCALL cercanaCALL cercanaCALL cercana

Page 8: Técnicas de Programación

La instrucción CALL cercana tiene 3 bytes de longitud; el primer byte contiene

el código y el segundo y tercero contiene el desplazamiento o distancia de ± 32K en los 8086-80286. La forma de instrucción es idéntica a la de brinco cercano. En los 80386 y 80486 se emplea un desplazamiento de 32 bits cuando trabajan en el modo protegido a fin de permitir una distancia de ± 2G bytes. Cuando se ejecuta un CALL cercano, primero salva en la pila la dirección de desplazamiento de la siguiente instrucción. Esa dirección está en el apuntador de instrucciones (IP o EIP). Después de salvar la dirección de retorno, suma los desplazamientos de los bytes 2 y 3 a IP para transferir el control al procedimiento. No hay instrucción CALL corta.

¿Por qué salvar IP o EIP en la pila? El apuntador de instrucciones siempre apunta a la siguiente instrucción en el

programa. Para la instrucción CALL, se salva el contenido de IP o EIP dentro de la pila, con lo cual el control del programa pasa a la instrucción que sigue al CALL después de que termina un procedimiento. En la siguiente figura se ilustran la dirección de retorno (IP) salva en la pila y la llamada a procedimiento para los 8086-80286.

El efecto de una instrucción CALL cercana en la pila y en los registros SP, SS e IP. Se ve la forma en que se almacena IP viejo en la pila.

� CALL lejanaCALL lejanaCALL lejanaCALL lejana

La instrucción CALL lejana es como un brinco lejano porque puede llamar

a un procedimiento almacenado en cualquier localidad de la memoria en el sistema. La instrucción CALL lejana tiene 5 bytes y tiene un código de operación

Page 9: Técnicas de Programación

seguido por el valor de los registros IP y CS. Los bytes 2 y 3 tienen el nuevo contenido de IP y los bytes 4 y 5 tienen el nuevo contenido de CS.

La instrucción CALL lejana salva el contenido de IP y de CS en la pila antes

de brincar a la dirección indicada por los bytes 2 a 5 de la instrucción. Esto permite que la CALL lejana llame a un procedimiento ubicado en cualquier lugar en la memoria y que retorne desde el procedimiento.

La instrucción CALL lejana

En la figura anterior se ilustra la forma en que CALL lejana llama a un

procedimiento lejano. En este caso se salva el contenido de IP y CS dentro de la pila. Luego, el programa se transfiere al procedimieto.

Llamada y retorno de una subrutina

INSTRUCCIONES CALL CON OPERANDOS REGISTROINSTRUCCIONES CALL CON OPERANDOS REGISTROINSTRUCCIONES CALL CON OPERANDOS REGISTROINSTRUCCIONES CALL CON OPERANDOS REGISTRO.

Las instrucciones CALL al igual que las JMP pueden contener un operando registro y un ejemplo es la instrucción CALL BX. Esta instrucción salva el contenido de IP dentro de la pila. Después, brinca a la direccion de

Page 10: Técnicas de Programación

desplazamiento, ubicada en el registro BX en el segmento de código actual. Este tipo de llamada siempre utiliza una dirección de desplazamiento de 16 bits almacenada en cualquier registros de 16 bits, excepto los registros de segmento.

En el ejemplo anterior, se ilustra el ejemplo de una instrucción CALL con un registro para llamar a un procedimiento que comienza en la direccion de desplazamiento COMP, la cual se carga al registro SI y, luego la instrucción CALL SI llama al procedimiento que empieza en la dirección COMP. INSTRUCCIONES CALL A DIRECCIONES INDIRINSTRUCCIONES CALL A DIRECCIONES INDIRINSTRUCCIONES CALL A DIRECCIONES INDIRINSTRUCCIONES CALL A DIRECCIONES INDIRECTAS EN LA MEMORIAECTAS EN LA MEMORIAECTAS EN LA MEMORIAECTAS EN LA MEMORIA

La instrucción CALL a una dirección indirecta en la memoria es de particular utilidad siempre que se desea esciger diferentes subrutinas en un programa. Este proceso de selección va ligado frecuentemente con un numero que direcciona a una direccion de llamada en una tabla de consulta.

INVOCACION ANIDADA DE SUBRUTINAS.INVOCACION ANIDADA DE SUBRUTINAS.INVOCACION ANIDADA DE SUBRUTINAS.INVOCACION ANIDADA DE SUBRUTINAS.

La instrucción RETRETRETRET de la subrutina B retorna la ejecución a la subrutina A

en su primera ejecución (denotada por la flecha número 3) y al programa principal en su segunda ejecución (denotada por la flecha número 6). Esto hace suponer, por tanto, que la dirección de retorno no puede ser decidida cuando se ejecuta esta instrucción sino en un momento anterior.

El instante en el que se sabe dónde ha de retomarse la ejecución tras una subrutina es precisamente en el momento de su invocación. Cuando el

Page 11: Técnicas de Programación

procesador está ejecutando la instrucción CALL obtiene la dirección de retorno como la de la instrucción siguiente en la secuencia.

Por lo tanto, el procesador, además de modificar la secuencia, al ejecutar la instrucción CALL debe guardar la dirección de retorno en un lugar prefijado del cual será obtenido por la instrucción RET. Pero, durante la ejecución de un programa es preciso almacenar múltiples direcciones de retorno de forma simultánea.

El que las subrutinas se puedan invocar de forma anidada hace que la utilización de los registros de propósito general para almacenar la dirección de retorno no sea factible. La alternativa es almacenarlas en memoria, pero la instrucción RET debe tener acceso a su operando implícito siempre en el mismo lugar. Además, esta zona de memoria debe poder almacenar un número arbitrario de direcciones de retorno, pues la invocación de subrutinas se puede anidar hasta niveles arbitrarios de profundidad.

Por tanto, se necesita un área de memoria que pueda almacenar tantas direcciones de retorno como subrutinas están siendo invocadas de forma anidada en un momento de la ejecución de un programa. La propiedad que tienen estas direcciones es que se almacenan por la instrucción CALL en un cierto orden, y son utilizadas por la instrucción RET en el orden inverso.

La estructura especialmente concebida para este propósito es la pila. En ella se almacena la dirección de retorno mientras se ejecuta el cuerpo de una subrutina. En caso de invocaciones anidadas, las direcciones de retorno pertinentes se guardan en la pila y están disponibles para la instrucción RET en el orden preciso.

La instrucción CALL, por consiguiente, realiza dos tareas: pasa a ejecutar la instrucción en la dirección dada como operando y almacena en la cima de la pila la dirección de la instrucción siguiente (al igual que lo haría una instrucción PUSH) que será la instrucción de retorno. Por su parte, la instrucción RET obtiene el dato de la cima de la pila (igual que lo haría la instrucción POP) y ejecuta un salto incondicional al lugar que indica. Ambas instrucciones, modifican el contador de programa.

Del funcionamiento de estas instrucciones se concluye que la cima de la pila justo antes de la ejecución de la primera instrucción de una subrutina contiene la dirección de retorno, entonces, antes de ejecutar la instrucción RET debe apuntar exactamente a la misma posición. Aunque esta condición es esencial para que el retorno de la subrutina se haga al lugar correcto, los

Page 12: Técnicas de Programación

procesadores no realizan comprobación alguna de que así se produce. Es responsabilidad del programador en ensamblador el manipular la pila en una subrutina de forma que la cima de la pila al comienzo de la ejecución sea exactamente la misma que justo antes de ejecutar la última instrucción.

Durante la ejecución de la subrutina se pueden hacer las operaciones necesarias sobre la pila siempre y cuando se conserve la dirección de retorno. El programa en ensamblador que comienza a ejecutar a partir de la etiqueta main también es una subrutina que invoca el sistema operativo, y por lo tanto se debe garantizar que la cima es idéntica al comienzo y al final del programa pues contiene la dirección de retorno.

RETRETRETRET

La instrucción para retorno (RET) extrae un numero de 16 bits (retorno cercano) de la pila y lo coloca en IP o bien un numero de 32 bits (retorno lejano) y lo coloca en IP y CS. Las instrucciones para retorno cercano y lejano estan definidas en el directivo PROC para el procedimiento. Esto selecciona en forma automática la instrucción adecuada para el retorno. En los 80386 y 80486 que trabajen en el modo protegido, el retorno lejano extrae 6 bytes de la pila; los primeros 4 contienen el nuevo valor para EIP y los dos ultimos el nuevo valor para CS. El retorno cercano para los 80386 y 80486 en modo protegido, extrae 4 bytes de la pila y los coloca en EIP.

Cuando se cambian IP o EIP, o IP o EIP y CS, la direccion de la siguiente instrucción esta en una nueva localidad en la memoria, la cual es la direccion de la instrucción que va inmediatamente despues de la llamada mas reciente a un procedimiento. En la siguiente figura se ilustra la forma en que una instrucción CALL se liga con un procedimiento y la forma en que la instrucción RET retorna en los microprocesadores 8086-80286.

Page 13: Técnicas de Programación

El efecto de una instrucción RET cercana en la pila y los registros SP, SS e IP.

Hay otra forma mas para la instrucción de retorno, la cual suma un

numero al contenido del apuntador de pila (SP) antes del retorno. Si hay que borrar lo salvado en la pila antes de un retorno, el desplazamiento de 16 bits se suma a SP antes de que el retorno recupere la direccion de retorno en la pila. El efecto es omitir o borrar datos de la pila.

EJEMPLOEJEMPLOEJEMPLOEJEMPLO

En este ejemplo se muestra la forma en que este tipo de retorno borra los datos salvados en la pila por algunas instrucciones PUSH. RET 4 suma 4 a SP antes de recuperar la direccion de retorno de la pila. Debido a que PUSH AX y PUSH BX, juntas, salvaron 4 bytes de datos en la pila, este retorno borra efectivamente a AX y BX de la pila. Este tipo de retorno rara vez aparece en los programas

PASO DE PARÁMETROS Y DEVOLUCIÓN DE RESULTADOSPASO DE PARÁMETROS Y DEVOLUCIÓN DE RESULTADOSPASO DE PARÁMETROS Y DEVOLUCIÓN DE RESULTADOSPASO DE PARÁMETROS Y DEVOLUCIÓN DE RESULTADOS

En general una subrutina consiste en una porción de código que realiza una operación con un conjunto de valores proporcionados por el programa que la invoca denominados parámetros, y que devuelve un resultado. Los

Page 14: Técnicas de Programación

parámetros son copias de ciertos valores que se ponen a disposición de la subrutina y que tras acabar su ejecución se descartan. El resultado, en cambio, es un valor que la subrutina calcula y copia en un lugar para que el programa invocador lo utilice. La figura siguiente ilustra la manipulación de parámetros y resultado.

Parámetros y resultado de una subrutina

Se necesita establecer las reglas que estipulen cómo y dónde deposita el programa que invoca una subrutina estos valores y cómo y dónde se deposita el resultado. En adelante, a la porción de código que realiza la llamada a la subrutina se le denominará ‘programa llamador’ mientras que al código de la subrutina se le denominará ‘programa llamado’. Las llamadas a subrutinas se puede hacer de forma ‘anidada’, es decir, un programa llamado invoca a su vez a otra subrutina con lo que pasa a comportarse como programa llamador.

� Paso de parámetros a través de registroPaso de parámetros a través de registroPaso de parámetros a través de registroPaso de parámetros a través de registro

El paso de parámetros a través de registro consiste en que el programa llamador y el llamado asumen que los parámetros se almacenan en ciertos registros específicos. Antes de la instrucción de llamada el programa llamador deposita los valores pertinentes en estos registros y la subrutina comienza a procesarlos directamente.

En general, dada una rutina que recibe n parámetros y devuelve m resultados, se necesita definir en qué registro deposita el programa llamador la copia de cada uno de los n parámetros, y en qué registro deposita la subrutina la copia del resultado obtenido. El ejemplo siguiente muestra las instrucciones necesarias en el caso de una subrutina que recibe como parámetros dos enteros a través de los registros %eax y %ebx y devuelve el resultado a través del registro %ecx.

Page 15: Técnicas de Programación

Ejemplo:Ejemplo:Ejemplo:Ejemplo: Paso de parámetros a través de registros

Al utilizar los registros %eax y %ebx para pasar los parámetros la subrutina no salva su contenido pues dispone de esos valores como si fuesen suyos. El registro %ecx, al contener el resultado, tampoco se debe salvar ni restaurar.

El principal inconveniente que tiene este esquema es el número limitado de registros de propósito general. En los lenguajes de alto nivel no hay límite en el número de parámetros que puede tener una función o método en su definición, y por tanto, si este número es muy alto, el procesador puede no tener registros suficientes.

A pesar de esta limitación, en el caso de subrutinas con muy pocos parámetros y que devuelve un único resultado, este mecanismo es muy eficiente pues el procesador no precisa almacenar datos en memoria. Los sistemas operativos suelen utilizar esta técnica para invocaciones de subrutinas internas de estas características.

� Paso dPaso dPaso dPaso de parámetros a través de memoriae parámetros a través de memoriae parámetros a través de memoriae parámetros a través de memoria

El paso de parámetros a través de memoria consiste en definir una zona de memoria conocida tanto para el programa llamador como para el llamado y en ella se copia el valor de los parámetros y el del resultado para su intercambio. La ventaja de esta técnica radica en que permite tener un número arbitrario de parámetros, pues tan sólo se requiere una zona más grande de memoria.

En general, para una subrutina que recibe n parámetros y devuelve m resultados se define una zona de memoria cuyo tamaño es la suma de los tamaños de todos ellos así como el orden en el que estarán almacenados. El

Page 16: Técnicas de Programación

siguiente ejemplo muestra las instrucciones necesarias para el caso de una subrutina que precisa tres parámetros de tamaño 32 bits y devuelve dos resultados de tamaño 8 bits. Se asume que la zona de memoria está definida a partir de la etiqueta params.

Ejemplo: Ejemplo: Ejemplo: Ejemplo: Paso de parámetros a través de memoria

El principal inconveniente de esta técnica es que necesita tener estas zonas de memoria previamente definidas. Además, en el caso de invocación anidada de subrutinas, se necesitan múltiples espacios de parámetros y resultados pues mientras la ejecución de una subrutina no termina, éstos siguen teniendo validez. El incluir esta definición junto con el código de una subrutina parecería una solución idónea, pues al escribir sus instrucciones se sabe el número y tamaño de parámetros y resultados. Pero existen subrutinas denominadas ‘recursivas’ que se caracterizan por contener una invocación a ellas mismas con un conjunto de parámetros diferente.

La conclusión es que se precisan tantas zonas para almacenar parámetros y

devolver resultados como invocaciones pendientes de terminar en cada momento de la ejecución. Pero este requisito de vigencia es idéntico al que tiene la dirección de retorno de una subrutina. Es más, la dirección de retorno se puede considerar un valor más que el programa llamador pasa al llamado para que éste lo utilice. En esta observación se basa la siguiente técnica de paso de parámetros.

Page 17: Técnicas de Programación

� Paso de parámetros a través de la pilaPaso de parámetros a través de la pilaPaso de parámetros a través de la pilaPaso de parámetros a través de la pila

El paso de parámetros a través de la pila tiene múltiples ventajas. En primer lugar, tanto parámetros como resultados se pueden considerar resultados temporales que tienen validez en un período muy concreto de la ejecución de un programa por lo que la pila favorece su manipulación. Además, dada una secuencia de llamadas a subrutinas, el orden de creación y destrucción de estos parámetros es el inverso tal y como permiten las instrucciones de gestión de la pila.

En general, para una subrutina que recibe n parámetro y devuelve m resultados el programa llamador reserva espacio en la cima de la pila para almacenar estos datos justo antes de ejecutar la instrucción CALL y lo elimina justo a continuación. Pero en la subrutina es necesario un mecanismo eficiente para acceder a la zona de parámetros y resultados. Al estar ubicada en la pila lo más intuitivo es utilizar el registro %esp que apunta a la cima y el modo de direccionamiento base + desplazamiento mediante la utilización de los desplazamientos pertinentes. Pero el inconveniente de este método es que la cima de la pila puede fluctuar a lo largo de la ejecución de la subrutina y por tanto los desplazamientos a utilizar varían.

Para que el acceso a los parámetros no dependa de la posición de la cima de la pila y se realice con desplazamientos constantes a lo largo de la ejecución de la subrutina, las primeras instrucciones almacenan una copia del puntero de pila en otro registro (generalmente %ebp) y al fijar su valor, los accesos a la zona de parámetros y resultados se realizan con desplazamientos constantes. Pero para preservar el valor de los registros, antes de crear este duplicado es preciso guardar en la pila una copia de este registro.

El ejemplo muestra las instrucciones necesarias para el caso de una

subrutina que precisa tres parámetros de tamaño 32 bits y devuelve un resultado de 8 bits.

Page 18: Técnicas de Programación

Ejemplo:Ejemplo:Ejemplo:Ejemplo: Paso de parámetros a través de la pila

La primera instrucción del programa llamador modifica el puntero de la pila para reservar espacio donde almacenar el resultado.

Al ser una posición de memoria sobre la que se escribirá el resultado no es preciso escribir ningún valor inicial, de ahí que no se utilice la instrucción push. A continuación se depositan en la pila los valores de los parámetros. El orden en que se almacenan debe ser conocido por el programa llamador y el llamado. Tras la ejecución de la subrutina se eliminan de la pila los parámetros, que al desempeñar ningún papel, basta con corregir el valor de la cima dejando la pila preparada para obtener el valor del resultado.

Por su parte, el programa llamado guarda la copia del registro %ebp para justo a continuación copiar el valor de %esp y fija su valor a la cima de la pila. A partir de este instante, cualquier dato que se ponga en la pila no afecta el valor de %ebp y el desplazamiento para acceder a los parámetros es respectivamente de 8, 12 y 16 pues en la posición debajo de la cima se encuentra la dirección de retorno. Para depositar el resultado se utiliza el desplazamiento 20. Tras terminar el cálculo del resultado se procede a deshacer la estructura de datos creada en la pila en orden inverso. Primero se descargan de la pila los registros salvados y a continuación se restaura el valor del registro %ebp dejando en la cima la dirección de retorno que necesita la instrucción ret.

Page 19: Técnicas de Programación

A la porción de memoria en la pila que contiene el espacio para la devolución de resultados, los parámetros, la dirección de retorno, la copia de %ebp se le denomina el ‘bloque de activación’. Al registro %ebp que ofrece un punto fijo de referencia a los datos se le denomina el ‘puntero’ al bloque de activación.

� Almacenamiento de variables locales a una subrutinaAlmacenamiento de variables locales a una subrutinaAlmacenamiento de variables locales a una subrutinaAlmacenamiento de variables locales a una subrutina

Además de la capacidad de definir y ejecutar subrutinas, los lenguajes de programación de alto nivel permiten la definición de variables locales. El ámbito de validez se reduce al instante en que se está ejecutando el código de la subrutina. De nuevo se precisa un mecanismo que gestione de forma eficiente estas variables. El ejemplo siguiente muestra la definición de un método en Java en el que las variables i, str y p son de ámbito local.

Ejemplo:Ejemplo:Ejemplo:Ejemplo: Definición de variables locales a un método

El ámbito de estas variables no impide que el valor de alguna de ellas sea devuelto como resultado tal y como muestra el método del ejemplo. La última línea copia el valor de la variable local en el lugar en el que se devuelve el resultado, y por tanto está disponible para el programa llamador.

El ámbito de estas variables es idéntico al de los parámetros y al de la

dirección de retorno, por lo que para almacenar estas variables se pueden utilizar cualquiera de las tres técnicas descritas anteriormente: en registros, en posiciones arbitrarias de memoria y en la pila.

El almacenamiento en la pila se hace en el bloque de activación justo a continuación de haber establecido el registro %ebp como puntero al bloque de activación. De esta forma, como el número de variables locales es siempre el mismo, utilizando desplazamientos con valores negativos y el registro base %ebp se accede a ellas desde cualquier punto de la subrutina.

Page 20: Técnicas de Programación

GESTIÓN DEL BLOQUE DE ACTIVACIÓNGESTIÓN DEL BLOQUE DE ACTIVACIÓNGESTIÓN DEL BLOQUE DE ACTIVACIÓNGESTIÓN DEL BLOQUE DE ACTIVACIÓN

De las técnicas descritas para la invocación de subrutinas, la que crea el bloque de activación en la pila es la más utilizada por los lenguajes de alto nivel. Se muestran los pasos a seguir por el programa llamador y el llamado para crear y destruir el bloque de activación.

Tras restaurar el valor de los registros utilizados por la subrutina, el estado

de la pila es tal que en la cima se encuentra el espacio para las variables locales y a continuación la copia del valor anterior de %ebp. Como el propio %ebp apunta a esa misma posición, la forma más fácil de restaurar la cima de la pila al valor correcto es asignándole a %esp el valor de %ebp. De esta forma no es preciso tener en cuenta el tamaño de la zona reservada para las variables locales. Esta técnica funciona incluso en el caso de que una subrutina no tenga variables locales.

Ejemplo de evolución del bloque de activEjemplo de evolución del bloque de activEjemplo de evolución del bloque de activEjemplo de evolución del bloque de activaciónaciónaciónación

Se considera un programa que invoca a la subrutina cuentacuentacuentacuenta que dada la dirección de un string terminado en cero y un carácter, devuelve el número de veces que el carácter aparece en el string como entero. La pila que recibe la subrutina tiene, en orden creciente de desplazamiento desde el puntero al bloque de activación, la dirección de retorno (siempre está presente como primer valor), la dirección del string, el carácter a comprobar como byte menos significativo del operando en la pila y el espacio para el resultado. El fragmento de código para invocar a esta subrutina se muestra en el siguiente ejemplo, se asume que la letra a buscar está almacenada en la etiqueta letraletraletraletra y el string en la etiqueta mensajemensajemensajemensaje.

Page 21: Técnicas de Programación

Pasos para la gestión del bloque de activación

Ejemplo:Ejemplo:Ejemplo:Ejemplo: Invocación de la rutina cuenta

La instrucción push letra tiene por operando una etiqueta que apunta a un dato de tamaño byte. Como los operandos de la pila son de 4 bytes, en ella se depositan la letra y los tres siguientes bytes. Esto no tiene importancia porque la subrutina únicamente accede al byte de menos peso tal y como se ha especificado en su definición. La figura muestra la evolución de la pila desde el punto de vista del programa llamador.

Page 22: Técnicas de Programación

Evolución de la pila desde el punto de vista del programa llamador

Ejemplo:Ejemplo:Ejemplo:Ejemplo: Código de la rutina cuenta

Page 23: Técnicas de Programación

ACCESOS CONDICIONALESACCESOS CONDICIONALESACCESOS CONDICIONALESACCESOS CONDICIONALES

Los accesos condicionales son aquellos que nos permiten controlar el flujo de ejecución de un programa escrito en lenguaje ensamblador es por ello que se van a describir de manera breve las instrucciones de control disponibles, la mayor parte de ellas se utilizan en conjunción con alguna instrucción que afecta el estado de las banderas la instrucción CMP sirve para este propósito por ejemplo la instrucción JA genera un salto si, después de la instrucción de comparación CF=ZF=0, la ejecución de las demás instrucciones de salto depende del estado de varios registros y banderas con excepción de la instrucción de salto incondicional JMP.

Por otra parte algunas de estas instrucciones de salto solo trabajan con cantidades sin signos mientras otras se aplican a operando con signos todos los saltos con excepción de JMP trasfieren control a una etiqueta destino denominada etiqueta corta lo anterior significa que la etiqueta debe encontrarse dentro del intervalo que va de -128 a + 127 bytes de la instrucción de salto.

En la siguiente secuencia de instrucciones:

CMP AH, AL

JAE CONT1

Se resta el registro AL al registro AH y la bandera de acarreo se activa, si el contenido de AH es menor que el de AL. El salto JAE ( salta si esta por encima o es igual a la etiqueta CONT1.todas estas instrucciones de accesos condicionales son similares asi que debemos familiarizarnos y comprender cabalmente como se lleva a cabo la transferencia de control de un programa

Otro ejemplo de uso de las instrucciones de salto es la siguiente:

CMP AH,DDDV ; Compare contenido de AH y DDDV

JNE CONT1 ; Salta a CONT1 si no igual a …

JMP CONT2 ; Vete a CONT1( incondicional)

CONT1: ……..

CONT2: …….

Primero se compara el contenido del registro AH con el de DDDV y si estos no son iguales entonces se transfiere el control a la etiqueta CONT1 de lo contrario se ejecuta el código que se encuentra inmediatamente después de la instrucción JNE

Page 24: Técnicas de Programación

GRUPO DE INSTRUCCIONES DE SALTOGRUPO DE INSTRUCCIONES DE SALTOGRUPO DE INSTRUCCIONES DE SALTOGRUPO DE INSTRUCCIONES DE SALTO

Instrucción Instrucción Instrucción Instrucción Propósito Propósito Propósito Propósito JA etiqueta corta

(JNBE) Salta si, por arriba/sino por debajo o es igual

JAE etiqueta corta

(JNB)

Salta si, por arriba o es igual/ si no por debajo

JB etiqueta corta

(JNAE) (JC)

Salta si por debajo/ si no por arriba y no es igual/ si existe acarreo

JBE etiqueta corta

(JNA)

Salta si por debajo o es igual/ si, no por arriba

JCXZ etiqueta corta

Salta si CX es cero

JE etiqueta corta

(JZ)

Salta si, es igual/ si, es cero

JG etiqueta corta

(JNLE)

Salta, si es mayor/ si, no es menor o no es igual

JGE etiqueta corta

(JNL)

Salta si es mayor o igual / si, no es menor

JL etiqueta corta

(JNGE)

Salta, si es menor/ si, no es mayor o no es igual

JLE etiqueta corta (JNG)

Salta, sí es menor o igual/ si no es mayor

JMP destino

Salta

JNC etiqueta corta

Salta, si no existe acarreo

JNE etiqueta corta

(JNZ)

Salta, si no es igual/ si, no es cero

JNO etiqueta corta

Salta, si no existe sobreflujo

JNP etiqueta corta (JPO)

Salta, si no es paridad par/ si la paridad es impar

JNS etiqueta corta

Salta si, no hay signo/ si, positivo

JO etiqueta corta Salta si, existe sobreflujo

JP etiqueta corta (JPE)

Salta si, paridad activada/ si, la paridad es par

JS etiqueta corta Salta si, signo activado

Page 25: Técnicas de Programación

CONVERSIONES DE DATOSCONVERSIONES DE DATOSCONVERSIONES DE DATOSCONVERSIONES DE DATOS

En los sistemas de computadoras, los datos rara vez están en la forma correcta. Una tarea principal del sistema es convertir los datos de una forma a otra. En esta sección se describen las conversiones entre binario y ASCII. Los datos binarios se extraen de un registro o de la memoria y se convierten en ASCII para exhibirlos en la pantalla. Los datos en ASCII se convierten a binarios al escribirlos en el teclado. También se aplica la conversión entre datos ASCII y hexadecimales.

Conversión de binario a ASCII Conversión de binario a ASCII Conversión de binario a ASCII Conversión de binario a ASCII

La conversión de binario a ASCII se logra en dos formas:

� Con la instrucción AAM si el numero es menor de 100. � Con una serie de divisiones decimales (dividir entre 10).

La instrucción AAM convierte el valor que hay en AX a un número de dos

dígitos en BCD no empacado. Si el numero en AX es 0062H (98 decimal) antes de ejecutar AAM, AX contendrá un 0908H después de que se ejecute AAM. No trata de un código ASCII, sino que se convierte a ASCII al agregar un 303011 a AX. En el ejemplo se ilustra un procedimiento que procesa el valor binario en AL (0 a 99) y lo exhibe en la pantalla como decimal. Este procedimiento borra un cero precedente, lo cual ocurre para los 0 a 9 con un código especial ASCII.

Ejemplo DESP PROC FAR PUSH DX ;salvar DX XOR AH,AN ;borrar AN AAM ;convertir a BCD ADD AH,20H ;sumar 20H CMP AH,20H ;probar si hay un cero avanzado JE DESP1 ;si hay cero inicial ADD AH,10H ;convertir a ASCII

DESP1:

PUSH AX ; MOV DL,AH ;exhibir primer digito MOV AH,6 ; INT 21H ; POP AK ; ADD AL,30H ;convertir a ASCII MOV DL,AL ;

Page 26: Técnicas de Programación

MOV AH,6 ;exhibir segundo digito INT 21H ; POP DX ;recuperar DX RET DESP END P

La razón por la cual AAM convierte cualquier número entre 0 y 99 a un número BCD sin empacar, de dos dígitos, es porque divide AX entre 10. El resultado se deja en AX, con lo cual AH contiene el cociente y AL el residuo. Este mismo sistema de dividir entre 10 se puede ampliar o expandir para convertir cualquier número completo de binario a una cadena de caracteres ASCII que se puede exhibir en la pantalla. El algoritmo para convertir de binario a ASCII es:

1. Dividir entre 10 y conservar el cociente en la pila como digito BCD significativo.

2. Repetir el paso 1 hasta que el cociente sea 0. 3. Recuperar cada residuo y sumarle un 30H para convertir a ASCII antes

de exhibir o imprimir.

EjemploEjemploEjemploEjemplo::::

DESPX PROC FAR

PUSH DX ;salvar BX, CX y DX

PUSH CX ;

PUSH BX ;

XOR CX,CX ;borrar CX

MOV BX,10 ;cargar 10

DESPX1:

XOR DX,DX ;borrar DX

DIV BX

PUSH DX ;salvar el residuo

INC CX ;contador del residuo

OR AX,AX ;probar el cociente

JNZ DESPX1 ;si no es cero

DESPX2:

Page 27: Técnicas de Programación

POP DX ;exhibir numero

MOV AH,6

ADD DL,30H ;convertir a ASCII

INT 21H

LOOP DESPX2 ;repetir

POP BX ;recuperar BX, CX y DX

POP CX

POP DX

RET

DESPX ENDP

En el ejemplo se muestra la forma en que el contenido de 16 bits sin signo de AX se convierte a ASCII y se escribe en la pantalla. En este caso se divide entre 10 y se salva el residuo en la pila después de cada división, para su posterior conversión a ASCII. Después de haber convertido todos los dígitos, se extraen los residuos de la pila y se los convierte a códigos ASCII para exhibir el resultado en la pantalla. Este procedimiento también borra cualesquiera ceros precedentes que pudieran ocurrir.

Conversión de ASCII a binarioConversión de ASCII a binarioConversión de ASCII a binarioConversión de ASCII a binario

Las conversiones de ASCII a binario suelen comenzar con una entrada del teclado. Si se oprime una sola tecla, la conversión ocurre cuando se resta 30H, pero hay un paso adicional. Después de restar 30H, el número se suma al resultado un ves que el resultado anterior se multiplico por 10. El algoritmo para convertir de binario a ASCII es:

1. Empezar con un resultado binario de cero. 2. Restar 30H del carácter tecleado en el teclado para convertir a

BCD. 3. Multiplicar el resultado por 10 y sumar el nuevo digito en BCD. 4. Repetir los pasos 2 y 3 hasta que el carácter tecleado no sea un

número en código ASCII.

Ejemplo LEEN PROC FAR

PUSH BX ;salvar BX y CX

PUSH CX

MOV CX, 10 ; cargar 10

XOR BX, BX ; borrar el resultado

Page 28: Técnicas de Programación

LEEN1:

MOV AH, 6 ;leer tecla

MOV DL, OFFH

INT 21H

JE LEEN1 ; esperar a la tecla

CMP AL,´D´ ;probar contra de 0

JB LEEN2 ; si es menor de 0

CMP AL, ´9´ ; probar contra de 9

JA LEEN2 ; si es mayor de 9

MOV DL, AL ; eco tecla

INT 21H

SUB AL,´0´ ;convertir a BCD

PUSH AX

MOV AX,BX ;multiplicar por 10

MUL CX

MOV BX,AX ;salvar el producto

POP AX

XOR AH,AH

ADD BX,AX ;sumar BCD al producto

JMP LEEN1 ;repetir

LEEN2:

MOV AX,BX ;cargar binario en AX

POP CX ; recuperar BX y CX

POP BX

RET

LEEN ENDP

Page 29: Técnicas de Programación

En el ejemplo se ilustra el procedimiento para poner en ejecución este algoritmo. En este caso, el número binario retorna al registro AX con un resultado de 16 bits. Si se requiere un resultado más grande, hay que volver a efectuar el procedimiento para una suma de 32 bits. Cada vez que se llama a este procedimiento, lee un número en el teclado hasta que se oprima una tecla que no sea del 0 al 9.

Empleo Empleo Empleo Empleo De Tablas Para Conversiones De DatosDe Tablas Para Conversiones De DatosDe Tablas Para Conversiones De DatosDe Tablas Para Conversiones De Datos

Las tablas se utilizan a menudo para convertir datos de una forma a otra. Una tabla esta formada en la memoria como una lista de datos que se consultan por un procedimiento para efectuar conversiones. En el caso de muchas tablas, a menudo se puede utilizar la instrucción XLAT para buscar o consultar datos en una tabla siempre y cuando a tabla contenga datos de 8 bits de ancho y con longitud igual o menor de 256 bytes.

Conversión de BSD a un código de 7 segmentos. Una aplicación sencilla en la que se emplea una tabla de conversión de BCD al código de 7 segmentos. En el ejemplo se ilustra un atabla que contiene los códigos de 7 segmentos para los numero 0 a 9. Estos códigos se emplean con la exhibición visual de 7 segmentos que se muestran en la figura. En esta exhibición de 7 segmentos se utilizan entradas activas en alto ( 1 lógico) para encender un segmento. El código esta dispuesto de modo que el segmento a este en la posición de bit 0 y el segmento g este en la posición de bit 6. A posición de bit 7 es cero en este ejemplo, pero se puede emplear para exhibir un punto decimal.

Ejemplo

SEG PROC FAR

PUSH BX MOV BX, OFFSET TABLA

XLAT CS: TABLA

POP BX

RET

TABLA DB 3FH

DB 6

DB 5BH

DB 4FH

DB 66H

Page 30: Técnicas de Programación

DB 5DH

DB 7DH

DB 7

DB 7FH

DB 6FH

SEG7 ENDP

EXHIBICIOEXHIBICIOEXHIBICIOEXHIBICION VISUAL 7 SEGMENTOSN VISUAL 7 SEGMENTOSN VISUAL 7 SEGMENTOSN VISUAL 7 SEGMENTOS

El procedimiento que efectúa la conversión contiene solo dos instrucciones y se supone que AL contiene el digito que se va a convertir al código de 7 segmentos. Una de las instrucciones direccionara a la tabla porque carga su dirección en BX y la otra efectúa la conversión y retorna el código de 7 segmentos en AL.

Debido a que la tabla de consulta esta ubicada en el segmento de código y a que la instrucción XLAT accede al segmento de datos en forma implícita la instrucción XLAT incluye un cambio de segmento. Se debe tener en cuenta que se agrega un operando mudo (TABLA) a l instrucción XLAT para poder agregar el prefijo (CS:) de cambio al segmento de código para la instrucción. Las instrucciones LODS y MOVS también cambian de segmento en la misma forma que XLAT con el empleo de un operador ficticio.

Byte de control

0 g f e d c b a

Page 31: Técnicas de Programación

Empleo de una tabla para accesar a datos ASCIIEmpleo de una tabla para accesar a datos ASCIIEmpleo de una tabla para accesar a datos ASCIIEmpleo de una tabla para accesar a datos ASCII

Algunas técnicas para programación requieren convertir los códigos numéricos a cadenas de caracteres en ASCII. Por ejemplo, se necesita exhibir los días de la semana para un programa de calendario. Debido a que la cantidad de caracteres en ASCII para cada día es diferente, se debe emplear algún tipo de tabla de los días de la semana que están en código ASCII.

Ejemplo

DIAS PROC FAR

PUSH DX

PUSH SI ; salvar DX y SI

MOV SI, OFFSET TABD ; direccionar TABD

XOR AH, AH ; borrar AX

ADD AX,AX ; duplicar AX

ADD SI, AX ; modificar dirección de la tabla

MOV DX, CS: [SI] ; obtener dirección de la cadena

MOV AX , CS; cambiar segmento de dato

PUSH DS

MOV DS, AX

MOV AH, 9

INT 21H ; exhibir la cadena

POP DS

POP SI ; recuperar DX y SI

POP DX

RET

DTAB DW DOM, LUN, MAR, MIE, JUE, VIER, SAB

SUN DB ‘DOMINGO S’

MON DB ‘LUNES S’

Page 32: Técnicas de Programación

TUE DB ‘MARTES S’

WEN DB ‘MIERCOLES S’

THU DB ‘JUEVES S’

FRI DB ‘VIERNES S’

SAT DB ‘SABADO S’

DIAS ENDP

En el ejemplo se presenta una tabla de consulta para cadenas de caracteres ASCII en el segmento de código. Cada cadena de caracteres contiene un día de la semana en código ASCII. L tabla señala cada día de la semana. El procedimiento se accesa a cada dia de la semana utiliza el registro AL y los números 0 a 6 significan domingo hasta sábado. Si AL contiene un dos cuando se llama a este procedimiento entonces aparece en pantalla la palabra martes.

Para que este procedimiento direccione a la tabla, primero hay que cargar su dirección en el registro SI. Después el número que hay en AL se convierte a un número de 16 bits y se duplica porque la tabla contiene 2 bytes en cada entrada. Luego, este índice e agrega en SI para direccionar la entrada correcta en la tabla de consulta. Ahora la dirección de la cadena de caracteres en ASCII se carga en DX por medio de la instrucción MOV DX, CS:[SI].

Antes de llamar la instrucción INT 21H DOS, se salva el registro DS en la pila y se carga con la dirección del segmento CS. Esto permite que la función 09H del DOS (exhibir cadena) se utilice para exhibir el día de la semana. Este procedimiento convierte los números del 0 a 6 a los días de a semana.

COCOCOCOMO DOCUMENTAR UN SISTEMA DE SOFTWAREMO DOCUMENTAR UN SISTEMA DE SOFTWAREMO DOCUMENTAR UN SISTEMA DE SOFTWAREMO DOCUMENTAR UN SISTEMA DE SOFTWARE

ObjetivoObjetivoObjetivoObjetivo

Un sistema pobremente documentado carece de valor aunque haya funcionado bien en alguna ocasión. En el caso de programas pequeños y poco importantes que sólo se utilizan durante un corto periodo de tiempo, unos cuantos comentarios en el código podrían ser suficientes. No obstante, la mayoría de los programas cuya única documentación es el código, se quedan obsoletos rápidamente y es imposible mantenerlos. El que la dedicación de un poco de esfuerzo a la documentación sea recompensado incluso dentro de los límites de un pequeño proyecto, constituye una sorpresa para la mayoría de los novatos.

Page 33: Técnicas de Programación

A menos que usted sea infalible y viva en un mundo en el que nada cambia, tendrá que volver a consultar el código que ya está escrito, y pondrá en duda decisiones que tomó durante el desarrollo del mismo. Si no documenta sus decisiones, se verá siempre cometiendo los mismos errores y tratando de comprender lo que pudo haber descrito fácilmente en una ocasión. La falta de documentación no sólo genera trabajo adicional, sino que también tiende a dañar la calidad del código. Si no posee una nítida caracterización del problema, es imposible que desarrolle una solución clara.

Aprender a documentar software es una tarea complicada y exige un criterio de ingeniería maduro. Documentar de forma concisa es un error habitual, pero el otro extremo puede resultar igual de perjudicial: si escribe documentaciones extensas, éstas atosigarán al lector y constituirán una carga a la hora de conservarlas. Es esencial documentar sólo los asuntos correctos. La documentación no sirve de ayuda para nadie si su extensión desanima a la gente a la hora de leerla.

Los principiantes tienen la tentación de centrar sus esfuerzos en temas sencillos, ya que éstos les resultan más fáciles de documentar. Esto es una pérdida de tiempo; no se aprende nada del esfuerzo y se termina escribiendo una documentación que es cualquier cosa excepto útil. Los principiantes también tienden a mostrarse reacios con los problemas de documentación. Esto trae consigo poca visión de futuro: si usted sabe que algún aspecto de su diseño no es del todo correcto, que alguna parte del problema no se ha aclarado o que es posible que parte del código tenga errores, ¡dígalo! Hará que el lector ahorre tiempo dándole vueltas a algo que aparentemente es erróneo, se acordará de dónde tiene que mirar si encuentra problemas y acabará teniendo una documentación más útil y honesta.

Otro asunto es cuándo documentar. Aunque algunas veces es conveniente posponer la tarea de la documentación mientras se realizan experimentos, los programadores con experiencia suelen documentar de forma metódica incluso el código provisional, los análisis de un problema inicial y los borradores de un diseño. Ellos creen que esto hace que la experimentación sea más productiva. Además, dado que han tomado la documentación como hábito, les resulta normal documentar a medida que van avanzando.

Este boletín facilita directrices sobre cómo documentar un sistema de software como el proyecto 6.170. Proporciona una estructura esquemática y algunos elementos obligatorios, pero deja bajo su propio criterio todo lo referente a los detalles. Es esencial que no piense que la documentación es un asunto rutinario y aburrido; si lo hace, su documentación no servirá para nada y será penosa a la hora de leerla y escribir. Así que documente de forma

Page 34: Técnicas de Programación

consciente: pregúntese a medida que lo hace, por qué lo hace y si está empleando su tiempo de la forma más eficaz.

Puede cortar y pegar en su documentación el texto de cualquiera de los boletines que le hemos facilitado. En concreto, es posible que quiera usar partes del boletín de problemas para describir los requisitos. No obstante, asegúrese de indicar claramente cualquier cambio que realice, ¡para que su ayudante técnico no tenga que emular manualmente el programa diff de Unix!

Esquema Esquema Esquema Esquema

Su documentación debería tener la siguiente estructura. Se facilita un número de páginas orientativo, típico de un proyecto 6.170; lo que presentamos a continuación son directrices, no un requisito.

1. Requisitos 1. Requisitos 1. Requisitos 1. Requisitos

La sección de requisitos describe el problema que se está solventando junto con la solución. Esta sección de la documentación resulta interesante tanto para los usuarios como para los implementadores; no debería contener detalles sobre la estrategia de implementación en concreto. Las otras partes de la documentación del sistema no serán de interés para los usuarios, únicamente para los implementadores, los encargados del mantenimiento y demás personal.

1. Visión general (hasta 1 página) Visión general (hasta 1 página) Visión general (hasta 1 página) Visión general (hasta 1 página) Una explicación del objetivo del sistema y de la funcionalidad del mismo.

2. Especificación revisada. Especificación revisada. Especificación revisada. Especificación revisada. Si le facilitaron especificaciones detalladas del comportamiento del sistema, es probable que encuentre que ciertas partes del mismo se encuentran infraespecificadas o son ambiguas. En esta sección debería aclarar tanto cualquier suposición que haya hecho sobre el significado de los requisitos, como cualquier extensión o modificación de los mismos.

3. Manual de usuario (1 Manual de usuario (1 Manual de usuario (1 Manual de usuario (1 ---- 5 páginas).5 páginas).5 páginas).5 páginas). Una descripción detallada sobre cómo el usuario puede utilizar el sistema, qué operaciones puede llevar a cabo, cuáles son los argumentos de la línea de comando, etc. Las especificaciones detalladas de los formatos deberían relegarse al Apéndice. Cualquier suposición relativa al entorno debería explicitarse aquí: por ejemplo, observe si el programa sólo se ejecuta en ciertas plataformas, si supone que la jerarquía de un cierto directorio está presente, si existen otras aplicaciones, etc. Junto con la visión general, este manual debería proporcionar toda la información que un usuario del sistema necesita.

4. Funcionamiento (1/2 página).Funcionamiento (1/2 página).Funcionamiento (1/2 página).Funcionamiento (1/2 página). ¿Qué recursos necesita el sistema para una operación normal y qué espacio y tiempo se espera que consuma?

Page 35: Técnicas de Programación

5. Análisis del problema (2 Análisis del problema (2 Análisis del problema (2 Análisis del problema (2 ---- 10 páginas).10 páginas).10 páginas).10 páginas). Una descripción clara del problema subyacente. Esto incluye el modelo conceptual que hay detrás del diseño (y posiblemente la interfaz de usuario), si no se ha tratado ya. El análisis del problema generalmente incluye uno o más modelos de objeto del problema, definiciones de sus conjuntos y relaciones y una discusión de asuntos complicados. Los objetos en los modelos de objeto del problema proceden del dominio del problema, no del código. Los modelos de objeto deberían incluir tanto diagramas como cualquier restricción textual fundamental, y deberían estar claramente expuestos para una correcta legibilidad. Esta parte también debería describir alternativas que hayan sido consideradas pero que se han rechazado, con razones, asuntos no resueltos o aspectos que no hayan sido totalmente aclarados y que vayan a solventarse más tarde.

Puede que los casos de uso le resulten útiles cuando escriba la especificación revisada y/o el manual de usuario. Un caso de uso es un objetivo específico y una lista de acciones que un usuario lleva a cabo para conseguir dicho objetivo. Un cliente puede, entre otras cosas, examinar la lista de acciones para decidir si la interfaz de usuario es aceptable. Si la colección de casos de uso abarca todos los objetivos deseados por el usuario, el cliente puede tener la seguridad de que el sistema cumplirá con su objetivo.

2. Diseño 2. Diseño 2. Diseño 2. Diseño

La sección de diseño de su documentación proporciona un cuadro de alto nivel de su estrategia de implementación.

1. Visión general (1/2 Visión general (1/2 Visión general (1/2 Visión general (1/2 ---- 3 páginas).3 páginas).3 páginas).3 páginas). Una visión general del diseño: organización de alto nivel, cuestiones de diseño especialmente interesantes, uso de librerías y otros módulos de terceros e indicadores de aspectos no resueltos o proclives al cambio. También incluye problemas con el diseño: decisiones que pueden resultar erróneas y los análisis de las consecuencias entre la flexibilidad y el funcionamiento que pueden ser juzgadas negativamente.

2. Estructura en tiempo de ejecución (1 Estructura en tiempo de ejecución (1 Estructura en tiempo de ejecución (1 Estructura en tiempo de ejecución (1 ---- 5 páginas). 5 páginas). 5 páginas). 5 páginas). Una descripción de la estructura del estado del programa en ejecución, expresada como un modelo de objeto de código. Éste debería ocultar las representaciones de los tipos de datos abstractos; su propósito consiste en mostrar las relaciones entre objetos. Los modelos de objeto deberían incluir tanto diagramas como cualquier restricción textual fundamental, y deberían estar claramente expuestos para una correcta legibilidad. Las representaciones de los tipos de datos deberían explicarse (con sus funciones de abstracción e invariantes de representación) si esas

Page 36: Técnicas de Programación

representaciones son poco comunes, especialmente complejas o decisivas para el diseño global. Observe que las funciones de abstracción y los invariantes de representación deberían aparecer aún así en su sitio normal en el propio código.

3. Estructura del módulo (1 Estructura del módulo (1 Estructura del módulo (1 Estructura del módulo (1 ---- 5 páginas). 5 páginas). 5 páginas). 5 páginas). Una descripción de la estructura sintáctica del texto del programa, expresada como un diagrama de dependencia entre módulos. Debería incluir la estructura del paquete y mostrar tanto las interfaces de Java™ del programa, calendario, material de clase, trabajos, exámenes, lecturas obligatorias, otras fuentes, prácticas, presentaciones, herramientas y proyectos, como las clases. No es necesario exhibir las dependencias de las clases en la API de Java™ del programa, calendario, material de clase, trabajos, exámenes, lecturas obligatorias, otras fuentes, prácticas, presentaciones, herramientas y proyectos. Su MDD (diagrama de dependencia entre módulos) debería estar claramente expuesto para una correcta legibilidad. Explique por qué se eligió esa estructura sintáctica en particular (ej.: introducción de interfaces para el desacoplamiento: qué desacoplan y por qué), y cómo se utilizaron los patrones de diseño concretos.

Para explicar la descomposición y otras decisiones de diseño, exponga que aportan simplicidad, extensibilidad (facilidad para añadir características nuevas), particionalidad (los distintos miembros del equipo pueden trabajar en las diferentes partes del diseño sin necesidad de mantener una comunicación constante) u objetivos similares relativos a la ingeniería de software.

3. Pruebas3. Pruebas3. Pruebas3. Pruebas

La sección de pruebas de su documentación indica el enfoque que usted ha elegido para verificar y validar su sistema. (En un sistema real, esto podría incluir tanto las pruebas de usuario para determinar la idoneidad del sistema como solución al problema descrito en la sección de requisitos, como la ejecución de suites de prueba para verificar la corrección algorítmica del código). Del mismo modo que no debería comunicar el diseño de su sistema presentando el código o incluso enumerando las clases, no debería únicamente enumerar las pruebas realizadas. En vez de hacer esto, explique cómo se seleccionaron las pruebas, por qué éstas bastaron, por qué un lector debería creer que no se ha omitido ninguna prueba importante y por qué debería creer que el sistema realmente funcionará como se desee cuando se ponga en práctica.

1. Estrategia (1 Estrategia (1 Estrategia (1 Estrategia (1 ---- 2 páginas).2 páginas).2 páginas).2 páginas). Una explicación de la estrategia general de las pruebas: blackbox y/o glassbox, top down (de arriba hacia abajo) y/o bottom up (de abajo hacia arriba), tipos de test beds (bancos de prueba) y manejadores de tests que se han utilizado, fuentes de datos del test, suites

Page 37: Técnicas de Programación

de prueba, métrica de cobertura, comprobación en tiempo de compilación frente a sentencias en tiempo de ejecución, razonamientos sobre su código, etc. Es posible que quiera usar técnicas distintas (o combinaciones de técnicas) en las diferentes partes del programa. Justifique sus decisión en cada caso.

Explique qué tipo de errores espera encontrar (¡y cuáles no!) con su estrategia. Comente qué aspectos del diseño dificultaron o facilitaron la validación.

2. Resultados del test (1/2 Resultados del test (1/2 Resultados del test (1/2 Resultados del test (1/2 ---- 2 páginas).2 páginas).2 páginas).2 páginas). Resumen de qué pruebas se han realizado y cuáles no, si es que queda alguna: qué módulos se han probado, y si se han probado a fondo. Indique el grado de confianza del código: ¿Qué tipo de defectos se han eliminado? ¿Cuáles son los que podrían quedar?

4. Reflexión4. Reflexión4. Reflexión4. Reflexión

La sección de reflexión (vulgarmente conocida como "post mortem") del documento es donde puede hacer generalizaciones partiendo de éxitos o fallos concretos, hasta llegar formular reglas que usted u otros puedan utilizar en el futuro desarrollo de software. ¿Qué fue lo que más le sorprendió? ¿Qué hubiera deseado saber cuando comenzó? ¿Cómo podría haber evitado los problemas que encontró durante el desarrollo?

1. EvaluaciónEvaluaciónEvaluaciónEvaluación (1/2 (1/2 (1/2 (1/2 ---- 1 páginas).1 páginas).1 páginas).1 páginas). Lo que usted considere éxitos o fracasos del desarrollo: problemas de diseño no resueltos, problemas de funcionamiento, etc. Identifique cuáles son los rasgos importantes de su diseño. Señale técnicas de diseño o implementación de las que se sienta especialmente orgulloso. Explique cuáles fueron los errores que cometió en su diseño y los problemas que causaron.

2. Lecciones (0,2 Lecciones (0,2 Lecciones (0,2 Lecciones (0,2 ---- 1 página).1 página).1 página).1 página). Qué lecciones ha aprendido de la experiencia: cómo podría haberlo hecho de otra forma una segunda vez, y cómo los errores de diseño e implementación pueden corregirse. Describa qué factores causaron problemas, como hitos errados, o errores y limitaciones conocidos.

3. Errores y limitaciones conocidos. Errores y limitaciones conocidos. Errores y limitaciones conocidos. Errores y limitaciones conocidos. ¿De qué manera su implementación no llega a alcanzar la especificación? Sea exacto. Aunque perderá puntos por errores y características no presentes, obtendrá puntos parciales por identificar de manera exacta esos errores y el origen del problema.

Page 38: Técnicas de Programación

5. Apéndice5. Apéndice5. Apéndice5. Apéndice

El apéndice contiene detalles de bajo nivel acerca del sistema, que no son necesarios para comprenderlo en un nivel superior, pero que se exigen para usarlo en la práctica o para verificar afirmaciones realizadas en cualquier parte del documento.

1. Formatos.Formatos.Formatos.Formatos. Una descripción de todos los formatos adoptados o garantizados por el programa: para un fichero E/O (fichero para entrada y salida de datos), argumentos de la línea de comando, diálogos de usuario, formatos de los mensajes para las comunicaciones en red, etc. Éstos deberían desglosarse en formatos visibles para el usuario, que son parte conceptual de los requisitos visibles para el usuario y del manual de usuario, y en formatos interiores que constituyen una parte conceptual de otros componentes de su documentación.

2. Especificaciones del módulo. Especificaciones del módulo. Especificaciones del módulo. Especificaciones del módulo. Debería extraer las especificaciones de su código y presentarlas por separado aquí. Si escribe sus comentarios en el estilo aceptado por Javadoc con el doclet de 6.170 , podrá generar documentos de la especificación de forma automática a partir del código. La especificación de un tipo abstracto debería incluir su visión general, campos de la especificación e invariantes abstractas (restricciones de especificación). La función de abstracción y el invariante de representación no forman parte de la especificación de un tipo.

3. Casos de prueba.Casos de prueba.Casos de prueba.Casos de prueba. Idealmente, su banco de pruebas lee tests de un archivo de casos de prueba en un formato que resulta cómodo de leer y escribir. No es necesario que incluya casos de prueba muy extensos; por ejemplo, simplemente podría observar el tamaño de una entrada aleatoria generada para realizar pruebas de estrés y proveer al programa que generó los tests. Indique cuál es la utilidad de cada grupo de tests (ej. "pruebas de estrés, entradas enormes", "pruebas de partición, todas las combinaciones de +/-/0 para argumentos enteros").

DOCUMENTACIÓN DEL CÓDIGODOCUMENTACIÓN DEL CÓDIGODOCUMENTACIÓN DEL CÓDIGODOCUMENTACIÓN DEL CÓDIGO

Comentarios a nivel de especificación Comentarios a nivel de especificación Comentarios a nivel de especificación Comentarios a nivel de especificación

Tipos de datos abstractos. Tipos de datos abstractos. Tipos de datos abstractos. Tipos de datos abstractos. Cada tipo de datos abstractos (clase o interfaz) debería tener:

1. Una sección de visión general visión general visión general visión general que dé una explicación de una o dos líneas sobre qué objetos del tipo representan y si son mutables.

2. Una lista de campos de especificacióncampos de especificacióncampos de especificacióncampos de especificación. Podría haber sólo uno; por ejemplo, un conjunto podría tener el campo elems representando al

Page 39: Técnicas de Programación

conjunto de elementos. Cada campo debería tener un nombre, un tipo y una breve explicación. Podría resultarle útil definir campos derivados campos derivados campos derivados campos derivados adicionales que facilitaran la escritura de las especificaciones de los métodos; para cada uno de éstos, debería indicar que es derivado y explicar cómo se ha obtenido a partir de los otros campos. Puede que haya invariantes de especificación invariantes de especificación invariantes de especificación invariantes de especificación que restrinjan los posibles valores de los campos de la especificación; si es así, debería precisarlos.

Especificaciones del método. Especificaciones del método. Especificaciones del método. Especificaciones del método. Todos los métodos públicos de las clases deberían tener especificaciones; los métodos privados complicados también deberían especificarse. Las especificaciones del método deberían seguir la estructura requires (requiere), modifies (modifica), throws (lanza), effects (resulta), returns (devuelve) que aparece descrita en el boletín de especificacionesy en clase. Observe que para el curso 6.170, puede suponer que los argumentos no son nulos, de no especificarse lo contrario.

Comentarios a nivel de implementación Comentarios a nivel de implementación Comentarios a nivel de implementación Comentarios a nivel de implementación

Notas para la implementación. Notas para la implementación. Notas para la implementación. Notas para la implementación. Los comentarios de una clase deberían incluir los siguientes elementos (los cuales, en el caso de clases destacadas, aparecen también en la sección Estructura en tiempo de ejecución de la documentación del diseño):

1. Una función de abstracción función de abstracción función de abstracción función de abstracción que defina cada campo de la especificación en función de los campos de representación. Las funciones de abstracción solamente son necesarias para las clases que son tipos de datos abstractos, y no para clases de tipo excepciones o como cualquier objeto de la GUI.

2. Un invariante de representacióninvariante de representacióninvariante de representacióninvariante de representación. Las RIs (implementaciones de referencia) son necesarias para cualquier clase que posea una representación (ej. no la mayoría de las excepciones). Es muy recomendable que pruebe los invariantes en un método checkRep() donde sea viable. Tenga cuidado al incluir en sus invariantes suposiciones acerca de lo que puede o no puede ser nulo.

3. Para las clases con representaciones complejas, añada una nota que explique la elección de la representación elección de la representación elección de la representación elección de la representación (también conocida como representación racionalrepresentación racionalrepresentación racionalrepresentación racional): qué análisis de ventajas y desventajas se realizaron y qué alternativas se han considerado y cuáles se han rechazado (y por qué).

Sentencias en tiempo de ejecución. Sentencias en tiempo de ejecución. Sentencias en tiempo de ejecución. Sentencias en tiempo de ejecución. Éstas deberían utilizarse juiciosamente, como se explicó en clase. Puede consultar Maguire Steve, Writing Solid Code, Microsoft Press, 1995, donde encontrará una discusión más extensa sobre la forma en que las sentencias.

Page 40: Técnicas de Programación

PRÁCTICA #7 PRÁCTICA #7 PRÁCTICA #7 PRÁCTICA #7

Estudio Del Sistema De Audio Del Computador Estudio Del Sistema De Audio Del Computador Estudio Del Sistema De Audio Del Computador Estudio Del Sistema De Audio Del Computador

SÍNTESIS DE SONIDOSÍNTESIS DE SONIDOSÍNTESIS DE SONIDOSÍNTESIS DE SONIDO

La producción de sonido es uno de los puntos más débiles de los ordenadores compatibles, que sólo superan por muy escaso margen a alguno de los micros legendarios de los 80, si bien las tarjetas de sonido han solventado el problema. Pero aquí nos conformaremos con describir la programación del altavoz. En todos los PCs existen dos métodos diferentes para generar sonido, con la utilización del 8254 o sin él, que veremos por separado.

Control directo del altavoz.Control directo del altavoz.Control directo del altavoz.Control directo del altavoz.

El altavoz del ordenador está ligado en todas las máquinas al bit 1 del puerto E/S 61h. Si se hace cambiar este bit (manteniéndolo durante cierto tiempo alto y durante cierto tiempo bajo, repitiendo el proceso a gran velocidad) se puede generar una onda cuadrada de sonido. Cuanto más deprisa se realice el proceso, mayor será la frecuencia del sonido. Por fortuna, la baja calidad del altavoz del PC redondea la onda cuadrada y produce un sonido algo más musical de forma involuntaria. No existe, en cualquier caso, control sobre el volumen, que dada la calidad del altavoz también está en función de la frecuencia.

Este método de producción de sonido tiene varios inconvenientes. Por un

lado, la frecuencia con que se hace vibrar al bit que lo produce, si no se tiene mucho cuidado, está a menudo más o menos ligada a la capacidad de proceso del ordenador: esto significa que el sonido es más grave en máquinas lentas y más agudo en las rápidas. Esto es particularmente grave y evidente cuando las temporizaciones se hacen con bucles de retardo con registros de la CPU: la frecuencia del sonido está totalmente a merced de la velocidad de la máquina en que se produce. Es por ello que el pitido de error que produce el teclado es a menudo distinto de unos ordenadores a otros, aunque tengan el mismo KEYB instalado.

Otro gran inconveniente de este método es que las interrupciones,

fundamentalmente la del temporizador, producen fuertes interferencias sobre el sonido. Por ello, es normal tenerlas inhibidas, con el consiguiente retraso de la hora. Por último, un tercer gran inconveniente es que la CPU está completamente dedicada a la producción de sonido, sin poder realizar otras tareas mientras tanto.

Page 41: Técnicas de Programación

Antes de comenzar a producir el sonido con este método hay que bajar la línea GATE del 8254, ya que cuando está en alto y se activa también el bit 1 del puerto E/S 61h, el temporizador es el encargado de producir el sonido (este es el segundo método, como veremos). Por tanto, es preciso poner primero a cero el bit 0 del mismo puerto (61h): CLI ; evitar posible INT 8, entre otras IN AL,61h AND AL,11111110b JMP SHORT $+2 ; estado de espera para E/S OUT 61h,AL ; bajar GATE del contador 2 del 8254 MOV CX,100h ; 256 vueltas otro_ciclo: PUSH CX IN AL,61h XOR AL,2 ; invertir bit 1 JMP SHORT $+2 OUT 61h,AL MOV CX,300 ; constante de retardo retardo: LOOP retardo POP CX LOOP otro_ciclo STI

Control Del Altavoz Por El TemporizControl Del Altavoz Por El TemporizControl Del Altavoz Por El TemporizControl Del Altavoz Por El Temporizador.ador.ador.ador.

El otro método posible consiste en emplear el contador 2 del

temporizador conectado al altavoz; así, enviando el período del sonido (1.193.180/frecuencia_en_Hz) a dicho contador (programado en modo 3), éste se encarga de generar el sonido. Esto permite obtener sonidos idénticos en todos los ordenadores. Existe el pequeño problema de que la duración del sonido ha de ser múltiplo de 1/18,2 segundos si se desea utilizar el reloj del sistema para determinarla (un bucle de retardo sería, una vez más, dependiente de la máquina) ya que el contador 2 está ahora ocupado en la producción de sonido y no se puede usar para temporizar (al menos, no sin hacer malabarismos). Alternativamente, se podría evaluar la velocidad de la CPU para ajustar las constantes de retardo o aumentar la velocidad de la interrupción periódica.

Para emplear este sistema, primero se prepara el contador 2 para

temporizar (poniendo a 1 el bit 0 del puerto 61h) y luego se conecta su salida al altavoz (poniendo a 1 el bit 1 del puerto 61h). Al final, conviene borrar ambos bits de nuevo. Ahora no es preciso inhibir las interrupciones para garantizar la calidad del sonido:

Page 42: Técnicas de Programación

MOV AL,10110110b ; contador 2, modo 3, operación 11 b, datos binarios OUT 43h,AL ; programar contador 2 MOV AX,2711 ; 1.193.180 / 440 Hz (nota LA) = 2711 JMP SHORT $+2 OUT 42h,AL MOV AL,AH JMP SHORT $+2 OUT 42h,AL ; frecuencia programada JMP SHORT $+2 IN AL,61h OR AL,00000011b JMP SHORT $+2 OUT 61h,AL ; altavoz sonando MOV CX,0 demora: LOOP demora ; esperar un cierto tiempo por el peor método IN AL,61h AND AL,11111100b JMP SHORT $+2 OUT 61h,AL ; altavoz callado

Las frecuencias en Hz de las distintas notas musicales están oficialmente

definidas y los músicos suelen tenerlas en cuenta a la hora de afinar los instrumentos. La escala cromática temperada, adoptada por la American Standards Asociation en 1936, establece el LA como nota de referencia en 440 Hz. En general, una vez conocidas las frecuencias de las notas de una octava, las de la octava siguiente o anterior se obtienen multiplicando y dividiendo por dos, respectivamente.

La fórmula de abajo permite obtener las frecuencias de las notas

asignándolas un número (a partir de 6 y hasta 88; el LA de 440 Hz es la nota 49) con una precisión razonable, máxime teniendo en cuenta que van a ir a parar al altavoz del PC. Tal curiosa relación se verifica debido a que la respuesta del oído humano es logarítmica, lo que ha permitido reducir a simples matemáticas el viejo saber milenario de los músicos.

Page 43: Técnicas de Programación

PRÁCTICA #8PRÁCTICA #8PRÁCTICA #8PRÁCTICA #8

Desarrollo De Una Desarrollo De Una Desarrollo De Una Desarrollo De Una Aplicación Con El Sistema De Audio Del ComputadorAplicación Con El Sistema De Audio Del ComputadorAplicación Con El Sistema De Audio Del ComputadorAplicación Con El Sistema De Audio Del Computador

TITLE Generación de Sonido (COM)

SOUNSG SEGMENT PARA 'code'

ASSUME CS:SOUNSG,DS:SOUNSG,SS:SOUNSG

ORG 100H

BEGIN: JMP SHORT MAIN

;-------------------------------------------------- ------------------------------

DURTION DW 1000 ;duración del tono

TONE DW 256H ;frecuencia

;-------------------------------------------------- ------------------------------

MAIN PROC NEAR

IN AL,61H ;obtener datos del puerto

PUSH AX ; y guardar

CLI ;limpiar interrupciones

CALL B10SPKR ;producir sonido

POP AX ;restablecer

OUT 61H,AL ;numero del puerto

STI ;restablecer interrupciones

RET

MAIN ENDP

B10SPKR PROC NEAR

B20: MOV DX,DURTION ;fijar duración del sonido

B30:

AND AL,11111100B ;poner en cero los bits 0 y 1

Page 44: Técnicas de Programación

OUT 61H,AL ;trasmitir a la bocina

MOV CX,TONE ;fijar duración

B40:

LOOP B40 ;retraso

OR AL,00000010B ;poner en uno el bit 1

OUT 61H,AL ;transmitir a la bocina

MOV CX,TONE ;fijar duración

B50:

LOOP B50 ;retraso

DEC DX ;reducir duración

JNZ B30 ;¿Continuar?

SHL DURTION,1 ;reducir frecuencia

SHR TONE,1 ;¿ahora es cero?

JNZ B20 ;si regresar

RET

B10SPKR ENDP

SOUNSG ENDS

END BEGIN

Page 45: Técnicas de Programación

CONCLUSIÓNCONCLUSIÓNCONCLUSIÓNCONCLUSIÓN

AL culminar la elaboración de este trabajo de investigación se han podido

conocer las distintas técnicas de programación así como sus funcionamientos,

importancias, ventajas a la hora de elaborar un programa en ASM, ya sea TASM,

NASM, hasta el emu8086, entre otros. Además se estudiaron diferentes ejemplos

de dichas técnicas para ilustrar la explicación de cada una. En fin, se pueden

acotar los diferentes aspectos para el análisis final de esta investigación:

� En general una subrutina consiste en una porción de código que realiza

una operación con un conjunto de valores proporcionados por el

programa que la invoca denominados parámetros, y que devuelve un

resultado.

� De las técnicas descritas para la invocación de subrutinas, la que crea el

bloque de activación en la pila es la más utilizada por los lenguajes de alto

nivel. Se muestran los pasos a seguir por el programa llamador y el

llamado para crear y destruir el bloque de activación.

� Los accesos condicionales permiten controlar el flujo de ejecución de un

programa escrito en lenguaje ensamblador es por ello que se van a

describir de manera breve las instrucciones de control disponibles.

� En los sistemas de computadoras, los datos rara vez están en la forma

correcta. Una tarea principal del sistema es convertir los datos de una

forma a otra.

� Las tablas se utilizan a menudo para convertir datos de una forma a otra.

Una tabla esta formada en la memoria como una lista de datos que se

consultan por un procedimiento para efectuar conversiones.

� Un sistema pobremente documentado carece de valor aunque haya

funcionado bien en alguna ocasión.

Page 46: Técnicas de Programación

BIBLIOGRAFÍA:BIBLIOGRAFÍA:BIBLIOGRAFÍA:BIBLIOGRAFÍA:

• PETER Abel, “Abel”. Lenguaje Ensamblador y Programación para IBM PC

y sus Compatibles.

• GARCIA De Celis, Ciriaco. El Universo Digital del IBM PC, AT Y PS/2.

Edición 4.0 (4º)

• http://books.google.com/books?ct=result&hl=es&id=vPmSWr-

wDt8C&dq=invocacion+a+subrutinas+%2B+tecnicas+de+programacion

&pg=PA368&lpg=PA368&sig=ACfU3U1JlKb81_PbnyATtfvsHTUnpxTipQ

&q=paginas+369+a+372#PPR1,M1

• http://www.it.uc3m.es/ttao/html/SUB.html#sub:sec:subroutines

• http://perso.wanadoo.es/pictob/tecprg.htm

• http://www.rinconsolidario.org/eps/asm8086/asm.html

• http://homepage.mac.com/eravila/asmix862.html

Page 47: Técnicas de Programación

ANEXOSANEXOSANEXOSANEXOS

CALL: Instrucción de llamada a subrutinaCALL: Instrucción de llamada a subrutinaCALL: Instrucción de llamada a subrutinaCALL: Instrucción de llamada a subrutina

El efecto relevante de esta instrucción es que deposita en la cima de la pila la dirección de retorno, o lo que es lo mismo, la dirección de la instrucción que sigue a esta en el flujo de ejecución. Esta instrucción no modifica los flags de la palabra de estado.

RET: Instrucción de retorno de subrutinaRET: Instrucción de retorno de subrutinaRET: Instrucción de retorno de subrutinaRET: Instrucción de retorno de subrutina

El aspecto relevante de esta instrucción es que supone que la dirección a la que debe retornar está almacenada en la cima de la pila. Esta instrucción no modifica los flags de la palabra de estado.

Page 48: Técnicas de Programación

INSTRUCCIONES PARA EL 8086INSTRUCCIONES PARA EL 8086INSTRUCCIONES PARA EL 8086INSTRUCCIONES PARA EL 8086

Page 49: Técnicas de Programación