Categorías
Ensamblador sjasm Programación

Moviendo nuestro personaje con ensamblador sjasm, los sprites

1.Introducción

2.¿Qué es un Sprite?

3.Creando nuestro spritecon tinysprite

4.Creando sprites con spritedevtools

5.Mirando nuestros sprites

6. spriteSXdevtool

1.Introducción

Se trata de introducir datos en la VRAM nada más, el VDP leerá esos datos y los presentará en pantalla.

¿Qué es un Sprite?

Son agrupaciones de 1 o 4 tiles (recuerda que los tiles se definen ponindo 8 bytes: 0001 1101 0100 0100 1011 0110 1100 0010 que representan a un cuadradito o losa) su definición debe de ponerse desde el área #3800 a la #4000 y también tenemos que poner en la posición de la VRAM #1b00 a la posición #1b80 los atributos (son 4 bytes) que representan:

1 byte (0) su posición en el eje X

2 byte (1) su posición en el eje Y

3 byte (2) número de sprite que hay en la zona de definición de la VRAM a partir de la posición VRAM  #3800:

4 byte (3) la definición del color, solo se puede utilizar un solo color, de los 8 bits que tiene del 1 al 4 (0 al 3) definen el color, se pueden poner varios colores superponiendo sprites, el 8 bit cuando está activado oculta el sprite (se llama early clock).

Hay una limitación a los sprites en la pantalla y es que solo se pueden poner 4 por línea.

Los datos de la definición de los sprites que a su vez se agrupan en planos que en realidad son más sprites(mirar el código de abajo) y de sus atributos los puedes encontrar en la web http://aorante.blogspot.com/2016/04/mapas-de-memoria-del-tms9918.html

3.Creando nuestro spritecon tinysprite

En este ejemplo vamos a utilizar los sprites de 4 tiles (32 bytes), para eso pinchamos en la web http://msx.jannone.org/tinysprite/tinysprite.html y dibujamos nuestros 32 bytes:

Pintamos también los otros sprites (los 3 que quedan) , a esta agrupación de sprites lo llamaremos bloque, también podemos decir que es el sprite persona con 4 planos:

Ya tenemos nuestros 32 bytes de mitrando arriba correspondientes a nuestro personaje y también los sprites de mirando a la derecha izquierda y abajo) por cada sprite es decir 128 bytes, pinchamos en Export sprites, espués en export as y elegimos asm hexadecimal y en copy to clipboard:

Aunque puedes bajarte el código fuente de aquí, puedes copiar y pegar este código para estudiarlo:

      
        output "sprite.bin"   ; debemos de poner esta instrucción o nos generaría un archivo.out, ponle espacios al principio
        
;Llamadas a rutinas bios
CHGET equ #009F    ; Se queda esperando que se pulse una tecla
CHPUT equ #00A2    ; escribe el caracter ascii almacenado en a                         
CHGMOD equ #005F   ; Cambia el modo de screen pero previamente necesita que se le asigne el modo en el registro a
LDIRVM equ #005C   ;Tansfiere bloques de la RAM a la VRAM, es la más importante, necesita previamente asignar valor al registro bc con la longitud, dc con la dirección de inicio de la VRAM y hl con la dirección de inicio de la RAM:
RDVDP equ #013E    ; Lee el registro de estado del VDP
WRTVDP equ #0047   ; Escribe en los registros del VDP 
;Llamadas direcciones de la ram
RG15AV equ #F3E0 ; alamcena el valor del registro 1 de escritura del VDP, hay unas rutinas de la bios que guardan es entas direcciones valores globals del sistema
    
    ; https://www.faq.msxnet.org/suffix.html
    db   #fe              
    dw   INICIO            
    dw   FINAL             
    dw   MAIN               
    
    org 33280                 
INICIO:
 

MAIN:  
    call poner_pantalla_en_modo_screen_2
    call volcar_sprite_y_atributos_en_VRAM
    call inicializar_personaje_principal
    call loop_pulsaciones_teclado
    ret




