Rutinas de impresión de sprites - Terminadas 21/07/2011

Subforo oficial del Sinclair QL: realiza aquí las consultas relativas a tu QL.

Moderador: Sir Cilve Sinclair

Avatar de Usuario
radastan
Phantomas
Mensajes: 2232
Registrado: Lun May 07, 2007 5:34 pm
Contactar:

Rutinas de impresión de sprites - Terminadas 21/07/2011

Mensaje por radastan » Mar Nov 04, 2008 11:26 am

Os expongo a continuación, por partes, una explicación de la rutina que estoy haciendo para usar sprites en modo 8. Para empezar os dejo con la introducción, si véis algún error decirlo y se corrige:



MODO GRAFICO
______________________________________

El modo gráfico que vamos a usar es el 8. Si queremos incializar la pantalla y usarla al completo debemos hacer

desde BASIC:

10 MODE 8
20 WINDOW 512, 256, 0, 0

Con la línea 10 escogemos el modo 8, que es de 256x256 pixels de resolución con cada pixel de un color (de 8 posibles+flash). Debido a que el BASIC del QL siempre piensa en el modo más alto de resolución, debemos abrir una ventana de juego como si estuviéramos en dicho modo, de ahí la línea 20, que abre una ventana desde la esquina 0,0

(x,y) a la 512,256.

Con estas dos líneas ya tenemos preparada la pantalla para empezar, aunque si hacemos un:

30 CLS

Tampoco viene mal.


DEFINICION DEL SPRITE
______________________________________

Desde BASIC hay que cargar cada sprite en memoria, para lo cual primero hay que reservar espacio:

90 SPRITE=RESPR (x)

donde x es el número de bytes a reservar. Tengamos en cuenta que los pixels en el modo 8 van agrupados de 4 en 4 (dos bytes que forman una palabra), por lo que el sprite debe ser múltiplo de 2 bytes.

Para la carga del sprite en si tenemos dos formas: desde BASIC o leyendo desde disco

Si lo hacemos desde BASIC hay que cargarlo con un DATA, ejemplo con un sprite de 4x4 pixels:

100 DATA 7,255,512,128

La rutina de carga en memoria sería:

110 RESTORE 100
120 FOR I=1 to 4 SETP 1
130 READ B
140 POKE_W, I, B
150 END FOR I

Ojo, tal y como vemos, al pokear con POKE_W, tenemos que hacerlo por pares de bytes, es decir una palabra de 16 bits (por eso hemos dicho que lo suyo es usar múltiplos de 4 pixels, dos bytes).

Este mismo ejemplo cargado desde microdrive puede ser:

100 LBYTES "mdv1_sprite", SPRITE

Es evidente que es mucho más práctico este último método, ya que podemos coger nuestro programa favorito de diseño y evitarnos engorros. Basta recordar que el flash lo vamos a usar como máscara...


EL SPRITE
______________________________________

Como somos muy listos vamos a realizar un truco para incluir en el propio sprite la máscara sin necesitar bytes adicionales. ¿Cómo? pues usando el bit de flash como máscara, ya que es muy improbable que queramos usar dicho byte.

Si el bit de máscara es 0 (flash desactivado) se imprimirá el valor de color del sprite.
Si el bit de máscara es 1 (flash activado) no se imprimirá nada y se respetará el pixel actual de fondo.

La razón no es otra que comprobar dicho byte en la rutina, y si es 1 se saltará la parte que imprime el pixel.

Por lo tanto, resumiendo todo lo visto:

- Los sprites tienen que ser múltiplos de 4 pixels de ancho
- Cada palabra (dos bytes) contiene 4 pixels
- El bit de flash lo usaremos como máscara, si es 1 no borrará el fondo, si es 0 imprimirá el sprite

Si no se pretende aprender ensamblador o trastear el mapa de pantalla con POKE se puede uno saltar el siguiente epígrafe.


LA PANTALLA
______________________________________

Para crear nuestra rutina de sprites debemos saber como es la pantalla del QL desde el punto de vista del procesador. Olvidad el BASIC porque esto es otro Mundo.

El comienzo del mapa de pantala está en 131072 (20000 en hexadecimal), es decir, al comienzo de la RAM.

Si nos centramos en el modo 8 nos encontramos que la pantalla está dividida en 256x256 pixels, con cada pixel ocupando 4 bits y posibilitando 8 colores más flash... pero, y aquí es cuando queremos matar a los ingenieros de Sincair, cada pixel no está definido como debería ser lo lógico en 4 bits agrupados.

Me explico, los pixels están agrupados en pantalla de 4 en 4, con la información mezclada en una palabra de 16 bits (dos bytes). Hay dos formas de comprenderlo, por pixel o por bit. Veamos primero por pixel, donde el pixel 1 es el de la izquierda y el pixel 4 el de la derecha:

PIXEL 1:

bit 15: verde (G) - RGB es el valor del color en esta norma
bit 14: flash (F) - Este es el bit de Flash
bit 7: rojo (R)
bit 6: azul (B)

PIXEL 2:

bit 13: verde (G)
bit 12: flash (F)
bit 5: rojo (R)
bit 4: azul (B)

PIXEL 3:

bit 11: verde (G)
bit 10: flash (F)
bit 3: rojo (R)
bit 2: azul (B)

PIXEL 4:

bit 9: verde (G)
bit 8: flash (F)
bit 1: rojo (R)
bit 0: azul (B)

