Diferencias
Muestra las diferencias entre dos versiones de la página.
Ambos lados, revisión anteriorRevisión previaPróxima revisión | Revisión previaÚltima revisiónAmbos lados, revisión siguiente | ||
cursos:ensamblador:gfx5_mapeados [06-01-2024 15:02] – [Enlaces] sromero | cursos:ensamblador:gfx5_mapeados [19-01-2024 08:25] – 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: | ||
| | ||
- | \\ | + | \\ |
{{ cursos: | {{ cursos: | ||
;#; | ;#; | ||
//Tilemap: componiendo un mapa en pantalla\\ a partir de tiles de un tileset/ | //Tilemap: componiendo un mapa en pantalla\\ a partir de tiles de un tileset/ | ||
;#; | ;#; | ||
- | \\ | + | \\ |
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. | ||
- | \\ | + | \\ |
{{ : | {{ : | ||
- | \\ | + | \\ |
Y no sólo podemos realizar juego de puzzle tipo Puzznic, Plotting o Sokoban: en base a tiles podemos crear videoaventuras, | Y no sólo podemos realizar juego de puzzle tipo Puzznic, Plotting o Sokoban: en base a tiles podemos crear videoaventuras, | ||
- | \\ | + | \\ |
===== Organización de los mapas en memoria ===== | ===== Organización de los mapas en memoria ===== | ||
Línea 50: | Línea 50: | ||
| | ||
- | \\ | + | \\ |
===== 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: | ||
- | \\ | + | \\ |
{{ : | {{ : | ||
- | \\ | + | \\ |
| | ||
- | \\ | + | \\ |
{{ : | {{ : | ||
- | \\ | + | \\ |
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: | ||
- | \\ | + | \\ |
- | {{ : | + | {{ : |
- | \\ | + | \\ |
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" | 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" | ||
Línea 85: | Línea 85: | ||
| | ||
- | \\ | + | \\ |
- | \\ | + | \\ |
**Organización horizontal** | **Organización horizontal** | ||
Línea 92: | Línea 92: | ||
<code z80> | <code z80> | ||
- | | + | |
</ | </ | ||
Línea 98: | Línea 98: | ||
<code z80> | <code z80> | ||
- | | + | |
</ | </ | ||
Línea 104: | Línea 104: | ||
<code z80> | <code z80> | ||
- | | + | |
</ | </ | ||
Línea 115: | Línea 115: | ||
sokoban_LEVEL1_h: | sokoban_LEVEL1_h: | ||
- | | + | |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
</ | </ | ||
- | 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 '' |
<code z80> | <code z80> | ||
sokoban_LEVEL1_h: | sokoban_LEVEL1_h: | ||
- | DEFB 8, | + | |
</ | </ | ||
Línea 140: | Línea 140: | ||
| | ||
- | \\ | + | \\ |
- | \\ | + | \\ |
**Organización vertical** | **Organización vertical** | ||
Línea 148: | Línea 148: | ||
| | ||
- | \\ | + | \\ |
- | {{ : | + | {{ : |
- | \\ | + | \\ |
En este caso, la pantalla comenzaría con 4 columnas de datos " | En este caso, la pantalla comenzaría con 4 columnas de datos " | ||
<code z80> | <code z80> | ||
- | | + | |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
</ | </ | ||
Línea 164: | Línea 164: | ||
<code z80> | <code z80> | ||
- | | + | |
</ | </ | ||
Línea 170: | Línea 170: | ||
<code z80> | <code z80> | ||
- | | + | |
</ | </ | ||
Línea 181: | Línea 181: | ||
sokoban_LEVEL1_v: | sokoban_LEVEL1_v: | ||
- | | + | |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
- | DEFB 8, | + | DEFB 8, |
</ | </ | ||
- | En este caso nuestros | + | En este caso nuestros |
| | ||
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, | + | 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 ('' |
| | ||
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: | ||
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | CALL Draw_Sprite | + | call Draw_Sprite |
</ | </ | ||
Línea 243: | Línea 243: | ||
| | ||
- | 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 ('' |
- | 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 ('' |
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: | ||
- | | + | |
- | FOR X=0 TO ANCHO_PANTALLA: | + | FOR X=0 TO ANCHO_PANTALLA: |
- | | + | TILE = [DIR_PANTALLA] |
- | | + | DIR_PANTALLA = DIR_PANTALLA + 1 |
- | PUSH DIR_MEM | + | push dIR_MEM |
- | | + | DIR_SPRITE = BASE_TILESET + (TILE*ANCHO_TILE*ALTO_TILE) |
- | | + | Dibujar Sprite desde DIR_SPRITE a DIR_MEM |
- | POP DIR_MEM | + | pop dIR_MEM |
- | | + | DIR_MEM = DIR_MEM + ANCHO_TILE |
</ | </ | ||
- | 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 '' |
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 ('' |
* Utilizaremos el valor de tile 255 como un tile " | * Utilizaremos el valor de tile 255 como un tile " | ||
Línea 288: | Línea 288: | ||
| | ||
A = (IX) | A = (IX) | ||
- | | + | |
Si A == 255 : | Si A == 255 : | ||
- | JR saltar_bloque | + | jr saltar_bloque |
- | | + | |
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) | ||
- | | + | |
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 | ||
- | | + | |
| | ||
HL = HL + ANCHO_TILE | HL = HL + ANCHO_TILE | ||
- | | + | |
- | | + | |
</ | </ | ||
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 ;;;;;; | + | |
- | LD IX, (DM_MAP) | + | ld ix, (DM_MAP) |
- | LD A, (DM_HEIGHT) | + | ld a, (DM_HEIGHT) |
- | LD B, A ; B = ALTO_EN_TILES (para bucle altura) | + | ld b, a ; 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) | + | ld a, (DM_HEIGHT) |
- | SUB B ; A = ALTO - iteracion_bucle = Y actual | + | sub b ; A = ALTO - iteracion_bucle = Y actual |
- | RLCA ; A = Y * 2 | + | |
- | ;;; Calculamos la direccion destino en pantalla como | + | |
- | | + | ;;; DIR_PANT = DIRECCION(X_INICIAL, |
- | LD BC, (DM_COORD_X) | + | ld bc, (DM_COORD_X) |
- | ADD A, B | + | add a, b |
- | LD B, A | + | ld b, a |
- | LD A, B | + | ld a, b |
- | AND $18 | + | |
- | ADD A, $40 | + | add a, $40 |
- | LD H, A | + | ld h, a |
- | LD A, B | + | ld a, b |
- | AND 7 | + | |
- | RRCA | + | rrca |
- | RRCA | + | rrca |
- | RRCA | + | rrca |
- | ADD A, C | + | add a, c |
- | LD L, A ; HL = DIR_PANTALLA(X_INICIAL, | + | ld l, a ; HL = DIR_PANTALLA(X_INICIAL, |
- | LD A, (DM_WIDTH) | + | ld a, (DM_WIDTH) |
- | LD B, A ; B = ANCHO_EN_TILES | + | ld b, a ; 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 |
- | JP Z, drawm16_next | + | jp z, drawm16_next |
- | LD B, A | + | ld b, a |
- | EX AF, AF' | + | ex af, af' |
- | LD A, B | + | ld a, b |
- | ;;; Calcular posicion origen (array sprites) en HL como: | + | |
- | | + | ;;; |
- | EX DE, HL ; Intercambiamos DE y HL (DE=destino) | + | ex de, hl ; 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 H, A | + | ld h, a |
- | ADD HL, BC ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 32) | + | add hl, bc ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 32) |
- | EX DE, HL ; Intercambiamos DE y HL (DE=origen, HL=destino) | + | ex de, hl ; 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 | + | |
- | 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), A ; 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), A ; 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 | + | |
- | 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) | + | |
- | | + | ; desde el septimo scanline de la fila Y+1 al primero de la Y+2 |
- | LD A, L | + | ld a, l |
- | ADD A, 31 | + | add a, 31 |
- | LD L, A | + | ld l, a |
- | JR C, drawm16_nofix_abajop | + | jr c, drawm16_nofix_abajop |
- | LD A, H | + | ld a, h |
- | SUB 8 | + | |
- | LD H, A | + | ld h, a |
drawm16_nofix_abajop: | drawm16_nofix_abajop: | ||
- | ;;; 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), A ; 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), A ; 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 | + | |
- | ;;; En este punto, los 16 scanlines del tile estan dibujados. | + | |
- | ;;;;;; 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. | + | |
- | LD A, H ; Codigo de Get_Attr_Offset_From_Image | + | ld a, h ; Codigo de Get_Attr_Offset_From_Image |
- | RRCA | + | rrca |
- | RRCA | + | rrca |
- | RRCA | + | rrca |
- | AND 3 | + | |
- | OR $58 | + | |
- | LD D, A | + | ld d, a |
- | LD E, L ; DE tiene el offset del attr de HL | + | ld e, l ; DE tiene el offset del attr de HL |
- | LD HL, (DM_ATTRIBS) | + | ld hl, (DM_ATTRIBS) |
- | EX AF, AF' | + | ex af, af' |
- | LD C, A | + | ld c, a |
- | LD B, 0 | + | ld b, 0 |
- | ADD HL, BC | + | add hl, bc |
- | ADD HL, BC | + | add hl, bc |
- | ADD HL, BC | + | add hl, bc |
- | ADD HL, BC ; HL = HL+HL=(DS_NUMSPR*4) = Origen de atributo | + | add hl, bc ; HL = HL+HL=(DS_NUMSPR*4) = Origen de atributo |
- | LDI | + | ldi |
- | LDI ; Imprimimos la primeras fila de atributos | + | |
- | ;;; Avance diferencial a la siguiente linea de atributos | + | |
- | LD A, E ; A = E | + | ld a, e ; 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, A ; Guardamos en E (E = E+30 + 2 por LDI=E+32) | + | ld e, a ; 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 | + | |
- | 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) | + | |
- | POP BC | + | pop bc |
- | DEC B ; Bucle vertical | + | dec b ; Bucle vertical |
- | JP NZ, drawm16_yloop | + | jp nz, drawm16_yloop |
- | RET | + | ret |
</ | </ | ||
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 | + | 1.- Eliminar el '' |
- | 2.- Eliminar los **INC DE**, **INC H** y **DEC L** antes del **DJNZ | + | 2.- Eliminar los '' |
- | \\ | + | \\ |
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 " | No obstante, si estamos ante un juego de pantallas " | ||
- | \\ | + | \\ |
==== 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 | + | |
- | CALL ClearScreen_Pattern | + | call ClearScreen_Pattern |
- | LD HL, sokoban1_gfx | + | ld hl, sokoban1_gfx |
- | | + | |
- | LD HL, sokoban1_attr | + | ld hl, sokoban1_attr |
- | | + | |
- | LD HL, sokoban_LEVEL1 | + | ld hl, sokoban_LEVEL1 |
- | | + | |
- | LD A, 16 | + | ld a, 16 |
- | | + | |
- | LD A, 12 | + | ld a, 12 |
- | | + | |
- | XOR A | + | xor a |
- | | + | |
- | | + | |
- | CALL DrawMap_16x16 | + | call DrawMap_16x16 |
loop: | loop: | ||
- | JR loop | + | jr loop |
; | ; | ||
ClearScreen_Pattern: | ClearScreen_Pattern: | ||
- | (...) ; Rellenado de fondo con un patron | + | |
- | RET ; (del capitulo de Sprites Lowres) | + | |
; | ; | ||
Línea 568: | Línea 566: | ||
DM_WIDTH | DM_WIDTH | ||
DM_HEIGHT | DM_HEIGHT | ||
- | |||
; | ; | ||
Línea 574: | Línea 571: | ||
; | ; | ||
sokoban_LEVEL1: | sokoban_LEVEL1: | ||
- | | + | |
- | DEFB 255, | + | DEFB 255, |
- | DEFB 255, | + | DEFB 255, |
- | DEFB 255, | + | DEFB 255, |
- | DEFB 255, | + | DEFB 255, |
- | DEFB 255, | + | DEFB 255, |
- | DEFB 255, | + | DEFB 255, |
- | DEFB 255, | + | DEFB 255, |
- | DEFB 255, | + | DEFB 255, |
- | DEFB 255, | + | DEFB 255, |
- | DEFB 255, | + | DEFB 255, |
- | DEFB 255, | + | DEFB 255, |
; | ; | ||
Línea 597: | Línea 593: | ||
sokoban1_gfx: | sokoban1_gfx: | ||
- | | + | |
- | DEFB | + | DEFB |
- | DEFB 127, | + | DEFB 127, |
- | DEFB | + | DEFB |
- | DEFB 127, | + | DEFB 127, |
- | DEFB 131, | + | DEFB 131, |
- | DEFB 255, | + | DEFB 255, |
- | DEFB 255, | + | DEFB 255, |
- | DEFB 127, | + | DEFB 127, |
- | DEFB 195, | + | DEFB 195, |
- | DEFB 255, | + | DEFB 255, |
- | DEFB 255, | + | DEFB 255, |
- | DEFB | + | DEFB |
- | DEFB 107, | + | DEFB 107, |
- | DEFB | + | DEFB |
- | DEFB 96, 6, 96, 6, 0, 0, 96, 6, 96, 6, | + | DEFB 96, 6, 96, 6, 0, 0, 96, 6, 96, 6, |
sokoban1_attr: | sokoban1_attr: | ||
- | | + | |
- | 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 | + | |
</ | </ | ||
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: | ||
- | \\ | + | \\ |
- | {{ : | + | {{ : |
- | \\ | + | \\ |
| | ||
Línea 636: | Línea 632: | ||
; | ; | ||
sokoban_LEVEL1: | sokoban_LEVEL1: | ||
- | | + | |
- | DEFB | + | DEFB |
- | DEFB | + | DEFB |
- | DEFB | + | DEFB |
- | DEFB | + | DEFB |
- | DEFB | + | DEFB |
- | DEFB 255, | + | DEFB 255, |
- | DEFB 255, | + | DEFB 255, |
- | DEFB 255, | + | DEFB 255, |
</ | </ | ||
Línea 650: | Línea 646: | ||
<code z80> | <code z80> | ||
- | LD HL, sokoban1_gfx | + | ld hl, sokoban1_gfx |
- | | + | |
- | LD HL, sokoban1_attr | + | ld hl, sokoban1_attr |
- | | + | |
- | LD HL, sokoban_LEVEL1 | + | ld hl, sokoban_LEVEL1 |
- | | + | |
- | LD A, 7 | + | ld a, 7 |
- | | + | |
- | LD A, 9 | + | ld a, 9 |
- | | + | |
- | LD A, 8 | + | ld a, 8 |
- | | + | |
- | LD A, 3 | + | ld a, 3 |
- | | + | |
- | | + | |
</ | </ | ||
- | 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 " | + | Si tenemos pantallas de diferentes tamaños podemos almacenarlas en memoria utilizando una estructura de datos con 4 bytes de información por pantalla: |
Esto puede valer para juegos como Sokoban (donde cada pantalla puede tener un tamaño diferente) pero no para juegos tipo plataformas/ | Esto puede valer para juegos como Sokoban (donde cada pantalla puede tener un tamaño diferente) pero no para juegos tipo plataformas/ | ||
- | \\ | + | \\ |
==== 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 ;;;;;; | + | |
- | LD IX, (DM_MAP) | + | ld ix, (DM_MAP) |
- | LD A, (DM_HEIGHT) | + | ld a, (DM_HEIGHT) |
- | LD B, A ; B = ALTO_EN_TILES (para bucle altura) | + | ld b, a ; 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) | + | ld a, (DM_HEIGHT) |
- | SUB B ; A = ALTO - iteracion_bucle = Y actual | + | sub b ; A = ALTO - iteracion_bucle = Y actual |
- | | + | ;;; NUEVO: Eliminamos |
- | ;;; Calculamos la direccion destino en pantalla como | + | |
- | | + | ;;; DIR_PANT = DIRECCION(X_INICIAL, |
- | LD BC, (DM_COORD_X) | + | ld bc, (DM_COORD_X) |
- | ADD A, B | + | add a, b |
- | LD B, A | + | ld b, a |
- | LD A, B | + | ld a, b |
- | AND $18 | + | |
- | ADD A, $40 | + | add a, $40 |
- | LD H, A | + | ld h, a |
- | LD A, B | + | ld a, b |
- | AND 7 | + | |
- | RRCA | + | rrca |
- | RRCA | + | rrca |
- | RRCA | + | rrca |
- | ADD A, C | + | add a, c |
- | LD L, A ; HL = DIR_PANTALLA(X_INICIAL, | + | ld l, a ; HL = DIR_PANTALLA(X_INICIAL, |
- | LD A, (DM_WIDTH) | + | ld a, (DM_WIDTH) |
- | LD B, A ; B = ANCHO_EN_TILES | + | ld b, a ; 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 |
- | JP Z, drawm8_next | + | jp z, drawm8_next |
- | LD B, A | + | ld b, a |
- | EX AF, AF' | + | ex af, af' |
- | LD A, B | + | ld a, b |
- | ;;; Calcular posicion origen (array sprites) en HL como: | + | |
- | | + | ;;; |
- | EX DE, HL ; Intercambiamos DE y HL (DE=destino) | + | ex de, hl ; Intercambiamos DE y HL (DE=destino) |
- | LD BC, (DM_SPRITES) | + | ld bc, (DM_SPRITES) |
- | LD L, A | + | ld l, a |
- | LD H, 0 | + | ld h, 0 |
- | ADD HL, HL | + | add hl, hl |
- | ADD HL, HL | + | add hl, hl |
- | ADD HL, HL ;;; NUEVO: NUM_SPRITE*8 en lugar de *32 | + | add hl, hl ;;; NUEVO: NUM_SPRITE*8 en lugar de *32 |
- | ADD HL, BC ; HL = BC + HL = DM_SPRITES + (DM_NUMSPR * 8) | + | add hl, bc ; HL = BC + HL = DM_SPRITES + (DM_NUMSPR * 8) |
- | EX DE, HL ; Intercambiamos DE y HL (DE=origen, HL=destino) | + | ex de, hl ; 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 | + | |
- | LD B, 8 | + | ld b, 8 |
- | drawm8_loop: | + | drawm8_loop: |
- | LD A, (DE) ; Bloque 1: Leemos dato del sprite | + | ld a, (de) ; Bloque 1: Leemos dato del sprite |
- | LD (HL), A ; 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 | + | |
- | ;;; En este punto, los 8 scanlines del tile estan dibujados. | + | |
- | ;;;;;; 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. | + | |
- | LD A, H ; Codigo de Get_Attr_Offset_From_Image | + | ld a, h ; Codigo de Get_Attr_Offset_From_Image |
- | RRCA | + | rrca |
- | RRCA | + | rrca |
- | RRCA | + | rrca |
- | AND 3 | + | |
- | OR $58 | + | |
- | LD D, A | + | ld d, a |
- | LD E, L ; DE tiene el offset del attr de HL | + | ld e, l ; DE tiene el offset del attr de HL |
- | LD HL, (DM_ATTRIBS) | + | ld hl, (DM_ATTRIBS) |
- | EX AF, AF' | + | ex af, af' |
- | LD C, A | + | ld c, a |
- | LD B, 0 ;;; NUEVO: HL = HL+DM_NUMSPR (NO *4) | + | ld b, 0 ;;; NUEVO: HL = HL+DM_NUMSPR (NO *4) |
- | ADD HL, BC ; HL = HL+DM_NUMSPR = Origen de atributo | + | add hl, bc ; HL = HL+DM_NUMSPR = Origen de atributo |
- | LD A, (HL) | + | ld a, (hl) |
- | LD (DE), A ;;; 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) | + | |
- | POP BC | + | pop bc |
- | DEC B ; Bucle vertical | + | dec b ; Bucle vertical |
- | JP NZ, drawm8_yloop | + | jp nz, drawm8_yloop |
- | RET | + | ret |
</ | </ | ||
- | \\ | + | \\ |
===== 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 " | * Estática: El jugador cambia de una pantalla a otra al acabar el nivel, sin que las pantallas estén " | ||
* 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, | * Lineal en dos ejes: Las pantallas se agrupan en forma de mapa bidimensional, | ||
- | \\ | + | \\ |
La // | La // | ||
Línea 815: | Línea 811: | ||
Hay 2 posibilidades de agrupación de las pantallas: como un **array de pantallas**, | Hay 2 posibilidades de agrupación de las pantallas: como un **array de pantallas**, | ||
- | \\ | + | \\ |
==== Mapa como array de pantallas ==== | ==== Mapa como array de pantallas ==== | ||
Línea 824: | Línea 820: | ||
<code z80> | <code z80> | ||
Pantalla_Inicio: | Pantalla_Inicio: | ||
- | | + | |
Pantalla_Salon: | Pantalla_Salon: | ||
- | | + | |
Pantalla_Pasillo: | Pantalla_Pasillo: | ||
- | | + | |
Pantalla_Escalera: | Pantalla_Escalera: | ||
- | | + | |
(...) | (...) | ||
Línea 842: | Línea 838: | ||
<code z80> | <code z80> | ||
Mapa: | Mapa: | ||
- | | + | |
- | DW Pantalla_Salon | + | DW Pantalla_Salon |
- | DW Pantalla_Pasillo | + | DW Pantalla_Pasillo |
- | DW Pantalla_Escalera | + | DW Pantalla_Escalera |
- | (...) ; (etc...) | + | (...) ; (etc...) |
- | DW 0000 ; Fin de pantalla | + | DW 0000 ; Fin de pantalla |
</ | </ | ||
Línea 865: | Línea 861: | ||
<code z80> | <code z80> | ||
- | | + | |
- | LD BC, Mapa ; BC = Inicio de la tabla Mapa | + | ld bc, Mapa ; BC = Inicio de la tabla Mapa |
- | LD A, (pantalla_actual) | + | ld a, (pantalla_actual) |
- | LD L, A | + | ld l, a |
- | LD H, 0 ; HL = Pantalla actual | + | ld h, 0 ; HL = Pantalla actual |
- | SLA L | + | sla l |
- | | + | |
- | ADD HL, BC ; HL = Mapa + (Pantalla actual * 2) | + | add hl, bc ; HL = Mapa + (Pantalla actual * 2) |
- | | + | |
- | 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 L, A | + | ld l, a |
- | | + | |
- | | + | |
</ | </ | ||
Línea 905: | Línea 901: | ||
Mapa: | Mapa: | ||
- | | + | |
- | DB -1, 1 ; Conexiones izq y derecha ID 0 | + | DB -1, 1 ; Conexiones izq y derecha ID 0 |
- | DW Pantalla_Salon | + | DW Pantalla_Salon |
- | DB 0, 2 ; Conexiones izq y derecha ID 1 | + | DB 0, 2 ; Conexiones izq y derecha ID 1 |
- | DW Pantalla_Pasillo | + | DW Pantalla_Pasillo |
- | DB 1, 3 ; Conexiones izq y derecha ID 2 | + | DB 1, 3 ; Conexiones izq y derecha ID 2 |
- | DW Pantalla_Escalera | + | DW Pantalla_Escalera |
- | DB 3, -1 ; Conexiones izq y derecha ID 3 | + | DB 3, -1 ; Conexiones izq y derecha ID 3 |
- | (...) | + | (...) |
</ | </ | ||
Línea 928: | Línea 924: | ||
<code z80> | <code z80> | ||
BYTES_POR_PANTALLA | BYTES_POR_PANTALLA | ||
- | RUTINA_ROM_HL_POR_DE | + | RUTINA_ROM_HL_POR_DE |
; | ; | ||
Línea 939: | Línea 935: | ||
; | ; | ||
Get_Screen_Pointer: | Get_Screen_Pointer: | ||
- | LD H, 0 | + | ld h, 0 |
- | LD D, H ; HL = PANTALLA | + | ld d, h ; HL = PANTALLA |
- | LD E, BYTES_POR_PANTALLA | + | ld e, BYTES_POR_PANTALLA |
- | | + | |
- | ADD HL, BC ; Lo sumamos al inicio del MAPA | + | add hl, bc ; Lo sumamos al inicio del MAPA |
- | | + | |
</ | </ | ||
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. | ||
- | | + | |
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> | ||
- | | + | |
- | LD HL, sokoban1_gfx | + | ld hl, sokoban1_gfx |
- | | + | |
- | LD HL, sokoban1_attr | + | ld hl, sokoban1_attr |
- | | + | |
- | LD A, 16 | + | ld a, 16 |
- | | + | |
- | LD A, 12 | + | ld a, 12 |
- | | + | |
- | XOR A | + | xor a |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
DibujarPantalla: | DibujarPantalla: | ||
- | LD BC, Mapa | + | ld bc, Mapa |
- | LD A, (pantalla_actual) | + | ld a, (pantalla_actual) |
- | LD L, A | + | ld l, a |
- | | + | |
- | 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 L, A | + | ld l, a |
- | | + | |
- | | + | |
- | 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 |
- | | + | |
- | 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 |
- | | + | |
</ | </ | ||
Línea 998: | Línea 994: | ||
<code z80> | <code z80> | ||
Mapa: | Mapa: | ||
- | | + | |
- | DW Pantalla_Salon | + | DW Pantalla_Salon |
- | DW Pantalla_Pasillo | + | DW Pantalla_Pasillo |
- | DW Pantalla_Escalera | + | DW Pantalla_Escalera |
- | (...) | + | (...) |
Conexiones: | Conexiones: | ||
- | | + | |
- | 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 |
- | (...) | + | (...) |
</ | </ | ||
Línea 1033: | Línea 1029: | ||
<code z80> | <code z80> | ||
Mapa: | Mapa: | ||
- | | + | |
- | DW -1, 1 ; Conexiones izq y derecha ID 0 | + | DW -1, 1 ; Conexiones izq y derecha ID 0 |
</ | </ | ||
Línea 1044: | Línea 1040: | ||
;;; Vector de direcciones de pantalla. | ;;; Vector de direcciones de pantalla. | ||
Mapa: | Mapa: | ||
- | | + | |
- | DB -1, 1 ; Conexiones izq y derecha ID 0 | + | DB -1, 1 ; Conexiones izq y derecha ID 0 |
- | DW titulo_inicio | + | DW titulo_inicio |
- | DW Pantalla_Salon | + | DW Pantalla_Salon |
- | DB 0, 2 ; Conexiones izq y derecha ID 1 | + | DB 0, 2 ; Conexiones izq y derecha ID 1 |
- | DW titulo_salon | + | DW titulo_salon |
- | DW Pantalla_Pasillo | + | DW Pantalla_Pasillo |
- | 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 | + | DW Pantalla_Escalera |
- | 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 | titulo_inicio | ||
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, | 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, | ||
- | \\ | + | \\ |
- | \\ | + | \\ |
**Repetición de pantallas** | **Repetición de pantallas** | ||
- | \\ | + | \\ |
- | \\ | + | \\ |
| | ||
Línea 1088: | Línea 1084: | ||
<code z80> | <code z80> | ||
Mapa: | Mapa: | ||
- | | + | |
- | DB -1, 1 ; Conexiones izq y derecha ID 0 | + | DB -1, 1 ; Conexiones izq y derecha ID 0 |
- | DW Pantalla_Salon | + | DW Pantalla_Salon |
- | DB 0, 2 ; Conexiones izq y derecha ID 1 | + | DB 0, 2 ; Conexiones izq y derecha ID 1 |
- | DW Pantalla_Pasillo | + | DW Pantalla_Pasillo |
- | DB 1, 3 ; Conexiones izq y derecha ID 2 | + | DB 1, 3 ; Conexiones izq y derecha ID 2 |
- | DW Pantalla_Pasillo | + | DW Pantalla_Pasillo |
- | DB 2, 4 ; Conexiones izq y derecha ID 3 | + | DB 2, 4 ; Conexiones izq y derecha ID 3 |
- | DW Pantalla_Pasillo | + | DW Pantalla_Pasillo |
- | DB 3, 5 ; Conexiones izq y derecha ID 4 | + | DB 3, 5 ; Conexiones izq y derecha ID 4 |
- | DW Pantalla_Escalera | + | DW Pantalla_Escalera |
- | DB 4, -1 ; Conexiones izq y derecha ID 5 | + | DB 4, -1 ; Conexiones izq y derecha ID 5 |
- | (...) | + | (...) |
</ | </ | ||
Línea 1108: | Línea 1104: | ||
- | \\ | + | \\ |
- | \\ | + | \\ |
**Transiciones entre 2 pantallas de mapa** | **Transiciones entre 2 pantallas de mapa** | ||
- | \\ | + | \\ |
- | \\ | + | \\ |
| | ||
- | \\ | + | \\ |
* 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 " | * Scroll de pantallas: Realizamos un scroll entre la pantalla que sale y la que entra. Consiste en realizar una " | ||
- | \\ | + | \\ |
- | \\ | + | \\ |
==== El mapa como array global de mapeado ==== | ==== El mapa como array global de mapeado ==== | ||
Línea 1138: | Línea 1134: | ||
Mapa | Mapa | ||
- | DB A, | + | |
- | DB A, | + | DB A, |
- | DB A, | + | DB A, |
- | DB A, | + | DB A, |
- | DB A, | + | DB A, |
- | DB A, | + | DB A, |
- | DB A, | + | DB A, |
- | DB A, | + | DB A, |
- | DB C, | + | DB C, |
- | DB C, | + | DB C, |
- | DB C, | + | DB C, |
- | DB C, | + | DB C, |
- | DB C, | + | DB C, |
- | DB C, | + | DB C, |
- | DB C, | + | DB C, |
- | DB C, | + | DB C, |
</ | </ | ||
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 " | 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 " | ||
- | 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), | + | 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 ('' |
- | \\ | + | \\ |
- | * 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 '' |
- | * 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 '' |
- | \\ | + | \\ |
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) | + | ld ix, (DM_MAP) |
- | ;;; 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 | + | |
- | LD BC, (DM_MAPX) | + | ld bc, (DM_MAPX) |
- | ADD HL, BC ; HL = MAPA_X + (ANCHO_MAPA * MAPA_Y) | + | add hl, bc ; HL = MAPA_X + (ANCHO_MAPA * MAPA_Y) |
- | EX DE, HL | + | ex de, hl |
- | ADD IX, DE ; IX = Inicio_Mapa + HL | + | add ix, de ; IX = Inicio_Mapa + HL |
- | | + | ;;; FIN NUEVO |
</ | </ | ||
Línea 1190: | Línea 1186: | ||
<code z80> | <code z80> | ||
- | ;;; NUEVO: Incrementar puntero de mapa a siguiente linea | + | |
- | LD BC, ANCHO_MAPA_TILES - ANCHO_PANTALLA | + | ld bc, ANCHO_MAPA_TILES - ANCHO_PANTALLA |
- | ADD IX, BC | + | add ix, bc |
- | | + | ;;; FIN NUEVO |
- | ;;; 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 |
</ | </ | ||
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 | + | MULT_HL_POR_DE |
; | ; | ||
Línea 1250: | Línea 1245: | ||
DrawMap_16x16_Map: | DrawMap_16x16_Map: | ||
- | LD IX, (DM_MAP) | + | ld ix, (DM_MAP) |
- | ;;; 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 | + | |
- | LD BC, (DM_MAPX) | + | ld bc, (DM_MAPX) |
- | ADD HL, BC ; HL = MAPA_X + (ANCHO_MAPA * MAPA_Y) | + | add hl, bc ; HL = MAPA_X + (ANCHO_MAPA * MAPA_Y) |
- | EX DE, HL | + | ex de, hl |
- | ADD IX, DE ; IX = Inicio_Mapa + HL | + | add ix, de ; IX = Inicio_Mapa + HL |
- | | + | ;;; FIN NUEVO |
- | LD A, (DM_HEIGHT) | + | ld a, (DM_HEIGHT) |
- | LD B, A ; B = ALTO_EN_TILES (para bucle altura) | + | ld b, a ; 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) | + | ld a, (DM_HEIGHT) |
- | SUB B ; A = ALTO - iteracion_bucle = Y actual | + | sub b ; A = ALTO - iteracion_bucle = Y actual |
- | RLCA ; A = Y * 2 | + | |
- | ;;; Calculamos la direccion destino en pantalla como | + | |
- | | + | ;;; DIR_PANT = DIRECCION(X_INICIAL, |
- | LD BC, (DM_COORD_X) | + | ld bc, (DM_COORD_X) |
- | ADD A, B | + | add a, b |
- | LD B, A | + | ld b, a |
- | LD A, B | + | ld a, b |
- | AND $18 | + | |
- | ADD A, $40 | + | add a, $40 |
- | LD H, A | + | ld h, a |
- | LD A, B | + | ld a, b |
- | AND 7 | + | |
- | RRCA | + | rrca |
- | RRCA | + | rrca |
- | RRCA | + | rrca |
- | ADD A, C | + | add a, c |
- | LD L, A ; HL = DIR_PANTALLA(X_INICIAL, | + | ld l, a ; HL = DIR_PANTALLA(X_INICIAL, |
- | LD A, (DM_WIDTH) | + | ld a, (DM_WIDTH) |
- | LD B, A ; B = ANCHO_EN_TILES | + | ld b, a ; 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 |
- | JP Z, drawmg16_next | + | jp z, drawmg16_next |
- | LD B, A | + | ld b, a |
- | EX AF, AF' | + | ex af, af' |
- | LD A, B | + | ld a, b |
- | ;;; Calcular posicion origen (array sprites) en HL como: | + | |
- | | + | ;;; |
- | EX DE, HL ; Intercambiamos DE y HL (DE=destino) | + | ex de, hl ; 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 H, A | + | ld h, a |
- | ADD HL, BC ; HL = BC + HL = DM_SPRITES + (DM_NUMSPR * 32) | + | add hl, bc ; HL = BC + HL = DM_SPRITES + (DM_NUMSPR * 32) |
- | EX DE, HL ; Intercambiamos DE y HL (DE=origen, HL=destino) | + | ex de, hl ; 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 | + | |
- | 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), A ; 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), A ; 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 | + | |
- | 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) | + | |
- | | + | ; desde el septimo scanline de la fila Y+1 al primero de la Y+2 |
- | LD A, L | + | ld a, l |
- | ADD A, 31 | + | add a, 31 |
- | LD L, A | + | ld l, a |
- | JR C, drawmg16_nofix_abajop | + | jr c, drawmg16_nofix_abajop |
- | LD A, H | + | ld a, h |
- | SUB 8 | + | |
- | LD H, A | + | ld h, a |
drawmg16_nofix_abajop: | drawmg16_nofix_abajop: | ||
- | ;;; 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), A ; 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), A ; 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 | + | |
- | ;;; En este punto, los 16 scanlines del tile estan dibujados. | + | |
- | ;;;;;; 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. | + | |
- | LD A, H ; Codigo de Get_Attr_Offset_From_Image | + | ld a, h ; Codigo de Get_Attr_Offset_From_Image |
- | RRCA | + | rrca |
- | RRCA | + | rrca |
- | RRCA | + | rrca |
- | AND 3 | + | |
- | OR $58 | + | |
- | LD D, A | + | ld d, a |
- | LD E, L ; DE tiene el offset del attr de HL | + | ld e, l ; DE tiene el offset del attr de HL |
- | LD HL, (DM_ATTRIBS) | + | ld hl, (DM_ATTRIBS) |
- | EX AF, AF' | + | ex af, af' |
- | LD C, A | + | ld c, a |
- | LD B, 0 | + | ld b, 0 |
- | ADD HL, BC | + | add hl, bc |
- | ADD HL, BC | + | add hl, bc |
- | ADD HL, BC | + | add hl, bc |
- | ADD HL, BC ; HL = HL+HL=(DM_NUMSPR*4) = Origen de atributo | + | add hl, bc ; HL = HL+HL=(DM_NUMSPR*4) = Origen de atributo |
- | LDI | + | ldi |
- | LDI ; Imprimimos la primeras fila de atributos | + | |
- | ;;; Avance diferencial a la siguiente linea de atributos | + | |
- | LD A, E ; A = E | + | ld a, e ; 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, A ; Guardamos en E (E = E+30 + 2 por LDI=E+32) | + | ld e, a ; 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 | + | |
- | 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 | + | |
- | LD BC, ANCHO_MAPA_TILES - ANCHO_PANTALLA | + | ld bc, ANCHO_MAPA_TILES - ANCHO_PANTALLA |
- | ADD IX, BC | + | add ix, bc |
- | | + | ;;; FIN NUEVO |
- | ;;; 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 z80> | <code z80> | ||
Línea 1433: | Línea 1428: | ||
; | ; | ||
Map_Inc_X: | Map_Inc_X: | ||
- | LD HL, (DM_MAPX) | + | ld hl, (DM_MAPX) |
- | | + | |
- | LD A, H | + | ld a, h |
- | CP (ANCHO_MAPA_TILES-ANCHO_PANTALLA) / 256 | + | CP (ANCHO_MAPA_TILES-ANCHO_PANTALLA) / 256 |
- | RET NZ | + | ret nz |
- | LD A, L | + | ld a, l |
- | 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. |
- | | + | |
- | RET | + | ret |
; | ; | ||
Línea 1451: | Línea 1446: | ||
; | ; | ||
Map_Inc_Y: | Map_Inc_Y: | ||
- | LD HL, (DM_MAPY) | + | ld hl, (DM_MAPY) |
- | | + | |
- | LD A, H | + | ld a, h |
- | CP (ALTO_MAPA_TILES-ALTO_PANTALLA) / 256 | + | CP (ALTO_MAPA_TILES-ALTO_PANTALLA) / 256 |
- | RET NZ | + | ret nz |
- | LD A, L | + | ld a, l |
- | 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. |
- | | + | |
- | RET | + | ret |
; | ; | ||
Línea 1469: | Línea 1464: | ||
; | ; | ||
Map_Dec_X: | Map_Dec_X: | ||
- | LD HL, (DM_MAPX) | + | ld hl, (DM_MAPX) |
- | LD A, H | + | ld a, h |
- | AND A | + | and a |
- | JR NZ, mapdecx_doit | + | jr nz, mapdecx_doit |
- | LD A, L | + | ld a, l |
- | AND A | + | and a |
- | RET Z | + | ret z |
- | mapdecx_doit: | + | mapdecx_doit: |
- | DEC HL | + | dec hl |
- | | + | |
- | RET | + | ret |
; | ; | ||
Línea 1485: | Línea 1480: | ||
; | ; | ||
Map_Dec_Y: | Map_Dec_Y: | ||
- | LD HL, (DM_MAPY) | + | ld hl, (DM_MAPY) |
- | LD A, H | + | ld a, h |
- | AND A | + | and a |
- | JR NZ, mapdecy_doit | + | jr nz, mapdecy_doit |
- | LD A, L | + | ld a, l |
- | AND A | + | and a |
- | RET Z | + | ret z |
- | mapdecy_doit: | + | mapdecy_doit: |
- | DEC HL | + | dec hl |
- | | + | |
- | RET | + | ret |
</ | </ | ||
- | 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 '' |
| | ||
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 | + | |
- | LD HL, sokoban1_gfx | + | ld hl, sokoban1_gfx |
- | | + | |
- | LD HL, sokoban1_attr | + | ld hl, sokoban1_attr |
- | | + | |
- | LD HL, mapa_ejemplo | + | ld hl, mapa_ejemplo |
- | | + | |
- | LD A, ANCHO_PANTALLA | + | ld a, ANCHO_PANTALLA |
- | | + | |
- | LD A, ALTO_PANTALLA | + | ld a, ALTO_PANTALLA |
- | | + | |
- | XOR A | + | xor a |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
redraw: | redraw: | ||
- | CALL DrawMap_16x16_Map | + | call DrawMap_16x16_Map |
bucle: | bucle: | ||
- | CALL LEER_TECLADO | + | call LEER_TECLADO |
- | BIT 0, A ; Modificamos MAPX y MAPY segun OPQA | + | bit 0, a ; Modificamos MAPX y MAPY segun OPQA |
- | JR Z, nopulsada_q | + | jr z, nopulsada_q |
- | | + | |
- | | + | |
nopulsada_q: | nopulsada_q: | ||
- | BIT 1, A | + | bit 1, a |
- | JR Z, nopulsada_a | + | jr z, nopulsada_a |
- | | + | |
- | | + | |
nopulsada_a: | nopulsada_a: | ||
- | BIT 2, A | + | bit 2, a |
- | JR Z, nopulsada_p | + | jr z, nopulsada_p |
- | | + | |
- | | + | |
nopulsada_p: | nopulsada_p: | ||
- | BIT 3, A | + | bit 3, a |
- | JR Z, nopulsada_o | + | jr z, nopulsada_o |
- | | + | |
- | | + | |
nopulsada_o: | nopulsada_o: | ||
- | JR bucle | + | jr bucle |
loop: | 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) |
- | | + | |
- | JR NZ, Control_no_up | + | jr nz, Control_no_up |
- | | + | |
Control_no_up: | Control_no_up: | ||
- | LD BC, $FDFE | + | ld bc, $fdfe |
- | IN A, (C) | + | in a, (c) |
- | | + | |
- | JR NZ, Control_no_down | + | jr nz, Control_no_down |
- | | + | |
Control_no_down: | Control_no_down: | ||
- | LD BC, $DFFE | + | ld bc, $dffe |
- | IN A, (C) | + | in a, (c) |
- | | + | |
- | JR NZ, Control_no_right | + | jr nz, Control_no_right |
- | | + | |
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) |
- | | + | |
- | JR NZ, Control_no_left | + | jr nz, Control_no_left |
- | | + | |
Control_no_left: | Control_no_left: | ||
- | LD A, D ; Devolvemos en A el estado de las teclas | + | ld a, d ; 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 | + | MULT_HL_POR_DE |
; | ; | ||
Línea 1620: | Línea 1616: | ||
; | ; | ||
mapa_ejemplo: | mapa_ejemplo: | ||
- | | + | |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | DEFB 1, | + | DEFB 1, |
- | END 35000 | + | |
</ | </ | ||
| | ||
- | \\ | + | \\ |
- | {{ : | + | {{ : |
- | \\ | + | \\ |
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 " | 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 " | ||
- | Para esta implementación serían necesarias 4 rutinas de scroll de una porción de videomemoria, | + | Para esta implementación serían necesarias 4 rutinas de scroll de una porción de videomemoria, |
- | El scroll mediante instrucciones de transferencia de N-1 FILAS o N-1 COLUMNAS de pantalla | + | El scroll mediante instrucciones de transferencia de N-1 FILAS o N-1 COLUMNAS de pantalla |
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 " | 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 " | ||
- | \\ | + | \\ |
===== 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. |
+ | |||
+ | 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**, | |
- | | + | |
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/ | 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/ | ||
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 " | A la hora de codificar las pantallas, podemos optar por diferentes " | ||
- | \\ | + | \\ |
* **Codificación básica**: se almacenan en el vector " | * **Codificación básica**: se almacenan en el vector " | ||
* **Codificación por agrupación horizontal**: | * **Codificación por agrupación horizontal**: | ||
* **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, | * **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, | ||
- | \\ | + | \\ |
| | ||
- | \\ | + | \\ |
=== 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, | + | DEFB 0, |
- | DEFB 0, | + | DEFB 0, |
- | DEFB 0, | + | DEFB 0, |
- | DEFB 0, | + | DEFB 0, |
- | DEFB 0, | + | DEFB 0, |
- | DEFB 0, | + | DEFB 0, |
- | DEFB 0, | + | DEFB 0, |
- | DEFB 0, | + | DEFB 0, |
- | DEFB 0, | + | DEFB 0, |
- | DEFB 0, | + | DEFB 0, |
- | DEFB 0, | + | DEFB 0, |
</ | </ | ||
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. | ||
- | | + | |
<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), | + | |
- | LD HL, (DM_ATTRIBS) | + | ld hl, (DM_ATTRIBS) |
- | LD (DS_ATTRIBS), | + | |
- | LD BC, (DM_COORD_X) | + | ld bc, (DM_COORD_X) |
- | LD DE, (DM_MAP) | + | ld de, (DM_MAP) |
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 |
- | RET Z ; En ese caso, salir | + | ret z ; En ese caso, salir |
- | ADD A, C ; A = (X_INICIO + COORD_X_TILE) | + | add a, c ; A = (X_INICIO + COORD_X_TILE) |
- | RLCA ; A = (X_INICIO + COORD_X_TILE) * 2 | + | |
- | LD (DS_COORD_X), | + | |
- | 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, B ; A = (X_INICIO + COORD_X_TILE) | + | add a, b ; A = (X_INICIO + COORD_X_TILE) |
- | RLCA ; A = (Y_INICIO + COORD_Y_TILE) | + | |
- | LD (DS_COORD_Y), | + | |
- | LD A, (DE) ; Leemos el valor del TILE | + | ld a, (de) ; Leemos el valor del TILE |
- | INC DE | + | inc de |
- | LD (DS_NUMSPR), | + | |
- | | + | exx ; Preservar todos los registros en shadows |
- | CALL DrawSprite_16x16_LD | + | |
- | EXX ; Recuperar valores de los registros | + | |
- | | + | jr drawm16cb_loop |
</ | </ | ||
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 " | 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 " | ||
- | 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 " | + | 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 " |
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 | + | |
- | | + | |
- | XOR A | + | xor a |
- | | + | |
- | XOR A | + | xor a |
- | | + | |
- | | + | |
- | LD HL, sokoban1_gfx | + | ld hl, sokoban1_gfx |
- | | + | |
- | LD HL, sokoban1_attr | + | ld hl, sokoban1_attr |
- | | + | |
- | LD HL, sokoban_LEVEL1_codif_basica | + | ld hl, sokoban_LEVEL1_codif_basica |
- | | + | |
- | LD A, 16 | + | ld a, 16 |
- | | + | |
- | LD A, 12 | + | ld a, 12 |
- | | + | |
- | XOR A | + | xor a |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
loop: | loop: | ||
- | JR loop | + | jr loop |
DM_SPRITES | DM_SPRITES | ||
Línea 2158: | Línea 2155: | ||
DS_NUMSPR | DS_NUMSPR | ||
- | END 35000 | + | |
</ | </ | ||
El resultado de la ejecución es el siguiente: | El resultado de la ejecución es el siguiente: | ||
- | \\ | + | \\ |
- | {{ : | + | {{ : |
- | \\ | + | \\ |
- | \\ | + | \\ |
- | \\ | + | \\ |
=== 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 tile1, tile2, tile3, tile4, 255 | + | DB tile1, tile2, tile3, tile4, 255 |
- | ; (255 = byte de fin de " | + | ; (255 = byte de fin de " |
</ | </ | ||
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 " | El script codificador en python que hemos visto en el apartado de codificación básica permite, mediante el flag " | ||
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), | + | |
- | LD HL, (DM_ATTRIBS) | + | ld hl, (DM_ATTRIBS) |
- | LD (DS_ATTRIBS), | + | |
- | LD BC, (DM_COORD_X) | + | ld bc, (DM_COORD_X) |
- | LD DE, (DM_MAP) | + | ld de, (DM_MAP) |
- | 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 |
- | RET Z ; En ese caso, salir | + | ret z ; En ese caso, salir |
- | ADD A, C ; A = (X_INICIO + COORD_X_TILE) | + | add a, c ; A = (X_INICIO + COORD_X_TILE) |
- | RLCA ; A = (X_INICIO + COORD_X_TILE) * 2 | + | |
- | LD (HL), A ; 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, B ; A = (Y_INICIO + COORD_Y_TILE) | + | add a, b ; A = (Y_INICIO + COORD_Y_TILE) |
- | RLCA ; A = (Y_INICIO + COORD_Y_TILE) | + | |
- | LD (DS_COORD_Y), | + | |
- | ;;; 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 |
- | JR Z, drawm16ch_read | + | jr z, drawm16ch_read |
- | | + | ld (DS_NUMSPR), |
- | | + | exx ; Preservar todos los registros en shadows |
- | CALL DrawSprite_16x16_LD | + | |
- | EXX ; Recuperar valores de los registros | + | |
- | | + | inc (hl) ; Avanzamos al siguiente tile |
- | INC (HL) ; COORD_X = COORD_X + 2 | + | |
- | | + | jr drawm16ch_tileloop |
</ | </ | ||
- | \\ | + | \\ |
- | \\ | + | \\ |
**Mapeado diferencial con agrupación por scanlines verticales** | **Mapeado diferencial con agrupación 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 '' |
<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), | + | |
- | LD HL, (DM_ATTRIBS) | + | ld hl, (DM_ATTRIBS) |
- | LD (DS_ATTRIBS), | + | |
- | LD BC, (DM_COORD_X) | + | ld bc, (DM_COORD_X) |
- | LD DE, (DM_MAP) | + | ld de, (DM_MAP) |
- | LD HL, DS_COORD_Y | + | ld hl, DS_COORD_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 |
- | RET Z ; En ese caso, salir | + | ret z ; En ese caso, salir |
- | ADD A, C ; A = (X_INICIO + COORD_X_TILE) | + | add a, c ; A = (X_INICIO + COORD_X_TILE) |
- | RLCA ; A = (X_INICIO + COORD_X_TILE) * 2 | + | |
- | LD (DS_COORD_X), | + | |
- | 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, B ; A = (Y_INICIO + COORD_Y_TILE) | + | add a, b ; A = (Y_INICIO + COORD_Y_TILE) |
- | RLCA ; A = (Y_INICIO + COORD_Y_TILE) | + | |
- | LD (HL), A ; CAMBIO: Establecemos COORD_Y a imprimir tile | + | |
- | ;;; 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 |
- | JR Z, drawm16cv_read | + | jr z, drawm16cv_read |
- | | + | ld (DS_NUMSPR), |
- | | + | exx ; Preservar todos los registros en shadows |
- | CALL DrawSprite_16x16_LD | + | |
- | EXX ; Recuperar valores de los registros | + | |
- | | + | inc (hl) |
- | INC (HL) ; COORD_Y = COORD_Y + 2 | + | |
- | | + | jr drawm16cv_tileloop |
</ | </ | ||
- | \\ | + | \\ |
- | \\ | + | \\ |
**Mapeado diferencial con agrupación mixta** | **Mapeado diferencial con agrupación mixta** | ||
- | \\ | + | \\ |
| | ||
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). | ||
- | | + | |
<code z80> | <code z80> | ||
Pantalla: | Pantalla: | ||
- | | + | |
- | DB tile1, tile2, (...), tileN, 255 | + | DB tile1, tile2, (...), tileN, 255 |
- | DB coordenada_x_primer_tile_horiz, | + | DB coordenada_x_primer_tile_horiz, |
- | DB tile1, tile2, (...), tileN, 254 | + | DB tile1, tile2, (...), tileN, 254 |
- | DB coordenada_x_primer_tile_vert, | + | DB coordenada_x_primer_tile_vert, |
- | DB tile1, tile2, (...), tileN, 255 | + | DB tile1, tile2, (...), tileN, 255 |
- | DB coordenada_x_primer_tile_vert, | + | DB coordenada_x_primer_tile_vert, |
- | DB tile1, tile2, (...), tileN, 255 | + | DB tile1, tile2, (...), tileN, 255 |
- | DB 255 | + | DB 255 |
- | | + | |
- | ; 255 como coordenada_x = fin de pantalla. | + | ; 255 como coordenada_x = fin de pantalla. |
- | ; 255 a final de scanline = byte de fin de " | + | ; 255 a final de scanline = byte de fin de " |
- | ; 254 a final de scanline = cambio de codificacion de horizontal a vertical | + | ; 254 a final de scanline = cambio de codificacion de horizontal a vertical |
</ | </ | ||
Línea 2434: | Línea 2431: | ||
| | ||
- | | + | |
<code z80> | <code z80> | ||
- | LD DE, DS_COORD_Y | + | ld de, DS_COORD_Y |
- | LD HL, DS_COORD_X | + | ld hl, DS_COORD_X |
</ | </ | ||
- | | + | |
<code z80> | <code z80> | ||
drawm16cm_dir1: | drawm16cm_dir1: | ||
- | NOP | + | nop |
- | INC (HL) | + | |
- | INC (HL) ; COORD = COORD + 2 | + | |
drawm16cm_dir2: | drawm16cm_dir2: | ||
- | NOP | + | nop |
</ | </ | ||
- | | + | |
| | ||
Línea 2459: | Línea 2456: | ||
drawm16cm_tileloop: | drawm16cm_tileloop: | ||
- | (...) | + | |
- | | + | cp 254 |
- | JR Z, drawm16cm_switch | + | jr z, drawm16cm_switch |
- | (...) | + | |
drawm16cm_switch: | drawm16cm_switch: | ||
- | ;;; Cambio de codificacion de horizontal a vertical: | + | |
- | LD A, $EB ; Opcode de EX DE, HL | + | ld a, $eb ; Opcode de ex de, hl |
- | LD (drawm16cm_dir1), | + | |
- | LD (drawm16cm_dir2), | + | |
- | JR drawm16cm_read | + | |
</ | </ | ||
- | Es decir, cuando se encuentra un valor 254 como fin de scanline saltamos a drawm16cm_switch, | + | Es decir, cuando se encuentra un valor 254 como fin de scanline saltamos a drawm16cm_switch, |
<code z80> | <code z80> | ||
drawm16cm_dir1: | drawm16cm_dir1: | ||
- | EX DE, HL | + | ex de, hl |
- | INC (HL) | + | |
- | INC (HL) ; COORD = COORD + 2 | + | |
drawm16cm_dir2: | drawm16cm_dir2: | ||
- | EX DE, HL | + | ex de, hl |
</ | </ | ||
- | 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 DE, HL) 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 |
- | | + | |
<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), | + | |
- | LD (drawm16cm_dir2), | + | |
</ | </ | ||
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), | + | |
- | LD (drawm16cm_dir2), | + | |
- | LD HL, (DM_SPRITES) | + | ld hl, (DM_SPRITES) |
- | LD (DS_SPRITES), | + | |
- | LD HL, (DM_ATTRIBS) | + | ld hl, (DM_ATTRIBS) |
- | LD (DS_ATTRIBS), | + | |
- | LD BC, (DM_COORD_X) | + | ld bc, (DM_COORD_X) |
- | LD IX, (DM_MAP) | + | ld ix, (DM_MAP) |
- | LD DE, DS_COORD_Y | + | ld de, DS_COORD_Y |
- | LD HL, DS_COORD_X | + | ld hl, DS_COORD_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 |
- | RET Z ; En ese caso, salir | + | ret z ; En ese caso, salir |
- | ADD A, C ; A = (X_INICIO + COORD_X_TILE) | + | add a, c ; A = (X_INICIO + COORD_X_TILE) |
- | RLCA ; A = (X_INICIO + COORD_X_TILE) * 2 | + | |
- | LD (HL), A ; 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, B ; A = (Y_INICIO + COORD_Y_TILE) | + | add a, b ; A = (Y_INICIO + COORD_Y_TILE) |
- | RLCA ; A = (Y_INICIO + COORD_Y_TILE) | + | |
- | LD (DE), A ; Establecemos COORD_Y a imprimir tile | + | |
- | ;;; 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 |
- | JR Z, drawm16cm_read | + | jr z, drawm16cm_read |
- | | + | cp 254 |
- | JR Z, drawm16cm_switch | + | jr z, drawm16cm_switch |
- | | + | ld (DS_NUMSPR), |
- | EXX ; Preservar todos los registros en shadows | + | |
- | CALL DrawSprite_16x16_LD | + | |
- | EXX ; Recuperar valores de los registros | + | |
- | drawm16cm_dir1: | + | drawm16cm_dir1: |
- | NOP ; NOP->INC COORD_X, | + | |
- | | + | inc (hl) |
- | INC (HL) ; COORD = COORD + 2 | + | |
- | drawm16cm_dir2: | + | drawm16cm_dir2: |
- | NOP ; NOP->INC COORD_X, | + | |
- | | + | jr drawm16cm_tileloop |
drawm16cm_switch: | drawm16cm_switch: | ||
- | ;;; Cambio de codificacion de horizontal a vertical: | + | |
- | LD A, $EB ; Opcode de EX DE, HL | + | ld a, $eb ; Opcode de ex de, hl |
- | LD (drawm16cm_dir1), | + | |
- | LD (drawm16cm_dir2), | + | |
- | JR drawm16cm_read | + | |
</ | </ | ||
Línea 2589: | Línea 2586: | ||
- | \\ | + | \\ |
- | \\ | + | \\ |
**Codificar X e Y en un único byte** | **Codificar X e Y en un único byte** | ||
- | \\ | + | \\ |
- | \\ | + | \\ |
| | ||
Línea 2606: | Línea 2603: | ||
<code z80> | <code z80> | ||
- | | + | mapa_codificado.append( X ) |
- | mapa_codificado.append( Y ) | + | mapa_codificado.append( Y ) |
</ | </ | ||
Línea 2613: | Línea 2610: | ||
<code z80> | <code z80> | ||
- | | + | mapa_codificado.append( (X * 16) + Y ) |
</ | </ | ||
- | | + | |
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 '' |
- | | + | |
<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) | + | |
- | ;;; Sumamos la coordenada X recogida y obtenemos desde el mapa la Y: | + | |
- | ADD A, C ; A = (X_INICIO + COORD_X_TILE) | + | add a, c ; A = (X_INICIO + COORD_X_TILE) |
- | RLCA ; A = (X_INICIO + COORD_X_TILE) * 2 | + | |
- | LD (HL), A ; 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, B ; A = (Y_INICIO + COORD_Y_TILE) | + | add a, b ; A = (Y_INICIO + COORD_Y_TILE) |
- | RLCA ; A = (Y_INICIO + COORD_Y_TILE) | + | |
- | LD (DE), A ; Establecemos COORD_Y a imprimir tile | + | |
</ | </ | ||
Línea 2667: | Línea 2664: | ||
<code z80> | <code z80> | ||
- | PUSH AF | + | push af |
- | AND %11110000 | + | |
- | RRCA ; Pasamos parte alta a parte baja | + | |
- | RRCA ; con 4 desplazamientos | + | |
- | RRCA | + | rrca |
- | RRCA ; Ya podemos sumar: | + | |
- | ADD A, C ; A = (X_INICIO + COORD_X_TILE) | + | add a, c ; A = (X_INICIO + COORD_X_TILE) |
- | RLCA ; A = (X_INICIO + COORD_X_TILE) * 2 | + | |
- | LD (HL), A ; Establecemos COORD_X a imprimir tile | + | |
- | POP AF | + | pop af |
- | AND %00001111 | + | |
- | ADD A, B ; A = (Y_INICIO + COORD_Y_TILE) | + | add a, b ; A = (Y_INICIO + COORD_Y_TILE) |
- | RLCA ; A = (Y_INICIO + COORD_Y_TILE) | + | |
- | LD (DE), A ; Establecemos COORD_Y a imprimir tile | + | |
</ | </ | ||
- | De la rutina original hemos eliminado las instrucciones | + | De la rutina original hemos eliminado las instrucciones |
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), | + | |
- | LD (drawm16cmxy_dir2), | + | |
- | LD HL, (DM_SPRITES) | + | ld hl, (DM_SPRITES) |
- | LD (DS_SPRITES), | + | |
- | LD HL, (DM_ATTRIBS) | + | ld hl, (DM_ATTRIBS) |
- | LD (DS_ATTRIBS), | + | |
- | LD BC, (DM_COORD_X) | + | ld bc, (DM_COORD_X) |
- | LD IX, (DM_MAP) | + | ld ix, (DM_MAP) |
- | LD DE, DS_COORD_Y | + | ld de, DS_COORD_Y |
- | LD HL, DS_COORD_X | + | ld hl, DS_COORD_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 |
- | 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 | + | |
- | RRCA ; Pasamos parte alta a parte baja | + | |
- | RRCA ; con 4 desplazamientos | + | |
- | RRCA | + | rrca |
- | RRCA ; Ya podemos sumar | + | |
- | ADD A, C ; A = (X_INICIO + COORD_X_TILE) | + | add a, c ; A = (X_INICIO + COORD_X_TILE) |
- | RLCA ; A = (X_INICIO + COORD_X_TILE) * 2 | + | |
- | LD (HL), A ; Establecemos COORD_X a imprimir tile | + | |
- | POP AF | + | pop af |
- | AND %00001111 | + | |
- | ADD A, B ; A = (Y_INICIO + COORD_Y_TILE) | + | add a, b ; A = (Y_INICIO + COORD_Y_TILE) |
- | RLCA ; A = (Y_INICIO + COORD_Y_TILE) | + | |
- | LD (DE), A ; 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 |
- | JR Z, drawm16cmxy_read | + | jr z, drawm16cmxy_read |
- | CP 254 | + | |
- | JR Z, drawm16cmxy_switch | + | jr z, drawm16cmxy_switch |
- | | + | ld (DS_NUMSPR), |
- | EXX ; Preservar todos los registros en shadows | + | |
- | CALL DrawSprite_16x16_LD | + | |
- | EXX ; Recuperar valores de los registros | + | |
- | drawm16cmxy_dir1: | + | drawm16cmxy_dir1: |
- | NOP ; NOP->INC COORD_X, | + | |
- | INC (HL) | + | |
- | INC (HL) ; COORD = COORD + 2 | + | |
- | drawm16cmxy_dir2: | + | drawm16cmxy_dir2: |
- | NOP ; NOP->INC COORD_X, | + | |
- | JR drawm16cmxy_tileloop | + | |
drawm16cmxy_switch: | drawm16cmxy_switch: | ||
- | ;;; Cambio de codificacion de horizontal a vertical: | + | |
- | LD A, $EB ; Opcode de EX DE, HL | + | ld a, $eb ; Opcode de ex de, hl |
- | LD (drawm16cmxy_dir1), | + | |
- | LD (drawm16cmxy_dir2), | + | |
- | JR drawm16cmxy_read | + | |
</ | </ | ||
- | \\ | + | \\ |
- | \\ | + | \\ |
**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 " | + | Una rutina " |
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: | ||
</ | </ | ||
- | Es difícil que la codificación básica sea más óptima que ninguna de las anteriores a menos que apenas haya bloques " | + | Es difícil que la codificación básica sea más óptima que ninguna de las anteriores a menos que apenas haya bloques " |
<code z80> | <code z80> | ||
Línea 2811: | Línea 2808: | ||
; | ; | ||
DrawMap_16x16_Codificada: | DrawMap_16x16_Codificada: | ||
- | LD HL, (DM_MAP) | + | ld hl, (DM_MAP) |
- | 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), | + | |
- | AND A ; Es A == 0? (MAP_CODIF_NONE) | + | and a ; Es A == 0? (MAP_CODIF_NONE) |
- | JR NZ, dm16c_nocero | + | jr nz, dm16c_nocero |
- | CALL DrawMap_16x16 | + | |
- | RET | + | ret |
dm16c_nocero: | dm16c_nocero: | ||
- | CP MAP_CODIF_HORIZ | + | |
- | JR NZ, dm16c_nohoriz | + | jr nz, dm16c_nohoriz |
- | CALL DrawMap_16x16_Cod_Horiz | + | |
- | RET | + | ret |
dm16c_nohoriz: | dm16c_nohoriz: | ||
- | CP MAP_CODIF_VERT | + | |
- | JR NZ, dm16c_novert | + | jr nz, dm16c_novert |
- | CALL DrawMap_16x16_Cod_Vert | + | |
- | RET | + | ret |
dm16c_novert: | dm16c_novert: | ||
- | CP MAP_CODIF_MIXTA | + | |
- | JR NZ, dm16c_nomixta | + | jr nz, dm16c_nomixta |
- | CALL DrawMap_16x16_Cod_Mixta | + | |
- | RET | + | ret |
dm16c_nomixta: | dm16c_nomixta: | ||
- | | + | call DrawMap_16x16_Cod_Basica |
- | RET | + | ret |
</ | </ | ||
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, | 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, | ||
- | \\ | + | \\ |
- | \\ | + | \\ |
**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 " | 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 " | ||
- | Lo bueno de las técnicas de codificación por scanlines horizontales, | + | Lo bueno de las técnicas de codificación por scanlines horizontales, |
| | ||
Línea 2882: | Línea 2879: | ||
</ | </ | ||
- | En este caso, los bloques " | + | En este caso, los bloques " |
- | \\ | + | \\ |
- | {{ : | + | {{ : |
- | \\ | + | \\ |
- | No hay colisión entre el 255 " | + | No hay colisión entre el 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 " | Otro efecto interesante para mejorar la riqueza gráfica de un juego es generar una " | ||
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 " | Por ejemplo, la siguiente captura de pantalla de un juego de los //Mojon Twins// muestra cómo los muros verticales rojos cercanos a la " | ||
- | \\ | + | \\ |
- | {{ : | + | {{ : |
- | \\ | + | \\ |
| | ||
Línea 2933: | Línea 2930: | ||
</ | </ | ||
- | \\ | + | \\ |
- | \\ | + | \\ |
=== Resumen de resultados de codificaciones empleadas === | === Resumen de resultados de codificaciones empleadas === | ||
| | ||
- | \\ | + | \\ |
|< 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, | Estos datos son una estimación muy general porque no todas las pantallas de Sokoban ocuparán lo mismo una vez codificadas, | ||
- | \\ | + | \\ |
- | \\ | + | \\ |
=== 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** | ||
- | \\ | + | \\ |
- | \\ | + | \\ |
| | ||
Línea 3065: | Línea 3062: | ||
</ | </ | ||
- | Si la separación entre 2 scanlines la forma 1 bloque " | + | Si la separación entre 2 scanlines la forma 1 bloque " |
- | Si la separación entre 2 scanlines la forman 2 bloques " | + | Si la separación entre 2 scanlines la forman 2 bloques " |
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_attrib_1 | + | DW dir_tileset_attrib_1 |
- | DB ancho_tiles_tileset1, | + | DB ancho_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, | + | DB ancho_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, | + | DB ancho_tiles_tileset3, |
</ | </ | ||
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 (...) |
- | | + | DB 255 (fin de pantalla) |
</ | </ | ||
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) | + | |
- | | + | DW tileset_1_attr+(0*4) |
- | | + | DB 16, 16 |
- | | + | DW tileset_1_gfx+(1*32) |
- | | + | DW tileset_1_attr+(1*4) |
- | | + | DB 16, 16 |
- | | + | DW tileset_1_gfx+(2*32) |
- | | + | DW tileset_1_attr+(2*4) |
- | | + | DB 16, 16 |
- | | + | DW tileset_2_gfx+(0*8) |
- | | + | DW tileset_2_attr+(0*1) |
- | | + | DB 8, 8 |
- | | + | DW tileset_2_gfx+(1*8) |
- | | + | DW tileset_2_attr+(1*0) |
- | | + | DB 8, 8 |
- | | + | DW logotipo_gfx |
- | | + | DW logotipo_attr |
- | | + | DB 32, 16 |
- | | + | DW piedra_gfx |
- | | + | DW piedra_attr |
- | | + | DB 32, 32 |
- | | + | DW muro_grande_gfx |
- | | + | DW muro_grande_attr |
- | | + | DB 64, 64 |
- | | + | DW grafico_escalera_gfx |
- | | + | DW grafico_escalera_attr |
- | | + | DB 16, 32 |
</ | </ | ||
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. | ||
- | | + | |
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 B, A | + | ld b, a |
- | LD A, (DS_WIDTH) | + | ld a, (DS_WIDTH) |
- | LD C, A ; Obtenemos datos del sprite | + | ld c, a ; Obtenemos datos del sprite |
- | ;;; B = ALTO de Sprite | + | |
- | | + | ;;; C = ANCHO de Sprite |
- | LD A, C ; A = ANCHO | + | ld a, c ; A = ANCHO |
- | CP 16 ; Comparar ancho | + | |
- | JR NZ, dspMN_no16 | + | jr nz, dspMN_no16 |
- | SUB B ; A = Ancho - alto | + | sub b ; A = Ancho - alto |
- | JR NZ, dspMN_generica | + | jr nz, dspMN_generica |
- | | + | ; Es cero, imprimir 16x16: |
- | CALL DrawSprite16x16 | + | |
- | RET | + | ret |
dspMN_no16: | dspMN_no16: | ||
- | LD A, C ; Recuperamos ancho | + | ld a, c ; Recuperamos ancho |
- | CP 8 ; ¿es 8? | + | |
- | JR NZ, dspMN_generica | + | jr nz, dspMN_generica |
- | SUB B ; A = Ancho - alto | + | sub b ; A = Ancho - alto |
- | JR NZ, dspMN_generica | + | jr nz, dspMN_generica |
- | | + | call DrawSprite_8x8 |
- | RET | + | ret |
dspMN_generica: | dspMN_generica: | ||
- | ;;; (resto rutina generica) | + | |
- | RET | + | ret |
</ | </ | ||
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_attr |
- | | + | DB 32, 16 |
- | | + | DW piedra_gfx |
- | | + | DW piedra_attr |
- | | + | DB 32, 32 |
- | | + | DW muro_grande_gfx |
- | | + | DW muro_grande_attr |
- | | + | DB 64, 64 |
- | | + | DW grafico_escalera_gfx |
- | | + | DW grafico_escalera_attr |
- | | + | DB 16, 32 |
</ | </ | ||
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_attr |
- | | + | DB 16, 16 |
- | | + | DW fondo_baldosas_gfx |
- | | + | DW fondo_baldosas_attr |
- | | + | DB 64, 64 |
- | | + | DW fondo_hierba_gfx |
- | | + | DW fondo_hierba_attr |
- | | + | DB 32, 32 |
</ | </ | ||
Línea 3281: | Línea 3278: | ||
<code z80> | <code z80> | ||
Mapa: | Mapa: | ||
- | | + | |
- | 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 | + | DW Pantalla_Salon |
- | 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 | + | DW Pantalla_Pasillo |
- | DB 1, 3 ; Conexiones izq y derecha ID 2 | + | DB 1, 3 ; Conexiones izq y derecha ID 2 |
- | DB FONDO_PIEDRA | + | DB FONDO_PIEDRA |
- | (...) | + | (...) |
</ | </ | ||
Línea 3299: | Línea 3296: | ||
| | ||
- | 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 |
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 " | 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 " | ||
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, | La primera de las posibilidades de compresión se basa en repetición de tiles consecutivos en las técnicas de agrupación, | ||
<code z80> | <code z80> | ||
- | | + | |
</ | </ | ||
Línea 3330: | Línea 3327: | ||
<code z80> | <code z80> | ||
- | | + | |
- | DB 1, 2, 2, 2, 2, 2, 2, 3, 4 | + | DB 1, 2, 2, 2, 2, 2, 2, 3, 4 |
</ | </ | ||
Línea 3337: | Línea 3334: | ||
<code z80> | <code z80> | ||
- | | + | |
- | DB 1, 253, 6, 2, 3, 4 | + | DB 1, 253, 6, 2, 3, 4 |
</ | </ | ||
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, | El sistema de compresión por patrones es el método que, probablemente, | ||
Línea 3365: | Línea 3362: | ||
<code z80> | <code z80> | ||
Pantalla: | Pantalla: | ||
- | | + | |
</ | </ | ||
- | | + | |
| | ||
Línea 3393: | Línea 3390: | ||
Patron0: | Patron0: | ||
- | | + | |
Patron1: | Patron1: | ||
- | | + | |
Patron2: | Patron2: | ||
- | | + | |
Patron3: | Patron3: | ||
- | | + | |
Patron4: | Patron4: | ||
- | | + | |
</ | </ | ||
Línea 3412: | Línea 3409: | ||
<code z80> | <code z80> | ||
Tabla_Patrones: | Tabla_Patrones: | ||
- | | + | |
- | DW Patron1 | + | DW Patron1 |
- | DW Patron2 | + | DW Patron2 |
- | DW Patron3 | + | DW Patron3 |
- | DW Patron4 | + | DW Patron4 |
- | (...) | + | (...) |
</ | </ | ||
Línea 3445: | Línea 3442: | ||
<code z80> | <code z80> | ||
Pantalla: | Pantalla: | ||
- | | + | |
- | 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 |
</ | </ | ||
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: | ||
- | \\ | + | \\ |
- | {{ : | + | {{ : |
- | \\ | + | \\ |
;#; | ;#; | ||
//Patrones codificables: | //Patrones codificables: | ||
;#; | ;#; | ||
- | \\ | + | \\ |
Este sistema acaba consiguiendo ratios de compresión muy buenos pero basa toda su técnica en el programa codificador: | Este sistema acaba consiguiendo ratios de compresión muy buenos pero basa toda su técnica en el programa codificador: | ||
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 " | La primera distinción suele ser marcar qué tiles son " | ||
- | \\ | + | \\ |
- | {{ : | + | {{ : |
- | \\ | + | \\ |
;#; | ;#; | ||
//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. | ||
- | \\ | + | \\ |
- | {{ : | + | {{ : |
- | \\ | + | \\ |
| | ||
Línea 3526: | Línea 3523: | ||
;;; (etc...) | ;;; (etc...) | ||
Propiedades_Tiles: | Propiedades_Tiles: | ||
- | | + | |
- | DB %00000001 | + | DB %00000001 |
- | DB %00000010 | + | DB %00000010 |
- | DB %00000001 | + | DB %00000001 |
- | DB %00000101 | + | DB %00000101 |
</ | </ | ||
Línea 3540: | Línea 3537: | ||
- | \\ | + | \\ |
===== Objetos y enemigos en el mapeado ===== | ===== Objetos y enemigos en el mapeado ===== | ||
Línea 3549: | Línea 3546: | ||
| | ||
- | \\ | + | \\ |
===== 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. | ||
- | \\ | + | \\ |
{{ : | {{ : | ||
- | \\ | + | \\ |
Mappy es más antiguo y se diseñó para juegos basados en las librerías de PC " | Mappy es más antiguo y se diseñó para juegos basados en las librerías de PC " | ||
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 " | + | 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 " |
- | 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 " | + | 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 " |
- | \\ | + | \\ |
===== Mapeando en posiciones de alta resolución ===== | ===== Mapeando en posiciones de alta resolución ===== | ||
Línea 3622: | Línea 3619: | ||
- | \\ | + | \\ |
===== Enlaces ===== | ===== Enlaces ===== | ||