Micro Sistemas 64

Linguagem de Máquina no MSX - II

Artigo Original: Daniel José Burd, Digitação: Wilson Pilon

Na edição anterior tivemos uma amostra do potencial do MSX. A partir de agora, vamos estudá-lo com mais detalhes. Para começar veremos a parte gráfica, especificamente o armazenamento na VRAM.

No mês passado, foi apresentado o doce à criancinha; esta, muito impressionada e quase hipinotizada com sua beleza, ficou a fitá-lo sem poder, no entanto, tocá-lo. Nesta edição, enfim, a criança poderá deliciar-se...

Ao refletir sobre essa introdução, imediatamente me veio à mente a história acima, pois vimos de relance o enorme potencial que possui um micro MSX, porém não foi possível interagir com ele.

A partir deste número vamos dissecar o nosso micro; estudaremos com detalhes cada um de seus membros. Inicialmente, veremos a parte gráfica, isto é, lidaremos com o Miguel Ângelo (lembram-se?).

A Instrução USR do BASIC

A execução de uma rotina em linguagem de máquina é, via de regra, iniciada pela instrução USR do BASIC (veja tabela). Para usá-la, primeiro definimos o endereço inicial da rotina, pro exemplo: DEFUSRn=&HB000 fará com que a instrução USRn(B) execute a rotina situada a partir do endereço &HB000. Lembre-se que B pode ser tanto um número como também uma string e que n deve estar entre 0 e 9.

Uso da instrução USR:

DEFUSR N = X Ex.: DEFUSR 1=&HB000 USR N(B) Ex.: USR 1(30)
N é um número
entre 0 e 9
que identifica
a rotina em
linguagem de
máquina.
X é o endereço de
execução da rotina
em linguagem de
máquina.
N é um número
entre 0 e 9
que indica
qual rotina
deve ser
executada.
B é o parâmetro
a ser passado à
rotina em linguagem
de máquina.
Definição Execução

Além de chamar uma rotina, a instrução USR pode passar parâmetros a essa rotina e também recebê-los de volta. Quando executamos A=USRn(B), o endereço &HF663 conterá um número que nos indicará que tipo de parâmetro B foi enviado. Veja a tabela abaixo:

Conteúdo
de &HF663
Tipo de parâmetro Posição do parâmetro
ou do vetor
Observação
3 string &HF7F8 e &HF7F9 contém o endereço
do vetor da
string
2 numérico inteiro &HF7F8 e &HF7F9  
4 numérico de precisão
simples
&HF7F6 até &HF7F9  
8 numérico de precisão
dupla
&HF7F6 até &HF7F9  

De posse do tipo de parâmetro, poderemos localizá-lo com o auxílio da mesma tabela descrita acima. Por exemplo: USR n(258%) fará &HF663=2. Isto indica que temos um número inteiro armazenado nas posições &HF7F8 e &HF7F9; portanto, &HF7F8=2 e &HF7F9=1, isto é 1*256+2=258.

Para devolvermos valores ao BASIC o processo é o mesmo, porém invertido. Devemos especificar o tipo de parâmetro no endereço &HF663 e, dependendo do tipo, posicionar corretamente na RAM o seu valor de acordo com a tabela. O programa da listagem 4 é um bom exemplo de uso da instrução USR.

ms64 0022

A VRAM (Vídeo RAM)

O MSX possui 16Kb exclusivos para uso de tela. Essa memória está diretamente ligada ao processador de vídeo e não ao Z80 como de costume. Isso significa que para alterarmos ou lermos uma dada posição de VRAM devemos fazê-lo através do VDP (processador de vídeo). Veja figura abaixo:

ms 64 vram

Se quizermos, por exemplo, ler a posição 10 da VRAM (a VRAM vai do endereço 0 até o &H3FFF), não podemos dar a instrução LD A, (10), pois essa instrução nos dará o conteúdo do endereço 10 da ROM e não o conteúdo da VRAM. Teremos que acessar a VRAM via VDP e essa comunicação entre VDP e Z80 é feita por intermédio de dois portos: &H98 - porto de dados - e &H99 - porto de comandos.

O procedimento de leitura e escrita na VRAM são muito semelhantes. Devemos, no porto de comandos, especificar o endereço desejado e se estamos efetuando uma leitura ou escrita. No porto de dados, recebemos (no caso de leitura) ou enviamos os dados (no caso de escrita). Observe a tabela abaixo:

