Curso de Jogos em Basic
Scroll em Assembly


Você está em: MarMSX >> Cursos >> Jogos em Basic   Foi visto no capítulo 2 desse curso, ao final da seção 2.2.1, que podemos aproveitar algumas características do modo de interpretação do Basic para ganhar a velocidade do Assembly, como por exemplo, enviar uma string longa para o comando PRINT em vez de caractere a caractere. Entretanto, determinadas rotinas não têm como se aproveitar disso e ficam extremamente lentas se programadas em Basic. Dessa forma, necessitamos recorrer à linguagem Assembly para aumentar a performance dessa rotina. É exatamente isso que alguns desenvolvedores de jogos em Basic fizeram em seus jogos.

  Deve-se ter em mente que não é para criar o jogo todo em Assembly, mas tão somente o trecho de programa (rotina) que necessita da performance de programas feitos em Assembly. Um exemplo disso é uma rotina para realizar o scroll vertical na tela feita no jogo Slalom Gigante, publicado na revista italiana MSX Computer Magazine #1.

  O melhor dessa história é que o programador Basic pode até não saber uma linha de Assembly, mas tão somente ter esse código pronto e saber usá-lo.
Adicionando código Assembly ao Basic   Após desenvolver os mnemônicos na linguagem Assembly e transformá-los em código de máquina, ou até mesmo já tê-los prontos, devemos colocá-los na memória através do comando POKE do Basic. Depois, temos que indicar ao interpretador Basic qual o endereço inicial do programa através do comando DEFUSR <n>. Por fim, a instrução USR <n> chama o programa em Assembly.

  Veja o exemplo a seguir.

  Código em Assembly para chamar a rotina da ROM para apagar a tela (CLS).
Cód Máq		Mnemônico
---------------------------
CD C3 00	CALL &H00C3
C9		RET 

  Em qual endereço de memória devemos colocar nosso programa? Em tese, temos de &H8000 a &HFFFF como áreas de RAM no modo Basic. Entretanto, em &H8000 começa nosso programa em Basic e também perto do final ficam variáveis do sistema. Assim, vamos colocá-lo em uma área intermediária, longe dos dois: &HC000.
  Podemos armazenar até 10 endereços de rotina em Assembly na função DEFUSR, atribuindo-se um valor de 0 a 9. Quando omitido o valor, assume-se o valor igual a 0. Assim, DEFUSR equivale a DEFUSR 0. O comando USR <n> chama a rotina do DEFUSR <n> equivalente.

 Programa em Basic.
10 PRINT"Tecle algo que vou apagar a tela ..."
20 POKE &HC000,&HCD
30 POKE &HC001,&HC3
40 POKE &HC002,0
50 POKE &HC003,&HC9
60 DEFUSR=&HC000
70 A$=INPUT$(1)
80 X=USR(0)

  O capítulo 19 do curso de Basic apresenta maiores detalhes sobre como inserir código Assembly em Basic.
Scroll vertical em Assembly   O que parece ser algo complicado de fazer, é na realidade muito simples. Isso porque o que necessitamos fazer é simplesmente mover um bloco de imagem da tela uma linha acima/abaixo. Veja a ilustração a seguir.

  Na ilustração acima, movemos uma linha a área assinalada pelo retângulo vermelho. Podemos fazer essa movimentação nos dois sentidos: tanto da ilustração à esquerda para a direita (a tela sobe), como da direita para a esquerda (a tela desce).

  Podemos notar que após a cópia do bloco em vermelho, uma linha não recebe informação e fica com "lixo". É a última linha se a imagem resultante é a da direita, e a primeira linha se a imagem resultante for a da esquerda. É nessa linha que devemos inserir informação nova da tela. Podemos fazer isso em Basic mesmo, pois a instrução PRINT pode imprimir toda a linha de uma só vez de forma rápida.

  Na screen 1, iremos mover a tabela de nomes. Ela começa na posição 6144 ou &H1800. Cada linha possui 32 posições (ou &H20) de blocos de 8x8 pixels. Assim, a segunda linha começa em &H1820, a terceira em &H1840 etc.

  No VDP do MSX 2 podemos mover porções de memória de vídeo dentro do próprio chip. Entretanto, no VDP do MSX 1 isso não é possível. Assim, devemos mover um bloco da VRAM para a RAM e depois devolvê-lo à VRAM.
  Há duas rotinas na ROM do MSX que realizam cópias de blocos inteiros da VRAM para RAM e vice-versa. Veja no esquema a seguir.
