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
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.
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.
Aprovechamos que ha salido el bit 7 a 1 para actualizar H.
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.