Pintando sprites de 16x16 en ensamblador

Chit chat general. Habla con los MojonTwins y con los amigos de los MojonTwins. Reza a Vah-ka. Delinque. Aviso: está PROHIBIDO tirarse peos fuerte. Si les cortas el pescuezo, vale.

Moderador: na_th_an

Avatar de Usuario
radastan
Mensajes: 692
Registrado: Vie, 20 Ago 2010, 12:54
Contactar:

Pintando sprites de 16x16 en ensamblador

Mensajepor radastan » Jue, 05 Dic 2013, 20:55

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).
Avatar de Usuario
na_th_an
Mensajes: 26413
Registrado: Vie, 09 Ene 2009, 12:18

Re: Pintando sprites de 16x16 en ensamblador

Mensajepor na_th_an » Jue, 05 Dic 2013, 21:32

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?
Como diría Rorshach: "Urm..."
Avatar de Usuario
na_th_an
Mensajes: 26413
Registrado: Vie, 09 Ene 2009, 12:18

Re: Pintando sprites de 16x16 en ensamblador

Mensajepor na_th_an » Jue, 05 Dic 2013, 21:34

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
}
')
Como diría Rorshach: "Urm..."
antoniovillena
Mensajes: 494
Registrado: Jue, 24 Oct 2013, 15:52

Re: Pintando sprites de 16x16 en ensamblador

Mensajepor antoniovillena » Jue, 05 Dic 2013, 21:47

¿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?
Avatar de Usuario
radastan
Mensajes: 692
Registrado: Vie, 20 Ago 2010, 12:54
Contactar:

Re: Pintando sprites de 16x16 en ensamblador

Mensajepor radastan » Vie, 06 Dic 2013, 13:25

$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.