BAS1K-Compiler para ZX-81

dancresp
Mensajes: 5339
Registrado: 13 Nov 2010 02:08
Agradecido : 151 veces
Agradecimiento recibido: 287 veces

BAS1K-Compiler para ZX-81

Mensajepor dancresp » 17 Abr 2015 20:19

Komp_Let.gif
Komp_Let.gif (38.13 KiB) Visto 1483 veces


EL PROGRAMA
Este programa es un pequeño compilador de lenguaje BASIC, preparado para ser ejecutado en la versión básica del ZX-81.

Introducimos una instrucción en BASIC y el compilador nos devuelve el código hexadecimal equivalente en código máquina.
Para compilar una nueva instrucción deberemos volver a ejecutar el programa con RUN.

Los comandos se deben introducir con una separación de un espacio entre la instrucción y el resto de la línea.

Esta compilador reconoce las siguientes instrucciones:

LET
Asignar un valor a una variable o incrementar el valor de una variable.
Las sumas se deben hacer con la misma variable, y no se puede asignar el valor de una variable a otra.

Ejemplos:
LET A=10
LET B=B+128
LET C=C-240

FOR
Inicio de un bucle. El valor inicial siempre debe ser 1 y el final puede tener un valor máximo de 255. No se puede usar STEP.

Ejemplo:
FOR F=1 TO 250

NEXT
Control del final de un bucle.

Ejemplo:
NEXT I

IF
Compara el valor de una variable con un valor numérico. A partir del valor numérico no es preciso escribir el resto de la línea.

Ejemplo:
IF A=050 THEN ...

GOTO
Saltar a otra posición del programa. No es preciso introducir el número de línea.

Ejemplo:
GOTO 100

GOSUB
Saltar a una subrutina. No es preciso introducir el número de línea.

Ejemplo:
GOSUB 100

RETURN
Volver de una subrutina.

Ejemplo:
RETURN

STOP
Finalizar la ejecución del programa y volver al intérprete BASIC.

Ejemplo:
STOP

PRINT
Imprimir un texto en pantalla. No es necesario introducir las comillas.

Ejemplo:
PRINT HELLO WORLD...

CLS
Borrar el contenido de la pantalla.

Ejemplo:
CLS

SCROLL
Subir una línea el contenido de la pantalla.

Ejemplo:
SCROLL

Limitaciones del compilador:
- Los nombres de las variables solo pueden contener una letra comprendida entre la “A” y la “P”.
- Las variables pueden contener un valor comprendido entre 0 y 255.
- Los valores de las variables se almacenan en la zona del buffer de la impresora. Al volver al BASIC se pierde su contenido.
- El valor numérico del IF debe contener un mínimo de 2 dígitos. Si es preciso se pueden poner 0 a la izquierda.
- La condición de un IF siempre debe ser un igual “=”, y el resultado debe ser un salto a una dirección de memoria.
- Cuando en el resultado aparece “(AD)”, se debe sustituir por la dirección de destino en hexadecimal de 16 bits (4 dígitos), indicando primero el byte bajo y después el byte alto. Esto afecta a “GOTO”, “GOSUB” y “NEXT”.


Descargar el compilador en formato ".P":
BAS1K-Compiler.rar
(859 Bytes) Descargado 52 veces


COMO FUNCIONA
A continuación detallo, línea por línea, el funcionamiento del programa.

Se utilizan las siguientes variables:
H – Variable que contiene el valor “16”.
F – Control de bucles.
A – Valor a convertir a hexadecimal.
X – Esta variable no está definida y se utiliza para detener el programa provocando un error 2.

A$ - Variable donde se guarda la línea a compilar.
B$ - Variable donde se devuelve el valor de la variable “A” en formato hexadecimal.
C$ - Variable donde se devuelve la dirección de la variable usada en la instrucción de la línea de entrada.

El programa ocupa un total de 30 líneas:
4 - Asignamos el valor 16 a la variable "H" para usarla en distintos puntos del programa.
8 - Entramos la línea a compilar.
10 - Inicio del bucle encargado de identificar la instrucción.
11 - Si las dos primeras letras de la instrucción coincide con las de la lista salta a la línea 13.
12 - Final del bucle.
14 - Si el ID de la instrucción es inferior a 9 calcula la dirección de la variable de CM.
15 - Salta a la línea con el código de la instrucción. Los ID son siempre impares.
16 - Pequeña rutina que convierte en hexadecimal el valor de "A" y lo guarda en "B$".
18 - Final de la subrutina.
20 - Compilación de FOR y parte de LET.
60 - Compilación de NEXT.
100 - Compilación de IF.
140 - Compilación de LET. Aprovecha código de la compilación de FOR.
180 - Compilación de GOTO y GOSUB.
260 - Compilación de RETURN y STOP.
300 - Compilación de CLS.
340 - Compilación de SCROLL.
380 - Compilación de PRINT.


EL PROGRAMA
BAS1K-Compiler.gif


APUNTES FINALES
En 1984 me compré el número 30 de la revista “El Ordenador Personal” y me dejó fascinado un pequeño programa que aparecía en la página 130. El “Trans-compilador de 1K para el ZX-81”.

OP_30.jpg


Este programa convertía una instrucción en BASIC a código máquina mediante una serie de caracteres hexadecimales. Flipé.
La cantidad de instrucciones que convertía era muy reducida, y con muchas limitaciones, pero bueno. Recuerdo haberlo tecleado y usado, pero nunca intenté ejecutar el código máquina que generaba, ya que por otra parte, necesitabas un pequeño programa en BASIC para cargar esos códigos en memoria y poder ejecutar el programa.

En los últimos tiempos he conseguido realizar varios intérpretes en el ZX-81 de 1K, como el K-Assembler, el FORTH-K, ZX-LEARN, y finalmente el LOGO-K. La excepcional acogida de éste último me ha animado a enfrentarme al gran reto… el compilador de BASIC de 1K definitivo ¡!! (redoble de tambores)

Enfrentándome a los fantasmas del pasado !!!
Lo primero que hice fue teclear el “Trans-Compilador” de la revista y ejecutarlo en mi emulador.
¿Cómo podía ser que ese programa tan “raro” pudiera compilar BASIC?

En su ejecución y análisis he detectado la “trampa”, y sus limitaciones:
- Habla de variables pero realmente asigna valores a registros del microprocesador.
- Al sumar o restar 1 a una variable (no se puede usar otro valor) usa la instrucción del Z80 “INC” o “DEC”.
- Hay instrucciones que no se sabe bien como introducirlas para que se compilen.
- Los valores numéricos que devuelve están en formato decimal, lo que requiere de nuestra conversión a hexadecimal.

En resumen, muy curioso pero el código que genera es difícilmente usable, ya que por ejemplo, si los valores se almacenan en un registro, este contenido puede ser fácilmente alterado al hacer llamadas a rutinas del sistema.

Así que tocaba programar una nueva versión que generara un código “realmente” usable.

El BAS1K-Kompiler de dancresp
Mi versión del compilador genera un código 100% usable, ya que las 16 variables disponibles realmente se guardan en una posición de memoria del buffer de la impresora, y los valores se muestran en formato hexadecimal.

Únicamente las direcciones de las instrucciones de salto (CALL o JP) se muestran como “(AD)” para ser reemplazadas con el valor hexadecimal correcto posteriormente.

Hice una lista con las instrucciones que debía incorporar mi compilador, y escribir el código ensamblador equivalente. Todo debería caber en 1K, aunque como con el “K-Assembler”, esto no quiere decir que sea un primer paso para un compilador posterior más completo.

Metiendo un compilador de BASIC en 639 bytes
El ZX-81 básico dispone de 1024 bytes, de los que descontando los 125 bytes de la zona de variables del sistema y un mínimo de 25 bytes de la memoria de vídeo dejan 874 bytes libres.

La definición de variables, el calculador, el tratamiento de cadenas y la memoria de video consumen memoria, con lo que el programa no debería ocupar más de 600 bytes.

