Entendendo a SCREEN4, parte 1

Olhando a documentação técnica do MSX2, parece que a resolução gráfica SCREEN4 (ou GRAPHICS3 no manual técnico do Yamaha V9938) é precariamente suportada pelos desenvolvedores do hardware e do software em geral. Pelo lado de hardware, a resolução não tem suporte a diversas funções de aceleração gráfica do V9938, como acesso ao blitter. Pelo lado do software, ela tem por padrão uma organização da memória mal planejada (com overlap de diversas tabelas distintas do VDP), escolhas estranhas (resolução de 192 pixels na vertical quando o sistema consegue oferecer 212) e falta de suporte a páginas de VRAM adicionais pelo BIOS.

Diante de tanta bizarrice, a velha limitação técnica do attribute clash herdado da SCREEN2 do MSX, que obriga grupos de 1×8 pontos a ter apenas duas cores, parece ser a menor delas. Até porque, por conta dessa economia de memória, ela é rápida o suficiente para realizar scroll horizontal por força bruta se necessário. Não é a toa que um dos mais celebrados jogos do MSX2, o Space Manbow, usa esta resolução.

O objetivo da semana era portar a biblioteca ubox MSX lib para usar a SCREEN4. Qual seria a dificuldade afinal? Sendo a SCREEN4 tão parecida com a SCREEN2, não é mesmo? Seria o melhor dos dois mundos: a velocidade da SCREEN2 com as funções de scroll e sprites do MSX2. Easy peasy lemon squeezy! Só que eu ainda tinha minhas dúvidas por conta da falta de suporte às diversas coisas que o V9938 tem a oferecer, mas o Parn confirmou que o scroll vertical não era uma delas. Verificando rapidamente com um pouco de código em BASIC, fui tratar de aprender mais a fundo como funciona esta resolução.

Para um apanhado geral sobre o funcionamento dos modos de vídeo do MSX, eu aconselho este ótimo artigo do Giovanni, pois aqui eu vou me limitar mais aos poucos avanços que o V9938 oferece à SCREEN4 bem como suas peculiaridades. Talvez mais tarde eu fale sobre a SCREEN2, pois eu andei brincando de suportar modos gráficos em Forth anteriormente e ainda pretendo falar mais sobre isso no futuro. 🤓

Para começar, a tela gráfica SCREEN4 parece dispor da mesma organização de memória que a SCREEN2: 3 tabelas de padrões (os desenho dos caracteres), 3 tabelas de nomes (os índice dos caracteres das tabelas anteriores), 3 tabelas de cores dos padrões (as cores de frente e fundo dos caracteres, por linha), a tabela de padrões de sprites (os desenho dos sprites) e a tabela de atributos de sprites (que reúne índice do sprite e suas coordenadas).

figura 1: organização da memória da SCREEN2.

Mas não é bem assim devido a uma grande novidade: o V9938 traz o registrador #23 que controla o scroll vertical da tela e pra isso acontecer é preciso ter… mais tela! O que isso quer dizer é que a tela é apenas uma janelinha que exibe um pedaço de uma área maior. Uma área maior significa mais memória. Antes, as três tabelas de padrões do MSX1 permitiam cobrir a tela inteira com padrões, pois 8 pontos por caractere × 8 colunas de caracteres × 3 tabelas de padrões = 192 pontos. Mas com a VRAM extra e o registrador de scroll vertical, o MSX2 reserva 256 pontos verticais para a tela inteira, então a resolução vertical máxima de 192 ou 212 é apenas uma janela que exibe uma parte desses 256 pontos verticais, deixando ou 64 ou 44 pontos de fora.

Pra preencher o espaço extra, o V9938 traz as seguintes novidades: uma tabela adicional de padrões, uma tabela adicional de nomes e uma tabela adicional de cores. Na SCREEN4, isso significa que são 4 tabelas de padrões, 4 de nomes e 4 de cores (8 pontos por caractere × 8 colunas de caracteres × 4 tabelas de padrões = 256 pontos). Então vamos colocar isso tudo num mapa da memória?

Isso é o que aparece por default quando escolhemos a SCREEN4 e já de cara vemos um problema: considerando que existem mais tabelas para representar a tela e as tabelas precisam ser contínuas, há agora um overlap entre três tabelas. A quarta parte da tabela de padrões preenche todo o espaço que antes era destinado para a tabela de nomes e de atributos de sprites, até encostar na tabela de cores. A tabela de nomes também cresceu e cobriu a tabela de atributos de sprites. Fora isso, a tabela de cores agora cobre a tabela de padrões de sprites. São quatro tabelas se sobrepondo porque essas áreas de memória foram configuradas para serem compatíveis com a SCREEN2 e sua memória reduzida de 16k, o que quer dizer que tentar exibir o conteúdo do 1/4 restante da tela resultará em um monte de lixo inutilizável. Pior que isso, tentar escrever nessa região acarretará no aparecimento de caracteres aleatórios por toda tela, pois a tabela de nomes inteira esta inserida na mesma região. Além de sprites aleatórios aparecendo por todo lugar, ótimo para a criação de glitch art. 😅

