UNIVERSIDAD DE CASTILLA-LA MANCHA
Transcripción
UNIVERSIDAD DE CASTILLA-LA MANCHA
UNIVERSIDAD DE CASTILLA-LA MANCHA ESCUELA SUPERIOR DE INFORMÁTICA INGENIERÍA EN INFORMÁTICA PROYECTO FIN DE CARRERA Entorno de programación para videoconsola Nintendo DS con fines docentes Manuel Rodrigo Gómez Septiembre, 2009 UNIVERSIDAD DE CASTILLA-LA MANCHA ESCUELA SUPERIOR DE INFORMÁTICA Departamento de Informática PROYECTO FIN DE CARRERA Entorno de programación para videoconsola Nintendo DS con fines docentes Autor: Manuel Rodrigo Gómez Director: Francisco Moya Fernández Septiembre, 2009 TRIBUNAL: Presidente: Vocal 1: Vocal 2: Secretario: FECHA DE DEFENSA: CALIFICACIÓN: PRESIDENTE Fdo.: VOCAL 1 Fdo.: VOCAL 2 Fdo.: SECRETARIO Fdo.: c Manuel Rodrigo Gómez. Se permite la copia, distribución y/o modificación de este docu mento bajo los términos de la licencia de documentación libre GNU, versión 1.1 o cualquier versión posterior publicada por la Free Software Foundation, sin secciones invariantes. Puede consultar esta licencia en http://www.gnu.org. Este documento fue compuesto con LATEX. Imágenes generadas con OpenOffice. RESUMEN ABSTRACT Portable video game systems are not only a multibillion-dollar computer industry, but also an attractive platform to enable people with knowledge of computer organization and architecture to develop innovative software applications. The authors show how socalled ”homebrewçomputing has led to significant innovations in computer science, and, by explaining the organization and architecture of the Nintendo DS, show how this game system is, in many ways, an ideal homebrew computing device. In addition, readers with an interest in homebrew computing are provided a guide to getting started with programming for the DS platform. AGRADECIMIENTOS Índice general Lista de figuras V Lista de tablas IX 1. INTRODUCCIÓN 1 1.1. Motivación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.2. Justificación del trabajo . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.3. Estructura del documento . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 2. OBJETIVOS DEL PROYECTO 5 2.1. Objetivos generales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.2. Objetivos especı́ficos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 9 3.1. La videoconsola Nintendo DS . . . . . . . . . . . . . . . . . . . . . . . . 10 3.1.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 3.1.2. Familia de la Nintendo DS . . . . . . . . . . . . . . . . . . . . . . 10 3.1.3. El hardware de la Nintendo DS . . . . . . . . . . . . . . . . . . . 11 3.2. Desarrollo de software casero para Nintendo DS . . . . . . . . . . . . . . . 16 3.2.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3.2.2. DevkitPro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 3.2.3. DevkitARM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 3.2.4. GNU Debugger . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.2.5. Entornos cruzados de desarrollo . . . . . . . . . . . . . . . . . . . 20 3.2.6. Emuladores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 3.3. Compilando para Nintendo DS . . . . . . . . . . . . . . . . . . . . . . . . 26 3.3.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 3.3.2. ABI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 3.3.3. EABI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 I ÍNDICE GENERAL II 3.3.4. AAPCS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 3.3.5. Explorando el ABI con GDB y DeSmuME . . . . . . . . . . . . . . 37 3.4. Gráficos con Nintendo DS . . . . . . . . . . . . . . . . . . . . . . . . . . 40 3.4.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 3.4.2. Interacción con los periféricos . . . . . . . . . . . . . . . . . . . . 42 3.4.3. Modo framebuffer . . . . . . . . . . . . . . . . . . . . . . . . . . 44 3.4.4. Modo extended rotoscale . . . . . . . . . . . . . . . . . . . . . . . 54 3.4.5. Modo teselado . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 4. MÉTODO DE TRABAJO 83 4.1. Adaptación del problema al Proceso Unificado . . . . . . . . . . . . . . . . 84 4.2. Construcción del entorno cruzado de desarrollo . . . . . . . . . . . . . . . 84 4.2.1. Comprensión del software . . . . . . . . . . . . . . . . . . . . . . 84 4.2.2. Construcción del diagrama de casos de uso . . . . . . . . . . . . . 88 4.2.3. Fase de análisis . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 4.2.4. Fase de diseño . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 4.2.5. Fase de implementación . . . . . . . . . . . . . . . . . . . . . . . 93 4.2.6. Pruebas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 4.3. Construcción del editor de fondos . . . . . . . . . . . . . . . . . . . . . . 95 4.3.1. Construcción del diagrama de casos de uso . . . . . . . . . . . . . 95 4.3.2. Priorización de casos de uso . . . . . . . . . . . . . . . . . . . . . 97 4.3.3. Iteración 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 4.3.4. Iteración 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 4.3.5. Iteración 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 4.3.6. Iteración 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 4.3.7. Iteración 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 4.3.8. Iteración 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 4.3.9. Iteración 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 4.3.10. Iteración 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 4.3.11. Iteración 9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 5. RESULTADOS 153 5.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 6. CONCLUSIONES Y PROPUESTAS 155 6.1. Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 6.2. Propuestas y lı́neas de investigación futuras . . . . . . . . . . . . . . . . . 156 ÍNDICE GENERAL III 6.2.1. Entorno cruzado de desarrollo . . . . . . . . . . . . . . . . . . . . 157 6.2.2. Editor de fondos . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 A. TUTORIAL DEL ENTORNO DE PROGRAMACIÓN A.1. Entorno cruzado de desarrollo . . . . . . . . . . . . A.1.1. Instalación en Debian GNU/Linux . . . . . . A.1.2. Creación de un proyecto para NDS . . . . . A.1.3. Configuración del proyecto . . . . . . . . . . A.1.4. Edición del archivo fuente . . . . . . . . . . A.1.5. Depuración del proyecto . . . . . . . . . . . A.1.6. Sesión de depuración . . . . . . . . . . . . . A.2. Editor de fondos . . . . . . . . . . . . . . . . . . . . A.2.1. Ejecución . . . . . . . . . . . . . . . . . . . A.2.2. Abrir imagen . . . . . . . . . . . . . . . . . A.2.3. Reorganizar paleta . . . . . . . . . . . . . . A.2.4. Convertir fondo . . . . . . . . . . . . . . . . A.2.5. Personalización del fondo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 159 159 160 160 163 165 166 167 167 168 170 170 171 B. FIGURAS 175 B.1. Resultados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175 Bibliografı́a 183 ÍNDICE GENERAL IV Lista de figuras 3.1. Nintendo DS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 3.2. Nintendo DS Lite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 3.3. Nintendo DSi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 3.4. Esquema del mapa de memoria de la NDS [21] . . . . . . . . . . . . . . . 14 3.5. Asignación de bancos de VRAM [18] . . . . . . . . . . . . . . . . . . . . 15 3.6. Arquitectura de la plataforma Eclipse [14] . . . . . . . . . . . . . . . . . . 22 3.7. Entorno conectado a un dispositivo remoto vı́a JTAG/BDM [12] . . . . . . 24 3.8. ABI para la arquitectura ARM [5] . . . . . . . . . . . . . . . . . . . . . . 28 3.9. Orden de los bytes en big-endian y en little-endian [8] . . . . . . . . . . . 30 3.10. Alineamiento del tipo compuesto MyData [28] . . . . . . . . . . . . . . . 32 3.11. Alineamiento de una estructura equivalente a MyData [28] . . . . . . . . . 33 3.12. La ejecución alcanza el punto de interrupción establecido . . . . . . . . . . 40 3.13. Exploración del ABI con KDbg . . . . . . . . . . . . . . . . . . . . . . . 41 3.14. Conexión de los periféricos de entrada-salida con el computador [26] . . . . 43 3.15. Esquema de la memoria destinada a los fondos de mapas de bits [24] . . . . 55 3.16. Ejemplo de paleta de colores [24] . . . . . . . . . . . . . . . . . . . . . . 58 3.17. Ejemplos de transformaciones afines . . . . . . . . . . . . . . . . . . . . . 61 3.18. Elementos básicos de los fondos teselados [25] . . . . . . . . . . . . . . . 65 3.19. Direcciones base para un mapa de 32×32 y de 64×64 [25] . . . . . . . . . 67 3.20. Desplazamientos en la memoria de fondos [25] . . . . . . . . . . . . . . . 68 3.21. Ejemplo de tesela [25] . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 3.22. Tesela en forma de flecha [25] . . . . . . . . . . . . . . . . . . . . . . . . 72 3.23. Misma tesela con diferente paleta [25] . . . . . . . . . . . . . . . . . . . . 73 3.24. Ejemplo de fondo del juego Yoshi Island [1] . . . . . . . . . . . . . . . . . 75 3.25. Ejemplo de fondo del juego Super Nenas [1] . . . . . . . . . . . . . . . . . 75 3.26. Tesela que representa la letra L [25] . . . . . . . . . . . . . . . . . . . . . 76 3.27. 16 bits que componen la entrada de un mapa [25] . . . . . . . . . . . . . . 78 3.28. Resultado final [25] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 V LISTA DE FIGURAS VI 3.29. Generando mapas, teselas y paletas [25] . . . . . . . . . . . . . . . . . . . 81 4.1. Interfaz gráfica de Zylin Embedded CDT . . . . . . . . . . . . . . . . . . 85 4.2. Diagrama de clases de una parte del plug-in Zylin Embedded CDT . . . . . 87 4.3. Vista funcional del entorno cruzado de desarrollo . . . . . . . . . . . . . . 89 4.4. Clases de análisis involucradas en el caso de uso Depurar mediante emulador 90 4.5. Diagrama de secuencia correspondiente al flujo normal . . . . . . . . . . . 91 4.6. Diagrama de clases modificado del plug-in Zylin Embedded CDT . . . . . 92 4.7. Vista funcional del editor de fondos . . . . . . . . . . . . . . . . . . . . . 96 4.8. Clases de análisis involucradas en el caso de uso Abrir imagen . . . . . . . 99 4.9. Clases de análisis involucradas en el caso de uso Crear fondo . . . . . . . . 100 4.10. Clases de análisis involucradas en el caso de uso Crear extended rotoscale . 103 4.11. Diagrama de secuencia del patrón MVP [13] . . . . . . . . . . . . . . . . . 104 4.12. Diagrama de clases del patrón MVP [13] . . . . . . . . . . . . . . . . . . . 105 4.13. Construcción del diagrama de clases del sistema utilizando el patrón MVP . 106 4.14. Diagrama de secuencia correspondiente al flujo normal . . . . . . . . . . . 106 4.15. Diagrama de secuencia correspondiente al flujo alternativo . . . . . . . . . 107 4.16. Diagrama de clases utilizando el patrón Builder . . . . . . . . . . . . . . . 108 4.17. Diagrama de secuencia correspondiente al flujo normal . . . . . . . . . . . 108 4.18. Diagrama de clases utilizando el patrón Proxy . . . . . . . . . . . . . . . . 109 4.19. Diagrama de secuencia correspondiente al flujo normal . . . . . . . . . . . 110 4.20. Diálogo de selección del tipo de fondo . . . . . . . . . . . . . . . . . . . . 111 4.21. Clases de análisis involucradas en el caso de uso Crear teselado . . . . . . 118 4.22. Diagrama de secuencia correspondiente al flujo normal . . . . . . . . . . . 120 4.23. Diagrama de secuencia correspondiente al flujo normal . . . . . . . . . . . 123 4.24. Clases de análisis involucradas en el caso de uso Modificar paleta . . . . . 125 4.25. Clases de análisis involucradas en el caso de uso Cambiar color . . . . . . 127 4.26. Clases de análisis involucradas en el caso de uso Modificar tesela . . . . . . 128 4.27. Clases de análisis involucradas en el caso de uso Agregar color nuevo . . . 129 4.28. Clases de análisis involucradas en el caso de uso Intercambiar colores . . . 129 4.29. Clases de análisis involucradas en el caso de uso Modificar fondo . . . . . . 130 4.30. Diagrama de secuencia correspondiente al flujo normal . . . . . . . . . . . 133 4.31. Diagrama de secuencia correspondiente al flujo normal . . . . . . . . . . . 134 4.32. Diagrama de secuencia correspondiente al flujo normal . . . . . . . . . . . 135 4.33. Diálogo Reorganizar paleta . . . . . . . . . . . . . . . . . . . . . . . . . . 135 4.34. Diálogo de selección de color . . . . . . . . . . . . . . . . . . . . . . . . . 137 4.35. Diagrama de secuencia correspondiente al flujo normal . . . . . . . . . . . 140 LISTA DE FIGURAS 4.36. 4.37. 4.38. 4.39. 4.40. 4.41. 4.42. VII Diagrama de secuencia correspondiente al flujo alternativo . . . . . Diagrama de secuencia correspondiente al flujo normal . . . . . . . Diagrama de secuencia correspondiente al flujo normal . . . . . . . Clases de análisis involucradas en el caso de uso Generar datos NDS Diagrama de secuencia correspondiente al flujo normal . . . . . . . Diagrama de clases del editor de fondos . . . . . . . . . . . . . . . Diagrama de secuencia correspondiente al flujo normal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 141 142 148 149 150 151 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 162 165 166 167 168 169 169 170 171 172 173 B.1. Fondo framebuffer a partir de una imagen de 192×256 pixels . . . . . . B.2. Fondo de rotación extendida a partir de una imagen de 128×128 pixels . B.3. Fondo de rotación extendida a partir de una imagen de 256×256 pixels . B.4. Fondo de rotación extendida a partir de una imagen de 512×256 pixels . B.5. Fondo de rotación extendida a partir de una imagen de 512×512 pixels . B.6. Fondo de rotación extendida a partir de una imagen de 512×1024 pixels B.7. Fondo de rotación extendida a partir de una imagen de 1024×512 pixels B.8. Fondo teselado de 256 colores a partir de una imagen de 32×32 teselas B.9. Fondo teselado de 256 colores a partir de una imagen de 32×64 teselas B.10. Fondo teselado de 16 colores a partir de una imagen de 64×32 teselas . B.11. Fondo teselado de 16 colores a partir de una imagen de 64×64 teselas . . . . . . . . . . . . . . . . . . . . . . . 175 176 176 177 177 178 178 179 180 181 182 A.1. Diálogo de creación de un proyecto C . . . . . . . A.2. Propiedades del proyecto . . . . . . . . . . . . . . A.3. Crear la configuración de depuración . . . . . . . . A.4. Punto de ruptura en el programa . . . . . . . . . . A.5. Salida del programa . . . . . . . . . . . . . . . . . A.6. Interfaz del editor de fondos . . . . . . . . . . . . A.7. Diálogo Nuevo fondo . . . . . . . . . . . . . . . . A.8. Abrir una imagen en modo teselado de 256 colores A.9. Diálogo Reorganizar paleta . . . . . . . . . . . . . A.10.Diálogo Convertir fondo . . . . . . . . . . . . . . A.11.Sustitución de un conjunto de teselas del fondo . . A.12.Espejos disponibles para una tesela . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . LISTA DE FIGURAS VIII Lista de tablas 3.1. Bancos de memoria de video . . . . . . . . . . . . . . . . . . . . . . . . . 15 3.2. Tamaño de los tipos de datos básicos . . . . . . . . . . . . . . . . . . . . . 30 3.3. Registros de ARM y su uso en AAPCS . . . . . . . . . . . . . . . . . . . . 34 3.4. Modos gráficos para el núcleo principal y el secundario [21] . . . . . . . . 42 3.5. Registro de control de energı́a REG POWERCNT . . . . . . . . . . . . . . 44 3.6. Colores en formato RGB de 16 bits . . . . . . . . . . . . . . . . . . . . . . 45 3.7. Posibles configuraciones del modo framebuffer . . . . . . . . . . . . . . . 45 3.8. Registro de control de pantalla REG DISPCNT [15] . . . . . . . . . . . . 46 3.9. Entrada de un mapa teselado . . . . . . . . . . . . . . . . . . . . . . . . . 74 4.1. Descripción textual del caso de uso Depurar mediante emulador . . . . . . 91 4.2. Caso de prueba 1, “en positivo” . . . . . . . . . . . . . . . . . . . . . . . . 95 4.3. Plan de iteraciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 4.4. Descripción textual del caso de uso Abrir imagen . . . . . . . . . . . . . . 100 4.5. Descripción textual del caso de uso Crear fondo . . . . . . . . . . . . . . . 101 4.6. Descripción textual del caso de uso Crear framebuffer . . . . . . . . . . . . 102 4.7. Descripción textual del caso de uso Crear extended rotoscale . . . . . . . . 103 4.8. Caso de prueba 1, “en positivo” . . . . . . . . . . . . . . . . . . . . . . . . 117 4.9. Caso de prueba 2, “en negativo” . . . . . . . . . . . . . . . . . . . . . . . 117 4.10. Caso de prueba 3, “en positivo” . . . . . . . . . . . . . . . . . . . . . . . . 117 4.11. Caso de prueba 4, “en positivo” . . . . . . . . . . . . . . . . . . . . . . . . 118 4.12. Descripción textual del caso de uso Crear teselado . . . . . . . . . . . . . 119 4.13. Caso de prueba 5, “en positivo” . . . . . . . . . . . . . . . . . . . . . . . . 122 4.14. Caso de prueba 6, “en positivo” . . . . . . . . . . . . . . . . . . . . . . . . 125 4.15. Descripción textual del caso de uso Modificar paleta . . . . . . . . . . . . 126 4.16. Descripción textual del caso de uso Cambiar color . . . . . . . . . . . . . 127 4.17. Descripción textual del caso de uso Modificar tesela . . . . . . . . . . . . . 128 4.18. Descripción textual del caso de uso Agregar color nuevo . . . . . . . . . . 130 IX LISTA DE TABLAS 4.19. Descripción textual del caso de uso Intecambiar colores . 4.20. Descripción textual del caso de uso Modificar fondo . . . 4.21. Caso de prueba 7, “en positivo” . . . . . . . . . . . . . . 4.22. Caso de prueba 8, “en positivo” . . . . . . . . . . . . . . 4.23. Caso de prueba 9, “en positivo” . . . . . . . . . . . . . . 4.24. Caso de prueba 10, “en positivo” . . . . . . . . . . . . . 4.25. Caso de prueba 11, “en positivo” . . . . . . . . . . . . . 4.26. Caso de prueba 12, “en positivo” . . . . . . . . . . . . . 4.27. Caso de prueba 13, “en positivo” . . . . . . . . . . . . . 4.28. Caso de prueba 14, “en positivo” . . . . . . . . . . . . . 4.29. Caso de prueba 15, “en positivo” . . . . . . . . . . . . . 4.30. Descripción textual del caso de uso Generar datos NDS . 4.31. Caso de prueba 16, “en positivo” . . . . . . . . . . . . . X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 132 138 138 139 139 145 145 146 146 147 148 152 5.1. Resultados para cada tamaño aceptado por los tipos de fondo . . . . . . . . 153 Capı́tulo 1 INTRODUCCIÓN 1.1. Motivación 1.2. Justificación del trabajo 1.3. Estructura del documento 1.1. Motivación Durante el curso 2007-2008 y el curso actual, 2008-2009, se puso en práctica en la Escuela Superior de Informática de Ciudad Real una nueva experiencia docente en el Área de Arquitectura y Tecnologı́a de Computadores. Dicha experiencia consistı́a en la utilización de la consola de videojuegos Nintendo DS como plataforma para el aprendizaje de los conceptos básicos de arquitectura de computadores. En concreto, la Nintendo DS sustituyó al microcontrolador Microchip PIC16F84 en las prácticas de laboratorio de la asignatura Estructura de Computadores. En la industria de los videojuegos, las consolas portátiles están viviendo en las fechas actuales su máximo auge. Los principales culpables de este auge son tanto Nintendo, con la Game Boy Advance y con la Nintendo DS, como Sony, con la Sony PlayStation Portable. Dichas compañı́as han logrado que estas consolas hayan entrado en millones de hogares de todo el mundo. En concreto, según datos oficiales de Nintendo, el número total de unidades vendidas en todo el mundo de cualquiera de los tipos de Nintendo DS alcanzó la cifra de 101 millones en el pasado mes de junio1 . Debido a la amplia difusión de la videoconsola Nintendo DS, existe una amplia comunidad de usuarios que se dedican al desarrollo de software casero. La tendencia de 1 Fuente: http://www.nintendo.co.jp/ir/library/historical_data/pdf/consolidated_ sales_e0903.pdf 1 CAPÍTULO 1. INTRODUCCIÓN 2 desarrollar este tipo de software para una videoconsola, se hizo popular por primera vez con la Game Boy Advance. El desarrollo de software para una videoconsola requiere de un conocimiento exhaustivo tanto del hardware del dispositivo como de sus especificaciones técnicas. Gracias a la extensa comunidad de desarrollo casero existente en Internet, cualquier persona puede tener acceso a la amplia documentación disponible para construir aplicaciones para la Nintendo DS. Por tanto, a causa de la amplia difusión de la videoconsola Nintendo DS y de su comunidad de desarrollo existente en Internet, cualquier alumno tiene la posibilidad de estudiar y profundizar en los conceptos de la asignatura Estructura de Computadores en su propia casa. Una de las ventajas de emplear una videoconsola como la Nintendo DS reside en que el alumno utilizará un dispositivo real con el cual esté familiarizado para abordar los objetivos de la asignatura. 1.2. Justificación del trabajo Como se ha comentado, una videconsola tan extendida como la Nintendo DS ofrece al alumno la posibilidad de estudiar y profundizar en los conceptos de la asignatura Estructura de Computadores en su propia casa. En concreto, cualquier alumno podrá realizar o terminar de realizar cualquier práctica de dicha asignatura fuera de un laboratorio docente de la escuela. Para la realización de las diversas prácticas, el alumno debe emplear una serie de herramientas desconocidas para la gran mayorı́a. La instalación, configuración y familiarización con estas herramientas supone un gran esfuerzo en tiempo para los alumnos de primer curso. Esta situación provoca que gran parte del tiempo disponible para las sesiones de prácticas se dediquen a temas que no entran dentro del ámbito de la asignatura. Por tanto, las sesiones de exploración de la arquitectura de la DS se reducen de forma considerable por falta de tiempo. Este proyecto pretende desarrollar un entorno amigable de programación apto para la docencia de Estructura de Computadores, el cual permita destinar gran parte del esfuerzo de los alumnos a tareas relacionadas con la exploración de la arquitectura de la Nintendo DS. Con este proyecto se intentará disminuir, en la mayor medida de lo posible, algunas tareas que no entran dentro del ámbito de estudio de la asignatura y que conciernen a materias de cursos superiores. Y, por otro lado, se intentará que las posibles dificultades que aparezcan en los alumnos, estén relacionadas con la arquitectura de la videoconsola y no CAPÍTULO 1. INTRODUCCIÓN 3 sean consecuencia de un software demasiado complejo, el cual requiera una gran curva de aprendizaje para los estudiantes de primer curso. 1.3. Estructura del documento Este documento está estructurado en site capı́tulos y en 2 anexos. El capı́tulo actual, relativo a la introducción, pretende ser una presentación, comentando el marco bajo el que se elabora este proyecto. En el segundo capı́tulo se concretará y expondrá el problema a resolver describiendo el entorno de trabajo, la situación y, además, se detallará lo que se pretende conseguir. Asimismo, se señalarán las limitaciones y condicionantes a considerar para la resolución del problema. En el tercer capı́tulo se mostrarán los conocimientos obtenidos en la búsqueda bibliográfica. Se articulará este capı́tulo en diversas secciones que permitan la exposición estructurada y didáctica de las distintas tecnologı́as y herramientas que se han utilizado para realizar el presente proyecto. En el cuarto capı́tulo se explicará el procedimiento empleado para conseguir los objetivos propuestos. En el quinto capı́tulo se muestran los resultados obtenidos, que darán paso al capı́tulo de conclusiones y propuestas expuestas. En el último capı́tulo se incluye la bibliografı́a consultada para la realización del proyecto. En el primer anexo se ilustrará con un tutorial del entorno de programación construido. Por último, en el segundo anexo se presentarán las figuras relacionadas con el capı́tulo de resultados. CAPÍTULO 1. INTRODUCCIÓN 4 Capı́tulo 2 OBJETIVOS DEL PROYECTO 2.1. Objetivos generales 2.2. Objetivos especı́ficos 2.1. Objetivos generales El objetivo principal de este proyecto es el desarrollo de un entorno de programación apto para ser utilizado en la docencia de la asignatura Estructura de Computadores. El producto software final permitirá a los alumnos, en primer lugar, reducir el tiempo empleado en tareas de instalación, configuración y aprendizaje del software empleado en las prácticas de laboratorio. En segundo lugar, permitirá una mayor dedicación del esfuerzo a labores de exploración de la arquitectura de la Nintendo DS. Con este proyecto se intentará disminuir, en la mayor medida de lo posible, algunas tareas que no entran dentro del ámbito de estudio de la asignatura y que conciernen a materias de cursos superiores. Y, por otro lado, se intentará que las posibles dificultades que aparezcan en los alumnos, estén relacionadas con la arquitectura de la videoconsola y no sean consecuencia de un software demasiado complejo, el cual requiera una gran curva de aprendizaje para los estudiantes de primer curso. 2.2. Objetivos especı́ficos A continuación se ofrece en una serie de puntos especı́ficos una visión más clara de los objetivos que se abordarán en el proyecto: 5 CAPÍTULO 2. OBJETIVOS DEL PROYECTO 6 Desarrollo del sistema utilizando como base Eclipse: El entorno de programación se construirá utilizando como base Eclipse. De manera más precisa, se utilizarán los plugin de Eclipse siguientes: 1. NDS Managedbuilder 2. Zylin Embedded CDT Bajo acoplamiento y alta cohesión: Se prestará un énfasis especial en la facilidad de actualización del sistema y la minimización del acoplamiento entre componentes. Depuración utilizando un emulador: El sistema proporcionará al alumno la posibilidad de poder utilizar un simulador de Nintendo DS durante el proceso de depuración de aplicaciones. En concreto, se utilizará el simulador libre de Nintendo DS DeSmuME. Múltiples vistas: El sistema debe tener la capacidad de ofrecer tres vistas diferentes de cada tesela de un fondo teselado, ya sea de 16 o 256 colores. Estas tres vistas serán las siguintes: 1. Vista del fondo 2. Vista del conjunto de teselas del fondo en edición 3. Vista individual de cada tesela para su edición Modos gráficos 2D: El sistema debe soportar el trabajo con los tres tipos diferentes de fondos 2D de la Nintendo DS. Estos son los siguientes: 1. Modo Framebuffer 2. Modo Extended Rotoscale 3. Modo Gráficos Teselados Fondos teselados de 16 y 256 colores: Se debe poder trabajar tanto con teselas de 256 colores como con teselas de 16 colores. Además, el sistema debe ofrecer la posibilidad de crear mapas teselados para la Nintendo DS partiendo desde cero, ası́ como trabajar con un mapa teselado existente. Edición de la paletas de colores: Tanto en el modo extended rotoscale como en el modo teselado de 256 colores y 16 colores, se debe permitir la edición de la paleta de colores asociada al fondo con el que se esté trabajando en ese momento. CAPÍTULO 2. OBJETIVOS DEL PROYECTO 7 El sistema generará datos entendibles por la videoconsola: Las entradas del sistema serán imágenes, a partir de los cuales se generarán los datos con los que la Nintendo DS es capaz de trabajar. Las imágenes de entrada se ajustarán al formato gráfico PNG (Portable Network Graphics). Desarrollo de un sistema multiplataforma: Se buscará el desarrollo de un sistema software que pueda funcionar en diversas plataformas. En concreto, se pretende que el sistema final pueda funcionar tanto en plataformas Microsoft Windows como en plataformas GNU/Linux. Desarrollo de un sistema viable desde el punto de vista docente: Se demostrará la viabilidad docente del sistema, mediante la preparación de un tutorial adaptado a asignaturas donde este tipo de plataforma tenga sentido, como por ejemplo, Estructura de Computadores, Interfaces y Periféricos, Sistemas Empotrados o Diseño de Sistemas Crı́ticos. Sencilla instalación y configuración del software: Es de gran importancia desvincular al usuario de las dependencias del sistema, ası́ como minimizar en la mayor medida de lo posible las configuraciones que el alumno tenga que realizar. Desarrollo del sistema utilizando software libre: Todo el desarrollo del proyecto se llevará a cabo mediante el empleo de tecnologı́as libres. CAPÍTULO 2. OBJETIVOS DEL PROYECTO 8 Capı́tulo 3 ANTECEDENTES, ESTADO DE LA CUESTIÓN 3.1. La videoconsola Nintendo DS 3.1.1. Introducción 3.1.2. Familia de la Nintendo DS 3.1.3. El hardware de la Nintendo DS 3.2. Desarrollo de software casero para Nintendo DS 3.2.1. Introducción 3.2.2. DevkitPro 3.2.3. DevkitARM 3.2.4. GNU Debugger 3.2.5. Entornos cruzados de desarrollo 3.2.6. Emuladores 3.3. Compilando para Nintendo DS 3.3.1. Introducción 3.3.2. ABI 3.3.3. EABI 3.3.4. AAPCS 3.3.5. Explorando el ABI con GDB y DeSmuME 3.4. Gráficos con Nintendo DS 3.4.1. Introducción 9 CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 10 3.4.2. Interacción con los periféricos 3.4.3. Modo framebuffer 3.4.4. Modo extended rotoscale 3.4.5. Modo teselado 3.1. La videoconsola Nintendo DS 3.1.1. Introducción Desarrollar para Nintendo DS supone desarrollar para un microprocesador ARM, más concretamente, para microprocesadores ARM9 y ARM7, a 67 y 33 MHz respectivamente. El procesador ARM9 se encarga de la lógica principal del programa, mientras que el ARM7, como procesador secundario, se encargará básicamente de gestionar el audio y la Wi-Fi. El hecho de que la consola NDS cuente con dos procesadores implica la generación de dos ejecutables distintos, uno para cada procesador. El ejecutable del ARM7 actuará como esclavo del ARM9, atendiendo peticiones de reproducción de sonido o comunicaciones vı́a Wi-Fi [27]. Los microprocesadores ARM1 tienen una gran importancia en el campo de los dispositivos portátiles, como teléfonos móviles o reproductores de mp3, gracias a su bajo consumo energético, entre otras razones. Cerca del 70 % de los procesadores de 32 bits del mercado poseen este chip en su núcleo [3]. 3.1.2. Familia de la Nintendo DS La familia de la Nintendo DS consta de tres miembros hasta la fecha actual. Por orden de lanzamiento en el mercado, estas tres videoconsolas son las siguientes: Nintendo DS (NDS): Sustituyó el procesador Z80 de la GameBoy Advance (GBA) por un ARM9 a 33Mhz, y mejoró el conjunto de aceleradores para gráficos de la GBA (véase la figura 3.1). Nintendo DS Lite: Sólo se diferencia de la NDS normal, aparte de por su aspecto más estilizado, en mejoras de consumo y diferentes niveles de control de brillo de la pantalla (véase la figura 3.2). 1 Sitio web oficial de ARM: http://www.arm.com CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 11 Nintendo DSi: Incorpora dos cámaras de baja resolución, las dos pantallas son algo mayores, un mejor sonido, más memoria (16 MiB RAM y 256 MiB Flash) e incorpora una nueva ranura para tarjetas SD. Además, la frecuencia del procesador ARM9 aumenta hasta 133 MHz. En cuanto a aspectos negativos, se debe señalar que pierde la ranura Slot2 de compatibilidad con los cartuchos de Game Boy Advance (véase la figura 3.3). Figura 3.1: Nintendo DS 3.1.3. El hardware de la Nintendo DS 3.1.3.1. Microprocesadores Como se ha comentado anteriormente, la consola cuenta con dos procesadores, un ARM9 y un ARM7, a 67 y 33 MHz respectivamente, ambos integrados en un único procesador especı́ficamente diseñado para Nintendo [7]. Esto supone una mejora significativa del hardware en comparación con su predecesora, la Game Boy Advance. Dicha videoconsola tenı́a un único procesador, el ARM7TDI a 16.8 MHz. Obviamente, se debe subrayar que el procesador ARM7 de la Nintendo DS trabaja a 16 MHz cuando funciona en modo Game Boy Advance. El nombre completo del procesador ARM7 es ARM7TDMI [4]. No tiene ni caché de instrucciones ni caché de datos, pero esto se ve compensando ligeramente por la rápida memoria que posee. El ARM7 es la única CPU que puede utilizarse para controlar la pantalla CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN Figura 3.2: Nintendo DS Lite Figura 3.3: Nintendo DSi 12 CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 13 táctil. Además, también es el encargado de controlar el micrófono, el sonido, las comunicaciones inalámbricas y el reloj de tiempo real. Debido a su mayor potencia en comparación con el ARM7, el ARM9 es el principal procesador y, como tal, se encargará de la mayorı́a del trabajo. La mayorı́a de las aplicaciones son ejecutadas en este proceador. Su nombre completo es ARM946E-S[6]. 3.1.3.2. Memoria Principal La memoria RAM principal de la Nintendo DS tiene un tamaño de 4 MiB. Dicha memoria almacena el ejecutable para el ARM9, ası́ como la gran mayorı́a de datos del ejecutable. Ambos procesadores pueden acceder a esta memoria en cualquier momento. Si ambos intentaran acceder a la vez, serı́a el que tenga mayor prioridad el que accederı́a, quedando el otro a la espera. En adición a estos 4 MiB de RAM, existen 656 KiB para memoria de video (VRAM), las pseudo-cachés WRAM e IWRAM de 96Kb, una memoria RAM adicional para la BIOS y una memoria virtual para video (Virtual Video RAM). En la figura 3.4 se puede observar el esquema del mapa de memoria de la consola. 3.1.3.3. Gráficos El hardware de video de la Nintendo DS se compone de dos núcleos gráficos 2D, uno principal (main) y otro secundario (sub). Dichos núcleos se diferencian únicamente en que el motor principal puede renderizar tanto la memoria de video virtual sin utilizar el motor 2D, como mapas de bits de 256 colores, ası́ como utilizar el motor 3D para el renderizado de alguno de sus fondos[26]. Además, la videoconsola cuenta con un motor gráfico 3D, el cual puede representar gráficos superiores a los de Nintendo 64. 3.1.3.4. Memoria de video VRAM La Nintendo DS tiene 9 bancos de memoria de video, los cuales se pueden usar con diferentes propósitos (véase la figura [21] 3.5). Cada de uno de estos bancos de memoria están etiquetado desde VRAM A hasta VRAM I y se pueden utilizar para almacenar los datos de sprites, de teselas, de texturas o de un mapa de pı́xeles. Asimismo, cada banco de memoria tiene un registro asociado, el cual se utiliza para activar su banco de memoria correspondiente. En la tabla 3.1 se muestra cada uno de los bancos de memoria de video existentes junto con su tamaño. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN Figura 3.4: Esquema del mapa de memoria de la NDS [21] 14 CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 15 Figura 3.5: Asignación de bancos de VRAM [18] La cantidad total de memoria de video es de 656 KiB. Se debe prestar un esfuerzo especial en cómo utilizar de manera eficiente esta cantidad de memoria flexible, pero muy limitada. Banco de memoria VRAM A VRAM B VRAM C VRAM D VRAM E VRAM F VRAM G VRAM H VRAM I Tamaño del banco (KiB) 128 128 128 128 64 16 16 32 16 Tabla 3.1: Bancos de memoria de video 3.1.3.5. Sonido Nintendo DS dispone de altavoces estéreo y cuenta con 16 canales de audio independientes [7]. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 3.1.3.6. 16 Comunicación inalámbrica La Nintendo DS soporta el estándar de protocolo de comunicaciones IEEE 802.11. El rango de comunicación inalámbrica varı́a de 10 a 30 metros, dependiendo de las circunstancias. En muchos casos, varios jugadores pueden participar en la misma partida multijugador utilizando una única tarjeta de juego [7]. 3.1.3.7. Entrada/Salida La videoconsola tiene un puerto para cartuchos de juegos de Nintendo DS y otro para juegos de Game Boy Advance2 . La NDS cuenta con una entrada para auriculares estéreo y otra entrada para micrófono [7]. 3.1.3.8. Doble pantalla Las dos pantallas LCD de Nintendo DS ofrecen un enfoque absolutamente innovador en lo que a videojuegos portátiles se refiere. Las dos pantallas, ambas de 3 pulgadas, son capaces de reproducir auténticos gráficos en tres dimensiones, incluso más avanzados que los de la Nintendo 64. La pantalla inferior de la DS emplea tecnologı́a táctil, siendo ésta la primera vez en la historia que una consola portátil utiliza este tipo de caracterı́sticas [7]. 3.1.3.9. Temporizador La Nintendo DS cuenta con un reloj de tiempo de real, el cual puede ser utilizado por una aplicación o juego para definir diferentes respuestas dependiendo de la hora del dı́a [7]. 3.2. Desarrollo de software casero para Nintendo DS 3.2.1. Introducción Según [9], se denomina homebrew al software casero no oficial realizado por programadores, ya sean aficionados o expertos, para cualquier plataforma. Generalmente, esta plataforma suele ser una videoconsola propietaria. No obstante, en la última década se han desarrollado algunas consolas diseñadas especı́ficamente para la ejecución de software casero como, por ejemplo, GP32 de GamePark o GP2X de GamePark Holdings. 2 La Nintendo DSi no cuenta con puerto para cartuchos de Game Boy Advance CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 17 El desarrollo de software casero está permitido en cualquiera de las consolas de Nintendo, siempre y cuando sea sin ánimo de lucro. En cualquier caso, se debe señalar que no todas las plataformas permiten el homebrew. El desarrollo de software para la Nintendo DS se puede realizar de dos maneras diferentes: 1. Utilizando el kit comercial de desarrollo de software (SDK) de Nintendo. 2. Utilizando DevkitPro, que es un conjunto de librerı́as, compiladores y utilidades para desarrollar software para varias plataformas. Además, es libre y de descarga gratuita. En los apartados siguientes de este capı́tulo se analizarán las principales herramientas existentes que ayudan al desarrollo de aplicaciones para Nintendo DS. 3.2.2. DevkitPro DevkitPro3 es un conjunto de librerı́as, compiladores y utilidades que permiten el desarrollo de aplicaciones para las consolas Game Boy Advance (GBA), GP32, GP2X, Playstation Portable (PSP), Nintendo DS y GameCube. Actualmente, DekvitPro cuenta con cuatro toolchains4 que permiten escribir aplicaciones y juegos para las consolas citadas: DevkitARM: es la toolchain utilizada para el desarrollo de aplicaciones para GBA, GP32 y Nintendo DS. DevkitGP2X: es la toolchain utilizada para el desarrollo de aplicaciones para la GamePark GP2X. DevkitPPC: es la toolchain utilizada para el desarrollo de aplicaciones para la Nintendo GameCube. DevkitPSP: es la toolchain utilizada para el desarrollo de aplicaciones para la Sony PSP. 3.2.3. DevkitARM DevkitARM es una toolchain de C y C++, basada en la colección de compiladores GNU (GCC), que permite crear binarios para la arquitectura ARM. Incluye todo lo necesario para crear software para la Nintendo DS, GBA y GP32. Las librerı́as que incluye DevkitARM son las siguientes: 3 Sitio web de devkitPro: http://www.devkitpro.org término toolchain se refiere a un conjunto de programas informáticos (tools) que se usan para crear un determinado producto. 4 El CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 18 LibNDS [22]: anteriormente conocida como NDSLIB, es una librerı́a que fue creada por Michael Noland y Jason Rogers. Esta librerı́a sirve como base para el desarrollo de programas para la Nintendo DS. Supone una alternativa libre frente al kit comercial de desarrollo de software (SDK) de Nintendo. LibNDS soporta casi todas las caracterı́sticas de la DS, incluyendo la pantalla táctil, el micrófono, el hardware 2D, el hardware 3D y las comunicaciones inalámbricas. LibFAT: esta librerı́a contiene una serie de rutinas para leer y escribir en sistemas de ficheros FAT como los de las tarjetas Secure Digital (SD), MultimediaCard (MMC) o CompactFlash (CF). DSWifi: esta librerı́a permite a los desarrolladores usar la Wi-Fi de la DS de una manera similar a cómo los ordenadores usan la tarjeta de red inalámbrica. LibGBA: esta librerı́a contiene las funciones necesarias para controlar el hardware de la Game Boy Advance. En Informática, es una tradición que el primer ejercicio a la hora de aprender o enseñar un nuevo lenguaje sea imprimir en pantalla “Hello World”. En este caso, para mostrar el funcionamiento de LibNDS se realizará un pequeño programa de ejemplo que imprima en la pantalla de arriba de la DS “Hello Mario”. 1 # include < nds .h > 2 # include < stdio .h > 3 int main ( int argc , char * argv []) 4 { 5 consoleDemoInit () ; 6 printf ( ‘ ‘ Hello Mario \ n ’ ’) ; 7 while (1) {}; 8 return 0; 9 } Algunas de las herramientas más destacadas de devkitArm son las siguientes [29]: Grit (GBA Image Transmogrifier) [34]: es un conversor de imágenes para la Game Boy Advance y la Nintendo DS. Grit acepta multitud de formatos de archivos (bmp, pcx, png, gif, jpeg, etc. . . ) con cualquier profundidad de bits y obtiene los datos que pueden ser usados directamente en el código de un programa para GBA o NDS. Los datos que genera Grit pueden ser datos de una paleta, datos de teselas, datos de un mapa o datos de un gráfico. Los formatos de salida disponibles son, entre otros, archivo C, archivo binario, archivo GNU Assembly o archivo RIFF. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 19 arm-eabi-gcc: es un compilador cruzado que genera código objeto para el ARM7 y el ARM9 a partir de código escrito en C o C++. arm-eabi-ld: es un enlazador que genera un archivo ejecutable en el formato estándar ELF para el entorno de ejecución ARM7 y ARM9 a partir del código objeto generado por el arm-eabi-gcc. arm-eabi-objcopy: es una herramienta que genera los archivos ejecutables reducidos .arm7 y .arm9 a partir del archivo ejecutable con formato ELF. Esta herramienta reduce al mı́nimo las necesidades de memoria de la videoconsola. Para ello, extra exclusivamente lo necesario para poder ejecutar el programa (instrucciones y datos). ndstool: la herramienta ndstool combina los archivos ejectuables .arm7 y .arm9 en un único archivo con extensión .nds añadiendo una cabecera descriptiva al comienzo. Opcionalmente, puede combinar junto con los archivos ejecutables otros datos como, por ejemplo, datos de gráficos. dsbuild: la herramienta dsbuild genera un archivo con extensión .ds.gba, el cual permite arrancar el programa desde el Slot2 (compatible con Game Boy Advance). 3.2.4. GNU Debugger El depurador de GNU, abreviado GDB [31], es un depurador simbólico avanzado con soporte de múltiples de arquitecturas, formatos de ejecutable y lenguajes de programación. Es utilizado en una amplı́sima variedad de productos comerciales en todo el mundo. GDB es un poderoso depurador que permite “ver” que está sucediendo dentro de otro programa mientras se ejecuta o lo que otro programa estaba haciendo en el momento que terminó inesperadamente su ejecución. Entre las capacidades más notorias que este depurador posee están: Depurar programas complejos con múltiples archivos. Detener el programa en una posición de la memoria del programa (breakpoints), detener el programa según una condición (watchpoints) o al emitirse una señal (catchpoints). Examinar las variables locales de la función que se está ejecutando en un momento determinado. Mostrar valores de expresiones arbitrariamente complejas cuando el programa se detiene. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 20 Examinar los registros del procesador. Modificar cualquier valor de registros o posición de memoria para estudiar el comportamiento del programa sin necesidad de recompilar. Mostrar la traza de llamadas del programa, es decir, las funciones que han sido invocadas y desde dónde. Ir avanzando la ejecución paso a paso hasta encontrar el punto exacto en el que ocurre el fallo inespiradamente del programa. El uso de los depuradores no se limita únicamente al campo de la localización y resolución de errores, sino que también se emplean para explorar la arquitectura sobre la que está ejecutando un programa. Para que GDB pueda ser aprovechado al máximo en la depuración de un programa, será necesario que éste haya sido compilado con soporte para depuración, lo cual se especifica al compilador GCC mediante la opción -g. 3.2.4.1. Front-ends para GDB Un front-end [23] no es más que una interfaz gráfica entre la aplicación y los usuarios. Por lo tanto, los front-ends para GDB realmente no son depuradores en sı́ mismos, sino que son sólo interfaces gráficas que redirigen las peticiones en forma de órdenes a GDB. Dentro del amplio abanico de front-ends que existen para GDB, se presentarán las propuestas más relevantes de entre aquellos que dan soporte a la depuración remota, ya que es esencial cuando se desarrolla para plataformas mediante compilación cruzada, como es el caso de la compilación y depuración de código para la consola Nintendo DS. Las principales propuestas son las siguientes: KDbg [30]: es un front-end desarrollado utilizando la arquitectura de componentes KDE. DDD [16]: son las siglas de Data Display Debugger. Lo más caracterı́stico de este front-end es que permite explorar los datos mediante interacción con la representación gráfica de los mismos, pues las estructuras de datos se representan como grafos. 3.2.5. Entornos cruzados de desarrollo Según [11], se puede definir un IDE (Integrated Development Environment) como un programa compuesto por un conjunto de herramientas útiles para un desarrollador de CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 21 software. Como elementos básicos, un IDE cuenta con un editor de código, un compilador/intérprete y un depurador. También puede dar soporte a más de un lenguaje de programación. Para desarrollar aplicaciones para Nintendo DS no es suficiente con utilizar un entorno de desarrollo integrado. Se necesita un entorno cruzado de desarrollo que soporte los lenguajes de programación C y C++. En un entorno cruzado de desarrollo se ven involucrados dos sistemas diferentes: Sistema Host: las actividades de desarrollo software tipı́camente se realizan en este sistema, el cual aporta facilidades para el desarrollo (editores, compiladores, montadores, etc. . . ). Sistema Target: el resultado de las actividades de desarrollo software se ejecutan en este sistema, distinto al anterior. Gracias al uso de herramientas cruzadas en Sistemas Host, es posible desarrollar y depurar aplicaciones para Sistemas Target. Dado que los requerimientos del Target son incompatibles con los del Host (el hardware del Target suele diseñarse de modo especı́fico para un proyecto), las herramientas de desarrollo y depuración cruzada deben permitir al desarrollador ajustarse lo máximo posible a los requerimientos del Target. Dentro del desarrollo de sistemas, una de las labores más importantes es la depuración del sistema. Esta labor puede realizarse de dos modos distintos: Utilizando emuladores: de este modo la labor de depuración puede realizarse en el Sistema Host de modo independiente del Sistema Target. Sin embargo de este modo se tiene una visión reducida del sistema, ya que este no está en su entorno real. También posee la desventaja de que para cada sistema particular se necesita un emulador. Utilizando el propio Sistema Target: de esta manera se tiene una visión más real de la forma de operar del sistema. Este modo exige incluir dentro del código de la aplicación código para poder realizar la depuración. Además, no todo sistema puede depurarse de este modo, ya que exige la parada del mismo, lo cual no siempre puede ser conveniente. 3.2.5.1. Eclipse La plataforma Eclipse5 consiste en un entorno de desarrollo integrado abierto y extensible. Eclipse sirve como IDE de Java y dispone de numerosas herramientas de desarrollo 5 Sitio web oficial de Eclipse: http://www.eclipse.org CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 22 de software. También da soporte a otros lenguajes de programación, como C/C++, Cobol, Fortran, PHP o Python. A la plataforma base de Eclipse se le pueden añadir extensiones (plug-in) para extender la funcionalidad (véase la figura 3.6). Figura 3.6: Arquitectura de la plataforma Eclipse [14] Además, el término Eclipse identifica a la comunidad de usuarios que constantemente están ampliando esta plataforma. Eclipse se divide en proyectos que tienen el objetivo de proporcionar una plataforma robusta, escalable y de calidad para el desarrollo de software. Está coordinado por la Eclipse Foundation, que es una organización sin ánimo de lucro creada para la promoción y la evolución de la plataforma Eclipse, Asimismo, la mencionada fundación da soporte tanto a la comunidad como al ecosistema Eclipse. CDT Eclipse fue creado originalmente en Java y para Java. Sin embargo, La mayorı́a de proyectos de desarrollo cruzado de software están escritos en C, C++ o en lenguaje ensamblador. Para que Eclipse fuera compatible con C y C++, se creó un plug-in llamado CDT6 (C/C++ Development Tools). Con Eclipse y CDT trabajando conjuntamente (Eclipse CDT), el entorno es capaz de soportar el desarrollo de código en C y C++. Sin embargo, el entorno que forman sólo es útil para el desarrollo de aplicaciones nativas. 6 Sitio web del plug-in CDT: http://www.eclipse.org/cdt/ CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 23 NDS Managedbuilder NDS Managedbuilder7 es un plug-in para Eclipse CDT. Se utiliza exclusivamente para el desarrollo de aplicaciones para Nintendo DS. Las principales caracterı́sticas de este plug-in son: Permite el desarrollo cruzado de software basado en las herramientas de programación producidas por el proyecto GNU (GNU toolchain. Soporta el desarrollo de proyectos en C y C++. También permite la programación en lenguaje ensamblador (ASM). Entre las herramientas que incluye se encuentran ObjCopy y NDS-Tool. Integra el depurador GDB de ARM. Permite la creación de proyectos para el ARM7 y el ARM9 de NDS Permite la creación de ROMs para NDS. Permite la creación de liberı́as para el ARM7 y el ARM9 de NDS. Con Eclipse y NDS Managedbuilder trabajando conjuntamente, Eclipse adquiere la forma de entorno cruzado de desarrollo de software para Nintendo DS. Sin embargo, el entorno creado no es un buen entorno de depuración cruzado para NDS, ya que no permite ni depurar remotamente en la propia consola, ni depurar utilizando un emulador de DS en la máquina host. Zylin Embedded CDT Zylin Embedded CDT8 es un plug-in de código abierto creado por Zylin AS Consulting que permite al depurador de Eclipse conectarse a un dispositivo remoto vı́a JTAG, BDM, Ethernet o usando una conexión en serie (véase la figura 3.7). Este plugin no es especı́ficamente para ARM. Puede ser usado con cualquier microprocesador empotrado que GDB sea capaz de soportar como MIPS, PowerPC o XScale, entre otros. Para que el depurador sea capaz de controlar el programa que se está depurando es necesaria la colaboración de unas rutinas auxiliares, los cabos del depurador (gdb stubs). Normalmente, estos cabos se ejecutan en la consola, combinados con el propio programa en 7 Sitio 8 Sitio web de NDS Managedbuilder: http://snipah.com/ web de Zylin AS Consulting: http://opensource.zylin.com/embeddedcdt.html CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 24 Figura 3.7: Entorno conectado a un dispositivo remoto vı́a JTAG/BDM [12] un único ejecutable. Sin embargo, esto suele imponer severas limitaciones a las capacidades de depuración. Ası́, por ejemplo, en la Nintendo DS no se tendrı́a la capacidad de parar el programa cuando se desee [27]. Para solucionar este problema de la depuración directa en la consola, se debe utilizar un emulador de Nintendo DS que incorpore directamente los cabos del depurador. En el entorno cruzado de desarrollo construido hasta este punto, no tiene integrado ningún emulador de Nintendo DS. Por tanto, cada vez que se pretenda depurar un programa utilizando un emulador, tal emulador tiene que ser arrancado y detenido manualmente. Es decir, el entorno y el emulador son totalmente independientes entre sı́. 3.2.5.2. VisualHAM VisualHAM9 es un entorno de desarrollo integrado creado para el SDK de Game Boy Advance llamado HAM. Esta disponible sólo para la plataforma Windows. En concreto, para Windows 2000 y para Windows XP. Alguna de las caracterı́sticas más destacadas de VisualHAM son: Sintaxis resaltada para código fuente escrito en C/C++, en lenguaje ensamblador o en GNU Makefiles. Función autocompletar en el código fuente. 9 Sitio web de VisualHAM: http://www.console-dev.de/projects/page_visualham/ CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 25 Salida de las herramientas redirigidas hacia la consola. Sugerencias durante la escritura de código fuente. 3.2.6. Emuladores El término emulación se refiere a la capacidad que tiene un hardware o software de reproducir el comportamiento de otro hardware o software diferente. Es posible utilizar hardware para emular hardware o usar software para emular software. En este documento se utilizará la palabra emulador para referirse a una aplicación software que emula a otro hardware. En [20] se define el término emulador como: “Un emulador es un programa que se ejecuta en un computador (sistema anfitrión del emulador) y, allı́, se encarga de recrear el comportamiento de un computador diferente (sistema objetivo del emulador)” La ventaja de utilizar un emulador de Nintendo DS es que no se necesita tener ni videoconsola ni cartuchos especiales. Sin embargo, las funcionalidades de la NDS que se soportarán dependerá del emulador utilizado. Existen multitud de emuladores heterogéneos de Nintendo DS. En las siguientes secciones se comentarán los más relevantes. 3.2.6.1. DeSmumE DeSmuME10 es uno de los pocos emuladores de Nintendo DS libres, con disponibilidad del código fuente. Aunque tiene sus limitaciones, implementa un respetable número de caracterı́sticas de la NDS, incluyendo el motor de gráficos 3D. Además, está disponible para un amplio rango de plataformas. Desde el punto de vista docente la caracterı́stica más sobresaliente de DeSmuME es que implementa la interfaz con el depurador GDB (cabos del depurador o gdb stubs) directamente en el propio emulador, sin necesidad de añadir ningún código adicional a los programas [27]. Además estos cabos pueden habilitarse de manera independiente en cada uno de los procesadores de la Nintendo DS. Para activar la interfaz con el depurador para el procesador ARM9 se empleará la opción –arm9gdb=puerto y para activar la interfaz con el depurador para el procesador ARM7 se utilizará la opción –arm7gdb=puerto. En ambos casos el puerto debe ser un número comprendido entre 1024 y 65535. Si se activan ambas opciones será necesario utilizar puertos diferentes para cada procesador. 10 Sitio web de DeSmuME: http://desmume.org/ CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 26 Para plataformas GNU/Linux, DeSmuME se puede ejecutar con tres interfaces diferentes (desmume, desmume-cli y desmume-glade), cada una de las cuales proporciona distintas funcionalidades. 3.2.6.2. No$GBA No$GBA (Nocash Game Boy Advance)11 es un emulador para GBA. También emula la NDS con unos resultados muy aceptables. Sin duda se trata del simulador más fiable de cuantos están disponibles libremente. Sin embargo, su principal carencia desde el punto de visa docente es que no tiene soporte integrado para el depurador GDB, por lo que no es útil para la depuración remota. 3.3. Compilando para Nintendo DS 3.3.1. Introducción El software de la consola está preparado para leer un cartucho de memoria en la memoria RAM principal y darle el control. Los cartuchos para ejecutar software casero (R4, M3, CycloDS, SuperCard, etc. . . ) tienen un programa interno (navegador o browser) que se comporta a su vez como cargador para otros programas. El proceso de carga se produce de forma muy similar a como lo hace el firmware, pero esta vez el programa se lee de una tarjeta de memoria Flash (SD, microSD, miniSD o CompactFlash) utilizando un protocolo propio de cada fabricante a través del mismo bus serie SPI por el que se leen los cartuchos comerciales. Es decir, a todos los efectos se comporta como si estuviera leyendo un cartucho comercial, pero esta vez de un simple archivo escrito en una tarjeta de memoria Flash. Por tanto, para poder programar la consola nuestro objetivo va a ser el de crear uno de esos archivos (de extensión .nds) que emulan el contenido de la memoria ROM de un cartucho comercial. Para generar el ejecutable para la NDS partiendo del código escrito en C, se comienza con la generación de código objeto para el ARM7 y ARM9, con la ayuda del compilador cruzado (arm-eabi-gcc). El código objeto generado tras la compilación será utilizado por el montador (arm-eabi-ld aunque frecuentemente se utiliza desde el propio arm-eabi-gcc) creando un archivo ejecutable para cada entorno de ejecución concreto, en este caso para el ARM7 y ARM9. Los archivos ejecutables responden a un formato estándar (denominado ELF) que facilita la interacción con un sistema operativo o con herramientas de depuración. Sin em11 Sitio web de No$GBA http://nocash.emubase.de/gba.htm CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 27 bargo, estos ejecutables contienen mucha más información aparte de las instrucciones y datos del programa. Para reducir al mı́nimo las necesidades de memoria de la consola, se extrae exclusivamente lo necesario para poder ejecutar el programa (instrucciones y datos) utilizando la herramienta arm-eabi-objcopy para generar los archivos ejecutables reducidos .arm7 y .arm9. La herramienta ndstool combinará estos dos archivos ejecutables, y opcionalmente otros datos (e.g. gráficos) en un único archivo con extensión .nds añadiendo una cabecera descriptiva al comienzo. La herramienta dsbuild generará un archivo similar con extensión .ds.gba que permite arrancar el programa desde el Slot2 (compatible con Game Boy Advance). Para ello añadirá una cabecerá distinta a la que se genera para DS, además de añadir un programa cargador al principio. En todo este proceso solo los archivos en lenguaje C (código fuente) podrı́an ser reutilizables para otra plataforma distinta. El código fuente es portable siempre que utilice un lenguaje y unas bibliotecas estándar. Sin embargo, no ocurre lo mismo con el código objeto, que sı́ será dependiente de la arquitectura y del compilador empleado. En general no es posible reutilizar código objeto ni siquiera entre dos compiladores distintos para la misma arquitectura. Esto es debido a que frecuentemente difieren en la forma en que se usan las caracterı́sticas del procesador para implementar las caracterı́sticas del lenguaje C. 3.3.2. ABI El ABI (Application Binary Interface) [5] para la arquitectura ARM es una colección de estándares, algunos generales y otros especı́ficos de la arquitectura ARM, que regulan la interacción entre archivos objeto o binarios, ası́ como con las herramientas de desarrollo para el espectro de entornos de ejecución basados en ARM. El concepto de ABI se refiere a: Las especificaciones que un ejecutable debe satisfacer para poder ser ejecutado en un entorno de ejecución especı́fico. Por ejemplo, el ABI de Linux para la Arquitectura ARM. Un aspecto particular de las especificaciones a las que se deben ajustar los archivos objeto reubicables, producidos independientemente para que éstos puedan ser enlazados y ejecutados estáticamente. Por ejemplo, el ABI de C++ para la Arquitectura ARM, el ABI de tiempo de ejecución para la Arquitectura ARM, el ABI de la Librerı́a C para la Arquitectura ARM. Esto permite que el código objeto generado tras la compilación sea portable a otros sistemas que implementen el mismo ABI. Por lo tanto, si los compiladores siguen unas determinadas reglas establecidas por el ABI se podrá garantizar la interoperabilidad. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 3.3.3. 28 EABI Todos los fabricantes de microprocesadores publican documentos que describen el convenio de paso de argumentos en las llamadas a procedimientos. ARM no es una excepción. En la figura 3.8 se muestra la estructura del ABI dominante en la arquitectura ARM, el EABI: Figura 3.8: ABI para la arquitectura ARM [5] Las siglas EABI provienen de Embedded ABI, el ABI especı́ficamente diseñado para sistemas empotrados. Se compone de un conjunto de documentos que especifica aspectos especı́ficos del EABI: El soporte básico de depuración. El ABI para las llamadas a procedimientos (AAPCS). El ABI para el manejo de excepciones. El ABI genérico de C++ (definido por Intel), y su especialización para la arquitectura ARM. El formato estándar de ejecutables ELF, y su especialización para la arquitectura ARM. El formato genérico de datos para la depuración DWARF, y su especialización para la arquitectura ARM. Formato de las secciones del ejecutable que proporcionan información al depurador. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 29 Formato y convenios utilizados en las bibliotecas estáticas y dinámicas, ası́ como el soporte de ejecución. Los convenios de la biblioteca estándar de C. Todos estos documentos están accesibles en [2]. En general, se trata de documentos cortos que definen de forma muy precisa los convenios del EABI. El siguiente apartado se centra en el estudio del mecanismo de paso de parámetros, especificado en el AAPCS. 3.3.4. AAPCS El convenio empleado para el paso de parámetros entre funciones conforme al EABI se recogen en [8]. Fundamentalmente contiene: Restricciones impuestas al programa que llama a una función para pueda comenzar la ejecución de dicha función. Restricciones impuestas al código de la función respecto al estado que debe preservar a lo largo de la ejecución (e.g. registros cuyo valor debe preservarse). Convenios utilizados para transmitir los argumentos y recoger el valor de retorno. Como todos los convenios de llamadas a procedimiento, el AAPCS tiene como principal criterio de diseño la eficiencia [28]. Además, el AAPCS está diseñado para soportar los dos repertorios de instrucciones de los microprocesadores ARM: el modo ARM y el modo Thumb. De hecho está especialmente diseñado para permitir la interoperabilidad de funciones en modo ARM y funciones en modo Thumb. 3.3.4.1. Alineamiento de datos El AAPCS determina un conjunto de tipos básicos que corresponden más o menos a los tipos básicos de C. En la tabla 3.2 se muestran los tamaños de cada tipo básico, ası́ como los requisitos de alineamiento en bytes: Que el alineamiento de un tipo de datos sea de N bytes quiere decir que las variables de ese tipo solo pueden almacenarse comenzando en direcciones de memoria múltiplo de N. Como se puede observar en la tabla 3.2, no es difı́cil recordar los alineamientos impuestos por el EABI porque coinciden con el tamaño del tipo básico. Un char puede almacenarse en cualquier posición de memoria, mientras que un short sólo puede almacenarse comenzando en direcciones pares, y un int debe comenzar en direcciones múltiplo de 4. Estos requisitos de alineamiento garantizan un buen compromiso entre velocidad y consumo de memoria, e impiden tener que leer o escribir más palabras de las necesarias CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN Categorı́a Enteros Coma flotante Punteros Tipo C unsigned char signed char unsigned short signed short unsigned int signed int unsigned long long signed long long float double a datos a código Tamaño 1 1 2 2 4 4 8 8 4 8 4 4 30 Alineamiento 1 1 2 2 4 4 8 8 4 8 4 4 Tabla 3.2: Tamaño de los tipos de datos básicos cuando se accede a una variable. Por ejemplo, si una variable de tipo int pudiera almacenarse en cualquier dirección par serı́a posible almacenar un entero repartido entre dos palabras de 4 bytes. Por tanto cualquier acceso a esa variable requerirı́a acceder a ambas palabras. El EABI permite que el orden de los bytes sea big-endian o little-endian. Esto se debe a que la mayorı́a de los procesadores ARM pueden configurarse en cualquiera de los dos modos. En el caso de la consola Nintendo DS, se limitará al estudio del orden littleendian, que es el seleccionado por defecto por DevkitPro, y el dominante en el mercado de microprocesadores actual. La figura 3.9 muestra la disposición de las palabras en memoria para cada una de estas ordenaciones: Figura 3.9: Orden de los bytes en big-endian y en little-endian [8] Se puede comprobar con un simple programa en C si un procesador está configurado en modo little-endian o en big-endian. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 31 # i n c l u d e < s t d i o . h> i n t main ( v o i d ) { int i = 1; char ∗p = ( char ∗ ) &i ; i f ( p [ 0 ] == 1 ) p r i n t f ( "Little Endian\n" ) ; else p r i n t f ( "Big Endian\n" ) ; return 0; } 3.3.4.2. Tipos compuestos El AAPCS también define las reglas de alineamiento para los tipos compuestos. En C hay tres tipos de tipos compuestos: Las estructuras struct o agregados inhomogéneos. Permite agrupar diferentes componentes de tipos básicos o compuestos en un mismo tipo. El alineamiento de las estructuras es el máximo de los alineamientos de sus componentes. El tamaño de las estructuras se redondea al múltiplo de su alineamiento más pequeño que pueda contener todos sus componentes. Los vectores o arrays, son una secuencia de objetos de un mismo tipo (agregación homogénea). El alineamiento de los vectores es el mismo que el de su tipo base. El tamaño es N veces el de su tipo base, siendo N el número de elementos del vector. Los tipos union, que tienen una sintaxis similar a las estructuras, pero en ellas todos los elementos se almacenan en la misma dirección de memoria. El alineamiento de las uniones es el máximo de los alineamientos de sus componentes. El tamaño de las uniones se redondea al múltiplo de su alineamiento más pequeño que pueda contener el mayor de sus componentes. La importancia de conocer estas reglas no debe subestimarse, pues influye significativamente en el consumo de memoria. Se pondrá como ejemplo la siguiente declaración simple en C: 1 struct MyData { 2 char c ; CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 3 double d ; 4 int s ; 5 32 }; 6 7 int main ( void ) { 8 char c ; 9 struct MyData data [5]; 10 ... Aplicando las reglas del EABI, el alineamiento del vector data es el de su tipo base (struct MyData). El alineamiento del tipo struct MyData es el máximo de los alineamientos de sus componentes. Esto es 8, el alineamiento correspondiente al elemento double d. Por otro lado, el tamaño de struct MyData tiene que ser un múltiplo de 8 que sea capaz de contener los tres elementos con sus respectivas condiciones de alineamiento. Ya sabemos que la estructura comienza en una posición de memoria múltiplo de 8. Por tanto, el caracter c se puede situar inmediatamente al comienzo. Sin embargo, el elemento d debe comenzar en una posición múltiplo de 8. Si c está ya en una posición múltiplo de 8 tendremos que introducir 7 bytes de relleno (padding) para poder situar el elemento d cumpliendo las reglas de alineamiento. El entero s puede situarse inmediatamente a continuación, puesto que d está alineado a 8 y ocupa 8 bytes. Eso indica que la siguiente posición disponible está alineada a 8 y, por tanto, también está alineada a 4. Finalmente, para que la estructura tenga un tamaño múltiplo de 8 se tiene que añadir otros 4 bytes de relleno (padding). El resultado se muestra en la figura 3.10: Figura 3.10: Alineamiento del tipo compuesto MyData [28] Conociendo las reglas de alineamiento del EABI, se pueden almacenar los mismos datos en una estructura equivalente que solo ocupe el 50 % de la memoria. En la figura 3.11 se muestra como quedarı́a la memoria: CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 33 Figura 3.11: Alineamiento de una estructura equivalente a MyData [28] Se puede compobar de una manera sencilla cómo el compilador de C fuerza el alineamiento impuesto por el EABI. Basta con crear un pequeño programa en C que defina variables consecutivas de distintos tipos, e imprima las direcciones y los tamaños de cada una. 3.3.4.3. Paso de argumentos Los procesadores ARM disponen de 16 registros (r0 a r15) que son visibles tanto en el modo ARM como en el modo Thumb. La mayorı́a de ellos son de propósito general, aunque los 2 superiores cumplen funciones especiales dentro de la arquitectura: el registro r15 es el contador de programa (PC), y el registro r14 se utiliza en la implementación de las llamadas a subrutinas para almacenar temporalmente la dirección de retorno (LR). Además, dispone de un registro de estado (CPSR). El resto de registros son de propósito general, pero el ABI restringe su uso de acuerdo con la tabla 3.3: 3.3.4.3.1. LLamadas a función y registro IP Los procesadores ARM disponen de una instrucción bl (branch and link, saltar y enlazar) para realizar los saltos a subrutinas. Esta instrucción salta a la dirección que se indica como único operando pero antes de alterar el contador de programa preserva su valor en el registro de enlace (lr, link register). El bit menos significativo del lr se utiliza para almacenar el repertorio de instrucciones en el que se encuentra el procesador en el momento del salto (1 si está en modo Thumb y 0 si está en modo ARM). La instrucción bl no tiene acceso a todo el rango de direcciones. Esto implica que en algunos casos el montador (linker) deberá introducir instrucciones de puente para permitir los saltos más allá del espacio que puede direccionar bl. El EABI da libertad al montador para introducir las instrucciones que sean necesarias, siempre que se se preserve el estado del procesador (incluyendo el CPSR) salvo el registro r12 (intra-procedure call register, CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN Registro r15 r14 r13 r12 r11 r10 Sinónimo r9 v6 r8 r7 r6 r5 r4 r3 r2 r1 r0 v5 v4 v3 v2 v1 a4 a3 a2 a1 Especial PC LR SP IP v8 v7 SB TR 34 Rol en AAPCS Contador de programa Registro de enlace Puntero de pila Registro temporal intra-llamada Registro de variable 8 Registro de variable 7 Platform register Registro de variable 5 Registro de variable 4 Registro de variable 3 Registro de variable 2 Registro de variable 1 Argumento/registro temporal 4 Argumento/registro temporal 3 Argumento/registro temporal 2 Argumento/registro temporal 1 Tabla 3.3: Registros de ARM y su uso en AAPCS IP). Dicho de otro modo, el compilador debe ser consciente de que el registro r12 puede ser modificado en cualquier instrucción de salto a funciones externas, o en los retornos de subrutinas. Con frecuencia en la práctica significa que este registro no está disponible para uso general salvo para cálculos temporales. No hay garantı́as de que se preserve su valor al realizar una llamada a función. El AAPCS reserva los primeros cuatro registros (r0 a r3) para el convenio de paso de argumentos, incluyendo el paso de los valores de retorno. El resto, entre r4 y r11, se pueden emplear para almacenar variables. Los registros reservados para el paso de argumentos pueden utilizarse para cualquier otro fin una vez han cumplido con su propósito. Es decir, no hay garantı́as de que se preserve su valor tras una llamada a función. En cambio, los registros r4 a r11 pueden contener variables del programa que llama, por lo que deben preservarse. Es importante señalar que esto no supone en modo alguno una limitación en el número de registros que pueden utilizarse en una función. Por ejemplo, si una función quisiera almacenar en el registro r4 una variable determinada12 lo único que exige el AAPCS es que primero debe preservar el contenido del registro en la pila y restaurarlo antes de volver de la función. 12 Las variables automáticas suelen asignarse a registros cuando se utilizan con mucha frecuencia para evitar excesivos accesos a la memoria. Por ejemplo, un contador de un bucle. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 3.3.4.3.2. 35 Restricciones de la pila El registro r13 se utiliza como puntero de pila (SP), es decir, contiene la dirección de la primera posición libre de la pila. Además, el EABI especifica que la pila crece hacia posiciones de memoria más bajas (full-descending). Asimismo, el compilador debe garantizar que: En todo momento el puntero de pila debe estar alineado al lı́mite de palabra de 4 bytes. En un interfaz público. Por ejemplo, cuando se llama a una función externa o se retorna de una función externa, el puntero de pila debe alinearse al lı́mite de doble palabra (8 bytes). El alineamiento extra en un interfaz público es conveniente, puesto que de esta forma el compilador puede asignar el marco de pila (stack frame) de la función sin preocuparse de ningún detalle de alineamiento previo. Es conveniente mencionar que 8 es el máximo requisito de alineamiento que puede tener cualquier tipo de datos en el EABI. 3.3.4.3.3. Valor de retorno Justo antes de realizar la llamada a función, el compilador debe generar las instrucciones necesarias para preparar el valor de retorno. En principio, como ocurre en muchas otras arquitecturas, el AAPCS intenta que el valor de retorno se devuelva en el registro r0, que una vez que ha cumplido su misión en el paso de argumentos puede reutilizarse para este fin. Sin embargo no siempre va a ser posible: Si el valor de retorno es de un tipo básico o compuesto de tamaño menor o igual a 4 bytes (char, short, int, long, float) entonces se retorna en r0. Si es necesario se expande a 4 bytes manteniendo el valor. Por ejemplo, un char con el valor −19 (0xED, en hexadecimal) pasará de ocupar un byte a ocupar los 4 bytes de r0 (0xFFFFFFED, en hexadecimal). Para ello, el compilador deberá utilizar la operación de extensión de signo. Los tipos compuestos pequeños se retornan en r0 sin extensión de ningún tipo. La disposición de los datos es la misma que tendrı́a en memoria. Si el valor de retorno es de un tipo básico de 8 bytes (double, long long) entonces se retorna en r0 y r1. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 36 Si el valor de retorno es de un tipo compuesto de más de 4 bytes o de tamaño indeterminado, entonces el resultado se devolverá en un área de memoria (pila) determinada por el que llama. La dirección de esta área de memoria se pasa a la función como un argumento extra. El compilador debe comprobar, antes de nada, el valor de retorno, porque es posible que encaje en el último caso contemplado. En ese caso, debe pasar un argumento extra a la función. 3.3.4.3.4. Paso de argumentos El AAPCS permite pasar argumentos a funciones en los registros r0 a r3 y en la pila. Si la función tiene pocos argumentos podrá utilizar solo los registros y no tendrá que realizar ningún acceso a la memoria, lo que incrementa notablemente el rendimiento. La asignación de parámetros a registros y pila se realiza aplicando un algoritmo en tres etapas. Se trata de un algoritmo que ejecuta el compilador en cada llamada a función. A pesar de su relativa complejidad, el resultado es un puñado de lı́neas de código muy eficientes. La etapa de inicialización se realiza una sola vez antes de procesar los argumentos. Se utilizan un par de contadores que llevan la cuenta de cuál es el primer registro libre para la asignación de un argumento (SiguienteRegistro, que se inicializa a r0) y cuál es la siguiente posición de la pila libre para asignar argumentos (SiguienteArgEnPila, que se inicializa a SP). Si la función devuelve un resultado en memoria, la dirección del resultado se escribe en r0 y SiguienteRegistro pasa a ser r1. La etapa de pre-padding y extensión de argumentos se ejecuta por cada argumento después de la inicialización. Básicamente, el objetivo de esta etapa es hacer que los argumentos ocupen un número entero de palabras. • Si se trata de un argumento compuesto y no se puede determinar el tamaño por la función, se copia en memoria y se reemplaza por un puntero a esta copia. • Si es de un tipo básico de menos de 4 bytes, se extiende a 4 bytes. • Si es de un tipo compuesto cuyo tamaño no es múltiplo de 4 bytes, se redondea a múltiplo de 4 bytes más cercano. Por último, la etapa de asignación de argumentos a registros y pila también se ejecuta iterativamente por cada argumento. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 37 • Si el argumento requiere 8 bytes asigna a SiguienteRegistro el siguiente registro par. Es decir, los argumentos de 8 bytes se pueden asignar a r0 y r1, o a r2 y r3, pero no a r1 y r2. • Si el tamaño del argumento no supera el tamaño de los registros disponibles para paso de argumentos, entonces se copia a registros empezando en SiguienteRegistro, y se incrementa SiguienteRegistro hasta el siguiente registro libre. • Si no cabe en los registros disponibles pero SiguienteArgEnPila contiene SP, es decir, todavı́a no se ha asignado ningún argumento a la pila, entonces se divide el argumento entre registros (primera parte) y pila e incrementa SiguienteArgEnPila a la siguiente posición libre. • En cualquier otro caso copia el argumento a SiguienteArgEnPila e incrementa SiguienteArgEnPila a la siguiente posición libre. 3.3.5. Explorando el ABI con GDB y DeSmuME El depurador GDB es una excelente herramienta para explorar el ABI y para analizar las instrucciones que genera el compilador para llamar a una función. De modo ilustrativo, en esta sección se mostrará el uso de la interfaz gráfica KDbg. Para evitar optimizaciones inesperadas que eliminen completamente las llamadas a función, es muy conveniente poner la definición de las funciones en unidades de compilación separadas. Se utilizará la plantilla de programa para el ARM9 incluida con DevkitPro: 1 2 3 $ cp -r / opt / devkitPro / examples / nds / templates / arm9 prueba $ cd prueba $ rm source /* Ahora se añaden los archivos fuente para este ejemplo en el directorio source. El programa principal simplemente llama a la función prueba. 1 // Archio source / main . c 2 void prueba () ; 3 int main () { 4 prueba () ; 5 return 0; 6 } La función prueba está definida en otro archivo independiente y simplemente realiza dos llamadas a función diferentes. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 1 // Archivo source / prueba . c 2 # include ‘‘ fn . h ’ ’ 38 3 4 void prueba () { 5 MyData ret = fnA (1.5 , ‘‘ Hola ’ ’) ; 6 fnB ( ret ) ; 7 } Las funciones están declaradas en un archivo de cabecera junto a los tipos que utilizan. 1 // Archivo source / fn . h 2 typedef struct Mydata_ { 3 double d ; 4 char s [10]; 5 } MyData ; 6 7 MyData fnA ( double d , char data []) ; 8 void fnB ( MyData md ) ; Por último la implementación de las funciones se realiza en un archivo C independiente para que el compilador no pueda optimizar las llamadas a función. 1 // Archivo source / fn . c 2 # include ‘‘ fn . h ’ ’ 3 # include < string .h > 4 5 MyData fnA ( double d , char data []) { 6 MyData md ; 7 md . d = d ; 8 strcpy ( md .s , data ) ; 9 return md ; 10 } 11 12 void fnB ( MyData md ) { 13 md . d = 1.0; 14 md . s [0] = ’ \0 ’; 15 } CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 39 En este ejemplo se utilizará el reperterio de instrucciones ARM, dejando a un lado el repertorio de instrucciones Thumb. Por este motivo se deberá comentar la siguiente lı́nea en el archivo Makefile: 1 $ ARCH := - mthumb - mthumb - interwork Para comentar la lı́nea basta con añadir el carácter al comienzo. Lo siguiente será crear las dos variables de entorno siguientes, sin las cuales la compilación no podrı́a realizarse. 1 2 $ export DEVKITPRO =/ opt / devkitPro $ export DEVKITARM =/ opt / devkitPro / devkitARM Una vez realizado estos cambios, ya se puede compilar. 1 $ make En otro terminal se ejecuta el depurador. En este caso se utilizará kdbg: 1 $ kdbg prueba . elf La primera vez que se ejecute KDbg, se debe configurar de tal modo que se cambie el GDB a la versión de DevkitPro. Para ello, en el menú Settings ⇒ Opciones globales se debe cambiar la opción Cómo iniciar GDB a arm-eabi-gdb -fullname -nx. Inmediatamente después, se abre el archivo main.c y se establece un punto de interrupción en la sentencia “prueba();”. A continuación, se cierrar KDbg y se vuelve a arrancar, pero esta vez conectando con el emulador DeSmuME: 1 2 $ desmume -- arm9gdb =7777 prueba . nds & $ kdbg -r :7777 prueba . elf A continuación, se pulsa el botón Ejecutar. En la figura 3.12 se muestra el resultado que se deberı́a obtener. Se puede observar que la ejecución ha alcanzado el punto de interrupción establecido anteriormente. Con el botón de ejecución paso a paso (step into by instruction) se puede entrar en cualquier función. En el menú View ⇒ Registros puede analizarse el valor de los registros CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 40 Figura 3.12: La ejecución alcanza el punto de interrupción establecido del procesador. En la ventana superior derecha están disponibles las variables del marco de pila actual. KDbg es especialmente aconsejable para aprender cómo funciona el ABI, ya que tiene la capacidad de ver el código ensamblador equivalente a cada sentencia C. Para ello, se debe pulsar simplemente sobre los signos + que preceden cada lı́nea. En la figura 3.13 se muestra el código ensamblador equivalente a la sentencia alcanzada durante el proceso de depuración. Con un conocimiento superficial de las instrucciones y viendo su efecto sobre los registros y sobre la pila, es posible entender perfectamente el EABI. 3.4. Gráficos con Nintendo DS 3.4.1. Introducción Experimentar con gráficos en la consola Nintendo DS proporciona la posibilidad de obtener una visión más concreta de cómo el procesador interactúa con dispositivos hardware como, por ejemplo, el hardware de video. La representación de gráficos plantea el reto de poner en práctica los conocimientos adquiridos sobre la organización de la memoria, para CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 41 Figura 3.13: Exploración del ABI con KDbg que se pueda ası́ hacer un uso eficiente de la misma en base a las reglas establecidas por el ABI [26]. El hardware de video de la Nintendo DS se compone de dos núcleos gráficos 2D, uno principal o main y otro secundario o sub, diferenciados en que el motor principal puede renderizar tanto la memoria de video virtual sin utilizar el motor 2D, como mapas de bits de 256 colores, ası́ como utilizar el motor 3D para el renderizado de alguno de sus fondos. Pero, ¿qué es un fondo? El concepto de fondo es básico para comprender cómo funcionan los modos de video de la Nintendo DS. Se puede entender como un concepto equivalente al de capa o layer, utilizado por algunas aplicaciones de diseño gráfico para facilitar la composición de una imagen a partir de la superposición del contenido de las capas. Los núcleos gráficos de la Nintendo DS disponen de cuatro fondos, etiquetados como BG0, BG1, BG2 y BG3, cuya configuración dependerá del tipo de gráfico que se represente. Un modo gráfico básicamente agrupa un conjunto de configuraciones para cada uno de los fondos. En la tabla 3.4 se resumen los modos disponibles para el núcleo principal y el secundario. Los seis primeros modos, Mode 0 a Mode 5, son comunes para los dos núcleos. Además, el núcleo principal cuenta con el Mode 6 y con el modo framebuffer. La tabla se compone básicamente de tres tipos diferentes de configuraciones para los fondos 2D, que son Text, Rotation y Extended Rotation y el modo framebuffer que pinta CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 42 Main 2D Engine Mode Mode 0 Mode 1 Mode 2 Mode 3 Mode 4 Mode 5 Mode 6 Framebuffer BG0 BG1 BG2 BG3 Text/3D Text Text Text Text/3D Text Text Rotation Text/3D Text Rotation Rotation Text/3D Text Text Extended Text/3D Text Rotation Extended Text/3D Text Extended Extended 3D Large Bitmap Direct VRAM display as a bitmap Sub 2D Engine Mode Mode 0 Mode 1 Mode 2 Mode 3 Mode 4 Mode 5 BG0 Text Text Text Text Text Text BG1 Text Text Text Text Text Text BG2 Text Text Rotation Text Rotation Extended BG3 Text Rotation Rotation Extended Extended Extended Tabla 3.4: Modos gráficos para el núcleo principal y el secundario [21] la imagen directamente sin utilizar fondos. A los modos Text también se les llama modos teselados, y a los modos Rotation también se les conoce como modos Rotoscale. 3.4.2. Interacción con los periféricos Como ya se sabe de apartados anteriores, la consola Nintendo DS dispone de una amplia variedad de periféricos. Estos periféricos se engloban dentro del sistema de entrada y salida, que constituye junto al procesador y la memoria uno de los elementos básicos de la estructura de un computador. La comunicación con el mundo exterior y la interacción computador-usuario es posible gracias al sistema de entrada-salida. Si se vuelve al programa “Hello Mario” que se realizó anteriormente, se puede ver que ese programa ya era capaz de interactuar con el usuario a través de la pantalla de la Nintendo DS, mostrando el mensaje “Hello Mario”. La pantalla es un claro ejemplo de periférico de salida. Mostrar un mensaje a través de ella es posible gracias al empleo de la función “printf()”, que pertenece a la biblioteca de entrada-salida estándar (stdio.h) del lenguaje C y que se incluı́a al comienzo del fichero fuente. Los periféricos de entrada-salida se comunican con el procesador y la memoria a través de los buses del sistema (direcciones, datos y control). Debido a la gran variedad de periféricos existente, estos suelen comunicarse con el procesador y la memoria a través de CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 43 controladores de entrada-salida. De esta forma, los controladores actúan de interfaz, y la comunicación y el control del periférico es posible gracias a una serie de registros accesibles para el procesador. Ası́, cada periférico se controla mediante el uso de un conjunto de registros de control, datos y estado13 . Tı́picamente, se debe configurar el periférico escribiendo en uno o varios registros de control, y se puede conocer su estado simplemente leyendo un registro de estado. En la figura 3.14 se puede ver gráficamente el funcionamiento de todo este sistema de entrada-salida. Figura 3.14: Conexión de los periféricos de entrada-salida con el computador [26] Los registros de datos permiten transferir datos a los periféricos, aunque, como se verá, es mucho más frecuente transmitir datos a través de una zona de memoria compartida entre el periférico y el procesador. Escribir y leer de estos registros en la Nintendo DS es muy sencillo. Esta consola utiliza la técnica de correspondencia en memoria para comunicación con los dispositivos (memory mapped devices). Esta técnica se basa en tratar los registros de los dispositivos como si se tratara de direcciones de memoria convencionales. Por ejemplo, la dirección 0x4000304 corresponde con el registro de control de alimentación de los periféricos (REG POWERCNT). Cada vez que el procesador escribe en esa dirección de memoria, está escribiendo en el registro de control, y cada vez que lee esa posición de memoria, está realmente leyendo el registro. Desde el punto de vista del procesador, no es posible distinguir si lee o escribe de memoria o de registros de dispositivos. En el caso de la Nintendo DS, para el procesador ARM9 los dispositivos de entrada y salida están mapeados en 13 No se deben confundir estos registros, que controlan los periféricos, con los registros de propósito general que contiene el procesador. Los registros del procesador actúan como almacenamiento ultra-rápido mientras que los registros de los periféricos simplemente actúan como interfaz con el procesador. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 44 memoria en la zona de direcciones comprendida entre la posición 0x04000000 y la posición 0x04FFFFFF. El registro de control de energı́a REG POWERCNT, mencionado anteriormente, es de 16 bits. Se puede observar en la tabla 3.5 para qué sirven y cómo se utilizan sus principales bits. Bit Sı́mbolo Descripción 0 1 2 3 9 15 POWER LCD POWER 2D A POWER MATRIX POWER 3D CORE POWER 2D B POWER SWAP LCDS Encender las pantallas LCD Activar procesador 2D principal Matriz de transformación 3D Activar procesador 3D Activar procesador 2D secundario Intercambia pantallas Tabla 3.5: Registro de control de energı́a REG POWERCNT De esta forma, si se quisiera encender las pantallas LCD y activar el procesador 2D principal de la consola, bastarı́a con utilizar la siguiente instrucción: 1 REG_POWERCNT = POWER_LCD | POWER_2D_A ; 3.4.3. Modo framebuffer La principal peculiaridad de este modo de representación es que la pantalla se mapea directamente a la memoria, sin utilizar el motor de renderizado. De forma que lo que se escribe en memoria es lo que se muestra. La pantalla se compone de 49152 pixels, organizados en 192 lı́neas de 256 pixels cada una, donde a su vez, el contenido de cada pixel representa un color en formato RGB de 16 bits. El formato RGB de 16 bits para especificar el color se representa como una mezcla de una cantidad determinada del color rojo (R), verde (G) y azul (B), dedicando 5 bits para determinar esa cantidad: 0 (ausencia de color) y 31 (máximo color). La librerı́a libnds proporciona la macro RBG15 para facilitar la definición de colores. Es suficiente con indicar la cantidad de cada uno de ellos, en un rango de 0 a 31, para obtener el color en formato RGB de 16 bits. En la tabla 3.6 se muestran algunos ejemplos de colores en este formato. El uso de esta macro simplifica la tarea de dibujado en la pantalla, ya que únicamente hay que asignar pixel a pixel el valor correspondiente al color que se desea pintar. Sin embargo, también puede ocurrir que lo que se desee pintar en pantalla sea una imagen existente, en cuyo caso la imagen debe venir descrita como un mapa de bits, con profundidad de CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN RGB15 RGB15(31,0,0) RGB15(0,31,0) RGB15(0,0,31) RGB15(0,0,0) RGB15(31,31,31) RGB15(31,31,0) RGB15(0,31,31) RGB15(31,0,31) 45 Color Rojo Verde Azul Negro Blanco Amarillo Cyan Magenta Tabla 3.6: Colores en formato RGB de 16 bits 16 bits, para adaptarse a la configuración del modo framebuffer. A continuación, se describen de manera más detallada estas dos formas de representación para el modo framebuffer, ası́ como otras cuestiones a tener cuenta a la hora de utilizar este modo. 3.4.3.1. Preparando el modo framebuffer El modo framebuffer admite cuatro tipos de configuración, que reciben el nombre de FB0, FB1, FB2 o FB2 y que básicamente establecen la zona de memoria cuyo contenido se va a volcar en la pantalla (véase tabla 3.7). Modo FB0 FB1 FB2 FB3 Memoria VRAM A VRAM B VRAM C VRAM D Tabla 3.7: Posibles configuraciones del modo framebuffer Antes de poder cargar los datos en memoria, se tiene que especificar el modo seleccionado, para lo cual se utiliza el registro REG DISPCNT. Este registro, de 31 bits, controla los modos y fondos activos en la pantalla de la consola. En la tabla 3.8 se explica cuál es la utilidad de los bits más importantes de este registro. Los primeros 15 bits actúan de la misma manera que lo hacı́an en la GBA. A continuación, se muestra un ejemplo de funcionamiento de este registro. 1 REG_DISPCNT = MODE_FB0 ; CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN Bit 0-2 Sı́mbolo MODE x 2D 3 ENABLE 3D 8 9 10 11 12 13 14 15 DISPLAY BG0 ACTIVE DISPLAY BG1 ACTIVE DISPLAY BG2 ACTIVE DISPLAY BG3 ACTIVE DISPLAY SPR ACTIVE DISPLAY WIN0 ON DISPLAY WIN1 ON DISPLAY SPR WIN ON 17 - 19 MODE FBx 46 Descripción Especifica el modo gráfico. 0 ≤ x ≥ 5 Especifica el modo 3D o 2D para BG0. (0 = 2D; 1 = 3D) Activa el renderizado de BG0 Activa el renderizado de BG1 Activa el renderizado de BG2 Activa el renderizado de BG3 Permite el uso de sprites Activa el uso de Window0 Activa el uso de Window1 Activa el uso de Sprite Window Activa el modo de video FB0, FB1, FB2 o FB3. 0 ≤ x ≥ 3 Tabla 3.8: Registro de control de pantalla REG DISPCNT [15] En este ejemplo, al especificar el modo de video, indirectamente se está especificando la zona de memoria donde se deberán escribir los datos que se van a mostrar en la pantalla. En base a la lı́nea anterior, que selecciona el modo FB0, el motor gráfico trabajará con los datos del banco de memoria VRAM A. Sin embargo, para poder utilizar los bancos de memoria es necesario que éstos estén habilitados y configurados según su finalidad. Como se muestra en la siguiente lı́nea de código, el banco A mapea directamente su contenido a la pantalla: 1 VRAM_A_CR = VRAM_A_ENABLE | VRAM_A_LCD ; Una vez se ha especificado el modo y se ha habilitado la zona de memoria donde se escribirán los datos, el siguiente paso consiste en escribir en la memoria el valor que se asignará a cada uno de los pixels de la pantalla, bien mediante una asignación manual, utilizando la macro RGB15, o escribiendo los datos correspondientes a una imagen especı́fica. 3.4.3.2. Mostrando imágenes de archivo Una vez que ya se ha configurado el modo de vı́deo, el siguiente paso consiste en transferir los datos que se van a mostrar. Dado que el modo framebuffer no utiliza fondos ni motor de renderizado, la tarea de representación quedará reducida a transferir al banco de memoria correspondiente los datos del gráfico a mostrar. Como se describirá a continuación, será necesario convertir esa imagen en un mapa de bits. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 47 La conversión de imágenes se realiza con la herramienta grit, que ya viene integrada en devkitPro. Por cada imagen a convertir existirá un archivo con extensión .grit, que contendrá las opciones especı́ficas para la generación de los datos convertidos. Para más información sobre las diferentes opciones disponibles se puede ejecutar en un terminal: 1 $ grit -- help Suponiendo que el nombre de la imagen a mostrar es wallpaper.png, se tendrá que editar un archivo de texto, con el mismo nombre que la imagen y con extensión .grit. Las opciones de conversión del modo framebuffer que se deben añadir son -gb y -gB16. Se debe escribir una opción por lı́nea. La primera opción indica que el formato de la conversión es un mapa de bits y la segunda opción, que tiene una profundidad de 16 bits por pixel. Una vez se ha ejecutado grit, se generan los datos de la conversión en un archivo que contiene el vector de datos correspondiente al mapa de bits de la imagen especificada. El formato del archivo de salida se puede ajustar a múltiples formatos y puede ser escogido por el usuario. La opción por defecto es generar los datos en un archivo en lenguaje ensamblador para ARM (archivo .s) y declarar estos datos en un archivo de cabecera (archivo .h) . Una vez que se dispone de la imagen, en el formato adecuado, el siguiente paso consiste en trasnferir los datos correspondientes a la memoria. Para ello, será necesario haber incluido el archivo de cabecera que contiene la declaración del vector de datos de la imagen. El código para cargar una imagen de archivo en la pantalla quedarı́a de la siguiente manera: 1 # include < nds .h > 2 # include " wallpaper . h " 3 4 int main ( void ) { 5 REG_DISPCNT = MODE_FB0 ; 6 VRAM_A_CR = VRAM_ENABLE | VRAM_A_LCD ; 7 dmaCopy ( wallpaperBitmap , VRAM_A , 256*192*2) ; 8 for (;;) swiWaitForVBlank () ; 9 return 0; 10 11 } Como se puede observar, mediante la función dmaCopy el vector de datos se copia al banco de memoria que está mapeado a la pantalla. En apartados posteriores de este documento se explica más detalladamente el proceso de transferencia de datos. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 3.4.3.3. 48 Dibujando pixels Otra forma alternativa de representar gráficos en modo framebuffer consiste en componer una imagen a partir de la asignación de colores a cada uno de los pixels que componen la pantalla. Al igual que en el caso anterior, el primer paso consistirá en especificar el modo de video utilizado, que para el caso del framebuffer, puede variar entre cualquiera de los cuatro disponibles, FB0, FB1, FB2 o FB3. A cada uno de los modos le corresponde un banco de memoria especı́fico. Para el ejemplo siguiente, se utilizará el modo FB0 al que le corresponde el banco VRAM A, cuyo contenido se pintará directamente en la pantalla LCD. 1 REG_DISPCNT = MODE_FB0 ; 2 VRAM_A_CR = VRAM_ENABLE | VRAM_A_LCD ; El siguiente paso difiere del apartado anterior, donde se hacı́a una transferencia directa de un vector de datos, correspondiente a la imagen a dibujar. En este caso, habrá que utilizar un bucle que recorra la región de memoria correspondiente, para escribir uno por uno el valor del color que se desee asignar a cada uno de los pixels que componen la pantalla. La macro RGB15 permite obtener ese valor, indicando la cantidad de color rojo, verde y azul de la que se compone el color que se pintará en cada pixel. Si se desea mostar un gradado vertical de color negro a azul, que ocupe toda la pantalla, se deberá ir incrementando la cantidad de azul a medida que se vaya descendiendo en la pantalla. El color negro se obtiene mediante la ausencia de color de las tres tonalidades. Por lo tanto, mediante RGB15(0,0,0) se obtiene el valor correspondiente. Dicho de otra forma, el nivel de color azul depende exclusivamente de la lı́nea que se esté pintando. Por tanto, basta con recorrer todos los puntos de la pantalla y pintarlos con el color correspondiente a la lı́nea. 1 # include < nds .h > 2 # include < stdio .h > 3 4 int main ( void ) 5 { 6 consoleDemoInit () ; 7 printf ( " Ejemplo de frame buffer " ) ; 8 CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 49 // Configuracion de pantalla principal 9 REG_DISPCNT = MODE_FB0 ; 10 // Framebuffer 11 12 // Configuracion de bloque A de la VRAM 13 VRAM_A_CR = VRAM_ENABLE | VRAM_A_LCD ; 14 15 int lin , col ; 16 unsigned short * fb = VRAM_A ; 17 for ( lin = 0; lin < 192; lin ++) 18 for ( col = 0; col < 256; col ++) 19 fb [ lin *256 + col ] = RGB15 (0 ,0 , lin 20 *32/192) ; 21 return 0; 22 23 } En la lı́nea 17, se define un puntero de tipo entero sin signo de 16 bits y que apunta a la dirección 0x6800000, que es el valor de la macro VRAM A, el comienzo de la memoria del framebuffer FB0. El hecho de definir el puntero a la memoria como unsigned short, es porque en este modo se necesita direccionar la memoria pixel a pixel de la pantalla. Cada pixel ocupa 16 bits (5 bits por componente y el bit más significativo sin usar). En la lı́nea 21, se utiliza el puntero fb para acceder al pixel correspondiente a la lı́nea lin y a la columna col. Dado que los pixels se almacenan por lı́neas y cada lı́nea tiene 256 puntos, se tendrá que multiplicar el número de lı́nea por 256 para ir al comienzo de la fila y sumar el número de columna para llegar al pixel correspondiente de la pantalla. La asignación del color a cada pixel consiste en obtener el valor del color en formato RGB15, y asignarlo a la posición de memoria del pixel correspondiente. 3.4.3.4. Limitaciones de la pila Una de las limitaciones más importantes de la consola Nintendo DS, en comparación con los ordenadores personales, es que la memoria se convierte en un recurso mucho más limitado. Se requiere un uso más restrictivo para que esta no se agote de manera prematura o inesperada. La pila, al ser una zona de la memoria, tampoco será un recurso ilimitado. No hacer un uso eficiente de la misma, puede llevar a situaciones de desbordamiento cuando se intenta mantener en la misma una cantidad de datos superior al espacio fı́sico disponible. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 50 Una vez más, conocer el ABI resulta fundamental para poder llevar a cabo una gestión eficiente de la pila. El ABI, en relación con la pila, establece que las variables automáticas se almacenan en la pila, por lo que habrá que hacer un uso razonable de las variables automáticas si no se desea desbordar la pila. El primer paso será determinar la cantidad de espacio reservado para la pila, con el objetivo de evitar que ese espacio se desborde. El linker o montador no sólo está encargado de combinar el código objeto generado tras la compilación de cada uno de los archivos de código fuente, sino que también se encarga de determinar la localización fı́sica en el hardware del código y datos de una aplicación. En cuanto a la localización fı́sica de la pila, la consola Nintendo DS la ubica en una región especial de memoria conocida como DTCM. La DTCM (Data Tightly Coupled Memory) es un tipo de memoria de datos muy rápida, de 16 KiB de tamaño, que se encuentra dentro del procesador ARM9. El tamaño de esta memoria se debe tener en cuenta, ya que este es el lı́mite fı́sico para la pila. Por lo tanto, se debe prestar especial atención cuando se trabaje con variables automáticas para que estas no superen los 16KiB disponibles, como es el caso del código que se lista a continuación: 1 # include < nds .h > 2 # include < stdio .h > 3 4 int main ( void ) 5 { 6 consoleDemoInit () ; 7 printf ( " Ejemplo de frame buffer " ) ; 8 9 // Configuracion de pantalla principal 10 REG_DISPCNT = MODE_FB0 ; // Framebuffer 11 // Configuracion de bloque A de la VRAM 12 VRAM_A_CR = VRAM_ENABLE | VRAM_A_LCD ; 13 14 int i =0 , j =0; 15 uint16 * color = VRAM_A ; 16 uint16 triangulo [192][256]; 17 18 19 for ( i =1; i <256; i ++) { if ( i ==127) triangulo [0][127] = 1; 20 21 22 else triangulo [0][ i ] = 0; CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 23 } 24 for ( i =1; i <192; i ++) 51 for ( j =0; j <256; j ++) { 25 triangulo [ i ][ j ] = triangulo [i -1][ j -1] + 26 triangulo [i -1][ j +1]; if ( triangulo [ i ][ j ] % 2) 27 color [ i *256+ j ] = RGB15 (31 ,0 ,0) ; 28 } 29 30 return 0; 31 32 } La compilación de este código no dará ningún error. Sin embargo, la ejecución del binario mostrará un fallo. La causa de este fallo radica en una gestión deficiente de la memoria de la pila. Se debe tener presente que el ABI establece la localización de las variables automáticas en la pila, como es el caso de la variable triangulo, definida como una matriz de 192x256 elementos. Como el vector es de tipo entero sin signo de 16 bits (2 bytes), ocupará un tamaño de 192x256x2 o lo que es lo mismo, 96KiB. Estos 96KiB superan por mucho los 16KiB disponibles en la memoria DTCM, donde se encuentra pila. La solución es declarar triangulo como una variable global (fuera de cualquier función) o indicar explı́citamente que se desea almacenar en la zona de variables estáticas: 1 static uint16 triangulo [192][256]; 3.4.3.5. Transferencia de datos La transferencia de datos juega un papel esencial en la representación gráfica, no sólo en el modo framebuffer. De ahı́ la necesidad de conocer las distintas formas existentes para cargar datos en memoria, más allá del direccionamiento básico mediante punteros. A lo largo de este apartado se describirán los fundamentos teóricos del acceso directo a memoria, ası́ como algunas de las funciones más comunes de la librerı́a libnds que le dan soporte y otros tipos de transferia de datos usando la CPU. 3.4.3.5.1. DMA (Direct Memory Access) La comunicación entre el procesador y un periférico de entrada y salida se realiza en dos pasos: un primer paso de sincronización entre ambos y un segundo paso de transferencia CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 52 de datos. Este proceso se puede realizar mediante el uso de las siguientes técnicas: sondeo o consulta (polling), interrupción, y acceso directo a memoria (DMA). En los dos primeros casos, la transferencia de datos se hace a través del procesador, mientras que mediante DMA la información pasa directamente entre los periféricos y la memoria. La técnica de sondeo resulta la más sencilla, ya que es el procesador el que se encarga de interrogar al periférico para iniciar la transferencia de datos. Esto hace que las entradas y salidas estén controladas por el programa, mientras el procesador está ocupado con esta tarea no pueda realizar otras. Por todo ello, se trata de una técnica poco eficiente para transferir grandes cantidades de información, especialmente si la transferencia es lenta. En la técnica basada en interrupciones, es el controlador del periférico el encargado de notificar al procesador cuándo se debe hacer una transferencia. Esto permite al procesador trabajar en paralelo, mientras no reciba interrupciones. Finalmente, la técnica basada en acceso directo a memoria deja en manos del controlador de DMA la operación de transferencia de datos entre periféricos y memoria. En este caso, la información ya no debe pasar por el procesador, permitiéndole poder realizar otras tareas simultáneamente. La única limitación que impone, es que el procesador y el controlador no pueden emplear los buses al mismo tiempo con el fin de evitar un conflicto. Esta técnica es, sin duda, la más eficiente para transferir grandes cantidades de datos entre periféricos y memoria de forma frecuente. Para las transferencias tipo DMA se emplea el controlador de DMA (CDMA) del sistema. Generalmente este controlador es más sofisticado que los controladores de entrada salida de los periféricos. La estructura interna del CDMA consta de los siguientes registros mapeados en memoria y accesibles para el procesador: Registros de dirección: contienen las direcciones de origen y destino de la transferencia a realizar. Se autoincrementan de forma automática. Contador de palabras: contiene el número de palabras a transferir; se decrementa después de transferir una palabra y cuando llega a 0 genera una señal para notificar la finalización de la transferencia. Registro de datos: Buffer intermedio contiene el dato a transferir. Lógica auxiliar de control: es la circuiterı́a encargada de notificar al procesador cuando se solicita el uso de los buses y cuando se ha terminado la transferencia. En el caso de la Nintendo DS, existen 4 canales DMA para cada procesador (ARM9 y ARM7) que permiten la transferencia de bloques de datos entre diferentes tipos de memoria y periféricos de entrada-salida. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 3.4.3.5.2. 53 Algunas funciones para transferencia de datos La librerı́a libnds proporciona una serie de funciones que soportan el proceso de transferencia de datos utilizando controladores de DMA. Anteriormente, se ha utilizado la función dmaCopy. El primer argumento de esta función se corresponde con la dirección de memoria donde se encuentran los datos a transferir, el segundo argumento indica la dirección de memoria donde se van a transferir y, finalmente, el tercer agumento indica el tamaño de esos datos en bytes. Es preferible utilizar funciones de transferencia sobre DMA, ya que al no intervenir la CPU, el proceso es más rápido y eficiente. Sin embargo, como la memoria DTCM no está conectada al bus accesible por el controlador de DMA, este no tiene acceso a la misma. Esto obliga a utilizar funciones de transferencia que utilicen la CPU como, por ejemplo, memcpy o swiCopy. La función dmaCopy tampoco permite la transferencia de un bloque de datos de tamaño superior a 128KiB. Por eso, si se quisiere cargar en memoria una imagen de 512x512 (256KiB, 512x512x2), también se tendrı́a que recurrir a una función de transferencia que utilizara la CPU, o hacer dos transferencia de 128KiB cada una, utilizando DMA. El código que se lista a continuación, utiliza el modo de video extended rotoscale, el cual se comentará más adelante. Para la transferencia de datos se utiliza la función memcpy, que como primer argumento recibe la dirección de memoria donde se transferirán los datos, como segundo argumento la dirección de memoria donde se encuentran los datos a transferir y, finalmente, el tamaño de los datos a transferir. 1 # include < nds .h > 2 # include < stdio .h > 3 # include " wallpaper . h " 4 5 int main ( void ) 6 { 7 consoleDemoInit () ; 8 // Configuracion de pantalla principal 9 REG_DISPCNT = MODE_5_2D | D IS PL AY _B G2_ AC TI VE ; 10 // Configuracion de bloque A de la VRAM 11 VRAM_A_CR = VRAM_ENABLE | VRAM_A_MAIN_BG ; 12 // Configuracion de bloque B de la VRAM 13 VRAM_B_CR = VRAM_ENABLE | VRAM_B_MAIN_BG ; 14 // Configuracion del fondo 2 15 BGCTRL [2] = BG_MAP_BASE (0) | Bg Size_B 8_512x 512 ; CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 54 16 17 // Copia del fondo 18 memcpy ( BG_GFX , wallpaperBitmap , 512*512) ; 19 } La otra opción para la transferencia de datos, es utilizar la función swiCopy. Esta función es una rutina de la BIOS que soporta la transferencia en bloques de 2 bytes. La definición de esta función establece, como primer argumento la dirección de memoria de la localización de los datos a transferir, como segundo argumento la dirección de memoria donde se transferirán los datos y, finalmente, el tamaño de los mismos, en cantidad de 2 bytes. Se puede utilizar el mismo código listado anteriormente, pero cambiando swiCopy por memcpy. Ambas funciones se diferencian en el orden de los argumentos de la función. 1 // Copia del fondo 2 // dmaCopy ( wallpaperBitmap , BG_GFX , 512*512) ; 3 swiCopy ( wallpaperBitmap , BG_GFX , 512*512) ; 3.4.4. Modo extended rotoscale 3.4.4.1. Introducción Los fondos extended rotoscale [24] se pueden componer a partir de un mapa de bits, como hace el modo framebuffer, o a partir de un mapa de teselas, concepto que se explicará cuando se describan los fondos teselados. Excepto el modo framebuffer, el resto de modos hacen uso de fondos (backgrounds). Existen cuatro fondos, BG0,BG1,BG2 y BG3, dispuestos de forma superpuesta. Normalmente, el BG0 es el más exterior y el BG3 el más interior, aunque se puede cambiar (prioridad de fondos). Cada uno de los fondos admite una configuración determinada, ası́ se hablará de fondos de rotación, de rotación extendida o teselados. Los modos etiquetados como Mode 3, Mode 4 y Mode 5 soportan una configuración en la que interviene al menos un fondo de tipo rotación extendida (extended rotoscale, ER. Sin embargo, es el Mode 5 el único que permite una configuración con dos fondos de este tipo, el fondo 2 o BG2 y el fondo 3 o BG3. Este es el modo que se utilizará en los apartados relativos al modo ER, tal y como se indica en la siguiente lı́nea de código: 1 REG_DISPCNT = MODE_5_2D | D IS PL AY _B G2_ AC TI VE ; CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 55 Como se puede observar, se especifica el modo 5, aunque únicamente se habilita el fondo 2, que es un fondo de tipo ER. Este tipo de fondo puede ser utilizado para representar mapas de bits, al igual que hacı́a el modo framebuffer, pero además con la funcionalidad añadida para rotar, escalar y desplazar el fondo a lo largo y ancho de la pantalla. 3.4.4.2. Mapeando bancos de memoria VRAM a los fondos extended rotoscale En lo referente a la gestión de memoria, la principal novedad que los fondos ER presentan con respecto al modo framebuffer, es que ahora el motor 2D espera encontrar los datos relativos a los gráficos en una zona de memoria especı́fica, referida como memoria de fondos (background memory). El motor 2D, por defecto, no tiene más memoria asignada que la de sprites y la de paletas. De ahı́ la necesidad de mapear bancos de la memoria de video VRAM a aquellas zonas donde el motor espera encontrar los datos a dibujar. La figura 3.15 describe el esquema de la memoria de fondos para el motor principal, organizada en bloques de 16KiB cada uno, etiquetados con nombres que van de BMP BASE 0 a BMP BASE 31. Esta región de memoria contiene el mapa de datos de la imagen que el motor 2D va a renderizar en la pantalla. Figura 3.15: Esquema de la memoria destinada a los fondos de mapas de bits [24] Un fondo ER se puede configurar de forma análoga al framebuffer, en la que cada CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 56 pixel se representa con el color de dicho pixel en formato RGB15 y ocupa 16 bits14 . Asimismo, se puede configurar en un modo especial de mapa de bits, en el que cada pixel se representa por un número de 8 bits que corresponde a su color dentro de la paleta de colores del sistema. Este es el modo que se empleará en los apartados dedicados al modo ER. En función del tamaño de los datos correspondientes a la imagen, se habilitarán los bancos de VRAM que se necesiten, teniendo en cuenta que el máximo se establece a 512KiB. Ası́, en el ejemplo que se muestra a continuación, el cual representa una imagen de 256 × 256, bastará con habilitar el banco A de la VRAM y comenzar la escritura de los datos del mapa a patir del BMP BASE 0, es decir, desde el principio de la memoria de video asignada. 1 VRAM_A_CR = VRAM_ENABLE | VRAM_A_MAIN_BG ; 2 BGCTRL [2] = BG_BMP_BASE (0) | Bg Size_B 8_256x 256 ; La primera lı́nea habilita el banco A de la VRAM y lo asigna a la memoria de fondos del motor principal, mapeando por lo tanto 256KiB, suficientes para contener los datos del mapa de una imagen de 256 × 25615 . La segunda lı́nea configura el fondo 2, indicando al motor 2D que los datos del mapa se encuentran desplazados 0 bytes en la memoria de fondos, indicado mediante la macro BG BMP BASE(0). Los datos del mapa para esta imagen ocupan los primeros 64KiB (256 × 256). Si se quisiera representar otro fondo con otra imagen, podrı́a estar contenida en la memoria de fondos, justo a continuación de la anterior imagen. Es decir, este segundo fondo comenzarı́a en el bloque 4 (cada bloque es de 16KiB), que se especifica en la configuración del fondo como BG BMP BASE(4). El motor secundario sólo admite un máximo de 128KiB para la memoria de fondos, mapeada en los bancos C, H e I de la VRAM. 3.4.4.3. Configuración de los fondos extended rotoscale La configuración de un fondo en modo ER conlleva la configuración de tres aspectos: Desplazamiento en la VRAM. Especificación del tamaño de la imagen y de cada pixel. La paleta de colores. 14 El bit más significativo se utiliza para indicar la opacidad. Si vale 1 el pixel es opaco y no deja ver el color de los fondos inferiores. Si vale 0 el pixel es transparente. 15 Puesto que cada pixel ocupará un byte (8 bits), la imagen ocupará 256 × 256 bytes, es decir, 64KiB. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 57 En el apartado anterior, se describió la configuración de los dos primeros aspectos. El desplazamiento en la VRAM se especifica en múltiplos de 16KiB, de manera que parece organizada en bloques lógicos. No hay ningún mecanismo que impida que se sobrepasen los lı́mites de estos bloques lógicos o BMP BASEs. Dado que la máxima memoria disponible para fondos es de 512KiB el rango posible de desplazamientos va entre 0 y 31, correspondientes a los deplazamientos de 0 bytes y 31 × 16KiB respectivamente. El tamaño que ocupa el mapa de bits dependerá del tamaño de la imagen a representar y del tamaño de cada pixel. La configuración del tamaño sólo permite la elección entre una lista preestablecida de valores, especificados como una enumeración en el fichero de cabecera. El tamaño de cada pixel solo puede ser 8 bits o 16 bits. En el siguiente fragmento de código se configura un fondo de mapa de bits de 16 bits por pixel para representar un gradado de negro a azul, suponiendo un tamaño de 256 × 256: 1 BGCTRL [2] = BG_BMP_BASE (0) | BgS iz e_ B1 6_ 25 6x 25 6 ; 2 uint16 * memoria = BG_GFX ; 3 4 int i , j ; 5 6 7 8 for ( i =0; i <192; i ++) for ( j =0; j <256; j ++) memoria [ i *256 + j ]= RGB15 (0 ,0 , i /(192/32) ) | BIT (15) ; En este caso, al tratarse de un mapa de bits de 16 bits por pixel, no existe paleta de colores. Een memoria se escriben los 16 bits correspondientes al color de cada pixel. Se debe puntualizar que el bit 15 debe estar habilitado para que el pixel no sea considerado como transparente, para lo cual se utiliza la macro BIT(15). Por defecto, la macro RGB15 deja el bit 15 a 0. Alternativamente, se podrı́a utilizar la macro ARGB16(1,r,g,b) que directamente pone el bit más significativo al valor indicado por el primer argumento. Las configuraciones del fondo que utilicen 8 bits por cada pixel no representan cada pixel por un color, sino por el número de color dentro de la paleta de colores del sistema. Una paleta es simplemente una colección de todos aquellos colores que se utilizan en la imagen a representar. En La figura 3.16 se muestra un ejemplo de paleta. Las paletas de la consola NDS están limitadas a un máximo de 256 colores diferentes. Para dibujar un gradado de negro a azul utilizando la paleta de colores, se requiere disponer de un vector de datos para contener el valor de los 16 bits correspondientes a cada una de las 32 tonalidades de azul. Este vector, al igual que ocurre con los datos de la imagen, CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 58 Figura 3.16: Ejemplo de paleta de colores [24] se debe escribir en aquella zona de memoria donde el motor gráfico 2D busca este tipo de datos. La dirección de comienzo de esa región es la contenida en la macro BG PALETTE. De esa manera, una vez se haya generado la paleta y se haya copiado en la memoria, el siguiente paso será escribir el mapa de datos. Este mapa contiene el número de la posición que ocupa el color que se representará en cada pixel, quedando el código para el ejemplo de gradado de la siguiente manera: 1 BGCTRL [2] = BG_BMP_BASE (0) | Bg Size_B 8_256x 256 ; 2 3 uint16 * paleta = BG_PALETTE ; 4 uint8 linea [256]; 5 uint8 * memoria = ( uint8 *) BG_GFX ; 6 7 int i , j ; 8 for ( i =0; i <32; i ++) paleta [ i ] = RGB15 (0 , 0 , i ) ; 9 10 11 for ( i =0; i <192; i ++) { for ( j =0; j <256; j ++) { 12 linea [ j ]= i /(192/32) ; 13 14 } 15 memcpy (& memoria [256* i ] , linea , 256) ; 16 } CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 59 La lı́nea número 1 se corresponde con la configuración del fondo, que en este caso es un fondo de 8 bits por pixel y tamaño de 256 × 256. La lı́nea 3 define un puntero de tipo entero corto sin signo al comienzo de la región de memoria donde el motor 2D espera encontrar los datos de la paleta. La paleta se rellena con todos los azules posibles en el bucle for de las lı́neas 8 y 9. La lı́nea 13, asigna posiciones de la paleta a cada uno de los pixeles de la lı́nea en curso, que se almacena en el vector linea. Finalmente, la lı́nea 15 copia la lı́nea en curso a la posición correspondiente de la memoria de video. 3.4.4.4. La matriz de transformación El código de ejemplo que se ha presentando en los apartados anteriores está incompleto, ya que el último paso para la representación de un gráfico en modo ER requiere la especificación de una matriz de transformaciones afines [35]. Los fondos de rotación extendida admiten rotaciones, desplazamientos y escalados. Incluso en el caso de que no se desee que la representación de la imagen sea objeto de alguna de estas modificaciones, es necesario que se especifique la matriz de transformación. Una transformación cambia la posición de los puntos del plano. Al aplicar una transformación sobre una imagen se obtiene otra imagen. Las transformaciones afines transforman rectas en rectas y preservan la propiedad de paralelismo. Matemáticamente son muy sencillas: ~v = A · (~u − u~0 ) (3.1) Donde u~o es el vector que determina el punto origen de coordenadas de la transformación, es decir, el punto de la imagen original que se transforma en el origen de coordenadas en el plano transformado. O bien, expresada por componentes: " # x0 = y0 ! " # a b x − x0 · c d y − y0 (3.2) Los motores gráficos de la consola, sin embargo, están más interesados en la operación contraria, obtener el punto de la imagen original dado un punto de la imagen transformada. Esto se debe a que su función es precisamente recorrer punto a punto de la pantalla (espacio transformado), pintándolo del color que corresponda (punto equivalente de la imagen original). Desde el punto de vista matemático serı́a: " # " # " # x x0 x0 = A−1 · 0 + y y y0 (3.3) CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 60 Si se desea poner todo en una única matriz P, la ecuación quedarı́a de la siguiente manera: h i h i xdx xdy x y = x0 y0 1 · ydx ydy dx dy (3.4) En la biblioteca libnds se proporciona una estructura de datos que facilita el manejo de la matriz de transformación, de nombre bg transform. Esta estructura se compone de los siguientes campos de datos: 1 typedef struct { 2 s16 xdx ; 3 s16 ydx ; 4 s16 xdy ; 5 s16 ydy ; 6 s32 dx ; 7 s32 dy ; 8 } bg_transform ; Cada campo se corresponde exactamente con la componente del mismo nombre en la matriz P de la ecuación 3.4. Dependiendo de los valores que se utilicen, se pueden conseguir un amplio conjunto de efectos. En la figura 3.17 se muestran algunos ejemplos que se pueden conseguir mediante transformaciones afines. 3.4.4.5. Valores reales en punto fijo En la consola NDS se tiene que programar la matriz P para obtener la transformación deseada. Aunque no se desee ninguna transformación de la imagen, se tiene que indicar la matriz P correspondiente a la transformación identidad. Nótese que los coeficientes de esta matriz son, en general, números reales. Los números reales se pueden representar en C mediante los tipos float o double, los cuales emplean un formato conocido como representación en coma flotante. Sin embargo, ninguno de estos tipos valen para especificar los coeficientes de la matriz de transformación. Para que la consola NDS los entienda, se deben especificar como números reales en representación de coma fija con 8 bits decimales. La representación en coma fija no es dificil de entender ni de manipular empleando números enteros convencionales. Basta con transformar el número real en una fracción CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 61 Figura 3.17: Ejemplos de transformaciones afines entera en la que el denominador es la base elevada al número de dı́gitos decimales considerados. El numerador de esa fracción serı́a su representación en coma fija. Por ejemplo, para representar el número 0, 25 en coma fija con dos dı́gitos de parte decimal, se transformarı́a en la fracción 25/100 y se tomarı́a el numerador 25. Lo único que cambia en un computador es que la base de numeración empleada es la binaria. Ası́, el número 0, 25(10 en binario serı́a 0, 01(2 . Para que se represente en coma fija de 2 dı́gitos binarios de parte decimal, se multiplicarı́a por 22 , es decir, se desplazarı́a el punto dos posiciones hacia la derecha, obteniendose 001. En el caso de la consola NDS, la matriz de transformación utiliza números de coma fija con 8 dı́gitos binarios de parte decimal. Por tanto el número 1, 0 serı́a un 1 seguido de 8 ceros de la parte decimal, es decir, 100000000(2 . O, lo que es lo mismo, 256. En el primer ejemplo de la figura 3.17, la matriz P equivalente a la transformación identidad (no transformación) serı́a: 1, 0 0 P = 0 1, 0 0 0 (3.5) CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 62 Que expresado en coma fija en los elementos de la estructura bg transform, se escribirı́a: 1 bg_transform [2] - > xdx = 256; 2 bg_transform [2] - > ydx = 0; 3 bg_transform [2] - > xdy = 0; 4 bg_transform [2] - > ydy = 256; 5 bg_transform [2] - > dx = 0; 6 bg_transform [2] - > dy = 0; El ı́ndice 2 significa que se está definiendo la matriz para el fondo BG2. Los desplazamientos dx y dy también se expresan en coma fija. A continuación se analizará el caso del efecto de espejo horizontal. La matriz P es: −1 0 P = 0 1 256 0 (3.6) Que expresado en coma fija en los elementos de la estructura bg transform, se escribirı́a: 1 bg_transform [2] - > xdx = - (1 << 8) ; 2 bg_transform [2] - > ydx = 0; 3 bg_transform [2] - > xdy = 0; 4 bg_transform [2] - > ydy = (1 << 8) ; 5 bg_transform [2] - > dx 6 bg_transform [2] - > dy = 0; = 256 << 8; En este caso, se ha utilizado el hecho de que el desplazamiento hacia la izquierda es equivalente a multiplicar por la base (en este caso 2). Por tanto, 8 desplazamientos es equivalente a multiplicar por 256. Por último, se verá un ejemplo más complejo. El caso de la rotación de 30 grados en sentido horario serı́a: cos π6 sen π6 P = − sen π6 cos π6 0 0 (3.7) CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 63 Que expresado en coma fija en los elementos de la estructura bg transform, se escribirı́a: 1 # include < math .h > 2 ... 3 bg_transform [2] - > xdx = cos ( M_PI /6.0) * 256.0; 4 bg_transform [2] - > ydx = sin ( M_PI /6.0) * 256.0; 5 bg_transform [2] - > xdy = - sin ( M_PI /6.0) * 256.0; 6 bg_transform [2] - > ydy = cos ( M_PI /6.0) * 256.0; 7 bg_transform [2] - > dx = 0; 8 bg_transform [2] - > dy = 0; En este caso se han realizado las operaciones en coma flotante y, posteriormente, se ha convertido implı́citamente el número resultante a un entero. Este último caso se ha escrito con efectos puramente ilustrativos, aunque no es ni mucho menos óptimo. Los procesadores de la NDS no tienen unidad aritmética de coma flotante, por lo que todas estas operaciones intermedias se simularán por el compilador, generando un código muy ineficiente. 3.4.5. Modo teselado 3.4.5.1. Introducción El nivel de complejidad de los modos gráficos de la NDS se ha ido incrementando durante las últimas secciones, pero también las posibilidades de representación que ofrecen. El framebuffer es el más sencillo y directo de manejar de los tres, mientras que el modo teselado representa el más versátil y flexible en cuanto a la utilización de recursos. Ası́, en el modo framebuffer se limitaba el tamaño de la imagen al de la pantalla de la consola, permitiendo únicamente la representación de un gráfico, ya que no soporta la utilización de fondos o capas. En los fondos de rotación extendida se introducen mejoras relevantes en cuanto al tipo de gráficos, el tamaño y número de gráficos que se pueden representar, ya que entra en juego el uso de fondos. Además, en los fondos ER se da soporte a una serie de transformaciones que ofrecen una mayor versatilidad a los fondos. En el siguiente escalón creciente de funcionalidad se encuentran los fondos teselados (tiled backgrounds), que, además, se pueden utilizar como fondos de rotación extendida. Un fondo teselado [25] se compone de dos partes: CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 64 Colección de teselas o tile set: es un conjunto de hasta 1024 teselas diferentes, donde cada tesela se identifica según la posición que ocupa dentro de la colección de teselas, de 0 a 1023. Mapa de teselas: es una matriz de tamaño 32×32, 32×64, 64×32 o 64×64 (dependiendo del tamaño del fondo), que especifica qué tesela se colocará en cada uno de las posiciones de esa matriz imaginaria de teselas en la que se ha divido el fondo. El concepto de mapa ya apareció en el contexto de los fondos de rotación extendida, pero, en este caso, las entradas del mapa no especificaban teselas, sino el ı́ndice de la paleta de colores asignado a cada uno de los pixeles que componı́a el fondo. De alguna manera, se puede concebir que la unidad mı́nima para el mapa de los fondos de rotación extendida era el pixel, mientras que para los fondos teselados es la tesela. La realidad es algo más compleja, ya que además de considerar la tesela como unidad mı́nima, en el contexto de los fondos teselados, el gráfico se va a describir en función de tres elementos básicos: La tesela: es la unidad mı́nima de composición de los fondos teselados. La transformación: Toda tesela puede admitir una transformación de entre tres diferentes, espejo con respecto al eje Y (espejo vertical), con respecto al eje X (espejo horizontal) o ambos. La paleta de colores: Existe un modo especial de gráficos teselados que divide la paleta de colores en 16 subpaletas de 16 colores. Sólo para este caso, en cada componente del mapa se tendrá que especificar la subpaleta utilizada de entre las 16 disponibles. Antes de que se estudien de manera más detallada los fundamentos de los fondos teselados, se hará un especial énfasis en aclarar la dinámica general de este tipo de fondos, utilizando para ello la figura 3.18. Como se puede observar, parte de la memoria de video VRAM puede configurarse para albergar la memoria de fondos, y dentro de esta, los datos de mapas y teselas que compondrán los fondos teselados. Simplificando al máximo los fundamentos de la representación de gráficos teselados, el mapa de un fondo teselado se compone de referencias a teselas y, cuando se utilicen paletas de 16 colores, también de referencias a la subpaleta utilizada por cada tesela. Por otro lado, en la zona de la memoria de fondos destinada a albergar los datos de las teselas, cada entrada se corresponderá con una tesela de 8×8 pixels. Cada pixel se representa mediante el ı́ndice del color correspondiente en la paleta de colores, de forma CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 65 Figura 3.18: Elementos básicos de los fondos teselados [25] similar a los modos rotoscale de 8 bits por pixel, o en un modo más compacto que sólo utiliza 4 bits por pixel al usar paletas de 16 colores. Finalmente, el último eslabón de esta cadena es la paleta o paletas de colores, que ya se conoce de secciones anteriores. Como principal novedad, la paleta de 256 colores puede utilizarse como un grupo de 16 paletas de 16 colores. 3.4.5.2. Fondos teselados En cualquiera de los modos disponibles para los dos motores gráficos (el secundario y el principal), hay al menos uno de los fondos configurado para trabajar como fondo teselado. Por ejemplo, el modo 5 elegido para la representación de fondos de rotación extendida, se puede utilizar también para representar fondos teselados, utilizando en este caso los fondos 0 ó 1. La configuración de los fondos teselados debe incluir tres elementos: Desplazamiento en la VRAM: especifica dónde se ubicarán los datos, tanto del mapa como de las teselas que compondrán el fondo. Tamaño: conjunto limitado de posibles tamaños, especificado en teselas. Tipo de paleta: existe la posibilidad de utilizar hasta 16 paletas de 16 colores o simplemente utilizar una de 256. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 66 El desplazamiento en la VRAM se explicará más detalladamente en el siguiente apartado. El tamaño del fondo vendrá especificado en teselas, siempre teniendo presente que cada tesela tiene unas dimensiones de 8×8 pixels. Además, el tamaño del fondo estará limitado a una de las posibles combinaciones de {32 ó 64} x {32 ó 64}. En cuanto a la paleta de colores, este tipo de fondos considera o bien una paleta de 256 colores o subpaletas de 16 colores cada una (hasta un máximo de 16 subpaletas), es decir, hay dos modos de colores para las teselas: Modo de 256 colores: A cada pixel se le asigna un ı́ndice de la paleta de colores. El ı́ndice será un entero de 8 bits, con un valor comprendido entre 0 y 255. Modo de 16 colores: En este caso un pixel es un ı́ndice a una paleta de 16 colores. Por lo tanto, el ı́ndice será un entero de 4 bits, con un valor comprendido entre 0 y 15. Aunque este modo requiere menos memoria, dejando ası́ más espacio para teselas y otros elementos en la VRAM, la calidad de la imagen renderizada puede verse comprometida al disponer de solo 16 colores diferentes en cada tesela. 3.4.5.2.1. Desplazamientos en la VRAM El motor gráfico renderiza los fondos teselados a partir de las entradas del mapa y el conjunto de teselas a las que hace referencia. Estos datos están contenidos en la memoria de fondos de la VRAM. Teniendo en cuenta que cada motor gráfico puede manejar un máximo de cuatro fondos, es necesario que cada fondo activado configure la localización de sus datos de mapas y teselas. Para ello, se utiliza el mismo registro de configuración del fondo, el registro REG BGnCNT o BGCTRL[n], donde la n se sustituye por el ı́ndice del fondo, tomando valores de 0 a 3. Este registro reserva 4 bits para especificar el desplazamiento de las teselas (en múltiplos de 16 KiB) y 5 bits para el mapa (en múltiplos de 2 KiB), de tal forma que las teselas admiten 24 = 16 posibles valores del desplazamiento base y los mapas admiten 25 = 32. Un mapa puede empezar en cualquier dirección múltiplo de 2KiB entre 0 × 2KiB y 31 × 2KiB. Expresado en hexadecimal, serı́a desde 0×0x800 hasta 31×0x800. El tamaño de un mapa depende del tamaño del fondo. Un mapa de 32 × 32 teselas será una sucesión de 32 × 32 entradas de 16 bits (esto se explicará con más detalle en las siguientes páginas), por lo que el tamaño total será de 2 × 32 × 32 = 2KiB. Es decir, en este caso el mapa entero cabe antes de la siguiente dirección base de otro mapa. Sin embargo, para un fondo de 64 × 64 teselas, los datos del mapa requieren 2 × 64 × 64 = 8KiB, es decir, las siguientes 3 posibles direcciones base no estarı́an disponibles para otro mapa (véase figura 3.19). CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 67 Figura 3.19: Direcciones base para un mapa de 32×32 y de 64×64 [25] Para que se facilite la configuración de las direcciones base, la liberı́a libnds incluye la macro BG MAP BASE(n) (donde esa n se corresponde con el múltiplo de 2KiB seleccionado, entre 0 y 31). Indicar en el registro de configuración del fondo que los datos del mapa se encuentran ubicados a partir de BG MAP BASE(1), significa que comenzarán en la posición de memoria 0x0800 a partir del principio de la memoria de fondos. Análogamente, decir que lo hacen en BG MAP BASE(31) significa que empiezan en la posición 0xf800, tal y como se puede comprobar en la figura 3.20. Una técnica similar a ésta ya se utilizó en los fondos de rotación extendida, para especificar la ubicación de los datos del mapa. Sin embargo, en este caso el desplazamiento base se indicaba como un múltiplo de 16KiB y los datos incluı́an directamente el mapa de bits a mostrar. En libnds se incluye la macro BG BMP BASE(n), que empleábamos en los modos rotoscale para indicar el comienzo del fondo en múltiplos de de 16KiB. Realmente tanto la macro BG MAP BASE(n) como la macro BG BMP BASE(n) hacen exactamente lo mismo, configuran los 5 bits reservados en los registros de configuración de los fondos para especificar el comienzo del mapa, aunque en este caso el desplazamiento es múltiplo de 16 KiB. Usar dos nombres distintos simplemente sirve para hacer explı́cito que se trata de direcciones diferentes. Como ya se ha comentado en algún momento, el motor de video no tiene memoria fı́sica dedicada. Parte de la memoria de video (VRAM) debe configurarse para ser utilizada como memoria de fondos. El coprocesador gráfico principal utiliza como memoria fondos el rango de memoria de 0x06000000 a 0x0607ffff (512KiB máximo) mientras que el secundario emplea el rango de 0x06200000 a 0x0621ffff (128KiB máximo). Gran parte de los bancos de la memoria de video pueden configurarse para ser accesibles en estos rangos de memoria, como ya hemos visto en el capı́tulo anterior. Todos los desplazamientos base de mapas y teselas son referidos al comienzo de estos rangos de memoria. En cuanto a las teselas, ocurre lo mismo que para los mapas, ya que ambos comparten la misma memoria de fondos y al igual que los mapas, utilizarán los desplazamientos para gestionar esa memoria como si estuviera organizada en bloques, sólo que en este caso, de tamaño 16KiB. La macro utilizada para especificar la dirección base es BG TILE BASE(n), CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN Figura 3.20: Desplazamientos en la memoria de fondos [25] 68 CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 69 donde n que toma valores entre 0 y 15. El número corresponde al múltiplo de 16KiB correspondiente, empezando en 0x0000, desplazándose en múltiplos de 0x4000 hasta el valor 0x3c000. Ya se sabe que una tesela es una colección de 64 pixeles, ¿pero cuál es el tamaño de un pixel? La NDS soporta dos modos de color para las teselas de un fondo teselado, el modo de 256 colores utiliza 8 bits para identificar un color de la paleta, de tal forma que cada pixel tendrá un tamaño de 1 byte. En cambio, si se usa 16 paletas de 16 colores cada una, el ı́ndice de paleta podrá especificarse con sólo 4 bits, lo que supone un ahorro de memoria en VRAM, aunque también supone al mismo tiempo una pérdida en cuanto al número de colores disponibles. Teniendo en cuenta que el desplazamiento en la memoria de fondos para las teselas es múltiplo 16KiB, cada bloque lógico puede almacenar 256 teselas en modo de 256 colores y 512 en modo de 16 colores: 256 teselas × 64 pixeles/tesela × 1 byte/pixel = 16KiB 512 teselas × 64 pixeles/tesela × 1/2 byte/pixel = 16KiB En la figura 3.20 se puede comprobar muy fácilmente por qué el primer listado de código es correcto, mientras que el segundo mostrará unos resultados que no son los esperados. Se debe ser muy cuidadoso a la hora de utilizar los desplazamientos en la memoria de fondos, ya que ésta es la misma para mapas y teselas, lo que significa que una incorrecta utilización de esos desplazamientos lleva a escribir unos datos sobre otros. Dadas las unidades de desplazamiento, cada unidad base de teselas se componen de 8 unidades base de mapas: 1 BGCTRL [0] = BG_32x32 | BG_COLOR_16 | BG_MAP_BASE (0) | BG_TILE_BASE (1) ; Esta configuración especifica que los datos del mapa están contenidos a partir de la dirección 0x0000 mientras que los datos de la tesela se encuentran a partir de la dirección 0x04000 (el múltiplo 1 de 16KiB). Como se puede observar, no existe superposición de los datos. Sin embargo, el siguiente fragmento de código es incorrecto, ya que los datos del mapa y los de las teselas están configurados a partir de la misma dirección: 1 BGCTRL [0] = BG_32x32 | BG_COLOR_16 | BG_MAP_BASE (0) | BG_TILE_BASE (0) ; CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 3.4.5.3. 70 Teselas Los gráficos teselados vienen descritos mediante una matriz de teselas, en lugar de una matriz de pixeles, donde cada tesela representa un pequeño mapa de bits de 8x8 pixeles. Para entender mejor el concepto de gráfico teselado, se puede imaginar la pantalla de la consola dividida en celdas de dimensiones 8x8 pixeles, donde cada cuadrado se corresponde con una tesela, quedando la pantalla dividida en 32 teselas de ancho y 24 de largo. Creando teselas de 16 y 256 colores Aunque el concepto de paleta de colores para los fondos de rotación extendida no difiere para el caso de los fondos teselados, sı́ que en este último caso caben dos posibilidades, en lugar de considerar una única paleta de 256 colores, considerar 16 paletas de 16 colores cada una. La principal ventaja que se obtiene al disponer de 16 paletas de colores estriba básicamente en la capacidad de representar los pixels de una misma tesela con colores diferentes, dependiendo de la paleta elegida, pues cada pixel se especifica mediante el ı́ndice que el color ocupa en la paleta. De esta manera, si el color 1 es rojo en una paleta, verde en otra y ası́ sucesivamente en las 16 paletas disponibles, será posible representar un pixel de una misma tesela de 16 colores diferentes. El hecho de utilizar una paleta de 256 ó de 16 colores va a determinar el número de bits necesarios para referenciar las entradas de la paleta. Naturalmente, como para el caso de los fondos de rotación extendida, que utilizaban una paleta de 256 colores, cada pixel se especifica mediante 8 bits. Sin embargo, para el caso de utilizar paletas de 16 colores, 4 bits son suficientes para identificar cada una de las 16 entradas disponibles. Por lo tanto, las entradas para cada uno de los 64 pixels que componen una tesela, serán de 4 u 8 bits, dependiendo del modo de color utilizado (16 ó 256). Como ya se ha introducido brevemente, los gráficos teselados son aquéllos que vienen descritos mediante una matriz de teselas, en lugar de una matriz de pixels, como ocurrı́a en el modo framebuffer o de rotación extendida. Cada tesela es equivalente a un pequeño mapa de bits, de 8x8 pixels. Por lo tanto la definición de una tesela no difiere demasiado de la definición de un fondo de rotación extendida, con la diferencia de que la tesela tiene un tamaño fijo de 8x8 y además, dependiendo del tipo del modo de color utilizado (16 ó 256 colores) se compondrá de entradas de 4 u 8 bits. El siguiente código se corresponde con la defición de una tesela que utiliza una paleta de 16 colores. Como se puede comprobar, cada elemento del vector de tipo unsigned char (de 8 bits de tamaño) especifica el color de dos pixels, dedicando los 4 bits correspondientes a cada uno de los dı́gitos de la notación hexadecimal utilizada a expresar el ı́ndice de la paleta para cada pixel. Debido al orden de bytes (endianness) de la máquina, los bits menos CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 71 significativos ocupan las posiciones más bajas, de tal forma, que por ejemplo, el valor 0x10, para los dos primeros pixels de la tesela, asignan al pixel 0 (el menos significativo) el ı́ndice 0 de la paleta y el pixel 1 (el más significativo) el ı́ndice 1 de la paleta. La figura 3.21 representa la tesela definida por el siguiente código. 1 unsigned char A16 [] = { 2 0 x00 ,0 x10 ,0 x01 ,0 x00 , 3 0 x00 ,0 x11 ,0 x11 ,0 x00 , 4 0 x00 ,0 x01 ,0 x10 ,0 x00 , 5 0 x10 ,0 x01 ,0 x10 ,0 x01 , 6 0 x10 ,0 x11 ,0 x11 ,0 x01 , 7 0 x10 ,0 x01 ,0 x10 ,0 x01 , 8 0 x10 ,0 x01 ,0 x10 ,0 x01 , 9 0 x00 ,0 x00 ,0 x00 ,0 x00 , 10 }; Figura 3.21: Ejemplo de tesela [25] Sin embargo, la forma más cómoda de definir teselas que utilizan paletas de 16 colores es utilizar un vector de tipo unsigned long, ya que como tal, cada elemento de este vector tiene un tamaño de 32 bits, de tal forma que adoptando una notación hexadecimal de 8 dı́gitos, cada uno de ellos se corresponde con la especificación del color de los pixels de la tesela. El siguiente código se corresponde con la definición de la misma tesela de la figura 3.21 utilizando un vector de unsigned long. Por la simetrı́a de la imagen representada en la tesela, no se aprecia el endianess utilizado, sin emabargo, en la definición de la segunda tesela (representa una flecha) sı́ que se aprecia, tal como se puede observar en la representación de la tesela en la figura 3.22. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 1 unsigned long A16 [] = { 2 0 x00011000 3 0 x00111100 , 4 0 x00100100 , 5 0 x01100110 , 6 0 x01111110 , 7 0 x01100110 , 8 0 x01100110 , 9 0 x00000000 , 10 72 }; Figura 3.22: Tesela en forma de flecha [25] Una de las principales ventajas derivadas del uso de paletas de 16 colores es que simplemente cambiando de paleta cambia el color de la tesela. Por ejemplo, para el mismo código la misma tesela representada en la figura 3.21, suponiendo ahora que se cambia la paleta y el color que ocupa la posicion 1 es el color verde, figura 6, la tesela asignará el color verdes a los pixels que apunten a la posición 1, como muestra la figura 5. Si se vuelve a cambiar de paleta, ahora por una paleta cuya elemento de ı́ndice 1 sea el color azul, la tesela también cambiará de aspecto, dibujando en azul los pixels que apunten contengan el ı́ndice 1. Cuando la paleta utilizada sea de 256 colores, el número de bits necesarios para especificar un pı́xel será de 8 (28 = 256) como ya se ha comentado anteriormente, de tal forma, que la misma tesela para representar la letra A, utilizando en este caso 8 bits por pixel se puede definir mediante un vector de unsigned char, quedando de la siguiente manera: CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 73 Figura 3.23: Misma tesela con diferente paleta [25] 1 unsigned char A256 [] = { 2 0 ,0 ,0 ,1 ,1 ,0 ,0 ,0 , 3 0 ,0 ,1 ,1 ,1 ,1 ,0 ,0 , 4 0 ,0 ,1 ,0 ,0 ,1 ,0 ,0 , 5 0 ,1 ,1 ,0 ,0 ,1 ,1 ,0 , 6 0 ,1 ,1 ,1 ,1 ,1 ,1 ,0 , 7 0 ,1 ,1 ,0 ,0 ,1 ,1 ,0 , 8 0 ,1 ,1 ,0 ,0 ,1 ,1 ,0 , 9 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 10 }; El uso de una notación decimal, en lugar de la hexadecimal, en este caso es más cómoda, pues cada pixel se corresponde con un elemento del vector de 64 elementos que describen los pixels de la tesela. Copiando las teselas en memoria La configuración especificada para el fondo teselado será determinante para obtener la dirección de memoria donde se escribirán los datos del mismo. Los ı́ndices empleado en la configuración en las macros BG TILE BASE(n) y BG MAP BASE(n), serán los mismos que se especifiquen en las macros utilizadas para obtener la dirección de memoria donde escribir los datos de mapas y teselas. La macro BG TILE RAM(n) se define como un puntero que apunta a la dirección de memoria 0x06000000 más el desplazamiento (n por 0x4000). Análogamente, la macro BG MAP RAM(n) se define como un puntero que apunta a la dirección de memoria 0x06000000 más el desplazamiento (n por 0x800). Suponiendo que se desear cargar en memoria las teselas generadas por Grit, para una imagen de archivo y dada una configuración inicial para el fondo como la que se indica CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 74 en el siguiente listado, en la lı́nea 1, que especifica un desplazamiento en memoria para las teselas de 16KiB. La lı́nea 2 muestra cómo utilizar la macro BG TILE RAM(1), con el mı́smo número empleado en BG TILE BASE(1): 1 BGCTRL [0] = BG_32x32 | BG_COLOR_16 | BG_MAP_BASE (0) | BG_TILE_BASE (1) ; 2 memcpy ( BG_TILE_RAM (1) , gnuTiles , gnuTilesLen ) ; Como se puede observar, la función utilizada para escribir los datos de las teselas es una función ya conocida, la memcpy. Sin embargo, también se podrı́a haber utilizado la función dmaCopy o swiCopy como ya se ha descrito en el capı́tulo anterior. Es importante discernir entre lo que es el desplazamiento en la memoria que se especifica mediante la macro BG TILE BASE(n) y la dirección de memoria, especificada con la macro BG TILE RAM(n), donde se escribirán los datos. Los desplazamientos en memoria se especifican en el momento de la configuración del fondo, mientras que la dirección de memoria se utiliza para acceder desde el programa a la zona donde está. 3.4.5.4. Mapas Como ya se ha adelantado, cada una de las entradas del mapa se representa mediante un conjunto de 16 bits, donde los 10 bits menos significativos especifican la tesela. De ahı́ que el conjunto máximo de teselas esté limitado a 1024, las máximas referenciables con 10 bits. Los dos bits siguientes se utilizan para especificar el efecto de espejo en la tesela, es decir, si tiene una reflexión horizontal, vertical o ambas. Finalmente, para teselas que utilizan paletas de 16 colores, se utilizan los 4 últimos bits para identificar la subpaleta de colores utilizada por esa tesela. Bits 15 14 13 12 Propósito Paleta 11 Espejo Vertical 10 Espejo Horizontal 9876543210 Índice Tabla 3.9: Entrada de un mapa teselado Se puede comprobar el uso eficiente de la memoria que se deriva del uso de los fondos teselados, cuando se observa un fondo como el representado en las figuras 3.24 y 3.25. El mapa para representar este fondo teselado estará compuesto de un gran número de entradas repetidas, apuntando a la misma tesela, ya que gran parte del fondo se compone de las mismas teselas, como la que se resalta en la figura 3.24. Además de repetir teselas, este CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 75 fondo también se compondrá de teselas representadas con algún tipo de reflexión, como es el caso de las dos teselas que se extraen del fondo de la figura 3.25, y cuyo reflejo horizontal permite representar parte del tejado de las casas. Figura 3.24: Ejemplo de fondo del juego Yoshi Island [1] Figura 3.25: Ejemplo de fondo del juego Super Nenas [1] 3.4.5.5. Generando las entradas del mapa La copia de los datos del mapa generados automáticamente con el grit no supone ninguna dificultad. Es bastante más interesante estudiar el proceso de composición de las entradas de un mapa a partir de las teselas que se quieren representar en el mismo. A lo largo de esta sección se detallará el proceso de creación de un mapa, para un fondo de CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 76 32x32 teselas, compuesto únicamente a partir de una única tesela, repetida a lo largo de todo el fondo y a la que se le aplicarán rotaciones verticales y horizontales. Veamos el siguiente código se corresponde con una tesela que representa la forma de la letra L: 1 const unsiged long myTiles [] = { 2 0 x10000000 , 3 0 x10000000 , 4 0 x10000000 , 5 0 x10000000 , 6 0 x10000000 , 7 0 x10000000 , 8 0 x10000000 , 9 0 x11111111 , 10 }; Figura 3.26: Tesela que representa la letra L [25] Con esta única tesela, el objetivo será crear un fondo que represente una rejilla, donde cada celda tenga unas dimensiones de 4x4 teselas, utilizando para ello únicamente esta tesela. Aplicando reflexiones horizontales, verticales y en ambas direcciones a la vez, se puede conseguir que esta misma tesela aparezca de cuatro maneras diferentes, cuya combinación nos permitirá obtener celdas de tamaño 4x4. A continuación, se verá el código necesario para cargar la tesela en la memoria de fondos y ver cómo se compone cada una de las entradas del mapa para este fondo: 1 REG_POWERCNT = POWER_ALL_2D ; 2 REG_DISPCNT = MODE_0_2D | D IS PL AY _B G0_ AC TI VE ; CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 3 VRAM_A_CR = VRAM_ENABLE | VRAM_A_MAIN_BG ; 4 BGCTRL [0] = BG_32x32 | BG_COLOR_16 | BG_MAP_BASE (0) | 77 BG_TILE_BASE (1) ; 5 6 memcpy (( void *) BG_TILE_RAM (1) , myTiles , sizeof ( myTiles ) ) ; 7 8 BG_PALETTE [0 xf1 ] = RGB15 (31 ,0 ,0) ; 9 10 int i , j ; 11 u16 * reja = ( u16 *) BG_MAP_RAM (0) ; 12 13 for ( i = 0; i < 32; ++ i ) { for ( j = 0; j < 32; ++ j ) { 14 reja [ i + 32* j ] = 0 xf000 | ( j %2 ? 0 x0800 : 0) | 15 ( i %2 ? 0 x0400 : 0) ; } 16 17 } En este ejemplo, el conjunto de tesela se limita a la tesela que se ha descrito anteriormente, de nombre myTiles, que en la lı́nea 7 del código se copia a la memoria de fondos, a partir de un desplazamiento inicial de 16KiB, ya que en la configuración del fondo se especificó un desplazamiento base de 1 unidad. Además, el fondo está configurado para utilizar una paleta de 16 colores. Se utilizará la paleta número 15, tal y como se puede deducir de la lı́nea 9, donde al segundo elemento de la paleta 15 se le asigna el color rojo16 , aunque no será hasta el momento de crear las entradas del mapa cuando se especificará el ı́ndice de la paleta utilizada. Seguidamente, la lı́nea 12 define un puntero de tipo entero sin signo, de 16 bits, apuntando a la memoria de fondos donde se almacenan los datos del mapa. Este puntero se utilizará para ir recorriendo esa región de la memoria de fondos e ir escribiendo a lo largo de la misma las entradas del mapa. En este ejemplo las entradas del mapa se componen a mano, en la lı́nea 16, que dentro de dos bucles for, recorre esa memoria de fondos para determinar las 32 × 32 teselas que van a componer el fondo. Analizando más detenidamente esta lı́nea y teniendo presente que cada entrada del mapa se compone de 16 bits, se puede utilizar una notación hexadecimal como forma abreviada de la representación binaria, de tal forma que cada dı́gito hexade16 Cuando se utilizan paletas de 16 colores, el uso de la notación hexadecimal para el ı́ndice de color en la paleta simplifica bastante su manejo, ya que el primer dı́gito hace referencia a la paleta, en este caso al ser f serı́a la paleta 15 (empieza en la paleta 0) y el segundo ı́ndice especifica la posición dentro de la paleta, en este caso el 1, indica la segunda posición de la paleta. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 78 cimal se corresponde con 4 bits. Por ejemplo 0xf000 equivale a 1111 0000 0000 0000. La entrada del mapa, es decir, el valor que se escribe en la memoria de fondos apuntada por el puntero reja tendrá una parte común y fija para todas las entradas, como es la referencia a la misma paleta (todas utilizan la misma paleta, la número 15) ası́ como la tesela, que también es única, y por lo tanto tendrá asignado el ı́ndice 0. La única variación reside en la reflexión aplicada a la tesela, vertical si la fila es impar y horizontal si la columna es impar y en ambas direcciones si tanto fila como columna son impares. La operación lógica OR a nivel de bit, habilita el bit cuando al menos uno está habilitado. Suponiendo la iteración i = 1 y j = 0, la situación es la siguiente: reja[1] = 0xf000 — 0x0000 — 0x0400, veamos cuál será el resultado de esta operación OR, que será el valor correspondiente con la entrada del mapa para la tesela que ocupa el segundo lugar, empezando por la esquina superior izquierda de la pantalla (fila 0, columna 1): 1111 0000 0000 0000 0000 0000 0000 0000 0000 0100 0000 0000 ——————————– 1111 0100 0000 0000 Como se puede observar, la única diferencia entre cada una de las entradas que componen el mapa para este fondo teselado, recae en los bits 10 y 11, que vendrán determinados por la paridad de fila y columna que dicha entrada representa en el fondo. El resultado final es el que aparece en la figura 11, donde cada celda tiene unas dimensiones de 4x4 teselas, la rejilla es de color rojo, tal y como se especificó en el código, quedando el interior de la celda en color transparente, ya que salvo que se especifique lo contrario, el ı́ndice 0 de cada paleta de colores se corresponde con el color transparente. Figura 3.27: 16 bits que componen la entrada de un mapa [25] 3.4.5.6. Copiando las entradas del mapa en memoria En el apartado anterior ya se ha adelantado la posibilidad de utilizar un puntero para recorrer y escribir en la memoria de fondos las entradas del mapa. Al estar compuestas por CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 79 Figura 3.28: Resultado final [25] un conjunto de 16 bits, evitan ası́ la limitación del bus a la hora de recorrer la memoria de video en unidades menores de 16 bits. No es necesario por tanto utilizar arrays intermedios como ocurrı́a en los mapas de los fondos de rotación extendida de 8 bits por pixel. En cualquier caso, si las entradas del mapa no se componen de manera manual, sino que se generan automáticamente a partir de una imagen del archivo se podrá utilizar cualquiera de las funciones de copia para transferirlos. Al igual que para el caso de las teselas, la zona de memoria donde se escribirán los datos del mapa vendrá determinada por la configuración del fondo. Las siguientes lı́neas de código presentan un ejemplo en el que el fondo se ha configurado con un desplazamiento inicial de 0 unidades para los mapas, especificado mediante la macro BG MAP BASE(0). La dirección de memoria de fondos, para escribir las entradas del mapa se obtiene utilizando la macro BG MAP RAM(0). 1 BGCTRL [0] = BG_32x32 | BG_COLOR_16 | BG_MAP_BASE (0) | BG_TILE_BASE (1) ; 2 memcpy ( BG_MAP_RAM (0) , fondoTiles , fondoTilesLen ) ; 3.4.5.7. Conversión de imágenes con grit Aunque no es la primera vez que se utiliza la aplicación Grit para la generación de los datos necesarios para mostrar en la pantalla de la consola una imagen de archivo, sı́ que es cierto que la generación de datos para cargar la imagen como un fondo teselado requiere una lista de opciones mayor que las vistas hasta el momento. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 80 Al igual que ocurrı́a para el framebuffer y para los fondos de rotación extendida, si se desea automatizar la generación de los datos de la imagen o imágenes, éstas deben estar ubicadas en el directorio de nombre data, que cuelga al mismo nivel que el directorio source (que a su vez contiene el código fuente). Por cada imagen que se desea convertir debe haber un fichero de texto, con el mismo nombre que la imagen, pero con extensión .grit. Cualquiera de los Makefile que se pueden encontrar con en los ejemplos de gráficos para la NDS que se han puesto a disposición del alumno, contiene las instrucciones necesarias para generar los datos en función de las opciones que se especifiquen en los archivos con extensión .grit, que para el caso de los fondos teselados, un ejemplo de las opciones más comunes son las siguientes: -p: Incluye los datos de la paleta de colores. -pe2: Especifica que el fin de la paleta, en este caso, el 2 ı́ndica que el fin de la paleta está en el segundo color, para imágenes en blanco y negro. Este valor se ajustará a los colores de la imagen. -gt: Indica que el formato de los datos del gráfico es teselado. -g: Incluye los datos del gráfico (en este caso las teselas). -gB4: 4 bits por pixel. -m: Incluye los datos del mapa. -mR4: Optimizar para 4bpp. -gT000000: Color transparente de la paleta, en este caso el negro. La figura 3.29 presenta de manera esquemática el proceso de obtención de los datos necesarios para mostrar una imagen de archivo en un fondo teselado. La aplicación grit genera automáticamente los datos correspondientes al mapa, teselas y paleta de colores, especificando las opciones de generación adecuadas para ello, aunque ésto se comentará de manera a lo largo de este documento. CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN Figura 3.29: Generando mapas, teselas y paletas [25] 81 CAPÍTULO 3. ANTECEDENTES, ESTADO DE LA CUESTIÓN 82 Capı́tulo 4 MÉTODO DE TRABAJO 4.1. Adaptación del problema al Proceso Unificado 4.2. Construcción del entorno cruzado de desarrollo 4.2.1. Comprensión del software 4.2.2. Construcción del diagrama de casos de uso 4.2.3. Fase de análisis 4.2.4. Fase de diseño 4.2.5. Fase de implementación 4.2.6. Pruebas 4.3. Construcción del editor de fondos 4.3.1. Construcción del diagrama de casos de uso 4.3.2. Priorización de casos de uso 4.3.3. Iteración 1 4.3.4. Iteración 2 4.3.5. Iteración 3 4.3.6. Iteración 4 4.3.7. Iteración 5 4.3.8. Iteración 6 4.3.9. Iteración 7 4.3.10. Iteración 8 4.3.11. Iteración 9 83 CAPÍTULO 4. MÉTODO DE TRABAJO 4.1. 84 Adaptación del problema al Proceso Unificado El presente proyecto se ha desarrollado utilizando la metodologı́a PUD. El Proceso Unificado de Desarrollo (PUD) [19] es una metodologı́a de desarrollo de software que se autodenomina “dirigida por casos de uso, iterativa e incremental”. Este trabajo se ha dividido en dos partes completamente independientes. Por un lado, se construirá un entorno cruzado de programación para el desarrollo de aplicaciones para la NDS. Con este entorno se podrá realizar la depuración de aplicaciones mediante el empleo de un emulador. Por otro lado, se desarrollará una aplicación que permita la edición de fondos para la Nintendo DS. La aplicación se podrá utilizar para trabajar tanto con fondos framebuffer, extended rotoscale, ası́ como fondos teselados. Este capı́tulo se dividirá en dos apartados princiaples: Construcción del entorno cruzado de desarrollo que permita la depuración de aplicaciones por medio de un emulador. Construcción de un editor de fondos para la Nintendo DS, el cual soporte el trabajo con los tres tipos de fondos disponibles en la videoconsola. En ambos apartados se comentarán los pasos necesario para resolver los problemas propuestos. Asimismo, se hará un énfasis especial en los mecanismos empleados, en lugar de incluir grandes cantidades de código. 4.2. Construcción del entorno cruzado de desarrollo 4.2.1. Comprensión del software En el apartado 3.2.5.1 del capı́tulo anterior se explicó cómo construir el entorno cruzado de desarrollo de software para la Nintendo DS, utilizando como base Eclipse. Dicho entorno permite la depuración directa en la consola. La depuración por medio de la consola impone bastantes limitaciones como, por ejemplo, no tener la capacidad de parar el programa que se está depurando cuando se desee. Para solucionar los problemas de la depuración directa en la consola, se debe utilizar un emulador de Nintendo DS. Sin embargo, este entorno no tiene integrado la opción de depurar un programa mediante el empleo de un emulador. En el caso de que se necesite utilizar un emulador para depurar un programa de NDS, dicho emulador debe ser gestionado de forma manual. Es decir, antes del comienzo de cada proceso de depuración se debe arrancar el emulador manualmente. Por lo tanto, el funcionamiento del entorno y del emulador son totalmente independientes entre sı́. CAPÍTULO 4. MÉTODO DE TRABAJO 85 Para que el entorno sea capaz de soportar la depuración mediante el empleo de un emulador, se deberá modificar el entorno. En concreto, se debe añadir está nueva funcionalidad al plug-in Zylin Embedded CDT. DeSmuME será el emulador que se integre dentro del entorno. El motivo de esta elección se debe a que DeSmuME es el único que implementa la interfaz con el depurador GDB (gdb stubs) directamente en el propio emulador, sin necesidad de añadir ningún código adicional a los programas de la Nintendo DS. 4.2.1.1. Análisis del código fuente Cuando un usuario se plantea depurar una aplicación con el plug-in Zylin Embedded CDT, el primer paso que debe realizar es configurar las opciones de depuración. La interfaz gráfica que proporciona este plug-in para configurar las opciones de depuración de una aplicación se muestra en la figura 4.1. En la figura se puede ver que el plug-in tiene cinco pestañas distintas: Main, Debugger, Commands, Source y Common. Figura 4.1: Interfaz gráfica de Zylin Embedded CDT Si se observa detenidamente el código del plug-in, se puede apreciar que para crear una pestaña en la interfaz se necesita crear una clase que cumpla un requisito. El requisito es que la clase implemente la interfaz ILaunchConfigurationTab, o bien, que esta clase herede de alguna clase que implemente dicha interfaz. El método createTabs de la clase LaunchConfigurationTabGroup es el encargado de la creación de las pestañas de la interfaz. CAPÍTULO 4. MÉTODO DE TRABAJO 1 86 public void createTabs ( I L a u n c h C o n f i g u r a t i o n D i a l o g dialog , String mode ) { 2 MainTab main = new MainTab () ; 3 I L a u n c h C o n f i g u r a t i o n T a b [] tabs = new I L a u n c h C o n f i g u r a t i o n T a b [] { 4 main , 5 new E m be d d ed D e bu g g er T a b ( false ) , 6 new CommandTab () , 7 new SourceLookupTab () , 8 new CommonTab () }; 9 setTabs ( tabs ) ; 10 } 11 12 } Cuando se ha escogido una configuración para la depuración, el usuario pulsa el botón de depurar y se inicia el el proceso de depuración. Ası́ bien, se debe prestar atención a la parte del código que realiza todo este proceso. Analizando el código fuente del plug-in, se observa que la clase EmbeddedMIProcessAdapter es la encargada de crear el proceso del depurador GDB. En concreto, se realiza en la función getGDBProcess, la cual devuelve el proceso que ha creaodo. 1 Process getGDBProcess ( String [] args , int launchTimeout , IProgressMonitor monitor ) El significado de los parámetros de esta función son los siguientes: args: representa los argumentos que se le pasan a GDB en el momento de su ejecución. launchTimeout: representa el tiempo máximo que necesita el proceso para lanzarse. monitor: este objeto se utiliza para monitorizar el progreso de una actividad. El problema ahora consiste en saber cómo obtiene el proceso de depuración la configuración seleccionada por el usuario. La solución se encuentra en las interfaces ILaunchConfiguration y LaunchConfigurationConstants. La primera de las interfaces se utiliza para guardar y recuperar una configuración escogida. Una configuración no es ni más ni menos CAPÍTULO 4. MÉTODO DE TRABAJO 87 que una serie de atributos definidos en la interfaz LaunchConfigurationConstants. La interfaz ILaunchConfiguration es la encargada de obtener los valores de estos atributos o de establecer nuevos valores para dichos atributos. Por ejemplo, las funciones para un atributo entero son las siguientes: 1 public void setAttribute ( String attributeName , int value ) 2 public int getAttribute ( String attributeName , int defaultValue ) La clase Launch se ocupa de recuperar la configuración escogida antes de que se inicie el proceso del depurador GDB. Dicha configuración serán los argumentos con los que se ejecute GDB. En la figura 4.2 se muestra el diagrama de clases de la parte del plug-in que se acaba de analizar. Figura 4.2: Diagrama de clases de una parte del plug-in Zylin Embedded CDT CAPÍTULO 4. MÉTODO DE TRABAJO 4.2.2. 88 Construcción del diagrama de casos de uso Una vez se ha identificado el alcance del entorno cruzado de desarrollo, se procederá a la construcción de la vista funcional del sistema (véase figura 4.3, que se implementará en la forma de un diagrama de casos de uso. Se puede deducir la presencia del actor Alumno, que será la persona que utilice el entorno. Puesto que el sistema permite la depuración remota, se puede identificar el actor Otro dispositivo. Además, el sistema también permite la depuración mediante el uso de un emulador, por lo que también se puede identificar el actor DeSmuME. Se ha añadido el caso de uso Depurar mediante emulador para completar las funcionalidades ya existentes. Este nuevo caso de uso está incluido dentro de Depurar remotamente. En general, las funcionalidades o casos de uso que se han identificado en el sistema son las siguientes: Editar texto Compilar aplicación Compilar para ARM7 Compilar para ARM9 Depurar aplicación Depurar remotamente Depurar mediante emulador El caso de uso Depurar mediante emulador es el único que no se ha desarrollado. Por tanto, en los próximos apartados se llevarán a cabo las fases n 4.2.3. Fase de análisis Descripción textual de casos de uso Además de gráficamente, los casos de uso se especificarán textualmente, en documentos que expliquen de manera clara el requisito funcional que están representando. Se va a utilizar una plantilla en forma de tabla, diseñada para contener la información significativa de cada caso de uso [19]. CAPÍTULO 4. MÉTODO DE TRABAJO Figura 4.3: Vista funcional del entorno cruzado de desarrollo 89 CAPÍTULO 4. MÉTODO DE TRABAJO 90 Descripción del caso de uso Depurar mediante emulador En la figura 4.4 se muestran las posibles clases de análisis identificadas para este caso de uso. En este caso de uso intervienen dos entidades. Una se encarga de guardar la configuración de la depuración elegida por el usuario (entidad DesmumeTab). La otra entidad (Lanzador) se encarga de obtener la configuración de la depuración antes de que se ejecute el emulador DeSmuME. El controlador Proceso es el responsable de arrancar el emulador. Figura 4.4: Clases de análisis involucradas en el caso de uso Depurar mediante emulador La descripción textual se muestra en la tabla 4.1. En este caso de uso existe sólo un flujo de eventos. 4.2.4. Fase de diseño Desarrollo del caso de uso Depurar mediante emulador Una vez se ha realizado la descripción textual del caso de uso, lo siguiente será proceder a su desarrollo. Para ello, se construirá el diagrama de secuencia de análisis del flujo de eventos normal, representando ası́ el escenario de ejecución del caso de uso (véase la figura 4.5. El diálogo de configuración del proceso de depuración original del plug-in, no es suficiente para soportar el empleo de un emulador. Ası́, se debe modificar dicho diálogo agregando una nueva pestaña con opciones exclusivas para DeSmuME. Además, se deberán retocar algunas pestañas existentes para no perder ninguna funcionalidad original del plugin. En la figura 4.6 se muestra el diagrama de clases de la parte del plug-in una vez que se han realizado todos los cambios. CAPÍTULO 4. MÉTODO DE TRABAJO Nombre: Depurar mediante emulador Abstracto: No Precondiciones: No existe un proceso de depuración con DeSmuME iniciado. Postcondiciones: Se inicia un proceso de depuración con el emulador DeSmuME. Rango: 1 Flujo normal: 1. El alumno seleciona las opciones que se utilizarán para la depuración mediante DeSmuME en el diálogo Configurar depuración y pulsa el botón Depurar. Las opciones elegidas se guardan en la entidad DesmumeTab. 2. El controlador Proceso crea la entidad Lanzador, que se encarga de recuperar la configuración escogida. 3. El controlador arranca el emulador DeSmuME en un nuevo proceso. Descripción: Este caso de uso se ocupa de gestionar la depuración de una aplicación utilizando el emulador DeSmuME. Tabla 4.1: Descripción textual del caso de uso Depurar mediante emulador Figura 4.5: Diagrama de secuencia correspondiente al flujo normal 91 CAPÍTULO 4. MÉTODO DE TRABAJO Figura 4.6: Diagrama de clases modificado del plug-in Zylin Embedded CDT 92 CAPÍTULO 4. MÉTODO DE TRABAJO 4.2.5. 93 Fase de implementación Depurar mediante emulador En primer lugar, se necesita modificar la interfaz gráfica. En concreto, se deben modificar algunos aspectos de varias pestañas y agregar una nueva. En esta nueva pestaña se mostrarán las opciones que acepta DeSmuME para la depuración. Para crear la nueva pestaña se creará una nueva clase DesmumeTab, la cual herederá de la clase CLaunchConfigurationTab. El constructor de esta clase tiene un parámetro de tipo MainTab. Este parámetro será utilizado para obtener el nombre del proyecto que se va a depurar. La definición de las principales operaciones utilizadas de esta clase se muestran a continuación: 1 public DesmumeTab ( MainTab main ) 2 public void createControl ( Composite parent ) 3 public void c re a t eO p t io n D es m u me ( Composite parent , int i ) 4 public void createArm9Option ( Composite comp , int i ) 5 public void createArm7Option ( Composite comp , int i ) 6 public void setDefaults ( I L a u n c h C o n f i g u r a t i o n W o r k i n g C o p y configuration ) 7 public void initializeFrom ( I L a u n c h C o n f i g u r a t i o n configuration ) 8 public void performApply ( I L a u n c h C o n f i g u r a t i o n W o r k i n g C o p y configuration ) 9 public String getNDSFileName ( String projectpath ) 10 public String getName () 11 public String getProjectName () La utilidad de estas funciones se explica a continuación: DesmumeTab: es el contructor de la clase. createControl: esta función crea los controles de alto nivel para la pestaña DeSmuME a partir de la clase Composite pasado como parámetro. Las instancia de dicha clase son controles capaces de contener otros controles. Dentro de esta función se llama a otras especı́ficas, las cuales construyen partes especı́ficas de la pestaña: createOptionDesmume, createArm9Option y createArm7Option. setDefaults: inicializa la configuración de la pestaña con valores por defecto. initializeFrom: inicializa los controles de la pestaña con los valores obtenidos de la configuración por defecto. CAPÍTULO 4. MÉTODO DE TRABAJO 94 performApply: copia los valores de los controles de la pestaña en la configuración del lanzador. getNDSFileName: obtiene el nombre del archivo .nds que será pasado a DeSmuME como argumento. getName: devuelve el nombre de la pestaña. getProjectName: obtiene el nombre del proyecto que se depurará a partir de la instancia de tipo MainTab. Cuando se inicie el proceso de depuración, deberá estar arrancado DeSmuME. Para ello, se deberá modificar la clase EmbeddedMIProcessAdapter. Ası́, antes de que se cree el proceso del depurador GDB, se creará el proceso que inicie DeSmuME. Con el fin de realizar esta tarea se ha agregado la función desmumeProcess a dicha clase. 1 Process desmumeProcess ( String [] args , int launchTimeout , IProgressMonitor monitor ) La utilidad de cada uno de los parámetros de la función es la siguiente: args: son los argumentos con los que se ejecutará DeSmuME. launchTimeout: tiempo que necesita el proceso para lanzarse. monitor: representa un objeto que monitoriza el progreso de una actividad. Además, la función destroy EmbeddedMIProcessAdapter se ha modificado para que destruya tanto el proceso del depurador GDB como el proceso del emulador DeSmuME cuando se el usuario detenga la depuración. La clase Launch es la encargada de obtener la configuración del depurador GDB y del emulador DeSmuME. Para obtener la configuración de DeSmuME ha sido necesario agregar a esta clase las siguientes funciones: 1 public String [] getDesmumeArgs ( I L a u n c h C o n f i g u r a t i o n configuration ) ; 2 public String [] getDesmumeArgs () ; 3 public boolean getOptionDesmume () ; La primera y la segunda función se utilizan para obtener los argumentos con los que se ejecutará DeSmuME. La tercera función indica si se desea utilizar el emulador durante el proceso de depuración. CAPÍTULO 4. MÉTODO DE TRABAJO 4.2.6. 95 Pruebas A partir de los diagramas de secuencia, se pueden definir casos de prueba. Para ello, se utilizará una plantilla en forma de tabla como la siguiente: Identificación del caso de prueba: 1 Caso de uso / Flujo de eventos: Depurar mediante emulador / Flujo normal Condiciones de ejecución: El depurador no debe estar arrancado. Descripción: Se inicia el proceso de depuración utilizando el emulador DeSmuME. Resultado esperado: Se inicia DeSmueME antes de inciarse el proceso del depurador GDB. Resultado obtenido: Correcto Tabla 4.2: Caso de prueba 1, “en positivo” 4.3. Construcción del editor de fondos 4.3.1. Construcción del diagrama de casos de uso Una vez se ha identificado el alcance de la aplicación de edición de fondos, se procederá a la construcción de la vista funcional del sistema (véase figura 4.7), que se implementará en la forma de un diagrama de casos de uso. Se puede deducir la existencia del actor Alumno, que será la persona que utilice la aplicación. Dado que el sistema permite crear un fondo a partir de una imagen, se puede deducir la presencia del actor Imagen. Para que el sistema pueda crear un fondo a partir de una imagen, se necesita realizar un conversión de dicha imagen en datos “entendibles”. De ahı́ la necesidad del actor Grit, el cual genera estos datos a partir de una imagen. Además, en el sistema se permite la generación de ficheros a partir de un fondo pintado en la aplicación. Estos ficheros serán archivos de texto, los cuales pueden ajustarse al formato que el alumno elija. Respecto de los casos de uso, se han identificado las siguientes funcionalidades en el sistema: Abrir imagen CAPÍTULO 4. MÉTODO DE TRABAJO Figura 4.7: Vista funcional del editor de fondos 96 CAPÍTULO 4. MÉTODO DE TRABAJO 97 Crear fondo Crear teselado Crear extended rotoscale Crear framebuffer Modificar paleta Cambiar color Agregar color nuevo Intercambiar colores Modificar tesela Modificar fondo Generar datos NDS 4.3.2. Priorización de casos de uso Se ha establecido un orden de priorización de los casos de uso del sistema. Dicho orden atiende a la necesidad de un caso de uso para implementar otro. Con este criterio, se ha programado la siguiente ordenación: 1. Abrir imagen 2. Crear fondo 3. Crear framebuffer 4. Crear extended rotoscale 5. Crear teselado 6. Modificar paleta 7. Cambiar color 8. Agregar color nuevo 9. Intercambiar colores CAPÍTULO 4. MÉTODO DE TRABAJO 98 10. Modificar tesela 11. Modificar fondo 12. Generar datos NDS Una vez se ha realizado la ordenación de los casos de uso, se debe planificar un conjunto de iteraciones, y ubicar en cada una de ellas un conjunto de casos de uso. Asimismo, se intentará colocar los casos de uso más o menos similares en cada iteración, de manera que el fragmento de sistema que se lleve construido sea funcionalmente correcto. Con estas consideraciones, la primera versión del plan de iteraciones se muestra en la tabla 4.3: Fase Iteración Caso de uso Comienzo 1 Elaboración 2 Abrir imagen Crear fondo Crear framebuffer Crear extended rotoscale Abrir imagen Crear fondo Crear framebuffer Crear teselado Crear extended rotoscale Crear teselado Modificar paleta Cambiar color Modificar tesela Agregar color nuevo Intercambiar colores Modificar fondo Modificar paleta Cambiar color Modificar tesela Agregar color nuevo Intercambiar colores Modificar fondo Generar datos NDS Modificar fondo Generar datos NDS 3 4 5 6 Construcción 7 8 Transición 9 10 Tabla 4.3: Plan de iteraciones Flujos de trabajo R A D I P X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X CAPÍTULO 4. MÉTODO DE TRABAJO 4.3.3. 99 Iteración 1 De acuerdo con el plan de iteraciones, en esta iteración se realizará el análisis de requisitos de los casos de uso Abrir imagen, Crear fondo, Crear framebuffer y Crear extended rotoscale. Esta iteración constituye la fase de comienzo del proyecto. 4.3.3.1. Fase de análisis Descripción del caso de uso Abrir imagen La figura 4.8 muestra las posibles clases de análisis identificadas para este caso de uso. El elemento más importantes que interviene en la ejecución de este caso de uso es el controlador, que es el encargado de comprobar que el tamaño de la imagen es adecuado para el fondo que se ha seleccionado. Si la anchura o la altura, o ambas, no cumple con los requisitos del fondo, entonces se mostrará en un diálogo un mensaje de error. Este caso de uso tiene relación con el caso de uso Crear fondo. Figura 4.8: Clases de análisis involucradas en el caso de uso Abrir imagen La descripción textual se muestra en la tabla 4.4. En este caso de uso existe un flujo normal y otro alternativo. El flujo alternativo corresponde al caso en que la imagen no se ajusta con los requisitos de tamaño del fondo seleccionado. Descripción del caso de uso Crear fondo Este caso de uso es abstracto y se encarga de mostrar en la ventana un fondo de cualquiera de los tipos posibles. En este caso de uso interviene la entidad Proxy, la cual se explicará con detalle en la siguiente iteración. La figura 4.9 muestra las posibles clases de análisis identificadas para este caso de uso abstracto. La descripción textual de este caso de uso se muestra en la tabla 4.5. Descripción del caso de uso Crear framebuffer Este caso de uso no añade ninguna clase propia de análisis, pero hereda todas las clases de análisis identificadas en el caso de uso Crear fondo. La descripción textual de este caso de uso se muestra en la tabla 4.6. CAPÍTULO 4. MÉTODO DE TRABAJO 100 Nombre: Abrir imagen Abstracto: No Precondiciones: Postcondiciones: Rango: 1 Flujo normal: 1. El alumno selecciona en el diálogo una imagen y el tipo de fondo que quiere crear. 2. El diálogo le pasa al controlador del caso de uso los datos introducidos. 3. El controlador comprueba que el tamaño de la imagen es válido para el fondo seleccionado. 4. El controlador le envı́a los datos introducidos al caso de uso Crear fondo, el cual se ejecuta a continuación. Flujo alternativo 1: 1. El alumno selecciona en el diálogo una imagen y el tipo de fondo que quiere crear. 2. El diálogo le pasa al controlador del caso de uso los datos introducidos. 3. El controlador comprueba que el tamaño de la imagen no es válido para el fondo seleccionado. 4. Se muestra un mensaje de error en el diálogo. Descripción: Tabla 4.4: Descripción textual del caso de uso Abrir imagen Figura 4.9: Clases de análisis involucradas en el caso de uso Crear fondo CAPÍTULO 4. MÉTODO DE TRABAJO Nombre: Crear fondo Abstracto: Sı́ Precondiciones: Existe una imagen y un fondo seleccionados. Dicha imagen tiene la altura y la anchura acorde con el tipo de fondo elegido. Postcondiciones: 1. Se muestra en la ventana los datos del fondo según el tipo elegido. Rango: 2 Flujo normal: 1. El controlador recibe la ruta de la imagen y el tipo de fondo del controlador del caso de uso Abrir imagen. 2. El controlador envı́a al proxy el tipo de fondo y la ruta de la imagen. 3. El controlador solicita al proxy los datos del fondo. 4. El proxy le dice a la herramienta Grit que se ejecute con las opciones que ha recibido del controlador. 5. El proxy envı́a los datos al controlador. 6. El controlador crea las instancias necesarios según el fondo. 7. El controlador le pasa a la ventana los datos del fondo. 8. Se muestran en la ventana los datos del fondo. Descripción: Este caso de uso se encarga de mostrar los datos del fondo en la ventana. Estos datos se mostrarán de una manera u otra dependiendo del tipo de fondo. Tabla 4.5: Descripción textual del caso de uso Crear fondo 101 CAPÍTULO 4. MÉTODO DE TRABAJO 102 Nombre: Crear framebuffer Abstracto: No Precondiciones: Existe una imagen y un fondo seleccionados. Dicha imagen tiene la altura y la anchura acorde con fondo de tipo framebuffer. Postcondiciones: 1. Se muestra en la ventana un fondo de tipo framebuffer. Rango: 3 Flujo normal: 1. El controlador recibe la ruta de la imagen y el tipo de fondo igual a framebuffer del controlador del caso de uso Abrir imagen. 2. El controlador envı́a al proxy el tipo de fondo y la ruta de la imagen. 3. El controlador solicita al proxy los datos del fondo. 4. El proxy le dice a la herramienta Grit que se ejecute con las opciones que ha recibido del controlador. 5. El proxy envı́a los datos al controlador. 6. El controlador le pasa a la ventana los datos del fondo. 7. Se muestran en la ventana los datos del fondo framebuffer. Descripción: Este caso de uso se encarga de mostrar los datos del fondo framebuffer en la ventana. Tabla 4.6: Descripción textual del caso de uso Crear framebuffer CAPÍTULO 4. MÉTODO DE TRABAJO 103 Descripción del caso de uso Crear extended rotoscale Este caso de uso además de heredar las clases de análisis identificadas en el caso de uso Crear fondo, se utiliza la entidad Paleta (véase figura 4.10). Figura 4.10: Clases de análisis involucradas en el caso de uso Crear extended rotoscale La descripción textual se muestra en la tabla 4.7. Nombre: Crear extended rotoscale Abstracto: No Precondiciones: Existe una imagen y un fondo seleccionados. Dicha imagen tiene la altura y la anchura acorde con el fondo de tipo extended rotoscale. Postcondiciones: 1. Se muestra en la ventana un fondo de tipo extended rotoscale. Rango: 4 Flujo normal: 1. El controlador recibe la ruta de la imagen y el tipo de fondo igual a extended rotoscale del controlador del caso de uso Abrir imagen. 2. El controlador envı́a al proxy el tipo de fondo y la ruta de la imagen. 3. El controlador solicita al proxy los datos de la paleta y del fondo. 4. El proxy le dice a la herramienta Grit que se ejecute con las opciones que ha recibido del controlador. 5. El proxy envı́a los datos al controlador. 6. El controlador crea una instancia de Paleta. 7. El controlador le pasa a la ventana los datos del fondo y de la paleta. 8. Se muestran en la ventana los datos y la paleta del fondo. Descripción: Este caso de uso se encarga de mostrar los datos y la paleta del fondo extended rotoscale en la ventana. Tabla 4.7: Descripción textual del caso de uso Crear extended rotoscale CAPÍTULO 4. MÉTODO DE TRABAJO 4.3.4. 104 Iteración 2 La fase de elaboración comienza con esta iteración. Según el plan de iteraciones, en esta iteración se realizará la fase de diseño, implementación y pruebas de los casos de uso Abrir imagen, Crear fondo y Crear framebuffer. 4.3.4.1. Fase de Diseño Utilización del patrón Modelo Vista Presentador El patrón Modelo Vista Presentador (MVP) [13] es un patrón orientado a la construccion de interfaces usuario que realiza la separacion de la logica de negocios de la logica de presentacion. En el MVP, el objetivo de la vista es capturar y manejar las eventos lanzados por el usuario, los cuales se envı́an directamente al presentador, el cual sabe qué hacer con cada uno de ellos. El presentador se comunica con el modelo y se coordina con los controles de la vista para la presentación de los datos (véase figura 4.11 y 4.12). Figura 4.11: Diagrama de secuencia del patrón MVP [13] Es importante mencionar que el MVP facilita la realización de pruebas de la lógica de presentación sin la necesidad de que el usuario intervenga en los tests. El sistema actual constará de cuatro tipos de vistas diferentes, una para la presentación de la paleta, una para la presentación de todas las teselas, otra para la presentación del mapa y otra para la presentación de la tesela en edición. Cada una de las vistas tendrá una relación de generalización con la clase abstracta IVista. En cuanto al número de presentadores existentes en el sistema, habrá un presentador diferente por cada vista. Por tanto, existirán CAPÍTULO 4. MÉTODO DE TRABAJO 105 Figura 4.12: Diagrama de clases del patrón MVP [13] 4 presentadores, cada uno de los cuales heredará de la clase abstracta IPresenter. En la capa de dominio se incluirán las clases Paleta y Tesela, las cuales implementan la interfaz Objeto (véase la figura 4.13). El presente patrón es de gran utilidad para el actual sistema en desarrollo, ya que cada vez que se modifique algún elemento de la capa de dominio inmediatamente se actualizarán las vistas que se vean afectadas por dicha modificación. Se debe señalar que el presentador PresenterMap tiene una relación de asociación no navegable con la interfaz Objeto. Dependiendo del modo gráfico con el que se trabaje, dicho presentador puede trabajar con paletas (modo extended rotoscale) o con teselas (modo teselado). Desarrollo del caso de uso Abrir imagen Una vez se ha realizado la descripción textual del primer caso de uso del plan de iteraciones, lo siguiente será proceder a su desarrollo. Para ello, se construirá un diagrama de secuencia de análisis por cada flujo de eventos, representando ası́ los diferentes escenarios de ejecución del caso de uso. En la figura 4.14 se puede observar el diagrama de secuencia del flujo normal del caso de uso Abrir imagen. Nótese que en este escenario, una vez se ha comprobado que la altura y la anchura de la imagen son correctas para el fondo elegido, se pasan los datos al CAPÍTULO 4. MÉTODO DE TRABAJO 106 Figura 4.13: Construcción del diagrama de clases del sistema utilizando el patrón MVP Figura 4.14: Diagrama de secuencia correspondiente al flujo normal CAPÍTULO 4. MÉTODO DE TRABAJO 107 controlador del caso de uso Crear fondo. Figura 4.15: Diagrama de secuencia correspondiente al flujo alternativo En la figura 4.15 se muestra el diagrama de secuencia correspondiente al flujo alternativo del presente caso de uso. Una vez que el diálogo comprueba que la imagen tiene el tamaño incorrecto para el tipo de fondo elegido, se muestra un mensaje de error en otro diálogo. Desarrollo del caso de uso Crear fondo con un Builder El patrón Builder (Constructor) [10] se utiliza cuando existe un conjunto de varias jerarquı́as de herencia y debe crearse un “lote” de objetos, formado por un conjunto de especializaciones de estas clases de la jeraquı́a. Tal conjunto de jerarquı́as existe en este caso, ya que se dispone de dos clases abstractas (IModo y IVista) con varias especializaciones cada una. En función del subtipo de la clase IModo, se deben crear unas instancias u otras de la otra clase (IVista), también con el subtipo adecuado. Con este patrón, se asigna a uno de los objetos del lote la responsabilidad de crear el resto de objetos, que serán instancias de alguno de los subtipos del resto de jerarquı́as de herencia (véase la figura 4.16). Al utilizar este patrón se incluye habitualmente una clase (a la que se conoce con el nombre de Director) que se encarga de la creación del builder. Antes de dicha creación, el builder se debe instanciar al subtipo concreto. Esta tarea se lleva a cabo en la clase Cliente. Para este caso de uso sólo será preciso crear un diagrama de secuencia. En concreto, se creará para el flujo normal del caso de uso (véase la figura 4.17). En el diagrama de clases de análisis de este caso de uso se identificó una clase Controller, la cual era demasiado general. Para mantener la coherencia con los patrones utilizados, dicha clase se ha descompuesto en nuevas clases de análisis, las cuales forman un diagrama algo más complejo que el identificado en un primer término en la fase de análisis de requisitos. CAPÍTULO 4. MÉTODO DE TRABAJO Figura 4.16: Diagrama de clases utilizando el patrón Builder Figura 4.17: Diagrama de secuencia correspondiente al flujo normal 108 CAPÍTULO 4. MÉTODO DE TRABAJO 109 En el diagrama de secuencia anterior se puede observar que existe una entidad llamada Proxy. El Proxy [10] es aplicable en bastantes situaciones comunes. Una de las más habituales es utilizar este patrón como intermediario para acceder a un dispositivo externo, permitiendo controlar el acceso a él. Sin embargo, en este caso, se utilizará cómo un proxy virtual, el cual se encarga de crear objetos costosos bajo demanda. En este caso los objetos costosos podrán ser, según el tipo de fondo, los datos del mapa, los datos de la paleta y los datos de las teselas. Dichos datos serán generados por Grit. En la figura 4.18 se puede observar como serı́a la parte del sistema diseñada utilizando el patrón Proxy. Figura 4.18: Diagrama de clases utilizando el patrón Proxy Desarrollo del caso de uso Crear framebuffer Este caso de uso no tiene ningún flujo de eventos alternativo. Por tanto, sólo se creará un diagrama de secuencia de análisis, el correspondiente al flujo normal de eventos (véase figura 4.19). Al igual que en el caso de uso Crear fondo, el controlador se ha dividido en otras clases de análisis más especı́ficas con el objetivo de adaptarse a los patrones que se están utilizando. En este tipo de fondo los datos que se obtienen del proxy son únicamente los correspondientes al fondo, ya que en el fondo framebuffer no intervienen ni paletas ni teselas. 4.3.4.2. Fase de implementación La implementación se ha realizado utilizando el lenguaje de programación C++, entre otras razones, por su eficiencia, rapidez y por las liberı́as que permite utilizar para el trabajo con imágenes. Sin duda, las más importantes para este sistema son GooCanvas, GdkPixbuf, y GModule. La liberı́a GdkPixbuf [17] trabaja con “buffer de pixels” (pixbuf) y permite almacenar en memoria la información de una imagen. Ası́, las principales posibilidades que proporciona son: cargar una imagen en memoria, guardar un pixbuf en un archivo de imagen y trabajar con subgrupos de pixels de un pixbuf. CAPÍTULO 4. MÉTODO DE TRABAJO 110 Figura 4.19: Diagrama de secuencia correspondiente al flujo normal GooCanvas [32] es un canvas para GTK+ que usa la librerı́a 2D Cairo para dibujar. Entre sus caracterı́sticas destacan el manejo de eventos, los elementos básicos de los que consta y la posibilidad de utilizar el “zoom” sobre sus elementos. La liberı́a GModule [33] proporciona las funciones necesarias para la carga de objetos (también conocidos como “plug-ins”) dinámicamente. Esta liberı́a es sumamente útil para este sistema porque es la que permite la carga en memoria de los datos generados por Grit. En esta fase se explicará cómo se han implementado las partes más importantes de los casos de uso. Abrir imagen Este caso de uso se ejecuta cuando el alumno pulsa el botón Abrir en la ventana principal de la aplicación. En ese preciso momento se emite la señal siguiente y es capturada: 1 static void o n _ a b r i r B u t t o n _ c l i c k e d ( GtkToolButton * toolbutton , gpointer data ) ; Una vez que la señal se ha capturado, se realizan cuatro acciones principalmente: CAPÍTULO 4. MÉTODO DE TRABAJO 111 Se muestra un diálogo de selección de imágenes. Se muestra un diálogo de selección del tipo de fondo (véase la figura 4.20). Se comprueba si tamaño de la imagen es adecuado con el fondo elegido. En caso de ser incorrecto se muestra un mensaje de error. En otro caso se creará un nuevo fondo. Figura 4.20: Diálogo de selección del tipo de fondo Cuando se crea un nuevo fondo, el controlador de este caso de uso (clase Cliente) se deberá comunicar con el controlador del caso de uso Crear fondo (clase Director). Todo esto se realiza creando un objeto de la clase Cliente, cuyo constructor tiene la siguiente forma: 1 Cliente ( Parametros & args ) ; El constructor recibe como único argumento una estructura llamada Parametros. Esta estructura se ha definido de la siguiente forma: 1 typedef struct { 2 std :: string filename ; 3 std :: string modo ; 4 } Parametros ; La estructura Parametros tiene dos campos: uno que se utiliza para almacenar la ruta una la imagen y otro que guarda el tipo de fondo que se quiere crear. El código del constructor de la clase Cliente es muy simple y es siguiente: CAPÍTULO 4. MÉTODO DE TRABAJO 1 Cliente :: Cliente ( Parametros & args ) 2 { 3 this - > director = new Director ; 4 new_window ( args ) ; 5 112 } La función new window es la responsable de crear una nueva ventana, la cual será de un tipo u otro dependiendo del valor del argumento que se le pasa como parámetro. 1 void new_window ( Parametros & args ) ; Crear fondo Como se ha comentado anteriormente, este caso de uso se ha construido utilizando el patrón Builder. La construcción del builder tiene lugar en la operación new window de la clase Cliente, la cual tiene como argumento único la dirección de una estructura de tipo Parametros: 1 void new_window ( Parametros & args ) ; En esta operación se instancia el builder al subtipo concreto y, posteriormente, la clase Director se encarga de su creación. La parte del código que se encarga de la instanciación al subtipo concreto es la siguiente: 1 ... 2 if ( ! args . modo . compare ( " teselado16 " ) ) { // teselado 16 colores builder = new ModoTeselado ( args . filename , 16) ; 3 4 } 5 else if ( ! args . modo . compare ( " teselado256 " ) ) { // teselado 256 colores builder = new ModoTeselado ( args . filename , 256) ; 6 7 } 8 else if ( ! args . modo . compare ( " framebuffer " ) ) { // framebuffer 9 builder = new ModoFramebuffer ( args . filename ) ; CAPÍTULO 4. MÉTODO DE TRABAJO 10 } 11 else if ( ! args . modo . compare ( " rotoscale " ) ) { // extended 113 rotoscale builder = new ModoRotoscale ( args . filename ) ; 12 13 } else { g_error ( " Error : modo %s desconocido \ n " , args . filename . 14 c_str () ) ; 15 } 16 ... Todos los subtipos de la clase IModo tienen como primer argumento una cadena (std::string) en la que se especifica la ruta de una imagen. Además, el modo teselado especifica un segundo argumento de tipo entero en el cual se indica el número de colores utilizados para dibujar cada tesela (16 o 256). La operación de creación se encarga de la construcción de cada una de las vistas y se ha implementado de la siguiente forma: 1 void Director :: construir () 2 { 3 this - > builder - > build_vpaleta () ; 4 this - > builder - > build_vteselas () ; 5 this - > builder - > build_vmapa () ; 6 this - > builder - > build_veditar () ; 7 } Se debe señalar que cada una de las funciones de creación de las vistas (build vpaleta, build vteselas, build vmapa y build veditar), se redefine en cada uno de los subtipos de la clase IModo. Todo este proceso que se ha descrito, se realiza durante la creación de cualquier tipo de fondo. Las diferencias surgen en la creación de cada una de las vistas, ya que cada modo tiene su propia idiosincrasia. La explicación de estas diferencias se realizarán durante la implementación de cada uno los tipos de fondo existentes. 1 void PresenterMap :: d ib u j ar _ f ra m e bu f f er () 2 { vistas [ " map " ] - > update (0 , 0 , this - > pixbuf ) ; 3 4 } CAPÍTULO 4. MÉTODO DE TRABAJO 114 La función update se ha sobrecargado en la clase IVista. En concreto, la función del código anterior se utiliza para mostrar el pixbuf pasado como argumento en la posición (x,y) del mapa. Su definición es la siguiente: 1 void update ( int x , int y , GdkPixbuf * pixbuf ) ; Crear framebuffer Este caso de uso hereda la implementación del builder del caso de uso Crear fondo. Por la propia naturaleza de este tipo de fondo, sólo se necesita redefinir el método build vmapa, el cual se muestra a continuación: 1 void ModoFramebuffer :: build_vmapa () 2 { IPresenter * pres = new PresenterMap ( filename , " 3 framebuffer " ) ; VistaMapa * vmapa = new VistaMapa ( pres , xml , filename , " 4 framebuffer " ) ; pres - > subscribe ( " map " , vmapa ) ; 5 6 if ( filename . size () ) { 7 vmapa - > cargar ( proxy - > get_datos_mapa () ) ; 8 } 9 10 11 presenter [ " map " ] = pres ; 12 vistas [ " map " ] = vmapa ; 13 } Los métodos de construcción de vistas (build vpaleta, build vteselas, build vmapa y build veditar) que son redefinidos por cada tipo de fondo, tienen la misma estructura. Cada uno de estos métodos realiza las tres acciones siguientes: En primer lugar, se crea el presentador y la vista con el subtipo adecuado de IPresenter y de IVista, respectivamente. Una vez construidos, el presentador se encarga de registrar (subscribe) la vista o vistas que quieren ser informadas cuando haya algún cambio en el modelo. CAPÍTULO 4. MÉTODO DE TRABAJO 115 En segundo lugar, el proxy se encarga de obtener los datos del mapa (get datos mapa), los cuales han sido generados por Grit. En tercer lugar, la vista se ocupa de iniciar la carga de los datos que obtiene del proxy. El proxy se comporta como un intermediario entre la vista y grit. Su funcionamiento, básicamente, consiste en solicitar datos a grit e ir devolviéndoselos a la vista. Tanto el proxy (clase GritProxy) como grit (clase Grit) heredan la misma interfaz (IProxy), la cual publica las principales operaciones siguientes: 1 std :: vector < unsigned int > get_datos_paleta () ; 2 std :: vector < unsigned int > get_datos_mapa () ; 3 std :: vector < unsigned int > g et_dat os_tes elas () ; 4 void convertir ( std :: string imagen , std :: string optn , std :: string output , std :: string header ) 5 ; Las tres primeras operaciones se encargan de solicitar datos, mientras que la última se utiliza para la conversión de un fondo en archivos de texto. Se explicará con detalle esta última operación durante la implementación del caso de uso Generar datos NDS. El proceso de solicitud de los datos del mapa (get datos mapa) es muy sencillo. En primer lugar, se comprueba si se ha instanciado el objeto grit. En caso negativo, se instancia llamando al constructor apropiado. Por último, se llama a la operación get datos mapa de dicho objeto. 1 std :: vector < unsigned int > GritProxy :: get_datos_mapa () 2 { if (! grit ) { 3 if (! modo . compare ( " framebuffer " ) ) 4 grit = new Grit ( filename , modo ) ; 5 else if (! modo . compare ( " rotoscale " ) ) 6 grit = new Grit ( filename , modo , 256) ; 7 else 8 grit = new Grit ( filename , modo , 9 n_colores ) ; 10 } 11 return grit - > get_datos_mapa () ; 12 } CAPÍTULO 4. MÉTODO DE TRABAJO 116 La clase Grit es la encargada de realizar la ejecución de la herramienta Grit y de cargar en memoria los datos de los archivos que se generan tras la ejecución (liberı́a GModule). Estos datos son simplemente vectores de enteros sin signo (unsigned int). En cuanto a la carga de datos por parte de la vista, se debe aclarar que la vista en sı́ misma no carga los datos. El proceso es algo más complejo. Como se está utilizando el patrón MVP, la vista únicamente se encarga de lo relacionado con la interfaz gráfica. Para tratar con el modelo ya está el presentador (clase IPresenter). De este modo, la vista se ocupa de realizar las llamadas al presentador, que es quien creará o modificará los objetos del modelo (teselas o paletas). 1 void VistaMapa :: cargar ( std :: vector < unsigned int > datos ) 2 { 3 ... 4 this - > pres - > cargar ( datos ) ; 5 } La función cargar del presentador no crea ninguna instancia del modelo, ya que no se utilizan ni paletas ni teselas. La definición de esta función de la clase IPresenter es la siguiente: 1 void cargar ( std :: vector < unsigned int > datos ) ; En esta función se crea una variable de tipo GdkPixbuf a partir del archivo de la imagen y le indica a la vista del mapa que se actualice por medio de la función: 1 void update ( int x , int y , GdkPixbuf * pixbuf ) ; 4.3.4.3. Pruebas A partir de los diagrama de secuencias, se puede definir casos de prueba. Para ello, se utilizará una plantilla en forma de tabla como la siguiente: 4.3.5. Iteración 3 En esta iteración se realizará la fase de análisis de requisitos del caso de uso Crear teselado. De acuerdo con el plan de iteraciones, también se realizará la fase de diseño, la fase de implementación y la fase de pruebas del caso de uso Crear extended rotoscale. CAPÍTULO 4. MÉTODO DE TRABAJO Identificación del caso de prueba: 1 Caso de uso / Flujo de eventos: Abrir imagen / Flujo normal Condiciones de ejecución: Descripción: El alumno indica en la ventana la ruta de una imagen y el tipo de fondo que se creará a partir de la imagen. Resultado esperado: La altura y la anchura de la imagen son aceptados por el tipo de fondo seleccionado. Resultado obtenido: Correcto Tabla 4.8: Caso de prueba 1, “en positivo” Identificación del caso de prueba: 2 Caso de uso / Flujo de eventos: Abrir imagen / Flujo alternativo Condiciones de ejecución: Descripción: El alumno indica en la ventana la ruta de una imagen y el tipo de fondo que se creará a partir de la imagen. Resultado esperado: Se muestra un diálogo de error en el que se indica que el tamaño de la imagen no es correcto para el tipo de fondo seleccionado. Resultado obtenido: Correcto Tabla 4.9: Caso de prueba 2, “en negativo” Identificación del caso de prueba: 3 Caso de uso / Flujo de eventos: Crear fondo / Flujo normal Condiciones de ejecución: El alumno ha seleccionado una imagen y un tipo de fondo adecuados. Descripción: Se crean los datos de un fondo a partir de la imagen y el tipo elegidos. Resultado esperado: Se muestran los datos de un fondo en la ventana. Resultado obtenido: Correcto Tabla 4.10: Caso de prueba 3, “en positivo” 117 CAPÍTULO 4. MÉTODO DE TRABAJO 118 Identificación del caso de prueba: 4 Caso de uso / Flujo de eventos: Crear framebuffer / Flujo normal Condiciones de ejecución: El alumno ha seleccionado una imagen con un tamaño adecuado para un fondo de tipo framebuffer. Descripción: Se crean los datos de un fondo framebuffer a partir de la imagen y el tipo elegidos. Resultado esperado: Se muestran los datos de un fondo framebuffer en la ventana. Resultado obtenido: Correcto Tabla 4.11: Caso de prueba 4, “en positivo” 4.3.5.1. Fase de análisis Descripción del caso de uso Crear teselado Este caso de uso hereda las clases de análisis identificados en el caso de uso Crear fondo y, además, agrega las entidades Tesela y Paleta (véase figura 4.21). Figura 4.21: Clases de análisis involucradas en el caso de uso Crear teselado La descripción textual se muestra en la tabla 4.12. 4.3.5.2. Fase de diseño Desarrollo del caso de uso Crear extended rotoscale En este caso de uso sólo se contempla un único flujo de eventos. Por tanto, sólo habrá un diagrama de secuencia de análisis para este caso de uso (véase figura 4.22). Al igual que en el caso de uso Crear fondo, el controlador se ha dividido en otras clases de análisis más especı́ficas con el objetivo de adaptarse a los patrones que se están utilizando. En el diagrama de secuencia anterior se ha representado un bucle que realiza dos iteraciones. Esto es debido a que en primer lugar se recogen los datos de la paleta que se han generado y, posteriormente, se obtienen los datos correspondientes al fondo. Cada iteración CAPÍTULO 4. MÉTODO DE TRABAJO Nombre: Crear teselado Abstracto: No Precondiciones: Existe una imagen y un fondo seleccionados. Dicha imagen tiene la altura y la anchura acorde con el fondo de tipo teselado. Postcondiciones: 1. Se muestra en la ventana un fondo de tipo teselado. Rango: 5 Flujo normal: 1. El controlador recibe la ruta de la imagen y el tipo de fondo igual a teselado del controlador del caso de uso Abrir imagen. 2. El controlador envı́a al proxy el tipo de fondo y la ruta de la imagen. 3. El controlador solicita al proxy los datos de la paleta y del fondo. 4. El proxy le dice a la herramienta Grit que se ejecute con las opciones que ha recibido del controlador. 5. El proxy envı́a los datos al controlador. 6. El controlador crea una instancia de Paleta y crea un conjunto de instancias de Tesela. 7. El controlador le pasa a la ventana los datos del fondo, de la paleta y de las teselas. 8. Se muestran en la ventana los datos, la paleta y las teselas del fondo. Descripción: Este caso de uso se encarga de mostrar la paleta, las teselas y los datos del fondo teselado en la ventana. Tabla 4.12: Descripción textual del caso de uso Crear teselado 119 CAPÍTULO 4. MÉTODO DE TRABAJO 120 Figura 4.22: Diagrama de secuencia correspondiente al flujo normal del bucle empieza con la obtención de los datos y concluye cuando la vista correspondiente le indica a la ventana que se actualice. 4.3.5.3. Fase de implementación Crear extended rotoscale En los fondos de rotación extendida se empieza a utilizar la paleta de colores. Por este motivo, el proceso de construcción se complica ligeramente en comparación con los fondos framebuffer. En el subtipo de la clase IModo correspondiente a los fondos de rotación extendida (clase ModoRotoscale), se deben redefinir los métodos encargados de construir la vista del mapa y la vista de la paleta (build vmapa y build vpaleta). Ambos métodos tienen la misma estructura, aunque obviamente cada uno se instancia con un subtipo distinto. En primer lugar, se construye la vista de la paleta y, posteriormente, la vista del fondo. La estructura es la siguiente: Primero se crea el presentador y la vista con el subtipo adecuado de IPresenter y de IVista, respectivamente. Una vez creados, el presentador se encarga de registrar (subscribe) las vistas que quieren ser informadas cuando haya algún cambio en el modelo (en la paleta, en este caso). CAPÍTULO 4. MÉTODO DE TRABAJO 121 El proxy se encarga de obtener los datos que se le soliciten. En este caso, se deben obtener los datos de la paleta y del fondo (get datos paleta y get datos mapa). Estos datos son generados por la herramienta Grit y se almacenan en memoria como un vector de enteros sin signo. La vista utiliza estos datos para transmitirlos al presentador, el cual crea aquellos objetos del modelo que correspondan. Una vez definidos los objetos del modelo, el presentador emite una actualización a cada una de las vistas que se hayan suscrito al servicio de actualizaciones. La novedad de este tipo de fondos con respecto a un fondo framebuffer reside en la paleta de colores. La vista se encarga de decirle al presentador que cargue la paleta. Para ello, se utiliza la función cargar. Esta función se ha sobrecargado en la clase IPresenter. El código de esta función es el siguiente: 1 void PresenterPal :: cargar ( std :: vector < unsigned int > datos , int ncolores ) 2 { 3 this - > paleta = new Paleta ( ncolores ) ; 4 this - > paleta - > crear ( datos ) ; 5 for ( unsigned int i = 0; i < this - > paleta - > get_data () . 6 size () ; i ++) { 7 int x = i % 8; 8 int y = (i - x ) /8; 9 this - > vistas [ " pal " ] - > update (x , y , this - > paleta - > get_data () . at ( i ) ) ; } 10 11 } El primer argumento corresponde a los datos de la paleta, mientras que el segundo argumento indica si la paleta es de 16 o de 256 colores. Una paleta en el sistema no es ni más ni menos que un vector de enteros sin signo, donde cada elemento corresponde a un color de la misma en formato RGB de 32 bits. CAPÍTULO 4. MÉTODO DE TRABAJO 122 Identificación del caso de prueba: 5 Caso de uso / Flujo de eventos: Crear extended rotoscale / Flujo normal Condiciones de ejecución: El alumno ha seleccionado una imagen con un tamaño adecuado para un fondo de tipo extended rotoscale. Descripción: Se crean los datos del fondo y de la paleta a partir de la imagen y el tipo elegidos. Resultado esperado: Se muestran el fondo y la paleta de un fondo extended rotoscale en la ventana. Resultado obtenido: Correcto Tabla 4.13: Caso de prueba 5, “en positivo” 4.3.5.4. Pruebas 4.3.6. Iteración 4 En esta iteración se realizará la fase de diseño, la fase de implementación y la fase de pruebas del caso de uso Crear teselado, según se indica en el plan de iteraciones. 4.3.6.1. Fase de diseño Desarrollo del caso de uso Crear teselado Este caso de uso tiene un único flujo de eventos y, por lo tanto, se realizará sólo un diagrama de secuencia de análisis (véase la figura 4.23). En el diagrama se puede observar que se ha utilizado un bucle, el cual se repite 3 veces. Ası́ bien, en cada iteración del bucle se pretende representar la obtención de los datos correspondientes a cada uno de los 3 elementos del fondo teselado. Estos elementos son, en orden de obtención, la paleta, el conjunto de teselas y el fondo o mapa. Por tanto, la primera iteración comienza con la recogida de los datos de la paleta y termina con la presentación de los mismos en la ventana. La segunda iteración empieza con la obtención de los datos del conjunto de teselas del fondo y termina cuando la vista le indica a la ventana que se actualice. La tercera y última iteración se inicia con la obtención de los datos del fondo y finaliza cuando la vista le pasa a la ventana el fondo que debe mostrar. Al igual que en el caso de uso Crear fondo, el controlador se ha dividido en otras clases de análisis más especı́ficas con el objetivo de adaptarse a los patrones que se están utilizando. CAPÍTULO 4. MÉTODO DE TRABAJO 123 Figura 4.23: Diagrama de secuencia correspondiente al flujo normal 4.3.6.2. Fase de implementación Crear teselado El proceso de construcción de un fondo teselado es más complicado que el proceso de construcción de un fondo framebuffer o de un fondo de rotación extendida. El modelo de este tipo de fondo lo constituyen tanto la paleta de colores como un conjunto de teselas. Ası́, en el subtipo de la clase IModo correspondiente a los fondos teselados (clase ModoTeselado), se deben redefinir todos los métodos de construcción de las vistas, es decir, el método de construcción de la vista de la paleta (build vpaleta), el de la vista de las teselas (build vteselas), el de la vista del mapa (build vmapa) y el de la vista de edición de cada tesela (build veditar). Todos estos métodos tienen la misma estructura, aunque obviamente cada uno se instancia con un subtipo distinto. La estructura es la siguiente: Primero se crea el presentador y la vista con el subtipo adecuado de IPresenter y de IVista, respectivamente. Una vez creados, el presentador se encarga de registrar (subscribe) las vistas que quieren ser informadas cuando haya algún cambio en el modelo (en la paleta, en este caso). El proxy se encarga de obtener los datos que se le soliciten. En este caso, se deben obtener los datos de la paleta, del conjunto de teselas y del fondo (get datos paleta, CAPÍTULO 4. MÉTODO DE TRABAJO 124 get datos teselas y get datos mapa). Estos datos son generados por la herramienta Grit y se almacenan en memoria como un vector de enteros sin signo. La vista utiliza estos datos para transmitirlos al presentador, el cual crea aquellos objetos del modelo que correspondan. Una vez definidos los objetos del modelo, el presentador emite una actualización a cada una de las vistas que se hayan suscrito al servicio de actualizaciones. La novedad de este tipo de fondo es la introducción de las teselas. Esto introduce la vista y el presentador de teselas. La vista llama a la función cargar del presentador, la cual crea las teselas del fondo a partir de un vector de enteros sin signo. La definición de esta función, sobrecargada en la clase IPresenter, es la siguiente: 1 void cargar ( std :: vector < unsigned int > datos ) ; A la hora de mostrar el fondo teselado, la vista del mapa llama al método cargar del presentador, el cual tiene un vector de enteros sin signo como único argumento que representa los datos del mapa. Este método llama a la función crear teselado del presentador se encarga de comunicar a la vista la tesela que tiene ir dibujando en cada posición. Ası́, para cada elemento del mapa se obtiene el número de tesela, si la tesela se dibuja utilizando algún tipo de espejo y el número de paleta. Esto último sólo es necesario para saber el número de la subpaleta que utiliza una tesela de 16 colores, ya que en las teselas de 256 colores la subpaleta siempre será la número 0. 4.3.6.3. Pruebas 4.3.7. Iteración 5 Según el plan de iteraciones, en la iteración 5 se realizará la fase de análisis de los casos de uso siguientes: Modificar paleta, Cambiar color y Modificar tesela. 4.3.7.1. Fase de análisis Descripción del caso de uso Modificar paleta La ejecución de este caso de uso supone la modificación de la paleta del fondo actual, ya sea un fondo teselado o un fondo extended rotoscale. Las posibles clases de análisis identificadas en este caso de uso abstracto se pueden ver en la figura 4.24. La descripción textual se muestra en la tabla 4.15. CAPÍTULO 4. MÉTODO DE TRABAJO 125 Identificación del caso de prueba: 6 Caso de uso / Flujo de eventos: Crear teselado / Flujo normal Condiciones de ejecución: El alumno ha seleccionado una imagen con un tamaño adecuado para un fondo de tipo teselado. Descripción: Se crean la paleta, las teselas y los datos de un fondo teselado a partir de la imagen y el tipo elegidos. Resultado esperado: Se muestran la paleta, las teselas y los datos de un fondo teselado en la ventana. Resultado obtenido: Correcto Tabla 4.14: Caso de prueba 6, “en positivo” Figura 4.24: Clases de análisis involucradas en el caso de uso Modificar paleta CAPÍTULO 4. MÉTODO DE TRABAJO Nombre: Modificar paleta Abstracto: Sı́ Precondiciones: En el sistema se debe haber creado un fondo que soporte el trabajo con paletas, es decir, cualquier tipo de fondo excepto el framebuffer. Postcondiciones: La paleta de colores del fondo se ha modificado. Rango: 6 Flujo normal: 1. El alumno pulsa el botón reorganizar paleta en la ventana, la cual muestra el diálogo de reorganizar la paleta. 2. El alumno selecciona una operación a realizar sobre la paleta. 3. El diálogo le envı́a a la vista la operación seleccionada y los datos necesarios para llevarla a cabo. 4. La vista le pasa al presentador los datos y le indica que se actualice. 5. El presentador actualiza la paleta y le pide a todas las vistas afectadas que se actualicen. Descripción: En este caso de uso se modifica la paleta de colores de un fondo existente. Tabla 4.15: Descripción textual del caso de uso Modificar paleta 126 CAPÍTULO 4. MÉTODO DE TRABAJO 127 Descripción del caso de uso Cambiar color Este caso de uso modifica un color de la paleta por el color que seleccione el alumno. Las posibles clases de análisis identificadas en este caso de uso abstracto se pueden ver en la figura 4.25. Figura 4.25: Clases de análisis involucradas en el caso de uso Cambiar color La descripción textual se muestra en la tabla 4.16. Nombre: Cambiar color Abstracto: No Precondiciones: En el sistema se debe haber creado un fondo que soporte el trabajo con paletas, es decir, cualquier tipo de fondo excepto el framebuffer. Postcondiciones: Se ha cambiado el color de un elemento de la paleta de colores. Rango: 7 Flujo normal: 1. El alumno pulsa el botón reorganizar paleta en la ventana, la cual muestra el diálogo de reorganizar la paleta. 2. El alumno selecciona un color de la paleta y pulsa el botón editar color en el diálogo, el cual muestra un diálogo de selección de color. 3. El alumno selecciona un color en el diálogo de selección de color. 4. La vista obtiene el nuevo color seleccionado. 5. La vista le pasa al presentador la posición que ocupa en la paleta el color afectado y el nuevo color, y le indica que se actualice. 6. El presentador actualiza la paleta y le pide a todas las vistas afectadas que se actualicen. Descripción: En este caso de uso se cambia el color de un elemento de la paleta de colores. Tabla 4.16: Descripción textual del caso de uso Cambiar color CAPÍTULO 4. MÉTODO DE TRABAJO 128 Descripción del caso de uso Modificar tesela Este caso de uso modifica una tesela de un fondo teselado. El alumno selecciona una tesela, un pixel de la misma y el color de la paleta que quiere que aparezca en la tesela. Las posibles clases de análisis identificadas se pueden ver en la figura 4.26. Figura 4.26: Clases de análisis involucradas en el caso de uso Modificar tesela Este caso de uso presenta un sólo flujo de eventos. La descripción textual se muestra en la tabla 4.17. Nombre: Modificar tesela Abstracto: No Precondiciones: En el sistema se debe haber creado un fondo teselado. Postcondiciones: Rango: 8 Flujo normal: 1. El alumno selecciona en la ventana una tesela del conjunto de teselas, un color de la paleta y un pixel de la tesela. 2. La ventana le pasa a la vista los datos seleccionados. 3. La vista le indica al presentador que se actualice. 4. El presentador actualiza la tesela seleccionada del conjunto y propaga a las vistas afectadas la actualización. Descripción: Tabla 4.17: Descripción textual del caso de uso Modificar tesela 4.3.8. Iteración 6 En la presente iteración se realizará la fase de análisis de los casos de uso Agregar color nuevo, Intercambiar colores y Modificar fondo. CAPÍTULO 4. MÉTODO DE TRABAJO 4.3.8.1. 129 Fase de análisis Descripción del caso de uso Agregar color nuevo Este caso de uso presenta un flujo normal y un flujo alternativo. El flujo normal corresponde a la situación en la que se añade un color a la paleta, mientras que el flujo alternativo ocurre cuando ya no se pueden agregar más colores a la paleta. Las posibles clases de análisis identificadas se pueden ver en la figura 4.27. Figura 4.27: Clases de análisis involucradas en el caso de uso Agregar color nuevo La descripción textual del caso de uso se muestra en la tabla 4.18. Descripción del caso de uso Intercambiar colores Este caso de uso intercambia la posición de dos colores de la misma paleta. Será especialmente útlil cuando se desee cambiar el elemento de la paleta que ocupa la posición del color transparente (el primer color de la paleta es transparente). Las posibles clases de análisis identificadas se pueden ver en la figura 4.28. Figura 4.28: Clases de análisis involucradas en el caso de uso Intercambiar colores La descripción textual del caso de uso se muestra en la tabla 4.19. Descripción del caso de uso Modificar fondo Este caso de uso se ejecuta cuando el alumno personaliza un fondo teselado de 16 o 256 colores, mediante la sustitución de una o varias teselas del fondo. Las posibles clases de análisis identificadas se pueden ver en la figura 4.29. La descripción textual del caso de uso se muestra en la tabla 4.20. CAPÍTULO 4. MÉTODO DE TRABAJO 130 Nombre: Agregar color nuevo Abstracto: No Precondiciones: En el sistema se debe haber creado un fondo teselado o extended rotoscale. Postcondiciones: Rango: 9 Flujo normal: 1. El alumno pulsa el botón reorganizar paleta en la ventana, la cual muestra el diálogo de reorganizar la paleta. 2. El alumno pulsa el botón agregar color en el diálogo. 3. La vista comprueba que existe suficente espacio en la paleta para agregar un nuevo color. 4. La vista muestra un diálogo de selección de color. 5. El alumno selecciona un color en el diálogo. 6. La vista recoge el color seleccionado. 7. La vista le pasa al presentador el nuevo color de la paleta. 8. El presentador actualiza la paleta y propaga las actualizaciones por las vistas afectadas. Flujo alternativo: 1. El alumno pulsa el botón reorganizar paleta en la ventana, la cual muestra el diálogo de reorganizar la paleta. 2. El alumno pulsa el botón agregar color en el diálogo. 3. La vista comprueba que no existe suficente espacio en la paleta para agregar un nuevo color. 4. La vista muestra un mensaje de error en un nuevo diálogo. Descripción: Tabla 4.18: Descripción textual del caso de uso Agregar color nuevo Figura 4.29: Clases de análisis involucradas en el caso de uso Modificar fondo CAPÍTULO 4. MÉTODO DE TRABAJO Nombre: Intercambiar colores Abstracto: No Precondiciones: En el sistema se debe haber creado un fondo teselado o extended rotoscale. Postcondiciones: En la paleta de colores del fondo se han intercambiado dos colores. Rango: 10 Flujo normal: 1. El alumno pulsa el botón reorganizar paleta en la ventana, la cual muestra el diálogo de reorganizar la paleta. 2. El alumno mueve el color seleccionado hasta la posición de otro color de la paleta, los cuales serán los elementos que se intercambien. 3. El diálogo envı́a a la vista la posición y el color de cada uno de los dos elementos. 4. La vista le pasa al presentador los datos de los colores involucrados en el intercambio. 5. El presentador comprueba si alguno de los dos elementos ocupa la posición del color transparente. En caso afirmativo, se actualiza la paleta. Por último, el presentador propaga las actualizaciones a las vistas interesadas. Descripción: En este caso de uso se intercambia la posición de dos colores de la paleta del fondo. Tabla 4.19: Descripción textual del caso de uso Intecambiar colores 131 CAPÍTULO 4. MÉTODO DE TRABAJO 132 Nombre: Modificar fondo Abstracto: No Precondiciones: En el sistema se debe haber creado un fondo teselado. Postcondiciones: El fondo se ha modificado por medio de la sustitución de una o varias teselas. Rango: 11 Flujo normal: 1. El alumno selecciona una tesela del conjunto de teselas en la ventana. Previamente, el alumno habrá elegido si quiere utilizar un tipo de espejo para la tesela. 2. El alumno hace click en una posición del fondo en la ventana y selecciona un conjunto de teselas. 3. La ventana le dice a la vista la posición de inicio y final que se ha seleccionado. 4. La vista le pide al presentador que le envı́e la tesela seleccionada, el cual cumple con la petición. 5. La vista le pasa al presentador la tesela y las posiciones del mapa en las que se debe colocar a dicha tesela. 6. El presentador actualiza los datos del fondo y le indica a la vista del mapa que se actualice. Descripción: En este caso de uso se reemplazan una o varias teselas del fondo. Tabla 4.20: Descripción textual del caso de uso Modificar fondo CAPÍTULO 4. MÉTODO DE TRABAJO 4.3.9. 133 Iteración 7 En esta iteración se inicia la fase de construcción del sistema. Durante el transcurso de la iteración se realizará la fase de diseño, la fase de implementación y la fase de pruebas de los casos de uso Modificar paleta, Cambiar color y Modificar tesela. 4.3.9.1. Fase de diseño Desarrollo del caso de uso Modificar paleta En este caso de uso sólo se contempla un único flujo de eventos. Por tanto, sólo habrá un diagrama de secuencia de análisis para este caso de uso (véase figura 4.30). En el diagrama se puede observar que cuando se produce cualquier operación que modifique la paleta, la vista le indica al presentador la actualización que debe realizar. Dicho presentador se encarga de actualizar la paleta (modelo) y de pedirle a todas las vistas con las que tenga relación que se actualicen. Esto se representa en el diagrama con un bucle, el cual se repite un número de veces igual al número de elementos del vector v (IVista). Figura 4.30: Diagrama de secuencia correspondiente al flujo normal Desarrollo del caso de uso Cambiar color En este caso de uso sólo se contempla un único flujo de eventos. Por tanto, sólo habrá un diagrama de secuencia de análisis para este caso de uso (véase figura 4.31). CAPÍTULO 4. MÉTODO DE TRABAJO 134 Figura 4.31: Diagrama de secuencia correspondiente al flujo normal Desarrollo del caso de uso Modificar tesela En este caso de uso contempla un flujo de eventos normal y, por tanto, habrá un diagrama de secuencia de análisis para este caso de uso (véase la figura 4.32). En el diagrama se puede observar que el presentador primero actualiza la tesela y, posteriormente, le indica a cada una de las cuatro vistas (la vista de la paleta, la vista del conjunto de teselas, la vista del mapa y la vista de edición de la tesela) que se actualicen. 4.3.9.2. Fase de implementación Modificar paleta Cuando se pulse el botón Reorganizar paleta de la ventana, se emite una señal que es capturada por la vista de la paleta. La función reorganizar button clicked captura esta señal y se encarga de mostrar el diálogo que permite la modificación de la paleta (véase la figura 4.33). Pero sin duda, la tarea más importante de esta función es detectar el botón del diálogo que se pulsa y, en función del botón pulsado, llamará a la función update con unos valores u otros. Se debe recordar que en este diálogo tendrán lugar las operaciones que pueden modificar una paleta de colores: editar un color, agregar un color nuevo o intercambiar dos colores. El código de la función update del presentador de la paleta utilizada para actualizar CAPÍTULO 4. MÉTODO DE TRABAJO Figura 4.32: Diagrama de secuencia correspondiente al flujo normal Figura 4.33: Diálogo Reorganizar paleta 135 CAPÍTULO 4. MÉTODO DE TRABAJO 136 la misma es el siguiente: 1 void PresenterPal :: update ( int pos_paleta , unsigned int color ) 2 { 3 int x = pos_paleta % 8; 4 int y = ( pos_paleta - x ) /8; 5 this - > paleta - > update ( pos_paleta , color ) ; 6 this - > vistas [ " pal " ] - > update (x , y , color ) ; 7 if ( this - > vistas [ " tiles " ]) { 8 this - > vistas [ " tiles " ] - > update ( pos_paleta , 9 color ) ; 10 } 11 if ( this - > vistas [ " edit " ]) { this - > vistas [ " edit " ] - > update ( pos_paleta , color 12 ); 13 } 14 this - > vistas [ " map " ] - > update ( pos_paleta ) ; 15 } Esta función realiza en primer lugar la modificación de la paleta. Tras realizar el cambio, se desencadena una propagación de llamadas a cada una de las vistas suscritas al servicio de actualizaciones para que se actualicen. Cambiar color La función reorganizar button clicked se ocupa de detectar si se ha pulsado el botón Editar color en el diálogo Reorganizar paleta. Cuando esto sucede, la función pick color de la vista de la paleta muestra un diálogo de selección de color (véase la figura 4.34). En este diálogo se selecciona el color que reemplazará al color seleccionado existente. Una vez elegido el nuevo color, el presentador realiza en primer lugar la modificación de la paleta. Para ello, llama al método update de la paleta, el cual cambia el color de la posición pos paleta por el color color. Este método se ha implementado ası́: 1 void Paleta :: update ( int pos_paleta , unsigned int color ) 2 { 3 4 if ( pos_paleta < ( int ) this - > data . size () ) this - > data . at ( pos_paleta ) = color ; CAPÍTULO 4. MÉTODO DE TRABAJO 137 Figura 4.34: Diálogo de selección de color else 5 this - > data . push_back ( color ) ; 6 7 } Como se ha explicado en la implementación del caso de uso Modificar paleta, tras realizar un cambio en la paleta, se desencadena una propagación de llamadas a cada una de las vistas suscritas al servicio de actualizaciones para que se actualicen. Modificar tesela Cuando se selecciona un pixel de una tesela en la vista de edición, se emite una señal que es capturada por la función change pixel. 4.3.9.3. 4.3.10. Pruebas Iteración 8 Durante esta iteración se realizará la fase de diseño, la fase de implementación y la fase de pruebas de los casos de uso Agregar color nuevo, Intercambiar colores y Modificar fondo, según el plan de iteraciones. CAPÍTULO 4. MÉTODO DE TRABAJO 138 Identificación del caso de prueba: 7 Caso de uso / Flujo de eventos: Modificar paleta / Flujo normal Condiciones de ejecución: En el sistema se debe haber creado un fondo que soporte el trabajo con paletas. Descripción: Se realiza un cambio en la paleta de colores del fondo actual. Resultado esperado: Se muestra en la ventana el cambio realizado en la paleta. Resultado obtenido: Correcto Tabla 4.21: Caso de prueba 7, “en positivo” Identificación del caso de prueba: 8 Caso de uso / Flujo de eventos: Cambiar color / Flujo normal Condiciones de ejecución: En el sistema se debe haber creado un fondo que soporte el trabajo con paletas. Descripción: Se realiza el cambio de color de un elemento de la paleta de colores del fondo actual. Resultado esperado: Se muestra en la ventana el nuevo color elegido para el elemento afectado de la paleta. Resultado obtenido: Correcto Tabla 4.22: Caso de prueba 8, “en positivo” CAPÍTULO 4. MÉTODO DE TRABAJO 139 Identificación del caso de prueba: 9 Caso de uso / Flujo de eventos: Modificar tesela / Flujo normal Condiciones de ejecución: En el sistema se debe haber creado un fondo teselado de 16 o 256 colores. Descripción: El alumno modifica un pixel de una tesela. Resultado esperado: Se muestra en la ventana la tesela con el cambio efectuado. Resultado obtenido: Correcto Tabla 4.23: Caso de prueba 9, “en positivo” Identificación del caso de prueba: 10 Caso de uso / Flujo de eventos: Modificar tesela / Flujo alternativo Condiciones de ejecución: En el sistema se debe haber creado un fondo teselado de 16 o 256 colores. Descripción: Se modifican los pixels de una tesela como consecuencia de un cambio en la paleta de colores. Resultado esperado: Se muestra en la ventana la tesela actualizada. Resultado obtenido: Correcto Tabla 4.24: Caso de prueba 10, “en positivo” CAPÍTULO 4. MÉTODO DE TRABAJO 4.3.10.1. 140 Fase de diseño Desarrollo del caso de uso Agregar color nuevo En este caso de uso se contemplan un flujo normal y un flujo de eventos alternativo. Por tanto, habrá un diagrama de secuencia de análisis para cada flujo de eventos (véase figura 4.35). Figura 4.35: Diagrama de secuencia correspondiente al flujo normal El diagrama de flujo de eventos alternativos se puede ver en la figura 4.36. Desarrollo del caso de uso Intercambiar colores En este caso de uso sólo se contempla un flujo de eventos. Por tanto, habrá un diagrama de secuencia de análisis para este flujo de eventos (véase figura 4.37). Desarrollo del caso de uso Modificar fondo Para este caso de uso se deberá construir un sólo diagrama de secuencia de análisis (véase la figura 4.38). CAPÍTULO 4. MÉTODO DE TRABAJO Figura 4.36: Diagrama de secuencia correspondiente al flujo alternativo Figura 4.37: Diagrama de secuencia correspondiente al flujo normal 141 CAPÍTULO 4. MÉTODO DE TRABAJO 142 Figura 4.38: Diagrama de secuencia correspondiente al flujo normal 4.3.10.2. Fase de implementación Agregar color nuevo La función reorganizar button clicked se ocupa de detectar si se ha pulsado el botón Añadir color en el diálogo Reorganizar paleta. Cuando esto sucede, dicha función comprueba si hay espacio suficiente en la paleta para agregar otro color. En caso afirmativo, la función pick color de la vista de la paleta muestra el diálogo de selección de color. En este diálogo se selecciona el nuevo color que se agregará a la paleta. Una vez elegido el nuevo color, el presentador realiza la modificación de la paleta. Para ello, llama al método update de la paleta (explicado en la implementación del caso de uso Cambiar color), el cual agrega el color color en la posición igual al número de elementos de la paleta más 1. Es decir, se agrega a continuación del último elemento de la paleta. Como se ha explicado en la implementación del caso de uso Modificar paleta, tras realizar un cambio en la paleta, se desencadena una propagación de llamadas a cada una de las vistas suscritas al servicio de actualizaciones para que se actualicen. Intercambiar colores Para implementar este caso de uso se ha utilizado la técnica de arrastrar y soltar (drag and drop). Ası́, para intercambiar dos colores se agarra un color de la paleta, el cual CAPÍTULO 4. MÉTODO DE TRABAJO 143 se mueve hasta la posición de otro color de la paleta y, finalmente, se suelta. El resultado es el intercambio de los colores de los dos elementos que intervienen en la acción. Para implementar esta técnica se han necesitado las tres funciones siguientes, definidas en la clase VistaPaleta: press color paleta: esta función captura la señal emitida cuando se pulsa un color de la paleta en el diálogo Reorganizar paleta. Lo que se realiza en este método es almacenar el elemento pulsado junto con las coordenadas de su posición en el momento de pulsarse. motion notify color paleta: esta función captura la señal que se emite cada vez que se mueve el ratón. Lo que realiza este método es pintar el elemento de la paleta en la posición que se mueva el ratón. Esto provoca la sensación de que el color de la paleta pulsado se está moviendo. release color paleta: esta función captura la señal que se emite cuando se suelta el botón del ratón una vez que se ha presionado. Cuando se ejecuta este método se obtiene el color de los dos elementos que intervienen en la operación. Después, el método llama a la función intercambiar colores del presentador. Esta función comprueba si alguno de los dos colores ocupa la posición del color transparente en la paleta. En caso afirmativo, se modifica la paleta y se propaga una actualización por aquellas vistas que estén interesadas. Modificar fondo Este caso de uso también se ha implementado utilizando la técnica de arrastrar y soltar. En concreto, dicha técnica se ha utilizado para realizar la selección de aquellas teselas del fondo que se desean reemplazar. Para la técnica de arrastrar y soltar se han implementado las tres funciones siguientes en la clase VistaMapa: on press tesela: esta función captura la señal que se emite cuando se pulsa una tesela del fondo. Asimismo, es la encargada de guardar la posición de dicha tesela. on motion notify: esta función simplemente se encarga de ir redimensionando el rectángulo rojo utilizado en la interfaz para simular la selección de un conjunto de teselas. on release tesela: esta función captura la señal que se emite cuando se suelta el botón del ratón. La primera tarea que se realiza es pedirle al presentador que le envı́e la tesela que se ha seleccionado (get tesela seleccionada). Posteriormente, se llama a la función update del presentador para cada una de las posiciones de las teselas que se hayan seleccionado. CAPÍTULO 4. MÉTODO DE TRABAJO 144 La función update del presentador es, sin duda, la más importante de este caso uso. Esta función tiene dos tareas bien diferenciadas: la primera es notificar a la vista que se actualice y, la segunda, consiste en actualizar la entrada del mapa para que se mantenga la coherencia entre la vista y los datos del fondo. Su implementación se puede ver a continuación: 1 void PresenterMap :: update ( int x , int y , int tesela ) 2 { 3 int i = y /8 , j = x /8; 4 unsigned int entrada_mapa = this - > mapa . at ( i *( height /8) + j); unsigned int paleta = ( entrada_mapa & 0 xF000 ) >> 12; // 5 obtener numero de paleta ; unsigned int new_flip = ( int ) this - > datos . at ( tesela ) -> 6 get_flip () ; 7 if ( new_flip == 0) { // no utilizar espejo 8 vistas [ " map " ] - > update (j , i , this - > datos . at ( 9 tesela ) -> get_data ( " " ) ) ; } else if ( new_flip == 1) { // espejo horizontal 10 vistas [ " map " ] - > update (j , i , this - > datos . at ( 11 tesela ) -> get_data ( " horizontal " ) ) ; } else if ( new_flip == 2) { // espejo vertical 12 vistas [ " map " ] - > update (j , i , this - > datos . at ( 13 tesela ) -> get_data ( " vertical " ) ) ; } else { // espejo horizontal y vertical 14 vistas [ " map " ] - > update (j , i , this - > datos . at ( 15 tesela ) -> get_data ( " horizontal and vertical " )); } 16 17 this - > mapa . at ( i *( height /8) + j ) = tesela + ( new_flip << 18 10) + ( paleta << 12) ; 19 } CAPÍTULO 4. MÉTODO DE TRABAJO Identificación del caso de prueba: 11 Caso de uso / Flujo de eventos: Agregar color nuevo / Flujo normal Condiciones de ejecución: En el sistema se debe haber creado un fondo que trabaje con paletas de colores. Descripción: El alumno agrega un nuevo elemento a la paleta de colores. Resultado esperado: Se muestra en la ventana y en el diálogo de reorganización de la paleta el cambio realizado sobre la paleta de colores. Resultado obtenido: Correcto Tabla 4.25: Caso de prueba 11, “en positivo” Identificación del caso de prueba: 12 Caso de uso / Flujo de eventos: Agregar color nuevo / Flujo alternativo Condiciones de ejecución: En el sistema se debe haber creado un fondo que trabaje con paletas de colores. Descripción: El alumno agrega un nuevo elemento a la paleta de colores, la cual tiene 256 colores. Resultado esperado: Se muestra un diálogo de error en el que se indica que la paleta ya tiene el número máximo de colores posibles. Resultado obtenido: Correcto Tabla 4.26: Caso de prueba 12, “en positivo” 145 CAPÍTULO 4. MÉTODO DE TRABAJO 146 Identificación del caso de prueba: 13 Caso de uso / Flujo de eventos: Intercambiar colores / Flujo normal Condiciones de ejecución: En el sistema se debe haber creado un fondo que trabaje con paletas de colores. Descripción: El alumno intercambia dos elementos de la paleta. Resultado esperado: Se muestra en el diálogo de reorganización de la paleta y en la ventana los colores de dos elementos de la paleta intercambiados. Resultado obtenido: Correcto Tabla 4.27: Caso de prueba 13, “en positivo” Identificación del caso de prueba: 14 Caso de uso / Flujo de eventos: Intercambiar colores / Flujo normal Condiciones de ejecución: En el sistema se debe haber creado un fondo que trabaje con paletas de colores. Descripción: El alumno intercambia dos elementos de la paleta. Resultado esperado: Se muestra en el diálogo de reorganización de la paleta y en la ventana los colores de dos elementos de la paleta intercambiados. Resultado obtenido: Correcto Tabla 4.28: Caso de prueba 14, “en positivo” CAPÍTULO 4. MÉTODO DE TRABAJO 147 Identificación del caso de prueba: 15 Caso de uso / Flujo de eventos: Modificar fondo / Flujo normal Condiciones de ejecución: En el sistema se debe haber creado un fondo teselado o extended rotoscale. Descripción: Se sustituyen una serie de teselas del fondo por otras en las posiciones que se han elegido. Resultado esperado: En primer lugar, la vista debe mostrar la tesela elegida en las posiciones que se seleccionaron. Y en segundo lugar se debe comprobar la coherencia de las entradas del mapa que se hayan visto afectadas. Resultado obtenido: Correcto Tabla 4.29: Caso de prueba 15, “en positivo” 4.3.10.3. Pruebas 4.3.11. Iteración 9 En esta iteración se realizará la fase de análisis, la fase de diseño, la fase de implementación y la fase de pruebas del caso de uso Generar datos NDS, según el plan de iteraciones. 4.3.11.1. Fase de análisis Descripción del caso de uso Generar datos NDS Este caso de uso se encarga de convertir la imagen del fondo que actualmente se muestra en la ventana, en ficheros de texto en el formato que elija el alumno. Estos ficheros generados son perfectamente utilizables en la programación de aplicaciones para Nintendo DS. Las posibles clases de análisis identificadas en este caso de uso se muestran en la figura 4.39. La descripción textual del caso de uso se muestra en la tabla 4.30. CAPÍTULO 4. MÉTODO DE TRABAJO 148 Figura 4.39: Clases de análisis involucradas en el caso de uso Generar datos NDS Nombre: Generar datos NDS Abstracto: No Precondiciones: En el sistema se debe haber creado un fondo teselado, extended rotoscale o framebuffer. Postcondiciones: Se han generado archivos de texto para la NDS a partir del fondo mostrado en la ventana. El formato de los archivos de texto es elegido por el alumno. Rango: 12 Flujo normal: 1. El alumno pulsa el botón convertir fondo en la ventana, la cual muestra un diálogo con opciones para la generaración del fondo para la NDS. 2. El alumno selecciona las opciones de salida para los ficheros de texto en el diálogo. 3. EL diálogo pasa las opciones al controlador. 4. El controlador guarda el fondo que se muestra en la pantalla en un archivo de tipo imagen. Para ello, solicita al presentador que le envı́e el fondo. 5. El controlador le pasa al proxy la ruta del archivo que ha creado. 6. El proxy le dice a Grit que se ejecute de acuerdo con los datos que ha recibido. Descripción: En este caso de uso se generan los archivos de texto que se utilizarán para la programación de la NDS a partir del fondo mostrado en la ventana. Tabla 4.30: Descripción textual del caso de uso Generar datos NDS CAPÍTULO 4. MÉTODO DE TRABAJO 4.3.11.2. 149 Fase de diseño Desarrollo del caso de uso Generar datos NDS En este caso de uso sólo se contempla un único flujo de eventos. Por tanto, sólo habrá un diagrama de secuencia de análisis para este caso de uso (véase figura 4.40). Figura 4.40: Diagrama de secuencia correspondiente al flujo normal Como se puede comprobar, este es el último que caso de uso que se tiene que desarrollar de acuerdo con el plan de iteraciones. Por tanto, se está en disposición de mostrar el diagrama de clases del editor de fondos completado. Dicho diagrama se puede observar en la figura 4.41. 4.3.11.3. Fase de implementación Generar datos NDS La función convert item de la clase IModo se encarga de capturar la señal que se emite cuando se pulsa el botón Convertir imagen. Cuando se ejecuta esta función, se muestra un diálogo con las opciones para convertir el fondo (véase la figura 4.42). Cuando se pulsa el botón Aceptar del diálogo, esta función ejecuta el método save de su misma clase, el cual se ha definido de la siguiente manera: 1 void save ( std :: string filename , IPresenter * presenter ) CAPÍTULO 4. MÉTODO DE TRABAJO Figura 4.41: Diagrama de clases del editor de fondos 150 CAPÍTULO 4. MÉTODO DE TRABAJO 151 Figura 4.42: Diagrama de secuencia correspondiente al flujo normal El primer parámetro de este método representa el nombre del archivo de la imagen que se creará para que sea utilizado por Grit. El segundo parámtetro es el presentador del mapa, que será utilizado para obtener los datos actuales del fondo que se muestra en la pantalla mediante la llamada a su función get mapa. Esta última función no tiene ningún argumento y devuelve un puntero a un elemento del tipo GdkPixbuf. Una vez que ha terminado de ejecutarse el método save, se recogen las opciones que se han introducido en el diálogo y, posteriormente, se ejecuta la función convertir del proxy. El código de esta última función es el siguiente: 1 void GritProxy :: convertir ( std :: string imagen , std :: string optn , std :: string output , std :: string header ) 2 { if (! grit ) { 3 grit = new Grit ( this - > filename , modo , n_colores 4 ); 5 } 6 grit - > convertir ( imagen , optn , output , header ) ; 7 } CAPÍTULO 4. MÉTODO DE TRABAJO 152 El significado de cada uno de los cuatro argumentos de la función se explica a continuación: imagen: representa la ruta del nombre del archivo de la imagen. optn: representa la opción elegida en el diálogo para el formato de salida de los archivos que generará Grit. ouput: representa el nombre de salida de los archivos que generará Grit. header: indica si se prefiere que Grit genere un fichero de encabezado. La función convertir instancia el objeto grit (clase Grit) si no estuviera instanciado y llama a su método convertir. En esta última función es realmente donde se realiza la ejecución de Grit (herramienta) con las opciones escogidas en un nuevo proceso. 4.3.11.4. Pruebas Identificación del caso de prueba: 16 Caso de uso / Flujo de eventos: Generar datos NDS / Flujo normal Condiciones de ejecución: En el sistema se debe haber creado un fondo framebuffer, teselado o extended rotoscale. Descripción: Se convierte el fondo mostrado en la ventana en los archivos de texto que se utilizarán para la programación de la NDS. Resultado esperado: Se generan el archivo o los archivos de texto en el formato que indique el alumno. Resultado obtenido: Correcto Tabla 4.31: Caso de prueba 16, “en positivo” Capı́tulo 5 RESULTADOS 5.1. Introducción 5.1. Introducción Este capı́tulo está dedicado a mostrar los resultados obtenidos al ejecutar el entorno de programación. En primer lugar, se ilustrará al lector con la presentación de los resultados del editor de fondos para cada uno de los distintos tipos de fondos junto con los tamaños que soportan (véase la tabla 5.1). Tipo de fondo Framebuffer Rotación extendida Teselado Tamaño 192×256 pixels 128×128 pixels 256×256 pixels 512×256.pixels 512×512 pixels 512×1024 pixels 1024×512 pixels 32×32 teselas 32×64 teselas 64×32 teselas 64×64 teselas Resultado (Anexo) Véase la figura B.1 Véase la figura B.2 Véase la figura B.3 Véase la figura B.4 Véase la figura B.5 Véase la figura B.6 Véase la figura B.7 Véase la figura B.8 Véase la figura B.9 Véase la figura B.10 Véase la figura B.11 Tabla 5.1: Resultados para cada tamaño aceptado por los tipos de fondo 153 CAPÍTULO 5. RESULTADOS 154 Capı́tulo 6 CONCLUSIONES Y PROPUESTAS 6.1. Conclusiones En el segundo capı́tulo se expusieron los objetivos que se pretendı́an conseguir para este proyecto. Ası́, los objetivos que se han alcanzado finalmente han sido los siguientes: Eclipse como base: El entorno de programación se ha construido utilizando como base Eclipse, al cual se han unido el plug-in NDS Managedbuilder y la nueva versión realizada del plug-in Zylin Embedded CDT. T Depuración utilizando un emulador: El emulador de Nintendo DS, DeSmuME, se ha integrado en el entorno para poder ser utilizado durante el proceso de depuración de aplicaciones. Bajo acoplamiento y alta cohesión: La minimización del acoplamiento entre componentes se ha logrado gracias al empleo de los patrones de diseño de software Modelo Vista Presentador, Builder y Proxy. Múltiples vistas: El editor de fondos se ha construido para que soporte múltiples vistas. Gracias al empleo del patrón Modelo Vista Presentador, cualquier cambio que se produzca en un elemento del modelo se verá reflejado en cada una de las vistas en las que intervenga el elemento afectado. Modos gráficos 2D: El editor de fondos ofrece la posibilidad de trabajar con los tres modos gráficos 2D de la Nintendo DS: modo framebuffer, modo extended rotoscale y modo teselado. El sistema generará datos entendibles por la videoconsola: A partir de una imagen indexada en formato PNG, el editor puede generar los archivos de texto que pue155 CAPÍTULO 6. CONCLUSIONES Y PROPUESTAS 156 den ser utilizados directamente para la programación de la Nintendo DS. Gracias al empleo de la herramienta Grit, se ha superado este objetivo. Fondos teselados de 16 y 256 colores: El editor es capaz de trabajar tanto con fondos teselados de 256 colores como con fondos teselados de 16 colores. Edición de la paletas de colores: Durante el trabajo con fondos teselados y con fondos de rotación extendida, el editor ofrece la posibilidad de personalizar la paleta de colores. En concreto, se pueden agregar colores a la paleta, editar los colores existentes e intercambiar colores de posición. Sencilla instalación y configuración del software: El entorno de programación ha reducido en gran medida el tiempo que el alumno tiene que dedicar a tareas de instalación, configuración y aprendizaje. Como consecuencia de la cobertura de este objetivo, se consigue que el alumno dedique gran parte de su esfuerzo a la exploración de la arquitectura de la Nintendo DS. Desarrollo de un sistema multiplataforma: Tanto las tecnologı́as utilizadas, como Eclipse, como los lenguajes de programación utilizados permiten que el sistema sea multiplataforma. El sistema ha sido probado en equipos que utilizan el sistema operativo Debian GNU/Linux, obteniéndose buenos resultados. Sin embargo, en plataformas Microsoft Windows no se ha probado el entorno de programación. Por tanto, este objetivo no se ha logrado del todo. Desarrollo del sistema utilizando software libre: Este objetivo se ha cumplido, ya que tanto Eclipse como los plug-ins utilizados son tecnologı́as libres. Asimismo, las tecnologı́as utilizadas para el desarrollo del editor, como GooCanvas o devkitPro, también son libres. Desarrollo de un sistema viable desde el punto de vista docente: Para demostrar la viabilidad se ha elaborado un tutorial del entorno de programación (véase el anexo A.1). 6.2. Propuestas y lı́neas de investigación futuras Existen varios aspectos del sistema que son susceptibles de ser mejorados en profundidad. A continuación se aportarán una serie de ideas para la realización de futuras mejoras sobre el entorno de programación. Estas ideas se han organizado en dos apartados diferentes. CAPÍTULO 6. CONCLUSIONES Y PROPUESTAS 6.2.1. 157 Entorno cruzado de desarrollo El hecho de que se haya integrado en el sistema el emulador DeSmuME durante el proceso de depuración de aplicaciones para Nintendo DS, abre una nueva posibilidad. Esta posibilidad serı́a la utilización del emulador durante el proceso de ejecución de cualquier aplicación para NDS que se esté desarrollando. Es decir, se podrı́a conseguir algo parecido a lo que hace el plug-in de Eclipse para el desarrollo de aplicaciones para móviles EclipseME. Este plug-in utiliza durante el proceso de ejecución y de depuración una especie de emulador en forma de teléfono móvil. Uno de los objetivos que no ha sido cubierto en su totalidad es el desarrollo de un sistema multiplataforma. En concreto, no se ha probado el correcto funcionamiento del entorno de programación en plataformas Microsoft Windows. La introducción de esta mejora ofrecerı́a al alumno un amplio abanico de posibilidades a la hora de realizar su trabajo. 6.2.2. Editor de fondos El editor de fondos es capaz de crear archivos, los cuales pueden ser utilizados para la programación de la Nintendo DS, a partir de una imagen en formato PNG. Una mejora que se podrı́a realizar es aumentar la compatibilidad con otros formatos de imagen, teniendo en cuenta que cada modo gráfico 2D tiene unas caracterı́sticas propias y no todos los modos gráficos pueden soportar el mismo formato. Otra mejora que se podrı́a estudiar serı́a ofrecer la posibilidad de crear un fondo teselado a partir de un fondo en blanco, es decir, partiendo de un conjunto vacı́o de teselas. Obviamente, se podrı́a pensar en ampliar esta mejora para fondos framebuffer y fondos de rotación extendida, pero no tendrı́a mucho sentido, ya que se estarı́a imitando las funcionalidades de cualquier programa de dibujo existente como, por ejemplo, GIMP. De cualquier modo, la introducción de esta mejora para los fondos teselados abre un amplio número de posibilidades a la hora de continuar con el desarrollo del editor de fondos: Integrar la posibilidad de crear una nueva tesela para un fondo teselado en el editor. La tesela deberı́a poder crearse tanto con 256 colores como con 16 colores. Implementación de un mecanismo que permita copiar o mover una o varias teselas de un fondo a otro. Integrar en el editor la posibilidad de copiar colores de una paleta a otra entre fondos distintos. Otra posibilidad que se podrı́a estudiar serı́a la compartición de las paletas de colores entre fondos teselados. CAPÍTULO 6. CONCLUSIONES Y PROPUESTAS 158 También se podrı́a estudiar la posibilidad de ampliar la funcionalidad del editor de fondos mediante la incorporación de sprites. Si los fondos constituyen los objetos estáticos de la videoconsola, los sprites forman la parte dinámica. Ası́, los personajes, los enemigos, las armas o items, entre otros, son sprites. No obstante, los sprites y los fondos son bastantes similares en muchos aspectos. La integración de sprites ampliarı́a en gran medida las funcionalidades que actualmente ofrece el editor. Anexo A TUTORIAL DEL ENTORNO DE PROGRAMACIÓN A.1. Entorno cruzado de desarrollo A.1.1. Instalación en Debian GNU/Linux A.1.2. Creación de un proyecto para NDS A.1.3. Configuración del proyecto A.1.4. Edición del archivo fuente A.1.5. Depuración del proyecto A.1.6. Sesión de depuración A.2. Editor de fondos A.2.1. Ejecución A.2.2. Abrir imagen A.2.3. Reorganizar paleta A.2.4. Convertir fondo A.2.5. Personalización del fondo A.1. Entorno cruzado de desarrollo A.1.1. Instalación en Debian GNU/Linux Este tutorial está dirigido a todos aquellos usuarios que utilicen como sistema operativo Debian GNU/Linux o cualquier distribución GNU/Linux basada en Debian. 159 ANEXO A. TUTORIAL DEL ENTORNO DE PROGRAMACIÓN 160 En primer lugar se debe añadir a las fuentes de paquetes una adicional. Para ello, se tiene que agregar al archivo /etc/apt/sources.list las dos lı́neas siguientes: 1 2 deb http :// arco . esi . uclm . es /~ francisco . moya / debian / ./ deb - src http :// arco . esi . uclm . es /~ francisco . moya / debian / ./ Se necesitan una serie de herramientas para el desarrollo de aplicaciones para Nintendo DS. La instalación de dichas herramientas se puede realizar instalando el paquete siguiente: 1 $ sudo aptitude install devkitpro - arm - eabi Para poder utilizar el entorno se necesita tener instalado previamente Eclipse C/C++ Development Tooling - CDT 1 junto con el plug-in NDS Managedbuilder2 . La nueva versión del plug-in Zylin Embedded CDT se puede instalar agregando la dirección http://arco.esi.uclm.es/~manuel.rodrigo/update/ en el centro de actualizaciones de Eclipse. A.1.2. Creación de un proyecto para NDS Para crear un nuevo proyecto se debe pulsar en la ventana principal de Eclipse en File / New / C project. En el diálogo (véase la figura A.1) que aparece se debe configurar lo siguiente: Introducir un nombre y una ubicación para el proyecto. En Project types seleccionar Nintendo DS Rom / Empty Project (libnds). Click en Next y en Finish. A.1.3. Configuración del proyecto Existen ligeras diferencias entre crear un proyecto para ARM9 y crear uno para ARM7. Las diferencias afectan principalmente al modo de configuración de uno y de otro. En este manual se ha creado un proyecto para el procesador ARM9 y, por tanto, se deberá configurar como tal. Para iniciar la configuración del proyecto, se debe pulsar en la ventana principal de Eclipse en Project Properties. En el diálogo nuevo que aparece (véase la figura A.2), se deberá desplegar la pestaña C/C++ Build. 1 Disponible 2 Disponible en http://www.eclipse.org . en http://snipah.com . ANEXO A. TUTORIAL DEL ENTORNO DE PROGRAMACIÓN Figura A.1: Diálogo de creación de un proyecto C 161 ANEXO A. TUTORIAL DEL ENTORNO DE PROGRAMACIÓN Figura A.2: Propiedades del proyecto 162 ANEXO A. TUTORIAL DEL ENTORNO DE PROGRAMACIÓN 163 La sección Discovery Options se deberá configurar del siguiente modo: 1. Seleccionar GCC per project scanner info profile en Discovery profile. 2. Desmarcar la opción Report path detection problems. 3. En Compiler invocation command introducir: /usr/bin/arm-eabi-gcc y, finalmente, aplicar los cambios. En la sección Environment: 1. Cambiar el valor de DEVKITARM DIR por /opt/devkitPro/devkitARM 2. Cambiar el valor de DEVKITPRO DIR por /opt/devkitPro En la sección Settings se deberán realizar varios cambios en la pestaña Tool Settings. En concreto, el apartado devkitARM C Compiler se debe configurar del siguiente modo: 1. Click en Directories. Añadir la ruta /usr/include. 2. Click en Debugging. En Debug level seleccionar Default (-g). 3. Click en Miscellaneous. Agregar -march=armv5te en Other flags. 4. Click en ARM. Desmarcar la opción Omit Frame Pointer. Dejar en blanco el campo CPU y cambiar el valor del campo Tune por arm946e-s. En el apartado NDS Tool configurar lo siguiente: 1. Click en ARM7. 2. Introducir /opt/devkitPro/libnds/default.arm7 en el campo Code y, por último, aplicar los cambios. A.1.4. Edición del archivo fuente Para familiarizarse con el entorno de desarrollo de aplicaciones para Nintendo DS, se va a utilizar como ejemplo una aplicación que utilice el algoritmo de Euclides para el cálculo del máximo común divisor. En primer lugar, se debe crear un fichero fuente en C dentro del proyecto actual. Para ello se debe realizar lo siguiente: 1. Click en File / New / Source File. 2. En Source File introducir main.c. ANEXO A. TUTORIAL DEL ENTORNO DE PROGRAMACIÓN 164 3. En Template seleccionar ¡None¿. 4. Click en Finish. A continuación se edita el nuevo fichero para que contenga el siguiente código: 1 # include < nds .h > 2 # include < stdio .h > 3 4 int mcd ( signed char a , signed char b ) 5 { 6 if ( b == 0) { 7 return a ; 8 } 9 else { return mcd (b , a % b ) ; 10 } 11 12 } 13 14 int main () 15 { consoleDemoInit () ; 16 17 18 int i = 12 , j = 99; 19 int div = mcd (i , j ) ; 20 21 printf ( " El mcd de %d y %d es %d \ n " , i , j , div ) ; 22 return 0; 23 } Si no se han producido errores de compilación, aparecerán varios archivos: mcd.elf mcd.arm9 mcd.nds De los tres archivos el único que preserva la información de depuración es el ejecutable mcd.elf. Por tanto, el depurador tendrá que trabajar necesariamente con él. Sin embargo la consola (o el simulador) sólo será capaz de ejecutar la imagen del cartucho mcd.nds. ANEXO A. TUTORIAL DEL ENTORNO DE PROGRAMACIÓN A.1.5. 165 Depuración del proyecto Para iniciar la depuración del proyecto lo primero que se debe realizar es cambiar la perspectiva de Eclipse. Se debe utilizar la perspectiva llamada Debug. Para ello: 1. Click en Window / Open Perspective / Other ... 2. Seleccionar Debug. 3. Click en Ok. A continuación, se debe crear la configuración de depuración para el proyecto (véase la figura A.3). Para ello: 1. Click en Run / Debug Configurations... 2. Doble click en Zylin Embedded debug (Native). Figura A.3: Crear la configuración de depuración En la pestaña Main: 1. En Project (Optional) indicar el nombre del proyecto. 2. Click en Search Project... Seleccionar el archivo .elf. ANEXO A. TUTORIAL DEL ENTORNO DE PROGRAMACIÓN 166 En la pestaña Debugger cambiar el valor de GDB debugger por arm-eabi-gdb. En la pestaña Commands seleccionar la opción I will use DeSmuME for remote debugging on the next TCP Port. En la pestaña DeSmuME: 1. Seleccionar la opción I will use DeSmuME for remote debugging. 2. Localizar la ubicación exacta de DeSmuME y aplicar los cambios. Para iniciar el proceso de depuración se debe pulsar el botón Debug. A.1.6. Sesión de depuración El programa, como todos los programas escritos en C, empieza en la función main. Por tanto, ese puede ser un buen punto para empezar a ver las caracterı́sticas de GDB. Con objeto de parar el programa justo cuando llegue a main, se pondrá un punto de ruptura3 . Para ello se debe hacer doble click en la barra gris justo al lado del código del programa. En Eclipse los puntos de ruptura se simbolizan con un circulo pequeño de color azul (véase la figura A.4). Figura A.4: Punto de ruptura en el programa Para iniciar la depuración se debe pulsar el botón Resume. Este botón se utiliza para reanudar o continuar la ejecución de un hilo actualmente suspendido. Para terminar la sesión de depuración en cualquier momento, se debe pulsar el botón Terminate, próximo al botón Resume. Este botón tiene forma de cuadrado y es de color rojo. Se puede examinar el valor de las variables locales de la función que se está ejecutando en este momento en la pestaña Variables. También, se pueden ver los registros del procesador en la pestaña Registers. 3 Breakpoint ANEXO A. TUTORIAL DEL ENTORNO DE PROGRAMACIÓN 167 Como únicamente se ha situado un punto de ruptura, al reanudar la depuración el programa terminará su ejecución, salvo que exista algún error en tiempo de ejecución. Si no se ha producido ninguna anomalı́a en la depuración, la salida del programa se puede ver en el simulador DeSmuME (véase la figura A.5). Figura A.5: Salida del programa A.2. Editor de fondos A.2.1. Ejecución Para ejecutar el editor simplemente hay que situarse en la carpeta de la aplicación y teclear: 1 $ ./ editor - ds En la figura A.6 se puede ver la interfaz de la aplicación. Los elementos principales de la interfaz son los siguientes: ANEXO A. TUTORIAL DEL ENTORNO DE PROGRAMACIÓN 168 Figura A.6: Interfaz del editor de fondos Paleta: en esta vista se muestran los colores de la paleta del fondo. Color actual: se muestra el color de la paleta que se ha seleccionado. Teselas: en esta vista se muestra el conjunto de teselas de un fondo teselado. Tesela actual: se muestra la tesela que actualmente ha sido seleccionada de entre el conjunto de teselas. La parte central de la interfaz se utiliza para mostrar el fondo. Botón Abrir: se utiliza para crear un nuevo fondo a partir de un archivo de imagen. Botón Ampliar y Reducir: se utilizan para realizar operaciones de zoom sobre el fondo. A.2.2. Abrir imagen Cuando se pulsa el botón Abrir y se selecciona un archivo de imagen, la interfaz muestra un diálogo (véase la figura A.7) en el que se debe seleccionar el tipo de fondo que se desea crear. En la figura A.8 se muestra el resultado de crear un fondo teselado de 256 colores a partir de una imagen existente. ANEXO A. TUTORIAL DEL ENTORNO DE PROGRAMACIÓN Figura A.7: Diálogo Nuevo fondo Figura A.8: Abrir una imagen en modo teselado de 256 colores 169 ANEXO A. TUTORIAL DEL ENTORNO DE PROGRAMACIÓN A.2.3. 170 Reorganizar paleta Al pulsar el botón Reorganizar paleta en la barra de herramientas de la interfaz aparecerá un diálogo (véase la figura A.9) en el cual se puede modificar una paleta. En este diálogo se pueden realizar tres operaciones sobre la paleta: Editar un color de la paleta. Agregar un nuevo color a la paleta. Intercambiar dos colores moviendo un color de la paleta encima de otro. Figura A.9: Diálogo Reorganizar paleta A.2.4. Convertir fondo Al pulsar el botón Reorganizar paleta en la barra de herramientas de la interfaz aparecerá un diálogo (véase la figura A.10) en el cual se muestran las opciones para la creación de los ficheros de salida a partir del fondo mostrado en la ventana. Las opciones del diálogo se explican a continuación: Nombre del archivo: nombre que se desea para los archivos de salida. Directorio de salida: directorio donde se crearán los archivos de salida. Formato de salida: indica el tipo de archivo o archivos de salida que se desean obtener. Archivo de encabezado: en caso de activarse se crea un fichero de encabezado. ANEXO A. TUTORIAL DEL ENTORNO DE PROGRAMACIÓN 171 Figura A.10: Diálogo Convertir fondo A.2.5. Personalización del fondo Un fondo teselado puede ser personalizado de dos formas distintas. La primera opción consiste en editar manualmente una tesela del fondo. La segunda opción consiste en sustituir unas teselas del fondo por otras. El proceso para editar manualmente una tesela del fondo es el siguiente: Pulsar en la tesela que se desee seleccionar del conjunto de teselas del fondo. Pulsar en el color de la paleta que se desee utilizar. El color seleccionado se puede ver en la parte de la interfaz llamada Color actual. Hacer click en el pixel de la tesela que se desee modificar. Recordar que esta acción se tiene que realizar en la vista de edición de teselas (Tesela actual). Para reemplazar unas teselas del fondo por otras se debe proceder del siguiente modo: Pulsar en la tesela que se desee seleccionar del conjunto de teselas del fondo. Hacer click en la tesela del fondo que desee sustituir. Si se desea reemplazar un conjunto extenso de teselas, simplemente se debe pulsar en una parte del fondo con el botón izquierdo del ratón y mantener presionado dicho botón mientras se mueve el ratón (véase la figura A.11). ANEXO A. TUTORIAL DEL ENTORNO DE PROGRAMACIÓN Figura A.11: Sustitución de un conjunto de teselas del fondo 172 ANEXO A. TUTORIAL DEL ENTORNO DE PROGRAMACIÓN 173 Cada tesela de un fondo puede ser configurada mediante la aplicación de un tipo de espejo (véase la figura A.12). Para ello, se debe pulsar con el botón derecho del ratón encima de una tesela y seleccionar el tipo de espejo que se desee. A continuación se citan las configuraciones posibles para una tesela: No utilizar ningún tipo de espejo (Do not flip). Utilizar espejo horizontal (Horizontal flip). Utilizar espejo vertical (Vertical flip). Utilizar espejo horizontal y espejo vertical (Horizontal and vertical flip). Figura A.12: Espejos disponibles para una tesela ANEXO A. TUTORIAL DEL ENTORNO DE PROGRAMACIÓN 174 Anexo B FIGURAS B.1. Resultados B.1. Resultados Figura B.1: Fondo framebuffer a partir de una imagen de 192×256 pixels 175 ANEXO B. FIGURAS Figura B.2: Fondo de rotación extendida a partir de una imagen de 128×128 pixels Figura B.3: Fondo de rotación extendida a partir de una imagen de 256×256 pixels 176 ANEXO B. FIGURAS Figura B.4: Fondo de rotación extendida a partir de una imagen de 512×256 pixels Figura B.5: Fondo de rotación extendida a partir de una imagen de 512×512 pixels 177 ANEXO B. FIGURAS 178 Figura B.6: Fondo de rotación extendida a partir de una imagen de 512×1024 pixels Figura B.7: Fondo de rotación extendida a partir de una imagen de 1024×512 pixels ANEXO B. FIGURAS Figura B.8: Fondo teselado de 256 colores a partir de una imagen de 32×32 teselas 179 ANEXO B. FIGURAS Figura B.9: Fondo teselado de 256 colores a partir de una imagen de 32×64 teselas 180 ANEXO B. FIGURAS Figura B.10: Fondo teselado de 16 colores a partir de una imagen de 64×32 teselas 181 ANEXO B. FIGURAS Figura B.11: Fondo teselado de 16 colores a partir de una imagen de 64×64 teselas 182 Bibliografı́a [1] The Video Game Museum. http://www.vgmuseum.com/. R [2] ARMLimited. The Official ARM site. http://www.arm.com. R [3] ARMLimited. ARM Architecture Reference Manual, 2000. Disponible en lı́nea en http://infocenter.arm.com/help/index.jsp. Accedido por última vez el 15/02/2009. R [4] ARMLimited. ARM7TDMI Technical Reference Manual, 2004. Disponible en lı́nea en http://infocenter.arm.com/help/index.jsp. Accedido por última vez el 15/02/2009. R R [5] ARMLimited. Application Binary Interface for the ARMArchitecture. The Base Standard, 2007. Disponible en lı́nea en http://infocenter.arm.com/help/ index.jsp. Accedido por última vez el 15/02/2009. R [6] ARMLimited. ARM946E-S Technical Reference Manual, 2007. Disponible en lı́nea en http://infocenter.arm.com/help/index.jsp. Accedido por última vez el 15/02/2009. R [7] ARMLimited. Nintendo DS Lite. ARM Powered Product, 2007. Disponible en lı́nea en http://www.arm.com/markets/home_solutions/armpp/11961.html. Accedido por última vez el 15/02/2009. R R [8] ARMLimited. Procedure Call Standard for the ARMArchitecture, 2007. Disponible en lı́nea en http://infocenter.arm.com/help/index.jsp. Accedido por última vez el 15/02/2009. [9] ESTEBAN, Daniel. Programación de videojuegos - Nintendo DS, 2007. Disponible en lı́nea en http://www.theninjabunny.com/libro-nds/. Accedido por última vez el 11/03/2009. 183 BIBLIOGRAFÍA 184 [10] GAMMA, Erich, HELM, Richard, JOHNSON, Ralph, and VLISSIDES, John. Design Patterns. Elements of Reusable Object-Oriented Software. Addison Wesley, 1998. [11] GONZÁLEZ, Jesús M., SEOANE, Joaquı́n, and ROBLES, Gregorio. Software Libre. Universitat Oberta de Catalunya, 2003. Disponible en lı́nea en cv.uoc.es/cdocent/ 6IP_5KXJ8EBO2FY26CJE.pdf. Accedido por última vez el 18/07/2009. [12] HANDLEY, Brian. Building an embedded cross-development environment with Eclipse. Macraigor Systems LLC, 2007. Disponible en lı́nea en http://www. embedded-control-europe.com/c_ece_knowhow/90/ecejul07p24.pdf. Accedido por última vez el 20/07/2009. [13] HUNTER, Martin. Tidying the House: The MVPC Software Design Pattern, 2006. [14] IBM Corporation. Eclipse Platform Technical Overview, 2003. Disponible en lı́nea en http://eclipse.org/whitepapers/eclipse-overview.pdf. Accedido por última vez el 18/07/2009. [15] LIPINSKI, Michael. Feasibility of the Nintendo DS for Teaching Problem-Based Learning in Kindergarten through Twelfth Grade Students. Faculty of the Graduate School at the University of Missouri-Columbia, 2008. Disponible en lı́nea en http://edt.missouri.edu/Summer2008/Thesis/LipinskiM-072508-T11610/ research.pdf. Accedido por última vez el 23/02/2009. [16] LÜTKEHAUS, Dorothea, ZELLER, Andreus, and et al. Debugging with DDD. Free Software Foundation, 2004. Disponible en lı́nea en http://www.gnu.org/ software/ddd/. Accedido por última vez el 17/07/2009. [17] MENA, Federico. GDK-PixBuf Reference Manual. The Free Software Foundation, 2000. Disponible en lı́nea en http://library.gnome.org/devel/gdk-pixbuf/ stable/. Accedido por última vez el 19/08/2009. [18] MOYA, Francisco. Introducción a los modos gráficos 2D de la Nintendo DS. Escuela Superior de Informática de Ciudad Real. Universidad de Castilla-La Mancha, 2008. [19] POLO, Macario. Manual de Ingenierı́a del Software II. Escuela Superior de Informática de Ciudad Real. Universidad de Castilla-La Mancha. [20] RAUBER, Andreas and BECKER, Christoph. Digital Preservation of Console Video Games. Vienna University of Technology, 2007. Disponible en lı́nea en www.ifs. tuwien.ac.at/~becker/pubs/guttenbrunner_games2007.pdf. Accedido por última vez el 17/07/2009. BIBLIOGRAFÍA 185 [21] ROGERS, Jason. Day 2: NDS Introduction. Disponible en lı́nea en http://www. dev-scene.com/NDS/Tutorials_Day_2. Accedido por última vez el 12/07/2009. [22] ROGERS, Jason, NOLAND, Michael, and MURPHY, Dave. LibNDS Reference Documentation, 2009. Disponible en lı́nea en http://libnds.devkitpro.org/ index.html. Accedido por última vez el 15/07/2009. [23] SANTOFIMIA, Maria José and MOYA, Francisco. Familiarización con el entorno de desarrollo. Escuela Superior de Informática de Ciudad Real. Universidad de CastillaLa Mancha, 2009. [24] SANTOFIMIA, Maria José and MOYA, Francisco. Gráficos en modo extended rotoscale con la NDS. Escuela Superior de Informática de Ciudad Real. Universidad de Castilla-La Mancha, 2009. [25] SANTOFIMIA, Maria José and MOYA, Francisco. Gráficos en modo teselado con la NDS. Escuela Superior de Informática de Ciudad Real. Universidad de Castilla-La Mancha, 2009. [26] SANTOFIMIA, Maria José and MOYA, Francisco. Gráficos en modo framebuffer con la NDS. Escuela Superior de Informática de Ciudad Real. Universidad de Castilla-La Mancha, 2009. [27] SANTOFIMIA, Maria José and MOYA, Francisco. Introducción a la consola Nintendo DS y al EABI. Escuela Superior de Informática de Ciudad Real. Universidad de Castilla-La Mancha, 2009. [28] SANTOFIMIA, Maria José and MOYA, Francisco. Introducción al EABI y al AAPCS. Escuela Superior de Informática de Ciudad Real. Universidad de Castilla-La Mancha, 2009. [29] SIVIANES, Francisco, BARROS, José, and MARTÍN, Antonio. Entorno de Desarrollo para NDS. Departamento de Tecnologı́a Electrónica. Escuela Universitaria Politécnica. Universidad de Sevilla, 2008. [30] SIXT, Johannes. KDbg. A Graphical Debugger Interface, 2008. Disponible en lı́nea en http://www.kdbg.org/. Accedido por última vez el 17/07/2009. [31] STALLMAN, Richard, PESCH, Roland, SHEBS, Stan, and et al. Debugging with GDB, the GNU Source-Level Debugger. Free Software Foundation, 2008. Disponible en lı́nea en http://www.gnu.org/software/gdb/documentation/. Accedido por última vez el 14/04/2009. BIBLIOGRAFÍA 186 [32] TAGLIARETTI, GianMario. GooCanvas Reference Manual. The GNOME Project, 2007. Disponible en lı́nea en http://library.gnome.org/devel/goocanvas/ unstable/. Accedido por última vez el 19/08/2009. [33] The GNOME Project. GLib Reference Manual. Dynamic Loading of Modules. Disponible en lı́nea en http://library.gnome.org/devel/glib/stable/. Accedido por última vez el 19/08/2009. [34] VIJN, Jasper. The Grit project site. http://www.coranac.com/category/proj/ grit/. [35] VIJN, Jasper. TONC. The Affine Transformation Matrix, 2008. Disponible en lı́nea en http://www.coranac.com/tonc/text/affine.htm. Accedido por última vez el 23/07/2009.