LDIRMV - copia um bloco da VRAM para a RAM.

Ponteiro:  0059H
Entrada:   BC=Comprimento
           DE=Endereço RAM
           HL=Endereço VRAM
LDIRVM - copia um bloco da RAM para a VRAM.

Ponteiro:  005CH
Entrada:   BC=Comprimento
           DE=Endereço VRAM
           HL=Endereço RAM

  Devemos então copiar o bloco assinalado em vermelho na figura mais acima da VRAM para a RAM e então devolver o mesmo bloco uma linha acima/abaixo.

  O programa genérico em Assembly é apresentado a seguir
Cód. Máq.	Mnemônicos
-------------------------------
21 20 18	LD   HL,&H1820	; Endereço inicial da VRAM (2a. linha)
11 00 C2	LD   DE,&HC200	; Endereço inicial da RAM
01 E0 02	LD   BC,&H02E0	; Comprimento do bloco (32 x 23 linhas)
CD 59 00	CALL &H0059	; Chama VRAM -> RAM
21 00 C2	LD   HL,&HC200	; Endereço inicial da RAM
11 00 18	LD   DE,&H1800	; Endereço inicial da VRAM (1a. linha)
01 E0 02	LD   BC,&H02E0	; Comprimento do bloco
CD 5C 00	CALL &H005C	; Chama RAM -> VRAM
C9		RET 		; Retorna
  Nesse exemplo movemos o bloco no sentido para cima.

  Nota: colocamos a área de transferência da RAM em uma região após o código do programa. Nosso programa em Assembly vai de &HC000 a &HC018. Assim, &HC200 é uma região segura para realizar a transferência, sem correr o risco de conflitar com o programa.

  Vamos testar esse código em Basic.
10 SCREEN 1 : KEY OFF : E=&HC000
20 READ A$ : IF A$="M" THEN 60
30 POKE E,VAL("&H"+A$)
40 E=E+1
50 GOTO 20
60 LOCATE 0,10:PRINT"Esta tela vai se mover":PRINT"para cima ..."
70 PRINT : PRINT "TECLE ALGO ..."
80 A$=INPUT$(1)
90 DEFUSR=&HC000:X=USR(0)
100 DATA 21,20,18 : ' LD HL,&H1820
110 DATA 11,00,C2 : ' LD DE,&HC200
120 DATA 01,E0,02 : ' LD BC,&H02E0
130 DATA CD,59,00 : ' CALL &H0059
140 DATA 21,00,C2 : ' LD HL,&HC200
150 DATA 11,00,18 : ' LD DE,&H1800
160 DATA 01,E0,02 : ' LD BC,&H02E0
170 DATA CD,5C,00 : ' CALL &H005C
180 DATA C9,M,    : ' RET
  Obs: ao lado dos códigos em linguagem de máquina nas instruções DATA, há um comentário com o respectivo código mnemônico.

  Após a execução desse programa, a tela se move uma linha para cima.
  Podemos mover quantas linhas quisermos, se colocarmos um loop. Então altere/acrescente as seguintes linhas ao programa anterior.
90 DEFUSR=&HC000
95 FOR X=1 TO 5 : X=USR(0) : NEXT X
  A cada iteração do loop, a rotina em linguagem de máquina para mover a tela uma linha acima é chamada. Observe a rapidez em que o movimento total é feito.

  Para modificar o sentido do movimento da tela, precisamos inverter os endereços do código em linguagem de máquina, uma vez que agora vamos copiar o bloco a partir da primeira linha para a segunda linha. Assim, o endereço inicial da VRAM é a partir de &H1800 em vez de &H1820, assim como retornamos os dados em &H1820, em vez de &H1800. Altere as seguintes linhas do programa anterior:
60 LOCATE 0,10:PRINT"Esta tela vai se mover":PRINT"para baixo ..."
...
100 DATA 21,00,18  ' LD HL,&H1800
...
150 DATA 11,20,18  ' LD DE,&H1820
  Observe que o código de máquina contém os valores de endereços de memória explícitos. Entretanto, para valores de 16 bits (2 bytes), o valor aparece invertido. Assim, 00 18 corresponde ao valor &H1800. Dito isso, fica fácil alterar esses valores.
