Jupiter Ace Forth (Tutoriales)

Elurdio
Mensajes: 510
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 115 veces
Agradecimiento recibido: 95 veces

Re: Jupiter Ace Forth (Tutoriales)

Mensajepor Elurdio » 01 Ago 2022 11:14

Último mensaje de la página anterior:

Hoy he pensado que lo de los códigos 11 y 12 en vez del 10 que usan DEFINER y COMPILER en la definición de su parte compilante podría ser más grave de lo que parece con respecto a mis estructuras de Control de usuario.

Continúo con el tema en el hilo utilidades (aquí). La cosa pinta fea... -shock igual no he tenido tanta suerte como me pensaba -banghead

Elurdio
Mensajes: 510
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 115 veces
Agradecimiento recibido: 95 veces

Re: Jupiter Ace Forth (Tutoriales)

Mensajepor Elurdio » 12 Ago 2022 15:56

Para completar el tutorial, resumo aquí lo referente a los "nuevos" códigos 11 y 12.

Cuando se crea una nueva palabra tipo DEFINER, en lugar del código 10, se coloca en la pila el código 12. Además, justo debajo del 12 se coloca la dirección del final del diccionario que devuelve HERE.

Cuando se ejecuta el DOES> durante la compilación, éste quita el 12 y utiliza el valor de debajo del mismo para sus cálculos (concretamente para calcular el valor de "address(azul)" (ver esquema DEFINER aquí), por lo que ambos desaparecen.

A continuación coloca el código 10 de siempre durante la compilación de la parte Runtime que luego ; (punto y coma) quitará.

En el caso de una palabra tipo COMPILER la cosa es igual salvo que donde digo 12 es ahora un 11 y donde digo DOES> ahora es RUNS>.

Como las compilantes "sencillas" (todas las compilantes del Ace Forth y también nuestro EVEN/END) no utilizan el 10 para nada, pues todo esto del 11 y el 12 tampoco va con ellas.

Esto es importante con según que tipo de compilantes "complejas", como por ejemplo, el caso de GOTO/LABEL.

Elurdio
Mensajes: 510
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 115 veces
Agradecimiento recibido: 95 veces

Re: Jupiter Ace Forth (Tutoriales)

Mensajepor Elurdio » 12 Ago 2022 21:42

Palabras Fanstamas y Semi-Fantasmas I

Voy a detallar más ésta técnica útil en ciertos casos.

En su día escribí un post en el hilo de "Utilidades" (debería haberlo llamado "miscelánea" o "cajón de sastre") donde introducía las palabras Fantasmas

Más adelante, en este otro post hablaba de lo que ahora llamo palabras Semi-Fantasmas

Allí hablaba de Programa Fantasma y ahora digo palabras Fantasmas, pues un programa no es más que un conjunto de palabras. Usaré ambos términos indistintamente.

Hasta la fecha solo he utilizado esto en dos programas. A saber, el Extractor de Diccionarios (al que hacía referencia el primer post) y al Cargador de Ficheros Fuente referido en el segundo post.

El Extractor de Diccionarios (aka Extractor) necesita borrar y/o redefinir multitud de palabras del diccionario mientras se ejecuta. Sin entrar en detalles, lo que hace es a partir de un diccionario nos lo reduce a otro más pequeño, con menos palabras.

Esto obliga a que el programa (el conjunto de palabras que conforman el programa) debe estar por debajo de las que se van a borrrar/redefinir. Lo normal es cargar primero tu programa (el Extractor en este caso) y luego, encima, cargar el diccionario que va a alterar. Así te aseguras que las palabras que conforman el Extractor NO se moverán durante la ejecución.

Si estuviera el Extractor encima, para empezar ya no se podrían hacer Forgets (pues afectarían al Extractor), y por otro lado, las redefiniciones moverían el Extractor en la memoria mientras se ejecuta lo cual es casi siempre fatal.

Pero esto tiene un inconveniente: Al terminar el Extractor, además del diccionario extraído, están también todas las palabras del Extractor y, lo que es peor, están por debajo de la que te interesa conservar (las extraídas), por lo que tienes que hacer a mano toda una serie de Redefiniciones y algún Forget para que te quede solo el diccionario extraído.

En el caso del Cargador de Ficheros Fuente (aka Cargador) siempre se carga primero el programa y a medida que se ejecuta va cargando la palabras del Fuente por encima de él. Luego aquí el Cargador NO se mueve en memoria. Pero queda "sepultado" por todas la palabras del fuente y siguen estando las palabras que pudieran haber antes de cargar el Cargador.

El problema es el mismo de antes, para dejar solo el programa generado por el Fuente tenemos que eliminar el Cargador a base de una serie de redefiniciones a mano. En mi mejor versión NO-Fantasma lo reducía a 4 redefiniciones máximo. Pero esto te cambia el orden de las palabras con respecto al que tenían en el fuente. Lo cual puede ser o no importante, según el caso.

Aquí entra como solución el tema de las palabras o programas Fantasmas y Semi-Fantasmas.

Elurdio
Mensajes: 510
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 115 veces
Agradecimiento recibido: 95 veces

Re: Jupiter Ace Forth (Tutoriales)

Mensajepor Elurdio » 13 Ago 2022 09:47

Palabras Fantasmas y Semi-Fantasmas II

Vamos a usar CARGADOR como ejemplo:
Fantochec red.png
Fantochec red.png (57.42 KiB) Visto 118 veces

Supongamos que tenemos el programa CARGADOR que cargará en memoria un Fichero Fuente, con lo que se generarán toda una serie de palabras en el diccionario.

CARGADOR consta de las palabras COLCHON, WORD1, WORD2, ···, WORD8 y WORD9.

En realidad CARGADOR solo utiliza las palabras WORD1 ··· WORD9. La palabra COLCHON es solo una palabra tipo CREATE de un tamaño grande, digamos 10.000 bytes. Esta palabra COLCHON ha de ser la primera del programa y su única función es crear un espacio interpuesto (o colchón) entre el final de la Pila de Datos (Data Stack) y WORD1.

En el esquema, la figura I representa el Ace Forth cuando está vacío, recién arrancado. El diccionario solo contiene la palabra FORTH (el vocabulario por defecto) y la Pila de datos (*).

Cargamos nuestro programa CARGADOR y nos queda como la figura II. Ahora el diccionario contiene FORTH seguido de COLCHON, WORD1, etc. hasta WORD9. Aquí acaba el diccionario. Le sigue la Pila de datos.

Ahora arrancamos CARGADOR ejecutando WORD1. Este lo primero que hace es:

FORGET COLCHON

Esto hace que se borren todas la palabras del programa CARGADOR, con lo que la cosa queda como se muestra en la figura III. Vemos que ahora el diccionario vuelve a ser como en la figura I, solo con FORTH seguido de la Pila de datos.

Pero las palabras borradas con FORGET siguen estando en memoria. Pues FORGET COLCHON lo que hace es decirle al Ace Forth que el diccionario acaba después de FORTH y mueve Pila de Datos a continuación de la nueva "última palabra" que ahora es FORTH.

Viene a ser como cuando borras un fichero en un disco, simplemente le dices al disco que el espacio que ocupaba está disponible, pero el fichero sigue estando ahí. Se irá sobreescribiendo a medida que añadas nuevos ficheros.

En el esquema las palabras que forman parte del diccionario están en color verde mientras las que se han borrado (desvinculado del diccionario) están en color violeta.

Vemos que ahora la Pila de datos ha sobreescrito una porción de COLCHON. Pero las palabras importantes, WORD1 ··· WORD9 siguen intactas.

Después de borrarse a sí mismo, CARGADOR sigue funcionando y ahora se dedica a compilar en el diccionario las palabras que lee del fichero Fuente.

Estas son WORDa, WORDb, ···· WORDp

Todas estas palabras, junto con FORTH son las únicas que hay en el diccionario, pues las de CARGADOR están borradas.

Vemos que a medida que se compilan palabras, se va sobreescribiendo más y más COLCHON (figuara IV)

Lo importante es que COLCHON sea lo suficientemente grande para que NUNCA se lleguen a sobreescribir las palabras importantes de CARGADOR, o sea, WORD1 ··· WORD9. Si eso sucediera se estropearía el invento.

Cuando CARGADOR termina de ejecutarse, en el diccionario solo tenemos el fruto de su ejecución, un diccionario con solo las palabra que había en el fuente que cargó (WORDa···WORDp). CARGADOR desapareció no mas empezar.

Esto es un ejemplo de un programa Fantasma.

Si hubiéramos usado EXTRACTOR como ejemplo, primero cargamos el diccionario a extraer y luego EXTRACTOR encima. El esquema sería similar, solo que en vez de ser FORTH la última palabra antes de cargar EXTRACTOR, sería la última del diccionario a extraer.

La idea en este caso es que EXTRACTOR no se mueva en memoria y COCHON está para impedir que la Pila de Datos llegue a sobreescribirlo si crece lo suficiente.

Aunque parece que la pila es lo único que puede sobreescribir a COLCHON, pues como dijimos, EXTRACTOR reduce el diccionario, no lo aumenta como hace CARGADOR, hay otro efecto, más importante aún, a tener en cuenta: REDEFINE puede temporalmente expandir el diccionario. Expansión que puede llegar a ser muy grande. Por eso COLCHON ha de ser grande también con EXTRACTOR.

(*) Aunque al inicio y al cargar el programa la Pila de Datos está vacía, siempre hay 12 bytes de seguridad entre el final del diccionario y la Pila. En el esquema esos 12 bytes los incluyo en el recuadro de Pila de Datos (Data Stack).

Imagen calidad original del Esquema de arriba
Fantochec.png
(185.25 KiB) No descargado aún

Elurdio
Mensajes: 510
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 115 veces
Agradecimiento recibido: 95 veces

Re: Jupiter Ace Forth (Tutoriales)

Mensajepor Elurdio » 13 Ago 2022 13:37

Palabras Fantasmas y Semi-Fantasmas III

En la explicación he usado FORGET COLCHON para borrar el programa fantasma. Pero en la práctica NO se puede usar FORGET por dos motivos:

  • Si llamamos a FORGET desde una palabra de usuario, espera el nombre de la palabra a borrar en el búfer de entrada. Se puede programar que lo coloque allí para FORGET pero es complicado. Es más sencillo tener una versión de FORGET que tome de la pila el CFA de la palabra a borrar.
  • Cuando se ejecuta FORGET desde un programa, éste se detiene.

Por todo lo dicho anteriormente usamos una palabra que es un FORGET simplificado. La he llamado OLVIDA. Cuando se ejecuta procede así:
  1. Toma el CFA de la palabra a borrar de la pila.
  2. Le dice a Ace Forth que la última palabra es ahora la anterior a la borrada
  3. Recoloca la pila de datos a su nueva posición detrás de la nueva última palabra.

: OLVIDA ( CFA - )
3 - @ CURRENT @
! STACKMOVE
;

Por la estructura de las palabras del Ace Forth sabemos que:
CFA - 3 = LkFA
(LkFA) = NLFA de la palabra anterior

Por otro lado, CURRENT contiene la dirección dónde se guarda el NLFA de la última palabra del Current Vocabulary(*). El NLFA que almacenemos ahí convertirá la palabra correspondiente en la última.

Por último, STACKMOVE mueve la pila (Data Stack) a su nueva posición (12 bytes después del final de la nueva "última palabra").

: STACKMOVE ( - ) 
CURRENT @ @ 3 +
DUP PF_SIZE + >R 15419
@ HERE - 12 =
IF
R> DUP 15415 ! 12
+ 15419 ! EXIT
THEN
R> DUP 12 + 15419
@ 4 - HERE 12
+
UDO
BEGIN
I @ OVER ! 2+
ULOOP+2
UNTIL
SWAP 15415 ! 15419 !
;

Decir que 15415 y 15419 son variables del sistema. La primera contiene la dirección del primer byte justo después del final del diccionario (es el valor que devuelve HERE) y la segunda contiene la dirección del primer byte justo después final de la pila (Data Stack).

En cuanto a UDO y ULOOP+2 junto con el BEGIN/UNTIL equivalen a un DO/+LOOP con incremento igual a 2 y de contador Sin signo (trata el índice como un número entero Sin signo)(**). Detalles aquí

: UDO ( n1,n2 - )
R> ROT >R SWAP >R
>R
;

: ULOOP+2 ( - )
R> R> 2+ >R >R
I' J U< 0= DUP
IF
R> R> R> DROP DROP
>R
THEN
;

(*) Recordar que TODOS mis programas del Jupiter Ace en este Foro presuponen que NO hay ningún otro vocabulario que el que viene por defecto (FORTH).
(**) He hecho versiones mejores de bucles sin signo, pero esta primera versión aún la usan muchos de mi programas

Elurdio
Mensajes: 510
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 115 veces
Agradecimiento recibido: 95 veces

Re: Jupiter Ace Forth (Tutoriales)

Mensajepor Elurdio » 13 Ago 2022 23:15

Palabras Fantasmas y Semi-Fantasmas IV

Vamos con las palabras Semi-Fantasmas:

Son palabras Fantasmas que, después de haber sido borradas (y por tanto, desvinculadas del diccionario) se vinculan de nuevo al diccionario de modo temporal, mientras se ejecuta el programa fantasma. El cual, antes de terminar, las vuelve a desvincular del diccionario.

O sea, las palabras Semi-Fantasmas:
  1. Están borradas
  2. Siguen vinculadas al diccionario.
El punto 1 implica que NO forman parte del diccionario. En este aspecto la cosa no cambia con respecto al ejemplo que vimos (CARGADOR)
El punto 2 implica que pueden ser llamadas y ejecutadas por palabras que no forman parte del programa fantasma.

En el caso de CARGADOR hay dos palabras que se transforman en Semi-Fantasmas. Son dos palabras inmediatas que se llaman ( y \ respectivamente. Se vinculan para que al cargar las palabras del Fuente, si tienen comentarios, se ejecuten estas palabras y los eliminen.

Proceso de Vinculación

En mi caso hago lo siguiente: (siguiendo el ejemplo de CARGADOR)

El programa Fantasma incluye una palabra "marcador". La llamo TIP y es de tipo CREATE con un PF de tamaño cero (sin PF).
Ha de estar colocada justo después de las palabras que serán Semi-Fantasmas y que estarán seguidas en el diccionario. En nuestro ejemplo primero está ( y luego \. A continuación viene TIP
···

· La memoria crece hacia abajo.
·
: (
ASCII ) WORD DROP
;
IMMEDIATE
: \
13 WORD DROP
;
IMMEDIATE
CREATE TIP
·
·
·
Al ejecutarse TIP dejará en la pila su PFA

Cuando se ejecuta CARGADOR lo primero que hace es borrarse de memoria y luego, ya siendo Fantasma, pregunta si quieres que se eliminen los comentarios de las palabras del fuente que se carga. Si respondes que sí, entonces procede a convertir las dos palabras comentadas, que ya son Fantasmas, en palabras Semi-Fantasmas. Para ello las re-vincula al diccionario ejecutando LINKW
: LINKW ( - )
TIP 5 - @ DUP \ Deja en la pila dos copias del NLFA de \
LASTWORD 15434 = \ Comprueba si la última palabra del diccionario es FORTH.
IF
CURRENT @ ! \ Si es así, convierte \ en la última palabra del diccionario
ELSE
15434 NEXTUP 3 - ! \ Si no, linka la que sigue a FORTH con \
THEN
2- @ 2- 15433 SWAP ! \ Linka ( con FORTH
;


15434 = CFA de FORTH
15433 = NLFA de FORTH

LASTWORD pone en la pila el CFA de la última palabra del diccionario.

: LASTWORD ( - CFA)
CURRENT @ @ 1+
;

NEXTUP
toma de la pila un CFA y devuelve el CFA de la palabra siguiente (subiendo en memoria). Su funcionamiento está detallado en este post del hilo "Utilidades"

: NEXTUP ( CFA1 - CFA2)
5 - DUP @ +
BEGIN
DUP C@ 127 >
IF
6 + 1
ELSE
1+ 0
THEN
UNTIL
;


Pongo un esquema del caso en que ya hay un diccionario con 3 palabras (además de FORTH) antes de cargar CARGADOR.
La memoria crece hacia arriba.
El bloque de la derecha (violeta) empieza en memoria mucho más arriba que el bloque de la izquierda (gris obscuro)
El bloque primero es el del diccionario. Tienen marco gris obscuro
El bloque segundo es el de las palabras borradas. Tienen marco violeta.
El primer par de bloques (a la izquierda de la línea naranja) representa la memoria después de borrarse CARGADOR
El segundo par de bloques (a la derecha de la línea naranja) representa la memoria después de usarse LINKW
Cada recuadro es una palabra con su cabecera y su PF. De la cabecera solo muestro los campos de interés (NL, LkF y NLF)
Semi-Fantoche red.png
Semi-Fantoche red.png (114.14 KiB) Visto 98 veces

Imagen calidad original del Esquema de arriba.
Semi-Fantoche.png
(231.12 KiB) No descargado aún


Proceso de Desvinculación

Este paso es mucho más sencillo que el anterior. Lo lleva a cabo la palabra RELINKW
: RELINKW ( - )
15433 15434 NEXTUP 3 -
!
;

Simplemente pone en el LkF de la primera palabra que viene después de FORTH (en nuestro esquema es WORD1) el NLF de FORTH con lo que \ y ( quedan deslinkadas del diccionario.


Volver a “Jupiter Ace”

¿Quién está conectado?

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