¿Bug de Sam Coupé o de SimCoupe?

Emuladores y aplicaciones que ayudarán a la perpetuación del Spectrum y su software en el futuro

Moderador: Sir Cilve Sinclair

Responder
Galileo
rst 0
Mensajes: 41
Registrado: Dom Ago 12, 2007 10:50 pm
Ubicación: Murcia (España)
Contactar:

¿Bug de Sam Coupé o de SimCoupe?

Mensaje por Galileo » Jue Ago 18, 2011 12:58 am

Hola. Hacía muchísimo tiempo que no escribía por aquí. Y es que parece que sólo en vacaciones puedo volcar mis energías en esta gran afición por el Spectrum.

Bueno, al caso.

Vereis, estoy haciendo pruebas con el SAM BASIC y, a las primeras de cambio, me he encontrado con una desagradable sorpresa. El caso es que me gustaría que alguien con un SAM Coupé físico comprobara esto:


El siguiente ejemplo muestra como se pueden pasar un número de argumentos indeterminados a un procedimiento mediante el uso de las instrucciones ITEM y DATA. ITEM devuelve el tipo del siguiente dato a leer mediante READ (1 si es una cadena de texto, 2 si es un número, y 0 si ya no quedan datos que leer)

10 DEFPROC ruido DATA
20 LOCAL a
30 DO UNTIL ITEM = 0 : REM Repite el bucle hasta (until) que ya no haya datos que leer
40 READ a
50 BEEP 1,a
60 LOOP
70 ENDPROC
80 ruido 10,50,3,20,32

Los datos suministrados pueden ser literales o variables de cualquier tipo y estar en cualquier orden.

5 let a$ = "y mas ruido"
10 DEFPROC ruido DATA
20 LOCAL a, a$
30 DO UNTIL ITEM = 0 : REM Repite el bucle hasta (until) que ya no haya datos que leer
40 IF ITEM = 2 THEN READ a: BEEP 1,a: ELSE READ a$: PRINT a$
60 LOOP
70 ENDPROC
80 ruido 10,"ruido",3,32,a$

El código anterior debería, además de reproducir los sonidos indicados como parámetros, mostrar en pantalla dos líneas con las leyendas "ruido" y "y mas ruido". En vez de eso, imprime "ruido" dos veces. La causa está relacionada con el uso de una variable local con el mismo nombre que otra global (a$). El motivo no alcanzo a comprenderlo, y lo considero un "bug". Modificando el nombre de la variable global o la local, funciona correctamente.

5 let b$ = "y mas ruido" : REM Nombre de la variable cambiado
10 DEFPROC ruido DATA
20 LOCAL a, a$
30 DO UNTIL ITEM = 0 : REM Repite el bucle hasta (until) que ya no haya datos que leer
40 IF ITEM = 2 THEN READ a: BEEP 1,a: ELSE READ a$: PRINT a$
60 LOOP
70 ENDPROC
80 ruido 10,"ruido",3,32,b$

Curiosamente, si se incluye la linea siguiente:

90 PRINT a

Obtendremos como respuesta el número 32, es decir, el último dato numérico leido en el procedimiento: ¡el contenido de una variable local a un procedimiento a transcendido al mismo!

Esto no parece ser nada bueno, así que me gustaría saber si le pasaba a la máquina real o si es un "bug" del emulador que utilizo.

Si alguien me puede ayudar se lo agradeceré, porque seguir o no con el SAM depende de la respuesta a mi pregunta.

Gracias anticipadas.
El Spectrum no necesita ser actualizado cada equis años, y SIEMPRE es compatible consigo mismo (chúpate esa, BG).

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 de Sam Coupé o de SimCoupe?

Mensaje por mcleod_ideafix » Jue Ago 18, 2011 2:50 am

Sin probar nada en la máquina real, juraría que este tipo de errores también ocurrirán en la máquina real, ya que el comportamietno del BASIC depende de lo que haya en la ROM, y ésta es la misma tanto en la máquina emulada por SimCoupé como en el SAM real.
Ahora bien, hubo varias versiones de la ROM del SAM, así que igual en la última versión (¿la 3?) arreglaran eso.
Voy a mirar...

