quinta-feira, 23 de julho de 2020

Matrix - Raining Code para o ZX81

Portei  para o ZX81(linha que no Brasil foi representada pelo TK-85, CP-200, etc) o programa que havia escrito para o MSX. Uma boa parte teve que ser reescrita, principalmente porque na versão para MSX eu utilizei o modo gráfico ("screen 2"). O algoritmo de animação, de um modo geral, é o mesmo, respeitadas as limitações da maquininha.

Para inciar o programa, digite RUN. Para interromper a execução, tecle NEW LINE.

Download: matrix.p





terça-feira, 14 de julho de 2020

Blackjack para o ZX81

A ideia


Ok, eu sei, "mais um blackjack para o ZX81", mas enfim... A ideia era fazer o programa em BASIC e apenas inserir umas rotinas em assembler para deixar mais bonito e fluído. E, o mais importante, aprender com o processo.

E nesta minha primeira aventura em programar para o ZX81 (no Brasil, TK-85, CP-200, etc), realmente aprendi bastante sobre essa maquininha, suas limitações práticas de hardware, a estrutura da memória, o uso da pilha de cálculo e até as limitações, ao programador, no uso do Z80 (que comento mais ao final).

Por razões óbvias, abandonei minha ideia inicial (besta) de fazer tudo direto no emulador do ZX81, utilizando o MSX para montar as rotinas em linguagem de máquina. Acabei usando o Pasmo como assembler, e o Appmake (do pacote z88dk) para gerar o arquivo ".p".  Depois eu integro ao resto do programa BASIC usando um ótimo utilitário do próprio  ZX81 (Toolkit, da Artic).

O processo


Fiz questão de completar todo o programa em BASIC mesmo, até para conhecer as peculiaridades do Sinclair BASIC, pegar intimidade com a máquina e ver até onde chegavam as otimizações. Quando não tinha mais jeito, passei a resolver com assembler.

Pra quem não conhece o ZX81 e seus clones da vida (TK82, TK83, TK85 e CP-200 aqui no Brasil), pode soar estranho utilizar rotinas em código de máquina para um jogo tão simples. Acontece que as limitações do Sinclair BASIC e o do próprio hardware exigem que partes do programa, que rodariam tranquilamente em qualquer BASIC interpretado da época, devam ser feitas em linguagem de máquina no ZX81.

Assim, desde o início, percebi que a rotina de embaralhamento das cartas e, para minha surpresa, a atualização dos valores do "caixa" e da "aposta" na tela, teriam que ser feitas em linguagem de máquina.

Uma rotina de embaralhamento adequada simula um baralho real e permite ao jogador contar cartas durante as partidas, o que dá uma profundidade extra ao gameplay. A solução em linguagem de máquina foi bastante simples e a sua execução é praticamente instantânea.

Agora, a atualização dos valores de aposta e caixa me deu algum trabalho, quero dizer, muito mais pelo estudo que demandou do que pela solução em si. Pensei inicialmente que o problema estava no cálculo dos valores em BASIC, mas não. O que acontece é que, para imprimir um número na tela, o computador converte o valor em uma string de caracteres. E é essa conversão, feita pela ROM do ZX81, que leva uma eternidade. Então tive que fazer uma rotina própria de conversão, capturando os valores das variáveis do BASIC, para depois imprimir o resultado diretamente na tela. Como resultado, a rotina ficou bastante rápida. Nesse aprendizado, percorri todos os livros que dispunha (físicos e virtuais), mas foi o Mario Schaefer (Usando Linguagem de Máquina - Aplicações em Assembly Z80) quem me deu a dica, mas não a solução. Esta, eu fui encontrar apenas nos rolos de pergaminho de Toni Baker.

O resto foi trivial, tratando mais da parte cosmética, além de otimizações menores.

O que é executado em linguagem de máquina


  • A limpeza de tela (tanto em fundo preto, como em fundo branco). A rotina do CLS do BASIC é lenta demais, na minha opinião;
  • A impressão e a animação de parte da tela inicial;
  • O embaralhamento das cartas (como num baralho real, utilizando o algoritmo knuth shuffle) e a animação das cartas na tela; 
  • A decodificação do valor e naipe das cartas para o BASIC ;
  • A atualização dos valores do caixa e aposta na tela;
  • A impressão das cartas. Eu até gostava da velocidade de impressão das cartas no BASIC, que conferiam uma certa dramaticidade, mas no final achei que não estava mais combinando com o ritmo do resto do jogo;
  • A leitura de teclas dentro da rotina de aposta, que possibilitou um processamento mais rápido dos valores retornados no BASIC;
  • A tela final do jogo, quando o  jogador "quebra a banca" ao ganhar mais de $ 30.000.


O que aprendi nessa aventura


  • Toda a estrutura de memória do ZX81;
  • A estrutura das linhas do BASIC na memória;
  • Local e forma de armazenamento de variáveis na memória;
  • A utilização da pilha de cálculo e rotinas de cálculo do ZX81 em linguagem de máquina;

Aprendi também que "coisas terríveis irão acontecer" (crash) na execução de uma rotina em linguagem de máquina no ZX81 se você:

  • Utilizar EX AF,AF';
  • Não devolver a pilha de cálculo na mesma posição que a encontrou no início da rotina LM;
  • Ao voltar ao BASIC, não devolver HL' com o mesmo valor que entrou na rotina em LM;
  • Num cross-assembler, inserir dados em ASCII e mandar imprimir no ZX81. Isto é óbvio, mas como sou distraído, gastei uns bons minutos debugando a rotina em assembler para chegar à conclusão que ela, em si, estava correta. O problema eram os dados em ASCII, que devem ser convertidos previamente para os valores utilizados pelo ZX81.

Agradecimentos


Agradeço a Sandro Lemos pelo apoio moral, dicas de onde encontrar utilitários para o ZX81 e pelo beta-testing. Agradeço igualmente ao Daniel Afarelli, também pelo apoio moral e pelo beta-testing.





Download: blackjack.p

Relógio BCD para o TK85 no ZX Assembler da Artic

Um relógio BCD (binary-coded decimal) mostra os dígitos decimais em formato binário. Cada dígito decimal das horas, minutos e segundos está ...