cursos:ensamblador:gfx1_vram

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:gfx1_vram [08-01-2024 05:40] – [El "atributo actual temporal" ATTR-T] sromerocursos:ensamblador:gfx1_vram [21-01-2024 18:43] (actual) – [Efectos sobre la imagen y los atributos] sromero
Línea 79: Línea 79:
 ===== La videomemoria del Spectrum ===== ===== La videomemoria del Spectrum =====
  
- Cuando comenzamos nuestro curso de ensamblador vimos la organización del mapa de memoria del Spectrum, con la ROM mapeada entre $0000 y $3FFFF, y los 16 o 48KB de memoria a continuación de la misma. A partir de la dirección de memoria $4000 y hasta $7FFF nos encontramos un área de memoria etiquetada como "videoram" o "videomemoria".+ Cuando comenzamos nuestro curso de ensamblador vimos la organización del mapa de memoria del Spectrum, con la ROM mapeada entre $0000 y $3fffF, y los 16 o 48KB de memoria a continuación de la misma. A partir de la dirección de memoria $4000 y hasta $7fff nos encontramos un área de memoria etiquetada como "videoram" o "videomemoria".
  
  Este área de aprox. 7 KB de memoria es donde podemos encontrar la representación digital de la imagen que estamos viendo en el monitor y que la ULA lee regularmente para poder generar la señal de vídeo que requiere el retrazar la imagen.  Este área de aprox. 7 KB de memoria es donde podemos encontrar la representación digital de la imagen que estamos viendo en el monitor y que la ULA lee regularmente para poder generar la señal de vídeo que requiere el retrazar la imagen.
Línea 133: Línea 133:
     ORG 50000     ORG 50000
  
-    LD HL, 18514       ; Scanline 0 en Y=96 +    ld hl, 18514           ; Scanline 0 en Y=96 
-    LD A, 60           ; 00111100b +    ld a, 60               ; 00111100b 
-    LD (HL), A         ; Escribir+    ld (hl), a             ; Escribir
  
-    LD HL, 18770       ; Scanline 1 en Y=97 +    ld hl, 18770           ; Scanline 1 en Y=97 
-    LD A, 66           ; 01000010b +    ld a, 66               ; 01000010b 
-    LD (HL), A         ; Escribir+    ld (hl), a             ; Escribir
  
-    LD HL, 19026       ; Scanline 2 en Y=98 +    ld hl, 19026           ; Scanline 2 en Y=98 
-    LD A, 66           ; 01000010b +    ld a, 66               ; 01000010b 
-    LD (HL), A         ; Escribir+    ld (hl), a             ; Escribir
  
-    LD HL, 19282       ; Scanline 3 en Y=99 +    ld hl, 19282           ; Scanline 3 en Y=99 
-    LD A, 126          ; 01111110b +    ld a, 126              ; 01111110b 
-    LD (HL), A         ; Escribir+    ld (hl), a             ; Escribir
  
-    LD HL, 19538       ; Scanline 4 en Y=100 +    ld hl, 19538           ; Scanline 4 en Y=100 
-    LD A, 66           ; 01000001b +    ld a, 66               ; 01000001b 
-    LD (HL), A         ; Escribir+    ld (hl), a             ; Escribir
  
-    LD HL, 19794       ; Scanline 5 en Y=101 +    ld hl, 19794           ; Scanline 5 en Y=101 
-    LD A, 66           ; 01000001b +    ld a, 66               ; 01000001b 
-    LD (HL), A         ; Escribir+    ld (hl), a             ; Escribir
  
-    LD HL, 20050       ; Scanline 6 en Y=102 +    ld hl, 20050           ; Scanline 6 en Y=102 
-    LD A66           01000001b +    ld a%01000001        También se puede usar binario 
-    LD (HL), A         ; Escribir+    ld (hl), a             ; Escribir
  
-    LD HL, 20306       ; Scanline 7 en Y=103 +    ld hl, 20306           ; Scanline 7 en Y=103 
-    LD A, 0            ; 00000000b +    ld a, 0                ; 00000000b 
-    LD (HL), A         ; Escribir+    ld (hl), a             ; Escribir
  
-    RET +    ret 
-END 50000+ 
 +    END 50000
 </code> </code>
  
Línea 190: Línea 191:
  
 \\  \\ 
-  * **El área de imagen**: Es el área de memoria que va desde $4000 (16384) hasta $57FF (22527). Este área de memoria de 6 KB almacena la información gráfica de 256x192 píxeles, donde cada byte (de 8 bits) define el estado de 8 píxeles (en cada bit del byte se tiene el estado de un pixel, con 1=activo, 0=no activo), de forma que se puede codificar cada línea de 256 pixeles con 256/8=32 bytes. Utilizando 32 bytes por línea, podemos almacenar el estado de una pantalla completa con 32*192 = 6144 bytes = 6 KB de memoria. Por ejemplo, la celdilla de memoria 16384 contiene el estado de los 8 primeros píxeles de la línea 0 de la pantalla, desde (0,0) a (7,0).+  * **El área de imagen**: Es el área de memoria que va desde $4000 (16384) hasta $57ff (22527). Este área de memoria de 6 KB almacena la información gráfica de 256x192 píxeles, donde cada byte (de 8 bits) define el estado de 8 píxeles (en cada bit del byte se tiene el estado de un pixel, con 1=activo, 0=no activo), de forma que se puede codificar cada línea de 256 pixeles con 256/8=32 bytes. Utilizando 32 bytes por línea, podemos almacenar el estado de una pantalla completa con 32*192 = 6144 bytes = 6 KB de memoria. Por ejemplo, la celdilla de memoria 16384 contiene el estado de los 8 primeros píxeles de la línea 0 de la pantalla, desde (0,0) a (7,0).
  
-  * **El área de atributos**: Es el área de memoria comprendida entre $5800 (22528) y $5AFF (23295). Cada uno de estos 768 bytes se denomina **atributo** y almacena los colores de pixel activo (tinta) y no activo (papel) de un bloque de 8x8 de la pantalla. Por ejemplo, la celdilla de memoria 22528 almacena el atributo de color del bloque (0,0) que se corresponde con los 64 píxeles desde las posiciones de pantalla (0,0) hasta (7,7).+  * **El área de atributos**: Es el área de memoria comprendida entre $5800 (22528) y $5aff (23295). Cada uno de estos 768 bytes se denomina **atributo** y almacena los colores de pixel activo (tinta) y no activo (papel) de un bloque de 8x8 de la pantalla. Por ejemplo, la celdilla de memoria 22528 almacena el atributo de color del bloque (0,0) que se corresponde con los 64 píxeles desde las posiciones de pantalla (0,0) hasta (7,7).
 \\  \\ 
  
Línea 224: Línea 225:
 <code basic> <code basic>
 10 BORDER 1: PAPER 1: INK 7: CLS 10 BORDER 1: PAPER 1: INK 7: CLS
-20 FOR = 10 TO 70 STEP 10 : CIRCLE 128, 96, R : NEXT R+20 FOR = 10 TO 70 STEP 10 : CIRCLE 128, 96, R : NEXT R
 30 PAUSE 0 30 PAUSE 0
 40 INK 2 : PLOT 30, 30 : DRAW 220, 120 40 INK 2 : PLOT 30, 30 : DRAW 220, 120
Línea 235: Línea 236:
 \\  \\ 
  
- A continuación pulsamos una tecla y se ejecuta el "INK 2 + PLOT + DRAWque traza una línea diagonal roja. Como en una misma celdilla de 8x8 no pueden haber 2 colores de tinta diferentes, cada pixel rojo que dibuja la rutina DRAW afecta a los 8x8 píxeles del recuadro al que corresponde. Cada nuevo pixel dibujado modifica los atributos de su correspondiente bloque en baja resolución, por lo que se alteran también los colores de los círculos allá donde coincidan con la línea:+ A continuación pulsamos una tecla y se ejecuta el ''INK 2'' ''PLOT'' ''DRAW'' que traza una línea diagonal roja. Como en una misma celdilla de 8x8 no pueden haber 2 colores de tinta diferentes, cada pixel rojo que dibuja la rutina DRAW afecta a los 8x8 píxeles del recuadro al que corresponde. Cada nuevo pixel dibujado modifica los atributos de su correspondiente bloque en baja resolución, por lo que se alteran también los colores de los círculos allá donde coincidan con la línea:
  
 \\  \\ 
Línea 283: Línea 284:
 ===== Videomemoria: Área de Imagen ===== ===== Videomemoria: Área de Imagen =====
  
- El área de imagen del Spectrum es el bloque de 6144 bytes (6KB) entre 16384 ($4000) y 22527 ($57FF). Cada una de las posiciones de memoria de este área almacenan la información de imagen (estado de los píxeles) de 8 píxeles de pantalla consecutivos, donde un bit a 1 significa que el pixel está encendido y un valor de 0 que está apagado.+ El área de imagen del Spectrum es el bloque de 6144 bytes (6KB) entre 16384 ($4000) y 22527 ($57ff). Cada una de las posiciones de memoria de este área almacenan la información de imagen (estado de los píxeles) de 8 píxeles de pantalla consecutivos, donde un bit a 1 significa que el pixel está encendido y un valor de 0 que está apagado.
  
  Como veremos cuando hablemos del área de atributos, que los píxeles estén a ON o a OFF no implica que la ULA sólo dibuje los píxeles activos. Si el pixel está activo (bit a 1), la ULA lo traza en pantalla utilizando el color de tinta actual que corresponda a ese píxel mientras que un bit a 0 significa que el pixel no está encendido y que la ULA debe de dibujarlo con el color de papel actual.  Como veremos cuando hablemos del área de atributos, que los píxeles estén a ON o a OFF no implica que la ULA sólo dibuje los píxeles activos. Si el pixel está activo (bit a 1), la ULA lo traza en pantalla utilizando el color de tinta actual que corresponda a ese píxel mientras que un bit a 0 significa que el pixel no está encendido y que la ULA debe de dibujarlo con el color de papel actual.
Línea 333: Línea 334:
 <code basic> <code basic>
 10 CLS 10 CLS