El bit 0 es el menos significativo de los 16 bits.

Si los agrupamos por bits:

VERDE:

bit 15: pixel 1
bit 13: pixel 2
bit 11: pixel 3
bit 9: pixel 4

FLASH:

bit 14: pixel 1
bit 12: pixel 2
bit 10: pixel 3
bit 8: pixel 4

ROJO:

bit 7: pixel 1
bit 5: pixel 2
bit 3: pixel 3
bit 1: pixel 4

AZUL:

bit 6: pixel 1
bit 4: pixel 2
bit 2: pixel 3
bit 0: pixel 4

Y ahora es cuando debemos tomar la terrible decisión... ¿cuál será la precisión de la rutina? es decir, con cual precisión vamos a imprimir los sprites en pantalla. Si usamos palabras completas los sprites se moverán cada 4 pixels (nada mal para usarlo desde BASIC), pero si queremos usar mayor precisión debemos entrar en una rutina que vaya situando los bits en el orden correcto.

Empecemos por lo fácil...

LA RUTINA
______________________________________

Bueno, comenzamos a programar el asunto y aprovecho de paso para dar unas lecciones de ensamblador.

COMO ACABAR LA RUTINA

Si, lo último es lo primero, y para hacer bien la rutina debemos saber como volver a BASIC sin liarla. Para ello basta hacer:

Código: Seleccionar todo

RUTINA:

MOVEQ   #0,D0
RTS

END RUTINA


Explico lo que hay:

- Con "RUTINA:" y "END RUTINA" delimitamos donde está la rutina. Se trata de decirle al ensamblador que parte del texto del editor es lo que vamos a ensamblar.
- "MOVEQ #0,D0" y "RTS" devuelven el control al Superbasic y dan por finalizada la ejecución del código máquina. Estamos cargando en el registro D0 e valor 0 para indicar que no ha habido ningún error, y con RTS salimos de la ejecución.

PASANDO VARIABLES

Para pasar variables a la rutina en ensamblador basta realizar una llamada a la misma con las variables de paso tras comas. Ejemplo:

CALL rutina, x, y, xx, yy, sprite

Aquí x e y sería la posición en pixels de la esquina superior izquierda del sprite, xx e yy indicarían el tamaño del sprite., y "sprite" sería la posición de memoria donde almacenamos el sprite a dibujar en pantalla.

Cuando lo hacemos así el código máquina mete los valores que pasemos a la llamada en un orden determinado dentro de los registros internos del procesador:

D1, D2, D3, D4, D5, D6, D7, A0, A1, A2, A3, A4, A5

No debemos usar D0, A6, y A7, ya que se usan para el SuperBasic (D0 es el retorno, por ejemplo).

PREPARANDO REGISTROS

Ya estamos en condiciones de iniciar nuestra rutina, y para ello debemos preparar los registros con los que vamos a trabajar.

Código: Seleccionar todo

LSR   #2,D1      ;Dividimos la posición X entre cuatro (0-64)
            ;ya que los pixels van agrupados en 4 en cada palabra
LEA.L   $20000,A0   ;Ponemos en A0 el comienzo de la pantalla
MOVE.L   D5, A1   ;Ponemos en A1 la posición del sprite


Para empezar, debido a que los pixels van mezclados de 4 en 4 en una palabra de 16 bits (hablamos del modo 8), tenemos que dividir la posición x entre cuatro para apuntar correctamente a donde tenemos que poner el sprite horizontalmente. En esta primera versión de la rutina vamos a hacerlo así por sencillez (y velocidad), más adelante tenemos tiempo de mejorar la cosa. El mejor truco para dividir por 4 en ensamblador es desplazar a la derecha dos bits la palabra, si entendéis un poco de binario os daréis cuenta que es así.

También preparamos los registros A0 y A1, para situar la posición inicial de pantalla y la del sprite.

Ahora hay que jugar con los registros para ir metiendo cada cosa en su lugar...

RUTINAS!!!

Bueno, os dejo la rutina final sin máscara, arreglada por McLeod porque cometí un par de errores imperdonables.

Código: Seleccionar todo

*-----------------------------------------------------------
* Autor         : Mc Leod
* Descripcion   : Rutina de sprites
*-----------------------------------------------------------
* X es la posición donde queremos poner el Sprite (0 izquierda) (múltiplo de 4)
* Y es la posición donde queremos poner el sprite (0 arriba)
* XX es el tamaño horizontal del Sprite (múltiplo de 4)
* YY es el tamaño vertical del Sprite
* SPRITE es la dirección de memoria del Sprite
*
* ENTRADA: X (D1), Y (D2), TamX (D3), TamY (D4), SPRITE (D5)

PonSprite
   move.l #131072,a0  ; a0 <- comienzo de la pantalla
   movea.l d5,a1  ; a1 <- comienzo de la definicion del sprite
   lsr.l #1,d1 ; X dividido entre 2
   andi.b #$fe,d1 ; y nos aseguramos de que sea par
   lsr #2,d3 ; TamX dividido entre 4
   subq #1,d3 ; restamos 1 a TamX porque asi lo necesitan los DBRA
   subq #1,d4 ; y lo mismo con TamY.
   add.l d1,a0 ; sumamos X a comienzo de pantalla
   lsl #7,d2 ; un scan de la pantalla tiene 128 bytes
   adda.l d2,a0 ; a0 apunta a la coordenada X,Y
   move.l d3,d5 ; d5 es backup de d3
   move.l a0,d6 ; d6 es backup de a0