Leitura da Posição &H3FFF da VRAM:

OUT (&H99),&HFF Byte menos significativo do endereço                
OUT (&H99),&H3F Byte mais significativo do endereço 7 6 5 4 3 2 1 0
    O bit 6 desse byte indica se estamos
lendo ou escrevendo na VRAM: se
for 0, estamos lendo; se for 1 estamos
escrevendo
0 0 1 1 1 1 1 1
        L
e
i
t
u
r
a
3 F
IN A,(&H98) O acumulador recebe o conteúdo da
posição &H3FFF da VRAM
               

Escrita na Posição &H3FFF da VRAM:

OUT (&H99),&HFF Byte menos significativo do endereço                
OUT (&H99),&H3F Byte mais significativo do endereço 7 6 5 4 3 2 1 0
    Alterado para indicar escrita 0 0 1 1 1 1 1 1
        E
s
c
r
i
t
a
3 F
OUT (&H98),A Coloca o conteúdo do acumulador no
endereço &H3FFF da VRAM
               

Não é muito complicado. Para nossa sorte, essas rotinas de leitura e escrita na VRAM estão prontinhas na ROM. A rotina de escrita WRVRM (Write VRaM) se inicia no endereço &H4D. O par de registradores HL deve conter o endereço a ser escrito, e o acumulador, o conteúdo futuro. Portanto, a rotina WRVRM é análoga a instrução LD (HL), A da ROM. A rotina da VRAM, RDVRM (Read VRaM), tem como entrada o endereço &H4A e é similar á instrução LD A,(HL). Vale a pena disassemblá-las!

A Estrutura da VRAM

Agora já sabemos ler e escrever na VRAM, porém ainda temos que conhecer a sua estrutura interna para podermos utilizá-la bem. Esta estrutura varia de acordo com o screen que estamos usando, no entanto, certas características são comuns a todos. Vamos a elas:

Tabela de Padrões e Tabela de Nomes - a tabela de padrões (ou formas) contém todos os símbolos que podem ser mostrados no vídeo em um dado screen. A figura abaixo mostra como a letra A é guardada nesta tabela e como são armazenados todos os caracteres.

ms 64 vrampadroes

Verifique na tabela abaixo que os screen 0 e 1 só possuem 256 formas diferentes, enquanto o screen 2 existe uma forma diferente para cada posição na tela, isto é, 24*32 formas, o que lhe permite a alta resolução gráfica.

SCREEN Modo Resolução Caracteres
na Tabela
de Padrões
Tamanho
do Caracter
Impresso
Número
de Cores
Número
Caracteres
por Tela
Possui
Sprites?
Tabela
de Cores
Localização
das Tabelas
na VRAM
1 gráfico
1
256x192 256 8x8 16 32x24=768 sim duas para
cada 8
formas
nomes=base(5)
cores=base(6)
padrões=base(7)
2 gráfico
2
256x192 256 8x8 16 32x24=768 sim duas para
cada 8
bits
nomes=base(10)
cores=base(11)
padrões=base(12) 
3 multi-
color
64x48 - 4x4 16 - sim quatro para
cada forma
nomes=base(15)
padrões=base(17) 
0 texto 256x192 256 6x8 2 de 16 40x24=960 não - nomes=base(0)
padrões=base(2) 

Existe também a tabela de nomes na VRAM, a qual é uma representação da tela. Cada byte desta tabela corresponde a uma posição da tela. No screen 0, por exemplo, esta tabela mede 40*24 bytes e nos screen 1 e 2 mede 32*24 bytes. O conteúdo de cada byte da tabela de nomes representa um caracter da tabela de formas, o qual está impresso no vídeo. Examine atentamente a próxima figura para esclarecer o tópico.

ms 64 vramnomes

Para saber em que posição da VRAM se localiza essas tabelas, veja a tabela acima.

Cores no screen 0: As cores nesse modo são três: uma para fundo, uma para letra e outra para borda. Essas cores ficam armazenadas nas posições &HF3E9, &HF3EA e &HF3EB, respectivamente.