-20 FOR I=0 TO 31 : POKE 16384+I, 170 : NEXT I+20 FOR i=0 TO 31 : POKE 16384+I, 170 : NEXT I
 30 PAUSE 0 30 PAUSE 0
 </code> </code>
Línea 355: Línea 356:
 <code basic> <code basic>
 10 CLS 10 CLS
-20 FOR I=0 TO 63 : POKE 16384+I, 170 : NEXT I+20 FOR i=0 TO 63 : POKE 16384+I, 170 : NEXT I
 30 PAUSE 0 30 PAUSE 0
 </code> </code>
Línea 386: Línea 387:
    * (etc...)    * (etc...)
  
- Así hasta el byte 18331 o $47FF (cuando hemos avanzado 32*8*8  = 32 bytes por 8 líneas de cada una de las 8 filas de caracteres), que contiene el estado de los 8 píxeles del bloque de baja resolución (31,7) de la pantalla.+ Así hasta el byte 18331 o $47ff (cuando hemos avanzado 32*8*8  = 32 bytes por 8 líneas de cada una de las 8 filas de caracteres), que contiene el estado de los 8 píxeles del bloque de baja resolución (31,7) de la pantalla.
  
 \\  \\ 
Línea 395: Línea 396:
 \\  \\ 
  
- ¿Qué quiere decir esto? Que los primeros 2KB de videoram (entre $4000 y $47FF) contienen la información de todos los píxeles de los 8 primeros bloques en baja resolución de pantalla, de forma que primero vienen todas las líneas horizontales 0 de cada bloque, luego todas las líneas horizontales 1 de cada bloque, líneas horizontales 2 de cada bloque, etc, hasta que se rellenan las últimas líneas horizontales (líneas 7) de los 8 primeros caracteres. Esto produce que el rellenado de los 2 primeros KB de la videoram rellene un área de pantalla entre (0,0) y (255,63), lo que se conoce como el **primer tercio** de la pantalla.+ ¿Qué quiere decir esto? Que los primeros 2KB de videoram (entre $4000 y $47ff) contienen la información de todos los píxeles de los 8 primeros bloques en baja resolución de pantalla, de forma que primero vienen todas las líneas horizontales 0 de cada bloque, luego todas las líneas horizontales 1 de cada bloque, líneas horizontales 2 de cada bloque, etc, hasta que se rellenan las últimas líneas horizontales (líneas 7) de los 8 primeros caracteres. Esto produce que el rellenado de los 2 primeros KB de la videoram rellene un área de pantalla entre (0,0) y (255,63), lo que se conoce como el **primer tercio** de la pantalla.
  
 \\  \\ 
-  * **Primer tercio**: Los 2 primeros KB de la videoram (de $4000 a $47FF) cubren los datos gráficos de los primeros 64 scanlines de la pantalla (líneas 0 a 7). +  * **Primer tercio**: Los 2 primeros KB de la videoram (de $4000 a $47ff) cubren los datos gráficos de los primeros 64 scanlines de la pantalla (líneas 0 a 7). 
-  * **Segundo tercio**: Los siguientes 2KB de la videoram (de $4800 a $4FFF) cubren los datos gráficos de los siguientes 64 scanlines de la pantalla (líneas 8 a 15). +  * **Segundo tercio**: Los siguientes 2KB de la videoram (de $4800 a $4fff) cubren los datos gráficos de los siguientes 64 scanlines de la pantalla (líneas 8 a 15). 
-  * **Tercer tercio**: Los siguientes 2KB de la videoram (de $5000 a $57FF) cubren los datos gráficos de los últimos 64 scanlines de la pantalla (líneas 16 a 23).+  * **Tercer tercio**: Los siguientes 2KB de la videoram (de $5000 a $57ff) cubren los datos gráficos de los últimos 64 scanlines de la pantalla (líneas 16 a 23).
 \\  \\ 
  
Línea 418: Línea 419:
  Con el programa de ejemplo del apartado //Explorando el área de imagen con un ejemplo// podremos comprobar experimentalmente la organización de la videomemoria y la división de la pantalla en tercios de 8 "caracteres" de 8 scanlines cada uno.  Con el programa de ejemplo del apartado //Explorando el área de imagen con un ejemplo// podremos comprobar experimentalmente la organización de la videomemoria y la división de la pantalla en tercios de 8 "caracteres" de 8 scanlines cada uno.
  
- Mientras tanto, sabiendo que entre $4000 y $57FF (6144 bytes) tenemos el área de imagen de la pantalla, donde cada byte representa el estado de 8 píxeles, podemos realizar la siguiente rutina útil que sirve para rellenar toda la pantalla con un patrón de píxeles determinado (CLS con patrón):+ Mientras tanto, sabiendo que entre $4000 y $57ff (6144 bytes) tenemos el área de imagen de la pantalla, donde cada byte representa el estado de 8 píxeles, podemos realizar la siguiente rutina útil que sirve para rellenar toda la pantalla con un patrón de píxeles determinado (CLS con patrón):
  
 <code z80> <code z80>
Línea 426: Línea 427:
 ;------------------------------------------------------- ;-------------------------------------------------------
 ClearScreen: ClearScreen:
-    PUSH HL +    push hl 
-    PUSH DE +    push de 
-    PUSH BC+    push bc
  
-    LD HL, 16384          ; HL = Inicio de la videoram +    ld hl, 16384          ; HL = Inicio de la videoram 
-    LD (HL),            ; Escribimos el patron A en (HL) +    ld (hl),            ; Escribimos el patron A en (HL) 
-    LD DE, 16385          ; Apuntamos DE a 16385 +    ld de, 16385          ; Apuntamos DE a 16385 
-    LD BC, 192*32-1       ; Copiamos 192*32-1 veces (HL) en (DE) +    ld bc, 192*32-1       ; Copiamos 192*32-1 veces (HL) en (DE) 
-    LDIR                  ; e incrementamos HL y DL. Restamos 1+    ldir                  ; e incrementamos HL y DL. Restamos 1
                           ; porque ya hemos escrito en 16384.                           ; porque ya hemos escrito en 16384.
  
-    POP BC +    pop bc 
-    POP DE +    pop de 
-    POP HL +    pop hl 
-    RET+    ret
 </code> </code>
  
  De esta forma, podemos llamar a nuestra rutina ClearScreen colocando en A el patron con el que rellenar la pantalla, que puede ser 0 para "limpiarla" o 1 para activar todos los píxeles a 1.  De esta forma, podemos llamar a nuestra rutina ClearScreen colocando en A el patron con el que rellenar la pantalla, que puede ser 0 para "limpiarla" o 1 para activar todos los píxeles a 1.
  
- Los PUSH y POP están puestos para preservar los valores de los registros, pero podemos quitarlos si vamos a llamar a esta función siempre de forma controlada y no queremos preservarlos (o se pueden preservar antes de llamarla).+ Los ''PUSH'' ''POP'' están puestos para preservar los valores de los registros, pero podemos quitarlos si vamos a llamar a esta función siempre de forma controlada y no queremos preservarlos (o se pueden preservar antes de llamarla).
  
  
Línea 466: Línea 467:
   ; Mostrando la organizacion de la videomemoria   ; Mostrando la organizacion de la videomemoria
  
-  ORG 50000+    ORG 50000 
   ; Pseudocodigo del programa:   ; Pseudocodigo del programa:
   ;   ;
Línea 478: Línea 480:
  
 Start: Start:
-    LD A, 0 +    ld a, 0 
-    CALL ClearScreen           ; Borramos la pantalla+    call ClearScreen           ; Borramos la pantalla
  
-    LD HL, 16384               ; HL apunta a la VRAM +    ld hl, 16384               ; HL apunta a la VRAM 
-    LD B, 192                  ; Repetimos para 192 lineas+    ld b, 192                  ; Repetimos para 192 lineas
  
 bucle_192_lineas: bucle_192_lineas:
-    LD D                   ; Nos guardamos el valor de D para el+    ld d                   ; Nos guardamos el valor de D para el
                                ; bucle exterior (usaremos B ahora en otro)                                ; bucle exterior (usaremos B ahora en otro)
-    LD B, 32                   ; B=32 para el bucle interior+    ld b, 32                   ; B=32 para el bucle interior
  
-    CALL Wait_For_Key          ; Esperamos que se pulse y libere tecla+    call Wait_For_Key          ; Esperamos que se pulse y libere tecla
  
-    LD A, 255                  ; 255 = 11111111b = todos los pixeles+    ld a, 255                  ; 255 = 11111111b = todos los pixeles
  
 bucle_32_bytes: bucle_32_bytes:
-    LD (HL),                 ; Almacenamos A en (HL) = 8 pixeles +    ld (hl),                 ; Almacenamos A en (HL) = 8 pixeles 
-    INC HL                     ; siguiente byte (siguientes 8 pix.) +    inc hl                     ; siguiente byte (siguientes 8 pix.) 
-    DJNZ bucle_32_bytes        ; 32 veces = 32 bytes = 1 scanline+    djnz bucle_32_bytes        ; 32 veces = 32 bytes = 1 scanline
  
-    LD B                   ; Recuperamos el B del bucle exterior+    ld b                   ; Recuperamos el B del bucle exterior
  
-    DJNZ bucle_192_lineas      ; Repetir 192 veces+    djnz bucle_192_lineas      ; Repetir 192 veces
  
-    JP Start                   ; Inicio del programa+    jp Start                   ; Inicio del programa
  
 ;----------------------------------------------------------------------- ;-----------------------------------------------------------------------
Línea 509: Línea 511:
 ;----------------------------------------------------------------------- ;-----------------------------------------------------------------------
 ClearScreen: ClearScreen:
-    LD HL, 16384 +    ld hl, 16384 
-    LD (HL), A +    ld (hl), a 
-    LD DE, 16385 +    ld de, 16385 
-    LD BC, 192*32-1 +    ld bc, 192*32-1 
-    LDIR +    ldir 
-    RET+    ret
  
-INCLUDE "utils.asm"+    INCLUDE "utils.asm"
  
-END 50000+    END 50000
 </code> </code>
  
