jueves, 16 de junio de 2016

VOLCADO DEL BUFFER CON LA PILA (I)




En esta serie de entradas voy a explicar otra forma de usar un buffer de impresión. Es bastante más rápido que el anterior, y a mi modo de ver más elegante, si bien ocupa algo más de memoria.

Te puede bajar el código de AQUI:
He metido un fondo más aparente.

Este buffer que vamos a usar copia literalmente la estructura de la pantalla del spectrum. No es como en las entradas anteriores, que lo creábamos a nuestra conveniencia. En este caso, todas las operaciones a realizar serán las mismas que si estuviéramos imprimiendo directamente en la pantalla. Por ello, es muy importante que entiendas la estructuración de la misma
Parece muy enrevesada para nuestra mente decimal, pero si piensas en Hexadecimal, tiene toda su lógica.
Ya comenté que viene muy bien explicado en el curso de Romero. Por favor, échalo un vistazo, ya que si no dominas cómo funciona el asunto, estas entradas se te pueden hacer un poco duras.

De todas formas, voy a dar un repaso rápido, que nunca viene mal.

La pantalla del Spectrum tiene 192 líneas.
Está dividida en tres tercios de 24 líneas.
Cada tercio está dividido en 8 caracteres.
Cada caracter está dividido en 8 líneas.
Cada línea mide 32 caracteres de ancho.
La pantalla empieza en la dirección 4000h y termina en la 57ffh.


Los tres tercios tienen la misma estructura, por lo que nos fijaremos sólo en uno.

Seguro que has visto cargar una pantalla en el spectrum muchas veces, y no lo hace de forma secuencial.






Carga las 32 direcciones de memoria de la primera línea, y la siguiente dirección es la primera línea del segundo caracter. Carga las 32 direcciones y la siguiente será la primera línea del tercer caracter... Así hasta que ha cargado las 32 direcciones de memoria de la primera línea del octavo caracter, que es el último del primer tercio.

Las siguiente dirección será la segunda línea del primer caracter y todo vuelve a empezar pero esta vez con las segundas líneas de cada caracter. Así hasta que carga el tercio.

Si estamos en la primera dirección de la primera línea del primer caracter (4000h) y queremos pasar a la dirección que está inmediatamente debajo de ésta (primera dirección de la segunda línea del primer caracter), ¿qué debemos hacer? He dicho antes que después de la dirección 32 de la primera línea del octavo caracter se pasa a la dirección 0 de la segunda línea del primer caracter. Osea que hemos avanzado 8 líneas*32 direcciones por línea=256 (100h) direcciones.  Sumamos 4000h+100h=4100h.
Osea, si el registro HL=4000h, nos basta con INC H y tendremos HL=4100h.

Para hallar la segunda línea, incrementamos H otra vez, y así con todas las líneas del caracter.
El problema es que si estamos en la última (4700h) e incrementamos H, nos da HL=4800h como dirección de la primera línea del segundo caracter. Y eso no es así. La primera línea del segundo caracter, como hemos visto, es 4000h+32 direcciones de memoria que ocupa la primera línea, o lo que es lo mismo en hexadecimal 4020h.

Esto nos supone ir comprobando cada vez que incrementemos H para pasar de línea, si nos hemos pasado al siguiente caracter. Esto se hace viendo si H es múltiplo de 8. Si lo hemos hecho, basta con restar 8 a H, y sumarle 20h a L.
48h-8=40h. 00h+20h=20h. Total que HL=4020h. Ya tenemos la dirección que buscábamos.

Así podemos hallar la dirección que está debajo de una dada dentro de un tercio, pero ¿qué pasa si nos pasamos de tercio? Con nuestro método, cuando estemos en el octavo caracter del primer tercio y en su última línea estaremos en la dirección 48E0h, H es múltiplo de 8, por lo que nos hemos pasado de caracter. Hacemos 48h-8=40h. E0h+20h=00. HL=4000h. Nos vuelve a la primera línea del primer caracter. Para cambiar de tercio basta sumarle 20h a L, sin restar 8 a H.
E0h+20h=00. HL=4800h, que es la primera dirección de la primera línea del segundo tercio.
4800h-4000h=800h. 800h no es otra cosa que lo que nos ocupa un tercio entero.

¿Cómo sabemos si al incrementar H nos hemos pasado de tercio además de caracter?
Pues porque al sumar 20h a L, nos va dando esto:

4700h     última línea primer caracter
4720h     última línea segundo caracter
4740h     última línea tercero caracter
4760h     última línea cuarto caracter
4780h     última línea quinro caracter
47A0h    última línea sexto caracter
47C0h    última línea séptimo caracter
47E0h    última línea octavo caracter

HL=47E0h. INC H para cambiar de línea. HL=48E0h. H es múltiplo de 8, por lo que hay que cambiar de caracter. Sumo 20h a L. HL=4800. Si te das cuenta L vuelve a ser igual a cero, por lo que en la operación de la suma ha habido acarreo (FLAG C=1). Esto significa que se nos ha acabado el tercio y no tenemos que restar 8 a H.

Todo esto lo hace la rutina SIGLIN de CONTENIDA.ASM, que ya medio expliqué, pero que seguro que ahora la entiendes perfectamente.





Como puedes ver, pensando en Hexadecimal, la distribución de la pantalla no es tan "rara" como parece.

En esta rutina se va a usar todo este planteamiento constantemente.

Como ya sabes, guardo los sprites en zigzag, así cuando termino de imprimir el último caracter del sprite, me basta con hallar la dirección que está debajo y volver de a imprimir de derecha a izquierda.

Esta rutina tiene alguna limitación. La coordenada Y tiene que ser siempre par. Esto es por lo siguiente: Sólo va a haber cambio de caracter al hallar la dirección que está debajo de una dada cuando la coordenada de partida sea impar (condición necesaria pero no suficiente). Si nuestra coordenada es siempre par, nos ahorramos una comprobación de cambio de caracter, ya que sabemos que nos basta con incrementar H para cambiar de línea.

Por la distribución de la pantalla, para pasar de una dirección a la contigua cuando imprimamos el sprite, sabemos que L nunca va a ser FFh ni 0. Por ello, todos estos incrementos y decrementos se pueden hacer con INC/DEC L en vez de con INC/DEC HL, con lo que ganamos mucha velocidad.
El problema de usar INC/DEC L cuando lo que queremos es INC/DEC HL ya lo hemos visto. Tenemos que estar seguros de que L nunca sea FFh al incrementar ni 0 al decrementar ya que éste pasaría a ser 0/FFh y H se quedaría con el mismo valor.

Esta ganancia en velocidad no es oro todo lo que reluce. En realidad, L sí que va a ser 0 o FFh, pero sólo en determinados puntos de los bordes de la pantalla. Por eso, la coordenada X nunca puede ser menor que 8 ni mayor que 248.
En este ejemplo volcamos 16 caracteres de alto por 24 de ancho.

Todo esto se verá  con el examen de la rutina.

Intenta comprender todo esto muy bien.

No hay comentarios:

Publicar un comentario