PonScan
   move.l d5,d3
   move.l d6,a0

Pon4pixels  ;o pon 8, si estamos en modo 4
   move.w (a1)+,(a0)+
   dbra d3,Pon4pixels
   
   addi.l #128,d6
   dbra d4,PonScan
   
   moveq #0,d0
   rts

        end PonSprite


Creo que no hace falta decir mucho más, funciona a las mil maravillas.

Podéis descargar el ejemplo de McLeod, con el código fuente, los binarios, etc, en:

http://www.bytemaniacos.com/ficheros/sinclairql/rutinas/ejemplo_sprites.zip

### RUTINA DE SPRITES USANDO MÁSCARA ###

Ya sabéis que McLeod es de los que les gusta hacer las cosas bien, y me mandó otra versión que cogía otro sprite como máscara. La máscara es la clásica donde 1 es la zona que queremos mantener y 0 la que vamos a borrar. Normalmente debería ser una silueta que envuelve al sprite.

Código: Seleccionar todo

;---------------------------------------------------------------------------------
* X es la posición donde queremos poner el Sprite (0 izquierda) (múltiplo de 4)
* Y es la posición donde queremos poner el sprite (0 arriba)
* TamX es el tamaño horizontal del Sprite (múltiplo de 4)
* TamY es el tamaño vertical del Sprite
* SPRITE es la dirección de memoria del Sprite
* Rutina pensada para MODE 8, usando el bit de flash como transparencia.
* ENTRADA: X (D1), Y (D2), TamX (D3), TamY (D4), Sprite (D5), Máscara (D6), Pantalla (D7)

SpriteTrans8
   movea.l d7,a0  ; a0 <- comienzo de la pantalla
   movea.l d5,a1  ; a1 <- comienzo de la definicion del sprite
   movea.l d6,a2  ; a2 <- comienzo de la definición de la máscara del sprite
   lsr.l #1,d1 ; X dividido entre 2
   andi.b #$fe,d1 ; y nos aseguramos de que sea par
   lsr #2,d3 ; TamX dividido entre 4
   subq #1,d3 ; restamos 1 a TamX porque asi lo necesitan los DBRA
   subq #1,d4 ; y lo mismo con TamY.
   add.l d1,a0 ; sumamos X a comienzo de pantalla
   lsl #7,d2 ; un scan de la pantalla tiene 128 bytes
   adda.l d2,a0 ; a0 apunta a la coordenada X,Y
   move.l d3,d5 ; d5 es backup de d3
   move.l a0,d6 ; d6 es backup de a0

PonScanT8
   move.l d5,d3
   move.l d6,a0

Pon4pixelsT8
   move.w (a0),d0    ; D0 contiene el cacho de pantalla que va a usarse
   move.w (a1)+,d1   ; D1 contiene el cacho de sprite a mostrar
   move.w (a2)+,d2   ; D2 va a ser una máscara para determinar qué bits del sprite se quedan

   and.w d2,d1      ; Enmascaramos el sprite
   not.w d2         ; Negamos la mascara...
   and.w d2,d0      ; ... y la aplicamos a la pantalla
   or.w d1,d0       ; juntamos sprite enmascarado con pantalla enmascarada
   move.w d0,(a0)+  ; y lo plantamos en la pantalla

   dbra d3,Pon4pixelsT8
   
   addi.l #128,d6
   dbra d4,PonScanT8
   
   moveq #0,d0
   rts
;---------------------------------------------------------------------------------

* X es la posición donde queremos poner el Sprite (0 izquierda) (múltiplo de 8)
* Y es la posición donde queremos poner el sprite (0 arriba)
* TamX es el tamaño horizontal del Sprite (múltiplo de 8)
* TamY es el tamaño vertical del Sprite
* SPRITE es la dirección de memoria del Sprite
* Rutina pensada para MODE 8, usando el bit de flash como transparencia.
* ENTRADA: X (D1), Y (D2), TamX (D3), TamY (D4), Sprite (D5), Máscara (D6), Pantalla (D7)

SpriteTrans4
   movea.l d7,a0  ; a0 <- comienzo de la pantalla
   movea.l d5,a1  ; a1 <- comienzo de la definicion del sprite
   movea.l d6,a2  ; a2 <- comienzo de la definición de la máscara del sprite
   lsr.l #2,d1 ; X dividido entre 4
   andi.b #$fe,d1 ; y nos aseguramos de que sea par
   lsr #3,d3 ; TamX dividido entre 8
   subq #1,d3 ; restamos 1 a TamX porque asi lo necesitan los DBRA
   subq #1,d4 ; y lo mismo con TamY.
   add.l d1,a0 ; sumamos X a comienzo de pantalla
   lsl #7,d2 ; un scan de la pantalla tiene 128 bytes
   adda.l d2,a0 ; a0 apunta a la coordenada X,Y
   move.l d3,d5 ; d5 es backup de d3
   move.l a0,d6 ; d6 es backup de a0

PonScanT4
   move.l d5,d3
   move.l d6,a0

