cursos:ensamblador:gfx2_direccionamiento

Diferencias

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

Enlace a la vista de comparación

Ambos lados, revisión anterior Revisión previa
Próxima revisión
Revisión previa
cursos:ensamblador:gfx2_direccionamiento [11-11-2010 10:08] sromerocursos:ensamblador:gfx2_direccionamiento [21-01-2024 17:22] (actual) – [Optimizaciones para Get_Pixel_Offset_HR] sromero
Línea 1: Línea 1:
- 
 ====== Gráficos (y II): Cálculo de direcciones y coordenadas ====== ====== Gráficos (y II): Cálculo de direcciones y coordenadas ======
  
  En el anterior capítulo exploramos la organización interna de los 6912 bytes de la videomemoria del Spectrum, separándo esta videomemoria en un área de 6144 bytes de //archivo de imagen// comenzando en la dirección 16384 y otros 768 bytes de //archivo de atributos// comenzando en la dirección 22528.  En el anterior capítulo exploramos la organización interna de los 6912 bytes de la videomemoria del Spectrum, separándo esta videomemoria en un área de 6144 bytes de //archivo de imagen// comenzando en la dirección 16384 y otros 768 bytes de //archivo de atributos// comenzando en la dirección 22528.
  
- La manipulación de dichas áreas de memoria nos permite el trazado de gráficos en pantalla y la manipulación de los colores que tienen dichos gráficos en el monitor. Esto es así porque este área de memoria es leída por el chip de la ULA 50 veces por segundo en sistemas de televisión PAL (Europa y Australia) y 60 veces por segundo en sistemas NTSC (América y Asia) para enviar la señal que el televisor convierte en una imagen para nuestros ojos. + La manipulación de dichas áreas de memoria nos permite el trazado de gráficos en pantalla y la manipulación de los colores que tienen dichos gráficos en el monitor. Esto es así porque este área de memoria es leída por el chip de la ULA 50 veces por segundo en sistemas de televisión PAL (Europa y Australia) y 60 veces por segundo en sistemas NTSC (América y Asia) para enviar la señal que el televisor convierte en una imagen para nuestros ojos.
  
  Es necesario que la ULA refresque la pantalla de forma continuada y regular ya que debe reflejar los cambios que los programas hagan en la videomemoria, así cómo para refrescar el estado de los píxeles del monitor (necesario por el funcionamiento de la tecnología CRT).  Es necesario que la ULA refresque la pantalla de forma continuada y regular ya que debe reflejar los cambios que los programas hagan en la videomemoria, así cómo para refrescar el estado de los píxeles del monitor (necesario por el funcionamiento de la tecnología CRT).
Línea 10: Línea 9:
  Sabemos por el capítulo anterior que escribir en la videomemoria nos permite trazar gráficos en pantalla. Hasta ahora hemos visto efectos globales aplicados a toda la vram (borrados, fundidos, etc), pero nuestro interés principal será, seguramente, el trazar gráficos con precisión de bloque o de pixel en la pantalla.  Sabemos por el capítulo anterior que escribir en la videomemoria nos permite trazar gráficos en pantalla. Hasta ahora hemos visto efectos globales aplicados a toda la vram (borrados, fundidos, etc), pero nuestro interés principal será, seguramente, el trazar gráficos con precisión de bloque o de pixel en la pantalla.
  
- Para poder realizar esta tarea necesitamos relacionar las posiciones de memoria de la videoram con las coordenadas (x,y) de pantalla cuya información gráfica representan. Necesitaremos pues programar rutinas de cálculo de direcciones en función de coordenadas de alta y baja resolución. Sabemos cómo dibujar, pero no cómo calcular la dirección de memoria donde hacerlo. Este es precisamente nuestro objetivo en esta sección. + Para poder realizar esta tarea necesitamos relacionar las posiciones de memoria de la videoram con las coordenadas (x,y) de pantalla cuya información gráfica representan. Necesitaremos pues programar rutinas de cálculo de direcciones en función de coordenadas de alta y baja resolución. Sabemos cómo dibujar, pero no cómo calcular la dirección de memoria donde hacerlo. Este es precisamente nuestro objetivo en esta sección.
  
  Para empezar, estableceremos una terminología unánime a la que haremos referencia a lo largo de todo el capítulo, con las siguientes definiciones:  Para empezar, estableceremos una terminología unánime a la que haremos referencia a lo largo de todo el capítulo, con las siguientes definiciones:
  
 \\  \\ 
-  * **Resolución del área gráfica**: El Spectrum dispone de una resolución de 256x192 píxeles cuyo estado se define en cada byte del área de imagen de la videomemoria. +   * **Resolución del área gráfica**: El Spectrum dispone de una resolución de 256x192 píxeles cuyo estado se define en cada byte del área de imagen de la videomemoria. 
-  * **Estado de un pixel**: Cada byte del área de imagen contiene el estado de 8 píxeles, de tal forma que cada uno de los bits de dicho byte pueden estar a 1 (pixel encendido, se traza con el color de tinta) o a 0 (apagado, se traza con el color del papel). + 
-  * **Resolución del área de atributos**: El Spectrum tiene una resolución de color de 32x24 puntos, que se mapean sobre la pantalla de forma que cada grupo de 8x8 píxeles del área gráfica tiene una correspondencia con un atributo del área de atributos. +   * **Estado de un pixel**: Cada byte del área de imagen contiene el estado de 8 píxeles, de tal forma que cada uno de los bits de dicho byte pueden estar a 1 (pixel encendido, se traza con el color de tinta) o a 0 (apagado, se traza con el color del papel). 
-  * **Atributo**: Un atributo define en los 8 bits de un byte el color de tinta y papel y el estado de brillo y parpadeo de un bloque concreto de la pantalla. + 
-  * **Bloque o carácter**: Si dividimos la pantalla de 256x192 en 32x24 bloques de color, nos quedan bloques de 8x8 píxeles que mantienen el mismo atributo de pantalla. Estamos acostumbrados a trabajar con bloques ya que el intérprete BASIC del Spectrum utiliza la fuente de la ROM de 8x8 en una rejilla de bloques que coincide con la resolución de atributos. Podemos pensar en los bloques como "posiciones de carácter"+   * **Resolución del área de atributos**: El Spectrum tiene una resolución de color de 32x24 puntos, que se mapean sobre la pantalla de forma que cada grupo de 8x8 píxeles del área gráfica tiene una correspondencia con un atributo del área de atributos. 
-  * **Scanline**: Un scanline es una línea normalmente horizontal de datos gráficos. Por ejemplo, el scanline 0 de pantalla es la línea gráfica que va desde (0,0) a (255,0), y que definen los 32 bytes de videomemoria que van desde 16384 hasta 16415. También se puede hablar del scanline de un sprite o de un carácter cuando nos referimos a una línea concreta de esa porción de gráfico. + 
-  * **Coordenadas (x,y)**: Se utiliza la nomenclatura (x,y) para definir la posición de un píxel en pantalla en función de su posición horizontal y vertical siendo (0,0) la esquina superior izquierda de la misma y (255,191) la esquina inferior derecha. Son, pues, "coordenadas en alta resolución"+   * **Atributo**: Un atributo define en los 8 bits de un byte el color de tinta y papel y el estado de brillo y parpadeo de un bloque concreto de la pantalla. 
-  * **Coordenadas (c,f)**: Se utiliza la nomenclatura (c,f), de (columna,fila), para hacer referencia a la posición de un bloque 8x8 en pantalla en función de su posición horizontal y vertical siendo (0,0) la esquina superior izquierda y (31,23) la esquina inferior derecha. Se conocen como "coordenadas en baja resolución" o "coordenadas de bloque" o "de carácter"+ 
-  * **Conversión (c,f) a (x,y)**: Como cada bloque es de 8x8 píxeles, podemos convertir una coordenada en baja resolución a coordenadas de pixel como (x,y) = (8*c,8*f). Asímismo, (c,f) = (x/8,y/8). +   * **Bloque o carácter**: Si dividimos la pantalla de 256x192 en 32x24 bloques de color, nos quedan bloques de 8x8 píxeles que mantienen el mismo atributo de pantalla. Estamos acostumbrados a trabajar con bloques ya que el intérprete BASIC del Spectrum utiliza la fuente de la ROM de 8x8 en una rejilla de bloques que coincide con la resolución de atributos. Podemos pensar en los bloques como "posiciones de carácter"
-  * **Tercio de pantalla**: El área gráfica del Spectrum se divide en 3 áreas de 2KB de videoram que almacenan la información de 256x64 píxeles. Estas áreas son comunmente denominadas "tercios"+ 
-  * **Offset** o **Desplazamiento**: Llamaremos offset o desplazamiento a la cantidad de bytes que tenemos que avanzar desde una base (normalmente el inicio de la propia memoria o un punto de la misma) para llegar a una posición de memoria. Así, un offset de 32 bytes desde 16384 referenciará a los 8 píxeles desde (0,1) a (7,1). En las rutinas que veremos, el offset estará calculado con $0000 como la base, es decir, serán offsets absolutos (posiciones de memoria).+   * **Scanline**: Un scanline es una línea normalmente horizontal de datos gráficos. Por ejemplo, el scanline 0 de pantalla es la línea gráfica que va desde (0,0) a (255,0), y que definen los 32 bytes de videomemoria que van desde 16384 hasta 16415. También se puede hablar del scanline de un sprite o de un carácter cuando nos referimos a una línea concreta de esa porción de gráfico. 
 + 
 +   * **Coordenadas (x,y)**: Se utiliza la nomenclatura (x,y) para definir la posición de un píxel en pantalla en función de su posición horizontal y vertical siendo (0,0) la esquina superior izquierda de la misma y (255,191) la esquina inferior derecha. Son, pues, "coordenadas en alta resolución"
 + 
 +   * **Coordenadas (c,f)**: Se utiliza la nomenclatura (c,f), de (columna,fila), para hacer referencia a la posición de un bloque 8x8 en pantalla en función de su posición horizontal y vertical siendo (0,0) la esquina superior izquierda y (31,23) la esquina inferior derecha. Se conocen como "coordenadas en baja resolución" o "coordenadas de bloque" o "de carácter"
 + 
 +   * **Conversión (c,f) a (x,y)**: Como cada bloque es de 8x8 píxeles, podemos convertir una coordenada en baja resolución a coordenadas de pixel como (x,y) = (8*c,8*f). Asímismo, (c,f) = (x/8,y/8). 
 + 
 +   * **Tercio de pantalla**: El área gráfica del Spectrum se divide en 3 áreas de 2KB de videoram que almacenan la información de 256x64 píxeles. Estas áreas son comunmente denominadas "tercios"
 + 
 +   * **Offset** o **Desplazamiento**: Llamaremos offset o desplazamiento a la cantidad de bytes que tenemos que avanzar desde una base (normalmente el inicio de la propia memoria o un punto de la misma) para llegar a una posición de memoria. Así, un offset de 32 bytes desde 16384 referenciará a los 8 píxeles desde (0,1) a (7,1). En las rutinas que veremos, el offset estará calculado con $0000 como la base, es decir, serán offsets absolutos (posiciones de memoria).
 \\ \\
  
Línea 45: Línea 54:
 \\  \\ 
   * **Área de imagen**:   * **Área de imagen**:
-    * El área de imagen se divide en 3 tercios de pantalla de 2KB de memoria cada uno, que van de $4000 a $47FF (tercio superior), de $4800 a $4FFF (tercio central) y de $5000 a $57FF (tercio inferior).+    * El área de imagen se divide en 3 tercios de pantalla de 2KB de memoria cada uno, que van de $4000 a $47ff (tercio superior), de $4800 a $4fff (tercio central) y de $5000 a $57ff (tercio inferior).
     * Cada uno de los tercios comprende 8 líneas de 32 bloques horizontales (256x64 píxeles). Dentro de cada uno de esos 2KB, tenemos, de forma lineal, 64 bloques de 32 bytes (256 píxeles) de información que representan cada scanline de esos 8 bloques.     * Cada uno de los tercios comprende 8 líneas de 32 bloques horizontales (256x64 píxeles). Dentro de cada uno de esos 2KB, tenemos, de forma lineal, 64 bloques de 32 bytes (256 píxeles) de información que representan cada scanline de esos 8 bloques.
     * Los primeros 32 bytes de dicho bloque contienen la información del scanline 0 del bloque 0. Avanzando de 32 en 32 bytes tenemos los datos del scanline 0 del bloque 1, el scanline 0 del bloque 2, el scanline 0 del bloque 3, etc, hasta que llegamos al scanline 7 del bloque 0. Los siguientes 32 bytes repiten el proceso pero con el scanline 1 de cada bloque.     * Los primeros 32 bytes de dicho bloque contienen la información del scanline 0 del bloque 0. Avanzando de 32 en 32 bytes tenemos los datos del scanline 0 del bloque 1, el scanline 0 del bloque 2, el scanline 0 del bloque 3, etc, hasta que llegamos al scanline 7 del bloque 0. Los siguientes 32 bytes repiten el proceso pero con el scanline 1 de cada bloque.
Línea 56: Línea 65:
 \\  \\ 
   * **Area de atributos**:   * **Area de atributos**:
-     * El área de atributos se encuentra en memoria inmediatamente después del área de imagen, por lo que empieza en la posición de memoria 16384+6144 = 22528 ($5800).  +     * El área de atributos se encuentra en memoria inmediatamente después del área de imagen, por lo que empieza en la posición de memoria 16384+6144 = 22528 ($5800). 
-     * Cada byte del área de atributos se denomina //atributo// y define el valor de color de tinta, papel, brillo y flash de un carácter / bloque de la pantalla. Esto implica que el área de atributos ocupa 32x24x1 = 768 bytes en memoria, por lo que empieza en 22528 ($5800) y acaba en 23295 ($5AFF).+     * Cada byte del área de atributos se denomina //atributo// y define el valor de color de tinta, papel, brillo y flash de un carácter / bloque de la pantalla. Esto implica que el área de atributos ocupa 32x24x1 = 768 bytes en memoria, por lo que empieza en 22528 ($5800) y acaba en 23295 ($5aff).
      * Los diferentes bits de un atributo de carácter son: Bit 7 = FLASH, Bit 6 = BRIGHT, Bits 5-3 = PAPER, Bits 2-0 = INK.      * Los diferentes bits de un atributo de carácter son: Bit 7 = FLASH, Bit 6 = BRIGHT, Bits 5-3 = PAPER, Bits 2-0 = INK.
      * Los valores de tinta y papel son un valor de 0-7 que junto al brillo como bit más significativo componen un índice (B-I-I-I) contra una paleta de colores interna definida en la ULA, donde el 0 es el color negro y el 15 el blanco de brillo máximo.      * Los valores de tinta y papel son un valor de 0-7 que junto al brillo como bit más significativo componen un índice (B-I-I-I) contra una paleta de colores interna definida en la ULA, donde el 0 es el color negro y el 15 el blanco de brillo máximo.
Línea 65: Línea 74:
  Nuestro capítulo de hoy tiene los siguientes objetivos prioritarios:  Nuestro capítulo de hoy tiene los siguientes objetivos prioritarios:
 \\  \\ 
 +
    * **Cálculo de posiciones de atributo**: Saber calcular la posición en memoria del atributo de una posición de carácter (c,f) o de un pixel (x,y).    * **Cálculo de posiciones de atributo**: Saber calcular la posición en memoria del atributo de una posición de carácter (c,f) o de un pixel (x,y).
    * **Cálculo de posiciones de carácter (baja resolución)**: Saber calcular la posición en memoria en que comienzan los datos gráficos (pixel 0,0 del carácter) de un carácter o bloque de 8x8 píxeles referenciado como (c,f) o (x,y), asumiendo una resolución de 32x24 bloques en pantalla coincidiendo con las posiciones de carácter de texto estándar.     * **Cálculo de posiciones de carácter (baja resolución)**: Saber calcular la posición en memoria en que comienzan los datos gráficos (pixel 0,0 del carácter) de un carácter o bloque de 8x8 píxeles referenciado como (c,f) o (x,y), asumiendo una resolución de 32x24 bloques en pantalla coincidiendo con las posiciones de carácter de texto estándar. 
Línea 88: Línea 98:
 \\  \\ 
  
- Es importante comprobar antes de llamar a nuestras rutinas si estas modifican algún registro o flag que necesitemos preservar. Podemos modificar las rutinas para que realicen PUSH y POP de los registros necesarios o hacer nosotros estos PUSH/POP en la rutina llamadora.+ Es importante comprobar antes de llamar a nuestras rutinas si estas modifican algún registro o flag que necesitemos preservar. Podemos modificar las rutinas para que realicen ''PUSH'' ''POP'' de los registros necesarios o hacer nosotros estos ''PUSH''/''POP'' en la rutina llamadora.
  
  Comencemos con las rutinas:  Comencemos con las rutinas:
Línea 95: Línea 105:
 === Get_Attribute_Offset === === Get_Attribute_Offset ===
  
- Una primera aproximación a la obtención de la dirección en memoria de un atributo concreto (columna,fila) podría ser la utilización de una tabla de 24 valores de 16 bits que alojara las direcciones de inicio en memoria de los atributos del primer carácter de cada fila. + Una primera aproximación a la obtención de la dirección en memoria de un atributo concreto (columna,fila) podría ser la utilización de una tabla de 24 valores de 16 bits que alojara las direcciones de inicio en memoria de los atributos del primer carácter de cada fila.
  
  De esta forma bastaría con utilizar el número de fila como índice en la tabla y sumar el número de columna para obtener la dirección de memoria de la celdilla de atributos de (c,f):  De esta forma bastaría con utilizar el número de fila como índice en la tabla y sumar el número de columna para obtener la dirección de memoria de la celdilla de atributos de (c,f):
  
-^ Línea f ^ Dirección en Hexadecimal ^ En Decimal ^ En Binario ^ +|< 70% >| 
 +^ Línea f ^ Dirección en Hexadecimal ^ En Decimal ^ En Binario ^
 | 0 | $5800 | 22528 | 0101100000000000b | | 0 | $5800 | 22528 | 0101100000000000b |
 | 1 | $5820 | 22560 | 0101100000100000b | | 1 | $5820 | 22560 | 0101100000100000b |
Línea 105: Línea 116:
 | 3 | $5860 | 22624 | 0101100001100000b | | 3 | $5860 | 22624 | 0101100001100000b |
 | 4 | $5880 | 22656 | 0101100010000000b | | 4 | $5880 | 22656 | 0101100010000000b |
-| 5 | $58A0 | 22688 | 0101100010100000b | +| 5 | $58a0 | 22688 | 0101100010100000b | 
-| 6 | $58C0 | 22720 | 0101100011000000b | +| 6 | $58c0 | 22720 | 0101100011000000b | 
-| 7 | $58E0 | 22752 | 0101100011100000b |+| 7 | $58e0 | 22752 | 0101100011100000b |
 | 8 | $5900 | 22784 | 0101100100000000b | | 8 | $5900 | 22784 | 0101100100000000b |
 | 9 | $5920 | 22816 | 0101100100100000b | | 9 | $5920 | 22816 | 0101100100100000b |
Línea 113: Línea 124:
 | 11 | $5960 | 22880 | 0101100101100000b | | 11 | $5960 | 22880 | 0101100101100000b |
 | 12 | $5980 | 22912 | 0101100110000000b | | 12 | $5980 | 22912 | 0101100110000000b |
-| 13 | $59A0 | 22944 | 0101100110100000b | +| 13 | $59a0 | 22944 | 0101100110100000b | 
-| 14 | $59C0 | 22976 | 0101100111000000b | +| 14 | $59c0 | 22976 | 0101100111000000b | 
-| 15 | $59E0 | 23008 | 0101100111100000b | +| 15 | $59e0 | 23008 | 0101100111100000b | 
-| 16 | $5A00 | 23040 | 0101101000000000b | +| 16 | $5a00 | 23040 | 0101101000000000b | 
-| 17 | $5A20 | 23072 | 0101101000100000b | +| 17 | $5a20 | 23072 | 0101101000100000b | 
-| 18 | $5A40 | 23104 | 0101101001000000b | +| 18 | $5a40 | 23104 | 0101101001000000b | 
-| 19 | $5A60 | 23136 | 0101101001100000b | +| 19 | $5a60 | 23136 | 0101101001100000b | 
-| 20 | $5A80 | 23168 | 0101101010000000b | +| 20 | $5a80 | 23168 | 0101101010000000b | 
-| 21 | $5AA0 | 23200 | 0101101010100000b | +| 21 | $5aa0 | 23200 | 0101101010100000b | 
-| 22 | $5AC0 | 23232 | 0101101011000000b | +| 22 | $5ac0 | 23232 | 0101101011000000b | 
-| 23 | $5AE0 | 23264 | 0101101011100000b |+| 23 | $5ae0 | 23264 | 0101101011100000b |
 //Direcciones del atributo en el carácter (0,f)// //Direcciones del atributo en el carácter (0,f)//
 \\  \\ 
Línea 139: Línea 150:
  
 <code> <code>
- Direccion_Atributo(x_bloque,y_bloque) = 22528 + (f*32) + c  + Direccion_Atributo(x_bloque,y_bloque) = 22528 + (f*32) + c 
-</code> +</code>
  
  Desde el inicio del área de atributos, avanzamos 32 bytes por fila hasta posicionarnos en el bloque de 32 bytes que referencia a nuestro bloque, y sumamos el número de columna.  Desde el inicio del área de atributos, avanzamos 32 bytes por fila hasta posicionarnos en el bloque de 32 bytes que referencia a nuestro bloque, y sumamos el número de columna.
Línea 155: Línea 166:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Get_Attribute_Offset_LR_SLOW: Get_Attribute_Offset_LR_SLOW:
-   ; calcular dir_atributo como "inicio_attr + (32*f) + c" +    ; calcular dir_atributo como "inicio_attr + (32*f) + c" 
-   LD H, 0 +    ld h, 0 
-   LD LB         ; HL = "fila" +    ld lb                  ; HL = "fila" 
-   ADD HLHL      ; HL = HL*2 +    add hlhl               ; HL = HL*2 
-   ADD HLHL      ; HL = HL*4 +    add hlhl               ; HL = HL*4 
-   ADD HLHL      ; HL = HL*8 +    add hlhl               ; HL = HL*8 
-   ADD HLHL      ; HL = HL*16 +    add hlhl               ; HL = HL*16 
-   ADD HLHL      ; HL = HL*32 +    add hlhl               ; HL = HL*32 
-   LD D, 0 +    ld d, 0 
-   LD EC         ; DE = "columna" +    ld ec                  ; DE = "columna" 
-   ADD HLDE      ; HL = fila*32 + columna +    add hlde               ; HL = fila*32 + columna 
-   LD DE, 22528    ; Direccion de inicio de atributos +    ld de, 22528             ; Direccion de inicio de atributos 
-   ADD HLDE      ; HL = 22528 + fila*32 + columna +    add hlde               ; HL = 22528 + fila*32 + columna 
-   RET+    ret
 </code> </code>
  
Línea 178: Línea 189:
  Veamos cómo podemos mejorar esta rutina: Si nos fijamos en la representación en binario de la anterior tabla de direcciones, veremos que todas ellas siguen un patrón común:  Veamos cómo podemos mejorar esta rutina: Si nos fijamos en la representación en binario de la anterior tabla de direcciones, veremos que todas ellas siguen un patrón común:
  
- +|< 60% >| 
-^ Linea f ^ Dirección en Hexadecimal ^ En Decimal ^ En Binario ^ +^ Linea f ^ Dirección en Hexadecimal ^ En Decimal ^ En Binario ^
 | 0 | $5800 | 22528 | //010110//**00000**00000b | | 0 | $5800 | 22528 | //010110//**00000**00000b |
 | 1 | $5820 | 22560 | //010110//**00001**00000b | | 1 | $5820 | 22560 | //010110//**00001**00000b |
Línea 186: Línea 197:
 | 4 | $5880 | 22656 | //010110//**00100**00000b | | 4 | $5880 | 22656 | //010110//**00100**00000b |
 | (...) | (...) | (...) | (...) | | (...) | (...) | (...) | (...) |
-| 21 | $5AA0 | 23200 | //010110//**10101**00000b | +| 21 | $5aa0 | 23200 | //010110//**10101**00000b | 
-| 22 | $5AC0 | 23232 | //010110//**10110**00000b | +| 22 | $5ac0 | 23232 | //010110//**10110**00000b | 
-| 23 | $5AE0 | 23264 | //010110//**10111**00000b |+| 23 | $5ae0 | 23264 | //010110//**10111**00000b |
  
-  * Los 6 bits más significativos de la dirección son 010110, que es la parte de la dirección que provoca que todas las posiciones estén entre $5800 y $5AFF.+  * Los 6 bits más significativos de la dirección son 010110, que es la parte de la dirección que provoca que todas las posiciones estén entre $5800 y $5aff.
   * Los bits 5, 6, 7 y 8 se corresponden con la fila que queremos consultar.   * Los bits 5, 6, 7 y 8 se corresponden con la fila que queremos consultar.
   * Los bits 0, 1, 2, 3 y 4 los utilizaremos para acceder a a la columna deseada. En la tabla anterior son siempre 0 porque estamos mostrando las direcciones de inicio de cada fila, es decir, de (0,f), por lo que estos bits 0-4 son 0.   * Los bits 0, 1, 2, 3 y 4 los utilizaremos para acceder a a la columna deseada. En la tabla anterior son siempre 0 porque estamos mostrando las direcciones de inicio de cada fila, es decir, de (0,f), por lo que estos bits 0-4 son 0.
Línea 207: Línea 218:
 <code z80> <code z80>
 ;------------------------------------------------------------- ;-------------------------------------------------------------
-; Get_Attribute_Offset_LR: +; Get_Attribute_Offset_LR:
 ; Obtener la direccion de memoria del atributo del caracter ; Obtener la direccion de memoria del atributo del caracter
 ; (c,f) especificado. Por David Webb. ; (c,f) especificado. Por David Webb.