Scroll parcial de tela   Se você jogou o Slalom Gigante, pôde observar que o autor do jogo reservou as duas primeiras linhas para colocar informações do jogo. Essas linhas não são alteradas pelo scroll. O que o autor fez na verdade foi começar o scroll a partir da terceira linha.
  Temos que ter em mente que além de alterar a linha inicial do scroll, devemos redimensionar o tamanho do bloco a ser copiado. Veja a ilustração a seguir, que começa o scroll a partir da 12a. linha.

Vamos calcular os parâmetros necessários.
Endereço inicial para a 12a. linha: 
&H1800 + 32 x 11 = &H1800 + &H0160 = &H1960

Próxima linha: &H1980

Tamanho do bloco: 12 linhas
12 x 32 = 384 = &H0180

  O programa em Basic que realiza o scroll parcial de tela é apresentado a seguir.
10 SCREEN 1 : KEY OFF : E=&HC000
20 READ A$ : IF A$="M" THEN 60
30 POKE E,VAL("&H"+A$)
40 E=E+1
50 GOTO 20
60 LOCATE 0,10:PRINT"Eu NAO me movo !"
65 LOCATE 0,12:PRINT"Eu me movo!"
70 PRINT : PRINT "TECLE ALGO ..."
80 A$=INPUT$(1)
90 DEFUSR=&HC000:X=USR(0)
100 DATA 21,80,19 : ' LD HL,&H1980
110 DATA 11,00,C2 : ' LD DE,&HC200
120 DATA 01,80,01 : ' LD BC,&H0180
130 DATA CD,59,00 : ' CALL &H0059
140 DATA 21,00,C2 : ' LD HL,&HC200
150 DATA 11,60,19 : ' LD DE,&H1960
160 DATA 01,80,01 : ' LD BC,&H0180
170 DATA CD,5C,00 : ' CALL &H005C
180 DATA C9,M,    : ' RET
Inserindo os dados da tela   Os dados da tela são inseridos linha a linha, conforme o scroll avance a tela. Se o sentido é para cima, devemos preencher a 24a. linha. Se for para baixo, a 1a. linha.

  Para ilustrar esse mecanismo, vamos criar o cenário da praia de Copacabana no Rio de Janeiro. Assim, utilizaremos os seguintes elementos estruturantes (blocos de 8x8 pixels):   Os elementos estruturantes formarão um desenho, que repetirá o padrão de quatro em quatro linhas. O layout do desenho da praia de Copacabana foi feito em um editor de imagens para PC (Kolourpaint do Linux), o que facilita muito o trabalho.
  A imagem a seguir ilustra a combinação dos elementos estruturantes a cada 4 linhas (padrão básico). O primeiro é o calçadão, seguido da rua, areia e mar.   A formação dos padrões é a seguinte:
1 2   10 10   9 9   12 13
3 4   10 11   9 9   12 14
5 6   10 10   9 9   12 13
7 8   10 11   9 9   12 14

  Iremos construir esse desenho na screen 1. Assim, os caracteres utilizados para cada padrão serão os seguintes:
Grupo	Caracteres	Padrões		Cores
---------------------------------------------
 12	'abcdefg	1-8		F1
 13     h		9		1A
 14	pq		10-11		FE
 15	xyz		12-14		74
  Dessa forma, por exemplo, o caractere "x" representa o padrão 12, "y" o padrão 13 e "z" o padrão 14.

  O programa a seguir modifica os padrões dos caracteres e as cores dos grupos de caracteres.
10 SCREEN 1 : WIDTH 32 : COLOR 15,0,0 : KEY OFF
20 FOR I=0 TO 63 : READ A$ : VPOKE 768+I, VAL("&H"+A$) : NEXT I
30 FOR I=0 TO 7 : READ A$ : VPOKE 832+I, VAL("&H"+A$) : NEXT I
40 FOR I=0 TO 15 : READ A$ : VPOKE 896+I, VAL("&H"+A$) : NEXT I
50 FOR I=0 TO 23 : READ A$ : VPOKE 960+I, VAL("&H"+A$) : NEXT I
55 '
60 ' Define cores de fundo
65 '
70 VPOKE &H2000+12,&HF1
80 VPOKE &H2000+13,&H1A
90 VPOKE &H2000+14,&HFE
100 VPOKE &H2000+15,&H74
195 '
200 ' Dados dos caracteres
205 '
210 DATA F0,FC,FC,FE,FE,FF,FF,FF ' Calcadao
220 DATA 0F,03,03,01,01,00,00,00
230 DATA FF,FF,FF,FE,FE,FC,FC,F0
240 DATA 00,00,00,01,01,03,03,0F
250 DATA F0,C0,C0,80,80,00,00,00
260 DATA 0F,3F,3F,7F,7F,FF,FF,FF
270 DATA 00,00,00,80,80,C0,C0,F0
280 DATA FF,FF,FF,7F,7F,3F,3F,0F
290 DATA 00,00,00,00,00,00,00,00 ' Areia
300 DATA 00,00,00,00,00,00,00,00 ' Asfalto
310 DATA 80,80,80,80,80,80,80,80
320 DATA 00,00,00,00,00,00,00,00 ' Mar
330 DATA 01,01,01,01,03,03,03,07
340 DATA 07,03,03,03,01,01,01,01

  Podemos salvar a parte da tabela de cores e caracteres modificados para não ter que reescrever todo esse código.
' Padrões
BSAVE "pat.dat",768,983,S

' Cores
BSAVE "col.dat",&H200C,&H200F,S 

  O mesmo podemos fazer com o código do scroll (com ele carregado na memória).
BSAVE "cod.bin",&HC000,&HC018

  Nota: não se preocupe, pois mais adiante você poderá fazer o download das duas tabelas e o código.

  Colocaremos 31 caracteres de modo a formar cada linha. Isso porque se colocarmos a linha cheia, o Basic saltará uma linha. O padrão básico é apresentado a seguir.   As quatro linhas desse padrão serão formadas pelos seguintes caracteres:
Linha 1: xyhhhhhhhhhhhhhhhhhhhh`a`a`apppp
Linha 2: xzhhhhhhhhhhhhhhhhhhhhbcbcbcpppq
Linha 3: xyhhhhhhhhhhhhhhhhhhhhdededepppp
Linha 4: xzhhhhhhhhhhhhhhhhhhhhfgfgfgpppq

  Após essa longa preparação, podemos iniciar nosso passeio por Copacabana. O código a seguir ilustra como alimentar o scroll com as linhas da praia. Mas antes, baixe aqui as tabelas e códigos prontos.
10 SCREEN 1 : WIDTH 32 : COLOR 15,0,0 : KEY OFF : DIM L$(4)
20 L$(1)="xyhhhhhhhhhhhhhhhhhhh`a`a`apppp"
30 L$(2)="xzhhhhhhhhhhhhhhhhhhhbcbcbcpppq"
40 L$(3)="xyhhhhhhhhhhhhhhhhhhhdededepppp"
50 L$(4)="xzhhhhhhhhhhhhhhhhhhhfgfgfgpppq"
60 LN=1
70 BLOAD"PAT.DAT",S
80 BLOAD"COL.DAT",S
90 BLOAD"COD.BIN"
100 DEFUSR=&HC000
110 X=USR(0)
120 LOCATE 0,23:PRINT L$(LN);
130 LN=LN+1 : IF LN>4 THEN LN=1
140 FOR T=1 TO 50 : NEXT
150 GOTO 110
  Resultado:   Colocamos as strings das linhas em uma tabela, pois é mais fácil referenciá-las.
  Observe na linha 90 que carregamos o código do scroll sem executá-lo (sem a extensão ,R).
  A linha 140 controla a velocidade do scroll.

  Podemos inverter o sentido do movimento? Sim. É só alterar/inserir as seguintes linhas:
60 L=4
...
95 POKE &HC001,0 : POKE &HC010,&H20 ' Altera código scroll
...
120 LOCATE 0,0:PRINT L$(LN);
130 LN=LN-1 : IF LN<1 THEN LN=4

  Nota importante: para dar efeito de scroll, os padrões dos blocos de linhas adjacentes deverão ser diferentes. Caso contrário, essa parte do cenário parecerá estática. Foi exatamente o que fizemos no mar e no asfalto, e também o autor do jogo Slalom Gigante com as árvores.
Scroll horizontal em Assembly   O scroll horizontal irá seguir os mesmos princípios do vertical. Só que nesse caso, iremos mover a tela somente um caractere em vez de uma linha. O problema desse scroll consiste em como imprimir uma coluna de caracteres de forma rápida.

  Rode o programa de Copacabana, deixe a tela se completar e dê CONTROL + STOP para parar a animação. Em seguida, pressione o CAPS LOCK e dê o seguinte comando:
POKE &hC001,0:POKE &HC010,1:X=USR(0)
  A tela andou para a direita! Isso porque agora definimos o salto de 1 caractere, em vez de 32 (uma linha). Experimente dar mais comandos X=USR(0). A tela continua indo para a direita.

  Para movermos no sentido contrário, devemos fazer:
POKE &hC001,1:POKE &HC010,0:X=USR(0)

  Aproveitando o programa "cod.bin" do exemplo de Copacabana, vamos rodar o exemplo a seguir.
10 SCREEN 1
20 BLOAD"cod.bin"
30 DEFUSR=&HC000
40 POKE &HC001,0:POKE&HC010,1
50 FOR I=0 TO 22
60 LOCATE 0,I:PRINT"A";
70 NEXT I
80 X=USR(0)
90 GOTO 50
  Esse programa faz o scroll horizontal desenhando a letra A na coluna 0. Observe o efeito de flicker (coluna piscando).

  A solução para esse problema será vista na próxima seção.
Código Assembly para todo o scroll   No exemplo do scroll vertical de Copacabana, também observamos um pequeno efeito de flicker no calçadão onde a linha é desenhada. A solução para esse problema e do scroll vertical é juntar todo o procedimento em um só código em Assembly. Assim, passamos a linha/coluna para a rotina em Assembly e ela se encarrega de mover a tela e preencher a lacuna.

  O curso de Basic, capítulo 19, explica como passar parâmetros para o código em Assembly através da instrução USR. Podemos passar até strings.
  Assim, nossa estratégia agora é passar a linha para o código que realizará todo o processo de scroll, em vez de mover a tela em Assembly e desenhar a linha em Basic.

  O código em Assembly do scroll completo vertical para cima é apresentado a seguir.
End   Código      Linha   Instrução        Comentários
-------------------------------------------------------
C000  		   10 	  ORG  &HC000
C000  D5	   20 	  PUSH DE         ; Salva ponteiro String
C001  21 20 18	   30 	  LD   HL,&H1820  ; Endereço inicial da VRAM
C004  11 00 C2	   40 	  LD   DE,&HC200  ; Endereço inicial da RAM
C007  01 E0 02	   50 	  LD   BC,&H2E0   ; Comprimento do bloco
C00A  CD 59 00	   60 	  CALL &H59       ; Chama VRAM -> RAM
C00D  21 00 C2	   70 	  LD   HL,&HC200  ; Endereço inicial da RAM
C010  11 00 18	   80 	  LD   DE,&H1800  ; Endereço inicial da VRAM
C013  01 E0 02	   90 	  LD   BC,&H2E0   ; Comprimento do bloco
C016  CD 5C 00	  100 	  CALL &H5C       ; Chama RAM -> VRAM
C019  E1	  110 	  POP  HL         ; Retorna DE em HL
C01A  23	  120 	  INC  HL         ; Passa para ponteiro string
C01B  5E	  130 	  LD   E,(HL)     ; 
C01C  23	  140 	  INC  HL         ; Copia end inicial da str em DE
C01D  56	  150 	  LD   D,(HL)     ; 
C01E  EB	  160 	  EX   DE,HL      ; Passa end inicial para HL (RAM)
C01F  11 E0 1A	  170 	  LD   DE,&H1AE0  ; Endereco incial da VRAM
C022  01 1F 00	  180 	  LD   BC,&H1F    ; Copia 31 caracteres
C025  CD 5C 00	  190     CALL &H5C       ; Chama RAM -> VRAM
C028  C9	  200     RET             ; Retorna ao Basic
  O que devemos fazer é apontar HL para o inicio da string passada como parâmetro e desenhar o bloco de 31 caracteres.

  Rode o programa de Copacabana com esse novo código.
