Este artículo es el final de una serie de artículos de eForth sobre una calculadora programable. Comience aquíLos comandos del idioma de entrada "Electrónica MK-161" ocupan solo la mitad del archivo eForth0.mkl. La segunda mitad está ocupada por tablas, cuyo desarrollo no fue menos difícil que escribir la parte algorítmica del traductor. Intentemos descubrir cómo se usan estas tablas.

El profesor Wirth enseña que "programar en pequeño" consiste en desarrollar dos componentes igualmente importantes: algoritmos y estructuras de datos.
Ya hemos encontrado una estructura de datos eForth. Este es el cuerpo de VCA (palabras de alto nivel) ubicado en la memoria de bytes. Cuatro manejadores interpretan los campos de parámetros de "su propio" VCA de diferentes maneras:
.DB DOVAR ; .DB … ; .DB DOCON ; .DW _ ; .DB DOCONM ; .DW _ ; .DB DOLST ; .DW 1, 2,… EXITT ;
La siguiente estructura de datos relativamente simple está asociada con los "mensajes estándar" de TYPE.
Todos los mensajes de eForth están numerados y transferidos a la memoria barata del programa. Si la palabra TIPO imprime una sola letra, su código puede ser el número de dicho mensaje, de 0 a 7.
; TYPE .BASE tblTYPE: .DBB str7,str6, str5, str4, str3, str2, str1, str0
En el lenguaje MK extendido, el seudocomando .BASE establece la "base" para el comando .DBB, que coloca secuencialmente los desplazamientos de línea str7, str6, en bytes, etc. relativo a la etiqueta base tblTYPE. Al agregar números del 0 al 7 a la dirección de la tabla, se puede leer este desplazamiento. Al agregar el desplazamiento encontrado a tblTYPE, obtenemos la dirección de la línea deseada.
El primer byte de la cadena contiene su longitud. eForth hace un uso extensivo de tales
líneas de conteo .
También encontramos la tabla tblTokens, que enumera las direcciones de código de las 208 palabras incrustadas. Si la palabra no es primitiva, la tabla contiene 0. Ir a la dirección 0 hará que eForth se reinicie, con un chirrido.
También se mencionó la tabla tblNames, en referencia a los nombres de las mismas 208 palabras. Estos nombres en forma de líneas de conteo se almacenan en la misma memoria de programa "de goma". La tabla tblNames en sí no estará disponible mientras se ejecuta eForth, pero la información contenida en ella no se perderá. En tiempo de compilación, eForth.f transferirá la dirección de los nombres a una estructura de datos más conveniente almacenada en la memoria decimal (ver 2).
También hablé sobre tblCHPUT, una tabla asociativa de códigos de control cuando mostraba una letra en la pantalla de una calculadora. Otras siete tablas, desde tblKeyNum hasta tblKeyRusF, traducen el código de un botón presionado en diferentes modos de teclado a un código de letra de 8 bits. La dirección de la subrutina responsable del modo de teclado activo está en el registro decimal ptrKbdInt.
En total, solo una estructura de datos permaneció sin ensamblar en el archivo eForth0.mkl, estas son tablas de reconocimiento de nombres. Dejémoslos de postre (ver 5) después del plato principal: dos tablas de encabezados almacenadas en la memoria decimal. Primero, nos armaremos con herramientas para "rellenar" estos encabezados.
1. Trabajar con encabezados: ¡CABEZA! y HEAD @
HEAD! ( xt nfa r -- ) r, xt nfa. HEAD@ ( r -- xt nfa lex ) r, xt, nfa .
Un registro decimal MK-161 puede memorizar 12 lugares decimales. eForth utiliza este registro para almacenar tres números pequeños, cada uno de 0 a 9999. Los
tres "campos" para almacenar estos números se denominan A, B y C: AAAABBBBCCCC. El signo decimal se refiere solo al campo A.
La primitiva HEAD @ obtiene el número de registro y divide el número desde allí en campos, y HEAD! recopila campos en un número largo y escribe el "monstruo" resultante en el registro especificado. Pero hay matices.
El "encabezado decimal" de la palabra contiene en el campo A la dirección de su nombre (nfa). Si esta dirección es negativa, el nombre se almacena en la memoria del programa. El campo B contiene la palabra token (xt). El campo C se llama léxico. Almacena el bit INMEDIATO y una señal de que la palabra está destinada solo para compilación.
HEAD @ divide el encabezado en partes. En la parte superior de la pila está el campo del léxico C, debajo está el campo de nombre A. El campo B, en el que generalmente se almacena el token, está en la parte inferior.
CABEZA! restablece el campo C.
2. Encabezados en línea

Los encabezados de cada una de las 208 palabras incorporadas (0 a 207) van en orden, comenzando con R44. El campo A siempre contiene un número negativo, ya que los nombres de estas palabras están codificados en la memoria del programa.
Los campos B y C son editables. Por lo tanto, el usuario puede redefinir las palabras incorporadas y hacer lo INMEDIATO que necesita de ellas (ver 4).
3. Encabezados de usuario

