Juego de caracteres en Beta BASIC; problemas con causa

Hace un par de semanas escribí acerca de un problema para usar un juego de caracteres con Beta BASIC, que me encontré durante el desarrollo de Alien-B. Unos días después bastó un poco de lógica sosegada para encontrar la causa del problema y diseñar una solución.

El origen de todo era una confusión: lo que el manual de Beta BASIC dice acerca de CLEAR es cierto, y los ejemplos en Beta BASIC Newsletter (número 2, página 3) son válidos; pero en ambos documentos se da por supuesto que previamente no ha sido definida ninguna ventana ni ningún atajo de teclado. Los datos de las ventanas y los atajos de teclado usan la zona entre el límite superior de la memoria utilizable por el programa y el inicio del código de Beta BASIC. Eso permite preservar esas zonas cuando borramos el programa, y también guardarlas junto con el código de Beta BASIC. Si no hay ventanas o atajos definidos, esa zona está vacía y sólo hay dos octetos de diferencia entre el límite superior de la memoria utilizable y el comienzo del código de Beta BASIC, como se muestra en los ejemplos de en Beta BASIC Newsletter. El problema es que cuando hay atajos de teclado o ventanas definidos y usamos CLEAR con un valor menor de 768, no es posible saber dónde termina la zona de memoria que almacena ambas tablas de datos, y el espacio libre creado se encuentra entre ella y el código de Beta BASIC. La documentación menciona vagamente un cero usado como indicador de final de dicha zona, pero los exámenes de memoria que he realizado no me han permitido identificarlo con seguridad.

Por tanto, la solución que me quedaba era la más sencilla: reservar el espacio necesario para el nuevo juego de caracteres antes de definir ventanas o atajos de teclado.

Sin embargo se me planteó un problema nuevo: Al reiniciar el programa con RUN, ¿cómo saber si el juego de caracteres ya había sido cargado en una ejecución anterior? Y, en dicho caso, ¿cómo saber la dirección de memoria en que se encuentra?

La solución a este nuevo problema la encontré en el utilísimo disco RAM de la ZX Spectrum 128, para cuyo aprovechamiento Beta BASIC proporciona buenas facilidades.

En primer lugar, necesitaba crear una constante con el nombre del fichero que, en el disco RAM, conservaría la dirección del juego de caracteres la primera vez que fuera cargado:

 120
     REM Constants

 280 DIM f$(10)
     LET f$="font#"
     REM name of the RAM disc array that holds the address of the game font.

Después necesitaba crear una matriz numérica en el disco RAM con dicho nombre de fichero, pero sólo si no existiera ya:

 300
     REM Variables

 320 IF NOT INSTRING(1,CAT$(),f$) THEN DIM !f$,(1)
       REM storage for the address of the game font

A continuación había que reordenar las llamadas a los procedimientos de inicialización, para cargar el juego de caracteres antes de definir las ventanas y los atajos de teclado:

 350
     REM Setup

 360 setupUdgs
 370 setupGraphs
 380 gameFont
     REM This must be before setupScreen and any DEF KEY
 390 setupScreen
 400 DEF KEY "0"," INK 4: PAPER 0: BORDER 0: CSIZE 0: CLS :normalFont: KEYWORDS 1"

Por último, había que reescribir los procedimientos de carga del juego de caracteres para que comprobaran si la matriz en el disco RAM contenía un número distinto de cero, en cuyo caso debían activar el juego de caracteres presente en dicha dirección de memoria en lugar de cargarlo de nuevo desde el disquete:

2550
     REM Setup

2560
     REM Fonts

2570 DEF PROC gameFont
2580   LOCAL address
2590   LET address=!f$,(1)
2600   IF NOT address THEN allocate 768,address
         LOAD d*"alien.font"CODE address
         LET address=address-256
         LET !f$,(1)=address
2610   DPOKE sysCHARS,address
2620 END PROC

2630 DEF PROC normalFont
2640   DPOKE sysCHARS,sysFont
2650 END PROC

2660 DEF PROC eraseFont
2670   superclear -768
       normalFont
2680 END PROC

Y por supuesto, hacen falta los procedimientos para reservar cómodamente bloques de memoria mayores de 767 octetos:

2350
     REM Memory management

2360 DEF PROC superclear bytes
2370   REM Do with any bytes amount what Beta BASIC's CLEAR does only with values under 768 bytes.
2380   LOCAL sign,n
2390   LET sign=SGN (bytes),bytes=ABS (bytes)
2400   FOR n=1 TO INT (bytes/767)
         CLEAR 767*sign
       NEXT n
       CLEAR MOD(bytes,767)*sign
2410 END PROC

2420 DEF PROC allocate bytes, REF address
2430   REM Lower the RAMTOP to get the specified bytes of free space, and return its address
2440   REM In Beta BASIC Newsletter #2, page 3, there is an example how to calculate the address.
2450   LOCAL oldRamtop
2460   LET oldRamtop=DPEEK(sysRAMTOP),bytes=ABS (bytes)
2470   superclear bytes
2480   LET address=oldRamtop-bytes+2
2490   REM PRINT address,DPEEK(sysramtop)
       rem debug!!!
2500 END PROC

2510 DEF PROC deallocate bytes
2520   REM Upper the RAMTOP to remove the specified bytes of free space.
2530   superclear -ABS (bytes)
2540 END PROC

Hace unos años escuché al bueno de Richard Stallman explicar, en una conferencia en Madrid, por qué la programación le parecía apasionante. No recuerdo sus palabras exactas pero sí su contenido: la programación es un gran juego que te permite hacer lo que quieras con tu intelecto y tu imaginación, sin límites. Así es. Yo añadiría además que la programación es muy gratificante porque nada en ella carece de causa lógica; incluso cuando un programa no funciona bien y tras muchas horas de trabajo no encuentro cuál es la causa del problema, que dicha causa existe y que antes o después la encontraré y, si no es externa, podré hacer los cambios necesarios y el problema desaparecerá.

Páginas relacionadas

Alien-B
Juego de invasores escrito en Beta BASIC para ZX Spectrum 128.
Manuales de Beta BASIC; compilando Linux 2.6.32; el renacimiento de xAce
Apuntes dispersos de un (retro)programador. 20 de mayo de 2010.
Una borrachera marciana, un virus mágico y una bandera; 767 octetos más uno; las máquinas sin tiempo
Apuntes dispersos de un (retro)programador. 29 de setiembre de 2010.