inicializar_personaje_principal:
    ;el entender como fncionan los registros ix del z80 es crucial, lo que hacen es servir de referencia o puntero para las posiciones de moeoria
    ld ix, atributos_personaje_sprite ;fijate en mover_persona_derecha,izquieda, arriba y abajo

loop_pulsaciones_teclado:
    halt ; sincroniza el teclado y pantalla con el procesador (que va muy rápido)
    call CHGET ;espera a que se pulse una tecla, cuando se pulsa la almacena en el registro a del z80

    cp 'p'
    jp z, mover_personaje_derecha
    cp 'o'
    jp z, mover_personaje_izquierda
    cp 'q'
    jp z, mover_personaje_arriba
    cp 'a'
    jp z, mover_personaje_abajo

    jp loop_pulsaciones_teclado


mover_personaje_arriba:
    ld a,(ix); obetenemos el valor actual de la posicion y
    sub 3 ; incrementamos en 3 el valor
    ld (ix), a ; se lo metemos al atributo
    ld (ix+2),#00;ponemos al patrón 1
    ld (ix+3),#08;le ponemos el color rojo
    call volcar_sprite_y_atributos_en_VRAM
    call loop_pulsaciones_teclado
mover_personaje_abajo:
    ld a,(ix); obetenemos el valor actual de la posicion y
    add 3 ; incrementamos en 3 el valor
    ld (ix), a ; se lo metemos al atributo
    ld (ix+2),#0c;le cambiamos al patrón 4 (12 en headecimal)
    ld (ix+3),#02; le ponemos el color verde
    call volcar_sprite_y_atributos_en_VRAM
    call loop_pulsaciones_teclado
mover_personaje_derecha:
    ld a,(ix+1); obetenemos el valor actual de la posicion x
    add 3; incrementamos en 3 el valor
    ld (ix+1), a ; se lo metemos al atributo
    ld (ix+2),#08 ;le asignamos el patrón 2
    ld (ix+3),#07; le cambiamos el color azul claro
    call volcar_sprite_y_atributos_en_VRAM
    call loop_pulsaciones_teclado
mover_personaje_izquierda:
    ld a,(ix+1); obetenemos el valor actual de la posicion x
    sub 3 ; incrementamos en 3 el valor
    ld (ix+1), a ; se lo metemos al atributo
    ld (ix+2),#04;le cambiamos el patrón 3
    ld (ix+3),#0B; le ponemos el color amarillo
    call volcar_sprite_y_atributos_en_VRAM
    call loop_pulsaciones_teclado



poner_pantalla_en_modo_screen_2:
     ;Cambiamos el modo de pantalla
    ld  a,2     ; La rutina CHGMOD nos obliga a poner en el registro a el modo de pantalla que queremos
    call CHGMOD ; Mira arriba, pone la explicación

    ld a,(RG15AV) ; esta direcciónd e memoria alamcena el valor el registro de lectura del VDP, mira arriba
    ;En or 0+0=0, 0+1=1, 1+1=1
    ;En and 0+0=0, 0+1=0, 1+1=1
    ;Con eso queremos cambiar los bits 7 y 8 del registro de escritura 1 del z80, queremos poner el 7 a 1 y también el 8 a 1
    ;el bit 7 del registro 1 pone los sprites en modo 16x16 (los que nostros queremos dibujar)
    ;el bit 8 queremos desactivarlo para no utilizar los sprites agrandados
    or 00000010b ; con or poniendo un 0 siempre respetamos los bits que hay y poniendo 1 1 obligamos a que sea 1
    and 11111110b ; con and obligamos a que el ultimo bit valga 0

    ld b,a ;carga en b el valor de a
    ld c,1 ; La rutina WRTVDP necesta que le carguemos previamente el entero en C del z80 del registro que queroms modificar
    call WRTVDP ;Escribe en los registros del VDP 
    ret