Línea 215: Línea 226:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Get_Attribute_Offset_LR: Get_Attribute_Offset_LR:
-   LD AB            ; Ponemos en A la fila (000FFFFFb) +    ld ab                  ; Ponemos en A la fila (000FFFFFb) 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   RRCA               ; Desplazamos A 3 veces (A=A>>3)  +    rrca                     ; Desplazamos A 3 veces (A=A>>3) 
-   AND 3              ; A = A AND 00000011 = los 2 bits mas +    and %00000011            ; A = A and %00000011 = los 2 bits mas 
-                      ; altos de FILA (000FFFFFb -> 000000FFb+                             ; altos de FILA (%000FFFFF -> %000000FF
-   ADD A$58         ; Ponemos los bits 15-10 como 010110b +    add a%01011000         ; Ponemos los bits 15-10 como 010110b 
-   LD HA            ; Lo cargamos en el byte alto de HL +    ld ha                  ; Lo cargamos en el byte alto de HL 
-   LD AB            ; Recuperamos de nuevo en A la FILA +    ld ab                  ; Recuperamos de nuevo en A la FILA 
-   AND 7              ; Nos quedamos con los 3 bits que faltan +    and %00000111            ; Nos quedamos con los 3 bits que faltan 
-   RRCA            +    rrca 
-   RRCA               ; Los rotamos para colocarlos en su +    rrca                     ; Los rotamos para colocarlos en su 
-   RRCA               ; ubicacion final (<<3) +    rrca                     ; ubicacion final (<<5 = >>3) 
-   ADD AC           ; Sumamos el numero de columna +    add ac                 ; Sumamos el numero de columna 
-   LD LA            ; Lo colocamos en L +    ld la                  ; Lo colocamos en L 
-   RET                ; HL = 010110FFFFFCCCCCb +    ret                      ; HL = %010110FFFFFCCCCC
 </code> </code>
  
- La rutina realiza operaciones de bits para ubicar los datos de FILA, COLUMNA y 010011b en las posiciones que requiere la dirección destino final. Aconsejamos al lector revisar el capítulo dedicado a //Desplazamientos de memoria, bits y operaciones lógicas// para recordar el efecto de los desplazamientos realizados con operaciones como RRCA, SRA, SLA, RLC, etc.+ La rutina realiza operaciones de bits para ubicar los datos de FILA, COLUMNA y 010011b en las posiciones que requiere la dirección destino final. Aconsejamos al lector revisar el capítulo dedicado a //Desplazamientos de memoria, bits y operaciones lógicas// para recordar el efecto de los desplazamientos realizados con operaciones como ''RRCA''''SRA''''SLA''''RLC'', etc.
  
- El coste de ejecución de esta rutina es de (RET aparte) 70 t-estados y no hace uso de DE, lo que es un ahorro sustancial tanto en tiempo de ejecución como en preservación de un registro muy utilizado. + El coste de ejecución de esta rutina es de (''RET'' aparte) 70 t-estados y no hace uso de DE, lo que es un ahorro sustancial tanto en tiempo de ejecución como en preservación de un registro muy utilizado.
  
  La salida de esta rutina se puede utilizar directamente para almacenar en (HL) el atributo del caracter (c,f) cuya direccion hemos solicitado:  La salida de esta rutina se puede utilizar directamente para almacenar en (HL) el atributo del caracter (c,f) cuya direccion hemos solicitado:
  
 <code z80> <code z80>
-   LD B, 10 +    ld b, 10 
-   LD C, 12 +    ld c, 12 
-   CALL Get_Attribute_Offset_LR +    call Get_Attribute_Offset_LR 
-    + 
-   LD A, 85             ; Brillo + Magenta sobre Cyan +    ld a, 85                 ; Brillo + Magenta sobre Cyan 
-   LD (HL), A           ; Establecemos el atributo de (12,10)+    ld (hl), a               ; Establecemos el atributo de (12,10)
 </code> </code>
  
  La rutina no hace ningún tipo de comprobación del rango de COLUMNA y FILA, por lo que si proporcionamos valores menores de cero o mayores de 31 o 23 respectivamente se devolverá una dirección de memoria fuera del área de atributos.  La rutina no hace ningún tipo de comprobación del rango de COLUMNA y FILA, por lo que si proporcionamos valores menores de cero o mayores de 31 o 23 respectivamente se devolverá una dirección de memoria fuera del área de atributos.
  
- La versión para coordenadas en alta resolución de la anterior rutina (**Get_Attribute_Offset_HR(x,y)**) se implementa fácilmente mediante la conversión de las coordenadas (x,y) en coordenadas (c,f) dividiendo x e y entre 8 para obtener las coordenadas de baja resolución que corresponden al pixel que estamos considerando.+ La versión para coordenadas en alta resolución de la anterior rutina (''Get_Attribute_Offset_HR(x,y)'') se implementa fácilmente mediante la conversión de las coordenadas (x,y) en coordenadas (c,f) dividiendo x e y entre 8 para obtener las coordenadas de baja resolución que corresponden al pixel que estamos considerando.
  
  Para eso, las primeras líneas de la rutina deberían ser:  Para eso, las primeras líneas de la rutina deberían ser:
  
 <code z80> <code z80>
-   SRL B +    srl b 
-   SRL B +    srl b 
-   SRL B              ; B = B/8 -> Ahora B es FILA +    srl b                    ; B = B/8 -> Ahora B es FILA 
-    + 
-   SRL C +    srl c 
-   SRL C +    srl c 
-   SRL C              ; C = C/8 -> Ahora C es COLUMNA+    srl c                    ; C = C/8 -> Ahora C es COLUMNA
 </code> </code>
  
- Una vez obtenido (c,f), el desarrollo de la rutina es el mismo que en el caso de Get_Attribute_Offset_LR(c,f):+ Una vez obtenido (c,f), el desarrollo de la rutina es el mismo que en el caso de ''Get_Attribute_Offset_LR(c,f)'':
  
 <code z80> <code z80>
 ;------------------------------------------------------------- ;-------------------------------------------------------------
-; Get_Attribute_Offset_HR: +; Get_Attribute_Offset_HR:
 ; Obtener la direccion de memoria del atributo del caracter al ; Obtener la direccion de memoria del atributo del caracter al
 ; que corresponde el pixel (x,y) especificado. ; que corresponde el pixel (x,y) especificado.
Línea 275: Línea 286:
 ; Salida:    HL = Direccion del atributo ; Salida:    HL = Direccion del atributo
 ;------------------------------------------------------------- ;-------------------------------------------------------------
-Get_Attribute_Offset_HR:    +Get_Attribute_Offset_HR: 
-   SRL B +    srl b 
-   SRL B +    srl b 
-   SRL B              ; B = B/8 -> Ahora B es FILA +    srl b                    ; B = B/8 -> Ahora B es FILA
-    +
-   SRL C +
-   SRL C +
-   SRL C              ; C = C/8 -> Ahora C es COLUMNA+
  
-   LD A, B +    srl c 
-   RRCA +    srl c 
-   RRCA +    srl c                    C/8 -> Ahora C es COLUMNA
-   RRCA               Desplazamos A 3 veces (A=A>>3) +
-   AND 3              ; A = A AND 00000011 = los 2 bits mas +
-                      ; altos de FILA (000FFFFFb -> 000000FFb)+
  
-   ADD A, $58         ; Ponemos los bits 15-10 como 010110b +    ld a, b 
-   LD HA            ; Lo cargamos en el byte alto de HL +    rrca 
-   LD AB            ; Recuperamos de nuevo en A la FILA +    rrca 
-   AND 7              ; Nos quedamos con los 3 bits que faltan +    rrca                     ; Desplazamos 3 veces (A=A>>3) 
-   RRCA            +    and %00000011            ; A = A and 00000011 = los 2 bits mas 
-   RRCA               ; Los rotamos para colocarlos en su +                             ; altos de FILA (000FFFFFb -> 000000FFb) 
-   RRCA               ; ubicacion final (<<3) +    add a%01011000         ; Ponemos los bits 15-10 como 010110b 
-   ADD AC           ; Sumamos el numero de columna +    ld ha                  ; Lo cargamos en el byte alto de HL 
-   LD LA            ; Lo colocamos en L +    ld ab                  ; Recuperamos de nuevo en A la FILA 
-   RET                ; HL = 010110FFFFFCCCCCb +    and %00000011            ; Nos quedamos con los 3 bits que faltan 
 +    rrca 
 +    rrca                     ; Los rotamos para colocarlos en su 
 +    rrca                     ; ubicacion final (<<5 = >>3) 
 +    add ac                 ; Sumamos el numero de columna 
 +    ld la                  ; Lo colocamos en L 
 +    ret                      ; HL = 010110FFFFFCCCCCb
 </code> </code>
  
Línea 310: Línea 320:
  
  La siguiente rutina nos proporciona, dada una dirección de memoria apuntada por HL y dentro de la zona de atributos, la posición (c,f) que corresponde a dicho carácter. Se basa en la descomposición de HL en los campos que componen la dirección del atributo:  La siguiente rutina nos proporciona, dada una dirección de memoria apuntada por HL y dentro de la zona de atributos, la posición (c,f) que corresponde a dicho carácter. Se basa en la descomposición de HL en los campos que componen la dirección del atributo:
- +
 <code z80> <code z80>
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 321: Línea 331:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Get_Attribute_Coordinates_LR: Get_Attribute_Coordinates_LR:
-                      ; Descomponemos HL = 010110FF FFFCCCCCb  +                             ; Descomponemos HL = 010110FF FFFCCCCCb 
-   LD AH            ; A = 010110FFb +    ld ah                  ; A = 010110FFb 
-   AND 3              ; A = bits 0, 1 de HL = 2 bits altos de F, CF=0 +    and %00000011            ; A = bits 0, 1 de HL = 2 bits altos de F, CF=0 
-   RLCA +    rlca 
-   RLCA +    rlca 
-   RLCA               ; Rotacion a izquierda 000000FFb -> 000FF000b +    rlca                     ; Rotacion a izquierda 000000FFb -> 000FF000b 
-   LD BA            ; B = 000FF000b+    ld ba                  ; B = 000FF000b
  
-   LD AL +    ld al 
-   AND 224            ; Nos quedamos con los 3 bits mas altos +    and %11100000            ; Nos quedamos con los 3 bits mas altos 
-   RLC A               +    rlca 
-   RLC A +    rlca 
-   RLC A              ; Rotacion a izquierda FFF00000b -> 00000FFFb +    rlca                     ; Rotacion a izquierda FFF00000b -> 00000FFFb 
-   OR B               ; A = A + B = 000FFFFFb +    or b                     ; A = A + B = 000FFFFFb 
-   LD BA            ; B = FILA+    ld ba                  ; B = FILA
  
-   LD AL +    ld al 
-   AND 31             ; Nos quedamos con los 5 bits mas bajos +    and %00011111            ; Nos quedamos con los 5 bits mas bajos 
-   LD CA            ; C = COLUMNA +    ld ca                  ; C = COLUMNA 
-    + 
-   RET+    ret
 </code> </code>
  
  De nuevo, el código no incluye ningún tipo de control sobre la dirección que se le proporciona, que podría estar fuera de la zona de atributos y le haría devolver valores en el rango 0-255 para B y para C que, obviamente, no corresponden con la dirección entrada en HL.  De nuevo, el código no incluye ningún tipo de control sobre la dirección que se le proporciona, que podría estar fuera de la zona de atributos y le haría devolver valores en el rango 0-255 para B y para C que, obviamente, no corresponden con la dirección entrada en HL.
  
- La rutina para trabajar con coordenadas en alta resolución (**Get_Attribute_Coordinates_HR(x,y)**) es esencialmente idéntica a su versión en baja resolución, salvo que finaliza multiplicando B y C por 8 (mediante instrucciones de desplazamiento a izquierda) para convertir las coordenadas (c,f) en (x,y). Los valores (x,y) resultantes se corresponderán con el pixel superior izquierdo del bloque apuntado por (c,f).+ La rutina para trabajar con coordenadas en alta resolución (''Get_Attribute_Coordinates_HR(x,y)'') es esencialmente idéntica a su versión en baja resolución, salvo que finaliza multiplicando B y C por 8 (mediante instrucciones de desplazamiento a izquierda) para convertir las coordenadas (c,f) en (x,y). Los valores (x,y) resultantes se corresponderán con el pixel superior izquierdo del bloque apuntado por (c,f).
  
 <code z80> <code z80>
Línea 358: Línea 368:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Get_Attribute_Coordinates_HR: Get_Attribute_Coordinates_HR:
-                      ; Descomponemos HL = 010110FF FFFCCCCCb  +                             ; Descomponemos HL = 010110FF FFFCCCCCb 
-   LD AH            ; A = 010110FFb +    ld ah                  ; A = 010110FFb 
-   AND              ; A = bits 0, 1 de HL = 2 bits altos de F, CF=0 +    and                    ; A = bits 0, 1 de HL = 2 bits altos de F, CF=0 
-   RLCA +    rlca 
-   RLCA +    rlca 
-   RLCA               ; Rotacion a izquierda 000000FFb -> 000FF000b +    rlca                     ; Rotacion a izquierda 000000FFb -> 000FF000b 
-   LD BA            ; B = 000FF000b+    ld ba                  ; B = 000FF000b
  
-   LD AL +    ld al 
-   AND 224            ; Nos quedamos con los 3 bits mas altos +    and %11100000            ; Nos quedamos con los 3 bits mas altos 
-   RLC A               +    rlca 
-   RLC A +    rlca 
-   RLC A              ; Rotacion a izquierda FFF00000b -> 00000FFFb +    rlca                     ; Rotacion a izquierda FFF00000b -> 00000FFFb 
-   OR B               ; A = A + B = 000FFFFFb +    or b                     ; A = A + B = 000FFFFFb 
-   LD BA            ; B = FILA+    ld ba                  ; B = FILA
  
-   LD AL +    ld al 
-   AND 31             ; Nos quedamos con los 5 bits mas bajos +    and %00011111            ; Nos quedamos con los 5 bits mas bajos 
-   LD CA            ; C = COLUMNA+    ld ca                  ; C = COLUMNA
  
-   SLA C +    sla c 
-   SLA C +    sla c 
-   SLA C              ; C = C*8+    sla c                    ; C = C*8
  
-   SLA B +    sla b 
-   SLA B +    sla b 
-   SLA B              ; B = B*8 +    sla b                    ; B = B*8
-    +
-   RET+
  
 +    ret
 </code> </code>
  
Línea 399: Línea 408:
 <code z80> <code z80>
 Atributo_derecha: Atributo_derecha:
-  INC HL            ; HL = HL + 1 +    inc hl                   ; HL = HL + 1 
-  +
 Atributo_izquierda: Atributo_izquierda:
-  DEC HL            ; HL = HL - 1 +    dec hl                   ; HL = HL - 1 
-  +
 Atributo_abajo: Atributo_abajo:
-  LD DE, 32 +    ld de, 32 
-  ADD HLDE        ; HL = HL + 32 +    add hlde               ; HL = HL + 32
  
 Atributo_arriba: Atributo_arriba:
-  LD DE, 32 +    ld de-32 
-  OR A              ; Clear Carry Flag +    add hlde               ; HL = HL - 32
-  SBC HLDE        ; HL = HL - 32 +
 </code> </code>
  
- Si tenemos la necesidad de preservar el valor del registro DE y el utilizarlo para sumar o restar 32 nos supone hacer un PUSH y POP del mismo a la pila y queremos evitar esto, podemos utilizar una combinación de ADD y ADC para sumar 32 al byte bajo de HL 0 + Acarreo al byte alto de HL:+ Si tenemos la necesidad de preservar el valor del registro DE y el utilizarlo para sumar o restar 32 nos supone hacer un PUSH y POP del mismo a la pila y queremos evitar esto, podemos sumar la parte baja después incrementar la parte alta si ha habido acarreo:
  
 <code z80> <code z80>
-Atributo_abajo_sin_usar_DE+Atributo_abajo_sin_usar_DE_2
-  LD AL           ; A = L +    ld al                  ; A = L 
-  ADD 32            ; Sumamos A = A + 32 . El Carry Flag se ve afectado. +    add a, 32                ; Sumamos A = A + 32 . El Carry Flag se ve afectado. 
-  LD LA           ; Guardamos en L (L = L+32) +    ld la                  ; Guardamos en L (L = L+32) 
-  LD A0           ; Ponemos A a cero, no podemos usar un "XOR A" +    jr ncattrab_noinc 
-                    ; o un "OR A" porque afectariamos al Carry Flag. +    inc h 
-  ADC H             ; A = H + CarryFlag +attrab_noinc:                ; Ahora HL = (H+CF)*256 + (L+32) = HL + 32
-  LD H, A           ; H = H + CarryFlag +
-                    ; Ahora HL = (H+CF)*256 + (L+32) = HL + 32+
  
 Atributo_arriba_sin_usar_DE: Atributo_arriba_sin_usar_DE:
-  LD AL           ; A = L +    ld al                  ; A = L 
-  SUB 32            ; Restamos A = A - 32 . El Carry Flag se ve afectado. +    sub 32                   ; Restamos A = A - 32 . El Carry Flag se ve afectado. 
-  LD LA           ; Guardamos en L (L = L-32) +    ld la                  ; Guardamos en L (L = L-32) 
-  LD AH           ; Ponemos A a "H" +    jr ncattrab_nodec 
-  SBC A, 0          ; A = H - CarryFlag +    dec h 
-  LD H, A           ; H = H - CarryFlag +attrab_nodec:                ; Ahora HL = (H+CF)*256 + (L+32) = HL 32 
-                    ; Ahora HL = (H-CF)*256 + (L-32) = HL 32+
 </code> </code>
  
- Este código no utiliza DE pero se apoya en el registro A para los cálculos. Si necesitamos preservar su valor, siempre podemos realizar un **EX AFAF** antes y después de la ejecución de la rutina.+ Nótese que, como nos apunta Jaime Tejedor en los foros de Speccy.org, el código con salto... 
 + 
 +<code z80> 
 +    jr nc, attrab_noinc 
 +    inc h 
 +attrab_noinc: 
 +</code> 
 + 
 + ... es más rápido que la combinación de ADD y ADC para sumar 32 al byte bajo de HL y 0 + Acarreo al byte alto de HL: 
 + 
 +<code z80> 
 +    ld a, 0                  ; Ponemos A a cero, no podemos usar un "xor a" 
 +                             ; o un "or a" porque afectariamos al Carry Flag. 
 +    adc a, h                 ; A = H + CarryFlag 
 +    ld h, a                  ; H = H + CarryFlag 
 +                             ; Ahora HL = (H+CF)*256 + (L+32) = HL + 32 
 +</code> 
 + 
 + Este código no utiliza DE pero se apoya en el registro A para los cálculos. Si necesitamos preservar su valor, siempre podemos realizar un ''ex afaf<nowiki>'</nowiki>'' antes y después de la ejecución de la rutina.
  
-\\  
 \\  \\ 
 ===== Cálculo de posiciones de caracteres ===== ===== Cálculo de posiciones de caracteres =====
Línea 459: Línea 482:
 \\  \\ 
  
- Nótese que podemos realizar las 2 primeras rutinas de forma que devuelvan el offset calculado bien en el registro DE o bien en el registro HL. Según utilicemos los registros en el código que llama a la rutina, puede sernos más conveniente recibir el valor en uno u otro registro. Si resulta necesario, podemos adaptar el código de las rutinas para que funcionen con uno u otro registro, o utilizar al final de la misma (o tras el CALL) un **EX HL, DE** que devuelva el resultado en el registro que más nos interese.+ Nótese que podemos realizar las 2 primeras rutinas de forma que devuelvan el offset calculado bien en el registro DE o bien en el registro HL. Según utilicemos los registros en el código que llama a la rutina, puede sernos más conveniente recibir el valor en uno u otro registro. Si resulta necesario, podemos adaptar el código de las rutinas para que funcionen con uno u otro registro, o utilizar al final de la misma (o tras el ''call'') un ''EX HL, DE'' que devuelva el resultado en el registro que más nos interese.
  
  
Línea 472: Línea 495:
  
 \\  \\ 
 +|< 80% >|
 ^ Linea f ^ Direccion (0,f) (HEX) ^ (Decimal) ^ (Binario) ^ Tercio (0-2) ^ Fila dentro del tercio ^ ^ Linea f ^ Direccion (0,f) (HEX) ^ (Decimal) ^ (Binario) ^ Tercio (0-2) ^ Fila dentro del tercio ^
 | 0 | $4000 | 16384 | 0100000000000000b | 0 (00b) | 0 | | 0 | $4000 | 16384 | 0100000000000000b | 0 (00b) | 0 |
Línea 478: Línea 502:
 | 3 | $4060 | 16480 | 0100000001100000b | 0 (00b) | 3 | | 3 | $4060 | 16480 | 0100000001100000b | 0 (00b) | 3 |
 | 4 | $4080 | 16512 | 0100000010000000b | 0 (00b) | 4 | | 4 | $4080 | 16512 | 0100000010000000b | 0 (00b) | 4 |
-| 5 | $40A0 | 16544 | 0100000010100000b | 0 (00b) | 5 | +| 5 | $40a0 | 16544 | 0100000010100000b | 0 (00b) | 5 | 
-| 6 | $40C0 | 16576 | 0100000011000000b | 0 (00b) | 6 | +| 6 | $40c0 | 16576 | 0100000011000000b | 0 (00b) | 6 | 
-| 7 | $40E0 | 16608 | 0100000011100000b | 0 (00b) | 7 |+| 7 | $40e0 | 16608 | 0100000011100000b | 0 (00b) | 7 |
 | 8 | $4800 | 18432 | 0100100000000000b | 1 (01b) | 0 | | 8 | $4800 | 18432 | 0100100000000000b | 1 (01b) | 0 |
 | 9 | $4820 | 18464 | 0100100000100000b | 1 (01b) | 1 | | 9 | $4820 | 18464 | 0100100000100000b | 1 (01b) | 1 |
Línea 486: Línea 510:
 | 11 | $4860 | 18528 | 0100100001100000b | 1 (01b) | 3 | | 11 | $4860 | 18528 | 0100100001100000b | 1 (01b) | 3 |
 | 12 | $4880 | 18560 | 0100100010000000b | 1 (01b) | 4 | | 12 | $4880 | 18560 | 0100100010000000b | 1 (01b) | 4 |
-| 13 | $48A0 | 18592 | 0100100010100000b | 1 (01b) | 5 | +| 13 | $48a0 | 18592 | 0100100010100000b | 1 (01b) | 5 | 
-| 14 | $48C0 | 18624 | 0100100011000000b | 1 (01b) | 6 | +| 14 | $48c0 | 18624 | 0100100011000000b | 1 (01b) | 6 | 
-| 15 | $48E0 | 18656 | 0100100011100000b | 1 (01b) | 7 |+| 15 | $48e0 | 18656 | 0100100011100000b | 1 (01b) | 7 |
 | 16 | $5000 | 20480 | 0101000000000000b | 2 (10b) | 0 | | 16 | $5000 | 20480 | 0101000000000000b | 2 (10b) | 0 |
 | 17 | $5020 | 20512 | 0101000000100000b | 2 (10b) | 1 | | 17 | $5020 | 20512 | 0101000000100000b | 2 (10b) | 1 |
Línea 494: Línea 518:
 | 19 | $5060 | 20576 | 0101000001100000b | 2 (10b) | 3 | | 19 | $5060 | 20576 | 0101000001100000b | 2 (10b) | 3 |
 | 20 | $5080 | 20608 | 0101000010000000b | 2 (10b) | 4 | | 20 | $5080 | 20608 | 0101000010000000b | 2 (10b) | 4 |
-| 21 | $50A0 | 20640 | 0101000010100000b | 2 (10b) | 5 | +| 21 | $50a0 | 20640 | 0101000010100000b | 2 (10b) | 5 | 
-| 22 | $50C0 | 20672 | 0101000011000000b | 2 (10b) | 6 | +| 22 | $50c0 | 20672 | 0101000011000000b | 2 (10b) | 6 | 
-| 23 | $50E0 | 20704 | 0101000011100000b | 2 (10b) | 7 |+| 23 | $50e0 | 20704 | 0101000011100000b | 2 (10b) | 7 |
 \\  \\ 
  
  Examinemos (y marquemos) los bits de la representación binaria de la dirección para una selección de elementos de la tabla:  Examinemos (y marquemos) los bits de la representación binaria de la dirección para una selección de elementos de la tabla:
  
 +|< 80% >|
 ^ Linea f ^ Direccion (0,f) (HEX) ^ (Decimal) ^ (Binario) ^ Tercio (0-2) ^ Fila dentro del tercio ^ ^ Linea f ^ Direccion (0,f) (HEX) ^ (Decimal) ^ (Binario) ^ Tercio (0-2) ^ Fila dentro del tercio ^
 | 0 | $4000 | 16384 | **010**//00//000**000**00000b | 0 (00b) | 0 | | 0 | $4000 | 16384 | **010**//00//000**000**00000b | 0 (00b) | 0 |
Línea 511: Línea 536:
 | 10 | $4840 | 18496 | **010**//01//000**010**00000b | 1 (01b) | 2 | | 10 | $4840 | 18496 | **010**//01//000**010**00000b | 1 (01b) | 2 |
 | (...) | (...) | (...) | (...) | (...) | (...) | | (...) | (...) | (...) | (...) | (...) | (...) |
-| 23 | $50E0 | 20704 | **010**//10//000**111**00000b | 2 (10b) | 7 |+| 23 | $50e0 | 20704 | **010**//10//000**111**00000b | 2 (10b) | 7 |
  
  Lo primero que puede llamarnos la atención es lo siguiente:  Lo primero que puede llamarnos la atención es lo siguiente:
Línea 522: Línea 547:
     * Conclusión: el número de tercio se corresponde con los 2 bits superiores de la coordenada Y, de tal forma que las fila (0,7) están en el tercio 00b, las filas 8-15 en el tercio 01b, y las 16-23 en el 10b.     * Conclusión: el número de tercio se corresponde con los 2 bits superiores de la coordenada Y, de tal forma que las fila (0,7) están en el tercio 00b, las filas 8-15 en el tercio 01b, y las 16-23 en el 10b.
   * Hay una relación directa entre el número de fila dentro de cada tercio (0-7) y los 3 bits superiores (5-7) del byte bajo de la dirección.   * Hay una relación directa entre el número de fila dentro de cada tercio (0-7) y los 3 bits superiores (5-7) del byte bajo de la dirección.
-  * Los 3 bytes más significativos de la dirección son siempre 010b. Esta es la parte de la composición de la dirección que ubica el offset en memoria en el rango de direcciones del área de imagen de la videoram ($4000 a $57FF).+  * Los 3 bytes más significativos de la dirección son siempre 010b. Esta es la parte de la composición de la dirección que ubica el offset en memoria en el rango de direcciones del área de imagen de la videoram ($4000 a $57ff).
   * Los 5 bytes menos significativos de la dirección son siempre cero en la tabla. En realidad, representan a la columna (posición c de carácter dentro de los 32 bytes de datos horizontales) pero al estar calculando direcciones de inicio de línea (c = 0 = 00000b), en nuestro caso son siempre cero.   * Los 5 bytes menos significativos de la dirección son siempre cero en la tabla. En realidad, representan a la columna (posición c de carácter dentro de los 32 bytes de datos horizontales) pero al estar calculando direcciones de inicio de línea (c = 0 = 00000b), en nuestro caso son siempre cero.
 \\  \\ 
Línea 551: Línea 576:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Get_Line_Offset_LR: Get_Line_Offset_LR:
-   LD AB         ; A = B, para extraer los bits de tercio +    ld ab                  ; A = B, para extraer los bits de tercio 
-   AND $18         ; A = A AND 00011000b +    and %00011000            ; A = A and 00011000b 
-                   ; A = estado de bits de TERCIO desde FILA +                             ; A = estado de bits de TERCIO desde FILA 
-   ADD A$40      ; Sumamos $40 (bits superiores = 010) +    add a%01000000         ; Sumamos $40 (bits superiores = 010) 
-   LD HA         ; Ya tenemos la parte alta calculada +    ld ha                  ; Ya tenemos la parte alta calculada 
-                   ; H = 010TT000 +                             ; H = 010TT000 
-   LD AB         ; Ahora calculamos la parte baja +    ld ab                  ; Ahora calculamos la parte baja 
-   AND 7           ; Nos quedamos con los bits más bajos de FILA +    and %00000111            ; Nos quedamos con los bits más bajos de FILA 
-                   ; que coinciden con FT (Fila dentro del tercio) +                             ; que coinciden con FT (Fila dentro del tercio) 
-   RRCA            ; Ahora A = 00000NNNb  (donde N=FT) +    rrca                     ; Ahora A = 00000NNNb  (donde N=FT) 
-   RRCA            ; Desplazamos A 3 veces +    rrca                     ; Desplazamos A 3 veces 
-   RRCA            ; A = NNN00000b +    rrca                     ; A = NNN00000b 
-   LD LA         ; Lo cargamos en la parte baja de la direccion +    ld la                  ; Lo cargamos en la parte baja de la direccion 
-   RET             ; HL = 010TT000NNN00000b+    ret                      ; HL = 010TT000NNN00000b
 </code> </code>
  
Línea 603: Línea 628:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Get_Char_Offset_LR: Get_Char_Offset_LR:
-   LD AB         ; A = B, para extraer los bits de tercio +    ld ab                  ; A = B, para extraer los bits de tercio 
-   AND $18         ; A = A AND 00011000b +    and %00011000            ; A = A and 00011000b 
-                   ; A = estado de bits de TERCIO desde FILA +                             ; A = estado de bits de TERCIO desde FILA 
-   ADD A$40      ; Sumamos $40 (bits superiores = 010) +    add a%01000000         ; Sumamos $40 (bits superiores = 010) 
-   LD HA         ; Ya tenemos la parte alta calculada +    ld ha                  ; Ya tenemos la parte alta calculada 
-                   ; H = 010TT000 +                             ; H = 010TT000 
-   LD AB         ; Ahora calculamos la parte baja +    ld ab                  ; Ahora calculamos la parte baja 
-   AND 7           ; Nos quedamos con los bits más bajos de FILA +    and %00000111            ; Nos quedamos con los bits más bajos de FILA 
-                   ; que coinciden con FT (Fila dentro del tercio) +                             ; que coinciden con FT (Fila dentro del tercio) 
-   RRCA            ; Ahora A = 00000NNNb     (N=FT) +    rrca                     ; Ahora A = 00000NNNb     (N=FT) 
-   RRCA            ; Desplazamos A 3 veces a la derecha +    rrca                     ; Desplazamos A 3 veces a la derecha 
-   RRCA            ; A = NNN00000b +    rrca                     ; A = NNN00000b 
-   ADD AC        ; Sumamos COLUMNA -> A = NNNCCCCCb +    add ac                 ; Sumamos COLUMNA -> A = NNNCCCCCb 
-   LD LA         ; Lo cargamos en la parte baja de la direccion +    ld la                  ; Lo cargamos en la parte baja de la direccion 
-   RET             ; HL = 010TT000NNNCCCCCb+    ret                      ; HL = 010TT000NNNCCCCCb
 </code> </code>
  
Línea 632: Línea 657:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Get_Char_Offset_HR: Get_Char_Offset_HR:
-   SRL B +    srl b 
-   SRL B +    srl b 
-   SRL B           ; B = B/8 -> Ahora B es FILA +    srl b                    ; B = B/8 -> Ahora B es FILA
-  +
-   SRL C +
-   SRL C +
-   SRL C           ; C = C/8 -> Ahora C es COLUMNA+
  
-   (...)           ; Resto de la rutina Get_Char_Offset_LR +    srl c 
-   RET+    srl c 
 +    srl c                    ; C = C/8 -> Ahora C es COLUMNA 
 + 
 +    (...)                    ; Resto de la rutina Get_Char_Offset_LR 
 +    ret
 </code> </code>
  
Línea 648: Línea 673:
  
  Nuestra siguiente subrutina tiene como objetivo el calcular la posición (c,f) en baja resolución de un carácter dado un offset en memoria que almacene alguno de los 64 pixeles del mismo. Llamar a esta función con la dirección de cualquiera de las 8 líneas de un carácter devolvería el mismo par de coordenadas (c,f):  Nuestra siguiente subrutina tiene como objetivo el calcular la posición (c,f) en baja resolución de un carácter dado un offset en memoria que almacene alguno de los 64 pixeles del mismo. Llamar a esta función con la dirección de cualquiera de las 8 líneas de un carácter devolvería el mismo par de coordenadas (c,f):
-  
  
 <code z80> <code z80>
Línea 661: Línea 685:
 Get_Char_Coordinates_LR: Get_Char_Coordinates_LR:
  
-   ; HL = 010TT000 NNNCCCCCb -> = 000TTNNNb y = 000CCCCCb+    ; HL = 010TT000 NNNCCCCCb -> 
 +    ;      Fila = 000TTNNNb y Columna = 000CCCCCb
  
-                   ; Calculo de la fila: +                             ; Calculo de la fila: 
-   LD AH         ; A = H, para extraer los bits de tercio +    ld ah                  ; A = H, para extraer los bits de tercio 
-   AND $18         ; A = 000TT000b +    and %00011000            ; A = 000TT000b 
-   LD BA         ; B = A = 000TT000b+    ld ba                  ; B = A = 000TT000b
  
-   LD AL         ; A = L, para extraer los bits de N (FT) +    ld al                  ; A = L, para extraer los bits de N (FT) 
-   AND $E0         ; A = A AND 11100000b = NNN00000b +    and %0b11100000          ; A = A and 11100000b = NNN00000b 
-   RLC A           ; Rotamos A 3 veces a la izquierda +    rlc a                    ; Rotamos A 3 veces a la izquierda 
-   RLC A +    rlc a 
-   RLC A           ; A = 00000NNNb +    rlc a                    ; A = 00000NNNb 
-   OR B            ; A = A OR B = 000TTNNNb +    or b                     ; A = A or b = 000TTNNNb 
-   LD BA         ; B = A = 000TTNNNb +    ld ba                  ; B = A = 000TTNNNb
- +
-                   ; Calculo de la columna: +
-   LD A, L         ; A = L, para extraer los bits de columna +
-   AND $1F         ; Nos quedamos con los ultimos 5 bits de L +
-   LD C, A         ; C = Columna +
-   RET             ; HL = 010TT000NNNCCCCCb+
  
 +                             ; Calculo de la columna:
 +    ld a, l                  ; A = L, para extraer los bits de columna
 +    and %00011111            ; Nos quedamos con los ultimos 5 bits de L
 +    ld c, a                  ; C = Columna
 +             ret             ; HL = 010TT000NNNCCCCCb
 </code> </code>
  
- Adaptar esta rutina a alta resolución (**Get_Char_Coordinates_HR(x,y)**) implicaría el multiplicar las coordenadas X e Y por 8, añadiendo el siguiente código inmediatamente antes del RET:+ Adaptar esta rutina a alta resolución (''Get_Char_Coordinates_HR(x,y)'') implicaría el multiplicar las coordenadas X e Y por 8, añadiendo el siguiente código inmediatamente antes del ret:
  
 <code z80> <code z80>
-   SLA C +    sla c 
-   SLA C +    sla c 
-   SLA C              ; C = C*8 +    sla c                    ; C = C*8 
-  + 
-   SLA B +    sla b 
-   SLA B +    sla b 
-   SLA B              ; B = B*8+    sla b                    ; B = B*8
 </code> </code>
  
Línea 705: Línea 729:
 **Recorrer los 8 scanlines de un bloque** **Recorrer los 8 scanlines de un bloque**
  
- Dada en HL la dirección del primer scanline de un bloque, podemos avanzar a lo largo de los 7 scanlines del mismo bloque sumando "256" a dicha dirección. Como sumar 256 equivale a incrementar la parte alta de la dirección, podemos subir y bajar al scanline anterior y siguiente de los 8 que componen el carácter mediante simples **DEC H** **INC H**:+ Dada en HL la dirección del primer scanline de un bloque, podemos avanzar a lo largo de los 7 scanlines del mismo bloque sumando "256" a dicha dirección. Como sumar 256 equivale a incrementar la parte alta de la dirección, podemos subir y bajar al scanline anterior y siguiente de los 8 que componen el carácter mediante simples ''dec h'' ''inc h'':
  
 <code z80> <code z80>
 Scanline_Arriba_HL: Scanline_Arriba_HL:
-  DEC H            ; H = H - 1  (HL = HL-255)+  dec h                      ; H = H - 1  (HL = HL-255)
  
 Scanline_Abajo_HL: Scanline_Abajo_HL:
-  INC H            ; H = H + 1  (HL = HL-255)+  inc h                      ; H = H + 1  (HL = HL-255)
 </code> </code>
  
Línea 728: Línea 752:
 <code z80> <code z80>
 Caracter_Derecha_HL: Caracter_Derecha_HL:
-  INC HL            ; HL = HL + 1 +    inc hl                   ; HL = HL + 1 
-  +
 Caracter_Izquierda_HL: Caracter_Izquierda_HL:
-  DEC HL            ; HL = HL - 1+    dec hl                   ; HL = HL - 1
 </code> </code>
  
Línea 737: Línea 761:
  Moverse un //carácter arriba// o //abajo// es más laborioso ya que tenemos que tener en cuenta los cambios de tercios. Para ello, basta con que recordemos la disposición de los bits de la dirección:  Moverse un //carácter arriba// o //abajo// es más laborioso ya que tenemos que tener en cuenta los cambios de tercios. Para ello, basta con que recordemos la disposición de los bits de la dirección:
  
 +|< 80% >|
 ^ Bits = ^ Dirección VRAM ^ Bits de Tercio ^ Bits de scanline ^ Bits de Carácter-Y ^ Bits de Columna ^ ^ Bits = ^ Dirección VRAM ^ Bits de Tercio ^ Bits de scanline ^ Bits de Carácter-Y ^ Bits de Columna ^
 | HL = | 010 | TT | SSS | NNN | CCCCC | | HL = | 010 | TT | SSS | NNN | CCCCC |
Línea 749: Línea 774:
  
 <code z80> <code z80>
-   LD AL                     ; Cargamos A en L y le sumamos 32 para +Caracter_Abajo_HL: 
-   ADD A32                   ; incrementar "Bloque dentro del tercio" +    ld al                       ; Cargamos A en L y le sumamos 32 para 
-   LD LA                     ; L = A +    add a%00100000              ; incrementar "Bloque dentro del tercio" (+32) 
-   JR NC, no_ajustar_H_abajob  ; Si esta suma produce acarreo, ajustar +    ld la                       ; L = A 
-   LD AH                     ; la parte alta sumando 8 a H (TT = TT + 1). +    jr nc, no_ajustar_H_abajob    ; Si esta suma produce acarreo, ajustar 
-   ADD A8                    ; Ahora NNN=000b y TT se ha incrementado. +    ld ah                       ; la parte alta sumando 8 a H (TT = TT + 1). 
-   LD HA                     ; H = A+    add a%00001000              ; Ahora NNN=000b y TT se ha incrementado. 
 +    ld ha                       ; H = A
 no_ajustar_H_abajob no_ajustar_H_abajob
-                               ; Ahora HL apunta al bloque de debajo.+                                  ; Ahora HL apunta al bloque de debajo.
 </code> </code>
  
Línea 763: Línea 789:
  
 <code z80> <code z80>
-   LD A, L                       ; Cargamos L en A 
-   AND 224                       ; A = A AND 11100000b 
-   JR NZ, no_ajustar_h_arribab   ; Si no es cero, no retrocedemos tercio 
-   LD A, H                       ; Si es cero, ajustamos tercio (-1) 
-   SUB 8                         ; Decrementamos TT 
-   LD H, A 
-no_ajustar_h_arribab: 
-   LD A, L                       ; Decrementar NNN 
-   SUB 32 
-   LD L, A                       ; NNN = NNN-1 
-</code> 
- 
- En forma de rutina: 
- 
-<code z80> 
-Caracter_Abajo_HL: 
-   LD A, L                     ; Cargamos A en L y le sumamos 32 para 
-   ADD A, 32                   ; incrementar "Bloque dentro del tercio" 
-   LD L, A                     ; L = A 
-   RET NC                      ; Si esta suma no produce acarreo, fin 
-   LD A, H                     ; la parte alta sumando 8 a H (TT = TT + 1). 
-   ADD A, 8                    ; Ahora NNN=000b y TT se ha incrementado. 
-   LD H, A                     ; H = A 
-   RET 
-  
 Caracter_Arriba_HL: Caracter_Arriba_HL:
-   LD A                      ; Cargamos L en A +    ld a                      ; Cargamos L en A 
-   AND 224                       ; A = A AND 11100000b +    and %11100000                 ; A = A and 11100000b 
-   JR NZ, nofix_h_arribab        ; Si no es cero, no retrocedemos tercio +    jr nz, nofix_h_arribab        ; Si no es cero, no retrocedemos tercio 
-   LD A                      ; Si es cero, ajustamos tercio (-1) +    ld a                      ; Si es cero, ajustamos tercio (-1) 
-   SUB 8                         ; Decrementamos TT +    sub %00001000                 ; Decrementamos TT 
-   LD HA+    ld ha
 nofix_h_arribab: nofix_h_arribab:
-   LD A                      ; Decrementar NNN +    ld a                      ; Decrementar NNN 
-   SUB 32 +    sub 32 
-   LD L                      ; NNN = NNN-1 +    ld l                      ; NNN = NNN-1 
-   RET+    ret
 </code> </code>
  
Línea 805: Línea 806:
  
 \\  \\ 
-\\ 
 ===== Cálculo de posiciones de píxeles ===== ===== Cálculo de posiciones de píxeles =====
  
Línea 837: Línea 837:
  
 \\  \\ 
 +|< 80% >|
 ^ Línea LowRes ^ Línea HiRes ^ Direccion (0,f) (HEX) ^ (Decimal) ^ (Binario) ^ Tercio (0-2) ^ Fila en el tercio ^ ^ Línea LowRes ^ Línea HiRes ^ Direccion (0,f) (HEX) ^ (Decimal) ^ (Binario) ^ Tercio (0-2) ^ Fila en el tercio ^
 | 0 | 0 | $4000 | 16384 | **010**//00//000**000**00000b | 0 (00b) | 0 | | 0 | 0 | $4000 | 16384 | **010**//00//000**000**00000b | 0 (00b) | 0 |
Línea 848: Línea 849:
  
 \\  \\ 
 +|< 80% >|
 ^ Coord. F ^ Coord. Y ^ Coord. Y (Binario) ^ Direccion (0,y) (HEX) ^ (Binario) ^ Tercio (0-2) ^ Fila en el tercio ^ ^ Coord. F ^ Coord. Y ^ Coord. Y (Binario) ^ Direccion (0,y) (HEX) ^ (Binario) ^ Tercio (0-2) ^ Fila en el tercio ^
 | 0 | 0 | 00//000//**000**b | $4100 | 01000**000**//000//00000b | 0 (00b) | 0 | | 0 | 0 | 00//000//**000**b | $4100 | 01000**000**//000//00000b | 0 (00b) | 0 |
Línea 873: Línea 875:
 \\  \\ 
  
- Por otra parte, ya sabemos que C es X / 8, por lo que ya tenemos todos los componentes para realizar nuestra rutina de cálculo de dirección de memoria. + Por otra parte, ya sabemos que C es X / 8, por lo que ya tenemos todos los componentes para realizar nuestra rutina de cálculo de dirección de memoria.
  
 \\  \\ 
Línea 888: Línea 890:
 \\  \\ 
  
- No obstante, recordemos que esta dirección de memoria obtenida hace referencia a 8 píxeles, por lo que necesitamos obtener además la información del número de bit con el que se corresponde nuestro pixel, que podemos extraer del resto de la división entre 8 de la coordenada X (P = X AND 7).+ No obstante, recordemos que esta dirección de memoria obtenida hace referencia a 8 píxeles, por lo que necesitamos obtener además la información del número de bit con el que se corresponde nuestro pixel, que podemos extraer del resto de la división entre 8 de la coordenada X (P = X and %00000111).
  
  La rutina resultante es similar a la vista en baja resolución con la descomposición de la coordenada Y en el "número de scanline" (0-7) y la "fila dentro del tercio (0-7)":  La rutina resultante es similar a la vista en baja resolución con la descomposición de la coordenada Y en el "número de scanline" (0-7) y la "fila dentro del tercio (0-7)":
Línea 903: Línea 905:
 Get_Pixel_Offset_HR: Get_Pixel_Offset_HR:
  
-   ; Calculo de la parte alta de la direccion: +    ; Calculo de la parte alta de la direccion: 
-   LD AB +    ld ab 
-   AND 7                       ; A = 00000SSSb +    and %00000111            ; A = 00000SSSb 
-   LD HA                     ; Lo guardamos en H +    ld ha                  ; Lo guardamos en H 
-   LD AB                     ; Recuperamos de nuevo Y +    ld ab                  ; Recuperamos de nuevo Y 
-   RRA +    rra 
-   RRA +    rra 
-   RRA                         ; Rotamos para asi obtener el tercio +    rra                      ; Rotamos para asi obtener el tercio 
-   AND 24                      ; con un AND 00011000b -> 000TT000b +    and %00011000            ; con un and 00011000b -> 000TT000b 
-   OR H                        ; H = H OR A = 00000SSSb OR 000TT000b +    or h                     ; H = H or a = 00000SSSb or 000TT000b 
-   OR 64                       ; Mezclamos H con 01000000b (vram) +    or %01000000             ; Mezclamos H con 01000000b (vram) 
-   LD HA                     ; Establecemos el "H" definitivo +    ld ha                  ; Establecemos el "H" definitivo
  
-   ; Calculo de la parte baja de la direccion: +    ; Calculo de la parte baja de la direccion: 
-   LD AC                     ; A = coordenada X +    ld ac                  ; A = coordenada X 
-   RRA +    rra 
-   RRA +    rra 
-   RRA                         ; Rotamos para obtener CCCCCb +    rra                      ; Rotamos para obtener CCCCCb 
-   AND 31                      ; A = A AND 31 = 000CCCCCb +    and %00011111            ; A = A and 31 = 000CCCCCb 
-   LD LA                     ; L = 000CCCCCb +    ld la                  ; L = 000CCCCCb 
-   LD AB                     ; Recuperamos de nuevo Y +    ld ab                  ; Recuperamos de nuevo Y 
-   RLA                         ; Rotamos para obtener NNN +    rla                      ; Rotamos para obtener NNN 
-   RLA +    rla 
-   AND 224                     ; A = A AND 11100000b +    and %11100000            ; A = A and 11100000b 
-   OR L                        ; L = NNNCCCCC +    or l                     ; L = NNNCCCCC 
-   LD LA                     ; Establecemos el "L" definitivo+    ld la                  ; Establecemos el "L" definitivo
  
-   ; Finalmente, calcular posicion relativa del pixel: +    ; Finalmente, calcular posicion relativa del pixel: 
-   LD AC                      ; Recuperamos la coordenada X +    ld ac                  ; Recuperamos la coordenada X 
-   AND 7                        AND 00000111 para obtener pixel +    and %00000111            and 00000111 para obtener pixel 
-                                ; A = 00000PPP +                             ; A = 00000PPP 
-   RET+    ret
 </code> </code>
  
- Esta rutina de 118 t-estados nos devuelve el valor de la dirección calculado en HL y la posición relativa del pixel dentro del byte:+ Esta rutina de 128 t-estados nos devuelve el valor de la dirección calculado en HL y la posición relativa del pixel dentro del byte:
  
-^ Valor de A ^ 7 ^ 6 ^ 5 ^ 4 ^ 3 ^ 2 ^ 1 ^ 0 ^  +|< 50% >| 
-| Posición del pixel\\ desde la izquierda | +7 | +6 | +5 | +4 | +3 | +2 | +1 | +0 |+^ Valor de A ^ 7 ^ 6 ^ 5 ^ 4 ^ 3 ^ 2 ^ 1 ^ 0 ^ 
 +| Posición del pixel\\ desde la izquierda | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
 | Posición del pixel\\ dentro del byte (Bit) | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | Posición del pixel\\ dentro del byte (Bit) | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
  
Línea 949: Línea 952:
  Por otra parte, si necesitamos activar (PLOT, bit=1), desactivar (UNPLOT, b=0) o testear el estado del pixel (x,y), podremos utilizar este valor "**posición del pixel**" para generar una **máscara de pixel**.  Por otra parte, si necesitamos activar (PLOT, bit=1), desactivar (UNPLOT, b=0) o testear el estado del pixel (x,y), podremos utilizar este valor "**posición del pixel**" para generar una **máscara de pixel**.
  
 + Si queremos convertir la //posición del pixel// en una //máscara de pixel// (por ejemplo, convertir ''A=6'' en el bit 6 activo (''A=%01000000''), podemos hacerlo con un bucle:
 +
 +<code z80>
 +    and %00000111               ; and 00000111 para obtener pixel
 +                                ; A = 00000PPP, y además setear ZF si es 0
 +    ;--- Nuevo código ---
 +    
 +    ld b, a                     ; Poner en B el numero de pixel
 +    ld a, %10000000             ; Activar sólo el bit 7 de A
 +    jr z, getpixoff_norotate    ; Si el ''and %00000111' dice que A == 0
 +                                ; entonces ya no necesitamos rotar
 +getpixoff_loop:
 +    rra                         ; Rotar A a la derecha B veces
 +    djnz getpixoff_lop
 +getpixoff_norotate:
 +                                ; Ahora A es una máscara de pixel
 +    ;--- Fin nuevo código ---
 +
 +    ret
 +</code>
  
 \\  \\ 
Línea 960: Línea 983:
  
 \\  \\ 
-^ Bit activo ^ 7 ^ 6 ^ 5 ^ 4 ^ 3 ^ 2 ^ 1 ^ 0 ^ +|< 50% >| 
 +^ Bit activo ^ 7 ^ 6 ^ 5 ^ 4 ^ 3 ^ 2 ^ 1 ^ 0 ^
 | Pixel | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | Pixel | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
 \\  \\ 
Línea 967: Línea 991:
  
 \\  \\ 
 +|< 30% >|
 ^ Valor de A ^ Máscara de pixel ^ ^ Valor de A ^ Máscara de pixel ^
 | 0 | 10000000b | | 0 | 10000000b |
Línea 977: Línea 1002:
 | 7 | 00000001b | | 7 | 00000001b |
 \\  \\ 
- +
  La porción de código que hace esta conversión es la siguiente:  La porción de código que hace esta conversión es la siguiente:
  
 <code z80> <code z80>
-   LD BA         ; Cargamos A (posicion de pixel) en B +    ld ba                  ; Cargamos A (posicion de pixel) en B 
-   INC B           ; Incrementamos B (para pasadas del bucle) +    inc b                    ; Incrementamos B (para pasadas del bucle) 
-   XOR A           ; A = 0 +    xor a                    ; A = 0 
-   SCF             ; Set Carry Flag (A=0, CF=1)+    scf                      ; Set Carry Flag (A=0, CF=1)
 pix_rotate_bit: pix_rotate_bit:
-   RRA             ; Rotamos A a la derecha B veces +    rra                      ; Rotamos A a la derecha B veces 
-   DJNZ pix_rotate_bit+    djnz pix_rotate_bit
 </code> </code>
  
- La rutina pone A a cero y establece el Carry Flag a 1, por lo que la primera ejecución de RRA (que siempre se realizará) ubica el 1 del CF en el bit 7 de A. A continuación el DJNZ que se realiza "B" veces mueve ese bit a 1 a la derecha (también "B" veces) dejando A con el valor adecuado según la tabla que acabamos de ver.+ La rutina pone A a cero y establece el Carry Flag a 1, por lo que la primera ejecución de ''RRA'' (que siempre se realizará) ubica el 1 del CF en el bit 7 de A. A continuación el ''DJNZ'' que se realiza "B" veces mueve ese bit a 1 a la derecha (también "B" veces) dejando A con el valor adecuado según la tabla que acabamos de ver.
  
 \\  \\ 
-{{ :cursos:ensamblador:rra.png | Instrucción RRA }}+{{ :cursos:ensamblador:rra.png | Instrucción rra }}
 \\  \\ 
  
Línea 1007: Línea 1032:
 ;-------------------------------------------------------- ;--------------------------------------------------------
 Relative_to_Mask: Relative_to_Mask:
-   LD BA         ; Cargamos A (posicion de pixel) en B +    ld ba                  ; Cargamos A (posicion de pixel) en B 
-   INC B           ; Incrementamos B (para pasadas del bucle) +    inc b                    ; Incrementamos B (para pasadas del bucle) 
-   XOR A           ; A = 0 +    xor a                    ; A = 0 
-   SCF             ; Set Carry Flag (A=0, CF=1)+    scf                      ; Set Carry Flag (A=0, CF=1)
 pix_rotate_bit: pix_rotate_bit:
-   RRA             ; Rotamos A a la derecha B veces +    rra                      ; Rotamos A a la derecha B veces 
-   DJNZ pix_rotate_bit +    djnz pix_rotate_bit 
-   RET+    ret
 </code> </code>
  
Línea 1022: Línea 1047:
 ; Activar el pixel apuntado por HL usando la máscara A ; Activar el pixel apuntado por HL usando la máscara A
 Plot_Pixel_HL: Plot_Pixel_HL:
-  OR (HL+    or (hl
-  LD (HL), A +    ld (hl), a 
-  RET+    ret
  
 ; Desactivar el pixel apuntado por HL usando la máscara A ; Desactivar el pixel apuntado por HL usando la máscara A
 Unplot_Pixel_HL: Unplot_Pixel_HL:
-  CPL +    cpl 
-  AND (HL+    and (hl
-  LD (HL), A +    ld (hl), a 
-  RET+    ret
  
 ; Testear el pixel apuntado por HL usando la máscara A ; Testear el pixel apuntado por HL usando la máscara A
 Test_Pixel_HL: Test_Pixel_HL:
-  AND (HL+    and (hl
-  RET+    ret
 </code> </code>
  
  La anterior rutina de PLOT funciona realizando un OR entre la máscara de pixel y el estado de actual de la memoria, y luego escribiendo el resultado de dicho OR en la videoram. De esta forma, sólo alteramos el pixel sobre el que queremos escribir.  La anterior rutina de PLOT funciona realizando un OR entre la máscara de pixel y el estado de actual de la memoria, y luego escribiendo el resultado de dicho OR en la videoram. De esta forma, sólo alteramos el pixel sobre el que queremos escribir.
- +
  Explicándolo con un ejemplo, supongamos que queremos escribir en el pixel (3,0) de la pantalla y ya hay píxeles activos en (0,0) y (7,0):  Explicándolo con un ejemplo, supongamos que queremos escribir en el pixel (3,0) de la pantalla y ya hay píxeles activos en (0,0) y (7,0):
  
Línea 1048: Línea 1073:
 </code> </code>
  
- Si ejecutaramos un simple "LD (HL), A", el resultado de la operación eliminaría los 2 píxeles activos que ya teníamos en memoria:+ Si ejecutaramos un simple ''ld (hl), a'', el resultado de la operación eliminaría los 2 píxeles activos que ya teníamos en memoria:
  
 <code> <code>
 Pixeles activos en (16384) = 10000001 Pixeles activos en (16384) = 10000001
 Máscara de pixel A         = 00010000 Máscara de pixel A         = 00010000
-OPERACION (HL)=A           LD (HL), A+OPERACION (HL)=A           ld (hl), a
 Resultado en (16384)       = 00010000 Resultado en (16384)       = 00010000
 </code> </code>
  
- Mediante el OR entre la máscara de pixel y la videomemoria conseguimos alterar el estado de (3,0) sin modificar los píxeles ya existentes:+ Mediante el ''OR'' entre la máscara de pixel y la videomemoria conseguimos alterar el estado de (3,0) sin modificar los píxeles ya existentes:
  
 <code> <code>
 Pixeles activos en (16384) = 10000001 Pixeles activos en (16384) = 10000001
 Máscara de pixel A         = 00010000 Máscara de pixel A         = 00010000
-OPERACION A = A OR (HL)    = OR (HL+OPERACION A = A or (hl)    = or (hl)
 Resultado en A             = 10010001 Resultado en A             = 10010001
-OPERACION (HL)=A           LD (HL), A+OPERACION (HL)=A           ld (hl), a
 Resultado en (16384)       = 10010001 Resultado en (16384)       = 10010001
 </code> </code>
  
- Si en lugar de un OR hubieramos complementado A y hubieramos hecho un AND, habríamos puesto a 0 el bit (y por tanto el pixel):+ Si en lugar de un ''OR'' hubieramos complementado A (''cpl A''y hubieramos hecho un ''AND'', habríamos puesto a 0 el bit (y por tanto el pixel):
  
 <code> <code>
 Pixeles activos en (16384) = 10000001 Pixeles activos en (16384) = 10000001
 Máscara de pixel A         = 00010000 Máscara de pixel A         = 00010000
-OPERACION A = CPL(A)       = 11101111 +OPERACION A = cpl(A)       = 11101111 
-OPERACION A = A OR (HL)    = AND (HL+OPERACION A = A or (hl)    = and (hl)
 Resultado en A             = 10000001 Resultado en A             = 10000001
-OPERACION (HL)=A           LD (HL), A+OPERACION (HL)=A           ld (hl), a
 Resultado en (16384)       = 10000001 Resultado en (16384)       = 10000001
 </code> </code>
Línea 1083: Línea 1108:
  
 <code z80> <code z80>
-  LD C, 127                   ; X = 127 +  ld c, 127                  ; X = 127 
-  LD B, 95                    ; Y = 95 +  ld b, 95                   ; Y = 95 
-  CALL Get_Pixel_Offset_HR    ; Calculamos HL y A +  call Get_Pixel_Offset_HR   ; Calculamos HL y A 
-  OR (HL                    ; OR de A y (HL) +  or (hl                   ; OR de A y (HL) 
-  LD (HL), A                  ; Activamos pixel+  ld (hl), a                 ; Activamos pixel
 </code> </code>
  
- La primera pregunta que nos planteamos es, si es imprescindible disponer de una máscara de pixel para dibujar o borrar píxeles, ¿por qué no incluir este código de rotación de A  directamente en la rutina de coordenación? La respuesta es, "depende de para qué vayamos a utilizar la rutina"+ La primera pregunta que nos planteamos es, si es imprescindible disponer de una máscara de pixel para dibujar o borrar píxeles, ¿por qué no incluir este código de rotación de A  directamente en la rutina de coordenación? La respuesta es, "depende de para qué vayamos a utilizar la rutina".
  
- Si la rutina va a ser utilizada principalmente para trazar píxeles, resultará conveniente incorporar al final de Get_Pixel_Offset_HR() el cálculo de la máscara, y devolver en A dicha máscara en lugar de la posición relativa del pixel.+ Si la rutina va a ser utilizada principalmente para trazar píxeles, resultará conveniente incorporar al final de ''Get_Pixel_Offset_HR()'' el cálculo de la máscara, y devolver en A dicha máscara en lugar de la posición relativa del pixel.
  
  Pero lo normal en el desarrollo de programas y juegos es que utilicemos la rutina de coordenación para obtener la posición inicial en la que comenzar a trazar sprites, bloques (del mapeado), fuentes de texto, marcadores. En ese caso es absurdo emplear "ciclos de reloj" adicionales para el cálculo de una máscara que sólo se utiliza en el trazado de puntos. En esas circunstancias resulta mucho más útil disponer de la posición relativa del pixel, para, como ya hemos comentado, conocer la cantidad de bits que necesitamos rotar estos datos gráficos antes de su trazado.  Pero lo normal en el desarrollo de programas y juegos es que utilicemos la rutina de coordenación para obtener la posición inicial en la que comenzar a trazar sprites, bloques (del mapeado), fuentes de texto, marcadores. En ese caso es absurdo emplear "ciclos de reloj" adicionales para el cálculo de una máscara que sólo se utiliza en el trazado de puntos. En esas circunstancias resulta mucho más útil disponer de la posición relativa del pixel, para, como ya hemos comentado, conocer la cantidad de bits que necesitamos rotar estos datos gráficos antes de su trazado.
  
- Por ese motivo, no hemos agregado esta pequeña porción de código a la rutina de Get_Pixel_Offset, siendo el programador quien debe decidir en qué formato quiere obtener la salida de la rutina.+ Por ese motivo, no hemos agregado esta pequeña porción de código a la rutina de ''Get_Pixel_Offset'', siendo el programador quien debe decidir en qué formato quiere obtener la salida de la rutina.
  
  
Línea 1102: Línea 1127:
 ==== La rutina de la ROM PIXEL-ADDRESS ==== ==== La rutina de la ROM PIXEL-ADDRESS ====
  
- Curiosamente, los usuarios de Spectrum tenemos disponible en la memoria ROM una rutina parecida, llamada **PIXEL-ADDRESS** (o PIXEL-ADD), utilizada por las rutinas POINT y PLOT de la ROM (y de BASIC). La rutina está ubicada en $22AA y su código es el siguiente:+ Curiosamente, los usuarios de Spectrum tenemos disponible en la memoria ROM una rutina parecida, llamada ''PIXEL-ADDRESS'' (o ''PIXEL-ADD''), utilizada por las rutinas ''POINT'' ''PLOT'' de la ROM (y de BASIC). La rutina está ubicada en $22aa y su código es el siguiente:
  
 <code z80> <code z80>
Línea 1115: Línea 1140:
 ; OUT: HL = address, A = pixel relative position in (HL) ; OUT: HL = address, A = pixel relative position in (HL)
  
-$22AA PIXEL-ADD +$22aa PIXEL-ADD 
-    LD    A,$AF               ; Test that the y co-ordinate (in +    ld    a,$af               ; Test that the y co-ordinate (in 
-    SUB                     ; B) is not greater than 175. +    sub                     ; B) is not greater than 175. 
-    JP    C,24F9,REPORT-B +    jp    c,24F9,REPORT-B 
-    LD    B,                ; B now contains 175 minus y.+    ld    b,                ; B now contains 175 minus y.
  
-$22B1 PIXEL_ADDRESS_B:        ; Entramos aqui para saltarnos la limitacion+$22b1 PIXEL_ADDRESS_B:        ; Entramos aqui para saltarnos la limitacion
                               ; hacia las 2 ultimas lineas de pantalla.                               ; hacia las 2 ultimas lineas de pantalla.
  
-    AND                     ; A holds b7b6b5b4b3b2b1b0, +    and                     ; A holds b7b6b5b4b3b2b1b0, 
-    RRA                       ; the bite of B. And now+    rra                       ; the bite of B. And now
                               ; 0b7b6b5b4b3b2b1.                               ; 0b7b6b5b4b3b2b1.
-    SCF +    scf 
-    RRA                       ; Now 10b7b6b5b4b3b2. +    rra                       ; Now 10b7b6b5b4b3b2. 
-    AND   A +    and   a 
-    RRA                       ; Now 010b7b6b5b4b3. +    rra                       ; Now 010b7b6b5b4b3. 
-    XOR   B +    xor   b 
-    AND   $F8                 ; Finally 010b7b6b2b1b0, so that +    and   $f8                 ; Finally 010b7b6b2b1b0, so that 
-    XOR                     ; H becomes 64 + 8*INT (B/64) + +    xor                     ; H becomes 64 + 8*INT (B/64) + 
-    LD    H,                ; B (mod 8), the high byte of the +    ld    h,                ; B (mod 8), the high byte of the 
-    LD    A,                ; pixel address. C contains X. +    ld    a,                ; pixel address. C contains X. 
-    RLCA                      ; A starts as c7c6c5c4c3c2c1c0. +    rlca                      ; A starts as c7c6c5c4c3c2c1c0. 
-    RLCA +    rlca 
-    RLCA                      ; And is now c2c1c0c7c6c5c4c3. +    rlca                      ; And is now c2c1c0c7c6c5c4c3. 
-    XOR   B +    xor   b 
-    AND   $C7 +    and   $c7 
-    XOR                     ; Now c2c1b5b4b3c5c4c3. +    xor                     ; Now c2c1b5b4b3c5c4c3. 
-    RLCA +    rlca 
-    RLCA                      ; Finally b5b4b3c7c6c5c4c3, so +    rlca                      ; Finally b5b4b3c7c6c5c4c3, so 
-    LD    L,                ; that L becomes 32*INT (B(mod +    ld    l,                ; that L becomes 32*INT (B(mod 
-    LD    A,                ; 64)/8) + INT(x/8), the low byte. +    ld    a,                ; 64)/8) + INT(x/8), the low byte. 
-    AND   $07                 ; A holds x(mod 8): so the pixel +    and   $07                 ; A holds x(mod 8): so the pixel 
-    RET                       ; is bit (A - 7) within the byte.+    ret                       ; is bit (A - 7) within the byte.
 </code> </code>
  
- Esta rutina tiene una serie de ventajas: Entrando por $22B1 tenemos 23 instrucciones (107 t-estados) que realizan el cálculo de la dirección de memoria además de la posición del pixel dentro del byte al que apunta dicha dirección. La rutina está ubicada en ROM, por lo que ahorramos esta pequeña porción de espacio en nuestro programa. Además, no usa la pila, no usa registros adicionales a B, C, HL y A, y no altera los valores de B y C durante el cálculo.+ Esta rutina tiene una serie de ventajas: Entrando por $22b1 tenemos 23 instrucciones (107 t-estados) que realizan el cálculo de la dirección de memoria además de la posición del pixel dentro del byte al que apunta dicha dirección. La rutina está ubicada en ROM, por lo que ahorramos esta pequeña porción de espacio en nuestro programa. Además, no usa la pila, no usa registros adicionales a B, C, HL y A, y no altera los valores de B y C durante el cálculo.
  
- Nótese que aunque la rutina está ubicada en $22AA y se entra con los valores (x,y) en C y B, el principio de la rutina está diseñado para evitar que PLOT y POINT puedan acceder a las 2 últimas filas (16 últimos píxeles) de la pantalla. Para saltarnos esta limitación entramos saltando con un CALL a $22B1 con la coordenada X en el registro C y la coordenada Y en los registros A y B:+ Nótese que aunque la rutina está ubicada en $22aa y se entra con los valores (x,y) en C y B, el principio de la rutina está diseñado para evitar que ''PLOT'' ''POINT'' puedan acceder a las 2 últimas filas (16 últimos píxeles) de la pantalla. Para saltarnos esta limitación entramos saltando con un ''call'' a $22b1 con la coordenada X en el registro C y la coordenada Y en los registros A y B:
  
 <code z80> <code z80>
-   LD A, (coord_x) +    ld a, (coord_x) 
-   LD CA +    ld ca 
-   LD A, (coord_y) +    ld a, (coord_y) 
-   LD BA +    ld ba 
-   CALL $22B1+    call $22b1
 </code> </code>
  
Línea 1167: Línea 1192:
  
 <code z80> <code z80>
-  ; Ejemplo de uso de pixel-address (ROM) +; Ejemplo de uso de pixel-address (ROM) 
-  ORG 50000+    ORG 50000
  
-PIXEL_ADDRESS EQU $22B1+PIXEL_ADDRESS EQU $22b1
  
 entrada: entrada:
  
-  ; Imprimimos un solo pixel en (0,0) +    ; Imprimimos un solo pixel en (0,0) 
-  LD C, 0                ; X = 0 +    ld c, 0                  ; X = 0 
-  LD B, 0                ; Y = 0 +    ld b, 0                  ; Y = 0 
-  LD AB                ; A = Y = 0 +    ld ab                  ; A = Y = 0 
-  CALL PIXEL_ADDRESS     ; HL = direccion (0,0) +    call PIXEL_ADDRESS       ; HL = direccion (0,0) 
-  LD A, 128              ; A = 10000000b (1 pixel). +    ld a, 128                ; A = 10000000b (1 pixel). 
-  LD (HL), A             ; Imprimimos el pixel+    ld (hl), a               ; Imprimimos el pixel
  
-  ; Imprimimos 8 pixeles en (255,191) +    ; Imprimimos 8 pixeles en (255,191) 
-  LD C, 255              ; X = 255 +    ld c, 255                ; X = 255 
-  LD B, 191              ; Y = 191 +    ld b, 191                ; Y = 191 
-  LD AB                ; A = Y = 191 +    ld ab                  ; A = Y = 191 
-  CALL PIXEL_ADDRESS      +    call PIXEL_ADDRESS 
-  LD A, 255              ; A = 11111111b (8 pixeles) +    ld a, 255                ; A = 11111111b (8 pixeles) 
-  LD (HL), A+    ld (hl), a
  
-  ; Imprimimos 4 pixeles en el centro de la pantalla +    ; Imprimimos 4 pixeles en el centro de la pantalla 
-  LD C, 127              ; X = 127 +    ld c, 127                ; X = 127 
-  LD B, 95               ; Y = 95 +    ld b, 95                 ; Y = 95 
-  LD AB                ; A = Y = 95 +    ld ab                  ; A = Y = 95 
-  CALL PIXEL_ADDRESS      +    call PIXEL_ADDRESS 
-  LD A170              ; A = 10101010b (4 pixeles) +    ld a%10101010          ; A = 10101010b (4 pixeles) 
-  LD (HL), A+    ld (hl), a
  
-loop:                    ; Bucle para no volver a BASIC y que +loop:                        ; Bucle para no volver a BASIC y que 
-  jr loop                ; no se borren la 2 ultimas lineas +    jr loop                  ; no se borren la 2 ultimas lineas 
-  + 
-END 50000+    END 50000
 </code> </code>
  
Línea 1207: Línea 1232:
  
 \\  \\ 
-{{ :cursos:ensamblador:gfx2_pixeladd.png | Ejemplo: rutina de la ROM pixelADD }}+{{ :cursos:ensamblador:gfx2_pixeladd.png?640 | Ejemplo: rutina de la ROM pixelADD }}
 \\  \\ 
  
- Nótese que la rutina de la ROM nos devuelve en A la posición relativa del pixel cuyas coordenadas hemos proporcionado, por lo que podemos convertir A en una **máscara de pixel** a la salida de la rutina encapsulando PIXEL-ADDRESS en una rutina "propia" que haga ambas operaciones, a cambio de 2 instrucciones extras (un CALL y un RET adicionales):+ Nótese que la rutina de la ROM nos devuelve en A la posición relativa del pixel cuyas coordenadas hemos proporcionado, por lo que podemos convertir A en una **máscara de pixel** a la salida de la rutina encapsulando ''PIXEL-ADDRESS'' en una rutina "propia" que haga ambas operaciones, a cambio de 2 instrucciones extras (un ''call'' y un ''RET'' adicionales):
  
 <code z80> <code z80>
-PIXEL_ADDRESS EQU $22B1+PIXEL_ADDRESS EQU $22b1
  
 ;---------------------------------------------------------- ;----------------------------------------------------------
Línea 1221: Línea 1246:
 ;---------------------------------------------------------- ;----------------------------------------------------------
 PIXEL_ADDRESS_MASK: PIXEL_ADDRESS_MASK:
-   CALL PIXEL_ADDRESS   ; Llamamos a la rutina de la ROM +    call PIXEL_ADDRESS       ; Llamamos a la rutina de la ROM 
-   LD BA              ; Cargamos A (posicion de pixel) en B +    ld ba                  ; Cargamos A (posicion de pixel) en B 
-   INC B                ; Incrementamos B (para pasadas del bucle) +    inc b                    ; Incrementamos B (para pasadas del bucle) 
-   XOR A                ; A = 0 +    xor a                    ; A = 0 
-   SCF                  ; Set Carry Flag (A=0, CF=1)+    scf                      ; Set Carry Flag (A=0, CF=1)
 pix_rotate_bit: pix_rotate_bit:
-   RRA                  ; Rotamos A a la derecha B veces +    rra                      ; Rotamos A a la derecha B veces 
-   DJNZ pix_rotate_bit +    djnz pix_rotate_bit 
-   RET+    ret
 </code> </code>
  
Línea 1245: Línea 1270:
 <code> <code>
 DIRECCION_DESTINO  = Tabla_Offsets_Linea[Y] + (X/8) DIRECCION_DESTINO  = Tabla_Offsets_Linea[Y] + (X/8)
-PIXEL_EN_DIRECCION = Resto(X/8) = X AND 7+PIXEL_EN_DIRECCION = Resto(X/8) = X AND %00000111
 </code> </code>
  
Línea 1254: Línea 1279:
 <code> <code>
 Scanline_Offsets: Scanline_Offsets:
-  DW 16384, 16640, 16896, 17152, 17408, 17664, 17920, 18176 +    DW 16384, 16640, 16896, 17152, 17408, 17664, 17920, 18176 
-  DW 16416, 16672, 16928, 17184, 17440, 17696, 17952, 18208 +    DW 16416, 16672, 16928, 17184, 17440, 17696, 17952, 18208 
-  DW 16448, 16704, 16960, 17216, 17472, 17728, 17984, 18240 +    DW 16448, 16704, 16960, 17216, 17472, 17728, 17984, 18240 
-  DW 16480, 16736, 16992, 17248, 17504, 17760, 18016, 18272 +    DW 16480, 16736, 16992, 17248, 17504, 17760, 18016, 18272 
-  DW 16512, 16768, 17024, 17280, 17536, 17792, 18048, 18304 +    DW 16512, 16768, 17024, 17280, 17536, 17792, 18048, 18304 
-  DW 16544, 16800, 17056, 17312, 17568, 17824, 18080, 18336 +    DW 16544, 16800, 17056, 17312, 17568, 17824, 18080, 18336 
-  DW 16576, 16832, 17088, 17344, 17600, 17856, 18112, 18368 +    DW 16576, 16832, 17088, 17344, 17600, 17856, 18112, 18368 
-  DW 16608, 16864, 17120, 17376, 17632, 17888, 18144, 18400 +    DW 16608, 16864, 17120, 17376, 17632, 17888, 18144, 18400 
-  DW 18432, 18688, 18944, 19200, 19456, 19712, 19968, 20224 +    DW 18432, 18688, 18944, 19200, 19456, 19712, 19968, 20224 
-  DW 18464, 18720, 18976, 19232, 19488, 19744, 20000, 20256 +    DW 18464, 18720, 18976, 19232, 19488, 19744, 20000, 20256 
-  DW 18496, 18752, 19008, 19264, 19520, 19776, 20032, 20288 +    DW 18496, 18752, 19008, 19264, 19520, 19776, 20032, 20288 
-  DW 18528, 18784, 19040, 19296, 19552, 19808, 20064, 20320 +    DW 18528, 18784, 19040, 19296, 19552, 19808, 20064, 20320 
-  DW 18560, 18816, 19072, 19328, 19584, 19840, 20096, 20352 +    DW 18560, 18816, 19072, 19328, 19584, 19840, 20096, 20352 
-  DW 18592, 18848, 19104, 19360, 19616, 19872, 20128, 20384 +    DW 18592, 18848, 19104, 19360, 19616, 19872, 20128, 20384 
-  DW 18624, 18880, 19136, 19392, 19648, 19904, 20160, 20416 +    DW 18624, 18880, 19136, 19392, 19648, 19904, 20160, 20416 
-  DW 18656, 18912, 19168, 19424, 19680, 19936, 20192, 20448 +    DW 18656, 18912, 19168, 19424, 19680, 19936, 20192, 20448 
-  DW 20480, 20736, 20992, 21248, 21504, 21760, 22016, 22272 +    DW 20480, 20736, 20992, 21248, 21504, 21760, 22016, 22272 
-  DW 20512, 20768, 21024, 21280, 21536, 21792, 22048, 22304 +    DW 20512, 20768, 21024, 21280, 21536, 21792, 22048, 22304 
-  DW 20544, 20800, 21056, 21312, 21568, 21824, 22080, 22336 +    DW 20544, 20800, 21056, 21312, 21568, 21824, 22080, 22336 
-  DW 20576, 20832, 21088, 21344, 21600, 21856, 22112, 22368 +    DW 20576, 20832, 21088, 21344, 21600, 21856, 22112, 22368 
-  DW 20608, 20864, 21120, 21376, 21632, 21888, 22144, 22400 +    DW 20608, 20864, 21120, 21376, 21632, 21888, 22144, 22400 
-  DW 20640, 20896, 21152, 21408, 21664, 21920, 22176, 22432 +    DW 20640, 20896, 21152, 21408, 21664, 21920, 22176, 22432 
-  DW 20672, 20928, 21184, 21440, 21696, 21952, 22208, 22464 +    DW 20672, 20928, 21184, 21440, 21696, 21952, 22208, 22464 
-  DW 20704, 20960, 21216, 21472, 21728, 21984, 22240, 22496+    DW 20704, 20960, 21216, 21472, 21728, 21984, 22240, 22496
 </code> </code>
  
Línea 1284: Línea 1309:
 <code> <code>
 Scanline_Offsets: Scanline_Offsets:
-  DW $4000, $4100, $4200, $4300, $4400, $4500, $4600, $4700 +    DW $4000, $4100, $4200, $4300, $4400, $4500, $4600, $4700 
-  DW $4020, $4120, $4220, $4320, $4420, $4520, $4620, $4720 +    DW $4020, $4120, $4220, $4320, $4420, $4520, $4620, $4720 
-  DW $4040, $4140, $4240, $4340, $4440, $4540, $4640, $4740 +    DW $4040, $4140, $4240, $4340, $4440, $4540, $4640, $4740 
-  DW $4060, $4160, $4260, $4360, $4460, $4560, $4660, $4760 +    DW $4060, $4160, $4260, $4360, $4460, $4560, $4660, $4760 
-  DW $4080, $4180, $4280, $4380, $4480, $4580, $4680, $4780 +    DW $4080, $4180, $4280, $4380, $4480, $4580, $4680, $4780 
-  DW $40A0, $41A0, $42A0, $43A0, $44A0, $45A0, $46A0, $47A0 +    DW $40a0, $41a0, $42a0, $43a0, $44a0, $45a0, $46a0, $47a0 
-  DW $40C0, $41C0, $42C0, $43C0, $44C0, $45C0, $46C0, $47C0 +    DW $40c0, $41c0, $42c0, $43c0, $44c0, $45c0, $46c0, $47c0 
-  DW $40E0, $41E0, $42E0, $43E0, $44E0, $45E0, $46E0, $47E0 +    DW $40e0, $41e0, $42e0, $43e0, $44e0, $45e0, $46e0, $47e0 
-  DW $4800, $4900, $4A00, $4B00, $4C00, $4D00, $4E00, $4F00 +    DW $4800, $4900, $4a00, $4b00, $4c00, $4d00, $4e00, $4f00 
-  DW $4820, $4920, $4A20, $4B20, $4C20, $4D20, $4E20, $4F20 +    DW $4820, $4920, $4a20, $4b20, $4c20, $4d20, $4e20, $4f20 
-  DW $4840, $4940, $4A40, $4B40, $4C40, $4D40, $4E40, $4F40 +    DW $4840, $4940, $4a40, $4b40, $4c40, $4d40, $4e40, $4f40 
-  DW $4860, $4960, $4A60, $4B60, $4C60, $4D60, $4E60, $4F60 +    DW $4860, $4960, $4a60, $4b60, $4c60, $4d60, $4e60, $4f60 
-  DW $4880, $4980, $4A80, $4B80, $4C80, $4D80, $4E80, $4F80 +    DW $4880, $4980, $4a80, $4b80, $4c80, $4d80, $4e80, $4f80 
-  DW $48A0, $49A0, $4AA0, $4BA0, $4CA0, $4DA0, $4EA0, $4FA0 +    DW $48a0, $49a0, $4aa0, $4ba0, $4ca0, $4da0, $4ea0, $4fa0 
-  DW $48C0, $49C0, $4AC0, $4BC0, $4CC0, $4DC0, $4EC0, $4FC0 +    DW $48c0, $49c0, $4ac0, $4bc0, $4cc0, $4dc0, $4ec0, $4fc0 
-  DW $48E0, $49E0, $4AE0, $4BE0, $4CE0, $4DE0, $4EE0, $4FE0 +    DW $48e0, $49e0, $4ae0, $4be0, $4ce0, $4de0, $4ee0, $4fe0 
-  DW $5000, $5100, $5200, $5300, $5400, $5500, $5600, $5700 +    DW $5000, $5100, $5200, $5300, $5400, $5500, $5600, $5700 
-  DW $5020, $5120, $5220, $5320, $5420, $5520, $5620, $5720 +    DW $5020, $5120, $5220, $5320, $5420, $5520, $5620, $5720 
-  DW $5040, $5140, $5240, $5340, $5440, $5540, $5640, $5740 +    DW $5040, $5140, $5240, $5340, $5440, $5540, $5640, $5740 
-  DW $5060, $5160, $5260, $5360, $5460, $5560, $5660, $5760 +    DW $5060, $5160, $5260, $5360, $5460, $5560, $5660, $5760 
-  DW $5080, $5180, $5280, $5380, $5480, $5580, $5680, $5780 +    DW $5080, $5180, $5280, $5380, $5480, $5580, $5680, $5780 
-  DW $50A0, $51A0, $52A0, $53A0, $54A0, $55A0, $56A0, $57A0 +    DW $50a0, $51a0, $52a0, $53a0, $54a0, $55a0, $56a0, $57a0 
-  DW $50C0, $51C0, $52C0, $53C0, $54C0, $55C0, $56C0, $57C0 +    DW $50c0, $51c0, $52c0, $53c0, $54c0, $55c0, $56c0, $57c0 
-  DW $50E0, $51E0, $52E0, $53E0, $54E0, $55E0, $56E0, $57E0+    DW $50e0, $51e0, $52e0, $53e0, $54e0, $55e0, $56e0, $57e0
 </code> </code>
  
Línea 1313: Línea 1338:
  
 <code python> <code python>
-$ cat specrows.py +$ cat specrows.py
 #!/usr/bin/python #!/usr/bin/python
  
Línea 1331: Línea 1356:
 </code> </code>
  
- La tabla de valores DW estaría incorporada en nuestro programa y por tanto pasaría a formar parte del "binario final", incluyendo en este aspecto la necesidad de carga desde cinta. + La tabla de valores ''DW'' estaría incorporada en nuestro programa y por tanto pasaría a formar parte del "binario final", incluyendo en este aspecto la necesidad de carga desde cinta.
  
  Si por algún motivo no queremos incluir la tabla en el listado, podemos generarla en el arranque de nuestro programa en alguna posición de memoria libre o designada a tal efecto mediante la siguiente rutina:  Si por algún motivo no queremos incluir la tabla en el listado, podemos generarla en el arranque de nuestro programa en alguna posición de memoria libre o designada a tal efecto mediante la siguiente rutina:
Línea 1341: Línea 1366:
 ;-------------------------------------------------------- ;--------------------------------------------------------
  
-Scanline_Offsets EQU $F900+Scanline_Offsets EQU $f900
  
 Generate_Scanline_Table: Generate_Scanline_Table:
-   LD DE, $4000 +    ld de, $4000 
-   LD HL, Scanline_Offsets +    ld hl, Scanline_Offsets 
-   LD B, 192+    ld b, 192
  
 genscan_loop: genscan_loop:
-   LD (HL), E +    ld (hl), e 
-   INC L +    inc l 
-   LD (HL), D           ; Guardamos en (HL) (tabla) +    ld (hl), d               ; Guardamos en (HL) (tabla) 
-   INC HL               ; el valor de DE (offset) +    inc hl                   ; el valor de DE (offset) 
-    + 
-   ; Recorremos los scanlines y bloques en un bucle generando las +    ; Recorremos los scanlines y bloques en un bucle generando las 
-   ; sucesivas direccione en DE para almacenarlas en la tabla.  +    ; sucesivas direccione en DE para almacenarlas en la tabla. 
-   ; Cuando se cambia de caracter, scanline o tercio, se ajusta: +    ; Cuando se cambia de caracter, scanline o tercio, se ajusta: 
-   INC D +    inc d 
-   LD AD +    ld ad 
-   AND 7 +    and %00000111 
-   JR NZ, genscan_nextline +    jr nz, genscan_nextline 
-   LD AE +    ld ae 
-   ADD A, 32 +    add a, 32 
-   LD EA +    ld ea 
-   JR C, genscan_nextline +    jr c, genscan_nextline 
-   LD AD +    ld ad 
-   SUB +    sub 
-   LD DA +    ld da 
-   +
 genscan_nextline: genscan_nextline:
-   DJNZ genscan_loop +    djnz genscan_loop 
-   RET+    ret
 </code> </code>
  
Línea 1390: Línea 1415:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Get_Pixel_Offset_LUT_HR: Get_Pixel_Offset_LUT_HR:
-   LD DE, Scanline_Offsets   ; Direccion de nuestra LUT +    ld de, Scanline_Offsets  ; Direccion de nuestra LUT 
-   LD LB                   ; L = Y +    ld lb                  ; L = Y 
-   LD H, 0 +    ld h, 0 
-   ADD HLHL                ; HL = HL * 2 = Y * 2 +    add hlhl               ; HL = HL * 2 = Y * 2 
-   ADD HLDE                ; HL = (Y*2) + ScanLine_Offset+    add hlde               ; HL = (Y*2) + ScanLine_Offset
                              ; Ahora Offset = [HL]                              ; Ahora Offset = [HL]
-   LD A, (HL               ; Cogemos el valor bajo de la direccion en A +    ld a, (hl              ; Cogemos el valor bajo de la direccion en A 
-   INC L +    inc l 
-   LD H, (HL               ; Cogemos el valor alto de la direccion en H +    ld h, (hl              ; Cogemos el valor alto de la direccion en H 
-   LD LA                   ; HL es ahora Direccion(0,Y)+    ld la                  ; HL es ahora Direccion(0,Y)
                              ; Ahora sumamos la X, para lo cual calculamos CCCCC                              ; Ahora sumamos la X, para lo cual calculamos CCCCC
-   LD AC                   ; Calculamos columna +    ld ac                  ; Calculamos columna 
-   RRA +    rra 
-   RRA +    rra 
-   RRA                       ; A = A>>3 = ???CCCCCb +    rra                      ; A = A>>3 = ???CCCCCb 
-   AND 31                    ; A = 000CCCCB +    and %00011111            ; A = 000CCCCB 
-   ADD AL                  ; HL = HL + C +    add al                 ; HL = HL + C 
-   LD LA +    ld la 
-   LD AC                   ; Recuperamos la coordenada (X) +    ld ac                  ; Recuperamos la coordenada (X) 
-   AND 7                     ; A = Posicion relativa del pixel +    and %00000111            ; A = Posicion relativa del pixel 
-   RET                       ; HL = direccion destino+    ret                      ; HL = direccion destino
 </code> </code>
  
Línea 1416: Línea 1441:
  
 <code z80> <code z80>
-  ; Ejemplo de uso de LUT +; Ejemplo de uso de LUT 
-  ORG 50000+    ORG 50000
  
 entrada: entrada:
  
-  CALL Generate_Scanline_Table +    call Generate_Scanline_Table 
-  LD B, 191+    ld b, 191
  
 loop_draw: loop_draw:
-  PUSH BC                ; Preservamos B (por el bucle)+    push bc                  ; Preservamos B (por el bucle)
  
-  LD C, 127              ; X = 127, Y = B +    ld c, 127                ; X = 127, Y = B
-   +
-  CALL Get_Pixel_Offset_LUT_HR+
  
-  LD A, 128 +    call Get_Pixel_Offset_LUT_HR
-  LD (HL), A             ; Imprimimos el pixel+
  
-  POP BC +    ld a, %10000000          ; 1 pixel en la parte izquierda del byte 
-  DJNZ loop_draw +    ld (hl), a               ; Imprimimos el pixel 
-   + 
-loop:                    ; Bucle para no volver a BASIC y que +    pop bc 
-  JR loop                ; no se borren la 2 ultimas lineas+    djnz loop_draw 
 + 
 +loop:                        ; Bucle para no volver a BASIC y que 
 +    jr loop                  ; no se borren la 2 ultimas lineas 
 + 
 +    END 50000
 </code> </code>
  
Línea 1444: Línea 1471:
  
 \\  \\ 
-{{ :cursos:ensamblador:gfx2_lut.png | Salida de ejemplo del programa gfx2_lut }}+{{ :cursos:ensamblador:gfx2_lut.png?640 | Salida de ejemplo del programa gfx2_lut }}
 \\  \\ 
  
Línea 1450: Línea 1477:
 ==== Optimizando la lectura a través de tablas ==== ==== Optimizando la lectura a través de tablas ====
  
- El coste de ejecución de la rutina **Get_Pixel_Offset_LUT_HR** es de 117 t-estados, demasiado elevada por culpa de las costosas (en términos temporales) instrucciones de 16 bits, sobre todo teniendo en cuenta que hemos empleado 384 bytes de memoria en nuestra tabla.+ El coste de ejecución de la rutina ''Get_Pixel_Offset_LUT_HR'' es de 117 t-estados, demasiado elevada por culpa de las costosas (en términos temporales) instrucciones de 16 bits, sobre todo teniendo en cuenta que hemos empleado 384 bytes de memoria en nuestra tabla.
  
  Una ingeniosa solución a este problema consiste en dividir la tabla de 192 direcciones de 16 bits en 2 tablas de 192 bytes cada una que almacenen la parte alta de la dirección en la primera de las tablas y la parte baja de la dirección en la segunda, de tal forma que:  Una ingeniosa solución a este problema consiste en dividir la tabla de 192 direcciones de 16 bits en 2 tablas de 192 bytes cada una que almacenen la parte alta de la dirección en la primera de las tablas y la parte baja de la dirección en la segunda, de tal forma que:
Línea 1469: Línea 1496:
   -> Partes bajas de las direcciones de pantalla   -> Partes bajas de las direcciones de pantalla
  
-Direccion_XX+256 hasta Direccion_XX+64447+Direccion_XX+256 hasta Direccion_XX+447
   -> Partes altas de las direcciones de pantalla.   -> Partes altas de las direcciones de pantalla.
 </code> </code>
  
- El paso de una tabla a otra se realizará incrementando o decrementando la parte alta del registro de 16 bits (INC H DEC H), gracias al hecho de que son 2 tablas múltiplos de 256 y consecutivas en memoria.+ El paso de una tabla a otra se realizará incrementando o decrementando la parte alta del registro de 16 bits (inc h dec h), gracias al hecho de que son 2 tablas múltiplos de 256 y consecutivas en memoria.
  
  Veamos primero la rutina para generar la tabla separando las partes altas y bajas y alineando ambas a una dirección múltiplo de 256:  Veamos primero la rutina para generar la tabla separando las partes altas y bajas y alineando ambas a una dirección múltiplo de 256:
Línea 1493: Línea 1520:
  
 Generate_Scanline_Table_Aligned: Generate_Scanline_Table_Aligned:
-   LD DE, $4000 +    ld de, $4000 
-   LD HL, Scanline_Offsets +    ld hl, Scanline_Offsets 
-   LD B, 192+    ld b, 192
  
 genscan_loop: genscan_loop:
-   LD (HL), E        ; Escribimos parte baja +    ld (hl), e               ; Escribimos parte baja 
-   INC H             ; Saltamos a tabla de partes altas +    inc h                    ; Saltamos a tabla de partes altas 
-   LD (HL), D        ; Escribimos parte alta +    ld (hl), d               ; Escribimos parte alta 
-   DEC H             ; Volvemos a tabla de partes bajas +    dec h                    ; Volvemos a tabla de partes bajas 
-   INC L             ; Siguiente valor +    inc l                    ; Siguiente valor 
-    + 
-   ; Recorremos los scanlines y bloques en un bucle generando las +    ; Recorremos los scanlines y bloques en un bucle generando las 
-   ; sucesivas direccione en DE para almacenarlas en la tabla.  +    ; sucesivas direccione en DE para almacenarlas en la tabla. 
-   ; Cuando se cambia de caracter, scanline o tercio, se ajusta: +    ; Cuando se cambia de caracter, scanline o tercio, se ajusta: 
-   INC D +    inc d 
-   LD AD +    ld ad 
-   AND 7 +    and %00000111 
-   JR NZ, genscan_nextline +    jr nz, genscan_nextline 
-   LD AE +    ld ae 
-   ADD A, 32 +    add a, 32 
-   LD EA +    ld ea 
-   JR C, genscan_nextline +    jr c, genscan_nextline 
-   LD AD +    ld ad 
-   SUB +    sub 
-   LD DA +    ld da 
-   +
 genscan_nextline: genscan_nextline:
-   DJNZ genscan_loop +    djnz genscan_loop 
-   RET+    ret
 </code> </code>
  
Línea 1528: Línea 1555:
  Estos 64 bytes no se utilizan en la rutina de generación ni (como veremos a continuación) en la de cálculo, por lo que podemos aprovecharlos para ubicar variables de nuestro programa, tablas temporales, etc, y así no desperdiciarlos.  Estos 64 bytes no se utilizan en la rutina de generación ni (como veremos a continuación) en la de cálculo, por lo que podemos aprovecharlos para ubicar variables de nuestro programa, tablas temporales, etc, y así no desperdiciarlos.
  
- Si necesitaramos reservar espacio en nuestro programa para después generar la tabla sobre él, podemos hacerlo mediante las directivas de preprocesado del ensamblador ORG (Origen) y DS (Define Space). Las siguientes líneas (ubicadas al final del fichero de código) reservan en nuestro programa un array de 448 bytes de longitud y tamaño cero alineado en una posición múltiplo de 256:+ Si necesitaramos reservar espacio en nuestro programa para después generar la tabla sobre él, podemos hacerlo mediante las directivas de preprocesado del ensamblador ''ORG'' (Origen) y ''DS'' (Define Space). Las siguientes líneas (ubicadas al final del fichero de código) reservan en nuestro programa un array de 448 bytes de longitud y tamaño cero alineado en una posición múltiplo de 256:
  
 <code z80> <code z80>
-ORG 64000+    ORG 64000
  
 Scanline_Offsets: Scanline_Offsets:
-   DS 448, 0+    DS 448, 0
 </code> </code>
  
Línea 1540: Línea 1567:
  
 <code> <code>
-; Macro de alineacion para PASMO +    ; Macro de alineacion para PASMO 
-align   macro value+    align   macro value
        if $ mod value        if $ mod value
        ds value - ($ mod value)        ds value - ($ mod value)
        endif        endif
        endm        endm
-        + 
-align 256+    align 256
  
 Scanline_Offsets: Scanline_Offsets:
 LUT_Scanlines_LO: LUT_Scanlines_LO:
-  DB $00, $00, $00, $00, $00, $00, $00, $00, $20, $20, $20, $20 +    DB $00, $00, $00, $00, $00, $00, $00, $00, $20, $20, $20, $20 
-  DB $20, $20, $20, $20, $40, $40, $40, $40, $40, $40, $40, $40 +    DB $20, $20, $20, $20, $40, $40, $40, $40, $40, $40, $40, $40 
-  DB $60, $60, $60, $60, $60, $60, $60, $60, $80, $80, $80, $80 +    DB $60, $60, $60, $60, $60, $60, $60, $60, $80, $80, $80, $80 
-  DB $80, $80, $80, $80, $A0, $A0, $A0, $A0, $A0, $A0, $A0, $A0 +    DB $80, $80, $80, $80, $a0, $a0, $a0, $a0, $a0, $a0, $a0, $a0 
-  DB $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $E0, $E0, $E0, $E0 +    DB $c0, $c0, $c0, $c0, $c0, $c0, $c0, $c0, $e0, $e0, $e0, $e0 
-  DB $E0, $E0, $E0, $E0, $00, $00, $00, $00, $00, $00, $00, $00 +    DB $e0, $e0, $e0, $e0, $00, $00, $00, $00, $00, $00, $00, $00 
-  DB $20, $20, $20, $20, $20, $20, $20, $20, $40, $40, $40, $40 +    DB $20, $20, $20, $20, $20, $20, $20, $20, $40, $40, $40, $40 
-  DB $40, $40, $40, $40, $60, $60, $60, $60, $60, $60, $60, $60 +    DB $40, $40, $40, $40, $60, $60, $60, $60, $60, $60, $60, $60 
-  DB $80, $80, $80, $80, $80, $80, $80, $80, $A0, $A0, $A0, $A0 +    DB $80, $80, $80, $80, $80, $80, $80, $80, $a0, $a0, $a0, $a0 
-  DB $A0, $A0, $A0, $A0, $C0, $C0, $C0, $C0, $C0, $C0, $C0, $C0 +    DB $a0, $a0, $a0, $a0, $c0, $c0, $c0, $c0, $c0, $c0, $c0, $c0 
-  DB $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $00, $00, $00, $00 +    DB $e0, $e0, $e0, $e0, $e0, $e0, $e0, $e0, $00, $00, $00, $00 
-  DB $00, $00, $00, $00, $20, $20, $20, $20, $20, $20, $20, $20 +    DB $00, $00, $00, $00, $20, $20, $20, $20, $20, $20, $20, $20 
-  DB $40, $40, $40, $40, $40, $40, $40, $40, $60, $60, $60, $60 +    DB $40, $40, $40, $40, $40, $40, $40, $40, $60, $60, $60, $60 
-  DB $60, $60, $60, $60, $80, $80, $80, $80, $80, $80, $80, $80 +    DB $60, $60, $60, $60, $80, $80, $80, $80, $80, $80, $80, $80 
-  DB $A0, $A0, $A0, $A0, $A0, $A0, $A0, $A0, $C0, $C0, $C0, $C0 +    DB $a0, $a0, $a0, $a0, $a0, $a0, $a0, $a0, $c0, $c0, $c0, $c0 
-  DB $C0, $C0, $C0, $C0, $E0, $E0, $E0, $E0, $E0, $E0, $E0, $E0+    DB $c0, $c0, $c0, $c0, $e0, $e0, $e0, $e0, $e0, $e0, $e0, $e0
  
 Free_64_Bytes: Free_64_Bytes:
-  DB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +    DB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 
-  DB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +    DB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 
-  DB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +    DB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 
-  DB 0, 0, 0, 0+    DB 0, 0, 0, 0
  
 LUT_Scanlines_HI: LUT_Scanlines_HI:
-  DB $40, $41, $42, $43, $44, $45, $46, $47, $40, $41, $42, $43 +    DB $40, $41, $42, $43, $44, $45, $46, $47, $40, $41, $42, $43 
-  DB $44, $45, $46, $47, $40, $41, $42, $43, $44, $45, $46, $47 +    DB $44, $45, $46, $47, $40, $41, $42, $43, $44, $45, $46, $47 
-  DB $40, $41, $42, $43, $44, $45, $46, $47, $40, $41, $42, $43 +    DB $40, $41, $42, $43, $44, $45, $46, $47, $40, $41, $42, $43 
-  DB $44, $45, $46, $47, $40, $41, $42, $43, $44, $45, $46, $47 +    DB $44, $45, $46, $47, $40, $41, $42, $43, $44, $45, $46, $47 
-  DB $40, $41, $42, $43, $44, $45, $46, $47, $40, $41, $42, $43 +    DB $40, $41, $42, $43, $44, $45, $46, $47, $40, $41, $42, $43 
-  DB $44, $45, $46, $47, $48, $49, $4A, $4B, $4C, $4D, $4E, $4F +    DB $44, $45, $46, $47, $48, $49, $4a, $4b, $4c, $4d, $4e, $4f 
-  DB $48, $49, $4A, $4B, $4C, $4D, $4E, $4F, $48, $49, $4A, $4B +    DB $48, $49, $4a, $4b, $4c, $4d, $4e, $4f, $48, $49, $4a, $4b 
-  DB $4C, $4D, $4E, $4F, $48, $49, $4A, $4B, $4C, $4D, $4E, $4F +    DB $4c, $4d, $4e, $4f, $48, $49, $4a, $4b, $4c, $4d, $4e, $4f 
-  DB $48, $49, $4A, $4B, $4C, $4D, $4E, $4F, $48, $49, $4A, $4B +    DB $48, $49, $4a, $4b, $4c, $4d, $4e, $4f, $48, $49, $4a, $4b 
-  DB $4C, $4D, $4E, $4F, $48, $49, $4A, $4B, $4C, $4D, $4E, $4F +    DB $4c, $4d, $4e, $4f, $48, $49, $4a, $4b, $4c, $4d, $4e, $4f 
-  DB $48, $49, $4A, $4B, $4C, $4D, $4E, $4F, $50, $51, $52, $53 +    DB $48, $49, $4a, $4b, $4c, $4d, $4e, $4f, $50, $51, $52, $53 
-  DB $54, $55, $56, $57, $50, $51, $52, $53, $54, $55, $56, $57 +    DB $54, $55, $56, $57, $50, $51, $52, $53, $54, $55, $56, $57 
-  DB $50, $51, $52, $53, $54, $55, $56, $57, $50, $51, $52, $53 +    DB $50, $51, $52, $53, $54, $55, $56, $57, $50, $51, $52, $53 
-  DB $54, $55, $56, $57, $50, $51, $52, $53, $54, $55, $56, $57 +    DB $54, $55, $56, $57, $50, $51, $52, $53, $54, $55, $56, $57 
-  DB $50, $51, $52, $53, $54, $55, $56, $57, $50, $51, $52, $53 +    DB $50, $51, $52, $53, $54, $55, $56, $57, $50, $51, $52, $53 
-  DB $54, $55, $56, $57, $50, $51, $52, $53, $54, $55, $56, $57+    DB $54, $55, $56, $57, $50, $51, $52, $53, $54, $55, $56, $57
 </code> </code>
  
Línea 1604: Línea 1631:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Get_Pixel_Offset_LUT_2: Get_Pixel_Offset_LUT_2:
-   LD AC                     ; Ponemos en A la X +    ld ac                       ; Ponemos en A la X 
-   RRA +    rra 
-   RRA +    rra 
-   RRA                         ; A = ???CCCCC +    rra                           ; A = ???CCCCC 
-   AND 31                      ; A = 000CCCCCb +    and %00011111                 ; A = 000CCCCCb 
-   LD LB                     ; B = coordenada Y +    ld lb                       ; B = coordenada Y 
-   LD H, Scanline_Offsets/256  ; Parte alta de la dir de tabla +    ld h, Scanline_Offsets/256    ; Parte alta de la dir de tabla 
-   ADD A, (HL                ; A = columna + tabla_baja[linea] +    add a, (hl                  ; A = columna + tabla_baja[linea] 
-   INC H                       ; saltamos a la siguiente tabla +    inc h                         ; saltamos a la siguiente tabla 
-   LD H, (HL                 ; cargamos en H la parte alta +    ld h, (hl                   ; cargamos en H la parte alta 
-   LD LA                     ; cargamos en L la parte baja +    ld la                       ; cargamos en L la parte baja 
-   LD AC                     ; Recuperamos la coordenada (X) +    ld ac                       ; Recuperamos la coordenada (X) 
-   AND 7                       ; A = Posicion relativa del pixel +    and %00000111                 ; A = Posicion relativa del pixel 
-   RET+    ret
 </code> </code>
  
- El coste de ejecución de esta rutina es de 77 t-estados, incluyendo el RET, la conversión de "X" en "Columna" y la obtención de la posición relativa del pixel. + El coste de ejecución de esta rutina es de 77 t-estados, incluyendo el ''RET'', la conversión de "X" en "Columna" y la obtención de la posición relativa del pixel.
- +
  
 \\  \\ 
Línea 1630: Línea 1656:
  Debemos elegir uno u otro sistema en función de las necesidades y requerimientos de nuestro programa: si disponemos de poca memoria libre y el tiempo de cálculo individual es suficiente, optaremos por la rutina de composición. Si, por contra, la cantidad de memoria libre no es un problema y sí que lo es el tiempo de cálculo, usaremos las rutinas basadas en tablas.  Debemos elegir uno u otro sistema en función de las necesidades y requerimientos de nuestro programa: si disponemos de poca memoria libre y el tiempo de cálculo individual es suficiente, optaremos por la rutina de composición. Si, por contra, la cantidad de memoria libre no es un problema y sí que lo es el tiempo de cálculo, usaremos las rutinas basadas en tablas.
  
- Los programadores debemos muchas veces determinar si una rutina es crítica o no según la cantidad de veces que se ejecute en el "bucle principal" y el porcentaje de tiempo que su ejecución supone en el programa. + Los programadores debemos muchas veces determinar si una rutina es crítica o no según la cantidad de veces que se ejecute en el "bucle principal" y el porcentaje de tiempo que su ejecución supone en el programa.
  
  Por ejemplo, supongamos una rutina de impresión de sprites de 3x3 bloques: aunque el tiempo de dibujado de los sprites en sí de un juego sea crítico, el posicionado en pantalla para cada sprite sólo se realiza una vez (para su esquina superior izquierda) frente a toda la porción de código que debe imprimir los 9 caracteres (9*8 bytes en pantalla) más sus atributos, con sus correspondientes rotaciones si el movimiento es pixel a pixel. El movimiento entre los diferentes bloques del sprite se realiza normalmente de forma diferencial. Probablemente, invertir "tiempo" para optimizar o "memoria" para tener tablas de precalculo sea más aconsejable en el cuerpo de la rutina de sprites o en tablas de sprites pre-rotados que en la coordenación en sí misma.  Por ejemplo, supongamos una rutina de impresión de sprites de 3x3 bloques: aunque el tiempo de dibujado de los sprites en sí de un juego sea crítico, el posicionado en pantalla para cada sprite sólo se realiza una vez (para su esquina superior izquierda) frente a toda la porción de código que debe imprimir los 9 caracteres (9*8 bytes en pantalla) más sus atributos, con sus correspondientes rotaciones si el movimiento es pixel a pixel. El movimiento entre los diferentes bloques del sprite se realiza normalmente de forma diferencial. Probablemente, invertir "tiempo" para optimizar o "memoria" para tener tablas de precalculo sea más aconsejable en el cuerpo de la rutina de sprites o en tablas de sprites pre-rotados que en la coordenación en sí misma.
Línea 1636: Línea 1662:
  La diferencia entre rutinas de tablas y de cálculos se resume en la siguiente tabla:  La diferencia entre rutinas de tablas y de cálculos se resume en la siguiente tabla:
  
 +|< 70% >|
 ^ Rutina ^ Tiempo de ejecución ^ Bytes rutina  ^ Bytes adicionales ^ Tamaño Total ^ ^ Rutina ^ Tiempo de ejecución ^ Bytes rutina  ^ Bytes adicionales ^ Tamaño Total ^
 | Cálculo | 118 t-estados | 32  | Ninguno | 32 | | Cálculo | 118 t-estados | 32  | Ninguno | 32 |
Línea 1646: Línea 1673:
  Los cálculos de las posiciones de píxeles en alta resolución son "costosos" por lo que a la hora de dibujar sprites, líneas, círculos o cualquier otra primitiva gráfica, lo normal es realizar el cálculo de una posición inicial y moverse diferencialmente respecto a la misma.  Los cálculos de las posiciones de píxeles en alta resolución son "costosos" por lo que a la hora de dibujar sprites, líneas, círculos o cualquier otra primitiva gráfica, lo normal es realizar el cálculo de una posición inicial y moverse diferencialmente respecto a la misma.
  
- Para eso se utilizan rutinas de posicionamiento diferencial como las que ya vimos en los atributos o en baja resolución que nos permitan movernos a cualquiera de los 8 píxeles de alrededor de la dirección HL y posición de pixel que estamos considerando. + Para eso se utilizan rutinas de posicionamiento diferencial como las que ya vimos en los atributos o en baja resolución que nos permitan movernos a cualquiera de los 8 píxeles de alrededor de la dirección HL y posición de pixel que estamos considerando.
  
 \\  \\ 
Línea 1665: Línea 1692:
  
 Pixel_Izquierda_HL_Mask: Pixel_Izquierda_HL_Mask:
-  RLC A            ; Rotamos A a la izquierda +    rlc a                    ; Rotamos A a la izquierda 
-  RET NC           ; Si no se activa el carry flag, volvemos +    ret nc                   ; Si no se activa el carry flag, volvemos 
-  DEC L            ; Si se activa, hemos pasado de 10000000b +    dec l                    ; Si se activa, hemos pasado de 10000000b 
-  RET              ; a 00000001b y ya podemos alterar HL +    ret                      ; a 00000001b y ya podemos alterar HL 
-   +
 Pixel_Derecha_HL_Mask: Pixel_Derecha_HL_Mask:
-  RRC A            ; Rotamos A a la derecha +    rrc a                    ; Rotamos A a la derecha 
-  RET NC           ; Si no se activa el carry flag, volvemos +    ret nc                   ; Si no se activa el carry flag, volvemos 
-  INC L            ; Si se activa, hemos pasado de 00000001b +    inc l                    ; Si se activa, hemos pasado de 00000001b 
-  RET              ; a 10000000b y ya podemos alterar HL+    ret                      ; a 10000000b y ya podemos alterar HL
 </code> </code>
  
  Son apenas 4 instrucciones, lo que resulta en un cálculo significativamente más rápido que volver a llamar a la rutina de coordenación original.  Son apenas 4 instrucciones, lo que resulta en un cálculo significativamente más rápido que volver a llamar a la rutina de coordenación original.
 +
 + Nótese cómo en lugar de utilizar ''dec hl'' o ''inc hl'' (6 t-estados), realizamos un ''dec l'' o ''inc l'' (4 t-estados), ya que dentro de un mismo scanline de pantalla no hay posibilidad de, moviendo a derecha o izquierda, variar el valor del byte alto de la dirección (siempre y cuando no excedamos los límites de la pantalla por la izquierda o por la derecha). De esta forma ahorramos 2 valiosos ciclos de reloj en una operación que suele realizarse en el bucle más interno de las rutinas de impresión de sprites.
  
  Si en lugar de una máscara de pixel tenemos en A la posición relativa (0-7), podemos utilizar el siguiente código:  Si en lugar de una máscara de pixel tenemos en A la posición relativa (0-7), podemos utilizar el siguiente código:
Línea 1686: Línea 1715:
  
 Pixel_Derecha_HL_Rel: Pixel_Derecha_HL_Rel:
-  INC A            ; Incrementamos A +    inc a                    ; Incrementamos A 
-  AND 7            ; Si A=8 -> A=0 +    and %00000111            ; Si A=8 -> A=0 
-  RET NZ           ; Si no es cero, hemos acabado +    ret nz                   ; Si no es cero, hemos acabado 
-  INC L            ; Si se activa, hemos pasado al byte +    inc l                    ; Si se activa, hemos pasado al byte 
-  RET              ; siguiente -> alterar HL +    ret                      ; siguiente -> alterar HL 
-   +
 Pixel_Izquierda_HL_Rel: Pixel_Izquierda_HL_Rel:
-  DEC A            ; Decrementamos A +    dec a                    ; Decrementamos A 
-  RET P            ; Si no hay overflow (A de 0 a 255), fin +    ret p                    ; Si no hay overflow (A de 0 a 255), fin 
-  AND 7            ; 11111111b -> 00000111b +    and %00000111            ; 11111111b -> 00000111b 
-  DEC L            ; Hemos pasado al byte siguiente -> +    dec l                    ; Hemos pasado al byte siguiente -> 
-  RET              ; alteramos HL+    ret                      ; alteramos HL
 </code> </code>
 +
 + Recordemos que ninguna de estas rutinas contempla los líneas izquierdo y derecho de la pantalla.
  
  
Línea 1704: Línea 1735:
 **Offset del pixel del scanline de arriba/abajo** **Offset del pixel del scanline de arriba/abajo**
  
- Moverse un scanline arriba o abajo requiere código adicional, como ya vimos en el apartado de coordenadas de caracteres, para detectar tanto los cambios de tercios como los cambios de caracteres. + Moverse un scanline arriba o abajo requiere código adicional, como ya vimos en el apartado de coordenadas de caracteres, para detectar tanto los cambios de tercios como los cambios de caracteres.
  
 +|< 70% >|
 ^ Bits = ^ Dirección VRAM ^ Bits de Tercio ^ Bits de scanline ^ Bits de Carácter-Y ^ Bits de Columna ^ ^ Bits = ^ Dirección VRAM ^ Bits de Tercio ^ Bits de scanline ^ Bits de Carácter-Y ^ Bits de Columna ^
 | HL = | 010 | TT | SSS | NNN | CCCCC | | HL = | 010 | TT | SSS | NNN | CCCCC |
Línea 1713: Línea 1745:
  El código para incrementar HL hacia el siguiente scanline horizontal detectando los saltos de tercio y de carácter sería el siguiente:  El código para incrementar HL hacia el siguiente scanline horizontal detectando los saltos de tercio y de carácter sería el siguiente:
  
-<code z80>  +<code z80>
 ; Avanzamos HL 1 scanline: ; Avanzamos HL 1 scanline:
-   INC H                       ; Incrementamos en 255 (siguiente scanline) +    inc h                    ; Incrementamos HL en 256 (siguiente scanline) 
-   LD AH                     ; Cargamos H en A +    ld ah                  ; Cargamos H en A 
-   AND 7                       ; Si despues del INC H los 3 bits son 0, +    and %00000111            ; Si despues del inc h los 3 bits son 0, 
-                               ; es porque era 111b y ahora 1000b. +                             ; es porque era 111b y ahora 1000b. 
-   JR NZ, nofix_abajop         ; Si no es cero, hemos acabado (solo INC H). +    jr nz, nofix_abajop      ; Si no es cero, hemos acabado (solo inc h). 
-   LD AL                     ; Es cero, hemos pasado del scanline 7 de un +    ld al                  ; Es cero, hemos pasado del scanline 7 de un 
-                               ; caracter al 0 del siguiente: ajustar NNN +                             ; caracter al 0 del siguiente: ajustar NNN 
-   ADD A32                   ; Ajustamos NNN (caracter dentro de tercio += 1) +    add a%00100000         ; Ajustamos NNN (caracter dentro de tercio += 1) 
-   LD LA                     ; Ahora hacemos la comprobacion de salto de tercio +    ld la                  ; Ahora hacemos la comprobacion de salto de tercio 
-   JR C, nofix_abajop          ; Si esta suma produce acarreo, habria que ajustar +    jr c, nofix_abajop       ; Si esta suma produce acarreo, habria que ajustar 
-                               ; tercio, pero ya lo hizo el INC H (111b -> 1000b) +                             ; tercio, pero ya lo hizo el inc h (111b -> 1000b) 
-   LD AH                     ; Si no produce acarreo, no hay que ajustar +    ld ah                  ; Si no produce acarreo, no hay que ajustar 
-   SUB 8                       ; tercio, por lo que restamos el bit TT que sumo +    sub %00001000            ; tercio, por lo que restamos el bit TT que sumo 
-   LD HA                     ; el INC H inicial.+    ld ha                  ; el inc h inicial.
  
 nofix_abajop: nofix_abajop:
-   ; HL contiene ahora la direccion del siguiente scanline +    ; HL contiene ahora la direccion del siguiente scanline 
-   ; ya sea del mismo caracter o el scanline 0 del siguiente.+    ; ya sea del mismo caracter o el scanline 0 del siguiente.
 </code> </code>
  
Línea 1738: Línea 1770:
  
 <code z80> <code z80>
-   LD AH +    ld ah 
-   AND 7                       ; Comprobamos scanline +    and %00000111            ; Comprobamos scanline 
-   JR Z, Anterior_SL_DEC       ; Si es cero, hay salto de caracter +    jr z, Anterior_SL_DEC    ; Si es cero, hay salto de caracter 
-   DEC H                       ; No es cero, basta HL = HL - 256 +    dec h                    ; No es cero, basta HL = HL - 256 
-   RET                         ; Hemos acabado (solo INC H). +    ret                      ; Hemos acabado (solo inc h). 
-Anterior_SL_DEC:               ; Hay que ir de caracter 000b a 111b +Anterior_SL_DEC:             ; Hay que ir de caracter 000b a 111b 
-   DEC H                       ; Decrementamos H +    dec h                    ; Decrementamos H 
-   LD AL                     ; Ajustamos NNN (caracter en tercio -=1) +    ld al                  ; Ajustamos NNN (caracter en tercio -=1) 
-   SUB 32 +    sub %00100000            ; -32 
-   LD LA +    ld la 
-   RET C                       ; Si se produjo carry, no hay que ajustar +    ret c                    ; Si se produjo carry, no hay que ajustar 
-   LD AH                     ; Se produjo carry, ajustamos el tercio +    ld ah                  ; Se produjo carry, ajustamos el tercio 
-   ADD A8                    ; por el DEC H inicial. +    add a%00001000         ; por el dec h inicial. (+8) 
-   LD HA+    ld ha
 </code> </code>
  
  
- Veamos este mismo código en forma de subrutina, aprovechando con RET la posibilidad de evitar los saltos hacia el final de las rutinas:+ Veamos este mismo código en forma de subrutina, aprovechando con ''RET'' la posibilidad de evitar los saltos hacia el final de las rutinas:
  
  
Línea 1769: Línea 1801:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Siguiente_Scanline_HL: Siguiente_Scanline_HL:
-   INC H +    inc h 
-   LD AH +    ld ah 
-   AND 7 +    and %00000111 
-   RET NZ +    ret nz 
-   LD AL +    ld al 
-   ADD A, 32 +    add a%00100000         ; +32 
-   LD LA +    ld la 
-   RET C +    ret c 
-   LD AH +    ld ah 
-   SUB +    sub %00001000            ; -
-   LD HA +    ld ha 
-   RET             ; Devolvemos en HL el valor ajustado+    ret                      ; Devolvemos en HL el valor ajustado
 </code> </code>
  
  
- La rutina para retroceder al scanline superior es de similar factura, con una pequeña reorganización del código para evitar el salto con JR:+ La rutina para retroceder al scanline superior es de similar factura, con una pequeña reorganización del código para evitar el salto con ''jr'':
  
  
Línea 1798: Línea 1830:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Anterior_Scanline_HL: Anterior_Scanline_HL:
-   LD AH +    ld ah 
-   DEC H +    dec h 
-   AND 7  +    and %00000111 
-   RET NZ +    ret nz 
-   LD A, 8 +    ld a%00001000          ; A = 
-   ADD AH +    add ah 
-   LD HA +    ld ha 
-   LD AL +    ld al 
-   SUB 32 +    sub %00100000            ; -32 
-   LD LA +    ld la 
-   RET NC +    ret nc 
-   LD AH +    ld ah 
-   SUB +    sub %00001000            ; -
-   LD HA +    ld ha 
-   RET             ; Devolvemos en HL el valor ajustado +    ret                      ; Devolvemos en HL el valor ajustado
 </code> </code>
  
- Siguiente_Scanline_HL será especialmente útil en el desarrollo de rutinas de impresión de Sprites, aunque lo normal es que incluyamos el código "inline" dentro de dichas rutinas, para ahorrar los ciclos de reloj usandos en un CALL+RET.+La rutina ''Siguiente_Scanline_HL'' será especialmente útil en el desarrollo de rutinas de impresión de Sprites, aunque lo normal es que incluyamos el código "inline" dentro de dichas rutinas, para ahorrar los ciclos de reloj usandos en un call+ret.
  
  
-\\ \\ +\\ 
 ===== Otras rutinas ===== ===== Otras rutinas =====
  
Línea 1831: Línea 1862:
 <code z80> <code z80>
 ;------------------------------------------------------------- ;-------------------------------------------------------------
-Image_Offset_From_Attr (DF-ATT):+Attr_Offset_From_Image (DF-ATT):
 ; ;
 ; Entrada:  HL = Direccion de memoria de imagen. ; Entrada:  HL = Direccion de memoria de imagen.
 ; Salida:   DE = Direccion de atributo correspondiente a HL. ; Salida:   DE = Direccion de atributo correspondiente a HL.
 ;------------------------------------------------------------- ;-------------------------------------------------------------
-Image_Offset_From_Attr+Attr_Offset_From_Image
-   LD AH +    ld ah 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   AND 3 +    and %00000011 
-   OR $58 +    or %01011000 
-   LD DA +    ld da 
-   LD EL +    ld el 
-   RET+    ret
 </code> </code>
  
Línea 1852: Línea 1883:
 <code z80> <code z80>
 ;------------------------------------------------------------- ;-------------------------------------------------------------
-Attr_Offset_From_Image (ATT_DF):+Image_Offset_From_Attr (ATT_DF):
 ; ;
 ; Entrada:  HL = Direccion de memoria de atributo. ; Entrada:  HL = Direccion de memoria de atributo.
 ; Salida:   DE = Direccion de imagen correspondiente a HL. ; Salida:   DE = Direccion de imagen correspondiente a HL.
 ;------------------------------------------------------------- ;-------------------------------------------------------------
-Attr_Offset_From_Image+Image_Offset_From_Attr
-   LD AH +    ld ah 
-   AND 3 +    and %00000011 
-   RLCA +    rlca 
-   RLCA +    rlca 
-   RLCA +    rlca 
-   OR $40 +    or %01000000 
-   LD DA +    ld da 
-   LD EL +    ld el 
-   RET+    ret
 </code> </code>
  
Línea 1881: Línea 1912:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Get_Char_Data: Get_Char_Data:
-   LD AB +    ld ab 
-   AND $18 +    and %00011000 
-   LD HA +    ld ha 
-   SET 6, H +    set 6, h 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   OR $58 +    or %01011000 
-   LD DA +    ld da 
-   LD AB +    ld ab 
-   AND 7 +    and %00000111 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   ADD AC +    add ac 
-   LD LA +    ld la 
-   LD EA +    ld ea 
-   LD A, (DE+    ld a, (de
-   RET+    ret
 </code> </code>
  
  Llamando a la anterior rutina con unas coordenadas (c,f) en C y B obtenemos la dirección de memoria de imagen (HL) y de atributo (DE) de dicho carácter, así como el valor del atributo en sí mismo (A).  Llamando a la anterior rutina con unas coordenadas (c,f) en C y B obtenemos la dirección de memoria de imagen (HL) y de atributo (DE) de dicho carácter, así como el valor del atributo en sí mismo (A).
  
 +\\ 
 +===== Optimizaciones para Get_Pixel_Offset_HR =====
 +
 + En ocasiones se puede reescribir una rutina de otra forma para ser ligeramente más eficiente, y las rutinas relacionadas con los gráficos (tanto "dibujar" gráficos como calcular la posición de dibujado) es una firma candidata a optimizarla todo lo posible.
 +
 +\\ //Dean Belfield//, en su página //L Break Into Program// nos proporciona la siguiente rutina optimizada para obtener la dirección de memoria de un pixel dadas su coordenadas (x,y) que requiere 117 t-estados, a costa de no devolvernos la posición relativa del pixel:
  
 \\  \\ 
 +<code z80>
 +; Get screen address - by Dean Belfield
 +;
 +;  B = Y pixel position
 +;  C = X pixel position
 +;  Returns address in HL
 +Get_Pixel_Address:
 +    ld a, b                  ; Calculate Y2,Y1,Y0
 +    and %00000111            ; Mask out unwanted bits
 +    or %01000000             ; Set base address of screen
 +    ld h, a                  ; Store in H
 +    ld a, b                  ; Calculate Y7,Y6
 +    rra                      ; Shift to position
 +    rra
 +    rra
 +    and %00011000            ; Mask out unwanted bits
 +    or h                     ; OR with Y2,Y1,Y0
 +    ld h, a                  ; Store in H
 +    ld a, b                  ; Calculate Y5,Y4,Y3
 +    rla                      ; Shift to position
 +    rla
 +    and %11100000            ; Mask out unwanted bits
 +    ld l, a                  ; Store in L
 +    ld a, c                  ; Calculate X4,X3,X2,X1,X0
 +    rra                      ; Shift into position
 +    rra
 +    rra
 +    and %00011111            ; Mask out unwanted bits
 +    or l                     ; OR with Y5,Y4,Y3
 +    ld l, a                  ; Store in L
 +    ret
 +</code>
  
 +Finalmente, //David Black// en su web //Overtaken by events// nos ofrece la siguiente rutina de 105 t-estados y 26 bytes:
 +
 +\\ 
 +<code z80>
 +; Get screen address - by David Black
 +;  B = Y pixel position
 +;  C = X pixel position
 +; Returns address in HL
 +Get_Screen_Address:
 +    ld a,b                  ; Work on the upper byte of the address
 +    and %00000111                ; a = Y2 Y1 y0
 +    or %01000000                 ; first three bits are always 010
 +    ld h,a                  ; store in h
 +    ld a,b                  ; get bits Y7, Y6
 +    rra                  ; move them into place
 +    rra
 +    rra
 +    and %00011000                ; mask off
 +    or h                  ; a = 0 1 0 Y7 Y6 Y2 Y1 Y0
 +    ld h,a                  ; calculation of h is now complete
 +    ld a,b                  ; get y
 +    rla
 +    rla
 +    and %11100000                ; a = y5 y4 y3 0 0 0 0 0
 +    ld l,a                  ; store in l
 +    ld a,c
 +    and %00011111                ; a = X4 X3 X2 X1
 +    or l                  ; a = Y5 Y4 Y3 X4 X3 X2 X1
 +    ld l,a                  ; calculation of l is complete
 +    ret
 +</code>
 +
 +Utilizando tablas, en esta misma web podemos ver las siguientes 2 aproximaciones de //Patrick Prendergast// en base a organizar los datos en memoria alineándolos de una forma que "desperdiciamos" memoria a cambio de que las rutinas sean más rápidas por cómo están alineados esos datos:
 +
 +<code z80>
 +; Store the LUT table in the format "y5 y4 y3 y7 y6 y2 y1 y0"
 +; Lower 5 bits where you need them for y and upper 3 bits to mask
 +; out to OR with X (which are replaced with 010 anyway).
 +; This way you'd only need 192 bytes for the table, which could be
 +; page-aligned for speed. You'd be looking at 69 cycles por request
 +; and 16 + 192 for the code + table.
 +;
 +; By Patrick Prendergast.
 +
 +; b = y, c = x
 +getScreenAddress:
 +    ld h,tbl >> 8
 +    ld l,b
 +    ld h,(hl)
 +    ld a,%11100000
 +    and h
 +    or c
 +    ld l,a
 +    ld a,%00011111
 +    and h
 +    or %01000000
 +    ld h,a
 +    ret
 +
 +tbl: ; y5 y4 y3 y7 y6 y2 y1 y0
 +    .db 0,1,2,3,4,5,6,7,32,33...
 +    
 +
 +; Option 2: if you are willing to [potentially] sacrifice
 +; some space for speed, you can divide the table so that
 +; you have the low and high bytes of your address list in
 +; 2 independent tables and have them both page aligned -
 +; with the low byte first in memory.
 +; This would completely remove to need to calc y*2 to get
 +; to your table offset.
 +; This would require 64 bytes of padding after the 1st table
 +; (due to both tables being page aligned) meaning you would
 +; need 448 bytes all up. That being said the 64 bytes of
 +; padding space is not needed so you can include any other
 +; data you might need there so it's not wasted.
 +; Then you would only need 47 cycles to lookup your address!
 +;
 +; By Patrick Prendergast.
 +
 +; b = y, c = x
 +getScreenAddress:
 +    ld h,tblLow >> 8
 +    ld l,b
 +    ld a,(hl)
 +    inc h
 +    ld h,(hl)
 +    or c
 +    ld l,a
 +    ret
 +
 +    ALIGN 256
 +tblLow: ; (ADDR & 0xFF)
 +    .db 0,0,0,0,0,0,0,0,32,32,32...
 +
 +    ALIGN 256
 +tblHigh: ; (ADDR >> 8)
 +    .db 64,65,66,67,68,69,70,71,64,65,66...
 +</code>
 +
 +Estas rutinas son realmente rápidas, teniendo la segunda un coste de sólo 47 t-estados por cálculo de dirección, a costa de ocupar más espacio por separar la parte alta y la parte baja de la tabla precalculada, y alinearlas en memoria en un múltiplo de 256 para evitar cálculos.
 +
 +\\ 
 ===== Ficheros ===== ===== Ficheros =====
  
Línea 1913: Línea 2084:
   * {{cursos:ensamblador:gfx2_lut.asm|Ejemplo de uso de Look Up Tables}}   * {{cursos:ensamblador:gfx2_lut.asm|Ejemplo de uso de Look Up Tables}}
   * {{cursos:ensamblador:gfx2_lut.tap|Tap del ejemplo anterior}}   * {{cursos:ensamblador:gfx2_lut.tap|Tap del ejemplo anterior}}
- +
 \\  \\ 
 ===== Enlaces ===== ===== Enlaces =====
Línea 1920: Línea 2091:
   * [[ :programacion:ensamblador:calculo-coordenadas | Cálculo de direcciones de pantalla a partir de coordenadas]] (por Metalbrain).   * [[ :programacion:ensamblador:calculo-coordenadas | Cálculo de direcciones de pantalla a partir de coordenadas]] (por Metalbrain).
   * [[http://www.z80.info|Web del Z80]].   * [[http://www.z80.info|Web del Z80]].
-  * [[http://www.ticalc.org/pub/text/z80/z80time.txt|Tiempos de ejecución y tamaños]] de las instrucciones del Z80.+  * [[http://www.ticalc.org/pub/text/z80/z80time.txt|Tiempos de ejecución y tamaños]] de las instrucciones del Z80 (ver z80time.txt).
   * [[http://www.worldofspectrum.org/faq/reference/z80reference.htm|Z80 Reference de WOS]].   * [[http://www.worldofspectrum.org/faq/reference/z80reference.htm|Z80 Reference de WOS]].
   * [[http://www.speccy.org/trastero/cosas/Fichas/fichas.htm|Microfichas de CM de MicroHobby]].   * [[http://www.speccy.org/trastero/cosas/Fichas/fichas.htm|Microfichas de CM de MicroHobby]].
-  * [[http://www.arrakis.es/~ninsesabe/pasmo/|PASMO]].+  * [[https://www.overtakenbyevents.com/lets-talk-about-the-zx-specrum-screen-layout-part-three/|Overtaken by Events - the screen layout (III)]].
  
 \\  \\ 
 +**[ [[.:indice|⬉]] | [[.:gfx1_vram|⬅]] | [[.:gfx3_sprites_lowres|➡]] ]**
  
  • cursos/ensamblador/gfx2_direccionamiento.1289470097.txt.gz
  • Última modificación: 11-11-2010 10:08
  • por sromero