Trabajar con solo 208 nombres predefinidos ahorra bytes de memoria, pero es inusualmente aburrido. Por lo tanto, desarrollé otra estructura de datos donde la fantasía al elegir un nombre se limita a solo 32 letras. Esta estructura consta de 32
listas , cada una de las cuales es responsable de las palabras del usuario de cierta longitud. Cada una de estas 32 listas tiene un título personal. Las listas mismas saltan sobre la memoria decimal, pero sus encabezados siempre se almacenan en R301 ... R332.
Ordenar palabras por longitud de nombre es un punto destacado importante de 161eForth. La ordenación reduce en gran medida el número de comparaciones cuando busca una palabra por su nombre, lo que acelera la compilación. ¿Quién necesita funciones hash si cada nombre tiene una longitud conocida?
Para simplificar, el
encabezado de la lista tiene la misma estructura con los campos A, B y C que el encabezado de la palabra. El propósito de estos campos es diferente. El campo A contiene el número del primer registro en la lista. El campo B contiene el número de registros proporcionados a la lista. El campo C almacena el número de palabras cuyos encabezados ya están en la lista.
Al comienzo del trabajo, los campos C son iguales a cero; las palabras están ausentes en todas las listas. Los campos B son 2, cada lista tiene un par de registros para comenzar. Los campos A indican bloques de 2 registros que comienzan con R333.
Cada lista contiene encabezados de palabras. Ya los hemos desmontado (ver. 1). Aquí, tal vez, la dirección del nombre (nfa) será positiva y apuntará a la línea de conteo, tradicionalmente almacenada frente al cuerpo del VCA. Además, el token en el campo B es la dirección del campo de código (cfa) que entra en la memoria binaria inmediatamente después de este nombre. Hay una excepción:
si la palabra ya se ha determinado, el campo A apuntará al nombre anterior. ¿Por qué guardar la cuerda de nuevo? La memoria binaria es cara.
Cuando todos los registros de la lista están llenos (B = C), la palabra PUBLICAR proporciona 5 lugares más libres, empujando esta estructura de datos en el lugar correcto y ajustando los enlaces (A) en los encabezados de la lista.
4. Publicación de una nueva palabra: TRABAJAR y PUBLICAR
LAST ( -- a ) . WORK ( -- a ) . PUBLISH ( -- ) . $,n ( nfa -- ) , nfa. ?UNIQUE ( a -- a ) , .
La estructura de datos desarrollada para el MK-161 para almacenar títulos de palabras resultó ser práctica y fácil de integrar en eForth. Cuando CREAR, CONSTANTE o: crea una nueva palabra, acceden a la palabra del sistema $, n para crear un título para la palabra con el nombre dado. $, n se refiere a? ÚNICO para la verificación: ¿creamos una nueva palabra o redefinimos la antigua?
Si ya existe una palabra con el mismo nombre, ÚNICO advierte al usuario sobre esto. Al mismo tiempo, la dirección del encabezado redefinido se ingresa en la ÚLTIMA variable del sistema. Para una nueva palabra, LAST se restablece a cero.
En cualquier caso, $, n crea un nuevo encabezado en la variable TRABAJO: es un registro decimal que puede almacenar 12 bits del encabezado. Si no se encontró el nombre, se incluye en el diccionario antes del campo de código, como sucede en 86eForth y muchos otros Forts.
El MK-161 logró prescindir de un "campo de comunicación" , lo que también ahorra memoria binaria.
La primitiva PUBLICAR completa la definición de una palabra. Al compilar palabras de dos puntos, PUBLISH se llama desde ;; como resultado, no se requirió el bit SMUDGE. La última variable determina el lugar donde se copia el encabezado de WORK. Si LAST es cero, se crea un nuevo encabezado en la lista correspondiente (ver 3). ¿Está completa la lista? Luego PUBLISH agregará 5 registros más, cuatro de ellos para el futuro.
Después de ejecutar PUBLISH, la variable LAST siempre apunta al título de la última palabra. Esto ayuda a INMEDIATO a hacer su trabajo cambiando el campo del léxico.
5. (ENCONTRAR) y tablas de reconocimiento de nombre
(FIND) ( a -- r T | a F ) r, a. FIND ( a -- a F | xt 1 | xt -1) . 1, IMMEDIATE.
Un primitivo (ENCONTRAR) gestiona la búsqueda de una palabra por su nombre. Primero, busca un nombre entre las palabras incorporadas con nombres previamente conocidos, luego verifica la lista de palabras de usuario con la longitud de nombre deseada (ver 3). Las tablas de reconocimiento de nombres aceleran enormemente este "primero". Así es como funcionan.
Al principio (ENCONTRAR) encuentra en la matriz tblLen la dirección de la tabla asociativa principal, en la que los nombres conocidos de la longitud requerida están "preparados". En esta tabla (ENCONTRAR) busca el primer carácter del nombre. En la mayoría de los casos, esto le permite averiguar de inmediato el
número de registro de título de la palabra buscada, por la primera letra y la longitud.
Sucede que varias palabras de la misma longitud tienen las mismas primeras letras. Luego, en lugar del número de registro (FIND), se topa con la dirección de la siguiente tabla asociativa (el número de lectura es 300 o más) y la búsqueda continúa en la segunda letra. Y así sucesivamente, hasta que se encuentre la palabra o se establezca que no existe dicha palabra.
Por supuesto, después de una coincidencia para las primeras letras (ENCONTRAR), se verifica el nombre completo. Pero las
tablas de reconocimiento hicieron que eForth sea rápido . Esta primavera, invertí mucho de mi tiempo en ellos, y ahora ahorran tiempo de búsqueda. Las "claves" en ellas están incluso ordenadas alfabéticamente. Lo sentimos, el firmware MK-161 lo escupe.
En aras de la compatibilidad, implementé la palabra ENCONTRAR de Fort ANS [4], que confía en la primitiva "trabajo negro" (ENCONTRAR). La palabra ya considerada? ÚNICO también está buscando su argumento a través de (ENCONTRAR).
6. Intérprete externo
El libro [1] contiene una descripción exhaustiva de eForth, que incluye un intérprete externo de "texto". Es él quien ejecuta o compila el texto fuente en el idioma Fort. Las diferencias con los intérpretes textuales de otros dialectos de Fort ([2], [3]) han aparecido en las últimas décadas, pero hay pocas.
A continuación se muestra un diagrama de bloques de un intérprete de texto tomado de [1]. Tenga cuidado: ¡este "intérprete" tiene un modo de compilación! La palabra $ COMPILE es responsable de compilar el texto de Forte en "código cosido", cuya ejecución examinamos con tanto detalle en el primer artículo. Cuando se ejecuta $ INTERPRET, las palabras ingresadas se ejecutan inmediatamente - modo de interpretación. EVAL "calcula" toda la cadena ingresada, invocando una de estas dos palabras para cada palabra ingresada.