Felizmente esse problema é fácil de resolver, pois o V9938 dispõe de registradores especiais que permitem mudar a região da memória que essas tabelas usam. Com 128k de RAM dá pra espalhar bem essas tabelas e poderíamos até criar várias páginas distintas para a SCREEN4 usando apenas o registrador R#2. Vamos começar com algo simples em BASIC mesmo:

10 SCREEN 4:COLOR 15,1,4
20 REM Ajuste das tabelas de VRAM
21 VDP(2)=&H10
22 VDP(3)=&HFF
23 VDP(11)=&H0
24 VDP(4)=&H3
25 VDP(5)=&H8F
26 VDP(12)=&H0
27 VDP(6)=&H9

Os comandos VDP() em modo escrita nos permite alterar valores dos registradores do VDP. Nenhum efeito muito visível ainda, mas isso é apenas para ajeitar as tabelas, removendo os overlaps. Uma ótima forma de futucar com esses valores manualmente é instalar o depurador do OpenMSX e selecionar a opção VDP registers view enquanto estiver na resolução correta e clicar nos bits da tabela Base Address Registers. A tabela de decodificação exibe a alteração na mesma hora:

Mas antes de desenhar, devemos colocar nas áreas de memória VRAM os valores esperados para o programa funcionar como se espera. Infelizmente as funções do BASIC não operam nessa região, então precisaremos invocar as alterações via VPOKE nós mesmos.

30 REM Limpa regiao de memoria escondida
31 FOR I%=&H1800 TO &H1FFF: VPOKE I%,0:NEXT
32 FOR I%=&H3800 TO &H3FFF:VPOKE I%,1:NEXT
33 FOR I%=0 TO 255:VPOKE &H4300+I%,I%:NEXT
34 FOR I%=&H4400 TO &H4FFF: VPOKE I%,0:NEXT

Para deixar a tela em branco (ou melhor, preto), preenchemos a quarta parte da tabela de padrões com o byte 00 (linha 31). Depois preenchemos a tabela de cor com o byte em 01, que representa a cor de frente transparente e fundo preto, para ficar igual ao restante da tela (linha 32). Então preenchemos a tabela de nomes com bytes de 0 até 255 (linha 33), porque é a tabela de nomes que nos permite desenhar na tela inteira, apenas alterando a tabela de padrões nos lugares corretos. E, por fim, preenchemos as tabelas de sprites com zero para ocultá-la totalmente (linha 34). Agora, por falta de um suporte melhor do BASIC para desenhar na nova região de memória, vamos encher a tela de texto:

50 OPEN"GRP:"FOR OUTPUT AS #1
51 FOR K%=0 TO 191 STEP 64
52 FOR J%=0 TO 63 STEP 8
53 FOR I%=0 TO 31
54 PRESET(I%*8,K%+J%)
55 IF J%=0 THEN GOSUB 60 ELSE GOSUB 61
56 NEXT:NEXT:NEXT
57 FOR I%=0 TO 2047:A=VPEEK(I%):VPOKE &H1800+I%,A:NEXT
58 FOR I%=0 TO 2047:A=VPEEK(&H2000+I%):VPOKE &H3800+I%,A:NEXT
59 FOR I=0 TO 255 STEP .125: VDP(24)=I:NEXT:GOTO 59
60 PRINT #1,CHR$(1)+CHR$(65+I%): RETURN
61 PRINT #1,CHR$(J%*4+I%): RETURN

Estou usando PRESET para marcar a posição em que o texto será escrito (linha 54). Observe que o BASIC não nos permite escrever no quarto final da tela pelo suporte de software incompleto a este modo, então escolhi copiar a tabela de padrões (linha 57) e também a tabela de cores (linha 58) do primeiro quarto da tela para o quarto quarto. A linha 59 serve apenas para mostrar o scroll em funcionamento.

Observe que o registrador de scroll vertical também funciona na SCREEN2, então ela também ficaria com 4 tabelas de padrões e de nomes quando usada no MSX2, mas como o BIOS na SCREEN2 executa em modo de compatibilidade TMS9918, você precisará usar as funções novas do BIOS do MSX2 para manipular endereços acima de 0x3FFF (16k).

No modo SCREEN4, o V9938 dispõe ainda de uma paleta de cores, que permite escolher num espaço de 9 bits (ou 512 cores), 16 delas para pintar a tela. Só que não é bem uma tabela mapeada em VRAM como nos outros casos, pois só tem a finalidade de ser usado em um comando do BIOS que importa cores de imagens lidas do disco em um formato padrão, além de ser em uma área fixa na memória que você poder sobrescrever sem nenhum tipo de efeito colateral.

Coloquei um link para um código de exemplo para manipular o scroll vertical na SCREEN4 aqui. O resultado está nesta animação:

Scroll vertical na SCREEN4.

OK, pessoal. Por enquanto é isso. Na próxima parte deste artigo, falarei sobre como implementar páginas de VRAM.