¿Y como se hace?
Lo primero ha sido introducir una línea con la que controlo la memoria que ocupa el programa en BASIC:
9999 PRINT (PEEK VAL"16396"+VAL"256"*PEEK VAL"16397")-VAL"16552"
Esta línea ocupa 43 bytes, que ganaré al borrarla al finalizar el desarrollo del programa, o al aparecer el maldito error 4.

Como siempre, he usado los trucos habituales del ZX-81 para ahorrar memoria en el uso de valores numéricos. Así, "NOT PI" es 0, uso de CODE y VAL, y como el valor 16 se usa varias veces, he asignado ese valor a la variable "H".

¿Cómo se guarda las líneas BASIC el ZX-81?
En un principio mi intención era escribir la línea mediante el propio editor del ZX-81, escribiéndola en la primera línea del programa en BASIC, y procesarla desde el BASIC con una llamada tipo “RUN 100”. Era una forma sencilla de hacerlo, ya que el programa se guarda a partir de la posición 16509, y cada instrucción tiene un Id único. Esto me facilitaba la parte que salta a la rutina correspondiente.

Esquema_Numeros.gif


El ejemplo anterior, muestra como se guardan las líneas en un programa en BASIC:
- Los dos primeros bytes contienen el número de línea.
- Los siguientes dos bytes contienen la longitud de la línea, excluyendo estos primeros 4 bytes.
- Contenido de la línea, carácter a carácter, en los que se sustituyen los comandos por su identificador único (Token), y un bloque especial de 6 bytes cuando hay valores numéricos.
- Bytes con un “118” que indica el final de línea.

Y el porque de los números...
Este mismo ejemplo sirve para comprender porque usando la instrucción CODE o VAL conseguimos ahorrar memoria cuando una línea contiene valores numéricos.

Como se puede ver, a parte de guardar el valor numérico dígito a dígito, a continuación guarda un bloque de 6 bytes, empezando siempre con un “126” que contienen el valor en un formato empaquetado, comprensible por el calculador.

Cuando se hace un LIST se muestran los dígitos hasta encontrar el código “126”, se suman 5 bytes para saltarse el bloque y sigue listando.

Pero cuando se ejecuta el programa se trabaja con los 5 bytes a partir del “126” y de esta forma el intérprete es más rápido, ya que tiene el valor en un formato comprensible por el calculador.

Al usar VAL o CODE, este bloque de 6 dígitos no se incluye, pero como la instrucción VAL ó CODE más las dos comillas de inicio y final ocupan 3 bytes, el ahorro queda en 3 bytes. En función de los dígitos del número, el ahorro de memoria puede aumentar o disminuir.

Al usar NOT PI, SGN PI, INT PI y otros, el ahorro puede llegar a los 4 ó 5 bytes.

Comienza la pesadilla...
Realmente, programar este compilador ha sido todo un reto.

Precisamente por la forma como el BASIC del ZX-81 se guarda los valores numéricos no me ha permitido hacer el análisis de la línea de la forma que yo quería, y he decidido hacer la entrada mediante un INPUT y guardarla en la variable A$.

Un clásico bucle comprendido entre las líneas 10 y 12 me permite identificar la instrucción y posteriormente saltar a la línea que la procesa y compila.

Las instrucciones están ordenadas de forma que primero trato las que usan variables, para que con una línea común (14) pueda obtener un código entre “0” y “F”, en función de la variable usada, y guardar la dirección de memoria completa en la variable “C$”. Debido a esto, solo puedo usar 16 variables, de una letra.

Una sencilla rutina en la línea 16 convierte el valor de la variable “A” en un código hexadecimal de 2 bytes, que se guarda en la variable “B$”.

Cada vez que se compila una línea finaliza la ejecución del programa. Para conseguirlo, he hecho referencia a la variable “X” en el PRINT correspondiente. Como no existe, da un error 2 y detiene la ejecución. Esto me ha ahorrado líneas… y memoria.

En el caso de las instrucciones NEXT y LET, gran parte del código es compartido ya que en el fondo lo único que hacen es asignar un valor a una variable, y lo único que varía es la posición del valor numérico.

