Hard Scroll no MSX 2


Você está em: MarMSX >> Cursos >> Assembly Z80   A partir do MSX 2, é possível fazer o scroll de tela via hardware. No MSX 2, o chip de vídeo V9938 permite rolar a tela verticalmente a uma velocidade fantástica. Um exemplo desse scroll é no jogo Aleste para MSX 2. Já no MSX 2+, o chip de vídeo V9958 permite o scroll vertical e horizontal via hardware.

  Mas afinal, qual a diferença entre o scroll via software e via hardware? No scroll via software, devemos mover manualmente todos os pixels de uma região para a outra. Já no scroll por hardware, o próprio chip se encarrega de mover toda a tela em uma direção (inclusive os sprites). Esse tipo de scroll não envolve o deslocamento de pixels, tornando-o sensivelmente mais rápido.

  Em ambos os MSXs, o VDP(24) se encarrega de deslocar a tela N pixels para cima. Veja o exemplo a seguir.
10 SCREEN 5
20 OPEN"GRP:" AS #1
30 CIRCLE(128,95),50,15
40 PAINT(128,95),4,15
50 PRESET(72,170),0,TPSET:PRINT#1,"Tecle algo ..."
60 A$=INPUT$(1)
70 VDP(24)=50
80 GOTO 80

  Quando movemos a tela na vertical, há uma área "suja" entre as linhas 212 e 255, que são os dados dos sprites [1]. Sim, o scroll via hardware acessa as linhas de 212 a 255, que estão ocultas na exibição de tela normal.
  A solução para o problema é executar a sub-rotina da BIOS que limpa todos os sprites (CLRSPR - &H69) e também limpar essa área através do comando COPY [1].

  Veja este novo exemplo:
10 SCREEN 5
15 DEFUSR=&H69:X=USR(0)
16 COPY(0,0)-(255,44),0 TO (0,212),0 ' Limpa area
20 CIRCLE(128,105),50,15
30 PAINT(128,95),12,15
40 ' Inicia scroll
50 FOR X=0 TO 255
60 VDP(24)=X
70 NEXT

  Uma vez que limpamos os dados dos sprites, como fazer para usá-los com o scroll de tela? Se criarmos novos sprites, não vamos sujar aquela área de novo? Vejamos a solução [2].

  Sabemos que o MSX 2 possui 4 páginas de vídeo para as screens 5 e 6, e 2 páginas de vídeo para as screens 7 e 8. Cada página de vídeo possui sua própria tabela de sprites. Dessa forma, poderíamos utilizar a área de sprites de outra página na página atual. Certo?
  Bem, a solução é essa sim, mas devemos tomar alguns cuidados. Por exemplo, os comandos SPRITE$(n) e PUT SPRITE modificam as tabelas de sprite de todas as páginas de vídeo. Logo, não podemos utilizá-los, pois voltarão a "sujar" a área de sprites da página 0.

  O programa a seguir demonstra como o SPRITE$(n) e o PUT SPRITE modificam todas as tabelas.
10 SCREEN 5
20 OPEN"GRP:" AS#1
30 DEFUSR=&H69:X=USR(0)
40 FOR P=0 TO 3
50 PRESET(52,100),0,TPSET:PRINT#1,"Estamos na pagina";P
60 COPY(0,0)-(255,44),P TO (0,212),P
70 FOR Y=0 TO 255 : VDP(24)=Y : NEXT Y
80 NEXT P
90 SPRITE$(0)=STRING$(8,255)
100 PUT SPRITE 0,(128,15),15,0
110 FOR P=0 TO 3
120 PRESET(52,120),0,TPSET:PRINT#1,"Agora com sprites";P
130 FOR Y=0 TO 255 : VDP(24)=Y : NEXT Y
140 NEXT P
  Observamos que, quando os sprites são acionados dessa forma, todas as áreas entre Y=212 e Y=255 de todas as páginas estão "sujas" novamente.

  Para solucionar esse problema, teremos que colocar os sprites manualmente na página 1 através do VPOKE. O artigo sobre sprites desse curso entra em detalhes sobre como o sprite é configurado na VRAM.

  As tabelas de sprites da página 1 da screen 5 se localizam a partir de &HFxxx da VRAM. Então, vamos criar um desenho de uma bola nessas tabelas e mostrá-lo.