volcar_sprite_y_atributos_en_VRAM:
;por favor antes de nada mira este dibujo: https://sites.google.com/site/multivac7/files-images/TMS9918_VRAMmap_G2_300dpi.png
;-----------------------------Definición del sprite en #3800  y volcado a la VRAM-------------------------------------------

    ld hl, sprites_personaje ; la rutina LDIRVM necesita haber cargado previamente la dirección de inicio de la RAM, para saber porqué he puesto 03800 fíjate este dibujo https://sites.google.com/site/multivac7/files-images/TMS9918_VRAMmap_G2_300dpi.png ,así es como está formado el VDP en screen 2
    ld de, #3800; la rutina necesita haber cargado previamente con de la dirección de inicio de la VRAM          
    ld bc, 8*4*4; 8 byte de cada tile * 4 que son los sprites de 16x16 y * 4 que son los sprites o planos que forman el sprite
    call  LDIRVM ; Mira arriba, pone la explicación

;-----------------------------Definición de los atributos en #1b00 y volcado a la VRAM------------------------------------

    ld hl, atributos_personaje_sprite ; la rutina LDIRVM necesita haber cargado previamente la dirección de inicio de la RAM, para saber porqué he puesto 0000 fíjate este dibujo https://sites.google.com/site/multivac7/files-images/TMS9918_VRAMmap_G2_300dpi.png ,así es como está formado el VDP en screen 2
    ld de, #1b00; la rutina necesita haber cargado previamente con de la dirección de inicio de la VRAM          
    ld bc,4; solo tenemos 1 plano de un personaje
    call  LDIRVM ; Mira arriba, pone la explicación

    ret
;para una expresión hexadecimal se puede utilizar tambiénm el sign $ mirar http://www.xl2s.tk/sjasmman4.html
;Definición de sprite 4 tiles

sprites_personaje:
    ; sprite 1
    ; patrón 1 del sprite_personaje, del @00 al #04 mirando arriba
    DB $03,$03,$03,$1F,$17,$17,$17,$17
    DB $17,$07,$04,$04,$04,$04,$04,$0C
    DB $00,$00,$00,$E0,$A0,$A0,$A0,$A0
    DB $A0,$80,$80,$80,$80,$80,$80,$C0

    ; sprite 2
    ; patrón 2 del sprite_personaje, del #04 al #07, mirando hacia la izquierda
    DB $01,$03,$01,$03,$03,$03,$03,$03
    DB $03,$03,$02,$02,$02,$02,$02,$07
    DB $80,$80,$80,$C0,$C0,$C0,$C0,$C0
    DB $C0,$80,$80,$80,$80,$80,$80,$80

    ; sprite 3
    ; patrón 3 del sprite_personaje, del #08 al # #0b, mirando hacia la derecha
    DB $03,$03,$03,$07,$07,$07,$07,$07
    DB $07,$03,$02,$02,$02,$02,$02,$03
    DB $00,$80,$00,$80,$80,$80,$80,$80
    DB $80,$80,$80,$80,$80,$80,$80,$C0

    ; sprite 3
    ; patrón 4 del sprite_personaje, del #0c al #0f, mirando abajo
    DB $03,$03,$03,$0F,$0F,$0F,$0F,$0F
    DB $0F,$07,$04,$04,$04,$04,$04,$0C
    DB $00,$00,$00,$C0,$C0,$C0,$C0,$C0
    DB $C0,$80,$80,$80,$80,$80,$80,$C0


