quarta-feira, 1 de abril de 2026

E uma bruxa passou pela minha janela

 

Um tanto atrasado para o Halloween de 2025, um tanto adiantado para o Halloween de 2026. Tenho tido pouco tempo para programar, então o desenvolvimento dos projetinhos tem sido mais lento. Porque caótico, sempre foi mesmo. Além disso, juntar forças para organizar, documentar e descrever o processo aqui no blog também é uma mão de obra a parte. Mas, de qualquer forma, é sempre uma satisfação quando o publico o registro da peripécias por aqui. Então vamos lá.

Sempre quis trabalhar com a ideia de animação suave no ZX81, então me propus a fazer um programa com as seguintes características:

  • a animação de uma bruxa voando de vassoura pelo céu, em movimento horizontal, da perspectiva de quem a estivesse vendo por uma janela;
  • a animação deveria ser o mais suave possível, dentro das limitações do computador. Então deveria ser pixel por pixel, e não caractere por caractere;
  • a bruxa deveria surgir aos poucos de um lado da tela, e desaparecer aos poucos do outro lado da tela;
  • a bruxa deveria voar em velocidades diferentes, escolhidas aleatoriamente a cada passagem;
  • a bruxa deveria voar em direções diferentes (esquerda para a direita ou direita para a esquerda), escolhidas aleatoriamente a cada passagem;
  • a bruxa deveria voar em alturas diferentes na janela, escolhidas aleatoriamente a cada passagem.

Primeiramente, fui achar uma pixel art de uma "bruxa-de-vassoura" em baixa resolução, em um tamanho bacana para a resolução do ZX81.

Depois que encontrei, copiei a figura ponto por ponto, utilizando uma ferramenta rústica para desenhar que fiz no Sinclair BASIC, utilizando os comandos PLOT e UNPLOT. Com ela, ao terminar o desenho, posso transferir a tela toda para uma variável (sim, o ZX81 permite isso) e assim salvar o desenho. Há também uma rotina para enviar a tela para a RAMTOP, o que permite utilizar o desenho em outro programa.

Para implementar a ideia de movimento ponto por ponto e não caractere por caractere, desenhei 4 sprites da bruxa:  dois com ela na direção esquerda para a direita, e mais dois com ela da direita para a esquerda. Isso porque a tela do ZX81 não é realmente dividida em pixels, mas em caracteres, cada um podendo representar 4 pixels (acesos ou apagados). Então o desenho que emula a bruxa um pixel à direita ou um pixel à esquerda é formado por um conjunto de caracteres gráficos completamente diferente. O quadro abaixo, com o exemplo de uma figura bem simples, de 3x2 caracteres, ilustra melhor o que estou dizendo:

 

A animação, em assembly, eu quis fazer utilizando ferramenta nativa, então usei o ZX Assembler. Entretanto, como passaria os dados dos desenhos para o programa-fonte a ser editado no assembler? A solução for fazer um patch do programa-fonte do ZX Assembler, inserindo os dados dos desenhos diretamente na área de memória onde se encontrava o programa-fonte, com os seguintes passos:

  1. Primeiro, no editor do ZX Assembler, inseri linhas de dados no programa-fonte, para criar espaço e salvei;
  2. Depois baixei a RAMTOP para 30000 e carreguei o programa com o desenho; 
  3. A seguir, utilizei uma rotina (que já estava pronta no programa para desenhar) e passei a tela do desenho para a RAMTOP;
  4. Carreguei o programa-fonte do ZX-Assembler e apaguei a linha 1, que contém o código-objeto gerado pelo assembler. Assim poderia descobrir com mais facilidade os endereços dos dados no programa-fonte do assembler;
  5. Com o programa-fonte na memória, programei uma rotina para descobrir, com PEEKs, o endereço inicial e final de cada linha de dados;
  6. Anotei o endereço inicial dos dados da primeira linha;
  7. Anotei quantos bytes deveria pular para recomeçar a inserir os dados a cada nova linha do programa-fonte;
  8. Com essas informações anotadas, escrevi um programinha em BASIC que passasse cada linha do desenho de cada bruxa (com 14 bytes por linha), que estava na RAMTOP, para cada linha de dados que eu havia reservado, pulando, evidentemente, os bytes de formatação do programa-fonte.

O resultado foi esse, na listagem do programa fonte dentro do editor do ZX Assembler: 

 

A seguir, programei a animação. Conforme planejado, alternei 2 desenhos da bruxa em cada direção para emular que ela se dava ponto-por-ponto antes de passar para a posição na tela. 

Enfim, quando já via a bruxa voando pela tela, naquele momento achei que estava faltando algo na animação. Então defini mais alguns elementos a serem implementados:

  • Que deveria haver estrelas no céu, representadas por asteriscos;
  • Que as estrelas apareceriam em posições aleatórias no céu;
  • Que as estrelas, obviamente, deveriam parecer estar em um plano mais afastado em relação ao plano da bruxa (a bruxa passando em frente às estrelas).

Então escrevi mais essa parte do programa até atingir o resultado desejado. Apesar de o sprite da bruxa ser, na realidade, branco (a bruxa) sobre fundo preto, o resultado final o sprite da bruxa parece ter fundo transparente, como num sprite por hardware. 

Animação finalizada, faltava ainda uma mensagem "Happy Halloween" alternando com as animações. Fiz como transição um efeito "veneziana", se abrindo, ou se fechando, para ficar coerente com a ideia da janela. Depois desenhei, na munheca, a mensagem de "Happy Halloween!!!" junto a uma lanterna de abóbora (jack-'o-lantern), que fiz "piscar" algumas vezes, invertendo as cores da tela, antes de usar novamente o efeito de transição e reiniciar a animação. O envio dos dados da tela com a mensagem foi feito da mesma maneira que procedi com as figuras da bruxa, com a diferença de que eram bem mais dados a serem transferidos ao código-fonte do montador (420 bytes).

Nessa aventura, um dos meus objetivos era testar os limites práticos, em termos de memória, do ZX Assembler. De fato, nas últimas montagens do programa, recebi mensagem de que a memória estava cheia. Na memória RAM de 16kB do ZX81 ficam o montador (ZX Assembler), o código-fonte do usuário (que reside na linha 2 do BASIC), e o código-objeto do usuário (que reside na linha 1 do BASIC), além de outras linhas de programa BASIC eventualmente utilizadas. Após a mensagem de "memória cheia", tive que fazer alguns ajustes, cortando desperdícios em linhas de comentários e fazendo algumas otimizações para reduzir o tamanho do programa.

Na versão final, ainda programei os créditos que aparecem no início da execução do programa. Não sobraram muitos bytes depois disso.

O "artesanato" dá trabalho, mas ver o resultado na tela é impagável, principalmente na de tubo. No vídeo abaixo é possível conferir como ficou, rodando no TK85: 


 O download do programa está disponível aqui.

 

E uma bruxa passou pela minha janela

  Um tanto atrasado para o Halloween de 2025, um tanto adiantado para o Halloween de 2026. Tenho tido pouco tempo para programar, então o de...