10 SCREEN 1 : WIDTH 32 : COLOR 15,0,0 : KEY OFF : DIM L$(4)
20 L$(1)="xyhhhhhhhhhhhhhhhhhhh`a`a`apppp"
30 L$(2)="xzhhhhhhhhhhhhhhhhhhhbcbcbcpppq"
40 L$(3)="xyhhhhhhhhhhhhhhhhhhhdededepppp"
50 L$(4)="xzhhhhhhhhhhhhhhhhhhhfgfgfgpppq"
60 LN=1
70 BLOAD"PAT.DAT",S
80 BLOAD"COL.DAT",S
90 BLOAD"SCROLLVU.BIN"
100 DEFUSR=&HC000
110 X$=USR(L$(LN))
120 LN=LN+1 : IF LN>4 THEN LN=1
130 FOR T=1 TO 50 : NEXT
140 GOTO 110
  Nota: observe na linha 110 que devemos retornar USR em uma variável alfanumérica, que no caso é X$. Se não for assim, retorna o erro "Type mismatch".

  Não se preocupe em converter os códigos em Assembly, pois todos eles já estão prontos no pacote de Copacabana. São eles:
  Para baixo, devemos modificar as seguintes linhas em Assembly:
C001  21 00 18	   30 	  LD   HL,&H1800  ; Endereço inicial da VRAM
...
C010  11 20 18	   80 	  LD   DE,&H1820  ; Endereço inicial da VRAM
...
C01F  11 00 18	  170 	  LD   DE,&H1800  ; Endereco incial da VRAM
  E essas em Basic:
90 BLOAD"SCROLLVU.BIN"
...
120 LN=LN-1 : IF LN<1 THEN LN=4

  Esse código é otimizado para o movimento vertical. Para o movimento horizontal, é necessário processar caractere por caractere. Isso se deve ao fato de que uma coluna de dados não ocupa posições contíguas de memória como uma linha.

  O código em Assembly do scroll completo horizontal para a direita é apresentado a seguir.
End   Código      Linha   Instrução          Comentários
--------------------------------------------------------
C000  		   10 	    ORG  &HC000
C000  D5	   20 	    PUSH DE
C001  21 00 18	   30 	    LD   HL,&H1800
C004  11 00 C2	   40 	    LD   DE,&HC200
C007  01 00 03	   50 	    LD   BC,&H300   ; Move a tela toda
C00A  CD 59 00	   60 	    CALL &H59
C00D  21 00 C2	   70 	    LD   HL,&HC200
C010  11 01 18	   80 	    LD   DE,&H1801
C013  01 00 03	   90 	    LD   BC,&H300
C016  CD 5C 00	  100 	    CALL &H5C
C019  E1	  110 	    POP  HL
C01A  23	  120 	    INC  HL
C01B  5E	  130 	    LD   E,(HL)
C01C  23	  140 	    INC  HL
C01D  56	  150 	    LD   D,(HL)     ; Pos inicial na RAM
C01E  06 18	  160 	    LD   B,&H18     ; Total 24 caracteres
C020  21 00 18	  170 	    LD   HL,&H1800  ; Pos inicial VRAM
C023  EB	  180 LOOP: EX   DE,HL
C024  7E	  190 	    LD   A,(HL)     ; Carrega caractere
C025  EB	  200 	    EX   DE,HL
C026  CD 4D 00	  210 	    CALL &H4D       ; Escreve byte na VRAM
C029  13	  220 	    INC  DE         ; Próximo caractere
C02A  C5	  230 	    PUSH BC         ; Salva BC
C02B  01 20 00	  240 	    LD   BC,&H20    ; Tamanho do salto
C02E  09	  250 	    ADD  HL,BC      ; Próxima linha
C02F  C1	  260 	    POP  BC         ; Recupera BC
C030  10 F1	  270 	    DJNZ LOOP
C032  C9	  280 	    RET 

  Para movera tela para a esquerda, façamos as seguintes alterações:
C001  21 01 18	   30 	    LD   HL,&H1801
...
C010  11 00 18	   80 	    LD   DE,&H1800
...
C020  21 1F 18	  170 	    LD   HL,&H181F  ; Pos inicial VRAM

  Por fim, um pequeno teste em Basic.
10 SCREEN 1
20 BLOAD"scrollhr.bin"
30 DEFUSR=&HC000
40 A=RND(-TIME)*50+33
50 A$=STRING$(24,CHR$(A))
60 X$=USR(A$)
70 GOTO 40




<< Anterior BasGame Próxima >>