---
# System prepended metadata

title: 'Análisis, diseño e implementación del juego Reversi8 en la placa Embest S3CEV40'

---

# Análisis, diseño e implementació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 prá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 específico se utilizará un **tablero 8x8** para implementar el juego, como en la prá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 interrupción de los botones (para eliminar rebotes y configurar como interaccionan estos con el programa) y de timers (tanto para medir los tiempos de ejecución/procesamiento como para ser utilizado por el propio botó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 añ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 prácticas es el de hacer un **prototipo del juego reversi**, así como adaptarlo para su ejecución sobre la placa de manera autónoma.

- Desarrollar mecanismos de bajo nivel para la depuración y control del tiempo, lo que incluye una pila de depuración, control de excepciones y una biblioteca de medición de tiempos.

- Adaptar los dispositivos de entrada de la placa, en concreto los botones, para su có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 ú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 medició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 ú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 bá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.