pbn - 17 - 1 © jaime alberto parra plaza clase 17 funciones y paso de parÁmetros a procedimientos
TRANSCRIPT
Pbn - 17 - 1© Jaime Alberto Parra Plaza
CLASE 17
FUNCIONES Y
PASO DE PARÁMETROS A
PROCEDIMIENTOS
Pbn - 17 - 2© Jaime Alberto Parra Plaza
Cuando se diseña un lenguaje de alto nivel (en adelante llamado HLL), usualmente se hace abstracción del sistema sobre el cual operará, es decir, un lenguaje dado se crea como si fuera a ejecutarse sobre un computador virtual.
Pbn - 17 - 3© Jaime Alberto Parra Plaza
Siendo las funciones unas de las herramientas más poderosas de cualquier lenguaje, el paso de parámetros entre ellas tampoco debe depender del tipo de máquina en particular en que se ejecute en un momento dado.
Pbn - 17 - 4© Jaime Alberto Parra Plaza
Se comentó antes que para transferir parámetros entre procedimientos se puede usar la pila o los registros.
Cuando una función es genérica, se usará como camino de transferencia la pila. Se dirá entonces que una función es un procedimiento que recibe parámetros a través de la pila, usando un mecanismo estandarizado.
Pbn - 17 - 5© Jaime Alberto Parra Plaza
APILAMIENTO:
La forma en que los HLL transfieren parámetros a sus funciones a través de la pila se presenta en dos formatos:
• Apilamiento por izquierda
• Apilamiento por derecha
Pbn - 17 - 6© Jaime Alberto Parra Plaza
Para explicar la diferencia se usará la función genérica:
func (a, b, c)
Se tiene que el orden IZQUIERDO es: a-b-c en tanto que el orden DERECHO será c-b-a.
Pbn - 17 - 7© Jaime Alberto Parra Plaza
Un HLL será apilador por la izquierda si ingresa los parámetros en la pila en orden izquierdo. Para el ejemplo, la pila quedaría así:
abcSP
Pbn - 17 - 8© Jaime Alberto Parra Plaza
Por el contrario, será apilador por la derecha si el orden derecho es el escogido. Este método es el que se tomará como supuesto en adelante (que es el caso de C):
cbaSP
Pbn - 17 - 9© Jaime Alberto Parra Plaza
Después de colocar los parámetros, se invoca el procedimiento respectivo; el microprocesador se encarga de apilar la dirección de retorno. El aspecto actual de la pila es:
cba
SP IP de retorno
Pbn - 17 - 10© Jaime Alberto Parra Plaza
Esa es la forma en que la pila está cuando se entra al procedimiento. Para acceder a los parámetros, es norma utilizar el registro BP, el cual permite direccionar a cualquier elemento dentro de la pila. Puesto que BP podría tener un valor anterior (llamado de una función desde otra) es común apilar también el valor actual de BP.
Pbn - 17 - 11© Jaime Alberto Parra Plaza
Direcciones relativas de cada parámetro al ser direccionado por BP (después de asignar a BP el valor de SP):
cba
BP = SPIP de retorno
antiguo BP
[BP+2][BP+4][BP+6][BP+8]
Pbn - 17 - 12© Jaime Alberto Parra Plaza
PLANTILLA DE FUNCIÓN:De acuerdo a lo anterior, la entrada y salida
típicas de una función son:
func PROCPUSH BPMOV BP, SP. . .. . .POP BPRET
func ENDP
Pbn - 17 - 13© Jaime Alberto Parra Plaza
VALORES RETORNADOS:
Prácticamente todos los HLL devuelven resultados a través del acumulador, usando los registros respectivos para acumulador de 8, 16 ó 32 bits.
Cuando el resultado es mayor a 32 bits, se retorna un puntero al lugar donde el resultado ha quedado guardado.
Pbn - 17 - 14© Jaime Alberto Parra Plaza
EJEMPLO:
Para visualizar mejor estas ideas se implementará la siguiente función, cuyo propósito en particular no interesa en el momento:
char funcX(int a, char b){
return (a + 5) / b;}
Pbn - 17 - 15© Jaime Alberto Parra Plaza
Como es ya conocido, el procedimiento tiene una macro asociada para interfaz con el exterior. La macro se encarga de apilar los parámetros y de invocar al procedimiento. Puesto que se desconoce si los argumentos serán o no constantes (que no pueden apilarse directamente) es usual trabajar con el registro BX.
Pbn - 17 - 16© Jaime Alberto Parra Plaza
Los trabajos usuales de la macro son:
• Apilar BX para preservar su valor
• Llevar cada argumento a BX e irlo apilando en el orden escogido
• Invocar al procedimiento
• Quitar los argumentos de la pila
• Desapilar BX para restituir su valor original
Pbn - 17 - 17© Jaime Alberto Parra Plaza
funcX MACRO ArgA, ArgBPUSH BX ; preservar reg.MOV BL, ArgB ; primer param.PUSH BXMOV BX, ArgA ; segundo param.PUSH BXCALL pfuncX ; llamar proc.ADD SP, 4 ; limpiar pilaPOP BX ; restituir reg.ENDM ; funcX
Pbn - 17 - 18© Jaime Alberto Parra Plaza
Las labores normales de un procedimiento:
• Apilar BP para preservar su valor
• Copiar SP en BP
• Apilar los registros que vayan a utilizarse
• Realizar las tareas necesarias, usando a BP como puntero a los parámetros
• Llevar el resultado al Acc si es necesario
• Desapilar los registros apilados
• Desapilar BP
• Retornar
Pbn - 17 - 19© Jaime Alberto Parra Plaza
pfuncX PROC
PUSH BPMOV BP, SPPUSH BXMOV AX, [BP+4]ADD AX, 5MOV BX, [BP+6]IDIV BLPOP BXPOP BPRET
pfuncX ENDP
Pbn - 17 - 20© Jaime Alberto Parra Plaza
EJERCICIOS:
• Intercambio de dos variables
• Obtener el mayor de dos valores
• Conversión en mayúsculas de una cadena
• Ordenamiento de una lista por el método de la burbuja
• Función seno a partir de datos en tabla
Pbn - 17 - 21© Jaime Alberto Parra Plaza
CARACTERÍSTICAS ESPECIALES DE LAS
FUNCIONES
Pbn - 17 - 22© Jaime Alberto Parra Plaza
Además de las consideraciones dadas anteriormente sobre las funciones en general, algunos aspectos particulares requieren especial atención. Ellos son:
• Retorno de errores
• Interfaz con otros lenguajes
• Recursividad
Pbn - 17 - 23© Jaime Alberto Parra Plaza
RETORNO DE ERRORES
Pbn - 17 - 24© Jaime Alberto Parra Plaza
Cuando se diseña una función, se debe tener en mente:
• Una solución general para la labor que hará la función
• Soluciones particulares para casos que no cubra la solución general
• Prever la posibilidad de errores y retornar un código de error indicándolo
Pbn - 17 - 25© Jaime Alberto Parra Plaza
CAUSAS DE ERROR:
Una función puede fallar en lograr su cometido por dos causas fundamentales:
• Los operandos no se ajustan al tipo o a las características esperadas (error de entrada)
• El resultado no se ajusta al tipo en que debe retornarse la solución (error de salida)
Pbn - 17 - 26© Jaime Alberto Parra Plaza
Puede visualizarse esto mejor por un ejemplo. Se sabe que la función atoi debe convertir una cadena formada por caracteres numéricos a su número entero equivalente:
atoi (“2345”) = 2345
Pbn - 17 - 27© Jaime Alberto Parra Plaza
Supóngase ahora que se pide realizar la conversión:
atoi (“23Wq&”)
Claramente, el argumento no se presta para la conversión y la función debe retornar un indicador del error.
Pbn - 17 - 28© Jaime Alberto Parra Plaza
Tradicionalmente, la solución es retornar un valor que no pertenezca al conjunto solución. Sin embargo, éste no es el caso, pues el rango de atoi es todo el conjunto de enteros.
La función C retorna un 0 cuando el argumento no puede convertirse a un número, ¡pero el 0 también puede ser una solución verdadera!.
Pbn - 17 - 29© Jaime Alberto Parra Plaza
Debido a esto, cuando se invoca a atoi y se recibe como respuesta un 0, debe añadirse código que analice la solución. Podría ser algo como esto:
Pbn - 17 - 30© Jaime Alberto Parra Plaza
Debido a esto, cuando se invoca a atoi y se recibe como respuesta un 0, debe añadirse código que analice la solución. Podría ser algo como esto:
x = atoi ( cadena );if ( !x )
if ( !cadena[0] )/* Error */
else/* Respuesta == 0 */
else/* Respuesta != 0 */
Pbn - 17 - 31© Jaime Alberto Parra Plaza
BANDERA DE CARRY:
El ensamblador permite una solución mejor a este problema, que no está disponible en ningún otro lenguaje.
Una función de ensamblador puede retornar 2 valores: La solución como tal y un indicador de si se logró éxito o fracaso en la actividad.
Pbn - 17 - 32© Jaime Alberto Parra Plaza
La solución se retorna en el acumulador, en tanto que el indicador éxito/fracaso es la bandera de acarreo.
Normalmente, esta bandera es fijada por el uP cuando hay sobreflujo aritmético. Pero puede también fijarse a voluntad del programador usando las órdenes:
CLC = Fijar el acarreo en 0STC = Fijar el acarreo en 1
Pbn - 17 - 33© Jaime Alberto Parra Plaza
Así, la función llamadora deberá revisar el valor del acarreo y deducir si hubo o no error, usando las órdenes de salto:
JC = Saltar si el acarreo está en 1JNC = Saltar si el acarreo está en 0
Quedando la interacción como:CALL AtoiJC Error; código para éxito
Error: ; código para fracaso
Pbn - 17 - 34© Jaime Alberto Parra Plaza
CÓDIGOS DE ERROR:
La misma función sirve para evidenciar otro concepto. Supóngase que se solicita:
atoi (“356712”);
Este argumento sí es convertible, pero al hacerlo se sobrepasa el máximo permitido. De nuevo, la función C retorna aquí un 0, y ahora sí es realmente difícil diferenciar este error del anterior.
Pbn - 17 - 35© Jaime Alberto Parra Plaza
Otra vez la solución será más adecuada en ensamblador. Cuando las causas de error son varias, se seguirá usando el acarreo para indicarlo, pero se usará el acumulador para explicar el tipo de error, conocido como un código de error. La asociación es:
if (Carry==0)/* Solución está en Acc */
else/* Código de error está en Acc */
Pbn - 17 - 36© Jaime Alberto Parra Plaza
INTERFAZ CON OTROS LENGUAJES
Pbn - 17 - 37© Jaime Alberto Parra Plaza
Para lograr un acople exitoso entre funciones que han sido elaboradas con diferentes lenguajes, de debe escoger uno de los lenguajes como el de referencia y someter el código escrito en otros lenguajes a los parámetros del escogido. Se presentan aspectos a considerar tales como: nombres de segmento, paso de parámetros, recepción de resultados, tipo de saltos, etc.
Pbn - 17 - 38© Jaime Alberto Parra Plaza
Dada la variedad de lenguajes, sólo se explicará formalmente cómo enlazar funciones hechas en ensamblador para ser invocadas desde un programa hecho en lenguaje C.
Además de ser C el lenguaje más popular actualmente, su forma de enlazar diversos códigos es bastante diferente a la de otros lenguajes conocidos.
Pbn - 17 - 39© Jaime Alberto Parra Plaza
Se darán diversas recomendaciones tanto para el código a ser llamado, que será escrito en ensamblador, como para el código llamador, hecho en ANSI C.
Se recalca que es C estándar y no C++, pues en este caso la forma de interacción es algo diferente.
Pbn - 17 - 40© Jaime Alberto Parra Plaza
ARCHIVO ENSAMBLADOR:
Escribir la(s) funcion(es) a enlazar en un archivo que sólo contenga el segmento de código, sin segmentos de datos o de pila, pues se supone que el programa llamador ya los ha declarado.
Pbn - 17 - 41© Jaime Alberto Parra Plaza
Si las funciones son estructuradas nunca usarán variables globales sino sólo parámetros de la pila (que debe ser una sola para todas funciones).
La función debe asumir que los parámetros se han colocado siguiendo el método de apilación por la derecha.
Pbn - 17 - 42© Jaime Alberto Parra Plaza
El nombre del segmento de código debe ser igual al usado por el programa llamador. El estándar impuesto por MicroSoft y que es seguido por la mayoría de los compiladores es denominarlo _TEXT.
Si este no es el caso, simplemente se puede leer el archivo .MAP generado por el compilador y determinar el nombre que asigna a los segmentos.
Pbn - 17 - 43© Jaime Alberto Parra Plaza
FUNCIONES:
Para que las funciones puedan ser accesadas desde el exterior del archivo, deben declararse como públicas al comienzo del segmento, en la forma:
public NombreFuncion
Pbn - 17 - 44© Jaime Alberto Parra Plaza
C exige que las funciones que son externas (que están en otro archivo) empiecen su nombre por el caracter de subrayado.
Así que las funciones deben declararse como:
_NombreFunc PROC
. . .
_NombreFunc ENDP
Pbn - 17 - 45© Jaime Alberto Parra Plaza
PARA EL ENSAMBLADOR:
Dado que C es un lenguaje sensible al tipo de letra, debe instruirse al ensamblador para que no cambie las letras a mayúsculas que es su opción por defecto.
Pbn - 17 - 46© Jaime Alberto Parra Plaza
PARA EL COMPILADOR:
Debe explicársele al compilador que la función a llamar está en otro archivo, es decir que es externa, al dar su prototipo:
extern TipoRet NombreFunc(TipoParams);
Para estar seguros que el código será compilado como C estándar, la extensión del archivo debe ser .C
Pbn - 17 - 47© Jaime Alberto Parra Plaza
PARA EL ENLAZADOR:
Una vez el ensamblador y el compilador hayan generado sus respectivos archivos .OBJ, se entregan al enlazador para obtener el producto ejecutable final.
En principio, cualquier enlazador servirá, puesto que el lenguajes origen de los archivos ya no interesa.
Pbn - 17 - 48© Jaime Alberto Parra Plaza
Debe tenerse en cuenta que el programa C requiere un código de inicio que el fabricante ubica en un archivo como código ensamblado. Existe uno distinto para cada modelo de memoria.
En principio, para simplificar las labores, puede hacerse uso de la utilidad de crear un proyecto que ofrecen todos los compiladores y colocar en él los archivos objeto obtenidos.
Pbn - 17 - 49© Jaime Alberto Parra Plaza
NOMENCLATURA:
Anteriormente se mencionó que una macro era la parte pública de un procedimiento y como tal debía tener el nombre significativo. Esto es válido cuando la función va a ser llamada desde otra función assembly. Cuando la función va a llamarse desde otro lenguaje la idea cambia.
Pbn - 17 - 50© Jaime Alberto Parra Plaza
Cuando la función se invoca desde otro lenguaje, el compilador de ese lenguaje se encarga de generar la macro de llamado, así que el nombre significativo debe darse al procedimiento.
Debe aclararse que esto es recomendable sólo cuando dicho procedimiento sea hecho exclusivamente para ser llamado desde otro lenguaje.
Pbn - 17 - 51© Jaime Alberto Parra Plaza
Sea la función a ser llamada:
funcY (int a, char b);
El compilador guarda los parámetros en la pila y llama al procedimiento, el cual deberá llamarse:
_funcY PROC
Pbn - 17 - 52© Jaime Alberto Parra Plaza
Cuando las funciones a invocar son recursivas (tema que se verá posteriormente), se tiene que desde dentro de la función assembly se hace un llamado a ella misma, aquí sí es válido el uso de la macro, puesto que se trata del caso de una función assembly llamando a otra función assembly.
Pbn - 17 - 53© Jaime Alberto Parra Plaza
Sea el llamado de la función recursiva:
funcRecursiva (int a);
De nuevo, el procedimiento se llamará:
_funcRecursiva PROC
. . .
mfuncRecursiva
. . .
_funcRecursiva ENDP
Pbn - 17 - 54© Jaime Alberto Parra Plaza
El código anterior muestra la recomendación para nombres de funciones recursivas que son invocadas desde otro lenguaje:
• Dar al procedimiento el nombre significativo (funcion)
• Dar a la macro el mismo nombre precedido de la letra m (mfuncion)
La macro sirve para que el procedimiento coloque los parámetros antes de llamarse a si mismo.
Pbn - 17 - 55© Jaime Alberto Parra Plaza
PREGUNTA 17:
¿Qué metodología debe seguirse para invocar rutinas de lenguaje ensamblador desde otros lenguajes distintos al C, y viceversa?. Dar ejemplos particulares.
Pbn - 17 - 56© Jaime Alberto Parra Plaza
< FIN DE LA CLASE 17 >
Pbn - 17 - 57© Jaime Alberto Parra Plaza
< FIN DE LA CLASE 17 >