Capítulo 9. Cuando usted guste

Parte 2: Más en concreto sobre el CPC6128

Contenido:


En esta sección vamos a tratar algunas cuestiones específicas del CPC6128. La información básica sobre estos temas se puede encontrar en el Curso de introducción y en el capítulo titulado Lista completa de las palabras clave del BASIC de Amstrad.

Temas tratados en esta sección:

  • Juegos de caracteres.
  • ASCII.
  • Variables.
  • Lógica.
  • Caracteres definidos por el usuario.
  • Formatos de escritura.
  • Ventanas.
  • Interrupciones.
  • Datos.
  • Sonido.
  • Gráficos.
  • Utilización del segundo banco de 64K de RAM.

Caracteres

Cuando se ponga al teclado de su CPC6128, no dé por supuesto que en la pantalla vayan a aparecer letras y números reconocibles. Ya hemos explicado que el ordenador no es una máquina de escribir. Lo que usted hace es pulsar una serie de conmutadores eléctricos; las señales eléctricas así producidas son traducidas por los circuitos internos para enviar a la pantalla grupos de puntos. Según sea la colocación de esos puntos, podremos reconocer letras, números u otros caracteres del "juego de caracteres" del CPC6128.

Algunos de estos caracteres no son accesibles directamente a través del teclado, sino mediante la instrucción PRINT CHR$(<número>). Esto es así porque la información se almacena en el ordenador en unidades de 1 byte y, como vimos en la parte 1 de este capítulo, 1 byte puede tener 256 formas posibles. Dado que el ordenador tiene que dedicar al menos un byte a cada carácter almacenado, más vale que aprovechemos las 256 combinaciones posibles en lugar de conformarnos con los 96 "caracteres estándar" de las máquinas de escribir y desperdiciar los 160 restantes.

Los caracteres estándar son un subconjunto del juego total de caracteres. Su nombre en informática es "caracteres ASCII", término derivado de "American Standard Code for Information Interchange" (código estándar norteamericano para el intercambio de la información). Se trata de un sistema diseñado para asegurar la compatibilidad de los datos transmitidos de un ordenador a otro. En el capítulo 7 hemos dado la lista de todos los caracteres ASCII, así como de los demás caracteres disponibles en el CPC6128, junto a sus números de código.

Vamos a verlos

Seguramente ya no encierra ningún secreto para usted un programa tan sencillo como éste:

10 FOR n=32 TO 255
20 PRINT CHR$(n);
30 NEXT

con el que hacemos que el ordenador muestre en la pantalla el juego completo de caracteres. Pero estudiemos un poco la esencia de este programa.

Lo primero que debemos observar es que al ordenador no le hemos dicho PRINT "abcdefghijklmn ...", sino PRINT CHR$(n). En vez de mencionar explícitamente los caracteres hemos puesto en su lugar una "variable". Una variable es un elemento de información que varía según impongan las instrucciones del programa. (El nombre que hemos dado a la variable en este caso, n, es arbitrario; podríamos haber puesto cualquier letra o combinación de letras, cualquier cosa que no fuera una palabra reservada de BASIC.)

¿Cómo sabemos que es una variable?

Un número tal como el 5 es fijo, está entre el 4 y el 6 y no tiene nada de variable. El carácter n también es fijo: es una letra concreta del alfabeto.

Entonces, ¿cómo distingue el ordenador las variables de las constantes? Si hubiéramos querido decirle que considerara la n como letra del alfabeto, la habríamos escrito entre comillas: "n". El ordenador habría respondido con el mensaje Syntax error, porque no entendería la línea FOR "n"=32 TO 255.

Al escribir la n sin comillas le hemos dicho al ordenador que n es una variable. La instrucción FOR de BASIC tiene que ir seguida del nombre de una variable, y el ordenador da por supuesto que lo que pongamos después de FOR es una variable.

También le hemos dicho al ordenador n=32 TO 255, con lo que hemos especificado el margen de variación de la variable: una sucesión que empieza en 32 y termina en 255.

Una vez declarada la variable, debemos decirle al ordenador qué tiene que hacer con ella; eso es lo que hace la línea 20:

20 PRINT CHR$(n);

Esta instrucción hace que el ordenador, cualquiera que sea el valor de n, busque en su memoria el carácter que corresponde a ese valor y lo escriba en la pantalla.

El punto y coma impide que el ordenador realice un retorno de carro y avance de línea después de escribir cada carácter. (De no ser así, los caracteres aparecerían uno debajo de otro en la primera columna de cada línea.)

La línea 30 le dice al ordenador que cuando haya concluido la tarea con el primer valor de n (que es el 32) debe retornar a la línea en la que está FOR y volver a hacer lo mismo con el siguiente (NEXT) valor de la variable n. Este mecanismo, denominado "bucle", es fundamental para la programación. Gracias a él nos ahorramos el tener que escribir largas series de instrucciones similares. Usted aprenderá enseguida a incluirlo en sus programas.

Cuando el bucle FOR ... NEXT alcanza el límite superior del margen declarado (255), el bucle termina y el ordenador vuelve al modo directo y muestra el mensaje Ready. Este mensaje indica que el ordenador está preparado para recibir más instrucciones, una de las cuales podría ser RUN, para ejecutar otra vez el programa. El programa está almacenado en la memoria y lo seguirá estando hasta que le digamos al ordenador que lo borre o apaguemos la máquina.

Este programa ilustra un hecho esencial de la informática: todo lo que el ordenador hace está relacionado con números. El ordenador ha escrito el alfabeto y todos los demás caracteres usando como referencia para acceder a ellos un número. Cuando usted pulsa la tecla A, no le está pidiendo al ordenador que escriba esa letra, sino que busque en su memoria la información numérica que necesita para exhibir el carácter en la pantalla. La localización de esa información es función del código numérico que se activa al pulsar la tecla. (Cada carácter tiene un código asociado; la relación completa se da en el capítulo titulado Para su referencia.)

Pero tampoco el hecho de visualizar el carácter tiene nada que ver con "escribir", sino que es un proceso numérico.

Por ejemplo, el código ASCII de la letra A es el 65. El ordenador tampoco entiende el número 65, por lo que tiene que traducirlo de decimal a una forma que le resulte más familiar: el código de máquina. Ya hemos estudiado los principios de esta conversión en la primera parte de este capítulo.

Al principio, la conversión de la notación decimal, a la que estamos tan acostumbrados, a notación hexadecimal nos parece muy difícil. Pensar en números decimales nos parece tan natural que cambiar de sistema de numeración es como cambiar de mano el tenedor y el cuchillo.

Para manejar la notación hexadecimal es necesario adquirir cierto grado de destreza mental. Una vez adquirida, se empieza a entender el por qué de muchos hechos informáticos y se hace patente la elegancia de la estructura de este sistema de numeración.

Si no conoce los sistemas binario y hexadecimal, le sugerimos que lea detenidamente la primera parte de este capítulo.

Cuando el ordenador ha convertido la pulsación de la tecla A en un número comprensible para él, busca en una zona de la memoria especificada por ese número, y en ella encuentra otra serie de números que describen la forma de la letra. El carácter que usted puede ver en la pantalla está formado por un bloque de datos, almacenado en la memoria en forma de "matriz" numérica:

Matriz en blanco (retícula)a minúsculaA mayúscula

Los elementos de la matriz son puntos dispuestos en filas columnas. El carácter se "escribe" encendiendo y apagando correctamente los puntos. En el CPC6128 cada matriz tiene 8 filas y 8 columnas. Si entre los 255 caracteres no encuentra el que necesita, usted mismo puede definirlo a su gusto con la instrucción SYMBOL que describiremos más adelante.

Los caracteres "definidos por el usuario" se construyen especificando cada uno de los 64 puntos de la matriz. Las combinaciones posibles con 1.84467E+19, es decir, aproximadamente un 2 seguido de 19 ceros. Si a esto añadimos la posibilidad de agrupar caracteres para formar bloques más grandes, vemos que las posibilidades de generar gráficos sólo están limitadas por la imaginación del usuario.

Lógica

Una diferencia fundamental entre una calculadora y un ordenador radica en la capacidad de éste para manejar operaciones lógicas y tomar decisiones en instrucciones del tipo IF ... THEN ... (si ... entonces ...). Para ello, los operadores lógicos tratan los valores a los que son aplicados como grupos de bits y examinan y modifican los bits uno a uno. No es fácil describir las operaciones lógicas sin antes dar unas definiciones concisas.

Las dos mitades de una expresión lógica son los "argumentos". Una expresión lógica tiene la siguiente forma:

<argumento> [<operador lógico> <argumento>]

donde <argumento> puede ser:

  • NOT <argumento>
  • <expresión numérica>
  • <expresión de relación>
  • (<expresión lógica>)

Los dos argumentos de un operador lógico tienen que ser enteros; de lo contrario se produce el error número 6.

Los operadores lógicos (en orden de prioridad) y su efecto sobre los bits son los siguientes:

  • AND. El resultado es 0 a menos que los bits de los dos argumentos sean 1.
  • OR. El resultado es 1 a menos que los bits de los dos argumentos sean 0.
  • XOR. El resultado es 1 a menos que los bits de los dos argumentos sean iguales.

AND es el operador lógico más utilizado.

PRINT 10 AND 10

da como resultado 10.

PRINT 10 AND 12

da como resultado 8.

PRINT 10 AND 1000

también da como resultado 8.

Esto se debe a que los números 10 y 1000 han sido convertidos a su forma binaria:

      1010
1111101000

La operación AND comprueba los bits dos a dos. Si los dos bits de las dos filas son 1, el resultado es 1:

0000001000

y este número binario es el 8 decimal. El operador lógico AND se utiliza para detectar cuándo se cumplen simultáneamente dos condiciones. He aquí un ejemplo que explica como:

10 INPUT "Dia del mes: ",dia
20 INPUT "Mes número: ",mes
30 IF dia=25 AND mes=12 THEN 50
40 CLS:GOTO 10
50 PRINT "Feliz Navidad!"

OR funciona de forma análoga, pero da como resultado 1 a no ser que los bits de los dos operandos sean 0, en cuyo caso el resultado es 0. Aplicado a los mismos números que en el ejemplo de AND,

PRINT 1000 OR 10
1002

Bit a bit:

      1010
1111101000

cuyo resultado es

1111101010

Un programa ejemplo:

10 CLS
20 INPUT "Mes número: ",mes
30 IF mes=12 OR mes=1 OR mes=2 THEN 50
40 GOTO 10
50 PRINT "Estamos en invierno!"

El operador NOT invierte todos los bits del argumento (convierte el 0 en 1, y viceversa):

10 CLS
20 INPUT "Mes número: ",mes
30 IF NOT(mes=6 OR mes=7 OR mes=8) THEN 50
40 GOTO 10
50 PRINT "No estamos en verano!"

Observe que se pueden combinar los operadores lógicos, sin más limitación que la longitud de la línea:

10 INPUT "Dia del mes: ",dia
20 INPUT "Mes número: ",mes
30 IF NOT(mes=12 OR mes=1) AND dia=29 THEN 50
40 CLS:GOTO 10
50 PRINT "No es ni diciembre ni enero, pero puede ser un año bisiesto"

El resultado de una expresión de relación es -1 o 0. La representación binaria del número -1 es una sucesión de bits iguales a 1; para el 0, todos los bits son 0. El resultado de una operación lógica entre argumentos de este tipo da –1 (verdadero) o 0 (falso).

Añada las siguientes líneas al programa anterior:

60 PRINT NOT(mes=12 OR mes=1)
70 PRINT (mes=12 OR mes=1)

Si al ejecutar el programa introduce 29 para el día y 2 para el mes, obtendrá el mensaje de la línea 50, y las líneas 60 y 70 escribirán los resultados de las operaciones.

Finalmente, XOR (OR eXclusivo) produce como resultado "verdadero" siempre que los argumentos sean diferentes.