Cores no screen 1: Esse modo já possui uma tabela de cores. O primeiro byte desta tabela é responsável pelas cores dos oito primeiros caracteres da tabela de formas; o segundo byte é responsável pelos oito caracteres seguintes da tabela de formas, e assim sucessivamente. Portanto, essa tabela mede 256 (caracteres)/8=32 bytes. Cada byte dessa tabela possui a estrutura mostrada na tabela abaixo. Rode o programa da listagem 1 e procure entendê-lo.

7 6 5 4 3 2 1 0
               
Cor do

plano
Cor do
fundo

Programa Listagem 1:

ms64 0015

Cores no screen 2: A tabela de cores neste modo 'e do mesmo tamanho que sua tabela de forma, sendo o seu primeiro byte responsável pelas cores do primeiro byte da tabela de padrões; o segundo byte responsável pelo segundo, e assim por diante. Como cada byte da tabela de cores armazena, no máximo, duas cores, isto explica a baixa resolução de cores no screen 2, isto é, duas cores para cada oito bits. Modifique as seguintes linhas do programa da listagem 1 e execute-o:

10 screen 2
50 vpoke(base(11)+i),a*16+(16-a)
65 goto 65

ms64 0016

Verifique o fato de uma alteração na tabela de padrões ou na tabela de nomes modificar o desenho na tela. Tente!

Cores no screen 3: Cada uma das 32*24 posições é dividida em quatro quadrados, os quais podem receber cores diferentes, mas não desenhos diferentes. Dessa forma, a tabela de padrões apenas indica as cores dos quadrados, usando para isso dois bytes. Veja a figura abaixo.

ms 64 vramscreen3

Agora digite o programa da listagem 2.

ms64 0017

Já conhecemos as principais tabelas que compõem a VRAM. O que nos falta é estudarmos a organização interna dessas tabelas. Por exemplo, se quisermos saber, onde fica o pronto cujas coordenadas são 150 e 30 do screen 2 na VRAM, é necessário o conhecimento da estrutura interna da tabela de padrões do screen 2.

A Organização das Tabelas no SCREEN 2

A partir da linha (L) e coluna (C), é muito simples achar a posição do ponto na tabela de formas e na tabela de cores. Dividamos a tela em 32 colunas e 24 linhas; cada linha em oito sublinhas (L') e cada coluna em oito subcolunas (C').

Queremos achar o ponto cujas coordenadas são 150 e 30. Observe a figura abaixo:

ms 64 vramposicao

Você concorda com esta notação? Para acharmos a posição deste ponto na tabela de formas basta colocar os seus bits (bits das coordenadas) na ordem da figura abaixo e temos o endereço correspondente.

ms 64 vramposicaoformas

Verifique que a subcoluna não aparece nesse esquema. Por quê? Desta forma a posição 150,30 é data pela próxima figura.

ms 64 vramposicaoformas2

Verifique isso com o auxílio do programa da listagem 3.

ms64 0018 ms64 0021

De posse da posição do ponto na tabela de padrões é muito simples achar a sua posição na tabela de cores. Basta setar o bit 5 do byte mais significativo do endereço e pronto! (Você saberia explicar por que isso funciona dessa forma?). No nosso caso, as cores responsáveis pela posição 150,30 estão na posição mostrada pela seguinte figura:

ms 64 vramposicaocores

A seguir, apresento duas rotinas muito úteis: a INC Y, que calcula o endereço do byte no qual está o ponto imediatamente abaixo do byte apontado pelo par de registradores HL, e a INC X, que calcula o próximo endereço à direita do ponto apontado por HL. Todos esses endereços são relativos à tabela de padrões. Procure entender essas rotinas: elas se mostrarão muito úteis na programação de animação em geral. (veja figura abaixo)

ms 64 vramincxincy

Tente fazer com a INC Y um traço vertical na tela e com a INC X um horizontal (lembre-se que essas rotinas foram feitas para o screen 2).

Enfim, a criança pôde deliciar-se...

(OT) Eu procuro deixar os artigos na forma original (mesmo com alguns erros toscos), mas corrigi alguns erros deste em particular. Outro item importante é que não pode-se assumir &H98 e &H99 como portos absolutos de I/O do VDP. Por fim, o artigo menciona as rotinas INC X e INC Y que não estão presentes.

Referência de Listagem
Listagem 1 cores.bas 
Listagem 2 multcr.bas
Listagem 3 posit.bas
Listagem 4 ponto.bas