Diferencias
Muestra las diferencias entre dos versiones de la página.
Ambos lados, revisión anterior Revisión previa Próxima revisión | Revisión previa | ||
cursos:ensamblador:gfx2_direccionamiento [13-11-2010 07:18] – [Cálculo de posiciones de atributo] sromero | cursos: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, | 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, | ||
Línea 10: | Línea 9: | ||
| | ||
- | 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. |
- | * **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**: | + | * **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**: | + | |
- | * **Bloque o carácter**: | + | * **Resolución del área de atributos**: |
- | * **Scanline**: | + | |
- | * **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, " | + | * **Atributo**: |
- | * **Coordenadas (c,f)**: Se utiliza la nomenclatura (c,f), de (columna, | + | |
- | * **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/ | + | * **Bloque o 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 " | + | |
- | * **Offset** o **Desplazamiento**: | + | * **Scanline**: |
+ | |||
+ | * **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 (c,f)**: Se utiliza la nomenclatura (c,f), de (columna, | ||
+ | |||
+ | * **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/ | ||
+ | |||
+ | * **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 " | ||
+ | |||
+ | * **Offset** o **Desplazamiento**: | ||
\\ | \\ | ||
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 // | + | * Cada byte del área de atributos se denomina // |
* 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: | ||
| | ||
\\ | \\ | ||
+ | |||
* **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)**: | * **Cálculo de posiciones de carácter (baja resolución)**: | ||
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 |
| | ||
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, | + | Una primera aproximación a la obtención de la dirección en memoria de un atributo concreto (columna, |
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 | |
// | // | ||
\\ | \\ | ||
Línea 139: | Línea 150: | ||
< | < | ||
- | | + | |
- | </ | + | </ |
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 " | + | |
- | LD H, 0 | + | ld h, 0 |
- | LD L, B ; HL = " | + | ld l, b |
- | ADD HL, HL | + | add hl, hl ; HL = HL*2 |
- | ADD HL, HL | + | add hl, hl ; HL = HL*4 |
- | ADD HL, HL | + | add hl, hl ; HL = HL*8 |
- | ADD HL, HL | + | add hl, hl ; HL = HL*16 |
- | ADD HL, HL | + | add hl, hl ; HL = HL*32 |
- | LD D, 0 | + | ld d, 0 |
- | LD E, C ; DE = " | + | ld e, c |
- | ADD HL, DE | + | add hl, de ; HL = fila*32 + columna |
- | LD DE, 22528 ; Direccion de inicio de atributos | + | ld de, 22528 |
- | ADD HL, DE | + | add hl, de ; HL = 22528 + fila*32 + columna |
- | RET | + | ret |
</ | </ | ||
Línea 178: | Línea 189: | ||
| | ||
- | + | |< 60% >| | |
- | ^ Linea f ^ Dirección en Hexadecimal ^ En Decimal ^ En Binario ^ | + | ^ Linea f ^ Dirección en Hexadecimal ^ En Decimal ^ En Binario ^ |
| 0 | $5800 | 22528 | // | | 0 | $5800 | 22528 | // | ||
| 1 | $5820 | 22560 | // | | 1 | $5820 | 22560 | // | ||
Línea 186: | Línea 197: | ||
| 4 | $5880 | 22656 | // | | 4 | $5880 | 22656 | // | ||
| (...) | (...) | (...) | (...) | | | (...) | (...) | (...) | (...) | | ||
- | | 21 | $5AA0 | 23200 | // | + | | 21 | $5aa0 | 23200 | // |
- | | 22 | $5AC0 | 23232 | // | + | | 22 | $5ac0 | 23232 | // |
- | | 23 | $5AE0 | 23264 | // | + | | 23 | $5ae0 | 23264 | // |
- | * 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 A, B | + | ld a, b |
- | RRCA | + | rrca |
- | RRCA | + | rrca |
- | RRCA ; Desplazamos A 3 veces (A=A>> | + | |
- | AND 3 | + | and %00000011 |
- | ; altos de FILA (000FFFFFb | + | |
- | ADD A, $58 ; Ponemos los bits 15-10 como 010110b | + | add a, %01011000 |
- | LD H, A | + | ld h, a |
- | LD A, B | + | ld a, b |
- | AND 7 | + | and %00000111 |
- | RRCA | + | rrca |
- | RRCA ; Los rotamos para colocarlos en su | + | |
- | RRCA ; ubicacion final (<<5 = >> | + | |
- | ADD A, C ; Sumamos el numero de columna | + | add a, c ; Sumamos el numero de columna |
- | LD L, A | + | ld l, a |
- | RET | + | |
</ | </ | ||
- | 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 // | + | 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 // |
- | 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 ('' |
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 | + | |
- | | + | |
- | LD A, 85 | + | ld a, 85 |
- | LD (HL), A ; Establecemos el atributo de (12,10) | + | |
</ | </ | ||
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, | + | La versión para coordenadas en alta resolución de la anterior rutina ('' |
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 | + | srl b |
- | | + | |
- | SRL C | + | srl c |
- | SRL C | + | srl c |
- | SRL C | + | srl c |
</ | </ | ||
- | Una vez obtenido (c,f), el desarrollo de la rutina es el mismo que en el caso de Get_Attribute_Offset_LR(c, | + | Una vez obtenido (c,f), el desarrollo de la rutina es el mismo que en el caso de '' |
<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: | ; Salida: | ||
; | ; | ||
- | Get_Attribute_Offset_HR: | + | Get_Attribute_Offset_HR: |
- | SRL B | + | srl b |
- | SRL B | + | srl b |
- | SRL B | + | srl b |
- | + | ||
- | SRL C | + | |
- | SRL C | + | |
- | SRL C ; C = C/8 -> Ahora C es COLUMNA | + | |
- | LD A, B | + | srl c |
- | RRCA | + | srl c |
- | | + | srl c |
- | | + | |
- | AND 3 ; A = A AND 00000011 = los 2 bits mas | + | |
- | ; altos de FILA (000FFFFFb | + | |
- | ADD A, $58 ; Ponemos los bits 15-10 como 010110b | + | ld a, b |
- | LD H, A | + | rrca |
- | LD A, B | + | rrca |
- | AND 7 | + | rrca ; Desplazamos |
- | RRCA | + | and %00000011 |
- | RRCA ; Los rotamos para colocarlos en su | + | ; altos de FILA (000FFFFFb -> 000000FFb) |
- | RRCA ; ubicacion final (<<5 = >> | + | add a, %01011000 |
- | ADD A, C ; Sumamos el numero de columna | + | ld h, a |
- | LD L, A | + | ld a, b |
- | RET | + | and %00000011 |
+ | rrca | ||
+ | | ||
+ | | ||
+ | add a, c ; Sumamos el numero de columna | ||
+ | ld l, a | ||
+ | | ||
</ | </ | ||
Línea 310: | Línea 320: | ||
La siguiente rutina nos proporciona, | La siguiente rutina nos proporciona, | ||
- | + | ||
<code z80> | <code z80> | ||
; | ; | ||
Línea 321: | Línea 331: | ||
; | ; | ||
Get_Attribute_Coordinates_LR: | Get_Attribute_Coordinates_LR: | ||
- | | + | ; Descomponemos HL = 010110FF FFFCCCCCb |
- | LD A, H | + | ld a, h |
- | AND 3 | + | and %00000011 |
- | RLCA | + | rlca |
- | RLCA | + | rlca |
- | RLCA ; Rotacion a izquierda 000000FFb -> 000FF000b | + | |
- | LD B, A | + | ld b, a |
- | LD A, L | + | ld a, l |
- | AND 224 ; Nos quedamos con los 3 bits mas altos | + | and %11100000 |
- | RLCA | + | rlca |
- | RLCA | + | rlca |
- | RLCA | + | |
- | OR B ; A = A + B = 000FFFFFb | + | or b ; A = A + B = 000FFFFFb |
- | LD B, A | + | ld b, a |
- | LD A, L | + | ld a, l |
- | AND 31 ; Nos quedamos con los 5 bits mas bajos | + | and %00011111 |
- | LD C, A | + | ld c, a |
- | | + | |
- | RET | + | ret |
</ | </ | ||
De nuevo, el código no incluye ningún tipo de control sobre la dirección que se le proporciona, | De nuevo, el código no incluye ningún tipo de control sobre la dirección que se le proporciona, | ||
- | La rutina para trabajar con coordenadas en alta resolución (**Get_Attribute_Coordinates_HR(x, | + | La rutina para trabajar con coordenadas en alta resolución ('' |
<code z80> | <code z80> | ||
Línea 358: | Línea 368: | ||
; | ; | ||
Get_Attribute_Coordinates_HR: | Get_Attribute_Coordinates_HR: | ||
- | | + | ; Descomponemos HL = 010110FF FFFCCCCCb |
- | LD A, H | + | ld a, h |
- | AND 3 ; A = bits 0, 1 de HL = 2 bits altos de F, CF=0 | + | |
- | RLCA | + | rlca |
- | RLCA | + | rlca |
- | RLCA ; Rotacion a izquierda 000000FFb -> 000FF000b | + | |
- | LD B, A | + | ld b, a |
- | LD A, L | + | ld a, l |
- | AND 224 ; Nos quedamos con los 3 bits mas altos | + | and %11100000 |
- | RLCA | + | rlca |
- | RLCA | + | rlca |
- | RLCA ; Rotacion a izquierda FFF00000b -> 00000FFFb | + | |
- | OR B ; A = A + B = 000FFFFFb | + | or b ; A = A + B = 000FFFFFb |
- | LD B, A | + | ld b, a |
- | LD A, L | + | ld a, l |
- | AND 31 ; Nos quedamos con los 5 bits mas bajos | + | and %00011111 |
- | LD C, A | + | ld c, a |
- | SLA C | + | sla c |
- | SLA C | + | sla c |
- | SLA C | + | sla c |
- | SLA B | + | sla b |
- | SLA B | + | sla b |
- | SLA B | + | sla b |
- | + | ||
- | RET | + | |
+ | ret | ||
</ | </ | ||
Línea 399: | Línea 408: | ||
<code z80> | <code z80> | ||
Atributo_derecha: | Atributo_derecha: | ||
- | INC HL | + | inc hl ; HL = HL + 1 |
- | + | ||
Atributo_izquierda: | Atributo_izquierda: | ||
- | DEC HL | + | dec hl ; HL = HL - 1 |
- | + | ||
Atributo_abajo: | Atributo_abajo: | ||
- | LD DE, 32 | + | ld de, 32 |
- | ADD HL, DE | + | add hl, de ; HL = HL + 32 |
Atributo_arriba: | Atributo_arriba: | ||
- | LD DE, -32 | + | ld de, -32 |
- | ADD HL, DE | + | add hl, de ; HL = HL - 32 |
</ | </ | ||
Línea 417: | Línea 426: | ||
<code z80> | <code z80> | ||
Atributo_abajo_sin_usar_DE_2: | Atributo_abajo_sin_usar_DE_2: | ||
- | LD A, L ; A = L | + | ld a, l |
- | | + | add a, 32 ; Sumamos A = A + 32 . El Carry Flag se ve afectado. |
- | LD L, A ; Guardamos en L (L = L+32) | + | ld l, a |
- | JR NC, attrab_noinc | + | jr nc, attrab_noinc |
- | INC H | + | inc h |
- | attrab_noinc: | + | attrab_noinc: |
- | | + | |
Atributo_arriba_sin_usar_DE: | Atributo_arriba_sin_usar_DE: | ||
- | LD A, L ; A = L | + | ld a, l |
- | | + | |
- | LD L, A ; Guardamos en L (L = L-32) | + | ld l, a |
- | JR NC, attrab_nodec | + | jr nc, attrab_nodec |
- | DEC H | + | dec h |
- | attrab_nodec: | + | attrab_nodec: |
</ | </ | ||
Línea 438: | Línea 446: | ||
<code z80> | <code z80> | ||
- | JR NC, attrab_noinc | + | jr nc, attrab_noinc |
- | INC H | + | inc h |
attrab_noinc: | attrab_noinc: | ||
</ | </ | ||
Línea 446: | Línea 454: | ||
<code z80> | <code z80> | ||
- | LD A, 0 | + | ld a, 0 ; Ponemos A a cero, no podemos usar un "xor a" |
- | ; o un "OR A" porque afectariamos al Carry Flag. | + | |
- | ADC H ; A = H + CarryFlag | + | adc a, h ; A = H + CarryFlag |
- | LD H, A ; H = H + CarryFlag | + | ld h, a |
- | ; Ahora HL = (H+CF)*256 + (L+32) = HL + 32 | + | |
</ | </ | ||
- | 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 AF, AF** antes y después de la ejecución de la rutina. | + | 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 '' |
- | \\ | ||
\\ | \\ | ||
===== Cálculo de posiciones de caracteres ===== | ===== Cálculo de posiciones de caracteres ===== | ||
Línea 475: | Línea 482: | ||
\\ | \\ | ||
- | | + | |
Línea 488: | 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 494: | 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 502: | 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 510: | 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 | |
\\ | \\ | ||
| | ||
+ | |< 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**// | | 0 | $4000 | 16384 | **010**// | ||
Línea 527: | Línea 536: | ||
| 10 | $4840 | 18496 | **010**// | | 10 | $4840 | 18496 | **010**// | ||
| (...) | (...) | (...) | (...) | (...) | (...) | | | (...) | (...) | (...) | (...) | (...) | (...) | | ||
- | | 23 | $50E0 | 20704 | **010**// | + | | 23 | $50e0 | 20704 | **010**// |
Lo primero que puede llamarnos la atención es lo siguiente: | Lo primero que puede llamarnos la atención es lo siguiente: | ||
Línea 538: | Línea 547: | ||
* Conclusión: | * Conclusió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. | * 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 567: | Línea 576: | ||
; | ; | ||
Get_Line_Offset_LR: | Get_Line_Offset_LR: | ||
- | LD A, B ; A = B, para extraer los bits de tercio | + | ld a, b |
- | AND $18 ; A = A AND 00011000b | + | and %00011000 |
- | | + | |
- | ADD A, $40 | + | add a, %01000000 |
- | LD H, A ; Ya tenemos la parte alta calculada | + | ld h, a |
- | | + | |
- | LD A, B ; Ahora calculamos la parte baja | + | ld a, b |
- | AND 7 ; Nos quedamos con los bits más bajos de FILA | + | and %00000111 |
- | | + | |
- | RRCA | + | |
- | RRCA | + | |
- | RRCA | + | |
- | LD L, A ; Lo cargamos en la parte baja de la direccion | + | ld l, a |
- | RET ; HL = 010TT000NNN00000b | + | |
</ | </ | ||
Línea 619: | Línea 628: | ||
; | ; | ||
Get_Char_Offset_LR: | Get_Char_Offset_LR: | ||
- | LD A, B ; A = B, para extraer los bits de tercio | + | ld a, b |
- | AND $18 ; A = A AND 00011000b | + | and %00011000 |
- | | + | |
- | ADD A, $40 | + | add a, %01000000 |
- | LD H, A ; Ya tenemos la parte alta calculada | + | ld h, a |
- | | + | |
- | LD A, B ; Ahora calculamos la parte baja | + | ld a, b |
- | AND 7 ; Nos quedamos con los bits más bajos de FILA | + | and %00000111 |
- | | + | |
- | RRCA | + | |
- | RRCA | + | |
- | RRCA | + | |
- | ADD A, C | + | add a, c ; Sumamos COLUMNA -> A = NNNCCCCCb |
- | LD L, A ; Lo cargamos en la parte baja de la direccion | + | ld l, a |
- | RET ; HL = 010TT000NNNCCCCCb | + | |
</ | </ | ||
Línea 648: | 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 |
- | + | ||
- | SRL C | + | |
- | SRL C | + | |
- | SRL C ; C = C/8 -> Ahora C es COLUMNA | + | |
- | | + | srl c |
- | RET | + | srl c |
+ | srl c ; C = C/8 -> Ahora C es COLUMNA | ||
+ | |||
+ | | ||
+ | ret | ||
</ | </ | ||
Línea 664: | Línea 673: | ||
| | ||
- | |||
<code z80> | <code z80> | ||
Línea 677: | Línea 685: | ||
Get_Char_Coordinates_LR: | Get_Char_Coordinates_LR: | ||
- | ; HL = 010TT000 NNNCCCCCb -> F = 000TTNNNb y C = 000CCCCCb | + | |
+ | ; Fila = 000TTNNNb y Columna | ||
- | ; Calculo de la fila: | + | ; Calculo de la fila: |
- | LD A, H ; A = H, para extraer los bits de tercio | + | ld a, h |
- | AND $18 ; A = 000TT000b | + | and %00011000 |
- | LD B, A ; B = A = 000TT000b | + | ld b, a |
- | LD A, L ; A = L, para extraer los bits de N (FT) | + | ld a, l |
- | AND $E0 ; A = A AND 11100000b = NNN00000b | + | and %0b11100000 |
- | RLC A ; Rotamos A 3 veces a la izquierda | + | rlc a |
- | RLC A | + | rlc a |
- | RLC A ; A = 00000NNNb | + | rlc a |
- | OR B | + | or b ; A = A or b = 000TTNNNb |
- | LD B, A ; B = A = 000TTNNNb | + | ld b, a |
- | + | ||
- | ; 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 | + | |
- | | + | |
+ | ; Calculo de la columna: | ||
+ | ld a, l ; A = L, para extraer los bits de columna | ||
+ | and %00011111 | ||
+ | ld c, a ; C = Columna | ||
+ | | ||
</ | </ | ||
- | | + | |
<code z80> | <code z80> | ||
- | SLA C | + | sla c |
- | SLA C | + | sla c |
- | SLA C | + | sla c |
- | + | ||
- | SLA B | + | sla b |
- | SLA B | + | sla b |
- | SLA B | + | sla b |
</ | </ | ||
Línea 721: | 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 " | + | 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 " |
<code z80> | <code z80> | ||
Scanline_Arriba_HL: | Scanline_Arriba_HL: | ||
- | | + | |
Scanline_Abajo_HL: | Scanline_Abajo_HL: | ||
- | | + | |
</ | </ | ||
Línea 744: | Línea 752: | ||
<code z80> | <code z80> | ||
Caracter_Derecha_HL: | Caracter_Derecha_HL: | ||
- | INC HL | + | inc hl ; HL = HL + 1 |
- | + | ||
Caracter_Izquierda_HL: | Caracter_Izquierda_HL: | ||
- | DEC HL | + | dec hl ; HL = HL - 1 |
</ | </ | ||
Línea 753: | Línea 761: | ||
| | ||
+ | |< 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 765: | Línea 774: | ||
<code z80> | <code z80> | ||
- | LD A, L ; Cargamos A en L y le sumamos 32 para | + | Caracter_Abajo_HL: |
- | ADD A, 32 ; incrementar " | + | ld a, l ; Cargamos A en L y le sumamos 32 para |
- | LD L, A ; L = A | + | add a, %00100000 |
- | JR NC, no_ajustar_H_abajob | + | ld l, a ; L = A |
- | LD A, H ; la parte alta sumando 8 a H (TT = TT + 1). | + | jr nc, no_ajustar_H_abajob |
- | ADD A, 8 | + | ld a, h ; la parte alta sumando 8 a H (TT = TT + 1). |
- | LD H, A ; H = A | + | add a, %00001000 |
+ | ld h, a ; H = A | ||
no_ajustar_H_abajob | no_ajustar_H_abajob | ||
- | ; Ahora HL apunta al bloque de debajo. | + | |
</ | </ | ||
Línea 779: | 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 | ||
- | 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 | ||
- | </ | ||
- | |||
- | 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 " | ||
- | 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, L ; Cargamos L en A | + | ld a, l ; Cargamos L en A |
- | AND 224 ; A = A AND 11100000b | + | and %11100000 |
- | JR NZ, nofix_h_arribab | + | jr nz, nofix_h_arribab |
- | LD A, H ; Si es cero, ajustamos tercio (-1) | + | ld a, h ; Si es cero, ajustamos tercio (-1) |
- | SUB 8 ; Decrementamos TT | + | sub %00001000 |
- | LD H, A | + | ld h, a |
nofix_h_arribab: | nofix_h_arribab: | ||
- | LD A, L ; Decrementar NNN | + | ld a, l ; Decrementar NNN |
- | SUB 32 | + | |
- | LD L, A ; NNN = NNN-1 | + | ld l, a ; NNN = NNN-1 |
- | RET | + | ret |
</ | </ | ||
Línea 821: | Línea 806: | ||
\\ | \\ | ||
- | \\ | ||
===== Cálculo de posiciones de píxeles ===== | ===== Cálculo de posiciones de píxeles ===== | ||
Línea 853: | 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**// | | 0 | 0 | $4000 | 16384 | **010**// | ||
Línea 864: | 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// | | 0 | 0 | 00// | ||
Línea 889: | 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 904: | 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 " | La rutina resultante es similar a la vista en baja resolución con la descomposición de la coordenada Y en el " | ||
Línea 919: | Línea 905: | ||
Get_Pixel_Offset_HR: | Get_Pixel_Offset_HR: | ||
- | ; Calculo de la parte alta de la direccion: | + | |
- | LD A, B | + | ld a, b |
- | AND 7 ; A = 00000SSSb | + | and %00000111 |
- | LD H, A ; Lo guardamos en H | + | ld h, a |
- | LD A, B ; Recuperamos de nuevo Y | + | ld a, b |
- | RRA | + | rra |
- | RRA | + | rra |
- | RRA ; Rotamos para asi obtener el tercio | + | |
- | AND 24 | + | and %00011000 |
- | OR H | + | or h ; H = H or a = 00000SSSb |
- | OR 64 ; Mezclamos H con 01000000b (vram) | + | or %01000000 |
- | LD H, A ; Establecemos el " | + | ld h, a |
- | ; Calculo de la parte baja de la direccion: | + | |
- | LD A, C ; A = coordenada X | + | ld a, c |
- | RRA | + | rra |
- | RRA | + | rra |
- | RRA ; Rotamos para obtener CCCCCb | + | |
- | AND 31 | + | and %00011111 |
- | LD L, A ; L = 000CCCCCb | + | ld l, a |
- | LD A, B ; Recuperamos de nuevo Y | + | ld a, b |
- | RLA ; Rotamos para obtener NNN | + | |
- | RLA | + | rla |
- | AND 224 ; A = A AND 11100000b | + | and %11100000 |
- | OR L | + | or l ; L = NNNCCCCC |
- | LD L, A ; Establecemos el " | + | ld l, a |
- | ; Finalmente, calcular posicion relativa del pixel: | + | |
- | LD A, C | + | ld a, c |
- | AND 7 | + | and %00000111 |
- | ; A = 00000PPP | + | |
- | RET | + | ret |
</ | </ | ||
- | 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 965: | 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 " | 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 " | ||
+ | Si queremos convertir la //posición del pixel// en una //máscara de pixel// (por ejemplo, convertir '' | ||
+ | |||
+ | <code z80> | ||
+ | and %00000111 | ||
+ | ; 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 | ||
+ | jr z, getpixoff_norotate | ||
+ | ; 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 | ||
+ | </ | ||
\\ | \\ | ||
Línea 976: | 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 983: | Línea 991: | ||
\\ | \\ | ||
+ | |< 30% >| | ||
^ Valor de A ^ Máscara de pixel ^ | ^ Valor de A ^ Máscara de pixel ^ | ||
| 0 | 10000000b | | | 0 | 10000000b | | ||
Línea 993: | 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 B, A ; Cargamos A (posicion de pixel) en B | + | ld b, a |
- | INC B ; Incrementamos B (para pasadas del bucle) | + | inc b |
- | XOR A ; A = 0 | + | xor a |
- | SCF ; Set Carry Flag (A=0, CF=1) | + | |
pix_rotate_bit: | pix_rotate_bit: | ||
- | | + | rra |
- | DJNZ pix_rotate_bit | + | |
</ | </ | ||
- | 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 " | + | La rutina pone A a cero y establece el Carry Flag a 1, por lo que la primera ejecución de '' |
\\ | \\ | ||
- | {{ : | + | {{ : |
\\ | \\ | ||
Línea 1023: | Línea 1032: | ||
; | ; | ||
Relative_to_Mask: | Relative_to_Mask: | ||
- | LD B, A ; Cargamos A (posicion de pixel) en B | + | ld b, a |
- | INC B ; Incrementamos B (para pasadas del bucle) | + | inc b |
- | XOR A ; A = 0 | + | xor a |
- | SCF ; Set Carry Flag (A=0, CF=1) | + | |
pix_rotate_bit: | pix_rotate_bit: | ||
- | | + | rra |
- | DJNZ pix_rotate_bit | + | |
- | RET | + | ret |
</ | </ | ||
Línea 1038: | 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) |
- | | + | |
- | 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 A | + | cpl 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 |
</ | </ | ||
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. | ||
- | + | ||
| | ||
Línea 1064: | Línea 1073: | ||
</ | </ | ||
- | Si ejecutaramos un simple | + | Si ejecutaramos un simple |
< | < | ||
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 | + | OPERACION (HL)=A |
Resultado en (16384) | Resultado en (16384) | ||
</ | </ | ||
- | | + | |
< | < | ||
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 | + | OPERACION (HL)=A |
Resultado en (16384) | Resultado en (16384) | ||
</ | </ | ||
- | 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 '' |
< | < | ||
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 | + | OPERACION (HL)=A |
Resultado en (16384) | Resultado en (16384) | ||
</ | </ | ||
Línea 1099: | Línea 1108: | ||
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
- | 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 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? |
- | 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 '' |
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 " | 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 " | ||
- | Por ese motivo, no hemos agregado esta pequeña porción de código a la rutina de Get_Pixel_Offset, | + | Por ese motivo, no hemos agregado esta pequeña porción de código a la rutina de '' |
Línea 1118: | Línea 1127: | ||
==== La rutina de la ROM PIXEL-ADDRESS ==== | ==== La rutina de la ROM PIXEL-ADDRESS ==== | ||
- | | + | |
<code z80> | <code z80> | ||
Línea 1131: | 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 |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | $22B1 PIXEL_ADDRESS_B: | + | $22b1 PIXEL_ADDRESS_B: |
; hacia las 2 ultimas lineas de pantalla. | ; hacia las 2 ultimas lineas de pantalla. | ||
- | | + | |
- | | + | |
; 0b7b6b5b4b3b2b1. | ; 0b7b6b5b4b3b2b1. | ||
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
- | 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. |
- | | + | |
<code z80> | <code z80> | ||
- | LD A, (coord_x) | + | ld a, (coord_x) |
- | LD C, A | + | ld c, a |
- | LD A, (coord_y) | + | ld a, (coord_y) |
- | LD B, A | + | ld b, a |
- | CALL $22B1 | + | |
</ | </ | ||
Línea 1183: | Línea 1192: | ||
<code z80> | <code z80> | ||
- | | + | ; Ejemplo de uso de pixel-address (ROM) |
- | ORG 50000 | + | ORG 50000 |
- | PIXEL_ADDRESS EQU $22B1 | + | PIXEL_ADDRESS EQU $22b1 |
entrada: | entrada: | ||
- | | + | |
- | LD C, 0 ; X = 0 | + | ld c, 0 ; X = 0 |
- | LD B, 0 ; Y = 0 | + | ld b, 0 ; Y = 0 |
- | LD A, B | + | ld a, b |
- | | + | |
- | LD A, 128 ; A = 10000000b (1 pixel). | + | ld a, 128 ; A = 10000000b (1 pixel). |
- | | + | |
- | | + | |
- | LD C, 255 ; X = 255 | + | ld c, 255 ; X = 255 |
- | LD B, 191 ; Y = 191 | + | ld b, 191 ; Y = 191 |
- | LD A, B | + | ld a, b |
- | | + | |
- | LD A, 255 ; A = 11111111b (8 pixeles) | + | ld a, 255 ; A = 11111111b (8 pixeles) |
- | | + | |
- | | + | |
- | LD C, 127 ; X = 127 | + | ld c, 127 ; X = 127 |
- | LD B, 95 | + | ld b, 95 |
- | LD A, B | + | ld a, b |
- | | + | |
- | LD A, 170 | + | ld a, %10101010 |
- | | + | |
- | 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 |
</ | </ | ||
Línea 1223: | Línea 1232: | ||
\\ | \\ | ||
- | {{ : | + | {{ : |
\\ | \\ | ||
- | | + | |
<code z80> | <code z80> | ||
- | PIXEL_ADDRESS EQU $22B1 | + | PIXEL_ADDRESS EQU $22b1 |
; | ; | ||
Línea 1237: | Línea 1246: | ||
; | ; | ||
PIXEL_ADDRESS_MASK: | PIXEL_ADDRESS_MASK: | ||
- | | + | call PIXEL_ADDRESS |
- | LD B, A | + | ld b, a |
- | INC B | + | inc b |
- | XOR A | + | xor a |
- | SCF | + | |
pix_rotate_bit: | pix_rotate_bit: | ||
- | | + | rra |
- | DJNZ pix_rotate_bit | + | |
- | RET | + | ret |
</ | </ | ||
Línea 1261: | Línea 1270: | ||
< | < | ||
DIRECCION_DESTINO | DIRECCION_DESTINO | ||
- | PIXEL_EN_DIRECCION = Resto(X/8) = X AND 7 | + | PIXEL_EN_DIRECCION = Resto(X/8) = X AND %00000111 |
</ | </ | ||
Línea 1270: | Línea 1279: | ||
< | < | ||
Scanline_Offsets: | Scanline_Offsets: | ||
- | | + | |
- | 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 |
</ | </ | ||
Línea 1300: | Línea 1309: | ||
< | < | ||
Scanline_Offsets: | Scanline_Offsets: | ||
- | | + | |
- | 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 |
</ | </ | ||
Línea 1329: | Línea 1338: | ||
<code python> | <code python> | ||
- | $ cat specrows.py | + | $ cat specrows.py |
# | # | ||
Línea 1347: | Línea 1356: | ||
</ | </ | ||
- | La tabla de valores DW estaría incorporada en nuestro programa y por tanto pasaría a formar parte del " | + | La tabla de valores |
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 1357: | 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 |
- | INC L | + | inc l |
- | 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 |
- | | + | ; sucesivas direccione en DE para almacenarlas en la tabla. |
- | | + | ; Cuando se cambia de caracter, scanline o tercio, se ajusta: |
- | INC D | + | inc d |
- | LD A, D | + | ld a, d |
- | AND 7 | + | and %00000111 |
- | JR NZ, genscan_nextline | + | jr nz, genscan_nextline |
- | LD A, E | + | ld a, e |
- | ADD A, 32 | + | add a, 32 |
- | LD E, A | + | ld e, a |
- | JR C, genscan_nextline | + | jr c, genscan_nextline |
- | LD A, D | + | ld a, d |
- | SUB 8 | + | |
- | LD D, A | + | ld d, a |
- | | + | |
genscan_nextline: | genscan_nextline: | ||
- | | + | djnz genscan_loop |
- | RET | + | ret |
</ | </ | ||
Línea 1406: | Línea 1415: | ||
; | ; | ||
Get_Pixel_Offset_LUT_HR: | Get_Pixel_Offset_LUT_HR: | ||
- | LD DE, Scanline_Offsets | + | ld de, Scanline_Offsets |
- | LD L, B ; L = Y | + | ld l, b |
- | LD H, 0 | + | ld h, 0 |
- | ADD HL, HL | + | add hl, hl ; HL = HL * 2 = Y * 2 |
- | ADD HL, DE | + | add hl, de ; 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) |
- | INC L | + | inc l |
- | LD H, (HL) ; Cogemos el valor alto de la direccion en H | + | ld h, (hl) |
- | LD L, A ; HL es ahora Direccion(0, | + | ld l, a |
; Ahora sumamos la X, para lo cual calculamos CCCCC | ; Ahora sumamos la X, para lo cual calculamos CCCCC | ||
- | LD A, C ; Calculamos columna | + | ld a, c |
- | RRA | + | rra |
- | RRA | + | rra |
- | RRA ; A = A>>3 = ???CCCCCb | + | |
- | AND 31 | + | and %00011111 |
- | ADD A, L | + | add a, l ; HL = HL + C |
- | LD L, A | + | ld l, a |
- | LD A, C ; Recuperamos la coordenada (X) | + | ld a, c |
- | AND 7 ; A = Posicion relativa del pixel | + | and %00000111 |
- | RET ; HL = direccion destino | + | |
</ | </ | ||
Línea 1432: | Línea 1441: | ||
<code z80> | <code z80> | ||
- | | + | ; 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 | + | push bc |
- | 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 |
- | | + | ld (hl), a ; Imprimimos el pixel |
- | + | ||
- | loop: ; Bucle para no volver a BASIC y que | + | pop bc |
- | | + | |
+ | |||
+ | loop: ; Bucle para no volver a BASIC y que | ||
+ | | ||
+ | |||
+ | END 50000 | ||
</ | </ | ||
Línea 1460: | Línea 1471: | ||
\\ | \\ | ||
- | {{ : | + | {{ : |
\\ | \\ | ||
Línea 1466: | 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 | + | El coste de ejecución de la rutina |
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 1489: | Línea 1500: | ||
</ | </ | ||
- | El paso de una tabla a otra se realizará incrementando o decrementando la parte alta del registro de 16 bits (INC H o 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 o dec h), gracias al hecho de que son 2 tablas múltiplos de 256 y consecutivas en memoria. |
| | ||
Línea 1509: | 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 |
- | INC H ; Saltamos a tabla de partes altas | + | inc h |
- | LD (HL), D | + | |
- | DEC H ; Volvemos a tabla de partes bajas | + | dec h |
- | INC L ; Siguiente valor | + | inc l |
- | | + | |
- | | + | ; Recorremos los scanlines y bloques en un bucle generando las |
- | | + | ; sucesivas direccione en DE para almacenarlas en la tabla. |
- | | + | ; Cuando se cambia de caracter, scanline o tercio, se ajusta: |
- | INC D | + | inc d |
- | LD A, D | + | ld a, d |
- | AND 7 | + | and %00000111 |
- | JR NZ, genscan_nextline | + | jr nz, genscan_nextline |
- | LD A, E | + | ld a, e |
- | ADD A, 32 | + | add a, 32 |
- | LD E, A | + | ld e, a |
- | JR C, genscan_nextline | + | jr c, genscan_nextline |
- | LD A, D | + | ld a, d |
- | SUB 8 | + | |
- | LD D, A | + | ld d, a |
- | | + | |
genscan_nextline: | genscan_nextline: | ||
- | | + | djnz genscan_loop |
- | RET | + | ret |
</ | </ | ||
Línea 1544: | 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 |
<code z80> | <code z80> | ||
- | ORG 64000 | + | |
Scanline_Offsets: | Scanline_Offsets: | ||
- | DS 448, 0 | + | |
</ | </ | ||
Línea 1556: | Línea 1567: | ||
< | < | ||
- | ; 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 $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 | + | DB 0, 0, 0, 0 |
LUT_Scanlines_HI: | LUT_Scanlines_HI: | ||
- | | + | |
- | 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 |
</ | </ | ||
Línea 1620: | Línea 1631: | ||
; | ; | ||
Get_Pixel_Offset_LUT_2: | Get_Pixel_Offset_LUT_2: | ||
- | LD A, C ; Ponemos en A la X | + | ld a, c ; Ponemos en A la X |
- | RRA | + | rra |
- | RRA | + | rra |
- | RRA ; A = ???CCCCC | + | |
- | AND 31 | + | and %00011111 |
- | LD L, B ; B = coordenada Y | + | ld l, b ; B = coordenada Y |
- | LD H, Scanline_Offsets/ | + | ld h, Scanline_Offsets/ |
- | ADD A, (HL) | + | add a, (hl) |
- | 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 L, A ; cargamos en L la parte baja | + | ld l, a ; cargamos en L la parte baja |
- | LD A, C ; Recuperamos la coordenada (X) | + | ld a, c ; Recuperamos la coordenada (X) |
- | AND 7 ; A = Posicion relativa del pixel | + | and %00000111 |
- | RET | + | ret |
</ | </ | ||
- | El coste de ejecución de esta rutina es de 77 t-estados, incluyendo el RET, la conversión de " | + | El coste de ejecución de esta rutina es de 77 t-estados, incluyendo el '' |
- | + | ||
\\ | \\ | ||
Línea 1646: | Línea 1656: | ||
| | ||
- | 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" | + | 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" |
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, | 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, | ||
Línea 1652: | 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 | ^ Rutina ^ Tiempo de ejecución ^ Bytes rutina | ||
| Cálculo | 118 t-estados | 32 | Ninguno | 32 | | | Cálculo | 118 t-estados | 32 | Ninguno | 32 | | ||
Línea 1662: | Línea 1673: | ||
Los cálculos de las posiciones de píxeles en alta resolución son " | Los cálculos de las posiciones de píxeles en alta resolución son " | ||
- | 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 1681: | Línea 1692: | ||
Pixel_Izquierda_HL_Mask: | Pixel_Izquierda_HL_Mask: | ||
- | RLC A | + | rlc a |
- | RET NC ; Si no se activa el carry flag, volvemos | + | ret nc ; Si no se activa el carry flag, volvemos |
- | DEC L | + | dec l |
- | | + | |
- | | + | |
Pixel_Derecha_HL_Mask: | Pixel_Derecha_HL_Mask: | ||
- | RRC A | + | rrc a |
- | RET NC ; Si no se activa el carry flag, volvemos | + | ret nc ; Si no se activa el carry flag, volvemos |
- | INC L | + | inc l |
- | | + | |
</ | </ | ||
Son apenas 4 instrucciones, | Son apenas 4 instrucciones, | ||
+ | |||
+ | | ||
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 1702: | Línea 1715: | ||
Pixel_Derecha_HL_Rel: | Pixel_Derecha_HL_Rel: | ||
- | INC A | + | inc a |
- | AND 7 ; Si A=8 -> A=0 | + | and %00000111 |
- | RET NZ ; Si no es cero, hemos acabado | + | ret nz ; Si no es cero, hemos acabado |
- | INC L | + | inc l |
- | | + | |
- | | + | |
Pixel_Izquierda_HL_Rel: | Pixel_Izquierda_HL_Rel: | ||
- | DEC A | + | dec a |
- | RET P | + | ret p |
- | AND 7 ; 11111111b -> 00000111b | + | and %00000111 |
- | DEC L | + | dec l |
- | | + | |
</ | </ | ||
Línea 1722: | Línea 1735: | ||
**Offset del pixel del scanline de arriba/ | **Offset del pixel del scanline de arriba/ | ||
- | | + | |
+ | |< 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 1731: | 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 HL en 256 (siguiente scanline) | + | inc h |
- | LD A, H ; Cargamos H en A | + | ld a, h |
- | AND 7 ; Si despues del INC H los 3 bits son 0, | + | and %00000111 |
- | | + | |
- | JR NZ, nofix_abajop | + | jr nz, nofix_abajop |
- | LD A, L ; Es cero, hemos pasado del scanline 7 de un | + | ld a, l |
- | | + | |
- | ADD A, 32 ; Ajustamos NNN (caracter dentro de tercio += 1) | + | add a, %00100000 |
- | LD L, A ; Ahora hacemos la comprobacion de salto de tercio | + | ld l, a |
- | JR C, nofix_abajop | + | jr c, nofix_abajop |
- | | + | |
- | LD A, H ; Si no produce acarreo, no hay que ajustar | + | ld a, h |
- | SUB 8 ; tercio, por lo que restamos el bit TT que sumo | + | sub %00001000 |
- | LD H, A ; el INC H inicial. | + | ld h, a |
nofix_abajop: | nofix_abajop: | ||
- | ; HL contiene ahora la direccion del siguiente scanline | + | |
- | | + | ; ya sea del mismo caracter o el scanline 0 del siguiente. |
</ | </ | ||
Línea 1756: | Línea 1770: | ||
<code z80> | <code z80> | ||
- | LD A, H | + | ld a, h |
- | AND 7 ; Comprobamos scanline | + | and %00000111 |
- | JR Z, Anterior_SL_DEC | + | jr z, Anterior_SL_DEC |
- | DEC H ; No es cero, basta HL = HL - 256 | + | dec h |
- | RET ; Hemos acabado (solo INC H). | + | |
- | Anterior_SL_DEC: | + | Anterior_SL_DEC: |
- | DEC H ; Decrementamos H | + | dec h |
- | LD A, L ; Ajustamos NNN (caracter en tercio -=1) | + | ld a, l |
- | SUB 32 | + | sub %00100000 |
- | LD L, A | + | ld l, a |
- | RET C ; Si se produjo carry, no hay que ajustar | + | ret c |
- | LD A, H ; Se produjo carry, ajustamos el tercio | + | ld a, h |
- | ADD A, 8 | + | add a, %00001000 |
- | LD H, A | + | ld h, a |
</ | </ | ||
- | | + | |
Línea 1787: | Línea 1801: | ||
; | ; | ||
Siguiente_Scanline_HL: | Siguiente_Scanline_HL: | ||
- | INC H | + | inc h |
- | LD A, H | + | ld a, h |
- | AND 7 | + | and %00000111 |
- | RET NZ | + | ret nz |
- | LD A, L | + | ld a, l |
- | ADD A, 32 | + | add a, %00100000 |
- | LD L, A | + | ld l, a |
- | RET C | + | ret c |
- | LD A, H | + | ld a, h |
- | SUB 8 | + | sub %00001000 |
- | LD H, A | + | ld h, a |
- | RET ; Devolvemos en HL el valor ajustado | + | |
</ | </ | ||
- | 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 '' |
Línea 1816: | Línea 1830: | ||
; | ; | ||
Anterior_Scanline_HL: | Anterior_Scanline_HL: | ||
- | LD A, H | + | ld a, h |
- | DEC H | + | dec h |
- | AND 7 | + | and %00000111 |
- | RET NZ | + | ret nz |
- | LD A, 8 | + | ld a, %00001000 |
- | ADD A, H | + | add a, h |
- | LD H, A | + | ld h, a |
- | LD A, L | + | ld a, l |
- | SUB 32 | + | sub %00100000 |
- | LD L, A | + | ld l, a |
- | RET NC | + | ret nc |
- | LD A, H | + | ld a, h |
- | SUB 8 | + | sub %00001000 |
- | LD H, A | + | ld h, a |
- | RET ; Devolvemos en HL el valor ajustado | + | |
</ | </ | ||
- | 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 " | + | La rutina '' |
- | \\ \\ | + | \\ |
===== Otras rutinas ===== | ===== Otras rutinas ===== | ||
Línea 1849: | Línea 1862: | ||
<code z80> | <code z80> | ||
; | ; | ||
- | ; Image_Offset_From_Attr | + | ; Attr_Offset_From_Image |
; | ; | ||
; Entrada: | ; Entrada: | ||
; Salida: | ; Salida: | ||
; | ; | ||
- | Image_Offset_From_Attr: | + | Attr_Offset_From_Image: |
- | LD A, H | + | ld a, h |
- | RRCA | + | rrca |
- | RRCA | + | rrca |
- | RRCA | + | rrca |
- | AND 3 | + | and %00000011 |
- | OR $58 | + | or %01011000 |
- | LD D, A | + | ld d, a |
- | LD E, L | + | ld e, l |
- | RET | + | ret |
</ | </ | ||
Línea 1870: | Línea 1883: | ||
<code z80> | <code z80> | ||
; | ; | ||
- | ; Attr_Offset_From_Image | + | ; Image_Offset_From_Attr |
; | ; | ||
; Entrada: | ; Entrada: | ||
; Salida: | ; Salida: | ||
; | ; | ||
- | Attr_Offset_From_Image: | + | Image_Offset_From_Attr: |
- | LD A, H | + | ld a, h |
- | AND 3 | + | and %00000011 |
- | RLCA | + | rlca |
- | RLCA | + | rlca |
- | RLCA | + | rlca |
- | OR $40 | + | or %01000000 |
- | LD D, A | + | ld d, a |
- | LD E, L | + | ld e, l |
- | RET | + | ret |
</ | </ | ||
Línea 1899: | Línea 1912: | ||
; | ; | ||
Get_Char_Data: | Get_Char_Data: | ||
- | LD A, B | + | ld a, b |
- | AND $18 | + | and %00011000 |
- | LD H, A | + | ld h, a |
- | SET 6, H | + | |
- | RRCA | + | rrca |
- | RRCA | + | rrca |
- | RRCA | + | rrca |
- | OR $58 | + | or %01011000 |
- | LD D, A | + | ld d, a |
- | LD A, B | + | ld a, b |
- | AND 7 | + | and %00000111 |
- | RRCA | + | rrca |
- | RRCA | + | rrca |
- | RRCA | + | rrca |
- | ADD A, C | + | add a, c |
- | LD L, A | + | ld l, a |
- | LD E, A | + | ld e, a |
- | LD A, (DE) | + | ld a, (de) |
- | RET | + | ret |
</ | </ | ||
| | ||
+ | \\ | ||
+ | ===== 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 " | ||
+ | |||
+ | \\ //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 | ||
+ | or %01000000 | ||
+ | ld h, a ; Store in H | ||
+ | ld a, b ; Calculate Y7,Y6 | ||
+ | rra ; Shift to position | ||
+ | rra | ||
+ | rra | ||
+ | and %00011000 | ||
+ | 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 | ||
+ | ld l, a ; Store in L | ||
+ | ld a, c ; Calculate X4, | ||
+ | rra ; Shift into position | ||
+ | rra | ||
+ | rra | ||
+ | and %00011111 | ||
+ | or l ; OR with Y5,Y4,Y3 | ||
+ | ld l, a ; Store in L | ||
+ | ret | ||
+ | </ | ||
+ | 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 | ||
+ | or %01000000 | ||
+ | ld h,a ; store in h | ||
+ | ld a,b ; get bits Y7, Y6 | ||
+ | rra ; move them into place | ||
+ | rra | ||
+ | rra | ||
+ | and %00011000 | ||
+ | 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 | ||
+ | ld l,a ; store in l | ||
+ | ld a,c | ||
+ | and %00011111 | ||
+ | or l ; a = Y5 Y4 Y3 X4 X3 X2 X1 | ||
+ | ld l,a ; calculation of l is complete | ||
+ | ret | ||
+ | </ | ||
+ | |||
+ | Utilizando tablas, en esta misma web podemos ver las siguientes 2 aproximaciones de //Patrick Prendergast// | ||
+ | |||
+ | <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, | ||
+ | | ||
+ | |||
+ | ; 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, | ||
+ | |||
+ | ALIGN 256 | ||
+ | tblHigh: ; (ADDR >> 8) | ||
+ | .db 64, | ||
+ | </ | ||
+ | |||
+ | 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, | ||
+ | |||
+ | \\ | ||
===== Ficheros ===== | ===== Ficheros ===== | ||
Línea 1931: | Línea 2084: | ||
* {{cursos: | * {{cursos: | ||
* {{cursos: | * {{cursos: | ||
- | + | ||
\\ | \\ | ||
===== Enlaces ===== | ===== Enlaces ===== | ||
Línea 1941: | Línea 2094: | ||
* [[http:// | * [[http:// | ||
* [[http:// | * [[http:// | ||
- | * [[http://www.arrakis.es/~ninsesabe/ | + | * [[https://www.overtakenbyevents.com/lets-talk-about-the-zx-specrum-screen-layout-part-three/|Overtaken by Events - the screen layout (III)]]. |
\\ | \\ | ||
+ | **[ [[.: | ||