A continuación resumimos todo lo dicho en una tabla, denominada tabla de verdad. Es una forma muy clara de ilustrar qué sucede en una operación lógica bit a bit.

Argumento A:      1010
Argumento B:      0110

Resultado de AND: 0010
Resultado de OR:  1110
Resultado de XOR: 1100

Caracteres definidos por el usuario

Una de las primeras aplicaciones que el lector encontrará para los números binarios es el diseño de caracteres con la instrucción SYMBOL. Como sabemos, el carácter se diseña en una retícula de 8 por 8; cada una de las 8 filas se convierte en un número binario poniendo un 1 en lugar del pixel que debe ser iluminado, y un cero en lugar del pixel que debe quedar del color del papel. Los 8 números así obtenidos serán los parámetros de SYMBOL. Por ejemplo, para definir un carácter que representa una casa:

la orden es

SYMBOL 240,8,60,66,165,129,181,177,255

o bien

SYMBOL 240,&08,&3C,&42,&A5,&81,&B5,&B1,&FF

o bien

SYMBOL 240,&X00001000,&X00111100,&X01000010,&X10100101,&X10000001,&X10110101,&X10110001,&X11111111

Para escribir el carácter así definido:

PRINT CHR$(240)

Finalmente, para agrupar caracteres se puede hacer

adosado$=CHR$(240)+CHR$(240)
PRINT adosado$

o bien

urbanizacion$=STRING$(15,240)
PRINT urbanizacion$

La imprenta

PRINT es la primera instrucción que se usa cuando se empieza a programar. En principio es muy sencilla, pero podemos complicarla considerablemente. En efecto, no basta con pedirle al ordenador que escriba, sino que también debemos decirle dónde y cómo.

Formato de escritura

La instrucción PRINT se puede utilizar de diversas formas. La más sencilla es poner a su derecha el elemento que se desea escribir, que puede ser un número, una cadena literal o el nombre de una variable:

PRINT 3
 3
PRINT "hola"
hola 
a=5
PRINT a
 5
a$="prueba" 
PRINT a$
 prueba 

En una instrucción PRINT se pueden poner varios elementos, intercalando entre ellos un separador, o bien TAB o SPC. Los separadores pueden ser la coma o el punto y coma. El punto y coma hace que el siguiente elemento se escriba inmediatamente a continuación del que se acaba de escribir; la coma provoca el salto a la siguiente zona de escritura. La anchura implícita de la zona de escritura es 13, pero se la puede modificar con la orden ZONE:

PRINT 3;-4;5
 3 -4  5
PRINT "buenos","dias"
 buenos             dias
PRINT 3,-4,5
 3             -4              5
ZONE 4
PRINT 3,-4,5
 3    -4     5

Observe que los números positivos llevan un espacio a la izquierda, mientras que en los negativos ese espacio está ocupado por el signo –. Todos los números llevan un espacio en blanco a la derecha. Las cadenas se escriben literalmente tal como están entre las comillas.

La función SPC lleva como parámetro una función numérica; "escribe" tantos espacios como indique el valor de la expresión. Si el valor es negativo, se toma el 0; si es mayor que la anchura de la ventana actual se toma esa anchura:

PRINT SPC(5)"hola"
     hola
x=3
PRINT SPC(x*3)"hola"
         hola

TAB es similar, pero el número de espacios que escribe es el necesario para que el siguiente elemento se empiece a escribir en la columna especificada.

