Archivo por meses: noviembre 2008

!Libre!

Después de cinco años de vida, FBZX por fin es totalmente libre. Efectivamente, cuando escribí mi emulador de Spectrum, había intentado sin éxito escribir también el emulador de Z80. Por desgracia la tarea era muy pesada (sobre todo por repetitiva y propensa a errores), así que al final decidí usar uno hecho por un programador llamado Marat Fayzullin.

Por desgracia había un serio inconveniente: las condiciones de uso de este código eran incompatibles con la GPL (y mucho). Esta situación nunca me agradó demasiado, pero dado que escribir desde cero y depurar un emulador de un procesador era una tarea demasiado árdua, al igual que intentar adaptar el código de FBZX a otro emulador de Z80 diferente, lo fuí dejando de lado durante años.

Así estuvo la cosa hasta que hace un mes, más o menos, recibí un mensaje de un empaquetador de ArchLinux que se encontró con el problema: en efecto, no podía seguir manteniendo el emulador en el repositorio por culpa de la incompatibilidad de licencias.

Ante ésto decidí que ya era hora de tomar cartas en el asunto, así que me puse a escribir un emulador desde cero. Sin embargo, tenía claro que no podía limitarme a teclear largas listas de sentencias CASE XX:, sino que tenía que emplear una aproximación más inteligente si quería hacerlo en un tiempo razonable y con unas mínimas garantías de éxito. Así, se me ocurrió que podía partir de una lista de opcodes de Z80, la cual podría parsearse con un pequeño programa que generaría por mí el código en C. En efecto, partiendo de una lista como:

LD A,B
LD A,C
LD A,(HL)
CP (HL)

es posible separar el comando (LD, CP…) de los parámetros (A, B, C, (HL)), y generar automáticamente código que realice las operaciones. De esta forma sólo tuve que escribir una vez el código que implementase la lectura y escritura de cada uno de los 51 posibles parámetros, la comprobación de las 8 posibles condiciones, y el funcionamiento de cada uno de los 81 comandos diferentes, y dejar que el parser se encargase de generar los 1792 opcodes posibles que puede ejecutar el Z80. Además, una ventaja extra es que cualquier error que cometiese sería más fácil de corregir, porque sólo tendría que hacerlo en el parser, y automáticamente se arreglarían todas las instrucciones afectadas.

El resultado es que he necesitado tan sólo unas cuarenta horas netas, repartidas en tres semanas, para escribir desde cero un emulador de Z80 plenamente funcional, y que, además, emula el juego completo de instrucciones del Z80, tanto las oficiales como las no oficiales. Y gracias a él, FBZX ya es completamente libre.

Dado que la idea del parser puede ser útil para hacer otros emuladores diferentes, he tenido especial cuidado en escribirlo de manera que se pueda cambiar fácilmente la lista de comandos, parámetros, etc, y así poder adaptarlo a otros procesadores como el 6502, el 6800, ARM…

Otros cambios

Aprovechando la nueva versión, también añadí soporte para sonido mediante los controladores ALSA, y la emulación del Spectrum 128K español (el original de Investrónica y Sinclair Research). Además, también almacena el nivel de volumen que se está usando, de manera que no haya que ajustarlo cada vez que se ejecute el programa de nuevo. Por último, he cambiado la licencia a la GPLv3.

SuperShow

También aproveché para sacar una pequeña revisión de SuperShow. El único cambio que hice fue traducir las variables de la plantilla que se usa para generar los scripts de Flash, de manera que si alguien que no hable castellano quiere mejorarla, lo tendrá más fácil.

La nueva Ubuntu y mi viejo ratón

(Actualizado 2. Ver al final del artículo) Ayer actualicé mi sistema a la nueva Ubuntu 8.10 y me encontré con el primer problema: mi ratón empezó a funcionar correctamente.

Para explicarlo un poco mejor: mi ratón tiene un total de siete botones: los dos de siempre, el central en la rueda, y dos extra en el pulgar. La rueda, además, se puede mover a derecha e izquierda: dos botones más. Hasta ahora Ubuntu (o más concretamente, las X-Windows) no reconocían los botones extra, y los interpretaba como si fuesen el botón central, lo que para mí era muy cómodo. En efecto, si uso el botón central de la rueda para pegar texto o abrir un enlace de Firefox en una nueva pestaña, es raro que no se me mueva algo y acabe pinchando donde no quiero, problema que resuelvo usando el botón del pulgar.

