A ideia original era que esta série de artigos tivesse apenas duas partes mas conforme escrevia começava a pensar em coisas interessantes que poderiam acrescentar conteúdo mas que ao mesmo tempo, me faziam correr o sério risco de perder o foco principal. Assim resolvi pegar todas as minhas ideias, palpites e algumas promessas e colocá-las aqui em uma espécie de terceira parte de conclusões chocantes e aterradoras!
Os sprites
Os sprites implementados por hardware não são exclusividade do TMS-9918 (MSX, ADAM, TI-99/4 etc), o mesmo recurso está presente no VCS 2600* (TIA), Atari-8bit (ANTIC e CTIA/GTIA, eles dividem a tarefa), Commodore 64 (VIC-II), Amiga (Denise), na série Plus dos Amstrad CPC (fico devendo o nome do integrado) etc… Em todos os casos são uma forma interessante e barata de se contornar o custo e velocidade de acesso das memórias de vídeo. Mas conforme as memórias foram ficando mais baratas e rápidas e os processadores razoavelmente poderosos para colocar objetos na base do AND e OR na tela sem dó nem piedade esta acabou caindo em desuso.
De forma bem simples, imagine que os sprites no MSX são como adesivos de 16×16 (ou 8×8) pontos que podem ser “colados” arbitrariamente sobre a tela sem afetar o que está embaixo. Você pode colar até trinta e dois deles mas desde que se lembre de que só quatro podem estar presentes na mesma linha horizontal — o 9918 não é rápido o suficiente para desenhar mais que isto e ainda fazer as outras tarefas — e que ele sempre irá desenhá-los uma linha abaixo de onde você realmente mandou, ou seja, você indica a linha 15 mas ele aparece na linha 16!
A codificação dos sprites é idêntica à forma como a tabela de padrões, ou seja, cada conjunto de 8 pontos fica codificado em um byte. Nos sprites de 8×8 são usados 8 bytes e 32 bytes nos de 16×16 (que são quatro sprites agrupados). A tabela de padrões ocupa 2KB e fica, em todos os modos de vídeo onde é suportado, entre 14336 e 16385 (&H3800..&H3FFF). Ou seja, dependendo do tamanho utilizado você pode ter 256 ou 64 sprites diferentes e que, em 16×16, ficarão agrupados da seguinte forma (para demonstrar uma ajudinha do indestrutível Cavaleiro Negro):
E que corresponderiam, quando em 8×8, aos seguintes sprites:
Os sprites também tem sua tabela de atributos e que dizem respeito ao posicionamento de cada na tela, cor e outros parâmetros. Ela começa em 6912 e é dividida em 32 blocos de 4 bytes, ou seja 128 bytes, (de &H1B00..&H1B7F) e como dá para perceber há pouco mais de 1KB sobrando aqui mas não é comendável usar pois nos MSX2 há outras informações guardadas nesta área. E cada bloco contém o seguinte:
- byte 0 — A linha onde está o sprite (0 a 255);
- byte 1 — A coluna onde está o sprite (0 a 255);
- byte 2 — Qual dos sprites (0 a 255, quando os sprites são de 16×16 os bits 0 e 1 são ignorados — em “microprocessês” significa dividir por quatro) e
- byte 3 — Os bits de 0 a 3 definem a cor do sprite (somente uma cor para ele todo), os bits de 4 a 6 não são usados e o bit 7 serve para deslocar o sprite 32 pontos para a esquerda.
Confesso para vocês que não sou lá muito fã do uso de sprites, aliás considero que alguns jogos de MSX foram destruídos pela insistência dos seus desenvolvedores em utilizá-los (tudo bem, o comentário é polêmico) mas para breve eu prometo apresentar algumas brincadeiras que podem ser feitas com os sprites (além de naves voando pela tela, claro).
(*) Minha única concessão aos consoles, aliás vale a mena ler sobre como é a geração dos sprites — e o nome que eles recebem — tanto no VCS 2600 quanto nos outros equipamentos da Atari.
VRAM “quase” linear
Na primeira parte eu toquei no assunto da não linearidade da VRAM, de como ela deixa complicado o cálculo da posição de um simples ponto na tela e que ela poderia ter uma linearidade simulada. A dica é simples, basta (re)organizar a tabela de nomes para atender as suas necessidades e deixar o resto com o 9918. Este aqui é um exemplo bem simples e que se aproveita da (baixa) velocidade do MSX-BASIC para criar a ilusão de movimento:
100 KEY OFF:COLOR 15,0,0 105 SCREEN 1,0:VPOKE 8192,0 110 FOR I%=0 TO 7:VPOKE I%,240 115 READ J%:POKE &HD000+I%,J% 120 VPOKE 8+I%,15:NEXT I% 125 DATA &HEF,&HAF,&H9F,&H8F,&H6F,&H8F,&H9F,&HAF 130 FOR Y%=0 TO 23 STEP 2 135 FOR X%=0 TO 31 STEP 2 140 VPOKE 6144+Y%*32+X%,0:VPOKE 6144+Y%*32+X%+1,1 145 VPOKE 6144+Y%*32+32+X%,1:VPOKE 6144+Y%*32+33+X%,0 150 NEXT X%,Y%:I%=0 155 VPOKE I%,VPEEK(I%)XOR255:VPOKE 8+I%,VPEEK(8+I%)XOR255 160 I%=(I%+1)AND7:TIME=0 165 IF I%=4 THEN VPOKE 6144+767*RND(1),32+64*RND(1) 170 VPOKE 8192,PEEK(&HD000+I%) 175 IF TIME<2 THEN 175 ELSE 155
Scroll horizontal, como no MSX2, e direto em MSX-BASIC! O que eu fiz foi desenhar uma padrão de grade com os caracteres ASCII 0 e 1 na tela, o resto é o processo de invertê-los (XOR 255) sequencialmente. Vale lembrar que a animação de toda a tela é feita mexendo 16 bytes um de cada vez.
Textos e gráficos (quase) simultâneos
Pode isso? Sim, claro que pode! Ainda mais se nos lembrarmos que o que temos são três telas empilhadas, que a SCREEN 1 e a SCREEN 2 são a mesma coisa e que o bobo na história é o MSX-BASIC que não ficará sabendo de nada (atenção povo do copiar e colar com a linha 165):
100 COLOR 15,0,0:KEY OFF:SCREEN 2:SCREEN 1:WIDTH 32 105 '0-PREPARAR O VDP 110 VDP(0)=VDP(0)OR2:VDP(1)=VDP(1)AND&HE7:VDP(3)=&HFF:VDP(4)=&H3 115 '1-PREPARANDO UM MODO TEXTO BONITO 120 FOR I%=256 TO 1023:J%=VPEEK(I%):VPOKE I%,J%OR(J%\2) 125 IF (I%AND7)>3 THEN VPOKE 8192+I%,&HE0 ELSE VPOKE 8192+I%,&HF0 130 NEXT I% 135 '2-PREPARANDO OS OUTROS DOIS TERCOS DE TELA 140 FOR I%=0 TO 255:VPOKE 6400+I%,I%:VPOKE 6656+I%,I%:NEXT I% 145 '3-ENGANANDO O MSX-BASIC 150 POKE &HF3B1,8 'APENAS OITO LINHAS! 155 FOR I%=0 TO 255 STEP 5:J%=16*RND(1) 160 POKE &HFCAF,1 'SCREEN 1 165 IF J%=0 THEN PRINT ELSE FOR K%=0 TO 3*RND(1):PRINT CHR$(32+80* RND(1));:NEXT K% 170 POKE &HFCAF,2 'SCREEN 2 175 LINE (0,64)-STEP(I%,127),J% 180 LINE (0,191)-STEP(I%,-127),J% 185 LINE (255,64)-STEP(-I%,127),J% 190 LINE (255,191)-STEP(-I%,-127),J% 195 NEXT I%:IF INKEY$<>CHR$(27) THEN 155 200 POKE &HF3B1,24:POKE &HFCAF,1:LOCATE ,0:END
A mágica fica por conta da variável de ambiente SCRMOD (&HFCAF) que guarda o modo de vídeo atualmente em uso. Se o valor é 1 o PRINT funciona, se o valor é 2 será a vez do LINE, basta deixar tudo combinado e acertado com o 9918 e não contar nada pro MSX-BASIC. O resultado final ficará mais ou menos assim… (use <ESC> para sair de forma civilizada ou sofrerá as consequências).
SCREEN2 “monocromática”
Sério? Conhecem o ditado: “o que serve pra Chico, serve pra Francisco”? Logo, se eu posso colorir a SCREEN 1 basta reverter a lógica para “descolorir” a SCREEN 2. E funciona! Tudo bem que no final ela não fica assim tão monocromática já que a tabela de atributos terá 64 bytes e espelhada por toda a tela — é até bom pois dá para fazer alguma(s) arte(s). Aliás, com tanta VRAM sobrando dá para habilitar uma segunda página de vídeo. Duvidam? Basta mexer um pouco e a nova organização da VRAM ficará assim (em hexa, pois é mais fácil):
&H0000..&H17FF -- Tabela de padrões (página 0, a visível) &H1800..&H1AFF -- Tabela de nomes &H1B00..&H1FFF -- Tabela de atributos dos sprites (128 bytes) &H2000..&H203F -- Tabela de atributos (64 bytes) &H2040..&H27FF -- Tabela de padrões dos sprites &H2800..&H3FFF -- Tabela de padrões (página 1, a invisível)
Ou seja, há duas páginas de vídeo, uma no início e outra no fim da VRAM. O resto fica espremido entre alas. É claro que nem tudo é perfeito, a tabela de atributos e a padrões dos sprites estão se atropelando e assim perdem-se dois sprites (os de número 0 e 1) dos 32 possíveis em 16×16 (obviamente são 8 a menos com os de 8×8). Outra coisa é que, a não ser que você tenha feito suas próprias rotinas, não dá para utilizar os comandos de desenho (PSET, PRESET, LINE, CIRCLE, DRAW e PAINT) pois eles destruirão o conteúdo da segunda página (lembra que enganamos o MSX-BASIC? Aqui ele dá o troco).
Precisamos, assim, de três rotinas que eu resolvi fazer em assembly. Uma para alternar entre as duas páginas (na verdade troca dinamicamente o conteúdo entre elas), outra que as inicialize (zere) corretamente liberando para uso e algo para se trocar a cor da tela**. O programa eu chamei de SCR2MONO.BAS, basta carregá-lo e executá-lo.
- As setas <←> e <→> para mudar a cor da frente;
- As setas <↑> e <↓> para mudar a cor do fundo;
- As teclas < , > e < . >para alterar a cor da borda;
- A tecla <SELECT> para trocar entre as páginas de vídeo e
- A tecla <ESC> para retornar ao BASIC.
Claro que a minha rotina poderia ser otimizada mas é só para dar uma ideia de como a coisa funciona. Assim como fiz na segunda parte, atualizei tanto a imagem de disco quanto o arquivo compactado com este e os demais programas desta parte.
(**) Na verdade você pode usar o comando SPRITE$ para trocar a cor da tela? Por exemplo, para fazer um equivalente do “COLOR 15,4” use “SPRITE$(0)=STRING$(32,&HF4):SPRITE$(1)=STRING$(32,&hF4)”.
Finalizando
E assim termino esta série cumprindo as promessas das partes anteriores e devendo algo específico quanto aos sprites para uma próxima oportunidade (mas já adianto que não haverá uma parte 4, será algo novo). Espero que tenham gostado, que tenha servido para “dar ideias” (o mais importante) e fazer alguém desencavar algum projeto engavetado ou começar um novo. Um agradecimento final e mais que especial ao fudeba Felipe Bergo pela seu excelente “Fudeba Assembler : Manual de Referência da Arquitetura MSX“, literalmente o meu livro de cabeceira durante a confecção destes três artigos e de quase tudo que eu faço para MSX, ou quase….
Até a próxima!
Giovanni, fico muito grato pelo artigo.
Muito bem escrito, didático e ao mesmo tempo convidativo à descoberta por experimentação.
Inclusive com uso de MSX Basic para a maioria dos exemplos. Aprende-se no Basic e depois porta-se para a linguagem de sua preferência.
Mais um trunfo do MSX!
Com isto creio que todo “fudeba wannabe” (eu!) é encorajado a prosseguir.
Valeu!
Eu é que agradeço, na verdade o objetivo é realmente este. O de estimular/incitar as pessoas a fuçarem um pouco. E não se esqueça que desta vez eu liberei, também, um “programeto” em assembly!
realmente muito bom , meus parabéns pelo artigo e pelo seu aniversário (pronto falei!) 8D