;---Definición de stributos sprite, a esto se le llama plano y cada plano tiene 4 bytes, solo nos caben 32 planos en el espacio de la VRAM
;Plano 1
atributos_personaje_sprite:
atributos_personaje_sprite_posicion_y DB $64
atributos_personaje_sprite_posicion_x DB $64
atributos_personaje_sprite_numero_sprite DB $03
atributos_personaje_sprite_color DB $08 ; aqui se defien el color y el early clock (que es para desparecer el sprite)
;lo enaterior es lo m ismo que poner atributos_personaje: DB $64,$64,$00,$08   ,le estamos diciendo en el eje y la posición 0 se vaya al pixel 150 (#64), en el x la posición 0, el número de sprite es el 0 y el último byte el 1000 1000 (1000 para que aparezca) y 1000 (el color rojo)=1000+1000=88 en decimal
;otra forma de crear esta definición es atributos_personaje DS 4,0 que creará 4 bytes con valor 0



FINAL:              



Ahora pinchamos en el botón Drive a de nuestro catapult-openMSX y eligimos Browse for disk folder y elegimos la carpeta donde tenemos nuestro sprite y cuando se abra el openMSX ponemos bload»sprite.bin»,r:

Aunque existen rutinas de la bios que te permiten caputar cuando pulsas los cursores, ver http://map.grauw.nl/resources/msxbios.php , rutinas GTSTCK y GTTRIG, nosotros vamos a utilizar las teclas o,p,q,a para mover al personaje:

Mirando nuestros sprites

Fijate que cuando abrimos el terminal con F10 y ponemos sprite_viewer paracen nuestros sprites:

Cierra el terminal pulsando otra vez F10, pincha en los cursores izquierda y derecha para cambiar de sprite, en la tecla o barra espacio para refrescar y en la tecla escape para cerrar el sprite_viewr:

Si nos conectamos al sprite con el openMSX debugger:

Y pinchamos en el Menú en View->add debuggeable viewer

y elegimos VRAM podremos ver los valores de la posición de memoria donde está nuestro plano, en la #1b00:

Tenemos en la #1b00 el eje y con el valor 61, en #1b01el eje x con el valor 82, en el registro #1b02 el color de 07 azul claro.

Puedes modificar esos valores para ver como cambia el sprite.

Existe una utilidad para crear sprites, se llama spriteSXdevtool: https://github.com/aseprite/aseprite/releases.

Mira como quedaría con esta utilidad,es muy sencilla tan solo tienes que trastear:

V

3 respuestas a «Moviendo nuestro personaje con ensamblador sjasm, los sprites»

Hola. Mi nombre es Paco… Acabo de descubrir tu blog y me parece muy interesante… más ahora que estoy aprendiendo ensamblador. Entiendo todo el código que has puesto y puedo adaptarlo (de hecho estoy jugando con estructuras y lo he adaptado para que el muñeco sea una estructura). El problema lo tengo con la comprobación de límites… me funcionan bien para los superiores pero con los límites inferiores (0) tengo problemas. Tampoco tengo claro en qué zona del código ponerlas o si poner aparte las comprobaciones…. ¿Puedes indicarme cómo lo haces?… gracias de antebrazo 😉

Buenas Paco, estoy muy agradecido de que leas nuestro blog, voy a configurar el correo de mi móvil para que me lleguen las entradas del blog.
-He creado un pequeño proyecto en el que intento resolver tus dudas: https://github.com/kikemadrigal/MSX1-asm-test-input-system/blob/main/src/main.s

Pruebalo desde aquí: https://kikemadrigal.github.io/MSX1-asm-test-input-system/?diska=iasm.dsk

-La comprobación de límites son las líneas 200, 213, 226 y 240

-Creo que en los juegos modernos las comprobaciones del sistema de entrada se hacen utilizando el modelo Entity Component System y se meten dentro del sistema de físicas del juego, no te compliques Paco y ponlas junto a la entrada del teclado, ya tendrás tiempo de complicarte la vida en el 5º o 6º juego que hagas 😊.

Revisando ese código me he dado cuenta de que he utilizado la rutina de la bios “CHGET”, puedas ver su descripción aquí http://map.grauw.nl/resources/msxbios.php
Esta rutina se queda esperando a que pulses una tecla, por eso, he creado un nuevo sistema de entrada de teclado (input_system2) que no se queda esperando nada, para tu juego también necesitarás utilizar los cursores y la barra espaciadora para el disparo, por eso he creado las rutinas “check_cursors” y “check_space_bar”.

Tan solo tengo de experiencia en ensamblador para MSX1 este proyecto:
https://github.com/kikemadrigal/assembler-MSX-MSXpolice
Puedes probarlo online gracias a webmsx:
http://msx.tipolisto.es/webmsx/standalone/index.html?DISKA_URL=files/msxpolice.dsk

Veo muy pocas personas que le metan mano a ensamblador, estaré encantado de ver tu proyecto.

Kike (kikemadrigal@hotmail.com).

Si hubiera un top ten de los tipos más agradecidos yo estaría el primero. Muchas gracias. Este fin de semana ya tengo tarea para estar entretenido. Un fuerte abrazo

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *