EFORTH para MK-161: Estruturas de dados

Este artigo é o fim de uma série de artigos eForth em uma calculadora programável. Comece aqui .

Os comandos do idioma de entrada "Electronics MK-161" ocupam apenas metade do arquivo eForth0.mkl. A segunda metade é ocupada por tabelas, cujo desenvolvimento não foi menos difícil do que escrever a parte algorítmica do tradutor. Vamos tentar descobrir como essas tabelas são usadas.


O professor Wirth ensina que “programar no pequeno” consiste em desenvolver dois componentes igualmente importantes - algoritmos e estruturas de dados.

Já encontramos uma estrutura de dados eForth. Esse é o corpo do VCA (palavras de alto nível) localizado na memória de bytes. Quatro manipuladores interpretam os campos de parâmetros do VCA "próprio" de maneiras diferentes:

.DB DOVAR ;      .DB … ;      .DB DOCON ;    .DW _ ;   .DB DOCONM ;     .DW _ ;   .DB DOLST ;     .DW 1, 2,… EXITT ;   

A seguinte estrutura de dados relativamente simples está associada ao TYPE "mensagens padrão". Todas as mensagens eForth são numeradas e transferidas para a memória barata do programa. Se a palavra TYPE imprimir uma única letra, seu código poderá ser o número dessa mensagem, de 0 a 7.

 ;   TYPE .BASE tblTYPE: .DBB str7,str6, str5, str4, str3, str2, str1, str0 

Na linguagem MK estendida, o pseudo-comando .BASE define a "base" para o comando .DBB, que coloca sequencialmente as compensações de linha de str7, str6, etc. em relação ao rótulo base tblTYPE. Adicionando números de 0 a 7 ao endereço da tabela, esse deslocamento pode ser lido a partir dele. Adicionando o deslocamento encontrado ao tblTYPE, obtemos o endereço da linha desejada.

O primeiro byte da string contém seu comprimento. O eForth faz uso extensivo dessas linhas de contagem .

Também encontramos a tabela tblTokens, que lista os endereços de código de todas as 208 palavras incorporadas. Se a palavra não for primitiva, a tabela conterá 0. Ir para o endereço 0 fará com que o eForth seja reiniciado, com um rangido.

A tabela tblNames também foi mencionada, referindo-se aos nomes das mesmas 208 palavras. Esses nomes na forma de linhas de contagem são armazenados na mesma memória de programa "borracha". A tabela tblNames em si não estará disponível enquanto o eForth estiver em execução, mas as informações contidas nela não serão perdidas. No momento da compilação, o eForth.f transferirá o endereço dos nomes para uma estrutura de dados mais conveniente armazenada na memória decimal (consulte 2).

Também falei sobre o tblCHPUT, uma tabela associativa de códigos de controle ao exibir uma letra na tela de uma calculadora. Outras sete tabelas, de tblKeyNum a tblKeyRusF, convertem o código de um botão pressionado em diferentes modos de teclado em um código de letra de 8 bits. O endereço da sub-rotina responsável pelo modo de teclado ativo está no registro decimal ptrKbdInt.

No total, apenas uma estrutura de dados permaneceu desmontada no arquivo eForth0.mkl, essas são tabelas de reconhecimento de nome. Vamos deixá-los para a sobremesa (veja 5) após o prato principal - duas tabelas de cabeçalho armazenadas na memória decimal. Primeiro, nos armaremos com ferramentas para "encher" esses títulos.

1. Trabalhe com títulos: CABEÇA! e HEAD @


 HEAD! ( xt nfa r -- )     r,  xt  nfa. HEAD@ ( r -- xt nfa lex )     r,  xt, nfa  . 

Um registro decimal MK-161 pode memorizar 12 casas decimais. O eForth usa esse registro para armazenar três números pequenos, cada um de 0 a 9999. Os três "campos" para armazenar esses números são chamados de A, B e C: AAAABBBBCCCC. O sinal decimal refere-se apenas ao campo A.

