UNIVERSIDAD DE EL SALVADORFACULTAD DE INGENIERIA Y ARQUITECTURA ESCUELA DE INGENIERIA ELECTRICA SISTEMAS DIGITALES PROGRAMABLES 19 DE MAYO DE 2017 LAB05: USO DEL LENGUAJE ENSAMBLADOR CON EL COMPLILADOR C. PORCENTAJE NOTA 1) INTRODUCCION 5% 2) OBJETIVOS 5% 3) MARCO TEORICO 10% 4) DESARROLLO DE LA PRACTICA 10% 5) ASIGNACIONES 40% ASIGNACION 1 8% ASIGNACION 2 8% ASIGNACION 3 8% ASIGNACION 4 8% ASIGNACION 5 8% CONCLUSIONES 30% NOTA FINAL ESTUDIANTES: Tabla de contenido INTRODUCCION ................................................................................................................................... 2 OBJETIVOS ........................................................................................................................................... 3 MARCO TEORICO ................................................................................................................................. 4 DESARROLLO DE LA PRÁCTICA ............................................................................................................ 5 ASIGNACIONES .................................................................................................................................... 8 CONCLUSIONES ................................................................................................................................. 15 1 INTRODUCCION. El lenguaje ensamblador sirve para muchos propósitos, como mejorar la velocidad del programa, reducir la memoria necesaria y supervisar el hardware. Pero hoy día, pocos programas están escritos completamente en ensamblador. Los compiladores son muy buenos en convertir código de alto nivel en un código de máquina eficiente. Ya que es mucho más fácil escribir código en un lenguaje de alto nivel, es más popular. Además, el código de alto nivel es mucho más portátil que el ensamblador. Cuando se usa ensamblador, se usa a menudo solo para pequeñas partes de código. Esto se puede hacer de dos maneras: llamando rutinas de ensamblador desde C o ensamblado en línea. Se puede utilizar el ensamblado en línea para incrustar instrucciones de lenguaje de ensamblado directamente en los programas de origen C y C++ sin realizar pasos adicionales de ensamblado y vínculo. El ensamblador alineado se compila en el programa compilador, por lo que no es necesario un ensamblador independiente como Microsoft Macro Assembler (MASM). Se puede usar esta manera mencionada usando la sentencia “asm” que permite incluir código ensamblado 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. Como otra opción se tiene ensamblar el código y luego enlazarlo a la compilación del código en C como llamado de rutina. Para esta práctica de laboratorio se estudian los pasos a seguir para la combinación de lenguaje ensamblador y lenguaje de alto nivel C, a través de la herramienta Borland. 2 OBJETIVOS General: Conocer las estructuras básicas para la combinación del lenguaje ensamblador con lenguaje de alto nivel C, así como las diferentes maneras en las que se puede usar esta mezcla para la creación de programas más eficientes. Específicos: Hacer uso de la herramienta Borland para la combinación de lenguaje ensamblador y lenguaje C. Utilizar como método principal para la combinación de ambos lenguajes el incluir código ensamblador dentro del programa escrito en código C. 3 MARCO TEORICO. 1. Incluir rutinas en ensamblador desde C. La palabra ASM Este es un mecanismo de escape que permite escribir código ensamblador para el hardware dentro de un programa en C. A menudo es capaz de referenciar variables C dentro del código ensamblador, lo que significa que se puede comunicar fácilmente con el código C y limitar el código ensamblador a lo necesario para ajustes eficientes o para utilizar instrucciones especiales del procesador. La sintaxis exacta que se debe usar cuando se escribe en lenguaje ensamblador es dependiente del compilador y se puede encontrar en la documentación del compilador. 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. 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). Como ejemplo del uso de esta sentencia se muestra las siguiente instrucciones que permiten cambiar el modo de video de la pantalla a un modo VGA estándar con código ensamblador, es mucho más sencillo: 4 DESARROLLO DE LA PRÁCTICA Practica 1: Compile el código apéndice A con Borland C: Código apéndice A: 1. #include <stdio.h> 2. /* 3. Este ejemplo muestra como combinar el codigo en lenguaje 4. Ensamblador con codigo C 5. Realiza las operaciones basicas suma, resta, multiplicacion y 6. Division con dos numeros de 8 bits 7. */ 8. int main() 9. { 10. int dato1, dato2, suma, resta, multiplicacion,division; 11. printf("Multiplicación de dos numeros \n"); 12. printf("\nPrimer numero: "); 13. scanf("%d", &dato1); 14. printf("Segundo número: "); 15. scanf("%d", &dato2); 16. 17. asm{ 18. push dx //Introduciendo los registros a la pila 19. push cx 20. push bx 21. push ax 22. 23. //Suma 24. mov ax,dato1 25. add ax,dato2 26. mov suma,ax 27. 28. //Resta 29. mov ax,dato1 30. sub ax,dato2 31. mov resta,ax 32. 33. //Multiplicacion 34. mov ax,dato1 35. imul dato2 36. mov multiplicacion , ax 37. 38. //Division 39. mov ax,dato1 40. div dato2 41. mov ah,0 42. mov division,ax 43. 44. pop ax 45. pop bx 46. pop cx 5 47. pop dx //Devolviendo los registros de la pila 48. 49. } 50. printf("\nSuma: %d\n", suma); 51. printf("Diferencia: %d\n",resta); 52. printf("Producto: %d\n", multiplicacion); 53. printf("Cociente: %d\n", division); 54. return 0; 55. } Al compilar el codigo anterior en Borland C se puede observar que el programa realiza las cuatro operaciones basicas, con dos numeros ingresados por el usuario como muestra la figura 1. Fig. 1: Compilación del código del Apéndice A. Practica 2: Compile el código del Apéndice B con Borlad C. Código apéndice B: 1. #include <stdio.h> 2. 3. main() 4. { 5. //El siguiente programa representa un ejemplo de una 6. //transmision de bytes de datos utilizando sus bits de paridad 7. //aunque los datos solo se miran en pantalla, en la 8. //practica se utiliza un puerto y la paridad para detectar 9. //errores en la recepcion. 10. 11. int byte1,byte2, paridad; 12. printf("introdusca el primer byte en hexadecimal \n"); 13. scanf("%x", &byte1); 14. printf("introdusca el segundo byte en hexadecimal \n"); 15. scanf("%x", &byte2); 6 16. 17. asm{ 18. push ax //Guardamos los registros que usaremos 19. push bx //Por razones de seguridad 20. mov paridad,0 21. mov ax,0 22. mov bx,byte1 23. and bx,0ffh //Operacion logica que activa la bandera de paridad 24. pushf //Guardamos el registro de banderas en la pila 25. pop ax //Y lo recuperamos sin alterarlo 26. and ax,04h //Probamos la bandera de paridad 27. cmp ax,04h 28. jne seguir 29. inc paridad //Incrementamos el resultado si hay bit de paridad 30. } 31. seguir: 32. asm{ 33. mov bx,byte2 //Repetimos lo mismo que hicimos 34. and bx,0ffh //con el primer byte 35. pushf 36. pop ax 37. and ax,04h 38. cmp ax,04h 39. jne final 40. add paridad, 2h 41. } 42. final: 43. asm{ 44. pop bx //Recuperacion de los registros antes 45. pop ax //De terminar las lineas de ensamblador 46. } 47. printf("\n byte con la paridad junto a los dos bytes originales:\n"); 48. printf("%X%X%X\n",paridad,byte2,byte1); 49. return 0; 50. } Fig. 2: Compilación del código del Apéndice B, obtiene paridad para detectar errores de transmisión . 7 ASIGNACIONES. Asignación 1: Mencionar 5 ventajas de combinar el lenguaje ensamblador con el compilador C. Programar la mayor parte del código en un lenguaje de alto nivel, lo que permite reducir el tamaño del código en general además de hacerlo más portable y eficiente. Hacer uso de lenguaje ensamblador para partes de código donde se necesita la máxima eficiencia ya que las instrucciones en ensamblador se ejecutan directamente desde la memoria. Permite obtener una mayor velocidad de los programas, esto ya que las instrucciones de ensamblador se ejecutan más rápidas que las de C, además Permite acceso directo a hardware para controladores de dispositivos. Para el ensamblado en línea no es necesario compilar y enlazar con diferentes programas para cada lenguaje, ya que incluyendo rutinas del ensamblador en el lenguaje C por medio de la instrucción asm el compilador de C puede ejecutar el programa. La combinación de lenguaje ensamblador con lenguaje de alto nivel C, permite obtener programas más eficientes y rápidos. Asignación 2: Mencione 5 aplicaciones de programas que utilicen lenguaje ensamblador con el compilador C. El kernel de un sistema operativo es un espacio tan cercano al hardware que únicamente puede ser realizado mediante el uso de ensamblador y lenguaje C. por ejemplo: Linux, Minix, Hurd, FreeBSD, OpenBSD, y Darwing, el kernel de Mac OS. máquinas virtuales de Java y .NET, desarrolladas en C usan instrucciones de código ensamblador. compiladores e intérpretes: se usa C e instrucciones en ensamblador en los de Perl o PHP, C++ en los lenguajes .NET y en el GCC, y Java para compilarse a sí mismo. Programas que requieran acceso directo a periféricos de entrada y salida pueden ser programados en C con instrucciones de ensamblador. 8 Aplicaciones que requieran una alta velocidad de ejecución, pueden ser programadas en C y por medio de instrucciones de ensamblador aumentar la velocidad de operaciones realizadas. Un ejemplo pueden ser aplicaciones de tabulación de datos en tiempo real. Asignación 3: Hacer un programa con 5 posiciones en memoria c/u de las cuales contienen un dato diferente, el usuario introduce un dato que es comparado con las posiciones de memoria, si el dato coincide, despliegue un mensaje “USTED ES GANADOR” de lo contrario despliegue el mensaje “SUERTE LA PROXIMA VEZ”.Recuerde: Hacer el programa lo más vistoso y práctico para el usuario con las herramientas conocidas de C o C++, es decir la captura de datos y despliegue de mensajes, las demás instrucciones deben realizarse con ensamblador. Descripción del código: El objetivo de este código es hacer un programa C usando instrucciones de ensamblador por medio de la sentencia asm, como se puede observar todas las configuraciones de la pantalla se hacen con instrucciones de ensamblador, así como la validación del dato ingresado para mostrar el mensaje especifico. Código: 1. #include <stdio.h> 2. 3. int main() 4. { 5. int pos1 = 14, pos2 = 10, pos3 = 50, pos4 = 69, pos5 = 99; 6. int dato; 7. char mensaje1[] = "USTED ES UN GANADOR"; 8. char mensaje2[] = "SUERTE LA PROXIMA VEZ"; 9. 10. asm{ 11. mov ah,06h //Borrando la pantalla 12. mov cx,0000 13. mov dx,2379 14. int 10h 15. 16. mov ah,02H //Posicionando el cursor en pantalla 17. mov dh,10 18. mov dl,10 19. mov bh,0 20. int 10h 21. } 22. 23. printf("INTRODUZCA UN NUMERO DEL 1 AL 100 Y DESCUBRA SI ES UN GANADOR\n"); 24. 25. asm{ 26. mov ah,02H //Posicionando el cursor 27. mov dh,12 9 28. mov dl,40 29. mov bh,0 30. int 10h 31. } 32. scanf("%d",&dato); //Leyendo el dato del usuario 33. 34. asm{ 35. mov ah,02H //Posicionando el cursor 36. mov dh,15 37. mov dl,31 38. mov bh,0 39. int 10h 40. } 41. 42. asm{ 43. 44. mov cx,dato //Comparando el dato contra 45. mov ax,pos1 //la primer posicion de memoria 46. cmp ax,cx 47. je ganador 48. 49. mov ax,pos2 //Comparando el dato contra 50. cmp ax,cx //la segundo posicion de memoria 51. je ganador 52. 53. mov ax,pos3 //Comparando el dato contra 54. cmp ax,cx //la tercera posicion de memoria 55. je ganador 56. 57. mov ax,pos4 //Comparando el dato contra 58. cmp ax,cx //la cuarta posicion de memoria 59. je ganador 60. 61. mov ax,pos5 //Comparando el dato contra 62. cmp ax,cx //la quinta posicion de memoria 63. je ganador 64. jmp fin //Salta a fin si no se encuentra 65. } //igualdad en las posiciones de memoria 66. 67. ganador: 68. printf("%s",mensaje1); 69. return 0; 70. 71. fin: 72. printf("%s",mensaje2); 73. return 0; 74. } Al compilar el código en Borland C se obtuvo lo que muestra la figura 3 para el ingreso de un dato valido y la figura 4 para el ingreso de un dato valido. 10 Fig. 3: Programa escrito en lenguaje C, con instrucciones de ensamblador para la asignación 3,muestra un mensaje para un numero invalido. Fig. 4: Programa escrito en lenguaje C, con instrucciones de ensamblador para la asignación 3, muestra un mensaje para un numero valido. 11 Asignación 4: Hacer un programa que espere una clave desde consola por ejemplo “programables”; el programa debe comprobar la clave tecleada con la que se encuentra en una variable, y si coinciden encender un led verde, si son diferentes encender un led rojo. Puede utilizar el puerto paralelo. Hágalo con instrucciones de C para escribir en el puerto, y también hágalo accesando el puerto con lenguaje ensamblador. Comente diferencias. Descripción del código: Para este código escrito en C, se hace uso de instrucciones en ensamblador para validar una clave ingresada y mostrar en pantalla el mensaje de validación. Código: 1. #include <stdio.h> 2. 3. int main () 4. { 5. char clave1[12]="programables"; 6. char clave[12]; 7. printf ("\nBienvenido\n"); 8. printf("Introduzca la contrasena\n"); 9. scanf("%s",&clave); 10. 11. asm{ 12. push dx 13. push cx 14. push bx 15. push ax 16. 17. cld 18. mov si, offset clave1 19. mov di, offset clave 20. mov cx,12 21. repne cmpsb //comparando los caracteres ingresados 22. jcxz wrong //contra la contraseña almacenada 23. jmp right 24. 25. } 26. right: 27. printf("\nLa contrasena ingresada es correcta\n"); 28. asm { 29. mov dx,378h 30. mov al,01h //asumiendo que el LED verde esta en el bit o del puerto 31. out dx,al 32. jmp fin 33. } 34. wrong: 35. printf("\nLa contrasena ingresada es incorrecta\n"); 36. asm { 37. mov dx,378h 38. mov al,02h //asumiendo que el LED rojo esta en el bit 1 del puerto 39. out dx,al 40. jmp fin 12 41. } 42. fin: 43. asm { 44. pop ax 45. pop bx 46. pop cx 47. pop dx 48. } 49. return 0; 50. } Al compilar el código se obtiene el programa deseado mostrado en la figura 5, donde se muestra para la clave valida e invalida. Fig. 5: Programa de validación de clave escrito en C con instrucciones de ensamblador para la asignación 4. Asignación 5: Investigar cómo se puede trabajar con lenguaje ensamblador en el sistema operativo Linux, es decir con que programa se puede “ensamblar” y “linkear” un código de este lenguaje, y cómo usar en este sistema el lenguaje c con lenguaje ensamblador. Ilustrar un ejemplo sencillo de cada caso. Existen diferentes herramientas para utilizar lenguaje ensamblador en Linux. Entre ellas se encuentra el programa ensamblador GAS (AS), en el cual se debe escribir el programa con sintaxis de AT&T, que tiene las siguientes características: Operación fuente, destino Se debe indicar el tamaño de los operandos (byte [b], palabra [w], etc.) en cada opcode. Se preceden los registros con un “%” y los literales con el signo “$”. 13 Código ejemplo: 14 CONCLUSIONES La combinación de lenguaje ensamblador con lenguaje de alto nivel C, permite la creación de programas más efectivos ya que comparte la portabilidad y eficiencia de lenguaje C y la rapidez de ejecución de las instrucciones en ensamblador. Se puede utilizar el ensamblado en línea para incrustar instrucciones de lenguaje de ensamblado directamente en los programas de origen C y C++ sin realizar pasos adicionales de ensamblado y vínculo. El uso de instrucciones de ensamblador en lenguaje C, permite el acceso directo a periféricos de entrada y salida, por lo que puede facilitar ciertas tareas a la hora de programar. Las aplicaciones que requieren alta velocidad de ejecución deben tener estrictamente instrucciones de ensamblador ya que ningún otro lenguaje puede acceder directamente a la memoria de la maquina. 15