Página 1 de 6

Pintando sprites de 16x16 en ensamblador

Publicado: Jue, 05 Dic 2013, 20:55
por radastan
Estoy trabajando en una rutina para imprimir sprites de 16x16 pixels a color (si, para movimiento a caracter).

El sprite estaría representado de forma lineal, byte a byte, desde la esquina superior izquierda a la inferior derecha, y luego los cuatro bytes de color en el mismo orden.

La rutina en concreto es la de mi curso de ensamblador, y la versión para Z88DK es:

$this->bbcode_second_pass_code('', '
void put_sprite_x16 (unsigned char *posicion, unsigned int x, unsigned int y)
{
// -------------------------------------------
// RUTINA DE IMPRESION DE UN SPRITE 16x16 PIXELS
// CON ATRIBUTOS EN CUALQUIER POSICION DE CARACTER
// ENTRADAS:
// D será la posición del cursor vertical en caracteres
// E será la posición del cursor horizontal en caracteres
// HL es la posición de memoria donde tenemos el sprite
// SALIDAS: se escribe en el mapa de pantalla
// ADVERTENCIAS: no comprueba límites de pantalla
// -------------------------------------------
#asm
ld hl,2 ;pasamos la variable de entrada al acumulador
add hl,sp
ld d, (hl)
inc hl
inc hl
ld e, (hl)
inc hl
inc hl
ld a, (hl)
inc hl
ld h, (hl)
ld l, a

push de ; salvamos los valores vertical y horizontal
push de ; salvamos los valores vertical y horizontal
push de ; salvamos los valores vertical y horizontal
call cdrw ; calculamos dirección de pantalla
ld b, 8
call draw
pop de ; recuperamos el valor horizontal
inc d ; incrementamos una línea
call cdrw
ld b, 8
call draw
; Ahora imprimimos los atributos
pop de ; recuperamos el valor horizontal
call cdrw
call catr
call colr
pop de ; recuperamos el valor horizontal
inc d ; incrementamos una línea
call cdrw
call catr
call colr
ret

.draw
ld a, (hl) ; HL indica la posición del sprite en memoria
ld (de),a ; de indica la posición de pantalla
inc hl
inc e
ld a,(hl) ; esta parte imprime el segundo byte
ld (de),a
inc hl
dec e
inc d
djnz draw ; decrementa B y si es cero deja de saltar a draw
ret

.colr
ld a,(hl)
ld (de),a
inc e
inc hl
ld a,(hl)
ld (de),a
dec e
inc hl
ret

.cdrw
ld a, d ; recuperamos el valor vertical
and 7 ; nos quedamos con la posición en el tercio
rrca
rrca
rrca ; rotamos para dejar su valor en múltiplos de 32 (linea)
and 224 ; borramos el resto de bits por si las moscas
or e ; sumamos el valor horizontal
ld e, a ; e preparado
ld a, d
and 24 ; modificamos según el tercio de pantalla
or 64 ; nos posicionamos a partir de 16384 (16384=64+0 en dos bytes)
ld d, a ; d preparado
ret

.catr
ld a,d
rra
rra
rra ; multiplicamos por 32
and 3 ; nos quedamos con los tres bits bajos
or 88 ; apuntamos al comienzo del mapa de atributos
ld d,a ; ya tenemos d listo, e no hay que cambiarlo
#endasm
}
')

La cuestión es que la veo lenta, y quería preguntaros que mejoras se le podría hacer.

Por cierto, ¿cómo demonios se puede trabajar con puertos desde Z88DK y ensamblador? Es que la instrucción out no la reconoce (o no se como ponerla para que la reconozca).

Re: Pintando sprites de 16x16 en ensamblador

Publicado: Jue, 05 Dic 2013, 21:32
por na_th_an
No sé muy bien qué necesitas, pero se puede referenciar punteros de la parte ASM desde C y viceversa:

$this->bbcode_second_pass_code('', 'extern unsigned char pointer [0];

#asm
._pointer db 0
#endasm')

Eso define un puntero a byte. Desde C puedes usarlo normalmente, *pointer = 3 o pointer[0] = 3 (es lo mismo). Desde ASM lo puedes usar también, con el "_" delante: ld a, (_pointer).

Sobre la rutina, yo no soy quien para dar consejos sobre ensamblador, pero se acelera mucho con muy poco coste si precalculas una tabla con las direcciones de cada linea de caracteres. En total sólo gastas 24x2 = 48 bytes. Cargas la dirección correcta al principio de la rutina, sumas la coordenada X, y luego solo tienes que ir incrementando D o E, según convenga.

¿A qué te refieres con lo del out?

Re: Pintando sprites de 16x16 en ensamblador

Publicado: Jue, 05 Dic 2013, 21:34
por na_th_an
Ah, joder, que además de tonto estoy ciego :lol: había leído "punteros" y no "puertos" :lol:

Desde C seguro que hay alguna biblioteca que incluir que la traiga, la verdad es que no lo he usado nunca... Mira en la wiki de z88dk.org que seguro que lo encuentras.

Desde ASM, pues como siempre... Cargas en BC la dirección y haces un out (C), A. Como aquí:

$this->bbcode_second_pass_code('', 'void SetRAMBank(void) {
#asm
.SetRAMBank
ld a, ($5b5c)
and f8h
or b
ld bc, $7ffd
ld ($5b5c), a
out (C), a
#endasm
}
')

Re: Pintando sprites de 16x16 en ensamblador

Publicado: Jue, 05 Dic 2013, 21:47
por antoniovillena
¿Puedes poner el código fuente completo para probarlo, junto con el make para saber cómo se compila?

Lo suyo es que midas los ciclos exactos que tarda en ejecutarse, ya sea con Ticks o en emulador. En función de ese dato te puedo decir si es rápido o lento. Las rutinas que estoy usando en el otro hilo tardan 2000 ciclos en pintar un sprites de 16x16, aunque la rutina es genérica, puede imprimir sprites de cualquier tamaño y de hecho la mayoría de las veces un sprite de 16x16 se pinta con 24x16 porque el sprite está desplazado. La siguiente cuenta a hacer es dividir los ciclos por el número de bytes, siempre suponiendo el peor de los casos. Serían 2000/32= 62.5 ciclos/byte. Más o menos es una buena velocidad, ten en cuenta que un LDI son 15 ciclos, y la rutina trabaja con sprites enmascarados por lo que el procesado es mayor. Posiblemente en tu rutina, con los bits sin enmascarar, una buena cifra sean 30 ciclos/byte, no lo sé, no tengo mucha experiencia pero creo que es un buen objetivo a seguir.

No he visto tu código en detalle, pero si los sprites no van enmascarados la forma más fácil de transferir bytes es mediante la pila (instrucciones push y pop). En cuanto a la orden out, ¿cúal es exactamente la instrucción que te da error al compilar?

Re: Pintando sprites de 16x16 en ensamblador

Publicado: Vie, 06 Dic 2013, 13:25
por radastan
$this->bbcode_second_pass_quote('antoniovillena', 'Â')¿Puedes poner el código fuente completo para probarlo, junto con el make para saber cómo se compila?


Te he dejado el esqueleto de C para Z88DK que estoy elaborando,

http://www.bytemaniacos.com/ficheros/zxspectrum/cursoz88dk.zip

Lo curioso es que ahora si lo veo bien, lo mismo se le fue la pelota al emulador.

Re: Pintando sprites de 16x16 en ensamblador

Publicado: Vie, 06 Dic 2013, 14:15
por antoniovillena
Vale, he medido los ciclos que tardas en pintar un sprite con un emulador y me salen 2638 ciclos, unos 82 ciclos/byte. Si fuese una rutina de pintado con máscara estaría bien, pero como no es el caso te puedo decir que es algo lento. Creo que podrías llegar a los 30 ciclos/byte tranquilamente sin demasiadas complicaciones.

El problema principal de rendimiento es que abusas de las subrutinas pequeñas. Intenta no usar ningún call, y basarte en las direcciones calculadas anteriormente. Por ejemplo si pintas 4 caracteres, calcula la posición sólo una vez (sin call), y luego básate en la dirección en la que acabas al pintar el primer carácter para pintar el segundo, y así sucesivamente hasta pintar el cuarto.

Re: Pintando sprites de 16x16 en ensamblador

Publicado: Vie, 06 Dic 2013, 16:15
por radastan
Vamos, que me recomiendas sólo hacer una vez el cálculo de la posición e ir incrementando, toda la razón del Mundo mundial.

A ver si saco un hueco y lo optimizo, tampoco pretendo hacer la rutina más rápida del Universo, pero al menos si una que sirva para cualquier juego sin grandes pretensiones.

Re: Pintando sprites de 16x16 en ensamblador

Publicado: Vie, 06 Dic 2013, 16:28
por na_th_an
Que es básicamente lo que yo te propuse, añadiendo que además te ahorras calcular el grueso de la dirección inicial si la pillas de la tabla.

Re: Pintando sprites de 16x16 en ensamblador

Publicado: Vie, 06 Dic 2013, 17:05
por radastan
$this->bbcode_second_pass_quote('na_th_an', 'Q')ue es básicamente lo que yo te propuse, añadiendo que además te ahorras calcular el grueso de la dirección inicial si la pillas de la tabla.


Tranquilo que tomo nota de los dos, ya os digo que no pretendo hacer la panacea, pero si algo sencillito que ayude a la peña a meterse en el Z88DK de forma muy sencilla. De echo el esqueleto lo he realizado basándome un poco en el de la churrera, no me pongo medallas de ningún tipo.

Tampoco es una alternativa a la churrera, sólo una opción para los que prefieren programar su código pero sin entrar en demasiados follones.

Re: Pintando sprites de 16x16 en ensamblador

Publicado: Vie, 06 Dic 2013, 18:16
por na_th_an
Si quieres rutinas facilonas de plataformas que funcionan a carácter pégame un grito que tengo algo por ahí :)