Cómo hacer scroll de una fila completa en Zx Spectrum

Foro dedicado a la programación en todo tipo de sistemas clásicos.
Avatar de Usuario
Bubu
Atari 1040 STf
Atari 1040 STf
Mensajes: 886
Registrado: 04 Abr 2018, 23:10
Sistema Favorito: Spectrum 16Kb/48Kb
primer_sistema: Spectrum 16Kb/48Kb
consola_favorita: Atari 2600
Primera consola: Nintendo GameBoy
Gracias dadas: 20 veces
Gracias recibidas: 60 veces

Re: Cómo hacer scroll de una fila completa en Zx Spectrum

Mensajepor Bubu » 11 Ago 2018, 13:07

Pero entóns tarda el doble, ¿nor? La idea era de alguna manera no hacer dos RRA's sino un RRA doble, idear algo que rote en memoria el doble y luego finalmente imprimir en pantalla el resultado final, no imprimir en pantalla las dos rotaciones de 1 pixel c/u.
Si algo funciona... ¡¡NO LO TOQUES!! ¡¡NI DE COÑA!!

Avatar de Usuario
explorer
MSX Turbo R
MSX Turbo R
Mensajes: 398
Registrado: 11 May 2014, 17:10
Sistema Favorito: Atari ST
primer_sistema: Atari 800XL/600XL
consola_favorita: Atari 2600
Primera consola: Atari 2600
Ubicación: Valladolid, España
Gracias dadas: 2 veces
Gracias recibidas: 138 veces
Contactar:

Re: Cómo hacer scroll de una fila completa en Zx Spectrum

Mensajepor explorer » 11 Ago 2018, 14:04

Una posibilidad sería cambiar CPU por RAM, usando una tabla de las 256 combinaciones posibles de rotación. Eso suma 512 bytes. Pero no te libra de hacer un AND y un OR de los nuevos bits altos. También puedes sustituir el AND por otra tabla, pero son 256 bytes más.

No hay instrucciones de rotación múltiple. Todas son desplazamientos o rotaciones de un solo bit.

Otra posibilidad es: se desplaza dos veces el byte de pantalla y los acarreos se almacenan, también con un desplazamiento, en un registro temporal. Una vez terminada la segunda rotación, hacemos un OR con otro registro temporal que guardaba los bits rotados del anterior byte.

El caso es que si sumas todas las operaciones, salen bastantes más ciclos que la de hacer la rotación de la línea dos veces.

Y aquí es donde nos damos cuenta del porqué se utilizaban las técnicas de prerotación de los sprites. O de solo rotar las zonas de pantalla que contienen algo (rotar el espacio que solo contiene fondo solo supone un gasto de ciclos de CPU).

Seguiremos pensando...

P.D.: existe la instrucción RRD que hace una rotación de nibbles entre los contenidos de los registros HL y A.

Hay otra posibilidad... hacer que dos bytes consecutivos de la memoria de pantalla estén en un registro doble, ya que desplazar y rotarlos es más rápido que rotar la memoria de pantalla y hacer luego el AND y el OR.

Avatar de Usuario
explorer
MSX Turbo R
MSX Turbo R
Mensajes: 398
Registrado: 11 May 2014, 17:10
Sistema Favorito: Atari ST
primer_sistema: Atari 800XL/600XL
consola_favorita: Atari 2600
Primera consola: Atari 2600
Ubicación: Valladolid, España
Gracias dadas: 2 veces
Gracias recibidas: 138 veces
Contactar:

Re: Cómo hacer scroll de una fila completa en Zx Spectrum

Mensajepor explorer » 12 Ago 2018, 03:32

Bueno, esta es mi solución, pero... es un desastre. Ocupa 495 bytes y tarda 16951 ciclos. La versión de un píxel tardaba 5154 ciclos.

Así que mi hipótesis de que es mejor ejecutar dos veces la rutina de un píxel sigue siendo cierta. No sé... habrá que darle una pensada...

Código: Seleccionar todo