La ventana en la que se escribe es la número 0, a no ser que se especifique otro número de canal (#) antes de la lista de elementos. Se pueden especificar otros canales para enviar la salida a otras ventanas. Los canales 8 y 9 están reservados para la impresora y el disco (o cinta), respectivamente. (Obsérvese que para "escribir" en el canal #9 se debe utilizar WRITE en lugar de PRINT.)

PRINT "hola"
hola                    - ventana 0
PRINT #0,"hola"
hola                    - también ventana 0
PRINT #4,"hola"
hola                    - ventana 4 (primera línea de la pantalla)
PRINT #8,"hola"
hola                    - en la impresora (si está conectada)

Con TAB y SPC se pueden controlar los formatos más sencillos, pero en cuanto el formato deseado sea un poco más complejo habrá que utilizar PRINT USING y una "plantilla" adecuada. Una plantilla de formato es una expresión literal que contiene caracteres especiales, cada uno de los cuales especifica un formato determinado. Estos caracteres, denominados "especificadores de formato", están explicados en detalle en la descripción de PRINT USING (capítulo 3). Aquí vamos a dar algunos ejemplos.

En primer lugar, he aquí los formatos disponibles para escribir cadenas literales:

" " escribe tantos caracteres como espacios haya en la plantilla, más dos:

PRINT USING "      ";"cadena de prueba"
cadena d

"!" escribe solamente el primer carácter de la cadena:

PRINT USING "!";"cadena de prueba"
c

Pero el formato literal más útil es seguramente "&". Con él se anula una función de BASIC por la cual, si una cadena es demasiado larga como para caber en la línea actual, el ordenador la escribe al principio de la línea siguiente. PRINT USING "&"; desactiva esa función. (En el siguiente ejemplo ponga BORDER 0 para hacer visibles los bordes del papel.)

MODE 1:LOCATE 39,1:PRINT "demasiado"
                                    -- línea 1 
demasiado                           -- línea 2
                                     
MODE 1:LOCATE 39,1:PRINT USING "&";"demasiado"
                                 de -- línea 1
masiado                             -- línea 2

Para los números se dispone de gran variedad de plantillas. La más sencilla es PRINT USING "#####", en la que cada # reserva espacio para un dígito:

PRINT USING "######";123
   123

La posición del punto decimal se puede especificar incluyendo un punto en la plantilla:

PRINT USING "####.#####';12.45
  12.45000

Los dígitos que quedan a la izquierda del punto decimal se pueden agrupar de tres en tres, separados por comas, si se pone una coma a la izquierda del punto en la plantilla:

PRINT USING "########,.####";123456.78
   123,456.7800

En el formato se pueden incluir los signos de dólar y de libra esterlina, para que aparezca siempre el signo monetario antes del primer dígito, aunque el número no llene el formato. Esto se consigue poniendo $$ o PtPt en la plantilla:

PRINT USING "$$##";7
  $7

PRINT USING "$$##";351
$351

PRINT USING "PtPt####,.##";1234.567
 Pt1,234.57

Observe cómo se ha redondeado este último número.

Se puede rellenar el espacio sobrante por la izquierda con asteriscos poniendo ** en la plantilla:

PRINT USING "**####.#";12.22
****12.2

Esto último se puede combinar con los signos monetarios poniendo **$ o **Pt en la plantilla.

Un signo + al principio de la plantilla especifica que siempre se debe escribir el signo del número a su izquierda. Si el + está al final de la plantilla, el signo se describe a la derecha del número.

El signo - sólo se puede poner al final de la plantilla; específica que se debe poner el signo - a la derecha del número si éste es negativo.

PRINT USING "+##";12
+12

PRINT USING "+##";-12
-12

PRINT USING "##+";12
12+

PRINT USING "##-";-12 
12-

PRINT USING "##-";12
12

La inclusión de "↑↑↑↑" en la plantilla hace que el número se escriba en forma exponencial:

PRINT USING "###.##^^^^";123.45
12.35E+01
  • Se debe sustituir ^^^^ por ↑↑↑↑.

En cualquier caso, si el número es demasiado grande como para caber en el formato especificado, el número no se trunca, sino que se lo escribe entero, precedido de un signo % para indicar lo que ha ocurrido:

PRINT USING "####";123456
%123456

Ventanas

EI BASIC del CPC6128 permite la definición y el control de hasta ocho ventanas de texto. Todas las acciones de control de textos se pueden referir a cualquiera de estas ventanas.

Las ventanas se definen con la orden WINDOW, que va seguida de 5 parámetros. El primero es opcional y especifica el número de la ventana que se va a definir; si se lo omite, el ordenador supone el 0, que es el canal por el que el BASIC emite sus mensajes normales (Ready, errores, etc.). Antes del número se pone el signo # para indicar que se está dando un número de canal. Los otros cuatro parámetros especifican los extremos izquierdo, derecho, superior e inferior de la ventana. Como son números de fila y de columna, pueden estar comprendidos entre 1 y 80 los dos primeros, y entre 1 y 25 los dos últimos.

El siguiente ejemplo define la ventana (canal) número 4, la cual se extiende desde la columna 7 hasta la 31, y desde la fila 6 hasta la 18. Reinicialice la máquina y luego escriba:

WINDOW #4,7,31,6,18

No parece que haya ocurrido nada, así que escriba lo siguiente:

INK 3,9
PAPER #4,3
CLS #4

En la pantalla ha aparecido un gran rectángulo verde, que es la ventana número 4. Este último ejemplo demuestra que las instrucciones PAPER y CLS se pueden referir a cualquiera de las ocho ventanas especificando el número de canal; su omisión hace que la orden actúe sobre la ventana número 0.

Las órdenes para las que se puede especificar número de ventana son las siguientes:

CLS, COPYCHR$, INPUT, LINE INPUT, LIST, LOCATE, PAPER, PEN, POS, PRINT, TAG, TAGOFF, VPOS, WINDOW, WRITE.

La ventana verde que hemos creado habrá borrado parte del texto que teníamos en la pantalla, que había sido enviado a la ventana número 0.

El texto se puede escribir en cualquier ventana especificando el número de canal en la instrucción PRINT:

PRINT #4,"estoy en la ventana 4"

Estas palabras han aparecido en el extremo superior de la ventana verde, no en la línea siguiente de la pantalla, que es lo que habría ocurrido con:

PRINT "estoy en la ventana 0"

Observe que al escribir esta última orden el texto ha invadido parte de la ventana verde.

Si usted desea confinar todos los mensajes de BASIC a la ventana número 4, intercámbiela con la implícita (la número 0) mediante la orden:

WINDOW SWAP 0,4

El mensaje Ready ha aparecido ahora en la ventana verde y el cursor está debajo de él. Escriba lo siguiente:

PRINT #4,"estoy en la ventana 4"

El texto ha aparecido debajo de la orden WINDOW SWAP, en lo que antes era la ventana 0 y ahora es la 4. Estos ejemplos demuestran que el ordenador recuerda la posición de escritura de cada ventana incluso después de un intercambio. Escriba lo siguiente:

LOCATE #4,20,1
PRINT "estoy en la ventana 0"
PRINT #4,"estoy en la ventana 4"

Mientras no se ejecuta una orden WINDOW, todas las ocho ventanas coinciden con la pantalla entera. Lo mismo ocurre después de una orden MODE. Así pues, si después de jugar con las ventanas se encuentra con que el cursor está en una muy pequeña, puede salir del lío escribiendo MODE 1:

MODE 1
WINDOW 20,21,7,18
MO
DE 
1

El ordenador se ha visto obligado a partir la palabra MODE, pero la instrucción funciona igual. (No olvide dejar un espacio entre la E y el 1.)

Ahora que ya tiene una idea de cómo funcionan las ventanas, escriba y pruebe el siguiente programa:

10 MODE 0
20 FOR n=0 TO 7
30 WINDOW #n,n+1,n+6,n+1,n+6
40 PAPER #n,n+4
50 CLS #n
60 FOR c=1 TO 200:NEXT c
70 NEXT n

Este programa crea 8 ventanas que se solapan y las borra con un color de papel diferente para cada una.

Cuando termine el programa y haya aparecido Ready, pulse [RETURN] varias veces para observar cómo afecta el desplazamiento de la ventana 0 a los bloques de colores. Sin embargo, aunque el contenido de las ventanas se desplace, las ventanas en sí siguen estando en el mismo sitio. Compruébelo escribiendo:

CLS #4

Fíjese también en los diferentes efectos de las siguientes órdenes:

LIST
LIST #4
LIST #3

Otra característica interesante de la orden WINDOW es que también admite los números que especifican los bordes izquierdo y derecho en orden inverso; es decir, aunque el primer parámetro sea mayor que el segundo, BASIC los interpreta en el orden correcto. Análogamente ocurre con los bordes inferior y superior.

10 MODE 0
20 a=1+RND*19:b=1+RND*19
30 c=1+RND*24:d=1+RND*24
40 e=RND*15
50 WINDOW a,b,c,d
60 PAPER e:CLS
70 GOTO 20

Interrupciones

Por si todavía no lo ha leído en otro lugar de este manual, le diremos que una de las principales innovaciones del software de los ordenadores Amstrad es su capacidad de gestión de las interrupciones desde BASIC; esto hace que el BASIC de Amstrad sea capaz de realizar acciones simultáneas controladas por programa. Esto es lo que se suele denominar “multitarea”, y se programa con las instrucciones AFTER y EVERY.

Esta misma habilidad queda demostrada por la forma en que se controla el sonido a través de recursos tales como las colas de sonido y la sincronización de canales.

De todo lo que tenga que ver con la medida del tiempo se encarga el cronómetro patrón del sistema, que es un circuito controlado por un cristal de cuarzo. Este reloj se encarga de cronometrar y de sincronizar los procesos que tienen lugar dentro del ordenador: desde el barrido de la pantalla hasta el envío de impulsos al microprocesador. Toda función del hardware que tenga que ver con el tiempo depende del reloj patrón.

Las instrucciones AFTER (después de) y EVERY (cada) hacen precisamente lo que su nombre sugiere. Así, AFTER invoca una determinada subrutina cuando ha transcurrido el tiempo especificado.

El CPC6128 mantiene un reloj de tiempo real. La orden AFTER permite que un programa de BASIC realice una tarea en un instante futuro especificado. Existen cuatro temporizadores de retardo, cada uno de los cuales puede tener una subrutina asociada.

Cuando el tiempo especificado ha transcurrido, la rutina se ejecuta automáticamente, exactamente igual que si el ordenador hubiera encontrado un GOSUB en la línea actual del programa. Cuando la rutina se acaba, con el RETURN habitual, el programa continúa a partir del punto en que fue interrumpido.

La orden EVERY permite que el programa de BASIC ejecute subrutinas a intervalos de tiempo regulares. También en este caso se dispone de cuatro temporizadores, a cada uno de los cuales se puede asignar una subrutina distinta.

Los temporizadores tienen prioridades de interrupción diferentes, lo que tiene importancia cuando varios de ellos compiten por interrumpir el programa en un momento dado. El temporizador 3 tiene la máxima prioridad, y el 0 la mínima (véase el capítulo Para su referencia).

10 MODE 1:n=14:x=RND*400
20 AFTER x,3 GOSUB 90
30 EVERY 25,2 GOSUB 170
40 EVERY 10,1 GOSUB 180
50 PRINT"Pruebe sus reflejos:"
60 PRINT"pulse la barra espaciadora"
70 PRINT"cuando yo le diga."
80 IF ind=1 THEN END ELSE 80
90 x=REMAIN(2)
100 IF INKEY(47)=-1 THEN 120
110 SOUND 1,900:PRINT"Tramposo!":GOTO 160
120 SOUND 129,20:PRINT"AHORA":t=TIME
130 IF INKEY(47)=-1 THEN 130
140 PRINT"Ha tardado";
150 PRINT(TIME-t)/300;"segundos"
160 CLEAR INPUT:ind=1:RETURN
170 SOUND 1,0,50:PRINT".";:RETURN
180 n=n+1:IF n>26 THEN n=14
190 INK 1,n:RETURN

Las órdenes AFTER y EVERY se pueden ejecutar en cualquier lugar del programa. Su efecto es reelegir la rutina asociada y poner a cero el temporizador correspondiente. Los temporizadores están compartidos por AFTER y EVERY, de modo que una orden AFTER cancela la anterior EVERY referida al mismo temporizador, y viceversa.

Las órdenes DI y EI inhiben y habilitan, respectivamente, las interrupciones. Esto sirve para permitir que cierta interrupción se procese sin ser interrumpida por otra más prioritaria. La orden REMAIN da el tiempo que queda en un temporizador y lo desactiva.

Listas de datos

Si un programa necesita que se le suministre siempre la misma información cada vez que se lo ejecuta, es preferible hacer que esa información esté contenida en el propio programa para no tener que teclearla con cada ejecución. Esto es posible gracias al par de instrucciones READ/DATA. La orden READ es similar a INPUT en el sentido de que asigna valores a variables, pero en lugar de captar los valores por el teclado los lee en listas encabezadas por la palabra DATA. Los dos ejemplos siguientes ilustran la diferencia:

10 INPUT "escriba 3 numeros separados por comas: "a,b,c
20 PRINT"los numeros son";a;"y";b;"y";c
run
10 READ a,b,c
20 PRINT"los numeros son";a;"y";b;"y";c
30 DATA 12,14,21
run

Los datos contenidos en las listas DATA van separados por comas, lo mismo que los que se teclean en respuesta a una instrucción INPUT.

Las listas DATA pueden contener, no sólo números, sino también cadenas literales constantes:

10 DIM a$(11)
20 FOR i=0 TO 11
30 READ a$(i)
40 NEXT
50 FOR i=0 TO 11
60 PRINT a$(i);" ";
70 NEXT
80 DATA Cuentan,de,un,sabio,que,un,dia,tan,pobre,y,misero,estaba
run

Observe que no es necesario encerrar entre comillas las cadenas que se ponen en las listas DATA. Las comillas en este caso son opcionales (también lo son en INPUT). El único caso en que son necesarias es cuando una cadena contiene una coma; si no se la pusiera entre comillas, la instrucción READ se detendría en la coma y no leería el resto de la cadena.

10 READ a$
20 WHILE a$<>"*"
30 PRINT a$
40 READ a$
50 WEND
60 DATA los dias de la semana son lunes, martes, miercoles, jueves, viernes y sabado
70 DATA "los dias de la semana son lunes, martes, miercoles, jueves, viernes y sabado"
80 DATA *
run

La cadena de la línea 60 contiene comas, y por lo tanto cada tramo será leído por READ y escrito por separado. En cambio, la de la línea 70 ha sido delimitada por comillas y READ la lee de una sola vez.

El ejemplo anterior demuestra que los datos pueden estar distribuidos en varias líneas (60, 70, 80, ...). Otro detalle no tan evidente es que las líneas DATA pueden estar en cualquier lugar del programa, antes o después de las instrucciones READ que leen la información.

Si un programa contiene varias instrucciones READ, cada una continúa leyendo donde terminó la anterior:

10 DATA 123,456,789,321,654,2343
20 FOR i=1 TO 5
30 READ num
40 total=total+num
50 NEXT
60 READ total2
70 IF total=total2 THEN PRINT "los datos son correctos" ELSE PRINT "hay un error en los datos"
run

Modifique uno de los primeros cinco números de la lista de la línea 10 y ejecute otra vez el programa. Esta técnica de poner al final de la lista un dato adicional que sea la suma de los anteriores constituye un buen método de detección de errores en los datos, especialmente si los datos son muchos. Se le llama "suma de verificación" (checksum).

Si un programa requiere datos mixtos (cadenas y números), se los puede combinar en una misma lista DATA, a condición de que READ los lea en el orden correcto. Por ejemplo, si una lista DATA contiene dos números seguidos de una cadena, la instrucción READ tendrá que leer primero dos variables numéricas y luego una literal:

10 DIM a(5),b(5),s$(5)
20 FOR i=1 TO 5
30 READ a(i),b(i),s$(i)
40 NEXT
50 DATA 1,7,Alfredo,3,9,Juan,2,2,Enrique,4,6,Pedro,9,1,Manuel
60 FOR i=1 TO 5
70 PRINT s$(i),":";a(i)*b(i)
80 NEXT

También se puede separar los datos en dos listas distintas:

10 DIM a(5),b(5),s$(5)
20 FOR i=1 TO 5
30 READ a(i),b(i)
40 NEXT
50 FOR i=1 TO 5
60 READ s$(1)
70 NEXT
80 DATA 1,7,3,9,2,2,4,6,9,1
90 DATA Alfredo,Juan,Enrique,Pedro,Manuel
100 FOR i=1 TO 5
110 PRINT s$(i),":";a(i)*b(i)
120 NEXT

Si ahora cambiamos el límite superior de la línea 20:

20 FOR i=1 TO 4

los dos primeros intentos de leer cadenas darán "9" y "1". Estos valores son cadenas válidas, pero el programa no funciona como esperábamos. Una forma de asegurar que el programa funcione correctamente consiste en incluir las líneas:

15 RESTORE 80
45 RESTORE 90

La orden RESTORE hace que el puntero de datos "apunte" hacia el principio de la línea especificada; por lo tanto se la puede incluir en una instrucción condicional para que el bloque de datos leídos dependa del resultado de alguna comprobación. Por ejemplo, en un juego que esté previsto para varios niveles de destreza, los datos de cada nivel pueden ser seleccionados mediante una variable adecuada. Veamos un ejemplo:

1000 REM seccion para dibujar la pantalla
1010 IF nivel=1 THEN RESTORE 2010
1020 IF nivel=2 THEN RESTORE 2510
1030 IF nivel=3 THEN RESTORE 3010
1040 FOR y=1 TO 25
1050 FOR x=1 TO 40
1060 READ car
1070 LOCATE x,y:PRINT CHR$(car);
1080 NEXT x,y
...
2000 REM DATA para pantalla 1
2010 DATA 200,190,244,244,210, ... etc.
...
2500 REM DATA para pantalla 2
2510 DATA 100,103,245,243,251, ... etc.
...
3000 REM DATA para pantalla 3
3010 DATA 190,191,192,193,194, ... etc.

Otra aplicación típica de las instrucciones DATA/READ/RESTORE es un programa que interprete una melodía. Los periodos de tono se pueden poner en listas data; RESTORE puede hacer que se repita una sección moviendo el puntero de datos hacia atrás:

10 FOR i=1 TO 3
20 RESTORE 100
30 READ nota
40 WHILE nota<>-1
50 SOUND 1,nota,35
60 READ nota
70 WEND
80 NEXT
90 SOUND 1,142,100
100 DATA 95,95,142,127,119,106
110 DATA 95,95,119,95,95,119,95
120 DATA 95,142,119,142,179,119
130 DATA 142,142,106,119,127,-1
run

El sonido de la música

De todas las instrucciones del CPC6128, es posible que las de sonido y envolventes le parezcan las más imponentes a primera vista. Y sin embargo, con un poco de práctica, muy pronto podrá programar ruidos de diversos tipos e incluso melodías con armonía.

Empecemos por analizar las cuatro primeras partes de la instrucción SOUND, que son las siguientes: situación de canales, periodo de tono, duración de la nota y volumen. Lo primero que hay que saber es en qué margen de valores puede estar cada uno de estos parámetros.

Aplazaremos el primero de momento porque es el más complicado. El segundo, período de tono, puede tener cualquier valor entero comprendido entre 0 y 4095. Sin embargo sólo algunos de estos valores producen notas reconocibles: son los que están relacionados en la parte 6 del capítulo Para su referencia. Por ejemplo, el número 239 produce la nota DO media; el 253 produce la nota SI inmediatamente anterior; los valores 240 a 252 producen cada uno un tono diferente, pero ninguno de ellos corresponde a la escala del piano. Si el parámetro es 0, no se produce ningún tono; esto es útil cuando se está generando ruido.

El tercer parámetro de la orden SOUND especifica la duración de la nota en unidades de centésimas de segundo. Su valor puede estar normalmente entre 1 y 32767. Si es 0, la duración queda controlada por la "envolvente" (de la que hablaremos más adelante). Si el parámetro es negativo, su valor absoluto indica cuántas veces se va a repetir la envolvente; por ejemplo, -3 significa "repetir la envolvente tres veces".

El cuarto parámetro especifica el volumen. Puede valer entre 0 y 15; el valor implícito es 12, que es el que el ordenador supone si no se especifica otra cosa. En los sonidos sencillos que hemos conocido hasta ahora, el volumen ha permanecido constante durante el tiempo en que ha sonado cada nota. Sin embargo, cuando se utiliza una "envolvente de volumen" para hacer que éste deje de ser constante, el parámetro "volumen" de SOUND se considera como volumen inicial de la nota.

Bueno, vamos con el parámetro de situación de canales. Quizá sepa ya el lector que el significado de este parámetro depende del valor de sus bits; para entenderlo necesitará saber algo acerca de los números binarios (parte 1 de este capítulo).

El sonido se puede generar en tres canales distintos. Si el ordenador está conectado a un amplificador estereofónico, un canal será el derecho, otro el izquierdo y otro común a ambos o central. Para especificar en qué canal o canales debe sonar una nota se utilizan los siguientes números:

  • 1 canal de A
  • 2 canal de B
  • 4 canal de C

Para enviar el sonido a varios canales, se suman los números correspondientes. Por ejemplo, para que la nota suene en los canales A y C, el parámetro debe ser 1+4=5.

SOUND 5,284

Se preguntará el lector por qué el número del canal C es 4, y no 3. Observe que estos tres números son potencias de 2(1=2↑0, 2=2↑1, 4=2↑2) y se combinan para dar un número binario. Si imaginamos un número binario de tres dígitos, cada uno de ellos se puede utilizar para indicar si el canal correspondiente debe estar conectado o desconectado. En el ejemplo anterior, 5 en decimal es equivalente a 1*4+0*2+1*1, es decir, 101 en binario. Si a los dígitos de este número binario les ponemos las etiquetas C, B y A, tenemos

C B A
1 0 1

de forma que C y A están conectados, mientras que B está desconectado. Si quisiéramos que la nota sonase en los canales A y B, el número tendría que ser

C B A
0 1 1

En decimal: 0*4+1*2+1*1=3. La orden SOUND sería

SOUND 3,142

Naturalmente, este número tiene que coincidir con el que se obtendría sumando los valores correspondientes a los dos canales: 1+2=3 (recuerde que A=1, B=2, C=4).

Si después de todo no ha entendido cómo funciona esto en binario, no se preocupe. Le basta con saber que las combinaciones de canales se programan sumando los números de selección de los canales deseados.

Lamentablemente (o afortunadamente, según cómo se mire), todavía podemos sumar otros números a este parámetro. Así, los números 8, 16 y 32 especifican que el sonido debe sincronizarse con otro canal (A, B o C, respectivamente). Ahora hace falta saber qué es eso de "sincronizar con un canal". Pues bien, los sonidos que hemos generado hasta ahora han ido directamente al canal especificado. Escriba lo siguiente:

SOUND 1,142,2000
SOUND 1,90,200

A menos que sea usted un mecanógrafo muy lento, habrá tenido tiempo de escribir la segunda orden antes de que se extinguiera el primer sonido. Esto ocurre porque el sistema de sonido puede guardar hasta cinco órdenes en cada una de las colas de los tres canales. Si queremos que suene una nota por el canal A y luego dos notas simultáneamente en los canales A y B, necesitamos un forma de indicar al ordenador que no debe ejecutar la nota del canal B mientras no haya terminado la primera del A. En esto consiste la sincronización de canales. Hay dos formas de conseguirla:

SOUND 1,200,1000
SOUND 3,90,200

En este ejemplo hemos dirigido la segunda nota a A y B, y por lo tanto no puede sonar mientras no haya concluido la primera. La limitación de este método es que el sonido que se envía a varios canales tiene que ser igual en todos ellos (en este caso, ,90,200 era igual para el A que para el B). El otro método es el siguiente:

SOUND 1,200,2000
SOUND 1+16,90,200
SOUND 2+8,140,400

Aquí hemos hecho que la segunda nota de A se sincronice con el sonido de B (y que éste se sincronice con el canal A). La ventaja de este método es evidente: las notas sincronizadas pueden ser (y en este caso son) diferentes. Estos números de sincronización también son interpretables bit a bit:

  • 8=2↑3, 16=2↑4, 32=2↑5

Así, el número de situación de canales se puede considerar como número binario cuyos dígitos tienen el siguiente significado:

Sincronizar conSumarSonar enSumar
C32C4
B16B2
A8A1

Por ejemplo, para hacer sonar una nota en el canal C sincronizado con el A:

0   0   1   1   0   0

Éste es el número binario 1100, equivalente a 8+4 en decimal.

Así pues, el número de situación de canales 12 ordena a la máquina que haga sonar una nota en el canal C y que espere por una nota que debe sonar en el canal A y que ha sido marcada para sincronizarla con el C.

Si ahora sumamos 64 (=2↑6) al parámetro, estamos indicando que la nota debe ser retenida. Esto significa que no va a sonar mientras no la liberemos con la orden RELEASE.

Finalmente, si sumamos 128 (=2↑7), borramos la cola de sonido del canal especificado.

Por ejemplo, si hemos ordenado un sonido que va a durar un buen rato, podemos anularlo borrando la cola del canal correspondiente:

SOUND 1,248,30000   ' (esta nota dura 5 minutos)
SOUND 1+128,0       ' (pero esta la detiene)

Cuando se esté en modo directo, la forma más rápida de interrumpir un sonido es pulsar [←DEL] al principio de la línea; el pitido que así se produce borra todas las colas de sonido.

Ahora que ya sabemos enviar sonido a cualquier combinación de canales (con sincronización, si es necesario), vamos a intentar producir algo más agradable que los molestos pitidos que produce una orden SOUND en esta forma sencilla. Lo haremos dotando al sonido de una envolvente: una gráfica que define cómo evoluciona la intensidad del sonido a lo largo del tiempo. Las notas producidas por los instrumentos musicales tienen una fase inicial de ataque en la que el volumen sube muy deprisa; después se mantiene a un nivel algo inferior hasta que finalmente decae gradualmente a cero. En el ordenador se puede dar una envolvente de este tipo a las notas producidas por la orden SOUND. La instrucción con la que se programa la envolvente es ENV. Veamos un ejemplo sencillo:

ENV 1,5,3,4,5,-3,8
SOUND 1,142,0,0,1

La instrucción ENV se debe ejecutar antes que la SOUND que la utiliza. Para invocar una envolvente en una instrucción SOUND se pone como quinto parámetro de éste el número de referencia de la envolvente, en este caso el 1, que es el número con que se la creó en la instrucción ENV. Los parámetros de ENV describen tanto la duración como el volumen de la nota, de modo que en SOUND podemos poner 0 en lugar de los datos de duración y volumen. La envolvente del ejemplo anterior hace que la nota crezca en 5 etapas, en cada una de las cuales el volumen aumenta en 3 unidades y cuya duración es de 4 centésimas de segundo. Después se especifica que el volumen debe decaer en 5 etapas, –3 unidades en cada una, siendo la duración de cada etapa 8 centésimas de segundo. Es decir, el primer parámetro de ENV es el número de referencia de la envolvente y va seguido por grupos de tres números. Dentro de cada grupo, el primer parámetro es el número de escalones de variación del volumen; el segundo, la amplitud de esos escalones; y el tercero, su duración. La duración total de cada sección será igual al producto del primer parámetro (número de escalones) por el tercero (duración de cada escalón). El aumento o disminución total del volumen es igual al producto del primer parámetro por el segundo (variación del volumen por escalón). La duración total de una envolvente es la suma de las correspondientes a las secciones que la integran.

En el ejemplo anterior, SOUND fijaba en 0 el volumen inicial de la nota. Pero esto no tiene que ser necesariamente así. En el siguiente ejemplo el volumen decrece desde el valor inicial 15 y luego vuelve a subir:

ENV 2,5,-2,1,20,0,1,10,1,1
SOUND 1,142,0,15,2

El número de esta envolvente es el 2. Consta de tres secciones. En la primera el volumen se reduce en 5 escalones de -2; es decir, varía a través de 5 etapas y en cada una se reduce en 2 unidades. La duración de cada etapa es de 1 centésima de segundo. La segunda sección tiene 20 etapas en las que no varía el volumen (0) y cada una de las cuales dura 1 centésima de segundo. Finalmente, la tercera sección consta de 10 etapas, con un incremento de volumen de 1 unidad y duración de 1 centésima de segundo cada una.

La instrucción SOUND especifica un volumen inicial de 15; al final de la primera sección ha decrecido hasta 5; se mantiene a ese nivel durante 20 centésimas de segundo y luego vuelve a crecer hasta alcanzar el nivel 15.

Para mejor visualizar y diseñar la forma de las envolventes conviene dibujarlas en un papel milimetrado y leer en él los valores de los parámetros requeridos. En las dos figuras siguientes mostramos las dos envolventes definidas hasta ahora.

El máximo número de secciones que se puede incluir en una envolvente es 5; como cada sección requiere 3 parámetros, la instrucción ENV puede llegar a tener 16 parámetros en total, contando el primero, que indica cuál de las 15 envolventes posibles se está definiendo. Podemos imaginar los números de volumen como dispuestos en círculo; si vamos subiendo de nivel y sobrepasamos el 15, volvemos al 0; análogamente, si intentamos descender por debajo del 0, volvemos al 15. Por ejemplo, en

ENV 3,9,5,20
SOUND 1,142,0,0,3

la envolvente consta de una sola sección de 9 etapas, cada una de las cuales incrementa el volumen en 5 unidades y dura 20 centésimas de segundo. Al terminar la tercera etapa, el volumen ha subido de 0 a 15; en la cuarta etapa volverá a 4; en la quinta a 9, etc. El proceso está ilustrado en la figura siguiente.

El margen de valores para el número de escalones es de 0 a 127. En cada uno el valor puede variar entre -128 y +127 (los valores negativos representan disminución). La duración de cada escalón puede ser de entre 0 y 255 (centésimas de segundo).

Ya sabemos cómo variar el volumen de una nota. Ahora podemos estudiar cómo se define la variación de su tono con el tiempo para producir efectos tales como el "vibrato".

El método es muy parecido al de las envolventes de volumen. Las envolventes de tono se definen con la instrucción ENT. Por ejemplo:

ENT 1,5,1,1,5,-1,1
SOUND 1,142,10,15,,1

Las envolventes de tono se invocan con la instrucción SOUND poniendo en esta como sexto parámetro el número de referencia de la envolvente. La instrucción ENT tiene que ser ejecutada antes que la SOUND que la utiliza.

Este primer ejemplo de ENT define la envolvente de tono número 1. La primera sección consta de 5 etapas; en cada una el periodo de tono crece en 1 unidad; cada una dura 1 centésima de segundo. La segunda sección consta de 5 etapas; la variación del periodo de tono en cada una de ellas es –1 (descenso); la duración de cada etapa es de 1 centésima de segundo. La duración total es, pues, 5+5=10 centésimas de segundo. Obsérvese que esta duración ha sido especificada en SOUND, ya que la duración de la envolvente de tono no determina la duración de la nota (mientras que sí lo hace la envolvente de volumen). Si la duración especificada en SOUND es menor que la de la envolvente, el final de ésta se perderá. Si es mayor, el final de la nota se ejecutará a tono constante. Esto último es también aplicable a las envolventes de volumen.

(Nótese la ausencia del número de envolvente de volumen en la última instrucción SOUND, debida a que aún no hemos definido una envolvente de volumen para este sonido.)

Las envolventes de tono normalmente duran menos que la nota. Se puede hacer que la envolvente se repita mientras la nota está sonando. Para ello se especifica un número de envolvente negativo, cuyo valor absoluto se cita en la instrucción SOUND:

ENT -5,4,1,1,4,-1,1
SOUND 1,142,100,12,,5

Esta repetición de la envolvente produce el efecto "vibrato". Cuando se diseñan envolventes de tono, es conveniente que el tono varíe simétricamente con respecto al valor inicial, de forma que al repetir la envolvente el tono no se desvíe demasiado con respecto al valor central. Pruebe el siguiente sonido:

ENT -6,3,1,1
SOUND 1,142,90,12,,6

Habrá observado que la frecuencia ha disminuido drásticamente. Esto ha ocurrido porque la envolvente impone un aumento del periodo de tono de 3 unidades y se repite 30 veces (90/3). No obstante, ese efecto se puede aprovechar para imitar trinos y sirenas:

ENT -7,20,1,1,20,-1,1
SOUND 1,100,400,12,,7
ENT -8,60,-1,1,60,1,1
SOUND 1,100,480,12,,8

Se pueden definir hasta 15 envolventes de tono, con números de referencia del 1 al 15; los números negativos indican que la envolvente se repite. Para cada sección, el número de escalones puede estar entre 0 y 239. La variación del periodo de tono en cada escalón puede ser de entre –128 y +127. La duración de cada escalón puede ser de entre 0 y 255 (centésimas de segundo). Cada envolvente puede tener hasta 5 secciones.

El último parámetro que se puede incluir en la instrucción SOUND, el séptimo, caracteriza el nivel de ruido que se añade al sonido. Obsérvese que sólo hay un canal de ruido y que, por consiguiente, cada vez que se especifica un nivel de ruido se anula la anterior especificación.

El ruido se puede mezclar con un tono, pero también se lo puede programar por separado, para lo cual se debe poner un 0 como período de tono en SOUND. Esto es útil para imitar ruidos de percusión:

ENT -3,2,1,1,2,-1,1
ENV 9,15,1,1,15,-1,1
FOR a=1 TO 10:SOUND 1,4000,0,0,9,3,15:NEXT

Un ruido como éste puede servir de base para imitar el de una locomotora. Obsérvese que hemos combinado los dos tipos de envolvente y el ruido. En SOUND hemos puesto 0 para los parámetros de duración y de volumen, por lo que estas características quedan controladas por las envolventes de volumen.

Como ya estamos en condiciones de utilizar SOUND, ENV y ENT a plena potencia, vamos a estudiar algunas otras órdenes y funciones.

Como el lector recordará, al describir el primer parámetro de SOUND dijimos que si le sumábamos el número 64 el sonido quedaba "retenido" en la cola, y que no sonaría mientras no lo liberásemos. La forma de liberarlo es ejecutar la orden RELEASE. Esta palabra va seguida de un número cuyos bits determinan a qué canales afecta la orden:

  • 4 significa canal C
  • 2 significa canal B
  • 1 significa canal A

Los canales se combinan sumando los números correspondientes. Así, para liberar el sonido de los tres canales la orden que se requiere es:

RELEASE 7

donde 7=1+2+4. Si no hay sonido retenido en ningún canal, la orden no tiene efecto. Pruebe lo siguiente:

SOUND 1+64,90
SOUND 2+64,140
SOUND 4+64,215
RELEASE 3:FOR t=1 TO 1000:NEXT:RELEASE 4

No se produce ningún sonido mientras no se ejecuta la primera orden RELEASE, la cual libera los sonidos de los canales A y B. Después de una pausa, la segunda orden RELEASE libera el canal C.

Hay otro método para sincronizar sonidos. Cuando se retiene un sonido sumando 64 a su número de situación de canal, no sólo queda él retenido, sino todos los que se envíen a continuación a ese mismo canal. Si se envían más de cuatro sonidos a una cola que está retenida, la máquina queda bloqueada hasta que se libere la cola (posiblemente al ejecutarse una subrutina invocada por AFTER o EVERY). Ésta no es una buena forma de gestionar los sonidos, ya que la máquina se detendrá cada vez que se llene una cola. Lo mismo ocurre si se ejecutan varias órdenes SOUND seguidas. Pruebe este programa:

10 FOR a=1 TO 8
20 SOUND 1,100*a,200
30 NEXT
40 PRINT"hola"
run

El texto no aparece en la pantalla inmediatamente, sino al cabo de tres segundos. Esto ocurre porque el programa no puede llegar a la línea 40 mientras no haya espacio libre en la cola para almacenar todos los sonidos.

BASIC dispone de un mecanismo de interrupción, similar al que se utiliza en AFTER, EVERY y ON BREAK GOSUB, mediante el cual se puede hacer que se ejecute una subrutina cada vez que queda espacio libre en una cola especificada:

10 a=0
20 ON SQ(1) GOSUB 1000
30 PRINT a;
40 GOTO 30
1000 a=a+10
1010 SOUND 1,a,200
1020 IF a<200 THEN ON SQ(1) GOSUB 1000
1030 RETURN

Observe que el programa no se detiene. La instrucción SOUND no se ejecuta mientras no hay espacio libre en la cola del canal A (1), hecho que detecta la orden ON SQ(1) GOSUB de la línea 20. Esta orden inicializa un mecanismo de interrupción que ejecuta la subrutina cada vez que queda un hueco libre en la cola especificada. El mecanismo tiene que ser reinicializado cada vez que se ejecuta la subrutina (línea 1020). En este ejemplo, la reinicialización sólo se produce si a es menor que 200.

En un programa que lleve a cabo acciones relativamente lentas (por ejemplo, mover objetos por la pantalla), se puede poner música de fondo programando una subrutina que ejecute una nota cada vez que quede un hueco libre en la cola. De esta forma se asegura que el programa no se va a detener en espera de que quede espacio en la cola. Si los valores de las notas están contenidos en listas DATA, se puede hacer que la subrutina de sonido deje de reinicializarse cuando los datos estén a punto de agotarse.

El parámetro que va entre paréntesis en la instrucción ON SQ( ) GOSUB puede ser 1, 2 o 4, dependiendo del canal cuya cola se desee examinar.

Hay una función, SQ( ), que se puede utilizar para determinar el estado de los canales de sonido. Su parámetro puede ser 1, 2 o 4. El valor generado por la función se interpreta bit a bit según la siguiente tabla:

BitDecimalSignificado
0, 1, 21 a 4Número de huecos libres en la cola
38La primera nota de la cola está marcada para sincronizar con el canal A
416La primera nota de la cola está marcada para sincronizar con el canal B
532La primera nota de la cola está marcada para sincronizar con el canal C
664La primera nota de la cola está retenida (está a 1 el bit de retención)
7128En este momento está sonando una nota

Pruebe el siguiente ejemplo:

10 SOUND 2,200
20 x=SQ(2)
30 PRINT BIN$(x)
run

La línea 30 escribe el número binario 10000100. El bit 7 está a 1, lo que indica que en el canal había una nota sonando cuando se ejecutó la línea 20. Los tres dígitos menos significativos son 100; equivalen al número decimal 4, y esto quiere decir que había cuatro espacios libres en la cola. Esta función examina la situación del canal en un punto específico del programa; en cambio, ON SQ( ) GOSUB examina la cola, y reacciona en consecuencia, en un punto indeterminado.

Hasta ahora todos los ejemplos han consistido en hacer sonar una o dos notas. Si se va a ejecutar un grupo de notas independientes, por ejemplo las de una melodía, sus características se pueden guardar en líneas DATA, para luego leerlas con READ e introducirlas en SOUND:

10 FOR octava=-1 TO 2
20 FOR x=1 TO 7:REM notas por octava
30 READ nota
40 SOUND 1,nota/2^octava
50 NEXT
60 RESTORE
70 NEXT
80 DATA 426,379,358,319,284,253,239
run

Se debe substituir el carácter ^ por ↑.

El ejemplo final de esta sección se basa en este concepto. En los canales A y B se hace sonar una melodía con ritmo, utilizando la sincronización. Este ejemplo muestra cómo utilizar las listas DATA para incluir información sobre nota, octava, duración y sincronización:

10 REM la linea 190 da la melodia en clave de agudos
20 REM la linea 200 da la melodia en clave de graves
30 DIM escala%(12):FOR x%=1 TO 12:READ escala%(x%):NEXT
40 canal1%=1:READ canal1$:canal2%=2:READ canal2$
50 CLS
60 velocidad%=12
70 escala$=" a-b b c+c d-e e f+f g+g"
80 ENV 1,2,5,2,8,-1,10,10,0,15
90 ENV 2,2,7,2,12,-1,10,10,0,15
100 ENT -1,1,1,1,2,-1,1,1,1,1
110 DEF FNm$(s$,s)=MID$(s$,s,1)
120 canal1%=1:GOSUB 200
130 canal2%=1:GOSUB 380
140 IF canal1%+canal2%>0 THEN 140
150 END
160 DATA &777,&70c,&6a7,&647,&5ed,&598
170 DATA &547,&4fc,&4b4,&470,&431,&3f4
180 DATA 4cr4f4f1f1g1A1-B2C2f4g2g1A1-B6A2Cr1f1g1f1g1a1-b1A1-b2C2g2A2g2f1g1a2g2f6e2c2e2c2g2e2c1-B1A2g2f4e4d8c4f3f1c2d4-b2fr2-B2A2g2f6e2gr4C4-B1a1f1-b1g2c2-b4a4g4fr6A2A2-B4-B2Ar2-B2A2g2f6e2g4C4-B1A1f1-B1g2C2-B4A4g8f.
190 DATA r4f4f8f4e4c4fr8f4e2f2e4d2e2d8c8c6e2f4g4g8e4f3f1c4dr8g4cr4e4c6f2d4c4c8fr8-e4dr8g8c4e4c6f2d4c4c8f.
200 REM enviar sonido al canal A
210 p1$=FNm$(canal1$,canal1%)
220 IF p1$<>"r" THEN r1%=0:GOTO 240
230 r1%=16:canal1%=canal1%+1:p1$=FNm$(canal1$,canal1%)
240 IF p1$="." THEN canal1%=0:RETURN ELSE I1%=VAL(p1$)
250 canal1%=canal1%+1
260 n1$=FNm$(canal1$,canal1%)
270 canal1%=canal1%+1
280 IF n1$="+" OR n1$="-" THEN 350
290 n1$=" "+n1$
300 nd1%=(1+INSTR(escala$,LOWER$(n1$)))/2
310 IF ASC(RIGHT$(n1$,1))>96 THEN o1%=8 ELSE o1%=16
320 SOUND 1+r1%,escala%(nd1%)/o1%,velocidad%*I1%,0,1,1
330 ON SQ(1) GOSUB 200
340 RETURN
350 n1$=n1$+FNm$(canal1$,canal1%)
360 canal1%=canal1%+1
370 GOTO 300
380 REM enviar sonido al canal B
390 p2$=FNm$(canal2$,canal2%)
400 IF p2$<>"r" THEN r2%=0:GOTO 420
410 r2%=8:canal2%=canal2%+1:p2$=FNm$(canal2$,canal2%)
420 IF p2$="." THEN canal2%=0:RETURN ELSE l2%=VAL(p2$)
430 canal2%=canal2%+1
440 n2$=FNm$(canal2$,canal2%)
450 canal2%=canal2%+1
460 IF n2$="+" OR n2$="-" THEN 530
470 n2$=" "+n2$
480 nd2%=(1+INSTR(escala$,LOWER$(n2$)))/2
490 IF ASC(RIGHT$(n2$,1))>96 THEN o2%=4 ELSE o2%=8
500 SOUND 2+r2%,escala%(nd2%)/o2%,velocidad%*l2%,0,1,2
510 ON SQ(2) GOSUB 380
520 RETURN
530 n2$=n2$+FNm$(canal2$,canal2%)
540 canal2%=canal2%+1
550 GOTO 480

run

Dicho gráficamente

En esta sección vamos a repasar las funciones gráficas del ordenador. Iremos construyendo paso a paso un primer ejemplo para ilustrar el funcionamiento de las diversas instrucciones.

Para empezar, dividimos la pantalla en una ventana de texto (línea 40) y una ventana gráfica (línea 30). Además, establecemos el modo de pantalla y asignamos tintas parpadeantes a dos plumas (líneas 20):

10 REM MASK Y TAG en una ventana
20 MODE 1:INK 2,10,4:INK 3,4,10
30 ORIGIN 440,100,440,640,100,300
40 WINDOW 1,26,1,25
50 CLG 2

Si ejecuta este programa, podrá ver un cuadrado parpadeante al lado derecho de la pantalla. Este cuadrado ha sido borrado con la pluma número 2 (magenta/cyan) por la línea 50. El origen de coordenadas ha sido trasladado al extremo inferior izquierdo del cuadrado. La orden MODE ha llevado el cursor al origen de coordenadas (X=0, Y=0); podemos dibujar una diagonal en el cuadrado con la línea 60:

60 DRAW 200,200,3

Ejecute el programa y observe el efecto. Añada ahora:

80 MOVE 0,2:FILL 3

La línea 80 lleva el cursor al interior de una de las dos mitades del cuadrado y la rellena con la pluma 3. El límite del recinto que se rellena es el borde de la ventana gráfica (que en este caso es también el del cuadrado) y cualquier recta que se haya dibujado con la pluma gráfica actual (la 3) o con la que se utiliza para rellenar (también la 3).

Ejecute esta versión del programa.

Para comprobar lo dicho acerca de los límites del recinto, añada la siguiente línea 70. Observe que la única razón para que el rellenado se limite a una mitad del cuadrado es que la pluma de rellenar es la misma con la que se dibujó la diagonal.

70 GRAPHICS PEN 1
run

Modifique la línea 80 para rellenar con la pluma 1 y ejecute el programa para comprobar esto último. Cuando lo haya hecho, vuelva a dejar la línea 80 como estaba (FILL 3).

Añada ahora las líneas siguientes para dibujar un cuadrado:

100 MOVE 20,20
110 DRAW 180,20
120 DRAW 180,180
130 DRAW 20,180
140 DRAW 20,20
run

Este cuadrado ha sido dibujado con la pluma 1 a causa de la línea 70. Si omitiéramos la línea 70, tendríamos que incluir ",1" como tercer parámetro de MOVE en la línea 100, o bien en el DRAW de la línea 110, para pedir al ordenador que cambiase la pluma gráfica.

Líneas discontinuas

Las rectas que dibujemos no tienen que ser necesariamente de trazo continuo, sino que pueden ser de puntos. La orden MASK nos permite definir el tamaño de los puntos. La forma que definamos se repetirá cada 8 pixels. Si la primera línea deja incompleto el último grupo de 8 pixels, la siguiente empezará dibujando los puntos restantes. Con una nueva orden MASK (quizá con el mismo parámetro) se hace que el siguiente dibujo empiece por el principio del grupo.

El parámetro de MASK es en realidad un byte binario en el que los bits que están a 1 indican que el pixel correspondiente se debe iluminar con la tinta de la pluma. En nuestro ejemplo vamos a poner el parámetro directamente en forma binaria (identificada por el prefijo &X); queremos que cada grupo de 8 pixels tenga cuatro pixels iluminados en el centro y dos apagados a cada lado. De esta forma las rectas serán de trazos, con cuatro pixels encendidos y cuatro apagados. Añada la línea siguiente:

90 MASK &X00111100
run

Pero vemos que el enlace en los rincones no es como nos gustaría. Lo que ocurre es que los pixels de las esquinas se dibujan dos veces: una al final de una recta y otra al principio de la siguiente. Una forma un tanto torpe de resolver este problema es incluir las líneas:

115 MOVE 180,22
125 MOVE 178,180
135 MOVE 20,178
RUN

Pero es más fácil poner ",0" como segundo parámetro de MASK para indicar que no se debe dibujar el primer punto de cada línea. Modifique la línea 90:

90 MASK &X00111100,0

y borre las que ya no necesitamos:

115
125
135

Ejecute el programa y observe que el cuadrado vuelve a ser simétrico. Si hubiéramos puesto ",1" como segundo parámetro de MASK, todas las rectas serían de trazo continuo con el primer pixel encendido.

Ahora observe los huecos entre trazos. En el triángulo inferior derecho hay algo que no está en el otro: es el papel gráfico, al que se ha dado el número 2 con la orden CLG 2 de la línea 50, pero que es invisible en el triángulo superior porque es del mismo color que el fondo. Modifique la línea 50:

50 CLG 2:GRAPHICS PAPER 0

y vuelva a ejecutar el programa. El papel ya es visible en todos los bordes del cuadrado.

Podemos hacer que el papel gráfico sea invisible ("transparente"). Esto significa que cuando dibujemos líneas de trazos, los huecos no taparán lo que haya debajo. Los dibujos se hacen transparentes añadiendo ",1" a la orden GRAPHICS PEN. (Para hacer los opacos el parámetro debe ser ",0"). Modifique la línea 70:

70 GRAPHICS PEN 1,1
run

y observe el resultado.

Además de dibujar rectas (y puntos sueltos), podemos escribir textos en la posición del cursor gráfico. La ventaja con respecto al método normal de escritura de textos es que así podemos situar los textos con mayor precisión (justamente en el pixel deseado, en vez de a saltos de 8 pixels).

Para escribir caracteres por este método, el cursor se pone en el lugar en el que queremos que quede el punto superior izquierdo del carácter, después ejecutamos la orden TAG (o TAG #x para otros canales) seguida de las órdenes PRINT normales. El cursor gráfico avanza automáticamente 8 pixels hacia la derecha cada vez que se escribe un carácter. Añada las siguientes líneas:

160 MOVE 64,108
170 TAG
180 PRINT "ELENA"
190 TAGOFF
run

(BASIC envía siempre sus mensajes a la ventana de texto, cualquiera que sea la situación del conmutador TAG/TAGOFF, pero es una buena costumbre cancelar TAG en cuanto se haya terminado de escribir en la pantalla gráfica.)

Pero ¿qué son las flechas que han salido a la derecha del nombre? Muy sencillo: el código de retorno del carro, CHR$(13), y el de avance de línea, CHR$(10), en versión gráfica. Las rutinas del firmware que escriben en la pantalla gráfica traducen los 32 primeros caracteres ASCII a sus versiones gráficas [igual que si se los enviara a la pantalla de texto precedidos de CHR$(1)]. La razón es que los 32 códigos de control normalmente sólo tienen utilidad en la pantalla de texto; por lo mismo, si sobrepasamos el borde derecho al escribir en la pantalla gráfica, el ordenador lo permite y no salta automáticamente a la línea siguiente.

Los signos de retorno del carro y de avance de línea se suprimen poniendo un punto y coma al final de la instrucción PRINT:

180 PRINT "ELENA";
run

El texto enviado a la pantalla gráfica usando TAG depende de las mismas órdenes GRAPHICS PEN que los dibujos. Así, en el ejemplo, el nombre lo escribe GRAPHICS PEN 1, y es transparente. La orden

150 GRAPHICS PEN 1,0
run

selecciona papel opaco, mientras que

150 GRAPHICS PEN 0,1
RUN

escribe con pluma número 0 en modo transparente.

Borre la línea 150 y ejecute el programa. La pluma gráfica vuelve a ser 1 en modo transparente (línea 70).

Caracteres transparentes

Hay un código de control que permite escribir caracteres en la pantalla de texto en modo transparente. Añada las siguientes líneas:

200 PRINT#2,CHR$(22);CHR$(1)
210 LOCATE #2,32,14:PRINT#2,"******"
220 LOCATE #2,32,14:PRINT#2,"______"
230 PRINT#2,CHR$(22);CHR$(0)
run

La línea 200 pone el canal #2 en modo transparente. Observe que los signos de subrayar se funden con los asteriscos sin borrarlos. De esta manera se pueden formar caracteres compuestos, incluso en varios colores. La línea 230 vuelve a poner el canal #2 en modo opaco.

Modos de tinta

Es posible dibujar gráficos de tal modo que la tinta con la que se dibuja interaccione con la ya presente en la pantalla. El número de tinta final para cada punto se calcula realizando una operación lógica con la antigua tinta del pixel y la tinta de la pluma gráfica. La operación puede ser AND, OR o XOR. El modo de tinta se puede especificar como cuarto parámetro en las instrucciones DRAW/DRAWR, PLOT/PLOTR o MOVE/MOVER, o bien enviando a la pantalla los códigos de control CHR$(23)+CHR$(<modo>). En cualquier caso, el modo es 1 para XOR, 2 para AND y 3 para OR. El valor 0 restaura el modo normal, en el que se dibuja sin interacción.

En el siguiente ejemplo ilustramos la combinación XOR. Este modo se utiliza habitualmente en los denominados "gráficos de tortuga", porque tiene la propiedad de que si se dibuja algo dos veces se restaura la situación original. La rutina que dibuja el cuadrado se ejecuta dos veces (en las líneas 110 y 130), así como la escritura con TAG (líneas 170 y 190). Las órdenes FRAME introducen un retardo suficiente como para que el efecto sea visible. Nótese que las órdenes de la línea 90 no llevan primer parámetro. Esto es correcto tratándose de estas órdenes concretas; lo hemos hecho así para no modificar las características correspondientes.

El tercer parámetro (,1) de la orden MOVE de la línea 220 selecciona la pluma gráfica número 1, anulando así la "3" seleccionada en la línea 60. El modo XOR se establece en el cuarto parámetro de la orden DRAWR de la línea 230. Obsérvese que también aquí hemos omitido un parámetro.

Podemos observar una consecuencia del efecto del segundo parámetro de MASK suprimiendo esta orden de la línea 90. Los rincones del cuadrado desaparecen porque se dibujan dos veces (al final de una recta y al principio de la siguiente) con XOR.

10 REM modos de tinta XOR
20 MODE 1:INK 2,10:INK 3,4
30 ORIGIN 440,100,440,640,100,300
40 WINDOW 1,26,1,25
50 CLG 2: GRAPHICS PAPER 0
60 DRAW 200,200,3
70 MOVE 2,0:FILL 3
80 ORIGIN 440,0,440,640,0,400
90 GRAPHICS PEN ,1:MASK ,0
100 FOR y=60 TO 318 STEP 2
110 GOSUB 220
120 FRAME:FRAME
130 GOSUB 220
140 NEXT
150 TAG
160 FOR y=60 TO 318 STEP 2
170 MOVE 96,y:PRINT CHR$(224);
180 FRAME:FRAME
190 MOVE 96,y:PRINT CHR$(224);
200 NEXT
210 END
220 MOVE 90,y,1
230 DRAWR 20,0,,1
240 DRAWR 0,20
250 DRAWR -20,0
260 DRAWR 0,-20
270 RETURN
run

Animación

Se puede dar ilusión de movimiento cambiando adecuadamente los colores asignados a las tintas. Aunque el contenido de la memoria de la pantalla no se modifica, los dibujos parecen moverse. Se da un ejemplo de este método en el programa de demostración que se suministra con el disco maestro del sistema (RUN"disc"). Sin embargo, el simple cambio de colores de ese ejemplo no es suficiente cuando las formas móviles tienen que solaparse. En el siguiente ejemplo recurrimos al modo OR para escribir los números 1 al 4 en la pantalla. (La forma se determina examinando el carácter que está en el extremo inferior izquierdo de la pantalla y ampliando lo que allí se encuentra.) Los números se escriben sucesivamente con las tintas 1, 2, 4 y 8 y con el modo OR activado (en este caso mediante una sucesión de códigos de control, línea 50).

El bucle que empieza en la línea 160 realiza una rotación de los colores de la paleta mediante una fórmula matemática que hace que se ilumine un número cada vez. Las tintas se establecen inspeccionándolas sucesivamente y determinando si contienen el componente binario que estamos buscando. Por ejemplo, el 3 fue dibujado con tinta 4; por lo tanto, para visualizar el número 3 debemos dar un color visible a todas las tintas cuyo número contenga un 4 binario. Esas tintas son:

4(0100), 5(0101), 6(0110), 7(0111), 12(1100), 13(1101), 14(1110), 15(1111)

En una aplicación práctica se calcularía qué tintas se deberían cambiar en cada etapa, y las líneas 180 a 200 se sustituirían por una rutina más rápida.

10 REM animacion con tintas
20 ON BREAK GOSUB 220
30 FOR i=1 TO 15:INK i,26:NEXT
40 m(1)=1:m(2)=2:m(3)=4:m(4)=8
50 MODE 0:PRINT CHR$(23);CHR$(3);:TAG
60 FOR p=1 TO 4
70 GRAPHICS PEN m(p),1
80 LOCATE #1,1,25:PRINT#1,CHR$(48+p);
90 FOR x=0 TO 7
100 FOR y=0 TO 14 STEP 2
110 IF TEST(x*4,y)=0 THEN 140
120 MOVE (x+6)*32,(y+6)*16:PRINT CHR$(143);
130 MOVE (x+6)*32,(y+7)*16:PRINT CHR$(143);
140 NEXT y,x,p
150 LOCATE #1,1,25:PRINT#1," ";
160 FOR p=1 TO 4
170 FOR i=1 TO 25:FRAME:NEXT
180 FOR i=0 TO 15
190 IF (i AND m(p))=0 THEN INK i,0 ELSE INK i,26
200 NEXT i,p
210 GOTO 160
220 INK 1,26
run

Sprites

En el ejemplo anterior hemos visto cómo se da ilusión de movimiento cambiando los colores después de escribir con las tintas 1, 2, 4 y 8. Un efecto muy distinto se consigue si se utilizan las mismas tintas pero se manejan los colores de forma diferente. Este efecto se conoce con el nombre de "planos de color"; lo ilustramos en el siguiente ejemplo.

10 REM montañas
20 DEFINT a-s,u-Z
30 INK 0,1:INK 1,26
40 INK 2,6:INK 3,6
50 FOR i=4 TO 7:INK i,9:NEXT
60 FOR i=8 TO 15:INK i,20:NEXT
70 MODE 0:DEG:ORIGIN 0,150:CLG:MOVE 0,150
80 FOR x=16 TO 640 STEP 16
90 DRAW x,COS(x)*150+RND*100,4
100 NEXT
110 MOVE 0,0:FILL 4
120 cx=175:GOSUB 320
130 cx=525:GOSUB 320
140 SYMBOL 252,0,0,&C,&1F,&30,&7F,&FF
150 SYMBOL 253,0,6,&E,&F2,2,&F2,&FE
160 SYMBOL 254,0,&60,&70,&7F,&7F,&7F,&7F
170 SYMBOL 255,0,0,0,&F8,&EC,&FE,&FF
180 pr$=CHR$(254)+CHR$(255)
190 pl$=CHR$(252)+CHR$(253)
200 TAG:t=TIME
210 FOR x=-32 TO 640 STEP 4
220 x2=((608-x)*2) MOD 640:hl=RND*10:hr=50*SIN(x)
230 GRAPHICS PEN 8,1:MOVE x,100+hr,,3:PRINT pr$;
240 GRAPHICS PEN 2,1:MOVE x2,115+hl,,3:PRINT pl$;
250 IF (TEST(X2-2,115+hl-12) AND 8)=8 THEN 380
260 IF TIME-t<30 THEN 260
270 FRAME:t=TIME
280 GRAPHICS PEN 7,1:MOVE x,100+hr,,2:PRINT pr$;
290 GRAPHICS PEN 13,1:MOVE x2,115+hl,,2:PRINT pl$;
300 NEXT
310 GOTO 210
320 MOVE cx,100
330 FOR x=0 TO 360 STEP 10
340 DRAW cx+SIN(x)*50+10*RND,100+COS(x)*25+10*RND,1
350 NEXT
360 DRAW cx,100:MOVE cx,90:FILL 1
370 RETURN
380 ENT -1,1,1,1
390 SOUND 1,25,400,15,,1,15
400 FOR y=100+hr TO -132 STEP -2
410 GRAPHICS PEN 7,1:MOVE x,y,,2:PRINT pr$;
420 GRAPHICS PEN 8,1:MOVE x,y-2,,3:PRINT pr$;
430 NEXT
440 GOTO 70

Para explicar cómo funciona, una vez más tenemos que expresar los números de INK en binario. Empezando por el número máximo (el 15), asignamos el color cyan a todas las tintas que tienen activo el bit del "8" (de la 15 a la 8). Después damos el color verde a todas las que tienen activado el bit del "4" (de la 7 a la 4). Las tintas 2 y 3 tienen a 1 el bit del "2" y les asignamos el color rojo. Finalmente, asignamos el color blanco a la tinta 1 y dejamos la tinta 0 en azul.

Los gráficos se envían a la pantalla en modo OR (líneas 230 y 240). El color que se ve en la pantalla en cada pixel está determinado por el bit más significativo del resultan te en ese punto. Así pues, una imagen que esté en un plano "más significativo" predomina sobre otra que esté en un plano "menos significativo", pero el fondo se conserva y vuelve a ser visible si se retira la imagen "más significativa". La forma de retirar la imagen consiste en dibujarla en modo AND: las tintas 7, 11, 13 y 14 borran las tintas 8, 4, 2 y 1, respectivamente (líneas 280 y 290).

Gráficos con el segundo banco de 64K de RAM

Para concluir este capítulo ofrecemos un programa "diseñador de pantallas gráficas" que utilizan los 64K adicionales de RAM.

10 'Diseñador de pantallas, por David Radisic
20 ' copyright (c) AMSOFT 1985
30 '
40 'No olvide ejecutar BANKMAN antes que este programa
50 ' **************************************************
60 '
70 ON ERROR GOTO 2740
80 DEFINT a-x
90 MODE 1:ch=127:ord=1:pn(0)=0:pn(1)=26:pn(2)=15:pn(3)=6:pn(4)=0:pn=1:norx=1:menu=1:zzz=HIMEM
100 DIM orden$(22)
110 norx$(0)="Normal":norx$(1)="XOR   ":norx$(2)="Transp":norx$(3)="XOR   "
120 RESTORE:READ ords$(1),ords$(2):ord$=CHR$(16)+CHR$(&7F)+ords$(1)+ords$(2)
130 READ ordnum:FOR i=1 TO ordnum:READ orden$(i):NEXT
140 READ parar$:IF parar$<>"**" THEN ord$(ord)=parar$:ord=ord+1:GOTO 140
150 WINDOW #0,1,40,1,3:PAPER #0,0:PEN #0,1:CLS #0
160 WINDOW #1,1,40,4,4:PAPER #1,3:PEN #1,1:CLS #1
170 ORIGIN 0,0,0,640,0,334
180 x=320:y=200:MOVE x,y
190 BORDER pn(4):FOR i=0 TO 3:INK i,pn(i):NEXT
200 MASK 255,0:PAPER 0:PEN 1:PAPER #1,3:PEN #1,1:GRAPHICS PEN pn,norx
210 IF ind<>5 THEN 280
220 IF pn<2 THEN flecha$=CHR$(240):px=(pn+1)*13 ELSE IF pn<4 THEN flecha$=CHR$(241):px=(pn-1)*13 ELSE flecha$=CHR$(243):px=37
230 LOCATE px,2:PRINT flecha$;
240 LOCATE 1,1:PRINT USING"   PEN 0 : ##   PEN 1 : ##";pn(0);pn(1);
250 LOCATE 29,2:PRINT USING"Border : ##";pn(4)
260 LOCATE 1,3:PRINT USING"   PEN 2 : ##   PEN 3 : ##";pn(2);pn(3);
270 LOCATE px,2:PRINT" ";
280 LOCATE #1,1,1:PRINT #1,USING"X :####  Y :####  ";x;y;:PRINT#1,"Modo de tinta: ";norx$(norx+(borrar*2));" ";
290 IF ind=0 THEN GOSUB 2260
300 '
310 GOSUB 970
320 '
330 IF ind>0 THEN 390
340 IF i$="" THEN 390
350 ord=INSTR(ord$,i$):IF ord=0 THEN 390
360 IF ord=1 THEN CLG:x=320:y=200:GOTO 390
370 IF ord=2 THEN RUN 70
380 ON ord-2 GOSUB 1240,1410,1520,1640,1840,1860,1950,2020,2090,2120,2170,2200,2660,2660,2660,2660,2390,2330,2200
390 IF tx=0 AND ty=0 THEN 200
400 IF ind>0 THEN 440
410 GOSUB 630
420 GOSUB 680:FRAME:GOSUB 680
430 GOTO 200
440 MOVE tempx,tempy,pn,1
450 ON ind GOSUB 470,490,550,640
460 GOTO 200
470 PLOT x,y:GOSUB 630:PLOT x,y
480 RETURN
490 DRAW tempx+x,tempy:DRAW tempx+x,tempy+y
500 DRAW tempx,tempy+y:DRAW tempx,tempy
510 GOSUB 630
520 DRAW tempx+x,tempy:DRAW tempx+x,tempy+y
530 DRAW tempx,tempy+y:DRAW tempx,tempy
540 RETURN
550 MOVE tempx,tempy:DRAWR x,y
560 IF treslados=0 THEN 580
570 DRAW tempxx,tempyy:DRAW tempx,tempy
580 GOSUB 630
590 MOVE tempx,tempy:DRAW tempx+x,tempy+y
600 IF treslados=0 THEN RETURN
610 DRAW tempxx,tempyy:DRAW tempx,tempy
620 RETURN
630 x=x+tx:y=y+ty:RETURN
640 MOVE tempx,tempy:DRAW x,y
650 GOSUB 630
660 MOVE tempx,tempy:DRAW x,y
670 RETURN
680 ' dibujar y borrar cursor
690 IF ind=5 THEN RETURN
700 MASK 255,1
710 IF ind>1 THEN xx=tempx+x:yy=tempy+y ELSE xx=x:yy=y
720 IF ind=4 THEN xx=x:yy=y
730 IF ind=1 THEN xx=x:yy=y
740 IF borrar=1 THEN 820
750 GOSUB 790
760 MASK 255,0
770 IF i$=" " THEN GOSUB 2150:i$=""
780 RETURN
790 MOVE xx-4,yy,pn,1:DRAW xx+4,yy
800 MOVE xx,yy-4:DRAW xx,yy+4
810 MOVE xx,yy,,xorn:RETURN
820 nx=1:GOSUB 1220
830 FRAME:GOSUB 1220
840 IF i$=" " THEN nx=norx:GRAPHICS PEN pn,1:GOSUB 1220
850 i$=""
860 IF ind<>6 THEN 760
870 IF movido=0 AND j$<>"" AND (j$<CHR$(240) OR j$>CHR$(247)) THEN ch=ASC(j$):movido=1
880 IF movido=0 THEN RETURN
890 LOCATE 5,2
900 FOR i=ch-5 TO ch+5
910 PEN ABS(i<>ch)+1
920 ch$=CHR$(1)+CHR$(ABS(i+256) MOD 256)
930 IF ch=i THEN PRINT" "ch$" "; ELSE PRINT ch$;
940 NEXT
950 PEN 1:PRINT"   = "ch"  ";
960 GOTO 760
970 ty=0:tx=0:GOSUB 680:FRAME:GOSUB 680
980 IF INKEY(0)<>-1 OR INKEY(72)<>-1 THEN ty=16
990 IF INKEY(2)<>-1 OR INKEY(73)<>-1 THEN ty=-16
1000 IF INKEY(8)<>-1 OR INKEY(74)<>-1 THEN tx=-16
1010 IF INKEY(1)<>-1 OR INKEY(75)<>-1 THEN tx=16
1020 IF INKEY(21)<>-1 OR INKEY(76)<>-1 THEN tx=tx/8:ty=ty/8
1030 IF tx=0 AND ty=0 THEN movido=0 ELSE movido=1
1040 j$=INKEY$:i$=UPPER$(j$)
1050 IF (i$=" " OR i$=CHR$(13)) AND ind>0 THEN 1090
1060 IF ind=5 THEN 1120
1070 IF ind=6 THEN 1170
1080 RETURN
1090 ON ind GOSUB 1240,1410,1640,1860,1950,2020
1100 i$=""
1110 RETURN
1120 IF movido=0 THEN RETURN
1130 IF tx>2 THEN pn=(pn+1) MOD 5 ELSE IF tx<-2 THEN pn=ABS((pn<1))*5-1+pn
1140 IF ty>2 THEN pn(pn)=(pn(pn)+1) MOD 27 ELSE IF ty<-2 THEN pn(pn)=ABS((pn(pn)<1))*27-1+pn(pn)
1150 GRAPHICS PEN pn:PEN #1,pn
1160 tx=0:ty=0:BORDER pn(pn):RETURN
1170 IF tx<0 THEN ch=ABS(ch+255) MOD 256
1180 IF ty<0 THEN ch=ABS(ch+246) MOD 256
1190 IF tx>0 THEN ch=(ch+1) MOD 256
1200 IF ty>0 THEN ch=(ch+10) MOD 256
1210 tx=0:ty=0:RETURN
1220 TAG:MOVE xx-8,yy+6,pn,nx:PRINT CHR$(ch);:TAGOFF
1230 RETURN
1240 ' C
1250 IF ind=1 THEN 1290
1260 ro=1:GOSUB 2240
1270 tempx=x:tempy=y:ind=1
1280 RETURN
1290 IF tempx=x AND tempy=y THEN 1390
1300 PLOT x,y,,1
1310 tix=MAX(x,tempx)-MIN(tempx,x):tiy=MAX(y,tempy)-MIN(tempy,y)
1320 ti=SQR((tix^2)+(tiy^2))
1330 ORIGIN tempx,tempy
1340 PLOT 0,0,pn,0:MOVE 0,-ti
1350 FOR z=0 TO PI*2+0.01 STEP PI/(ti/2)
1360 DRAW SIN(z+PI)*ti,COS(z+PI)*ti,pn,norx
1370 NEXT z
1380 ORIGIN 0,0
1390 x=tempx:y=tempy:tempx=0:tempy=0:ind=0
1400 RETURN
1410 ' R
1420 IF ind=2 THEN 1470
1430 ro=2:GOSUB 2240
1440 tempx=x:tempy=y:ind=2
1450 x=0:y=0
1460 RETURN
1470 IF norx=1 THEN 1500
1480 MOVE tempx,tempy:DRAW tempx+x,tempy,,norx
1490 DRAW tempx+x,tempy+y:DRAW tempx,tempy+y:DRAW tempx,tempy
1500 x=tempx:y=tempy:ind=0
1510 RETURN
1520 ' . (rellenar)
1530 ro=3:GOSUB 2240
1540 GOSUB 1620:IF i$=" " THEN 1600
1550 colorlim=VAL(i$)
1560 ro=4:GOSUB 2240
1570 GOSUB 1620:IF i$=" " THEN 1600
1580 rellenador=VAL(i$)
1590 MOVE x,y,colorlim:FILL rellenador
1600 ind=0:i$=""
1610 RETURN
1620 i$=INKEY$:IF (i$<"0" OR i$>"3") AND i$<>" " THEN 1620
1630 RETURN
1640 ' T
1650 IF ind=3 THEN 1700
1660 ind=3:ro=5:GOSUB 2240
1670 tempx=x:tempy=y
1680 x=0:y=0
1690 RETURN
1700 IF treslados<>0 THEN 1770
1710 ro=6:GOSUB 2240
1720 MOVE 0,0,pn,1:GOSUB 590
1730 tempxx=tempx+x:tempyy=tempy+y:x=x/2:y=20
1740 treslados=1
1750 GOSUB 550:GOSUB 590
1760 RETURN
1770 IF norx=1 THEN 1800
1780 MOVE tempxx,tempyy,,norx:DRAW tempx,tempy
1790 DRAW tempx+x,tempy+y:DRAW tempxx,tempyy
1800 tempxx=0:tempyy=0
1810 x=tempx:y=tempy:treslados=0
1820 tempx=0:tempy=0:ind=0
1830 RETURN
1840 ' @
1850 norx=1:borrar=borrar XOR 1:RETURN
1860 ' \ (linea recta)
1870 IF ind=4 THEN 1910
1880 ro=7:GOSUB 2240
1890 tempx=x:tempy=y:ind=4
1900 RETURN
1910 IF norx=1 THEN 1930
1920 MOVE tempx,tempy,,norx:DRAW x,y
1930 x=tempx:y=tempy:ind=0
1940 RETURN
1950 ' I
1960 IF ind=5 THEN ind=0:CLS:INK 3,coltemp:INK pn,col:GOTO 1990
1970 CLS:ind=5:BORDER pn(pn)
1980 RETURN
1990 FOR i=0 TO 3:INK i,pn(i):NEXT:BORDER pn(4)
2000 IF pn=4 THEN pn=1
2010 CLS:RETURN
2020 ' A
2030 IF ind=6 THEN 2070
2040 tempx=0:tempy=0:CLS
2050 borrar=1:ind=6:norx=1:movido=1
2060 RETURN
2070 ind=0
2080 RETURN
2090 ' N
2100 norx=0
2110 RETURN
2120 ' B
2130 GRAPHICS PEN pn,0:TAG:MOVE xx-8,yy+6,,0:PRINT" ";:TAGOFF
2140 RETURN
2150 ' <BARRA ESPACIADORA>
2160 PLOT x,y,pn,norx:RETURN
2170 ' X
2180 norx=1
2190 RETURN
2200 ' M
2210 menu=menu MOD 2+1
2220 GOSUB 2260:RETURN
2230 i$=UPPER$(INKEY$):IF i$="" OR INSTR(gri$,i$)=0 THEN 2230 ELSE RETURN
2240 CLS:borrar=0:PRINT ord$(ro);:LOCATE 1,3:PRINT"<BARRA> ";:IF ro=3 OR ro=4 THEN PRINT"para terminar"
2250 RETURN
2260 CLS:ind=-1
2270 FOR i=1 TO LEN(ords$(menu))
2280 ps=i+ABS(menu=2)*LEN(ords$(1))
2290 PEN 1:PRINT"<"MID$(ords$(menu),i,1)">"MID$(orden$(ps),2,4)" ";
2300 NEXT
2310 PRINT"<CLR>   <DEL>   <BARRA>";
2320 RETURN
2330 ' G
2340 GOSUB 2460:IF nombrefichero$="" THEN 2370
2350 GOSUB 2550
2360 SAVE nombrefichero$,b,&c000,&4000
2370 GOSUB 2260
2380 RETURN
2390 ' L
2400 GOSUB 2460:IF nombrefichero$="" THEN 2440
2410 GOSUB 2730
2420 LOAD nombrefichero$,&c000
2430 GOSUB 2570
2440 GOSUB 2260
2450 RETURN
2460 CLS:LOCATE 10,3:PRINT"<RETURN> para abandonar!";
2470 LOCATE 1,1:PRINT"Nombre del fichero? ";
2480 INPUT "",nombrefichero$:IF nombrefichero$="" THEN RETURN
2490 n=INSTR(nombrefichero$,"."):IF n=0 THEN 2520
2500 IF n=1 THEN 2460
2510 nombrefichero$=LEFT$(nombrefichero$,n-1)
2520 nombrefichero$=LEFT$(nombrefichero$,8)+".pan"
2530 CLS
2540 RETURN
2550 FOR i=0 TO 4:POKE &c000+i,pn(i):NEXT
2560 RETURN
2570 FOR i=0 TO 4:pn(i)=PEEK(&c000+i) MOD 27:NEXT
2580 cn=0:FOR i=0 TO 2:IF pn(i)=pn(i+1) THEN cn=cn+1
2590 NEXT:IF cn=3 THEN 2630
2600 FOR i=0 TO 3:INK i,pn(i):NEXT
2610 BORDER pn(4):pn=1:GRAPHICS PEN pn
2620 RETURN
2630 pn(0)=0:pn(1)=26:pn(2)=15:pn(3)=6:pn(4)=0
2640 GOTO 2600
2650 ' 1, 2, 3 y 4
2660 CLS:PRINT"Quiere <G>uardar":PRINT TAB(8)"<R>ecuperar":PRINT TAB(6)"o <I>intercambiar la pantalla?";
2670 gri$="GRI"+CHR$(13):GOSUB 2230:IF i$=CHR$(13) THEN 2260
2680 bnk2=(ord-13):bnk1=1
2690 IF i$="G" THEN CLS:GOSUB 2550:|SCREENCOPY,bnk2,bnk1
2700 IF i$="R" THEN GOSUB 2730:|SCREENCOPY,bnk1,bnk2:GOSUB 2570
2710 IF i$="I" THEN CLS:GOSUB 2730:GOSUB 2550:|SCREENSWAP,bnk2,bnk1:GOSUB 2570
2720 GOSUB 2260:RETURN
2730 FOR i=0 TO 3:INK i,0:NEXT:BORDER 0:RETURN
2740 CLS:GOSUB 2600:RESUME 2260
2750 DATA "CR.T@\IANBXM","1234LGM"
2760 DATA 19,Circunferencia,Recuadro,".rellenar",Triangulo,Alternar,\recta,"Inks ",ASCII,Normal,Borrar,"Xor  ","Menu ","1     ","2    ","3    ","4     ","Leer ",Grabar,"Menu "
2770 DATA Circunferencia,Recuadro,Color del limite,Color para rellenar,Triangulo 1,Triangulo 2,Linea recta,**

Las dos órdenes |SCREENCOPY y |SCREENSWAP se cargan con el programa BANKMAN (cara 1 del juego de discos del sistema). Estas órdenes copian e intercambian imágenes de pantalla entre los diversos bloques de 16K de memoria y entre los dos bancos de 64K.

Antes de probar el programa "diseñador de pantallas" es necesario ejecutar el "gestor de bancos". Inserte en la unidad la cara 1 del juego de discos del sistema y escriba lo siguiente:

run"bankman"

A continuación ejecute el programa "diseñador de pantallas". En el monitor aparecerá un menú y un cursor gráfico parpadeante. Pulse la tecla correspondiente a la opción elegida.

Por ejemplo, para dibujar una circunferencia pulse

C

y luego la tecla [↑] hasta que el cursor pulsátil esté a unos dos o tres centímetros por encima del centro de la pantalla.

Finalmente, pulse la barra espaciadora para ejecutar la función "Circunferencia", después de lo cual el programa vuelve al menú.

Si se teclea una M (de "M"enú) se obtiene un segundo menú que ofrece las opciones de "G"uardar y "R"ecuperar pantallas y de manipular el contenido de las pantallas "l", "2", "3" y "4" (que están en el segundo banco de 64K de RAM).

Para utilizar estas funciones, escriba el "número de memoria" (1, 2, 3 o 4); aparecerá otro menú, en el cual se puede elegir:

OpciónAcción
GAlmacenar una pantalla
RRecuperar una pantalla
IIntercambiar pantallas
[RETURN]Abandonar este menú

Por ejemplo, para almacenar la pantalla actual en la memoria 2, pulse el "2" seguido de la "G".

Si se sale de este menú, pulsando "M" puede volver a él.

El programa "diseñador de pantallas" realiza diversas funciones: rectángulos, circunferencias, triángulos, rectas, encendido y apagado de puntos, rellenado de recintos y dibujo de caracteres.

Cuando se ha terminado el diseño de una pantalla, se la puede grabar en disco para su posterior recuperación.

Con esto termina el último capítulo del manual. Probando y analizando los programas que le hemos ofrecido en estas páginas, y experimentando con ellos, es de esperar que usted haya llegado a conocer con bastante profundidad el BASIC de Amstrad y el propio CPC6128.