A primitiva HEAD @ obtém o número do registro e divide o número de lá em campos, e HEAD! coleta campos em um número longo e grava o "monstro" resultante no registro especificado. Mas existem nuances.

O “cabeçalho decimal” da palavra contém no campo A o endereço de seu nome (nfa). Se este endereço for negativo, o nome é armazenado na memória do programa. O campo B contém a palavra token (xt). O campo C é chamado de léxico. Ele armazena o bit IMMEDIATE e um sinal de que a palavra se destina apenas à compilação.

HEAD @ divide o cabeçalho em partes. No topo da pilha está o campo de léxico C, abaixo dele está o campo de nome A. O campo B, no qual o token geralmente é armazenado, fica na parte inferior.

CABEÇA! redefine o campo C.

2. Cabeçalhos em linha




Os cabeçalhos de cada uma das 208 palavras incorporadas (0 a 207) estão em ordem, começando com R44. O campo A sempre contém um número negativo, pois os nomes dessas palavras são codificados na memória do programa.

Os campos B e C são editáveis. Portanto, o usuário pode redefinir as palavras incorporadas e tornar o IMEDIATO necessário delas (consulte 4).

3. Cabeçalhos do usuário




Trabalhar com apenas 208 nomes predefinidos economiza memória de bytes, mas é incomumente entediante. Portanto, desenvolvi outra estrutura de dados em que a fantasia de escolher um nome é limitada a apenas 32 letras. Essa estrutura consiste em 32 listas , cada uma das quais é responsável por palavras de usuários com um determinado comprimento. Cada uma dessas 32 listas tem um título pessoal. As próprias listas saltam sobre a memória decimal, mas seus cabeçalhos são sempre armazenados em R301 ... R332.

A classificação das palavras pelo tamanho do nome é um destaque importante do 161eForth. A classificação reduz bastante o número de comparações quando você procura uma palavra pelo nome, acelerando a compilação. Quem precisa de funções de hash se cada nome tiver um comprimento conhecido?

Para simplificar, o cabeçalho da lista tem a mesma estrutura com os campos A, B e C que o cabeçalho da palavra. O objetivo desses campos é diferente. O campo A contém o número do primeiro registro na lista. O campo B contém o número de registros fornecidos à lista. O campo C armazena o número de palavras cujos títulos já estão na lista.

No início do trabalho, os campos C são iguais a zero e as palavras estão ausentes em todas as listas. Os campos B são 2, cada lista recebe alguns registros para começar. Os campos A indicam blocos de 2 registros começando com R333.

Cada lista contém títulos de palavras. Já os desmontamos (ver. 1). Aqui, talvez, o endereço do nome (nfa) seja positivo e aponte para a linha de contagem, tradicionalmente armazenada na frente do corpo do VCA. Além disso, o token no campo B é o endereço do campo de código (cfa) que entra na memória binária imediatamente após esse nome. Há uma exceção - se a palavra já tiver sido determinada, o campo A apontará para o nome antigo. Por que armazenar a string novamente? A memória binária é cara.

Quando todos os registros da lista estão cheios (B = C), a palavra PUBLISH fornece mais 5 locais gratuitos, empurrando essa estrutura de dados no lugar certo e ajustando os links (A) nos cabeçalhos da lista.

4. Publicação de uma nova palavra: TRABALHO e PUBLICAÇÃO


 LAST ( -- a )      . WORK ( -- a )     . PUBLISH ( -- )     . $,n ( nfa -- )     ,    nfa. ?UNIQUE ( a -- a )  ,    . 

A estrutura de dados desenvolvida para o MK-161 para armazenar títulos de palavras acabou sendo prática e facilmente integrada ao eForth. Quando CREATE, CONSTANT ou: cria uma nova palavra, eles acessam a palavra do sistema $, n para criar um título para a palavra com o nome fornecido. $, n refere-se a? ÚNICO para verificação - criamos uma nova palavra ou redefinimos a antiga?