; ********************************************************************
;  Scroll de doble pixel para Spectrum
;
;    Demo en asm80.com
;
;    $Date: Sun, 11 agosto 2018 23:34:40 +0100 $
;    $Author: Joaquín Ferrero $
;    $Revision: $
;
; ********************************************************************

;--------------------------------------------------------------------
; Entorno y constantes

        .ENGINE zxs

VIDEORAM    = $4000
TERCIO      = 1
FILA        = 4

              ;[ 010 TT SSS ] - [ RRR CCCCC ]
INICIO      = VIDEORAM | (TERCIO * 2^11) | (FILA * 2^5)


;--------------------------------------------------------------------

        .CSEG
        .ORG    40000
        .ENT    $

;--------------------------------------------------------------------
; Relleno de pantalla con un patrón de prueba

; tamaño del bloque a pintar
ANCHO       = 16 ; en bytes
ALTO        = 8  ; en píxeles

        .BLOCK

            LD      b,ALTO
            LD      hl,INICIO | (16 - ANCHO/2)
            LD      ixh,01111111B
           
LOOPrelleno:
            PUSH    bc

            LD      de,hl
            INC     de
            LD      bc,ANCHO-1

            LD      a,ixh
            LD      (hl),a
            RRCA
            LD      ixh,a

            LDIR

            INC     h
            LD      a,l
            SUB     ANCHO-1
            LD      l,a

            POP     bc
            DJNZ    LOOPrelleno

        .ENDBLOCK

;--------------------------------------------------------------------
; Bucle principal

        .BLOCK

            LD      bc,$0200

LOOP:             
            PUSH    bc
            CALL    ROT2_RIGHT

; bucle de espera
            LD      bc,$1000        ; Cambiar para tener otra espera
WAIT:
            DEC     c
            JR      nz,WAIT
            DJNZ    WAIT

            POP     bc
            DEC     c
            JR      nz,LOOP
            DJNZ    LOOP

            RET

        .ENDBLOCK

;--------------------------------------------------------------------
; Desplazar a la derecha dos píxeles
;--------------------------------------------------------------------

        .BLOCK

@ROT2_RIGHT:                        ;16951;

            LD      hl,INICIO       ;10; comienzo del área a rotar
            LD      c,l             ; 4; recordar número de fila (RRR), columna 0
            LD      b,8             ; 7; número de líneas a rotar

LOOP_SCAN:                          ;16920;
            LD      de,0            ;10; caché píxeles (d=anterior,e=actual)

                                    ; rota las primeras columnas
        .REPT 31                    ;2015;
            rotaYpinta              ;50;

            LD      d,e             ; 4; intercambiamos cachés
            LD      e,0             ; 7;
            INC     l               ; 4; siguiente columna
        .ENDM
                                    ; rota última columna
            rotaYpinta              ;50;

            LD      l,c             ; 4; recuperamos fila, columna 0
            LD      a,(HL)          ; 7; primera columna
            OR      e               ; 4; píxeles que salieron de la última columna
            LD      (hl),a          ; 7; nueva primera columna

            INC     h               ; 4; Siguiente scanline
;            DJNZ    LOOP_SCAN
            DEC     b               ; 4;
            JP      NZ,LOOP_SCAN    ;10;

            RET                     ;10;

        .MACRO rotaYpinta           ;50;
            LD      a,(hl)          ; 7; byte de pantalla
            SRL     a               ; 8; primer píxel
            RR      e               ; 8; guardamos C
            SRL     a               ; 8; segundo píxel
            RR      e               ; 8; guardamos C
            OR      d               ; 4; recuperamos los píxeles rotados del byte anterior
            LD      (hl),a          ; 7; lo ponemos en pantalla
        .ENDM

        .ENDBLOCK

;--------------------------------------------------------------------


Avatar de Usuario
Bubu
Atari 1040 STf
Atari 1040 STf
Mensajes: 886
Registrado: 04 Abr 2018, 23:10
Sistema Favorito: Spectrum 16Kb/48Kb
primer_sistema: Spectrum 16Kb/48Kb
consola_favorita: Atari 2600
Primera consola: Nintendo GameBoy
Gracias dadas: 20 veces
Gracias recibidas: 60 veces

Re: Cómo hacer scroll de una fila completa en Zx Spectrum

Mensajepor Bubu » 12 Ago 2018, 19:43

En resumen, es más rápido desplazar 1 píxel N veces, que desplazar N veces 1 píxel, aunque suene a los mismo. Pues bueno es saberlo. Y por tanto para más velocidad ya sería pre-rotar en tiempo de desarrollo lo que se quiera mostrar. Bueno, voy a tratar de implementar el Frogger a ver cómo se vería con la rutina de desplazamientos normal de 1 píxel. En cuanto lo tenga os comentaré los resultados.

Muchas muchísimas gracias, explorer
Si algo funciona... ¡¡NO LO TOQUES!! ¡¡NI DE COÑA!!

Avatar de Usuario
Bubu
Atari 1040 STf
Atari 1040 STf
Mensajes: 886
Registrado: 04 Abr 2018, 23:10
Sistema Favorito: Spectrum 16Kb/48Kb
primer_sistema: Spectrum 16Kb/48Kb
consola_favorita: Atari 2600
Primera consola: Nintendo GameBoy
Gracias dadas: 20 veces
Gracias recibidas: 60 veces

Re: Cómo hacer scroll de una fila completa en Zx Spectrum

Mensajepor Bubu » 12 Ago 2018, 19:51

Por cierto, un pequeño cálculo de servilleta; voy a ver qué ocuparía en RAM el almacenamiento de las imágenes de la carretera y río predesplazadas:

son 10 filas, por tanto 10 x 8 x 32 = 2.560 bytes

eso sería una posición, como hay que almacenar las 8 pre-rotaciones:

2560 x 8 = 20.480 bytes


Maaaadredediossss... ¡¡20 KB!! Teniendo en cuén que el Spectrum cuenta con menos de 42KB para pograma, nos quedarían 22KB para hacer el pograma. Nu sé yo...
La técnica que usé yo para hacer este juego en su momento estaba mucho más optimizada: tenía almacenada en RAM sólo la imagen de cada objeto por separado, no de toda la fila. Pero era una locura el tratamiento de todo esto. Aun así lo hice, pero la rutina de detección de choques era una paranoia que no veas... En fins, esto ya es otra historia.
Si algo funciona... ¡¡NO LO TOQUES!! ¡¡NI DE COÑA!!

Avatar de Usuario
explorer
MSX Turbo R
MSX Turbo R
Mensajes: 398
Registrado: 11 May 2014, 17:10
Sistema Favorito: Atari ST
primer_sistema: Atari 800XL/600XL
consola_favorita: Atari 2600
Primera consola: Atari 2600
Ubicación: Valladolid, España
Gracias dadas: 2 veces
Gracias recibidas: 138 veces
Contactar:

Re: Cómo hacer scroll de una fila completa en Zx Spectrum

Mensajepor explorer » 13 Ago 2018, 00:25

Y esa es una de las explicaciones por las cuales muchos juegos no hacían desplazamiento borde a borde, o a toda pantalla. Ponían bordes decorativos para reducir el ancho del juego. O solo ocupaban dos de los tres tercios (el otro tercio reservado para marcadores).

Si con la calculadora en la mano te quedas sin memoria, habrá que empezar a recortar. Lo más obvio es no desplazar las partes vacías, pero volvemos a lo que comentas: llevar el control de todos los objetos que están en pantalla.

Bueno, es cuestión de organizarse.

Las rutinas que he escrito, se podrían mejorar en tiempo triplicando el código utilizado para detectar "espacio vacío" (no es necesario desplazar valores 0 o valores 255 o $AAAA o $5555 y demás patrones que se pueden repetir). Mejoraría la velocidad a cambio de aumentar el código. Pero solo valdría la pena si hay pocos objetos en la fila.

Avatar de Usuario
Bubu
Atari 1040 STf
Atari 1040 STf
Mensajes: 886
Registrado: 04 Abr 2018, 23:10
Sistema Favorito: Spectrum 16Kb/48Kb
primer_sistema: Spectrum 16Kb/48Kb
consola_favorita: Atari 2600
Primera consola: Nintendo GameBoy
Gracias dadas: 20 veces
Gracias recibidas: 60 veces