10 SCREEN 5
20 DEFUSR=&H69:X=USR(0)
30 COPY(0,0)-(255,44),0 TO (0,212),0 ' Limpa area
40 CIRCLE(128,105),50,15
50 PAINT(128,95),12,15
65 GOSUB 500
70 ' Inicia scroll
80 FOR X=0 TO 255
90 VDP(24)=X
100 NEXT
110 GOTO 80
497 '
498 ' Desenha sprite
499 '
500 VPOKE &HF800,&B0011100
510 VPOKE &HF801,&B0111110
520 VPOKE &HF802,&B1111111
530 VPOKE &HF803,&B1111111
540 VPOKE &HF804,&B1111111
550 VPOKE &HF805,&B1111111
560 VPOKE &HF806,&B0111110
570 VPOKE &HF807,&B0011100
575 VPOKE &HF600,100  ' Y
580 VPOKE &HF601,128  ' X
590 VPOKE &HF602,0    ' Numero sprite
600 FOR F=&HF400 TO &HF40F:VPOKE F,15:NEXT ' Define cor
610 RETURN
  Cade o sprite? Esquecemos alguma coisa? Sim. As tabelas de sprites da página 0 ainda apontam para &H7xxx em vez de &HFxxx.

  Assim, devemos mudar a localização das tabelas dos sprites nos registros do VDP [3]:
Registro  VDP  Controla
  #5       5   Endereço LOW da tabela de atributos do sprite
 #11      12   Endereço HI da tabela de atributos do sprite
  #6       6   Endereço da tabela de padrões de sprite

  Entretanto, não passamos o endereço da VRAM com os valores diretamente. Os endereços de memória de vídeo do MSX 2 são números de 17 bits. Por causa disso, foram criados modos peculiares de se passar endereços de VRAM nesses registros. As tabelas a seguir mostram como funcionam os endereçamentos nos registros #5, #6 e #11 [3]:

Registro Endereçamento
#5 A14 A13 A12 A11 A10 1 1 1
#11 0 0 0 0 0   0   A16 A15
#6 0 0 A16 A15 A14 A13 A12 A11

  O endereço de 17 bits é formado a partir da combinação dos bits An, conforme mostra o quadro abaixo. Os valores estão agrupados por 8 bits.
A16  A15A14A13A12A11A10A9A8  A7A6A5A4A3A2A1A0

  Os registros #5 e #11 controlam os seguintes bits assinalados em vermelho:
A16  A15A14A13A12A11A10A9A8  A7A6A5A4A3A2A1A0
  Os demais bits são tratados como 0.

  O comando VDP do Basic DEPENDE da página ativa. Assim, para obtermos o endereço das tabelas de sprites de cada página através do VDP, devemos estar localizados na referida página. Exemplo:
10 SCREEN 5
20 SET PAGE 1,1
30 A=VDP(5):B=VDP(12)
40 SCREEN 0
50 PRINT"LOW:";A
60 PRINT"HI:";B
  Obtém o endereço da tabela de atributos dos sprites da página 1.

  A seguir, são apresentados os valores retornados dos endereços LOW e HI das tabelas de atributos dos sprites das páginas do MSX 2.
Screen  Página 0  Página 1  Página 2  Página 3
 5/6    0 e 239   1 e 239   2 e 239   3 e 239
 7/8    1 e 247   3 e 247

  Na página 0 das screens 5/6, o valor HI do endereço é 0, enquanto que o valor LOW é 239 (&b11101111). Assim temos:
16   15 14 13 12 11 10 09 08   07 06 05 04 03 02 01 00
 0    0  1  1  1  0  1  0  0    0  0  0  0  0  0  0  0
  Os valores assinalados em azul correspondem aos bits do HI, enquanto que os valores assinalados em verde correspondem aos bits do LOW.

  Convertendo o valor de 17 bits em hexadecimal, temos o endereço &H7400. Esse endereço é o conjunto de tabelas de cores + atributos dos sprites da página 0.

  Para o registro #6, que controla o endereço da tabela de padrões dos sprites, o modo de formação do endereço é semelhante. A seguir, são apresentados os valores retornados para os endereços das tabelas de padrões dos sprites para as telas do MSX 2 - VDP(6).