Mirado.
La versión de la ROM del SimCoupé es, como cabría esperar, la última (la 3). Esto se averigua con PRINT (PEEK 15) / 10

Por otro lado, he encontrado esto en "The complete guide to SAM Basic":
complete guide to SAM Basic escribió:Procedure with Local, Data
When passing variables as described on the last page, the variables are AUTOMATICALLY LOCAL to the procedure - you don't need to use LOCAL. So, in the program

Código: Seleccionar todo

10 DEFPROC drawline x,y
20 PLOT x,y: DRAW 5,0
30 ENDPROC
40 FOR x=0 to 50
50 drawline x,x
60 NEXT x

the variables x and y are kept separate in and out of the procedure. The program will draw a series of lines, to make a thick
diagonal line.


Este programa, por ejemplo:

Código: Seleccionar todo

10 DEF PROC ruido a,a$
20 BEEP 1,a: PRINT a$
30 LET a$=""
40 ENDPROC
50 LET a$="ruido"
60 ruido 10,a$
70 PRINT a$


Funcionará correctamente. El a$ que se pasa como parámetro en la llamada se localiza y en la línea 30 se borra, pero esto no afecta al a$ que se declaró en la línea 50, que es impreso en la línea 70. Es decir, este programa imprime dos veces la palabra "ruido" lo cual es correcto.

Este otro...

Código: Seleccionar todo

10 DEF PROC ruido DATA
20 READ a: BEEP 1,a: READ a$: PRINT a$
30 LET a$=""
40 ENDPROC
50 LET a$="ruido"
60 ruido 10,a$
70 PRINT a$


No funciona bien. La línea 70 no imprime nada.

Si añado una línea 15...

Código: Seleccionar todo

10 DEF PROC ruido DATA
[b]15 LOCAL a,a$[/b]
20 READ a: BEEP 1,a
25 READ a$: PRINT a$
30 LET a$=""
40 ENDPROC
50 LET a$="ruido"
60 ruido 10,a$
70 PRINT a$


Me da error de variable no encontrada en la línea 25.

Si en cambio el LOCAL lo pongo más abajo, detrás de los READ's y antes de la línea que machaca su valor...

Código: Seleccionar todo

10 DEF PROC ruido DATA
20 READ a: BEEP 1,a
25 READ a$: PRINT a$
[b]27 LOCAL a,a$[/b]
30 LET a$=""
40 ENDPROC
50 LET a$="ruido"
60 ruido 10,a$
70 PRINT a$


El programa funciona :shock:

Por las pruebas que he hecho, veo que en cuanto se ejecuta LOCAL a$, el valor que existiera hasta ahora de a$ "desaparece" y a todos los efectos cualquier intento de usar a$ después del LOCAL, sin haberla definido, da error de variable no encontrada. Cuando termina el procedimiento, pues el valor anterior de a$ reaparece y todos contentos.

Pero tu programa usa una triquiñuela que me da a mi que se les pasó por alto a los de Gordon-Miles Technology, y es que una vez que has hecho el LOCAL dentro del procedimiento, efectivamente el a$ desaparece y cualquier intento de usarla da variable no encontrada. En esas que haces un READ a$ y lo que toca leer en DATA es precisamente una cadena referenciada por un nombre de variable, a$. Ahí es donde el BASIC se hace la picha un lío y lee como valor el último valor válido de a$ DENTRO DEL CONTEXTO DEL PROCEDIMIENTO, es decir, lee el último valor que tuvo a$ dentro de ese procedimiento.

El a$ de fuera del procedimiento no resulta afectado. Si a tu programa añades un PRINT a$ al final, después de la llamada a "ruido", verás que sigue valiendo "mas ruido".