Re: Cómo hacer scroll de una fila completa en Zx Spectrum

Mensajepor Bubu » 14 Ago 2018, 17:45

OK, centrémonos entonces en el caso que propongo: el Frogger. Como ves, hay una carretera y un río. Unos coches van de izquierda a derecha y otros de derecha a izquierda, y cada fila a diferente velocidad. Al río le pasa lo mismo. Además, depende de la fase, salen más o menos coches y más o menos troncos. Pero eso sí, dada una fase, nunca cambian estos parámetros.
Así las cosas, ¿cuál crees que sería el método más óptimo? ¿Imprimir una sola vez en pantalla la carretera y río, y entóns a scrollear píxel a píxel? Se me ocurre otra manera, creo que es la que más velocidad coge:

Tengo los sprites almacenados por un lado. Ahora, pinto en RAM (no en VRAM) cada fila del río predesplazada en todas sus posibilidades (uséase, 8 veces) tomando como gráficos los sprites almacenados y haciendo los desplazamientos en tiempo de ejecución, antes de empezar a jugar la fase. Ahora, tomo de ese buffer la fila con DESPL=0 y la pinto en VRAM. Pasado un tiempo determinado, tomo de ese buffer esa fila pero con DESPL=1 y la pinto en VRAM, encima de la que tenía DESPL=0.

Así que lo único que voy haciendo aquí es LD A, (DE) + LD (HL), A todo el rato. Eso sí, el buffer ocuparía los 10 x 32 x 8 x 8 bytes, es decir, se necesita un buffer de 20KB. Téngase en cuén que el método anterior de hacer scroll de la VRAM de píxel en píxel haría que la propia rana hiciera scroll en la carretera :D Con el buffer no, porque pinto la carretera, pinto la rana, pinto la carretera desplazada, pinto la rana, pinto la carretera más desplazada, pinto la rana, etc, etc.
Si algo funciona... ¡¡NO LO TOQUES!! ¡¡NI DE COÑA!!

Avatar de Usuario
Namek
Atari 1040 STf
Atari 1040 STf
Mensajes: 838
Registrado: 11 Jul 2011, 13:13
Gracias dadas: 18 veces
Gracias recibidas: 63 veces

Re: Cómo hacer scroll de una fila completa en Zx Spectrum

Mensajepor Namek » 14 Ago 2018, 19:26

Muy buenas, voy a darte mi humilde opinión a ver que te parece.

Ya que vas a usar un buffer para las rotaciones, mejor tener los sprites pre-rotados y ya que no van a tener fondo harias la misma transferencia que con el buffer y para colmo te ahorras el tiempo de ejecución de transferir los huecos vacios, por tanto ahorras memoria y tiempo de CPU que puedes usar para los calculos de posicionamiento de los sprites. Que opinas tu? :-k

Avatar de Usuario
explorer
MSX Turbo R
MSX Turbo R
Mensajes: 398
Registrado: 11 May 2014, 17:10
Sistema Favorito: Atari ST
primer_sistema: Atari 800XL/600XL
consola_favorita: Atari 2600
Primera consola: Atari 2600
Ubicación: Valladolid, España
Gracias dadas: 2 veces
Gracias recibidas: 138 veces
Contactar:

Re: Cómo hacer scroll de una fila completa en Zx Spectrum

Mensajepor explorer » 14 Ago 2018, 21:28

Bubu escribió:OK, centrémonos entonces en el caso que propongo: el Frogger. Como ves, hay una carretera y un río. Unos coches van de izquierda a derecha y otros de derecha a izquierda, y cada fila a diferente velocidad. Al río le pasa lo mismo. Además, depende de la fase, salen más o menos coches y más o menos troncos. Pero eso sí, dada una fase, nunca cambian estos parámetros.
Dentro de una fase no cambian los enemigos, pero cada X segundos se oye un sonido como de espita de gas, y los coches y el cocodrilo de la última fila aumentan súbitamente de velocidad. Yo creo que incluso alguna fila multiplica su velocidad por dos.