Línea 535: Línea 537:
  Recomendamos al lector que continue la ejecución del programa hasta recorrer toda la pantalla y que trate de anticiparse mentalmente acerca de dónde se mostrará la siguiente línea antes de realizar la pulsación de teclado.  Recomendamos al lector que continue la ejecución del programa hasta recorrer toda la pantalla y que trate de anticiparse mentalmente acerca de dónde se mostrará la siguiente línea antes de realizar la pulsación de teclado.
  
- Es probable que la pauta de rellenado de la pantalla de nuestro ejemplo le resulte más que familiar al lector: efectivamente, es el mismo orden de relleno que producen las pantallas de carga de los juegos cargadas a partir de un //LOAD "" SCREEN$//. La carga de pantalla desde cinta con "LOAD "" SCREEN$no es más que la lectura desde cinta de los 6912 bytes de una pantalla completa (6144 bytes de imagen y 768 bytes de atributos) y su almacenamiento lineal en $4000.+ Es probable que la pauta de rellenado de la pantalla de nuestro ejemplo le resulte más que familiar al lector: efectivamente, es el mismo orden de relleno que producen las pantallas de carga de los juegos cargadas a partir de un ''LOAD "" SCREEN$''. La carga de pantalla desde cinta con ''LOAD "" SCREEN$'' no es más que la lectura desde cinta de los 6912 bytes de una pantalla completa (6144 bytes de imagen y 768 bytes de atributos) y su almacenamiento lineal en $4000.
  
  La lectura secuencial desde cinta y su escritura lineal en videomemoria resulta en la carga de los datos gráficos en el mismo orden de scanlines en que nuestro programa de ejemplo ha rellenado la pantalla, seguida de la carga de los atributos, que en un rápido avance (sólo 768 bytes a cargar desde cinta) dotaba a la pantalla de carga de su color.  La lectura secuencial desde cinta y su escritura lineal en videomemoria resulta en la carga de los datos gráficos en el mismo orden de scanlines en que nuestro programa de ejemplo ha rellenado la pantalla, seguida de la carga de los atributos, que en un rápido avance (sólo 768 bytes a cargar desde cinta) dotaba a la pantalla de carga de su color.
  
- Del mismo modo, un simple //SAVE "imagen" SCREEN$// //SAVE "imagen" CODE 16384, 6912// toma los 6912 bytes de la videoram y los almacena en cinta. El lector puede acudir al capítulo dedicado a //Rutinas de SAVE y LOAD// para refrescar la información acerca de la carga de datos desde cinta e inclusión de pantallas gráficas completas en sus programas.+ Del mismo modo, un simple ''SAVE "imagen" SCREEN$'' ''SAVE "imagen" CODE 16384, 6912'' toma los 6912 bytes de la videoram y los almacena en cinta. El lector puede acudir al capítulo dedicado a //Rutinas de SAVE y LOAD// para refrescar la información acerca de la carga de datos desde cinta e inclusión de pantallas gráficas completas en sus programas.
  
- Muchos programas comerciales trataban de evitar la carga de la pantalla visible scanline a scanline, para lo que cargaban los datos de SCREEN$ en un área de memoria libre y después transferían rápidamente esta pantalla a videoram con instrucciones LDIR.+ Muchos programas comerciales trataban de evitar la carga de la pantalla visible scanline a scanline, para lo que cargaban los datos de SCREEN$ en un área de memoria libre y después transferían rápidamente esta pantalla a videoram con instrucciones ''LDIR''.
  
- Este concepto, el de Pantalla Virtual, resulta muy interesante: podemos utilizar un área de memoria alta para simular que es la pantalla completa o una zona (la de juego) de la misma. Esto permitía dibujar los sprites y gráficos sobre ella (sin que el jugador viera nada de estos dibujados, puesto que dicha zona de RAM no es videoram), y volcarla regularmente sobre videoram tras un HALT. De esta forma se evita que el jugador pueda ver parpadeos en el dibujado de los sprites o la construcción de la pantalla "a trozos". La utilización de una pantalla virtual implicará el consumo de casi 7KB de memoria para almacenar nuestra "vscreen", por lo que lo normal sería sólo replicar el área de juego (evitando marcadores y demás) si pensamos utilizar esta técnica.+ Este concepto, el de Pantalla Virtual, resulta muy interesante: podemos utilizar un área de memoria alta para simular que es la pantalla completa o una zona (la de juego) de la misma. Esto permitía dibujar los sprites y gráficos sobre ella (sin que el jugador viera nada de estos dibujados, puesto que dicha zona de RAM no es videoram), y volcarla regularmente sobre videoram tras un ''HALT''. De esta forma se evita que el jugador pueda ver parpadeos en el dibujado de los sprites o la construcción de la pantalla "a trozos". La utilización de una pantalla virtual implicará el consumo de casi 7KB de memoria para almacenar nuestra "vscreen", por lo que lo normal sería sólo replicar el área de juego (evitando marcadores y demás) si pensamos utilizar esta técnica.
  
  
Línea 564: Línea 566:
 | 7 | $4700 | | 7 | $4700 |
  
- Tal y como está organizada la videoram, basta con calcular la dirección de inicio del bloque en baja resolución donde queremos trazar un carácter, imprimir los 8 píxeles que forman su scanline (con la escritura de un único byte en videomemoria), y saltar a la siguiente posición de videomemoria donde escribir. Como se puede apreciar en la tabla anterior, este salto a la siguiente línea se realiza con un simple INC del byte alto de la direccion (INC H en el caso de que estemos usando HL para escribir). De esta forma se simplifican las rutinas de trazado de caracteres y UDGs de la ROM.+ Tal y como está organizada la videoram, basta con calcular la dirección de inicio del bloque en baja resolución donde queremos trazar un carácter, imprimir los 8 píxeles que forman su scanline (con la escritura de un único byte en videomemoria), y saltar a la siguiente posición de videomemoria donde escribir. Como se puede apreciar en la tabla anterior, este salto a la siguiente línea se realiza con un simple INC del byte alto de la direccion (''inc h'' en el caso de que estemos usando HL para escribir). De esta forma se simplifican las rutinas de trazado de caracteres y UDGs de la ROM.
  
  Pensemos que los antecesores del ZX Spectrum (ZX80 y ZX81) tenían una videomemoria orientada al texto en baja resolución, y con la visión del software de la época y la potencia de los microprocesadores existentes lo normal era pensar en el Spectrum como un microordenador orientado a programar en BASIC y realizar programas "de gestión", más que pensar en él como una máquina de juegos. En este contexto, potenciar la velocidad de ejecución del trazado de texto era crucial.  Pensemos que los antecesores del ZX Spectrum (ZX80 y ZX81) tenían una videomemoria orientada al texto en baja resolución, y con la visión del software de la época y la potencia de los microprocesadores existentes lo normal era pensar en el Spectrum como un microordenador orientado a programar en BASIC y realizar programas "de gestión", más que pensar en él como una máquina de juegos. En este contexto, potenciar la velocidad de ejecución del trazado de texto era crucial.
Línea 572: Línea 574:
 ===== Videomemoria: Área de atributos ===== ===== Videomemoria: Área de atributos =====
  
- El área de atributos es el bloque de 768 bytes entre $5800 (22528) y $5AFF (23295), ambas celdillas de memoria incluídas. Cada una de las posiciones de memoria de este área almacenan la información de color (color de tinta, color de papel, brillo y flash) de un bloque de 8x8 píxeles en la pantalla.+ El área de atributos es el bloque de 768 bytes entre $5800 (22528) y $5aff (23295), ambas celdillas de memoria incluídas. Cada una de las posiciones de memoria de este área almacenan la información de color (color de tinta, color de papel, brillo y flash) de un bloque de 8x8 píxeles en la pantalla.
  
  El tamaño de 768 bytes de este área viene determinado por la resolución del sistema de color del Spectrum: Hemos dicho que el sistema gráfico dispone de una resolución de 256x192, pero el sistema de color divide la pantalla en bloques de 8x8 píxeles, lo que nos da una resolución de color de 256/8 x 192/8 = 32x24 bloques. Como la información de color de cada bloque se codifica en un único byte, para almacenar la información de color de toda una pantalla se requieren 32 x 24 x 1 = 768 bytes.  El tamaño de 768 bytes de este área viene determinado por la resolución del sistema de color del Spectrum: Hemos dicho que el sistema gráfico dispone de una resolución de 256x192, pero el sistema de color divide la pantalla en bloques de 8x8 píxeles, lo que nos da una resolución de color de 256/8 x 192/8 = 32x24 bloques. Como la información de color de cada bloque se codifica en un único byte, para almacenar la información de color de toda una pantalla se requieren 32 x 24 x 1 = 768 bytes.
Línea 587: Línea 589:
 \\  \\ 
  
- Se puede decir que el área de atributos es totalmente lineal; consta de 768 bytes que se corresponden de forma consecutiva con el estado de cada bloque y de cada fila horizontal de bloques de pantalla: Los primeros 32 bytes del área se corresponden con la primera fila horizontal de bloques, los siguientes 32 bytes con la segunda, los siguientes 32 bytes con la tercera, hasta los últimos 32 bytes, que se corresponden con los de la línea 23. El byte alojado en la última posición ($5AFF) se corresponde con el atributo del bloque (31,23).+ Se puede decir que el área de atributos es totalmente lineal; consta de 768 bytes que se corresponden de forma consecutiva con el estado de cada bloque y de cada fila horizontal de bloques de pantalla: Los primeros 32 bytes del área se corresponden con la primera fila horizontal de bloques, los siguientes 32 bytes con la segunda, los siguientes 32 bytes con la tercera, hasta los últimos 32 bytes, que se corresponden con los de la línea 23. El byte alojado en la última posición ($5aff) se corresponde con el atributo del bloque (31,23).
  
  A continuación podemos ver una tabla que muestra los inicios y fin de cada línea de atributos en pantalla:  A continuación podemos ver una tabla que muestra los inicios y fin de cada línea de atributos en pantalla:
Línea 593: Línea 595:
 |< 50% >| |< 50% >|
 ^ Línea ^ Inicio (carácter 0,N) ^ Fin (carácter 31,N) ^ ^ Línea ^ Inicio (carácter 0,N) ^ Fin (carácter 31,N) ^
-| 0 | $5800 | $581F +| 0 | $5800 | $581f 
-| 1 | $5820 | $583F +| 1 | $5820 | $583f 
-| 2 | $5860 | $585F +| 2 | $5860 | $585f 
-| 3 | $5840 | $587F +| 3 | $5840 | $587f 
-| 4 | $5880 | $589F +| 4 | $5880 | $589f 
-| 5 | $58A0 | $58BF +| 5 | $58a0 | $58bf 
-| 6 | $58C0 | $58DF +| 6 | $58c0 | $58df 
-| 7 | $58E0 | $58FF +| 7 | $58e0 | $58ff 
-| 8 | $5900 | $591F +| 8 | $5900 | $591f 
-| 9 | $5920 | $593F +| 9 | $5920 | $593f 
-| 10 | $5940 | $595F +| 10 | $5940 | $595f 
-| 11 | $5960 | $597F +| 11 | $5960 | $597f 
-| 12 | $5980 | $599F +| 12 | $5980 | $599f 
-| 13 | $59A0 | $59BF +| 13 | $59a0 | $59bf 
-| 14 | $59C0 | $59DF +| 14 | $59c0 | $59df 
-| 15 | $59E0 | $59FF +| 15 | $59e0 | $59ff 
-| 16 | $5A00 | $5A1F +| 16 | $5a00 | $5a1f 
-| 17 | $5A20 | $5A3F +| 17 | $5a20 | $5a3f 
-| 18 | $5A40 | $5A5F +| 18 | $5a40 | $5a5f 
-| 19 | $5A60 | $5A7F +| 19 | $5a60 | $5a7f 
-| 20 | $5A80 | $5A9F +| 20 | $5a80 | $5a9f 
-| 21 | $5AA0 | $5ABF +| 21 | $5aa0 | $5abf 
-| 22 | $5AC0 | $5ADF +| 22 | $5ac0 | $5adf 
-| 23 | $5AE0 | $5AFF |+| 23 | $5ae0 | $5aff |
  
  Esta organización del área de atributos es muy sencilla y permite un cálculo muy sencillo de la posición de memoria del atributo de un bloque concreto de pantalla. Es decir, podemos encontrar fácilmente la posición de memoria que almacena el atributo que corresponde a un bloque concreto en baja resolución de pantalla mediante:  Esta organización del área de atributos es muy sencilla y permite un cálculo muy sencillo de la posición de memoria del atributo de un bloque concreto de pantalla. Es decir, podemos encontrar fácilmente la posición de memoria que almacena el atributo que corresponde a un bloque concreto en baja resolución de pantalla mediante:
Línea 720: Línea 722:
  
 <code z80> <code z80>
-  ; Mostrando la organizacion de la videomemoria (atributos) +; Mostrando la organizacion de la videomemoria (atributos) 
-  ORG 50000+    ORG 50000
  
   ; Pseudocodigo del programa:   ; Pseudocodigo del programa:
Línea 734: Línea 736:
  
 Start: Start:
-    LD A, 0 +    ld a, 0 
-    CALL ClearScreen           ; Borramos la pantalla+    call ClearScreen           ; Borramos la pantalla
  
-    LD HL, 22528               ; HL apunta a la VRAM +    ld hl, 22528               ; HL apunta a la VRAM 
-    LD B, 24                   ; Repetimos para 192 lineas+    ld b, 24                   ; Repetimos para 192 lineas
  
 bucle_lineas: bucle_lineas:
-    LD D                   ; Nos guardamos el valor de D para el+    ld d                   ; Nos guardamos el valor de D para el
                                ; bucle exterior (usaremos B ahora en otro)                                ; bucle exterior (usaremos B ahora en otro)
-    LD B, 32                   ; B=32 para el bucle interior+    ld b, 32                   ; B=32 para el bucle interior
  
-    CALL Wait_For_Key          ; Esperamos que se pulse y libere tecla+    call Wait_For_Key          ; Esperamos que se pulse y libere tecla
  
-    LD A, (papel)              ; Cogemos el valor del papel +    ld a, (papel)              ; Cogemos el valor del papel 
-    INC A                      ; Lo incrementamos +    inc a                      ; Lo incrementamos 
-    LD (papel),              ; Lo guardamos de nuevo +    ld (papel),              ; Lo guardamos de nuevo 
-    CP 8                       ; Si es == 8 (>7), resetear +    cp 8                       ; Si es == 8 (>7), resetear 
-    JR NZ,no_resetear_papel+    jr nz,no_resetear_papel
  
-    LD A, 255 +    ld a, 255 
-    LD (papel),              ; Lo hemos reseteado: lo guardamos +    ld (papel),              ; Lo hemos reseteado: lo guardamos 
-    XOR A                      ; A=0+    xor a                      ; A=0
  
 no_resetear_papel: no_resetear_papel:
  
-    SLA A                      ; Desplazamos A 3 veces a la izquierda +    sla a                      ; Desplazamos A 3 veces a la izquierda 
-    SLA A                      ; para colocar el valor 0-7 en los bits +    sla a                      ; para colocar el valor 0-7 en los bits 
-    SLA A                      ; donde se debe ubicar PAPER (bits 3-5).+    sla a                      ; donde se debe ubicar PAPER (bits 3-5).
  
 bucle_32_bytes: bucle_32_bytes:
-    LD (HL),                 ; Almacenamos A en (HL) = attrib de 8x8 +    ld (hl),                 ; Almacenamos A en (HL) = attrib de 8x8 
-    INC HL                     ; siguiente byte (siguientes 8x8 pixeles.) +    inc hl                     ; siguiente byte (siguientes 8x8 pixeles.) 
-    DJNZ bucle_32_bytes        ; 32 veces = 32 bytes = 1 scanline de bloques+    djnz bucle_32_bytes        ; 32 veces = 32 bytes = 1 scanline de bloques
  
-    LD B                   ; Recuperamos el B del bucle exterior+    ld b                   ; Recuperamos el B del bucle exterior
  
-    DJNZ bucle_lineas          ; Repetir 24 veces+    djnz bucle_lineas          ; Repetir 24 veces
  
-    JP Start                   ; Inicio del programa+    jp Start                   ; Inicio del programa
  
 papel  DEFB   255              ; Valor del papel papel  DEFB   255              ; Valor del papel
- 
  
 ;----------------------------------------------------------------------- ;-----------------------------------------------------------------------
Línea 782: Línea 783:
 ;----------------------------------------------------------------------- ;-----------------------------------------------------------------------
 ClearScreen: ClearScreen:
-    LD HL, 16384 +    ld hl, 16384 
-    LD (HL), A +    ld (hl), a 
-    LD DE, 16385 +    ld de, 16385 
-    LD BC, 192*32-1 +    ld bc, 192*32-1 
-    LDIR +    ldir 
-    RET+    ret
  
-INCLUDE "utils.asm"+    INCLUDE "utils.asm"
  
-END 50000+    END 50000
 </code> </code>
  
Línea 805: Línea 806:
  
  La memoria de atributos difiere de la de imagen en cuanto a que es totalmente lineal y que cada byte representa al bloque inmediatamente siguiente. Al llegar a la esquina derecha de la pantalla, el siguiente byte se corresponde con el primero de la siguiente línea.  La memoria de atributos difiere de la de imagen en cuanto a que es totalmente lineal y que cada byte representa al bloque inmediatamente siguiente. Al llegar a la esquina derecha de la pantalla, el siguiente byte se corresponde con el primero de la siguiente línea.
- 
  
  Con esta información, nos podemos crear la siguiente rutina para establecer el valor de atributos de toda la pantalla:  Con esta información, nos podemos crear la siguiente rutina para establecer el valor de atributos de toda la pantalla:
Línea 816: Línea 816:
 ;------------------------------------------------------- ;-------------------------------------------------------
 ClearAttributes: ClearAttributes:
-    PUSH HL +    push hl 
-    PUSH DE +    push de 
-    PUSH BC+    push bc
  
-    LD HL, 22528          ; HL = Inicio del area de atributos +    ld hl, 22528          ; HL = Inicio del area de atributos 
-    LD (HL),            ; Escribimos el patron A en (HL) +    ld (hl),            ; Escribimos el patron A en (HL) 
-    LD DE, 22529          ; Apuntamos DE a 22528 +    ld de, 22529          ; Apuntamos DE a 22528 
-    LD BC, 24*32-1        ; Copiamos 767 veces (HL) en (DE) +    ld bc, 24*32-1        ; Copiamos 767 veces (HL) en (DE) 
-    LDIR                  ; e incrementamos HL y DL. Restamos 1+    ldir                  ; e incrementamos HL y DL. Restamos 1
                           ; porque ya hemos escrito en 22528.                           ; porque ya hemos escrito en 22528.
-    POP BC +    pop bc 
-    POP DE +    pop de 
-    POP HL +    pop hl 
-    RET+    ret
 </code> </code>
  
- Dado lo habitual que puede ser llamar a ClearScreen y ClearAttributes, podemos desarrollar una función ClearScreenAttributes que realice ambas funciones en una misma llamada:+ Dado lo habitual que puede ser llamar a ''ClearScreen'' ''ClearAttributes'', podemos desarrollar una función ''ClearScreenAttributes'' que realice ambas funciones en una misma llamada:
  
 <code z80> <code z80>
Línea 840: Línea 840:
 ;----------------------------------------------------------------------- ;-----------------------------------------------------------------------
 ClearScreenAttrib: ClearScreenAttrib:
-    PUSH DE +    push de 
-    PUSH BC +    push bc 
-    PUSH BC+    push bc
  
-    LD A              ; A = el atributo +    ld a              ; A = el atributo 
-    EX AFAF'            ; Nos guardamos el atributo en A' +    ex afaf             ; Nos guardamos el atributo en A' 
-    LD A              ; Cargamos en A el patron +    ld a              ; Cargamos en A el patron 
-    LD HL, 16384          ; HL = Inicio del area de imagen +    ld hl, 16384          ; HL = Inicio del area de imagen 
-    LD (HL),            ; Escribimos el valor de A en (HL) +    ld (hl),            ; Escribimos el valor de A en (HL) 
-    LD DE, 16385          ; Apuntamos DE a 16385 +    ld de, 16385          ; Apuntamos DE a 16385 
-    LD BC, 192*32-1       ; Copiamos 6142 veces (HL) en (DE) +    ld bc, 192*32-1       ; Copiamos 6142 veces (HL) en (DE) 
-    LDIR+    ldir
  
-    EX AFAF'            ; Recuperamos A (atributo) de A' +    ex afaf             ; Recuperamos A (atributo) de A' 
-    INC HL                ; Incrementamos HL y DE +    inc hl                ; Incrementamos HL y DE 
-    INC DE                ; para entrar en area de atributos +    inc de                ; para entrar en area de atributos 
-    LD (HL),            ; Almacenamos el atributo +    ld (hl),            ; Almacenamos el atributo 
-    LD BC, 24*32-1        ; Ahora copiamos 767 bytes +    ld bc, 24*32-1        ; Ahora copiamos 767 bytes 
-    LDIR+    ldir
  
-    POP BC +    pop bc 
-    POP BC +    pop bc 
-    POP DE +    pop de 
-    RET+    ret
 </code> </code>
  
Línea 874: Línea 874:
  El borde tiene un color único que la ULA utiliza para retrazar todos y cada uno de los píxeles de este marco. Podemos cambiar este color accediendo en el Z80 al puerto de la ULA que controla el borde.  El borde tiene un color único que la ULA utiliza para retrazar todos y cada uno de los píxeles de este marco. Podemos cambiar este color accediendo en el Z80 al puerto de la ULA que controla el borde.
  
- El conocido comando de BASIC "BORDERllama a la rutina de la ROM //BORDER// en $2294, la cual realiza el cambio del color del borde mediante el acceso a la ULA y además actualiza la variable del sistema BORDCR en 23624d.+ El conocido comando de BASIC ''BORDER'' llama a la rutina de la ROM ''BORDER'' en $2294, la cual realiza el cambio del color del borde mediante el acceso a la ULA y además actualiza la variable del sistema ''BORDCR'' en 23624d.
  
- Concretamente, basta con escribir un valor en el rango 0-7 en el puerto $FE (254) para que la ULA utilice ese valor desde ese instante como color del borde. Las típicas líneas "de carga" en el borde que podemos ver durante las rutinas de LOAD y SAVE son cambios del color del borde realizados rápidamente como indicadores de la carga mientras la ULA está dibujando el cuadro actual. Si se cambia el borde con la suficiente rapidez, la ULA cambiará el color con que lo está dibujando cuando todavía no ha acabado la generación del cuadro de imagen actual. El valor 0-7 representa el identificador de color a utilizar de la paleta de 8 colores de la ULA, y este valor lo almacena internamente la ULA (no el Z80), ya que requiere de acceso instantáneo a él durante la generación del vídeo.+ Concretamente, basta con escribir un valor en el rango 0-7 en el puerto $fe (254) para que la ULA utilice ese valor desde ese instante como color del borde. Las típicas líneas "de carga" en el borde que podemos ver durante las rutinas de ''LOAD'' ''SAVE'' son cambios del color del borde realizados rápidamente como indicadores de la carga mientras la ULA está dibujando el cuadro actual. Si se cambia el borde con la suficiente rapidez, la ULA cambiará el color con que lo está dibujando cuando todavía no ha acabado la generación del cuadro de imagen actual. El valor 0-7 representa el identificador de color a utilizar de la paleta de 8 colores de la ULA, y este valor lo almacena internamente la ULA (no el Z80), ya que requiere de acceso instantáneo a él durante la generación del vídeo.
  
- En el capítulo dedicado a los Puertos de Entrada / Salida pudimos ya observar un ejemplo de cambio de color del borde, que ahora vamos a modificar para separar el OUT en una función //SetBorder// propia:+ En el capítulo dedicado a los Puertos de Entrada / Salida pudimos ya observar un ejemplo de cambio de color del borde, que ahora vamos a modificar para separar el OUT en una función ''SetBorder'' propia:
  
 <code z80> <code z80>
 ; Cambio del color del borde al pulsar espacio ; Cambio del color del borde al pulsar espacio
-  ORG 50000+    ORG 50000
  
-    LD B, 6              ; 6 iteraciones, color inicial borde+    ld b, 6              ; 6 iteraciones, color inicial borde
  
 start: start:
  
 bucle: bucle:
-    LD A, $7F            ; Semifila B a ESPACIO +    ld a, $7f            ; Semifila B a ESPACIO 
-    IN A, ($FE)          ; Leemos el puerto +    in a, ($fe)          ; Leemos el puerto 
-    BIT 0,             ; Testeamos el bit 0 (ESPACIO) +    bit 0,             ; Testeamos el bit 0 (ESPACIO) 
-    JR NZ, bucle         ; Si esta a 1 (no pulsado), esperar+    jr nz, bucle         ; Si esta a 1 (no pulsado), esperar
  
-    LD A             ; A = B +    ld a             ; A = B 
-    CALL SetBorder       ; Cambiamos el color del borde+    call SetBorder       ; Cambiamos el color del borde
  
 suelta_tecla:            ; Ahora esperamos a que se suelte la tecla suelta_tecla:            ; Ahora esperamos a que se suelte la tecla
-    LD A, $7F            ; Semifila B a ESPACIO +    ld a, $7f            ; Semifila B a ESPACIO 
-    IN A, ($FE)          ; Leemos el puerto +    in a, ($fe)          ; Leemos el puerto 
-    BIT 0,             ; Testeamos el bit 0 +    bit 0,             ; Testeamos el bit 0 
-    JR Z, suelta_tecla   ; Saltamos hasta que se suelte+    jr z, suelta_tecla   ; Saltamos hasta que se suelte
  
-    DJNZ bucle           ; Repetimos "B" veces +    djnz bucle           ; Repetimos "B" veces 
-    LD B, 7 +    ld b, 7 
-    JP start             ; Y repetir+    jp start             ; Y repetir
  
 salir: salir:
-    RET+    ret
  
 ;------------------------------------------------------------ ;------------------------------------------------------------
Línea 914: Línea 914:
 ;------------------------------------------------------------ ;------------------------------------------------------------
 SetBorder: SetBorder:
-    OUT (254), A +    out ($fe), a 
-    RET+    ret
  
-  END 50000            ; Ejecucion en 50000+    END 50000            ; Ejecucion en 50000
 </code> </code>
  
Línea 926: Línea 926:
 \\  \\ 
  
- Si por algún motivo necesitaramos actualizar la variable del sistema BORDCR (porque vayamos a llamar a rutinas de la ROM que lo puedan manipular), bastará con modificar SetBorder para que almacene el valor del borde en la posición de memoria (23624) colocando primero el valor 0-7 en la posición de bits de PAPEL y estableciendo la tinta a negro si el brillo está activo:+ Si por algún motivo necesitaramos actualizar la variable del sistema ''BORDCR'' (porque vayamos a llamar a rutinas de la ROM que lo puedan manipular), bastará con modificar SetBorder para que almacene el valor del borde en la posición de memoria (23624) colocando primero el valor 0-7 en la posición de bits de papel y estableciendo la tinta a negro si el brillo está activo:
  
 <code z80> <code z80>
Línea 934: Línea 934:
 ;------------------------------------------------------------ ;------------------------------------------------------------
 SetBorder: SetBorder:
-    OUT (254),           ; Cambiamos el color del borde +    out ($fe),           ; Cambiamos el color del borde 
-    RLCA +    rlca 
-    RLCA +    rlca 
-    RLCA                   ; A = A*8 (colocar en bits PAPER) +    rlca                   ; A = A*8 (colocar en bits PAPER) 
-    BIT 5,               ; Mirar si es un color BRIGHT +    bit 5,               ; Mirar si es un color BRIGHT 
-    JR NZ, SetBorder_fin   ; No es bright -> guardarlo+    jr nz, SetBorder_fin   ; No es bright -> guardarlo
                            ; Si es bright                            ; Si es bright
-    XOR 7                  ; -> cambiar la tinta a 0+    xor %00000111          ; -> cambiar la tinta a 0
  
 SetBorder_fin: SetBorder_fin:
-    LD (23624),        ; Salvar el valor en BORDCR+    ld (23624),        ; Salvar el valor en BORDCR
  
-    RET+    ret
 </code> </code>
  
- Mantener actualizado BORDCR puede ser útil si pretendemos llamar a la rutina de la ROM BEEPER (en $03B65), ya que el puerto que se utiliza para controlar el altavoz es el mismo que el del borde (salvo que se utiliza el bit 4 del valor que se envía con OUT $FE). La rutina BEEPER carga el valor de BORDCR para, además del manipular el bit 4 del puerto, cargar los bits 0, 1 y 2 con el borde actual para que éste no cambie. Si no estuviera almacenado el valor del borde en BORDCR y BEEPER no lo incluyera en los bits 0-2 de su OUT, lo establecería en negro (000) con cada cambio del estado del speaker.+ Mantener actualizado ''BORDCR'' puede ser útil si pretendemos llamar a la rutina de la ROM ''BEEPER'' (en $03b65), ya que el puerto que se utiliza para controlar el altavoz es el mismo que el del borde (salvo que se utiliza el bit 4 del valor que se envía con OUT $fe). La rutina BEEPER carga el valor de ''BORDCR'' para, además del manipular el bit 4 del puerto, cargar los bits 0, 1 y 2 con el borde actual para que éste no cambie. Si no estuviera almacenado el valor del borde en ''BORDCR'' ''BEEPER'' no lo incluyera en los bits 0-2 de su OUT, lo establecería en negro (000) con cada cambio del estado del speaker.
  
  Finalmente, recomendamos al lector que elimine del programa anterior la necesidad de pulsar y soltar una tecla. De esta forma podrá verificar qué sucede cuando se cambia el color del borde mientras la ULA lo está dibujando:  Finalmente, recomendamos al lector que elimine del programa anterior la necesidad de pulsar y soltar una tecla. De esta forma podrá verificar qué sucede cuando se cambia el color del borde mientras la ULA lo está dibujando:
Línea 955: Línea 955:
 <code z80> <code z80>
  ; Cambio del color del borde mientras la ULA dibuja  ; Cambio del color del borde mientras la ULA dibuja
-  ORG 50000+    ORG 50000
  
-    LD B, 6              ; 6 iteraciones, color inicial borde+    ld b, 6              ; 6 iteraciones, color inicial borde
  
 start: start:
  
 bucle: bucle:
-    LD A             ; A = B +    ld a             ; A = B 
-    CALL SetBorder       ; Cambiamos el color del borde+    call SetBorder       ; Cambiamos el color del borde
  
-    DJNZ bucle           ; Repetimos "B" veces +    djnz bucle           ; Repetimos "B" veces 
-    LD B, 7 +    ld b, 7 
-    JP start             ; Y repetir+    jp start             ; Y repetir
  
 salir: salir:
-    RET+    ret 
 + 
 +    END 50000
 </code> </code>
  
Línea 982: Línea 984:
  
 <code basic> <code basic>
-10 FOR I=0 TO 7 : BORDER I : NEXT I : GOTO 10+10 FOR i=0 TO 7 : BORDER I : NEXT I : GOTO 10
 </code> </code>
  
Línea 991: Línea 993:
 \\  \\ 
  
- Cabe destacar que en esta ocasión BASIC es todavía más rápido de lo normal pues la ejecución de BORDER I acaba resultando en la llamada a la función de la ROM "BORDERque apenas tiene 12 instrucciones (parecida a nuestra SetBorder), lo que deja todavía más en evidencia la velocidad de lo que es el intérprete de BASIC en sí.+ Cabe destacar que en esta ocasión BASIC es todavía más rápido de lo normal pues la ejecución de ''BORDER I'' acaba resultando en la llamada a la función de la ROM ''BORDER'' que apenas tiene 12 instrucciones (parecida a nuestra ''SetBorder''), lo que deja todavía más en evidencia la velocidad de lo que es el intérprete de BASIC en sí.
  
 \\  \\ 
 ===== El "atributo actual temporal" ATTR-T ===== ===== El "atributo actual temporal" ATTR-T =====
  
- Ahora que conocemos el formato de una celdilla de atributo podemos hablar de la variable del sistema ATTR-T (dirección de memoria **$5C8F** o **23695**), la cual almacena el atributo actual temporal que las rutinas de la ROM del Spectrum como nuestra conocida RST 16.+ Ahora que conocemos el formato de una celdilla de atributo podemos hablar de la variable del sistema ''ATTR-T'' (dirección de memoria **$5c8f** o **23695**), la cual almacena el atributo actual temporal que las rutinas de la ROM del Spectrum como nuestra conocida rst 16.
  
- A continuación tenemos un ejemplo que imprime cadenas con diferentes atributos de color. Para ello se ha creado una rutina PrintString basada en imprimir caracteres mediante RST 16, que utiliza el valor de ATTR-T.+ A continuación tenemos un ejemplo que imprime cadenas con diferentes atributos de color. Para ello se ha creado una rutina PrintString basada en imprimir caracteres mediante ''rst 16'', que utiliza el valor de ''ATTR-T''.
  
 <code z80> <code z80>
 ; Prueba ATTR-P ; Prueba ATTR-P
-  ORG 50000+    ORG 50000
  
 Start: Start:
  
-    LD A, 1                    ; Borde azul +    ld a, 1                    ; Borde azul 
-    CALL BORDER+    call BORDER
  
-    LD A, 8+4 +    ld a, 8+4 
-    LD (CLS_COLOR), A +    ld (CLS_COLOR), a 
-    CALL CLS+    call CLS
  
-    LD A, 56                   ; Negro sobre gris +    ld a, 56                   ; Negro sobre gris 
-    LD (ATTR_T),             ; Cambiamos ATTR-T+    ld (ATTR_T),             ; Cambiamos ATTR-T
  
-    LD DE, linea1 +    ld de, linea1 
-    CALL PrintString+    call PrintString
  
-    LD A, 12                   ; Verde sobre azul +    ld a, 12                   ; Verde sobre azul 
-    LD (ATTR_T),             ; Cambiamos ATTR-T+    ld (ATTR_T),             ; Cambiamos ATTR-T
  
-    LD DE, linea2 +    ld de, linea2 
-    CALL PrintString+    call PrintString
  
-    LD A, 64+2+9               ; Atributos: m + brillo. +    ld a, 64+2+9               ; Atributos: m + brillo. 
-    LD (ATTR_T),             ; Cambiamos ATTR-T+    ld (ATTR_T),             ; Cambiamos ATTR-T
  
-    LD DE, linea2 +    ld de, linea2 
-    CALL PrintString+    call PrintString
  
-    CALL Wait_For_Key          ; Esperamos que se pulse y libere tecla+    call Wait_For_Key          ; Esperamos que se pulse y libere tecla
  
-    RET                        ; Fin del programa+    ret                        ; Fin del programa
  
-ATTR_T    EQU   $5C8F+ATTR_T    EQU   $5c8f
  
 ;------------------------------------------------------------------------- ;-------------------------------------------------------------------------
 ; Datos ; Datos
 ;------------------------------------------------------------------------- ;-------------------------------------------------------------------------
-linea1 DEFB 'Impreso con ATTR-T actual', _CR, _CR, _EOS +linea1  DEFB 'Impreso con ATTR-T actual', _CR, _CR, _EOS 
-linea2 DEFB 'Esto es una prueba', _CR,'cambiando los atributos', _CR, _CR, _EOS+linea2  DEFB 'Esto es una prueba', _CR,'cambiando los atributos', _CR, _CR, _EOS
  
-INCLUDE "utils.asm"+    INCLUDE "utils.asm"
  
-END 50000+    END 50000
 </code> </code>
  
- Con nuestra nueva rutina de PrintString trazaremos en pantalla 1 línea con los atributos actuales seguida de 2 líneas con diferentes atributos. Nótese como RST 16 entiende e interpreta en las cadenas los códigos de control como por ejemplo 13 (retorno de carro).+ Con nuestra nueva rutina de ''PrintString'' trazaremos en pantalla 1 línea con los atributos actuales seguida de 2 líneas con diferentes atributos. Nótese como rst 16 entiende e interpreta en las cadenas los códigos de control como por ejemplo 13 (retorno de carro).
  
 \\  \\ 
Línea 1059: Línea 1061:
 ===== Efectos sobre la imagen y los atributos ===== ===== Efectos sobre la imagen y los atributos =====
  
- Ahora ya conocemos la organización de la zona de imagen y atributos y sabemos (del capítulo sobre rutinas de SAVE/LOAD) cargar en ella datos gráficos desde cinta o incluir los datos gráficos en nuestro propio programa y volcarlos con instrucciones LDIR. Estamos pues en disposición de realizar pequeñas y sencillas rutinas de borrado de pantalla o de aparición de los datos en la misma de diferentes formas, como por ejemplo:+ Ahora ya conocemos la organización de la zona de imagen y atributos y sabemos (del capítulo sobre rutinas de SAVE/LOAD) cargar en ella datos gráficos desde cinta o incluir los datos gráficos en nuestro propio programa y volcarlos con instrucciones ''LDIR''. Estamos pues en disposición de realizar pequeñas y sencillas rutinas de borrado de pantalla o de aparición de los datos en la misma de diferentes formas, como por ejemplo:
  
   * Efectos de fundido de los atributos de pantalla a negro.   * Efectos de fundido de los atributos de pantalla a negro.
Línea 1078: Línea 1080:
 Start: Start:
     ; Rellenamos la VRAM de pixeles copiando 6 KB de la ROM     ; Rellenamos la VRAM de pixeles copiando 6 KB de la ROM
-    LD HL, 0 +    ld hl, 0 
-    LD DE, 16384 +    ld de, 16384 
-    LD BC, 6144 +    ld bc, 6144 
-    LDIR+    ldir
  
-    CALL Wait_For_Key +    call Wait_For_Key 
-    CALL FadeScreen+    call FadeScreen
  
-    RET+    ret
  
 ;----------------------------------------------------------------------- ;-----------------------------------------------------------------------
Línea 1092: Línea 1094:
 ;----------------------------------------------------------------------- ;-----------------------------------------------------------------------
 FadeScreen: FadeScreen:
-    PUSH AF +    push af 
-    PUSH BC +    push bc 
-    PUSH DE +    push de 
-    PUSH HL                      ; Preservamos los registros+    push hl                      ; Preservamos los registros
  
-    LD B, 9                      ; Repetiremos el bucle 9 veces +    ld b, 9                      ; Repetiremos el bucle 9 veces 
-    LD C, 0                      ; Nuestro contador de columna+    ld c, 0                      ; Nuestro contador de columna
  
 fadegfx_loop1: fadegfx_loop1:
-    LD HL, 16384                 ; Apuntamos HL a la zona de atributos +    ld hl, 16384                 ; Apuntamos HL a la zona de atributos 
-    LD DE, 6144                  ; Iteraciones bucle+    ld de, 6144                  ; Iteraciones bucle
  
 fadegfx_loop2: fadegfx_loop2:
-    LD A, (HL)                   ; Cogemos el grupo de 8 pixeles+    ld a, (hl)                   ; Cogemos el grupo de 8 pixeles
  
  
     ;-- Actuamos sobre el valor de los pixeles --     ;-- Actuamos sobre el valor de los pixeles --
-    CP 0                         ; +    cp 0                         ; 
-    JR Z, fadegfx_save           ; Si ya es cero, no hacemos nada+    jr z, fadegfx_save           ; Si ya es cero, no hacemos nada
  
-    EX AFAF'                   ; Nos guardamos el dato+    ex afaf                    ; Nos guardamos el dato
  
-    LD A                     ; Pasamos el contador a A +    ld a                     ; Pasamos el contador a A 
-    CP 15                        ; Comparamos A con 15 +    cp 15                        ; Comparamos A con 15 
-    JR NC, fadegfx_mayor15       ; Si es mayor, saltamos+    jr nc, fadegfx_mayor15       ; Si es mayor, saltamos
  
-    EX AFAF'                   ; Recuperamos en A los pixeles +    ex afaf                    ; Recuperamos en A los pixeles 
-    RLA                          ; Rotamos A a la izquierda +    rla                          ; Rotamos A a la izquierda 
-    JR fadegfx_save              ; Y guardamos el dato+    jr fadegfx_save              ; Y guardamos el dato
  
 fadegfx_mayor15: fadegfx_mayor15:
-    EX AFAF'                   ; Recuperamos en A los pixeles +    ex afaf                    ; Recuperamos en A los pixeles 
-    SRL A                        ; Rotamos A a la derecha+    srl a                        ; Rotamos A a la derecha
  
     ;-- Fin actuacion sobre el valor de los pixeles --     ;-- Fin actuacion sobre el valor de los pixeles --
Línea 1130: Línea 1132:
 fadegfx_save: fadegfx_save:
  
-    LD (HL),                   ; Almacenamos el atributo modificado +    ld (hl),                   ; Almacenamos el atributo modificado 
-    INC HL                       ; Avanzamos puntero de memoria+    inc hl                       ; Avanzamos puntero de memoria
  
     ; Incrementamos el contador y comprobamos si hay que resetearlo     ; Incrementamos el contador y comprobamos si hay que resetearlo
-    INC C +    inc c 
-    LD AC +    ld ac 
-    CP 32 +    cp 32 
-    JR NZ, fadegfx_continue+    jr nz, fadegfx_continue
  
-    LD C, 0+    ld c, 0
  
 fadegfx_continue: fadegfx_continue:
  
-    DEC DE +    dec de 
-    LD AD +    ld ad 
-    OR E +    or e 
-    JP NZ, fadegfx_loop2      ; Hasta que DE == 0+    jp nz, fadegfx_loop2      ; Hasta que DE == 0
  
-    DJNZ fadegfx_loop1        ; Repeticion 9 veces+    djnz fadegfx_loop1        ; Repeticion 9 veces
  
-    POP HL +    pop hl 
-    POP DE +    pop de 
-    POP BC +    pop bc 
-    POP AF                       ; Restauramos registros +    pop af                       ; Restauramos registros 
-    RET+    ret
  
-INCLUDE "utils.asm"+    INCLUDE "utils.asm"
  
-END 50000+    END 50000
 </code> </code>
  
Línea 1176: Línea 1178:
 ;----------------------------------------------------------------------- ;-----------------------------------------------------------------------
 FadeAttributes: FadeAttributes:
-    PUSH AF +    push af 
-    PUSH BC +    push bc 
-    PUSH DE +    push de 
-    PUSH HL                      ; Preservamos los registros+    push hl                      ; Preservamos los registros
  
-    LD B, 9                      ; Repetiremos el bucle 9 veces+    ld b, 9                      ; Repetiremos el bucle 9 veces
  
 fadescreen_loop1: fadescreen_loop1:
-    LD HL, 16384+6144            ; Apuntamos HL a la zona de atributos +    ld hl, 16384+6144            ; Apuntamos HL a la zona de atributos 
-    LD DE, 768                   ; Iteraciones bucle+    ld de, 768                   ; Iteraciones bucle
  
-    HALT +    halt 
-    HALT                         ; Ralentizamos el efecto+    halt                         ; Ralentizamos el efecto
  
 fadescreen_loop2: fadescreen_loop2:
-    LD A, (HL)                   ; Cogemos el atributo +    ld a, (hl)                   ; Cogemos el atributo 
-    AND 127                      ; Eliminamos el bit de flash +    and %01111111                ; Eliminamos el bit de flash 
-    LD CA+    ld ca
  
-    AND 7                        ; Extraemos la tinta (AND 00000111b) +    and %00000111                ; Extraemos la tinta (and 00000111b) 
-    JR Z, fadescreen_ink_zero    ; Si la tinta ya es cero, no hacemos nada+    jr z, fadescreen_ink_zero    ; Si la tinta ya es cero, no hacemos nada
  
-    DEC A                        ; Si no es cero, decrementamos su valor+    dec a                        ; Si no es cero, decrementamos su valor
  
 fadescreen_ink_zero: fadescreen_ink_zero:
  
-    EX AFAF'                   ; Nos hacemos una copia de la tinta en A' +    ex afaf                    ; Nos hacemos una copia de la tinta en A' 
-    LD A                     ; Recuperamos el atributo +    ld a                     ; Recuperamos el atributo 
-    SRA A +    sra a 
-    SRA A                        ; Pasamos los bits de paper a 0-2 +    sra a                        ; Pasamos los bits de paper a 0-2 
-    SRA A                        ; con 3 instrucciones de desplazamiento >>+    sra a                        ; con 3 instrucciones de desplazamiento >>
  
-    AND 7                        ; Eliminamos el resto de bits +    and %00000111                ; Eliminamos el resto de bits 
-    JR Z, fadescreen_paper_zero  ; Si ya es cero, no lo decrementamos+    jr z, fadescreen_paper_zero  ; Si ya es cero, no lo decrementamos
  
-    DEC A                        ; Lo decrementamos+    dec a                        ; Lo decrementamos
  
 fadescreen_paper_zero: fadescreen_paper_zero:
-    SLA A +    sla a 
-    SLA A                        ; Volvemos a color paper en bits 3-5 +    sla a                        ; Volvemos a color paper en bits 3-5 
-    SLA A                        ; Con 3 instrucciones de desplazamiento <<+    sla a                        ; Con 3 instrucciones de desplazamiento <<
  
-    LD C                     ; Guardamos el papel decrementado en A +    ld c                     ; Guardamos el papel decrementado en A 
-    EX AFAF'                   ; Recuperamos A' +    ex afaf                    ; Recuperamos A' 
-    OR C                         ; A = A OR C   PAPEL OR TINTA+    or c                         ; A = A or c   PAPEL OR TINTA
  
-    LD (HL),                   ; Almacenamos el atributo modificado +    ld (hl),                   ; Almacenamos el atributo modificado 
-    INC HL                       ; Avanzamos puntero de memoria+    inc hl                       ; Avanzamos puntero de memoria
  
-    DEC DE +    dec de 
-    LD AD +    ld ad 
-    OR E +    or e 
-    JP NZ, fadescreen_loop2      ; Hasta que DE == 0+    jp nz, fadescreen_loop2      ; Hasta que DE == 0
  
-    DJNZ fadescreen_loop1        ; Repeticion 9 veces+    djnz fadescreen_loop1        ; Repeticion 9 veces
  
-    POP HL +    pop hl 
-    POP DE +    pop de 
-    POP BC +    pop bc 
-    POP AF                       ; Restauramos registros +    pop af                       ; Restauramos registros 
-    RET+    ret
 </code> </code>
  
Línea 1244: Línea 1246:
  
  Rutinas más complejas pueden producir cortinillas y efectos mucho más vistosos. En la revista Microhobby se publicaron muchos de estos efectos de zoom, desaparición de pantalla o inversión, dentro de la sección //Trucos//.  Rutinas más complejas pueden producir cortinillas y efectos mucho más vistosos. En la revista Microhobby se publicaron muchos de estos efectos de zoom, desaparición de pantalla o inversión, dentro de la sección //Trucos//.
 +
 +Del mismo modo, el libro **//40 Best Machine code Routines for the ZX Spectrum//** ("Las 40 mejores rutinas en código máquina para el ZX Spectrum") de //John Hardman y Andrew Hewson// nos proporciona una serie de rutinas en ensamblador para realizar diferentes acciones con los píxeles y los atributos de la videoram, como por ejemplo:
 +
 +\\ 
 +   * Scrollear atributos a izquierda, derecha, arriba o abajo.
 +   * Scroll de pantalla de un carácter (8 pixels) a izquierda, derecha, arriba o abajo.
 +   * Scroll de pantalla de un pixel a izquierda, derecha, arriba o abajo.
 +   * Mezclar dos imágenes con ''OR'' o ''XOR''.
 +   * Inversión de la pantalla (píxeles a 0 se ponen a 1, y píxeles a 1 se ponen a 0).
 +   * Invertir carácter vertical y horizontalmente.
 +   * Rotar carácter 90º en sentido horario.
 +   * Alterar todos los atributos de la pantalla (los bits deseados).
 +   * Cambiar todos los atributos de la pantalla de un determinado valor por otro valor.
 +   * Rellenado de regiones cerradas (poniendo a 1 los píxeles dentro de esas regiones).
 +   * Impresión de figuras.
 +   * Copia de una zona de la pantalla en otra, ampliándola por una cantidad entera (por ejemplo, x2 o x3).
 +
  
 \\  \\ 
 ===== La Shadow VRAM de los modelos de 128K ===== ===== La Shadow VRAM de los modelos de 128K =====
  
- En el capítulo dedicado a la paginación de memoria en los modelos de 128KB se habló de la paginación de bloques de 16KB sobre el área entre $C000 y $FFFF. El bloque de 16KB que almacena la videoram (el bloque 5, o, como se le conoce técnicamente, **RAM5**) está normalmente mapeado sobre $4000.+ En el capítulo dedicado a la paginación de memoria en los modelos de 128KB se habló de la paginación de bloques de 16KB sobre el área entre $c000 y $ffff. El bloque de 16KB que almacena la videoram (el bloque 5, o, como se le conoce técnicamente, **RAM5**) está normalmente mapeado sobre $4000.
  
 \\  \\ 
Línea 1257: Línea 1276:
 \\  \\ 
  
- En los modelos de 128K, existe un segundo bloque de 16KB que podemos utilizar como VideoRAM (**Shadow VRAM**). El Z80 y la ULA nos permiten mapear **RAM7** sobre $C000-$FFFF, dejando la VideoRAM original sobre $4000. Más interesante todavía, la ULA puede visualizar el contenido de RAM7 en lugar del de RAM5 aunque no hayamos mapeado RAM7 en ningún sitio. Y recordemos que también podemos mapear la VRAM estándar (RAM5) sobre $C000, accediendo a ella a través de $C000 además de mediante $4000.+ En los modelos de 128K, existe un segundo bloque de 16KB que podemos utilizar como VideoRAM (**Shadow VRAM**). El Z80 y la ULA nos permiten mapear **RAM7** sobre $c000-$ffff, dejando la VideoRAM original sobre $4000. Más interesante todavía, la ULA puede visualizar el contenido de RAM7 en lugar del de RAM5 aunque no hayamos mapeado RAM7 en ningún sitio. Y recordemos que también podemos mapear la VRAM estándar (RAM5) sobre $c000, accediendo a ella a través de $c000 además de mediante $4000.
  
- El poder visualizar una VRAM aunque no esté mapeada y el poder mapear tanto RAM5 como RAM7 sobre $C000 nos permite organizar el código de nuestro programa para que siempre escriba sobre $C000, teniendo mapeada en $C000 la pantalla que actualmente no esté visible.+ El poder visualizar una VRAM aunque no esté mapeada y el poder mapear tanto RAM5 como RAM7 sobre $c000 nos permite organizar el código de nuestro programa para que siempre escriba sobre $c000, teniendo mapeada en $c000 la pantalla que actualmente no esté visible.
  
  La utilidad principal de esta funcionalidad es la de poder generar un cuadro de imagen o animación en una "pantalla virtual" (la pantalla shadow) que no es visible, cambiando la visualización a esta pantalla una vez compuesta la imagen actual. De esta forma es posible trabajar con una pantalla completa sin que nos alcance el haz de electrones durante su dibujado, especialmente en juegos que realicen scrolles de todo el área de imagen.  La utilidad principal de esta funcionalidad es la de poder generar un cuadro de imagen o animación en una "pantalla virtual" (la pantalla shadow) que no es visible, cambiando la visualización a esta pantalla una vez compuesta la imagen actual. De esta forma es posible trabajar con una pantalla completa sin que nos alcance el haz de electrones durante su dibujado, especialmente en juegos que realicen scrolles de todo el área de imagen.
Línea 1266: Línea 1285:
  
 \\  \\ 
-   * Mapeamos RAM7 sobre $C000.+   * Mapeamos RAM7 sobre $c000.
    * Visualizamos RAM5 (RAM7 no es visible).    * Visualizamos RAM5 (RAM7 no es visible).
-   * Trabajamos sobre $C000 (sobre RAM7). Los cambios en nuestra pantalla shadow no son visibles. +   * Trabajamos sobre $c000 (sobre RAM7). Los cambios en nuestra pantalla shadow no son visibles. 
-   * Esperamos una interrupción (mediante HALT o mediante coordinación con la ISR de la ULA).+   * Esperamos una interrupción (mediante halt o mediante coordinación con la ISR de la ULA).
    * Cambiamos la visualización a RAM7 (RAM5 deja de ser visible).    * Cambiamos la visualización a RAM7 (RAM5 deja de ser visible).
-   * Mapeamos ahora RAM5 sobre $C000+   * Mapeamos ahora RAM5 sobre $c000
-   * Trabajamos sobre $C000 (sobre RAM5). Los cambios en nuestra pantalla shadow no son visibles.+   * Trabajamos sobre $c000 (sobre RAM5). Los cambios en nuestra pantalla shadow no son visibles.
    * Repetimos el proceso.    * Repetimos el proceso.
 \\  \\ 
  
- Con este mecanismo siempre trabajamos sobre $C000 pero los cambios que realizamos sobre esta pantalla virtual no son perceptibles por el usuario. Cambiando la visualización de la VRAM a nuestra pantalla actual tras una interrupción hacemos los cambios visibles de forma inmediata, sin que el haz de electrones afecte a nuestro scroll o al dibujado de sprites. La tasa de fotogramas por segundo ya no sería de 50 (no podríamos generar 1 cuadro de imagen por interrupción) pero se evitaría un posible molesto efecto de parpadeo o cortinilla.+ Con este mecanismo siempre trabajamos sobre $c000 pero los cambios que realizamos sobre esta pantalla virtual no son perceptibles por el usuario. Cambiando la visualización de la VRAM a nuestra pantalla actual tras una interrupción hacemos los cambios visibles de forma inmediata, sin que el haz de electrones afecte a nuestro scroll o al dibujado de sprites. La tasa de fotogramas por segundo ya no sería de 50 (no podríamos generar 1 cuadro de imagen por interrupción) pero se evitaría un posible molesto efecto de parpadeo o cortinilla.
  
- La desventaja de este sistema es que utilizamos $C000-$FFFF como pantalla virtual con lo que perdemos 16KB efectivos de RAM así como la posibilidad de paginar sobre $C000. Nos quedan así 16KB de memoria (entre $8000 y $BFFF) para alojar el código de nuestro programa, los datos gráficos, textos, etc. Esto puede ser una enorme limitación según el tipo de juego o programa que estemos realizando.+ La desventaja de este sistema es que utilizamos $c000-$ffff como pantalla virtual con lo que perdemos 16KB efectivos de RAM así como la posibilidad de paginar sobre $c000. Nos quedan así 16KB de memoria (entre $8000 y $bfff) para alojar el código de nuestro programa, los datos gráficos, textos, etc. Esto puede ser una enorme limitación según el tipo de juego o programa que estemos realizando.
  
- En realidad, si diseñamos adecuadamente nuestro programa, podemos aprovechar más de 16KB, puesto que sólo necesitamos mapear RAM5 ó RAM7 en $C000 durante la generación de la pantalla virtual. Esto obliga a que los gráficos, fuentes, sprites y mapeados del juego deban estar disponibles en $8000-$BFFF, pero una vez finalizada la generación de la pantalla podemos volver a mapear RAM0 sobre $C000, volviendo a la lógica del juego que podría estar ubicada en ese bloque, junto al resto de variables, imágenes o textos usados en los menúes, efectos sonoros, músicas, etc.+ En realidad, si diseñamos adecuadamente nuestro programa, podemos aprovechar más de 16KB, puesto que sólo necesitamos mapear RAM5 ó RAM7 en $c000 durante la generación de la pantalla virtual. Esto obliga a que los gráficos, fuentes, sprites y mapeados del juego deban estar disponibles en $8000-$bfff, pero una vez finalizada la generación de la pantalla podemos volver a mapear RAM0 sobre $c000, volviendo a la lógica del juego que podría estar ubicada en ese bloque, junto al resto de variables, imágenes o textos usados en los menúes, efectos sonoros, músicas, etc.
  
- Como véis, se necesita tener muy controlada la ubicación de las diferentes rutinas y variables y diseñar el juego para que mapee la página adecuada en cada momento y salte a una rutina concreta sólo cuando la rutina a la que hace referencia un CALL esté contenida en la página mapeada.+ Como véis, se necesita tener muy controlada la ubicación de las diferentes rutinas y variables y diseñar el juego para que mapee la página adecuada en cada momento y salte a una rutina concreta sólo cuando la rutina a la que hace referencia un call esté contenida en la página mapeada.
  
- Se reseñó también, en el apartado //Particularidades del +2A/+3// la existencia de unos modos extendidos de paginación que permitirían ubicar la segunda VideoRAM (el bloque 7, o RAM7) sobre $4000, permitiendo el alternar entre la visualización de RAM5 o de RAM7 sin perder la memoria $C000-$FFFF como "Pantalla Virtual":+ Se reseñó también, en el apartado //Particularidades del +2A/+3// la existencia de unos modos extendidos de paginación que permitirían ubicar la segunda VideoRAM (el bloque 7, o RAM7) sobre $4000, permitiendo el alternar entre la visualización de RAM5 o de RAM7 sin perder la memoria $c000-$ffff como "Pantalla Virtual":
  
 \\  \\ 
Línea 1293: Línea 1312:
 \\  \\ 
  
- Como puede verse en la figura anterior, los modos Bit2 = 0, Bit1 = 1 (Bancos 4-5-6-3) y Bit2 = 1, Bit 1=1 (Bancos 4-7-6-3) del puerto $1FFD permiten paginar cualquiera de las 2 videorams (RAM5 o RAM7) sobre $4000.+ Como puede verse en la figura anterior, los modos Bit2 = 0, Bit1 = 1 (Bancos 4-5-6-3) y Bit2 = 1, Bit 1=1 (Bancos 4-7-6-3) del puerto $1ffd permiten paginar cualquiera de las 2 videorams (RAM5 o RAM7) sobre $4000.
  
- Pese a las posibilidades de "animación sin parpadeo" que proporcionan estas técnicas, la utilización de cualquiera de las dos tiene una desventaja clara además de la "pérdida" (durante el dibujado de la pantalla shadow) de los 16KB $C000-$FFFF, y es la incompatibilidad con modelos de 48K, requiriendo un modelo de 128Kb para paginar RAM7 o incluso de un +2A/+3 para el uso de la paginación extendida. Si a los 16KB de RAM5 le restamos los 7KB de pantalla nos quedan otros 9KB adicionales, pero con la particularidad de que ese bloque de memoria está "compartido" con la ULA por lo que la velocidad de lectura, escritura y ejecución efectiva de este bloque se puede ver reducida hasta en un 25%.+ Pese a las posibilidades de "animación sin parpadeo" que proporcionan estas técnicas, la utilización de cualquiera de las dos tiene una desventaja clara además de la "pérdida" (durante el dibujado de la pantalla shadow) de los 16KB $c000-$ffff, y es la incompatibilidad con modelos de 48K, requiriendo un modelo de 128Kb para paginar RAM7 o incluso de un +2A/+3 para el uso de la paginación extendida. Si a los 16KB de RAM5 le restamos los 7KB de pantalla nos quedan otros 9KB adicionales, pero con la particularidad de que ese bloque de memoria está "compartido" con la ULA por lo que la velocidad de lectura, escritura y ejecución efectiva de este bloque se puede ver reducida hasta en un 25%.
  
 \\  \\ 
  • cursos/ensamblador/gfx1_vram.1704692422.txt.gz
  • Última modificación: 08-01-2024 05:40
  • por sromero