Pon4pixelsT4
   move.w (a0),d0    ; D0 contiene el cacho de pantalla que va a usarse
   move.w (a1)+,d1   ; D1 contiene el cacho de sprite a mostrar
   move.w (a2)+,d2   ; D2 va a ser una máscara para determinar qué bits del sprite se quedan

   and.w d2,d1      ; Enmascaramos el sprite
   not.w d2         ; Negamos la mascara...
   and.w d2,d0      ; ... y la aplicamos a la pantalla
   or.w d1,d0       ; juntamos sprite enmascarado con pantalla enmascarada
   move.w d0,(a0)+  ; y lo plantamos en la pantalla

   dbra d3,Pon4pixelsT4
   
   addi.l #128,d6
   dbra d4,PonScanT4
   
   moveq #0,d0
   rts
;---------------------------------------------------------------------------------

ClearScreen
   ;D1 = direccion de comienzo de la pantalla a borrar
   move.l #8191,d2
   movea.l d1,a0
BucleClear
   move.l #0,(a0)+
   dbra d2,BucleClear
   moveq #0,d0
   rts

   
        end 0


Podéis descargar el ejemplo de McLeod, con el código fuente, los binarios, etc, en:

http://www.bytemaniacos.com/ficheros/sinclairql/rutinas/gusano.zip

Contenido del fichero:
- El fichero .C convierte un bitmap de 24 bits en formato RAW a dos ficheros: el sprite y la máscara. En el bitmap original, cualquier pixel que tenga como color el (128,0,128) será tratado como transparente.
- Un sprite de 32x64 píxeles (para que se vea bien en modo 8) en formato PNG
- El mismo sprite, pero en formato RAW
- Los dos ficheros _bin que salen al procesar el RAW por el programa C: el sprite y la máscara.
- Código fuente de las rutinas empleadas.
- Código bnario de dichas rutinas.
- Programa en SuperBASIC que implementa la demo.

Para que el programa funcione bien, la segunda pantalla debe estar disponible. Esto significa que hay que arrancar con Minerva usando F4 (modo 8, doble pantalla). Con la ROM original no se puede, ya que pone las variables del sistema en la pantalla secundaria.

Y para finalizar, un gran aplauso a McLeod por la proeza, ahora ya no hay excusa para programar juegos desde BASIC con una calidad gráfica decente.
Última edición por radastan el Jue Jul 21, 2011 10:26 pm, editado 12 veces en total.
_________________________________________
Hay otras páginas.... pero no son Bytemaniacos
http://www.bytemaniacos.com
Orgullo de 8 bits
_________________________________________

Avatar de Usuario
badaman
Sabreman
Mensajes: 499
Registrado: Mar Ene 29, 2008 10:58 am
Contactar:

Re: RUTINA DE IMPRESION DE SPRITES

Mensaje por badaman » Mar Nov 04, 2008 2:43 pm

Dos apuntes:

El uso de WINDOW no es necesario, pues accedes directamente a la memoria de pantalla, es decir, que los sprites camparán libremente por toda la pantalla sin limitaciones de ventanas.

En la línea: 90 SPRITE=RESPR (x) , "x" indica el número de bytes, no el de palabras.
Sinclair QL, la respuesta profesional de los 80

Avatar de Usuario
radastan
Phantomas
Mensajes: 2232
Registrado: Lun May 07, 2007 5:34 pm
Contactar:

Re: RUTINA DE IMPRESION DE SPRITES

Mensaje por radastan » Mar Nov 04, 2008 2:54 pm

badaman escribió:Dos apuntes:

El uso de WINDOW no es necesario, pues accedes directamente a la memoria de pantalla, es decir, que los sprites camparán libremente por toda la pantalla sin limitaciones de ventanas.

En la línea: 90 SPRITE=RESPR (x) , "x" indica el número de bytes, no el de palabras.


El uso de WINDOW si es necesario ya que es una rutina para usar desde BASIC... por lo que si vas a usar rutinas de imrepsión (ej. de texto) deberás usar esa línea.

Respecto a lo de RESPR, ok, no me quedaba claro.

Lo acabo de modificar, dime si es correcto.
_________________________________________
Hay otras páginas.... pero no son Bytemaniacos
http://www.bytemaniacos.com
Orgullo de 8 bits
_________________________________________

Avatar de Usuario
badaman
Sabreman
Mensajes: 499
Registrado: Mar Ene 29, 2008 10:58 am
Contactar:

Re: RUTINA DE IMPRESION DE SPRITES

Mensaje por badaman » Mar Nov 04, 2008 3:02 pm

Entiendo que uses MODE 8 para cambiar de modo de pantalla desde SuperBASIC, pero no veo clara la utilidad de WINDOW en la rutina para sprites, pues la rutina nunca va a limitarse a las definiciones de ventanas del usuario.

Si lo que estás haciendo es un programa en el que quieres borrar todo el área de pantalla, entonces es otra cosa. Pero aquí estas hablando de la rutina del sprite.

Como cada byte va a tener 2 pixels lo suyo es hacer sprites múltiplos de dicha cantidad.


Según la distribución de pantalla, son necesarios dos bytes para definir cualquier pixel. Lo que deberías indicar aquí es que el valor de x debe ser múltiplo de 2.

Supongo que lo que realmente querias decir es que cada pixel va a ocupar parte de dos bytes, y lo suyo es hacer sprites múltiplos de 2.
Sinclair QL, la respuesta profesional de los 80

Avatar de Usuario
radastan
Phantomas
Mensajes: 2232
Registrado: Lun May 07, 2007 5:34 pm
Contactar:

Re: RUTINA DE IMPRESION DE SPRITES

Mensaje por radastan » Mar Nov 04, 2008 4:09 pm

