Survive — Os sprites e o MSX

survive-4_linha-208

Não se empolgue com esta imagem introdutória toda colorida, esta parte é quase uma síntese das minhas desventuras tentando entender corretamente como funciona na tabela de atributos o posicionamento dos sprites no MSX e consequentemente nos MSX2, MSX2+ e MSX turbo R.

Então, sejam bem vindos à publicação mais chata do universo…

O que é um sprite?

É algo que carece alguma explicação, de forma bem simples os sprites (no caso os gerados por hardware pois também é possível tê-los em software) são um recurso do circuito de vídeo que permite desenhar pequenos bitmaps sobre a tela sem que estejam sobrescrevendo diretamente o conteúdo daquela posição na memória do vídeo — daí o nome de sprite (espírito). Como estão à parte do conteúdo da tela podem ser movidos facilmente sem a preocupação de preservar e recolocar a área por eles sobreposta. Lindo, não?

Mais ou menos, os sprites não são resposta para tudo eles tem limitações quanto ao número, quantidade exibida na tela, dimensões e até cores. Especificamente no caso do processador de vídeo do MSX, o TMS9928¹, os valores são 256 ou 64 padrões de sprites, apenas 32 exibidos na tela (somente 4 na mesma linha), tamanho de 8×8 ou 16×16 (podendo ser duplicados nos eixos horizontal e vertical) e monocromáticos (bit 1 = cor, bit 0 = transparente).

Sendo repetitivo, para não ser mais repetitivo, um pouco mais sobre os sprites no MSX pode ser encontrado na terceira parte do meu “MSX: As sete faces da SCREEN 1” lá no Retrocomputaria.

(¹) Quando uso TMS9928 faço referência direta aos TMS9118, TMS9128, TMS9129, TMS9918A, TMS9928A e TMS9929A da Texas Instruments e ao T6950 da Toshiba. E indireta aos V9938 e V9958 da Yamaha e alguns dos 315-5XXX da SEGA.

Os sprites na teoria

No MSX-BASIC o comando para definir um sprite é:

SPRITE$(«número do sprite»)="«string de até 32 caracteres»"

Para desenhá-lo na tela é:

PUT SPRITE «camada»,(«coluna»,«linha»)[,«cor»][,«número do sprite»]

O que ambos fazem é (re)escrever o conteúdos ou parâmetros passados aos comandos nas tabelas de padrões e de atributos dos sprites da VRAM, respectivamente.

A primeira tabela, a de padrões, ocupa uma área de 2KiB, começando geralmente em 14.336 (0x3800) e, de acordo com o tamanho dos sprites (8×8 ou 16>16), ficando organizada logicamente em blocos de 8 ou 32 bytes que correspondem ao número do sprite.

A tabela de atributos fica armazenada em uma área bem pequena, de 128 bytes (32 planos × 4 bytes), geralmente começando no endereço 6.912 (0x1B00) e tem uma estrutura até sofisticada:

Endereço Posição Y Posição X Padrão Cor
6912 byte byte byte byte
6916 byte byte byte byte
7032 byte byte byte byte
7036 byte byte byte byte