Después del diagrama de bloques, el autor descifra cuál de los bloques lo hace. Aquí está su traducción. Los nombres de bloque generalmente coinciden con los nombres de palabras de eForth. La palabra NOMBRE? está ausente en mi implementación, se reemplazó con éxito por rápido (ENCONTRAR) (ver. 5).
El libro también proporciona el código fuente para cada palabra eForth en la versión de Windows, con breves explicaciones. ¿Cuál es la versión para MK-161 diferente? Ya te lo dije. El código fuente para mi implementación está en el archivo:
the-hacker.ru/2019/161eforth0.5b.zipFinalmente, mencionaré la implementación de la palabra
(PARSE) en el lenguaje MK-161 : en Windows es VCA. La depuración tomó una semana, pero
aceleró la compilación a la mitad . La palabra (PARSE) hace todo el "trabajo sucio" para que PARSE aísle palabras individuales del flujo de texto de entrada.
Mis adiciones al intérprete externo son dos palabras, además del ciclo habitual de SALIDA: el TLOAD ya mencionado y tomado de versiones anteriores de ARCHIVO. La palabra ARCHIVO traduce E / S a la consola, pero lee líneas para interpretación desde el puerto RS-232. Después del procesamiento exitoso de cada línea, se envía al puerto una carta con el código 11. El archivo descargado de la computadora debe terminar con la palabra QUIT.
Todavía no he depurado la palabra ARCHIVO. Si alguien lo necesita, comparta sus impresiones.
La revisión 161eForth de los lugares difíciles ha terminado, pero Fort es una herramienta increíblemente flexible que cada propietario puede personalizar. Incluso cuando hayas descubierto todo a fondo, alguien en algún lugar del planeta inventará otro truco que puede sorprenderte.
Aquí están las palabras finales del autor eForth de [1]:
Durante 26 años, he reescrito eForth muchas, muchas veces. En cada doblaje, traté de hacerlo más simple y claro. Ahora en 86eForth v5.2, creo que he logrado la corrección y, por lo tanto, estoy muy feliz.
Como dijo Einstein:
Todo debe hacerse lo más simple posible, pero no más simple.
Hacer 86eForth v5.2 aún más fácil, quizás romperlo o no ser útil como herramienta de programación.
Literatura
- Dr. Chen-Hanson Ting. eForth and Zen - 3rd Edition, 2017. Disponible en Amazon Kindle.
- Baranov S.N., Nozdrunov N.R. Fuerte lenguaje y su implementación. - L .: Ingeniería mecánica. Leningrado Departamento, 1988.
- Semenov Yu.A. Programación en el lenguaje FORT. - M .: Radio y comunicaciones, 1991.
- ANS Cuarto estándar. X3.215-1994. Traducción
- Documentación SP-Forth .
- Offete Enterprises (Dr. Chen-Hanson Ting) , autor de 86eForth v5.2, está en inglés.
- La historia de Mikhail Pukhov "True Truth" con el programa "Moonwalker-1", donde obtuve KDPV y me encantan las calculadoras soviéticas.