badaman escribió:Entiendo que uses MODE 8 para cambiar de modo de pantalla desde SuperBASIC, pero no veo clara la utilidad de WINDOW en la rutina para sprites, pues la rutina nunca va a limitarse a las definiciones de ventanas del usuario.

Si lo que estás haciendo es un programa en el que quieres borrar todo el área de pantalla, entonces es otra cosa. Pero aquí estas hablando de la rutina del sprite.

Como cada byte va a tener 2 pixels lo suyo es hacer sprites múltiplos de dicha cantidad.


Según la distribución de pantalla, son necesarios dos bytes para definir cualquier pixel. Lo que deberías indicar aquí es que el valor de x debe ser múltiplo de 2.

Supongo que lo que realmente querias decir es que cada pixel va a ocupar parte de dos bytes, y lo suyo es hacer sprites múltiplos de 2.


Efectivamente, lo de WINDOWS es para poder hacer CLS tranquilamente, pero vamos, que se peude usar al gusto de cada uno porque la rutina se salta las ventanas.

Respecto a lo de que x debe ser múltiplo de 2, vamos a ver... la pantalla se divide en palabras de 16 bits, ¿no? eso son 2 bytes, por lo que realmente lo cómodo de cara a la rutina en ensamblador es hacer todo en múltiplos de 4 pixels (dos bytes, o una palabra).

¿A eso te refieres?

De todas formas esto es un borador, ahora mismo le estoy metiendo mano a la rutina para que se complemente con el texto (por ejemplo, me viene mejor que la máscara fuese con flash apagado, pero es más cómodo de cara al usuario lo contrario). De cara a la rutina puedo usar byts, palabras de 16 bits, o dobles palabras de 32 bits, por lo que puedo elegir. Evidentemente lo más rápido es usar 32 bits, lo que nos da 8 pixels de anchura, pero eso implica que el movimiento del sprite será de 8 en 8 pixels, algo brusco, y es mucho mejor pensar en múltiplos de 2 o 4 aún cuando signifique una rutina un pelín más lenta.

A ver que sale.
_________________________________________
Hay otras páginas.... pero no son Bytemaniacos
http://www.bytemaniacos.com
Orgullo de 8 bits
_________________________________________

Avatar de Usuario
badaman
Sabreman
Mensajes: 499
Registrado: Mar Ene 29, 2008 10:58 am
Contactar:

Re: RUTINA DE IMPRESION DE SPRITES

Mensaje por badaman » Mar Nov 04, 2008 4:25 pm

Eso me pasa por ponertelo a huevo XD

A ver, si hablas de pixels, lo mínmimo imprescindible para definirlos son 2 bytes.

Otra cosa es lo que mejor te venga a ti a la hora de hacer la rutina.

Por otra parte, cualquier multiplo de 4 lo es también de 2.

Añadido posterior:

Respecto a lo de que x debe ser múltiplo de 2, vamos a ver... la pantalla se divide en palabras de 16 bits, ¿no? eso son 2 bytes, por lo que realmente lo cómodo de cara a la rutina en ensamblador es hacer todo en múltiplos de 4 pixels (dos bytes, o una palabra).

¿A eso te refieres?


Veo que has cambiado el texto. Efectivamente. como mínimo deben ser 2 bytes, osea, multiplo de 2, no una palabra larga o multiplo de 4 como decias.

Deberá ser también mútiplo de 4 si te interesa a ti para tu rutina, pero no necesariamente para almacenar datos de pantalla.
Última edición por badaman el Mar Nov 04, 2008 5:45 pm, editado 1 vez en total.
Sinclair QL, la respuesta profesional de los 80

Avatar de Usuario
radastan
Phantomas
Mensajes: 2232
Registrado: Lun May 07, 2007 5:34 pm
Contactar:

Re: RUTINA DE IMPRESION DE SPRITES

Mensaje por radastan » Mar Nov 04, 2008 4:57 pm

badaman escribió:Eso me pasa por ponertelo a huevo XD
A ver, si hablas de pixels, lo mínmimo imprescindible para definirlos son 2 bytes.


Nuevamente hablas de palabras... cuando puedo acceder a la memoria desde ensamblador como bytes. Te vuelvo a decir que puedo usar direccionamiento como byte (dos pixels), que no es imprescindible hacerlo como palabra. Eso no quita que sea más fácil y rápido hacerlo como palabra (dos bytes cuatro pixels).

Espera un par de horillas y te enseño lo que llevo, y ya decidimos si usar 2,4,8 o mil millones de pixels. :lol:
_________________________________________
Hay otras páginas.... pero no son Bytemaniacos
http://www.bytemaniacos.com
Orgullo de 8 bits
_________________________________________

Avatar de Usuario
badaman
Sabreman
Mensajes: 499
Registrado: Mar Ene 29, 2008 10:58 am
Contactar:

Re: RUTINA DE IMPRESION DE SPRITES

Mensaje por badaman » Mar Nov 04, 2008 5:09 pm

A ver Radas, si cada pixel esta definido por 4 bits, y cada byte contiene sólo 2 bits de esos 4, si vas a pintar un pixel necesitas almacenar una palabra (dos bytes) si o si.

Recuerda como se almacena la info de los bits: http://www.speccy.org/sinclairql/man/sqlpa/apE.htm

Dirección par: GFGFGFGF
Dirección impar: RBRBRBRB

Cada letra es un bit que indica su componente de color:

G=Green
F=Flash
R=Red
B=Blue

Pero es que estas hablando del valor que debe tener "x" en la línea:

90 SPRITE=RESPR (x)

y "x" debe ser multiplo de 2 por fuerza si lo que quieres es almacenar información que contenga pixels de pantalla.
Última edición por badaman el Mar Nov 04, 2008 5:39 pm, editado 1 vez en total.
Sinclair QL, la respuesta profesional de los 80

Avatar de Usuario
radastan
Phantomas
Mensajes: 2232
Registrado: Lun May 07, 2007 5:34 pm
Contactar:

Re: RUTINA DE IMPRESION DE SPRITES

Mensaje por radastan » Mar Nov 04, 2008 5:32 pm

Lo se, pero es que no es eso lo quetrato de decir. Una cosa es lo que haga la rutina a nivel interno y otra de cara al usuario.

Un momentillo y publico la parte del tutorial dedicado a la pantalla.
_________________________________________
Hay otras páginas.... pero no son Bytemaniacos
http://www.bytemaniacos.com
Orgullo de 8 bits
_________________________________________

Avatar de Usuario
badaman
Sabreman
Mensajes: 499
Registrado: Mar Ene 29, 2008 10:58 am
Contactar:

Re: RUTINA DE IMPRESION DE SPRITES

Mensaje por badaman » Mar Nov 04, 2008 5:38 pm

Vale, pero entonces tú has cambiado de tema, porque estamos hablando de lo que se va a almacenar con RESPR y no de como va a funcionar tu rutina, algo a lo que no le he puesto pegas en ningún momento.
Última edición por badaman el Mar Nov 04, 2008 5:47 pm, editado 1 vez en total.
Sinclair QL, la respuesta profesional de los 80

Avatar de Usuario
radastan
Phantomas
Mensajes: 2232
Registrado: Lun May 07, 2007 5:34 pm
Contactar:

Re: RUTINA DE IMPRESION DE SPRITES

Mensaje por radastan » Mar Nov 04, 2008 5:47 pm

Aclaro un poco el tema, y de paso amplio un poco el tutorial.
_________________________________________
Hay otras páginas.... pero no son Bytemaniacos
http://www.bytemaniacos.com
Orgullo de 8 bits
_________________________________________

Avatar de Usuario
radastan
Phantomas
Mensajes: 2232
Registrado: Lun May 07, 2007 5:34 pm
Contactar:

Re: RUTINA DE IMPRESION DE SPRITES

Mensaje por radastan » Lun Jun 28, 2010 8:20 pm

Bueno, comenzamos a programar el asunto y aprovecho de paso para dar unas lecciones de ensamblador.

COMO ACABAR LA RUTINA

Si, lo último es lo primero, y para hacer bien la rutina debemos saber como volver a BASIC sin liarla. Para ello basta hacer:

Código: Seleccionar todo

RUTINA:

MOVEQ   #0,D0
RTS

END RUTINA


Explico lo que hay:

- Con "RUTINA:" y "END RUTINA" delimitamos donde está la rutina. Se trata de decirle al ensamblador que parte del texto del editor es lo que vamos a ensamblar.
- "MOVEQ #0,D0" y "RTS" devuelven el control al Superbasic y dan por finalizada la ejecución del código máquina. Estamos cargando en el registro D0 e valor 0 para indicar que no ha habido ningún error, y con RTS salimos de la ejecución.

PASANDO VARIABLES

Para pasar variables a la rutina en ensamblador basta realizar una llamada a la misma con las variables de paso tras comas. Ejemplo:

CALL rutina, x, y, xx, yy, sprite

Aquí x e y sería la posición en pixels de la esquina superior izquierda del sprite, xx e yy indicarían el tamaño del sprite., y "sprite" sería la posición de memoria donde almacenamos el sprite a dibujar en pantalla.

Cuando lo hacemos así el código máquina mete los valores que pasemos a la llamada en un orden determinado dentro de los registros internos del procesador:

D1, D2, D3, D4, D5, D6, D7, A0, A1, A2, A3, A4, A5

No debemos usar D0, A6, y A7, ya que se usan para el SuperBasic (D0 es el retorno, por ejemplo).

PREPARANDO REGISTROS

Ya estamos en condiciones de iniciar nuestra rutina, y para ello debemos preparar los registros con los que vamos a trabajar.

Código: Seleccionar todo

LSR   #2,D1      ;Dividimos la posición X entre cuatro (0-64)
            ;ya que los pixels van agrupados en 4 en cada palabra
LEA.L   $20000,A0   ;Ponemos en A0 el comienzo de la pantalla
MOVE.L   D5, A1   ;Ponemos en A1 la posición del sprite


Para empezar, debido a que los pixels van mezclados de 4 en 4 en una palabra de 16 bits (hablamos del modo 8), tenemos que dividir la posición x entre cuatro para apuntar correctamente a donde tenemos que poner el sprite horizontalmente. En esta primera versión de la rutina vamos a hacerlo así por sencillez (y velocidad), más adelante tenemos tiempo de mejorar la cosa. El mejor truco para dividir por 4 en ensamblador es desplazar a la derecha dos bits la palabra, si entendéis un poco de binario os daréis cuenta que es así.

También preparamos los registros A0 y A1, para situar la posición inicial de pantalla y la del sprite.

Ahora hay que jugar con los registros para ir metiendo cada cosa en su lugar...
_________________________________________
Hay otras páginas.... pero no son Bytemaniacos
http://www.bytemaniacos.com
Orgullo de 8 bits
_________________________________________

Avatar de Usuario
radastan
Phantomas
Mensajes: 2232
Registrado: Lun May 07, 2007 5:34 pm
Contactar:

Re: Rutina de impresión de sprites - act. 21/07/2011

Mensaje por radastan » Jue Jul 21, 2011 10:24 pm

Bueno, os dejo la rutina final sin máscara, arreglada por McLeod porque cometí un par de errores imperdonables.

Código: Seleccionar todo

*-----------------------------------------------------------
* Autor         : Mc Leod
* Descripcion   : Rutina de sprites
*-----------------------------------------------------------
* X es la posición donde queremos poner el Sprite (0 izquierda) (múltiplo de 4)
* Y es la posición donde queremos poner el sprite (0 arriba)
* XX es el tamaño horizontal del Sprite (múltiplo de 4)
* YY es el tamaño vertical del Sprite
* SPRITE es la dirección de memoria del Sprite
*
* ENTRADA: X (D1), Y (D2), TamX (D3), TamY (D4), SPRITE (D5)

PonSprite
   move.l #131072,a0  ; a0 <- comienzo de la pantalla
   movea.l d5,a1  ; a1 <- comienzo de la definicion del sprite
   lsr.l #1,d1 ; X dividido entre 2
   andi.b #$fe,d1 ; y nos aseguramos de que sea par
   lsr #2,d3 ; TamX dividido entre 4
   subq #1,d3 ; restamos 1 a TamX porque asi lo necesitan los DBRA
   subq #1,d4 ; y lo mismo con TamY.
   add.l d1,a0 ; sumamos X a comienzo de pantalla
   lsl #7,d2 ; un scan de la pantalla tiene 128 bytes
   adda.l d2,a0 ; a0 apunta a la coordenada X,Y
   move.l d3,d5 ; d5 es backup de d3
   move.l a0,d6 ; d6 es backup de a0

PonScan
   move.l d5,d3
   move.l d6,a0

Pon4pixels  ;o pon 8, si estamos en modo 4
   move.w (a1)+,(a0)+
   dbra d3,Pon4pixels
   
   addi.l #128,d6
   dbra d4,PonScan
   
   moveq #0,d0
   rts

        end PonSprite


Creo que no hace falta decir mucho más, funciona a las mil maravillas.

Podéis descargar el ejemplo de McLeod, con el código fuente, los binarios, etc, en:

http://www.bytemaniacos.com/ficheros/sinclairql/rutinas/ejemplo_sprites.zip

### RUTINA DE SPRITES USANDO MÁSCARA ###

Ya sabéis que McLeod es de los que les gusta hacer las cosas bien, y me mandó otra versión que cogía otro sprite como máscara. La máscara es la clásica donde 1 es la zona que queremos mantener y 0 la que vamos a borrar. Normalmente debería ser una silueta que envuelve al sprite.

Código: Seleccionar todo

;---------------------------------------------------------------------------------
* X es la posición donde queremos poner el Sprite (0 izquierda) (múltiplo de 4)
* Y es la posición donde queremos poner el sprite (0 arriba)
* TamX es el tamaño horizontal del Sprite (múltiplo de 4)
* TamY es el tamaño vertical del Sprite
* SPRITE es la dirección de memoria del Sprite
* Rutina pensada para MODE 8, usando el bit de flash como transparencia.
* ENTRADA: X (D1), Y (D2), TamX (D3), TamY (D4), Sprite (D5), Máscara (D6), Pantalla (D7)

SpriteTrans8
   movea.l d7,a0  ; a0 <- comienzo de la pantalla
   movea.l d5,a1  ; a1 <- comienzo de la definicion del sprite
   movea.l d6,a2  ; a2 <- comienzo de la definición de la máscara del sprite
   lsr.l #1,d1 ; X dividido entre 2
   andi.b #$fe,d1 ; y nos aseguramos de que sea par
   lsr #2,d3 ; TamX dividido entre 4
   subq #1,d3 ; restamos 1 a TamX porque asi lo necesitan los DBRA
   subq #1,d4 ; y lo mismo con TamY.
   add.l d1,a0 ; sumamos X a comienzo de pantalla
   lsl #7,d2 ; un scan de la pantalla tiene 128 bytes
   adda.l d2,a0 ; a0 apunta a la coordenada X,Y
   move.l d3,d5 ; d5 es backup de d3
   move.l a0,d6 ; d6 es backup de a0

PonScanT8
   move.l d5,d3
   move.l d6,a0

Pon4pixelsT8
   move.w (a0),d0    ; D0 contiene el cacho de pantalla que va a usarse
   move.w (a1)+,d1   ; D1 contiene el cacho de sprite a mostrar
   move.w (a2)+,d2   ; D2 va a ser una máscara para determinar qué bits del sprite se quedan

   and.w d2,d1      ; Enmascaramos el sprite
   not.w d2         ; Negamos la mascara...
   and.w d2,d0      ; ... y la aplicamos a la pantalla
   or.w d1,d0       ; juntamos sprite enmascarado con pantalla enmascarada
   move.w d0,(a0)+  ; y lo plantamos en la pantalla

   dbra d3,Pon4pixelsT8
   
   addi.l #128,d6
   dbra d4,PonScanT8
   
   moveq #0,d0
   rts
;---------------------------------------------------------------------------------

