O jonesforth-macintel é uma reimplementação que fiz para Macintosh Intel (32 bits) do jonesforth, que é um interpretador FORTH escrito em Assembly i386 para Linux. O projeto está hospetado no GitHub em github.com/ruda/jonesforth-macintel.
De volta aos anos 80, a maioria dos computadores de 8 bits da época iniciavam com o interpretador BASIC, por ser uma linguagem fácil de aprender. O BASIC entretanto, era uma linguagem muito limita e o uso da linguagem assembly, principalmente dos processadores Z-80 e 6509, era necessário para o desenvolvimento de programas mais interessantes.
Um interpretador FORTH parecia ser mais adequado a esses sistemas de recursos modestos, por estar mais próximo do nível da máquina; mas a verdade é que FORTH nunca pegou nessas computadores de 8 bits. Foi somente nos anos 90, com o OpenFirmware é que FORTH ganhou espaço nas workstations Unix, como as da Sun Microsystems.
Falo de brincadeira aos meus amigos que gostaria de construir uma máquina do tempo e reescrever a história, para fazer com que FORTH conseguisse ser a linguagem padrão das máquinas de 8 bits, removendo os danos causados por aprender BASIC :)
Enquanto isso não for possível de acontecer, eu resolvi aproveitar uma implementação de FORTH simples e de bom entendimento para o meu Macintosh, e esta é a razão do port do jonesforth para o Macintel: celebrar o passado mas pensando no presente.
Compile o interpretador diretamente com o GCC (requer a instalação do XCode) através do comando:
$ gcc -m32 -nostdlib jonesforth-macintel.s -o jonesforth
Para executar o interpretador é necessário incluir as palavras adicionais (WORDs) definidas no jonesforth.f
e também a entrada padrão, necessário para receber os comandos do usuário, como em:
$ cat jonesforth.f - | ./jonesforth JONESFORTH VERSION 47 OK ." Hello World!" Hello World! 1 2 + . 3 BYE
Perdido em FORTH? Eu recomendo ler o artigo (em Inglês) Simple Forth que é um ótimo tutorial sobre a linguagem.
A parte fácil do port é que a arquitetura é a mesma, Intel x86. Seria mais difícil portar para uma arquitetura diferente, como as RISCs PowerPC ou ARM, devido as mudanças de opcodes, registradores e demais idiossincrasias de arquitetura.
O Linux utiliza o GNU assembler, enquanto o Macintosh utiliza um assembler Mach-O, que se assemelha com o GNU Assembler, o que ajudou no processo do port. Tive que ler o manual de ambos os assemblers para entender as semelhantes e diferenças – ainda bem que haviam mais semelhanças.
O processo de port pode ser divido em três grandes frases: a primeira fase foi fazer com que o port compilasse no meu Mac. A segunda fase consistiu em remover as primeiras falhas de segmentações e core dumps básicos. A terceira fase foi o polimento do interpretador, permitindo que ele se comportasse igual a versão Linux.
Em todas as fases do processo eu pude contar com o depurador GDB, que foi a ferramenta importantíssima para que o port tivesse êxito, particularmente gostei do comando x
, que faz um dump ou exame de uma região de memória/registradores e do comando info reg
, que exibe o estado de todos os registradores da máquina.
A diferença fundamental entre a versão Linux e Mac é maneira com que se faz as chamadas de sistema (syscalls). No Linux os parâmetros pra syscall são postos em registradores, e no Macintosh (que segue os BSDs) são postos na pilha. Eu escrevi um um artigo que explica as chamadas de sistema no Macintosh: Chamadas de Sistema (syscalls) no Mac OS X.
Ports relacionados: