cursos:ensamblador:gfx5_mapeados

Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Ambos lados, revisión anteriorRevisión previa
Próxima revisión
Revisión previa
cursos:ensamblador:gfx5_mapeados [06-01-2024 16:44] – [Propiedades de los tiles y el mapeado] sromerocursos:ensamblador:gfx5_mapeados [19-01-2024 08:27] (actual) sromero
Línea 11: Línea 11:
  En este capítulo veremos cómo definir las pantallas del mapeado y rutinas para imprimir estas pantallas en la videomemoria.  En este capítulo veremos cómo definir las pantallas del mapeado y rutinas para imprimir estas pantallas en la videomemoria.
  
-\\+\\ 
 ===== Creación de mapas a partir de bloques o tiles ===== ===== Creación de mapas a partir de bloques o tiles =====
  
Línea 24: Línea 24:
  Pueden existir incluso identificadores numéricos de bloque que nuestra rutina trate de forma especial, para definir por ejemplo bloques que no deben de ser dibujados y que permitan ver el fondo que hay en la pantalla, como sucede en el caso de Sokoban:  Pueden existir incluso identificadores numéricos de bloque que nuestra rutina trate de forma especial, para definir por ejemplo bloques que no deben de ser dibujados y que permitan ver el fondo que hay en la pantalla, como sucede en el caso de Sokoban:
  
-\\+\\ 
 {{ cursos:ensamblador:gfx3_tilemap.png | Tilemaps }} {{ cursos:ensamblador:gfx3_tilemap.png | Tilemaps }}
 ;#; ;#;
 //Tilemap: componiendo un mapa en pantalla\\ a partir de tiles de un tileset/spriteset + mapa.// //Tilemap: componiendo un mapa en pantalla\\ a partir de tiles de un tileset/spriteset + mapa.//
 ;#; ;#;
-\\+\\ 
  
  Crear los mapeados mediante tiles nos permite un enorme ahorro de memoria ya que en lugar de necesitar 6912 bytes por cada pantalla de juego nos basta con un tileset y pantallas formadas por repetición de los mismos. Una pantalla completa (256x192) formada por tiles de 16x16 ocupará apenas (256/16)x (192/16) = 16x12 = 192 bytes. En los 7 KB que ocupa una pantalla gráfica entera podemos definir 36 pantallas de juego en base a tiles.  Crear los mapeados mediante tiles nos permite un enorme ahorro de memoria ya que en lugar de necesitar 6912 bytes por cada pantalla de juego nos basta con un tileset y pantallas formadas por repetición de los mismos. Una pantalla completa (256x192) formada por tiles de 16x16 ocupará apenas (256/16)x (192/16) = 16x12 = 192 bytes. En los 7 KB que ocupa una pantalla gráfica entera podemos definir 36 pantallas de juego en base a tiles.
Línea 35: Línea 35:
  Una gran parte de los juegos de Spectrum tienen sus pantallas formadas por mapas de tiles, debido al gran ahorro de memoria que suponen. El diseñador de los gráficos y de las pantallas será el principal responsable de que existan suficientes bloques diferentes para representar todos los elementos del mapeado con suficiente diversidad gráfica.  Una gran parte de los juegos de Spectrum tienen sus pantallas formadas por mapas de tiles, debido al gran ahorro de memoria que suponen. El diseñador de los gráficos y de las pantallas será el principal responsable de que existan suficientes bloques diferentes para representar todos los elementos del mapeado con suficiente diversidad gráfica.
  
-\\+\\ 
 {{ :cursos:ensamblador:gfx5_gamemaps.png | Juegos basados en tiles }} {{ :cursos:ensamblador:gfx5_gamemaps.png | Juegos basados en tiles }}
-\\+\\ 
  
  Y no sólo podemos realizar juego de puzzle tipo Puzznic, Plotting o Sokoban: en base a tiles podemos crear videoaventuras, los mapeados de un juego de disparos, juegos de laberintos, plataformas, o generar todas las pantallas en que se desenvuelva un arcade. Una vez impresa la pantalla en base a bloques, el juego puede desarrollarse pixel a pixel en cuanto al movimiento del personaje principal y los enemigos y objetos.  Y no sólo podemos realizar juego de puzzle tipo Puzznic, Plotting o Sokoban: en base a tiles podemos crear videoaventuras, los mapeados de un juego de disparos, juegos de laberintos, plataformas, o generar todas las pantallas en que se desenvuelva un arcade. Una vez impresa la pantalla en base a bloques, el juego puede desarrollarse pixel a pixel en cuanto al movimiento del personaje principal y los enemigos y objetos.
  
-\\+\\ 
 ===== Organización de los mapas en memoria ===== ===== Organización de los mapas en memoria =====
  
Línea 50: Línea 50:
  Comencemos examinando la forma de definir y trazar una única pantalla. Posteriormente veremos las estructuras de datos necesarias para definir un mapa completo como una matriz de pantallas.  Comencemos examinando la forma de definir y trazar una única pantalla. Posteriormente veremos las estructuras de datos necesarias para definir un mapa completo como una matriz de pantallas.
  
-\\+\\ 
 ===== Pantallas del mapa ===== ===== Pantallas del mapa =====
  
Línea 59: Línea 59:
  Una pantalla en formato horizontal contiene los identificadores de tiles de la pantalla en scanlines horizontales de tiles, comenzando con la fila 0, a la cual le sigue la fila 1, la 2, la 3, etc.  Una pantalla en formato horizontal contiene los identificadores de tiles de la pantalla en scanlines horizontales de tiles, comenzando con la fila 0, a la cual le sigue la fila 1, la 2, la 3, etc.
  
-\\+\\ 
  El tileset de Sokoban (el primero de los sets gráficos disponibles) es el siguiente:  El tileset de Sokoban (el primero de los sets gráficos disponibles) es el siguiente:
  
-\\+\\ 
 {{ :cursos:ensamblador:gfx5_sokoset.png | Tileset de Sokoban }} {{ :cursos:ensamblador:gfx5_sokoset.png | Tileset de Sokoban }}
-\\+\\ 
  
  Veamos ampliados los bloques de 16x16:  Veamos ampliados los bloques de 16x16:
  
-\\+\\ 
 {{ :cursos:ensamblador:gfx3_tilesetsk.png | (Ampliacion del tileset) }} {{ :cursos:ensamblador:gfx3_tilesetsk.png | (Ampliacion del tileset) }}
-\\+\\ 
  
  Como puede apreciarse, hay 8 tiles gráficos que numeramos desde el 0 al 7 siendo el 0 un bloque vacío de fondo negro.  Como puede apreciarse, hay 8 tiles gráficos que numeramos desde el 0 al 7 siendo el 0 un bloque vacío de fondo negro.
  
-\\+\\ 
  Ahora veamos cómo codificar una pantalla utilizando el tileset que acabamos de ver. Utilizaremos para eso la primera pantalla del juego, que es la siguiente:  Ahora veamos cómo codificar una pantalla utilizando el tileset que acabamos de ver. Utilizaremos para eso la primera pantalla del juego, que es la siguiente:
  
-\\+\\ 
 {{ :cursos:ensamblador:gfx5_sokobanl1.png?640 | Pantalla 1 de Sokoban }} {{ :cursos:ensamblador:gfx5_sokobanl1.png?640 | Pantalla 1 de Sokoban }}
-\\+\\ 
  
  Es una pantalla de 16x12 tiles formados por 2x2 bloques cada uno (16x16=256 y 12x16=192 -> 256x192 pixeles). Hay 7 tiles gráficos, uno "en blanco" (el 0), y un tile que es transparente (el que permite que se vea el fondo). Los tiles están impresos sobre un fondo negro decorado con diferentes logotipos y gráficos del robot protagonista (de ahí la importancia de los tiles transparentes, que permiten que no se sobreescriba el fondo).  Es una pantalla de 16x12 tiles formados por 2x2 bloques cada uno (16x16=256 y 12x16=192 -> 256x192 pixeles). Hay 7 tiles gráficos, uno "en blanco" (el 0), y un tile que es transparente (el que permite que se vea el fondo). Los tiles están impresos sobre un fondo negro decorado con diferentes logotipos y gráficos del robot protagonista (de ahí la importancia de los tiles transparentes, que permiten que no se sobreescriba el fondo).
Línea 85: Línea 85:
  Veamos el proceso de construcción de la pantalla: Codificaremos cada "bloque" de pantalla con el identificador numérico que lo representa en el tileset, siendo 0 el tile inicial de contenido vacío, y siendo 8 el código especial que indique que no se debe dibujar el tile (de forma que sea transparente y deje ver el fondo).  Veamos el proceso de construcción de la pantalla: Codificaremos cada "bloque" de pantalla con el identificador numérico que lo representa en el tileset, siendo 0 el tile inicial de contenido vacío, y siendo 8 el código especial que indique que no se debe dibujar el tile (de forma que sea transparente y deje ver el fondo).
  
-\\ +\\  
-\\+\\ 
 **Organización horizontal** **Organización horizontal**
  
Línea 92: Línea 92:
  
 <code z80> <code z80>
-  DEFB 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8+    DEFB 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
 </code> </code>
  
Línea 98: Línea 98:
  
 <code z80> <code z80>
-  DEFB 8,8,8,8,8,8,2,3,1,4,8,8,8,8,8,8+    DEFB 8,8,8,8,8,8,2,3,1,4,8,8,8,8,8,8
 </code> </code>
  
Línea 104: Línea 104:
  
 <code z80> <code z80>
-  DEFB 8,8,8,8,1,2,3,0,0,5,4,8,8,8,8,8+    DEFB 8,8,8,8,1,2,3,0,0,5,4,8,8,8,8,8
 </code> </code>
  
Línea 115: Línea 115:
  
 sokoban_LEVEL1_h: sokoban_LEVEL1_h:
-  DEFB 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,+    DEFB 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
-  DEFB 8,8,8,8,8,8,2,3,1,4,8,8,8,8,8,+    DEFB 8,8,8,8,8,8,2,3,1,4,8,8,8,8,8,
-  DEFB 8,8,8,8,1,2,3,0,0,5,4,8,8,8,8,+    DEFB 8,8,8,8,1,2,3,0,0,5,4,8,8,8,8,
-  DEFB 8,8,8,8,4,0,6,6,0,0,5,8,8,8,8,+    DEFB 8,8,8,8,4,0,6,6,0,0,5,8,8,8,8,
-  DEFB 8,8,8,8,5,0,0,6,0,0,4,8,8,8,8,+    DEFB 8,8,8,8,5,0,0,6,0,0,4,8,8,8,8,
-  DEFB 8,8,8,8,4,0,0,0,0,0,5,8,8,8,8,+    DEFB 8,8,8,8,4,0,0,0,0,0,5,8,8,8,8,
-  DEFB 8,8,8,8,5,2,3,0,0,2,3,8,8,8,8,+    DEFB 8,8,8,8,5,2,3,0,0,2,3,8,8,8,8,
-  DEFB 8,8,8,8,8,8,1,0,0,0,4,8,8,8,8,+    DEFB 8,8,8,8,8,8,1,0,0,0,4,8,8,8,8,
-  DEFB 8,8,8,8,8,8,4,7,7,7,5,8,8,8,8,+    DEFB 8,8,8,8,8,8,4,7,7,7,5,8,8,8,8,
-  DEFB 8,8,8,8,8,8,5,2,3,2,3,8,8,8,8,+    DEFB 8,8,8,8,8,8,5,2,3,2,3,8,8,8,8,
-  DEFB 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,+    DEFB 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
-  DEFB 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8+    DEFB 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
 </code> </code>
  
- Hemos ubicado los datos en una representación visual clara mediante un DEFB por cada fila horizontal, pero el aspecto real de los datos en memoria es totalmente lineal:+ Hemos ubicado los datos en una representación visual clara mediante un ''DEFB'' por cada fila horizontal, pero el aspecto real de los datos en memoria es totalmente lineal:
  
 <code z80> <code z80>
 sokoban_LEVEL1_h: sokoban_LEVEL1_h:
- DEFB 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,2,3,1,4,(...),8,8,8,8+    DEFB 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,2,3,1,4,(...),8,8,8,8
 </code> </code>
  
Línea 140: Línea 140:
  Normalmente, la conversión de datos gráficos a identificadores de tile no se realiza manualmente sino que se emplea un "programa de dibujo de mapeados" (editor de mapas) con el que "dibujamos tiles" utilizando el tileset como paleta y que permite exportar el mapa directamente como datos.  Normalmente, la conversión de datos gráficos a identificadores de tile no se realiza manualmente sino que se emplea un "programa de dibujo de mapeados" (editor de mapas) con el que "dibujamos tiles" utilizando el tileset como paleta y que permite exportar el mapa directamente como datos.
  
-\\ +\\  
-\\+\\ 
 **Organización vertical** **Organización vertical**
  
Línea 148: Línea 148:
  Veamos de nuevo la pantalla inicial de Sokoban para codificarla verticalmente:  Veamos de nuevo la pantalla inicial de Sokoban para codificarla verticalmente:
  
-\\+\\ 
 {{ :cursos:ensamblador:gfx5_sokobanl1.png?640 | Pantalla 1 de Sokoban }} {{ :cursos:ensamblador:gfx5_sokobanl1.png?640 | Pantalla 1 de Sokoban }}
-\\+\\ 
  
  En este caso, la pantalla comenzaría con 4 columnas de datos "transparentes".  En este caso, la pantalla comenzaría con 4 columnas de datos "transparentes".
  
 <code z80> <code z80>
-  DEFB 8,8,8,8,8,8,8,8,8,8,8,+    DEFB 8,8,8,8,8,8,8,8,8,8,8,
-  DEFB 8,8,8,8,8,8,8,8,8,8,8,+    DEFB 8,8,8,8,8,8,8,8,8,8,8,
-  DEFB 8,8,8,8,8,8,8,8,8,8,8,+    DEFB 8,8,8,8,8,8,8,8,8,8,8,
-  DEFB 8,8,8,8,8,8,8,8,8,8,8,8+    DEFB 8,8,8,8,8,8,8,8,8,8,8,8
 </code> </code>
  
Línea 164: Línea 164:
  
 <code z80> <code z80>
-  DEFB 8,8,1,4,5,4,5,8,8,8,8,8+    DEFB 8,8,1,4,5,4,5,8,8,8,8,8
 </code> </code>
  
Línea 170: Línea 170:
  
 <code z80> <code z80>
-  DEFB 8,8,2,0,0,0,2,8,8,8,8,8+    DEFB 8,8,2,0,0,0,2,8,8,8,8,8
 </code> </code>
  
Línea 181: Línea 181:
  
 sokoban_LEVEL1_v: sokoban_LEVEL1_v:
-  DEFB 8,8,8,8,8,8,8,8,8,8,8,+    DEFB 8,8,8,8,8,8,8,8,8,8,8,
-  DEFB 8,8,8,8,8,8,8,8,8,8,8,+    DEFB 8,8,8,8,8,8,8,8,8,8,8,
-  DEFB 8,8,8,8,8,8,8,8,8,8,8,+    DEFB 8,8,8,8,8,8,8,8,8,8,8,
-  DEFB 8,8,8,8,8,8,8,8,8,8,8,+    DEFB 8,8,8,8,8,8,8,8,8,8,8,
-  DEFB 8,8,1,4,5,4,5,8,8,8,8,+    DEFB 8,8,1,4,5,4,5,8,8,8,8,
-  DEFB 8,8,2,0,0,0,2,8,8,8,8,+    DEFB 8,8,2,0,0,0,2,8,8,8,8,
-  DEFB 8,2,3,6,0,0,3,1,4,5,8,+    DEFB 8,2,3,6,0,0,3,1,4,5,8,
-  DEFB 8,3,0,6,6,0,0,0,7,2,8,+    DEFB 8,3,0,6,6,0,0,0,7,2,8,
-  DEFB 8,1,0,0,0,0,0,0,7,3,8,+    DEFB 8,1,0,0,0,0,0,0,7,3,8,
-  DEFB 8,4,5,0,0,0,2,0,7,2,8,+    DEFB 8,4,5,0,0,0,2,0,7,2,8,
-  DEFB 8,8,4,5,4,5,3,4,5,3,8,+    DEFB 8,8,4,5,4,5,3,4,5,3,8,
-  DEFB 8,8,8,8,8,8,8,8,8,8,8,+    DEFB 8,8,8,8,8,8,8,8,8,8,8,
-  DEFB 8,8,8,8,8,8,8,8,8,8,8,+    DEFB 8,8,8,8,8,8,8,8,8,8,8,
-  DEFB 8,8,8,8,8,8,8,8,8,8,8,+    DEFB 8,8,8,8,8,8,8,8,8,8,8,
-  DEFB 8,8,8,8,8,8,8,8,8,8,8,+    DEFB 8,8,8,8,8,8,8,8,8,8,8,
-  DEFB 8,8,8,8,8,8,8,8,8,8,8,8+    DEFB 8,8,8,8,8,8,8,8,8,8,8,8
 </code> </code>
  
- En este caso nuestros "DEFBs" son de 12 bytes cada uno, teniendo un total de 16 columnas de 12 bytes = los mismos 192 bytes de datos de pantalla.+ En este caso nuestros ''DEFB''son de 12 bytes cada uno, teniendo un total de 16 columnas de 12 bytes = los mismos 192 bytes de datos de pantalla.
  
  Según codifiquemos el mapa en formato horizontal o vertical tendremos que programar una rutina de impresión de pantalla u otra, ya que el orden de obtención de los datos desde el "vector de pantalla" es diferente.  Según codifiquemos el mapa en formato horizontal o vertical tendremos que programar una rutina de impresión de pantalla u otra, ya que el orden de obtención de los datos desde el "vector de pantalla" es diferente.
Línea 210: Línea 210:
  
  
-\\+\\ 
 ==== Rutinas de impresión de pantallas ==== ==== Rutinas de impresión de pantallas ====
  
- En este apartado vamos a ver rutinas diseñadas para dibujar una pantalla estática formada a base de tiles. La rutina debe imprimir un mapa de ANCHOxALTO tiles a partir de una posición inicial (X_INICIO,Y_INICIO), y está pensada para el trazado de una pantalla que no tiene scroll de ningún tipo y sobre la que después trabajaremos.+ En este apartado vamos a ver rutinas diseñadas para dibujar una pantalla estática formada a base de tiles. La rutina debe imprimir un mapa de ANCHOxALTO tiles a partir de una posición inicial (''X_INICIO'',''Y_INICIO''), y está pensada para el trazado de una pantalla que no tiene scroll de ningún tipo y sobre la que después trabajaremos.
  
  Ejemplos de juegos con este "formato" de pantalla son Manic Miner, Bubble Bobble, Sabre Wulf, Dynamite Dan, Sir Fred, etc.... La pantalla del mapa se dibuja una sóla vez y no se redibuja se o cambia de pantalla a menos que el personaje cruce los límites de la misma.  Ejemplos de juegos con este "formato" de pantalla son Manic Miner, Bubble Bobble, Sabre Wulf, Dynamite Dan, Sir Fred, etc.... La pantalla del mapa se dibuja una sóla vez y no se redibuja se o cambia de pantalla a menos que el personaje cruce los límites de la misma.
Línea 226: Línea 226:
 ;;; Aproximacion 1: ;;; Aproximacion 1:
 FOR Y=0 TO ALTO_PANTALLA_EN_TILES: FOR Y=0 TO ALTO_PANTALLA_EN_TILES:
-  FOR X=0 TO ANCHO_PANTALLA_EN_TILES: +    FOR X=0 TO ANCHO_PANTALLA_EN_TILES: 
-     TILE = PANTALLA[x][y] +       TILE = PANTALLA[x][y] 
-     XTILE = X_INICIAL + X*ANCHO_TILE +       XTILE = X_INICIAL + X*ANCHO_TILE 
-     YTILE = Y_INICIAL + Y*ALTO_TILE +       YTILE = Y_INICIAL + Y*ALTO_TILE 
-     CALL Draw_Sprite+       call Draw_Sprite
 </code> </code>
  
Línea 243: Línea 243:
  Veamos una aproximación mucho más óptima:  Veamos una aproximación mucho más óptima:
  
-1.- Teniendo en cuenta que los datos de la pantalla son consecutivos en memoria, vamos a utilizar un puntero para obtener los datos de los tiles linealmente sin tener que calcular la posición dentro del array de datos una y otra vez. Bastará con incrementar nuestro puntero (DIR_PANTALLA) para apuntar al siguiente dato.+1.- Teniendo en cuenta que los datos de la pantalla son consecutivos en memoria, vamos a utilizar un puntero para obtener los datos de los tiles linealmente sin tener que calcular la posición dentro del array de datos una y otra vez. Bastará con incrementar nuestro puntero (''DIR_PANTALLA'') para apuntar al siguiente dato.
  
-2.- En lugar de calcular la posición en videomemoria (DIR_MEM) de cada tile, calcularemos una sóla vez esta posición para el primer tile de cada fila y avanzaremos diferencialmente a lo largo de la misma. De esta forma sólo realizamos un cálculo de dirección de videomemoria por fila, y no por tile.+2.- En lugar de calcular la posición en videomemoria (''DIR_MEM'') de cada tile, calcularemos una sóla vez esta posición para el primer tile de cada fila y avanzaremos diferencialmente a lo largo de la misma. De esta forma sólo realizamos un cálculo de dirección de videomemoria por fila, y no por tile.
  
  
Línea 256: Línea 256:
 DIR_PANTALLA = Direccion de memoria de los datos de la PANTALLA actual DIR_PANTALLA = Direccion de memoria de los datos de la PANTALLA actual
 FOR Y=0 TO ALTO: FOR Y=0 TO ALTO:
-  DIR_MEM = Posicion_Memoria( X_INICIAL, Y_INICIAL + Y*ALTO_BLOQUE ) +    DIR_MEM = Posicion_Memoria( X_INICIAL, Y_INICIAL + Y*ALTO_BLOQUE ) 
-  FOR X=0 TO ANCHO_PANTALLA: +    FOR X=0 TO ANCHO_PANTALLA: 
-     TILE = [DIR_PANTALLA] +        TILE = [DIR_PANTALLA] 
-     DIR_PANTALLA = DIR_PANTALLA + 1 +        DIR_PANTALLA = DIR_PANTALLA + 1 
-     PUSH DIR_MEM +        push DIR_MEM 
-     DIR_SPRITE = BASE_TILESET + (TILE*ANCHO_TILE*ALTO_TILE) +        DIR_SPRITE = BASE_TILESET + (TILE*ANCHO_TILE*ALTO_TILE) 
-     Dibujar Sprite desde DIR_SPRITE a DIR_MEM +        Dibujar Sprite desde DIR_SPRITE a DIR_MEM 
-     POP DIR_MEM +        pop DIR_MEM 
-     DIR_MEM = DIR_MEM + ANCHO_TILE+        DIR_MEM = DIR_MEM + ANCHO_TILE
 </code> </code>
  
- Esta rutina sólo realiza un cálculo de posición de videomemoria por fila y además accede a nuestra pantalla de mapa linealmente. Antes de dibujar el tile en pantalla hacemos un PUSH de la dirección actual de memoria ya que la rutina de impresión la modifica. El posterior POP nos recupera la posición de impresión inicial de forma que baste un simple incremento de la misma para apuntar en videomemoria a la posición del siguiente tile que tenemos que dibujar.+ Esta rutina sólo realiza un cálculo de posición de videomemoria por fila y además accede a nuestra pantalla de mapa linealmente. Antes de dibujar el tile en pantalla hacemos un ''PUSH'' de la dirección actual de memoria ya que la rutina de impresión la modifica. El posterior POP nos recupera la posición de impresión inicial de forma que baste un simple incremento de la misma para apuntar en videomemoria a la posición del siguiente tile que tenemos que dibujar.
  
  Vamos a desarrollar un poco más la rutina en un pseudocódigo más parecido a ASM y con más detalles. Para ello establecemos las siguientes premisas:  Vamos a desarrollar un poco más la rutina en un pseudocódigo más parecido a ASM y con más detalles. Para ello establecemos las siguientes premisas:
  
-\\ +\\  
-   * Utilizaremos IX como puntero de la pantalla del mapa (DIR_PANTALLA).+   * Utilizaremos IX como puntero de la pantalla del mapa (''DIR_PANTALLA'').
    * Utilizaremos el valor de tile 255 como un tile "especial" que la rutina no dibujará. Este tile será pues un tile transparente que dejará ver el fondo en contraposición al típico bloque "0" vacío que borra un tile de pantalla.    * Utilizaremos el valor de tile 255 como un tile "especial" que la rutina no dibujará. Este tile será pues un tile transparente que dejará ver el fondo en contraposición al típico bloque "0" vacío que borra un tile de pantalla.
  
Línea 288: Línea 288:
        bucle_anchura:        bucle_anchura:
           A = (IX)           A = (IX)
-          INC IX+          inc ix
  
           Si A == 255 :           Si A == 255 :
-             JR saltar_bloque+             jr saltar_bloque
  
-          PUSH HL+          push hl
           DIR_SPRITE = BASE_TILESET + (A*ANCHO_TILE*ALTO_TILE)           DIR_SPRITE = BASE_TILESET + (A*ANCHO_TILE*ALTO_TILE)
           HL = BASE_TILESET + (A*8*TAMAÑO_BLOQUES_EN_CADA_TILE)           HL = BASE_TILESET + (A*8*TAMAÑO_BLOQUES_EN_CADA_TILE)
-          PUSH HL+          push hl
           Imprimir_Sprite_de_HL_a_DE           Imprimir_Sprite_de_HL_a_DE
           Convertir DIR HL imagen en DIR HL atributos           Convertir DIR HL imagen en DIR HL atributos
           Imprimir_Atributos_Sprite           Imprimir_Atributos_Sprite
-          POP HL+          pop hl
  
        saltar_bloque:        saltar_bloque:
           HL = HL + ANCHO_TILE           HL = HL + ANCHO_TILE
-          DJNZ bucle_anchura+          djnz bucle_anchura
  
-  DJNZ bucle_altura+  djnz bucle_altura
 </code> </code>
  
Línea 312: Línea 312:
  
  
-\\+\\ 
 ==== Rutina para bloques de 16x16 con mapeados horizontales ==== ==== Rutina para bloques de 16x16 con mapeados horizontales ====
  
Línea 347: Línea 347:
 DrawMap_16x16: DrawMap_16x16:
  
-   ;;;;;; Impresion de la parte grafica de los tiles ;;;;;; +    ;;;;;; Impresion de la parte grafica de los tiles ;;;;;; 
-   LD IX, (DM_MAP)           ; IX apunta al mapa +    ld ix, (DM_MAP)           ; IX apunta al mapa 
-   LD A, (DM_HEIGHT) +    ld a, (DM_HEIGHT) 
-   LD B                  ; B = ALTO_EN_TILES (para bucle altura)+    ld b                  ; B = ALTO_EN_TILES (para bucle altura)
  
 drawm16_yloop: drawm16_yloop:
-   PUSH BC                   ; Guardamos el valor de B+    push bc                   ; Guardamos el valor de B
  
-   LD A, (DM_HEIGHT)         ; A = ALTO_EN_TILES +    ld a, (DM_HEIGHT)         ; A = ALTO_EN_TILES 
-   SUB B                     ; A = ALTO - iteracion_bucle = Y actual +    sub b                     ; A = ALTO - iteracion_bucle = Y actual 
-   RLCA                      ; A = Y * 2+    rlca                      ; A = Y * 2
  
-   ;;; Calculamos la direccion destino en pantalla como +    ;;; Calculamos la direccion destino en pantalla como 
-   ;;; DIR_PANT = DIRECCION(X_INICIAL, Y_INICIAL + Y*2) +    ;;; DIR_PANT = DIRECCION(X_INICIAL, Y_INICIAL + Y*2) 
-   LD BC, (DM_COORD_X)       ; B = DB_COORD_Y y C = DB_COORD_X +    ld bc, (DM_COORD_X)       ; B = DB_COORD_Y y C = DB_COORD_X 
-   ADD AB +    add ab 
-   LD BA +    ld ba 
-   LD AB +    ld ab 
-   AND $18 +    and $18 
-   ADD A, $40 +    add a, $40 
-   LD HA +    ld ha 
-   LD AB +    ld ab 
-   AND +    and 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   ADD AC +    add ac 
-   LD L                  ; HL = DIR_PANTALLA(X_INICIAL,Y_INICIAL+Y*2)+    ld l                  ; HL = DIR_PANTALLA(X_INICIAL,Y_INICIAL+Y*2)
  
-   LD A, (DM_WIDTH) +    ld a, (DM_WIDTH) 
-   LD B                  ; B = ANCHO_EN_TILES+    ld b                  ; B = ANCHO_EN_TILES
  
 drawm16_xloop: drawm16_xloop:
-   PUSH BC                   ; Nos guardamos el contador del bucle+    push bc                   ; Nos guardamos el contador del bucle
  
-   LD A, (IX+0)              ; Leemos un byte del mapa +    ld a, (ix+0)              ; Leemos un byte del mapa 
-   INC IX                    ; Apuntamos al siguiente byte del mapa+    inc ix                    ; Apuntamos al siguiente byte del mapa
  
-   CP 255                    ; Bloque especial a saltar: no se dibuja +    cp 255                    ; Bloque especial a saltar: no se dibuja 
-   JP Z, drawm16_next+    jp z, drawm16_next
  
-   LD BA +    ld ba 
-   EX AFAF               ; Nos guardamos una copia del bloque en A' +    ex afaf               ; Nos guardamos una copia del bloque en A' 
-   LD AB+    ld ab
  
-   ;;; Calcular posicion origen (array sprites) en HL como: +    ;;; Calcular posicion origen (array sprites) en HL como: 
-   ;;;     direccion = base_sprites + (NUM_SPRITE*32) +    ;;;     direccion = base_sprites + (NUM_SPRITE*32) 
-   EX DEHL                 ; Intercambiamos DE y HL (DE=destino) +    ex dehl                 ; Intercambiamos DE y HL (DE=destino) 
-   LD BC, (DM_SPRITES) +    ld bc, (DM_SPRITES) 
-   LD L, 0 +    ld l, 0 
-   SRL A +    srl a 
-   RR L +    rr l 
-   RRA +    rra 
-   RR L +    rr l 
-   RRA +    rra 
-   RR L +    rr l 
-   LD HA +    ld ha 
-   ADD HLBC                ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 32) +    add hlbc                ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 32) 
-   EX DEHL                 ; Intercambiamos DE y HL (DE=origen, HL=destino)+    ex dehl                 ; Intercambiamos DE y HL (DE=origen, HL=destino)
  
-   PUSH HL                   ; Guardamos el puntero a pantalla recien calculado +    push hl                   ; Guardamos el puntero a pantalla recien calculado 
-   PUSH HL+    push hl
  
-   ;;; Impresion de los primeros 2 bloques horizontales del tile+    ;;; Impresion de los primeros 2 bloques horizontales del tile
  
-   LD B, 8+    ld b, 8
 drawm16_loop1: drawm16_loop1:
  
-   LD A, (DE)                ; Bloque 1: Leemos dato del sprite +    ld a, (de)                ; Bloque 1: Leemos dato del sprite 
-   LD (HL),                ; Copiamos dato a pantalla +    ld (hl),                ; Copiamos dato a pantalla 
-   INC DE                    ; Incrementar puntero en sprite +    inc de                    ; Incrementar puntero en sprite 
-   INC L                     ; Incrementar puntero en pantalla +    inc l                     ; Incrementar puntero en pantalla 
-   LD A, (DE)                ; Bloque 2: Leemos dato del sprite +    ld a, (de)                ; Bloque 2: Leemos dato del sprite 
-   LD (HL),                ; Copiamos dato a pantalla +    ld (hl),                ; Copiamos dato a pantalla 
-   INC DE                    ; Incrementar puntero en sprite +    inc de                    ; Incrementar puntero en sprite 
-   INC H                     ; Hay que sumar 256 para ir al siguiente scanline +    inc h                     ; Hay que sumar 256 para ir al siguiente scanline 
-   DEC L                     ; pero hay que restar el INC L que hicimos. +    dec l                     ; pero hay que restar el inc l que hicimos. 
-   DJNZ drawm16_loop1 +    djnz drawm16_loop1 
-   INC L                     ; Decrementar el ultimo incrementado en el bucle+    inc l                     ; Decrementar el ultimo incrementado en el bucle
  
-   ; Avanzamos HL 1 scanline (codigo de incremento de HL en 1 scanline) +    ; Avanzamos HL 1 scanline (codigo de incremento de HL en 1 scanline) 
-   ; desde el septimo scanline de la fila Y+1 al primero de la Y+2 +    ; desde el septimo scanline de la fila Y+1 al primero de la Y+2 
-   LD AL +    ld al 
-   ADD A, 31 +    add a, 31 
-   LD LA +    ld la 
-   JR C, drawm16_nofix_abajop +    jr c, drawm16_nofix_abajop 
-   LD AH +    ld ah 
-   SUB +    sub 
-   LD HA+    ld ha
 drawm16_nofix_abajop: drawm16_nofix_abajop:
  
-   ;;; Impresion de los segundos 2 bloques horizontales: +    ;;; Impresion de los segundos 2 bloques horizontales: 
-   LD B, 8+    ld b, 8
 drawm16_loop2: drawm16_loop2:
-   LD A, (DE)                ; Bloque 1: Leemos dato del sprite +    ld a, (de)                ; Bloque 1: Leemos dato del sprite 
-   LD (HL),                ; Copiamos dato a pantalla +    ld (hl),                ; Copiamos dato a pantalla 
-   INC DE                    ; Incrementar puntero en sprite +    inc de                    ; Incrementar puntero en sprite 
-   INC L                     ; Incrementar puntero en pantalla +    inc l                     ; Incrementar puntero en pantalla 
-   LD A, (DE)                ; Bloque 2: Leemos dato del sprite +    ld a, (de)                ; Bloque 2: Leemos dato del sprite 
-   LD (HL),                ; Copiamos dato a pantalla +    ld (hl),                ; Copiamos dato a pantalla 
-   INC DE                    ; Incrementar puntero en sprite +    inc de                    ; Incrementar puntero en sprite 
-   INC H                     ; Hay que sumar 256 para ir al siguiente scanline +    inc h                     ; Hay que sumar 256 para ir al siguiente scanline 
-   DEC L                     ; pero hay que restar el INC L que hicimos. +    dec l                     ; pero hay que restar el inc l que hicimos. 
-   DJNZ drawm16_loop2+    djnz drawm16_loop2
  
  
-   ;;; En este punto, los 16 scanlines del tile estan dibujados.+    ;;; En este punto, los 16 scanlines del tile estan dibujados.
  
-   ;;;;;; Impresion de la parte de atributos del tile ;;;;;;+    ;;;;;; Impresion de la parte de atributos del tile ;;;;;;
  
-   POP HL                    ; Recuperar puntero a inicio de tile+    pop hl                    ; Recuperar puntero a inicio de tile
  
-   ;;; Calcular posicion destino en area de atributos en DE. +    ;;; Calcular posicion destino en area de atributos en DE. 
-   LD A                  ; Codigo de Get_Attr_Offset_From_Image +    ld a                  ; Codigo de Get_Attr_Offset_From_Image 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   AND +    and 
-   OR $58 +    or $58 
-   LD DA +    ld da 
-   LD E                  ; DE tiene el offset del attr de HL+    ld e                  ; DE tiene el offset del attr de HL
  
-   LD HL, (DM_ATTRIBS) +    ld hl, (DM_ATTRIBS) 
-   EX AFAF               ; Recuperamos el bloque del mapa desde A' +    ex afaf               ; Recuperamos el bloque del mapa desde A' 
-   LD CA +    ld ca 
-   LD B, 0 +    ld b, 0 
-   ADD HLBC +    add hlbc 
-   ADD HLBC +    add hlbc 
-   ADD HLBC +    add hlbc 
-   ADD HLBC                ; HL = HL+HL=(DS_NUMSPR*4) = Origen de atributo+    add hlbc                ; HL = HL+HL=(DS_NUMSPR*4) = Origen de atributo
  
-   LDI +    ldi 
-   LDI                       ; Imprimimos la primeras fila de atributos+    ldi                       ; Imprimimos la primeras fila de atributos
  
-   ;;; Avance diferencial a la siguiente linea de atributos +    ;;; Avance diferencial a la siguiente linea de atributos 
-   LD A                  ; A = E +    ld a                  ; A = E 
-   ADD A, 30                 ; Sumamos A = A + 30 mas los 2 INCs de LDI+    add a, 30                 ; Sumamos A = A + 30 mas los 2 INCs de ldi
-   LD E                  ; Guardamos en E (E = E+30 + 2 por LDI=E+32) +    ld e                  ; Guardamos en E (E = E+30 + 2 por ldi=E+32) 
-   JR NC, drawm16_att_noinc +    jr nc, drawm16_att_noinc 
-   INC D+    inc d
 drawm16_att_noinc: drawm16_att_noinc:
-   LDI +    ldi 
-   LDI                       ; Imprimimos la segunda fila de atributos+    ldi                       ; Imprimimos la segunda fila de atributos
  
-   POP HL                    ; Recuperamos el puntero al inicio+    pop hl                    ; Recuperamos el puntero al inicio
  
 drawm16_next: drawm16_next:
-   INC L                     ; Avanzamos al siguiente tile en pantalla +    inc l                     ; Avanzamos al siguiente tile en pantalla 
-   INC L                     ; horizontalmente+    inc l                     ; horizontalmente
  
-   POP BC                    ; Recuperamos el contador para el bucle +    pop bc                    ; Recuperamos el contador para el bucle 
-   DEC B                     ; DJNZ se sale de rango, hay que usar DEC+JP +    dec b                     ; djnz se sale de rango, hay que usar DEC+jp 
-   JP NZ, drawm16_xloop+    jp nz, drawm16_xloop
  
-   ;;; En este punto, hemos dibujado ANCHO tiles en pantalla (1 fila) +    ;;; En este punto, hemos dibujado ANCHO tiles en pantalla (1 fila) 
-   POP BC +    pop bc 
-   DEC B                     ; Bucle vertical +    dec b                     ; Bucle vertical 
-   JP NZ, drawm16_yloop+    jp nz, drawm16_yloop
  
-   RET+    ret
 </code> </code>
  
Línea 513: Línea 513:
  Por motivos de espacio (gran extensión de la rutina), la impresión de los 4 bloques gráficos se realiza en dos bucles aunque la versión definitiva de nuestro programa debería desenrollarlos con los siguientes cambios:  Por motivos de espacio (gran extensión de la rutina), la impresión de los 4 bloques gráficos se realiza en dos bucles aunque la versión definitiva de nuestro programa debería desenrollarlos con los siguientes cambios:
  
-\\ +\\  
-1.- Eliminar el **INC L** tras el **DJNZ drawm16_loop1**.+1.- Eliminar el ''inc l'' tras el ''djnz drawm16_loop1''.
  
-2.- Eliminar los **INC DE****INC H** **DEC L** antes del **DJNZ drawm16_loop2**+2.- Eliminar los ''inc de''''inc h'' ''dec l'' antes del ''djnz drawm16_loop2''
-\\+\\ 
  
  Por supuesto, si se conocen de antemano otros parámetros fijos de la rutina (ancho, alto, posición inicial, etc) podemos utilizar estos valores directamente (como constantes o incluso anticipar el cálculo de la dirección de pantalla inicial o usar una tabla de direcciones iniciales de tile precalculadas) para acelerar la ejecución de la misma.  Por supuesto, si se conocen de antemano otros parámetros fijos de la rutina (ancho, alto, posición inicial, etc) podemos utilizar estos valores directamente (como constantes o incluso anticipar el cálculo de la dirección de pantalla inicial o usar una tabla de direcciones iniciales de tile precalculadas) para acelerar la ejecución de la misma.
Línea 523: Línea 523:
  No obstante, si estamos ante un juego de pantallas "fijas" y "estáticas", el tiempo de dibujado de la pantalla será prácticamente inapreciable para el jugador, por lo que no suele ser necesario realizar optimizaciones extremas.  No obstante, si estamos ante un juego de pantallas "fijas" y "estáticas", el tiempo de dibujado de la pantalla será prácticamente inapreciable para el jugador, por lo que no suele ser necesario realizar optimizaciones extremas.
  
-\\+\\ 
 ==== Ejemplo: Impresión de una pantalla 16x16 ==== ==== Ejemplo: Impresión de una pantalla 16x16 ====
  
Línea 530: Línea 530:
 <code z80> <code z80>
 ; Ejemplo impresion mapa de 16x16 ; Ejemplo impresion mapa de 16x16
-ORG 35000+    ORG 35000
  
-  CALL ClearScreen_Pattern        ; Imprimimos patron de fondo+    call ClearScreen_Pattern        ; Imprimimos patron de fondo
  
-  LD HL, sokoban1_gfx +    ld hl, sokoban1_gfx 
-  LD (DM_SPRITES), HL +    ld (DM_SPRITES), hl 
-  LD HL, sokoban1_attr +    ld hl, sokoban1_attr 
-  LD (DM_ATTRIBS), HL +    ld (DM_ATTRIBS), hl 
-  LD HL, sokoban_LEVEL1 +    ld hl, sokoban_LEVEL1 
-  LD (DM_MAP), HL +    ld (DM_MAP), hl 
-  LD A, 16 +    ld a, 16 
-  LD (DM_WIDTH),                ; ANCHO +    ld (DM_WIDTH),                ; ANCHO 
-  LD A, 12 +    ld a, 12 
-  LD (DM_HEIGHT),               ; ALTO +    ld (DM_HEIGHT),               ; ALTO 
-  XOR A +    xor a 
-  LD (DM_COORD_X),              ; X = Y = 0 +    ld (DM_COORD_X),              ; X = Y = 0 
-  LD (DM_COORD_Y),              ; Establecemos valores llamada+    ld (DM_COORD_Y),              ; Establecemos valores llamada
  
-  CALL DrawMap_16x16              ; Imprimir pantalla de mapa+    call DrawMap_16x16              ; Imprimir pantalla de mapa
  
 loop: loop:
-  JR loop +    jr loop
  
 ;----------------------------------------------------------------------- ;-----------------------------------------------------------------------
 ClearScreen_Pattern:              ; Rutina para incluir: ClearScreen_Pattern:              ; Rutina para incluir:
-   (...)                          ; Rellenado de fondo con un patron +    (...)                          ; Rellenado de fondo con un patron 
-   RET                            ; (del capitulo de Sprites Lowres) +    ret                            ; (del capitulo de Sprites Lowres)
  
 ;----------------------------------------------------------------------- ;-----------------------------------------------------------------------
Línea 568: Línea 566:
 DM_WIDTH    DEFB   0 DM_WIDTH    DEFB   0
 DM_HEIGHT   DEFB   0 DM_HEIGHT   DEFB   0
- 
  
 ;----------------------------------------------------------------------- ;-----------------------------------------------------------------------
Línea 574: Línea 571:
 ;----------------------------------------------------------------------- ;-----------------------------------------------------------------------
 sokoban_LEVEL1: sokoban_LEVEL1:
-  DEFB 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +    DEFB 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 
-  DEFB 255,255,255,255,255,255,2,3,1,4,255,255,255,255,255,255 +    DEFB 255,255,255,255,255,255,2,3,1,4,255,255,255,255,255,255 
-  DEFB 255,255,255,255,1,2,3,0,0,5,4,255,255,255,255,255 +    DEFB 255,255,255,255,1,2,3,0,0,5,4,255,255,255,255,255 
-  DEFB 255,255,255,255,4,0,6,6,0,0,5,255,255,255,255,255 +    DEFB 255,255,255,255,4,0,6,6,0,0,5,255,255,255,255,255 
-  DEFB 255,255,255,255,5,0,0,6,0,0,4,255,255,255,255,255 +    DEFB 255,255,255,255,5,0,0,6,0,0,4,255,255,255,255,255 
-  DEFB 255,255,255,255,4,0,0,0,0,0,5,255,255,255,255,255 +    DEFB 255,255,255,255,4,0,0,0,0,0,5,255,255,255,255,255 
-  DEFB 255,255,255,255,5,2,3,0,0,2,3,255,255,255,255,255 +    DEFB 255,255,255,255,5,2,3,0,0,2,3,255,255,255,255,255 
-  DEFB 255,255,255,255,255,255,1,0,0,0,4,255,255,255,255,255 +    DEFB 255,255,255,255,255,255,1,0,0,0,4,255,255,255,255,255 
-  DEFB 255,255,255,255,255,255,4,7,7,7,5,255,255,255,255,255 +    DEFB 255,255,255,255,255,255,4,7,7,7,5,255,255,255,255,255 
-  DEFB 255,255,255,255,255,255,5,2,3,2,3,255,255,255,255,255 +    DEFB 255,255,255,255,255,255,5,2,3,2,3,255,255,255,255,255 
-  DEFB 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +    DEFB 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 
-  DEFB 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +    DEFB 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
  
 ;----------------------------------------------------------------------- ;-----------------------------------------------------------------------
Línea 597: Línea 593:
  
 sokoban1_gfx: sokoban1_gfx:
-  DEFB   0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0 +    DEFB   0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0 
-  DEFB   0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0 +    DEFB   0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0 
-  DEFB 127,252,193, 86,152,  2,180,170, 173, 86,153,254,194,170,255,254 +    DEFB 127,252,193, 86,152,  2,180,170, 173, 86,153,254,194,170,255,254 
-  DEFB   0,  0,102,102, 51, 50,153,152, 204,204,102,102, 51, 50,  0,  0 +    DEFB   0,  0,102,102, 51, 50,153,152, 204,204,102,102, 51, 50,  0,  0 
-  DEFB 127,102,205, 76,151, 24,205, 50, 151,102,205, 76,151, 24,205, 50 +    DEFB 127,102,205, 76,151, 24,205, 50, 151,102,205, 76,151, 24,205, 50 
-  DEFB 131,102,153, 76,173, 24,181, 50, 153,102,195, 76,127, 24,  0,  0 +    DEFB 131,102,153, 76,173, 24,181, 50, 153,102,195, 76,127, 24,  0,  0 
-  DEFB 255,252,255,134,255, 50,255, 90, 255,106,255, 50,255,134,255,254 +    DEFB 255,252,255,134,255, 50,255, 90, 255,106,255, 50,255,134,255,254 
-  DEFB 255,254,255,254,255,254,255,250, 255,242,253,166,255,252,  0,  0 +    DEFB 255,254,255,254,255,254,255,250, 255,242,253,166,255,252,  0,  0 
-  DEFB 127,252,205,134,151, 50,205,106, 151, 90,205, 50,151,134,205,254 +    DEFB 127,252,205,134,151, 50,205,106, 151, 90,205, 50,151,134,205,254 
-  DEFB 195,254,153,254,173,254,181,250, 153,242,195,166,127,252,  0,  0 +    DEFB 195,254,153,254,173,254,181,250, 153,242,195,166,127,252,  0,  0 
-  DEFB 255,254,255,254,255,254,255,254, 255,254,255,254,191,254,255,254 +    DEFB 255,254,255,254,255,254,255,254, 255,254,255,254,191,254,255,254 
-  DEFB 255,134,191, 50,255,106,191, 90, 159, 50,207,134,127,252,  0,  0 +    DEFB 255,134,191, 50,255,106,191, 90, 159, 50,207,134,127,252,  0,  0 
-  DEFB   0,  0,127,254, 95,250, 96,  6, 111,182,111,118, 96,230,109,214 +    DEFB   0,  0,127,254, 95,250, 96,  6, 111,182,111,118, 96,230,109,214 
-  DEFB 107,182,103,  6,110,246,109,246,  96,  6, 95,250,127,254,  0,  0 +    DEFB 107,182,103,  6,110,246,109,246,  96,  6, 95,250,127,254,  0,  0 
-  DEFB   0,  0,123,222,123,222, 96,  6,  96,  6,  0,  0, 96,  6, 96,  6 +    DEFB   0,  0,123,222,123,222, 96,  6,  96,  6,  0,  0, 96,  6, 96,  6 
-  DEFB  96,  6, 96,  6,  0,  0, 96,  6,  96,  6,123,222,123,222,  0,  0+    DEFB  96,  6, 96,  6,  0,  0, 96,  6,  96,  6,123,222,123,222,  0,  0
  
 sokoban1_attr: sokoban1_attr:
-  DEFB   0,  0,  0,  0,  5,  5, 70, 70, 5, 70,  5, 70, 69, 71, 69, 71 +    DEFB   0,  0,  0,  0,  5,  5, 70, 70, 5, 70,  5, 70, 69, 71, 69, 71 
-  DEFB   5, 69, 69, 71, 69, 69, 71, 71, 2, 66, 66, 67,  6, 70, 70, 71+    DEFB   5, 69, 69, 71, 69, 69, 71, 71, 2, 66, 66, 67,  6, 70, 70, 71
  
-END 35000+    END 35000
 </code> </code>
  
  El resultado de la ejecución del anterior ejemplo es la siguiente pantalla:  El resultado de la ejecución del anterior ejemplo es la siguiente pantalla:
  
-\\+\\ 
 {{ :cursos:ensamblador:gfx5_sokoban1.png?640 | Programa de ejemplo impresion pantalla 16x16}} {{ :cursos:ensamblador:gfx5_sokoban1.png?640 | Programa de ejemplo impresion pantalla 16x16}}
-\\+\\ 
  
  Nótese que las pantallas de mapa no tienen por qué tener el tamaño exacto de 256x192 de la pantalla de TV. Nuestra pantalla anterior ocupa más memoria de la estrictamente necesaria, ya que gran parte de la información alrededor del "área de juego real" son bloques transparentes.  Nótese que las pantallas de mapa no tienen por qué tener el tamaño exacto de 256x192 de la pantalla de TV. Nuestra pantalla anterior ocupa más memoria de la estrictamente necesaria, ya que gran parte de la información alrededor del "área de juego real" son bloques transparentes.
Línea 636: Línea 632:
 ;----------------------------------------------------------------------- ;-----------------------------------------------------------------------
 sokoban_LEVEL1: sokoban_LEVEL1:
-  DEFB 255,255,  2,  3,  4,  1,255 +    DEFB 255,255,  2,  3,  4,  1,255 
-  DEFB   4,  2,  3,  0,  0,  5,  1 +    DEFB   4,  2,  3,  0,  0,  5,  1 
-  DEFB   1,  0,  6,  6,  0,  0,  5 +    DEFB   1,  0,  6,  6,  0,  0,  5 
-  DEFB   5,  0,  0,  6,  0,  0,  1 +    DEFB   5,  0,  0,  6,  0,  0,  1 
-  DEFB   1,  0,  0,  0,  0,  0,  5 +    DEFB   1,  0,  0,  0,  0,  0,  5 
-  DEFB   5,  2,  3,  0,  0,  2,  3 +    DEFB   5,  2,  3,  0,  0,  2,  3 
-  DEFB 255,255,  4,  0,  0,  0,  1 +    DEFB 255,255,  4,  0,  0,  0,  1 
-  DEFB 255,255,  1,  7,  7,  7,  5 +    DEFB 255,255,  1,  7,  7,  7,  5 
-  DEFB 255,255,  5,  2,  3,  2,  3+    DEFB 255,255,  5,  2,  3,  2,  3
 </code> </code>
  
Línea 650: Línea 646:
  
 <code z80> <code z80>
-  LD HL, sokoban1_gfx +    ld hl, sokoban1_gfx 
-  LD (DM_SPRITES), HL +    ld (DM_SPRITES), hl 
-  LD HL, sokoban1_attr +    ld hl, sokoban1_attr 
-  LD (DM_ATTRIBS), HL +    ld (DM_ATTRIBS), hl 
-  LD HL, sokoban_LEVEL1 +    ld hl, sokoban_LEVEL1 
-  LD (DM_MAP), HL +    ld (DM_MAP), hl 
-  LD A, 7 +    ld a, 7 
-  LD (DM_WIDTH),                ; ANCHO +    ld (DM_WIDTH),                ; ANCHO 
-  LD A, 9 +    ld a, 9 
-  LD (DM_HEIGHT),               ; ALTO +    ld (DM_HEIGHT),               ; ALTO 
-  LD A, 8 +    ld a, 8 
-  LD (DM_COORD_X),              ; X_INICIAL +    ld (DM_COORD_X),              ; X_INICIaL 
-  LD A, 3 +    ld a, 3 
-  LD (DM_COORD_Y),              ; Y_INICIAL +    ld (DM_COORD_Y),              ; Y_INICIaL 
-  CALL DrawMap_16x16              ; Imprimir pantalla de mapa+    call DrawMap_16x16              ; Imprimir pantalla de mapa
 </code> </code>
  
- Si tenemos pantallas de diferentes tamaños podemos almacenarlas en memoria utilizando una estructura de datos con 4 bytes de información por pantalla: ancho, alto, x_inicial e y_inicial. De esta forma cada pantalla ocuparía en memoria sólo el espacio necesario para definirla, sin necesidad de que todas las pantallas se adapten a un tamaño común. Nuestra rutina de cambio de pantalla recogería estos 4 bytes de datos de la tabla de "tamaños y posiciones" para establecer los parámetros de entrada a DrawMap_16x16 y dibujar la pantalla en su posición correcta.+ Si tenemos pantallas de diferentes tamaños podemos almacenarlas en memoria utilizando una estructura de datos con 4 bytes de información por pantalla: ''ancho''''alto''''x_inicial'' ''y_inicial''. De esta forma cada pantalla ocuparía en memoria sólo el espacio necesario para definirla, sin necesidad de que todas las pantallas se adapten a un tamaño común. Nuestra rutina de cambio de pantalla recogería estos 4 bytes de datos de la tabla de "tamaños y posiciones" para establecer los parámetros de entrada a ''DrawMap_16x16'' y dibujar la pantalla en su posición correcta.
  
  Esto puede valer para juegos como Sokoban (donde cada pantalla puede tener un tamaño diferente) pero no para juegos tipo plataformas/aventuras/arcade donde todas las pantallas tienen un tamaño fijo. En ese caso basta con usar nuestra rutina con unos valores fijos para todas ellas.  Esto puede valer para juegos como Sokoban (donde cada pantalla puede tener un tamaño diferente) pero no para juegos tipo plataformas/aventuras/arcade donde todas las pantallas tienen un tamaño fijo. En ese caso basta con usar nuestra rutina con unos valores fijos para todas ellas.
  
-\\+\\ 
 ==== Rutina para bloques de 8x8 con mapeados horizontales ==== ==== Rutina para bloques de 8x8 con mapeados horizontales ====
  
Línea 683: Línea 679:
 DrawMap_8x8: DrawMap_8x8:
  
-   ;;;;;; Impresion de la parte grafica de los tiles ;;;;;; +    ;;;;;; Impresion de la parte grafica de los tiles ;;;;;; 
-   LD IX, (DM_MAP)           ; IX apunta al mapa +    ld ix, (DM_MAP)           ; IX apunta al mapa 
-   LD A, (DM_HEIGHT) +    ld a, (DM_HEIGHT) 
-   LD B                  ; B = ALTO_EN_TILES (para bucle altura)+    ld b                  ; B = ALTO_EN_TILES (para bucle altura)
  
 drawm8_yloop: drawm8_yloop:
-   PUSH BC                   ; Guardamos el valor de B+    push bc                   ; Guardamos el valor de B
  
-   LD A, (DM_HEIGHT)         ; A = ALTO_EN_TILES +    ld a, (DM_HEIGHT)         ; A = ALTO_EN_TILES 
-   SUB B                     ; A = ALTO - iteracion_bucle = Y actual +    sub b                     ; A = ALTO - iteracion_bucle = Y actual 
-                             ;;; NUEVO: Eliminamos RLCA (no multiplicar Y*2)+                              ;;; NUEVO: Eliminamos rlca (no multiplicar Y*2)
  
-   ;;; Calculamos la direccion destino en pantalla como +    ;;; Calculamos la direccion destino en pantalla como 
-   ;;; DIR_PANT = DIRECCION(X_INICIAL, Y_INICIAL + Y) +    ;;; DIR_PANT = DIRECCION(X_INICIAL, Y_INICIAL + Y) 
-   LD BC, (DM_COORD_X)       ; B = DB_COORD_Y y C = DB_COORD_X +    ld bc, (DM_COORD_X)       ; B = DB_COORD_Y y C = DB_COORD_X 
-   ADD AB +    add ab 
-   LD BA +    ld ba 
-   LD AB +    ld ab 
-   AND $18 +    and $18 
-   ADD A, $40 +    add a, $40 
-   LD HA +    ld ha 
-   LD AB +    ld ab 
-   AND +    and 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   ADD AC +    add ac 
-   LD L                  ; HL = DIR_PANTALLA(X_INICIAL,Y_INICIAL+Y*2)+    ld l                  ; HL = DIR_PANTALLA(X_INICIAL,Y_INICIAL+Y*2)
  
-   LD A, (DM_WIDTH) +    ld a, (DM_WIDTH) 
-   LD B                  ; B = ANCHO_EN_TILES+    ld b                  ; B = ANCHO_EN_TILES
  
 drawm8_xloop: drawm8_xloop:
-   PUSH BC                   ; Nos guardamos el contador del bucle+    push bc                   ; Nos guardamos el contador del bucle
  
-   LD A, (IX+0)              ; Leemos un byte del mapa +    ld a, (ix+0)              ; Leemos un byte del mapa 
-   INC IX                    ; Apuntamos al siguiente byte del mapa+    inc ix                    ; Apuntamos al siguiente byte del mapa
  
-   CP 255                    ; Bloque especial a saltar: no se dibuja +    cp 255                    ; Bloque especial a saltar: no se dibuja 
-   JP Z, drawm8_next+    jp z, drawm8_next
  
-   LD BA +    ld ba 
-   EX AFAF               ; Nos guardamos una copia del bloque en A' +    ex afaf               ; Nos guardamos una copia del bloque en A' 
-   LD AB+    ld ab
  
-   ;;; Calcular posicion origen (array sprites) en HL como: +    ;;; Calcular posicion origen (array sprites) en HL como: 
-   ;;;     direccion = base_sprites + (NUM_SPRITE*8) +    ;;;     direccion = base_sprites + (NUM_SPRITE*8) 
-   EX DEHL                 ; Intercambiamos DE y HL (DE=destino) +    ex dehl                 ; Intercambiamos DE y HL (DE=destino) 
-   LD BC, (DM_SPRITES) +    ld bc, (DM_SPRITES) 
-   LD LA +    ld la 
-   LD H, 0 +    ld h, 0 
-   ADD HLHL +    add hlhl 
-   ADD HLHL +    add hlhl 
-   ADD HLHL                ;;; NUEVO: NUM_SPRITE*8 en lugar de *32 +    add hlhl                ;;; NUEVO: NUM_SPRITE*8 en lugar de *32 
-   ADD HLBC                ; HL = BC + HL = DM_SPRITES + (DM_NUMSPR * 8) +    add hlbc                ; HL = BC + HL = DM_SPRITES + (DM_NUMSPR * 8) 
-   EX DEHL                 ; Intercambiamos DE y HL (DE=origen, HL=destino)+    ex dehl                 ; Intercambiamos DE y HL (DE=origen, HL=destino)
  
-   PUSH HL                   ; Guardamos el puntero a pantalla recien calculado +    push hl                   ; Guardamos el puntero a pantalla recien calculado 
-   PUSH HL+    push hl
  
-   ;;; Impresion de los primeros 2 bloques horizontales del tile +    ;;; Impresion de los primeros 2 bloques horizontales del tile 
-   LD B, 8+    ld b, 8
  
-drawm8_loop:                 ;;; NUEVO: Bucle de impresion de 1 solo bloque +drawm8_loop:                  ;;; NUEVO: Bucle de impresion de 1 solo bloque 
-   LD A, (DE)                ; Bloque 1: Leemos dato del sprite +    ld a, (de)                ; Bloque 1: Leemos dato del sprite 
-   LD (HL),                ; Copiamos dato a pantalla +    ld (hl),                ; Copiamos dato a pantalla 
-   INC DE                    ; Incrementar puntero en sprite +    inc de                    ; Incrementar puntero en sprite 
-   INC H                     ; Hay que sumar 256 para ir al siguiente scanline +    inc h                     ; Hay que sumar 256 para ir al siguiente scanline 
-   DJNZ drawm8_loop+    djnz drawm8_loop
  
-   ;;; En este punto, los 8 scanlines del tile estan dibujados.+    ;;; En este punto, los 8 scanlines del tile estan dibujados.
  
-   ;;;;;; Impresion de la parte de atributos del tile ;;;;;; +    ;;;;;; Impresion de la parte de atributos del tile ;;;;;; 
-   POP HL                    ; Recuperar puntero a inicio de tile+    pop hl                    ; Recuperar puntero a inicio de tile
  
-   ;;; Calcular posicion destino en area de atributos en DE. +    ;;; Calcular posicion destino en area de atributos en DE. 
-   LD A                  ; Codigo de Get_Attr_Offset_From_Image +    ld a                  ; Codigo de Get_Attr_Offset_From_Image 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   AND +    and 
-   OR $58 +    or $58 
-   LD DA +    ld da 
-   LD E                  ; DE tiene el offset del attr de HL+    ld e                  ; DE tiene el offset del attr de HL
  
-   LD HL, (DM_ATTRIBS) +    ld hl, (DM_ATTRIBS) 
-   EX AFAF               ; Recuperamos el bloque del mapa desde A' +    ex afaf               ; Recuperamos el bloque del mapa desde A' 
-   LD CA +    ld ca 
-   LD B, 0                   ;;; NUEVO: HL = HL+DM_NUMSPR (NO *4) +    ld b, 0                   ;;; NUEVO: HL = HL+DM_NUMSPR (NO *4) 
-   ADD HLBC                ; HL = HL+DM_NUMSPR = Origen de atributo+    add hlbc                ; HL = HL+DM_NUMSPR = Origen de atributo
  
-   LD A, (HL+    ld a, (hl
-   LD (DE),                ;;; NUEVO: Impresion de un unico atributo. +    ld (de),                ;;; NUEVO: Impresion de un unico atributo. 
-   POP HL                    ; Recuperamos el puntero al inicio+    pop hl                    ; Recuperamos el puntero al inicio
  
 drawm8_next: drawm8_next:
-   INC L                     ; Avanzamos al siguiente tile en pantalla+    inc l                     ; Avanzamos al siguiente tile en pantalla
  
-   POP BC                    ; Recuperamos el contador para el bucle +    pop bc                    ; Recuperamos el contador para el bucle 
-   DEC B                     ; DJNZ se sale de rango, hay que usar DEC+JP +    dec b                     ; djnz se sale de rango, hay que usar DEC+jp 
-   JP NZ, drawm8_xloop+    jp nz, drawm8_xloop
  
-   ;;; En este punto, hemos dibujado ANCHO tiles en pantalla (1 fila) +    ;;; En este punto, hemos dibujado ANCHO tiles en pantalla (1 fila) 
-   POP BC +    pop bc 
-   DEC B                     ; Bucle vertical +    dec b                     ; Bucle vertical 
-   JP NZ, drawm8_yloop+    jp nz, drawm8_yloop
  
-   RET+    ret
 </code> </code>
  
-\\+\\ 
 ===== El mapa como conjunto de pantallas ===== ===== El mapa como conjunto de pantallas =====
  
  Ya conocemos la forma de diseñar e imprimir pantallas individuales de mapeado, pero seguimos necesitando agrupar estas pantallas para componer un mapa de tamaño superior al del área de visión del jugador. En esta sección veremos las estructuras de datos necesarias para definir el mapeado total del juego así como la interconexión entre las diferentes pantallas.  Ya conocemos la forma de diseñar e imprimir pantallas individuales de mapeado, pero seguimos necesitando agrupar estas pantallas para componer un mapa de tamaño superior al del área de visión del jugador. En esta sección veremos las estructuras de datos necesarias para definir el mapeado total del juego así como la interconexión entre las diferentes pantallas.
  
-\\+\\ 
 ==== Estructura de datos del mapa ==== ==== Estructura de datos del mapa ====
  
Línea 805: Línea 801:
  Las pantallas se interconectan formando un mapa. Esta conexión puede ser:  Las pantallas se interconectan formando un mapa. Esta conexión puede ser:
  
-\\+\\ 
   * Estática: El jugador cambia de una pantalla a otra al acabar el nivel, sin que las pantallas estén "conectadas" realmente entre sí salvo por el número de nivel actual (Ejemplo: Manic Miner, Sokoban, Bubble Bobble, Puzznic, Plotting...).   * Estática: El jugador cambia de una pantalla a otra al acabar el nivel, sin que las pantallas estén "conectadas" realmente entre sí salvo por el número de nivel actual (Ejemplo: Manic Miner, Sokoban, Bubble Bobble, Puzznic, Plotting...).
   * Lineal en un sólo eje: Las pantallas se agrupan una a continuación de la otra con desarrollo del juego en una dirección, ya sea izquierda-derecha (R-Type, Game Over, Target Renegade...) o arriba-abajo (Flying Shark, Commando...).   * Lineal en un sólo eje: Las pantallas se agrupan una a continuación de la otra con desarrollo del juego en una dirección, ya sea izquierda-derecha (R-Type, Game Over, Target Renegade...) o arriba-abajo (Flying Shark, Commando...).
   * Lineal en dos ejes: Las pantallas se agrupan en forma de mapa bidimensional, permitiendo al jugador moverse en el mapeado hacia arriba, abajo, izquierda o derecha (Sabre Wulf, Las Tres Luces de Glaurung, Atic Atac, Sir Fred...).   * Lineal en dos ejes: Las pantallas se agrupan en forma de mapa bidimensional, permitiendo al jugador moverse en el mapeado hacia arriba, abajo, izquierda o derecha (Sabre Wulf, Las Tres Luces de Glaurung, Atic Atac, Sir Fred...).
-\\+\\ 
  
  La //pantalla// es una //ventana// dentro del mapeado, por lo que resulta necesario disponer de una estructura de datos que nos permita representar los 3 modelos de mapa que acabamos de describir.  La //pantalla// es una //ventana// dentro del mapeado, por lo que resulta necesario disponer de una estructura de datos que nos permita representar los 3 modelos de mapa que acabamos de describir.
Línea 815: Línea 811:
  Hay 2 posibilidades de agrupación de las pantallas: como un **array de pantallas**, o como una **matriz global de mapeado**.  Hay 2 posibilidades de agrupación de las pantallas: como un **array de pantallas**, o como una **matriz global de mapeado**.
  
-\\+\\ 
 ==== Mapa como array de pantallas ==== ==== Mapa como array de pantallas ====
  
Línea 824: Línea 820:
 <code z80> <code z80>
 Pantalla_Inicio: Pantalla_Inicio:
-  DB 0, 0, 0, 3, (...)+    DB 0, 0, 0, 3, (...)
  
 Pantalla_Salon: Pantalla_Salon:
-  DB 1, 2, 3, 4, (...)+    DB 1, 2, 3, 4, (...)
  
 Pantalla_Pasillo: Pantalla_Pasillo:
-  DB 2, 2, 2, 2, (...)+    DB 2, 2, 2, 2, (...)
  
 Pantalla_Escalera: Pantalla_Escalera:
-  DB 4, 4, 5, 1, (...)+    DB 4, 4, 5, 1, (...)
  
 (...) (...)
Línea 842: Línea 838:
 <code z80> <code z80>
 Mapa: Mapa:
-  DW Pantalla_Inicio         ; Pantalla 0 +    DW Pantalla_Inicio         ; Pantalla 0 
-  DW Pantalla_Salon          ; Pantalla 1 +    DW Pantalla_Salon          ; Pantalla 1 
-  DW Pantalla_Pasillo        ; Pantalla 2 +    DW Pantalla_Pasillo        ; Pantalla 2 
-  DW Pantalla_Escalera       ; Pantalla 3 +    DW Pantalla_Escalera       ; Pantalla 3 
-  (...)                      ; (etc...) +    (...)                      ; (etc...) 
-  DW 0000                    ; Fin de pantalla+    DW 0000                    ; Fin de pantalla
 </code> </code>
  
Línea 865: Línea 861:
  
 <code z80> <code z80>
-  ;;; Calculamos la posicion de "pantalla_actual" en al tabla +    ;;; Calculamos la posicion de "pantalla_actual" en al tabla 
-  LD BC, Mapa                     ; BC = Inicio de la tabla Mapa +    ld bc, Mapa                     ; BC = Inicio de la tabla Mapa 
-  LD A, (pantalla_actual)         ; A = Pantalla actual +    ld a, (pantalla_actual)         ; A = Pantalla actual 
-  LD LA +    ld la 
-  LD H, 0                         ; HL = Pantalla actual +    ld h, 0                         ; HL = Pantalla actual 
-  SLA L +    sla l 
-  RL  H                           ; HL = Pantalla actual * 2 +    rl                            ; HL = Pantalla actual * 2 
-  ADD HLBC                      ; HL = Mapa + (Pantalla actual * 2)+    add hlbc                      ; HL = Mapa + (Pantalla actual * 2)
  
-  ;;; Ahora leemos de (HL) la dirección de dibujado en el mismo HL +    ;;; Ahora leemos de (HL) la dirección de dibujado en el mismo HL 
-  LD A, (HL)                      ; Leemos la parte baja de la direccion en A +    ld a, (hl)                      ; Leemos la parte baja de la direccion en A 
-  INC HL                          ; ... para no corromper HL y poder leer ... +    inc hl                          ; ... para no corromper HL y poder leer ... 
-  PUSH HL +    push hl 
-  LD H, (HL)                      ; ... la parte alta sobre H ... +    ld h, (hl)                      ; ... la parte alta sobre H ... 
-  LD LA +    ld la 
-  LD (DM_MAP), HL                 ; Almacenamos el mapa a imprimir +    ld (DM_MAP), hl                 ; Almacenamos el mapa a imprimir 
-  CALL DrawMap_16x16              ; Imprimimos el mapa+    call DrawMap_16x16              ; Imprimimos el mapa
 </code> </code>
  
Línea 905: Línea 901:
  
 Mapa: Mapa:
-  DW Pantalla_Inicio            ; ID = 0 +    DW Pantalla_Inicio            ; ID = 0 
-  DB -1, 1                      ; Conexiones izq y derecha ID 0 +    DB -1, 1                      ; Conexiones izq y derecha ID 0 
-  DW Pantalla_Salon             ; ID = 1 +    DW Pantalla_Salon             ; ID = 1 
-  DB 0, 2                       ; Conexiones izq y derecha ID 1 +    DB 0, 2                       ; Conexiones izq y derecha ID 1 
-  DW Pantalla_Pasillo           ; ID = 2 +    DW Pantalla_Pasillo           ; ID = 2 
-  DB 1, 3                       ; Conexiones izq y derecha ID 2 +    DB 1, 3                       ; Conexiones izq y derecha ID 2 
-  DW Pantalla_Escalera          ; ID = 3 +    DW Pantalla_Escalera          ; ID = 3 
-  DB 3, -1                      ; Conexiones izq y derecha ID 3 +    DB 3, -1                      ; Conexiones izq y derecha ID 3 
-  (...)+    (...)
 </code> </code>
  
Línea 928: Línea 924:
 <code z80> <code z80>
 BYTES_POR_PANTALLA     EQU   4 BYTES_POR_PANTALLA     EQU   4
-RUTINA_ROM_HL_POR_DE   EQU   $30A9+RUTINA_ROM_HL_POR_DE   EQU   $30a9
  
 ;------------------------------------------------------------ ;------------------------------------------------------------
Línea 939: Línea 935:
 ;------------------------------------------------------------ ;------------------------------------------------------------
 Get_Screen_Pointer: Get_Screen_Pointer:
-  LD H, 0 +    ld h, 0 
-  LD D                        ; HL = PANTALLA +    ld d                        ; HL = PANTALLA 
-  LD E, BYTES_POR_PANTALLA        ; DE = BYTES POR PANTALLA +    ld e, BYTES_POR_PANTALLA        ; DE = BYTES POR PANTALLA 
-  CALL RUTINA_ROM_HL_POR_DE       ; HL = HL * DE +    call RUTINA_ROM_HL_POR_DE       ; HL = HL * DE 
-  ADD HLBC                      ; Lo sumamos al inicio del MAPA +    add hlbc                      ; Lo sumamos al inicio del MAPA 
-  RET                             ; HL = MAPA + (PANTALLA*BYTES)+    ret                             ; HL = MAPA + (PANTALLA*BYTES)
 </code> </code>
  
  Para realizar la multiplicación hemos utilizado la rutina HL=HL*DE de la ROM del Spectrum. Si no estamos en un Spectrum sino que estamos programando para otro sistema Z80, bastará con llamar a la rutina de multiplicación adecuada.  Para realizar la multiplicación hemos utilizado la rutina HL=HL*DE de la ROM del Spectrum. Si no estamos en un Spectrum sino que estamos programando para otro sistema Z80, bastará con llamar a la rutina de multiplicación adecuada.
  
- Podríamos haber realizado la multiplicación por 4 mediante desplazamientos, pero utilizando una rutina de multiplicación nos aseguramos que Get_Screen_Pointer pueda ser utilizado para mapas que definan más conexiones.+ Podríamos haber realizado la multiplicación por 4 mediante desplazamientos, pero utilizando una rutina de multiplicación nos aseguramos que ''Get_Screen_Pointer'' pueda ser utilizado para mapas que definan más conexiones.
  
  Ahora ya podemos acceder a los datos de una pantalla concreta:  Ahora ya podemos acceder a los datos de una pantalla concreta:
  
 <code z80> <code z80>
-  ;;; En el inicio del programa... +    ;;; En el inicio del programa... 
-  LD HL, sokoban1_gfx +    ld hl, sokoban1_gfx 
-  LD (DM_SPRITES), HL +    ld (DM_SPRITES), hl 
-  LD HL, sokoban1_attr +    ld hl, sokoban1_attr 
-  LD (DM_ATTRIBS), HL +    ld (DM_ATTRIBS), hl 
-  LD A, 16 +    ld a, 16 
-  LD (DM_WIDTH),                ; ANCHO +    ld (DM_WIDTH),                ; ANCHO 
-  LD A, 12 +    ld a, 12 
-  LD (DM_HEIGHT),               ; ALTO +    ld (DM_HEIGHT),               ; ALTO 
-  XOR A +    xor a 
-  LD (DM_COORD_X),              ; X = Y = 0 +    ld (DM_COORD_X),              ; X = Y = 0 
-  LD (DM_COORD_Y),              ; Establecemos valores llamada+    ld (DM_COORD_Y),              ; Establecemos valores llamada
  
-  (...)+    (...)
  
-  ;;; En el bucle principal de nuestro programa:+    ;;; En el bucle principal de nuestro programa:
 DibujarPantalla: DibujarPantalla:
-  LD BC, Mapa +    ld bc, Mapa 
-  LD A, (pantalla_actual) +    ld a, (pantalla_actual) 
-  LD LA +    ld la 
-  CALL Get_Screen_Pointer         ; HL = Datos de la pantalla +    call Get_Screen_Pointer         ; HL = Datos de la pantalla 
-  LD A, (HL)                      ; Leemos la parte baja de la direccion en A +    ld a, (hl)                      ; Leemos la parte baja de la direccion en A 
-  INC HL                          ; ... para no corromper HL y poder leer ... +    inc hl                          ; ... para no corromper HL y poder leer ... 
-  PUSH HL +    push hl 
-  LD H, (HL)                      ; ... la parte alta sobre H ... +    ld h, (hl)                      ; ... la parte alta sobre H ... 
-  LD LA +    ld la 
-  LD (DM_MAP), HL                 ; Almacenamos el mapa a imprimir +    ld (DM_MAP), hl                 ; Almacenamos el mapa a imprimir 
-  CALL DrawMap_16x16              ; Imprimimos el mapa+    call DrawMap_16x16              ; Imprimimos el mapa
  
-  POP HL                          ; Recuperamos el puntero a datos de pantalla +    pop hl                          ; Recuperamos el puntero a datos de pantalla 
-  INC HL                          ; Avanzamos hasta el primer ID de conexion +    inc hl                          ; Avanzamos hasta el primer ID de conexion 
-  LD A, (HL)                      ; Leemos conexion izquierda +    ld a, (hl)                      ; Leemos conexion izquierda 
-  LD (con_izquierda),           ; la almacenamos +    ld (con_izquierda),           ; la almacenamos 
-  INC HL                          ; Avanzamos hasta el segundo ID de conexion +    inc hl                          ; Avanzamos hasta el segundo ID de conexion 
-  LD A, (HL)                      ; Leemos conexion a derecha +    ld a, (hl)                      ; Leemos conexion a derecha 
-  LD (con_derecha),             ; la almacenamos+    ld (con_derecha),             ; la almacenamos
 </code> </code>
  
Línea 998: Línea 994:
 <code z80> <code z80>
 Mapa: Mapa:
-  DW Pantalla_Inicio            ; ID = 0 +    DW Pantalla_Inicio            ; ID = 0 
-  DW Pantalla_Salon             ; ID = 1 +    DW Pantalla_Salon             ; ID = 1 
-  DW Pantalla_Pasillo           ; ID = 2 +    DW Pantalla_Pasillo           ; ID = 2 
-  DW Pantalla_Escalera          ; ID = 3 +    DW Pantalla_Escalera          ; ID = 3 
-  (...)+    (...)
  
 Conexiones: Conexiones:
-  DB -1, 1                      ; Conexiones izq y derecha ID 0 +    DB -1, 1                      ; Conexiones izq y derecha ID 0 
-  DB 0, 2                       ; Conexiones izq y derecha ID 1 +    DB 0, 2                       ; Conexiones izq y derecha ID 1 
-  DB 1, 3                       ; Conexiones izq y derecha ID 2 +    DB 1, 3                       ; Conexiones izq y derecha ID 2 
-  DB 3, -1                      ; Conexiones izq y derecha ID 3 +    DB 3, -1                      ; Conexiones izq y derecha ID 3 
-  (...)+    (...)
  
 </code> </code>
Línea 1033: Línea 1029:
 <code z80> <code z80>
 Mapa: Mapa:
-  DW Pantalla_Inicio            ; ID = 0 +    DW Pantalla_Inicio            ; ID = 0 
-  DW -1, 1                      ; Conexiones izq y derecha ID 0+    DW -1, 1                      ; Conexiones izq y derecha ID 0
 </code> </code>
  
Línea 1044: Línea 1040:
 ;;; Vector de direcciones de pantalla. ;;; Vector de direcciones de pantalla.
 Mapa: Mapa:
-  DW Pantalla_Inicio            ; ID = 0 +    DW Pantalla_Inicio            ; ID = 0 
-  DB -1, 1                      ; Conexiones izq y derecha ID 0 +    DB -1, 1                      ; Conexiones izq y derecha ID 0 
-  DW titulo_inicio              ; Direccion de la cadena de titulo +    DW titulo_inicio              ; Direccion de la cadena de titulo 
-  DW Pantalla_Salon             ; ID = 1 +    DW Pantalla_Salon             ; ID = 1 
-  DB 0, 2                       ; Conexiones izq y derecha ID 1 +    DB 0, 2                       ; Conexiones izq y derecha ID 1 
-  DW titulo_salon               ; Direccion de la cadena de titulo +    DW titulo_salon               ; Direccion de la cadena de titulo 
-  DW Pantalla_Pasillo           ; ID = 2 +    DW Pantalla_Pasillo           ; ID = 2 
-  DB 1, 3                       ; Conexiones izq y derecha ID 2 +    DB 1, 3                       ; Conexiones izq y derecha ID 2 
-  DW titulo_pasillo +    DW titulo_pasillo 
-  DW Pantalla_Escalera          ; ID = 3 +    DW Pantalla_Escalera          ; ID = 3 
-  DW titulo_escalera +    DW titulo_escalera 
-  DB 3, -1                      ; Conexiones izq y derecha ID 3 +    DB 3, -1                      ; Conexiones izq y derecha ID 3 
-  (...)+    (...)
  
 titulo_inicio   DB "La pantalla de inicio", 0 titulo_inicio   DB "La pantalla de inicio", 0
Línea 1076: Línea 1072:
  De nuevo, como vimos antes, es posible mantener los datos las pantallas en diferentes tablas (tabla de direccion de datos, tabla de conexiones, tabla de títulos) para facilitar el acceso a los mismos vía operaciones de desplazamiento, aunque si la obtención de estos datos no es prioritaria, el poder acceder a ellos mediante una única operación y un único puntero puede acabar resultando más rápido (o simplemente, cómodo) que en múltiples tablas.  De nuevo, como vimos antes, es posible mantener los datos las pantallas en diferentes tablas (tabla de direccion de datos, tabla de conexiones, tabla de títulos) para facilitar el acceso a los mismos vía operaciones de desplazamiento, aunque si la obtención de estos datos no es prioritaria, el poder acceder a ellos mediante una única operación y un único puntero puede acabar resultando más rápido (o simplemente, cómodo) que en múltiples tablas.
  
-\\ +\\  
-\\+\\ 
 **Repetición de pantallas** **Repetición de pantallas**
-\\ +\\  
-\\+\\ 
  
  Gracias a nuestro mapa definido como "vector" de pantallas podemos repetir pantallas en nuestro mapeado sin duplicar los datos gráficos.  Gracias a nuestro mapa definido como "vector" de pantallas podemos repetir pantallas en nuestro mapeado sin duplicar los datos gráficos.
Línea 1088: Línea 1084:
 <code z80> <code z80>
 Mapa: Mapa:
-  DW Pantalla_Inicio            ; ID = 0 +    DW Pantalla_Inicio            ; ID = 0 
-  DB -1, 1                      ; Conexiones izq y derecha ID 0 +    DB -1, 1                      ; Conexiones izq y derecha ID 0 
-  DW Pantalla_Salon             ; ID = 1 +    DW Pantalla_Salon             ; ID = 1 
-  DB 0, 2                       ; Conexiones izq y derecha ID 1 +    DB 0, 2                       ; Conexiones izq y derecha ID 1 
-  DW Pantalla_Pasillo           ; ID = 2 +    DW Pantalla_Pasillo           ; ID = 2 
-  DB 1, 3                       ; Conexiones izq y derecha ID 2 +    DB 1, 3                       ; Conexiones izq y derecha ID 2 
-  DW Pantalla_Pasillo           ; ID = 3 +    DW Pantalla_Pasillo           ; ID = 3 
-  DB 2, 4                       ; Conexiones izq y derecha ID 3 +    DB 2, 4                       ; Conexiones izq y derecha ID 3 
-  DW Pantalla_Pasillo           ; ID = 4 +    DW Pantalla_Pasillo           ; ID = 4 
-  DB 3, 5                       ; Conexiones izq y derecha ID 4 +    DB 3, 5                       ; Conexiones izq y derecha ID 4 
-  DW Pantalla_Escalera          ; ID = 5 +    DW Pantalla_Escalera          ; ID = 5 
-  DB 4, -1                      ; Conexiones izq y derecha ID 5 +    DB 4, -1                      ; Conexiones izq y derecha ID 5 
-  (...)+    (...)
 </code> </code>
  
Línea 1108: Línea 1104:
  
  
-\\ +\\  
-\\+\\ 
 **Transiciones entre 2 pantallas de mapa** **Transiciones entre 2 pantallas de mapa**
-\\ +\\  
-\\+\\ 
  
  Cuando el jugador pasa de una pantalla a otra debemos realizar el borrado de la pantalla en curso y la impresión de la nueva. Para realizar esto tenemos diferentes opciones:  Cuando el jugador pasa de una pantalla a otra debemos realizar el borrado de la pantalla en curso y la impresión de la nueva. Para realizar esto tenemos diferentes opciones:
  
-\\+\\ 
   * Transición abrupta: Simplemente realizamos un borrado del área de pantalla donde se dibuja el mapa y trazamos sobre ese área los datos de la siguiente pantalla. Es el método más común (y el más rápido), utilizado en la mayoría de juegos.   * Transición abrupta: Simplemente realizamos un borrado del área de pantalla donde se dibuja el mapa y trazamos sobre ese área los datos de la siguiente pantalla. Es el método más común (y el más rápido), utilizado en la mayoría de juegos.
   * Fundido de pantalla: Realizamos un fundido de la pantalla actual a negro, dibujamos la nueva pantalla con atributos a negro, y realizamos un fundido desde negro a los nuevos atributos.   * Fundido de pantalla: Realizamos un fundido de la pantalla actual a negro, dibujamos la nueva pantalla con atributos a negro, y realizamos un fundido desde negro a los nuevos atributos.
   * Scroll de pantallas: Realizamos un scroll entre la pantalla que sale y la que entra. Consiste en realizar una "salida" de la pantalla actual en la dirección contraria de los límites cruzados por el personaje (por ejemplo: si éste sale por la derecha, scrolleamos la pantalla actual hacia la izquierda), de forma que para cada línea de la pantalla saliente aparezca por la derecha una línea del mapa entrante (la nueva pantalla actual). Este scroll, que debe de ser rápido para no ralentizar el cambio de pantallas en el juego, requiere la realización de 4 rutinas específicas que realicen scroll de una porción de la pantalla y que permitan dibujar N filas o N columnas de la nueva pantalla entrante a partir de una posición (x,y) dada.   * Scroll de pantallas: Realizamos un scroll entre la pantalla que sale y la que entra. Consiste en realizar una "salida" de la pantalla actual en la dirección contraria de los límites cruzados por el personaje (por ejemplo: si éste sale por la derecha, scrolleamos la pantalla actual hacia la izquierda), de forma que para cada línea de la pantalla saliente aparezca por la derecha una línea del mapa entrante (la nueva pantalla actual). Este scroll, que debe de ser rápido para no ralentizar el cambio de pantallas en el juego, requiere la realización de 4 rutinas específicas que realicen scroll de una porción de la pantalla y que permitan dibujar N filas o N columnas de la nueva pantalla entrante a partir de una posición (x,y) dada.
-\\+\\ 
  
  
-\\+\\ 
 ==== El mapa como array global de mapeado ==== ==== El mapa como array global de mapeado ====
  
Línea 1138: Línea 1134:
  
 Mapa Mapa
- DB A,A,A,A,A,A,A,A,B,B,B,B,B,B,B,+    DB A,A,A,A,A,A,A,A,B,B,B,B,B,B,B,
- DB A,A,A,A,A,A,A,A,B,B,B,B,B,B,B,+    DB A,A,A,A,A,A,A,A,B,B,B,B,B,B,B,
- DB A,A,A,A,A,A,A,A,B,B,B,B,B,B,B,+    DB A,A,A,A,A,A,A,A,B,B,B,B,B,B,B,
- DB A,A,A,A,A,A,A,A,B,B,B,B,B,B,B,+    DB A,A,A,A,A,A,A,A,B,B,B,B,B,B,B,
- DB A,A,A,A,A,A,A,A,B,B,B,B,B,B,B,+    DB A,A,A,A,A,A,A,A,B,B,B,B,B,B,B,
- DB A,A,A,A,A,A,A,A,B,B,B,B,B,B,B,+    DB A,A,A,A,A,A,A,A,B,B,B,B,B,B,B,
- DB A,A,A,A,A,A,A,A,B,B,B,B,B,B,B,+    DB A,A,A,A,A,A,A,A,B,B,B,B,B,B,B,
- DB A,A,A,A,A,A,A,A,B,B,B,B,B,B,B,+    DB A,A,A,A,A,A,A,A,B,B,B,B,B,B,B,
- DB C,C,C,C,C,C,C,C,D,D,D,D,D,D,D,+    DB C,C,C,C,C,C,C,C,D,D,D,D,D,D,D,
- DB C,C,C,C,C,C,C,C,D,D,D,D,D,D,D,+    DB C,C,C,C,C,C,C,C,D,D,D,D,D,D,D,
- DB C,C,C,C,C,C,C,C,D,D,D,D,D,D,D,+    DB C,C,C,C,C,C,C,C,D,D,D,D,D,D,D,
- DB C,C,C,C,C,C,C,C,D,D,D,D,D,D,D,+    DB C,C,C,C,C,C,C,C,D,D,D,D,D,D,D,
- DB C,C,C,C,C,C,C,C,D,D,D,D,D,D,D,+    DB C,C,C,C,C,C,C,C,D,D,D,D,D,D,D,
- DB C,C,C,C,C,C,C,C,D,D,D,D,D,D,D,+    DB C,C,C,C,C,C,C,C,D,D,D,D,D,D,D,
- DB C,C,C,C,C,C,C,C,D,D,D,D,D,D,D,+    DB C,C,C,C,C,C,C,C,D,D,D,D,D,D,D,
- DB C,C,C,C,C,C,C,C,D,D,D,D,D,D,D,D+    DB C,C,C,C,C,C,C,C,D,D,D,D,D,D,D,D
 </code> </code>
  
Línea 1162: Línea 1158:
  Por ejemplo, supongamos que la posición inicial del área de visión es (0,0), con lo que la primera pantalla impresa serán los 8x8 bloques "A" del ejemplo. Para avanzar hacia la derecha basta con incrementar el puntero "xmapa" con lo que la siguiente impresión de la pantalla mostrará 7 columnas "A" y una columna "B" en el extremo derecho de la pantalla.  Por ejemplo, supongamos que la posición inicial del área de visión es (0,0), con lo que la primera pantalla impresa serán los 8x8 bloques "A" del ejemplo. Para avanzar hacia la derecha basta con incrementar el puntero "xmapa" con lo que la siguiente impresión de la pantalla mostrará 7 columnas "A" y una columna "B" en el extremo derecho de la pantalla.
  
- La impresión de este tipo de pantallas requiere una rutina similar a las rutinas de impresión sin agrupación que ya hemos visto (DrawMap_16x16), pero modificada en los siguientes términos:+ La impresión de este tipo de pantallas requiere una rutina similar a las rutinas de impresión sin agrupación que ya hemos visto (''DrawMap_16x16''), pero modificada en los siguientes términos:
  
-\\ +\\  
-  * Cambio 1: Debe de calcular la posición inicial de lectura de datos para la impresión como **Mapa + (ymapa*ANCHO_MAPA) + xmapa**+  * Cambio 1: Debe de calcular la posición inicial de lectura de datos para la impresión como ''Mapa + (ymapa * ANCHO_MAPA) + xmapa''
-  * Cambio 2: Una vez impreso un scanline horizontal de ANCHO_PANTALLA datos, debe de avanzar el registro usado como puntero de datos en el mapa un total de **ANCHO_MAPA-ANCHO_PANTALLA** bytes (para posicionarse en el siguiente scanline de datos del mapa). +  * Cambio 2: Una vez impreso un scanline horizontal de ''ANCHO_PANTALLA'' datos, debe de avanzar el registro usado como puntero de datos en el mapa un total de ''ANCHO_MAPA - ANCHO_PANTALLA'' bytes (para posicionarse en el siguiente scanline de datos del mapa). 
-\\+\\ 
  
  La rutina de impresión de este tipo de mapas tiene el primero de los cambios descritos al principio de la misma:  La rutina de impresión de este tipo de mapas tiene el primero de los cambios descritos al principio de la misma:
Línea 1174: Línea 1170:
 DrawMap_16x16_Map: DrawMap_16x16_Map:
  
-   LD IX, (DM_MAP)           ; IX apunta al mapa+    ld ix, (DM_MAP)           ; IX apunta al mapa
  
-   ;;; NUEVO: Posicionamos el puntero de mapa en posicion inicial. +    ;;; NUEVO: Posicionamos el puntero de mapa en posicion inicial. 
-   LD HL, (DM_MAPY) +    ld hl, (DM_MAPY) 
-   LD DE, ANCHO_MAPA_TILES +    ld de, ANCHO_MAPA_TILES 
-   CALL MULT_HL_POR_DE       ; HL = (ANCHO_MAPA * MAPA_Y) +    call MULT_HL_POR_DE       ; HL = (ANCHO_MAPA * MAPA_Y) 
-   LD BC, (DM_MAPX) +    ld bc, (DM_MAPX) 
-   ADD HLBC                ; HL = MAPA_X + (ANCHO_MAPA * MAPA_Y) +    add hlbc                ; HL = MAPA_X + (ANCHO_MAPA * MAPA_Y) 
-   EX DEHL +    ex dehl 
-   ADD IXDE                ; IX = Inicio_Mapa + HL +    add ixde                ; IX = Inicio_Mapa + HL 
-   ;;; FIN NUEVO+    ;;; FIN NUEVO
 </code> </code>
  
Línea 1190: Línea 1186:
  
 <code z80> <code z80>
-   ;;; NUEVO: Incrementar puntero de mapa a siguiente linea +    ;;; NUEVO: Incrementar puntero de mapa a siguiente linea 
-   LD BC, ANCHO_MAPA_TILES - ANCHO_PANTALLA +    ld bc, ANCHO_MAPA_TILES - ANCHO_PANTALLA 
-   ADD IXBC +    add ixbc 
-   ;;; FIN NUEVO+    ;;; FIN NUEVO
  
-   ;;; En este punto, hemos dibujado ANCHO tiles en pantalla (1 fila) +    ;;; En este punto, hemos dibujado ANCHO tiles en pantalla (1 fila) 
-   POP BC +    pop bc 
-   DEC B                     ; Bucle vertical +    dec b                     ; Bucle vertical 
-   JP NZ, drawmg16_yloop+    jp nz, drawmg16_yloop
  
-   RET+    ret
 </code> </code>
  
Línea 1228: Línea 1224:
 ;;; Rutina de la ROM del Spectrum, en otros sistemas ;;; Rutina de la ROM del Spectrum, en otros sistemas
 ;;; sustituir por una rutina especifica de multiplicacion ;;; sustituir por una rutina especifica de multiplicacion
-MULT_HL_POR_DE         EQU   $30A9 +MULT_HL_POR_DE         EQU   $30a9
  
 ;--------------------------------------------------------------- ;---------------------------------------------------------------
Línea 1250: Línea 1245:
 DrawMap_16x16_Map: DrawMap_16x16_Map:
  
-   LD IX, (DM_MAP)           ; IX apunta al mapa+    ld ix, (DM_MAP)           ; IX apunta al mapa
  
-   ;;; NUEVO: Posicionamos el puntero de mapa en posicion inicial. +    ;;; NUEVO: Posicionamos el puntero de mapa en posicion inicial. 
-   LD HL, (DM_MAPY) +    ld hl, (DM_MAPY) 
-   LD DE, ANCHO_MAPA_TILES +    ld de, ANCHO_MAPA_TILES 
-   CALL MULT_HL_POR_DE       ; HL = (ANCHO_MAPA * MAPA_Y) +    call MULT_HL_POR_DE       ; HL = (ANCHO_MAPA * MAPA_Y) 
-   LD BC, (DM_MAPX) +    ld bc, (DM_MAPX) 
-   ADD HLBC                ; HL = MAPA_X + (ANCHO_MAPA * MAPA_Y) +    add hlbc                ; HL = MAPA_X + (ANCHO_MAPA * MAPA_Y) 
-   EX DEHL +    ex dehl 
-   ADD IXDE                ; IX = Inicio_Mapa + HL +    add ixde                ; IX = Inicio_Mapa + HL 
-   ;;; FIN NUEVO+    ;;; FIN NUEVO
  
-   LD A, (DM_HEIGHT) +    ld a, (DM_HEIGHT) 
-   LD B                  ; B = ALTO_EN_TILES (para bucle altura)+    ld b                  ; B = ALTO_EN_TILES (para bucle altura)
  
 drawmg16_yloop: drawmg16_yloop:
-   PUSH BC                   ; Guardamos el valor de B+    push bc                   ; Guardamos el valor de B
  
-   LD A, (DM_HEIGHT)         ; A = ALTO_EN_TILES +    ld a, (DM_HEIGHT)         ; A = ALTO_EN_TILES 
-   SUB B                     ; A = ALTO - iteracion_bucle = Y actual +    sub b                     ; A = ALTO - iteracion_bucle = Y actual 
-   RLCA                      ; A = Y * 2+    rlca                      ; A = Y * 2
  
-   ;;; Calculamos la direccion destino en pantalla como +    ;;; Calculamos la direccion destino en pantalla como 
-   ;;; DIR_PANT = DIRECCION(X_INICIAL, Y_INICIAL + Y*2) +    ;;; DIR_PANT = DIRECCION(X_INICIAL, Y_INICIAL + Y*2) 
-   LD BC, (DM_COORD_X)       ; B = DB_COORD_Y y C = DB_COORD_X +    ld bc, (DM_COORD_X)       ; B = DB_COORD_Y y C = DB_COORD_X 
-   ADD AB +    add ab 
-   LD BA +    ld ba 
-   LD AB +    ld ab 
-   AND $18 +    and $18 
-   ADD A, $40 +    add a, $40 
-   LD HA +    ld ha 
-   LD AB +    ld ab 
-   AND +    and 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   ADD AC +    add ac 
-   LD L                  ; HL = DIR_PANTALLA(X_INICIAL,Y_INICIAL+Y*2)+    ld l                  ; HL = DIR_PANTALLA(X_INICIAL,Y_INICIAL+Y*2)
  
-   LD A, (DM_WIDTH) +    ld a, (DM_WIDTH) 
-   LD B                  ; B = ANCHO_EN_TILES+    ld b                  ; B = ANCHO_EN_TILES
  
 drawmg16_xloop: drawmg16_xloop:
-   PUSH BC                   ; Nos guardamos el contador del bucle+    push bc                   ; Nos guardamos el contador del bucle
  
-   LD A, (IX+0)              ; Leemos un byte del mapa +    ld a, (ix+0)              ; Leemos un byte del mapa 
-   INC IX                    ; Apuntamos al siguiente byte del mapa+    inc ix                    ; Apuntamos al siguiente byte del mapa
  
-   CP 255                    ; Bloque especial a saltar: no se dibuja +    cp 255                    ; Bloque especial a saltar: no se dibuja 
-   JP Z, drawmg16_next+    jp z, drawmg16_next
  
-   LD BA +    ld ba 
-   EX AFAF               ; Nos guardamos una copia del bloque en A' +    ex afaf               ; Nos guardamos una copia del bloque en A' 
-   LD AB+    ld ab
  
-   ;;; Calcular posicion origen (array sprites) en HL como: +    ;;; Calcular posicion origen (array sprites) en HL como: 
-   ;;;     direccion = base_sprites + (NUM_SPRITE*32) +    ;;;     direccion = base_sprites + (NUM_SPRITE*32) 
-   EX DEHL                 ; Intercambiamos DE y HL (DE=destino) +    ex dehl                 ; Intercambiamos DE y HL (DE=destino) 
-   LD BC, (DM_SPRITES) +    ld bc, (DM_SPRITES) 
-   LD L, 0 +    ld l, 0 
-   SRL A +    srl a 
-   RR L +    rr l 
-   RRA +    rra 
-   RR L +    rr l 
-   RRA +    rra 
-   RR L +    rr l 
-   LD HA +    ld ha 
-   ADD HLBC                ; HL = BC + HL = DM_SPRITES + (DM_NUMSPR * 32) +    add hlbc                ; HL = BC + HL = DM_SPRITES + (DM_NUMSPR * 32) 
-   EX DEHL                 ; Intercambiamos DE y HL (DE=origen, HL=destino)+    ex dehl                 ; Intercambiamos DE y HL (DE=origen, HL=destino)
  
-   PUSH HL                   ; Guardamos el puntero a pantalla recien calculado +    push hl                   ; Guardamos el puntero a pantalla recien calculado 
-   PUSH HL+    push hl
  
-   ;;; Impresion de los primeros 2 bloques horizontales del tile+    ;;; Impresion de los primeros 2 bloques horizontales del tile
  
-   LD B, 8+    ld b, 8
 drawmg16_loop1: drawmg16_loop1:
  
-   LD A, (DE)                ; Bloque 1: Leemos dato del sprite +    ld a, (de)                ; Bloque 1: Leemos dato del sprite 
-   LD (HL),                ; Copiamos dato a pantalla +    ld (hl),                ; Copiamos dato a pantalla 
-   INC DE                    ; Incrementar puntero en sprite +    inc de                    ; Incrementar puntero en sprite 
-   INC L                     ; Incrementar puntero en pantalla +    inc l                     ; Incrementar puntero en pantalla 
-   LD A, (DE)                ; Bloque 2: Leemos dato del sprite +    ld a, (de)                ; Bloque 2: Leemos dato del sprite 
-   LD (HL),                ; Copiamos dato a pantalla +    ld (hl),                ; Copiamos dato a pantalla 
-   INC DE                    ; Incrementar puntero en sprite +    inc de                    ; Incrementar puntero en sprite 
-   INC H                     ; Hay que sumar 256 para ir al siguiente scanline +    inc h                     ; Hay que sumar 256 para ir al siguiente scanline 
-   DEC L                     ; pero hay que restar el INC L que hicimos. +    dec l                     ; pero hay que restar el inc l que hicimos. 
-   DJNZ drawmg16_loop1 +    djnz drawmg16_loop1 
-   INC L                     ; Decrementar el ultimo incrementado en el bucle+    inc l                     ; Decrementar el ultimo incrementado en el bucle
  
-   ; Avanzamos HL 1 scanline (codigo de incremento de HL en 1 scanline) +    ; Avanzamos HL 1 scanline (codigo de incremento de HL en 1 scanline) 
-   ; desde el septimo scanline de la fila Y+1 al primero de la Y+2 +    ; desde el septimo scanline de la fila Y+1 al primero de la Y+2 
-   LD AL +    ld al 
-   ADD A, 31 +    add a, 31 
-   LD LA +    ld la 
-   JR C, drawmg16_nofix_abajop +    jr c, drawmg16_nofix_abajop 
-   LD AH +    ld ah 
-   SUB +    sub 
-   LD HA+    ld ha
 drawmg16_nofix_abajop: drawmg16_nofix_abajop:
  
-   ;;; Impresion de los segundos 2 bloques horizontales: +    ;;; Impresion de los segundos 2 bloques horizontales: 
-   LD B, 8+    ld b, 8
 drawmg16_loop2: drawmg16_loop2:
-   LD A, (DE)                ; Bloque 1: Leemos dato del sprite +    ld a, (de)                ; Bloque 1: Leemos dato del sprite 
-   LD (HL),                ; Copiamos dato a pantalla +    ld (hl),                ; Copiamos dato a pantalla 
-   INC DE                    ; Incrementar puntero en sprite +    inc de                    ; Incrementar puntero en sprite 
-   INC L                     ; Incrementar puntero en pantalla +    inc l                     ; Incrementar puntero en pantalla 
-   LD A, (DE)                ; Bloque 2: Leemos dato del sprite +    ld a, (de)                ; Bloque 2: Leemos dato del sprite 
-   LD (HL),                ; Copiamos dato a pantalla +    ld (hl),                ; Copiamos dato a pantalla 
-   INC DE                    ; Incrementar puntero en sprite +    inc de                    ; Incrementar puntero en sprite 
-   INC H                     ; Hay que sumar 256 para ir al siguiente scanline +    inc h                     ; Hay que sumar 256 para ir al siguiente scanline 
-   DEC L                     ; pero hay que restar el INC L que hicimos. +    dec l                     ; pero hay que restar el inc l que hicimos. 
-   DJNZ drawmg16_loop2+    djnz drawmg16_loop2
  
-   ;;; En este punto, los 16 scanlines del tile estan dibujados.+    ;;; En este punto, los 16 scanlines del tile estan dibujados.
  
-   ;;;;;; Impresion de la parte de atributos del tile ;;;;;;+    ;;;;;; Impresion de la parte de atributos del tile ;;;;;;
  
-   POP HL                    ; Recuperar puntero a inicio de tile+    pop hl                    ; Recuperar puntero a inicio de tile
  
-   ;;; Calcular posicion destino en area de atributos en DE. +    ;;; Calcular posicion destino en area de atributos en DE. 
-   LD A                  ; Codigo de Get_Attr_Offset_From_Image +    ld a                  ; Codigo de Get_Attr_Offset_From_Image 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   AND +    and 
-   OR $58 +    or $58 
-   LD DA +    ld da 
-   LD E                  ; DE tiene el offset del attr de HL+    ld e                  ; DE tiene el offset del attr de HL
  
-   LD HL, (DM_ATTRIBS) +    ld hl, (DM_ATTRIBS) 
-   EX AFAF               ; Recuperamos el bloque del mapa desde A' +    ex afaf               ; Recuperamos el bloque del mapa desde A' 
-   LD CA +    ld ca 
-   LD B, 0 +    ld b, 0 
-   ADD HLBC +    add hlbc 
-   ADD HLBC +    add hlbc 
-   ADD HLBC +    add hlbc 
-   ADD HLBC                ; HL = HL+HL=(DM_NUMSPR*4) = Origen de atributo+    add hlbc                ; HL = HL+HL=(DM_NUMSPR*4) = Origen de atributo
  
-   LDI +    ldi 
-   LDI                       ; Imprimimos la primeras fila de atributos+    ldi                       ; Imprimimos la primeras fila de atributos
  
-   ;;; Avance diferencial a la siguiente linea de atributos +    ;;; Avance diferencial a la siguiente linea de atributos 
-   LD A                  ; A = E +    ld a                  ; A = E 
-   ADD A, 30                 ; Sumamos A = A + 30 mas los 2 INCs de LDI+    add a, 30                 ; Sumamos A = A + 30 mas los 2 INCs de ldi
-   LD E                  ; Guardamos en E (E = E+30 + 2 por LDI=E+32) +    ld e                  ; Guardamos en E (E = E+30 + 2 por ldi=E+32) 
-   JR NC, drawmg16_att_noinc +    jr nc, drawmg16_att_noinc 
-   INC D+    inc d
 drawmg16_att_noinc: drawmg16_att_noinc:
-   LDI +    ldi 
-   LDI                       ; Imprimimos la segunda fila de atributos+    ldi                       ; Imprimimos la segunda fila de atributos
  
-   POP HL                    ; Recuperamos el puntero al inicio+    pop hl                    ; Recuperamos el puntero al inicio
  
 drawmg16_next: drawmg16_next:
-   INC L                     ; Avanzamos al siguiente tile en pantalla +    inc l                     ; Avanzamos al siguiente tile en pantalla 
-   INC L                     ; horizontalmente+    inc l                     ; horizontalmente
  
-   POP BC                    ; Recuperamos el contador para el bucle +    pop bc                    ; Recuperamos el contador para el bucle 
-   DEC B                     ; DJNZ se sale de rango, hay que usar DEC+JP +    dec b                     ; djnz se sale de rango, hay que usar DEC+jp 
-   JP NZ, drawmg16_xloop+    jp nz, drawmg16_xloop
  
-   ;;; NUEVO: Incrementar puntero de mapa a siguiente linea +    ;;; NUEVO: Incrementar puntero de mapa a siguiente linea 
-   LD BC, ANCHO_MAPA_TILES - ANCHO_PANTALLA +    ld bc, ANCHO_MAPA_TILES - ANCHO_PANTALLA 
-   ADD IXBC +    add ixbc 
-   ;;; FIN NUEVO+    ;;; FIN NUEVO
  
-   ;;; En este punto, hemos dibujado ANCHO tiles en pantalla (1 fila) +    ;;; En este punto, hemos dibujado ANCHO tiles en pantalla (1 fila) 
-   POP BC +    pop bc 
-   DEC B                     ; Bucle vertical +    dec b                     ; Bucle vertical 
-   JP NZ, drawmg16_yloop+    jp nz, drawmg16_yloop
  
-   RET+    ret
 </code> </code>
  
- Además es necesario realizar rutinas adicionales que gestionen el movimiento por pantalla alterando DM_MAPX y DM_MAPY sin permitir incrementarlos más allá de (Ancho_Mapa-Ancho_Pantalla) y (Alto_Mapa-Alto_Pantalla) o decrementarlos por debajo de cero:+ Además es necesario realizar rutinas adicionales que gestionen el movimiento por pantalla alterando ''DM_MAPX'' ''DM_MAPY'' sin permitir incrementarlos más allá de (''ANCHO_MAPA ANCHO_PANTALLA'') y (''ALTO_MAPA ALTO_PANTALLA'') o decrementarlos por debajo de cero:
  
 <code z80> <code z80>
Línea 1433: Línea 1428:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Map_Inc_X: Map_Inc_X:
-  LD HL, (DM_MAPX)+    ld hl, (DM_MAPX)
  
-  ;;; Comparacion 16 bits de HL y (ANCHO_MAPA-ANCHO_PANTALLA) +    ;;; Comparacion 16 bits de HL y (ANCHO_MAPA-ANCHO_PANTALLA) 
-  LD AH +    ld ah 
-  CP (ANCHO_MAPA_TILES-ANCHO_PANTALLA) / 256 +    CP (ANCHO_MAPA_TILES-ANCHO_PANTALLA) / 256 
-  RET NZ +    ret nz 
-  LD AL +    ld al 
-  CP (ANCHO_MAPA_TILES-ANCHO_PANTALLA) % 256 +    CP (ANCHO_MAPA_TILES-ANCHO_PANTALLA) % 256 
-  RET Z+    ret z
  
-  INC HL                     ; No eran iguales, podemos incrementar. +    inc hl                     ; No eran iguales, podemos incrementar. 
-  LD (DM_MAPX), HL +    ld (DM_MAPX), hl 
-  RET+    ret
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 1451: Línea 1446:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Map_Inc_Y: Map_Inc_Y:
-  LD HL, (DM_MAPY)+    ld hl, (DM_MAPY)
  
-  ;;; Comparacion 16 bits de HL y (ALTO_MAPA-ALTO_PANTALLA) +    ;;; Comparacion 16 bits de HL y (ALTO_MAPA-ALTO_PANTALLA) 
-  LD AH +    ld ah 
-  CP (ALTO_MAPA_TILES-ALTO_PANTALLA) / 256 +    CP (ALTO_MAPA_TILES-ALTO_PANTALLA) / 256 
-  RET NZ +    ret nz 
-  LD AL +    ld al 
-  CP (ALTO_MAPA_TILES-ALTO_PANTALLA) % 256 +    CP (ALTO_MAPA_TILES-ALTO_PANTALLA) % 256 
-  RET Z+    ret z
  
-  INC HL                     ; No eran iguales, podemos incrementar. +    inc hl                     ; No eran iguales, podemos incrementar. 
-  LD (DM_MAPY), HL +    ld (DM_MAPY), hl 
-  RET+    ret
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 1469: Línea 1464:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Map_Dec_X: Map_Dec_X:
-  LD HL, (DM_MAPX) +    ld hl, (DM_MAPX) 
-  LD AH +    ld ah 
-  AND A +    and a 
-  JR NZ, mapdecx_doit        ; Verificamos que DM_MAPX no sea 0 +    jr nz, mapdecx_doit        ; Verificamos que DM_MAPX no sea 0 
-  LD AL +    ld al 
-  AND A +    and a 
-  RET Z +    ret z 
-mapdecx_doit: +    mapdecx_doit: 
-  DEC HL +    dec hl 
-  LD (DM_MAPX), HL           ; No es cero, podemos decrementar +    ld (DM_MAPX), hl           ; No es cero, podemos decrementar 
-  RET+    ret
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 1485: Línea 1480:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Map_Dec_Y: Map_Dec_Y:
-  LD HL, (DM_MAPY) +    ld hl, (DM_MAPY) 
-  LD AH +    ld ah 
-  AND A +    and a 
-  JR NZ, mapdecy_doit        ; Verificamos que DM_MAPX no sea 0 +    jr nz, mapdecy_doit        ; Verificamos que DM_MAPX no sea 0 
-  LD AL +    ld al 
-  AND A +    and a 
-  RET Z +    ret z 
-mapdecy_doit: +    mapdecy_doit: 
-  DEC HL +    dec hl 
-  LD (DM_MAPY), HL           ; No es cero, podemos decrementar +    ld (DM_MAPY), hl           ; No es cero, podemos decrementar 
-  RET+    ret
 </code> </code>
  
- El incremento de DM_MAPX y DM_MAPY requiere verificar que ninguna de las 2 variables excede ANCHO_MAPA-ANCHO_PANTALLA y ALTO_MAPA-ALTO_PANTALLA respectivamente. Sus decrementos requieren comprobar que el valor actual de estas variables no es cero.+ El incremento de ''DM_MAPX'' ''DM_MAPY'' requiere verificar que ninguna de las 2 variables excede ''ANCHO_MAPA - ANCHO_PANTALLA'' ''ALTO_MAPA - ALTO_PANTALLA'' respectivamente. Sus decrementos requieren comprobar que el valor actual de estas variables no es cero.
  
  Utilicemos las anteriores rutinas en un programa de ejemplo en el que podemos mover una ventana de 14x11 bloques a través de un mapa de 32x24 tiles usando las teclas O, P, Q y A:  Utilicemos las anteriores rutinas en un programa de ejemplo en el que podemos mover una ventana de 14x11 bloques a través de un mapa de 32x24 tiles usando las teclas O, P, Q y A:
Línea 1504: Línea 1499:
 <code z80> <code z80>
 ; Ejemplo impresion mapa de 16x16 desde array global ; Ejemplo impresion mapa de 16x16 desde array global
-ORG 35000+    ORG 35000
  
-  LD HL, sokoban1_gfx +    ld hl, sokoban1_gfx 
-  LD (DM_SPRITES), HL +    ld (DM_SPRITES), hl 
-  LD HL, sokoban1_attr +    ld hl, sokoban1_attr 
-  LD (DM_ATTRIBS), HL +    ld (DM_ATTRIBS), hl 
-  LD HL, mapa_ejemplo +    ld hl, mapa_ejemplo 
-  LD (DM_MAP), HL +    ld (DM_MAP), hl 
-  LD A, ANCHO_PANTALLA +    ld a, ANCHO_PANTALLA 
-  LD (DM_WIDTH), A +    ld (DM_WIDTH), a 
-  LD A, ALTO_PANTALLA +    ld a, ALTO_PANTALLA 
-  LD (DM_HEIGHT), A +    ld (DM_HEIGHT), a 
-  XOR A +    xor a 
-  LD (DM_COORD_X), A +    ld (DM_COORD_X), a 
-  LD (DM_COORD_Y), A +    ld (DM_COORD_Y), a 
-  LD (DM_MAPX),            ; Establecemos MAPXMAPY iniciales = 0 +    ld (DM_MAPX),            ; Establecemos MaPXMaPY iniciales = 0 
-  LD (DM_MAPY), A+    ld (DM_MAPY), a
  
 redraw: redraw:
-  CALL DrawMap_16x16_Map     ; Imprimir pantalla de mapa+    call DrawMap_16x16_Map     ; Imprimir pantalla de mapa
  
 bucle: bucle:
-  CALL LEER_TECLADO          ; Leemos el estado de O, P, Q, A+    call LEER_TECLADO          ; Leemos el estado de O, P, Q, A
  
-  BIT 0,                   ; Modificamos MAPX y MAPY segun OPQA +    bit 0,                   ; Modificamos MAPX y MAPY segun OPQA 
-  JR Z, nopulsada_q +    jr z, nopulsada_q 
-  CALL Map_Dec_Y +    call Map_Dec_Y 
-  JR redraw+    jr redraw
 nopulsada_q: nopulsada_q:
-  BIT 1, A +    bit 1, a 
-  JR Z, nopulsada_a +    jr z, nopulsada_a 
-  CALL Map_Inc_Y +    call Map_Inc_Y 
-  JR redraw+    jr redraw
 nopulsada_a: nopulsada_a:
-  BIT 2, A +    bit 2, a 
-  JR Z, nopulsada_p +    jr z, nopulsada_p 
-  CALL Map_Inc_X +    call Map_Inc_X 
-  JR redraw+    jr redraw
 nopulsada_p: nopulsada_p:
-  BIT 3, A +    bit 3, a 
-  JR Z, nopulsada_o +    jr z, nopulsada_o 
-  CALL Map_Dec_X +    call Map_Dec_X 
-  JR redraw+    jr redraw
 nopulsada_o: nopulsada_o:
-  JR bucle+    jr bucle
  
 loop: loop:
-  JR loop+  jr loop
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 1562: Línea 1557:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 LEER_TECLADO: LEER_TECLADO:
-  LD D, 0 +    ld d, 0 
-  LD BC, $FBFE +    ld bc, $fbfe 
-  IN A, (C+    in a, (c
-  BIT 0,                   ; Leemos la tecla Q +    bit 0,                   ; Leemos la tecla Q 
-  JR NZ, Control_no_up       ; No pulsada, no cambiamos nada en D +    jr nz, Control_no_up       ; No pulsada, no cambiamos nada en D 
-  SET 0,                   ; Pulsada, ponemos a 1 el bit 0+    set 0,                   ; Pulsada, ponemos a 1 el bit 0
 Control_no_up: Control_no_up:
  
-  LD BC, $FDFE +    ld bc, $fdfe 
-  IN A, (C+    in a, (c
-  BIT 0,                   ; Leemos la tecla A +    bit 0,                   ; Leemos la tecla A 
-  JR NZ, Control_no_down     ; No pulsada, no cambianos nada en D +    jr nz, Control_no_down     ; No pulsada, no cambianos nada en D 
-  SET 1,                   ; Pulsada, ponemos a 1 el bit 1+    set 1,                   ; Pulsada, ponemos a 1 el bit 1
 Control_no_down: Control_no_down:
  
-  LD BC, $DFFE +    ld bc, $dffe 
-  IN A, (C+    in a, (c
-  BIT 0,                   ; Leemos la tecla P +    bit 0,                   ; Leemos la tecla P 
-  JR NZ, Control_no_right    ; No pulsada +    jr nz, Control_no_right    ; No pulsada 
-  SET 2,                   ; Pulsada, ponemos a 1 el bit 2+    set 2,                   ; Pulsada, ponemos a 1 el bit 2 
 Control_no_right: Control_no_right:
-                             ; BC ya vale $DFFE, (O y P en misma fila) +                               ; BC ya vale $dffe, (O y P en misma fila) 
-  BIT 1,                   ; Tecla O +    bit 1,                   ; Tecla O 
-  JR NZ, Control_no_left +    jr nz, Control_no_left 
-  SET 3, D+    set 3, d
 Control_no_left: Control_no_left:
  
-  LD A                   ; Devolvemos en A el estado de las teclas +    ld a                   ; Devolvemos en A el estado de las teclas 
-  RET+    ret
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 1614: Línea 1610:
 ;;; Rutina de la ROM del Spectrum, en otros sistemas ;;; Rutina de la ROM del Spectrum, en otros sistemas
 ;;; sustituir por una rutina especifica de multiplicacion ;;; sustituir por una rutina especifica de multiplicacion
-MULT_HL_POR_DE         EQU   $30A9+MULT_HL_POR_DE         EQU   $30a9
  
 ;----------------------------------------------------------------------- ;-----------------------------------------------------------------------
Línea 1620: Línea 1616:
 ;----------------------------------------------------------------------- ;-----------------------------------------------------------------------
 mapa_ejemplo: mapa_ejemplo:
-  DEFB 1,2,1,1,2,1,2,1,2,1,1,2,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,+    DEFB 1,2,1,1,2,1,2,1,2,1,1,2,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  DEFB 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+    DEFB 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-  DEFB 1,0,0,0,0,0,0,0,0,0,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+    DEFB 1,0,0,0,0,0,0,0,0,0,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-  DEFB 1,0,0,0,0,0,0,0,0,2,3,2,3,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,+    DEFB 1,0,0,0,0,0,0,0,0,2,3,2,3,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,
-  DEFB 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,+    DEFB 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,
-  DEFB 1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,7,7,0,0,0,+    DEFB 1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,7,7,0,0,0,
-  DEFB 1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,7,7,7,0,0,+    DEFB 1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,7,7,7,0,0,
-  DEFB 1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,5,0,0,0,0,0,0,2,3,2,3,2,0,+    DEFB 1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,5,0,0,0,0,0,0,2,3,2,3,2,0,
-  DEFB 1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,4,0,0,0,0,0,0,0,0,0,0,0,0,+    DEFB 1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,4,0,0,0,0,0,0,0,0,0,0,0,0,
-  DEFB 1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,5,0,0,0,0,0,0,0,0,0,0,0,0,+    DEFB 1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,5,0,0,0,0,0,0,0,0,0,0,0,0,
-  DEFB 1,4,2,3,2,3,0,0,0,0,2,3,2,3,2,3,2,3,4,2,3,2,0,0,0,0,0,0,0,0,0,+    DEFB 1,4,2,3,2,3,0,0,0,0,2,3,2,3,2,3,2,3,4,2,3,2,0,0,0,0,0,0,0,0,0,
-  DEFB 1,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+    DEFB 1,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-  DEFB 1,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+    DEFB 1,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-  DEFB 1,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+    DEFB 1,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-  DEFB 1,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,2,3,2,3,0,0,0,0,0,0,0,0,0,0,+    DEFB 1,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,2,3,2,3,0,0,0,0,0,0,0,0,0,0,
-  DEFB 1,0,0,0,6,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+    DEFB 1,0,0,0,6,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-  DEFB 1,0,0,6,6,6,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+    DEFB 1,0,0,6,6,6,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-  DEFB 1,0,2,3,2,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,+    DEFB 1,0,2,3,2,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,
-  DEFB 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,+    DEFB 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,
-  DEFB 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,0,0,0,0,+    DEFB 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,0,0,0,0,
-  DEFB 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,6,0,0,0,+    DEFB 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,6,0,0,0,
-  DEFB 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,2,3,2,3,2,3,2,3,0,0,+    DEFB 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,2,3,2,3,2,3,2,3,0,0,
-  DEFB 1,0,2,3,2,2,2,3,2,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+    DEFB 1,0,2,3,2,2,2,3,2,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-  DEFB 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1+    DEFB 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
  
-END 35000+    END 35000
 </code> </code>
  
  Veamos una captura del resultado del anterior ejemplo después de moverse a través del mapeado:  Veamos una captura del resultado del anterior ejemplo después de moverse a través del mapeado:
  
-\\+\\ 
 {{ :cursos:ensamblador:gfx5_maparray.png?640 | Moviendose por un mapa en array global }} {{ :cursos:ensamblador:gfx5_maparray.png?640 | Moviendose por un mapa en array global }}
-\\+\\ 
  
  El anterior ejemplo es meramente ilustrativo del uso de la rutina de impresión: para un juego basado en scroll (como simula el ejemplo con la pulsación de teclas) resultará mucho más rápido y eficiente realizar un scroll del contenido del área de juego y trazar sólo la fila o columna de datos que "entra" en el área de visión del jugador.  El anterior ejemplo es meramente ilustrativo del uso de la rutina de impresión: para un juego basado en scroll (como simula el ejemplo con la pulsación de teclas) resultará mucho más rápido y eficiente realizar un scroll del contenido del área de juego y trazar sólo la fila o columna de datos que "entra" en el área de visión del jugador.
  
- Para esta implementación serían necesarias 4 rutinas de scroll de una porción de videomemoria, según el tipo de movimiento, y la impresión en la primera/última fila/columna de visión del dato de la pantalla entrante. Sigue siendo necesario modificar DM_MAPX y DM_MAPY.+ Para esta implementación serían necesarias 4 rutinas de scroll de una porción de videomemoria, según el tipo de movimiento, y la impresión en la primera/última fila/columna de visión del dato de la pantalla entrante. Sigue siendo necesario modificar ''DM_MAPX'' ''DM_MAPY''.
  
- El scroll mediante instrucciones de transferencia de N-1 FILAS o N-1 COLUMNAS de pantalla  gráfica y de atributos resultará bastante más rápido y eficiente que la rutina de impresión de sprites integrada en DrawMap, al no tener que realizar apenas cálculos.+ El scroll mediante instrucciones de transferencia de N-1 FILAS o N-1 COLUMNAS de pantalla  gráfica y de atributos resultará bastante más rápido y eficiente que la rutina de impresión de sprites integrada en ''DrawMap'', al no tener que realizar apenas cálculos.
  
  Como desventaja principal de los mapeados basados en arrays globales de tiles, en este tipo de mapeados no podemos utilizar de una forma inmediata las técnicas de "agrupación" o "compresión" que veremos a continuación.  Como desventaja principal de los mapeados basados en arrays globales de tiles, en este tipo de mapeados no podemos utilizar de una forma inmediata las técnicas de "agrupación" o "compresión" que veremos a continuación.
  
-\\+\\ 
 ===== Mapeados diferenciales ===== ===== Mapeados diferenciales =====
  
Línea 1671: Línea 1667:
  Para reducir el espacio que ocupan nuestras pantallas y por tanto poder incluir más pantallas en la misma cantidad de memoria podemos utilizar diferentes métodos.  Para reducir el espacio que ocupan nuestras pantallas y por tanto poder incluir más pantallas en la misma cantidad de memoria podemos utilizar diferentes métodos.
  
- El primero de ellos, la **compresión** con algoritmos conocidos (como RLE o ZX0): podemos tomar todo el bloque de datos con información sobre las pantallas y lo comprimirlo antes de salvarlo a cinta. Después, tendremos 2 opciones: o bien descomprimirlo durante el proceso de carga en su ubicación definitiva en memoria (con lo cual ahorremos tiempo de carga desde cinta, pero no memoria) o bien guardarlo en memoria comprimido (ahorrando espacio) y descomprimirlo al vuelo en algún buffer temporal antes de su uso (por ejemplo, al principio de cada nivel), lo que sí que supondría un ahorro de memoria incluso pese a necesitar ese buffer para la descompresión. + El primero de ellos, la **compresión** con algoritmos conocidos (como RLE o ZX0): podemos tomar todo el bloque de datos con información sobre las pantallas y lo comprimirlo antes de salvarlo a cinta. Después, tendremos 2 opciones: o bien descomprimirlo durante el proceso de carga en su ubicación definitiva en memoria (con lo cual ahorremos tiempo de carga desde cinta, pero no memoria) o bien guardarlo en memoria comprimido (ahorrando espacio) y descomprimirlo al vuelo en algún buffer temporal antes de su uso (por ejemplo, al principio de cada nivel), lo que sí que supondría un ahorro de memoria incluso pese a necesitar ese buffer para la descompresión.  
-  + 
- En el siguiente capítulo entraremos en detalle en rutinas de compresión.+ En la mayoría de las ocasiones la compresión es la solución más eficiente a la necesidad de reducir el tamaño de mapas, sprites, textos o músicas. En el siguiente capítulo entraremos en detalle en rutinas de compresión, y es muy probable que gracias a sencillos algoritmos de compresión básicos (RLE ym sobre todo, ZX0), sea suficiente para nuestras necesidades. 
 + 
 + Pero no siempre la compresión es la solución a todos los problemas y a veces hay que dar un paso atrás, mirar el conjunto de datos y fijarnos si podemos reducir lo que ocupa de alguna forma más inteligente mediante la **codificación**.
  
-  + Este segundo método, la **codificación**, consiste en encontrar patrones repetidos en las pantallas y codificarlos de una forma inteligente. Es una especie de compresión, pero muy sencilla y visual, y se puede "desempaquetar" al vuelo cuando estamos imprimiendo las pantallas.
- El segundo método, que vamos a ver ahora, es la **codificación**, que consiste en encontrar patrones repetidos en las pantallas y codificarlos. Es una especie de compresión, pero muy sencilla y visual, y se puede "desempaquetar" al vuelo cuando estamos imprimiendo las pantallas.+
  
  La técnica que vamos a ver se basa en no almacenar el total de datos de la pantalla, sino que ignoraremos los datos en blanco/transparentes. Los datos de la pantalla incluirán sólo los tiles que deben de ser dibujados, asumiendo que el resto de la pantalla es "fondo", "transparente" o "suelo".  La técnica que vamos a ver se basa en no almacenar el total de datos de la pantalla, sino que ignoraremos los datos en blanco/transparentes. Los datos de la pantalla incluirán sólo los tiles que deben de ser dibujados, asumiendo que el resto de la pantalla es "fondo", "transparente" o "suelo".
Línea 1685: Línea 1682:
  
  
-\\+\\ 
 ==== Mapeados diferenciales con un único tileset ==== ==== Mapeados diferenciales con un único tileset ====
  
Línea 1694: Línea 1691:
  A la hora de codificar las pantallas, podemos optar por diferentes "algoritmos":  A la hora de codificar las pantallas, podemos optar por diferentes "algoritmos":
  
-\\+\\ 
   * **Codificación básica**: se almacenan en el vector "pantalla" los datos de cada tile que realmente deba de ser impreso. Los datos mínimos necesarios son la coordenada X, la coordenada Y y el identificador de tile.   * **Codificación básica**: se almacenan en el vector "pantalla" los datos de cada tile que realmente deba de ser impreso. Los datos mínimos necesarios son la coordenada X, la coordenada Y y el identificador de tile.
   * **Codificación por agrupación horizontal**: Para evitar incluir la coordenada X e Y en cada tile, podemos agrupar tiles consecutivos horizontalmente y marcar sólo la posición del primero, ahorrando 2 bytes por cada tile que le sigue.   * **Codificación por agrupación horizontal**: Para evitar incluir la coordenada X e Y en cada tile, podemos agrupar tiles consecutivos horizontalmente y marcar sólo la posición del primero, ahorrando 2 bytes por cada tile que le sigue.
   * **Codificación por agrupación vertical**: Para evitar incluir la coordenada X e Y en cada tile, podemos agrupar tiles consecutivos verticalmente y marcar sólo la posición del primero, ahorrando 2 bytes por cada tile bajo él.   * **Codificación por agrupación vertical**: Para evitar incluir la coordenada X e Y en cada tile, podemos agrupar tiles consecutivos verticalmente y marcar sólo la posición del primero, ahorrando 2 bytes por cada tile bajo él.
   * **Codificación por agrupación mixta**: Cada tile se codifica por agrupación horizontal o vertical según produzca un mayor o menor ahorro de tamaño. La pantalla contiene primero los scanlines horizontales, seguidos de un byte de valor 254, y después los scanlines verticales.   * **Codificación por agrupación mixta**: Cada tile se codifica por agrupación horizontal o vertical según produzca un mayor o menor ahorro de tamaño. La pantalla contiene primero los scanlines horizontales, seguidos de un byte de valor 254, y después los scanlines verticales.
-\\+\\ 
  
  Veamos en detalle todos estos tipos de codificación.  Veamos en detalle todos estos tipos de codificación.
  
  
-\\+\\ 
 === Mapeado diferencial básico (sin agrupación) === === Mapeado diferencial básico (sin agrupación) ===
  
Línea 1719: Línea 1716:
 <code z80> <code z80>
 sokoban_LEVEL1: sokoban_LEVEL1:
-  DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+    DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-  DEFB 0,0,0,0,0,0,2,3,1,4,0,0,0,0,0,+    DEFB 0,0,0,0,0,0,2,3,1,4,0,0,0,0,0,
-  DEFB 0,0,0,0,1,2,3,0,0,5,4,0,0,0,0,+    DEFB 0,0,0,0,1,2,3,0,0,5,4,0,0,0,0,
-  DEFB 0,0,0,0,4,0,6,6,0,0,5,0,0,0,0,+    DEFB 0,0,0,0,4,0,6,6,0,0,5,0,0,0,0,
-  DEFB 0,0,0,0,5,0,0,6,0,0,4,0,0,0,0,+    DEFB 0,0,0,0,5,0,0,6,0,0,4,0,0,0,0,
-  DEFB 0,0,0,0,4,0,0,0,0,0,5,0,0,0,0,+    DEFB 0,0,0,0,4,0,0,0,0,0,5,0,0,0,0,
-  DEFB 0,0,0,0,5,2,3,0,0,2,3,0,0,0,0,+    DEFB 0,0,0,0,5,2,3,0,0,2,3,0,0,0,0,
-  DEFB 0,0,0,0,0,0,1,0,0,0,4,0,0,0,0,+    DEFB 0,0,0,0,0,0,1,0,0,0,4,0,0,0,0,
-  DEFB 0,0,0,0,0,0,4,7,7,7,5,0,0,0,0,+    DEFB 0,0,0,0,0,0,4,7,7,7,5,0,0,0,0,
-  DEFB 0,0,0,0,0,0,5,2,3,2,3,0,0,0,0,+    DEFB 0,0,0,0,0,0,5,2,3,2,3,0,0,0,0,
-  DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+    DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-  DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0+    DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 </code> </code>
  
Línea 2048: Línea 2045:
  El script codifica esta pantalla con 106 bytes cuando el tamaño original era de 192, consiguiendo una compresión de aprox. el 46% (casi la mitad de tamaño). Esto quiere decir que el mapeado de nuestro programa podría ser (manteniendo este ratio de compresión en todas las pantallas), hasta casi el doble de grande que el mapa máximo actual.  El script codifica esta pantalla con 106 bytes cuando el tamaño original era de 192, consiguiendo una compresión de aprox. el 46% (casi la mitad de tamaño). Esto quiere decir que el mapeado de nuestro programa podría ser (manteniendo este ratio de compresión en todas las pantallas), hasta casi el doble de grande que el mapa máximo actual.
  
- Veamos a continuación una sencilla rutina que permitiría imprimir un mapa con este tipo de codificación. La rutina se basa en recorrer el array de datos de pantalla obtenido el valor X, Y y de TILE y llamando a DrawSprite16x16 para la impresión de cada tile:+ Veamos a continuación una sencilla rutina que permitiría imprimir un mapa con este tipo de codificación. La rutina se basa en recorrer el array de datos de pantalla obtenido el valor X, Y y de TILE y llamando a ''DrawSprite16x16'' para la impresión de cada tile:
  
 <code z80> <code z80>
Línea 2068: Línea 2065:
 DrawMap_16x16_Cod_Basica: DrawMap_16x16_Cod_Basica:
  
-   LD HL, (DM_SPRITES) +    ld hl, (DM_SPRITES) 
-   LD (DS_SPRITES), HL       ; Establecer tileset (graficos) +    ld (DS_SPRITES), hl       ; Establecer tileset (graficos) 
-   LD HL, (DM_ATTRIBS) +    ld hl, (DM_ATTRIBS) 
-   LD (DS_ATTRIBS), HL       ; Establecer tileset (atributos) +    ld (DS_ATTRIBS), hl       ; Establecer tileset (atributos) 
-   LD BC, (DM_COORD_X)       ; B = Y_INICIO, C = X_INICIO +    ld bc, (DM_COORD_X)       ; B = Y_INICIO, C = X_INICIO 
-   LD DE, (DM_MAP)           ; DE apunta al mapa+    ld de, (DM_MAP)           ; DE apunta al mapa
  
 drawm16cb_loop: drawm16cb_loop:
-   LD A, (DE)                ; Leemos el valor de COORD_X_TILE +    ld a, (de)                ; Leemos el valor de COORD_X_TILE 
-   INC DE                    ; Apuntamos al siguiente byte del mapa+    inc de                    ; Apuntamos al siguiente byte del mapa
  
-   CP 255                    ; Bloque especial fin de pantalla +    cp 255                    ; Bloque especial fin de pantalla 
-   RET Z                     ; En ese caso, salir+    ret z                     ; En ese caso, salir
  
-   ADD A                 ; A = (X_INICIO + COORD_X_TILE) +    add a                 ; A = (X_INICIO + COORD_X_TILE) 
-   RLCA                      ; A = (X_INICIO + COORD_X_TILE) * 2 +    rlca                      ; A = (X_INICIO + COORD_X_TILE) * 2 
-   LD (DS_COORD_X),        ; Establecemos COORD_X a imprimir tile+    ld (DS_COORD_X),        ; Establecemos COORD_X a imprimir tile
  
-   LD A, (DE)                ; Leemos el valor de COORD_Y_TILE +    ld a, (de)                ; Leemos el valor de COORD_Y_TILE 
-   INC DE +    inc de 
-   ADD A                 ; A = (X_INICIO + COORD_X_TILE) +    add a                 ; A = (X_INICIO + COORD_X_TILE) 
-   RLCA                      ; A = (Y_INICIO + COORD_Y_TILE) +    rlca                      ; A = (Y_INICIO + COORD_Y_TILE) 
-   LD (DS_COORD_Y),        ; Establecemos COORD_Y a imprimir tile+    ld (DS_COORD_Y),        ; Establecemos COORD_Y a imprimir tile
  
-   LD A, (DE)                ; Leemos el valor del TILE +    ld a, (de)                ; Leemos el valor del TILE 
-   INC DE +    inc de 
-   LD (DS_NUMSPR),         ; Establecemos el TILE+    ld (DS_NUMSPR),         ; Establecemos el TILE
  
-   EXX                       ; Preservar todos los registros en shadows +    exx                       ; Preservar todos los registros en shadows 
-   CALL DrawSprite_16x16_LD  ; Imprimir el tile con los parametros +    call DrawSprite_16x16_LD  ; Imprimir el tile con los parametros 
-   EXX                       ; Recuperar valores de los registros+    exx                       ; Recuperar valores de los registros
  
-   JR drawm16cb_loop         ; Repetimos hasta encontrar el 255+    jr drawm16cb_loop         ; Repetimos hasta encontrar el 255
 </code> </code>
  
Línea 2107: Línea 2104:
  Por otra parte, como ya no estamos dibujando los bloques 0, antes de llamar a la rutina de dibujado diferencial es necesario borrar el contenido del área donde vamos a dibujar con el color plano del bloque 0 para que el resultado de la impresión incluya los bloques vacíos en aquellas áreas en que no dibujamos. Es decir, vaciaremos los "bloques vacíos" borrando inicialmente la pantalla antes de realizar la impresión de los "bloques con datos".  Por otra parte, como ya no estamos dibujando los bloques 0, antes de llamar a la rutina de dibujado diferencial es necesario borrar el contenido del área donde vamos a dibujar con el color plano del bloque 0 para que el resultado de la impresión incluya los bloques vacíos en aquellas áreas en que no dibujamos. Es decir, vaciaremos los "bloques vacíos" borrando inicialmente la pantalla antes de realizar la impresión de los "bloques con datos".
  
- Para borrar este área de pantalla podemos utilizar un simple borrado de atributos (establecer todos los atributos a cero) siempre y cuando los personajes del juego que se moverán sobre las áreas "vacías" se dibujen mediante transferencia y no mediante operaciones lógicas. Recordemos que si hemos borrado mediante atributos en negro estas áreas están vacías de color pero no de contenido gráfico y la impresión con OR haría aparecer el antiguo contenido gráfico de la pantalla. En el caso de impresión de sprites con OR sobre el área "vacía" del mapa sería necesario realizar el borrado previo a la impresión del mapa no como borrado de atributos sino como borrado de zona gráfica y de atributos.+ Para borrar este área de pantalla podemos utilizar un simple borrado de atributos (establecer todos los atributos a cero) siempre y cuando los personajes del juego que se moverán sobre las áreas "vacías" se dibujen mediante transferencia y no mediante operaciones lógicas. Recordemos que si hemos borrado mediante atributos en negro estas áreas están vacías de color pero no de contenido gráfico y la impresión con or haría aparecer el antiguo contenido gráfico de la pantalla. En el caso de impresión de sprites con OR sobre el área "vacía" del mapa sería necesario realizar el borrado previo a la impresión del mapa no como borrado de atributos sino como borrado de zona gráfica y de atributos.
  
  El siguiente programa ejemplo hace uso de la anterior rutina:  El siguiente programa ejemplo hace uso de la anterior rutina:
Línea 2113: Línea 2110:
 <code z80> <code z80>
 ; Ejemplo impresion mapa de 16x16 codificacion basica ; Ejemplo impresion mapa de 16x16 codificacion basica
-ORG 35000+    ORG 35000
  
-  ;;; Borramos la pantalla (graficos y atributos) +    ;;; Borramos la pantalla (graficos y atributos) 
-  XOR A +    xor a 
-  CALL ClearScreen +    call ClearScreen 
-  XOR A +    xor a 
-  CALL ClearAttributes+    call ClearAttributes
  
-  ;;; Establecer valores de llamada: +    ;;; Establecer valores de llamada: 
-  LD HL, sokoban1_gfx +    ld hl, sokoban1_gfx 
-  LD (DM_SPRITES), HL +    ld (DM_SPRITES), hl 
-  LD HL, sokoban1_attr +    ld hl, sokoban1_attr 
-  LD (DM_ATTRIBS), HL +    ld (DM_ATTRIBS), hl 
-  LD HL, sokoban_LEVEL1_codif_basica +    ld hl, sokoban_LEVEL1_codif_basica 
-  LD (DM_MAP), HL +    ld (DM_MAP), hl 
-  LD A, 16 +    ld a, 16 
-  LD (DM_WIDTH), A +    ld (DM_WIDTH), a 
-  LD A, 12 +    ld a, 12 
-  LD (DM_HEIGHT), A +    ld (DM_HEIGHT), a 
-  XOR A +    xor a 
-  LD (DM_COORD_X), A +    ld (DM_COORD_X), a 
-  LD (DM_COORD_Y), A+    ld (DM_COORD_Y), a
  
-  ;;; Impresion de pantalla por codificacion basica +    ;;; Impresion de pantalla por codificacion basica 
-  CALL DrawMap_16x16_Cod_Basica+    call DrawMap_16x16_Cod_Basica
  
 loop: loop:
-  JR loop+    jr loop
  
 DM_SPRITES  DEFW   0 DM_SPRITES  DEFW   0
Línea 2158: Línea 2155:
 DS_NUMSPR   DEFB   0 DS_NUMSPR   DEFB   0
  
-END 35000+    END 35000
 </code> </code>
  
  El resultado de la ejecución es el siguiente:  El resultado de la ejecución es el siguiente:
  
-\\+\\ 
 {{ :cursos:ensamblador:gfx5_codifb.png?640 | Codificación básica }} {{ :cursos:ensamblador:gfx5_codifb.png?640 | Codificación básica }}
-\\+\\ 
  
-\\ +\\  
-\\+\\ 
 === Mapeado diferencial con agrupación por scanlines === === Mapeado diferencial con agrupación por scanlines ===
  
Línea 2176: Línea 2173:
  
 <code z80> <code z80>
-  DB coordenada_x_primer_tile, coordenada_y_primer_tile, +    DB coordenada_x_primer_tile, coordenada_y_primer_tile, 
-  DB tile1, tile2, tile3, tile4, 255 +    DB tile1, tile2, tile3, tile4, 255 
-  ; (255 = byte de fin de "scanline")+    ; (255 = byte de fin de "scanline")
 </code> </code>
  
Línea 2187: Línea 2184:
  Al final de nuestra pantalla necesitaremos un valor 255 adicional para que la rutina de impresión, al recogerlo como coordenada X, detecta la finalización de la misma.  Al final de nuestra pantalla necesitaremos un valor 255 adicional para que la rutina de impresión, al recogerlo como coordenada X, detecta la finalización de la misma.
  
-\\ +\\  
-\\+\\ 
 **Mapeado diferencial con agrupación por scanlines horizontales** **Mapeado diferencial con agrupación por scanlines horizontales**
-\\+\\ 
  
  El script codificador en python que hemos visto en el apartado de codificación básica permite, mediante el flag "**-h**" codificar la pantalla buscando ristras de tiles horizontales consecutivos y codificándolos de esta forma. Nótese que un sólo tile sería codificado como una ristra de tamaño 1 (x, y, tile, 255).  El script codificador en python que hemos visto en el apartado de codificación básica permite, mediante el flag "**-h**" codificar la pantalla buscando ristras de tiles horizontales consecutivos y codificándolos de esta forma. Nótese que un sólo tile sería codificado como una ristra de tamaño 1 (x, y, tile, 255).
Línea 2232: Línea 2229:
 DrawMap_16x16_Cod_Horiz: DrawMap_16x16_Cod_Horiz:
  
-   LD HL, (DM_SPRITES) +    ld hl, (DM_SPRITES) 
-   LD (DS_SPRITES), HL       ; Establecer tileset (graficos) +    ld (DS_SPRITES), hl       ; Establecer tileset (graficos) 
-   LD HL, (DM_ATTRIBS) +    ld hl, (DM_ATTRIBS) 
-   LD (DS_ATTRIBS), HL       ; Establecer tileset (atributos) +    ld (DS_ATTRIBS), hl       ; Establecer tileset (atributos) 
-   LD BC, (DM_COORD_X)       ; B = Y_INICIO, C = X_INICIO +    ld bc, (DM_COORD_X)       ; B = Y_INICIO, C = X_INICIO 
-   LD DE, (DM_MAP)           ; DE apunta al mapa+    ld de, (DM_MAP)           ; DE apunta al mapa
  
-   LD HL, DS_COORD_X+    ld hl, DS_COORD_X
  
 drawm16ch_read: drawm16ch_read:
-   LD A, (DE)                ; Leemos el valor de COORD_X_TILE +    ld a, (de)                ; Leemos el valor de COORD_X_TILE 
-   INC DE                    ; Apuntamos al siguiente byte del mapa+    inc de                    ; Apuntamos al siguiente byte del mapa
  
 drawm16ch_loop: drawm16ch_loop:
-   CP 255                    ; Bloque especial fin de pantalla +    cp 255                    ; Bloque especial fin de pantalla 
-   RET Z                     ; En ese caso, salir+    ret z                     ; En ese caso, salir
  
-   ADD A                 ; A = (X_INICIO + COORD_X_TILE) +    add a                 ; A = (X_INICIO + COORD_X_TILE) 
-   RLCA                      ; A = (X_INICIO + COORD_X_TILE) * 2 +    rlca                      ; A = (X_INICIO + COORD_X_TILE) * 2 
-   LD (HL),                ; Establecemos COORD_X a imprimir tile+    ld (hl),                ; Establecemos COORD_X a imprimir tile
  
-   LD A, (DE)                ; Leemos el valor de COORD_Y_TILE +    ld a, (de)                ; Leemos el valor de COORD_Y_TILE 
-   INC DE +    inc de 
-   ADD A                 ; A = (Y_INICIO + COORD_Y_TILE) +    add a                 ; A = (Y_INICIO + COORD_Y_TILE) 
-   RLCA                      ; A = (Y_INICIO + COORD_Y_TILE) +    rlca                      ; A = (Y_INICIO + COORD_Y_TILE) 
-   LD (DS_COORD_Y),        ; Establecemos COORD_Y a imprimir tile+    ld (DS_COORD_Y),        ; Establecemos COORD_Y a imprimir tile
  
-   ;;; Bucle impresion de todos los tiles del scanline (aunque sea 1 solo)+    ;;; Bucle impresion de todos los tiles del scanline (aunque sea 1 solo)
  
 drawm16ch_tileloop: drawm16ch_tileloop:
-   LD A, (DE)                ; Leemos el valor del TILE de pantalla +    ld a, (de)                ; Leemos el valor del TILE de pantalla 
-   INC DE                    ; Incrementamos puntero+    inc de                    ; Incrementamos puntero
  
-   CP 255 +    cp 255 
-   JR Z, drawm16ch_read      ; Si es fin de tile codificado, fin bucle+    jr z, drawm16ch_read      ; Si es fin de tile codificado, fin bucle
  
-   LD (DS_NUMSPR),         ; Establecemos el TILE+    ld (DS_NUMSPR),         ; Establecemos el TILE
  
-   EXX                       ; Preservar todos los registros en shadows +    exx                       ; Preservar todos los registros en shadows 
-   CALL DrawSprite_16x16_LD  ; Imprimir el tile con los parametros +    call DrawSprite_16x16_LD  ; Imprimir el tile con los parametros 
-   EXX                       ; Recuperar valores de los registros+    exx                       ; Recuperar valores de los registros
  
-   INC (HL)                  ; Avanzamos al siguiente tile +    inc (hl)                  ; Avanzamos al siguiente tile 
-   INC (HL)                  ; COORD_X = COORD_X + 2+    inc (hl)                  ; COORD_X = COORD_X + 2
  
-   JR drawm16ch_tileloop     ; Repetimos hasta encontrar el 255+    jr drawm16ch_tileloop     ; Repetimos hasta encontrar el 255
 </code> </code>
  
-\\ +\\  
-\\+\\ 
 **Mapeado diferencial con agrupación por scanlines verticales** **Mapeado diferencial con agrupación por scanlines verticales**
-\\+\\ 
  
  Según el tipo de pantalla que estemos codificando, es posible que existan más agrupaciones de bloques en "scanlines verticales" (columnas de bloques) que horizontales (filas de bloques). En ese caso, puede convenirmos codificar los bloques por scanlines verticales.  Según el tipo de pantalla que estemos codificando, es posible que existan más agrupaciones de bloques en "scanlines verticales" (columnas de bloques) que horizontales (filas de bloques). En ese caso, puede convenirmos codificar los bloques por scanlines verticales.
  
- Si utilizamos el script en python con el flag **-v** sobre la pantalla de ejemplo, obtenemos la siguiente pantalla codificada:+ Si utilizamos el script en python con el flag ''-v'' sobre la pantalla de ejemplo, obtenemos la siguiente pantalla codificada:
  
 <code z80> <code z80>
Línea 2324: Línea 2321:
 DrawMap_16x16_Cod_Vert: DrawMap_16x16_Cod_Vert:
  
-   LD HL, (DM_SPRITES) +    ld hl, (DM_SPRITES) 
-   LD (DS_SPRITES), HL       ; Establecer tileset (graficos) +    ld (DS_SPRITES), hl       ; Establecer tileset (graficos) 
-   LD HL, (DM_ATTRIBS) +    ld hl, (DM_ATTRIBS) 
-   LD (DS_ATTRIBS), HL       ; Establecer tileset (atributos) +    ld (DS_ATTRIBS), hl       ; Establecer tileset (atributos) 
-   LD BC, (DM_COORD_X)       ; B = Y_INICIO, C = X_INICIO +    ld bc, (DM_COORD_X)       ; B = Y_INICIO, C = X_INICIO 
-   LD DE, (DM_MAP)           ; DE apunta al mapa+    ld de, (DM_MAP)           ; DE apunta al mapa
  
-   LD HL, DS_COORD_Y         ; CAMBIO: Ahora HL apunta a la variable Y+    ld hl, DS_COORD_Y         ; CAMBIO: Ahora HL apunta a la variable Y
  
 drawm16cv_read: drawm16cv_read:
-   LD A, (DE)                ; Leemos el valor de COORD_X_TILE +    ld a, (de)                ; Leemos el valor de COORD_X_TILE 
-   INC DE                    ; Apuntamos al siguiente byte del mapa+    inc de                    ; Apuntamos al siguiente byte del mapa
  
 drawm16cv_loop: drawm16cv_loop:
-   CP 255                    ; Bloque especial fin de pantalla +    cp 255                    ; Bloque especial fin de pantalla 
-   RET Z                     ; En ese caso, salir+    ret z                     ; En ese caso, salir
  
-   ADD A                 ; A = (X_INICIO + COORD_X_TILE) +    add a                 ; A = (X_INICIO + COORD_X_TILE) 
-   RLCA                      ; A = (X_INICIO + COORD_X_TILE) * 2 +    rlca                      ; A = (X_INICIO + COORD_X_TILE) * 2 
-   LD (DS_COORD_X),        ; CAMBIO: Establecemos COORD_X a imprimir tile+    ld (DS_COORD_X),        ; CaMBIO: Establecemos COORD_X a imprimir tile
  
-   LD A, (DE)                ; Leemos el valor de COORD_Y_TILE +    ld a, (de)                ; Leemos el valor de COORD_Y_TILE 
-   INC DE +    inc de 
-   ADD A                 ; A = (Y_INICIO + COORD_Y_TILE) +    add a                 ; A = (Y_INICIO + COORD_Y_TILE) 
-   RLCA                      ; A = (Y_INICIO + COORD_Y_TILE) +    rlca                      ; A = (Y_INICIO + COORD_Y_TILE) 
-   LD (HL),                ; CAMBIO: Establecemos COORD_Y a imprimir tile+    ld (hl),                ; CAMBIO: Establecemos COORD_Y a imprimir tile
  
-   ;;; Bucle impresion de todos los tiles del scanline (aunque sea 1 solo)+    ;;; Bucle impresion de todos los tiles del scanline (aunque sea 1 solo)
  
 drawm16cv_tileloop: drawm16cv_tileloop:
  
-   LD A, (DE)                ; Leemos el valor del TILE de pantalla +    ld a, (de)                ; Leemos el valor del TILE de pantalla 
-   INC DE                    ; Incrementamos puntero+    inc de                    ; Incrementamos puntero
  
-   CP 255 +    cp 255 
-   JR Z, drawm16cv_read      ; Si es fin de tile codificado, fin bucle+    jr z, drawm16cv_read      ; Si es fin de tile codificado, fin bucle
  
-   LD (DS_NUMSPR),         ; Establecemos el TILE+    ld (DS_NUMSPR),         ; Establecemos el TILE
  
-   EXX                       ; Preservar todos los registros en shadows +    exx                       ; Preservar todos los registros en shadows 
-   CALL DrawSprite_16x16_LD  ; Imprimir el tile con los parametros +    call DrawSprite_16x16_LD  ; Imprimir el tile con los parametros 
-   EXX                       ; Recuperar valores de los registros+    exx                       ; Recuperar valores de los registros
  
-   INC (HL+    inc (hl
-   INC (HL)                  ; COORD_Y = COORD_Y + 2+    inc (hl)                  ; COORD_Y = COORD_Y + 2
  
-   JR drawm16cv_tileloop     ; Repetimos hasta encontrar el 255+    jr drawm16cv_tileloop     ; Repetimos hasta encontrar el 255
 </code> </code>
  
-\\ +\\  
-\\+\\ 
 **Mapeado diferencial con agrupación mixta** **Mapeado diferencial con agrupación mixta**
-\\+\\ 
  
  Finalmente, cabe la posibilidad de que en una misma pantalla nos encontremos "filas" de tiles y "columnas" de tiles que convenga codificar con un método u otro.  Finalmente, cabe la posibilidad de que en una misma pantalla nos encontremos "filas" de tiles y "columnas" de tiles que convenga codificar con un método u otro.
Línea 2390: Línea 2387:
  Como desventaja, nuestro tileset sólo puede contener ahora 254 tiles (0-253).  Como desventaja, nuestro tileset sólo puede contener ahora 254 tiles (0-253).
  
- Nuestro script de conversión en Python, con el flag "**-m**", calcula todas las posibilidades de codificación de cada tile y los procesa en orden de mayor a menor cantidad de tiles agrupados para codificar toda la pantalla en un formato mixto donde cada ristra de datos de la pantalla tendría el siguiente aspecto:+ Nuestro script de conversión en Python, con el flag ''-m'', calcula todas las posibilidades de codificación de cada tile y los procesa en orden de mayor a menor cantidad de tiles agrupados para codificar toda la pantalla en un formato mixto donde cada ristra de datos de la pantalla tendría el siguiente aspecto:
  
 <code z80> <code z80>
 Pantalla: Pantalla:
-  DB coordenada_x_primer_tile_horiz, coordenada_y_primer_tile_horiz, +    DB coordenada_x_primer_tile_horiz, coordenada_y_primer_tile_horiz, 
-  DB tile1, tile2, (...), tileN, 255 +    DB tile1, tile2, (...), tileN, 255 
-  DB coordenada_x_primer_tile_horiz, coordenada_y_primer_tile_horiz, +    DB coordenada_x_primer_tile_horiz, coordenada_y_primer_tile_horiz, 
-  DB tile1, tile2, (...), tileN, 254 +    DB tile1, tile2, (...), tileN, 254 
-  DB coordenada_x_primer_tile_vert, coordenada_y_primer_tile_vert, +    DB coordenada_x_primer_tile_vert, coordenada_y_primer_tile_vert, 
-  DB tile1, tile2, (...), tileN, 255 +    DB tile1, tile2, (...), tileN, 255 
-  DB coordenada_x_primer_tile_vert, coordenada_y_primer_tile_vert, +    DB coordenada_x_primer_tile_vert, coordenada_y_primer_tile_vert, 
-  DB tile1, tile2, (...), tileN, 255 +    DB tile1, tile2, (...), tileN, 255 
-  DB 255+    DB 255
  
-  ; Donde: +    ; Donde: 
-  ; 255 como coordenada_x = fin de pantalla. +    ; 255 como coordenada_x = fin de pantalla. 
-  ; 255 a final de scanline = byte de fin de "scanline" +    ; 255 a final de scanline = byte de fin de "scanline" 
-  ; 254 a final de scanline = cambio de codificacion de horizontal a vertical+    ; 254 a final de scanline = cambio de codificacion de horizontal a vertical
 </code> </code>
  
Línea 2434: Línea 2431:
  Concretamente, tomamos como base la rutina de impresión de scanlines horizontales y utilizamos un sencillo truco para convertirla en la rutina de impresión de scanlines verticales. Lo haremos mediante **código automodificable** (self-modifying code).  Concretamente, tomamos como base la rutina de impresión de scanlines horizontales y utilizamos un sencillo truco para convertirla en la rutina de impresión de scanlines verticales. Lo haremos mediante **código automodificable** (self-modifying code).
  
- Primero, cargamos en HL la dirección de la variable COORD_X y en DE la dirección de la variable COORD_Y:+ Primero, cargamos en HL la dirección de la variable ''COORD_X'' y en DE la dirección de la variable ''COORD_Y'':
  
 <code z80> <code z80>
-   LD DE, DS_COORD_Y         ; DE apunta a la coordenada Y +    ld de, DS_COORD_Y         ; DE apunta a la coordenada Y 
-   LD HL, DS_COORD_X         ; HL apunta a la coordenada X+    ld hl, DS_COORD_X         ; HL apunta a la coordenada X
 </code> </code>
  
- Despues, incluímos 2 instrucciones **NOP** (de 1 byte, con opcode 0), antes y después de los **INC (HL)** que producen el incremento de la coordenada apuntada por HL (COORD_X). Además, establecemos 2 etiquetas del programa ensamblador en las posiciones de los 2 NOPs para poder referenciar la dirección de memoria donde se han ensamblado estos NOPs en el código:+ Despues, incluímos 2 instrucciones ''NOP'' (de 1 byte, con opcode 0), antes y después de los ''inc (hl)'' que producen el incremento de la coordenada apuntada por HL (''COORD_X''). Además, establecemos 2 etiquetas del programa ensamblador en las posiciones de los 2 NOPs para poder referenciar la dirección de memoria donde se han ensamblado estos NOPs en el código:
  
 <code z80> <code z80>
 drawm16cm_dir1: drawm16cm_dir1:
-   NOP +    nop 
-   INC (HL+    inc (hl
-   INC (HL)                  ; COORD = COORD + 2+    inc (hl)                  ; COORD = COORD + 2
 drawm16cm_dir2: drawm16cm_dir2:
-   NOP+    nop
 </code> </code>
  
- Cuando entramos por primera vez en la rutina, y comenzamos a procesar "scanlines", sabemos que son todos horizontales hasta que encontremos el valor "254" (cambio de horizontales a verticales), por lo que nuestra rutina de impresión de tiles en bucle horizontal funciona adecuadamente: se ejecuta un NOP (que no tiene ningún efecto salvo el consumo de 4 ciclos de reloj), después los 2 INC (HL) y luego otro NOP, lo que produce el incremento de COORD_X en 2 unidades (HL apunta a COORD_X).+ Cuando entramos por primera vez en la rutina, y comenzamos a procesar "scanlines", sabemos que son todos horizontales hasta que encontremos el valor "254" (cambio de horizontales a verticales), por lo que nuestra rutina de impresión de tiles en bucle horizontal funciona adecuadamente: se ejecuta un nop (que no tiene ningún efecto salvo el consumo de 4 ciclos de reloj), después los 2 ''inc (hl)'' y luego otro ''NOP'', lo que produce el incremento de ''COORD_X'' en 2 unidades (HL apunta a ''COORD_X'').
  
  Cuando la rutina encuentra un valor 254 como "fin de scanline" debe de cambiar al modo de impresión vertical, por lo que dentro del bucle de procesado del scanline añadimos el siguiente código:  Cuando la rutina encuentra un valor 254 como "fin de scanline" debe de cambiar al modo de impresión vertical, por lo que dentro del bucle de procesado del scanline añadimos el siguiente código:
Línea 2459: Línea 2456:
 drawm16cm_tileloop: drawm16cm_tileloop:
  
-   (...)+    (...)
  
-   CP 254 +    cp 254 
-   JR Z, drawm16cm_switch    ; Codigo 254 -> cambiar a codif. vertical+    jr z, drawm16cm_switch    ; Codigo 254 -> cambiar a codif. vertical
  
-   (...)+    (...)
  
 drawm16cm_switch: drawm16cm_switch:
-   ;;; Cambio de codificacion de horizontal a vertical: +    ;;; Cambio de codificacion de horizontal a vertical: 
-   LD A, $EB                 ; Opcode de EX DEHL +    ld a, $eb                 ; Opcode de ex dehl 
-   LD (drawm16cm_dir1),    ; Lo escribimos sobre los NOPs +    ld (drawm16cm_dir1),    ; Lo escribimos sobre los NOPs 
-   LD (drawm16cm_dir2), A +    ld (drawm16cm_dir2), a 
-   JR drawm16cm_read+    jr drawm16cm_read
 </code> </code>
  
- Es decir, cuando se encuentra un valor 254 como fin de scanline saltamos a drawm16cm_switch, la cual escribe un valor **$EB** (EX DEHL) en las posiciones de memoria donde antes había un NOP, cambiando la porción de código que habíamos visto antes por:+ Es decir, cuando se encuentra un valor 254 como fin de scanline saltamos a drawm16cm_switch, la cual escribe un valor **$eb** (''ex dehl'') en las posiciones de memoria donde antes había un nop, cambiando la porción de código que habíamos visto antes por:
  
 <code z80> <code z80>
 drawm16cm_dir1: drawm16cm_dir1:
-   EX DEHL +    ex dehl 
-   INC (HL+    inc (hl
-   INC (HL)                  ; COORD = COORD + 2+    inc (hl)                  ; COORD = COORD + 2
 drawm16cm_dir2: drawm16cm_dir2:
-   EX DEHL+    ex dehl
 </code> </code>
  
- Esto provoca que, a partir de haber encontrado el 254 y hasta que finalice la rutina (código 255 de fin de pantalla), los INC (HL) incrementen COORD_Y (debido al EX DEHL) en lugar de COORD_X, convirtiendo la rutina en un sistema de impresión de scanlines horizontales.+ Esto provoca que, a partir de haber encontrado el 254 y hasta que finalice la rutina (código 255 de fin de pantalla), los inc (hl) incrementen ''COORD_Y'' (debido al ''ex dehl'') en lugar de ''COORD_X'', convirtiendo la rutina en un sistema de impresión de scanlines horizontales.
  
- Cuando salimos de la rutina, esta se queda con los valores de "EX DEHL" en memoria, por lo que la siguiente vez que sea llamada tenemos que asegurarnos de que vuelven a estar los NOPs en su lugar, porque las pantallas siempre empiezan por scanlines horizontales. Para lograr esto, nuestra rutina debe empezar por la colocación del "NOPen las direcciones apuntadas por las etiquetas:+ Cuando salimos de la rutina, esta se queda con los valores de ''ex dehl'' en memoria, por lo que la siguiente vez que sea llamada tenemos que asegurarnos de que vuelven a estar los NOPs en su lugar, porque las pantallas siempre empiezan por scanlines horizontales. Para lograr esto, nuestra rutina debe empezar por la colocación del ''NOP'' en las direcciones apuntadas por las etiquetas:
  
 <code z80> <code z80>
 DrawMap_16x16_Cod_Mixta: DrawMap_16x16_Cod_Mixta:
  
-   XOR A                     ; Opcode de "NOP+    xor a                     ; Opcode de "nop
-   LD (drawm16cm_dir1),    ; Almacenar en la posicion de las labels +    ld (drawm16cm_dir1),    ; almacenar en la posicion de las labels 
-   LD (drawm16cm_dir2), A+    ld (drawm16cm_dir2), a
 </code> </code>
  
Línea 2517: Línea 2514:
 DrawMap_16x16_Cod_Mixta: DrawMap_16x16_Cod_Mixta:
  
-   XOR A                     ; Opcode de "NOP+    xor a                     ; Opcode de "nop
-   LD (drawm16cm_dir1),    ; Almacenar en la posicion de labels +    ld (drawm16cm_dir1),    ; almacenar en la posicion de labels 
-   LD (drawm16cm_dir2), A+    ld (drawm16cm_dir2), a
  
-   LD HL, (DM_SPRITES) +    ld hl, (DM_SPRITES) 
-   LD (DS_SPRITES), HL       ; Establecer tileset (graficos) +    ld (DS_SPRITES), hl       ; Establecer tileset (graficos) 
-   LD HL, (DM_ATTRIBS) +    ld hl, (DM_ATTRIBS) 
-   LD (DS_ATTRIBS), HL       ; Establecer tileset (atributos) +    ld (DS_ATTRIBS), hl       ; Establecer tileset (atributos) 
-   LD BC, (DM_COORD_X)       ; B = Y_INICIO, C = X_INICIO +    ld bc, (DM_COORD_X)       ; B = Y_INICIO, C = X_INICIO 
-   LD IX, (DM_MAP)           ; DE apunta al mapa+    ld ix, (DM_MAP)           ; DE apunta al mapa
  
-   LD DE, DS_COORD_Y         ; DE apunta a la coordenada Y +    ld de, DS_COORD_Y         ; DE apunta a la coordenada Y 
-   LD HL, DS_COORD_X         ; HL apunta a la coordenada X+    ld hl, DS_COORD_X         ; HL apunta a la coordenada X
  
 drawm16cm_read: drawm16cm_read:
-   LD A, (IX+0)              ; Leemos el valor de COORD_X_TILE +    ld a, (ix+0)              ; Leemos el valor de COORD_X_TILE 
-   INC IX                    ; Apuntamos al siguiente byte del mapa+    inc ix                    ; Apuntamos al siguiente byte del mapa
  
 drawm16cm_loop: drawm16cm_loop:
-   CP 255                    ; Bloque especial fin de pantalla +    cp 255                    ; Bloque especial fin de pantalla 
-   RET Z                     ; En ese caso, salir+    ret z                     ; En ese caso, salir
  
-   ADD A                 ; A = (X_INICIO + COORD_X_TILE) +    add a                 ; A = (X_INICIO + COORD_X_TILE) 
-   RLCA                      ; A = (X_INICIO + COORD_X_TILE) * 2 +    rlca                      ; A = (X_INICIO + COORD_X_TILE) * 2 
-   LD (HL),                ; Establecemos COORD_X a imprimir tile+    ld (hl),                ; Establecemos COORD_X a imprimir tile
  
-   LD A, (IX+0)              ; Leemos el valor de COORD_Y_TILE +    ld a, (ix+0)              ; Leemos el valor de COORD_Y_TILE 
-   INC IX +    inc ix 
-   ADD A                 ; A = (Y_INICIO + COORD_Y_TILE) +    add a                 ; A = (Y_INICIO + COORD_Y_TILE) 
-   RLCA                      ; A = (Y_INICIO + COORD_Y_TILE) +    rlca                      ; A = (Y_INICIO + COORD_Y_TILE) 
-   LD (DE),                ; Establecemos COORD_Y a imprimir tile+    ld (de),                ; Establecemos COORD_Y a imprimir tile
  
-   ;;; Bucle impresion vertical de los N tiles del scanline+    ;;; Bucle impresion vertical de los N tiles del scanline
 drawm16cm_tileloop: drawm16cm_tileloop:
-   LD A, (IX+0)              ; Leemos el valor del TILE de pantalla +    ld a, (ix+0)              ; Leemos el valor del TILE de pantalla 
-   INC IX                    ; Incrementamos puntero+    inc ix                    ; Incrementamos puntero
  
-   CP 255 +    cp 255 
-   JR Z, drawm16cm_read      ; Si es fin de tile codificado, fin bucle+    jr z, drawm16cm_read      ; Si es fin de tile codificado, fin bucle
  
-   CP 254 +    cp 254 
-   JR Z, drawm16cm_switch    ; Codigo 254 -> cambiar a codif. vertical+    jr z, drawm16cm_switch    ; Codigo 254 -> cambiar a codif. vertical
  
-   LD (DS_NUMSPR),         ; Establecemos el TILE +    ld (DS_NUMSPR),         ; Establecemos el TILE 
-   EXX                       ; Preservar todos los registros en shadows +    exx                       ; Preservar todos los registros en shadows 
-   CALL DrawSprite_16x16_LD  ; Imprimir el tile con los parametros +    call DrawSprite_16x16_LD  ; Imprimir el tile con los parametros 
-   EXX                       ; Recuperar valores de los registros+    exx                       ; Recuperar valores de los registros
  
-drawm16cm_dir1:              ; Etiqueta con la direccion del NOP +drawm16cm_dir1:               ; Etiqueta con la direccion del nop 
-   NOP                       ; NOP->INC COORD_X,  EX DE,HL->INC COORD_Y+    nop                       ; nop->inc cOORD_X,  ex de,hl->inc cOORD_Y
  
-   INC (HL+    inc (hl
-   INC (HL)                  ; COORD = COORD + 2+    inc (hl)                  ; COORD = COORD + 2
  
-drawm16cm_dir2:              ; Etiqueta con la direccion del NOP +drawm16cm_dir2:               ; Etiqueta con la direccion del nop 
-   NOP                       ; NOP->INC COORD_X,  EX DE,HL->INC COORD_Y+    nop                       ; nop->inc cOORD_X,  ex de,hl->inc cOORD_Y
  
-   JR drawm16cm_tileloop     ; Repetimos hasta encontrar el 255+    jr drawm16cm_tileloop     ; Repetimos hasta encontrar el 255
  
 drawm16cm_switch: drawm16cm_switch:
-   ;;; Cambio de codificacion de horizontal a vertical: +    ;;; Cambio de codificacion de horizontal a vertical: 
-   LD A, $EB                 ; Opcode de EX DEHL +    ld a, $eb                 ; Opcode de ex dehl 
-   LD (drawm16cm_dir1),    ; Ahora se hace el EX DEHL y por lo tanto +    ld (drawm16cm_dir1),    ; ahora se hace el ex dehl y por lo tanto 
-   LD (drawm16cm_dir2),    ; INC (HL) incrementa COORD_Y en vez de X +    ld (drawm16cm_dir2),    ; inc (hl) incrementa COORD_Y en vez de X 
-   JR drawm16cm_read         ; Volvemos al bucle de lectura+    jr drawm16cm_read         ; Volvemos al bucle de lectura
 </code> </code>
  
Línea 2589: Línea 2586:
  
  
-\\ +\\  
-\\+\\ 
 **Codificar X e Y en un único byte** **Codificar X e Y en un único byte**
-\\ +\\  
-\\+\\ 
  
  Veamos una modificación del script/programa de codificación y de la rutinas de impresión que nos van a resultar realmente útiles siempre que nuestro mapa no esté formado por tiles de 8x8 píxeles.  Veamos una modificación del script/programa de codificación y de la rutinas de impresión que nos van a resultar realmente útiles siempre que nuestro mapa no esté formado por tiles de 8x8 píxeles.
Línea 2606: Línea 2603:
  
 <code z80> <code z80>
-  mapa_codificado.append( X ) +mapa_codificado.append( X ) 
-  mapa_codificado.append( Y )+mapa_codificado.append( Y )
 </code> </code>
  
Línea 2613: Línea 2610:
  
 <code z80> <code z80>
-  mapa_codificado.append( (X * 16) + Y )+mapa_codificado.append( (X * 16) + Y )
 </code> </code>
  
- Aunque hemos dicho que el tamaño máximo del mapa será de 16x16, la realidad es que puede ser como máximo de 16x14, ya que si ambas coordenadas X e Y valen 15 ($F), el byte resultante compuesto sería $FF que es el código de final de pantalla. De la misma forma, si X vale 15 e Y vale 14, el valor resultante, $FE, sería confundido por la rutina mixta con el código especial de cambio de scanlines horizontales a verticales.+ Aunque hemos dicho que el tamaño máximo del mapa será de 16x16, la realidad es que puede ser como máximo de 16x14, ya que si ambas coordenadas X e Y valen 15 ($f), el byte resultante compuesto sería $fF que es el código de final de pantalla. De la misma forma, si X vale 15 e Y vale 14, el valor resultante, $fE, sería confundido por la rutina mixta con el código especial de cambio de scanlines horizontales a verticales.
  
  Así, con un mapa de 16x14, el máximo tamaño de pantalla que podemos ocupar según las dimensiones de cada tile serían:  Así, con un mapa de 16x14, el máximo tamaño de pantalla que podemos ocupar según las dimensiones de cada tile serían:
Línea 2628: Línea 2625:
  Las dimensiones que podemos ver en la tabla hacen esta optimización inusable para juegos con tiles de 8x8 pixeles.  Las dimensiones que podemos ver en la tabla hacen esta optimización inusable para juegos con tiles de 8x8 pixeles.
  
- Por otra parte, recordemos que debemos modificar la rutina para que separe los bytes de COORD_X y COORD_Y en 2 valores diferentes, lo que supone un pequeño tiempo adicional de procesado por cada scanline.+ Por otra parte, recordemos que debemos modificar la rutina para que separe los bytes de ''COORD_X'' ''COORD_Y'' en 2 valores diferentes, lo que supone un pequeño tiempo adicional de procesado por cada scanline.
  
- Nuestra pantalla de pruebas es de 16x12, por lo que podemos perfectamente codificarla con esta técnica, utilizando el flag **-a**, además del tipo de codificación:+ Nuestra pantalla de pruebas es de 16x12, por lo que podemos perfectamente codificarla con esta técnica, utilizando el flag ''-a'', además del tipo de codificación:
  
 <code z80> <code z80>
Línea 2650: Línea 2647:
  
 <code z80> <code z80>
-   ;;; (venimos del LD A, (IX+0) / INC IX de la coordenada X)+    ;;; (venimos del ld a, (ix+0) / inc ix de la coordenada X)
  
-   ;;; Sumamos la coordenada X recogida y obtenemos desde el mapa la Y: +    ;;; Sumamos la coordenada X recogida y obtenemos desde el mapa la Y: 
-   ADD A                 ; A = (X_INICIO + COORD_X_TILE) +    add a                 ; A = (X_INICIO + COORD_X_TILE) 
-   RLCA                      ; A = (X_INICIO + COORD_X_TILE) * 2 +    rlca                      ; A = (X_INICIO + COORD_X_TILE) * 2 
-   LD (HL),                ; Establecemos COORD_X a imprimir tile+    ld (hl),                ; Establecemos COORD_X a imprimir tile
  
-   LD A, (IX+0)              ; Leemos el valor de COORD_Y_TILE +    ld a, (ix+0)              ; Leemos el valor de COORD_Y_TILE 
-   INC IX +    inc ix 
-   ADD A                 ; A = (Y_INICIO + COORD_Y_TILE) +    add a                 ; A = (Y_INICIO + COORD_Y_TILE) 
-   RLCA                      ; A = (Y_INICIO + COORD_Y_TILE) +    rlca                      ; A = (Y_INICIO + COORD_Y_TILE) 
-   LD (DE),                ; Establecemos COORD_Y a imprimir tile+    ld (de),                ; Establecemos COORD_Y a imprimir tile
 </code> </code>
  
Línea 2667: Línea 2664:
  
 <code z80> <code z80>
-   PUSH AF +    push af 
-   AND %11110000             ; Nos quedamos con la parte alta (COORD_X) +    and %11110000             ; Nos quedamos con la parte alta (COORD_X) 
-   RRCA                      ; Pasamos parte alta a parte baja +    rrca                      ; Pasamos parte alta a parte baja 
-   RRCA                      ; con 4 desplazamientos +    rrca                      ; con 4 desplazamientos 
-   RRCA +    rrca 
-   RRCA                      ; Ya podemos sumar: +    rrca                      ; Ya podemos sumar: 
-   ADD A                 ; A = (X_INICIO + COORD_X_TILE) +    add a                 ; A = (X_INICIO + COORD_X_TILE) 
-   RLCA                      ; A = (X_INICIO + COORD_X_TILE) * 2 +    rlca                      ; A = (X_INICIO + COORD_X_TILE) * 2 
-   LD (HL),                ; Establecemos COORD_X a imprimir tile+    ld (hl),                ; Establecemos COORD_X a imprimir tile
  
-   POP AF +    pop af 
-   AND %00001111             ; Nos quedamos con la parte baja (COORD_Y)+    and %00001111             ; Nos quedamos con la parte baja (COORD_Y)
  
-   ADD A                 ; A = (Y_INICIO + COORD_Y_TILE) +    add a                 ; A = (Y_INICIO + COORD_Y_TILE) 
-   RLCA                      ; A = (Y_INICIO + COORD_Y_TILE) +    rlca                      ; A = (Y_INICIO + COORD_Y_TILE) 
-   LD (DE),                ; Establecemos COORD_Y a imprimir tile+    ld (de),                ; Establecemos COORD_Y a imprimir tile
 </code> </code>
  
- De la rutina original hemos eliminado las instrucciones "**LD A, (IX+0)**" "**INC IX**" (29 ciclos de reloj menos) y hemos añadido PUSH/POP AF e instrucciones AND y RLCA (51 ciclos de reloj más) resultando en una rutina que es 22 ciclos de reloj más lenta por scanline. Pero a cambio de estos 22 ciclos de reloj se pueden producir grandes ahorros en las pantallas resultantes.+ De la rutina original hemos eliminado las instrucciones ''ld a, (ix+0)'' ''inc ix'' (29 ciclos de reloj menos) y hemos añadido ''push af''/''pop af'' e instrucciones ''AND'' ''RLCA'' (51 ciclos de reloj más) resultando en una rutina que es 22 ciclos de reloj más lenta por scanline. Pero a cambio de estos 22 ciclos de reloj se pueden producir grandes ahorros en las pantallas resultantes.
  
  La rutina de impresión de 16x16 Mixta con coordenadas X e Y codificadas en un mismo byte quedaría como el código que sigue:  La rutina de impresión de 16x16 Mixta con coordenadas X e Y codificadas en un mismo byte quedaría como el código que sigue:
Línea 2702: Línea 2699:
 DrawMap_16x16_Cod_Mixta_XY: DrawMap_16x16_Cod_Mixta_XY:
  
-   XOR A                     ; Opcode de "NOP+    xor a                     ; Opcode de "nop
-   LD (drawm16cmxy_dir1),  ; Almacenar en la posicion de las labels +    ld (drawm16cmxy_dir1),  ; almacenar en la posicion de las labels 
-   LD (drawm16cmxy_dir2), A +    ld (drawm16cmxy_dir2), a 
-   LD HL, (DM_SPRITES) +    ld hl, (DM_SPRITES) 
-   LD (DS_SPRITES), HL       ; Establecer tileset (graficos) +    ld (DS_SPRITES), hl       ; Establecer tileset (graficos) 
-   LD HL, (DM_ATTRIBS) +    ld hl, (DM_ATTRIBS) 
-   LD (DS_ATTRIBS), HL       ; Establecer tileset (atributos) +    ld (DS_ATTRIBS), hl       ; Establecer tileset (atributos) 
-   LD BC, (DM_COORD_X)       ; B = Y_INICIO, C = X_INICIO +    ld bc, (DM_COORD_X)       ; B = Y_INICIO, C = X_INICIO 
-   LD IX, (DM_MAP)           ; DE apunta al mapa+    ld ix, (DM_MAP)           ; DE apunta al mapa
  
-   LD DE, DS_COORD_Y         ; DE apunta a la coordenada Y +    ld de, DS_COORD_Y         ; DE apunta a la coordenada Y 
-   LD HL, DS_COORD_X         ; HL apunta a la coordenada X+    ld hl, DS_COORD_X         ; HL apunta a la coordenada X
  
 drawm16cmxy_read: drawm16cmxy_read:
-   LD A, (IX+0)              ; Leemos el valor de COORDENADAS +    ld a, (ix+0)              ; Leemos el valor de COORDENADAS 
-   INC IX                    ; Apuntamos al siguiente byte del mapa+    inc ix                    ; Apuntamos al siguiente byte del mapa
  
 drawm16cmxy_loop: drawm16cmxy_loop:
-   CP 255                    ; Bloque especial fin de pantalla +    cp 255                    ; Bloque especial fin de pantalla 
-   RET Z                     ; En ese caso, salir+    ret z                     ; En ese caso, salir
  
-   PUSH AF                   ; Extraccion de coordenadas XY en X e Y +    push af                   ; Extraccion de coordenadas XY en X e Y 
-   AND %11110000             ; Nos quedamos con la parte alta (COORD_X) +    and %11110000             ; Nos quedamos con la parte alta (COORD_X) 
-   RRCA                      ; Pasamos parte alta a parte baja +    rrca                      ; Pasamos parte alta a parte baja 
-   RRCA                      ; con 4 desplazamientos +    rrca                      ; con 4 desplazamientos 
-   RRCA +    rrca 
-   RRCA                      ; Ya podemos sumar +    rrca                      ; Ya podemos sumar 
-   ADD A                 ; A = (X_INICIO + COORD_X_TILE) +    add a                 ; A = (X_INICIO + COORD_X_TILE) 
-   RLCA                      ; A = (X_INICIO + COORD_X_TILE) * 2 +    rlca                      ; A = (X_INICIO + COORD_X_TILE) * 2 
-   LD (HL),                ; Establecemos COORD_X a imprimir tile +    ld (hl),                ; Establecemos COORD_X a imprimir tile 
-   POP AF +    pop af 
-   AND %00001111             ; Nos quedamos con la parte baja (COORD_Y) +    and %00001111             ; Nos quedamos con la parte baja (COORD_Y) 
-   ADD A                 ; A = (Y_INICIO + COORD_Y_TILE) +    add a                 ; A = (Y_INICIO + COORD_Y_TILE) 
-   RLCA                      ; A = (Y_INICIO + COORD_Y_TILE) +    rlca                      ; A = (Y_INICIO + COORD_Y_TILE) 
-   LD (DE),                ; Establecemos COORD_Y a imprimir tile+    ld (de),                ; Establecemos COORD_Y a imprimir tile
  
    ;;; Bucle impresion vertical de los N tiles del scanline    ;;; Bucle impresion vertical de los N tiles del scanline
 drawm16cmxy_tileloop: drawm16cmxy_tileloop:
-   LD A, (IX+0)              ; Leemos el valor del TILE de pantalla +    ld a, (ix+0)              ; Leemos el valor del TILE de pantalla 
-   INC IX                    ; Incrementamos puntero+    inc ix                    ; Incrementamos puntero
  
-   CP 255 +    cp 255 
-   JR Z, drawm16cmxy_read    ; Si es fin de tile codificado, fin bucle +    jr z, drawm16cmxy_read    ; Si es fin de tile codificado, fin bucle 
-   CP 254 +    cp 254 
-   JR Z, drawm16cmxy_switch  ; Codigo 254 -> cambiar a codif. vertical+    jr z, drawm16cmxy_switch  ; Codigo 254 -> cambiar a codif. vertical
  
-   LD (DS_NUMSPR),         ; Establecemos el TILE +    ld (DS_NUMSPR),         ; Establecemos el TILE 
-   EXX                       ; Preservar todos los registros en shadows +    exx                       ; Preservar todos los registros en shadows 
-   CALL DrawSprite_16x16_LD  ; Imprimir el tile con los parametros +    call DrawSprite_16x16_LD  ; Imprimir el tile con los parametros 
-   EXX                       ; Recuperar valores de los registros+    exx                       ; Recuperar valores de los registros
  
-drawm16cmxy_dir1:            ; Etiqueta con la direccion del NOP +drawm16cmxy_dir1:            ; Etiqueta con la direccion del nop 
-   NOP                       ; NOP->INC COORD_X,  EX DE,HL->INC COORD_Y +    nop                       ; nop->inc cOORD_X,  ex de,hl->inc cOORD_Y 
-   INC (HL+    inc (hl
-   INC (HL)                  ; COORD = COORD + 2 +    inc (hl)                  ; COORD = COORD + 2 
-drawm16cmxy_dir2:            ; Etiqueta con la direccion del NOP +drawm16cmxy_dir2:            ; Etiqueta con la direccion del nop 
-   NOP                       ; NOP->INC COORD_X,  EX DE,HL->INC COORD_Y +    nop                       ; nop->inc cOORD_X,  ex de,hl->inc cOORD_Y 
-   JR drawm16cmxy_tileloop   ; Repetimos hasta encontrar el 255+    jr drawm16cmxy_tileloop   ; Repetimos hasta encontrar el 255
  
 drawm16cmxy_switch: drawm16cmxy_switch:
-   ;;; Cambio de codificacion de horizontal a vertical: +    ;;; Cambio de codificacion de horizontal a vertical: 
-   LD A, $EB                 ; Opcode de EX DEHL +    ld a, $eb                 ; Opcode de ex dehl 
-   LD (drawm16cmxy_dir1),    ; Ahora se hace el EX DEHL y por lo tanto +    ld (drawm16cmxy_dir1),    ; ahora se hace el ex dehl y por lo tanto 
-   LD (drawm16cmxy_dir2),    ; INC (HL) incrementa COORD_Y en vez de X +    ld (drawm16cmxy_dir2),    ; inc (hl) incrementa COORD_Y en vez de X 
-   JR drawm16cmxy_read+    jr drawm16cmxy_read
 </code> </code>
  
  
-\\ +\\  
-\\+\\ 
 **Codificar 2 tiles en un mismo byte** **Codificar 2 tiles en un mismo byte**
-\\ +\\  
-\\+\\ 
  
  Si tenemos menos de 16 tiles podemos codificar 2 tiles en un mismo byte, suponiendo un ahorro de memoria de un 50%. Esto limita mucho la riqueza gráfica del juego resultante a menos que dispongamos de diferentes tilesets gráficos y que cada pantalla pueda tener asociado un set diferente, lo que nos limitaría de forma efectiva a 16 tiles diferentes **por pantalla**.  Si tenemos menos de 16 tiles podemos codificar 2 tiles en un mismo byte, suponiendo un ahorro de memoria de un 50%. Esto limita mucho la riqueza gráfica del juego resultante a menos que dispongamos de diferentes tilesets gráficos y que cada pantalla pueda tener asociado un set diferente, lo que nos limitaría de forma efectiva a 16 tiles diferentes **por pantalla**.
Línea 2781: Línea 2778:
  
  
-\\ +\\  
-\\+\\ 
 **Mapeado diferencial con diferentes codificaciones** **Mapeado diferencial con diferentes codificaciones**
-\\ +\\  
-\\+\\ 
  
  Como para cada pantalla puede ser más apropiado un tipo de codificación que otro, podemos codificar cada una de ellas con el método que resulte más adecuado y alterar nuestro el mapa o la propia pantalla para que almacene también la información de tipo de codificación.  Como para cada pantalla puede ser más apropiado un tipo de codificación que otro, podemos codificar cada una de ellas con el método que resulte más adecuado y alterar nuestro el mapa o la propia pantalla para que almacene también la información de tipo de codificación.
Línea 2791: Línea 2788:
  La forma más sencilla es que el primer byte de la pantalla contenga el tipo de codificación utilizado.  La forma más sencilla es que el primer byte de la pantalla contenga el tipo de codificación utilizado.
  
- Una rutina "wrapper" (o "envoltorio") de impresión de pantalla obtendría del mapa la dirección de la pantalla en HL o DE, leería el tipo de codificación utilizado (primer byte de la pantalla), incrementaría HL o DE, almacenaría el valor resultante en (DM_MAP) y llamaría a la función de impresión adecuada según el tipo de codificación que acabamos de leer.+ Una rutina "wrapper" (o "envoltorio") de impresión de pantalla obtendría del mapa la dirección de la pantalla en HL o DE, leería el tipo de codificación utilizado (primer byte de la pantalla), incrementaría HL o DE, almacenaría el valor resultante en (''DM_MAP'') y llamaría a la función de impresión adecuada según el tipo de codificación que acabamos de leer.
  
  Como ID de codificación se podría utilizar, por ejemplo:  Como ID de codificación se podría utilizar, por ejemplo:
Línea 2803: Línea 2800:
 </code> </code>
  
- Es difícil que la codificación básica sea más óptima que ninguna de las anteriores a menos que apenas haya bloques "transparentes" o "vacíos", pero aún así se ha contemplado su uso en la rutina. Hemos incluído también la posibilidad de utilizar una pantalla sin agrupación indicando MAP_CODIF_NONE al inicio de la misma.+ Es difícil que la codificación básica sea más óptima que ninguna de las anteriores a menos que apenas haya bloques "transparentes" o "vacíos", pero aún así se ha contemplado su uso en la rutina. Hemos incluído también la posibilidad de utilizar una pantalla sin agrupación indicando ''MAP_CODIF_NONE'' al inicio de la misma.
  
 <code z80> <code z80>
Línea 2811: Línea 2808:
 ;--------------------------------------------------------------- ;---------------------------------------------------------------
 DrawMap_16x16_Codificada: DrawMap_16x16_Codificada:
-   LD HL, (DM_MAP)            ; HL apunta al mapa +    ld hl, (DM_MAP)            ; HL apunta al mapa 
-   LD A, (HL)                 ; Leemos tipo de codificacion +    ld a, (hl)                 ; Leemos tipo de codificacion 
-   INC HL                     ; Incrementamos puntero (a datos) +    inc hl                     ; Incrementamos puntero (a datos) 
-   LD (DM_MAP), HL            ; Guardamos el valor en DM_MAP+    ld (DM_MAP), hl            ; Guardamos el valor en DM_MAP
  
-   AND A                      ; Es A == 0? (MAP_CODIF_NONE) +    and a                      ; Es A == 0? (MAP_CODIF_NONE) 
-   JR NZ, dm16c_nocero        ; No +    jr nz, dm16c_nocero        ; No 
-   CALL DrawMap_16x16 +    call DrawMap_16x16 
-   RET+    ret
  
 dm16c_nocero: dm16c_nocero:
-   CP MAP_CODIF_HORIZ         ; Es A == MAP_CODIF_HORIZ? +    CP MAP_CODIF_HORIZ         ; Es A == MAP_CODIF_HORIZ? 
-   JR NZ, dm16c_nohoriz       ; No, saltar +    jr nz, dm16c_nohoriz       ; No, saltar 
-   CALL DrawMap_16x16_Cod_Horiz +    call DrawMap_16x16_Cod_Horiz 
-   RET+    ret
  
 dm16c_nohoriz: dm16c_nohoriz:
-   CP MAP_CODIF_VERT          ; Es A == MAP_CODIF_VERT? +    CP MAP_CODIF_VERT          ; Es A == MAP_CODIF_VERT? 
-   JR NZ, dm16c_novert        ; No, saltar +    jr nz, dm16c_novert        ; No, saltar 
-   CALL DrawMap_16x16_Cod_Vert +    call DrawMap_16x16_Cod_Vert 
-   RET+    ret
  
 dm16c_novert: dm16c_novert:
-   CP MAP_CODIF_MIXTA         ; Es A == MAP_CODIF_MIXTA? +    CP MAP_CODIF_MIXTA         ; Es A == MAP_CODIF_MIXTA? 
-   JR NZ, dm16c_nomixta       ; No, saltar +    jr nz, dm16c_nomixta       ; No, saltar 
-   CALL DrawMap_16x16_Cod_Mixta +    call DrawMap_16x16_Cod_Mixta 
-   RET+    ret
  
 dm16c_nomixta:                ; Entonces es basica. dm16c_nomixta:                ; Entonces es basica.
-   CALL DrawMap_16x16_Cod_Basica +    call DrawMap_16x16_Cod_Basica 
-   RET+    ret
 </code> </code>
  
Línea 2850: Línea 2847:
  En cualquier caso, si detectamos que la técnica de compresión mixta obtiene mejores resultados para la gran mayoría de las pantallas, podemos optar por codificar todo con el algoritmo mixto (aunque algunas pantallas pudieran ocupar más con este método que con otro, serían una minoría) y así evitar la inclusión de las otras rutinas de impresión y la rutina DrawMap_16x16_Codificada que acabamos de ver. Esta será, probablemente, la mejor de las opciones si el espacio ahorrado por codificar cada pantalla con un sistema diferente es menor que la inclusión en nuestro programa de las 4 rutinas (básica, horizontal, vertical, mixta) y la rutina wrapper que acabamos de ver. Además, también ganaremos algo de tiempo en la impresión de las pantallas al saltarnos la ejecución de la  rutina wrapper.  En cualquier caso, si detectamos que la técnica de compresión mixta obtiene mejores resultados para la gran mayoría de las pantallas, podemos optar por codificar todo con el algoritmo mixto (aunque algunas pantallas pudieran ocupar más con este método que con otro, serían una minoría) y así evitar la inclusión de las otras rutinas de impresión y la rutina DrawMap_16x16_Codificada que acabamos de ver. Esta será, probablemente, la mejor de las opciones si el espacio ahorrado por codificar cada pantalla con un sistema diferente es menor que la inclusión en nuestro programa de las 4 rutinas (básica, horizontal, vertical, mixta) y la rutina wrapper que acabamos de ver. Además, también ganaremos algo de tiempo en la impresión de las pantallas al saltarnos la ejecución de la  rutina wrapper.
  
-\\ +\\  
-\\+\\ 
 **Pantallas con blancos y transparencias** **Pantallas con blancos y transparencias**
-\\ +\\  
-\\+\\ 
  
  En el caso de la compresión básica no conseguíamos mejoras de tamaño en las pantallas si existían bloques transparentes (255) además de bloques vacíos (0), ya que nosotros sólo podíamos considerar a nivel de codificación uno de los 2 como "bloque no dibujable".  En el caso de la compresión básica no conseguíamos mejoras de tamaño en las pantallas si existían bloques transparentes (255) además de bloques vacíos (0), ya que nosotros sólo podíamos considerar a nivel de codificación uno de los 2 como "bloque no dibujable".
  
- Lo bueno de las técnicas de codificación por scanlines horizontales, verticales o mixtos es que los bloques vacíos o los transparentes (según nos interese en el juego) se pueden codificar junto a los bloques normales, modificando las variables IGNORE_VALUES y BLANK del script de codificación en python.+ Lo bueno de las técnicas de codificación por scanlines horizontales, verticales o mixtos es que los bloques vacíos o los transparentes (según nos interese en el juego) se pueden codificar junto a los bloques normales, modificando las variables ''IGNORE_VALUES'' ''BLANK'' del script de codificación en python.
  
  Concretamente, tenemos que establecer ambas variables con el identificador de tile que queremos  "ignorar" (y por lo tanto no codificar).  Concretamente, tenemos que establecer ambas variables con el identificador de tile que queremos  "ignorar" (y por lo tanto no codificar).
Línea 2882: Línea 2879:
 </code> </code>
  
- En este caso, los bloques "0" (fondo negro) se han codificado junto a los demás bloques normales (mejorando la "compresión") y los bloques 255 (transparentes) no, por lo que si en los ejemplos anteriores eliminamos el ClearScreen() y lanzamos el DrawMap correspondiente, obtenemos la siguiente pantalla:+ En este caso, los bloques "0" (fondo negro) se han codificado junto a los demás bloques normales (mejorando la "compresión") y los bloques 255 (transparentes) no, por lo que si en los ejemplos anteriores eliminamos el ClearScreen() y lanzamos el ''DrawMap'' correspondiente, obtenemos la siguiente pantalla:
  
-\\+\\ 
 {{ :cursos:ensamblador:gfx5_transp.png?640 | Codificando los blancos pero no las transparencias }} {{ :cursos:ensamblador:gfx5_transp.png?640 | Codificando los blancos pero no las transparencias }}
-\\+\\ 
  
- No hay colisión entre el 255 "transparente" y el 255 "fin de scanline" porque el codificador lo que ha hecho es, precisamente, no codificar los bloques 255 con lo que estos no aparecen en la pantalla resultante. Por contra, sí que es necesario incluir los bloques 0 ya que pretendemos que sean dibujados para "tapar" el fondo (por eso se ha cambiando BLANK e IGNORE_VALUES en el script codificador, cambiando 0 por 255).+ No hay colisión entre el 255 "transparente" y el 255 "fin de scanline" porque el codificador lo que ha hecho es, precisamente, no codificar los bloques 255 con lo que estos no aparecen en la pantalla resultante. Por contra, sí que es necesario incluir los bloques 0 ya que pretendemos que sean dibujados para "tapar" el fondo (por eso se ha cambiando ''BLANK'' ''IGNORE_VALUES'' en el script codificador, cambiando 0 por 255).
  
  
-\\ +\\  
-\\+\\ 
 **Efectos sobre los tiles adyacentes al fondo** **Efectos sobre los tiles adyacentes al fondo**
-\\ +\\  
-\\+\\ 
  
  Otro efecto interesante para mejorar la riqueza gráfica de un juego es generar una "sombra" para aquellos tiles que estén cercanos a otros tiles definidos como "fondo".  Otro efecto interesante para mejorar la riqueza gráfica de un juego es generar una "sombra" para aquellos tiles que estén cercanos a otros tiles definidos como "fondo".
Línea 2901: Línea 2898:
  Por ejemplo, la siguiente captura de pantalla de un juego de los //Mojon Twins// muestra cómo los muros verticales rojos cercanos a la "bellota" proyectan hacia la derecha, sobre el suelo "morado", una sombra generada durante el proceso de trazado de la pantalla. Esta sombra hace que los tiles de suelo cercanos al muro sean diferentes del resto de tiles de suelo, sin necesidad de haber definido un tile específico para ello.  Por ejemplo, la siguiente captura de pantalla de un juego de los //Mojon Twins// muestra cómo los muros verticales rojos cercanos a la "bellota" proyectan hacia la derecha, sobre el suelo "morado", una sombra generada durante el proceso de trazado de la pantalla. Esta sombra hace que los tiles de suelo cercanos al muro sean diferentes del resto de tiles de suelo, sin necesidad de haber definido un tile específico para ello.
  
-\\+\\ 
 {{ :cursos:ensamblador:gfx5_cotbss08.png?544 | Sombreados sobre tiles }} {{ :cursos:ensamblador:gfx5_cotbss08.png?544 | Sombreados sobre tiles }}
-\\+\\ 
  
  Podemos utilizar esta técnica en caso de necesitar ahorrar memoria, aunque lo más rápido en términos de trazado sería el disponer de tiles específicos "con sombras" y que el mapa los tenga definidos en las posiciones necesarias.  Podemos utilizar esta técnica en caso de necesitar ahorrar memoria, aunque lo más rápido en términos de trazado sería el disponer de tiles específicos "con sombras" y que el mapa los tenga definidos en las posiciones necesarias.
Línea 2933: Línea 2930:
 </code> </code>
  
-\\ +\\  
-\\+\\ 
 === Resumen de resultados de codificaciones empleadas === === Resumen de resultados de codificaciones empleadas ===
  
  Veamos una tabla resumen de los resultados de codificar nuestra pantalla de ejemplo (el nivel 1 de Sokoban) con diferentes técnicas. Inclúimos además una estimación de cuánto ocuparían 100 pantallas de juego y cuántas pantallas cabrían en 16KB de memoria asumiendo que, de media, todas ocuparan tras su codificación tamaños parecidos del primer nivel:  Veamos una tabla resumen de los resultados de codificar nuestra pantalla de ejemplo (el nivel 1 de Sokoban) con diferentes técnicas. Inclúimos además una estimación de cuánto ocuparían 100 pantallas de juego y cuántas pantallas cabrían en 16KB de memoria asumiendo que, de media, todas ocuparan tras su codificación tamaños parecidos del primer nivel:
  
-\\+\\ 
 |< 70% >| |< 70% >|
 ^ Codificación ^ Tamaño (bytes) ^ Ocupación 100 pantallas ^ Pantallas en 16KB ^ ^ Codificación ^ Tamaño (bytes) ^ Ocupación 100 pantallas ^ Pantallas en 16KB ^
Línea 2948: Línea 2945:
 | Scanlines mixtos | 72 | 7 KB | 227 pantallas | | Scanlines mixtos | 72 | 7 KB | 227 pantallas |
 | Scanlines mixtos + XY agrupados | 60 | 5.8 KB | 273 pantallas | | Scanlines mixtos + XY agrupados | 60 | 5.8 KB | 273 pantallas |
-\\+\\ 
  
  Estos datos son una estimación muy general porque no todas las pantallas de Sokoban ocuparán lo mismo una vez codificadas, pero si utilizamos la técnica de codificación más adecuada a cada pantalla podemos acercarnos mucho a esas 273 pantallas totales en 16 KB de memoria, muy alejadas de las 85 que caben con los datos "en crudo".  Estos datos son una estimación muy general porque no todas las pantallas de Sokoban ocuparán lo mismo una vez codificadas, pero si utilizamos la técnica de codificación más adecuada a cada pantalla podemos acercarnos mucho a esas 273 pantallas totales en 16 KB de memoria, muy alejadas de las 85 que caben con los datos "en crudo".
  
  
-\\ +\\  
-\\+\\ 
 === Posibles mejoras en el codificador y las rutinas de impresión === === Posibles mejoras en el codificador y las rutinas de impresión ===
  
-\\ +\\  
-\\+\\ 
 **Detección de situaciones especiales de codificación mixta** **Detección de situaciones especiales de codificación mixta**
-\\ +\\  
-\\+\\ 
  
  Una posible mejora sería la de modificar el script de codificación mixta para que detecte ciertas situaciones en la que no es óptimo actualmente. El algoritmo que se ha aplicado en el codificador es un algoritmo genérico basado en buscar la mayor cantidad de tiles consecutivos para una posterior codificación horizontal o vertical por orden de cantidad de tiles. Este algoritmo no detecta determinadas situaciones donde la mejor codificación no es la que más cantidad de tiles consecutivos consigue.  Una posible mejora sería la de modificar el script de codificación mixta para que detecte ciertas situaciones en la que no es óptimo actualmente. El algoritmo que se ha aplicado en el codificador es un algoritmo genérico basado en buscar la mayor cantidad de tiles consecutivos para una posterior codificación horizontal o vertical por orden de cantidad de tiles. Este algoritmo no detecta determinadas situaciones donde la mejor codificación no es la que más cantidad de tiles consecutivos consigue.
Línea 3039: Línea 3036:
  
  
-\\ +\\  
-\\+\\ 
 **Agrupar 2 scanlines separados por 2 o menos huecos** **Agrupar 2 scanlines separados por 2 o menos huecos**
-\\ +\\  
-\\+\\ 
  
  Finalmente, podríamos modificar el script codificador para que detecte las situaciones en que  tengamos 2 scanlines (horizontales o verticales) separados por 1 ó 2 bloques "vacíos" (transparentes).  Finalmente, podríamos modificar el script codificador para que detecte las situaciones en que  tengamos 2 scanlines (horizontales o verticales) separados por 1 ó 2 bloques "vacíos" (transparentes).
Línea 3065: Línea 3062:
 </code> </code>
  
- Si la separación entre 2 scanlines la forma 1 bloque "transparente" , añadimos 1 byte "252" pero eliminamos el "255" del primer scanline y el COORD_X y COORD_Y del segundo, por lo que sumamos 1 bytes pero restamos 3, ahorrando 2 bytes.+ Si la separación entre 2 scanlines la forma 1 bloque "transparente" , añadimos 1 byte "252" pero eliminamos el "255" del primer scanline y el ''COORD_X'' ''COORD_Y'' del segundo, por lo que sumamos 1 bytes pero restamos 3, ahorrando 2 bytes.
  
- Si la separación entre 2 scanlines la forman 2 bloques "transparentes" , añadimos 2 bytes "252" pero eliminamos el "255" del primer scanline y el COORD_X y COORD_Y del segundo, por lo que sumamos 2 bytes pero restamos 3, ahorrando 1 byte.+ Si la separación entre 2 scanlines la forman 2 bloques "transparentes" , añadimos 2 bytes "252" pero eliminamos el "255" del primer scanline y el ''COORD_X'' ''COORD_Y'' del segundo, por lo que sumamos 2 bytes pero restamos 3, ahorrando 1 byte.
  
  Para más de 3 bloques transparentes no se produce ningún ahorro, sino todo lo contrario.  Para más de 3 bloques transparentes no se produce ningún ahorro, sino todo lo contrario.
Línea 3074: Línea 3071:
  
  
-\\+\\ 
 ==== Mapeados de diferentes tilesets ==== ==== Mapeados de diferentes tilesets ====
  
Línea 3083: Línea 3080:
 <code z80> <code z80>
 Tabla_IDs_Tilesets: Tabla_IDs_Tilesets:
-  DW dir_tileset_gfx_1 +    DW dir_tileset_gfx_1 
-  DW dir_tileset_attrib_1 +    DW dir_tileset_attrib_1 
-  DB ancho_tiles_tileset1, alto_tiles_tileset1 +    DB ancho_tiles_tileset1, alto_tiles_tileset1 
-  DW dir_tileset_gfx_2 +    DW dir_tileset_gfx_2 
-  DW dir_tileset_attrib_2 +    DW dir_tileset_attrib_2 
-  DB ancho_tiles_tileset2, alto_tiles_tileset2 +    DB ancho_tiles_tileset2, alto_tiles_tileset2 
-  DW dir_tileset_gfx_3 +    DW dir_tileset_gfx_3 
-  DW dir_tileset_attrib_3 +    DW dir_tileset_attrib_3 
-  DB ancho_tiles_tileset3, alto_tiles_tileset3+    DB ancho_tiles_tileset3, alto_tiles_tileset3
 </code> </code>
  
Línea 3098: Línea 3095:
 <code z80> <code z80>
 Pantalla1: Pantalla1:
-   DB ID_TILESET, X, Y, ID_TILE, ID_TILESET, X, Y, ID_TILE +    DB ID_TILESET, X, Y, ID_TILE, ID_TILESET, X, Y, ID_TILE 
-   DB ID_TILESET, X, Y, ID_TILE, ID_TILESET, X, Y, ID_TILE +    DB ID_TILESET, X, Y, ID_TILE, ID_TILESET, X, Y, ID_TILE 
-   DB (...) +    DB (...) 
-   DB 255 (fin de pantalla)+    DB 255 (fin de pantalla)
 </code> </code>
  
Línea 3113: Línea 3110:
  
  
-\\+\\ 
 ==== Mapeados de tiles de cualquier tamaño de bloque ==== ==== Mapeados de tiles de cualquier tamaño de bloque ====
  
Línea 3124: Línea 3121:
 <code z80> <code z80>
 Tabla_Tiles: Tabla_Tiles:
-   DW tileset_1_gfx+(0*32)             ; Cada tile ocupa 8*4=32 bytes en el tileset 16x16 +    DW tileset_1_gfx+(0*32)             ; Cada tile ocupa 8*4=32 bytes en el tileset 16x16 
-   DW tileset_1_attr+(0*4)             ; Cada tile ocupa 4 atributos en el tileset 16x16 +    DW tileset_1_attr+(0*4)             ; Cada tile ocupa 4 atributos en el tileset 16x16 
-   DB 16, 16 +    DB 16, 16 
-   DW tileset_1_gfx+(1*32)             ; Apuntamos a datos del tile 1 del tileset +    DW tileset_1_gfx+(1*32)             ; Apuntamos a datos del tile 1 del tileset 
-   DW tileset_1_attr+(1*4) +    DW tileset_1_attr+(1*4) 
-   DB 16, 16 +    DB 16, 16 
-   DW tileset_1_gfx+(2*32) +    DW tileset_1_gfx+(2*32) 
-   DW tileset_1_attr+(2*4) +    DW tileset_1_attr+(2*4) 
-   DB 16, 16 +    DB 16, 16 
-   DW tileset_2_gfx+(0*8)              ; Cada tile ocupa 8 bytes en el tileset 8x8 +    DW tileset_2_gfx+(0*8)              ; Cada tile ocupa 8 bytes en el tileset 8x8 
-   DW tileset_2_attr+(0*1)             ; Cada tile ocupa 1 atributo en el tileset 8x8 +    DW tileset_2_attr+(0*1)             ; Cada tile ocupa 1 atributo en el tileset 8x8 
-   DB 8, 8 +    DB 8, 8 
-   DW tileset_2_gfx+(1*8) +    DW tileset_2_gfx+(1*8) 
-   DW tileset_2_attr+(1*0) +    DW tileset_2_attr+(1*0) 
-   DB 8, 8 +    DB 8, 8 
-   DW logotipo_gfx +    DW logotipo_gfx 
-   DW logotipo_attr +    DW logotipo_attr 
-   DB 32, 16 +    DB 32, 16 
-   DW piedra_gfx +    DW piedra_gfx 
-   DW piedra_attr +    DW piedra_attr 
-   DB 32, 32 +    DB 32, 32 
-   DW muro_grande_gfx +    DW muro_grande_gfx 
-   DW muro_grande_attr +    DW muro_grande_attr 
-   DB 64, 64 +    DB 64, 64 
-   DW grafico_escalera_gfx +    DW grafico_escalera_gfx 
-   DW grafico_escalera_attr +    DW grafico_escalera_attr 
-   DB 16, 32+    DB 16, 32
 </code> </code>
  
Línea 3162: Línea 3159:
  La pantalla acabará en un valor 255 y los valores de X e Y deberán ser coordenadas exactas de pantalla. La rutina de impresión recorrerá cada byte de la pantalla (hasta encontrar el fin de pantalla o 255) y trazará todos los sprites llamando a la rutina genérica de DrawSprite_MxN.  La pantalla acabará en un valor 255 y los valores de X e Y deberán ser coordenadas exactas de pantalla. La rutina de impresión recorrerá cada byte de la pantalla (hasta encontrar el fin de pantalla o 255) y trazará todos los sprites llamando a la rutina genérica de DrawSprite_MxN.
  
- Nuestra rutina de impresión puede acceder a los datos del tile mediante multiplicación de ID_EN_TABLA_TILE por 2+2+2 e imprimir el sprite gfx/attr/ancho/alto leído desde la tabla.+ Nuestra rutina de impresión puede acceder a los datos del tile mediante multiplicación de ''ID_EN_TABLA_TILE'' por 2+2+2 e imprimir el sprite gfx/attr/ancho/alto leído desde la tabla.
  
  Con este sistema se debe de diseñar y codificar manualmente la pantalla, pero nos permite tener una riqueza gráfica que no siempre se puede conseguir sólo con tiles de tamaños fijos.  Con este sistema se debe de diseñar y codificar manualmente la pantalla, pero nos permite tener una riqueza gráfica que no siempre se puede conseguir sólo con tiles de tamaños fijos.
Línea 3173: Línea 3170:
 DrawSprite_MxN_LD_extendida: DrawSprite_MxN_LD_extendida:
  
-   LD A, (DS_HEIGHT) +    ld a, (DS_HEIGHT) 
-   LD BA +    ld ba 
-   LD A, (DS_WIDTH) +    ld a, (DS_WIDTH) 
-   LD C                  ; Obtenemos datos del sprite+    ld c                  ; Obtenemos datos del sprite
  
-   ;;; B = ALTO de Sprite +    ;;; B = ALTO de Sprite 
-   ;;; C = ANCHO de Sprite+    ;;; C = ANCHO de Sprite
  
-   LD A                  ; A = ANCHO +    ld a                  ; A = ANCHO 
-   CP 16                     ; Comparar ancho +    cp 16                     ; Comparar ancho 
-   JR NZ, dspMN_no16         ; ¿es 16?+    jr nz, dspMN_no16         ; ¿es 16?
  
-   SUB B                     ; A = Ancho - alto +    sub b                     ; A = Ancho - alto 
-   JR NZ, dspMN_generica     ; Si no es cero, rutina generica +    jr nz, dspMN_generica     ; Si no es cero, rutina generica 
-                             ; Es cero, imprimir 16x16: +                                ; Es cero, imprimir 16x16: 
-   CALL DrawSprite16x16      ; Imprimir via especifica 16x16 +    call DrawSprite16x16      ; Imprimir via especifica 16x16 
-   RET+    ret
  
 dspMN_no16: dspMN_no16:
-   LD A                  ; Recuperamos ancho +    ld a                  ; Recuperamos ancho 
-   CP 8                      ; ¿es 8? +    cp 8                      ; ¿es 8? 
-   JR NZ, dspMN_generica     ; Si no es 8, ni 16, generica +    jr nz, dspMN_generica     ; Si no es 8, ni 16, generica 
-   SUB B                     ; A = Ancho - alto +    sub b                     ; A = Ancho - alto 
-   JR NZ, dspMN_generica     ; Si no es cero, rutina generica+    jr nz, dspMN_generica     ; Si no es cero, rutina generica
  
-   CALL DrawSprite_8x8       ; Imprimir via especifica 8x8 +    call DrawSprite_8x8       ; Imprimir via especifica 8x8 
-   RET+    ret
  
 dspMN_generica: dspMN_generica:
-   ;;; (resto rutina generica) +    ;;; (resto rutina generica) 
-   RET+    ret
 </code> </code>
  
Línea 3209: Línea 3206:
  
  
-\\ +\\  
-\\+\\ 
 **Tabla sólo de los tiles no estándar** **Tabla sólo de los tiles no estándar**
-\\+\\ 
  
  Lo más normal es que el mayor porcentaje de gráficos de la pantalla se tomen desde un tileset y unos pocos desde bitmaps de diferentes tamaños ajenos a éste. En ese caso nos podemos ahorrar la tabla de direcciones de tiles si, por ejemplo, definimos estos gráficos especiales a partir de un determinado valor numérico.  Lo más normal es que el mayor porcentaje de gráficos de la pantalla se tomen desde un tileset y unos pocos desde bitmaps de diferentes tamaños ajenos a éste. En ese caso nos podemos ahorrar la tabla de direcciones de tiles si, por ejemplo, definimos estos gráficos especiales a partir de un determinado valor numérico.
Línea 3220: Línea 3217:
 <code z80> <code z80>
 Tiles_Extendidos: Tiles_Extendidos:
-   DW logotipo_gfx +    DW logotipo_gfx 
-   DW logotipo_attr +    DW logotipo_attr 
-   DB 32, 16 +    DB 32, 16 
-   DW piedra_gfx +    DW piedra_gfx 
-   DW piedra_attr +    DW piedra_attr 
-   DB 32, 32 +    DB 32, 32 
-   DW muro_grande_gfx +    DW muro_grande_gfx 
-   DW muro_grande_attr +    DW muro_grande_attr 
-   DB 64, 64 +    DB 64, 64 
-   DW grafico_escalera_gfx +    DW grafico_escalera_gfx 
-   DW grafico_escalera_attr +    DW grafico_escalera_attr 
-   DB 16, 32+    DB 16, 32
 </code> </code>
  
Línea 3239: Línea 3236:
  
  
-\\+\\ 
 ===== Fondos personalizados para cada pantalla ===== ===== Fondos personalizados para cada pantalla =====
  
Línea 3260: Línea 3257:
  
 Fondos: Fondos:
-   DW fondo_piedra_gfx +    DW fondo_piedra_gfx 
-   DW fondo_piedra_attr +    DW fondo_piedra_attr 
-   DB 16, 16 +    DB 16, 16 
-   DW fondo_baldosas_gfx +    DW fondo_baldosas_gfx 
-   DW fondo_baldosas_attr +    DW fondo_baldosas_attr 
-   DB 64, 64 +    DB 64, 64 
-   DW fondo_hierba_gfx +    DW fondo_hierba_gfx 
-   DW fondo_hierba_attr +    DW fondo_hierba_attr 
-   DB 32, 32+    DB 32, 32
 </code> </code>
  
Línea 3281: Línea 3278:
 <code z80> <code z80>
 Mapa: Mapa:
-  DW Pantalla_Inicio            ; ID = 0 +    DW Pantalla_Inicio            ; ID = 0 
-  DB -1, 1                      ; Conexiones izq y derecha ID 0 +    DB -1, 1                      ; Conexiones izq y derecha ID 0 
-  DB FONDO_BALDOSAS +    DB FONDO_BALDOSAS 
-  DW Pantalla_Salon             ; ID = 1 +    DW Pantalla_Salon             ; ID = 1 
-  DB 0, 2                       ; Conexiones izq y derecha ID 1 +    DB 0, 2                       ; Conexiones izq y derecha ID 1 
-  DB FONDO_PIEDRA +    DB FONDO_PIEDRA 
-  DW Pantalla_Pasillo           ; ID = 2 +    DW Pantalla_Pasillo           ; ID = 2 
-  DB 1, 3                       ; Conexiones izq y derecha ID 2 +    DB 1, 3                       ; Conexiones izq y derecha ID 2 
-  DB FONDO_PIEDRA +    DB FONDO_PIEDRA 
-  (...)+    (...)
 </code> </code>
  
Línea 3299: Línea 3296:
  Finalmente, en lugar de realizar un borrado del área de pantalla donde vamos a imprimir el mapa sería necesario programar una rutina que utilice el sprite Fondo (mediante su ID y la tabla de Fondos). La rutina deberá "rellenar" vía impresión por repetición el área de juego con el fondo seleccionado. Para simplificar la rutina es recomendable que todos los tamaños de los tiles de fondo sean dividores exactos del tamaño de la pantalla, de forma que la rutina no tenga que calcular si la impresión del último tile que quepa en la misma ha de ser dibujado total o parcialmente. Si el área de juego es de 256 píxeles, podremos utilizar tiles de ancho 8 (32 repeticiones), 16 (16 repeticiones), 32 (8 repeticiones), 64 (4 repeticiones), 128 (2 repeticiones) o incluso de 256 (1 repetición). Lo mismo ocurriría para la altura.  Finalmente, en lugar de realizar un borrado del área de pantalla donde vamos a imprimir el mapa sería necesario programar una rutina que utilice el sprite Fondo (mediante su ID y la tabla de Fondos). La rutina deberá "rellenar" vía impresión por repetición el área de juego con el fondo seleccionado. Para simplificar la rutina es recomendable que todos los tamaños de los tiles de fondo sean dividores exactos del tamaño de la pantalla, de forma que la rutina no tenga que calcular si la impresión del último tile que quepa en la misma ha de ser dibujado total o parcialmente. Si el área de juego es de 256 píxeles, podremos utilizar tiles de ancho 8 (32 repeticiones), 16 (16 repeticiones), 32 (8 repeticiones), 64 (4 repeticiones), 128 (2 repeticiones) o incluso de 256 (1 repetición). Lo mismo ocurriría para la altura.
  
- La rutina tendría que utilizar DrawSpriteMxN (ya que los fondos pueden tener cualquier tamaño) pero se recomienda que emplee rutinas específicas en los tamaños de fondo para los que tengamos rutina de impresión disponible.+ La rutina tendría que utilizar ''DrawSpriteMxN'' (ya que los fondos pueden tener cualquier tamaño) pero se recomienda que emplee rutinas específicas en los tamaños de fondo para los que tengamos rutina de impresión disponible.
  
  De esta forma, rellenamos el área de juego con el fondo asociado a dicha pantalla y después llamamos a DrawMap para dibujar sobre este "fondo" los tiles reales.  De esta forma, rellenamos el área de juego con el fondo asociado a dicha pantalla y después llamamos a DrawMap para dibujar sobre este "fondo" los tiles reales.
Línea 3310: Línea 3307:
  
  
-\\+\\ 
 ===== Compresión de los datos de pantalla ===== ===== Compresión de los datos de pantalla =====
  
  Como hemos comentado antes, todavía podríamos arañar algunos bytes adicionales a las pantallas utilizando técnicas de compresión básicas.  Como hemos comentado antes, todavía podríamos arañar algunos bytes adicionales a las pantallas utilizando técnicas de compresión básicas.
  
-\\ +\\  
-\\+\\ 
 **Compresión por repetición** **Compresión por repetición**
-\\ +\\  
-\\+\\ 
  
  La primera de las posibilidades de compresión se basa en repetición de tiles consecutivos en las técnicas de agrupación, utilizando un tipo de compresión que utilice un byte de control "253", de tal modo que si se encuentra un valor de tile 253, a éste valor le siga el tile a repetir y el número de repeticiones (o al revés).  La primera de las posibilidades de compresión se basa en repetición de tiles consecutivos en las técnicas de agrupación, utilizando un tipo de compresión que utilice un byte de control "253", de tal modo que si se encuentra un valor de tile 253, a éste valor le siga el tile a repetir y el número de repeticiones (o al revés).
  
 <code z80> <code z80>
-  DB 253, NUMERO_REPETICIONES, TILE_A_REPETIR+    DB 253, NUMERO_REPETICIONES, TILE_A_REPETIR
 </code> </code>
  
Línea 3330: Línea 3327:
  
 <code z80> <code z80>
-  ; 9 bytes +    ; 9 bytes 
-  DB 1, 2, 2, 2, 2, 2, 2, 3, 4+    DB 1, 2, 2, 2, 2, 2, 2, 3, 4
 </code> </code>
  
Línea 3337: Línea 3334:
  
 <code z80> <code z80>
-  ; 6 bytes +    ; 6 bytes 
-  DB 1, 253, 6, 2, 3, 4+    DB 1, 253, 6, 2, 3, 4
 </code> </code>
  
Línea 3349: Línea 3346:
  Este tipo de algoritmo de compresión se denomina RLE y lo veremos en el siguiente capítulo.  Este tipo de algoritmo de compresión se denomina RLE y lo veremos en el siguiente capítulo.
  
-\\ +\\  
-\\+\\ 
 **Compresión por patrones** **Compresión por patrones**
-\\ +\\  
-\\+\\ 
  
  El sistema de compresión por patrones es el método que, probablemente, producirá los mejores resultados de reducción de tamaño de las pantallas en la mayoría de juegos.  El sistema de compresión por patrones es el método que, probablemente, producirá los mejores resultados de reducción de tamaño de las pantallas en la mayoría de juegos.
Línea 3365: Línea 3362:
 <code z80> <code z80>
 Pantalla: Pantalla:
-  DB X_TILE, Y_TILE, 253, ID_PATRON+    DB X_TILE, Y_TILE, 253, ID_PATRON
 </code> </code>
  
- (También podemos agrupar X_TILE e Y_TILE en un mismo byte como hicimos en el script codificador).+ (También podemos agrupar ''X_TILE'' ''Y_TILE'' en un mismo byte como hicimos en el script codificador).
  
  Supongamos la siguiente pantalla ilustrativa:  Supongamos la siguiente pantalla ilustrativa:
Línea 3393: Línea 3390:
  
 Patron0: Patron0:
-  DB CODIF_HORIZONTAL, 1, 2, 3, 2, 4, 255+    DB CODIF_HORIZONTAL, 1, 2, 3, 2, 4, 255
  
 Patron1: Patron1:
-  DB CODIF_VERTICAL, 8, 8, 8, 8, 8, 8, 8, 255+    DB CODIF_VERTICAL, 8, 8, 8, 8, 8, 8, 8, 255
  
 Patron2: Patron2:
-  DB CODIF_VERTICAL, 7, 7, 7, 7, 7, 7, 7, 255+    DB CODIF_VERTICAL, 7, 7, 7, 7, 7, 7, 7, 255
  
 Patron3: Patron3:
-  DB CODIF_VERTICAL, 7, 7, 7, 7, 7, 255+    DB CODIF_VERTICAL, 7, 7, 7, 7, 7, 255
  
 Patron4: Patron4:
-  DB CODIF_VERTICAL, 8, 8, 8, 8, 8, 255+    DB CODIF_VERTICAL, 8, 8, 8, 8, 8, 255
 </code> </code>
  
Línea 3412: Línea 3409:
 <code z80> <code z80>
 Tabla_Patrones: Tabla_Patrones:
-  DW Patron0 +    DW Patron0 
-  DW Patron1 +    DW Patron1 
-  DW Patron2 +    DW Patron2 
-  DW Patron3 +    DW Patron3 
-  DW Patron4 +    DW Patron4 
-  (...)+    (...)
 </code> </code>
  
Línea 3445: Línea 3442:
 <code z80> <code z80>
 Pantalla: Pantalla:
-  DB 2, 1, 253, 0 +    DB 2, 1, 253, 0 
-  DB 8, 2, 253, 0 +    DB 8, 2, 253, 0 
-  DB 1, 4, 253, 0 +    DB 1, 4, 253, 0 
-  DB 0, 6, 253, 0 +    DB 0, 6, 253, 0 
-  DB 7, 6, 253, 0 +    DB 7, 6, 253, 0 
-  DB 15, 0, 253, 1 +    DB 15, 0, 253, 1 
-  DB 19, 0, 253, 1 +    DB 19, 0, 253, 1 
-  DB 16, 0, 253, 2 +    DB 16, 0, 253, 2 
-  DB 20, 0, 253, 2 +    DB 20, 0, 253, 2 
-  DB 17, 0, 253, 3 +    DB 17, 0, 253, 3 
-  DB 18, 0, 253, 4 +    DB 18, 0, 253, 4 
-  DB 17, 5, 9, 9, 255 +    DB 17, 5, 9, 9, 255 
-  DB 17, 6, 9, 9, 255+    DB 17, 6, 9, 9, 255
 </code> </code>
  
Línea 3468: Línea 3465:
  Y cabe decir que dadas las limitaciones de memoria del Spectrum, los juegos tienen a repetir patrones de tiles para los diferentes tipos de suelos, techos, paredes, etc:  Y cabe decir que dadas las limitaciones de memoria del Spectrum, los juegos tienen a repetir patrones de tiles para los diferentes tipos de suelos, techos, paredes, etc:
  
-\\+\\ 
 {{ :cursos:ensamblador:gfx5_tresluces.png?1018 | Tres luces de Glaurung }} {{ :cursos:ensamblador:gfx5_tresluces.png?1018 | Tres luces de Glaurung }}
-\\+\\ 
 ;#; ;#;
 //Patrones codificables: suelo, paredes verticales, etc// //Patrones codificables: suelo, paredes verticales, etc//
 ;#; ;#;
-\\+\\ 
  
  Este sistema acaba consiguiendo ratios de compresión muy buenos pero basa toda su técnica en el programa codificador: el script/programa debe analizar todas las pantallas del mapeado en una pasada (no vale con analizar sólo la pantalla que estamos codificando).  Este sistema acaba consiguiendo ratios de compresión muy buenos pero basa toda su técnica en el programa codificador: el script/programa debe analizar todas las pantallas del mapeado en una pasada (no vale con analizar sólo la pantalla que estamos codificando).
Línea 3486: Línea 3483:
  
  
-\\+\\ 
 ===== Propiedades de los tiles y el mapeado ===== ===== Propiedades de los tiles y el mapeado =====
  
Línea 3495: Línea 3492:
  La primera distinción suele ser marcar qué tiles son "sólidos" y a través de cuáles puede pasar el jugador. En muchos juegos se utiliza el tile 0 como tile "de fondo" y el resto de tiles como bloques sólidos (Manic Miner, etc) pero es posible que necesitemos que algunos tiles sean dibujados y sin embargo nuestro personaje pueda pasar a través de ellos. Todos los tiles serán iguales para la rutina de impresión, pero no lo serán para las rutinas de gestión de movimiento de nuestro personaje y de los enemigos.  La primera distinción suele ser marcar qué tiles son "sólidos" y a través de cuáles puede pasar el jugador. En muchos juegos se utiliza el tile 0 como tile "de fondo" y el resto de tiles como bloques sólidos (Manic Miner, etc) pero es posible que necesitemos que algunos tiles sean dibujados y sin embargo nuestro personaje pueda pasar a través de ellos. Todos los tiles serán iguales para la rutina de impresión, pero no lo serán para las rutinas de gestión de movimiento de nuestro personaje y de los enemigos.
  
-\\+\\ 
 {{ :cursos:ensamblador:gfx5_blinky.gif?512 | Atravesando tiles }} {{ :cursos:ensamblador:gfx5_blinky.gif?512 | Atravesando tiles }}
-\\+\\ 
 ;#; ;#;
 //Nuestro personaje tiene que poder atravesar la columna// //Nuestro personaje tiene que poder atravesar la columna//
 ;#; ;#;
-\\+\\ 
  
  En ese caso, podemos utilizar un valor numérico como límite entre tiles no sólidos y tiles sólidos. Por ejemplo, podemos considerar que los primeros tiles 0-99 son atravesables por el jugador y los tiles del 100 en adelante serán sólidos. La rutina que gestione el movimiento de nuestro personaje deberá obtener del mapa el identificador de tile y permitirnos pasar a través de él o no según el valor obtenido.  En ese caso, podemos utilizar un valor numérico como límite entre tiles no sólidos y tiles sólidos. Por ejemplo, podemos considerar que los primeros tiles 0-99 son atravesables por el jugador y los tiles del 100 en adelante serán sólidos. La rutina que gestione el movimiento de nuestro personaje deberá obtener del mapa el identificador de tile y permitirnos pasar a través de él o no según el valor obtenido.
Línea 3507: Línea 3504:
  Para ciertos juegos es posible que ni siquiera necesitemos definir propiedades de solidez y que (según el tipo de juego) baste con que el personaje se pueda mover sobre el color de fondo y que no pueda atravesar cualquier color distinto de este.  Para ciertos juegos es posible que ni siquiera necesitemos definir propiedades de solidez y que (según el tipo de juego) baste con que el personaje se pueda mover sobre el color de fondo y que no pueda atravesar cualquier color distinto de este.
  
-\\+\\ 
 {{ :cursos:ensamblador:gfx5_turrican.gif?512 | Turrican }} {{ :cursos:ensamblador:gfx5_turrican.gif?512 | Turrican }}
-\\+\\ 
  
  Aparte de la típica clasificación entre bloque sólido y no sólido, puede sernos imprescindible otorgar ciertas propiedades a los tiles y en ese caso nos resultará necesario disponer de algún tipo de estructura de datos que almacene esta información. Por ejemplo, otro caso típico de propiedad de tile es el de indicar si un determinado bloque de pantalla produce o no la muerte del jugador: como pinchos, lava, fuego, etc.  Aparte de la típica clasificación entre bloque sólido y no sólido, puede sernos imprescindible otorgar ciertas propiedades a los tiles y en ese caso nos resultará necesario disponer de algún tipo de estructura de datos que almacene esta información. Por ejemplo, otro caso típico de propiedad de tile es el de indicar si un determinado bloque de pantalla produce o no la muerte del jugador: como pinchos, lava, fuego, etc.
Línea 3526: Línea 3523:
 ;;; (etc...) ;;; (etc...)
 Propiedades_Tiles: Propiedades_Tiles:
-  DB %00000000        ; Propiedades tile 0 (tile vacio) +    DB %00000000        ; Propiedades tile 0 (tile vacio) 
-  DB %00000001        ; Propiedades tile 1 (bloque) +    DB %00000001        ; Propiedades tile 1 (bloque) 
-  DB %00000010        ; Propiedades tile 2 (suelo que se rompe) +    DB %00000010        ; Propiedades tile 2 (suelo que se rompe) 
-  DB %00000001        ; Propiedades tile 3 (otro bloque) +    DB %00000001        ; Propiedades tile 3 (otro bloque) 
-  DB %00000101        ; Propiedades tile 4 ("pinchos")+    DB %00000101        ; Propiedades tile 4 ("pinchos")
 </code> </code>
  
Línea 3540: Línea 3537:
  
  
-\\+\\ 
 ===== Objetos y enemigos en el mapeado ===== ===== Objetos y enemigos en el mapeado =====
  
Línea 3549: Línea 3546:
  Cuando tratemos el capítulo dedicados a estructuras de datos veremos ejemplos de cómo definir estructuras que almacenen la información de posición, datos gráficos y características de objetos, enemigos, interruptores, puertas, y otros items del juego.  Cuando tratemos el capítulo dedicados a estructuras de datos veremos ejemplos de cómo definir estructuras que almacenen la información de posición, datos gráficos y características de objetos, enemigos, interruptores, puertas, y otros items del juego.
  
-\\+\\ 
 ===== Diseño y creación del mapeado ===== ===== Diseño y creación del mapeado =====
  
Línea 3569: Línea 3566:
  Tiled es el editor más moderno de los 3, y soporta múltiples opciones de exportación del mapa, lo que puede facilitar la creación del script de conversión a formato ASM. Además, es una herramienta Free Software, por lo que disponemos tanto del código fuente como del permiso para modificarlo y adaptarlo a nuestras necesidades.  Tiled es el editor más moderno de los 3, y soporta múltiples opciones de exportación del mapa, lo que puede facilitar la creación del script de conversión a formato ASM. Además, es una herramienta Free Software, por lo que disponemos tanto del código fuente como del permiso para modificarlo y adaptarlo a nuestras necesidades.
  
-\\+\\ 
 {{ :cursos:ensamblador:gfx5_mapeditor.png | Map Editor }} {{ :cursos:ensamblador:gfx5_mapeditor.png | Map Editor }}
-\\+\\ 
  
  Mappy es más antiguo y se diseñó para juegos basados en las librerías de PC "Allegro" y (actualmente) "SDL". El código fuente está basado en la librería Allegro y puede ser también modificado a nuestro antojo.  Mappy es más antiguo y se diseñó para juegos basados en las librerías de PC "Allegro" y (actualmente) "SDL". El código fuente está basado en la librería Allegro y puede ser también modificado a nuestro antojo.
Línea 3582: Línea 3579:
  
  
-\\+\\ 
 ===== Mapeando sobre pantallas virtuales ===== ===== Mapeando sobre pantallas virtuales =====
  
Línea 3589: Línea 3586:
  Esto implica la necesidad de 6912 bytes de memoria para evitar que el usuario vea la generación del mapa, por lo que no es normal utilizar esta técnica a menos que esté realmente justificado.  Esto implica la necesidad de 6912 bytes de memoria para evitar que el usuario vea la generación del mapa, por lo que no es normal utilizar esta técnica a menos que esté realmente justificado.
  
- Si utilizamos pantallas virtuales necesitaremos modificar todas las rutinas de impresión para que trabajen con una dirección destino diferente en lugar de sobre $4000. Para que las rutinas sigan pudiendo utilizar los trucos de composición de dirección en base a desplazamientos de bits que vimos en capítulos anteriores lo normal es que busquemos una dirección de memoria libre cuyos 3 bits más altos ya no sean "010b" (caso de la videoram -> $4000 -> 0100000000000000b) sino, por ejemplo "110" ($C000). De esta forma se pueden alterar las rutinas fácilmente para trabajar sobre un área de 7KB equivalente a la videoram pero comenzando en $C000.+ Si utilizamos pantallas virtuales necesitaremos modificar todas las rutinas de impresión para que trabajen con una dirección destino diferente en lugar de sobre $4000. Para que las rutinas sigan pudiendo utilizar los trucos de composición de dirección en base a desplazamientos de bits que vimos en capítulos anteriores lo normal es que busquemos una dirección de memoria libre cuyos 3 bits más altos ya no sean "010b" (caso de la videoram -> $4000 -> 0100000000000000b) sino, por ejemplo "110" ($c000). De esta forma se pueden alterar las rutinas fácilmente para trabajar sobre un área de 7KB equivalente a la videoram pero comenzando en $c000.
  
- Por otra parte, si nos estamos planteando el usar una pantalla virtual simplemente para que no se vea el proceso de construcción de la pantalla, podemos ahorrarnos la pantalla virtual separando la rutina de impresión de pantalla en 2: una de impresión de gráficos y otra de impresión de atributos. Así, rellenamos el área de pantalla que aloja el mapa con atributos "cero" (negro), trazamos los datos gráficos (que no podrá ver el usuario) y después trazamos los atributos. Estos atributos podemos trazarlos directamente en pantalla tras un HALT, o en una "tabla de atributos virtual" de 768 que después copiaremos sobre el área de atributos. De esta forma utilizamos una pantalla virtual de 768 bytes en lugar de requerir 6912.+ Por otra parte, si nos estamos planteando el usar una pantalla virtual simplemente para que no se vea el proceso de construcción de la pantalla, podemos ahorrarnos la pantalla virtual separando la rutina de impresión de pantalla en 2: una de impresión de gráficos y otra de impresión de atributos. Así, rellenamos el área de pantalla que aloja el mapa con atributos "cero" (negro), trazamos los datos gráficos (que no podrá ver el usuario) y después trazamos los atributos. Estos atributos podemos trazarlos directamente en pantalla tras un halt, o en una "tabla de atributos virtual" de 768 que después copiaremos sobre el área de atributos. De esta forma utilizamos una pantalla virtual de 768 bytes en lugar de requerir 6912.
  
  
-\\+\\ 
 ===== Mapeando en posiciones de alta resolución ===== ===== Mapeando en posiciones de alta resolución =====
  
Línea 3622: Línea 3619:
  
  
-\\+\\ 
 ===== Enlaces ===== ===== Enlaces =====
  
  • cursos/ensamblador/gfx5_mapeados.1704559475.txt.gz
  • Última modificación: 06-01-2024 16:44
  • por sromero