Como usar el compilador
El funcionamiento del compilador es muy sencillo, y en si se podría decir que se compone de 4 pasos, tal y como se puede ver en el siguiente esquema.

ComoUsar.gif


Pasos de la compilación:
1. Se escribe el programa, por ejemplo en un papel.
2. Se introducen las líneas en el compilador y se apunta el código resultante.
3. Se calcula la dirección de memoria de cada línea, teniendo en cuenta que la primera dirección debe ser la 16514 (4082h). Cada 2 caracteres hexadecimales corresponden a un byte.
4. Se sustituyen los “(AD)” por la dirección correcta, teniendo en cuenta que se deben invertir el orden de los dos pares de bytes. Esto es así para GOTO, GOSUB y NEXT. En este último caso debe saltar a la siguiente dirección de la línea que contiene el FOR, ya que sino entraría en un bucle infinito.

Una vez finalizado, se deberá cargar el código hexadecimal en la memoria...

Probando el código compilado
Ahora falta cargar el código en memoria para poder ejecutar el programa.

Pasos de la carga del código máquina:
1. Poner en la línea 1 REM tantos caracteres como bytes tenga el programa y cargar el código hexadecimal en la variable A$ de la línea 10.
2. Ejecutar el programa para cargar el código hexadecimal en la línea REM. Como se puede ver, los caracteres han cambiado.
3. A continuación se han de borrar desde la línea 10 hasta la 70, y se crea una línea 10 con un USR 16514.
4. Al hacer RUN el BASIC ignora el contenido de la línea REM pero si ejecuta el USR, y muestra el resultado del programa.

Com_1.gif


A partir de aquí, el programa se puede grabar en una cinta de cassette con un simple SAVE ”nombre” para poderlo cargar posteriormente.

También es recomendable hacer una grabación del programa antes de pasar al paso 2, ya que en caso de error al introducir el código hexadecimal se podría volver a cargar y revisar lo introducido. En este caso también se debe hacer con SAVE.

Programa cargador “limpio”:
CM-Loader.gif
CM-Loader.gif (24.79 KiB) Visto 1422 veces


En el supuesto que el programa a cargar sea muy largo, la línea 30 se debería sustituir por:

30 IF A$=”” THEN INPUT A$

Y se deberían introducir los códigos en grupos reducidos.


Rendimiento
A modo de ejemplo, el programa compilado se limita a mostrar 250 letras “A” en pantalla, una a continuación de la otra.

En BASIC el programa tarda aproximadamente unos 5,7 segundos, y en código máquina a tardado menos de 0,2 segundos.

Está claro que no es el mejor ejemplo para hacer una prueba de rendimiento ya que el código máquina puede ser miles de veces más rápido que el BASIC, pero incluso así, ha sido bastante más rápido.


Pues nada más, solo me queda desearos una muy feliz compilación !!!

El compilador se ha desarrollado íntegramente en el emulador “EightyOne” de Windows.

Os invito a probarlo.


Komp_Print.gif
Komp_Print.gif (40.97 KiB) Visto 1483 veces

Komp_For.gif
Komp_For.gif (33.28 KiB) Visto 1483 veces

Komp_Next.gif
Komp_Next.gif (34.46 KiB) Visto 1483 veces

Komp_If.gif
Komp_If.gif (35.43 KiB) Visto 1483 veces

Komp_Let.gif
Komp_Let.gif (38.13 KiB) Visto 1483 veces

Avatar de Usuario
ron
Mensajes: 18402
Registrado: 28 Oct 2010 14:20
Ubicación: retrocrypta
Agradecido : 1575 veces
Agradecimiento recibido: 1315 veces

Re: BAS1K-Compiler para ZX-81

Mensajepor ron » 18 Abr 2015 09:16

Me he quedado a cuadros con el post. Buenísimo.
Se ve que lo sigues haciendo ! he disfrutado con la lectura.

Gracias dancresp

Avatar de Usuario
web8bits
Mensajes: 1031
Registrado: 31 Oct 2010 10:34
Ubicación: Vigo
Agradecido : 109 veces
Agradecimiento recibido: 84 veces
Contactar:

Re: BAS1K-Compiler para ZX-81

Mensajepor web8bits » 18 Abr 2015 10:52

Una vez más lo has logrado, sin palabras.

Gran trabajo.

Avatar de Usuario
Luis
Mensajes: 1084
Registrado: 03 Nov 2010 19:00
Agradecido : 209 veces
Agradecimiento recibido: 113 veces

Re: BAS1K-Compiler para ZX-81

Mensajepor Luis » 19 Abr 2015 11:30

Tengo que enchufar el ZX81 y probarlo "in situ", se me ha torcido el culo leyendo sobre el compilador éste, está genial -shock

Tengo varias preguntas:

¿Has probado un programa en Basic y uno compilado, para comparar velocidades?
Una vez obtenido el hexadecimal, ¿es posible salvar el programa compilado de alguna manera? En la segunda parte parece que sólo puede introducirse y ejecutarse, pero que tras eso se pierde.
West of House
You are standing in an open field west of a white house, with a boarded front door. There is a small mailbox here.

Avatar de Usuario
ron
Mensajes: 18402
Registrado: 28 Oct 2010 14:20
Ubicación: retrocrypta
Agradecido : 1575 veces
Agradecimiento recibido: 1315 veces

Re: BAS1K-Compiler para ZX-81

Mensajepor ron » 19 Abr 2015 12:06

Harnas, eso que ando liado con la enfermería cacharril, jaaja había pensado algo muy parecido a lo que has puesto, yo también lo estuve leyendo y me salieron las mismas inquietudes... así que te agradezco el post ya que sino se me habría pasado del todo. A ver que nos dice wally dancresp !!!

dancresp
Mensajes: 5339
Registrado: 13 Nov 2010 02:08
Agradecido : 151 veces
Agradecimiento recibido: 287 veces

Re: BAS1K-Compiler para ZX-81

Mensajepor dancresp » 21 Abr 2015 00:11

harnas escribió:Tengo varias preguntas:

¿Has probado un programa en Basic y uno compilado, para comparar velocidades?
Una vez obtenido el hexadecimal, ¿es posible salvar el programa compilado de alguna manera? En la segunda parte parece que sólo puede introducirse y ejecutarse, pero que tras eso se pierde.

He añadido una parte al final del post/ladrillo con varios apartados explicando como usar el compilador y como cargar el código generado.

A partir de aquí no puede haber dudas, y el que no compile es porque no quiere... -thumbup

Avatar de Usuario
Luis
Mensajes: 1084
Registrado: 03 Nov 2010 19:00
Agradecido : 209 veces
Agradecimiento recibido: 113 veces

Re: BAS1K-Compiler para ZX-81

Mensajepor Luis » 21 Abr 2015 07:22

Dudas aclaradas, mejor explicado imposible -drinks

Una última preguntilla: ¿Funcionan igual los modos FAST y SLOW al ejecutar un programa compilado?
West of House
You are standing in an open field west of a white house, with a boarded front door. There is a small mailbox here.

dancresp
Mensajes: 5339
Registrado: 13 Nov 2010 02:08
Agradecido : 151 veces
Agradecimiento recibido: 287 veces

Re: BAS1K-Compiler para ZX-81

Mensajepor dancresp » 22 Abr 2015 13:33

harnas escribió:Una última preguntilla: ¿Funcionan igual los modos FAST y SLOW al ejecutar un programa compilado?

Exactamente igual.

Lo bueno es que normalmente el FAST no es necesario en C.M., porque ya va bastante fast por si solo...

Avatar de Usuario
carlosjuliopr
Mensajes: 423
Registrado: 20 Ago 2012 22:13
Ubicación: Puerto Rico
Agradecido : 13 veces
Agradecimiento recibido: 18 veces

Re: BAS1K-Compiler para ZX-81

Mensajepor carlosjuliopr » 05 May 2015 20:49

excelentisimo aporte , gracias ! !! :-)
"We need to build computers for the masses, not the classes",Jack Tramiel -cocbm1


Volver a “Lenguajes de Programación y herramientas Dev”

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 1 invitado