Se uma palavra com o mesmo nome já existir ,? UNIQUE avisa o usuário sobre isso. Ao mesmo tempo, o endereço do cabeçalho redefinido é inserido na variável de sistema LAST. Para uma nova palavra, ÚLTIMO é redefinido para zero.

De qualquer forma, $, n cria um novo cabeçalho na variável WORK - é um registro decimal que pode armazenar 12 bits do cabeçalho. Se o nome não foi encontrado, ele será incluído no dicionário antes do campo de código, como acontece no 86eForth e em muitos outros Fortes. O MK-161 conseguiu passar sem um "campo de comunicação" , o que também economiza memória binária.

A primitiva PUBLISH completa a definição de uma palavra. Ao compilar palavras em dois pontos, PUBLISH é chamado de ;; como resultado, o bit SMUDGE não era necessário. O local em que o cabeçalho do WORK é copiado é determinado pela variável LAST. Se LAST for zero, um novo cabeçalho será criado na lista correspondente (consulte 3). A lista está completa? Em seguida, PUBLISH adicionará mais 5 registros, quatro deles para o futuro.

Depois de executar PUBLISH, a variável LAST sempre aponta para o título da última palavra. Isso ajuda a IMMEDIATE a fazer seu trabalho alterando o campo léxico.

5. (FIND) e tabelas de reconhecimento de nomes


 (FIND) ( a -- r T | a F )    r,        a. FIND ( a -- a F | xt 1 | xt -1)    .  1,  IMMEDIATE. 

Um primitivo (FIND) gerencia a pesquisa de uma palavra por seu nome. Primeiro, ele procura um nome entre as palavras incorporadas com nomes conhecidos anteriormente e depois verifica a lista de palavras do usuário com o comprimento de nome desejado (consulte 3). As tabelas de reconhecimento de nomes aceleram bastante esse "primeiro". Aqui está como eles funcionam.

No início (FIND), encontra na matriz tblLen o endereço da tabela associativa principal, na qual nomes bem conhecidos do comprimento necessário são "preparados". Nesta tabela (FIND), procura pelo primeiro caractere do nome. Na maioria dos casos, isso permite que você descubra imediatamente o número do registro de título da palavra pesquisada - pela primeira letra e comprimento.

Acontece que várias palavras do mesmo comprimento têm as mesmas primeiras letras. Então, em vez do número do registro (FIND), ele se depara com o endereço da próxima tabela associativa (o número de leitura é 300 ou mais) e a pesquisa continua na segunda letra. E assim por diante, até que a palavra seja encontrada ou seja estabelecido que não existe tal palavra.

Obviamente, após uma correspondência pelas primeiras letras (FIND), o nome inteiro é verificado. Mas as tabelas de reconhecimento tornaram o eForth rápido . Nesta primavera, investi muito tempo neles e agora eles economizam tempo de pesquisa. As "chaves" nelas são classificadas em ordem alfabética. Desculpe, o firmware MK-161 cuspiu nele.

Por uma questão de compatibilidade, implementei a palavra FIND do Fort ANS [4], que confia no primitivo "black work" (FIND). A palavra já considerada? UNIQUE também está procurando seu argumento (FIND).

6. Intérprete externo


O livro [1] contém uma descrição exaustiva do eForth, incluindo um intérprete externo de "texto". É ele quem executa ou compila o texto fonte na linguagem Fort. As diferenças em relação aos intérpretes textuais de outros dialetos do Forte ([2], [3]) apareceram nas últimas décadas, mas existem poucos.

Abaixo está um diagrama de blocos de um intérprete de texto retirado de [1]. Tenha cuidado - este "intérprete" tem um modo de compilação! A palavra $ COMPILE é responsável por compilar o texto Forte em "código costurado", cuja execução examinamos em detalhes no primeiro artigo. Quando $ INTERPRET é executado, as palavras inseridas são executadas imediatamente - modo de interpretação. EVAL "calcula" toda a cadeia inserida, invocando uma dessas duas palavras para cada palavra inserida.



Após o diagrama de blocos, o autor descriptografa qual dos blocos faz. Aqui está a tradução dela. Os nomes dos blocos geralmente correspondem aos nomes das palavras eForth. A palavra NOME? está ausente na minha implementação, é substituído com sucesso por rápido (FIND) (consulte. 5).

PRINCIPALConfigurar um mecanismo virtual Fort
FRIOInicializar variáveis ​​do sistema
AbortarLiberar pilha de dados. Manipulador de erro
SAIRRedefina a pilha de retorno e insira o loop do interpretador
QUERYAceitar entrada de texto do terminal
EVALCalcular ou interpretar uma sequência de texto
PARSESelecione uma palavra do texto inserido
$ INTERPRETInterpretar a palavra
$ COMPILECompilar palavra
NOME?Pesquise uma palavra em um dicionário
NÚMERO?Converter texto em inteiro
EXECUTARExecutar palavra
IMMED?Esta palavra é um comando imediato?
LiteralCompilar um literal inteiro
COMPILECompilar token


O livro também fornece o código-fonte para cada palavra eForth na versão do Windows, com breves explicações. Qual é a versão do MK-161 diferente, eu já lhe disse. O código fonte da minha implementação está no arquivo: the-hacker.ru/2019/161eforth0.5b.zip

Finalmente, mencionarei a implementação da palavra (PARSE) na linguagem MK-161 - no Windows é VCA. A depuração levou uma semana, mas acelerou a compilação pela metade . A palavra (PARSE) faz todo o "trabalho sujo" do PARSE para isolar palavras individuais do fluxo de texto de entrada.

Minhas adições ao intérprete externo são duas palavras, além do habitual ciclo QUIT: o já mencionado TLOAD e extraído de versões anteriores do FILE. A palavra FILE traduz E / S para o console, mas lê linhas para interpretação da porta RS-232. Após o processamento bem-sucedido de cada linha, uma carta com o código 11. é exibida na porta.O arquivo baixado do computador deve terminar com a palavra QUIT.

Ainda não depurei a palavra FILE. Se alguém precisar, compartilhe suas impressões.

A revisão 161eForth dos pontos difíceis terminou, mas o Fort é uma ferramenta incrivelmente flexível que todo proprietário pode personalizar. Mesmo quando você descobrir tudo, alguém em algum lugar do planeta encontrará outro truque que poderá surpreendê-lo.

Aqui estão as palavras finais do autor eForth de [1]:

Por 26 anos, reescrevi o eForth muitas e muitas vezes. Em todas as dublagens, tentei torná-lo mais simples e claro. Agora, no 86eForth v5.2, acho que atingi a correção e, portanto, estou muito feliz.

Como Einstein disse:
Tudo deve ser feito da maneira mais simples possível, mas não mais simples.

Tornando o 86eForth v5.2 ainda mais fácil, talvez quebrando ou não seja útil como ferramenta de programação.

Literatura


  1. Dr. Chen-Hanson Ting. eForth and Zen - 3rd Edition, 2017. Disponível no Amazon Kindle.
  2. Baranov S.N., Nozdrunov N.R. Linguagem Fort e sua implementação. - L.: Engenharia mecânica. Leningrado Departamento, 1988.
  3. Semenov Yu.A. Programação na linguagem FORT. - M.: Rádio e comunicações, 1991.
  4. ANS Quarto padrão. X3.215-1994. Tradução
  5. Documentação SP-Forth .
  6. Offete Enterprises (Dr. Chen-Hanson Ting) , autor de 86eForth v5.2, está em inglês.
  7. A história de Mikhail Pukhov "True Truth" com o programa "Moonwalker-1", onde eu adquiri o KDPV e amo calculadoras soviéticas.

Source: https://habr.com/ru/post/pt452572/


All Articles