Hay personajes que solo aparecen de vez en cuando, como la nutria que te muerde si estás en el extremo del tronco. Generalmente, parece que los objetos que desaparecen por un lado, luego aparecen por el otro, pero... a medida que aumenta la fase suelen tardar más en aparecer.

Todos los objetos de una fila se mueven a la misma velocidad, exceptuando la nutria, que a veces da un acelerón. La serpiente aparece sobre un tronco de las primeras filas o en descansillo de la mitad.

Es lo que he visto hasta ahora. Es un juego cuya versión arcade funciona en z80 a 3 Mhz, por lo que, en teoría, sería posible llevarlo al Spectrum. La pantalla tiene una resolución de 224×768.

Bubu escribió:Así las cosas, ¿cuál crees que sería el método más óptimo? ¿Imprimir una sola vez en pantalla la carretera y río, y entóns a scrollear píxel a píxel? Se me ocurre otra manera, creo que es la que más velocidad coge:

Tengo los sprites almacenados por un lado. Ahora, pinto en RAM (no en VRAM) cada fila del río predesplazada en todas sus posibilidades (uséase, 8 veces) tomando como gráficos los sprites almacenados y haciendo los desplazamientos en tiempo de ejecución, antes de empezar a jugar la fase. Ahora, tomo de ese buffer la fila con DESPL=0 y la pinto en VRAM. Pasado un tiempo determinado, tomo de ese buffer esa fila pero con DESPL=1 y la pinto en VRAM, encima de la que tenía DESPL=0.
Hay que borrar lo pintado, claro. Un truco sería definir un borde negro a los sprites de un ancho igual a la máxima velocidad que puede alcanzar. O usar un método de borrado tan sencillo como pintar 8 bytes 0 siempre.

El método que describes es lo que se conoce como doble búfer. Se puede hacer, desde luego, pero luego hay que ver cuánto tarda el micro en volcarlo todo a pantalla. ¿Y eso implica que también debe volcar las zonas vacías? Quizás perdemos las ventajas.

Los sistemas de doble búfer más inteligentes solo borran las diferencias entre cuadros y pintan solo lo que hay de nuevo. No es algo.... sencillo.

Bubu escribió:Así que lo único que voy haciendo aquí es LD A, (DE) + LD (HL), A todo el rato. Eso sí, el buffer ocuparía los 10 x 32 x 8 x 8 bytes, es decir, se necesita un buffer de 20KB. Téngase en cuén que el método anterior de hacer scroll de la VRAM de píxel en píxel haría que la propia rana hiciera scroll en la carretera :D Con el buffer no, porque pinto la carretera, pinto la rana, pinto la carretera desplazada, pinto la rana, pinto la carretera más desplazada, pinto la rana, etc, etc.
Un LDIR podría servir para mover una línea entera del sprite, de (HL)++ a (DE)++.

El que haya una rana (y si luego pones a la rana macho, también) ya implica que no puedes hacer scroll en la parte de abajo. Sí en la de arriba, ya que todo se mueve. De todas maneras, yo creo que lo mejor es no desplazar toda la fila y pensar más en términos de Sprites.

Bueno... si el juego se hace sencillo, podría valer un desplazamiento de toda la fila, claro.

Avatar de Usuario
explorer
MSX Turbo R
MSX Turbo R
Mensajes: 398
Registrado: 11 May 2014, 17:10
Sistema Favorito: Atari ST
primer_sistema: Atari 800XL/600XL
consola_favorita: Atari 2600
Primera consola: Atari 2600
Ubicación: Valladolid, España
Gracias dadas: 2 veces
Gracias recibidas: 138 veces
Contactar:

Re: Cómo hacer scroll de una fila completa en Zx Spectrum

Mensajepor explorer » 14 Ago 2018, 23:27

Creo que en una de las Microhobby se comentaba que había un límite en el número de sprites de 16×16 de la rutina que publicaron, a partir del cual se notaba más el parpadeo.

Si consigues que la rutina de pintado sea muy rápida, te puedes ahorrar el pintado de algún sprite porque aún estará en el mismo píxel.


Volver a “Programación”

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 8 invitados