ruby2basic 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +373 -0
- data/README.md +55 -0
- data/bin/ruby2basic +11 -0
- data/docs/1-pcbasic.md +69 -0
- data/docs/2-freebasic.md +149 -0
- data/docs/3-gambas.md +14 -0
- data/docs/4-bwbasic.md +9 -0
- data/docs/5-zxboriel.md +466 -0
- data/docs/README.md +10 -0
- data/lib/ruby2basic/version.rb +5 -0
- data/lib/ruby2basic/zxspectrum.rb +146 -0
- data/lib/ruby2basic.rb +5 -0
- metadata +78 -0
data/docs/5-zxboriel.md
ADDED
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
[<< back](./README.md)
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
BASIC
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
# 5. zxboriel
|
|
8
|
+
|
|
9
|
+
----
|
|
10
|
+
Instalar zxboriel en opensuse
|
|
11
|
+
|
|
12
|
+
¡Excelente elección! ZXBasic (también conocido como el compilador de Boriel) es la herramienta definitiva si quieres programar para el legendario Sinclair ZX Spectrum usando un PC con openSUSE. Es un compilador cruzado (cross-compiler) que convierte tu código BASIC en código máquina de Z80.
|
|
13
|
+
|
|
14
|
+
Aquí tienes los pasos para dejarlo listo en tu sistema:
|
|
15
|
+
1. Requisitos previos
|
|
16
|
+
|
|
17
|
+
ZXBasic está escrito en Python, por lo que openSUSE ya tiene casi todo lo necesario. Solo asegúrate de tener las herramientas básicas:
|
|
18
|
+
Bash
|
|
19
|
+
|
|
20
|
+
sudo zypper install python3 python3-pip
|
|
21
|
+
|
|
22
|
+
2. Instalación de ZXBasic (Boriel)
|
|
23
|
+
|
|
24
|
+
La forma más limpia y moderna de instalarlo es a través de pip (el gestor de paquetes de Python):
|
|
25
|
+
Bash
|
|
26
|
+
|
|
27
|
+
pip install zxbasic
|
|
28
|
+
|
|
29
|
+
Verificación
|
|
30
|
+
|
|
31
|
+
Para comprobar que se instaló correctamente, escribe en tu terminal:
|
|
32
|
+
Bash
|
|
33
|
+
|
|
34
|
+
zxc --version
|
|
35
|
+
|
|
36
|
+
(El comando principal se llama zxc, que significa ZX Compiler).
|
|
37
|
+
3. Tu primer programa para el Spectrum
|
|
38
|
+
|
|
39
|
+
Crea un archivo llamado test.bas con tu editor de texto:
|
|
40
|
+
Fragmento de código
|
|
41
|
+
|
|
42
|
+
' Mi primer programa para Spectrum
|
|
43
|
+
BORDER 1: PAPER 7: INK 0
|
|
44
|
+
CLS
|
|
45
|
+
PRINT AT 10, 5; "HOLA DESDE OPENSUSE!"
|
|
46
|
+
PRINT AT 12, 5; "HECHO CON BORCEL'S BASIC"
|
|
47
|
+
PAUSE 0
|
|
48
|
+
|
|
49
|
+
Compilar a formato .tap
|
|
50
|
+
|
|
51
|
+
Ahora, convierte ese código en una cinta virtual que un emulador pueda leer:
|
|
52
|
+
Bash
|
|
53
|
+
|
|
54
|
+
zxc test.bas --tap --autorun
|
|
55
|
+
|
|
56
|
+
--tap: Crea un archivo .tap (formato de cinta).
|
|
57
|
+
|
|
58
|
+
--autorun: Hace que el programa se ejecute solo al cargarse.
|
|
59
|
+
|
|
60
|
+
4. Cómo probarlo (Emuladores en openSUSE)
|
|
61
|
+
|
|
62
|
+
Para ver tu programa funcionando, necesitas un emulador de ZX Spectrum. En openSUSE tienes varias opciones excelentes en los repositorios:
|
|
63
|
+
Opción A: Fuse (El más clásico)
|
|
64
|
+
Bash
|
|
65
|
+
|
|
66
|
+
sudo zypper install fuse-emulator-gtk
|
|
67
|
+
|
|
68
|
+
Luego simplemente abre tu archivo: fuse test.tap.
|
|
69
|
+
Opción B: ZEsarUX (Muy potente y con muchas máquinas)
|
|
70
|
+
|
|
71
|
+
Es el favorito de muchos desarrolladores actuales. Puedes descargarlo de su GitHub oficial o buscarlo en el OBS de openSUSE.
|
|
72
|
+
5. Recomendación de "Pro": El IDE
|
|
73
|
+
|
|
74
|
+
Programar en la terminal está bien, pero si quieres una experiencia completa, busca "ZX IDE" o usa VS Code con la extensión "ZX Basic".
|
|
75
|
+
|
|
76
|
+
Esto te permitirá:
|
|
77
|
+
|
|
78
|
+
Resaltado de sintaxis (colores).
|
|
79
|
+
|
|
80
|
+
Compilar con un solo clic.
|
|
81
|
+
|
|
82
|
+
Lanzar el emulador automáticamente al compilar.
|
|
83
|
+
|
|
84
|
+
Nota importante: ZXBasic usa una sintaxis muy parecida a FreeBASIC (permite etiquetas, no requiere números de línea), pero mantiene los comandos clásicos del Spectrum como POKE, PEEK, PLOT y DRAW.
|
|
85
|
+
|
|
86
|
+
¿Quieres que te ayude a configurar un "Makefile" o un script sencillo para que al guardar tu código se abra automáticamente en el emulador Fuse?
|
|
87
|
+
|
|
88
|
+
Si
|
|
89
|
+
|
|
90
|
+
¡Genial! Tener un flujo de trabajo automatizado te ahorra mucho tiempo: escribes el código, lanzas el comando y ¡pum!, el emulador se abre con tu programa cargado.
|
|
91
|
+
|
|
92
|
+
En Linux (y específicamente en openSUSE), la forma más elegante de hacer esto es con un pequeño script de Bash o un archivo Makefile.
|
|
93
|
+
|
|
94
|
+
Aquí tienes la solución más sencilla: un script llamado build.sh.
|
|
95
|
+
1. Crea el script de automatización
|
|
96
|
+
|
|
97
|
+
Crea un archivo llamado build.sh en la misma carpeta donde tengas tus programas .bas y pega esto:
|
|
98
|
+
Bash
|
|
99
|
+
|
|
100
|
+
#!/bin/bash
|
|
101
|
+
|
|
102
|
+
# Comprobamos si nos has pasado un nombre de archivo
|
|
103
|
+
if [ -z "$1" ]; then
|
|
104
|
+
echo "Uso: ./build.sh nombre_del_archivo.bas"
|
|
105
|
+
exit 1
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
FILE_NAME=$(basename "$1" .bas)
|
|
109
|
+
|
|
110
|
+
echo "--- Compilando $1 ---"
|
|
111
|
+
|
|
112
|
+
# 1. Compilar con ZXBasic (Boriel)
|
|
113
|
+
# --tap: genera cinta, --autorun: carga automática, --overwrite: sobreescribe si ya existe
|
|
114
|
+
zxc "$1" --tap --autorun --overwrite
|
|
115
|
+
|
|
116
|
+
# 2. Comprobar si la compilación tuvo éxito
|
|
117
|
+
if [ $? -eq 0 ]; then
|
|
118
|
+
echo "--- Éxito! Abriendo en Fuse ---"
|
|
119
|
+
# 3. Lanzar el emulador Fuse con el archivo generado
|
|
120
|
+
fuse-emulator-gtk "${FILE_NAME}.tap" &
|
|
121
|
+
else
|
|
122
|
+
echo "--- Error en la compilación ---"
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
2. Dale permisos de ejecución
|
|
126
|
+
|
|
127
|
+
Abre tu terminal en esa carpeta y escribe:
|
|
128
|
+
Bash
|
|
129
|
+
|
|
130
|
+
chmod +x build.sh
|
|
131
|
+
|
|
132
|
+
3. ¡A programar!
|
|
133
|
+
|
|
134
|
+
Ahora, cada vez que quieras probar tu programa (por ejemplo test.bas), solo tienes que escribir en la terminal:
|
|
135
|
+
Bash
|
|
136
|
+
|
|
137
|
+
./build.sh test.bas
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
¿Por qué esto es mejor?
|
|
141
|
+
|
|
142
|
+
Limpieza: No tienes que escribir los parámetros de zxc cada vez.
|
|
143
|
+
|
|
144
|
+
Multitarea: El símbolo & al final de la línea de Fuse permite que el emulador se abra sin bloquear tu terminal, para que puedas seguir editando código.
|
|
145
|
+
|
|
146
|
+
Depuración: Si cometes un error en el BASIC, el script se detendrá y te mostrará el error exacto antes de intentar abrir el emulador.
|
|
147
|
+
|
|
148
|
+
Truco extra para openSUSE (VSCodium / VS Code)
|
|
149
|
+
|
|
150
|
+
Si usas VS Code (o VSCodium), puedes integrar esto aún más:
|
|
151
|
+
|
|
152
|
+
Pulsa Ctrl + Shift + B.
|
|
153
|
+
|
|
154
|
+
Selecciona "Configure Build Task".
|
|
155
|
+
|
|
156
|
+
Pega una configuración que llame a este ./build.sh ${file}.
|
|
157
|
+
|
|
158
|
+
Así, con solo pulsar una tecla en el editor, verás tu código ejecutándose en el Spectrum virtual.
|
|
159
|
+
|
|
160
|
+
¿Quieres que probemos un código divertido para ZX Spectrum, como generar música con BEEP o dibujar algo con PLOT y DRAW?
|
|
161
|
+
|
|
162
|
+
Aprender zxboriel
|
|
163
|
+
|
|
164
|
+
Aprender ZXBasic (Boriel) es fascinante porque combina lo mejor de dos mundos: la sencillez del BASIC de los 80 con la potencia de un lenguaje moderno (estructuras, funciones y optimización).
|
|
165
|
+
|
|
166
|
+
Aquí tienes tu hoja de ruta para dominarlo en openSUSE.
|
|
167
|
+
1. Conceptos Fundamentales (Diferencias con el Spectrum original)
|
|
168
|
+
|
|
169
|
+
A diferencia del Sinclair BASIC que venía en la ROM de la computadora, el compilador de Boriel:
|
|
170
|
+
|
|
171
|
+
No usa números de línea (aunque los soporta para compatibilidad).
|
|
172
|
+
|
|
173
|
+
Es un compilador: El código se traduce a lenguaje máquina antes de ejecutarse, por lo que es mucho más rápido (hasta 10-50 veces más rápido que el BASIC original).
|
|
174
|
+
|
|
175
|
+
Tipos de datos: En el Spectrum original todo eran números "flotantes". En Boriel puedes usar UByte (0-255) o Integer para que el procesador Z80 trabaje a toda velocidad.
|
|
176
|
+
|
|
177
|
+
2. Tu primera lección: Tipos de datos y Etiquetas
|
|
178
|
+
|
|
179
|
+
En Boriel, si quieres velocidad, declara tus variables.
|
|
180
|
+
Fragmento de código
|
|
181
|
+
|
|
182
|
+
' Ejemplo de programa optimizado
|
|
183
|
+
Dim i As UByte ' Un byte es ideal para bucles de 0 a 255
|
|
184
|
+
Dim x As UByte, y As UByte
|
|
185
|
+
|
|
186
|
+
Border 0: Paper 0: Ink 7: Cls
|
|
187
|
+
|
|
188
|
+
BuclePrincipal:
|
|
189
|
+
x = Int(Rnd * 32)
|
|
190
|
+
y = Int(Rnd * 22)
|
|
191
|
+
Print At y, x; "*"
|
|
192
|
+
|
|
193
|
+
' Un pequeño retardo
|
|
194
|
+
For i = 1 To 100: Next i
|
|
195
|
+
|
|
196
|
+
Goto BuclePrincipal
|
|
197
|
+
|
|
198
|
+
3. Estructuras de Control Modernas
|
|
199
|
+
|
|
200
|
+
Olvida los GOTO si quieres hacer programas grandes. Boriel soporta:
|
|
201
|
+
|
|
202
|
+
If...Then...Else...End If: Bloques completos de decisión.
|
|
203
|
+
|
|
204
|
+
Do...Loop: Bucles infinitos o condicionales.
|
|
205
|
+
|
|
206
|
+
While...End While: Ejecuta mientras se cumpla una condición.
|
|
207
|
+
|
|
208
|
+
For...Next: Para conteos.
|
|
209
|
+
|
|
210
|
+
Ejemplo de limpieza visual:
|
|
211
|
+
Fragmento de código
|
|
212
|
+
|
|
213
|
+
Do
|
|
214
|
+
If Inkey$ = "p" Then
|
|
215
|
+
Print "Pulsaste P"
|
|
216
|
+
End If
|
|
217
|
+
Loop Until Inkey$ = "q" ' Sale del programa al pulsar Q
|
|
218
|
+
|
|
219
|
+
4. El "Superpoder": Funciones y Subrutinas
|
|
220
|
+
|
|
221
|
+
En el BASIC antiguo usabas GOSUB 1000. En Boriel creas funciones reales:
|
|
222
|
+
Fragmento de código
|
|
223
|
+
|
|
224
|
+
' Define una subrutina
|
|
225
|
+
Sub DibujarCuadro(x As UByte, y As UByte)
|
|
226
|
+
Plot x, y
|
|
227
|
+
Draw 10, 0
|
|
228
|
+
Draw 0, 10
|
|
229
|
+
Draw -10, 0
|
|
230
|
+
Draw 0, -10
|
|
231
|
+
End Sub
|
|
232
|
+
|
|
233
|
+
' La usas así:
|
|
234
|
+
DibujarCuadro(50, 50)
|
|
235
|
+
DibujarCuadro(100, 80)
|
|
236
|
+
|
|
237
|
+
5. Recursos para profundizar
|
|
238
|
+
|
|
239
|
+
Para aprender de verdad, necesitas estas tres fuentes:
|
|
240
|
+
|
|
241
|
+
La Wiki de Boriel: Es la biblia. Están todos los comandos explicados. https://zxbasic.readthedocs.io/
|
|
242
|
+
|
|
243
|
+
El foro de Boriel: Donde están los expertos en Spectrum compartiendo trucos de optimización.
|
|
244
|
+
|
|
245
|
+
Programas de ejemplo: Boriel incluye una carpeta llamada examples al instalarlo. Te recomiendo leer el código de los juegos que vienen ahí.
|
|
246
|
+
|
|
247
|
+
6. Tu "Tarea" para hoy
|
|
248
|
+
|
|
249
|
+
Intenta modificar el script que hicimos antes para crear un programa que mueva una letra "A" por la pantalla usando las teclas O (izquierda) y P (derecha).
|
|
250
|
+
|
|
251
|
+
Pista de comandos:
|
|
252
|
+
|
|
253
|
+
Inkey$: Lee la tecla pulsada.
|
|
254
|
+
|
|
255
|
+
Over 1: Para que al imprimir una letra sobre otra no borre el fondo (o imprimir un espacio para borrar el rastro).
|
|
256
|
+
|
|
257
|
+
¿Te gustaría que te escriba el código base para ese movimiento de personaje y lo analizamos juntos?
|
|
258
|
+
|
|
259
|
+
Zxboriel se puede escribir en minúsculas?
|
|
260
|
+
|
|
261
|
+
¡Sí, totalmente! De hecho, escribir en minúsculas es la forma estándar y recomendada de usar ZXBasic (Boriel) hoy en día.
|
|
262
|
+
|
|
263
|
+
A diferencia del Spectrum original (donde los comandos eran "tokens" que siempre aparecían en mayúsculas al pulsar una tecla), Boriel es un compilador moderno. Aquí tienes los detalles:
|
|
264
|
+
1. No distingue entre mayúsculas y minúsculas
|
|
265
|
+
|
|
266
|
+
Para el compilador, estas tres líneas son exactamente lo mismo:
|
|
267
|
+
|
|
268
|
+
PRINT "HOLA"
|
|
269
|
+
|
|
270
|
+
Print "Hola"
|
|
271
|
+
|
|
272
|
+
print "hola"
|
|
273
|
+
|
|
274
|
+
2. La ventaja de las minúsculas
|
|
275
|
+
|
|
276
|
+
La mayoría de los programadores que usan Boriel en openSUSE prefieren las minúsculas por varias razones:
|
|
277
|
+
|
|
278
|
+
Legibilidad: Es mucho más descansado para la vista leer un código largo en minúsculas.
|
|
279
|
+
|
|
280
|
+
Estilo moderno: Se parece más a lenguajes como Python, C o FreeBASIC.
|
|
281
|
+
|
|
282
|
+
Diferenciación: Muchos usan mayúsculas para sus propias variables y minúsculas para los comandos del sistema (o al revés) para distinguir rápido qué es qué.
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
3. Ejemplo comparativo
|
|
286
|
+
|
|
287
|
+
Estilo "Retro" (Gritando):
|
|
288
|
+
Fragmento de código
|
|
289
|
+
|
|
290
|
+
10 BORDER 1: PAPER 7: INK 0: CLS
|
|
291
|
+
20 LET X = 10
|
|
292
|
+
30 PRINT AT 10, X; "HOLA"
|
|
293
|
+
|
|
294
|
+
Estilo "Boriel" (Moderno y limpio):
|
|
295
|
+
Fragmento de código
|
|
296
|
+
|
|
297
|
+
border 1
|
|
298
|
+
paper 7
|
|
299
|
+
ink 0
|
|
300
|
+
cls
|
|
301
|
+
|
|
302
|
+
dim x as ubyte = 10
|
|
303
|
+
print at 10, x; "hola"
|
|
304
|
+
|
|
305
|
+
4. Una excepción importante: Las etiquetas (Labels)
|
|
306
|
+
|
|
307
|
+
Aunque el lenguaje no distingue mayúsculas en los comandos, es una buena práctica ser consistente con las etiquetas de tus saltos (goto / gosub).
|
|
308
|
+
|
|
309
|
+
Si creas una etiqueta llamada Bucle_Principal:, intenta llamarla siempre igual, aunque el compilador probablemente te perdone si escribes goto bucle_principal.
|
|
310
|
+
5. Configuración en openSUSE
|
|
311
|
+
|
|
312
|
+
Si usas un editor como Kate o VSCodium en openSUSE, el resaltado de sintaxis (colores) funcionará perfectamente escribas como escribas. Los editores modernos suelen poner los comandos en un color (ej. azul) y tu texto en otro, sin importar si usas mayúsculas.
|
|
313
|
+
|
|
314
|
+
¿Te gustaría que hagamos el pequeño programa de mover la letra "A" con las teclas O y P usando solo minúsculas para que veas lo limpio que queda? Solo dímelo y te paso el código "estilo moderno".
|
|
315
|
+
|
|
316
|
+
Zxboriel. Hola mundo y factorial iterativo y factorial recursivo
|
|
317
|
+
|
|
318
|
+
¡Excelente! Vamos a ver estos tres ejemplos en ZXBasic (Boriel). Notarás que el factorial recursivo es donde Boriel realmente brilla frente al BASIC antiguo, ya que el Spectrum original no permitía recursividad real de forma sencilla.
|
|
319
|
+
|
|
320
|
+
Aquí tienes el código en formato moderno (minúsculas y sin números de línea).
|
|
321
|
+
1. Hola Mundo
|
|
322
|
+
|
|
323
|
+
El clásico, para verificar que tu build.sh en openSUSE funciona.
|
|
324
|
+
Fragmento de código
|
|
325
|
+
|
|
326
|
+
border 1
|
|
327
|
+
paper 7
|
|
328
|
+
ink 0
|
|
329
|
+
cls
|
|
330
|
+
|
|
331
|
+
print "Hola Mundo desde openSUSE!"
|
|
332
|
+
print "Presiona una tecla..."
|
|
333
|
+
pause 0
|
|
334
|
+
|
|
335
|
+
2. Factorial Iterativo
|
|
336
|
+
|
|
337
|
+
Este usa un bucle for. Es la forma más eficiente en un procesador Z80 porque no consume memoria de la pila (stack).
|
|
338
|
+
Fragmento de código
|
|
339
|
+
|
|
340
|
+
function factorial_iter(n as ubyte) as long
|
|
341
|
+
dim i as ubyte
|
|
342
|
+
dim res as long = 1
|
|
343
|
+
|
|
344
|
+
if n = 0 then return 1
|
|
345
|
+
|
|
346
|
+
for i = 1 to n
|
|
347
|
+
res = res * i
|
|
348
|
+
next
|
|
349
|
+
|
|
350
|
+
return res
|
|
351
|
+
end function
|
|
352
|
+
|
|
353
|
+
' Prueba del programa
|
|
354
|
+
cls
|
|
355
|
+
dim n as ubyte = 5
|
|
356
|
+
print "Factorial Iterativo de "; n; " es:"
|
|
357
|
+
print factorial_iter(n)
|
|
358
|
+
|
|
359
|
+
3. Factorial Recursivo
|
|
360
|
+
|
|
361
|
+
Boriel permite que una función se llame a sí misma. Nota importante: La recursividad en el Spectrum es lenta y puede agotar la memoria si el número es muy grande, pero para aprender la lógica es perfecta.
|
|
362
|
+
Fragmento de código
|
|
363
|
+
|
|
364
|
+
function factorial_rec(n as ubyte) as long
|
|
365
|
+
if n = 0 then
|
|
366
|
+
return 1
|
|
367
|
+
else
|
|
368
|
+
return n * factorial_rec(n - 1)
|
|
369
|
+
end if
|
|
370
|
+
end function
|
|
371
|
+
|
|
372
|
+
' Prueba del programa
|
|
373
|
+
print
|
|
374
|
+
print "Factorial Recursivo de 5 es:"
|
|
375
|
+
print factorial_rec(5)
|
|
376
|
+
|
|
377
|
+
Cosas clave que debes saber:
|
|
378
|
+
|
|
379
|
+
El tipo long: He usado as long (32 bits) para el resultado porque el factorial crece muy rápido. Un integer normal en el Spectrum solo llega hasta 32767, ¡y el factorial de 8 ya es 40320!
|
|
380
|
+
|
|
381
|
+
ubyte para el contador: Como el Spectrum tiene poca memoria y un procesador de 8 bits, usar ubyte (0-255) para la variable n e i hace que el programa sea mucho más rápido que si usaras integer.
|
|
382
|
+
|
|
383
|
+
Indentación: Fíjate que he usado espacios para "meter hacia adentro" el código de los if y for. A Boriel no le importa, pero a tus ojos sí (hace el código mucho más profesional).
|
|
384
|
+
|
|
385
|
+
¿Cómo probarlo todo junto?
|
|
386
|
+
|
|
387
|
+
Puedes copiar los tres bloques en un solo archivo .bas, añadir un pause 0 al final para que la pantalla no se cierre, y usar el script que creamos antes:
|
|
388
|
+
./build.sh programa.bas
|
|
389
|
+
|
|
390
|
+
¿Te gustaría que te explique cómo optimizar aún más el código usando fixed (punto fijo) o prefieres intentar hacer un programa que pida el número por teclado con input?
|
|
391
|
+
|
|
392
|
+
Haz los ejemplos con pcbasic y freebasic
|
|
393
|
+
|
|
394
|
+
Para cerrar el círculo, aquí tienes los tres ejemplos comparados en PC-BASIC (el estilo de 1985 con números de línea) y FreeBASIC (el estilo moderno de 32/64 bits).
|
|
395
|
+
|
|
396
|
+
Notarás que la mayor diferencia es cómo manejamos la recursividad, ya que el BASIC antiguo no la soportaba de forma nativa.
|
|
397
|
+
1. Hola Mundo
|
|
398
|
+
En PC-BASIC (Estilo GW-BASIC)
|
|
399
|
+
Basic
|
|
400
|
+
|
|
401
|
+
10 CLS
|
|
402
|
+
20 PRINT "HOLA MUNDO DESDE PC-BASIC"
|
|
403
|
+
30 END
|
|
404
|
+
|
|
405
|
+
En FreeBASIC (Estilo Moderno)
|
|
406
|
+
Fragmento de código
|
|
407
|
+
|
|
408
|
+
Cls
|
|
409
|
+
Print "Hola Mundo desde FreeBASIC"
|
|
410
|
+
Sleep ' Espera a que pulses una tecla
|
|
411
|
+
|
|
412
|
+
2. Factorial Iterativo (Bucle FOR)
|
|
413
|
+
En PC-BASIC
|
|
414
|
+
|
|
415
|
+
Aquí usamos una variable global F para el resultado y N para el número.
|
|
416
|
+
Basic
|
|
417
|
+
|
|
418
|
+
10 CLS
|
|
419
|
+
20 INPUT "Introduce un numero: ", N
|
|
420
|
+
30 F = 1
|
|
421
|
+
40 FOR I = 1 TO N
|
|
422
|
+
50 F = F * I
|
|
423
|
+
60 NEXT I
|
|
424
|
+
70 PRINT "El factorial es:"; F
|
|
425
|
+
80 END
|
|
426
|
+
|
|
427
|
+
En FreeBASIC
|
|
428
|
+
|
|
429
|
+
Usamos una función limpia y tipos de datos protegidos para evitar que el número se "desborde".
|
|
430
|
+
Fragmento de código
|
|
431
|
+
|
|
432
|
+
Function FactorialIter(n As Integer) As ULongInt
|
|
433
|
+
Dim res As ULongInt = 1
|
|
434
|
+
For i As Integer = 1 To n
|
|
435
|
+
res *= i
|
|
436
|
+
Next
|
|
437
|
+
Return res
|
|
438
|
+
End Function
|
|
439
|
+
|
|
440
|
+
Print "Factorial de 10 (Iterativo):"; FactorialIter(10)
|
|
441
|
+
|
|
442
|
+
3. Factorial Recursivo
|
|
443
|
+
|
|
444
|
+
Aquí es donde está el gran cambio tecnológico.
|
|
445
|
+
En PC-BASIC (Simulación con GOSUB)
|
|
446
|
+
|
|
447
|
+
PC-BASIC no permite que una función se llame a sí misma. Para simularlo, los programadores antiguos usaban una "pila" manual o, más comúnmente, simplemente no lo hacían porque el GOSUB no crea variables nuevas cada vez.
|
|
448
|
+
|
|
449
|
+
Nota: En PC-BASIC es mejor quedarse con la versión iterativa. Intentar hacer recursividad real requiere trucos muy complejos con POKE y PEEK.
|
|
450
|
+
|
|
451
|
+
En FreeBASIC (Recursividad Real)
|
|
452
|
+
|
|
453
|
+
FreeBASIC maneja la memoria de forma automática, permitiendo que la función se llame a sí misma elegantemente.
|
|
454
|
+
Fragmento de código
|
|
455
|
+
|
|
456
|
+
Function FactorialRec(n As Integer) As ULongInt
|
|
457
|
+
If n <= 1 Then
|
|
458
|
+
Return 1
|
|
459
|
+
Else
|
|
460
|
+
Return n * FactorialRec(n - 1)
|
|
461
|
+
End If
|
|
462
|
+
End Function
|
|
463
|
+
|
|
464
|
+
Print "Factorial de 5 (Recursivo):"; FactorialRec(5)
|
|
465
|
+
Sleep
|
|
466
|
+
|
data/docs/README.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
|
|
2
|
+
# BASIC
|
|
3
|
+
|
|
4
|
+
1. [PC BASIC](./1-pcbasic.md)
|
|
5
|
+
2. [FreeBASIC](./2-freebasic.md)
|
|
6
|
+
3. [Gambas](./3-gambas.md)
|
|
7
|
+
4. [bwBASIC](./4-bwbasic.md)
|
|
8
|
+
5. [zxboriel](./5-zxboriel.md)
|
|
9
|
+
|
|
10
|
+
> **NOTA**: En la programación moderna se desaconseja el uso de GOTO (porque crea lo que llamamos "código espagueti", difícil de seguir), FreeBASIC lo mantiene por pura compatibilidad y nostalgia.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#frozen_string_literal: true
|
|
2
|
+
require "prism"
|
|
3
|
+
|
|
4
|
+
module Ruby2Basic
|
|
5
|
+
class ZXSpectrum
|
|
6
|
+
def initialize(code)
|
|
7
|
+
@result = Prism.parse(code)
|
|
8
|
+
@comments = @result.comments.map do |c|
|
|
9
|
+
{ line: c.location.start_line, text: c.location.slice.gsub(/^#\s*/, "") }
|
|
10
|
+
end
|
|
11
|
+
@lines = []
|
|
12
|
+
@line_num = 10
|
|
13
|
+
@subs = []
|
|
14
|
+
@labels = {}
|
|
15
|
+
@arrays = []
|
|
16
|
+
@string_vars = []
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def transpile
|
|
20
|
+
return "Error de sintaxis" unless @result.success?
|
|
21
|
+
process_nodes(@result.value.statements.body)
|
|
22
|
+
|
|
23
|
+
output = []
|
|
24
|
+
@arrays.each { |dim| output << "#{@line_num} #{dim}"; @line_num += 10 }
|
|
25
|
+
output += @lines
|
|
26
|
+
output << "#{@line_num} STOP"
|
|
27
|
+
@line_num += 10
|
|
28
|
+
output += @subs
|
|
29
|
+
output.join("\n")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
# MODIFICACIÓN: Separamos el comando del contenido para no aplicar upcase a todo
|
|
35
|
+
def add_basic(command, content = "", to_subs: false)
|
|
36
|
+
line = "#{@line_num} #{command.upcase} #{content}".strip
|
|
37
|
+
to_subs ? @subs << line : @lines << line
|
|
38
|
+
@line_num += 10
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def process_nodes(nodes, to_subs: false)
|
|
42
|
+
nodes.each do |node|
|
|
43
|
+
check_for_comments(node.location.start_line, to_subs)
|
|
44
|
+
process_node(node, to_subs: to_subs)
|
|
45
|
+
end
|
|
46
|
+
check_for_comments(1000000, to_subs)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def check_for_comments(current_node_line, to_subs)
|
|
50
|
+
while @comments.any? && @comments.first[:line] <= current_node_line
|
|
51
|
+
comment = @comments.shift
|
|
52
|
+
# REM se queda en mayúsculas, pero el texto del comentario se respeta
|
|
53
|
+
add_basic("REM", comment[:text], to_subs: to_subs)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def process_node(node, to_subs: false)
|
|
58
|
+
case node
|
|
59
|
+
when Prism::LocalVariableWriteNode
|
|
60
|
+
name = node.name.to_s
|
|
61
|
+
val = resolve(node.value)
|
|
62
|
+
|
|
63
|
+
if node.value.is_a?(Prism::StringNode) || node.value.is_a?(Prism::InterpolatedStringNode)
|
|
64
|
+
@string_vars << name unless @string_vars.include?(name)
|
|
65
|
+
add_basic("LET", "#{name}$ = #{val}", to_subs: to_subs)
|
|
66
|
+
else
|
|
67
|
+
add_basic("LET", "#{name} = #{val}", to_subs: to_subs)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
when Prism::CallNode
|
|
71
|
+
case node.name.to_s
|
|
72
|
+
when "puts"
|
|
73
|
+
arg = node.arguments&.arguments&.first
|
|
74
|
+
add_basic("PRINT", resolve(arg), to_subs: to_subs)
|
|
75
|
+
when "times"
|
|
76
|
+
limit = resolve(node.receiver).to_i - 1
|
|
77
|
+
var = "i" # Variable de control en minúscula
|
|
78
|
+
add_basic("FOR", "#{var} = 0 TO #{limit}", to_subs: to_subs)
|
|
79
|
+
process_nodes(node.block.body.body, to_subs: to_subs) if node.block&.body
|
|
80
|
+
add_basic("NEXT", var, to_subs: to_subs)
|
|
81
|
+
else
|
|
82
|
+
if @labels[node.name.to_s]
|
|
83
|
+
add_basic("GOSUB", @labels[node.name.to_s].to_s, to_subs: to_subs)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
when Prism::IfNode
|
|
88
|
+
cond = resolve(node.predicate)
|
|
89
|
+
stmt_count = node.statements&.body&.size || 0
|
|
90
|
+
jump_to = @line_num + (stmt_count * 10) + 10
|
|
91
|
+
add_basic("IF NOT", "#{cond} THEN GOTO #{jump_to}", to_subs: to_subs)
|
|
92
|
+
process_nodes(node.statements.body, to_subs: to_subs) if node.statements
|
|
93
|
+
|
|
94
|
+
when Prism::DefNode
|
|
95
|
+
method_name = node.name.to_s
|
|
96
|
+
@labels[method_name] = @line_num
|
|
97
|
+
add_basic("REM", "SUB: #{method_name}", to_subs: true)
|
|
98
|
+
process_nodes(node.body.body, to_subs: true) if node.body
|
|
99
|
+
add_basic("RETURN", "", to_subs: true)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def resolve(node)
|
|
104
|
+
return "0" if node.nil?
|
|
105
|
+
case node
|
|
106
|
+
when Prism::IntegerNode, Prism::FloatNode then node.slice
|
|
107
|
+
when Prism::StringNode then "\"#{node.content}\""
|
|
108
|
+
when Prism::LocalVariableReadNode
|
|
109
|
+
name = node.name.to_s
|
|
110
|
+
@string_vars.include?(name) ? "#{name}$" : name
|
|
111
|
+
|
|
112
|
+
when Prism::InterpolatedStringNode
|
|
113
|
+
parts = node.parts.map do |part|
|
|
114
|
+
if part.is_a?(Prism::StringNode)
|
|
115
|
+
"\"#{part.content}\""
|
|
116
|
+
elsif part.is_a?(Prism::EmbeddedStatementsNode)
|
|
117
|
+
inner = part.statements.body.first
|
|
118
|
+
res = resolve(inner)
|
|
119
|
+
var_name = inner.respond_to?(:name) ? inner.name.to_s : nil
|
|
120
|
+
|
|
121
|
+
if !inner.is_a?(Prism::StringNode) && !@string_vars.include?(var_name)
|
|
122
|
+
"STR$(#{res})"
|
|
123
|
+
else
|
|
124
|
+
res
|
|
125
|
+
end
|
|
126
|
+
else
|
|
127
|
+
resolve(part)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
parts.join("+")
|
|
131
|
+
|
|
132
|
+
when Prism::CallNode
|
|
133
|
+
if node.receiver && node.arguments && node.arguments.arguments.size == 1
|
|
134
|
+
left = resolve(node.receiver)
|
|
135
|
+
right = resolve(node.arguments.arguments.first)
|
|
136
|
+
op = node.name.to_s == "==" ? "=" : node.name.to_s
|
|
137
|
+
"#{left}#{op}#{right}"
|
|
138
|
+
else
|
|
139
|
+
node.name.to_s
|
|
140
|
+
end
|
|
141
|
+
else "0"
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|