No primeiro byte está a posição vertical, no segundo a posição horizontal, no terceiro está o padrão utilizado (em sprites com tamanho de 8×8 a correlação é direta com o valor usado em SPRITE$() mas em16×16 o VDP desconsidera os dois primeiros bits e trata tudo como múltiplos de 4; o MSX-BASIC “conserta” multiplicando por 4 o valor passado no comando — isto é você “diz” sprite 2 e o VDP “escuta” que é o sprite 8.

Na quarta posição da tabela fica armazenada a cor do sprite que vai de 0 até 15 (os bits de 3 até 0). Dos bits restantes o bit 7 é utilizado para indicar se o desenho do padrão ocorrerá 32 pixeis à esquerda do valor indicado na posição horizontal, este sujeito é chamado de Early Clock.

Até agora tudo tranquilo, não? Mais ou menos.

Os sprites na prática

survive-4_sprites.png

Explicação rápida, nos dois primeiros VPOKE “transformei” o caractere ASCII 0 (o NULL) em uma linha negra; defini o sprite de número 0 como uma caixa de 16×16, coloquei três cópias dele na tela em vermelho, verde e amarelo; digitei NULL 32 vezes na tela — no MSX-BASIC você usa a combinação «Ctrl»+«A» e «@» — e, por último, exibi a área da VRAM referente aos atributos dos três sprites. Ufa!

Posicionamento vertical

Repare que quando você posiciona um sprite na tela ele aparece na linha seguinte a que foi indicada — veja que na figura os três sprites deveriam estar sobre a linha e não logo  abaixo dela.

Sobre este assunto a Texas Instruments diz o seguinte em TMS9900 : TMS9918A / TMS9928A / TMS9929A Video Display Processors – Data Manual (nov/1982) — o trecho sublinhado é por minha conta:

The first byte indicates the vertical distance of the sprite from the top of the screen, in pixels. It is defined such that a value of -1 put the sprite butted up at the top of the screen, touching the backdrop area. (…)

Ou seja, fingiram que não há nada de errado e em uma homenagem póstuma a René Descartes redefiniram o plano cartesiano! 😀

Já que o próprio VDP acredita que eles estão realmente na linha 87 (0x57 em hexadecimal) quando efetivamente não estão só resta lembrar que graças a este bug feature deve-se fazer um “menos 1” na posição vertical antes de desenhar o sprite na tela.

Mais posicionamento vertical

Tem mais! Os valores verticais válidos vão de 0 até 255, sendo que  de 0 até 191 — que na prática é de -1 a 190 — correspondem a área visível na tela, de 192 até 223 é a parte inferior da borda e de 224 a 255 a parte superior. Dentro deste conjunto de valores um deles, o 208, foi escolhido para ser uma “chave” que diz ao VDP para interromper imediatamente a rotina de desenho dos sprites — pensando em C ele é como um break colocado em um laço.

Para visualizar melhor como funciona, teste este sujeito em MSX-BASIC aqui:

O que ele faz é desenhar 24 sprites na tela e colocar um de cada vez, da camada 23 até a 0, na linha 208.

Posicionamento horizontal

Considerando que um byte armazena 256 valores — sem discordância de que entre 0 e 255 há 256 valores, ok? — e que corresponde exatamente a resolução horizontal da tela, como fazer para que um sprite desapareça gradativamente pelos cantos direito e esquerdo?

No lado direito é “simples” pois o que não coube na tela será ocultado pela borda. Porém, e este é o motivo das aspas, é que para o sprite desaparecer completamente no lado direito ele precisaria ser colocado na 257ª posição da tela, ou seja, em 256.

Como só tenho 1 byte isto significa que 0xFF+0x01→0x00 e o sprite aparecendo por completo do outro lado. No lado esquerdo acontece justamente o inverso, assim que o sprite alcança a borda ele simplesmente irá reaparecer gradativamente do lado direito!

Todos estes [d]efeitos podem ser vistos  na minha primeira rotina (em câmera lenta):

Resumindo, utilizando toda a largura da tela e desejando que os sprites comportem-se de modo natural quando próximos das bordas fica difícil usar somente um byte para posicionamento horizontal. Logo, não dá para correlacionar diretamente a variável contendo a posição do onjeto e o sprite na VRAM.

Refazendo o PUT SPRITE

Ou seja, o MSX tem resolução de 256×192 enquanto que os sprites estão em uma “tela virtual estranhamente não linear e com um abismo” de 320×256. Especificamente no caso do Survive as peculiaridades do posicionamento vertical não são problema (ufa!), mas o mesmo não pode ser dito do horizontal. Solução? Simular o funcionamento do PUT SPRITE!

Justamente, escrever uma rotina que se comporte exatamente como o comando do MSX-BASIC que ao serem passados os valores de  X, Y, Cor e Sprite faça:

  1. Se for possível, corrija Y fazendo um “menos 1”;
  2. Se X for menor que 0 acrescente 32 a X e ligue o bit Early Clock em Cor e
  3. Com sprites de 16×16 multiplique o valor do Sprite por 4.

Mas antes de me torturar em assembly de Z80 preferi fazê-la primeiro em C e comparar seu funcionamento:

Hora de verificar se ficou igual — com FIX_Y desligado para ficar igual ao MSX-BASIC:

$ gcc -o put_sprite putsprite.c
$ ./put_sprite 
6912 : 57 70 00 08
6916 : 57 18 00 82
6920 : 57 F8 00 0A
6924 : 00 00 00 0F

Este quarto sprite serve para verificar se a rotina realmente funciona tal qual o PUT SPRITE, experimente para ver o que acontece em um MSX real. 🙂

Fim da quarta parte

Não dissera que seria chato? Aliás, para que a necessidade de tanta precisão no posicionamento dos sprites? Isto tem relação com um recurso útil disponível quando se trabalha com eles: a detecção de colisão. Isto é,  o VDP sabe quando dois sprites se sobrepõe e pode avisar que está acontecendo — claro que , para ajudar, ele não cita os envolvidos.

Até a próxima!

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s