Screen  Página 0  Página 1  Página 2  Página 3
 5/6      15        31        47        63
 7/8      30        62

  Na página 1 das screens 5/6, o valor é 31 (&b00011111)
16   15 14 13 12 11 10 09 08   07 06 05 04 03 02 01 00
 0    1  1  1  1  1  0  0  0    0  0  0  0  0  0  0  0
  Convertendo o valor de 17 bits em hexadecimal, temos o endereço &HF800.

  Para evitar a complexidade dos cálculos de endereços dessas tabelas, vamos utilizar os valores padrão para as tabelas de outras páginas. Assim, como desejamos apontar a tabela de sprites da página 0 para as tabelas de sprites da página 1 na screen 5, fazemos:
35 VDP(6)=31: VDP(12)=1: VDP(5)=239
  Lá esta ela! Observe que bolinha acompanha o scroll da tela.

  Se desejarmos movimentar a bolinha, alteramos o valor de Y na tabela de sprites a cada iteração:
95 VPOKE &HF600,(X+100) MOD 255


  Scroll em Assembly

  De modo a tornar a animação mais rápida, iremos mover a tela, bem como atualizar a coordenada Y do sprite, em Assembly.

  Antes de começar, vamos ver a lista de registros do VDP [3]:
┌──────┬─────────┬──────────────────────────────────────────────────────────┐
│ R#n  │   VDP   │			  Função  			    │
├──────┼─────────┼──────────────────────────────────────────────────────────┤
│ R#0  │ VDP(0)  │ mode register #0					    │
│ R#1  │ VDP(1)  │ mode register #1					    │
│ R#2  │ VDP(2)  │ pattern name table					    │
│ R#3  │ VDP(3)  │ colour table (LOW)					    │
│ R#4  │ VDP(4)  │ pattern generator table				    │
│ R#5  │ VDP(5)  │ sprite attribute table (LOW) 			    │
│ R#6  │ VDP(6)  │ sprite pattern generator table			    │
│ R#7  │ VDP(7)  │ border colour/character colour at text mode		    │
│ R#8  │ VDP(9)  │ mode register #2					    │
│ R#9  │ VDP(10) │ mode register #3					    │
│ R#10 │ VDP(11) │ colour table (HIGH)					    │
│ R#11 │ VDP(12) │ sprite attribute table (HIGH)			    │
│ R#12 │ VDP(13) │ character colour at text blinks			    │
│ R#13 │ VDP(14) │ blinking period					    │
│ R#14 │ VDP(15) │ VRAM access address (HIGH)				    │
│ R#15 │ VDP(16) │ indirect specification of S#n			    │
│ R#16 │ VDP(17) │ indirect specification of P#n			    │
│ R#17 │ VDP(18) │ indirect specification of R#n			    │
│ R#18 │ VDP(19) │ screen location adjustment (ADJUST)			    │
│ R#19 │ VDP(20) │ scanning line number when the interrupt occurs	    │
│ R#20 │ VDP(21) │ colour burst signal 1				    │
│ R#21 │ VDP(22) │ colour burst signal 2				    │
│ R#22 │ VDP(23) │ colour burst signal 3				    │
│ R#23 │ VDP(24) │ screen hard scroll					    │
└──────┴─────────┴──────────────────────────────────────────────────────────┘

  O primeiro passo é criar um programa em Assembly para modificar o registro R#23 (ou VDP 24) do VDP.
End  Código             Mnemônicos
-------------------------------------
			5 WRTVDP: EQU &H012D
			6 EXTROM: EQU &H015F
			10 ORG &HC000
C000  01 17 32		20 LD BC,&H3217		; Move 50, R#23 
C003  DD 21 2D 01	30 LD IX, WRTVRM
C007  CD 5F 01		40 CALL EXTROM
C00A  C9		50 RET

  Para testar em Basic digite o seguinte programa:
10 SCREEN 5
20 CIRCLE(128,95),50,15
30 FOR T=0 TO 10
40 READ A$:POKE &HC000+T,VAL("&H"+A$)
50 NEXT T
60 DEFUSR=&HC000
70 A$=INPUT$(1)
80 X=USR(0)
90 GOTO 90
500 DATA 01,17,32,DD,21,2D,01
510 DATA CD,5F,01,C9
  O deslocamento vertical foi feito em Assembly.

  Listagem completa em Assembly para alterar o registro #23 e mover o sprite é apresentada a seguir.