Yo creo que no es un bug, porque el comportamiento de LOCAL es correcto: permite usar dentro de un procedimiento un nombre de variable aunque éste exista en el programa principal, y no machaca a estas últimas. Sólo hay que recordar que dentro de un procedimiento, si hay una variable global y una local y ambas tienen el mismo nombre, la local será la visible. Es exactamente igual que como pasa en C.

El bug, de haberlo, es que DATA puede tomar como valores no solamente literales numéricas o de cadena, sino también puede ser cualquier expresión. Y la cosa es que la expresión que toque leer con READ no es evaluada hasta el mismo momento en que se ejecuta el READ. De ahí que cuando vas a leer el dato a$ que está en DATA, el evaluador de expresiones, efectivamente, evalúa a$ según el ámbito en que se encuentra, y en el ámbito actual, el único a$ que existe es el que se ha "tocado" dentro del procedimiento, y ese es aquel que tiene el valor "ruido".

Recordando esto, es decir, que en caso de colisión de nombres de variables dentro de un procedimiento, se usará aquella variable definida como LOCAL en favor de cualquier otra definida fuera del procedimiento, y recordando que las expresiones en DATA no se evaluarán hasta que el READ correspondiente no se ejecute, no tendrás problemas.

En definiva: creo que tendrías que evitar usar expresiones en DATA, y sólo ceñirte a literales numéricos o de cadena. De esa forma, no tendrás estos comportamientos extraños...
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 de Sam Coupé o de SimCoupe?

Mensaje por mcleod_ideafix » Jue Ago 18, 2011 4:28 am

Galileo escribió:Curiosamente, si se incluye la linea siguiente:

90 PRINT a

Obtendremos como respuesta el número 32, es decir, el último dato numérico leido en el procedimiento: ¡el contenido de una variable local a un procedimiento a transcendido al mismo!


Sólo ocurre eso si "a" no existe como variable global. Si haces:
7 LET a=0

Al llegar a la línea 90, lo que se imprime es un 0, no un 32. Es decir, se ha respetado y preservado el valor de "a" como variable global.
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 de Sam Coupé o de SimCoupe?

Mensaje por mcleod_ideafix » Jue Ago 18, 2011 4:36 am

He estado probando a ver si con KEYIN podrías construir una llamada al procedimiento "ruido" que contuviera sólo literales, pero construidos desde variables. Es decir, lograr alguna forma en la que la evaluación de los datos de DATA se haga cuando se hace la llamada al procedimiento, y no cuando se está ya dentro de él, desde READ.

Lo que he probado es sustituir la llamada:
80 ruido 10,"ruido",3,32,a$

Por esto
80 KEYIN "ruido 10,"+CHR$ 34+"ruido"+CHR$ 34+",3,32,"+CHR$ 34+a$+CHR$ 34

Pero al llegar a esta línea me da error de que no encuentra DEF PROC. Quizás KEYIN esté limitado sólamente a primitivas del BASIC...

EDITO: de esta forma sí funciona:
80 KEYIN "81 ruido 10,"+CHR$ 34+"ruido"+CHR$ 34+",3,32,"+CHR$ 34+a$+CHR$ 34

Es decir, lo que hago es crear una línea de BASIC justo después de la 80, que contiene la llamada a "ruido" con todos sus datos ya evaluados. No hay paso de nombres de variables, por lo que no hay confusión posible.
Para que no se quede esa línea 81 todo el rato se puede añadir una línea 82 así:

80 KEYIN "81 ruido 10,"+CHR$ 34+"ruido"+CHR$ 34+",3,32,"+CHR$ 34+a$+CHR$ 34
82 DELETE 81 TO 81

De todas formas, aunque no se ponga el DELETE, cada vez que se ejecute la línea 80 se generará una nueva línea 81 con la llamada actualizada a "ruido" sobreescribiendo lo que hubiera.

En definitiva, este sería tu programa "corregido" y comprobado no sólamente para el caso de a$, sino también de a.

Código: Seleccionar todo