* X es la posición donde queremos poner el Sprite (0 izquierda) (múltiplo de 8)
* Y es la posición donde queremos poner el sprite (0 arriba)
* TamX es el tamaño horizontal del Sprite (múltiplo de 8)
* TamY es el tamaño vertical del Sprite
* SPRITE es la dirección de memoria del Sprite
* Rutina pensada para MODE 8, usando el bit de flash como transparencia.
* ENTRADA: X (D1), Y (D2), TamX (D3), TamY (D4), Sprite (D5), Máscara (D6), Pantalla (D7)

SpriteTrans4
   movea.l d7,a0  ; a0 <- comienzo de la pantalla
   movea.l d5,a1  ; a1 <- comienzo de la definicion del sprite
   movea.l d6,a2  ; a2 <- comienzo de la definición de la máscara del sprite
   lsr.l #2,d1 ; X dividido entre 4
   andi.b #$fe,d1 ; y nos aseguramos de que sea par
   lsr #3,d3 ; TamX dividido entre 8
   subq #1,d3 ; restamos 1 a TamX porque asi lo necesitan los DBRA
   subq #1,d4 ; y lo mismo con TamY.
   add.l d1,a0 ; sumamos X a comienzo de pantalla
   lsl #7,d2 ; un scan de la pantalla tiene 128 bytes
   adda.l d2,a0 ; a0 apunta a la coordenada X,Y
   move.l d3,d5 ; d5 es backup de d3
   move.l a0,d6 ; d6 es backup de a0

PonScanT4
   move.l d5,d3
   move.l d6,a0

Pon4pixelsT4
   move.w (a0),d0    ; D0 contiene el cacho de pantalla que va a usarse
   move.w (a1)+,d1   ; D1 contiene el cacho de sprite a mostrar
   move.w (a2)+,d2   ; D2 va a ser una máscara para determinar qué bits del sprite se quedan

   and.w d2,d1      ; Enmascaramos el sprite
   not.w d2         ; Negamos la mascara...
   and.w d2,d0      ; ... y la aplicamos a la pantalla
   or.w d1,d0       ; juntamos sprite enmascarado con pantalla enmascarada
   move.w d0,(a0)+  ; y lo plantamos en la pantalla

   dbra d3,Pon4pixelsT4
   
   addi.l #128,d6
   dbra d4,PonScanT4
   
   moveq #0,d0
   rts
;---------------------------------------------------------------------------------

ClearScreen
   ;D1 = direccion de comienzo de la pantalla a borrar
   move.l #8191,d2
   movea.l d1,a0
BucleClear
   move.l #0,(a0)+
   dbra d2,BucleClear
   moveq #0,d0
   rts

   
        end 0


Podéis descargar el ejemplo de McLeod, con el código fuente, los binarios, etc, en:

http://www.bytemaniacos.com/ficheros/sinclairql/rutinas/gusano.zip

Contenido del fichero:
- El fichero .C convierte un bitmap de 24 bits en formato RAW a dos ficheros: el sprite y la máscara. En el bitmap original, cualquier pixel que tenga como color el (128,0,128) será tratado como transparente.
- Un sprite de 32x64 píxeles (para que se vea bien en modo 8) en formato PNG
- El mismo sprite, pero en formato RAW
- Los dos ficheros _bin que salen al procesar el RAW por el programa C: el sprite y la máscara.
- Código fuente de las rutinas empleadas.
- Código bnario de dichas rutinas.
- Programa en SuperBASIC que implementa la demo.

Para que el programa funcione bien, la segunda pantalla debe estar disponible. Esto significa que hay que arrancar con Minerva usando F4 (modo 8, doble pantalla). Con la ROM original no se puede, ya que pone las variables del sistema en la pantalla secundaria.

Y para finalizar, un gran aplauso a McLeod por la proeza, ahora ya no hay excusa para programar juegos desde BASIC con una calidad gráfica decente.
_________________________________________
Hay otras páginas.... pero no son Bytemaniacos
http://www.bytemaniacos.com
Orgullo de 8 bits
_________________________________________

afx
Sabreman
Mensajes: 396
Registrado: Dom Feb 24, 2008 10:56 pm

Re: Rutinas de impresión de sprites - Terminadas 21/07/2011

Mensaje por afx » Jue Dic 08, 2011 7:49 pm

He probado estas rutinas.

La versión de Mcleod, va muy bien, pero como tal como se comenta necesita Minerva. Sería fantástico tener una versión para QDOS.

Con la versión de Radastan, al mover el sprite deja rastro de fondo. No se si estoy haciendo algo mal o tengo que manejar a pelo el restaurado de fondo por donde pasa el sprite.

Avatar de Usuario
radastan
Phantomas
Mensajes: 2232
Registrado: Lun May 07, 2007 5:34 pm
Contactar:

Re: Rutinas de impresión de sprites - Terminadas 21/07/2011

Mensaje por radastan » Jue Dic 08, 2011 9:04 pm

afx escribió:Con la versión de Radastan, al mover el sprite deja rastro de fondo. No se si estoy haciendo algo mal o tengo que manejar a pelo el restaurado de fondo por donde pasa el sprite.


Efectivamente, no restaura el fondo porque se trata de una rutina rápida. La de Mc Leod usa máscara, pero la mía no.

De todas formas sirve de sobra para hacer muchas cosas, es un comienzo.
_________________________________________
Hay otras páginas.... pero no son Bytemaniacos
http://www.bytemaniacos.com
Orgullo de 8 bits
_________________________________________

Responder

¿Quién está conectado?

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