End  Código             Mnemônicos
----------------------------------------------
012D  		    	5 WRTVDP:	EQU  &H12D
015F  		    	6 EXTROM:	EQU  &H015F
0109  		  	7 WRTVRM:	EQU  &H109
C000  		   	10 		ORG  &HC000
C000  06 00	   	15 		LD   B,0
C002  C5		16 LOOP:	PUSH BC
C003  0E 17	 	20 		LD   C,&H17	; Usa o valor B do loop
C005  DD 21 2D 01	30 		LD   IX,WRTVDP
C009  CD 5F 01	   	40 		CALL EXTROM
C00C  78	   	50 		LD   A,B
C00D  C6 64	   	60 		ADD  A,100	; Também usa o B do loop,
C00F  21 00 F6	   	70 		LD   HL,&HF600	; mas desloca Y em 100
C012  DD 21 09 01   	80 		LD   IX,WRTVRM
C016  CD 5F 01	   	90 		CALL EXTROM
C019  C1	  	100 		POP  BC		; Retorna o valor B do loop
C01A  10 E6	  	110 		DJNZ LOOP
C01C  C9	  	120 		RET 

  O programa em Basic para testar é apresentado a seguir.
10 SCREEN 5
20 DEFUSR=&H69:X=USR(0)
30 COPY(0,0)-(255,44),0 TO (0,212),0 ' Limpa area
35 VDP(6)=31: VDP(12)=1: VDP(5)=239
40 CIRCLE(128,105),50,15
50 PAINT(128,95),12,15
65 GOSUB 500
70 ' Le dados de LM
80 E=&HC000
90 READ A$:IF A$="M" THEN 200
100 POKE E,VAL("&H"+A$)
110 E=E+1 : GOTO 90
200 ' Inicia scroll
210 DEFUSR=&HC000
220 X=USR(0)
230 GOTO 220
497 '
498 ' Desenha sprite
499 '
500 VPOKE &HF800,&B0011100
510 VPOKE &HF801,&B0111110
520 VPOKE &HF802,&B1111111
530 VPOKE &HF803,&B1111111
540 VPOKE &HF804,&B1111111
550 VPOKE &HF805,&B1111111
560 VPOKE &HF806,&B0111110
570 VPOKE &HF807,&B0011100
575 VPOKE &HF600,100  ' Y
580 VPOKE &HF601,128  ' X
590 VPOKE &HF602,0    ' Numero sprite
600 FOR F=&HF400 TO &HF40F:VPOKE F,15:NEXT ' Define cor
610 RETURN
997 '
998 ' Codigo de maquina
999 '
1000 DATA 06,00,C5,0E,17,DD,21,2D,01,CD,5F,01,78
1010 DATA C6,64,21,00,F6,DD,21,09,01,CD,5F,01,C1
1020 DATA 10,E6,C9,M

  Compare a velocidade de scroll desse programa com aquele feito todo em Basic. Para isso, acrescente a seguinte linha:
225 FOR Y=255 TO 0 STEP-1:VDP(24)=Y:NEXT
  Os scrolls em Basic e em Assembly se alternam.

  Podemos inserir um delay para a movimentação se tornar mais lenta. É so acrescentar as seguintes linhas na listagem Assembly:
06 14		95        LD B,20
10 FE		96 DELAY: DJZN DELAY

  Altere as linhas do programa em Basic:
1010 DATA C6,64,21,00,F6,DD,21,09,01,CD,5F,01,06
1020 DATA 14,10,FE,C1,10,E2,C9,M

  Através do comando "POKE &HC01A, valor", podemos alterar o tempo de delay. Temos:
205 POKE &HC01A,0 ' Mais lento que 255
ou
205 POKE &HC01A,1 ' Mais rápido
  Obs: de 1 a 255, depois 0 vai ficando mais lento o scroll.



  Referências:

  1- Vertical Scroll (in Basic), em https://www.msx.org/forum/development/msx-development/vertical-scroll-basic.
  2- Programa Sterren (estrelas). MSX Computer Magazine #30, MBI Publications, 1989.
  3- MSX2 Technical Handbook, ASCII Corporation, 1987.