10 DEF PROC ruido DATA
20 LOCAL a,a$
30 DO UNTIL ITEM=0
40 IF ITEM=2 THEN READ a: BEEP 1,a: ELSE READ a$: PRINT a$
50 LOOP
60 END PROC
70 LET a$="y mas ruido": LET a=32
80 KEYIN "81 ruido 10,"+CHR$ 34+"ruido"+CHR$ 34+",3,"+a+","+CHR$ 34+a$+CHR$ 34: REM Esto es equivalente a haber escrito una linea 81 que pusiera: 81 ruido 10,"ruido",3,a,a$
82 DELETE 81 TO 81
Web: ZX Projects | Twitter: @zxprojects

Galileo
rst 0
Mensajes: 41
Registrado: Dom Ago 12, 2007 10:50 pm
Ubicación: Murcia (España)
Contactar:

Re: ¿Bug de Sam Coupé o de SimCoupe?

Mensaje por Galileo » Jue Ago 18, 2011 12:27 pm

Hola, mcleod_ideafix. Dado tu interés por el SAM Coupé tenía la esperanza de que respondieras. Gracias.

Si en cambio el LOCAL lo pongo más abajo, detrás de los READ's y antes de la línea que machaca su valor...

Código: Seleccionar todo

10 DEF PROC ruido DATA
20 READ a: BEEP 1,a
25 READ a$: PRINT a$
[b]27 LOCAL a,a$[/b]
30 LET a$=""
40 ENDPROC
50 LET a$="ruido"
60 ruido 10,a$
70 PRINT a$



El programa funciona


Bien, entiendo por qué funciona. La variable "a", al usarla en un READ en la línea 20, se declara como global y se inicializa con el valor tomado de la DATA. La variable "a$" ya estaba declarada fuera del procedimiento (global), con lo que, al hacer READ con ella se limita a leer el valor de a$, o sea, de si misma.

Pero tu programa usa una triquiñuela que me da a mi que se les pasó por alto a los de Gordon-Miles Technology, y es que una vez que has hecho el LOCAL dentro del procedimiento, efectivamente el a$ desaparece y cualquier intento de usarla da variable no encontrada. En esas que haces un READ a$ y lo que toca leer en DATA es precisamente una cadena referenciada por un nombre de variable, a$. Ahí es donde el BASIC se hace la picha un lío y lee como valor el último valor válido de a$ DENTRO DEL CONTEXTO DEL PROCEDIMIENTO, es decir, lee el último valor que tuvo a$ dentro de ese procedimiento.

El a$ de fuera del procedimiento no resulta afectado. Si a tu programa añades un PRINT a$ al final, después de la llamada a "ruido", verás que sigue valiendo "mas ruido".

Yo creo que no es un bug, porque el comportamiento de LOCAL es correcto ...


En esto no había caido (tengo que dormir más :oops:). Gracias por iluminarme. Tu explicación es correcta, como se puede comprobar en este ejemplo:

Código: Seleccionar todo

10 DEF PROC ruido DATA
20 LOCAL a, a$
30 LET a$ = "y mas ruido"
40 READ a: BEEP 1,a
50 READ a$: PRINT a$
60 END PROC
70 LET a$ = "ruido"
80 ruido 10, a$
90 PRINT a,a$


por mcleod_ideafix el Jue Ago 18, 2011 4:28 am

Galileo escribió:
Curiosamente, si se incluye la linea siguiente:

90 PRINT a

Obtendremos como respuesta el número 32, es decir, el último dato numérico leido en el procedimiento: ¡el contenido de una variable local a un procedimiento a transcendido al mismo!


Sólo ocurre eso si "a" no existe como variable global. Si haces:
7 LET a=0

Al llegar a la línea 90, lo que se imprime es un 0, no un 32. Es decir, se ha respetado y preservado el valor de "a" como variable global.


Sí, ya me di cuenta de ello. Pero, en el caso que indico, no debería ocurrir que una variable declarada como LOCAL se convierta en global por arte de birlibirloque. Esto sí que me concederás que es un bug, y preocupante, porque podríamos vernos utilizando variables no inicializadas (correctamente) sin darnos cuenta. Por ejemplo, declaro dentro de un procedimiento una variable local "a", y fuera pretendo usar una variable global "as". Si me equivoco y pongo "a" en vez de "as" el intérprete no me dará el mensaje de error al efecto (2 a not found), y eventualmente podría resultar complicado localizar el error.

