Bug en Fred

Juegos, aplicaciones, ROMs;
todo lo que se pueda ejecutar en un Spectrum

Moderador: Sir Cilve Sinclair

Responder
Avatar de Usuario
Rinconete
Jack The Nipper
Mensajes: 193
Registrado: Mar Oct 09, 2007 7:32 pm
Ubicación: Valladolid

Bug en Fred

Mensaje por Rinconete » Sab Nov 22, 2008 10:58 pm

No sé si este es el lugar adecuado para este hilo. Allá voy:

Me he topado con el siguiente bug en el juego Fred: si cargas el juego y cuando sale el menú de presentación escoges la opción Jugar, ojo, sin escoger joystick, ni teclado, ni redefinir teclas, entonces al iniciar el juego el personaje se lía a saltar y a disparar descontroladamente.

Al principio pensaba que se trataba de un problema del emulador, por lo de la emulación del Joystick, pero no. Mirando el código puede verse que en el sitio donde se lee el puerto para ver si se han pulsado las teclas hay una "variable", correspondiente al puerto, sin inicializar.

Si se pulsa Teclado (o Redefinir teclas) o Joystick, se inicializa con el valor $FE y $1F, que se corresponden con los puertos de lectura del teclado y del joystick, respectivamente. Sin embargo, si se pulsa jugar directamente el valor del puerto es $00, lo que produce el efecto comentado.

Curioso, ¿verdad?

Avatar de Usuario
mcleod_ideafix
Johnny Jones
Mensajes: 3985
Registrado: Vie Sep 21, 2007 1:26 am
Ubicación: Jerez de la Frontera
Contactar:

Re: Bug en Fred

Mensaje por mcleod_ideafix » Vie Nov 28, 2008 1:52 pm

Curioso... ya que en un Spectrum de los de la época en la que se programó el Fred, el puerto $00 es funcionalmente equivalente al puerto $FE.
Bueno, lo que pasa en el Fred es algo más que una variable. Desde luego sí que es un bug curioso:

Comenzando en 77FEh, hay una sección del programa que se encarga de leer los controles del personaje. En esa sección aparece la siguiente secuencia repetida varias veces (una por cada tecla de control del personaje):

Código: Seleccionar todo

LD A,semifila
IN A,(puerto_a_leer)
AND mascara
DB tipo_de_salto,$1E
resto de la secuencia donde se procesa el movimiento asociado a ese control
JR fin_de_la_rutina_de_leer_controles
OTRATECLA: este es el destino de la instrucción de salto que ha de parchearse (ver texto)

Cuando se elige un control, sea teclado, joystick o redefiniendo teclas, lo que se está haciendo es parchear dentro de esta sección, todas las ocurrencias de este código. Concretamente se parchean las posiciones de memoria que en el código aparecen como etiquetas:

    semifila: ignorada para joystick Kempston. En modo teclado indica qué semifila ha de seleccionarse. La opción de redefinir teclas calcula qué semifila ha de leerse y pone el valor aquí.
    puerto_a_leer: tomará el valor $FE o $1F, dependiendo de que se use teclado o joystick Kempston.
    mascara: lo mismo que la semifila: para obtener el valor de la tecla dentro de la semifila.
    tipo_de_salto: este es el más curioso. Resulta que la codificación de "tecla pulsada" es distinta para el teclado que para el Kempston. Para el primero, una tecla pulsada es 0, y para el segundo, es 1. En "tipo_de_salto" hay que poner un código de operación de una instrucción que salte cuando la tecla no está pulsada. Es decir, aquí aparecerá el código de la instrucción JR NZ para el caso del teclado, y JR Z para el caso de joystick.

Como se ve en el código, en cada vuelta del bucle de juego, se van leyendo los controles uno a uno. Si el primero no está pulsado, se pasa al segundo, si no, al tercero, etc etc. Si uno de ellos está pulsado, se realiza la acción correspondiente y se deja de leer controles en esa vuelta.

Recién cargado el programa, el primer control que se chequea es el salto, que comienza en 77FEh, y queda así:

Código: Seleccionar todo

LD A,$FB
IN A,($00)
AND $08
NOP  ;aqui deberia estar el codigo de operacion de salto JR Z o JR NZ
DEC E   ;esto es en realidad el argumento de la instruccion de salto anterior
...
...
JR $7873

Dado que no hay instrucción de salto condicional que chequee si la tecla de salto se pulsó o no, la rutina asociada a este control (el salto) se ejecuta siempre, y por tanto, no sigue chequeando el resto de controles. Por eso se queda saltando sin parar y no permite moverse ni hacer otra cosa.
Web: ZX Projects | Twitter: @zxprojects

Avatar de Usuario
Rinconete
Jack The Nipper
Mensajes: 193
Registrado: Mar Oct 09, 2007 7:32 pm
Ubicación: Valladolid

Re: Bug en Fred

Mensaje por Rinconete » Sab Nov 29, 2008 11:30 pm

mcleod_ideafix escribió:Curioso... ya que en un Spectrum de los de la época en la que se programó el Fred, el puerto $00 es funcionalmente equivalente al puerto $FE.


No sabía eso, ¿puedes explicarlo, plis?

mcleod_ideafix escribió:Bueno, lo que pasa en el Fred es algo más que una variable. Desde luego sí que es un bug curioso:...


¡Buen análisis!. No me había detenido a estudiar el porqué del efecto del disparo y salto que explicas en tu mensaje, asumiendo que estaba relacionado con la lectura del puerto, craso error. Aunque sí había estudiado la rutina de control del juego, ya que me llamó la atención lo del "parcheo" del código como dices, que se hace en la rutina en $7CEF.

Avatar de Usuario
Rinconete
Jack The Nipper
Mensajes: 193
Registrado: Mar Oct 09, 2007 7:32 pm
Ubicación: Valladolid

Re: Bug en Fred

Mensaje por Rinconete » Dom Nov 30, 2008 8:13 am

Se me olvidaba, se soluciona pokeando

POKE $7F48,$FE ; Puerto: teclado = $FE, joystick = $1F
POKE $7F49,$20 ; Salto: teclado = JR NZ ($20), joystick = JR Z ($28)

Avatar de Usuario
mcleod_ideafix
Johnny Jones
Mensajes: 3985
Registrado: Vie Sep 21, 2007 1:26 am
Ubicación: Jerez de la Frontera
Contactar:

Re: Bug en Fred

Mensaje por mcleod_ideafix » Dom Nov 30, 2008 6:59 pm

Rinconete escribió:
mcleod_ideafix escribió:Curioso... ya que en un Spectrum de los de la época en la que se programó el Fred, el puerto $00 es funcionalmente equivalente al puerto $FE.

No sabía eso, ¿puedes explicarlo, plis?

Sencillo: en un Spectrum "normal", la ULA se decodifica usando sólo el bit A0 del bus de direcciones. Se activa cuando A0 vale 0 (además de otras cosas). Así que cualquier acceso a un puerto cuya dirección sea par, estará accediendo a la ULA.
Coge un Spectrum o cualquier buen emulador y verás que haciendo un OUT 254,1 o haciendo OUT 0,1 pone en ambos casos el color del borde a azul.
Web: ZX Projects | Twitter: @zxprojects

Avatar de Usuario
mcleod_ideafix
Johnny Jones
Mensajes: 3985
Registrado: Vie Sep 21, 2007 1:26 am
Ubicación: Jerez de la Frontera
Contactar:

Re: Bug en Fred

Mensaje por mcleod_ideafix » Dom Nov 30, 2008 7:01 pm

Rinconete escribió:Se me olvidaba, se soluciona pokeando
POKE $7F48,$FE ; Puerto: teclado = $FE, joystick = $1F
POKE $7F49,$20 ; Salto: teclado = JR NZ ($20), joystick = JR Z ($28)

No del todo. Tienes que pokear los 4 o 5 sitios donde se hace una lectura de teclado y se salta. Como ya comenté en el post, hay una secuencia como la que describí por cada tecla de control del personaje.
Web: ZX Projects | Twitter: @zxprojects

Avatar de Usuario
Rinconete
Jack The Nipper
Mensajes: 193
Registrado: Mar Oct 09, 2007 7:32 pm
Ubicación: Valladolid

Re: Bug en Fred

Mensaje por Rinconete » Dom Nov 30, 2008 8:50 pm

mcleod_ideafix escribió:
Rinconete escribió:
mcleod_ideafix escribió:Curioso... ya que en un Spectrum de los de la época en la que se programó el Fred, el puerto $00 es funcionalmente equivalente al puerto $FE.

No sabía eso, ¿puedes explicarlo, plis?

Sencillo: en un Spectrum "normal", la ULA se decodifica usando sólo el bit A0 del bus de direcciones. Se activa cuando A0 vale 0 (además de otras cosas). Así que cualquier acceso a un puerto cuya dirección sea par, estará accediendo a la ULA.
Coge un Spectrum o cualquier buen emulador y verás que haciendo un OUT 254,1 o haciendo OUT 0,1 pone en ambos casos el color del borde a azul.


vaya que curioso, supongo que Sinclair utilizaría este esquema para ahorrar componentes

Avatar de Usuario
Rinconete
Jack The Nipper
Mensajes: 193
Registrado: Mar Oct 09, 2007 7:32 pm
Ubicación: Valladolid

Re: Bug en Fred

Mensaje por Rinconete » Dom Nov 30, 2008 9:16 pm

mcleod_ideafix escribió:
Rinconete escribió:Se me olvidaba, se soluciona pokeando
POKE $7F48,$FE ; Puerto: teclado = $FE, joystick = $1F
POKE $7F49,$20 ; Salto: teclado = JR NZ ($20), joystick = JR Z ($28)

No del todo. Tienes que pokear los 4 o 5 sitios donde se hace una lectura de teclado y se salta. Como ya comenté en el post, hay una secuencia como la que describí por cada tecla de control del personaje.


Pruébalo y verás que funciona. La razón está en la rutina en $7CEF que comentaba antes. Esta rutina se llama cada vez que se inicia el juego (opción jugar) y parchea el código donde se hace el control del juego con:

Valor en $7F48, que contiene el puerto
Valor en $7F49, que contiene el opcode para el tipo de salto
Tabla de 10 valores en $7D56 que contiene las parejas de bytes correspondientes a la semifila y la máscara

Cuando se inicia el juego por primera vez, la tabla en $7D56 contiene la definición para las teclas QWERT (izquierda, derecha, abajo, arriba y fuego). También, cuando se pulsa la opción Teclado en el menú principal se copia sobre esa tabla la definición en $7D60, que es esa misma, pero en esa rutina también se inicializan $7F48 y $7F49 con los valores correspondientes (ver rutina en $7C78).

Peeero, si se inicia el juego directamente (sin pulsar teclado ni redefinir teclas) entonces $7F48 y $7F49 contienen ceros, por eso decía que están sin inicializar. Como comentabas en tu post, si al menos el valor del salto estuviera inicializado no pasaría nada, peeero al tener $00 en el salto entonces se machaca el opcode del salto, que nada más cargar es $20 (JR NZ) y que pasa a ser NOP ($00).

Copio el código en $7D56:

Código: Seleccionar todo

L_7CEF: LD HL, L_7D56    ; Toma el buffer para definición del control del juego:
                         ; Arriba      $FB, $08   R
                         ; Abajo       $FB, $04   E
                         ; Izquierda   $FB, $01   Q
                         ; Derecha     $FB, $02   W
                         ; Fuego       $FB, $10   T
; Inicializa el puerto en función del control teclado/joystick
;
; Teclado       Joystick
; IN A,($FE)    IN A,($1F)
; $FE           $1F
   LD A, (L_7F48)
   LD (L_7801), A
   LD (L_7826), A
   LD (L_7839), A
   LD (L_7862), A
   LD (L_78A0), A
; Inicializa la instrucción de salto en función del control teclado/joystick
;
; Teclado          Joystick
; JR NZ, L_$$$$    JR Z, L_$$$$
; $20              $28
   LD A, (L_7F49)
   LD (L_7829), A
   LD (L_783C), A
   LD (L_7804), A
   LD (L_7865), A
   LD (L_78A3), A
; Copia los 10 bytes que definen el control utilizado (joystick o
; teclado) en distintas variables.
   CALL L_7D53
   LD (L_77FF), A   ; Arriba
   CALL L_7D53
   LD (L_7803), A
   CALL L_7D53
   LD (L_7824), A   ; Abajo
   CALL L_7D53
   LD (L_7828), A
   CALL L_7D53
   LD (L_7837), A   ; Izquierda
   CALL L_7D53
   LD (L_783B), A
   CALL L_7D53
   LD (L_7860), A   ; Derecha
   CALL L_7D53
   LD (L_7864), A
   CALL L_7D53
   LD (L_789E), A   ; Fuego
   CALL L_7D53
   LD (L_78A2), A
   RET
L_7D53: LD A, (HL)
   INC HL
   RET              ; $7D55

Avatar de Usuario
mcleod_ideafix
Johnny Jones
Mensajes: 3985
Registrado: Vie Sep 21, 2007 1:26 am
Ubicación: Jerez de la Frontera
Contactar:

Re: Bug en Fred

Mensaje por mcleod_ideafix » Dom Nov 30, 2008 10:28 pm

Rinconete escribió:
mcleod_ideafix escribió:
Rinconete escribió:Se me olvidaba, se soluciona pokeando
POKE $7F48,$FE ; Puerto: teclado = $FE, joystick = $1F
POKE $7F49,$20 ; Salto: teclado = JR NZ ($20), joystick = JR Z ($28)

No del todo. Tienes que pokear los 4 o 5 sitios donde se hace una lectura de teclado y se salta. Como ya comenté en el post, hay una secuencia como la que describí por cada tecla de control del personaje.


Pruébalo y verás que funciona.


Ahhh! Entiendo :D Muchas gracias!!
Web: ZX Projects | Twitter: @zxprojects

Responder

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 9 invitados