Crea tu propio slideshow
Aprovechando que me han pedido hacer un slideshow temático para la revista, he decidido dedicar el artículo de este número a examinar su código. Por falta de tiempo no he añadido ningún efecto, tan sólo he modificado ligeramente el primer efecto de líneas entrelazadas, que en FSC3 copiaba los atributos al principio y ahora deja los atributos en blanco y negro y los copia de la imagen original al final. También he realizado diversos cambios y optimizaciones, aunque seguro que siguen quedando partes mejorables.
Con este código, que podéis modificar a vuestro antojo para incluir nuevos efectos o mejoras, y vuestras propias fotos, veréis lo sencillo que resulta hacer un slideshow resultón.
Las fotos
El principal ingrediente de vuestro slideshow deben ser las fotos, para crearlas no tenéis mas que coger las fotos originales en formato .jpg y utilizar los programas BMP2SCR EXP (la versión PRO también es válida) y SevenuP (especialmente para el retoque, la conversión no funciona demasiado bien para fotos reales) para convertirlas al formato del Spectrum. No me voy a extender mas porque el asunto se saldría de lo que es la programación en ensamblador en sí misma.
Una vez que tengáis las fotos en formato .SCR, las podéis pasar a TAP con mi programa BIN2CODE, modificáis el código fuente del visor para que aparezca el número de fotos que tengáis en la etiqueta N_S y lo compiláis con Pasmo (ZMAC y AS80 también funcionan, para otros sería necesario hacer algunos pequeños cambios), lo pasáis a cinta con BIN2TAP y por último concatenáis los archivos del programa y las pantallas para obtener el slideshow completo.
El núcleo del programa
Esta parte del código no tiene en principio ninguna complicación, tan sólo se dedica a cargar una pantalla en la memoria, escoger un efecto para esa pantalla y aplicar dicho efecto para mostrarla. Todas las pantallas se cargan en la dirección 49512, que tiene la ventaja de que tan sólo se diferencia en un bit con 16384, de este hecho se van a aprovechar varios de los efectos. Una vez mostrada, se hace una pequeña pausa, se comprueba si quedan mas pantallas, y de ser así se incrementa el número de efecto y se vuelve al principio, reseteando dicho número de efecto si ya hemos mostrado todos los disponibles.
El código ya está comentado, así que no necesitaré extenderme demasiado. Vamos a verlo:
;SLIDESHOW ampliable, Version 2 by Metalbrain N_S EQU 18 ; Numero de pantallas N_E EQU 4 ; Numero de efectos LD_BYTES EQU 1366 ; Rutina de carga de la ROM
Aquí se definen etiquetas globales, que serán sustituidas por su valor numérico donde aparezcan, por lo que no ocupan memoria y no son variables.
ORG 32768 ; Direccion de comienzo del codigo ; Nucleo principal del slide-show XOR A ;Iniciar numero de pantalla a 0 LD (N_SCREEN),A EXTBUC: XOR A ;Iniciar numero de efecto a 0 INTBUC: LD (N_EFFECT),A ;Guardar efecto actual LD DE,17 ;Cabecera, no hace falta en verdad LD IX,49152 ; XOR A ; SCF ;Se puede suprimir quitando tambien CALL LD_BYTES ; las cabeceras de las pantallas LD DE,6912 ;Numero de bytes LD IX,49152 ;Direccion de comienzo LD A,255 ;Identificador de bloque SCF ;Carry flag a 1 para cargar CALL LD_BYTES ;Llamar a la rutina de carga
La rutina LD_BYTES que utilizamos para cargar, carga un bloque de DE bytes que tenga el identificador especificado por A en la dirección que indica IX. Para que cargue en lugar de verificar se necesita que la bandera de acarreo esté a 1 (por eso se utiliza SCF).
LD HL,TABLE-2 LD A,(N_EFFECT) ; Seleccionar efecto INC A LD B,A SELEFF: INC HL INC HL DJNZ SELEFF
Cada efecto tiene su dirección de comienzo en una tabla. Con esto HL queda apuntando a la dirección de la rutina del siguiente efecto, contenida en dicha tabla.
LD A,(HL) ;Introducir efecto en la direccion INC HL ; a la que se llama con CALL LD (THEJUMP1),A LD A,(HL) LD (THEJUMP2),A THEJUMP: DEFB 205 ;CALL THEJUMP1: DEFB 0 THEJUMP2: DEFB 0
La dirección de comienzo de cada efecto se introduce directamente como dato en la llamada CALL, por lo que estamos usando código automodificable. Esto no es una buena práctica de programación en procesadores más potentes (o falla o se carga la caché), pero con el Z80 no hay problema. Al llegar a THEJUMP, se hace una llamada al efecto correspondiente.
LD B,250 ; Pausa de 5 segundos, para que de tiempo a PPAUSE: HALT ; ver las pantallas al usar emuladores con DJNZ PPAUSE ; carga instantanea
Si preferimos usar el slideshow en una cinta real (o desactivar la carga rápida en el emulador que usemos), podemos bajar el valor de B, o incluso omitir totalmente esta parte.
LD A,(N_SCREEN) ;Incrementar el contador de pantallas INC A CP N_S JR Z,THEEND ;Si se han visto todas ya, acabar LD (N_SCREEN),A LD A,(N_EFFECT) ;Incrementar contador de efecto INC A CP N_E JR Z,EXTBUC ;Si se han usado todos, empezar de 0 JR INTBUC ;Si no, usar el siguiente
Esta parte del código resulta bastante sencilla, no creo que deba explicar nada más.
Y llegamos al final:
THEEND: XOR A ;Esperar pulsacion de tecla WAITKEY: IN A,(254) CPL AND 31 JR Z,WAITKEY RET ;Volver al BASIC
Si no hay ninguna tecla pulsada, el resultado de la instrucción IN serán xxx11111, mientras que si hay pulsada alguna tecla aparecerá algún 0 en los últimos 5 bits. La rutina WAITKEY es por lo tanto una forma muy común de esperar a que se pulse una tecla.
;Variables del bucle principal del slideshow N_EFFECT: DEFB 0 N_SCREEN: DEFB 0 ;Tabla de efectos TABLE: DEFW EF1 DEFW EF2 DEFW EF3 DEFW EF4
En la tabla de efectos podemos ordenar los efectos como queramos e incluso poner alguno repetido para que aparezca con mas frecuencia que los demás, lo más importante es que el número de efectos que aparezcan en la tabla sea igual que el especificado en la constante N_E definida mas arriba, si fuera menor podría introducirse basura en la llamada al efecto con nefastas consecuencias.
Primer efecto: Entrelazado de líneas
Este efecto se basa en hacer un desplazamiento lateral de la pantalla, de forma que vaya entrando, pero dividiendo las líneas de forma que las pares entren por la parte de la izquierda de la pantalla, moviéndose a la derecha, y las impares entren por la derecha, avanzando hacia la izquierda. En el efecto se realizan dos bucles, el externo se repite 32 veces, una para cada carácter que avanzamos, y el interno 96 veces (192 / 2), uno por cada pareja de líneas que desplazamos.
; EF1: Efecto entrelazado de lineas, por Metalbrain EF1_COUNT: DEFB 0 Esta variable se utilizará de contador de líneas en el bucle interno del efecto. EF1: LD A,56 LD HL,22528 LD DE,22529 LD (HL),A LD BC,767 LDIR ;Fijar los atributos
Con esto fijamos los atributos de la pantalla a ink=0, paper=7. Así podremos ver el desplazamiento de las líneas en todo momento.
A partir de ahora los propios comentarios del código son bastante abundantes, así que no tendré que detenerme demasiado.
LD BC,1 ;BC = numero de caracteres que entran ; de cada linea en cada iteracion. ; Varia de 1 a 32, con 32 acabamos ; de mostrar toda la pantalla y por lo ; tanto llegar a 33 significa que ; el efecto ha terminado
Este valor de BC va a tener varios usos. Uno de ellos es ajustar las direcciones de origen para líneas pares y de destino en las impares a su valor correcto en cada iteración. Otro es servir de contador en la instrucción LDIR que vamos a utilizar para mover los datos.
EF1_EXTBUC: LD HL,49152+32 ;HL = direccion de origen en los ; movimientos de bytes, por lo que ; se situa en la zona de memoria ; donde hemos cargado la pantalla ; que tenemos que presentar ;Las lineas pares entran hacia la ; derecha, por lo que HL al principio ; apunta 32 bytes mas alla del ; comienzo de la pantalla. Asi al ; restar BC, conseguiremos el comienzo ; del extremo derecho que tenemos que ; pintar de la linea. LD DE,16384 ;DE = destino en los movimientos de ; bytes, por lo que se situa en la ; zona de memoria correspondiente a ; la pantalla que aparece en la TV ;Al principio apunta justo al comienzo ; de la pantalla, pues es donde va a ; aparecer la primera linea. LD A,96 ;96 parejas de lineas quedan por ; mover EF1_INTBUC: LD (EF1_COUNT),A ;Guardar la variable PUSH HL ;Guardar HL y DE en la pila PUSH DE ; para recuperarlos tras hacer PUSH HL ; los LDIRs PUSH DE OR A ;Poner el Carry Flag a 0 para hacer ; que el SBC sea como un SUB SBC HL,BC ;Origen de los bytes que vamos a mover LD A,C ;Preservar C LDIR ;Mover linea par, entrando por la ; izquierda hacia la derecha LD C,A ;Recuperar C
Sabemos que B=0, por lo que es mejor preservar el valor de BC de esta manera que usando las instrucciones PUSH y POP, que consumen 11 estados cada una en lugar de 4.
POP DE ;Recuperar DE y HL POP HL ;Las lineas impares entran por la ; derecha hacia la izquierda, de forma ; que tenemos que cambiar las ; posiciones relativas de DE y HL ; (aparte de hacer que bajen 1 linea ; y se coloquen en la impar) ;En este caso DE hay que colocarlo ; pasado el final de la linea y ; restarle BC para que obtenga su ; valor final EX DE,HL ;Intercambiamos DE y HL porque DE ; no permite realizar restas. En lugar ; de poner aqui esta instruccion, se ; podria haber puesto justo antes del ; SBC y operar en las siguientes ; 6 instrucciones con E y D. Lo mismo ; da una cosa que otra. LD A,32 ;Sumamos 288 a HL, 32 para colocar ADD A,L ; al final de la linea y 256 para LD L,A ; bajar a la linea impar. LD A,1 ADC A,H LD H,A
La forma de realizar esta suma parece complicada, pero es la mejor manera de hacerla con los registros e instrucciones que el Z80 pone a nuestra disposición. En total usamos 8 bytes y 30 estados. Una suma de 16 bits ya consume de por si 2 bytes y 11 estados, a esto le sumamos otros 3 bytes y 10 estados para cargar el valor 288 en un registro, y otros 2 bytes y 22 estados para preservar y liberar un registro con las instrucciones de pila. En total ganaríamos 1 byte a costa de 13 estados.
SBC HL,BC ;Restamos el numero de bytes a mover ; para acabar de ajustar la direccion EX DE,HL ;Y volvemos a dejar el resultado en DE LD A,C ;Preservar valor de C LD C,224 ;Sumamos 224 a HL, lo cual es equivalente a ADD HL,BC ;restar los 32 que nos sobran y sumar 256 ;para bajar una linea LD C,A ; Recuperar valor de C LDIR ;Mover la linea impar LD C,A ;Recuperar numero de bytes de nuevo
En este caso el hecho de que 224 sea menor que 0, favorece el uso de BC para operar directamente con una operación de 16 bits, al contrario que en caso anterior.
POP DE ;Recuperar posiciones originales POP HL ; en pantalla y en memoria DEC HL ;hacer que la linea de HL sea par CALL NEXT2_EXDEHL ;Bajar 2 lineas para DE CALL NEXT2_EXDEHL ;Y otras 2 para HL
No os preocupéis si no entendéis cómo la misma rutina puede operar sobre dos registros diferentes, lo veremos más adelante.
INC HL ;recuperar el +32 que necesitamos LD A,(EF1_COUNT) ;Obtener cuenta de lineas DEC A ;Decrementar JR NZ,EF1_INTBUC ;Si no es cero, repetir el bucle ; interno para las 96 parejas de ; lineas HALT ;Pausa HALT INC C ;Incrementar numero de pixels a ; mostrar en la siguiente iteracion LD A,C ;Comparar con 33 CP 33 JR NZ,EF1_EXTBUC ;Continuar el bucle externo si es ; necesario LD HL,49152+6144 LD DE,16384+6144 LD BC,768 LDIR ;Mover los atributos RET ;Regresar a la rutina principal
Ahora vamos a ver la rutina que operaba sobre dos registros distintos con la misma llamada. Aquí tenemos su comienzo:
NEXT2_EXDEHL: INC D ; Incrementa 2 lineas + intercambio de HL y DE NEXT_EXDEHL: EX DE,HL ; Incrementa 1 linea + intercambio de HL y DE
Como podéis observar el secreto es muy sencillo: al comienzo de la rutina se intercambian los valores de HL y DE, de tal forma que la rutina actúa primero sobre HL con el valor de DE, y luego vuelve a actuar sobre HL pero esta vez sobre el valor que tenía originalmente, y el intercambio inicial además ha dejado en DE el dato procesado la primera vez. El incremento inicial también puede ponerse debajo de la orden de intercambio (pero en este caso habría que poner INC H, obviamente), pero está puesto así para que existan otros puntos de entrada en la rutina que puedan ser útiles. En un principio la rutina fue diseñada para que pudieran usarla distintos efectos en el slideshow FSC3, y colocada en una zona de rutinas comunes antes de los efectos, aunque al final tan sólo este efecto utiliza la rutina, y por eso lo he movido aquí esta vez. La instrucción de intercambio también podría haberse puesto fuera de la rutina, envolviendo una de las llamadas, pero en ese caso habría que repetirla, mientras que teniéndola dentro ahorramos un byte. Y mejor dejo ya de enrollarme y continuamos viendo la parte que realiza la bajada de línea:
NEXT_HL: INC H ;Incrementa 1 linea, si pasamos de caracter LD A,H ; se queda a 0 y se produce un aumento de ; tercio AND 7 RET NZ ;Salir si no hemos pasado de caracter LD A,L ADD A,32 ;Pasar al siguiente caracter LD L,A RET C ;Salir si se produjo un cambio de tercio LD A,H SUB 8 ;Quitar el aumento de tercio que no se produjo LD H,A RET
Para entender esta rutina, hay que recordar el formato de las direcciones de pantalla:
byte alto | byte bajo |
---|---|
010 tt yyy | YYY XXXXX |
Donde:
tt | tercio de la pantalla, de 00 a 10 (con 11 estamos con los atributos o fuera de la pantalla). |
YYY | fila dentro del tercio (en caracteres). |
yyy | fila dentro del carácter (en pixels). |
XXXXX | columna |
Y tenemos que tener en cuenta 3 casos:
- cuando estamos dentro de un carácter: para bajar al siguiente carácter basta con incrementar H, ya que con esto aumentamos el campo yyy, que es lo que se requiere.
- cuando pasamos dentro de un carácter al siguiente dentro del mismo tercio, hay que pasar el campo yyy de 7 a 0, y debemos incrementar el campo YYY sumando 32 a L.
- cuando pasamos de un tercio al siguiente, hay que pasar los campos yyy e YYY a 0, e incrementar tt.
Cuando la línea es par, siempre se va a dar el caso 1, y no es necesario hacer ninguna comprobación, por eso funciona el incremento del comienzo de forma que se bajan dos líneas.
Segundo efecto: Atributos en espiral
Este efecto es algo más sencillo que el anterior. Básicamente, copiamos el gráfico en la pantalla con los atributos en negro, y vamos copiando los atributos siguiendo una espiral rectangular para ir desvelando el contenido de la pantalla.
Veamos la rutina:
; EF2: Efecto atributos espiral, por Metalbrain EF2_COUNT: DEFB 0 EF2: LD DE,16384+6144+1 ;Comienzo zona atributos + 1 LD HL,16384+6144 ;Comienzo zona atributos XOR A ;A = 0 (color negro) LD (HL),A ;Poner color negro en el primer ; caracter de la pantalla LD BC,767 ;Numero de caracteres a borrar LDIR ;Poner pantalla en negro LD DE,16384 LD HL,49152 LD BC,6144 LDIR ;Copiar pixels LD IX,1 ;Incremento horizontal LD IY,32 ;Incremento vertical
Comenzamos por la esquina superior izquierda, por lo que el primer incremento horizontal es positivo (vamos a la derecha) y el vertical también (hacia abajo).
LD HL,16383+6144 ;Iniciar HL = Zona de atributos-1 LD A,32 ;Contador inicial LD (EF2_COUNT),A ;Iniciar variable EF2_EXTBUC: LD A,(EF2_COUNT) ;Contador LD B,A ;B = numero de caracteres a desvelar PUSH IX POP DE ;DE = IX = incremento horizontal CALL EF2_BUC ;Mostrar una linea horizontal de ; atributos
La rutina EF2_BUC que veremos más adelante funciona especificando en DE el incremento para obtener el siguiente carácter.
PUSH DE POP IX ;IX = DE
Al volver, en DE aparece el valor negado del que entró, de modo que lo almacenamos para que la siguiente línea se recorra en sentido contrario.
LD A,(EF2_COUNT) DEC A LD (EF2_COUNT),A ;Decrementar cuenta de bytes
La espiral cada vez se hace más pequeña.
SUB 8 ;Restar 8 para obtener el numero ; de caracteres verticales para el ; siguiente paso JR Z,EF2_END ;Si salen 0, hemos acabado
Aquí se detecta cuándo se produce el final de la rutina. La última línea que se recorre va a ser horizontal porque la pantalla es mas ancha que alta.
LD B,A ;B = numero de caracteres a desvelar PUSH IY POP DE ;DE = IY = incremento vertical CALL EF2_BUC ;Mostrar linea vertical PUSH DE POP IY ;IY = DE JR EF2_EXTBUC ;Repetir hasta salir
Con esto concluye el bucle principal del efecto, veamos ahora la subrutina para copiar un número de caracteres especificado en B incrementando HL en DE bytes cada vez.
EF2_BUC: ADD HL,DE ;Actualizar HL con el incremento ; que le corresponde SET 7,H ;Hacer que HL apunte a la zona de ; memoria alta donde se almacena el ; grafico. Esto es posible hacerlo ; asi porque las direcciones de ; almacen y de pantalla solo se ; diferencian en un bit LD A,(HL) ;Tomar valor RES 7,H ;Volver a apuntar a la pantalla LD (HL),A ;Escribir valor. Con esto desvelamos ; el caracter que habia oculto. LD A,B ;Hacer un HALT solo cada 8 caracteres AND 7 ; desvelados (hacerlo siempre JR NZ,EF2_NOHALT ; resultaria muy lento) HALT EF2_NOHALT: DJNZ EF2_BUC ;Mostrar todos los caracteres que se ; han pedido HALT ;Otro HALT LD A,D ;Negar el valor de DE, con lo cual CPL ; cambiamos el sentido la proxima LD D,A ; vez que avancemos LD A,E NEG LD E,A
La parte alta de DE basta con complementarla, pero la baja hay que negarla.
EF2_END: RET ;Volver de la rutina EF2_BUC, o bien ; regresar a la rutina principal al ; acabar el efecto
Y eso es todo lo que este efecto ha dado de sí. Ya os dije que era sencillo. Seguramente hay mejores formas de hacer esto en lugar de usar los registros IX e IY, pero tenía prisa y esto fue lo primero que se me ocurrió. Si el lector lo desea, puede intentar otras ideas mejores.
Tercer efecto: Caracteres desordenados
Este efecto es bastante espectacular: sin que se borre la pantalla anterior, los caracteres de la nueva van apareciendo de forma aparentemente aleatoria, hasta que sustituyen por completo la imagen anterior. Para lograr cubrir toda la pantalla, necesitaremos una tabla donde indicar qué caracteres han sido ya sustituidos y cuáles no. La tabla no ocupará los 768 bytes de la tabla de atributos, sino tan sólo 256 correspondientes a un tercio. En cada iteración de la rutina desvelaremos un carácter de cada tercio, con una separación entre las posiciones de forma que no quede cantoso. Para escoger qué carácter va a ser el siguiente, usaremos los bytes de la ROM como guía para saber la separación entre un carácter que aparezca y el siguiente.
Vamos al lío:
; EF3: Efecto caracteres desordenados, por Metalbrain EF3_FILLTABLE EQU 49152-256 ; Tabla para el efecto 3 EF3_POINTER: DEFW 0 EF3_COUNTER: DEFB 0 EF3: LD B,0 ;Contador de 0 a 0 -> 256 iteraciones LD HL,EF3_FILLTABLE;Tabla de relleno, para indicar que ; caracteres hemos volcado ya y cuales ; no XOR A ;Valor inicial para rellenar la tabla LD (EF3_COUNTER),A ;Iniciar contador EF3_FILLIT: LD (HL),A ;Llenar la tabla
Un 0 en la tabla indicará que el carácter está libre, o sea, que aún no ha sido sustituido.
INC L DJNZ EF3_FILLIT LD IX,(EF3_POINTER) ;IX apunta a la ROM, de donde se toman ; los valores de incremento que ; indican cuantas posiciones libres ; tenemos que saltar en la tabla antes ; de quedarnos con una EF3_EXTBUC: LD A,(EF3_COUNTER) ;Contador en A LD B,A ;Y en B AND A ;Ver si es 0 LD A,(IX+0) ;Cargar en valor de incremento JR Z,EF3_NOADJUST ;Si B vale 0, equivale a 256 y no hay ; que ajustar el valor de incremento EF3_ADJUST: SUB B ;Restar el contador al valor de JR NC,EF3_ADJUST ; incremento hasta que obtengamos un ADD A,B ; acarreo, y entonces lo volvemos a ; sumar. Con eso logramos que el ; valor de incremento sea menor que ; el contador de posiciones libres, ; para no estar dandole vueltas a la ; tabla tontamente. En otras palabras, ; hacemos A = (A MOD B), la operacion ; del modulo EF3_NOADJUST: LD B,A ;El valor de incremento ajustado lo ; ponemos en B, para el bucle con DJNZ XOR A ;En A ponemos el valor a buscar: 0 ; (no copiado todavia) EF3_PARSE: INC L ;Buscamos en la tabla B valores libres CP (HL) JR NZ,EF3_PARSE DJNZ EF3_PARSE INC (HL) ;Marcamos el valor en la tabla PUSH HL ;Guardamos HL para mas tarde LD H,128+88 ;Apuntamos a la zona de atributos ; origen
Hay que tener en cuenta que el valor 88 en binario es 01011000, correspondiente a la zona de atributos.
LD B,3 ;3 caracteres, uno por cada tercio INC IX ;Incrementamos IX para apuntar a un ; valor de incremento diferente en la ; ROM. Este INC puede colocarse en ; cualquier otra parte del programa EF3_OTHERTHIRD: PUSH BC PUSH HL ;Preservar registros LD E,L ;DE=HL-32768, misma direccion pero LD A,H ; en pantalla SUB 128 LD D,A LD A,(HL) ;Copiar atributo LD (DE),A ;Pasar de direccion de atributos a ; direccion de pixels en DE y HL
Para realizar la operación indicada en el comentario, tan sólo tenemos que modificar el valor alto del registro, ya que el valor bajo es idéntico (el campo yyy lo ponemos a 0):
atributo | 010 11 0tt | YYY XXXXX |
gráfico | 010 tt yyy | YYY XXXXX |
Veamos el código:
; A Carry LD A,D ;010110tt ? RLCA ;10110tt0 0 RLA ;0110tt00 1 RLCA ;110tt000 0
Aquí lo más sencillo sería haber usado desplazamientos, ya que lo que queremos es desplazar a la izquierda a la vez que introducimos ceros por la derecha, pero por desgracia no existen instrucciones de 1 byte que permitan desplazar directamente en A de la misma forma que existen rotaciones, de forma que podemos aprovechar que conocemos el valor de los bits que van a salir para ir introduciendo esos ceros. Si os parece lioso, repasad las microfichas con las instrucciones de rotación y observad los valores.
LD H,A
Aprovechamos que ha salido el bit 7 a 1 para actualizar H.
AND 88 ;010tt000 LD D,A
En lugar del valor 88 para el AND, nos vale cualquier valor de la forma 01x11xxx, por ejemplo 127, y en lugar del AND también podría haberse hecho SUB 128, o XOR 128, o ADD A,128. El caso es quitar el bit 7. RES 7,A también podría usarse, pero cuesta un estado más.
LD B,8 ;8 bytes por caracter EF3_DOCHAR: LD A,(HL) ;Copiar byte de pixels LD (DE),A INC H ;Apuntar a la siguientes lineas INC D DJNZ EF3_DOCHAR
Aquí no nos tenemos que preocupar del cambio de carácter, con lo que el diseño de la pantalla del Spectrum se vuelve una ventaja.
POP HL ;Recuperar HL POP BC INC H ;Apuntar al siguiente tercio LD A,49 ;Pequeno desplazamiento entre los ADD A,L ; caracteres que se muestran de cada LD L,A ; tercio, para que parezca mas ; aleatorio DJNZ EF3_OTHERTHIRD ;Repetir para los 3 tercios POP HL ;Recuperar direccion de la tabla LD A,(EF3_COUNTER) ;Chequear la paridad del contador y AND A ; hacer un HALT solo cuando el JP PE,EF3_NOHALT ; contador tenga paridad impar. Por HALT ; termino medio, se hara una pausa ; cada 6 caracteres copiados EF3_NOHALT: DEC A ;Repetir hasta completar todos los LD (EF3_COUNTER),A ; caracteres de la pantalla JR NZ,EF3_EXTBUC EF3_END: LD (EF3_POINTER),IX;Actualizar el puntero, asi si varias HALT ; pantallas usan este efecto, tendran RET ; patrones diferentes de aparicion ; de los caracteres
Con esto acaba el efecto, espero que os haya gustado tanto como a mí, porque estoy bastante orgulloso de cómo quedó y las ideas que utilicé.
Cuarto efecto: Heat up, cool down
Este efecto se basa en el que posiblemente sea el mas sencillo de los efectos de la demoscene, el primero que se enseña en los tutoriales: el fade-in/fade-out. Estos efectos consisten en variar la paleta de colores para que, sin tocar el gráfico que hay en pantalla, éste aparezca (fade-in) o se desvanezca (fade-out) poco a poco. En Spectrum no hay paleta, pero los atributos vienen a ser como una paleta de cada carácter.
Partiendo de unos atributos a 0, para hacer un fade-in basta con ir aumentando la tinta y papel de cada carácter hasta que se alcance el valor original, momento en el que no se aumenta más para ese carácter. Lo malo es que este proceso tan sólo tiene un máximo de 7 pasos, de forma que se me ocurrió otra variante dividida en dos mitades. La primera es un proceso de “calentamiento” (de ahí el “Heat up”), en la cual se hace un fade-in como el indicado para la tinta a la vez que se sube también el color del papel, pero para éste se sigue aumentando su valor hasta llegar al blanco, independientemente del valor original, y tras una pequeña pausa, comienza el proceso de “enfriamiento” (“cool down”) en el que se va decrementando tan sólo el papel hasta llegar a su valor original.
Tras la larga explicación, la rutina en sí es bastante sencilla:
EF4: LD DE,16384+6144 LD HL,49152+6144 LD BC,3 ;Numero de tercios, B=0 para dar ; 256 vueltas con DJNZ
En otras palabras, poniendo BC a 3 estamos metiendo un 768 en CB, que es lo que se usa haciendo un DJNZ seguido de DEC C/JR NZ.
EF4_FLABRI: LD A,(HL) ;Coger atributo AND 192 ;Dejar los bits de flash y bright LD (DE),A ;Fijar flash y bright INC HL INC DE DJNZ EF4_FLABRI ;Procesar caracteres del tercio DEC C JR NZ,EF4_FLABRI ;Repetir para los 3 tercios
Los atributos de flash y bright se copian al principio para no tener que tratarlos después en el proceso.
LD DE,16384 LD HL,49152 LD BC,6144 LDIR ;Copiar pantalla menos atributos HALT LD E,1 ;Heat actual en registro E EF4_HEAT_EXT: LD HL,49152+6144 ;Apuntar al primer atributo LD BC,3 ;C=3 tercios de B=0=256 caracteres EF4_HEAT_INT: LD A,(HL) ;Cargar atributo en A RES 7,H ;Pasar a coordenada de pantalla AND 7 ;Aislar bits de tinta CP E ;Comparar con Heat actual CCF ;Invertir acarreo. Si CF=1, es que EF4_NOINK: LD A,8 ; tenemos que incrementar la tinta ADC A,(HL) ; ademas del papel LD (HL),A ; SET 7,H ;Recuperar posicion original INC HL ;Avanzar posicion DJNZ EF4_HEAT_INT ;Repetir tercio DEC C JR NZ,EF4_HEAT_INT ;Repetir los 3 tercios HALT HALT HALT HALT ;Pausa INC E ;Incrementar Heat LD A,E CP 8 JR NZ,EF4_HEAT_EXT ;Si es menor que 8, seguir calentando LD B,12 ;Pausa antes de pasar a enfriar el EF4_PEAKPAUSE: HALT ; el papel DJNZ EF4_PEAKPAUSE LD E,56 ;Cool a 7 (56 = 7*8) EF4_COOL_EXT: LD HL,49152+6144 ;Apuntar al primer atributo LD BC,3 ;como antes, CB = 768 EF4_COOL_INT: LD A,(HL) ;Tomar atributo RES 7,H ;Pasar a coordenada de pantalla AND 56 ;Aislar bits del papel CP E ;Comprobar si papel < Cool JR NC,EF4_NOPAPER ; si no lo es, no enfriamos LD A,248 ;Sumar 248 es como restar 8 ADD A,(HL) ;Enfriar papel LD (HL),A EF4_NOPAPER: SET 7,H ;Apuntar a la pantalla virtual INC HL ;Avanzar posicion DJNZ EF4_COOL_INT DEC C JR NZ,EF4_COOL_INT ;Repetir bucle interno HALT HALT HALT HALT ;Pausa LD A,E SUB 8 ;Bajar valor de Cool LD E,A JR NC,EF4_COOL_EXT ;Si no es 0, seguir enfriando RET ;Salir del efecto
Y eso es todo.
Conclusión
Espero que lo hayáis entendido todo y seáis capaces de hacer algún que otro efecto por vuestra cuenta, no olvidéis que lo importante es practicar. Y que os divirtáis modificando los míos o haciendo vuestros propios slideshows. Como de costumbre, si hay algo que no veis claro (incluso después de varias miradas), os ponéis en contacto conmigo, preferiblemente a través de la lista de correo [z80asm].
Hasta la próxima.
Enlaces y ficheros
Metalbrain
Julio 2004
MagazineZX #8