En fin, gracias otra vez por responder.

Un saludo a toda la afición espectrumera.
El Spectrum no necesita ser actualizado cada equis años, y SIEMPRE es compatible consigo mismo (chúpate esa, BG).

Galileo
rst 0
Mensajes: 41
Registrado: Dom Ago 12, 2007 10:50 pm
Ubicación: Murcia (España)
Contactar:

Re: ¿Bug de Sam Coupé o de SimCoupe?

Mensaje por Galileo » Jue Ago 18, 2011 10:06 pm

Respecto a las variables locales a un procedimiento, el Manual del usuario del SAM Coupé dice:
END PROC erases any LOCAL variables, and restores their 'normal' values, if they exist.
Es el hecho de que se indique expresamente este extremo el que me hace dudar sobre si el fallo es de la máquina original o del emulador.

Como me estoy dedicando a estudiar el SAM BASIC a base de hacerme mi propia Guía del usuario (en castellano) siguiendo las explicaciones de "The Complete Guide to SAM BASIC", de Graham Burtenshaw, pues sigo descubriendo cosas.

Aun declarando un número determinado de parámetros para un procedimiento, se puede ejercitar cierta flexibilidad en cuanto a los que han de proporcionársele. En el siguiente ejemplo es factible omitir los parámetros y y r, ya que se le han declarado valores por defecto (con DEFAULT).

Código: Seleccionar todo

10 DEF PROC circulo x,y,r
20 DEFAULT y=x, r=80
30 CIRCLE x,y,r
40 FILL x,y
50 END PROC
60 circulo 80


Aquí nos encontramos con otro "bug". Si bien las variables usadas en la lista de parámetros son auténticamente locales (en principio, no existen fuera del procedimiento), el hecho de aplicarles luego la instrucción DEFAULT las torna globales, como puede apreciarse al añadir la siguiente línea:

Código: Seleccionar todo

70 PRINT y, r, x


Sólo al intentar imprimir la variable x se obtiene el mensaje de error "2 x not found, 70:1".

Como atenuación para estos problemas creo que será mejor adoptar un estilo de programación que nombre con un prefijo determinado a las variables presuntamente locales. Así, por ejemplo, los parámetros anteriores podrían renombrarse L_x, L_y y L_r.

Un saludo, y ya iré informando de mis "descubrimientos".

P.D.: Si lo que estoy contando ya es conocido, agradecería que se me avisase para evitar duplicidades innecesarias.
El Spectrum no necesita ser actualizado cada equis años, y SIEMPRE es compatible consigo mismo (chúpate esa, BG).

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 de Sam Coupé o de SimCoupe?

Mensaje por mcleod_ideafix » Sab Ago 20, 2011 1:24 am

Galileo escribió:Respecto a las variables locales a un procedimiento, el Manual del usuario del SAM Coupé dice:
END PROC erases any LOCAL variables, and restores their 'normal' values, if they exist.
Es el hecho de que se indique expresamente este extremo el que me hace dudar sobre si el fallo es de la máquina original o del emulador.


Sigo pensando que si existe el bug en el emulador, también existirá en la máquina real, ya que ambas usan la misma ROM. Las diferencias entre una máquina real y una emulada suelen ir por el tema del hardware: que un determinado efecto de video o sonido no salga igual en el emulador que en la máquina real. El tipo de cosas que pasan en tus programas no dependen del hardware, al menos no en principio, y la emulación del Z80 que usan los emuladores es lo suficientemente precisa como para que funcione igual y dé los mismos resultados que un Z80 real en la inmensa mayoría de casos.
Web: ZX Projects | Twitter: @zxprojects

Galileo
rst 0
Mensajes: 41
Registrado: Dom Ago 12, 2007 10:50 pm
Ubicación: Murcia (España)
Contactar:

