3.2.- Procedimientos. 3.2.1 Procedimientos internos. 3.2.2 3. 2.2 Pr Proce ocedim dimie iento ntos s ex exter ternos nos,, Definición de procedimientos
Un procedimiento es un conjunto de instrucciones que tienen la finalidad de ejecutar una tarea especifica dentro de un programa. Los procedimientos son muy similares a las macros. Un procedimiento se declara una sola vez en el codigo fuente y cuando el programa se ensambla y ejecuta, el procedimiento se coloca en memoria para que pueda ser utilizado por el programa. Acontinuación se presentan los pasos necesarios para ejecutar un procedimiento:
1.-Se encuentra la llamada Call 2.-El microprocesador microprocesador almacena en la Pila el contenido del IP 3.-Se coloca en el IP el valor del desplazamiento correspondiente al Procedimien Procedimiento to 4.-El microprocesador ejecuta las instrucciones del procedimiento 5.-El procedimiento termina cuando se encuentra la instrucción Ret 6.-Se saca de la pila el valor original del IP y se continua el flujo del programa. Un rocedimiento se declara de la siguiente forma:
PROC nombre instrucción instrucción... RET ENDP NOMBRE
3.2.1 Procedimientos internos. Los procedimientos internos son aquellos que se declaran y semismo programa, también son llamados procedimientos locales. Un ejemplo de procedimiento interno es el siguiente: ;Procedimiento: GotoXY ;Descripción: Coloca el cursor una posición especifica de la pantalla ;Parámetros: Dl=X,Dh=Y ;********************************************************************** PROC GotoXY xor bh,bh mov ah,02h int 10h ret endp GotoXY Arriba.
3.2.2 Procedimientos externos. Los procedimientos externos, a diferencia de los internos, se declaran en módulos o programas separados al programa donde el procedimiento es llamado, en otras palabras, la llamada al procedimiento se encuentra en un programa y el procedimiento en otro. Para poder utilizar procedimientos externos, es necesario que sean declarados como públicos en el programa donde se encuentran y que sean llamados como externos en el programa donde serán usados. Se debe contar con tres directivas de ensamble: .PUBLIC para declarar los procedimientos como públicos, .EXTERN para indicar que el procedimiento que se va a usar está fuera del programa y .INCLUDE para enlazar el programa que contiene los procedimientos con el programa que los llama.
PUBLIC PROC1 ; Se define como público PROC1 PROC FAR ; comienzo del procedimiento (lejano) (instrucciones) ; Instrucciones del procedimiento RET ;Instrucción para retornar PROC1 ENDP ; Final del procedimiento Para llamar a un procedimiento se utiliza la instrucción CALL: CALL nombre_procedimiento
Por ejemplo Este programa muestra la forma de utilizar procedimientos y datos externos en los programas por medio de las directivas de inclusión include y public.
.MODEL TINY .INCLUDE proc2.ASM ;Incluir el archivo proc2.asm ;el cual contiene la variable de cadena ;Cad1 y los procedimientos externos ;usados en este programa. .DATA Cad2 db 'Esta es una cadena de prueba 2...',13,10,'$' .CODE INICIO: ;Punto de entrada al programa Mov Dl,20 ;X=20 Mov Dh,10 ;Y=10 Call GotoXY ;GotoXY 20,10 Lea DX,Cad2 ;DX->Cad2 en Proc3.asm Call Imprime_Cad ;Imprime Cad2 Lea DX,Cad1 ;DX->Cad1 en Proc2.asm Call Imprime_Cad ;Imprime Cad1 Mov AX,04C00h ;Fin del programa Int 21h ; END INICIO END
.1.-Directivas para compilación híbrida.
Introducción. La programación hibrida es utilizada en los casos en donde el código en ensamblador dificulta la estructuración del programa, proporciona un mecanismo por medio del cual podemos aprovechar las ventajas del lenguaje ensamblador y los lenguajes de alto nivel, todo esto con el fin escribir programas más rápidos y eficientes. Ejemplo de un programa con un bloque de instrucciones en ensamblador: Este programa muestra como se construye un programa híbrido utilizando un bloque Asm... End; en Turbo Pascal. El programa solicita que se introduzcan dos número, después calcula la suma por medio de la instrucción Add de ensamblador y finalmente imprime el resultado en la pantalla.
Program hibrido; Procedure Limpia_Pantalla; Assembler; Asm Mov AX, 0600h Mov BH, 18h Mov CX, 0000h Mov DX, 184Fh Int 10h End;.
En fin podemos mezclar el código ensamblador con el código de cualquier otro lenguaje que admita este procedimiento. En pascal antes de escribir el código en ensamblador ay que poner Asm y al final de nuestro código End. Por otro lado, Asm nos permite incluir bloques de instrucciones en lenguaje ensamblador en cualquier parte del programa sin necesidad de escribir procedimientos completos en ensamblador. Al trabajar con un lenguaje de alto nivel, en ocasiones nos encontramos con el problema de que necesitamos que haga determinada función o trabajo pero desafortunadamente ésta solo existe en otro lenguaje que no es el que necesitamos utilizar, o simplemente, no encontramos esa función en ningún lenguaje de alto nivel.
En este momento el lenguaje ensamblador constituye una herramienta no solo eficaz, sino simple para producir un parche para el compilador de nuestro lenguaje preferido. Tal vez el mayor problema con el que nos enfrentemos sea el de cómo conectar ambos programas (el de alto y el de bajo niveles) y cómo pasar variables de un programa al otro. Para conseguir nuestro objetivo se utilizan pseudo-operadores, es decir, instrucciones que aparecen en el código fuente del ensamblador pero que no generan ninguna instrucción de máquina, pero proporcionan directivas para que el ensamblador pueda operar con datos, ramificaciones condicionales, generación de listados y con macros durante el proceso de ensamble.
PUBLIC El pseudo-operador que nos interesa es del tipo de pseudooperadores para datos y se conoce como PUBLIC, el cual permite que símbolos en el código fuente sean enlazados por otros programas que se van a enlazar juntos. Para esto, la información es pasada al linker (enlazador). PUBLIC permite el intercambio de información intersegmentos. El formato de este pseudo-operador es PUBLIC número, variable o rótulo
Ejemplo de uso de PUBLIC: PUBLIC nombre Instrucciones RET nombre ENDP
4.2.- Funciones en Ensamblador . Para llamar a las interrupciones es conveniente conocer antes ciertas estructuras y uniones.
struct WORDREGS { unsigned int cx, dx, si, di, cflag, flags; };
ax, bx,
struct BYTEREGS { unsigned char bl, bh, cl, ch, dl, dh; };
al, ah,
union
REGS
struct unsigned int cs;
struct
{ struct
struct BYTEREGS h; };
SREGS { unsigned int ss; };
REGPACK { r_cx, r_dx;
WORDREGS x;
unsigned int es; unsigned int ds;
unsigned
r_ax, r_bx, unsigned
r_bp, r_si, r_di, r_ds, r_es, r_flags; };
���������
A continuación, se listan las funciones que permiten invocar las interrupciones:
int int86(int interrupción, union REGS *entrada, union REGS *salida); int int86x(int interrupción, union REGS *entrada, union REGS *salida, struct REGS *rsegmento); void intr(int interrupción, struct REGPACK *registros);
Las dos primeras funciones se basan en la declaración de dos uniones: una para entrada y otra para salida, que simbolizan los valores iniciales (antes de llamar a la interrupción) y finales (tras la llamada) en los registros. Si se desea que la misma unión que indica los valores iniciales devuelva los finales, se puede indicar por duplicado: union REGS regs; regs.h.ah = 0; regs.h.al = 0x13; /* VGA 320x200 - 256 colores */ int86 (0x10, ®s, ®s); /* cambiar modo de vídeo */
LLAMADAS DESDE UN PROGRAMA ESCRITO EN C Las llamadas desde C varían sensiblemente de las de Pascal. Lo primero que hay que tener en cuenta es que el orden de los
parámetros es el inverso. Se leen de derecha a izquierda en la declaración de la función o procedimiento. Otra característica es que el compilador de C añade al principio del nombre de la función o procedimiento el carácter de subrayado, por lo que es necesario hacerlo también en el módulo ensamblador. La última es que el retorno de la función se debe hacer simplemente con RET , sin parámetros. El compilador de C se encarga de añadir el código necesario para eliminar los parámetros de la pila:
Test( i , j , 1); se traduciría en: mov ax,1 push ax push WORD PTR DGROUP:_j push WORD PTR DGROUP:_i call NEAR PTR_Test add sp, En las versiones más modernas del compilador se admiten las llaves '{' y '}' para agrupar varias sentencias asm: asm { push ax; push cx; mov cx,dato1 mov ax,0h } mult: asm { add ax,dato2 loop mult mov resultado,ax pop cx; pop ax; }
4.3.- Operadores. Operadores Aritméticos: Pueden emplearse libremente (+), (-), (*) y (/). En este último caso la división es siempre entera. También se admiten los operadores MOD (resto de la división) y SHL/SHR (desplazar a la izquierda/derecha cierto número de bits). Operadores Lógicos: Pueden ser el AND, OR, XOR y NOT. Realizan las operaciones lógicas en las expresiones.
Operadores relacionales: Devuelven condiciones de cierto (0FFFFh o 0FFh) o falso (0) evaluando una expresión. Pueden ser: EQ (igual), NE (no igual), LT (menor que), GT (mayor que), LE (menor o igual que), GE (mayor o igual que). Operador PTR: Redefine el atributo de tipo (BYTE, WORD, DWORD, QWORD, TBYTE) o el de distancia (NEAR o FAR) de un operando de memoria. Por ejemplo, si se tiene una tabla definida de la siguiente manera: Tabla DW 10 DUP (0) ; 10 palabras a 0 Para colocar en AL el primer byte de la misma, la instrucción MOV AL, tabla es incorrecta, ya que tabla (una cadena 10 palabras) no cabe en el registro AL. Lo que desea el programador debe indicirselo en este caso explícitamente al ensamblador de la siguiente manera:
MOV AL,BYTE PTR tabla Los operadores de memoria son operadores unarios que devuelven el resultado de una operación directa de memoria. Estos operadores se utilizan principalmente para depurar código en lenguaje ensamblador. {BY | WO | DW} address
El operador BY devuelve un entero corto que contiene el primer byte de la dirección (address). Este operador simula BYTE PTR. El operador WO devuelve un entero corto que contiene el valor de la palabra, dos bytes, de la dirección (address). Este operador simula la operación WORD PTRde Microsoft Macro Assembler. El operador DW devuelve un entero largo que contiene el valor de los cuatro primeros bytes de la dirección (address). Este operador simula DWORD PTR. El especificador de formato x que se utiliza en algunos de estos ejemplos hace que el resultado se muestre en hexadecimal. Ejemplos •
Para mostrar el primer byte en la dirección de la variable sum:
BY sum •
Para mostrar la primera palabra en la dirección de la variable new_set: WO new_set
•
Para mostrar la palabra doble en la dirección de sum: DW sum
•
Para mostrar el byte al que apunta el registro EBP con un desplazamiento de 6: BY ebp+6,x
•
Para mostrar la palabra a la que apunta el puntero de pila, la última palabra insertada en la pila: WO esp,x
•
Para mostrar la palabra doble a la que apunta el registro ESI: DW esi,x
4.3.- Operadores. Operadores Aritméticos: Pueden emplearse libremente (+), (-), (*) y (/). En este último caso la división es siempre entera. También se admiten los operadores MOD (resto de la división) y SHL/SHR (desplazar a la izquierda/derecha cierto número de bits). Operadores Lógicos: Pueden ser el AND, OR, XOR y NOT. Realizan las operaciones lógicas en las expresiones. Operadores relacionales: Devuelven condiciones de cierto (0FFFFh o 0FFh) o falso (0) evaluando una expresión. Pueden ser: EQ (igual), NE (no igual), LT (menor que), GT (mayor que), LE (menor o igual que), GE (mayor o igual que).
Operador PTR: Redefine el atributo de tipo (BYTE, WORD, DWORD, QWORD, TBYTE) o el de distancia (NEAR o FAR) de un operando de memoria. Por ejemplo, si se tiene una tabla definida de la siguiente manera: Tabla DW 10 DUP (0) ; 10 palabras a 0 Para colocar en AL el primer byte de la misma, la instrucción MOV AL, tabla es incorrecta, ya que tabla (una cadena 10 palabras) no cabe en el registro AL. Lo que desea el programador debe indicirselo en este caso explícitamente al ensamblador de la siguiente manera:
MOV AL,BYTE PTR tabla Los operadores de memoria son operadores unarios que devuelven el resultado de una operación directa de memoria. Estos operadores se utilizan principalmente para depurar código en lenguaje ensamblador. {BY | WO | DW} address
El operador BY devuelve un entero corto que contiene el primer byte de la dirección (address). Este operador simula BYTE PTR. El operador WO devuelve un entero corto que contiene el valor de la palabra, dos bytes, de la dirección (address). Este operador simula la operación WORD PTRde Microsoft Macro Assembler. El operador DW devuelve un entero largo que contiene el valor de los cuatro primeros bytes de la dirección (address). Este operador simula DWORD PTR. El especificador de formato x que se utiliza en algunos de estos ejemplos hace que el resultado se muestre en hexadecimal. Ejemplos •
Para mostrar el primer byte en la dirección de la variable sum: BY sum •
Para mostrar la primera palabra en la dirección de la variable new_set:
WO new_set •
Para mostrar la palabra doble en la dirección de sum: DW sum
•
Para mostrar el byte al que apunta el registro EBP con un desplazamiento de 6: BY ebp+6,x
•
Para mostrar la palabra a la que apunta el puntero de pila, la última palabra insertada en la pila: WO esp,x
•
Para mostrar la palabra doble a la que apunta el registro ESI: DW esi,x
4.4.- Integrar módulos de ensamblador en lenguajes de alto nivel. LA SENTENCIA ASM
La sentencia asm permite incluir código ensamblador dentro del programa C, utilizando los mnemónicos normales del ensamblador. Sin embargo, el uso de esta posibilidad está más o menos limitado según la versión del compilador. En Turbo C 2.0, los programas que utilizan este método es necesario salir a la línea de comandos para compilarlos con el tradicional compilador de línea, lo cual resulta poco atractivo. En Turbo C++ 1.0, se puede configurar adecuadamente el compilador para que localice el Turbo Assembler y lo utilice automáticamente para ensamblar, sin necesidad de salir del entorno integrado. Sin embargo, es a partir del Borland C++ cuando se puede trabajar a gusto: en concreto, la versión Borland C++ 2.0 permite ensamblar sin rodeos código ensamblador incluido dentro del listado C. El único inconveniente es la limitación del hardware disponible: para un PC/XT, el Turbo C 2.0 es el único compilador aceptablemente rápido. Sin embargo, en un 286 es más recomendable el Turbo C++, mientras que en un 386 modesto (o incluso en un 286 potente) resulta más interesante emplear el
Borland C++ 2.0: las versiones 3.X de este compilador son las más adecuadas para un 486 o superior (bajo DOS). La sintaxis de asm se puede entender fácilmente con un ejemplo. El propósito del siguiente programa: Eliminar archivos dando la direccion de donde se encuentra el archivo que se decea borrar y la extencion del mismo, si no te mandara un error : #include #include int main(){ int orror=0; char * dir; clrscr(); printf("Direccion del archivo:\n"); scanf("%s",dir); _asm{ asm mov ah,0041h asm mov dx,dir asm int 21h asm jc oorror asm jmp continuar } oorror: asm mov orror, ax continuar: if(orror){ printf("!Error de Archivo!"); } else{ printf("!Exito al eliminar Archivo!"); } getch(); return -1; }
Otro ejemplo es la elaboración de de un programa, es la elaboración de un reloj hibrido, como se muestra acontinuación: # include # include void main (void) { char bandera, horas, minutos, segundos, centesimas;
clrscr(); bandera=0; for (;;) { _asm{ mov ah,2ch int 21h mov horas,ch mov minutos,cl mov segundos,dh mov centesimas,dl } if (segundos!=bandera) { printf ("\n%i:%i:%i. %i",horas,minutos,segundos,centesimas); bandera=segundos; } } getch(); }