Por desgracia la nueva versión utiliza HAL para detectar y gestionar el ratón, y éste sí reconoce los nuevos botones, con lo que el botón del pulgar ahora me lleva a la página anterior cada vez que lo pulso.

Por suerte el nuevo sistema también permite reconfigurar el sistema con más comodidad, y lo que es más interesante, sin necesidad de reiniciar las X, sino simplemente desenchufando y volviendo a enchufar el ratón. La manera es mediante unos ficheros XML (con extensión .fdi ) almacenados en /etc/hal/fdi/policy. Estos ficheros permiten modificar y personalizar completamente el funcionamiento de cualquier dispositivo, aunque la documentación es algo confusa.

Para los impacientes, la solución es tan simple como crear un fichero cualquiera en ese directorio (por ejemplo, /etc/hal/fdi/policy/mouse.fdi ) que contenga las siguientes lineas:

<device>
    <match key="info.capabilities" contains="input.mouse">
        <merge key="input.x11_options.ButtonMapping" type="string">1 2 3 4 5 6 7 2 2</merge>
    </match>
</device>

Para los que quieran personalizarlo más, la asignación de eventos es:

  1. Botón izquierdo
  2. Botón central
  3. Botón derecho
  4. Rueda arriba
  5. Rueda abajo
  6. Rueda izquierda
  7. Rueda derecha
  8. Página anterior (en un navegador)
  9. Página siguiente (en un navegador)

En el fichero anterior lo que hago es especificar que los eventos 8 y 9 deben responder como un evento 2, mientras que los demás permanecen inalterados.

Actualización: modifiqué el fichero FDI para que filtre los dispositivos, quedándose sólo con los ratones (tag match).

Actualización 2: Por desgracia la solución no sirve porque sólo funciona cuando primero se cargan las X y luego el módulo del ratón (por ejemplo, al desenchufarlo y volverlo a enchufar con las X ya cargadas). Cuando se arranca el ordenador desde cero, como se carga primero el módulo USB y luego las X, no hace nada.

La solución, al final, es más sencilla:

  • Se crea un fichero .Xmodmap en el directorio del usuario, que contenga la línea pointer = 1 8 3 4 5 6 7 2
  • Se sale de las X y se vuelve a entrar
  • Preguntará si se quiere utilizar un fichero Xmodmap, y mostrará la lista de los que ha encontrado (estará el que acabamos de crear)
  • Lo seleccionamos y lo añadimos a la lista de activos, y nos aseguramos de marcar la opción «No volver a preguntar».

Y con esto sí se resolverá el problema. El único defecto es que no se pueden repetir eventos, por lo que tuve que, simplemente, invertir los eventos 2 y 8, en lugar de asignar el 2 a todos los que me interesaba. Pero menos da una piedra…

Bailando Samba

En el trabajo han montado un servidor windows para compartir ficheros e impresoras, y como la mayoría de los servidores de este tipo, utiliza el protocolo SMB de Microsoft.

Acceder a estos servicios desde Linux parecía tan sencillo como abrir Lugares->Red y navegar gracias a SAMBA; pero, como de costumbre, las cosas nunca son tan sencillas. En efecto, el firewall que utilizo no me dejaba acceder, así que tras rebuscar en Internet encontré que tenía que abrir los puertos 135, 137, 138, 139 y 445. Me parecía extraño que, queriendo trabajar sólo como cliente (sin compartir yo nada) tuviese que abrir puertos, pero de todas formas lo probé. El resultado: ni con esas funcionaba, aunque si quitaba el firewall entonces iba como la seda.

Bastante mosqueado volví a buscar en Internet, pero no encontré absolutamente nada, así que me armé de paciencia y de Wireshark y empecé a ver qué demonios pasaba con los paquetes. Así fue como descubrí que el problema estaba en la resolución de nombres. En efecto, cuando un recurso no está en el DNS (un servidor, una impresora), SAMBA utiliza su propio servicio para descubrir su dirección IP, para lo que envía una petición UDP a toda la red usando la dirección de broadcast. El problema es que la respuesta no viene de la dirección de broadcast, sino de un equipo concreto (el buscado), por lo que el firewall la bloquea.

Pensando que era un bug del módulo ip_conntrack escribí a los desarrolladores para avisarles. Su respuesta, sin embargo, fue que cargase el módulo nf_conntrack_netbios_ns. Y efectivamente, con dicho módulo, y sin necesidad de abrir ningún puerto, todo ha funcionado perfectamente.