Re: ¿Bug de Sam Coupé o de SimCoupe?

Mensaje por Galileo » Sab Ago 20, 2011 11:48 pm

Acabo de hacer la prueba con Beta Basic 4.0 (en Fuse) del asunto de DEFAULT y LOCAL, y funciona correctamente. Puesto que SAM BASIC está diseñado por la misma persona y basado en Beta Basic, me resulta difícil creer que el fallo fuera del sistema original. Sé que estoy pesado con esto, pero ¿no hay nadie con un Sam Coupé real que lo pueda comprobar? Me gustaría mucho salir de dudas.


Editado : He probado con otro emulador de SAM Coupe (el "ascd 0.96") y tiene el mismo fallo. Así pues, creo que mcleod_ideafix tiene razón: son "bugs" de SAM BASIC.

Un saludo.
El Spectrum no necesita ser actualizado cada equis años, y SIEMPRE es compatible consigo mismo (chúpate esa, BG).

Galileo
rst 0
Mensajes: 41
Registrado: Dom Ago 12, 2007 10:50 pm
Ubicación: Murcia (España)
Contactar:

Re: ¿Bug de Sam Coupé o de SimCoupe?

Mensaje por Galileo » Dom Ago 21, 2011 7:33 pm

Bueno, otro "bug" (supongo).

BUTTON
Obtiene el estado de los pulsadores del ratón. La sintaxis es BUTTON n, donde n puede ser 1, 2 o 3. Por ejemplo, PRINT BUTTON 1 mostrará si el botón izquierdo está pulsado o no. Si bien el Manual del Usuario del SAM Coupé indica que se utiliza un 1 para indicar pulsado y un 0 para lo contrario, al menos en mis emuladores de SAM Coupé (SimCoupe corriendo sobre WindowsXP, y ascd 0.96 corriendo sobre DOS-BOX), dichos valores están intercambiados (0 = pulsado, 1 = no pulsado).

Editado: He encontrado un programa en el disco ENCELADUS Magazine Issue 06 (Aug 1991) (Relion Software) que usa el ratón, y efectivamente lo trata de la manera que he indicado. Debe ser el manual el que no está bien. O eso o el diseñador se equivocó y no se molestó en cambiarlo. Para mí sigue siendo más lógico como lo dice el manual. Pero bueno, sabiéndolo no es mayor problema.

Reeditado:

Código: Seleccionar todo

...
CFD4 ;MOUSE BUTTON N (1/2/3) RETURNS 1 IF PRESSED, 0 IF NOT
CFD4
CFD4 111E04 FPBUTTON: LD DE,0400H+30 ;LIMIT TO 0-3
CFD7 CD621F CALL LIMBYTE
CFDA FE03 CP 3 ;CY IF 0/1/2, NC IF 3
CFDC DEFF SBC A,0FFH ;00,01,02,04
CFDE 2001 JR NZ,FPBT2
CFE0
CFE0 2F CPL ;'BUTTON 0' TEST ALL 3
CFE1
CFE1 218F5B FPBT2: LD HL,BUTSTAT ;HAS BITS 2-0 SET FOR BUTTONS 3-1
CFE4 A6 AND (HL)
CFE5 2802 JR Z,STKAB
CFE7
CFE7 3E01 LD A,1
CFE9
CFE9 C3DA1C STKAB: JP STACKA
...


Lo anterior lo he extraido de un documento titulado "OVERVIEW OF THE ROM v3.0 by Andy Wright". Y se ve que sí, que la intención era la que se enuncia en el manual. Sería interesante que alguien que entendiera de código máquina le hechara un vistazo al listado y comentara si sabe qué es lo que ha pasado.

ftp://ftp.nvg.ntnu.no/pub/sam-coupe/docs/SAM%20Coupe%20ROM%20v3.0%20Annotated%20Disassembly.pdf
El Spectrum no necesita ser actualizado cada equis años, y SIEMPRE es compatible consigo mismo (chúpate esa, BG).

Responder

¿Quién está conectado?

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