# Análisis, diseño e implementación del juego Reversi8 en la placa Embest S3CEV40 ![imagen portad](https://upload.wikimedia.org/wikipedia/commons/2/20/Othello-Standard-Board.jpg) ***Por Pedro Ramoneda y Paul Hodgetts*** > Disponible versión online en: https://hackmd.io/kEFa-vTgQAesUjU5G3Vk_A?view --- [TOC] ## Resumen ejecutivo En estas prácticas de la asignatura **Proyecto Hardware** del grado de Ingeniería Informática se ha implementando **el juego Reversi** en la placa **Embest S3CEV40**. En concreto, en estas dos prácticas que son la segunda y la tercera de la asignatura, se ha completado el juego mostrándolo por pantalla, con el control de este por medio de botones físicos y mediante la pantalla de manera táctil, la creación de una pila de depuración, la gestión de varias excepciones y mecanismos para poder saber el estado del juego. En este caso específico se utilizará un **tablero 8x8** para implementar el juego, como en la práctica anterior, el cual se muestra en memoria y en **la pantalla LCD** y además se puede interactuar con él utilizando los botones situados en el lado inferior izquierdo de la placa o de manera táctil. Para lograr el **control del juego por medio de los botones físicos** se han modificado varios ficheros suministrados por los profesores de la asignatura: para controlar rutinas de interrupción de los botones (para eliminar rebotes y configurar como interaccionan estos con el programa) y de timers (tanto para medir los tiempos de ejecución/procesamiento como para ser utilizado por el propio botón). También se han creado rutinas y funciones para gestionar las interrupciones hardware de los botones a posteriori, para no sobrecargar mas de lo necesario estas interrupciones. Se han realizado varios cambios para comprobar externamente el funcionamiento del juego. Se ha implementado un *latido*, con el objetivo de saber si el juego sigue en funcionamiento. Esto se ha ha hecho por medio del parpadeo de un led que permite saber que no ha habido ningún problema. También se ha implementado que en el caso de que haya una excepción el latido se pause y por por la pantalla 8led se muestre que excepcción se está produciendo. El programa se compila con un nivel de optimización de `-O2`, con el patrón de volteo más rápido que se desarrolló, es decir, con el algoritmo iterativo. Y se ha cargado en la placa de manera que se cuando esta arranque se inicialice el programa, para que se pueda ejecutar autónomamente . Además, salvo la inicialización de los los dispositivos el resto del programa se ejecuta en **modo usuario**. Opcionalmente, se han añadido mejoras al programa propuesto, se han solucionado posibles problemas relacionados con el linker script proporcionado. También se han completado tiempos y resultados obtenidos al medir las funciones de voltear de la práctica 1. Además se ha estudiado como poder hacer el autoincremento de la fila y columna y como optimizar los `timers` añadiendo otro tipo de interrupciones con mas prioridad para tener una lectura del tiempo mas precisa. Tras varios problema se ha obtenido un resultado con el que el equipo se ha sentido orgulloso y ha cumplido con las expectativas que se tenían, centrado en la usabilidad, la sencillez, la buena estructura del proyecto y la eficiencia que da trabajar a bajo nivel. ## Objetivos El objetivo principal de estas prácticas es el de hacer un **prototipo del juego reversi**, así como adaptarlo para su ejecución sobre la placa de manera autónoma. - Desarrollar mecanismos de bajo nivel para la depuración y control del tiempo, lo que incluye una pila de depuración, control de excepciones y una biblioteca de medición de tiempos. - Adaptar los dispositivos de entrada de la placa, en concreto los botones, para su cómodo uso dentro del juego. - Aprovechar dispositivos de salida de la placa para ofrecer una retroalimentación al jugador. - Considerar aspectos de bajo nivel para mejorar la eficiencia del programa. - Facilitar la adaptación del juego para distinto hardware. - Mostrar por la pantalla LCD el desarrollo del juego, el tiempo transcurrido y el tiempo de cómputo. - Poder controlar el juego desde la pantalla de manera táctil, centrandonos en un diseño centrado en el usuario y eficiente. - Carga del programa en la placa para que este se pueda ejecutar sin tener que estar conectado a un ordenador mediante el JTAG. - La mayoría de la ejecución del juego se ha de realizar en modo usuario. Por último, se han de considerar mejoras para la experiencia del usuario del juego, haciendo que el flujo de información entre el usuario y la máquina sea óptimo e implementar todo lo que se pueda. ## Organización del proyecto Con respecto a la anterior entrega, se pueden observar los siguientes módulos que se han añadido (ignorando sus respectivas cabeceras `*.h`): - **`button.c`**: Controladores para los botones derecho e izquierdo. - **`exceptionHandler.c`**: Rutinas de atención para excepciones `SWI`, `Undef` y `Data Abort`. - **`jugada_por_botones.c`**: Definición del comportamiento de juego por botones: selección de fila y columna en el turno del jugador. - **`botones_antirebotes.c`**: Módulo para la captación de pulsación de botón sin rebotes. - **`timer2.c`**: Funciones de inicialización y uso del *timer2* para la medición de tiempos. - **`44binit.asm`**: Rutina de inicialización de la placa, zonas de memoria, excepciones, etc. - **`lista_circular.c`**: Implementación de un tipo abstracto de dato *lista circular*. - **`BMP.c`**: funciones para definir mapas de bits del LCD. Colección de funciones de bajo nivel, suministradas por los profesores. - **`LCD.c`**: funciones para imprimir por pantalla. Colección de funciones suministradas por los profesores. - **`TP.c`**: funciones para controlar de manera táctil la pantalla LCD directamente. Colección de funciones de bajo nivel, suministradas por los profesores. - **`LCDUtils.c`**: funciones desarrolladas para interactuar con las librerias suministradas por los profesores `BMP.c`, `TP.c` y `TP.c`. - **`reversi8.c`**: funciones que contienen la lógica del juego reversi, las funciones propias del juego, su desarrollo y las llamadas a los dispositivos o a las librerias que controlan los dispositivos del juego. - **`main.c`**: Programa principal de juego que contiene la lógica principal de juego reversi, así como la inicialización de los dispositivos necesarios. - **`definiciones.h`**: se definen varios flags y tipos útiles para el resto del proyecto. ### Abstracción de *hardware* Tanto para facilitar el desarrollo del proyecto con el simulador, como para ofrecer portabilidad del juego en cuanto a plataformas se refiere, se ha de definir una línea entre lo que separa código cómun entre plataformas y el específico de dispositivos. Dado que el juego se ha escrito en su mayoría en C, se pueden emplear sus recursos de *compilación condicional*. Esto quiere decir que dadas X definiciones, el compilador empleará sólo el código que estas definiciones especifiquen. En el módulo `definiciones` se añade una definición (`#define`) para una plataforma y el código específco para esta plataforma se establece de la siguiente manera: ```c #ifdef <plataforma1> // código de plataforma1 #else // código de plataforma2 #endif ``` ## Metodología de trabajo El entorno de trabajo empleado en esta entrega no sufre grandes cambios respecto a la anterior. Se usa el entorno de desarrollo Eclipse con soporte de compilación cruzada para ARM. Herramientas adicionales se usan para depurar el juego sobre la placa Embest o sobre un *simulador*. Recordando el objetivo de desarrollar el juego con cierta portabilidad de hardware, se realizan las pruebas oportunas tanto en placa como en simulador, buscando el mismo comportamiento en el juego. Es por lo cual, el código adaptado a la simulación introducirá esperas activas, con el propósito de poder ofrecer la información que un jugador daría interactuando con la placa. Además, debido a la falta de tiempo y con la ayuda de los profesores se desarrolló un plan de integración. Este estuvo basado en versiones de **git** para probar rápidamente en la placa las mejoras realizadas en el simulador. Este método se ha mostrado muy efectivo y se empleará en futuros proyectos. También, en el proceso de desarrollo, se han realizado programas de prueba para testar comportamientos fuera del comportamiento normal del juego. Por ejemplo, se fuerza cierta excepción para comprobar que se abandona el flujo del juego y que se estanque la ejecución en un lugar del programa donde sea fácil identificar la causa de excepción. ## Desarrollo del proyecto En esta entrega se podrá observar la adopción de nuevos comportamientos en el juego Reversi. Algunos son fundamentales para la interacción en el juego, otros aportan información para depuración y otros pueden ser puramente estéticos. Pero todos ellos contribuyen a que el prototipo pueda ser ejecutado de manera autónoma sin la necesidad de estar conectado a un ordenador. ### Interacción jugador - juego Reversi Se han desarrollado dos formas principales de interacción con la máquina que porta el juego Reversi. Una primera versión del juego presenta como controles dos botones y el 8-LED, mientras que una segunda versión descarta el 8-LED a favor de una LCD táctil. #### Primera versión: Juego por botones Se aprovechan dos botones de la placa para jugar al juego Reversi. Para ello, se debe implementar un módulo que recoja las interrupciones generadas por el botón y que les asigne un comportamiento específico. Al pulsar el botón se pueden generar más interrupciones de las deseadas (lo cual se denomina *rebotes*). Otro módulo se encargará de gestionar estos rebotes para detectar una única interrupción bajo la pulsación de un botón. Este comportamiento se describe mediante unas fases: ![Imagen antirrebotes](https://i.imgur.com/z5TwaQP.png) Finalmente, se diseña un módulo donde se define el comportamiento que deben adoptar loso botones. El botón izquierdo seleccionará el número de fila o columna (`8` si se quiere pasar el turno), y el botón derecho confirmará la selección. Para hacer saber al usuario si va a marcar la fila o la columna, o su número, se emplea el *8-Led*. Este dispositivo mostrará `F` cuando se pida un número de fila, `C` cuando se pida el número de columna y el número marcado se mostrará a continuación. A medida que se pulsa el botón izquierdo, el número incrementará, retornando a `0` si supera el valor máximo (`8`). La mecánica que describe la jugada por botones se puede observar aquí: ![automata jugada](https://i.imgur.com/r9zLlx2.png) #### Segunda versión: Juego en LCD táctil Se descarta el *feedback* que ofrecía el 8-LED al intentar marcar una jugada, en pro del uso de una LCD que muestre el tablero de juego e información relevante. Los botones adoptarán otro comportamiento al visto en la primera versión, aunque aprovechando la funcionalidad antirrebotes. Se ha diseñado un módulo `lcdUtils` que se encargue de *plasmar* el tablero y otros elementos del juego en la pantalla LCD. Entre estos elemenos destaca: una secuencia de `X` a marcar para calibrar el táctil, una pantalla inicial de bienvenida al juego, la pantalla principal con el tablero e infromación sobre movimientos y el tiempo de juego, y finalmente, una pantalla de resultados. ![Tablero de juego Reversi en la LCD](https://i.imgur.com/Zu9cQtT.png) Los botones tendrán otro comportamiento respecto a la anterior versión. Cuando sea el turno del jugador (`CANDIDATA`), indicarán de forma incremental fila y columna (en botones derecho e izquierdo respectivamente) y en el tablero se mostrará la jugada candidata. Para aceptar la jugada se deberá tocar el panel táctil. El jugador tendrá 2 segundos para cancelar la jugada (`ESPERANDO`), siendo cancelar tocar de nuevo la pantalla o pulsar un botón. Una vez se haya aceptado la jugada, la máquina realizará su movimiento (`LISTO`). Se puede describir este comportamiento de la siguiente forma: ![](https://i.imgur.com/rtiMtZj.png) ### Captura de excepciones En todo proyecto informático que se precie se han de tener en cuenta casos excepcionales a un comportamiento normal. Del mismo modo, se han de desarrollar mecanismos que permitan identificar estos casos anómalos en el flujo de ejecución del juego. Esto puede deberse a un error informático en la programación o incluso, un fallo de *hardware*. En concreto, este proyecto abarcará excepciones de tres tipos: ***Software Interrupt***, ***Undefined*** y ***Data Abort***. Se diseña un módulo `exceptionHandler` que permita identificar a las excepciones mencionadas y que les proporcione un comportamiento (*rutina de servicio*). Las tres excepciones se capturarán y el juego entrará en un bucle infinito mostrando en el 8-LED un número identificativo para reconocer la excepción. ### Parpadeo de LED En un juego no pueden faltar elementos decorativos, y es por ello por lo que se aprovecha un dispositivo que atraiga al jugador. Este dispositivo es un LED que parpadea cada medio segundo a lo largo de toda la ejecución del juego. Un módulo `led` ayudará a manejar el dispositivo LED, y su parpadeo gracias a un número definido de interrupciones del *timer0*. ### Calibración Como ya se ha dicho, la interacción con el juego además que por medio de los botones se hace a través de la pantalla. Para el uso preciso de la pantalla es necesario calibrarla previamente. Este proceso consiste en crear una relación entre el punto en el que se toca la pantalla táctil y la pantalla LCD que en un primer momento no tienen nada que ver, aunque físicamente parezca el mismo componente. Para realizar el calibrado se van realizando lecturas de los toques de ocho puntos de la pantalla, en cada punto se toman varios valores para obtener la media (debido a la baja precisión de la pantalla) y a partir de los toques obetindos se obtienen los máximos y mínimos valores que toman las coordenadas X e Y de la pantalla. Por el tamaño de la pantalla LCD, los píxeles reales tienen un valor máximo de 320 en el eje X y 240 en el eje Y. Sin embargo, como ya se ha dicho los valores leidos por las funciones de la librería para interaccionar con la pantalla táctil no se corresponden con los de la pantalla LCD. Por ello, se comprueba que el valor leído sea mayor o igual que el X e Y mínimo que se ha cogido, y se hace una serie de funciones lineales que los relacione. <br/> $$ X_{leido} < X_{min} ~\rightarrow~ X_{real} = \frac{320~\cdotp(X_{min} - X_{leido})}{X_{max} - X_{min}} $$ $$ X_{leido} > X_{min} ~\rightarrow~ X_{real} = \frac{320~\cdotp(X_{leido} - X_{min})}{X_{max} - X_{min}} $$ $$ Y_{leido} < Y_{max} ~\rightarrow~ Y_{real} = \frac{240~\cdotp(Y_{max} - Y_{leido})}{Y_{max} - Y_{min}} $$ $$ Y_{leido} > Y_{max} ~\rightarrow~ Y_{real} = \frac{320~\cdotp(Y_{leido} - Y_{max})}{Y_{max} - Y_{min}} $$ <br/> La realidad, es que los valores tomados por la pantalla son muy dispares e incluso toman valores extraños algunas muestras iniciales en algunas ocasiones. Por ello, se ha realizado un diseño que maximice la jugabilidad disminuyendo lo máximo posible el toque de zonas muy precisas de la pantalla. ### Cambio modo usuario El modo SVC, en teoría, debe ser utilizado para operaciones con permisos especiales o que requieran acceder a recursos propios de la placa. En este caso solamente sería necesario para la edición de varios registros de configuración. Toda esta configuración se hace al principio del programa, para configurar la placa, los timers, el 8-LED, los botones y la pantalla LCD, así como su funcionalidad táctil. Tras configurar todo esto se llama a una función que se ha creado en ensamblador `cambiarUsuario`, que realiza el cambio de modo SVC a modo usuario. Esta función podría dar problemas si se llama desdefuera del main del programa por ello, como se indicó por parte del profesoradose ya que no se realiza una copia de la pila SVC a la pila USR y dicha información se pierde. Somos conscientes de dicho problema, pero como no realizamos ninguna de las condiciones de fallo es perfectamente usable en nuestro programa. Además, podría dar problemas en el caso de que hubiera variables locales inicializadas ya que no se guarda una copia de la pila SVC en la de USR. Para el prototipo desarrollado en concreto esto no ocasiona ningún problema. Sin embargo, se considera que en hipotéticas ampliaciones del proyecto podría ocasionar problemas. ```ASM cambiarModoUsuario: mov r2, lr mrs r0, cpsr bic r0, r0, #MODEMASK orr r1, r0, #USERMODE msr cpsr_cxsf, r1 ldr sp, =UserStack mov pc, r2 ``` ### Mejoras adicionales Otros aspectos que pueden resultar interesantes en el juego también se han desarrollado. Esto abarca un juego por botones con autoincremento, y unas correcciones sobre las asignaciones de zonas de memoria. #### Botones con autoincremento Bajo la medida de lo posible, se ha tratado desarrollar una mejora sobre la interacción con los botones. Así pues, un botón al ser mantenido debería ir incrementando el contador de columna o fila a un ritmo razonable. Por desgracia, este comportamiento queda pendiente de ser *pulido*. Por ello, se entregan sus respectivas líneas de código comentadas. Dichas líneas se pueden encontrar en el módulo `jugada_por_botones` de la entrega anterior. #### Una mejor definición de zonas de memoria Se han realizado cambios respecto al *linker script* que había sido proporcionado. El problema es que no se realizaban correctamente las alinciones en algunos casos determinados, en concreto dependiendo del tipo del último dato almacenado antes de una etiqueta. Para arreglarlo, se ha añadido un `. = ALIGN (4);` en la zona decladara en el linker de datos, esto hace que las etiquetas quedan alineadas y se solucione el posible problema. ### Carga de memoria en RAM En general, la dirección de la gestión del proyecto ha sido ,poco a poco, ir independizando la placa del ordenador desde el que se desarrolla. Desde un primer prototipo del juego que se ejecutaba completamente sobre la RAM, y se podía observar desde OpenOCD. Por tanto, es normal que el último paso del proyecto seaes *flashear* el programa en la RAM de la placa para que éste sea ejecutado cada vez que se reinicia la placa. Para conseguir esto se ha editado la carga del código en memoria de la placa, esto se hace desde la función de ResetHandler en `44binit.asm`, la cual es llamada al reiniciar la placa. El código modificado se presenta a continuación. ```ASM ldr r0,=(SMRDATA-0xc000000) ldmia r0,{r1-r13} ldr r0,=0x01c80000 /* BWSCON Address */ stmia r0,{r1-r13} LDR r0,=0x0 LDR r1,=Image_RO_Base LDR r3,=Image_ZI_Base LoopRw: cmp r1, r3 ldrcc r2, [r0], #4 strcc r2, [r1], #4 bcc LoopRw /* código nuevo (Darío) */ LDR r0, =Image_ZI_Base LDR r1, =Image_ZI_Limit mov r3, #0 LoopZI: cmp r0, r1 strcc r3, [r0], #4 bcc LoopZI /* fin código nuevo (Darío) */ ``` ### Proyecto adaptable al *hardware* Tal y como ha quedado definido el proyecto, basta con modificar una única línea de código en todo el proyecto para poder ejecutar el juego en placa o en simulador. Para ser precisos, esta línea se encuentra en el fichero `definiciones.h`: ```c ... #define SIMULADOR 1 //Comentar para ejecutar sobre la placa ... ``` ### Correcciones y cuestiones sobre entregas anteriores Se ha mejorado el comportamiento defenido en los *timers*, obteniendo el efecto deseado y con un coste computacional algo menor. También, se ha descartado la mejora de autoincremento que se había tratado de implementar en una primera versión. Su comportamiento no terminó de ofrecer el resultado esperado y se optó por invertir los esfuerzos en el desarrollo de otros elementos del proyecto. ## Guía de juego Reversi Para facilitar al usuario la interacción con el juego en una placa Embest S3CEV40, se han elaborado las siguientes instrucciones: 1. Al encender la placa se mostrará una `X` en una de las esquinas de la pantalla LCD. Se debe pulsar sobre esta `X`, y del mismo modo con las siguientes que aparezcan. Una vez pulsadas todas las `X`, habrá finalizado el **proceso de calibración** del panel táctil. 2. Se le presentará una pantalla de bienvenida al juego. El usuario puede presionar uno de los botones o el panel táctil para continuar. 3. Ahora se le mostrará el tablero inicial, además del tiempo de juego transcurrido y el tiempo que *ha estado pensando* su moviemiento la máquina. Esto último aparecerá en blanco al inicio, ya que empieza jugando el usuario. El usuario tiene varias opciones: 3.a. Puede usar el botones para fijar una jugada. El botón izquierdo moverá la ficha a la derecha (retornando al extremo izquierdo si llega a la útlima casilla), y el botón izquierdo moverá la ficha hacia abajo (retornando al extremo superior si llega a la última casilla). Una vez el jugador haya quedado satisfecho con un movimiento **válido**, tocará el panel táctil. 3.b. Si el usuario desea pasar turno, debe fijar una jugada **no válida**. Se interpretará como no válida si la ficha no es contigua a ninguna otra o está encima de otra ficha. 4. El jugador tiene dos segundos para cancelar su movimiento. Si lo desea cancelar, debe presionar un botón o tocar el panel táctil. Si está seguro de su movimiento, deberá esperar este tiempo. 5. El oponente (la máquina) realizará una jugada en respuesta al movimiento anterior del jugador. El jugador podrá conocer cuanto ha tardado el oponente en *pensar* a jugada, como se muestra en pantalla. El juego continua tal y como se indica en el punto 3. 6. Una vez se haya llenado el tablero, se mostrará una pantalla final indicando el ganador de la partida. Para juegar una nueva partida, se debe pulsar un botón o tocar el panel táctil. El juego comenzará a partir del paso 2. ### Diagrama de secuencia de juego Las instrucciones anteriores se pueden reflejar en el siguiente diagrama de secuencia: ![](https://i.imgur.com/dZ44Ffz.png) ## Problemas encontrados A lo largo del trabajo, se han encontrado varios problemas, algunos más faciles de resolver que otros. Sobre todo los que mas tiempo. nos han llevado son los debidos a la propia configuración del proyecto, así como incompatibilidades entre sistemas operativos. También se ha tenido problemas con la pantalla LCD (se ralentizaba) que han sido resueltos haciendo una entrada/salida mas eficiente, como se ha indicado anteriormente. Además, se ha tenido problemas con la mala precisión de la funcionalidad táctil de la pantalla y también han sido solucionados En las configuraciones de las interrupciones se ha invertido bastante tiempo yaque se modificó algún bit que no había de ser modificado. ## Resultados Tras todo el desarrollo que se ha llevado a cabo por los objetivos anteriormente definidos, se puede defender dos grandes características en la evolución del proyecto: independizar la placa del uso de un ordenador externo para su manejo, facilitar la adaptación del juego a hardware diferente al utilizado, y una forma más natural de interacción con el juego. ## Conclusiones En general, estamos muy satisfechos con el resultado obtenido y con el trabajo que ha realizado. Queremos destacar las técnicas de bajo nivel usadas, como las optimizaciones al código ensamblador de la primera práctica y la interacción con periféricos, que se habían visto ya en otras asignaturas pero no puestas en práctica. El prototipo funciona sin ningún tipo de problema compilado con -O2, una vista empírica del efecto de las optimizaciones del compilador en el programa es una enseñanza fundamental que nos llevamos todos los alumnos de la asignatura. Hemos desarrollado un prototipo estable, eficiente, con un flujo persona-ordenador eficiente, con fichas redondas y distinguibles y con todas las funcionalidades básicas necesarias implementadas. Además, se han realizado varias mejoras, el patrón volteo iterativo, la optimización del linker, los botones que se autoincrementan y el uso de FIQs aunque estos dos últimos aún han de ser depurados. ## Anexo Se presenta el código del proyecto en un anexo: ### `botones_antirebotes.c` ```c /********************************************************************************** * Fichero: botones_antirebotes.c * Autores: Paul Hodgetts y Pedro Ramoneda * Descrip: funciones del control de antirebotes de botones del s3c44b0x * Versión: 20.11.18 **********************************************************************************/ #include "botones_antirebotes.h" #include "definiciones.h" #include "timer2.h" #include "button.h" enum estado_botones_antirebotes { inicio, esp_inicial, esp_activa, final }; enum { tiempo_espera_inicial = 200000, tiempo_espera_activa = 20000, tiempo_espera_final = 100000 }; static volatile enum estado_button boton_actual = button_none; static volatile enum estado_botones_antirebotes estado_actual = inicio; static volatile unsigned int tiempo_referencia; void botones_antirebotes_callback(enum estado_button boton_presionado); void botones_antirebotes_iniciar(void) { estado_actual = inicio; boton_actual = button_none; button_empezar(&botones_antirebotes_callback); } void botones_antirebotes_callback(enum estado_button boton_presionado) { estado_actual = esp_inicial; boton_actual = boton_presionado; tiempo_referencia = timer2_leer(); // push_debug(0xFF, tiempo_referencia); } enum estado_button botones_antirebotes_leer() { enum estado_button ans = boton_actual; //boton_actual = button_none; return ans; } void botones_antirebotes_gestion(unsigned int ahora) { switch (estado_actual) { case inicio: break; case esp_inicial: if (ahora - tiempo_referencia > tiempo_espera_inicial) { //push_debug(esp_inicial, 0); estado_actual = esp_activa; tiempo_referencia = ahora; } break; case esp_activa: if (ahora - tiempo_referencia > tiempo_espera_activa) { enum estado_button boton_nuevo = button_estado(); //push_debug(esp_activa, boton_nuevo); tiempo_referencia = ahora; if (boton_nuevo != boton_actual) { boton_actual = boton_nuevo; estado_actual = final; } } break; case final: if (ahora - tiempo_referencia > tiempo_espera_final) { //push_debug(final, 0); estado_actual = inicio; button_empezar(&botones_antirebotes_callback); } break; } } ``` --- ### `botones_antirebotes.h` ```c /********************************************************************************** * Fichero: botones_antirebotes.h * Autores: Paul Hodgetts y Pedro Ramoneda * Descrip: cabecera del control de antirebotes de botones del s3c44b0x * Versión: 20.11.18 **********************************************************************************/ #ifndef _BOTONES_ANTIREBOTES_H_ #define _BOTONES_ANTIREBOTES_H_ #include "button.h" #include "lista_circ.h" void botones_antirebotes_iniciar(void); enum estado_button botones_antirebotes_leer(); void botones_antirebotes_gestion(unsigned int tiempo_actual); #endif /* _BOTONES_ANTIREBOTES_H_ */ ``` --- ### `button.c` ```c /********************************************************************************************* * Fichero: button.c * Autor: Paul Hodgetts y Pedro Ramoneda * Descrip: Funciones de manejo de los pulsadores (EINT6-7) * Version: 20.11.18 *********************************************************************************************/ /*--- ficheros de cabecera ---*/ #include "button.h" #include "8led.h" #include "definiciones.h" #include "lista_circ.h" enum bit_boton { bit_boton_izq = 6, bit_boton_der = 7 }; static funcion_callback_button *button_callback; #ifndef SIMULADOR #include "44blib.h" #include "44b.h" static unsigned int contador = 0; /* declaración de función que es rutina de servicio de interrupción * https://gcc.gnu.org/onlinedocs/gcc/ARM-Function-Attributes.html */ void button_ISR(void) __attribute__((interrupt("IRQ"))); /*--- codigo de funciones ---*/ void button_ISR(void) { rINTMSK |= BIT_EINT4567; // Comprobar que hay una funcion en el callback y llamarla si la hay button_callback(button_estado()); push_debug(222,contador); contador++; /* Finalizar ISR */ rEXTINTPND = 0xf; // borra los bits en EXTINTPND rI_ISPC |= BIT_EINT4567; // borra el bit pendiente en INTPND } // Inicializaci�n de los botones void button_iniciar(void) { /* Configuracion del controlador de interrupciones. Estos registros están definidos en 44b.h */ // Se quitan PND y EXTINTPND ya que se eliminan las peticiones pendientes en la llamada a sys_init() // rI_ISPC = 0x3ffffff; // Borra INTPND escribiendo 1s en I_ISPC rEXTINTPND = 0xf; // Borra EXTINTPND escribiendo 1s en el propio registro rINTMOD &= ~(0x200000); // Configura las linas como de tipo IRQ //rINTMSK &= ~(BIT_EINT4567); // habilitamos interrupcion linea eint4567 en vector de mascaras /* Establece la rutina de servicio para Eint4567 */ pISR_EINT4567 = (int)button_ISR; /* Configuracion del puerto G */ rPCONG = 0xffff; // Establece la funcion de los pines (EINT0-7) rPUPG = 0x0; // Habilita el "pull up" del puerto rEXTINT |= 0x22222222; // Configura las lineas de int. como de flanco de bajada /* Por precaucion, se vuelven a borrar los bits de INTPND y EXTINTPND */ rI_ISPC |= BIT_EINT4567; rEXTINTPND = 0xf; } // Activa la atenci�n de las interrupciones de bot�n. void button_empezar(funcion_callback_button *funcion_callback) { button_callback = funcion_callback; rEXTINTPND = 0xf; rINTMSK &= ~(BIT_EINT4567); // habilitamos interrupcion linea eint4567 en vector de mascaras } // Devuelve el estado actual del bot�n. enum estado_button button_estado(void) { char boton_izquierdo_pulsado = ~rPDATG & (1 << bit_boton_izq); char boton_derecho_pulsado = ~rPDATG & (1 << bit_boton_der); if (boton_derecho_pulsado && boton_izquierdo_pulsado) { return button_dr; } else if (boton_derecho_pulsado) { return button_dr; } else if (boton_izquierdo_pulsado) { return button_iz; } return button_none; } #else // Inicializaci�n de los botones void button_iniciar(void) {} // Activa la atenci�n de las interrupciones de bot�n. void button_empezar(funcion_callback_button *funcion_callback) { button_callback = funcion_callback; } // Devuelve el estado actual del bot�n. enum estado_button button_estado(void) { char boton_izquierdo_pulsado = 0; char boton_derecho_pulsado = 0; char ready = 0; while (!ready) {} if (boton_derecho_pulsado && boton_izquierdo_pulsado) { return button_dr; } else if (boton_derecho_pulsado) { return button_dr; } else if (boton_izquierdo_pulsado) { return button_iz; } return button_none; } #endif ``` --- ### `button.h` ```c /********************************************************************************************* * Fichero: button.h * Autor: Paul Hodgetts y Pedro Ramoneda * Descrip: Cabecera de manejo de los pulsadores (EINT6-7) * Version: 20.11.18 *********************************************************************************************/ #ifndef _BUTTON_H_ #define _BUTTON_H_ enum estado_button { button_none = 0, button_iz = 4, button_dr = 8, }; typedef void(funcion_callback_button)(enum estado_button); // Inicializaci�n de los botones void button_iniciar(void); // Activa la atenci�n de las interrupciones de bot�n. void button_empezar(funcion_callback_button *funcion_callback); // Devuelve el estado actual del bot�n. enum estado_button button_estado(void); #endif /* _BUTTON_H_ */ ``` --- ### `definiciones.h` ```c /********************************************************************************************* * File: DEF.H * Author: embest * Desc: data type define * History: *********************************************************************************************/ #ifndef __DEFINICIONES_H_ #define __DEFINICIONES_H_ //#define SIMULADOR 1 //Comentar para ejecutar sobre la placa #ifndef ULONG #define ULONG unsigned long #endif #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #endif /* __DEFINICIONES_H_ */ ``` --- ### `exceptionHandler.c` ```c #include <stdint.h> #include "8led.h" #include "exceptionHandler.h" #include "definiciones.h" enum tipo_8led { OTRO_8LED = 0xF, SWI_8LED = 0x1, DABT_8LED = 0x2, UDEF_8LED = 0x3 }; #ifndef SIMULADOR #include "44b.h" enum tipo_interrupcion { SWI = 0x13, DABT = 0x17, UDEF = 0x1B, }; enum { DELAY_8LED = 5000, BLANK_8LED = 16, }; void exception_bucle(enum tipo_8led numero_8led); void exception_tratamiento(void); void exception_inicializar(void) { pISR_UNDEF = (unsigned)exception_tratamiento; pISR_SWI = (unsigned)exception_tratamiento; pISR_DABORT = (unsigned)exception_tratamiento; } void exception_tratamiento(void) { uint32_t interrupcion; uint32_t dir_instruccion; enum tipo_8led num_8led; __asm__ volatile( "mrs %0, cpsr\n" "and %0, %0, #0x1F" : "+r"(interrupcion)); __asm__ volatile( "mov %0, lr" : "=r"(dir_instruccion)); if (interrupcion == SWI) { dir_instruccion -= 4; num_8led = SWI_8LED; } else if (interrupcion == DABT) { dir_instruccion -= 8; num_8led = DABT_8LED; } else if (interrupcion == UDEF) { dir_instruccion -= 4; num_8led = UDEF_8LED; } else { num_8led = OTRO_8LED; } D8Led_init(); while (1){ D8Led_symbol(num_8led); Delay(DELAY_8LED); D8Led_symbol(BLANK_8LED); Delay(DELAY_8LED); } } #endif ``` --- ### `exceptionHandler.h` ```c #ifndef _EXCEPTIONS_H_ #define _EXCEPTIONS_H_ void exception_inicializar(void); #endif /* _EXCEPTIONS_H_ */ ``` --- ### `jugada_por_botones.c` ```c /********************************************************************************** * Fichero: jugada_por_botones.c * Autores: Paul Hodgetts y Pedro Ramoneda * Descrip: funciones del control de juego por botones del s3c44b0x * Versión: 20.11.18 **********************************************************************************/ #include "jugada_por_botones.h" #include "definiciones.h" #include "8led.h" #include "botones_antirebotes.h" #include "tp.h" enum { paso_tiempo = 200 }; static char fila = 0, columna = 0; static char aceptada = 0; static unsigned int ult_lectura = 0; enum estado_button ultimo_leido = button_none; /********************************************************************************************* * name: jugada_lista() * func: Devuelve 1 si la jugada esta lista, 0 en caso contrario * para: none * ret: 1 si la jugada esta lista, 0 en caso contrario * modify: none * comment: *********************************************************************************************/ char jugada_lista() { return aceptada; } /********************************************************************************************* * name: jugada_tomada() * func: Establece si se ha procesado la ultima jugada * para: none * ret: none * modify: none * comment: *********************************************************************************************/ void jugada_tomada() { aceptada = 0; } /********************************************************************************************* * name: num_fila() * func: Devuelve el numero de fila indicado en la jugada actual * para: none * ret: numero de fila actual * modify: none * comment: *********************************************************************************************/ char num_fila() { return fila; } /********************************************************************************************* * name: num_col() * func: Devuelve el numero de columna indicado en la jugada actual * para: none * ret: numero de columna actual * modify: none * comment: *********************************************************************************************/ char num_col() { return columna; } /********************************************************************************************* * name: reset_fila_columna() * func: Reestablece el numero de fila y columna para una nueva jugada * para: none * ret: none * modify: none * comment: *********************************************************************************************/ void reset_fila_columna() { columna = 0; fila = 0; } /********************************************************************************************* * name: jugada_por_botones_gestion() * func: Actualiza el estado del juego en función de los botones pulsados o touchscreen * para: estado: estado de juego actual * ahora: tiempo actual * ret: none * modify: estado: actualiza el estado de juego en función de las entradas * comment: *********************************************************************************************/ void jugada_por_botones_gestion (jugada * estado, unsigned int ahora) { enum estado_button boton_leido = botones_antirebotes_leer(); // El juego está esperando un tiempo para aceptar o cancelar jugada if (*estado == ESPERANDO) { // El jugador cancela el movimiento if ((ultimo_leido == button_none && boton_leido != button_none) || getTouch() != 0) { *estado = CANDIDATA; aceptada = 0; } // Ha pasado un tiempo y el jugador no ha cancelado el movimiento else if (ahora - ult_lectura >= paso_tiempo) { *estado = LISTO; } } // El juego espera que se le especifique una jugada else if (*estado == CANDIDATA) { // El jugador presiona el boton derecho if (ultimo_leido == button_none && boton_leido == button_dr) { fila++; if (fila > 7) fila = 0; ultimo_leido = button_dr; } // El jugador presiona el boton izquierdo else if (ultimo_leido == button_none && boton_leido == button_iz) { columna++; if (columna > 7) columna = 0; ultimo_leido = button_iz; } else if (ultimo_leido != button_none && boton_leido == button_none) { ultimo_leido = button_none; } // El jugador presiona el touchscreen if (getTouch()) { aceptada = 1; *estado = ESPERANDO; // --> El jugador tiene un tiempo para cancelar ult_lectura = ahora; } } } ``` --- ### `jugada_por_botones.h` ```c /********************************************************************************** * Fichero: jugada_por_botones.h * Autores: Paul Hodgetts y Pedro Ramoneda * Descrip: cabecera del control de juego por botones del s3c44b0x * Versión: 20.11.18 **********************************************************************************/ #ifndef JUGADA_POR_BOTONES_H_ #define JUGADA_POR_BOTONES_H_ // Estado de la jugada typedef enum { LISTO = 0, // Se ha realizado una jugada CANDIDATA = 1, // El jugador puede cancelar su movimiento ESPERANDO = 2 // Se espera a que el jugador finalice su turno } jugada; /********************************************************************************************* * name: jugada_lista() * func: Devuelve 1 si la jugada esta lista, 0 en caso contrario * para: none * ret: 1 si la jugada esta lista, 0 en caso contrario * modify: none * comment: *********************************************************************************************/ char jugada_lista(); /********************************************************************************************* * name: jugada_tomada() * func: Establece si se ha procesado la ultima jugada * para: none * ret: none * modify: none * comment: *********************************************************************************************/ void jugada_tomada(); /********************************************************************************************* * name: num_fila() * func: Devuelve el numero de fila indicado en la jugada actual * para: none * ret: numero de fila actual * modify: none * comment: *********************************************************************************************/ char num_fila(); /********************************************************************************************* * name: num_col() * func: Devuelve el numero de columna indicado en la jugada actual * para: none * ret: numero de columna actual * modify: none * comment: *********************************************************************************************/ char num_col(); /********************************************************************************************* * name: reset_fila_columna() * func: Reestablece el numero de fila y columna para una nueva jugada * para: none * ret: none * modify: none * comment: *********************************************************************************************/ void reset_fila_columna(); /********************************************************************************************* * name: jugada_por_botones_gestion() * func: Actualiza el estado del juego en función de los botones pulsados o touchscreen * para: estado: estado de juego actual * ahora: tiempo actual * ret: none * modify: estado: actualiza el estado de juego en función de las entradas * comment: *********************************************************************************************/ void jugada_por_botones_gestion(jugada * estado, unsigned int ahora); #endif /* JUGADA_POR_BOTONES_H_ */ ``` --- ### `lcdUtils.c` ```c #include "lcdUtils.h" static unsigned int tiempo_total=0; static unsigned int tiempo_calculo=0; static const int squareSize = 25; static const int margin = 30; const static char ASCI_total[]="T_total: "; const static char ASCI_calculo[]="T_calculos: "; const static char ASCI_colon[]=":"; const static char ASCI_ms[]="ms"; const static char ASCI_ganas[]="FELICIDADES, HAS GANADO!!!"; const static char ASCI_pierdes[]="OHHH QUE PENA, HAS PERDIDO!!!"; const static char ASCI_nuevo[]="PULSA PARA EMEPEZAR OTRA VEZ"; const static char ASCI_pasar[]="PASAR"; const static char ASCI_finalizar[]="FINALIZAR"; const static char mens[]="TOQUE LA PANTALLA PARA EMPEZAR"; /*Funcion que pasa un entero a una cadena de char*/ int miItoa(int i, char* buff){ int aux=i; int nDigits=0; while(aux!=0){ nDigits++; aux=aux/10; } int j=0; nDigits++; if (i<0) {nDigits++;} buff[nDigits-1]='\0'; if (i<0){ buff[0]='-'; i=abs(i); j=1; } int k=nDigits-2; while(i!=0){ aux=i%10; i=i/10; buff[k]='0'+aux; k--; } return nDigits-1; } const INT8U piezaNegra[] = { WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, BLACK, BLACK, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, WHITE, WHITE, WHITE, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, WHITE, WHITE, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, WHITE, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, WHITE, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, WHITE, WHITE, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, WHITE, WHITE, WHITE, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, BLACK, BLACK, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, }; const INT8U piezaBlanca[] = { WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, BLACK, BLACK, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, BLACK, WHITE, WHITE, WHITE, WHITE, BLACK, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, WHITE, WHITE, WHITE, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, WHITE, WHITE, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, WHITE, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, WHITE, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, WHITE, WHITE, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, WHITE, WHITE, WHITE, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, BLACK, WHITE, WHITE, WHITE, WHITE, BLACK, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, BLACK, BLACK, BLACK, BLACK, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, }; const INT8U piezaBorrar[] = { WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, }; const INT8U piezaGray[] = { WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, WHITE, WHITE, WHITE, WHITE, WHITE, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, WHITE, WHITE, WHITE, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, WHITE, WHITE, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, WHITE, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, WHITE, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, WHITE, WHITE, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, WHITE, WHITE, WHITE, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, WHITE, WHITE, WHITE, WHITE, WHITE, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, DARKGRAY, DARKGRAY, DARKGRAY, DARKGRAY, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, }; STRU_BITMAP Stru_Bitmap_gbBlanca = {0x10, 4, 16, 16, TRANSPARENCY, (INT8U *)piezaBlanca}; STRU_BITMAP Stru_Bitmap_gbNegra = {0x10, 4, 16, 16, TRANSPARENCY, (INT8U *)piezaNegra}; STRU_BITMAP Stru_Bitmap_gbBorrar = {0x10, 4, 16, 16, TRANSPARENCY, (INT8U *)piezaBorrar}; STRU_BITMAP Stru_Bitmap_gbGray = {0x10, 4, 16, 16, TRANSPARENCY, (INT8U *)piezaGray}; /********************************************************************************************* * name: mostrarTiempoTotal(int tiempo) * func: Muestra el tiempo total de juego que ha transcurrido. * para: tiempo: tiempo total en segundos * ret: none * modify: none * comment: *********************************************************************************************/ void mostrarTiempoTotal(int tiempo){ Lcd_DspAscII8x16(260, 8, BLACK, "Tiempo"); Lcd_DspAscII8x16(260, 32, BLACK, "total"); INT8U ans[] = ""; miItoa(tiempo, ans); Lcd_DspAscII8x16(260, 64, BLACK, ans); } /********************************************************************************************* * name: mostrarTiempoCalculo(int tiempo) * func: Muestra el tiempo de calculo para el ultimo movimiento * para: tiempo: tiempo de calculo en microsegundos * ret: none * modify: none * comment: *********************************************************************************************/ void mostrarTiempoCalculo(int tiempo){ Lcd_DspAscII8x16(260, 128, BLACK, "Tiempo"); Lcd_DspAscII8x16(260, 152, BLACK, "calc."); INT8U ans[] = ""; miItoa(tiempo, ans); Lcd_DspAscII8x16(260, 184, BLACK, ans); } /********************************************************************************************* * name: Lcd_Tablero(...) * func: Muestra el tablero de juego con las fichas en juego y una candidata * si es turno del usuario * para: tablero: matriz de fichas de tablero reversi filaCand: fila de ficha candidata columnaCand: columna de ficha candidata mostrarCandidata: 1 si hay que mostrar ficha candidata, 0 en caso contrario estado: estado de la jugada (ver `struct jugada`) * ret: none * modify: none * comment: *********************************************************************************************/ void Lcd_Tablero(char tablero[][8], int filaCand, int columnaCand, char mostrarCandidata, jugada estado) { const int ajuste = (squareSize - 16)/2; /* clear screen */ Lcd_Clr(); //Lcd_Active_Clr(); int i = 0, j = 0; char out = 0; for (i = 0; i < 8; i++) { out = 48+i; Lcd_DspAscII8x16(i*squareSize + margin + squareSize/3, 5, BLACK, &out); out = 48+i; Lcd_DspAscII8x16(margin - 2*squareSize/3, squareSize + i*squareSize, BLACK, &out); } for (i = 0; i < 8; i++) { for (j = 0; j < 8; j++) { Lcd_Draw_Box(i*squareSize + margin, j*squareSize + 20, i*squareSize + margin + squareSize-1, j*squareSize + 20 + squareSize-1, 14); } } for (i = 0; i < 8; i++) { for (j = 0; j < 8; j++) { if (tablero[i][j] == 1) { //PELIGRO quitar squareSize/3 para cuadrar ficha en el cuadrado BitmapView(j*squareSize + margin + ajuste, squareSize + i*squareSize, Stru_Bitmap_gbBlanca); } else if (tablero[i][j] == 2) { BitmapView(j*squareSize + margin + ajuste, squareSize + i*squareSize, Stru_Bitmap_gbNegra); } } } if (mostrarCandidata && estado == CANDIDATA){ Lcd_DspAscII8x16(10, 9*squareSize,BLACK,"Pulse para jugar"); if (filaCand < 8 || columnaCand < 8) BitmapView(columnaCand*squareSize + margin + ajuste, squareSize + filaCand*squareSize, Stru_Bitmap_gbGray); } else if (mostrarCandidata && estado == ESPERANDO) { Lcd_DspAscII8x16(10, 9*squareSize,BLACK,"Pulse para cancelar"); if (filaCand < 8 || columnaCand < 8) BitmapView(columnaCand*squareSize + margin + ajuste, squareSize + filaCand*squareSize, Stru_Bitmap_gbNegra); } tiempo_total = timer2_leer()/1000000; mostrarTiempoTotal(tiempo_total); Lcd_Draw_HLine(260,310,104,BLACK,2); mostrarTiempoCalculo(tiempo_calculo); Lcd_Dma_Trans(); } /* Boton de pasar de turno*/ void tecladoPasar(){ // Lcd_Draw_Box(); // Lcd_DspAscII8x16(); } /* Boton de finalizar partida*/ void tecladoFinalizar(){ // Lcd_Draw_Box(); // Lcd_DspAscII8x16(); } /********************************************************************************************* * name: resultadosFinales(int b, int n) * func: muestra el resultado de la partida * para: b: numero de fichas blancas n: numero de fichas negras * ret: none * modify: none * comment: *********************************************************************************************/ void resultadosFinales(int b, int n){ Lcd_Clr(); Lcd_Active_Clr(); if (b < n){ // GANASTE!!!!! Lcd_DspAscII8x16(50,40,BLACK,ASCI_ganas); } else { // PERDISTE!!!!! Lcd_DspAscII8x16(50,40,BLACK,ASCI_pierdes); } Lcd_DspAscII8x16(50,150,BLACK,ASCI_nuevo); Lcd_Dma_Trans(); } const static char x[]="X"; /********************************************************************************************* * name: setTiempoCalculo(...) * func: Actualiza el tiempo de calculo del movimiento de la máquina * para: t_calc: tiempo en microsegundos * ret: none * modify: none * comment: *********************************************************************************************/ void setTiempoCalculo(unsigned int t_calc) { tiempo_calculo = t_calc; } /********************************************************************************************* * name: pantalla_inicial() * func: muestra la pantalla inicial de juego y espera a que el usuario comience la partida * para: none * ret: none * modify: none * comment: *********************************************************************************************/ void pantalla_inicial() { Lcd_Clr(); Lcd_Active_Clr(); Lcd_DspAscII8x16(40,120,BLACK, mens); Lcd_Dma_Trans(); //esperar a que algo sea pulsado while (botones_antirebotes_leer()== 0 && getTouch()==0); Lcd_Clr(); Lcd_Active_Clr(); } /********************************************************************************************* * name: calibrar_reversi() * func: muestra una serie de pantallas con una X donde tocar para calibrar el touchscreen * para: none * ret: none * modify: none * comment: *********************************************************************************************/ void calibrar_reversi(){ /* Se inicializan los controladores de TS*/ TS_init(); /* clear de la pantalla inicial */ Lcd_Clr(); Lcd_Active_Clr(); //Lcd_DspAscII8x16(230,30,BLACK, s); Lcd_Dma_Trans(); /*calibrar al resetear la placa*/ calibrarInicio(); Lcd_Clr(); Lcd_Active_Clr(); } ``` --- ### `lcdUtils.h` ```c #include "jugada_por_botones.h" #include "lcd.h" #include "bmp.h" #include "tp.h" /********************************************************************************************* * name: Lcd_Tablero(...) * func: Muestra el tablero de juego con las fichas en juego y una candidata * si es turno del usuario * para: tablero: matriz de fichas de tablero reversi filaCand: fila de ficha candidata columnaCand: columna de ficha candidata mostrarCandidata: 1 si hay que mostrar ficha candidata, 0 en caso contrario estado: estado de la jugada (ver `struct jugada`) * ret: none * modify: none * comment: *********************************************************************************************/ void Lcd_Tablero(char tablero[][8], int filaCand, int columnaCand, char mostrarCandidata, jugada estado); /********************************************************************************************* * name: setTiempoCalculo(...) * func: Actualiza el tiempo de calculo del movimiento de la máquina * para: t_calc: tiempo en microsegundos * ret: none * modify: none * comment: *********************************************************************************************/ void setTiempoCalculo(unsigned int t_calc); /********************************************************************************************* * name: pantalla_inicial() * func: muestra la pantalla inicial de juego y espera a que el usuario comience la partida * para: none * ret: none * modify: none * comment: *********************************************************************************************/ void pantalla_inicial(); /********************************************************************************************* * name: calibrar_reversi() * func: muestra una serie de pantallas con una X donde tocar para calibrar el touchscreen * para: none * ret: none * modify: none * comment: *********************************************************************************************/ void calibrar_reversi(); ``` --- ### `main.c` ```c /********************************************************************************************* * Fichero: main.c * Autor: * Descrip: punto de entrada de C * Version: <P4-ARM.timer-leds> *********************************************************************************************/ /********************************************************************************************* * name: inicializar_dispositivos() * func: Prepara el hardware necesario para correr reversi en la placa EMBEST S3CEV40 * para: none * ret: none * modify: none * comment: *********************************************************************************************/ void inicializar_dispositivos(){ /* Inicializa controladores */ sys_init(); // Inicializacion de la placa, interrupciones y puertos exception_inicializar(); timer_init(); // Inicializacion del temporizador timer2_inicializar(); // Inicializacion de timer2 button_iniciar(); // inicializamos los pulsadores. Cada vez que se pulse se verá reflejado en el 8led D8Led_init(); // inicializamos el 8led botones_antirebotes_iniciar(); Lcd_Init(); /* Valor inicial de los leds */ leds_off(); led1_on(); } /********************************************************************************************* * name: cambiarModoUsuario() * func: Prepara el hardware para correr en modo usuario * para: none * ret: none * modify: none * comment: *********************************************************************************************/ extern void cambiarModoUsuario(); /*--- codigo de funciones ---*/ void Main(void) { #ifndef SIMULADOR inicializar_dispositivos(); cambiarModoUsuario(); calibrar_reversi(); #endif // Bucle infinito de partidas while(1){ reversi_main(); } } ``` --- ### `reversi.c` ```c /********************************************************************************************* * Fichero: reversi8.c * Autor: * Descrip: punto de entrada de C * Version: <P4-ARM.timer-leds> *********************************************************************************************/ #include "reversi8.h" static int cuadrante=0; static jugada estado_juego_global = CANDIDATA; static const char __attribute__ ((aligned (8))) tabla_valor[DIM][DIM] = { {8,2,7,3,3,7,2,8}, {2,1,4,4,4,4,1,2}, {7,4,6,5,5,6,4,7}, {3,4,5,0,0,5,4,3}, {3,4,5,0,0,5,4,3}, {7,4,6,5,5,6,4,7}, {2,1,4,4,4,4,1,2}, {8,2,7,3,3,7,2,8} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 0 indica CASILLA_VACIA, 1 indica FICHA_BLANCA y 2 indica FICHA_NEGRA // pone el tablero a cero y luego coloca las fichas centrales. void init_table(char tablero[][DIM], char candidatas[][DIM]) { int i, j; for (i = 0; i < DIM; i++) { for (j = 0; j < DIM; j++) tablero[i][j] = CASILLA_VACIA; } for (i = 3; i < 5; ++i) { for(j = 3; j < 5; ++j) { tablero[i][j] = i == j ? FICHA_BLANCA : FICHA_NEGRA; } } for (i = 2; i < 6; ++i) { for (j = 2; j < 6; ++j) { if((i>=3) && (i < 5) && (j>=3) && (j<5)) { candidatas[i][j] = CASILLA_OCUPADA; } else { candidatas[i][j] = SI; //CASILLA_LIBRE; } } } // arriba hay versi�n alternativa tablero[3][3] = FICHA_BLANCA; tablero[4][4] = FICHA_BLANCA; tablero[3][4] = FICHA_NEGRA; tablero[4][3] = FICHA_NEGRA; candidatas[3][3] = CASILLA_OCUPADA; candidatas[4][4] = CASILLA_OCUPADA; candidatas[3][4] = CASILLA_OCUPADA; candidatas[4][3] = CASILLA_OCUPADA; // casillas a explorar: candidatas[2][2] = SI; candidatas[2][3] = SI; candidatas[2][4] = SI; candidatas[2][5] = SI; candidatas[3][2] = SI; candidatas[3][5] = SI; candidatas[4][2] = SI; candidatas[4][5] = SI; candidatas[5][2] = SI; candidatas[5][3] = SI; candidatas[5][4] = SI; candidatas[5][5] = SI; } //////////////////////////////////////////////////////////////////////////////// // Espera a que ready valga 1. // CUIDADO: si el compilador coloca esta variable en un registro, no funcionar�. // Hay que definirla como "volatile" para forzar a que antes de cada uso la cargue de memoria char ficha_valida(char tablero[][DIM], char f, char c, int *posicion_valida) { char ficha; // ficha = tablero[f][c]; // no puede accederse a tablero[f][c] // ya que alg�n �ndice puede ser negativo if ((f < DIM) && (f >= 0) && (c < DIM) && (c >= 0) && (tablero[f][c] != CASILLA_VACIA)) { *posicion_valida = 1; ficha = tablero[f][c]; } else { *posicion_valida = 0; ficha = CASILLA_VACIA; } return ficha; } //////////////////////////////////////////////////////////////////////////////// // La funci�n patr�n volteo es una funci�n recursiva que busca el patr�n de volteo // (n fichas del rival seguidas de una ficha del jugador actual) en una direcci�n determinada // SF y SC son las cantidades a sumar para movernos en la direcci�n que toque // color indica el color de la pieza que se acaba de colocar // la funci�n devuelve PATRON_ENCONTRADO (1) si encuentra patr�n y NO_HAY_PATRON (0) en caso contrario // FA y CA son la fila y columna a analizar // longitud es un par�metro por referencia. Sirve para saber la longitud del patr�n que se est� analizando. Se usa para saber cuantas fichas habr�a que voltear int patron_volteo_iter(char tablero[][DIM], int *longitud, char FA, char CA, char SF, char SC, char color) { int posicion_valida; // indica si la posici�n es valida y contiene una ficha de alg�n jugador int patron = NO_HAY_PATRON; // indica si se ha encontrado un patr�n o no int encontrado; // indica si se ha terminado de comprobar el patrón char casilla; // casilla es la casilla que se lee del tablero FA = FA + SF; CA = CA + SC; // casilla = tablero[f][c]; // no puede accederse a tablero[f][c] // ya que alg�n �ndice puede ser negativo if ((FA < DIM) && (FA >= 0) && (CA < DIM) && (CA >= 0) && (tablero[FA][CA] != CASILLA_VACIA)) { posicion_valida = 1; casilla = tablero[FA][CA]; } else { posicion_valida = 0; casilla = CASILLA_VACIA; } // mientras la casilla est� en el tablero, no est� vac�a, // y es del color rival seguimos buscando el patron de volteo while (encontrado == 0) { if ((posicion_valida == 1) && (casilla != color)) { *longitud = *longitud + 1; FA = FA + SF; CA = CA + SC; // casilla = tablero[f][c]; // no puede accederse a tablero[f][c] // ya que alg�n �ndice puede ser negativo if ((FA < DIM) && (FA >= 0) && (CA < DIM) && (CA >= 0) && (tablero[FA][CA] != CASILLA_VACIA)) { posicion_valida = 1; casilla = tablero[FA][CA]; } else { posicion_valida = 0; casilla = CASILLA_VACIA; } } // si la ultima posici�n era v�lida y la ficha es del jugador actual, // entonces hemos encontrado el patr�n else if ((posicion_valida == 1) && (casilla == color)) { if (*longitud > 0) // longitud indica cuantas fichas hay que voltear { patron = PATRON_ENCONTRADO; } encontrado = 1; } else encontrado = 1; // en caso contrario es que no hay patr�n } return patron; } //////////////////////////////////////////////////////////////////////////////// // voltea n fichas en la direcci�n que toque // SF y SC son las cantidades a sumar para movernos en la direcci�n que toque // color indica el color de la pieza que se acaba de colocar // FA y CA son la fila y columna a analizar void voltear(char tablero[][DIM], char FA, char CA, char SF, char SC, int n, char color) { int i; for (i = 0; i < n; i++) { FA = FA + SF; CA = CA + SC; tablero[FA][CA] = color; } } //////////////////////////////////////////////////////////////////////////////// // comprueba si hay que actualizar alguna ficha // no comprueba que el movimiento realizado sea v�lido // f y c son la fila y columna a analizar // char vSF[DIM] = {-1,-1, 0, 1, 1, 1, 0,-1}; // char vSC[DIM] = { 0, 1, 1, 1, 0,-1,-1,-1}; int actualizar_tablero(char tablero[][DIM], char f, char c, char color) { char SF, SC; // cantidades a sumar para movernos en la direcci�n que toque int i, flip, patron; for (i = 0; i < DIM; i++) // 0 es Norte, 1 NE, 2 E ... { SF = vSF[i]; SC = vSC[i]; // flip: numero de fichas a voltear flip = 0; // patron = patron_volteo(tablero, &flip, f, c, SF, SC, color); CAMBIO patron = patron_volteo_iter(tablero, &flip, f, c, SF, SC, color); //printf("Flip: %d \n", flip); if (patron == PATRON_ENCONTRADO ) { voltear(tablero, f, c, SF, SC, flip, color); } } return 0; } ///////////////////////////////////////////////////////////////////////////////// // Recorre todo el tablero comprobando en cada posici�n si se puede mover // En caso afirmativo, consulta la puntuaci�n de la posici�n y si es la mejor // que se ha encontrado la guarda // Al acabar escribe el movimiento seleccionado en f y c // Candidatas // NO 0 // SI 1 // CASILLA_OCUPADA 2 int elegir_mov(char candidatas[][DIM], char tablero[][DIM], char *f, char *c) { int i, j, k, found; int mf = -1; // almacena la fila del mejor movimiento encontrado int mc; // almacena la columna del mejor movimiento encontrado char mejor = 0; // almacena el mejor valor encontrado int patron, longitud; char SF, SC; // cantidades a sumar para movernos en la direcci�n que toque // Recorremos todo el tablero comprobando d�nde podemos mover // Comparamos la puntuaci�n de los movimientos encontrados y nos quedamos con el mejor for (i=0; i<DIM; i++) { for (j=0; j<DIM; j++) { // indica en qu� casillas quiz� se pueda mover if (candidatas[i][j] == SI) { if (tablero[i][j] == CASILLA_VACIA) { found = 0; k = 0; // en este bucle comprobamos si es un movimiento v�lido // (es decir si implica voltear en alguna direcci�n) while ((found == 0) && (k < DIM)) { SF = vSF[k]; // k representa la direcci�n que miramos SC = vSC[k]; // 1 es norte, 2 NE, 3 E ... // nos dice qu� hay que voltear en cada direcci�n longitud = 0; patron = patron_volteo_iter(tablero, &longitud, i, j, SF, SC, FICHA_BLANCA); // //printf("%d ", patron); if (patron == PATRON_ENCONTRADO) { found = 1; if (tabla_valor[i][j] > mejor) { mf = i; mc = j; mejor = tabla_valor[i][j]; } } k++; // si no hemos encontrado nada probamos con la siguiente direcci�n } } } } } *f = (char) mf; *c = (char) mc; // si no se ha encontrado una posici�n v�lida devuelve -1 return mf; } /////////////////////////////////////////////////////////// // Cuenta el n�mero de fichas de cada color. // Los guarda en la direcci�n b (blancas) y n (negras) /////////////////////////////////////////////////////////// void contar(char tablero[][DIM], int *b, int *n) { int i,j; *b = 0; *n = 0; // recorremos todo el tablero contando las fichas de cada color for (i=0; i<DIM; i++) { for (j=0; j<DIM; j++) { if (tablero[i][j] == FICHA_BLANCA) { (*b)++; } else if (tablero[i][j] == FICHA_NEGRA) { (*n)++; } } } } void actualizar_candidatas(char candidatas[][DIM], char f, char c) { // donde ya se ha colocado no se puede volver a colocar // En las posiciones alrededor s� candidatas[f][c] = CASILLA_OCUPADA; if (f > 0) { if (candidatas[f-1][c] != CASILLA_OCUPADA) candidatas[f-1][c] = SI; if ((c > 0) && (candidatas[f-1][c-1] != CASILLA_OCUPADA)) candidatas[f-1][c-1] = SI; if ((c < 7) && (candidatas[f-1][c+1] != CASILLA_OCUPADA)) candidatas[f-1][c+1] = SI; } if (f < 7) { if (candidatas[f+1][c] != CASILLA_OCUPADA) candidatas[f+1][c] = SI; if ((c > 0) && (candidatas[f+1][c-1] != CASILLA_OCUPADA)) candidatas[f+1][c-1] = SI; if ((c < 7) && (candidatas[f+1][c+1] != CASILLA_OCUPADA)) candidatas[f+1][c+1] = SI; } if ((c > 0) && (candidatas[f][c-1] != CASILLA_OCUPADA)) candidatas[f][c-1] = SI; if ((c < 7) && (candidatas[f][c+1] != CASILLA_OCUPADA)) candidatas[f][c+1] = SI; } // Estructura de datos donde están encapsulados todos los argumentos importantes del reversi. // Según la mayoría de las guías de estilo cuando hay muchos argumentos en una función y // tienen características similares es mejor encapsularlos. struct Data { char __attribute__ ((aligned (8))) candidatas[DIM][DIM]; int done; // la m�quina ha conseguido mover o no int move; // el humano ha conseguido mover o no int blancas, negras; // n�mero de fichas de cada color int fin; // fin vale 1 si el humano no ha podido mover // (ha introducido un valor de movimiento con alg�n 8) // y luego la m�quina tampoco puede char f, c; // fila y columna elegidas por la m�quina para su movimiento }; /********************************************************************************************* * name: reversi_inicializar(struct Data *data) * func: Establece unos valores iniciales para una estrucutra de datos de reversi * para: data: estructura de datos que contiene la información de la partida actual * ret: none * modify: data: se le asignan valores iniciales a la estructura de datos * comment: *********************************************************************************************/ void reversi_inicializar(struct Data *data){ int i, j; for (i = 0; i < DIM; i++) { for (j = 0; j < DIM; j++) { data->candidatas[i][j] = NO; } } data->move = 0; data->fin = 0; } /********************************************************************************************* * name: reversi_procesar(struct Data *data) * func: Actualiza una estrucutra de datos de reversi con la información actual de partida * para: data: estructura de datos que contiene la información de la partida actual * ret: none * modify: data: se le asigna el resultado de realizar los movimientos del usuario y maquina * comment: *********************************************************************************************/ void reversi_procesar(struct Data *data){ int tiempo_calculo; data->move = 0; // si la fila o columna son 8 asumimos que el jugador no puede mover if (((fila) != DIM) && ((columna) != DIM)) { tablero[fila][columna] = FICHA_NEGRA; tiempo_calculo=timer2_leer(); actualizar_tablero(tablero, fila, columna, FICHA_NEGRA); actualizar_candidatas(data->candidatas, fila, columna); tiempo_calculo=timer2_leer()-tiempo_calculo; setTiempoCalculo(tiempo_calculo); data->move = 1; } // escribe el movimiento en las variables globales fila columna data->done = elegir_mov(data->candidatas, tablero, &(data->f), &(data->c)); if (data->done == -1) { if (data->move == 0) data->fin = 1; } else { tablero[data->f][data->c] = FICHA_BLANCA; actualizar_tablero(tablero, data->f, data->c, FICHA_BLANCA); actualizar_candidatas(data->candidatas, data->f, data->c); } estado_juego_global = CANDIDATA; } /********************************************************************************************* * name: reversi_main() * func: Bucle principal para UNA partida de reversi * para: none * ret: none * modify: none * comment: *********************************************************************************************/ void reversi_main(){ // Bienvenida pantalla_inicial(); struct Data data; unsigned int ahora0 = 0; // tiempo leido por timer0 unsigned int ahora2 = 0; // tiempo leido por timer2 unsigned int switched = 0; // marca de tiempo desde que se apagó/encendió el LED unsigned int refreshed = 0; // marca de tiempo desde que se refrescó la imagen de LCD char readyCandidata = 0; // 1 si la jugada está lista, 0 en caso contrario char encendido = FALSE; // TRUE si está el LED encendido, FALSE en caso contrario char ** testy = tablero; // puntero a tablero --> PARA FACILITAR EL SEGUIMIENTO // DEL FLUJO DE PROGRAMA ///////////////////////////// // ESTADO INICIAL ///////////////////////////// reversi_inicializar(&data); estado_juego_global=CANDIDATA; init_table(tablero,data.candidatas); Lcd_Tablero(tablero, 0, 0, FALSE, estado_juego_global); timer_empezar(); timer2_empezar(); ///////////////////////////// // BUCLE PRINC. DE JUEGO ///////////////////////////// switched = 0; refreshed = 0; while (data.fin == 0) { // CONTROL DE LED - Cada cuarto de segundo if (ahora0 - switched >= 25) { if (encendido == TRUE) { led1_off(); encendido = FALSE; } else { led1_on(); encendido = TRUE; } switched = ahora0; } // Marca temporal que se puede utilizar en el resto de funciones ahora2 = timer2_leer(); ahora0 = timer_leer(); // Actualizar estado de pulsación de botones y touchscreen botones_antirebotes_gestion(ahora2); jugada_por_botones_gestion (&estado_juego_global, ahora0); readyCandidata = jugada_lista(); unsigned int tst1 = timer2_leer(); ahora0 = timer_leer(); unsigned int tst2 = timer2_leer(); // REFRESH DE TABLERO - Cada cuarto de segundo if (ahora0 - refreshed >= 25) { Lcd_Tablero(tablero, num_fila(), num_col(), encendido, estado_juego_global); refreshed = ahora0; } // ACEPTAR JUGADA if (readyCandidata && estado_juego_global == LISTO) { columna = num_col(); fila = num_fila(); // jugada no válida --> pasar turno if (data.candidatas[fila][columna] != SI) { fila = 8; columna = 8; } reversi_procesar(&data); readyCandidata = 0; Lcd_Clr(); Lcd_Active_Clr(); columna = 0; fila = 0; reset_fila_columna(); jugada_tomada(); } } ///////////////////////////// // PANTALLA RESULTADOS ///////////////////////////// contar(data.candidatas, &data.blancas, &data.negras); // Aqui se muestra la pantalla de resultados resultadosFinales(data.blancas, data.negras); // Esperamos a que el usuario presione un botón o touchscreen while (botones_antirebotes_leer() == 0 && getTouch() == 0); } ``` --- ### `reversi.h` ```c /********************************************************************************************* * Fichero: reversi8.h * Autor: * Descrip: punto de entrada de C * Version: <P4-ARM.timer-leds> *********************************************************************************************/ /*--- ficheros de cabecera ---*/ #include "definiciones.h" #ifndef SIMULADOR #include "44b.h" #include "44blib.h" #endif #include "lcdUtils.h" #include "button.h" #include "jugada_por_botones.h" #include "8led.h" #include "led.h" #include "timer.h" #include "timer2.h" #include "exceptionHandler.h" #include "botones_antirebotes.h" // Tama�o del tablero enum { DIM=8 }; // Valores que puede devolver la funci�n patron_volteo()) enum { NO_HAY_PATRON = 0, PATRON_ENCONTRADO = 1 }; // Estados de las casillas del tablero enum { CASILLA_VACIA = 0, FICHA_BLANCA = 1, FICHA_NEGRA = 2 }; // candidatas: indica las posiciones a explorar // Se usa para no explorar todo el tablero innecesariamente // Sus posibles valores son NO, SI, CASILLA_OCUPADA const char NO = 0; const char SI = 1; const char CASILLA_OCUPADA = 2; ///////////////////////////////////////////////////////////////////////////// // TABLAS AUXILIARES // declaramos las siguientes tablas como globales para que sean m�s f�ciles visualizarlas en el simulador // __attribute__ ((aligned (8))): specifies a minimum alignment for the variable or structure field, measured in bytes, in this case 8 bytes // Tabla de direcciones. Contiene los desplazamientos de las 8 direcciones posibles const char vSF[DIM] = {-1,-1, 0, 1, 1, 1, 0,-1}; const char vSC[DIM] = { 0, 1, 1, 1, 0,-1,-1,-1}; ////////////////////////////////////////////////////////////////////////////////////// // Variables globales que no deber�an serlo // tablero, fila, columna y ready son varibles que se deber�an definir como locales dentro de reversi8. char fila=0, columna=0, ready = 0; // extern int patron_volteo(char tablero[][8], int *longitud,char f, char c, char SF, char SC, char color); ////////////////////////////// // Tablero sin inicializar ////////////////////////////// char __attribute__ ((aligned (8))) tablero[DIM][DIM] = { {CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA}, {CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA}, {CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA}, {CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA}, {CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA}, {CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA}, {CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA}, {CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA,CASILLA_VACIA} }; /********************************************************************************************* * name: reversi_main() * func: Bucle principal para UNA partida de reversi * para: none * ret: none * modify: none * comment: *********************************************************************************************/ void reversi_main(); ``` Otros ficheros se pueden encontrar en el directorio de fuentes del proyecto.