cursos:ensamblador:gfx3_sprites_lowres

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:gfx3_sprites_lowres [18-01-2024 08:51] sromerocursos:ensamblador:gfx3_sprites_lowres [19-01-2024 08:21] (actual) sromero
Línea 125: Línea 125:
 \\  \\ 
   * El cálculo de posición en memoria de las coordenadas (c,f) en las que vamos a dibujar el Sprite.   * El cálculo de posición en memoria de las coordenadas (c,f) en las que vamos a dibujar el Sprite.
-  * El dibujado de cada scanline del sprite en pantalla, ya sea con LD/LDIR o con operaciones lógicas tipo OR/XOR.+  * El dibujado de cada scanline del sprite en pantalla, ya sea con LD/ldir o con operaciones lógicas tipo OR/XOR.
   * El avance a través del sprite para acceder a otros scanlines del mismo.   * El avance a través del sprite para acceder a otros scanlines del mismo.
   * El avance diferencial en pantalla para movernos hacia la derecha (por cada bloque de anchura del sprite), y hacia abajo (por cada scanline de cada bloque y por cada bloque de altura del sprite).   * El avance diferencial en pantalla para movernos hacia la derecha (por cada bloque de anchura del sprite), y hacia abajo (por cada scanline de cada bloque y por cada bloque de altura del sprite).
Línea 142: Línea 142:
  En este sentido, en alguna de las rutinas utilizaremos variables en memoria para alojar datos de entrada o datos temporales o intermedios. Aunque acceder a la memoria es "lenta" comparada con tener los datos guardados en registros, cuando comenzamos a manejar muchos parámetros de entrada (y de trabajo) en una rutina y además hay que realizar cálculos con ellos, es habitual que agotemos los registros disponibles, más todavía teniendo en cuenta la necesidad de realizar dichos cálculos. En muchas ocasiones se acaba realizando uso de la pila con continuos PUSHes y POPs destinados a guardar valores y recuperarlos posteriormente a realizar los cálculos o en ciertos puntos de la rutina.  En este sentido, en alguna de las rutinas utilizaremos variables en memoria para alojar datos de entrada o datos temporales o intermedios. Aunque acceder a la memoria es "lenta" comparada con tener los datos guardados en registros, cuando comenzamos a manejar muchos parámetros de entrada (y de trabajo) en una rutina y además hay que realizar cálculos con ellos, es habitual que agotemos los registros disponibles, más todavía teniendo en cuenta la necesidad de realizar dichos cálculos. En muchas ocasiones se acaba realizando uso de la pila con continuos PUSHes y POPs destinados a guardar valores y recuperarlos posteriormente a realizar los cálculos o en ciertos puntos de la rutina.
  
- Las instrucciones ''PUSH'' y ''POP'' toman 11 y 10 t-estados respectivamente, mientras que escribir o leer un valor de 8 bits en memoria (''LD (NN), A'' y ''LD A, (NN)'') requiere 13 t-estados y escribir o leer un valor de 16 bits toma 20 t-estados ''*LD (NN), rr'' y ''LD rr, (NN)'') con la excepción de ''LD (NN), HL'' que cuesta 16 t-estados.+ Las instrucciones ''PUSH'' y ''POP'' toman 11 y 10 t-estados respectivamente, mientras que escribir o leer un valor de 8 bits en memoria (''ld (NN), a'' y ''ld a, (NN)'') requiere 13 t-estados y escribir o leer un valor de 16 bits toma 20 t-estados ''ld (NN), rr'' y ''ld rr, (NN)'') con la excepción de ''ld (NN), hl'' que cuesta 16 t-estados.
  
 \\  \\ 
 |< 50% 30% 20% >| |< 50% 30% 20% >|
 ^ Instrucción ^ Tiempo en t-estados ^ ^ Instrucción ^ Tiempo en t-estados ^
-PUSH rr | 11 | +push rr | 11 | 
-PUSH IX PUSH IY | 16 | +push ix push iy | 16 | 
-POP rr | 10 | +pop rr | 10 | 
-POP IX POP IY | 14 | +pop ix pop iy | 14 | 
-LD (NN), | 13 | +ld (NN), | 13 | 
-LD A, (NN) | 13 | +ld a, (NN) | 13 | 
-LD rr, (NN) | 20 | +ld rr, (NN) | 20 | 
-LD (NN), rr | 20 | +ld (NN), rr | 20 | 
-LD (NN), HL | 16 |+ld (NN), hl | 16 |
 \\  \\ 
  
Línea 567: Línea 567:
 ===== Paso de parámetros a las rutinas ===== ===== Paso de parámetros a las rutinas =====
  
- Al programar esta rutina, y cualquiera de las que veremos en este capítulo, debemos decidir cómo pasar los parámetros de entrada a la misma, ya que en estas rutinas manejaremos bastantes parámetros y en alguno de los casos no tendremos suficientes registros libres para establecerlos antes del CALL.+ Al programar esta rutina, y cualquiera de las que veremos en este capítulo, debemos decidir cómo pasar los parámetros de entrada a la misma, ya que en estas rutinas manejaremos bastantes parámetros y en alguno de los casos no tendremos suficientes registros libres para establecerlos antes del call.
  
  En este sentido, podemos utilizar para el paso de los parámetros, las siguientes posibilidades:  En este sentido, podemos utilizar para el paso de los parámetros, las siguientes posibilidades:
Línea 664: Línea 664:
 ;    Bajar a siguiente scanline en pantalla (HL). ;    Bajar a siguiente scanline en pantalla (HL).
  
-; Si base_atributos == 0 -> RET+; Si base_atributos == 0 -> ret
 ; Calcular posicion origen de los atributos array_attr+NUM_SPRITE en HL. ; Calcular posicion origen de los atributos array_attr+NUM_SPRITE en HL.
 ; Calcular posicion destino en area de atributos en DE. ; Calcular posicion destino en area de atributos en DE.
Línea 690: Línea 690:
  La misma rutina que vamos a crear servirá para dibujar gráficos con atributos o sin atributos. Nos puede interesar el dibujado de gráficos sin atributos en juegos monocolor donde los atributos de fondo ya están establecidos en pantalla y no sea necesario re-escribirlos, ahorrando ciclos de reloj al no realizar esta tarea sin efectos en pantalla. Nótese que aunque no dibujemos los atributos de un sprite, esto no quiere decir que no tendrá color en pantalla: si no modificamos los atributos de una posición (c,f), el sprite que dibujemos en esas coordenadas adoptará los colores de tinta y papel que ya tenía esa posición de pantalla.  La misma rutina que vamos a crear servirá para dibujar gráficos con atributos o sin atributos. Nos puede interesar el dibujado de gráficos sin atributos en juegos monocolor donde los atributos de fondo ya están establecidos en pantalla y no sea necesario re-escribirlos, ahorrando ciclos de reloj al no realizar esta tarea sin efectos en pantalla. Nótese que aunque no dibujemos los atributos de un sprite, esto no quiere decir que no tendrá color en pantalla: si no modificamos los atributos de una posición (c,f), el sprite que dibujemos en esas coordenadas adoptará los colores de tinta y papel que ya tenía esa posición de pantalla.
  
- En lugar de crear 2 rutinas diferentes, una que imprima un sprite con atributos y otra que lo haga sin ellos, vamos a utilizar en este capítulo una única rutina indicando en el parámetro de la dirección de atributos si queremos dibujarlos o no. La rutina comprobará si la dirección de atributos es 0 (basta con comprobar su parte alta) y si es así, saldrá con un RET sin realizar los cálculos de atributos (dirección origen, destino, y dibujado).+ En lugar de crear 2 rutinas diferentes, una que imprima un sprite con atributos y otra que lo haga sin ellos, vamos a utilizar en este capítulo una única rutina indicando en el parámetro de la dirección de atributos si queremos dibujarlos o no. La rutina comprobará si la dirección de atributos es 0 (basta con comprobar su parte alta) y si es así, saldrá con un ret sin realizar los cálculos de atributos (dirección origen, destino, y dibujado).
  
  Utilizaremos este sistema de comprobación (DIR_ATTR==0) para evitar la necesidad de crear 2 rutinas diferentes, aunque en un juego lo normal será que adaptemos la rutina al caso concreto y exacto (SIN o CON atributos) para evitar la comprobación de DIR_ATTR=0 y cualquier otro código no necesario.  Utilizaremos este sistema de comprobación (DIR_ATTR==0) para evitar la necesidad de crear 2 rutinas diferentes, aunque en un juego lo normal será que adaptemos la rutina al caso concreto y exacto (SIN o CON atributos) para evitar la comprobación de DIR_ATTR=0 y cualquier otro código no necesario.
Línea 724: Línea 724:
  
     ; Guardamos en BC la pareja (x,y) -> B=COORD_Y y C=COORD_X     ; Guardamos en BC la pareja (x,y) -> B=COORD_Y y C=COORD_X
-    LD BC, (DS_COORD_X)+    ld bc, (DS_COORD_X)
  
     ;;; Calculamos las coordenadas destino de pantalla en DE:     ;;; Calculamos las coordenadas destino de pantalla en DE:
-    LD AB +    ld ab 
-    AND $18 +    and $18 
-    ADD A, $40 +    add a, $40 
-    LD D          ; Ya tenemos la parte alta calculada (010TT000) +    ld d          ; Ya tenemos la parte alta calculada (010TT000) 
-    LD A          ; Ahora calculamos la parte baja +    ld a          ; Ahora calculamos la parte baja 
-    AND +    and 
-    RRCA +    rrca 
-    RRCA +    rrca 
-    RRCA              ; A = NNN00000b +    rrca              ; A = NNN00000b 
-    ADD A         ; Sumamos COLUMNA -> A = NNNCCCCCb +    add a         ; Sumamos COLUMNA -> A = NNNCCCCCb 
-    LD E          ; Lo cargamos en la parte baja de la direccion+    ld e          ; Lo cargamos en la parte baja de la direccion
                       ; DE contiene ahora la direccion destino.                       ; DE contiene ahora la direccion destino.
  
Línea 743: Línea 743:
     ;;;     direccion = base_sprites + (NUM_SPRITE*8)     ;;;     direccion = base_sprites + (NUM_SPRITE*8)
  
-    LD BC, (DS_SPRITES) +    ld bc, (DS_SPRITES) 
-    LD A, (DS_NUMSPR) +    ld a, (DS_NUMSPR) 
-    LD H, 0 +    ld h, 0 
-    LD L          ; HL = DS_NUMSPR +    ld l          ; HL = DS_NUMSPR 
-    ADD HLHL        ; HL = HL * 2 +    add hlhl        ; HL = HL * 2 
-    ADD HLHL        ; HL = HL * 4 +    add hlhl        ; HL = HL * 4 
-    ADD HLHL        ; HL = HL * 8 = DS_NUMSPR * 8 +    add hlhl        ; HL = HL * 8 = DS_NUMSPR * 8 
-    ADD HLBC        ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 8)+    add hlbc        ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 8)
                       ; HL contiene la direccion de inicio en el sprite                       ; HL contiene la direccion de inicio en el sprite
  
-    EX DEHL         ; Intercambiamos DE y HL (DE=origen, HL=destino)+    ex dehl         ; Intercambiamos DE y HL (DE=origen, HL=destino)
  
     ;;; Dibujar 8 scanlines (DE) -> (HL) y bajar scanline     ;;; Dibujar 8 scanlines (DE) -> (HL) y bajar scanline
     ;;; Incrementar scanline del sprite (DE)     ;;; Incrementar scanline del sprite (DE)
  
-    LD B, 8          ; 8 scanlines -> 8 iteraciones+    ld b, 8          ; 8 scanlines -> 8 iteraciones
  
 drawsp8x8_loopLD: drawsp8x8_loopLD:
-    LD A, (DE)       ; Tomamos el dato del sprite +    ld a, (de)       ; Tomamos el dato del sprite 
-    LD (HL),       ; Establecemos el valor en videomemoria +    ld (hl),       ; Establecemos el valor en videomemoria 
-    INC DE           ; Incrementamos puntero en sprite +    inc de           ; Incrementamos puntero en sprite 
-    INC H            ; Incrementamos puntero en pantalla (scanline+=1) +    inc h            ; Incrementamos puntero en pantalla (scanline+=1) 
-    DJNZ drawsp8x8_loopLD+    djnz drawsp8x8_loopLD
  
     ;;; En este punto, los 8 scanlines del sprite estan dibujados.     ;;; En este punto, los 8 scanlines del sprite estan dibujados.
-    LD AH +    ld ah 
-    SUB 8              ; Recuperamos la posicion de memoria del +    sub 8              ; Recuperamos la posicion de memoria del 
-    LD B           ; scanline inicial donde empezamos a dibujar +    ld b           ; scanline inicial donde empezamos a dibujar 
-    LD C           ; BC = HL - 8+    ld c           ; BC = HL - 8
  
-    ;;; Considerar el dibujado de los atributos (Si DS_ATTRIBS=0 -> RET+    ;;; Considerar el dibujado de los atributos (Si DS_ATTRIBS=0 -> ret
-    LD HL, (DS_ATTRIBS)+    ld hl, (DS_ATTRIBS)
  
-    XOR A              ; A = 0 +    xor a              ; A = 0 
-    ADD A          ; A = 0 + H = H +    add a          ; A = 0 + H = H 
-    RET Z              ; Si H = 0, volver (no dibujar atributos)+    ret z              ; Si H = 0, volver (no dibujar atributos)
  
     ;;; Calcular posicion destino en area de atributos en DE.     ;;; Calcular posicion destino en area de atributos en DE.
-    LD A           ; Codigo de Get_Attr_Offset_From_Image +    ld a           ; Codigo de Get_Attr_Offset_From_Image 
-    RRCA               ; Obtenemos dir de atributo a partir de +    rrca               ; Obtenemos dir de atributo a partir de 
-    RRCA               ; dir de zona de imagen. +    rrca               ; dir de zona de imagen. 
-    RRCA               ; Nos evita volver a obtener X e Y +    rrca               ; Nos evita volver a obtener X e Y 
-    AND 3              ; y hacer el calculo completo de la +    and 3              ; y hacer el calculo completo de la 
-    OR $58             ; direccion en zona de atributos +    or $58             ; direccion en zona de atributos 
-    LD DA +    ld da 
-    LD E           ; DE tiene el offset del attr de HL+    ld e           ; DE tiene el offset del attr de HL
  
-    LD A, (DS_NUMSPR)  ; Cogemos el numero de sprite a dibujar +    ld a, (DS_NUMSPR)  ; Cogemos el numero de sprite a dibujar 
-    LD CA +    ld ca 
-    LD B, 0 +    ld b, 0 
-    ADD HLBC         ; HL = HL+DS_NUMSPR = Origen de atributo+    add hlbc         ; HL = HL+DS_NUMSPR = Origen de atributo
  
     ;;; Copiar (HL) en (DE) -> Copiar atributo de sprite a pantalla     ;;; Copiar (HL) en (DE) -> Copiar atributo de sprite a pantalla
-    LD A, (HL+    ld a, (hl
-    LD (DE),         ; Mas rapido que LDI (7+7 vs 16 t-estados) +    ld (de),         ; Mas rapido que ldi (7+7 vs 16 t-estados) 
-    RET                ; porque no necesitamos incrementar HL y DE+    ret                ; porque no necesitamos incrementar HL y DE
 </code> </code>
  
  Al respecto del código de la rutina, caben destacar las siguientes consideraciones:  Al respecto del código de la rutina, caben destacar las siguientes consideraciones:
  
-  * Nótese que en la rutina se emplean las subrutinas ''Get_Char_Offset_LR'' y ''Attr_Offset_From_Image'' con el código de las mismas embebido dentro de la rutina principal. Esto se hace con el objetivo de evitar los correspondientes CALLs RET y para poder personalizarlas (en este caso, están modificadas para devolver la dirección calculada en DE en lugar de en HL, por requerimientos del código de ''DrawSprite_8x8'').+  * Nótese que en la rutina se emplean las subrutinas ''Get_Char_Offset_LR'' y ''Attr_Offset_From_Image'' con el código de las mismas embebido dentro de la rutina principal. Esto se hace con el objetivo de evitar los correspondientes calls ret y para poder personalizarlas (en este caso, están modificadas para devolver la dirección calculada en DE en lugar de en HL, por requerimientos del código de ''DrawSprite_8x8'').
  
   * Para realizar la transferencia de datos entre el sprite (apuntado por DE) y la pantalla (apuntada por HL) hemos utilizado 2 instrucciones de transferencia LD usando A como registro intermedio en lugar de utilizar una instrucción ''LDI''. Más adelante veremos el por qué de esta elección.   * Para realizar la transferencia de datos entre el sprite (apuntado por DE) y la pantalla (apuntada por HL) hemos utilizado 2 instrucciones de transferencia LD usando A como registro intermedio en lugar de utilizar una instrucción ''LDI''. Más adelante veremos el por qué de esta elección.
  
-  * Como ya vimos en el capítulo anterior, para avanzar o retroceder el puntero HL en pantalla, en lugar de utilizar ''DEC HL'' o ''INC HL'' (6 t-estados), realizamos un ''DEC L'' o ''INC L'' (4 t-estados). Esto es posible porque dentro de un mismo scanline de pantalla no varía el valor del byte alto de la dirección. Esta pequeña optimización no podemos realizarla con el puntero de datos del Sprite porque no tenemos la certeza de que esté dentro de una página de 256 bytes y que, por lo tanto, alguno de los incrementos del puntero deba modificar la parte alta del mismo.+  * Como ya vimos en el capítulo anterior, para avanzar o retroceder el puntero HL en pantalla, en lugar de utilizar ''dec hl'' o ''inc hl'' (6 t-estados), realizamos un ''dec l'' o ''inc l'' (4 t-estados). Esto es posible porque dentro de un mismo scanline de pantalla no varía el valor del byte alto de la dirección. Esta pequeña optimización no podemos realizarla con el puntero de datos del Sprite porque no tenemos la certeza de que esté dentro de una página de 256 bytes y que, por lo tanto, alguno de los incrementos del puntero deba modificar la parte alta del mismo.
  
-  * La rutina que hemos visto, por simplicar el código, utiliza un bucle de 8 iteraciones para dibujar los 8 scanlines. Esto ahorra espacio (ocupación de la rutina) pero implica un testeo del contador y salto por cada iteración (excepto en la última). En una rutina crítica, si tenemos suficiente espacio libre, y si conocemos de antemano el número de iteraciones exacto de un bucle, lo óptimo sería **desenrollar el bucle**, es decir, repetir 8 veces el código de impresión. De este modo evitamos el ''LD B, 8'' y el ''DJNZ bucle''.+  * La rutina que hemos visto, por simplicar el código, utiliza un bucle de 8 iteraciones para dibujar los 8 scanlines. Esto ahorra espacio (ocupación de la rutina) pero implica un testeo del contador y salto por cada iteración (excepto en la última). En una rutina crítica, si tenemos suficiente espacio libre, y si conocemos de antemano el número de iteraciones exacto de un bucle, lo óptimo sería **desenrollar el bucle**, es decir, repetir 8 veces el código de impresión. De este modo evitamos el ''ld b, 8'' y el ''djnz bucle''.
  
  En el caso de nuestra rutina de ejemplo, cambiaríamos...  En el caso de nuestra rutina de ejemplo, cambiaríamos...
  
 <code z80> <code z80>
-    LD B, 8          ; 8 scanlines+    ld b, 8          ; 8 scanlines
  
 drawsp8x8_loopLD: drawsp8x8_loopLD:
-    LD A, (DE)       ; Tomamos el dato del sprite +    ld a, (de)       ; Tomamos el dato del sprite 
-    LD (HL),       ; Establecemos el valor en videomemoria +    ld (hl),       ; Establecemos el valor en videomemoria 
-    INC DE           ; Incrementamos puntero en sprite +    inc de           ; Incrementamos puntero en sprite 
-    INC H            ; Incrementamos puntero en pantalla (scanline+=1) +    inc h            ; Incrementamos puntero en pantalla (scanline+=1) 
-    DJNZ drawsp8x8_loopLD+    djnz drawsp8x8_loopLD
 </code> </code>
  
Línea 827: Línea 827:
  
 <code z80> <code z80>
-    LD A, (DE)       ; Scanline 0 +    ld a, (de)       ; Scanline 0 
-    LD (HL), A +    ld (hl), a 
-    INC DE +    inc de 
-    INC H+    inc h
  
-    LD A, (DE)       ; Scanline 1 +    ld a, (de)       ; Scanline 1 
-    LD (HL), A +    ld (hl), a 
-    INC DE +    inc de 
-    INC H+    inc h
  
-    LD A, (DE)       ; Scanline 2 +    ld a, (de)       ; Scanline 2 
-    LD (HL), A +    ld (hl), a 
-    INC DE +    inc de 
-    INC H+    inc h
  
-    LD A, (DE)       ; Scanline 3 +    ld a, (de)       ; Scanline 3 
-    LD (HL), A +    ld (hl), a 
-    INC DE +    inc de 
-    INC H+    inc h
  
-    LD A, (DE)       ; Scanline 4 +    ld a, (de)       ; Scanline 4 
-    LD (HL), A +    ld (hl), a 
-    INC DE +    inc de 
-    INC H+    inc h
  
-    LD A, (DE)       ; Scanline 5 +    ld a, (de)       ; Scanline 5 
-    LD (HL), A +    ld (hl), a 
-    INC DE +    inc de 
-    INC H+    inc h
  
-    LD A, (DE)       ; Scanline 6 +    ld a, (de)       ; Scanline 6 
-    LD (HL), A +    ld (hl), a 
-    INC DE +    inc de 
-    INC H+    inc h
  
-    LD A, (DE)       ; Scanline 7 +    ld a, (de)       ; Scanline 7 
-    LD (HL), A +    ld (hl), a 
-    INC DE +    inc de 
-    ;;;INC H         ; no es necesario el ultimo INC H+    ;;;inc h         ; no es necesario el ultimo inc h
 </code> </code>
  
- Nótese cómo al desenrollar el bucle ya no es necesario el último ''INC H'' para avanzar al siguiente scanline de pantalla. El ''INC DE'' sí que es necesario ya que tenemos que avanzar en el sprite al primero de los atributos (aunque este INC también podría realizarse después del código de comprobación de la dirección de atributo, evitando hacerlo si no queremos imprimirlos).+ Nótese cómo al desenrollar el bucle ya no es necesario el último ''inc h'' para avanzar al siguiente scanline de pantalla. El ''inc de'' sí que es necesario ya que tenemos que avanzar en el sprite al primero de los atributos (aunque este INC también podría realizarse después del código de comprobación de la dirección de atributo, evitando hacerlo si no queremos imprimirlos).
  
- Al no ser necesario el ''INC H'', en la versión desenrollada del bucle tenemos que cambiar la resta de HL - 8 por HL - 7:+ Al no ser necesario el ''inc h'', en la versión desenrollada del bucle tenemos que cambiar la resta de HL - 8 por HL - 7:
  
 <code z80> <code z80>
     ;;; En este punto, los 8 scanlines del sprite estan dibujados.     ;;; En este punto, los 8 scanlines del sprite estan dibujados.
-    LD AH +    ld ah 
-    SUB 7              ; Recuperamos la posicion de memoria del +    sub 7              ; Recuperamos la posicion de memoria del 
-    LD B           ; scanline inicial donde empezamos a dibujar +    ld b           ; scanline inicial donde empezamos a dibujar 
-    LD C           ; BC = HL - 7+    ld c           ; BC = HL - 7
 </code> </code>
 +
 + Recordemos que mediante las directivas ''REPT''/''ENDM'' de pasmo y ''REPT''/''ENDR'' de sjasmplus, podríamos escribir el bloque de 8 repeticiones con:
 + 
 +<code z80>
 +    REPT 8
 +    ld a, (de)       ; Tomamos el dato del sprite
 +    ld (hl), a       ; Establecemos el valor en videomemoria
 +    inc de           ; Incrementamos puntero en sprite
 +    inc h            ; Incrementamos puntero en pantalla (scanline+=1)
 +    ENDM
 +</code>
 +
 + O, si queremos ahorrarnos el ''inc h'' final, podemos usar un ''REPT 7'' y volver a escribir las 4 instrucciones sin el ''inc h''. Este código es mucho más legible, hace crecer menos los ficheros, pero por las diferencias de directivas entre ensambladores nos ata a uno o a otro.
  
  Lo normal es desenrollar sólo aquellas rutinas lo suficiente críticas e importantes como para compensar el mayor espacio en memoria con un menor tiempo de ejecución. En esta rutina evitaríamos la pérdida de ciclos de reloj en el establecimiento del contador, en el testeo de condición de salida y en el salto, a cambio de una mayor ocupación de espacio tras el ensamblado.  Lo normal es desenrollar sólo aquellas rutinas lo suficiente críticas e importantes como para compensar el mayor espacio en memoria con un menor tiempo de ejecución. En esta rutina evitaríamos la pérdida de ciclos de reloj en el establecimiento del contador, en el testeo de condición de salida y en el salto, a cambio de una mayor ocupación de espacio tras el ensamblado.
Línea 890: Línea 903:
     ORG 35000     ORG 35000
  
-    CALL ClearScreen_Pattern+    call ClearScreen_Pattern
  
     ; Establecemos los parametros de entrada a la rutina     ; Establecemos los parametros de entrada a la rutina
     ; Los 2 primeros se pueden establecer una unica vez     ; Los 2 primeros se pueden establecer una unica vez
-    LD HL, cara_gfx +    ld hl, cara_gfx 
-    LD (DS_SPRITES), HL +    ld (DS_SPRITES), hl 
-    LD HL, cara_attrib +    ld hl, cara_attrib 
-    LD (DS_ATTRIBS), HL +    ld (DS_ATTRIBS), hl 
-    LD A, 15 +    ld a, 15 
-    LD (DS_COORD_X), A +    ld (DS_COORD_X), a 
-    LD A, 8 +    ld a, 8 
-    LD (DS_COORD_Y), A +    ld (DS_COORD_Y), a 
-    XOR A +    xor a 
-    LD (DS_NUMSPR), A+    ld (DS_NUMSPR), a
  
-    CALL DrawSprite_8x8_LD+    call DrawSprite_8x8_LD
  
 loop: loop:
-    JR loop +    jr loop 
-    RET+    ret
  
 ; Variables que usaremos como parámetros ; Variables que usaremos como parámetros
Línea 923: Línea 936:
 ;-------------------------------------------------------------------- ;--------------------------------------------------------------------
 ClearScreen_Pattern: ClearScreen_Pattern:
-    LD B, 191                   ; Numero de lineas a rellenar+    ld b, 191                   ; Numero de lineas a rellenar
  
 cs_line_loop: cs_line_loop:
-    LD C, 0 +    ld c, 0 
-    LD AB +    ld ab 
-    LD BA +    ld ba 
-    CALL $22B1                  ; ROM (Pixel-Address)+    call $22b1                  ; ROM (Pixel-Address)
  
-    LD AB +    ld ab 
-    AND +    and 
-    JR Z, cs_es_par +    jr z, cs_es_par 
-    LD A, 170 +    ld a, 170 
-    JR cs_pintar+    jr cs_pintar
  
 cs_es_par: cs_es_par:
-    LD A, 85+    ld a, 85
  
 cs_pintar: cs_pintar:
-    LD D                    ; Salvar el contador del bucle +    ld d                    ; Salvar el contador del bucle 
-    LD B, 32                    ; Imprimir 32 bytes+    ld b, 32                    ; Imprimir 32 bytes
  
 cs_x_loop: cs_x_loop:
-    LD (HL), A +    ld (hl), a 
-    INC HL +    inc hl 
-    DJNZ cs_x_loop+    djnz cs_x_loop
  
-    LD B                    ; Recuperamos el contador externo +    ld b                    ; Recuperamos el contador externo 
-    DJNZ cs_line_loop           ; Repetimos 192 veces +    djnz cs_line_loop           ; Repetimos 192 veces 
-    RET+    ret
  
 ;-------------------------------------------------------------------- ;--------------------------------------------------------------------
Línea 994: Línea 1007:
  
 \\  \\ 
-**Transferencia por LDI vs LD+INC**+**Transferencia por ldi vs LD+INC**
 \\  \\ 
  
Línea 1005: Línea 1018:
 <code z80> <code z80>
 drawsp8x8_loop: drawsp8x8_loop:
-    LD A, (DE)         ; A = (DE) = leer dato del sprite +    ld a, (de)         ; A = (DE) = leer dato del sprite 
-    LD (HL),         ; (HL) = A = escribir dato a la pantalla +    ld (hl),         ; (HL) = A = escribir dato a la pantalla 
-    INC DE             ; Incrementamos DE (puntero sprite) +    inc de             ; Incrementamos DE (puntero sprite) 
-    INC H              ; Incrementamos scanline HL (HL+=256) +    inc h              ; Incrementamos scanline HL (HL+=256) 
-    DJNZ drawsp8x8_loop+    djnz drawsp8x8_loop
 </code> </code>
  
- Las instrucciones antes del ''DJNZ'' tienen un coste de ejecución de 7, 7, 6 y 4 t-estados respectivamente (empezando por el ''LD A, (DE)'' y acabando por el ''INC H''). Esto suma un total de 24 t-estados por cada byte transferido.+ Las instrucciones antes del ''DJNZ'' tienen un coste de ejecución de 7, 7, 6 y 4 t-estados respectivamente (empezando por el ''ld a, (de)'' y acabando por el ''inc h''). Esto suma un total de 24 t-estados por cada byte transferido.
  
  Si invertimos el uso de los punteros y utilizamos HL como puntero al Sprite (origen) y DE como puntero a pantalla (destino), el bucle anterior podría haberse reescrito de la siguiente forma:  Si invertimos el uso de los punteros y utilizamos HL como puntero al Sprite (origen) y DE como puntero a pantalla (destino), el bucle anterior podría haberse reescrito de la siguiente forma:
Línea 1018: Línea 1031:
 <code z80> <code z80>
 drawsp8x8_loop: drawsp8x8_loop:
-    LDI                ; Copia (HL) en (DE) y HL++ DE++ +    ldi                ; Copia (HL) en (DE) y HL++ DE++ 
-    INC D              ; Sumamos 256 (+1=257) +    inc d              ; Sumamos 256 (+1=257) 
-    DEC E              ; Restamos 1 (+=256) +    dec e              ; Restamos 1 (+=256) 
-    DJNZ drawsp8x8_loop+    djnz drawsp8x8_loop
 </code> </code>
  
  Aunque es un formato más compacto, el coste de ejecución es el mismo (16+4+4 = 24 t-estados).  Aunque es un formato más compacto, el coste de ejecución es el mismo (16+4+4 = 24 t-estados).
  
- Entre las 2 posibles formas de realizar la impresión (''LD''+''INC'' vs ''LDI''), utilizaremos la primera porque, como veremos a continuación, la impresión de sprites mediante operaciones lógicas o mediante máscaras no permite el uso de LDI y utilizar la primera técnica hace todas las rutinas muy similares entre sí y por lo tanto podremos aplicar en todas cualquier mejora u optimización de una forma más rápida y sencilla.+ Entre las 2 posibles formas de realizar la impresión (''LD''+''INC'' vs ''LDI''), utilizaremos la primera porque, como veremos a continuación, la impresión de sprites mediante operaciones lógicas o mediante máscaras no permite el uso de ldi y utilizar la primera técnica hace todas las rutinas muy similares entre sí y por lo tanto podremos aplicar en todas cualquier mejora u optimización de una forma más rápida y sencilla.
  
  Además, ''LDI'' decrementa el registro BC tras las transferencia, por lo que si lo utilizamos en un bucle tenemos que tener en cuenta que cada ''LDI'' puede alterar el valor de BC y por tanto del contador de iteraciones del bucle, lo cual es otro motivo para elegir ''LD''+''INC'' vs ''LDI''.  Además, ''LDI'' decrementa el registro BC tras las transferencia, por lo que si lo utilizamos en un bucle tenemos que tener en cuenta que cada ''LDI'' puede alterar el valor de BC y por tanto del contador de iteraciones del bucle, lo cual es otro motivo para elegir ''LD''+''INC'' vs ''LDI''.
Línea 1047: Línea 1060:
 Valor en Videomemoria (HL):     10101010 Valor en Videomemoria (HL):     10101010
 Valor en el sprite - reg. A:    00111110 Valor en el sprite - reg. A:    00111110
-Operación:                      LD (HL), A+Operación:                      ld (hl), a
 Resultado en VRAM:              00111110 Resultado en VRAM:              00111110
 </code> </code>
Línea 1069: Línea 1082:
  
 <code z80> <code z80>
-    LD B, 8          ; 8 scanlines -> 8 iteraciones+    ld b, 8          ; 8 scanlines -> 8 iteraciones
  
 drawsp8x8_loopLD: drawsp8x8_loopLD:
-    LD A, (DE)       ; Tomamos el dato del sprite +    ld a, (de)       ; Tomamos el dato del sprite 
-    LD (HL),       ; Establecemos el valor en videomemoria +    ld (hl),       ; Establecemos el valor en videomemoria 
-    INC DE           ; Incrementamos puntero en sprite +    inc de           ; Incrementamos puntero en sprite 
-    INC H            ; Incrementamos puntero en pantalla +    inc h            ; Incrementamos puntero en pantalla 
-    DJNZ drawsp8x8_loopLD+    djnz drawsp8x8_loopLD
 </code> </code>
  
Línea 1082: Línea 1095:
  
 <code z80> <code z80>
-    LD B, 8             ; 8 scanlines -> 8 iteraciones+    ld b, 8             ; 8 scanlines -> 8 iteraciones
  
 drawsp8x8_loop_or: drawsp8x8_loop_or:
-    LD A, (DE)          ; Tomamos el dato del sprite +    ld a, (de)          ; Tomamos el dato del sprite 
-    OR (HL)             ; NUEVO: Hacemos un OR del scanline con el fondo +    or (hl)             ; NUEVO: Hacemos un OR del scanline con el fondo 
-    LD (HL),          ; Establecemos el valor del OR en videomemoria +    ld (hl),          ; Establecemos el valor del OR en videomemoria 
-    INC DE              ; Incrementamos puntero en sprite (DE+=1) +    inc de              ; Incrementamos puntero en sprite (DE+=1) 
-    INC H               ; Incrementamos puntero en pantalla (HL+=256) +    inc h               ; Incrementamos puntero en pantalla (HL+=256) 
-    DJNZ drawsp8x8_loop_or+    djnz drawsp8x8_loop_or
 </code> </code>
  
Línea 1111: Línea 1124:
  
     ; Guardamos en BC la pareja (x,y) -> B=COORD_Y y C=COORD_X     ; Guardamos en BC la pareja (x,y) -> B=COORD_Y y C=COORD_X
-    LD BC, (DS_COORD_X)+    ld bc, (DS_COORD_X)
  
     ;;; Calculamos las coordenadas destino de pantalla en DE:     ;;; Calculamos las coordenadas destino de pantalla en DE:
-    LD AB +    ld ab 
-    AND $18 +    and $18 
-    ADD A, $40 +    add a, $40 
-    LD D          ; Ya tenemos la parte alta calculada (010TT000) +    ld d          ; Ya tenemos la parte alta calculada (010TT000) 
-    LD A          ; Ahora calculamos la parte baja +    ld a          ; Ahora calculamos la parte baja 
-    AND +    and 
-    RRCA +    rrca 
-    RRCA +    rrca 
-    RRCA              ; A = NNN00000b +    rrca              ; A = NNN00000b 
-    ADD A         ; Sumamos COLUMNA -> A = NNNCCCCCb +    add a         ; Sumamos COLUMNA -> A = NNNCCCCCb 
-    LD E          ; Lo cargamos en la parte baja de la direccion+    ld e          ; Lo cargamos en la parte baja de la direccion
                       ; DE contiene ahora la direccion destino.                       ; DE contiene ahora la direccion destino.
  
Línea 1130: Línea 1143:
     ;;;     direccion = base_sprites + (NUM_SPRITE*8)     ;;;     direccion = base_sprites + (NUM_SPRITE*8)
  
-    LD BC, (DS_SPRITES) +    ld bc, (DS_SPRITES) 
-    LD A, (DS_NUMSPR) +    ld a, (DS_NUMSPR) 
-    LD H, 0 +    ld h, 0 
-    LD L          ; HL = DS_NUMSPR +    ld l          ; HL = DS_NUMSPR 
-    ADD HLHL        ; HL = HL * 2 +    add hlhl        ; HL = HL * 2 
-    ADD HLHL        ; HL = HL * 4 +    add hlhl        ; HL = HL * 4 
-    ADD HLHL        ; HL = HL * 8 = DS_NUMSPR * 8 +    add hlhl        ; HL = HL * 8 = DS_NUMSPR * 8 
-    ADD HLBC        ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 8)+    add hlbc        ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 8)
                       ; HL contiene la direccion de inicio en el sprite                       ; HL contiene la direccion de inicio en el sprite
  
-    EX DEHL         ; Intercambiamos DE y HL (DE=origen, HL=destino)+    ex dehl         ; Intercambiamos DE y HL (DE=origen, HL=destino)
  
     ;;; Dibujar 8 scanlines (DE) -> (HL) y bajar scanline     ;;; Dibujar 8 scanlines (DE) -> (HL) y bajar scanline
     ;;; Incrementar scanline del sprite (DE)     ;;; Incrementar scanline del sprite (DE)
  
-    LD B, 8          ; 8 scanlines -> 8 iteraciones+    ld b, 8          ; 8 scanlines -> 8 iteraciones
  
 drawsp8x8_loop_or: drawsp8x8_loop_or:
-    LD A, (DE)         ; Tomamos el dato del sprite +    ld a, (de)         ; Tomamos el dato del sprite 
-    OR (HL)            ; NUEVO: Hacemos un OR del scanline con el fondo +    or (hl)            ; NUEVO: Hacemos un OR del scanline con el fondo 
-    LD (HL),         ; Establecemos el valor en videomemoria +    ld (hl),         ; Establecemos el valor en videomemoria 
-    INC DE             ; Incrementamos puntero en sprite +    inc de             ; Incrementamos puntero en sprite 
-    INC H              ; Incrementamos puntero en pantalla (scanline+=1) +    inc h              ; Incrementamos puntero en pantalla (scanline+=1) 
-    DJNZ drawsp8x8_loop_or+    djnz drawsp8x8_loop_or
  
     ;;; En este punto, los 8 scanlines del sprite estan dibujados.     ;;; En este punto, los 8 scanlines del sprite estan dibujados.
-    LD AH +    ld ah 
-    SUB 8              ; Recuperamos la posicion de memoria del +    sub 8              ; Recuperamos la posicion de memoria del 
-    LD B           ; scanline inicial donde empezamos a dibujar +    ld b           ; scanline inicial donde empezamos a dibujar 
-    LD C           ; BC = HL - 8+    ld c           ; BC = HL - 8
  
-    ;;; Considerar el dibujado de los atributos (Si DS_ATTRIBS=0 -> RET+    ;;; Considerar el dibujado de los atributos (Si DS_ATTRIBS=0 -> ret
-    LD HL, (DS_ATTRIBS)+    ld hl, (DS_ATTRIBS)
  
-    XOR A              ; A = 0 +    xor a              ; A = 0 
-    ADD A          ; A = 0 + H = H +    add a          ; A = 0 + H = H 
-    RET Z              ; Si H = 0, volver (no dibujar atributos)+    ret z              ; Si H = 0, volver (no dibujar atributos)
  
     ;;; Calcular posicion destino en area de atributos en DE.     ;;; Calcular posicion destino en area de atributos en DE.
-    LD A           ; Codigo de Get_Attr_Offset_From_Image +    ld a           ; Codigo de Get_Attr_Offset_From_Image 
-    RRCA               ; Obtenemos dir de atributo a partir de +    rrca               ; Obtenemos dir de atributo a partir de 
-    RRCA               ; dir de zona de imagen. +    rrca               ; dir de zona de imagen. 
-    RRCA               ; Nos evita volver a obtener X e Y +    rrca               ; Nos evita volver a obtener X e Y 
-    AND 3              ; y hacer el calculo completo de la +    and 3              ; y hacer el calculo completo de la 
-    OR $58             ; direccion en zona de atributos +    or $58             ; direccion en zona de atributos 
-    LD DA +    ld da 
-    LD E           ; DE tiene el offset del attr de HL+    ld e           ; DE tiene el offset del attr de HL
  
-    LD A, (DS_NUMSPR)  ; Cogemos el numero de sprite a dibujar +    ld a, (DS_NUMSPR)  ; Cogemos el numero de sprite a dibujar 
-    LD CA +    ld ca 
-    LD B, 0 +    ld b, 0 
-    ADD HLBC         ; HL = HL+DS_NUMSPR = Origen de atributo+    add hlbc         ; HL = HL+DS_NUMSPR = Origen de atributo
  
     ;;; Copiar (HL) en (DE) -> Copiar atributo de sprite a pantalla     ;;; Copiar (HL) en (DE) -> Copiar atributo de sprite a pantalla
-    LD A, (HL+    ld a, (hl
-    LD (DE),         ; Mas rapido que LDI (7+7 vs 16 t-estados) +    ld (de),         ; Mas rapido que ldi (7+7 vs 16 t-estados) 
-    RET                ; porque no necesitamos incrementar HL y DE+    ret                ; porque no necesitamos incrementar HL y DE
 </code> </code>
  
Línea 1203: Línea 1216:
 \\  \\ 
  
- ¿Qué ha ocurrido con el sprite? ¿Por qué le faltan los "ojos" y hay un pixel activo en medio de la "boca"? Sencillamente, porque mediante el OR hemos impreso el sprite respetando el valor a 1 de los píxeles del fondo cuando el mismo pixel estaba a 0 en nuestro sprite. Eso ha hecho que alrededor de nuestro "personaje" no se haya borrado el fondo, ya que los píxeles a cero de nuestro sprite se convierten en "transparentes". Por desgracia, eso también hace que los ojos del personaje sean transparentes en lugar de estar a cero. En el caso del ejemplo anterior, los "ojos" del personaje coinciden con 2 píxeles de pantalla activos por lo que la operación OR los deja a 1. Lo mismo ocurre con el pixel en el centro de la boca, que se corresponde con un pixel activo en la pantalla.+ ¿Qué ha ocurrido con el sprite? ¿Por qué le faltan los "ojos" y hay un pixel activo en medio de la "boca"? Sencillamente, porque mediante el or hemos impreso el sprite respetando el valor a 1 de los píxeles del fondo cuando el mismo pixel estaba a 0 en nuestro sprite. Eso ha hecho que alrededor de nuestro "personaje" no se haya borrado el fondo, ya que los píxeles a cero de nuestro sprite se convierten en "transparentes". Por desgracia, eso también hace que los ojos del personaje sean transparentes en lugar de estar a cero. En el caso del ejemplo anterior, los "ojos" del personaje coinciden con 2 píxeles de pantalla activos por lo que la operación or los deja a 1. Lo mismo ocurre con el pixel en el centro de la boca, que se corresponde con un pixel activo en la pantalla.
  
  En tal caso, ¿qué hacemos para imprimir nuestro sprite respetando el fondo pero que a su vez podamos disponer de zonas que no sean transparentes?  En tal caso, ¿qué hacemos para imprimir nuestro sprite respetando el fondo pero que a su vez podamos disponer de zonas que no sean transparentes?
Línea 1294: Línea 1307:
  
     ; Guardamos en BC la pareja (x,y) -> B=COORD_Y y C=COORD_X     ; Guardamos en BC la pareja (x,y) -> B=COORD_Y y C=COORD_X
-    LD BC, (DS_COORD_X)+    ld bc, (DS_COORD_X)
  
     ;;; Calculamos las coordenadas destino de pantalla en DE:     ;;; Calculamos las coordenadas destino de pantalla en DE:
-    LD AB +    ld ab 
-    AND $18 +    and $18 
-    ADD A, $40 +    add a, $40 
-    LD D          ; Ya tenemos la parte alta calculada (010TT000) +    ld d          ; Ya tenemos la parte alta calculada (010TT000) 
-    LD A          ; Ahora calculamos la parte baja +    ld a          ; Ahora calculamos la parte baja 
-    AND +    and 
-    RRCA +    rrca 
-    RRCA +    rrca 
-    RRCA              ; A = NNN00000b +    rrca              ; A = NNN00000b 
-    ADD A         ; Sumamos COLUMNA -> A = NNNCCCCCb +    add a         ; Sumamos COLUMNA -> A = NNNCCCCCb 
-    LD E          ; Lo cargamos en la parte baja de la direccion+    ld e          ; Lo cargamos en la parte baja de la direccion
                       ; DE contiene ahora la direccion destino.                       ; DE contiene ahora la direccion destino.
  
Línea 1313: Línea 1326:
     ;;;     direccion = base_sprites + (NUM_SPRITE*16)     ;;;     direccion = base_sprites + (NUM_SPRITE*16)
  
-    LD BC, (DS_SPRITES) +    ld bc, (DS_SPRITES) 
-    LD A, (DS_NUMSPR) +    ld a, (DS_NUMSPR) 
-    LD H, 0 +    ld h, 0 
-    LD L           ; HL = DS_NUMSPR +    ld l           ; HL = DS_NUMSPR 
-    ADD HLHL         ; HL = HL * 2 +    add hlhl         ; HL = HL * 2 
-    ADD HLHL         ; HL = HL * 4 +    add hlhl         ; HL = HL * 4 
-    ADD HLHL         ; HL = HL * 8 +    add hlhl         ; HL = HL * 8 
-    ADD HLHL         ; HL = HL * 16 = DS_NUMSPR * 16 +    add hlhl         ; HL = HL * 16 = DS_NUMSPR * 16 
-    ADD HLBC         ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 16)+    add hlbc         ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 16)
                        ; HL contiene la direccion de inicio en el sprite                        ; HL contiene la direccion de inicio en el sprite
  
-    EX DEHL          ; Intercambiamos DE y HL para el OR+    ex dehl          ; Intercambiamos DE y HL para el OR
  
     ;;; Dibujar 8 scanlines (DE) -> (HL) + bajar scanline y avanzar en SPR     ;;; Dibujar 8 scanlines (DE) -> (HL) + bajar scanline y avanzar en SPR
-    LD B, 8+    ld b, 8
  
 drawspr8x8m_loop: drawspr8x8m_loop:
-    LD A, (DE)         ; Obtenemos un byte del sprite (el byte de mascara) +    ld a, (de)         ; Obtenemos un byte del sprite (el byte de mascara) 
-    AND (HL)           ; A = A AND (HL+    and (hl)           ; A = A and (hl
-    LD C           ; Nos guardamos el valor del AND +    ld c           ; Nos guardamos el valor del AND 
-    INC DE             ; Avanzamos al siguiente byte (el dato grafico) +    inc de             ; Avanzamos al siguiente byte (el dato grafico) 
-    LD A, (DE)         ; Obtenemos el byte grafico +    ld a, (de)         ; Obtenemos el byte grafico 
-    OR C               ; A = A OR C = A OR (MASK AND FONDO+    or c               ; A = A or c = A OR (MASK and fONDO
-    LD (HL),         ; Imprimimos el dato tras aplicar operaciones logicas +    ld (hl),         ; Imprimimos el dato tras aplicar operaciones logicas 
-    INC DE             ; Avanzamos al siguiente dato del sprite +    inc de             ; Avanzamos al siguiente dato del sprite 
-    INC H              ; Incrementamos puntero en pantalla (siguiente scanline) +    inc h              ; Incrementamos puntero en pantalla (siguiente scanline) 
-    DJNZ drawspr8x8m_loop+    djnz drawspr8x8m_loop
  
     ;;; En este punto, los 8 scanlines del sprite estan dibujados.     ;;; En este punto, los 8 scanlines del sprite estan dibujados.
-    LD AH +    ld ah 
-    SUB 8              ; Recuperamos la posicion de memoria del +    sub 8              ; Recuperamos la posicion de memoria del 
-    LD B           ; scanline inicial donde empezamos a dibujar +    ld b           ; scanline inicial donde empezamos a dibujar 
-    LD C           ; BC = HL - 8+    ld c           ; BC = HL - 8
  
-    ;;; Considerar el dibujado de los atributos (Si DS_ATTRIBS=0 -> RET+    ;;; Considerar el dibujado de los atributos (Si DS_ATTRIBS=0 -> ret
-    LD HL, (DS_ATTRIBS)+    ld hl, (DS_ATTRIBS)
  
-    XOR A              ; A = 0 +    xor a              ; A = 0 
-    ADD A          ; A = 0 + H = H +    add a          ; A = 0 + H = H 
-    RET Z              ; Si H = 0, volver (no dibujar atributos)+    ret z              ; Si H = 0, volver (no dibujar atributos)
  
     ;;; Calcular posicion destino en area de atributos en DE.     ;;; Calcular posicion destino en area de atributos en DE.
-    LD A           ; Codigo de Get_Attr_Offset_From_Image +    ld a           ; Codigo de Get_Attr_Offset_From_Image 
-    RRCA               ; Obtenemos dir de atributo a partir de +    rrca               ; Obtenemos dir de atributo a partir de 
-    RRCA               ; dir de zona de imagen. +    rrca               ; dir de zona de imagen. 
-    RRCA               ; Nos evita volver a obtener X e Y +    rrca               ; Nos evita volver a obtener X e Y 
-    AND 3              ; y hacer el calculo completo de la +    and 3              ; y hacer el calculo completo de la 
-    OR $58             ; direccion en zona de atributos +    or $58             ; direccion en zona de atributos 
-    LD DA +    ld da 
-    LD E           ; DE tiene el offset del attr de HL+    ld e           ; DE tiene el offset del attr de HL
  
-    LD A, (DS_NUMSPR)  ; Cogemos el numero de sprite a dibujar +    ld a, (DS_NUMSPR)  ; Cogemos el numero de sprite a dibujar 
-    LD CA +    ld ca 
-    LD B, 0 +    ld b, 0 
-    ADD HLBC         ; HL = HL+DS_NUMSPR = Origen de atributo+    add hlbc         ; HL = HL+DS_NUMSPR = Origen de atributo
  
     ;;; Copiar (HL) en (DE) -> Copiar atributo de sprite a pantalla     ;;; Copiar (HL) en (DE) -> Copiar atributo de sprite a pantalla
-    LD A, (HL+    ld a, (hl
-    LD (DE),         ; Mas rapido que LDI (7+7 vs 16 t-estados) +    ld (de),         ; Mas rapido que ldi (7+7 vs 16 t-estados) 
-    RET                ; porque no necesitamos incrementar HL y DE+    ret                ; porque no necesitamos incrementar HL y DE
 </code> </code>
  
Línea 1389: Línea 1402:
 </code> </code>
  
- El código para el cálculo agrega un ''ADD HLHL'' adicional para esta tarea:+ El código para el cálculo agrega un ''add hlhl'' adicional para esta tarea:
  
 <code z80> <code z80>
-    LD BC, (DS_SPRITES) +    ld bc, (DS_SPRITES) 
-    LD A, (DS_NUMSPR) +    ld a, (DS_NUMSPR) 
-    LD H, 0 +    ld h, 0 
-    LD L          ; HL = DS_NUMSPR +    ld l          ; HL = DS_NUMSPR 
-    ADD HLHL        ; HL = HL * 2 +    add hlhl        ; HL = HL * 2 
-    ADD HLHL        ; HL = HL * 4 +    add hlhl        ; HL = HL * 4 
-    ADD HLHL        ; HL = HL * 8 +    add hlhl        ; HL = HL * 8 
-    ADD HLHL        ; HL = HL * 16 = DS_NUMSPR * 16 +    add hlhl        ; HL = HL * 16 = DS_NUMSPR * 16 
-    ADD HLBC        ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 16)+    add hlbc        ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 16)
                       ; HL contiene la direccion de inicio en el sprite                       ; HL contiene la direccion de inicio en el sprite
 </code> </code>
Línea 1407: Línea 1420:
  
 <code z80> <code z80>
-    LD B, 8+    ld b, 8
 drawspr8x8m_loop: drawspr8x8m_loop:
-    LD A, (DE)        ; Obtenemos un byte del sprite (el byte de mascara) +    ld a, (de)        ; Obtenemos un byte del sprite (el byte de mascara) 
-    AND (HL)          ; A = A AND (HL+    and (hl)          ; A = A and (hl
-    LD C          ; Nos guardamos el valor del AND +    ld c          ; Nos guardamos el valor del AND 
-    INC DE            ; Avanzamos al siguiente byte (el dato grafico) +    inc de            ; Avanzamos al siguiente byte (el dato grafico) 
-    LD A, (DE)        ; Obtenemos el byte grafico +    ld a, (de)        ; Obtenemos el byte grafico 
-    OR C              ; A = A OR C = A OR (MASK AND FONDO+    or c              ; A = A or c = A OR (MASK and fONDO
-    LD (HL),        ; Imprimimos el dato tras aplicar operaciones logicas +    ld (hl),        ; Imprimimos el dato tras aplicar operaciones logicas 
-    INC DE            ; Avanzamos al siguiente dato del sprite +    inc de            ; Avanzamos al siguiente dato del sprite 
-    INC H             ; Incrementamos puntero en pantalla (siguiente scanline) +    inc h             ; Incrementamos puntero en pantalla (siguiente scanline) 
-    DJNZ drawspr8x8m_loop+    djnz drawspr8x8m_loop
 </code> </code>
  
Línea 1487: Línea 1500:
    * El cálculo de la dirección origen en el sprite cambia, ya que ahora cada scanline ocupa 2 bytes y no 1, y tenemos 2 bloques de altura en el sprite y no uno. Antes calculábamos la dirección origen como BASE+(DS_NUMSPR*8), pero ahora tendremos que avanzar 8*2*2=32 bytes por cada sprite en los sprites sin máscara. El cálculo quedaría como BASE+(DS_NUMSPR*32).    * El cálculo de la dirección origen en el sprite cambia, ya que ahora cada scanline ocupa 2 bytes y no 1, y tenemos 2 bloques de altura en el sprite y no uno. Antes calculábamos la dirección origen como BASE+(DS_NUMSPR*8), pero ahora tendremos que avanzar 8*2*2=32 bytes por cada sprite en los sprites sin máscara. El cálculo quedaría como BASE+(DS_NUMSPR*32).
  
-   * Para multiplicar DS_NUMSPR por 32 vamos a utilizar desplazamientos a la derecha de un pseudo-registro de 16 bits formado por A y L en lugar de utilizar sumas sucesivas **ADD HLHL**. Esta técnica requiere menos ciclos de reloj para su ejecución.+   * Para multiplicar DS_NUMSPR por 32 vamos a utilizar desplazamientos a la derecha de un pseudo-registro de 16 bits formado por A y L en lugar de utilizar sumas sucesivas **add hlhl**. Esta técnica requiere menos ciclos de reloj para su ejecución.
  
    * La impresión de datos debe imprimir todo un scanline horizontal del Sprite (2 bytes) antes de avanzar al siguiente scanline de pantalla.    * La impresión de datos debe imprimir todo un scanline horizontal del Sprite (2 bytes) antes de avanzar al siguiente scanline de pantalla.
Línea 1513: Línea 1526:
 ;    Dibujar byte (DE) -> (HL), trazando el scanline del 2o bloque ;    Dibujar byte (DE) -> (HL), trazando el scanline del 2o bloque
 ;    Incrementar DE ;    Incrementar DE
-;    Bajar a siguiente scanline en pantalla (HL), sumando 256 (INC H/DEC L).+;    Bajar a siguiente scanline en pantalla (HL), sumando 256 (inc h/dec l).
  
 ; Avanzar puntero de pantalla (HL) a la posicion de la segunda ; Avanzar puntero de pantalla (HL) a la posicion de la segunda
Línea 1523: Línea 1536:
 ;    Dibujar byte (DE) -> (HL), trazando el scanline del 2o bloque ;    Dibujar byte (DE) -> (HL), trazando el scanline del 2o bloque
 ;    Incrementar DE ;    Incrementar DE
-;    Bajar a siguiente scanline en pantalla (HL), sumando 256 (INC H/DEC L).+;    Bajar a siguiente scanline en pantalla (HL), sumando 256 (inc h/dec l).
  
-; Si base_atributos == 0 -> RET+; Si base_atributos == 0 -> ret
 ; Calcular posicion origen de los atributos array_attr+(NUM_SPRITE*4) en HL. ; Calcular posicion origen de los atributos array_attr+(NUM_SPRITE*4) en HL.
 ; Calcular posicion destino en area de atributos en DE. ; Calcular posicion destino en area de atributos en DE.
Línea 1559: Línea 1572:
  
     ; Guardamos en BC la pareja (x,y) -> B=COORD_Y y C=COORD_X     ; Guardamos en BC la pareja (x,y) -> B=COORD_Y y C=COORD_X
-    LD BC, (DS_COORD_X)+    ld bc, (DS_COORD_X)
  
     ;;; Calculamos las coordenadas destino de pantalla en DE:     ;;; Calculamos las coordenadas destino de pantalla en DE:
-    LD AB +    ld ab 
-    AND $18 +    and $18 
-    ADD A, $40 +    add a, $40 
-    LD DA +    ld da 
-    LD AB +    ld ab 
-    AND +    and 
-    RRCA +    rrca 
-    RRCA +    rrca 
-    RRCA +    rrca 
-    ADD AC +    add ac 
-    LD EA+    ld ea
  
-    PUSH DE           ; Lo guardamos para luego, lo usaremos para+    push de           ; Lo guardamos para luego, lo usaremos para
                       ; calcular la direccion del atributo                       ; calcular la direccion del atributo
  
Línea 1580: Línea 1593:
     ;;;     direccion = base_sprites + (NUM_SPRITE*32)     ;;;     direccion = base_sprites + (NUM_SPRITE*32)
     ;;; Multiplicamos con desplazamientos, ver los comentarios.     ;;; Multiplicamos con desplazamientos, ver los comentarios.
-    LD BC, (DS_SPRITES) +    ld bc, (DS_SPRITES) 
-    LD A, (DS_NUMSPR) +    ld a, (DS_NUMSPR) 
-    LD L, 0           ; AL = DS_NUMSPR*256 +    ld l, 0           ; AL = DS_NUMSPR*256 
-    SRL A             ; Desplazamos a la derecha para dividir por dos +    srl a             ; Desplazamos a la derecha para dividir por dos 
-    RR L              ; AL = DS_NUMSPR*128 +    rr l              ; AL = DS_NUMSPR*128 
-    RRA               ; Rotamos, ya que el bit que salio de L al CF fue 0 +    rra               ; Rotamos, ya que el bit que salio de L al CF fue 0 
-    RR L              ; AL = DS_NUMSPR*64 +    rr l              ; AL = DS_NUMSPR*64 
-    RRA               ; Rotamos, ya que el bit que salio de L al CF fue 0 +    rra               ; Rotamos, ya que el bit que salio de L al CF fue 0 
-    RR L              ; AL = DS_NUMSPR*32 +    rr l              ; AL = DS_NUMSPR*32 
-    LD H          ; HL = DS_NUMSPR*32 +    ld h          ; HL = DS_NUMSPR*32 
-    ADD HLBC        ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 32)+    add hlbc        ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 32)
                       ; HL contiene la direccion de inicio en el sprite                       ; HL contiene la direccion de inicio en el sprite
  
-    EX DEHL         ; Intercambiamos DE y HL (DE=origen, HL=destino)+    ex dehl         ; Intercambiamos DE y HL (DE=origen, HL=destino)
  
     ;;; Repetir 8 veces (primeros 2 bloques horizontales):     ;;; Repetir 8 veces (primeros 2 bloques horizontales):
-    LD B, 8+    ld b, 8
  
 drawsp16x16_loop1: drawsp16x16_loop1:
-    LD A, (DE)         ; Bloque 1: Leemos dato del sprite +    ld a, (de)         ; Bloque 1: Leemos dato del sprite 
-    LD (HL),         ; Copiamos dato a pantalla +    ld (hl),         ; Copiamos dato a pantalla 
-    INC DE             ; Incrementar puntero en sprite +    inc de             ; Incrementar puntero en sprite 
-    INC L              ; Incrementar puntero en pantalla+    inc l              ; Incrementar puntero en pantalla
  
-    LD A, (DE)         ; Bloque 2: Leemos dato del sprite +    ld a, (de)         ; Bloque 2: Leemos dato del sprite 
-    LD (HL),         ; Copiamos dato a pantalla +    ld (hl),         ; Copiamos dato a pantalla 
-    INC DE             ; Incrementar puntero en sprite+    inc de             ; Incrementar puntero en sprite
  
-    INC H              ; Hay que sumar 256 para ir al siguiente scanline +    inc h              ; Hay que sumar 256 para ir al siguiente scanline 
-    DEC L              ; pero hay que restar el INC L que hicimos. +    dec l              ; pero hay que restar el inc l que hicimos. 
-    DJNZ drawsp16x16_loop1+    djnz drawsp16x16_loop1
  
     ; Avanzamos HL 1 scanline (codigo de incremento de HL en 1 scanline)     ; Avanzamos HL 1 scanline (codigo de incremento de HL en 1 scanline)
     ; desde el septimo scanline de la fila Y+1 al primero de la Y+2     ; desde el septimo scanline de la fila Y+1 al primero de la Y+2
  
-    ;;;INC H           ; No hay que hacer INC H, lo hizo en el bucle +    ;;;inc h           ; No hay que hacer inc h, lo hizo en el bucle 
-    ;;;LD A        ; No hay que hacer esta prueba, sabemos que +    ;;;ld a        ; No hay que hacer esta prueba, sabemos que 
-    ;;;AND 7           ; no hay salto (es un cambio de bloque) +    ;;;and 7           ; no hay salto (es un cambio de bloque) 
-    ;;;JR NZ, drawsp16_nofix_abajop +    ;;;jr nz, drawsp16_nofix_abajop 
-    LD AL +    ld al 
-    ADD A, 32 +    add a, 32 
-    LD LA +    ld la 
-    JR C, drawsp16_nofix_abajop +    jr c, drawsp16_nofix_abajop 
-    LD AH +    ld ah 
-    SUB +    sub 
-    LD HA+    ld ha
  
 drawsp16_nofix_abajop: drawsp16_nofix_abajop:
  
     ;;; Repetir 8 veces (segundos 2 bloques horizontales):     ;;; Repetir 8 veces (segundos 2 bloques horizontales):
-    LD B, 8+    ld b, 8
  
 drawsp16x16_loop2: drawsp16x16_loop2:
-    LD A, (DE)         ; Bloque 1: Leemos dato del sprite +    ld a, (de)         ; Bloque 1: Leemos dato del sprite 
-    LD (HL),         ; Copiamos dato a pantalla +    ld (hl),         ; Copiamos dato a pantalla 
-    INC DE             ; Incrementar puntero en sprite +    inc de             ; Incrementar puntero en sprite 
-    INC L              ; Incrementar puntero en pantalla+    inc l              ; Incrementar puntero en pantalla
  
-    LD A, (DE)         ; Bloque 2: Leemos dato del sprite +    ld a, (de)         ; Bloque 2: Leemos dato del sprite 
-    LD (HL),         ; Copiamos dato a pantalla +    ld (hl),         ; Copiamos dato a pantalla 
-    INC DE             ; Incrementar puntero en sprite+    inc de             ; Incrementar puntero en sprite
  
-    INC H              ; Hay que sumar 256 para ir al siguiente scanline +    inc h              ; Hay que sumar 256 para ir al siguiente scanline 
-    DEC L              ; pero hay que restar el INC L que hicimos. +    dec l              ; pero hay que restar el inc l que hicimos. 
-    DJNZ drawsp16x16_loop2+    djnz drawsp16x16_loop2
  
     ;;; En este punto, los 16 scanlines del sprite estan dibujados.     ;;; En este punto, los 16 scanlines del sprite estan dibujados.
  
-    POP BC             ; Recuperamos el offset del primer scanline+    pop bc             ; Recuperamos el offset del primer scanline
  
-    ;;; Considerar el dibujado de los atributos (Si DS_ATTRIBS=0 -> RET+    ;;; Considerar el dibujado de los atributos (Si DS_ATTRIBS=0 -> ret
-    LD HL, (DS_ATTRIBS)+    ld hl, (DS_ATTRIBS)
  
-    XOR A              ; A = 0 +    xor a              ; A = 0 
-    ADD A          ; A = 0 + H = H +    add a          ; A = 0 + H = H 
-    RET Z              ; Si H = 0, volver (no dibujar atributos)+    ret z              ; Si H = 0, volver (no dibujar atributos)
  
     ;;; Calcular posicion destino en area de atributos en DE.     ;;; Calcular posicion destino en area de atributos en DE.
-    LD A           ; Codigo de Get_Attr_Offset_From_Image +    ld a           ; Codigo de Get_Attr_Offset_From_Image 
-    RRCA               ; Obtenemos dir de atributo a partir de +    rrca               ; Obtenemos dir de atributo a partir de 
-    RRCA               ; dir de zona de imagen. +    rrca               ; dir de zona de imagen. 
-    RRCA               ; Nos evita volver a obtener X e Y +    rrca               ; Nos evita volver a obtener X e Y 
-    AND 3              ; y hacer el calculo completo de la +    and 3              ; y hacer el calculo completo de la 
-    OR $58             ; direccion en zona de atributos +    or $58             ; direccion en zona de atributos 
-    LD DA +    ld da 
-    LD E           ; DE tiene el offset del attr de HL+    ld e           ; DE tiene el offset del attr de HL
  
-    LD A, (DS_NUMSPR)  ; Cogemos el numero de sprite a dibujar +    ld a, (DS_NUMSPR)  ; Cogemos el numero de sprite a dibujar 
-    LD CA +    ld ca 
-    LD B, 0 +    ld b, 0 
-    ADD HLBC         ; HL = HL+DS_NUMSPR +    add hlbc         ; HL = HL+DS_NUMSPR 
-    ADD HLBC         ; HL = HL+DS_NUMSPR*2 +    add hlbc         ; HL = HL+DS_NUMSPR*2 
-    ADD HLBC         ; HL = HL+DS_NUMSPR*3 +    add hlbc         ; HL = HL+DS_NUMSPR*3 
-    ADD HLBC         ; HL = HL+HL=(DS_NUMSPR*4) = Origen de atributo+    add hlbc         ; HL = HL+HL=(DS_NUMSPR*4) = Origen de atributo
  
-    LDI +    ldi 
-    LDI                ; Imprimimos las 2 primeras filas de atributo+    ldi                ; Imprimimos las 2 primeras filas de atributo
  
     ;;; Avance diferencial a la siguiente linea de atributos     ;;; Avance diferencial a la siguiente linea de atributos
-    LD A           ; A = L +    ld a           ; A = L 
-    ADD A, 30          ; Sumamos A = A + 30 mas los 2 INCs de LDI+    add a, 30          ; Sumamos A = A + 30 mas los 2 INCs de ldi
-    LD E           ; Guardamos en L (L = L+30 + 2 por LDI=L+32) +    ld e           ; Guardamos en L (L = L+30 + 2 por ldi=L+32) 
-    JR NC, drawsp16x16_attrab_noinc +    jr nc, drawsp16x16_attrab_noinc 
-    INC D+    inc d
 drawsp16x16_attrab_noinc: drawsp16x16_attrab_noinc:
-    LDI +    ldi 
-    LDI +    ldi 
-    RET                ; porque no necesitamos incrementar HL y DE+    ret                ; porque no necesitamos incrementar HL y DE
 </code> </code>
  
 \\  \\ 
- Lo primero que nos llama la atención de la rutina es la forma de multiplicar por 32 el valor de ''DS_NUMSPR''. Una primera aproximación de multiplicación de HL = NUM_SPR * 32 podría ser aumentar el número de sumas ''ADD HLHL'' tal y como se realizan en las rutinas de 8x8:+ Lo primero que nos llama la atención de la rutina es la forma de multiplicar por 32 el valor de ''DS_NUMSPR''. Una primera aproximación de multiplicación de HL = NUM_SPR * 32 podría ser aumentar el número de sumas ''add hlhl'' tal y como se realizan en las rutinas de 8x8:
  
 <code z80> <code z80>
     ;;; Multiplicar DS_SPRITES por 32 con sumas     ;;; Multiplicar DS_SPRITES por 32 con sumas
-    LD BC, (DS_SPRITES) +    ld bc, (DS_SPRITES) 
-    LD A, (DS_NUMSPR) +    ld a, (DS_NUMSPR) 
-    LD H, 0           ; H = 0 +    ld h, 0           ; H = 0 
-    LD L          ; HL = DS_NUMSPR +    ld l          ; HL = DS_NUMSPR 
-    ADD HLHL        ; HL = HL * 2 +    add hlhl        ; HL = HL * 2 
-    ADD HLHL        ; HL = HL * 4 +    add hlhl        ; HL = HL * 4 
-    ADD HLHL        ; HL = HL * 8 +    add hlhl        ; HL = HL * 8 
-    ADD HLHL        ; HL = HL * 16 +    add hlhl        ; HL = HL * 16 
-    ADD HLHL        ; HL = HL * 32 +    add hlhl        ; HL = HL * 32 
-    ADD HLBC        ; HL = DS_SPRITES + (DS_NUMSPR * 32)+    add hlbc        ; HL = DS_SPRITES + (DS_NUMSPR * 32)
 </code> </code>
  
- Esta porción de código tarda 11 t-estados por cada ''ADD'' de 16 bits, más 7 t-estados de ''LD H, 0'', más 4 de ''LD LA'', lo que da un total de 77 t-estados para realizar la multiplicación.+ Esta porción de código tarda 11 t-estados por cada ''ADD'' de 16 bits, más 7 t-estados de ''ld h, 0'', más 4 de ''ld la'', lo que da un total de 77 t-estados para realizar la multiplicación.
  
  La técnica empleada en el listado, proporcionada por **metalbrain**, implica cargar el valor de DS_NUMSPR en la parte alta de un registro de 16 bits (con lo que el registro tendría el valor de DS_NUMSPR*256, como si lo hubieramos desplazado 8 veces a la izquierda), y después realizar desplazamientos de 16 bits a la derecha, dividiendo este valor por 2 en cada desplazamiento. Con un desplazamiento, obtenemos en el registro el valor DS_NUMSPR * 128, con otro desplazamiento DS_NUMSPR * 64, y con otro más DS_NUMSPR * 32.  La técnica empleada en el listado, proporcionada por **metalbrain**, implica cargar el valor de DS_NUMSPR en la parte alta de un registro de 16 bits (con lo que el registro tendría el valor de DS_NUMSPR*256, como si lo hubieramos desplazado 8 veces a la izquierda), y después realizar desplazamientos de 16 bits a la derecha, dividiendo este valor por 2 en cada desplazamiento. Con un desplazamiento, obtenemos en el registro el valor DS_NUMSPR * 128, con otro desplazamiento DS_NUMSPR * 64, y con otro más DS_NUMSPR * 32.
Línea 1715: Línea 1728:
 <code z80> <code z80>
     ;;; Multiplicar DS_SPRITES por 32 con desplazamientos >>     ;;; Multiplicar DS_SPRITES por 32 con desplazamientos >>
-    LD BC, (DS_SPRITES) +    ld bc, (DS_SPRITES) 
-    LD A, (DS_NUMSPR) +    ld a, (DS_NUMSPR) 
-    LD L, 0           ; AL = DS_NUMSPR*256 +    ld l, 0           ; AL = DS_NUMSPR*256 
-    SRL A             ; Desplazamos a la derecha para dividir por dos +    srl a             ; Desplazamos a la derecha para dividir por dos 
-    RR L              ; AL = DS_NUMSPR*128 +    rr l              ; AL = DS_NUMSPR*128 
-    RRA               ; Rotamos, ya que el bit que salio de L al CF fue 0 +    rra               ; Rotamos, ya que el bit que salio de L al CF fue 0 
-    RR L              ; AL = DS_NUMSPR*64 +    rr l              ; AL = DS_NUMSPR*64 
-    RRA               ; Rotamos, ya que el bit que salio de L al CF fue 0 +    rra               ; Rotamos, ya que el bit que salio de L al CF fue 0 
-    RR L              ; AL = DS_NUMSPR*32 +    rr l              ; AL = DS_NUMSPR*32 
-    LD H          ; HL = DS_NUMSPR*32 +    ld h          ; HL = DS_NUMSPR*32 
-    ADD HLBC        ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 32)+    add hlbc        ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 32)
                       ; HL contiene la direccion de inicio en el sprite                       ; HL contiene la direccion de inicio en el sprite
 </code> </code>
Línea 1733: Línea 1746:
  Si tuvieramos que multiplicar por 64 (realizar un ''RRA''/''RL'' menos), el coste sería todavía menor: 12 t-estados menos con un total de 46 t-estados. En el caso de las rutinas de 8x8, resultaba más rápido realizar la multiplicación por medio de sumas que por desplazamientos, con un coste total de 55 t-estados.  Si tuvieramos que multiplicar por 64 (realizar un ''RRA''/''RL'' menos), el coste sería todavía menor: 12 t-estados menos con un total de 46 t-estados. En el caso de las rutinas de 8x8, resultaba más rápido realizar la multiplicación por medio de sumas que por desplazamientos, con un coste total de 55 t-estados.
  
- Otra parte interesante de la rutina de dibujado de sprites está en la impresión de los datos gráficos. En esta ocasión hay que imprimir 2 bytes horizontales en cada scanline, y sumar 256 para avanzar a la siguiente línea de pantalla. Esto nos obliga a decrementar HL en 1 unidad (con ''DEC L'') para compensar el avance horizontal utilizado para posicionarnos en el lugar de dibujado del segundo bloque del sprite. Tras esto, ya podemos hacer el avance de scanline con un simple ''INC H'' (HL=HL+256).+ Otra parte interesante de la rutina de dibujado de sprites está en la impresión de los datos gráficos. En esta ocasión hay que imprimir 2 bytes horizontales en cada scanline, y sumar 256 para avanzar a la siguiente línea de pantalla. Esto nos obliga a decrementar HL en 1 unidad (con ''dec l'') para compensar el avance horizontal utilizado para posicionarnos en el lugar de dibujado del segundo bloque del sprite. Tras esto, ya podemos hacer el avance de scanline con un simple ''inc h'' (HL=HL+256).
  
- Una vez finalizado el bucle de 8 iteraciones que imprime los datos de los 2 bloques de la fila 1 del sprite, debemos avanzar al siguiente scanline de pantalla (8 más abajo de la posicion Y inicial) para trazar los 2 bloques restantes (los bloques "de abajo"). Para ello se ha insertado el código de ''Siguiente_Scanline_HL'' dentro de la rutina (evitando el ''CALL'' y el ''RET''). La instrucción inicial ''INC H'' de la rutina que vimos en el capítulo anterior no es necesaria porque la ejecuta la última iteración del bucle anterior.+ Una vez finalizado el bucle de 8 iteraciones que imprime los datos de los 2 bloques de la fila 1 del sprite, debemos avanzar al siguiente scanline de pantalla (8 más abajo de la posicion Y inicial) para trazar los 2 bloques restantes (los bloques "de abajo"). Para ello se ha insertado el código de ''Siguiente_Scanline_HL'' dentro de la rutina (evitando el ''call'' y el ''RET''). La instrucción inicial ''inc h'' de la rutina que vimos en el capítulo anterior no es necesaria porque la ejecuta la última iteración del bucle anterior.
  
  Tras ajustar HL tenemos que dibujar los 2 últimos bloques del sprite, con un bucle similar al que dibujó los 2 primeros.  Tras ajustar HL tenemos que dibujar los 2 últimos bloques del sprite, con un bucle similar al que dibujó los 2 primeros.
Línea 1744: Línea 1757:
  
 <code z80> <code z80>
-    LD B, 16           ; 16 iteraciones+    ld b, 16           ; 16 iteraciones
  
 drawsp16x16_loop: drawsp16x16_loop:
-    LD A, (DE)         ; Bloque 1: Leemos dato del sprite +    ld a, (de)         ; Bloque 1: Leemos dato del sprite 
-    LD (HL),         ; Copiamos dato a pantalla +    ld (hl),         ; Copiamos dato a pantalla 
-    INC DE             ; Incrementar puntero en sprite +    inc de             ; Incrementar puntero en sprite 
-    INC L              ; Incrementar puntero en pantalla +    inc l              ; Incrementar puntero en pantalla 
-    LD A, (DE)         ; Bloque 2: Leemos dato del sprite +    ld a, (de)         ; Bloque 2: Leemos dato del sprite 
-    LD (HL),         ; Copiamos dato a pantalla +    ld (hl),         ; Copiamos dato a pantalla 
-    INC DE             ; Incrementar puntero en sprite +    inc de             ; Incrementar puntero en sprite 
-    DEC L              ; Decrementamos el avance realizado+    dec l              ; Decrementamos el avance realizado
  
     ; Avanzamos HL 1 scanline (codigo de incremento de HL en 1 scanline)     ; Avanzamos HL 1 scanline (codigo de incremento de HL en 1 scanline)
-    ;;;INC H           ; No hay que hacer INC H, lo hizo en el bucle +    ;;;inc h           ; No hay que hacer inc h, lo hizo en el bucle 
-    ;;;LD A        ; No hay que hacer esta prueba, sabemos que +    ;;;ld a        ; No hay que hacer esta prueba, sabemos que 
-    ;;;AND 7           ; no hay salto (es un cambio de bloque) +    ;;;and 7           ; no hay salto (es un cambio de bloque) 
-    ;;;JR NZ, drawsp16_nofix_abajop +    ;;;jr nz, drawsp16_nofix_abajop 
-    LD AL +    ld al 
-    ADD A, 32 +    add a, 32 
-    LD LA +    ld la 
-    JR C, drawsp16_nofix_abajop +    jr c, drawsp16_nofix_abajop 
-    LD AH +    ld ah 
-    SUB +    sub 
-    LD HA+    ld ha
 drawsp16_nofix_abajop: drawsp16_nofix_abajop:
  
-    DJNZ drawsp16x16_loop+    djnz drawsp16x16_loop
 </code> </code>
  
- Este código es más pequeño en tamaño que el uso de 2 bucles, pero estamos efectuando un ''JR'' innecesario en 14 de las 16 iteraciones, ya que sólo se debe chequear el caracter/tercio en el salto de un bloque de pantalla al siguiente, que sólo ocurre 1 vez en el caso de un sprite de 2x2  bloques impreso en posiciones de carácter. Además, en la última iteración es innecesario incrementar y ajustar HL, por lo que son ciclos de reloj que se malgastan.+ Este código es más pequeño en tamaño que el uso de 2 bucles, pero estamos efectuando un ''jr'' innecesario en 14 de las 16 iteraciones, ya que sólo se debe chequear el caracter/tercio en el salto de un bloque de pantalla al siguiente, que sólo ocurre 1 vez en el caso de un sprite de 2x2  bloques impreso en posiciones de carácter. Además, en la última iteración es innecesario incrementar y ajustar HL, por lo que son ciclos de reloj que se malgastan.
  
- Finalmente, a la hora de escribir los atributos cambia el cálculo de la posición origen (ahora es DS_NUMSPR*4, cuya multiplicación realizamos en base a 4 sumas), así como la copia de los 4 bytes, ya que hay que imprimir los 2 primeros (''LDI'' + ''LDI''), avanzar DE hasta la siguiente "fila de atributos", y copiar los 2 siguientes (con otras 2 instrucciones LDI).+ Finalmente, a la hora de escribir los atributos cambia el cálculo de la posición origen (ahora es DS_NUMSPR*4, cuya multiplicación realizamos en base a 4 sumas), así como la copia de los 4 bytes, ya que hay que imprimir los 2 primeros (''LDI'' + ''LDI''), avanzar DE hasta la siguiente "fila de atributos", y copiar los 2 siguientes (con otras 2 instrucciones ldi).
  
  Veamos la ejecución de la rutina con un sencillo ejemplo... Primero dibujamos en SevenuP el pequeño personaje de 2x2 bloques con el que abríamos este apartado y lo exportamos a ASM:  Veamos la ejecución de la rutina con un sencillo ejemplo... Primero dibujamos en SevenuP el pequeño personaje de 2x2 bloques con el que abríamos este apartado y lo exportamos a ASM:
Línea 1802: Línea 1815:
  
 <code z80> <code z80>
-    LD HL, bicho_gfx +    ld hl, bicho_gfx 
-    LD (DS_SPRITES), HL +    ld (DS_SPRITES), hl 
-    LD HL, bicho_attrib +    ld hl, bicho_attrib 
-    LD (DS_ATTRIBS), HL +    ld (DS_ATTRIBS), hl 
-    LD A, 13 +    ld a, 13 
-    LD (DS_COORD_X), A +    ld (DS_COORD_X), a 
-    LD A, 8 +    ld a, 8 
-    LD (DS_COORD_Y), A +    ld (DS_COORD_Y), a 
-    XOR A +    xor a 
-    LD (DS_NUMSPR), A +    ld (DS_NUMSPR), a 
-    CALL DrawSprite_16x16_LD+    call DrawSprite_16x16_LD
 </code> </code>
  
Línea 1831: Línea 1844:
 ==== Impresión 16x16 usándo operaciones lógicas ==== ==== Impresión 16x16 usándo operaciones lógicas ====
  
- Podemos convertir la anterior rutina fácilmente en una rutina de impresión con operaciones lógicas añadiendo el ''OR (HL)'' (o el ''XOR'') antes de la escritura del dato en pantalla.+ Podemos convertir la anterior rutina fácilmente en una rutina de impresión con operaciones lógicas añadiendo el ''or (hl)'' (o el ''XOR'') antes de la escritura del dato en pantalla.
  
  Simplemente, reemplazaríamos las diferentes operaciones de transferencia cambiando:  Simplemente, reemplazaríamos las diferentes operaciones de transferencia cambiando:
  
 <code z80> <code z80>
-    LD A, (DE)         ; Bloque 1: Leemos dato del sprite +    ld a, (de)         ; Bloque 1: Leemos dato del sprite 
-    LD (HL),         ; Copiamos dato a pantalla +    ld (hl),         ; Copiamos dato a pantalla 
-    INC DE             ; Incrementar puntero en sprite +    inc de             ; Incrementar puntero en sprite 
-    INC L              ; Incrementar puntero en pantalla+    inc l              ; Incrementar puntero en pantalla
 </code> </code>
  
Línea 1845: Línea 1858:
  
 <code z80> <code z80>
-    LD A, (DE)         ; Bloque 1: Leemos dato del sprite +    ld a, (de)         ; Bloque 1: Leemos dato del sprite 
-    OR (HL)            ; Realizamos operación lógica +    or (hl)            ; Realizamos operación lógica 
-    LD (HL),         ; Copiamos dato a pantalla +    ld (hl),         ; Copiamos dato a pantalla 
-    INC DE             ; Incrementar puntero en sprite +    inc de             ; Incrementar puntero en sprite 
-    INC L              ; Incrementar puntero en pantalla+    inc l              ; Incrementar puntero en pantalla
 </code> </code>
  
Línea 1862: Línea 1875:
  
 \\  \\ 
-  * El cálculo de la dirección origen en el sprite cambia, ya que ahora cada scanline ocupa 4 bytes (2 del sprite y 2 de la máscara) y no 2, y tenemos 2 bloques de altura en el sprite y no uno. Como tenemos el doble de datos gráficos por cada sprite, si antes habíamos calculado la dirección origen como BASE+(DS_NUMSPR*32), ahora tendremos que calcularla como BASE+(DS_NUMSPR*64). Esta circunstancia nos ahorra un desplazamiento del pseudoregistro "AL" hacia la derecha (evitamos un ''RRA'' + ''RR L''), lo que nos permite hacer la operación de multiplicación con sólo 46 t-estados:+  * El cálculo de la dirección origen en el sprite cambia, ya que ahora cada scanline ocupa 4 bytes (2 del sprite y 2 de la máscara) y no 2, y tenemos 2 bloques de altura en el sprite y no uno. Como tenemos el doble de datos gráficos por cada sprite, si antes habíamos calculado la dirección origen como BASE+(DS_NUMSPR*32), ahora tendremos que calcularla como BASE+(DS_NUMSPR*64). Esta circunstancia nos ahorra un desplazamiento del pseudoregistro "AL" hacia la derecha (evitamos un ''RRA'' + ''rr l''), lo que nos permite hacer la operación de multiplicación con sólo 46 t-estados:
  
 \\  \\ 
Línea 1884: Línea 1897:
  
     ; Guardamos en BC la pareja (x,y) -> B=COORD_Y y C=COORD_X     ; Guardamos en BC la pareja (x,y) -> B=COORD_Y y C=COORD_X
-    LD BC, (DS_COORD_X)+    ld bc, (DS_COORD_X)
  
     ;;; Calculamos las coordenadas destino de pantalla en DE:     ;;; Calculamos las coordenadas destino de pantalla en DE:
-    LD AB +    ld ab 
-    AND $18 +    and $18 
-    ADD A, $40 +    add a, $40 
-    LD DA +    ld da 
-    LD AB +    ld ab 
-    AND +    and 
-    RRCA +    rrca 
-    RRCA +    rrca 
-    RRCA +    rrca 
-    ADD AC +    add ac 
-    LD EA+    ld ea
  
-    PUSH DE           ; Lo guardamos para luego, lo usaremos para+    push de           ; Lo guardamos para luego, lo usaremos para
                       ; calcular la direccion del atributo                       ; calcular la direccion del atributo
  
Línea 1905: Línea 1918:
     ;;;     direccion = base_sprites + (NUM_SPRITE*64)     ;;;     direccion = base_sprites + (NUM_SPRITE*64)
     ;;; Multiplicamos con desplazamientos, ver los comentarios.     ;;; Multiplicamos con desplazamientos, ver los comentarios.
-    LD BC, (DS_SPRITES) +    ld bc, (DS_SPRITES) 
-    LD A, (DS_NUMSPR) +    ld a, (DS_NUMSPR) 
-    LD L, 0           ; AL = DS_NUMSPR*256 +    ld l, 0           ; AL = DS_NUMSPR*256 
-    SRL A             ; Desplazamos a la derecha para dividir por dos +    srl a             ; Desplazamos a la derecha para dividir por dos 
-    RR L              ; AL = DS_NUMSPR*128 +    rr l              ; AL = DS_NUMSPR*128 
-    RRA               ; Rotamos, ya que el bit que salio de L al CF fue 0 +    rra               ; Rotamos, ya que el bit que salio de L al CF fue 0 
-    RR L              ; AL = DS_NUMSPR*64 +    rr l              ; AL = DS_NUMSPR*64 
-    LD H          ; HL = DS_NUMSPR*64 +    ld h          ; HL = DS_NUMSPR*64 
-    ADD HLBC        ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 64)+    add hlbc        ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 64)
                       ; HL contiene la direccion de inicio en el sprite                       ; HL contiene la direccion de inicio en el sprite
  
-    EX DEHL         ; Intercambiamos DE y HL para las OP LOGICAS+    ex dehl         ; Intercambiamos DE y HL para las OP LOGICAS
  
     ;;; Dibujar 8 scanlines (DE) -> (HL) + bajar scanline y avanzar en SPR     ;;; Dibujar 8 scanlines (DE) -> (HL) + bajar scanline y avanzar en SPR
-    LD B, 8+    ld b, 8
  
 drawspr16m_loop1: drawspr16m_loop1:
-    LD A, (DE)       ; Obtenemos un byte del sprite (el byte de mascara) +    ld a, (de)       ; Obtenemos un byte del sprite (el byte de mascara) 
-    AND (HL)         ; A = A AND (HL+    and (hl)         ; A = A and (hl
-    LD C         ; Nos guardamos el valor del AND +    ld c         ; Nos guardamos el valor del AND 
-    INC DE           ; Avanzamos al siguiente byte (el dato grafico) +    inc de           ; Avanzamos al siguiente byte (el dato grafico) 
-    LD A, (DE)       ; Obtenemos el byte grafico +    ld a, (de)       ; Obtenemos el byte grafico 
-    OR C             ; A = A OR C = A OR (MASK AND FONDO+    or c             ; A = A or c = A OR (MASK and fONDO
-    LD (HL),       ; Imprimimos el dato tras aplicar operaciones logicas +    ld (hl),       ; Imprimimos el dato tras aplicar operaciones logicas 
-    INC DE           ; Avanzamos al siguiente dato del sprite +    inc de           ; Avanzamos al siguiente dato del sprite 
-    INC L            ; Avanzamos al segundo bloque en pantalla+    inc l            ; Avanzamos al segundo bloque en pantalla
  
-    LD A, (DE)       ; Obtenemos un byte del sprite (el byte de mascara) +    ld a, (de)       ; Obtenemos un byte del sprite (el byte de mascara) 
-    AND (HL)         ; A = A AND (HL+    and (hl)         ; A = A and (hl
-    LD C         ; Nos guardamos el valor del AND +    ld c         ; Nos guardamos el valor del AND 
-    INC DE           ; Avanzamos al siguiente byte (el dato grafico) +    inc de           ; Avanzamos al siguiente byte (el dato grafico) 
-    LD A, (DE)       ; Obtenemos el byte grafico +    ld a, (de)       ; Obtenemos el byte grafico 
-    OR C             ; A = A OR C = A OR (MASK AND FONDO+    or c             ; A = A or c = A OR (MASK and fONDO
-    LD (HL),       ; Imprimimos el dato tras aplicar operaciones logicas +    ld (hl),       ; Imprimimos el dato tras aplicar operaciones logicas 
-    INC DE           ; Avanzamos al siguiente dato del sprite+    inc de           ; Avanzamos al siguiente dato del sprite
  
-    DEC L            ; Volvemos atras del valor que incrementamos +    dec l            ; Volvemos atras del valor que incrementamos 
-    INC H            ; Incrementamos puntero en pantalla (siguiente scanline) +    inc h            ; Incrementamos puntero en pantalla (siguiente scanline) 
-    DJNZ drawspr16m_loop1+    djnz drawspr16m_loop1
  
     ; Avanzamos HL 1 scanline (codigo de incremento de HL en 1 scanline)     ; Avanzamos HL 1 scanline (codigo de incremento de HL en 1 scanline)
     ; desde el septimo scanline de la fila Y+1 al primero de la Y+2     ; desde el septimo scanline de la fila Y+1 al primero de la Y+2
  
-    ;;;INC H           ; No hay que hacer INC H, lo hizo en el bucle +    ;;;inc h           ; No hay que hacer inc h, lo hizo en el bucle 
-    ;;;LD A        ; No hay que hacer esta prueba, sabemos que +    ;;;ld a        ; No hay que hacer esta prueba, sabemos que 
-    ;;;AND 7           ; no hay salto (es un cambio de bloque) +    ;;;and 7           ; no hay salto (es un cambio de bloque) 
-    ;;;JR NZ, drawsp16_nofix_abajop +    ;;;jr nz, drawsp16_nofix_abajop 
-    LD AH +    ld ah 
-    AND +    and 
-    JR NZ, drawsp16m_nofix_abajop +    jr nz, drawsp16m_nofix_abajop 
-    LD AL +    ld al 
-    ADD A, 32 +    add a, 32 
-    LD LA +    ld la 
-    JR C, drawsp16m_nofix_abajop +    jr c, drawsp16m_nofix_abajop 
-    LD AH +    ld ah 
-    SUB +    sub 
-    LD HA+    ld ha
 drawsp16m_nofix_abajop: drawsp16m_nofix_abajop:
  
     ;;; Repetir 8 veces (segundos 2 bloques horizontales):     ;;; Repetir 8 veces (segundos 2 bloques horizontales):
-    LD B, 8+    ld b, 8
  
 drawspr16m_loop2: drawspr16m_loop2:
-    LD A, (DE)       ; Obtenemos un byte del sprite (el byte de mascara) +    ld a, (de)       ; Obtenemos un byte del sprite (el byte de mascara) 
-    AND (HL)         ; A = A AND (HL+    and (hl)         ; A = A and (hl
-    LD C         ; Nos guardamos el valor del AND +    ld c         ; Nos guardamos el valor del AND 
-    INC DE           ; Avanzamos al siguiente byte (el dato grafico) +    inc de           ; Avanzamos al siguiente byte (el dato grafico) 
-    LD A, (DE)       ; Obtenemos el byte grafico +    ld a, (de)       ; Obtenemos el byte grafico 
-    OR C             ; A = A OR C = A OR (MASK AND FONDO+    or c             ; A = A or c = A OR (MASK and fONDO
-    LD (HL),       ; Imprimimos el dato tras aplicar operaciones logicas +    ld (hl),       ; Imprimimos el dato tras aplicar operaciones logicas 
-    INC DE           ; Avanzamos al siguiente dato del sprite +    inc de           ; Avanzamos al siguiente dato del sprite 
-    INC L            ; Avanzamos al segundo bloque en pantalla+    inc l            ; Avanzamos al segundo bloque en pantalla
  
-    LD A, (DE)       ; Obtenemos un byte del sprite (el byte de mascara) +    ld a, (de)       ; Obtenemos un byte del sprite (el byte de mascara) 
-    AND (HL)         ; A = A AND (HL+    and (hl)         ; A = A and (hl
-    LD C         ; Nos guardamos el valor del AND +    ld c         ; Nos guardamos el valor del AND 
-    INC DE           ; Avanzamos al siguiente byte (el dato grafico) +    inc de           ; Avanzamos al siguiente byte (el dato grafico) 
-    LD A, (DE)       ; Obtenemos el byte grafico +    ld a, (de)       ; Obtenemos el byte grafico 
-    OR C             ; A = A OR C = A OR (MASK AND FONDO+    or c             ; A = A or c = A OR (MASK and fONDO
-    LD (HL),       ; Imprimimos el dato tras aplicar operaciones logicas +    ld (hl),       ; Imprimimos el dato tras aplicar operaciones logicas 
-    INC DE           ; Avanzamos al siguiente dato del sprite+    inc de           ; Avanzamos al siguiente dato del sprite
  
-    DEC L            ; Volvemos atras del valor que incrementamos +    dec l            ; Volvemos atras del valor que incrementamos 
-    INC H            ; Incrementamos puntero en pantalla (siguiente scanline) +    inc h            ; Incrementamos puntero en pantalla (siguiente scanline) 
-    DJNZ drawspr16m_loop2+    djnz drawspr16m_loop2
  
     ;;; En este punto, los 16 scanlines del sprite estan dibujados.     ;;; En este punto, los 16 scanlines del sprite estan dibujados.
  
-    POP BC             ; Recuperamos el offset del primer scanline+    pop bc             ; Recuperamos el offset del primer scanline
  
-    ;;; Considerar el dibujado de los atributos (Si DS_ATTRIBS=0 -> RET+    ;;; Considerar el dibujado de los atributos (Si DS_ATTRIBS=0 -> ret
-    LD HL, (DS_ATTRIBS)+    ld hl, (DS_ATTRIBS)
  
-    XOR A              ; A = 0 +    xor a              ; A = 0 
-    ADD A          ; A = 0 + H = H +    add a          ; A = 0 + H = H 
-    RET Z              ; Si H = 0, volver (no dibujar atributos)+    ret z              ; Si H = 0, volver (no dibujar atributos)
  
     ;;; Calcular posicion destino en area de atributos en DE.     ;;; Calcular posicion destino en area de atributos en DE.
-    LD A           ; Codigo de Get_Attr_Offset_From_Image +    ld a           ; Codigo de Get_Attr_Offset_From_Image 
-    RRCA               ; Obtenemos dir de atributo a partir de +    rrca               ; Obtenemos dir de atributo a partir de 
-    RRCA               ; dir de zona de imagen. +    rrca               ; dir de zona de imagen. 
-    RRCA               ; Nos evita volver a obtener X e Y +    rrca               ; Nos evita volver a obtener X e Y 
-    AND 3              ; y hacer el calculo completo de la +    and 3              ; y hacer el calculo completo de la 
-    OR $58             ; direccion en zona de atributos +    or $58             ; direccion en zona de atributos 
-    LD DA +    ld da 
-    LD E           ; DE tiene el offset del attr de HL+    ld e           ; DE tiene el offset del attr de HL
  
-    LD A, (DS_NUMSPR)  ; Cogemos el numero de sprite a dibujar +    ld a, (DS_NUMSPR)  ; Cogemos el numero de sprite a dibujar 
-    LD CA +    ld ca 
-    LD B, 0 +    ld b, 0 
-    ADD HLBC         ; HL = HL+DS_NUMSPR +    add hlbc         ; HL = HL+DS_NUMSPR 
-    ADD HLBC         ; HL = HL+DS_NUMSPR*2 +    add hlbc         ; HL = HL+DS_NUMSPR*2 
-    ADD HLBC         ; HL = HL+DS_NUMSPR*3 +    add hlbc         ; HL = HL+DS_NUMSPR*3 
-    ADD HLBC         ; HL = HL+HL=(DS_NUMSPR*4) = Origen de atributo+    add hlbc         ; HL = HL+HL=(DS_NUMSPR*4) = Origen de atributo
  
-    LDI +    ldi 
-    LDI                ; Imprimimos las 2 primeras filas de atributo+    ldi                ; Imprimimos las 2 primeras filas de atributo
  
     ;;; Avance diferencial a la siguiente linea de atributos     ;;; Avance diferencial a la siguiente linea de atributos
-    LD A           ; A = L +    ld a           ; A = L 
-    ADD A, 30          ; Sumamos A = A + 30 mas los 2 INCs de LDI+    add a, 30          ; Sumamos A = A + 30 mas los 2 INCs de ldi
-    LD E           ; Guardamos en L (L = L+30 + 2 por LDI=L+32) +    ld e           ; Guardamos en L (L = L+30 + 2 por ldi=L+32) 
-    JR NC, drawsp16m_attrab_noinc +    jr nc, drawsp16m_attrab_noinc 
-    INC D+    inc d
 drawsp16m_attrab_noinc: drawsp16m_attrab_noinc:
-    LDI +    ldi 
-    LDI +    ldi 
-    RET                ; porque no necesitamos incrementar HL y DE+    ret                ; porque no necesitamos incrementar HL y DE
 </code> </code>
  
Línea 2112: Línea 2125:
 \\  \\ 
 \\  \\ 
-**Impresión con transferencia (LD/LDI):**+**Impresión con transferencia (LD/ldi):**
  
 La impresión por transferencia directa de datos (sprite -> pantalla) es adecuada para juegos con movimiento "bloque a bloque" (en baja resolución), donde todos los sprites (y el mapa de juego) se mueven en la rejilla de 32x24 bloques del Spectrum (juegos de puzzle, rogue-likes, juegos basados en mapas de bloques, etc). La impresión por transferencia directa de datos (sprite -> pantalla) es adecuada para juegos con movimiento "bloque a bloque" (en baja resolución), donde todos los sprites (y el mapa de juego) se mueven en la rejilla de 32x24 bloques del Spectrum (juegos de puzzle, rogue-likes, juegos basados en mapas de bloques, etc).
Línea 2177: Línea 2190:
  La rutina, al ser genérica, y para que pueda ser legible por el lector, no es especialmente eficiente: debido a la necesidad de realizar multiplicaciones de 16 bits, se utilizan los registros HL, DE y BC y nos vemos obligados a hacer continuos PUSHes y POPs de estos registros, así como a apoyarnos en variables de memoria. La rutina podría ser optimizada en cuanto a algoritmo y/o en cuanto a reordenación de código y uso de shadow-registers para tratar de ganar todos los t-estados posibles.  La rutina, al ser genérica, y para que pueda ser legible por el lector, no es especialmente eficiente: debido a la necesidad de realizar multiplicaciones de 16 bits, se utilizan los registros HL, DE y BC y nos vemos obligados a hacer continuos PUSHes y POPs de estos registros, así como a apoyarnos en variables de memoria. La rutina podría ser optimizada en cuanto a algoritmo y/o en cuanto a reordenación de código y uso de shadow-registers para tratar de ganar todos los t-estados posibles.
  
- Incluso una opción mucho más recomendable sería crear tantas rutinas específicas como tamaños de sprites tengamos en nuestro juego y hacer una rutina "maestra" (un //wrapper//) que llame a una u otra según el tamaño del Sprite que estemos solicitando imprimir. Un par de comprobaciones sobre el ancho y el alto del Sprite y saltos condicionales con un CALL final a la rutina adecuada resultará muchísimo más óptimo que la rutina que vamos a examinar:+ Incluso una opción mucho más recomendable sería crear tantas rutinas específicas como tamaños de sprites tengamos en nuestro juego y hacer una rutina "maestra" (un //wrapper//) que llame a una u otra según el tamaño del Sprite que estemos solicitando imprimir. Un par de comprobaciones sobre el ancho y el alto del Sprite y saltos condicionales con un call final a la rutina adecuada resultará muchísimo más óptimo que la rutina que vamos a examinar:
  
 <code z80> <code z80>
Línea 2202: Línea 2215:
  
     ;;;; Multiplicamos ancho por alto (en bloques)     ;;;; Multiplicamos ancho por alto (en bloques)
-    LD A, (DS_WIDTH) +    ld a, (DS_WIDTH) 
-    LD CA +    ld ca 
-    LD A, (DS_HEIGHT) +    ld a, (DS_HEIGHT) 
-    RLCA               ; Multiplicamos por 8, necesitamos +    rlca               ; Multiplicamos por 8, necesitamos 
-    RLCA               ; la altura en pixeles (FILAS*8) +    rlca               ; la altura en pixeles (FILAS*8) 
-    RLCA               ; Y la guardamos porque la necesitaremos: +    rlca               ; Y la guardamos porque la necesitaremos: 
-    LD (drawsp_height), A+    ld (drawsp_height), a
  
     ;;; Multiplicamos Ancho_bloques * Alto_pixeles:     ;;; Multiplicamos Ancho_bloques * Alto_pixeles:
-    LD BA +    ld ba 
-    XOR A              ; A = 0+    xor a              ; A = 0
 drawsp_mul1: drawsp_mul1:
-    ADD A          ; A = A + C   (B veces)  = B*C +    add a          ; A = A + C   (B veces)  = B*C 
-    DJNZ drawsp_mul1   ; B veces -> A = A*C = Ancho * Alto+    djnz drawsp_mul1   ; B veces -> A = A*C = Ancho * Alto
                         ; Ahora A = Ancho*Alto (maximo 255!!!)                         ; Ahora A = Ancho*Alto (maximo 255!!!)
  
     ;;; Multiplicamos DS_NUMSPR por (Ancho_bloques*Alto_pixeles)     ;;; Multiplicamos DS_NUMSPR por (Ancho_bloques*Alto_pixeles)
-    LD B           ; Repetimos Ancho * Alto veces +    ld b           ; Repetimos Ancho * Alto veces 
-    LD HL, 0 +    ld hl, 0 
-    LD D           ; HL = 0 +    ld d           ; HL = 0 
-    LD A, (DS_NUMSPR) +    ld a, (DS_NUMSPR) 
-    LD E           ; DE = DS_NUMSPR+    ld e           ; DE = DS_NUMSPR
 drawsp_mul2: drawsp_mul2:
-    ADD HLDE         ; HL = HL+DS_NUMSPR +    add hlde         ; HL = HL+DS_NUMSPR 
-    DJNZ drawsp_mul2   ; Sumamos HL+DE B veces = DS_NUMSPR*B+    djnz drawsp_mul2   ; Sumamos HL+DE B veces = DS_NUMSPR*B
  
                         ; guardamos el valor de ancho*alto_pixeles*NUMSPR                         ; guardamos el valor de ancho*alto_pixeles*NUMSPR
-    LD (drawsp_width_by_height), HL+    ld (drawsp_width_by_height), hl
  
     ;;; Calculamos direccion origen copia en el sprite     ;;; Calculamos direccion origen copia en el sprite
-    LD BC, (DS_SPRITES) +    ld bc, (DS_SPRITES) 
-    ADD HLBC         ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR*ANCHO*ALTO)+    add hlbc         ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR*ANCHO*ALTO)
                        ; HL contiene la direccion de inicio en el sprite                        ; HL contiene la direccion de inicio en el sprite
  
     ;;; Calculamos las coordenadas destino de pantalla en DE:     ;;; Calculamos las coordenadas destino de pantalla en DE:
  
-    LD BC, (DS_COORD_X)     ; B = Y,  C = X +    ld bc, (DS_COORD_X)     ; B = Y,  C = X 
-    LD AB +    ld ab 
-    AND $18 +    and $18 
-    ADD A, $40 +    add a, $40 
-    LD DA +    ld da 
-    LD AB +    ld ab 
-    AND +    and 
-    RRCA +    rrca 
-    RRCA +    rrca 
-    RRCA +    rrca 
-    ADD AC +    add ac 
-    LD EA +    ld ea 
-    PUSH DE            ; Lo guardamos para luego, lo usaremos para+    push de            ; Lo guardamos para luego, lo usaremos para
                        ; calcular la direccion del atributo                        ; calcular la direccion del atributo
-    EX DEHL          ; Intercambiamos DE y HL (DE=origen, HL=destino)+    ex dehl          ; Intercambiamos DE y HL (DE=origen, HL=destino)
  
  
     ;;; Bucle de impresión vertical     ;;; Bucle de impresión vertical
                         ; Recogemos de nuevo la altura en pixeles                         ; Recogemos de nuevo la altura en pixeles
-    LD A, (drawsp_height) +    ld a, (drawsp_height) 
-    LD B           ; Contador del bucle exterior del bucle+    ld b           ; Contador del bucle exterior del bucle
  
 drawsp_yloop: drawsp_yloop:
-    LD C           ; Nos guardamos el contador+    ld c           ; Nos guardamos el contador
  
     ;;; Bucle de impresion horizontal     ;;; Bucle de impresion horizontal
-    LD A, (DS_WIDTH) +    ld a, (DS_WIDTH) 
-    LD BA+    ld ba
  
-    PUSH HL            ; Guardamos en pila inicio de scanline+    push hl            ; Guardamos en pila inicio de scanline
                         ; para poder volver a el luego                         ; para poder volver a el luego
 drawsp_xloop: drawsp_xloop:
-    LD A, (DE)         ; Leemos dato del sprite +    ld a, (de)         ; Leemos dato del sprite 
-    LD (HL),         ; Copiamos dato a pantalla +    ld (hl),         ; Copiamos dato a pantalla 
-    INC DE             ; Incrementar puntero en sprite +    inc de             ; Incrementar puntero en sprite 
-    INC L              ; Incrementar puntero en pantalla +    inc l              ; Incrementar puntero en pantalla 
-    DJNZ drawsp_xloop +    djnz drawsp_xloop 
-    POP HL             ; Recuperamos de pila inicio de scanline+    pop hl             ; Recuperamos de pila inicio de scanline
  
     ;;; Avanzamos al siguiente scanline de pantalla     ;;; Avanzamos al siguiente scanline de pantalla
-    INC H +    inc h 
-    LD AH +    ld ah 
-    AND +    and 
-    JR NZ, drawspNM_nofix +    jr nz, drawspNM_nofix 
-    LD AL +    ld al 
-    ADD A, 32 +    add a, 32 
-    LD LA +    ld la 
-    JR C, drawspNM_nofix +    jr c, drawspNM_nofix 
-    LD AH +    ld ah 
-    SUB +    sub 
-    LD HA+    ld ha
 drawspNM_nofix: drawspNM_nofix:
  
-    LD BC +    ld bc 
-    DJNZ drawsp_yloop  ; Repetimos "alto_en_pixeles" veces+    djnz drawsp_yloop  ; Repetimos "alto_en_pixeles" veces
  
     ;;; Aqui hemos dibujado todo el sprite, vamos a los attributos     ;;; Aqui hemos dibujado todo el sprite, vamos a los attributos
  
-    POP BC             ; Recuperamos el offset del primer scanline+    pop bc             ; Recuperamos el offset del primer scanline
  
-    ;;; Considerar el dibujado de atributos (Si DS_ATTRIBS=0 -> RET+    ;;; Considerar el dibujado de atributos (Si DS_ATTRIBS=0 -> ret
-    LD  A,[DS_ATTRIBS+1]     ; para obtener la parte alta de la direccion +    ld  a,[DS_ATTRIBS+1]     ; para obtener la parte alta de la direccion 
-    OR                     ; para comprobar si es 0 +    or                     ; para comprobar si es 0 
-    RET Z                    ; Si H = 0, volver (no dibujar atributos)+    ret z                    ; Si H = 0, volver (no dibujar atributos)
  
     ;;; Calcular posicion destino en area de atributos en DE.     ;;; Calcular posicion destino en area de atributos en DE.
-    LD A           ; Codigo de Get_Attr_Offset_From_Image +    ld a           ; Codigo de Get_Attr_Offset_From_Image 
-    RRCA               ; Obtenemos dir de atributo a partir de +    rrca               ; Obtenemos dir de atributo a partir de 
-    RRCA               ; dir de zona de imagen. +    rrca               ; dir de zona de imagen. 
-    RRCA               ; Nos evita volver a obtener X e Y +    rrca               ; Nos evita volver a obtener X e Y 
-    AND 3              ; y hacer el calculo completo de la +    and 3              ; y hacer el calculo completo de la 
-    OR $58             ; direccion en zona de atributos +    or $58             ; direccion en zona de atributos 
-    LD DA +    ld da 
-    LD E           ; DE tiene el offset del attr de HL +    ld e           ; DE tiene el offset del attr de HL 
-    PUSH DE            ; Guardamos una copia+    push de            ; Guardamos una copia
  
     ; Recuperamos el valor de ancho_caracteres * alto_en_pixeles * NUMSPR     ; Recuperamos el valor de ancho_caracteres * alto_en_pixeles * NUMSPR
     ; para ahorrarnos repetir otra vez dos multiplicaciones:     ; para ahorrarnos repetir otra vez dos multiplicaciones:
-    LD HL, (drawsp_width_by_height)+    ld hl, (drawsp_width_by_height)
  
     ;;; HL = ANCHO_BLOQUES*ALTO_PIXELES*NUMSPR     ;;; HL = ANCHO_BLOQUES*ALTO_PIXELES*NUMSPR
     ;;; El Alto lo necesitamos en BLOQUES, no en píxeles-> dividir /8     ;;; El Alto lo necesitamos en BLOQUES, no en píxeles-> dividir /8
-    SRL H     ; Desplazamos H a la derecha +    srl h     ; Desplazamos H a la derecha 
-    RR L      ; Rotamos L a la derecha introduciendo CF +    rr l      ; Rotamos L a la derecha introduciendo CF 
-    SRL H     ; +    srl h     ; 
-    RR L      ; +    rr l      ; 
-    SRL H     ; +    srl h     ; 
-    RR L      ; Resultado : HL = HL >> 3 = HL / 8+    rr l      ; Resultado : HL = HL >> 3 = HL / 8
  
     ;;;; HL = ANCHO_BLOQUES*ALTO_BLOQUES*NUMSPR     ;;;; HL = ANCHO_BLOQUES*ALTO_BLOQUES*NUMSPR
-    LD CL +    ld cl 
-    LD BH +    ld bh 
-    LD HL, (DS_ATTRIBS) +    ld hl, (DS_ATTRIBS) 
-    ADD HLBC         ; HL = Base_Atributos + (DS_NUMSPR*ALTO*ANCHO)+    add hlbc         ; HL = Base_Atributos + (DS_NUMSPR*ALTO*ANCHO)
  
-    POP DE             ; Recuperamos direccion destino+    pop de             ; Recuperamos direccion destino
  
-    LD A, (DS_HEIGHT) +    ld a, (DS_HEIGHT) 
-    LD BA+    ld ba
  
     ;;; Bucle impresion vertical de atributos     ;;; Bucle impresion vertical de atributos
 drawsp_attyloop: drawsp_attyloop:
-    LD CB+    ld cb
  
-    PUSH DE            ; Guardamos inicio de linea de atributos +    push de            ; Guardamos inicio de linea de atributos 
-    LD A, (DS_WIDTH) +    ld a, (DS_WIDTH) 
-    LD BA+    ld ba
  
     ;;; Bucle impresion horizontal de atributos     ;;; Bucle impresion horizontal de atributos
 drawsp_attxloop: drawsp_attxloop:
-    LD A, (HL)         ; Leer atributo del sprite +    ld a, (hl)         ; Leer atributo del sprite 
-    INC HL +    inc hl 
-    LD (DE),         ; Escribir atributo +    ld (de),         ; Escribir atributo 
-    INC E +    inc e 
-    DJNZ  drawsp_attxloop+    djnz  drawsp_attxloop
  
-    POP DE             ; Recuperamos inicio de linea de atributos+    pop de             ; Recuperamos inicio de linea de atributos
  
     ;;; Avance diferencial a la siguiente linea de atributos     ;;; Avance diferencial a la siguiente linea de atributos
-    LD AE +    ld ae 
-    ADD A, 32 +    add a, 32 
-    LD EA +    ld ea 
-    JR NC, drawsp_attrab_noinc +    jr nc, drawsp_attrab_noinc 
-    INC D+    inc d
 drawsp_attrab_noinc: drawsp_attrab_noinc:
  
-    LD BC +    ld bc 
-    DJNZ drawsp_attyloop+    djnz drawsp_attyloop
  
-    RET+    ret
  
 drawsp_height          DB 0 drawsp_height          DB 0
Línea 2380: Línea 2393:
     ORG 35000     ORG 35000
  
-    LD HL, bicho_gfx +    ld hl, bicho_gfx 
-    LD (DS_SPRITES), HL +    ld (DS_SPRITES), hl 
-    LD HL, bicho_attrib +    ld hl, bicho_attrib 
-    LD (DS_ATTRIBS), HL +    ld (DS_ATTRIBS), hl 
-    LD A, 13 +    ld a, 13 
-    LD (DS_COORD_X), A +    ld (DS_COORD_X), a 
-    LD A, 8 +    ld a, 8 
-    LD (DS_COORD_Y), A +    ld (DS_COORD_Y), a 
-    LD A, 2 +    ld a, 2 
-    LD (DS_WIDTH), A +    ld (DS_WIDTH), a 
-    LD A, 2 +    ld a, 2 
-    LD (DS_HEIGHT), A +    ld (DS_HEIGHT), a 
-    XOR A +    xor a 
-    LD (DS_NUMSPR), A+    ld (DS_NUMSPR), a
  
-    CALL DrawSprite_MxN_LD +    call DrawSprite_MxN_LD 
-    RET+    ret
  
 ; Variables que usaremos como parámetros ; Variables que usaremos como parámetros
Línea 2447: Línea 2460:
  
 - cuando vayas a imprimir un caracter consulta primero su entrada en el buffer. - cuando vayas a imprimir un caracter consulta primero su entrada en el buffer.
-Si está libre puedes volcar el gráfico directamente con LD (HL),A... porque es+Si está libre puedes volcar el gráfico directamente con ld (hl),a... porque es
 más rápido y sabes *seguro* que puedes machacar lo que haya en pantalla. Si por más rápido y sabes *seguro* que puedes machacar lo que haya en pantalla. Si por
 el contrario está ocupado (o sea, hay otro sprite en esa posición) sabes que tienes el contrario está ocupado (o sea, hay otro sprite en esa posición) sabes que tienes
Línea 2538: Línea 2551:
     ;;; Recogida de datos y parametros     ;;; Recogida de datos y parametros
     (...)     (...)
-    LD IX, (DS_TEMPBUF)+    ld ix, (DS_TEMPBUF)
     (...)     (...)
  
     ;;; Bucle de impresion del Sprite:     ;;; Bucle de impresion del Sprite:
-    LD B, 8          ; 8 scanlines+    ld b, 8          ; 8 scanlines
  
 drawsp8x8_loopLD: drawsp8x8_loopLD:
-    LD A, (HL)       ; NUEVO: Leemos el valor actual del fondo +    ld a, (hl)       ; NUEVO: Leemos el valor actual del fondo 
-    LD (IX),       ; NUEVO: Lo almacenamos en el array temporal +    ld (ix),       ; NUEVO: Lo almacenamos en el array temporal 
-    INC IX           ; NUEVO: Incrementamos IX (puntero array fondo)+    inc ix           ; NUEVO: Incrementamos IX (puntero array fondo)
  
                         ; Ya podemos imprimir el sprite                         ; Ya podemos imprimir el sprite
-    LD A, (DE)       ; Tomamos el dato del sprite +    ld a, (de)       ; Tomamos el dato del sprite 
-    LD (HL),       ; Establecemos el valor en videomemoria +    ld (hl),       ; Establecemos el valor en videomemoria 
-    INC DE           ; Incrementamos puntero en sprite +    inc de           ; Incrementamos puntero en sprite 
-    INC H            ; Incrementamos puntero en pantalla (scanline+=1) +    inc h            ; Incrementamos puntero en pantalla (scanline+=1) 
-    DJNZ drawsp8x8_loopLD+    djnz drawsp8x8_loopLD
  
     (...)     (...)
     ;;; Impresion de atributos     ;;; Impresion de atributos
-    LD A, (DE)       ; NUEVO: Leemos el atributo actual +    ld a, (de)       ; NUEVO: Leemos el atributo actual 
-    LD (IX),       ; NUEVO: Lo guardamos en el array temporal+    ld (ix),       ; NUEVO: Lo guardamos en el array temporal
  
-    LD A, (HL)       ; Ya podemos imprimir el atributo +    ld a, (hl)       ; Ya podemos imprimir el atributo 
-    LD (DE), A +    ld (de), a 
-    RET+    ret
  
 DS_TEMPBUF   DEFW  0 DS_TEMPBUF   DEFW  0
 </code> </code>
  
- El registro IX no es especialmente rápido (10 t-estados el ''INC IX'' y 19 t-estados el ''LD (IX), A'' o ''LD (IX+0), A'', pero es más rápido utilizarlo que escribir y ejecutar una rutina adicional para almacenar el fondo.+ El registro IX no es especialmente rápido (10 t-estados el ''inc ix'' y 19 t-estados el ''ld (ix), a'' o ''ld (ix+0), a'', pero es más rápido utilizarlo que escribir y ejecutar una rutina adicional para almacenar el fondo.
  
  Cuando necesitemos borrar el sprite, bastará con recuperar el fondo que había antes de imprimirlo, es decir, volcar el contenido del buffer temporal en la posición del sprite a borrar. Para realizar este volcado podemos llamar a una de las funciones de volcado de Sprites basada en transferencias ya que el buffer del fondo en memoria se corresponde con un spriteset de 1 sólo elemento (DS_NUMSPR=0), sin máscara. Las rutinas impresión de 8x8, 16x16 o genéricas basadas en LD servirían para recuperar el contenido del fondo usando como origen el buffer temporal donde lo hemos almacenado.  Cuando necesitemos borrar el sprite, bastará con recuperar el fondo que había antes de imprimirlo, es decir, volcar el contenido del buffer temporal en la posición del sprite a borrar. Para realizar este volcado podemos llamar a una de las funciones de volcado de Sprites basada en transferencias ya que el buffer del fondo en memoria se corresponde con un spriteset de 1 sólo elemento (DS_NUMSPR=0), sin máscara. Las rutinas impresión de 8x8, 16x16 o genéricas basadas en LD servirían para recuperar el contenido del fondo usando como origen el buffer temporal donde lo hemos almacenado.
Línea 2597: Línea 2610:
  
 <code z80> <code z80>
-    LD BC, (DS_SPRITES)+    ld bc, (DS_SPRITES)
 </code> </code>
  
Línea 2603: Línea 2616:
  
 <code z80> <code z80>
-    LD BC, Tabla_Sprites+    ld bc, Tabla_Sprites
 </code> </code>
  
- Establecer BC a un valor inmediato (''LD BC, NN'') tiene un coste de 10 t-estados, frente a los 20 t-estados que cuesta establecerlo desde una posición de memoria (''LD BC, (NN)''). De esta forma evitamos cargar desde memoria las direcciones de los tilesets y también tener que establecerlos al inicio del programa.+ Establecer BC a un valor inmediato (''ld bc, NN'') tiene un coste de 10 t-estados, frente a los 20 t-estados que cuesta establecerlo desde una posición de memoria (''ld bc, (NN)''). De esta forma evitamos cargar desde memoria las direcciones de los tilesets y también tener que establecerlos al inicio del programa.
  
  
Línea 2616: Línea 2629:
  Normalmente no deberíamos requerir de esta operación porque lo normal es estar sincronizado con las interrupciones y realizar el redibujado de los sprites en la ISR o al volver de la ISR de atención a la ULA.  Normalmente no deberíamos requerir de esta operación porque lo normal es estar sincronizado con las interrupciones y realizar el redibujado de los sprites en la ISR o al volver de la ISR de atención a la ULA.
  
- No obstante, si no estamos sincronizados con las interrupciones o si estamos usando IM1, es un dato interesante que nos puede evitar la ejecución de código ISR en medio de la ejecución de la rutina.+ No obstante, si no estamos sincronizados con las interrupciones o si estamos usando im1, es un dato interesante que nos puede evitar la ejecución de código ISR en medio de la ejecución de la rutina.
  
  Trataremos este tema con más detalle en el capítulo dedicado a //Animaciones//, en el apartado sobre //Evitar Flickering o Parpadeos//.  Trataremos este tema con más detalle en el capítulo dedicado a //Animaciones//, en el apartado sobre //Evitar Flickering o Parpadeos//.
Línea 2627: Línea 2640:
  Una opción realmente ingeniosa para optimizar el acceso al sprite sería la de utilizar la pila para realizar operaciones de lectura de 2 bytes del sprite con una única instrucción (''POP'').  Una opción realmente ingeniosa para optimizar el acceso al sprite sería la de utilizar la pila para realizar operaciones de lectura de 2 bytes del sprite con una única instrucción (''POP'').
  
- Apuntando SP a nuestro sprite, un simple ''POP DE'' carga en E y D (en ese orden) los 2 bytes apuntados por SP e incrementa la dirección contenida en DE en 2 unidades, por lo que realizamos 2 operaciones de lectura y 2 incrementos (de 16 bits) en 1 byte de código y 10 t-estados, frente a los 7+7+6+6 = 26 t-estados que cuestan las operaciones LD+INC por separado. Eso implica leer en E y D dos bytes de datos del sprite o el byte de máscara y un byte de datos.+ Apuntando SP a nuestro sprite, un simple ''pop de'' carga en E y D (en ese orden) los 2 bytes apuntados por SP e incrementa la dirección contenida en DE en 2 unidades, por lo que realizamos 2 operaciones de lectura y 2 incrementos (de 16 bits) en 1 byte de código y 10 t-estados, frente a los 7+7+6+6 = 26 t-estados que cuestan las operaciones LD+INC por separado. Eso implica leer en E y D dos bytes de datos del sprite o el byte de máscara y un byte de datos.
  
- Como desventaja, debemos deshabilitar las interrupciones con **DI** al principio del programa y habilitarlas de nuevo antes del RET con ''EI'' ya que no podemos permitir la ejecución de una ISR estando SP apuntando al Sprite. Un ''RST'' o ''CALL'' (así como un ''PUSH'') ejecutado con SP apuntando al Sprite provocaría la corrupción del mismo al introducirse en la pila la dirección de memoria del ''RET'' o ''RETI'' para la ISR.+ Como desventaja, debemos deshabilitar las interrupciones con **di** al principio del programa y habilitarlas de nuevo antes del ret con ''EI'' ya que no podemos permitir la ejecución de una ISR estando SP apuntando al Sprite. Un ''RST'' o ''call'' (así como un ''PUSH'') ejecutado con SP apuntando al Sprite provocaría la corrupción del mismo al introducirse en la pila la dirección de memoria del ''RET'' o ''RETI'' para la ISR.
  
  Veamos el pseudocódigo de una rutina que use la pila de esta forma:  Veamos el pseudocódigo de una rutina que use la pila de esta forma:
  
 <code> <code>
-DI+di
 ; Calcular dirección destino en pantalla ; Calcular dirección destino en pantalla
 ; Calcular dirección origen de sprite ; Calcular dirección origen de sprite
Línea 2646: Línea 2659:
  
 ; Recuperar puntero de pila ; Recuperar puntero de pila
-EI +ei 
-RET+ret
 </code> </code>
  
- A continuación vamos a ver una versión "reducida" (sin impresión de atributos, y con un bucle de 16 iteraciones sin desenrollar) de ''DrawSprite_16x16'' que utiliza la pila para recuperar los datos del sprite mediante ''POP DE''. Esto implica la lectura de 2 bytes en una sóla instrucción y el doble incremento de DE para apuntar a los 2 siguientes elementos.+ A continuación vamos a ver una versión "reducida" (sin impresión de atributos, y con un bucle de 16 iteraciones sin desenrollar) de ''DrawSprite_16x16'' que utiliza la pila para recuperar los datos del sprite mediante ''pop de''. Esto implica la lectura de 2 bytes en una sóla instrucción y el doble incremento de DE para apuntar a los 2 siguientes elementos.
  
- En la rutina guardaremos el valor de SP en una variable temporal, antes de modificarlo, para poder recuperarlo antes del RET. Una vez calculada la dirección de origen del sprite a dibujar, realizaremos la copia de SP en ''DS_TEMP_SP'' (variable de memoria) y cargaremos el valor de HL en SP:+ En la rutina guardaremos el valor de SP en una variable temporal, antes de modificarlo, para poder recuperarlo antes del ret. Una vez calculada la dirección de origen del sprite a dibujar, realizaremos la copia de SP en ''DS_TEMP_SP'' (variable de memoria) y cargaremos el valor de HL en SP:
  
 <code z80> <code z80>
Línea 2666: Línea 2679:
  
     ;;; Deshabilitar interrupciones (vamos a cambiar SP).     ;;; Deshabilitar interrupciones (vamos a cambiar SP).
-    DI+    di
  
     ; Guardamos en BC la pareja (x,y) -> B=COORD_Y y C=COORD_X     ; Guardamos en BC la pareja (x,y) -> B=COORD_Y y C=COORD_X
-    LD BC, (DS_COORD_X)+    ld bc, (DS_COORD_X)
  
     ;;; Calculamos las coordenadas destino de pantalla en DE:     ;;; Calculamos las coordenadas destino de pantalla en DE:
-    LD AB +    ld ab 
-    AND $18 +    and $18 
-    ADD A, $40 +    add a, $40 
-    LD DA +    ld da 
-    LD AB +    ld ab 
-    AND +    and 
-    RRCA +    rrca 
-    RRCA +    rrca 
-    RRCA +    rrca 
-    ADD AC +    add ac 
-    LD EA+    ld ea
  
     ;;; Guardamos el valor actual y correcto de SP     ;;; Guardamos el valor actual y correcto de SP
-    LD (DS_TEMP_SP), SP+    ld (DS_TEMP_sp), sp
  
     ;;; Calcular posicion origen (array sprites) en HL:     ;;; Calcular posicion origen (array sprites) en HL:
-    LD BC, (DS_SPRITES) +    ld bc, (DS_SPRITES) 
-    LD A, (DS_NUMSPR) +    ld a, (DS_NUMSPR) 
-    LD L, 0           ; AL = DS_NUMSPR*256 +    ld l, 0           ; AL = DS_NUMSPR*256 
-    SRL A             ; Desplazamos a la derecha para dividir por dos +    srl a             ; Desplazamos a la derecha para dividir por dos 
-    RR L              ; AL = DS_NUMSPR*128 +    rr l              ; AL = DS_NUMSPR*128 
-    RRA               ; Rotamos, ya que el bit que salio de L al CF fue 0 +    rra               ; Rotamos, ya que el bit que salio de L al CF fue 0 
-    RR L              ; AL = DS_NUMSPR*64 +    rr l              ; AL = DS_NUMSPR*64 
-    RRA               ; Rotamos, ya que el bit que salio de L al CF fue 0 +    rra               ; Rotamos, ya que el bit que salio de L al CF fue 0 
-    RR L              ; AL = DS_NUMSPR*32 +    rr l              ; AL = DS_NUMSPR*32 
-    LD H          ; HL = DS_NUMSPR*32 +    ld h          ; HL = DS_NUMSPR*32 
-    ADD HLBC        ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 32)+    add hlbc        ; HL = BC + HL = DS_SPRITES + (DS_NUMSPR * 32)
                       ; HL contiene la direccion de inicio en el sprite                       ; HL contiene la direccion de inicio en el sprite
  
     ;;; Establecemos el valor de SP apuntando al sprite     ;;; Establecemos el valor de SP apuntando al sprite
-    LD SPHL+    ld sphl
  
-    EX DEHL         ; Intercambiamos DE y HL (DE=origen, HL=destino)+    ex dehl         ; Intercambiamos DE y HL (DE=origen, HL=destino)
  
     ;;; Repetir 8 veces (primeros 2 bloques horizontales):     ;;; Repetir 8 veces (primeros 2 bloques horizontales):
-    LD B, 16+    ld b, 16
  
 drawsp16x16_stack_loop: drawsp16x16_stack_loop:
-    POP DE             ; Recuperamos 2 bytes del sprite+    pop de             ; Recuperamos 2 bytes del sprite
                         ; Y ademas no hace falta sumar 2 a DE                         ; Y ademas no hace falta sumar 2 a DE
-    LD (HL),         ; Ahora imprimimos los 2 bytes en pantalla +    ld (hl),         ; Ahora imprimimos los 2 bytes en pantalla 
-    INC L +    inc l 
-    LD (HL), D +    ld (hl), d 
-    DEC L+    dec l
  
     ; Avanzamos HL 1 scanline (codigo de incremento de HL en 1 scanline)     ; Avanzamos HL 1 scanline (codigo de incremento de HL en 1 scanline)
     ; desde el septimo scanline de la fila Y+1 al primero de la Y+2     ; desde el septimo scanline de la fila Y+1 al primero de la Y+2
  
-    INC H              ; Siguiente scanline +    inc h              ; Siguiente scanline 
-    LD A           ; Ajustar tercio/bloque si necesario +    ld a           ; Ajustar tercio/bloque si necesario 
-    AND +    and 
-    JR NZ, drawsp16_nofix_abajop +    jr nz, drawsp16_nofix_abajop 
-    LD AL +    ld al 
-    ADD A, 32 +    add a, 32 
-    LD LA +    ld la 
-    JR C, drawsp16_nofix_abajop +    jr c, drawsp16_nofix_abajop 
-    LD AH +    ld ah 
-    SUB +    sub 
-    LD HA+    ld ha
  
 drawsp16_nofix_abajop: drawsp16_nofix_abajop:
-    DJNZ drawsp16x16_stack_loop+    djnz drawsp16x16_stack_loop
  
     ;;; En este punto, los 16 scanlines del sprite estan dibujados.     ;;; En este punto, los 16 scanlines del sprite estan dibujados.
  
     ;;; Recuperamos el valor de SP     ;;; Recuperamos el valor de SP
-    LD HL, (DS_TEMP_SP) +    ld hl, (DS_TEMP_SP) 
-    LD SPHL+    ld sphl
  
-    EI                  ; Habilitar de nuevo interrupciones +    ei                  ; Habilitar de nuevo interrupciones 
-    RET+    ret
 </code> </code>
  
- Al igual que en el caso de las rutinas anteriores, si desenrollamos el bucle de 16 iteraciones en 2 bucles de 8, podemos utilizar **INC H** dentro de los bucles y el código de //Scanline_Abajo_HL// entre ambos. El bucle convertido en dos quedaría de la siguiente forma:+ Al igual que en el caso de las rutinas anteriores, si desenrollamos el bucle de 16 iteraciones en 2 bucles de 8, podemos utilizar **inc h** dentro de los bucles y el código de //Scanline_Abajo_HL// entre ambos. El bucle convertido en dos quedaría de la siguiente forma:
  
 <code z80> <code z80>
 drawsp16x16_stack_loop1: drawsp16x16_stack_loop1:
-    POP DE             ; Recuperamos 2 bytes del sprite+    pop de             ; Recuperamos 2 bytes del sprite
                        ; Y ademas no hace falta sumar 2 a DE                        ; Y ademas no hace falta sumar 2 a DE
-    LD (HL),         ; Ahora imprimimos los 2 bytes en pantalla +    ld (hl),         ; Ahora imprimimos los 2 bytes en pantalla 
-    INC L              ; de la parte superior del Sprite +    inc l              ; de la parte superior del Sprite 
-    LD (HL), D +    ld (hl), d 
-    DEC L +    dec l 
-    INC H +    inc h 
-    DJNZ drawsp16x16_stack_loop1+    djnz drawsp16x16_stack_loop1
  
     ; Avanzamos HL 1 scanline (codigo de incremento de HL en 1 scanline)     ; Avanzamos HL 1 scanline (codigo de incremento de HL en 1 scanline)
     ; desde el septimo scanline de la fila Y+1 al primero de la Y+2     ; desde el septimo scanline de la fila Y+1 al primero de la Y+2
     ; No hay que comprobar si (H & $07) == 0 porque sabemos que lo es     ; No hay que comprobar si (H & $07) == 0 porque sabemos que lo es
-    LD AL +    ld al 
-    ADD A, 32 +    add a, 32 
-    LD LA +    ld la 
-    JR C, drawsp16_nofix_abajop +    jr c, drawsp16_nofix_abajop 
-    LD AH +    ld ah 
-    SUB +    sub 
-    LD HA+    ld ha
 drawsp16_nofix_abajop: drawsp16_nofix_abajop:
  
-   LD B,8+   ld b,8
 drawsp16x16_stack_loop2: drawsp16x16_stack_loop2:
-    POP DE             ; Recuperamos 2 bytes del sprite+    pop de             ; Recuperamos 2 bytes del sprite
                        ; Y ademas no hace falta sumar 2 a DE                        ; Y ademas no hace falta sumar 2 a DE
-    LD (HL),         ; Ahora imprimimos los 2 bytes en pantalla +    ld (hl),         ; Ahora imprimimos los 2 bytes en pantalla 
-    INC L              ; de la parte inferior del Sprite +    inc l              ; de la parte inferior del Sprite 
-    LD (HL), D +    ld (hl), d 
-    DEC L +    dec l 
-    INC H +    inc h 
-    DJNZ drawsp16x16_stack_loop2+    djnz drawsp16x16_stack_loop2
 </code> </code>
  
Línea 2820: Línea 2833:
     ; SP apunta a nuestro sprite     ; SP apunta a nuestro sprite
     ; HL apunta ya a la pantalla, lugar de dibujado     ; HL apunta ya a la pantalla, lugar de dibujado
-    LD B, ANCHO+    ld b, ANCHO
  
 LOOP: LOOP:
-    POP DE          ; Leemos mascara y datos graficos +    pop de          ; Leemos mascara y datos graficos 
-    LD A, (HL)      ; Leemos dato en la memoria +    ld a, (hl)      ; Leemos dato en la memoria 
-    AND E +    and e 
-    OR D +    or d 
-    LD (HL),      ; Aplicamos la mascara y guardamos en pantalla +    ld (hl),      ; Aplicamos la mascara y guardamos en pantalla 
-    INC HL          ; Avanzamos puntero de dibujado en pantalla +    inc hl          ; Avanzamos puntero de dibujado en pantalla 
-    DJNZ LOOP+    djnz LOOP
 </code> </code>
  
  • cursos/ensamblador/gfx3_sprites_lowres.1705567901.txt.gz
  • Última modificación: 18-01-2024 08:51
  • por sromero