cursos:ensamblador:gfx4_fuentes

Diferencias

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

Enlace a la vista de comparación

Ambos lados, revisión anteriorRevisión previa
Próxima revisión
Revisión previa
cursos:ensamblador:gfx4_fuentes [03-01-2024 08:38] sromerocursos:ensamblador:gfx4_fuentes [19-01-2024 08:23] (actual) sromero
Línea 3: Línea 3:
  En prácticamente cualquier programa o juego nos encontraremos con la necesidad de imprimir en pantalla texto y datos numéricos en diferentes posiciones de pantalla: los menúes, los nombres de los niveles, los marcadores y puntuaciones, etc.  En prácticamente cualquier programa o juego nos encontraremos con la necesidad de imprimir en pantalla texto y datos numéricos en diferentes posiciones de pantalla: los menúes, los nombres de los niveles, los marcadores y puntuaciones, etc.
  
- La impresión de fuentes de texto es una aplicación directa de las rutinas de impresión de sprites en baja resolución que creamos en el capítulo anterior. Cada carácter es un sprite de 8x8 píxeles a dibujar en coordenadas (c,f) de baja resolución.+ La impresión de fuentes de texto es una aplicación directa de las rutinas de impresión de sprites en baja resolución que creamos en el capítulo anterior. Cada carácter es un sprite de 8x8 píxeles a dibujar en coordenadas (COLUMNAFILA) de baja resolución.
  
  Crearemos una rutina de impresión de caracteres y, basada en esta, una rutina de impresión de cadenas, con la posibilidad de añadir códigos de control y de formato, e impresión de datos numéricos o variables decimales, hexadecimales, binarias y de cadena.  Crearemos una rutina de impresión de caracteres y, basada en esta, una rutina de impresión de cadenas, con la posibilidad de añadir códigos de control y de formato, e impresión de datos numéricos o variables decimales, hexadecimales, binarias y de cadena.
Línea 11: Línea 11:
  Finalmente, examinaremos una fuente de texto de 4x8 píxeles que nos permitirá una resolución en pantalla de 64 columnas de caracteres en pantalla.  Finalmente, examinaremos una fuente de texto de 4x8 píxeles que nos permitirá una resolución en pantalla de 64 columnas de caracteres en pantalla.
  
-\\  +\\ 
-\\ +\\
 ===== Fuentes de texto 8x8 ===== ===== Fuentes de texto 8x8 =====
  
  Si creamos un juego gráfico de caracteres como un tileset de sprites de 8x8 píxeles cada uno conteniendo una letra del alfabeto, podemos utilizar las rutinas de impresión de sprites de 8x8 para trazar en pantalla cualquier carácter (visto como un sprite de 1x1 bloques).  Si creamos un juego gráfico de caracteres como un tileset de sprites de 8x8 píxeles cada uno conteniendo una letra del alfabeto, podemos utilizar las rutinas de impresión de sprites de 8x8 para trazar en pantalla cualquier carácter (visto como un sprite de 1x1 bloques).
  
-\\ +\\
 {{ :cursos:ensamblador:gfx4_sevenup01.png | Parte de un charset en SevenuP}} {{ :cursos:ensamblador:gfx4_sevenup01.png | Parte de un charset en SevenuP}}
 ;#; ;#;
 //Parte de un tileset de caracteres en SevenuP// //Parte de un tileset de caracteres en SevenuP//
 ;#; ;#;
-\\ +\\
  
  Ubicando las letras en el spriteset con el mismo orden que el código ASCII (empezando por el espacio, código 32, como sprite cero), podemos utilizar el código ASCII de la letra a imprimir como número de sprite dentro del tileset.  Ubicando las letras en el spriteset con el mismo orden que el código ASCII (empezando por el espacio, código 32, como sprite cero), podemos utilizar el código ASCII de la letra a imprimir como número de sprite dentro del tileset.
  
-\\ +\\
 {{ :cursos:ensamblador:gfx4_asciiwikipedia.png |}} {{ :cursos:ensamblador:gfx4_asciiwikipedia.png |}}
 ;#; ;#;
 //Los 95 caracteres imprimibles (96 en el Spectrum) del código ASCII.\\ Fuente: Wikipedia// //Los 95 caracteres imprimibles (96 en el Spectrum) del código ASCII.\\ Fuente: Wikipedia//
 ;#; ;#;
-\\ +\\
  
  Podríamos diseñar una fuente de 256 caracteres en el editor de Sprites, pero se requerirían 256*8 = 4096 bytes (4KB!) para almacenar los datos en memoria. En realidad, no es necesario crear 256 caracteres en el editor, puesto que en el Spectrum:  Podríamos diseñar una fuente de 256 caracteres en el editor de Sprites, pero se requerirían 256*8 = 4096 bytes (4KB!) para almacenar los datos en memoria. En realidad, no es necesario crear 256 caracteres en el editor, puesto que en el Spectrum:
Línea 40: Línea 40:
   * Los códigos ASCII del 165 al 255 son los códigos de control (//tokens//) de las instrucciones de BASIC en modo 48K (del 163 al 255 en modo 128K).   * Los códigos ASCII del 165 al 255 son los códigos de control (//tokens//) de las instrucciones de BASIC en modo 48K (del 163 al 255 en modo 128K).
  
-\\ +\\
 {{ :cursos:ensamblador:gfx4_zxcharset.png | Juego de caracteres del Spectrum 32-164}} {{ :cursos:ensamblador:gfx4_zxcharset.png | Juego de caracteres del Spectrum 32-164}}
 ;#; ;#;
 //Códigos ASCII en el Spectrum desde el 32 al 164\\ Fuente: Wikipedia// //Códigos ASCII en el Spectrum desde el 32 al 164\\ Fuente: Wikipedia//
 ;#; ;#;
-\\ +\\
  
  Como hay un total de 96 caracteres de texto imprimibles (desde el 32 al 127), para disponer de un juego de caracteres suficiente con minúsculas, mayúsculas, signos de puntuación y dígitos nos bastaría con una fuente de 96 sprites de 8 bytes cada uno, es decir, un total de 768 bytes.  Como hay un total de 96 caracteres de texto imprimibles (desde el 32 al 127), para disponer de un juego de caracteres suficiente con minúsculas, mayúsculas, signos de puntuación y dígitos nos bastaría con una fuente de 96 sprites de 8 bytes cada uno, es decir, un total de 768 bytes.
  
- Podemos agregar "caracteres" adicionales para disponer de códigos de control que nos permitan imprimir vocales con acentos, eñes, cedilla (ç), etc. Al definir los textos de nuestro programa habría que utilizar estos códigos de control en formato numérico (DB) intercalados con el texto ASCII ("España" = DB "Espa", codigo_enye, "a").+ Podemos agregar "caracteres" adicionales para disponer de códigos de control que nos permitan imprimir vocales con acentos, eñes, cedilla (ç), etc. Al definir los textos de nuestro programa habría que utilizar estos códigos de control en formato numérico (DB) intercalados con el texto ASCII ("España"''DB "Espa", codigo_enye, "a'').
  
  Si tenemos problemas de espacio en nuestro programa también podemos utilizar un set de caracteres más reducido que acaben en el ASCII 'Z' (ASCII 90), lo que nos dejaría un charset con números, signos de puntuación y letras mayúsculas (sin minúsculas). El espacio ocupado por este spriteset sería de 90-32=58 caracteres, es decir, 464 bytes.  Si tenemos problemas de espacio en nuestro programa también podemos utilizar un set de caracteres más reducido que acaben en el ASCII 'Z' (ASCII 90), lo que nos dejaría un charset con números, signos de puntuación y letras mayúsculas (sin minúsculas). El espacio ocupado por este spriteset sería de 90-32=58 caracteres, es decir, 464 bytes.
Línea 59: Línea 59:
  Para los ejemplos de este capítulo utilizaremos una fuente personalizada de texto de 64 caracteres (signos de puntuación, dígitos numéricos y letras mayúsculas, incluídas 5 vocales con acentos) dibujada por Javier Vispe y convertida a código con SevenuP:  Para los ejemplos de este capítulo utilizaremos una fuente personalizada de texto de 64 caracteres (signos de puntuación, dígitos numéricos y letras mayúsculas, incluídas 5 vocales con acentos) dibujada por Javier Vispe y convertida a código con SevenuP:
  
-\\  +\\ 
-{{ :cursos:ensamblador:gfx4_charset1.png | Fuente de ejemplo }} +{{ :cursos:ensamblador:gfx4_charset1.png?640 | Fuente de ejemplo }} 
-\\ +\\
  
  Exportando los datos en SevenuP en formato //X Char, Char Line, Y Char// como //Gfx only//, obtenemos el siguiente fichero .asm:  Exportando los datos en SevenuP en formato //X Char, Char Line, Y Char// como //Gfx only//, obtenemos el siguiente fichero .asm:
Línea 77: Línea 77:
  
 charset1: charset1:
-   DEFB   0,  0,  0,  0,  0,  0,  0,  0,   0, 24, 24, 24, 24,  0, 24,  0 +    DEFB   0,  0,  0,  0,  0,  0,  0,  0,   0, 24, 24, 24, 24,  0, 24,  0 
-   DEFB   0,108,108, 72,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0 +    DEFB   0,108,108, 72,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0 
-   DEFB   0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0 +    DEFB   0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0 
-   DEFB  56,  0, 76, 56,110,196,122,  0,   0, 12, 12,  8,  0,  0,  0,  0 +    DEFB  56,  0, 76, 56,110,196,122,  0,   0, 12, 12,  8,  0,  0,  0,  0 
-   DEFB   0, 24,  0, 48, 48, 48, 24,  0,   0, 24,  0, 12, 12, 12, 24,  0 +    DEFB   0, 24,  0, 48, 48, 48, 24,  0,   0, 24,  0, 12, 12, 12, 24,  0 
-   DEFB   0,  0,  0,  0,  0,  0,  0,  0,   0, 24,  0,126,126, 24, 24,  0 +    DEFB   0,  0,  0,  0,  0,  0,  0,  0,   0, 24,  0,126,126, 24, 24,  0 
-   DEFB   0,  0,  0,  0,  0, 12, 12,  8,   0,  0,  0,126,126,  0,  0,  0 +    DEFB   0,  0,  0,  0,  0, 12, 12,  8,   0,  0,  0,126,126,  0,  0,  0 
-   DEFB   0,  0,  0,  0,  0, 56, 56,  0,   0,  0,  6, 12, 24, 48, 96,  0 +    DEFB   0,  0,  0,  0,  0, 56, 56,  0,   0,  0,  6, 12, 24, 48, 96,  0 
-   DEFB 124,  0,206,214,230,254,124,  0,  28,  0,124, 28, 28, 28, 28,  0 +    DEFB 124,  0,206,214,230,254,124,  0,  28,  0,124, 28, 28, 28, 28,  0 
-   DEFB 124,  0,198, 28,112,254,254,  0, 124,  0,198, 12,198,254,124, +    DEFB 124,  0,198, 28,112,254,254,  0, 124,  0,198, 12,198,254,124, 
-   DEFB  12,  0, 60,108,254,254, 12,  0, 254,  0,192,252, 14,254,252, +    DEFB  12,  0, 60,108,254,254, 12,  0, 254,  0,192,252, 14,254,252, 
-   DEFB  60,  0,224,252,198,254,124,  0, 254,  0, 14, 28, 28, 56, 56,  0 +    DEFB  60,  0,224,252,198,254,124,  0, 254,  0, 14, 28, 28, 56, 56,  0 
-   DEFB 124,  0,198,124,198,254,124,  0, 124,  0,198,126,  6,126,124, +    DEFB 124,  0,198,124,198,254,124,  0, 124,  0,198,126,  6,126,124, 
-   DEFB   0,  0, 24, 24,  0, 24, 24,  0, 118,  6,192,254,254,198,198, +    DEFB   0,  0, 24, 24,  0, 24, 24,  0, 118,  6,192,254,254,198,198, 
-   DEFB 246,  6,192,252,192,254,254,  0,  12, 12, 48, 56, 56, 56, 56,  0 +    DEFB 246,  6,192,252,192,254,254,  0,  12, 12, 48, 56, 56, 56, 56,  0 
-   DEFB 118,  6,192,198,198,254,124,  0, 198,  6,192,198,198,254,124, +    DEFB 118,  6,192,198,198,254,124,  0, 198,  6,192,198,198,254,124, 
-   DEFB   0, 24,  0, 24, 24, 24, 24,  0, 124,  0,198,254,254,198,198, +    DEFB   0, 24,  0, 24, 24, 24, 24,  0, 124,  0,198,254,254,198,198, 
-   DEFB 252,  0,198,252,198,254,252,  0, 124,  0,198,192,198,254,124, +    DEFB 252,  0,198,252,198,254,252,  0, 124,  0,198,192,198,254,124, 
-   DEFB 252,  0,198,198,198,254,252,  0, 254,  0,192,252,192,254,254, +    DEFB 252,  0,198,198,198,254,252,  0, 254,  0,192,252,192,254,254, 
-   DEFB 254,  0,224,252,224,224,224,  0, 124,  0,192,206,198,254,124, +    DEFB 254,  0,224,252,224,224,224,  0, 124,  0,192,206,198,254,124, 
-   DEFB 198,  0,198,254,254,198,198,  0,  56,  0, 56, 56, 56, 56, 56,  0 +    DEFB 198,  0,198,254,254,198,198,  0,  56,  0, 56, 56, 56, 56, 56,  0 
-   DEFB   6,  0,  6,  6,198,254,124,  0, 198,  0,220,248,252,206,198, +    DEFB   6,  0,  6,  6,198,254,124,  0, 198,  0,220,248,252,206,198, 
-   DEFB 224,  0,224,224,224,254,254,  0, 198,  0,254,254,214,198,198, +    DEFB 224,  0,224,224,224,254,254,  0, 198,  0,254,254,214,198,198, 
-   DEFB 198,  0,246,254,222,206,198,  0, 124,  0,198,198,198,254,124, +    DEFB 198,  0,246,254,222,206,198,  0, 124,  0,198,198,198,254,124, 
-   DEFB 252,  0,198,254,252,192,192,  0, 124,  0,198,198,198,252,122, +    DEFB 252,  0,198,254,252,192,192,  0, 124,  0,198,198,198,252,122, 
-   DEFB 252,  0,198,254,252,206,198,  0, 126,  0,224,124,  6,254,252, +    DEFB 252,  0,198,254,252,206,198,  0, 126,  0,224,124,  6,254,252, 
-   DEFB 254,  0, 56, 56, 56, 56, 56,  0, 198,  0,198,198,198,254,124, +    DEFB 254,  0, 56, 56, 56, 56, 56,  0, 198,  0,198,198,198,254,124, 
-   DEFB 198,  0,198,198,238,124, 56,  0, 198,  0,198,198,214,254,108, +    DEFB 198,  0,198,198,238,124, 56,  0, 198,  0,198,198,214,254,108, 
-   DEFB 198,  0,124, 56,124,238,198,  0, 198,  0,238,124, 56, 56, 56,  0 +    DEFB 198,  0,124, 56,124,238,198,  0, 198,  0,238,124, 56, 56, 56,  0 
-   DEFB 254,  0, 28, 56,112,254,254,  0,  60,102,219,133,133,219,102, 60 +    DEFB 254,  0, 28, 56,112,254,254,  0,  60,102,219,133,133,219,102, 60 
-   DEFB   0,  0, 96, 48, 24, 12,  6,  0,  24,  0, 24, 48, 96,102, 60,  0 +    DEFB   0,  0, 96, 48, 24, 12,  6,  0,  24,  0, 24, 48, 96,102, 60,  0 
-   DEFB  60,  0, 70, 12, 24,  0, 24,  0,   0,  0,  0,  0,  0,  0,  0,126+    DEFB  60,  0, 70, 12, 24,  0, 24,  0,   0,  0,  0,  0,  0,  0,  0,126
 </code> </code>
  
-\\ +\\
 ==== PrintChar_8x8 ==== ==== PrintChar_8x8 ====
  
- La rutina de impresión de caracteres debe de recoger el código ASCII a dibujar y realizar el cálculo para posicionar un puntero "origen" dentro del tileset contra el ASCII correspondiente. También debe calcular la dirección destino en la pantalla en base a las coordenadas en baja resolución. Una vez trazado el carácter, establecerá el atributo del mismo con el valor contenido en FONT_ATTRIB.+ La rutina de impresión de caracteres debe de recoger el código ASCII a dibujar y realizar el cálculo para posicionar un puntero "origen" dentro del tileset contra el ASCII correspondiente. También debe calcular la dirección destino en la pantalla en base a las coordenadas en baja resolución. Una vez trazado el carácter, establecerá el atributo del mismo con el valor contenido en ''FONT_ATTRIB''.
  
 <code z80> <code z80>
Línea 131: Línea 131:
 PrintChar_8x8: PrintChar_8x8:
  
-   LD BC, (FONT_X)      ; B = Y,  C = X +    ld bc, (FONT_X)      ; B = Y,  C = X 
-   EX AFAF          ; Nos guardamos el caracter en A'+    ex afaf          ; Nos guardamos el caracter en A'
  
-   ;;; 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 E             ; DE contiene ahora la direccion destino.+    ld e             ; DE contiene ahora la direccion destino.
  
-   ;;; Calcular posicion origen (array sprites) en HL como: +    ;;; Calcular posicion origen (array sprites) en HL como: 
-   ;;;     direccion = base_sprites + (NUM_SPRITE*8) +    ;;;     direccion = base_sprites + (NUM_SPRITE*8) 
-   EX AFAF          ; Recuperamos el caracter a dibujar de A' +    ex afaf          ; Recuperamos el caracter a dibujar de A' 
-   LD BC, (FONT_CHARSET) +    ld bc, (FONT_CHARSET) 
-   LD H, 0 +    ld h, 0 
-   LD LA +    ld la 
-   ADD HLHL +    add hlhl 
-   ADD HLHL +    add hlhl 
-   ADD HLHL +    add hlhl 
-   ADD HLBC         ; HL = BC + HL = FONT_CHARSET + (A * 8)+    add hlbc         ; HL = BC + HL = FONT_CHARSET + (A * 8)
  
-   EX DEHL          ; Intercambiamos DE y HL (DE=origen, HL=destino)+    ex dehl          ; Intercambiamos DE y HL (DE=origen, HL=destino)
  
-   ;;; Dibujar 7 scanlines (DE) -> (HL) y bajar scanline (y DE++) +    ;;; Dibujar 7 scanlines (DE) -> (HL) y bajar scanline (y DE++) 
-   LD B, 7            ; 7 scanlines a dibujar+    ld b, 7            ; 7 scanlines a dibujar
  
 drawchar8_loop: drawchar8_loop:
-   LD A, (DE)         ; Tomamos el dato del caracter +    ld a, (de)         ; Tomamos el dato del caracter 
-   LD (HL),         ; Establecemos el valor en videomemoria +    ld (hl),         ; Establecemos el valor en videomemoria 
-   INC DE             ; Incrementamos puntero en caracter +    inc de             ; Incrementamos puntero en caracter 
-   INC H              ; Incrementamos puntero en pantalla (scanline+=1) +    inc h              ; Incrementamos puntero en pantalla (scanline+=1) 
-   DJNZ drawchar8_loop+    djnz drawchar8_loop
  
-   ;;; La octava iteracion (8o scanline) aparte, para evitar los INCs +    ;;; La octava iteracion (8o scanline) aparte, para evitar los INCs 
-   LD A, (DE)         ; Tomamos el dato del caracter +    ld a, (de)         ; Tomamos el dato del caracter 
-   LD (HL),         ; Establecemos el valor en videomemoria+    ld (hl),         ; Establecemos el valor en videomemoria
  
-   LD A           ; Recuperamos el valor inicial de HL +    ld a           ; Recuperamos el valor inicial de HL 
-   SUB 7              ; Restando los 7 "INC H"'s realizados+    sub 7              ; Restando los 7 "inc h"'s realizados
  
-   ;;; Calcular posicion destino en area de atributos en DE. +    ;;; Calcular posicion destino en area de atributos en DE. 
-                      ; Tenemos A = H +                       ; Tenemos A = H 
-   RRCA               ; Codigo de Get_Attr_Offset_From_Image +    rrca               ; Codigo de Get_Attr_Offset_From_Image 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   AND +    and 
-   OR $58 +    or $58 
-   LD DA +    ld da 
-   LD EL+    ld el
  
-   ;;; Escribir el atributo en memoria +    ;;; Escribir el atributo en memoria 
-   LD A, (FONT_ATTRIB) +    ld a, (FONT_ATTRIB) 
-   LD (DE),         ; Escribimos el atributo en memoria +    ld (de),         ; Escribimos el atributo en memoria 
-   RET+    ret
 </code> </code>
  
- La rutina es muy parecida a las que ya vimos en el capítulo anterior para impresión de sprites 8x8, pero eliminando el cálculo del atributo origen al establecerlo desde la variable FONT_ATTRIB.+ La rutina es muy parecida a las que ya vimos en el capítulo anterior para impresión de sprites 8x8, pero eliminando el cálculo del atributo origen al establecerlo desde la variable ''FONT_ATTRIB''.
  
- Además, hemos semi-desenrollado el bucle de impresión para hacer 7 iteraciones y después escribir el último scanline fuera del bucle. De esta forma evitamos el INC DE INC H que se realizaría para el último scanline y que es innecesario. De nuevo, desenrollar totalmente el bucle sería lo más óptimo, ya que evitaríamos el "LD B, 7y el "DJNZ".+ Además, hemos semi-desenrollado el bucle de impresión para hacer 7 iteraciones y después escribir el último scanline fuera del bucle. De esta forma evitamos el ''inc de'' ''inc h'' que se realizaría para el último scanline y que es innecesario. De nuevo, desenrollar totalmente el bucle sería lo más óptimo, ya que evitaríamos el ''ld b, 7'' y el ''DJNZ''.
  
  La impresión se realiza por transferencia, pero podemos convertirla en impresión por operación lógica cambiando los:  La impresión se realiza por transferencia, pero podemos convertirla en impresión por operación lógica cambiando los:
  
 <code z80> <code z80>
-   LD A, (DE)       ; Tomamos el dato del caracter +    ld a, (de)       ; Tomamos el dato del caracter 
-   LD (HL),       ; Establecemos el valor en videomemoria+    ld (hl),       ; Establecemos el valor en videomemoria
 </code> </code>
  
Línea 207: Línea 207:
  
 <code z80> <code z80>
-   LD A, (DE)       ; Tomamos el dato del caracter +    ld a, (de)       ; Tomamos el dato del caracter 
-   OR (HL)          ; Hacemos OR entre dato y pantalla +    or (hl)          ; Hacemos OR entre dato y pantalla 
-   LD (HL),       ; Establecemos el valor en videomemoria+    ld (hl),       ; Establecemos el valor en videomemoria
 </code> </code>
  
- Para llamar a nuestra nueva rutina establecemos los valores de impresión en las variables de memoria y realizamos el CALL correspondiente. Aunque en esta rutina podríamos utilizar el paso por registros, vamos a emplear paso de parámetros por variables de memoria por 2 motivos:+ Para llamar a nuestra nueva rutina establecemos los valores de impresión en las variables de memoria y realizamos el ''call'' correspondiente. Aunque en esta rutina podríamos utilizar el paso por registros, vamos a emplear paso de parámetros por variables de memoria por 2 motivos:
  
   * Para que sean utilizables desde BASIC.   * Para que sean utilizables desde BASIC.
   * Para crear un sistema de coordenadas de texto X, Y y de atributos que nos permita, como veremos más adelante en el capítulo, gestionar la impresión de texto de una manera más cómoda.   * Para crear un sistema de coordenadas de texto X, Y y de atributos que nos permita, como veremos más adelante en el capítulo, gestionar la impresión de texto de una manera más cómoda.
  
- Nótese que nuestro set de caracteres empieza en el ASCII 32 (espacio) el cual se corresponde con el sprite 0 (sprite en blanco). En teoría, la rutina debería restar 32 a cada código ASCII recibido en el registro A para encontrar el identificador de sprite correcto en el tileset. + Nótese que nuestro set de caracteres empieza en el ASCII 32 (espacio) el cual se corresponde con el sprite 0 (sprite en blanco). En teoría, la rutina debería restar 32 a cada código ASCII recibido en el registro A para encontrar el identificador de sprite correcto en el tileset.
  
- En lugar de realizar una resta en cada impresión, podemos establecer el valor de FONT_CHARSET a la dirección del array menos 256 (32*8), con lo que cuando se calcule la dirección origen usando el ASCII real estaremos accediendo al sprite correcto.+ En lugar de realizar una resta en cada impresión, podemos establecer el valor de ''FONT_CHARSET'' a la dirección del array menos 256 (32*8), con lo que cuando se calcule la dirección origen usando el ASCII real estaremos accediendo al sprite correcto.
  
- Así, asignando a FONT_CHARSET el valor de "charset1-256", cuando solicitemos imprimir el ASCII 32, estaremos accediendo realmente a charset1-256+(32*8) = charset1-256+256 = charset1. De esta forma no necesitamos restar 32 a ningun carácter ASCII para que la rutina imprima el carácter correspondiente real a un valor ASCII.+ Así, asignando a ''FONT_CHARSET'' el valor de "charset1-256", cuando solicitemos imprimir el ASCII 32, estaremos accediendo realmente a charset1-256+(32*8) = charset1-256+256 = charset1. De esta forma no necesitamos restar 32 a ningun carácter ASCII para que la rutina imprima el carácter correspondiente real a un valor ASCII.
  
  El código de asignación de variables iniciales y llamada a la función sería pues similar al siguiente:  El código de asignación de variables iniciales y llamada a la función sería pues similar al siguiente:
  
 <code z80> <code z80>
-  ;;; Establecimiento de valores iniciales+    ;;; Establecimiento de valores iniciales
  
-  LD HL, charset1-256        ; Saltamos los 32 caracteres iniciales +    ld hl, charset1-256        ; Saltamos los 32 caracteres iniciales 
-  LD (FONT_CHARSET), HL      ; Establecemos el valor del charset +    ld (FONT_CHARSET), hl      ; Establecemos el valor del charset 
-  LD A, 6 +    ld a, 6 
-  LD (FONT_ATTRIB),        ; Color amarillo sobre negro+    ld (FONT_ATTRIB),        ; Color amarillo sobre negro
  
-  ;;; Impresion de un caracter "X" en 15,8   +    ;;; Impresion de un caracter "X" en 15,8 
-  LD A, 15 +    ld a, 15 
-  LD (FONT_X), A +    ld (FONT_X), a 
-  LD A, 8 +    ld a, 8 
-  LD (FONT_Y), A +    ld (FONT_Y), a 
-  LD A, 'X' +    ld a, 'X' 
-  CALL PrintChar_8x8         ; Imprimimos el caracter+    call PrintChar_8x8         ; Imprimimos el caracter
 </code> </code>
  
- **Nótese que esta implementación de PrintChar_8x8 modifica registros y no los preserva, por lo que si tenemos que salvaguardar el valor de algún registro, debemos realizar PUSH antes de llamar a la función y POP al volver de ella**.+ **Nótese que esta implementación de ''PrintChar_8x8'' modifica registros y no los preserva, por lo que si tenemos que salvaguardar el valor de algún registro, debemos realizar ''PUSH'' antes de llamar a la función y ''POP'' al volver de ella**.
  
  El siguiente ejemplo (donde se ha omitido el código de las funciones ya vistas hasta ahora) muestra el charset 8x8 de 64 caracteres en pantalla, en un bucle de 4 líneas de 16 caracteres cada una:  El siguiente ejemplo (donde se ha omitido el código de las funciones ya vistas hasta ahora) muestra el charset 8x8 de 64 caracteres en pantalla, en un bucle de 4 líneas de 16 caracteres cada una:
  
 <code z80> <code z80>
-  ; Visualizacion del charset de ejemplo +; Visualizacion del charset de ejemplo 
-  ORG 32768+    ORG 35000
  
-  LD H, 0 +    ld h, 0 
-  LD LA +    ld la 
-  CALL ClearScreenAttrib     ; Borramos la pantalla+    call ClearScreenAttrib     ; Borramos la pantalla
  
-  LD HL, charset1-256        ; Saltamos los 32 caracteres iniciales +    ld hl, charset1-256        ; Saltamos los 32 caracteres iniciales 
-  LD (FONT_CHARSET), HL +    ld (FONT_CHARSET), hl 
-  XOR A +    xor a 
-  LD (FONT_X), A +    ld (FONT_X), a 
-  INC A +    inc a 
-  LD (FONT_Y),             ; Empezamos en (0,1) +    ld (FONT_Y),             ; Empezamos en (0,1) 
-  LD A, 6 +    ld a, 6 
-  LD (FONT_ATTRIB),        ; Color amarillo sobre negro+    ld (FONT_ATTRIB),        ; Color amarillo sobre negro
  
-  LD C, 32                   ; Empezamos en caracter 32+    ld c, 32                   ; Empezamos en caracter 32
  
-  ;;; Bucle vertical +    ;;; Bucle vertical 
-  LD E, 4+    ld e, 4
  
 bucle_y: bucle_y:
  
-  ;;; Bucle horizontal +    ;;; Bucle horizontal 
-  LD B, 16                   ; Imprimimos 16 caracteres horiz+    ld b, 16                   ; Imprimimos 16 caracteres horiz
  
 bucle_x: bucle_x:
-  LD A, (FONT_X) +    ld a, (FONT_X) 
-  INC A +    inc a 
-  LD (FONT_X),              ; X = X + 1+    ld (FONT_X),              ; X = X + 1
  
-  LD AC +    ld ac 
-  PUSH BC +    push bc 
-  PUSH DE                     ; Preservamos registros +    push de                     ; Preservamos registros 
-  CALL PrintChar_8x8          ; Imprimimos el caracter "C" +    call PrintChar_8x8          ; Imprimimos el caracter "C" 
-  POP DE +    pop de 
-  POP BC+    pop bc
  
-  INC C                       ; Siguiente caracter +    inc c                       ; Siguiente caracter 
-  DJNZ bucle_x                ; Repetir 16 veces+    djnz bucle_x                ; Repetir 16 veces
  
-  LD A, (FONT_Y)              ; Siguiente linea: +    ld a, (FONT_Y)              ; Siguiente linea: 
-  INC A +    inc a 
-  INC A                       ; Y = Y + 2 +    inc a                       ; Y = Y + 2 
-  LD (FONT_Y), A +    ld (FONT_Y), a 
-  XOR A +    xor a 
-  LD (FONT_X),              ; X = 0+    ld (FONT_X),              ; X = 0
  
-  DEC E +    dec e 
-  JR NZ, bucle_y              ; Repetir 4 veces (16*4=64 caracteres)+    jr nz, bucle_y              ; Repetir 4 veces (16*4=64 caracteres)
  
 loop: loop:
-  JR loop +    jr loop 
-  RET +    ret
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 309: Línea 308:
 FONT_Y         DB    0 FONT_Y         DB    0
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 +
 +    END 35000
 </code> </code>
  
 +Un apunte: en este caso hemos usado ''FONT_CHARSET'' como una variable ''DW'' (2 bytes) embebida en el código, pero podríamos haber aprovechado la variable del sistema ''CHARS'' para ello, cambiando el ''FONT_CHARSET DW 0'' por ''FONT_CHARSET EQU $5c36'' (una referencia a memoria, no un espacio en ella), de forma que ''FONT_CHARSET'' apunte a ''CHARS'', y ahorrar esos 2 bytes.
  
-\\ +\\
 ==== PrintString_8x8 ==== ==== PrintString_8x8 ====
-  + 
- Nuestro siguiente objetivo es el de poder agrupar diferentes caracteres en una cadena y diseñar una función que permita imprimir toda la cadena mediante llamadas consecutivas a PrintChar_8x8.+ Nuestro siguiente objetivo es el de poder agrupar diferentes caracteres en una cadena y diseñar una función que permita imprimir toda la cadena mediante llamadas consecutivas a ''PrintChar_8x8''.
  
  Definiremos las cadenas de texto como ristras de códigos ASCII acabadas en un byte 0 (valor 0, no ASCII '0'):  Definiremos las cadenas de texto como ristras de códigos ASCII acabadas en un byte 0 (valor 0, no ASCII '0'):
Línea 336: Línea 338:
 ;    Imprimir el carácter llamando a PrintChar_8x8 ;    Imprimir el carácter llamando a PrintChar_8x8
 ;    Ajustar coordenada X en FONT_X ;    Ajustar coordenada X en FONT_X
-;    Ajustar coordenada Y en FONT_Y   +;    Ajustar coordenada Y en FONT_Y
 </code> </code>
  
Línea 355: Línea 357:
 ; FONT_ATTRIB  = Atributo a utilizar en la impresion. ; FONT_ATTRIB  = Atributo a utilizar en la impresion.
 ; Registro HL  = Puntero a la cadena de texto a imprimir. ; Registro HL  = Puntero a la cadena de texto a imprimir.
-;                Debe acabar en +;                Debe acabar en
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 PrintString_8x8: PrintString_8x8:
-    + 
-   ;;; Bucle de impresion de caracter+    ;;; Bucle de impresion de caracter
 pstring8_loop: pstring8_loop:
-   LD A, (HL)                ; Leemos un caracter de la cadena +    ld a, (hl)                ; Leemos un caracter de la cadena 
-   OR A +    or a 
-   RET Z                     ; Si es 0 (fin de cadena) volver +    ret z                     ; Si es 0 (fin de cadena) volver 
-   INC HL                    ; Siguiente caracter en la cadena +    inc hl                    ; Siguiente caracter en la cadena 
-   PUSH HL                   ; Salvaguardamos HL +    push hl                   ; Salvaguardamos HL 
-   CALL PrintChar_8x8        ; Imprimimos el caracter +    call PrintChar_8x8        ; Imprimimos el caracter 
-   POP HL                    ; Recuperamos HL+    pop hl                    ; Recuperamos HL
  
-   ;;; Ajustamos coordenadas X e Y +    ;;; Ajustamos coordenadas X e Y 
-   LD A, (FONT_X)            ; Incrementamos la X +    ld a, (FONT_X)            ; Incrementamos la X 
-   INC A                     ; pero comprobamos si borde derecho +    inc a                     ; pero comprobamos si borde derecho 
-   CP 31                     ; X > 31? +    cp 31                     ; X > 31? 
-   JR C, pstring8_noedgex    ; No, se puede guardar el valor+    jr c, pstring8_noedgex    ; No, se puede guardar el valor
  
-   LD A, (FONT_Y)            ; Cogemos coordenada Y +    ld a, (FONT_Y)            ; Cogemos coordenada Y 
-   CP 23                     ; Si ya es 23, no incrementar +    cp 23                     ; Si ya es 23, no incrementar 
-   JR NC, pstring8_noedgey   ; Si es 23, saltar+    jr nc, pstring8_noedgey   ; Si es 23, saltar
  
-   INC A                     ; No es 23, cambiar Y +    inc a                     ; No es 23, cambiar Y 
-   LD (FONT_Y), A+    ld (FONT_Y), a
  
 pstring8_noedgey: pstring8_noedgey:
-   LD (FONT_Y),            ; Guardamos la coordenada Y +    ld (FONT_Y),            ; Guardamos la coordenada Y 
-   XOR A                     ; Y ademas hacemos A = X = 0+    xor a                     ; Y ademas hacemos A = X = 0
  
 pstring8_noedgex pstring8_noedgex
-   LD (FONT_X),            ; Almacenamos el valor de X +    ld (FONT_X),            ; almacenamos el valor de X 
-   JR pstring8_loop+    jr pstring8_loop
 </code> </code>
  
Línea 394: Línea 396:
  
 <code z80> <code z80>
-  ;;; Prueba de impresion de cadenas: +    ;;; Prueba de impresion de cadenas: 
-   + 
-  LD HL, charset1-256          ; Saltamos los 32 caracteres iniciales +    ld hl, charset1-256       ; Saltamos los 32 caracteres iniciales 
-  LD (FONT_CHARSET), HL +    ld (FONT_CHARSET), hl 
-  LD A, 64+3 +    ld a, 64+3 
-  LD (FONT_ATTRIB), A +    ld (FONT_ATTRIB), a 
-  LD HL, cadena +    ld hl, cadena 
-  LD A, 0 +    ld a, 0 
-  LD (FONT_X), A +    ld (FONT_X), a 
-  LD A, 15 +    ld a, 15 
-  LD (FONT_Y), A +    ld (FONT_Y), a 
-  CALL PrintString_8x8 +    call PrintString_8x8 
-  RET+    ret
  
 cadena DB "PRUEBA DE IMPRESION DE TEXTO DE UNA CADENA LARGA.", 0 cadena DB "PRUEBA DE IMPRESION DE TEXTO DE UNA CADENA LARGA.", 0
Línea 413: Línea 415:
  Y el resultado en pantalla es:  Y el resultado en pantalla es:
  
-\\  +\\ 
-{{ :cursos:ensamblador:gfx4_pstring.png | Impresion de cadenas }} +{{ :cursos:ensamblador:gfx4_pstring.png?640 | Impresion de cadenas }} 
-\\ +\\
  
- La forma más óptima de programar la rutina de impresión consistiría en integrar el código de PrintChar_8x8 dentro de PrintString_8x8 evitando el recálculo de offset en pantalla en cada carácter. Se utilizaría DE como puntero en la fuente y HL como puntero en pantalla. Una vez calculada la posición inicial en pantalla para el primer carácter se variaría HL apropiadamente, incrementandolo en 1 para avances hacia la derecha tras la impresión de un carácter. Los retornos de carro se realizarían restando 31 a L y ejecutando //Caracter_Abajo_HL//. De esta forma se evitaría no sólo el CALL y RET contra PrintChar_8x8 sino que tampoco habría que realizar el continuo cálculo de la posición destino. Después habría que trazar los atributos repitiendo el proceso desde el inicio de la cadena.+ La forma más óptima de programar la rutina de impresión consistiría en integrar el código de ''PrintChar_8x8'' dentro de ''PrintString_8x8'' evitando el recálculo de offset en pantalla en cada carácter. Se utilizaría DE como puntero en la fuente y HL como puntero en pantalla. Una vez calculada la posición inicial en pantalla para el primer carácter se variaría HL apropiadamente, incrementandolo en 1 para avances hacia la derecha tras la impresión de un carácter. Los retornos de carro se realizarían restando 31 a L y ejecutando //Caracter_Abajo_HL//. De esta forma se evitaría no sólo el ''call'' ''RET'' contra ''PrintChar_8x8'' sino que tampoco habría que realizar el continuo cálculo de la posición destino. Después habría que trazar los atributos repitiendo el proceso desde el inicio de la cadena.
  
  Normalmente no se suele imprimir texto durante el desarrollo del juego (al menos no en el bucle principal de juego), por lo que puede no ser necesario llegar a este extremo de optimización a cambio de una mayor ocupación de las rutinas en memoria.  Normalmente no se suele imprimir texto durante el desarrollo del juego (al menos no en el bucle principal de juego), por lo que puede no ser necesario llegar a este extremo de optimización a cambio de una mayor ocupación de las rutinas en memoria.
Línea 423: Línea 425:
  Si vamos a utilizar la impresión de cadenas en la introducción del juego o programa, los menúes, la descripción de las fases, los créditos, la pausa o los mensajes entre nivel y nivel o el final del juego, probablemente será suficiente con la rutina que acabamos de ver.  Si vamos a utilizar la impresión de cadenas en la introducción del juego o programa, los menúes, la descripción de las fases, los créditos, la pausa o los mensajes entre nivel y nivel o el final del juego, probablemente será suficiente con la rutina que acabamos de ver.
  
- Las cadenas de texto que se puedan usar en el bucle principal del programa deberían imprimirse como Sprites, y los valores numéricos como impresión tipo "PrintChar_8x8de cada uno de sus dígitos, realizando sólo una conversión numérica del número de dígitos realmente necesario.+ Las cadenas de texto que se puedan usar en el bucle principal del programa deberían imprimirse como Sprites, y los valores numéricos como impresión tipo ''PrintChar_8x8'' de cada uno de sus dígitos, realizando sólo una conversión numérica del número de dígitos realmente necesario.
  
-\\ +\\
 ==== Impresión de valores numéricos ==== ==== Impresión de valores numéricos ====
  
Línea 432: Línea 434:
  Para poder realizar esta impresión necesitamos rutinas que conviertan un valor numérico en una representación ASCII, la cual imprimiremos luego con PrintString_8x8. Según la base de conversión, necesitaremos las siguientes rutinas:  Para poder realizar esta impresión necesitamos rutinas que conviertan un valor numérico en una representación ASCII, la cual imprimiremos luego con PrintString_8x8. Según la base de conversión, necesitaremos las siguientes rutinas:
  
-\\ +\\
    * **Bin2String** : Convierte el valor numérico de un registro o variable en una cadena ASCII con la representación binaria (unos y ceros) de dicho valor (base=2).    * **Bin2String** : Convierte el valor numérico de un registro o variable en una cadena ASCII con la representación binaria (unos y ceros) de dicho valor (base=2).
    * **Hex2String** : Convierte el valor numérico de un registro o variable en una cadena ASCII con la representación en hexadecimal (2 o 4 dígitos del 0 a la F) de dicho valor (base=16).    * **Hex2String** : Convierte el valor numérico de un registro o variable en una cadena ASCII con la representación en hexadecimal (2 o 4 dígitos del 0 a la F) de dicho valor (base=16).
    * **Int2String** : Convierte el valor numérico sin signo de un registro o variable en una cadena ASCII con la representación decimal (N dígitos 0-9) de dicho valor (base=10).    * **Int2String** : Convierte el valor numérico sin signo de un registro o variable en una cadena ASCII con la representación decimal (N dígitos 0-9) de dicho valor (base=10).
-\\ +\\
  
  El parámetro de entrada a las rutinas será L para valores de 8 bits o HL para valores de 16 bits, y todas las rutinas utilizarán un área temporal de memoria para escribir en ella la cadena resultante de la conversión:  El parámetro de entrada a las rutinas será L para valores de 8 bits o HL para valores de 16 bits, y todas las rutinas utilizarán un área temporal de memoria para escribir en ella la cadena resultante de la conversión:
Línea 451: Línea 453:
  
 <code z80> <code z80>
-   ;;; Guardar en L el valor a convertir y llamar a rutina +    ;;; Guardar en L el valor a convertir y llamar a rutina 
-   LD LA +    ld la 
-   CALL Dec2String_8 +    call Dec2String_8 
-    + 
-   ;;; Imprimir la cadena resultante de la conversion: +    ;;; Imprimir la cadena resultante de la conversion: 
-   LD HL, conv2string +    ld hl, conv2string 
-   CALL PrintString_8x8+    call PrintString_8x8
 </code> </code>
  
  Veamos las diferentes rutinas:  Veamos las diferentes rutinas:
  
-\\  +\\ 
-\\ +\\
 === Bin2String: Conversión a representación binaria === === Bin2String: Conversión a representación binaria ===
  
  La conversión de un valor numérico a una representación binaria se basa en testear el estado de cada uno de los bits del registro "parámetro" y almacenar en la cadena destino apuntada por DE un valor ASCII '0' ó '1' según el resultado del testeo.  La conversión de un valor numérico a una representación binaria se basa en testear el estado de cada uno de los bits del registro "parámetro" y almacenar en la cadena destino apuntada por DE un valor ASCII '0' ó '1' según el resultado del testeo.
  
- En lugar de ejecutar 8 ó 16 comparaciones con el comando BIT, utilizaremos la rotación a la derecha del registro parámetro de forma que el bit a comprobar sea desplazado al Carry Flag y podamos testear su valor con un **JR NC** **JR C**.+ En lugar de ejecutar 8 ó 16 comparaciones con el comando ''BIT'', utilizaremos la rotación a la derecha del registro parámetro de forma que el bit a comprobar sea desplazado al Carry Flag y podamos testear su valor con un ''JR NC'' ''JR C''.
  
  La rutina de conversión tiene 2 puntos de entrada diferentes según necesitemos convertir un número de 8 bits (valor en registro L) o de 16 bits (valor en registro HL), pero utiliza el mismo core de conversión para ambos casos:  La rutina de conversión tiene 2 puntos de entrada diferentes según necesitemos convertir un número de 8 bits (valor en registro L) o de 16 bits (valor en registro HL), pero utiliza el mismo core de conversión para ambos casos:
Línea 484: Línea 486:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
  
-Bin2String_16:               ; Punto de entrada de 16 bits +Bin2String_16:                ; Punto de entrada de 16 bits 
-   LD DE, conv2string        ; DE = puntero cadena destino +    ld de, conv2string        ; DE = puntero cadena destino 
-   LD C                  ; C = a convertir (parte alta) +    ld c                  ; C = a convertir (parte alta) 
-   CALL Bin2String_convert   ; Llamamos a rutina conversora +    call Bin2String_convert   ; Llamamos a rutina conversora 
-   JR Bin2String_8b          ; Convertimos la parte baja, +    jr Bin2String_8b          ; Convertimos la parte baja, 
-                             ; saltando el LD DE, conv2string+                              ; saltando el ld de, conv2string
  
-Bin2String_8:                ; Punto de entrada de 8 bits +Bin2String_8:                 ; Punto de entrada de 8 bits 
-   LD DE, conv2string        ; DE = puntero cadena destino+    ld de, conv2string        ; DE = puntero cadena destino
  
 Bin2String_8b: Bin2String_8b:
-   LD C                  ; C = a convertir (parte baja) +    ld c                  ; C = a convertir (parte baja) 
-   CALL Bin2String_convert   ; Llamamos a rutina conversora +    call Bin2String_convert   ; Llamamos a rutina conversora 
-   XOR A +    xor a 
-   LD (DE),                ; Guardar End Of String +    ld (de),                ; Guardar End Of String 
-   RET+    ret
  
 Bin2String_convert: Bin2String_convert:
-   LD B, 8                   ; 8 iteraciones +    ld b, 8                   ; 8 iteraciones 
-b2string_loop:               ; Bucle de conversion +b2string_loop:                ; Bucle de conversion 
-   RL C +    rl c 
-   LD A, '1'                 ; Valor en A por defecto +    ld a, '1'                 ; Valor en A por defecto 
-   JR NC, b2string_noC +    jr nc, b2string_noC 
-   LD (DE),                ; Lo almacenamos en la cadena +    ld (de),                ; Lo almacenamos en la cadena 
-   INC DE +    inc de 
-   DJNZ b2string_loop +    djnz b2string_loop 
-   RET+    ret
 b2string_noC: b2string_noC:
-   DEC A                     ; A = '0' +    dec a                     ; A = '0' 
-   LD (DE), A +    ld (de), a 
-   INC DE                    ; Lo almacenamos y avanzamos +    inc de                    ; Lo almacenamos y avanzamos 
-   DJNZ b2string_loop +    djnz b2string_loop 
-   RET+    ret
 </code> </code>
  
- En el área de memoria apuntada por //conv2string// tendremos la representación binaria en ASCII del valor en L o HL, acabado en un carácter 0, lista para imprimir con PrintString_8x8.+ En el área de memoria apuntada por ''conv2string'' tendremos la representación binaria en ASCII del valor en L o HL, acabado en un carácter 0, lista para imprimir con ''PrintString_8x8''.
  
-\\  +\\ 
-\\ +\\
 === Hex2String: Conversión a representación hexadecimal === === Hex2String: Conversión a representación hexadecimal ===
  
Línea 539: Línea 541:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Hex2String_16: Hex2String_16:
-   LD DE, conv2string        ; Cadena destino +    ld de, conv2string        ; Cadena destino 
-   LD AH +    ld ah 
-   CALL B2AHex_Num1          ; Convertir Hex1 de H +    call B2AHex_Num1          ; Convertir Hex1 de H 
-   LD AH +    ld ah 
-   CALL B2AHex_Num2          ; Convertir Hex2 de H +    call B2AHex_Num2          ; Convertir Hex2 de H 
-   JR Hex2String_8b          ; Realizar conversion de L+    jr Hex2String_8b          ; Realizar conversion de L
  
-Hex2String_8:                ; Entrada para la rut de 8 bits +Hex2String_8:                 ; Entrada para la rutina de 8 bits 
-   LD DE, conv2string+    ld de, conv2string
  
 Hex2String_8b: Hex2String_8b:
-   LD AL +    ld al 
-   CALL B2AHex_Num1          ; Convertir Hex1 de L +    call B2AHex_Num1          ; Convertir Hex1 de L 
-   LD AL +    ld al 
-   CALL B2AHex_Num2          ; Convertir Hex2 de L +    call B2AHex_Num2          ; Convertir Hex2 de L 
-   XOR A +    xor a 
-   LD (DE),                ; Guardar End Of String +    ld (de),                ; Guardar End Of String 
-   RET+    ret
  
 B2AHex_Num1: B2AHex_Num1:
-   RRA +    rra 
-   RRA +    rra 
-   RRA                       ; Desplazamos 4 veces >> +    rra                       ; Desplazamos 4 veces >> 
-   RRA                       ; para poder usar el siguiente bloque+    rra                       ; para poder usar el siguiente bloque
  
 B2AHex_Num2: B2AHex_Num2:
-   OR $F0                    ; Enmascaramos 11110000 +    or $f0                    ; Enmascaramos 11110000 
-   DAA                       ; Ajuste BCD +    daa                       ; Ajuste BCD 
-   ADD A, $A0 +    add a, $a0 
-   ADC A, $40 +    adc a, $40 
-   LD (DE),                ; Guardamos dato +    ld (de),                ; Guardamos dato 
-   INC DE +    inc de 
-   RET+    ret
 </code> </code>
  
Línea 577: Línea 579:
  
  
-\\  +\\ 
-\\ +\\
 === Int2String: Conversión a representación decimal === === Int2String: Conversión a representación decimal ===
  
Línea 593: Línea 595:
 ;----------------------------------------------------------------- ;-----------------------------------------------------------------
 Int2String_16: Int2String_16:
-   LD DE, conv2string           ; Apuntamos a cadena destino +    ld de, conv2string           ; Apuntamos a cadena destino 
-   LD BC, -10000                ; Calcular digito decenas de miles +    ld bc, -10000                ; Calcular digito decenas de miles 
-   CALL Int2Dec_num1 +    call Int2Dec_num1 
-   LD BC, -1000                 ; Calcular digito miles +    ld bc, -1000                 ; Calcular digito miles 
-   CALL Int2Dec_num1 +    call Int2Dec_num1 
-   JR Int2String_8b             ; Continuar en rutina de 8 bits (2)+    jr Int2String_8b             ; Continuar en rutina de 8 bits (2) 
 + 
 +Int2String_8:                    ; Punto de entrada de rutina 8 bits 
 +    ld de, conv2string           ; Apuntar a cadena destino 
 +    ld h, 0                      ; Parte alta de HL = 0
  
-Int2String_8:                   ; Punto de entrada de rutina 8 bits 
-   LD DE, conv2string           ; Apuntar a cadena destino 
-   LD H, 0                      ; Parte alta de HL = 0 
-    
 Int2String_8b:                  ; rutina de 8 bits (2) Int2String_8b:                  ; rutina de 8 bits (2)
-   LD BC, -100                  ; Calcular digito de centenas +    ld bc, -100                  ; Calcular digito de centenas 
-   CALL Int2Dec_num1 +    call Int2Dec_num1 
-   LD C, -10                    ; Calcular digito de decenas +    ld c, -10                    ; Calcular digito de decenas 
-   CALL Int2Dec_num1 +    call Int2Dec_num1 
-   LD C                     ; Calcular unidades +    ld c                     ; Calcular unidades 
-   CALL Int2Dec_num1 +    call Int2Dec_num1 
-   XOR A +    xor a 
-   LD (DE),                   ; Almacenar un fin de cadena +    ld (de),                   ; Almacenar un fin de cadena 
-   RET+    ret
  
 Int2Dec_num1: Int2Dec_num1:
-   LD A,'0'-1                   ; Contador unidades, empieza '0'-1 +    ld a,'0'-1                   ; Contador unidades, empieza '0'-1 
-   +
 Int2Dec_num2: Int2Dec_num2:
-   INC A                        ; Incrementamos el digito ('0', ... '9'+    inc a                        ; Incrementamos el digito ('0', ... '9'
-   ADD HLBC                   ; Restamos "unidades" hasta sobrepasarlo +    add hlbc                   ; Restamos "unidades" hasta sobrepasarlo 
-   JR C, Int2Dec_num2           ; Repetir n veces +    jr c, Int2Dec_num2           ; Repetir n veces 
-   SBC HLBC                   ; Deshacemos el último paso +    sbc hlbc                   ; Deshacemos el último paso 
-   LD (DE),                   ; Almacenamos el valor +    ld (de),                   ; Almacenamos el valor 
-   INC DE +    inc de 
-   RET+    ret
 </code> </code>
  
- Esta rutina almacena en la cadena todos los valores '0' que obtiene, incluídos los de las unidades superiores al primer dígito del número (//leading zeros//). Así, la conversión del valor de 8 bits 17 generará una cadena "017" y la conversión del número de 16 bits 1034 generará la cadena "01034"+ Esta rutina almacena en la cadena todos los valores '0' que obtiene, incluídos los de las unidades superiores al primer dígito del número (//leading zeros//). Así, la conversión del valor de 8 bits 17 generará una cadena "017" y la conversión del número de 16 bits 1034 generará la cadena "01034".
  
-\\  +\\ 
-\\ +\\
 **Eliminando los "leading zeros"** **Eliminando los "leading zeros"**
-\\  +\\ 
-\\ +\\
  
  Si no queremos imprimir los ceros que hay al principio de una cadena apuntada por HL, podemos utilizar la siguiente rutina que incrementa HL mientras encuentre ceros ASCII al principio de la cadena, y sale de la rutina cuando encuentra el fin de cadena o un carácter distinto de '0'.  Si no queremos imprimir los ceros que hay al principio de una cadena apuntada por HL, podemos utilizar la siguiente rutina que incrementa HL mientras encuentre ceros ASCII al principio de la cadena, y sale de la rutina cuando encuentra el fin de cadena o un carácter distinto de '0'.
Línea 644: Línea 646:
 ;----------------------------------------------------------------- ;-----------------------------------------------------------------
 INC_HL_Remove_Leading_Zeros: INC_HL_Remove_Leading_Zeros:
-   LD A, (HL)                ; Leemos caracter de la cadena +    ld a, (hl)                ; Leemos caracter de la cadena 
-   OR A +    or a 
-   RET Z                     ; Fin de cadena -> volver +    ret z                     ; Fin de cadena -> volver 
-   CP '0'                     +    cp '0' 
-   RET NZ                    ; Distinto de '0', volver +    ret nz                    ; Distinto de '0', volver 
-   INC HL                    ; '0', incrementar HL y repetir +    inc hl                    ; '0', incrementar HL y repetir 
-   JR INC_HL_Remove_Leading_Zeros+    jr INC_HL_Remove_Leading_Zeros
 </code> </code>
  
Línea 656: Línea 658:
  
 <code z80> <code z80>
-   ;;; Imprimir variable de 8 bits (podría ser un registro) +    ;;; Imprimir variable de 8 bits (podría ser un registro) 
-   LD A, (variable_8bits) +    ld a, (variable_8bits) 
-   LD LA +    ld la 
-   CALL Int2String_8 +    call Int2String_8 
-   LD HL, conv2string +    ld hl, conv2string 
-   CALL INC_HL_Remove_Leading_Zeros +    call INC_HL_Remove_Leading_Zeros 
-   CALL PrintString_8x8+    call PrintString_8x8
  
-   ;;; Imprimir variable de 16 bits +    ;;; Imprimir variable de 16 bits 
-   LD BC, (variable_16bits) +    ld bc, (variable_16bits) 
-   PUSH BC +    push bc 
-   POP HL                               ; HL = BC +    pop hl                               ; HL = BC 
-   CALL Int2String_16 +    call Int2String_16 
-   LD HL, conv2string +    ld hl, conv2string 
-   CALL INC_HL_Remove_Leading_Zeros +    call INC_HL_Remove_Leading_Zeros 
-   CALL PrintString_8x8+    call PrintString_8x8
 </code> </code>
  
-\\  +\\ 
-\\ +\\
 **Justificando los "leading zeros"** **Justificando los "leading zeros"**
-\\  +\\ 
-\\ +\\
  
- Finalmente, el usuario **climacus** en los foros de Speccy.org nos ofrece la siguiente variación de INC_HL_Remove_Leading_Zeros para que la rutina imprima espacios en lugar de "leading zeros", lo que provoca que el texto en pantalla esté justificado a la derecha en ocupando siempre 3 (valores de 8 bits) ó 5 (valores de 16 bits) caracteres. Esto permite que los valores impresos puedan sobreescribir en pantalla valores anteriores aunque estemos imprimiendo un valor "menor" que el que reside en pantalla:+ Finalmente, el usuario **climacus** en los foros de Speccy.org nos ofrece la siguiente variación de ''INC_HL_Remove_Leading_Zeros'' para que la rutina imprima espacios en lugar de "leading zeros", lo que provoca que el texto en pantalla esté justificado a la derecha en ocupando siempre 3 (valores de 8 bits) ó 5 (valores de 16 bits) caracteres. Esto permite que los valores impresos puedan sobreescribir en pantalla valores anteriores aunque estemos imprimiendo un valor "menor" que el que reside en pantalla:
  
 <code z80> <code z80>
 INC_HL_Justify_Leading_Zeros: INC_HL_Justify_Leading_Zeros:
-   LD A, (HL)                ; Leemos caracter de la cadena +    ld a, (hl)                ; Leemos caracter de la cadena 
-   OR A +    or a 
-   RET Z                     ; Fin de cadena -> volver +    ret z                     ; Fin de cadena -> volver 
-   CP '0'                     +    cp '0' 
-   RET NZ                    ; Distinto de '0', volver +    ret nz                    ; Distinto de '0', volver 
-   INC HL                    ; '0', incrementar HL y repetir +    inc hl                    ; '0', incrementar HL y repetir 
-   LD A, ' ' +    ld a, ' ' 
-   CALL Font_Blank           ; Imprimimos espacio y avanzamos +    call Font_Blank           ; Imprimimos espacio y avanzamos 
-   JR INC_HL_Justify_Leading_Zeros+    jr INC_HL_Justify_Leading_Zeros
 </code> </code>
  
  De esta forma, podemos tener en pantalla un valor "12345" que se vea sobreescrito por un valor "100" al imprimir "  100" (con 2 espacios delante).  De esta forma, podemos tener en pantalla un valor "12345" que se vea sobreescrito por un valor "100" al imprimir "  100" (con 2 espacios delante).
  
- También podemos sustituir "CALL Font_Blankpor un simple "CALL Font_Inc_Xpara que se realice el avance del cursor horizontalmente pero sin la impresión del carácter espacio.+ También podemos sustituir ''call Font_Blank'' por un simple ''call Font_Inc_X'' para que se realice el avance del cursor horizontalmente pero sin la impresión del carácter espacio.
  
-\\  +\\ 
-\\ +\\
 === Int2String_8_2Digits: Valores enteros de 2 dígitos === === Int2String_8_2Digits: Valores enteros de 2 dígitos ===
  
Línea 707: Línea 709:
 <code z80> <code z80>
 ;----------------------------------------------------------------------- ;-----------------------------------------------------------------------
-; Int2String_8_2Digits: Convierte el valor del registro A en una +; Int2String_8_2Digits: Convierte el valor del registro A en una
 ; cadena de texto de max. 2 caracteres (0-99) decimales en DE. ; cadena de texto de max. 2 caracteres (0-99) decimales en DE.
 ; IN:   A = Numero a convertir ; IN:   A = Numero a convertir
Línea 715: Línea 717:
 ;----------------------------------------------------------------------- ;-----------------------------------------------------------------------
 Int2String_8_2Digits: Int2String_8_2Digits:
-   LD D, '0'                      ; Empezar en ASCII '0'  +    ld d, '0'                      ; Empezar en ASCII '0' 
-   DEC D                          ; Decrementar porque el bucle hace INC +    dec d                          ; Decrementar porque el bucle hace INC 
-   LD E, 10                       ; Base 10 +    ld e, 10                       ; Base 10 
-   AND A                          ; Carry Flag = 0 +    and a                          ; Carry Flag = 0 
- +
 dtoa2dloop: dtoa2dloop:
-   INC D                          ; Incrementar numero de decenas +    inc d                          ; Incrementar numero de decenas 
-   SUB E                          ; Quitar una unidad de decenas de A +    sub e                          ; Quitar una unidad de decenas de A 
-   JR NC, dtoa2dloop              ; Si A todavia no es negativo, seguir +    jr nc, dtoa2dloop              ; Si A todavia no es negativo, seguir 
-   ADD A                      ; Decrementado demasiado, volver atras +    add a                      ; Decrementado demasiado, volver atras 
-   ADD A, '0'                     ; Convertir a ASCII +    add a, '0'                     ; Convertir a ASCII 
-   LD E                       ; E contiene las unidades +    ld e                       ; E contiene las unidades 
-   RET+    ret
 </code> </code>
  
Línea 735: Línea 737:
  
 <code z80> <code z80>
-  LD A, (vidas) +    ld a, (vidas) 
-  CALL Int2String_8_2Digits +    call Int2String_8_2Digits 
-   + 
-  ;; Imprimir parte alta (decenas) +    ;; Imprimir parte alta (decenas) 
-  LD A, 0 +    ld a, 0 
-  LD (FONT_X), A +    ld (FONT_X), a 
-  LD AD +    ld ad 
-  CALL PrintChar_8x8 +    call PrintChar_8x8 
-     + 
-  ;;; Imprimir parte baja (unidades) +    ;;; Imprimir parte baja (unidades) 
-  LD A, 1 +    ld a, 1 
-  LD (FONT_X), A +    ld (FONT_X), a 
-  LD AE +    ld ae 
-  CALL PrintChar_8x8+    call PrintChar_8x8
 </code> </code>
  
  En un posterior apartado de este mismo capítulo veremos cómo integrar estas funciones en las rutinas de impresión de cadenas con formato.  En un posterior apartado de este mismo capítulo veremos cómo integrar estas funciones en las rutinas de impresión de cadenas con formato.
  
-\\  +\\ 
-\\ +\\
 ===== Fuente estándar 8x8 de la ROM ===== ===== Fuente estándar 8x8 de la ROM =====
  
- En el Spectrum disponemos de un tipo de letra estándar de 8x8 pregrabado en ROM. Los caracteres imprimibles alojados en la ROM del Spectrum van desde el 32 (espacio) al 127 (carácter de copyright), empezando el primero en $3D00 (15161 decimal) y acabando el último en $3FFF (16383, el último byte de la ROM).+ En el Spectrum disponemos de un tipo de letra estándar de 8x8 pregrabado en ROM. Los caracteres imprimibles alojados en la ROM del Spectrum van desde el 32 (espacio) al 127 (carácter de copyright), empezando el primero en **$3d00** (15161 decimal) y acabando el último en **$3fff** (16383, el último byte de la ROM).
  
- Existe una variable del sistema llamada //CHARS// (de 16 bits, ubicada en las direcciones 23606 y 23607) que contiene la dirección de memoria del juego de caracteres que esté en uso por BASIC.+ Existe una variable del sistema llamada ''CHARS'' (de 16 bits, ubicada en las direcciones 23606 y 23607) que contiene la dirección de memoria del juego de caracteres que esté en uso por BASIC.
  
- Por defecto, CHARS contiene el valor $3D00 (el tipo de letra estándar) menos 256. El hecho de restar 256 al inicio real de la fuente es porque los caracteres definidos en la ROM empiezan en el 32 y restando 256 (8 bytes por carácter para 32 caracteres = 256 bytes), al igual que hicimos nosotros con nuestro charset personalizado, podemos hacer coincidir un ASCII > 32 con **CHARS+(8*VALOR_ASCII)**.+ Por defecto, ''CHARS'' contiene el valor **$3d00** (la fuente de letras estándar) menos 256. El hecho de restar 256 al inicio real de la fuente es porque los caracteres definidos en la ROM empiezan en el 32 y restando 256 (8 bytes por carácter para 32 caracteres = 256 bytes), al igual que hicimos nosotros con nuestro charset personalizado, podemos hacer coincidir un ASCII > 32 con **CHARS + (8 * VALOR_ASCII)**.
  
- El valor por defecto de //CHARS// es, pues, $3D00 - $0100 = $3C00.+ El valor por defecto de ''CHARS'' es, pues, $3d00 - $0100 = $3c00.
  
- El juego de caracteres estándar es inmutable al estar en ROM. La variable CHARS permitía, en el BASIC del Spectrum, definir un juego de caracteres personalizado en RAM y apuntar CHARS a su dirección en memoria. La definición de los 21 UDGs (19 en el +2A/+3) también está en RAM (desde $FF58 a $FFFF), ya que deben de ser personalizables por el usuario.+ El juego de caracteres estándar es inmutable al estar en ROM. La variable ''CHARS'' permite, en el BASIC del Spectrum, definir un juego de caracteres personalizado en RAM y apuntar ''CHARS'' a su dirección en memoria. La definición de los 21 UDGs (19 en el +2A/+3) también está en RAM (desde $ff58 a $ffff), ya que deben de ser personalizables por el usuario.
  
  Veamos el aspecto de la tipográfia estándar del Spectrum:  Veamos el aspecto de la tipográfia estándar del Spectrum:
  
-\\  +\\ 
-{{ :cursos:ensamblador:gfx4_charsetrom.png | El juego de caracteres de la ROM}}+{{ :cursos:ensamblador:gfx4_charsetrom.png?640 | El juego de caracteres de la ROM}}
 ;#; ;#;
 //El juego de caracteres estándar de la ROM// //El juego de caracteres estándar de la ROM//
 ;#; ;#;
-\\ +\\
  
- El formato en memoria de la fuente de la ROM es idéntico a un spriteset de 8x8 sin atributos, tal y como hemos definido las fuentes de texto personalizadas de nuestras rutinas y ejemplos anteriores. A partir de $3D00 empiezan los 8 bytes de datos (8 scanlines) del carácter 32, a los que siguen los 8 scanlines del carácter 33, etc., así hasta el carácter 127.+ El formato en memoria de la fuente de la ROM es idéntico a un spriteset de 8x8 sin atributos, tal y como hemos definido las fuentes de texto personalizadas de nuestras rutinas y ejemplos anteriores. A partir de $3d00 empiezan los 8 bytes de datos (8 scanlines) del carácter 32, a los que siguen los 8 scanlines del carácter 33, etc., así hasta el carácter 127.
  
- Gracias a esto podemos utilizar esta tipografía en nuestros juegos y programas (ahorrando así tener que definir nuestro propio charset y ocupar memoria con él) directamente con las rutinas de impresión de caracteres y cadenas que hemos utilizado con las fuentes de texto personalizables. Basta con establecer FONT_CHARSET a la dirección adecuada, $3C00:+ Gracias a esto podemos utilizar esta tipografía en nuestros juegos y programas (ahorrando así tener que definir nuestro propio charset y ocupar memoria con él) directamente con las rutinas de impresión de caracteres y cadenas que hemos utilizado con las fuentes de texto personalizables. Basta con establecer ''FONT_CHARSET'' a la dirección adecuada, $3c00:
  
 <code z80> <code z80>
-  LD HL, 15616-256           ; Saltamos los 32 caracteres iniciales +    ld hl, 15616-256           ; Saltamos los 32 caracteres iniciales 
-  LD (FONT_CHARSET), HL      ; Ya podemos utilizar la tipografia del +    ld (FONT_CHARSET), hl      ; Ya podemos utilizar la tipografia del 
-                             ; Spectrum con nuestras rutinas.+                               ; Spectrum con nuestras rutinas.
 </code> </code>
  
  Esto nos permite ahorrar 768 bytes de memoria en nuestro programa (el de un charset personalizado) y disponer de un recurso para la impresión de texto con un aspecto "estándar" (al que el usuario ya está acostumbrado).  Esto nos permite ahorrar 768 bytes de memoria en nuestro programa (el de un charset personalizado) y disponer de un recurso para la impresión de texto con un aspecto "estándar" (al que el usuario ya está acostumbrado).
  
-\\  +\\ 
-\\ +\\
 ===== Sistema de gestión de texto ===== ===== Sistema de gestión de texto =====
  
Línea 796: Línea 798:
  Normalmente no se usará este tipo de rutinas en un juego arcade o videoaventura, pero puede aprovecharse en programas no lúdicos y en juegos basados en texto o con gran cantidad de texto (managers deportivos, aventuras de texto, RPGs, etc).  Normalmente no se usará este tipo de rutinas en un juego arcade o videoaventura, pero puede aprovecharse en programas no lúdicos y en juegos basados en texto o con gran cantidad de texto (managers deportivos, aventuras de texto, RPGs, etc).
  
- Ya hemos visto cómo las rutinas PrintChar_8x8 y PrintString_8x8 hacen uso de las variables FONT_X, FONT_Y, FONT_CHARSET y FONT_ATTRIB. En este apartado definiremos más variables, funciones para manipularlas y nuevas funciones de impresión que hagan uso avanzado de ambas.+ Ya hemos visto cómo las rutinas ''PrintChar_8x8'' ''PrintString_8x8'' hacen uso de las variables ''FONT_X''''FONT_Y''''FONT_CHARSET'' ''FONT_ATTRIB''. En este apartado definiremos más variables, funciones para manipularlas y nuevas funciones de impresión que hagan uso avanzado de ambas.
  
  La sección sobre sistemas de gestión de texto se divide en:  La sección sobre sistemas de gestión de texto se divide en:
Línea 808: Línea 810:
  Cuando trabajamos con texto tenemos que tener en cuenta que no se suelen requerir niveles de optimización de tiempos de ejecución como en el caso de la rutinas de impresión de sprites. En este caso, dado que el tiempo de "lectura" del usuario es significativamente mayor que el tiempo de ejecución, las rutinas deben tratar de ahorrar espacio en memoria para que el programa pueda disponer de la mayor cantidad de texto posible.  Cuando trabajamos con texto tenemos que tener en cuenta que no se suelen requerir niveles de optimización de tiempos de ejecución como en el caso de la rutinas de impresión de sprites. En este caso, dado que el tiempo de "lectura" del usuario es significativamente mayor que el tiempo de ejecución, las rutinas deben tratar de ahorrar espacio en memoria para que el programa pueda disponer de la mayor cantidad de texto posible.
  
-\\ +\\
 ==== Variables de nuestro sistema de gestión ==== ==== Variables de nuestro sistema de gestión ====
  
Línea 814: Línea 816:
  
   * **FONT_X**, **FONT_Y** : Coordenadas X e Y en baja resolución (comenzando en 0) de la posición actual para la próxima impresión de un carácter (cursor).   * **FONT_X**, **FONT_Y** : Coordenadas X e Y en baja resolución (comenzando en 0) de la posición actual para la próxima impresión de un carácter (cursor).
-  * **FONT_CHARSET** : Apunta al spriteset de la fuente de texto (charset) a utilizar. El valor por defecto es $3D00-256 (la fuente de la ROM).+  * **FONT_CHARSET** : Apunta al spriteset de la fuente de texto (charset) a utilizar. El valor por defecto es $3d00-256 (la fuente de la ROM).
   * **FONT_ATTRIB** : Almacena el atributo en uso para la impresión de caracteres.   * **FONT_ATTRIB** : Almacena el atributo en uso para la impresión de caracteres.
   * **FONT_STYLE** : Almacena un valor numérico que define el estilo de impresión a utilizar. Por defecto es 0 (estilo normal).   * **FONT_STYLE** : Almacena un valor numérico que define el estilo de impresión a utilizar. Por defecto es 0 (estilo normal).
Línea 825: Línea 827:
  
 <code z80> <code z80>
-FONT_CHARSET     DW    $3D00-256 +FONT_CHARSET     DEFW    $3d00-256 
-FONT_ATTRIB      DB    56       ; Negro sobre gris +FONT_ATTRIB      DEFB    56         ; Negro sobre gris 
-FONT_STYLE       DB    0 +FONT_STYLE       DEFB    0 
-FONT_X           DB    0 +FONT_X           DEFB    0 
-FONT_Y           DB    0 +FONT_Y           DEFB    0 
-FONT_SCRWIDTH    EQU   32 +FONT_SCRWIDTH    EQU     32 
-FONT_SCRHEIGHT   EQU   24+FONT_SCRHEIGHT   EQU     24
 </code> </code>
  
  El resto de constantes (como los códigos de control) los veremos más adelante en sus correspondientes apartados.  El resto de constantes (como los códigos de control) los veremos más adelante en sus correspondientes apartados.
  
-\\ +\\
 ==== Subrutinas de gestión de posición y atributos ==== ==== Subrutinas de gestión de posición y atributos ====
  
Línea 843: Línea 845:
 <code z80> <code z80>
 ;------------------------------------------------------------- ;-------------------------------------------------------------
-FONT_CHARSET     DW    $3D00-256+FONT_CHARSET     DW    $3d00-256
 FONT_ATTRIB      DB    56       ; Negro sobre gris FONT_ATTRIB      DB    56       ; Negro sobre gris
 FONT_STYLE       DB    0 FONT_STYLE       DB    0
Línea 855: Línea 857:
 FONT_UNDERSC     EQU   2 FONT_UNDERSC     EQU   2
 FONT_ITALIC      EQU   3 FONT_ITALIC      EQU   3
- 
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 862: Línea 863:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Font_Set_Charset: Font_Set_Charset:
-   LD (FONT_CHARSET), HL +    ld (FONT_CHARSET), hl 
-   RET +    ret
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 871: Línea 871:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Font_Set_Style: Font_Set_Style:
-   LD (FONT_STYLE), +    ld (FONT_STYLE), a 
-   RET +    ret
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 880: Línea 879:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Font_Set_X: Font_Set_X:
-   LD (FONT_X), +    ld (FONT_X), a 
-   RET +    ret
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 889: Línea 887:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Font_Set_Y: Font_Set_Y:
-   LD (FONT_Y), +    ld (FONT_Y), a 
-   RET +    ret
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 899: Línea 896:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Font_Set_XY: Font_Set_XY:
-   LD (FONT_X), BC +    ld (FONT_X), bc 
-   RET +    ret
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 909: Línea 905:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Font_Set_Ink: Font_Set_Ink:
-   PUSH BC                   ; Preservamos registros +    push bc                   ; Preservamos registros 
-   AND 7                     ; Borramos bits 7-3 +    and 7                     ; Borramos bits 7-3 
-   LD B                  ; Lo guardamos en B +    ld b                  ; Lo guardamos en B 
-   LD A, (FONT_ATTRIB)       ; Cogemos el atributo actual +    ld a, (FONT_ATTRIB)       ; Cogemos el atributo actual 
-   AND %11111000             ; Borramos el valor de INK +    and %11111000             ; Borramos el valor de INK 
-   OR B                      ; Insertamos INK en A +    or b                      ; Insertamos INK en A 
-   LD (FONT_ATTRIB),       ; Guardamos el valor de INK +    ld (FONT_ATTRIB),       ; Guardamos el valor de INK 
-   POP BC +    pop bc 
-   RET +    ret
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 926: Línea 921:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Font_Set_Paper: Font_Set_Paper:
-   PUSH BC                   ; Preservamos registros +    push bc                   ; Preservamos registros 
-   AND 7                     ; Borramos bits 7-3 +    and 7                     ; Borramos bits 7-3 
-   RLCA                      ; A = 00000XXX -> 0000XXX0 +    rlca                      ; A = 00000XXX -> 0000XXX0 
-   RLCA                      ; A = 000XXX00 +    rlca                      ; A = 000XXX00 
-   RLCA                      ; A = 00XXX000 <-- Valor en paper +    rlca                      ; A = 00XXX000 <-- Valor en paper 
-   LD B                  ; Lo guardamos en B +    ld b                  ; Lo guardamos en B 
-   LD A, (FONT_ATTRIB)       ; Cogemos el atributo actual +    ld a, (FONT_ATTRIB)       ; Cogemos el atributo actual 
-   AND %11000111             ; Borramos los datos de PAPER +    and %11000111             ; Borramos los datos de PAPER 
-   OR B                      ; Insertamos PAPER en A +    or b                      ; Insertamos PAPER en A 
-   LD (FONT_ATTRIB),       ; Guardamos el valor de PAPER +    ld (FONT_ATTRIB),       ; Guardamos el valor de PaPER 
-   POP BC +    pop bc 
-   RET +    ret
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 945: Línea 939:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Font_Set_Attrib: Font_Set_Attrib:
-   LD (FONT_ATTRIB), +    ld (FONT_ATTRIB), a 
-   RET +    ret
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 955: Línea 948:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Font_Set_Bright: Font_Set_Bright:
-   AND 1                     ; A = solo bit 0 de A +    and 1                     ; A = solo bit 0 de A 
-   LD A, (FONT_ATTRIB)       ; Cargamos en A el atributo +    ld a, (FONT_ATTRIB)       ; Cargamos en A el atributo 
-   JR NZ, fsbright_1         ; Si el bit solicitado era  +    jr nz, fsbright_1         ; Si el bit solicitado era 
-   RES 6,                  ; Seteamos a 0 el bit de flash +    res 6,                  ; Seteamos a 0 el bit de flash 
-   LD (FONT_ATTRIB),       ; Escribimos el atributo +    ld (FONT_ATTRIB),       ; Escribimos el atributo 
-   RET+    ret
 fsbright_1: fsbright_1:
-   SET 6,                  ; Seteamos a 1 el bit de brillo +    set 6,                  ; Seteamos a 1 el bit de brillo 
-   LD (FONT_ATTRIB),       ; Escribimos el atributo +    ld (FONT_ATTRIB),       ; Escribimos el atributo 
-   RET +    ret
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 973: Línea 965:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Font_Set_Flash: Font_Set_Flash:
-   AND 1                     ; A = solo bit 0 de A +    and 1                     ; A = solo bit 0 de A 
-   LD A, (FONT_ATTRIB)       ; Cargamos en A el atributo +    ld a, (FONT_ATTRIB)       ; Cargamos en A el atributo 
-   JR NZ, fsflash_1          ; Si el bit solicitado era  +    jr nz, fsflash_1          ; Si el bit solicitado era 
-   RES 7,                  ; Seteamos a 0 el bit de flash +    res 7,                  ; Seteamos a 0 el bit de flash 
-   LD (FONT_ATTRIB),       ; Escribimos el atributo +    ld (FONT_ATTRIB),       ; Escribimos el atributo 
-   RET+    ret
 fsflash_1: fsflash_1:
-   SET 7,                  ; Seteamos a 1 el bit de flash +    set 7,                  ; Seteamos a 1 el bit de flash 
-   LD (FONT_ATTRIB),       ; Escribimos el atributo +    ld (FONT_ATTRIB),       ; Escribimos el atributo 
-   RET +    ret
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 992: Línea 983:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Font_Blank: Font_Blank:
-   LD A, ' '                 ; Imprimir caracter espacio +    ld a, ' '                 ; Imprimir caracter espacio 
-   PUSH BC +    push bc 
-   PUSH DE +    push de 
-   PUSH HL +    push hl 
-   CALL PrintChar_8x8        ; Sobreescribir caracter +    call PrintChar_8x8        ; Sobreescribir caracter 
-   POP HL +    pop hl 
-   POP DE +    pop de 
-   POP BC +    pop bc 
-   CALL Font_Inc_X           ; Incrementamos la coord X +    call Font_Inc_X           ; Incrementamos la coord X 
-   RET +    ret
- +
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 1011: Línea 1000:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Font_Inc_X: Font_Inc_X:
-   LD A, (FONT_X)            ; Incrementamos la X +    ld a, (FONT_X)            ; Incrementamos la X 
-   INC A                     ; pero comprobamos si borde derecho +    inc a                     ; pero comprobamos si borde derecho 
-   CP FONT_SCRWIDTH-1        ; X > ANCHO-1? +    cp FONT_SCRWIDTH-1        ; X > ANCHO-1? 
-   JR C, fincx_noedgex       ; No, se puede guardar el valor +    jr c, fincx_noedgex       ; No, se puede guardar el valor 
-   CALL Font_CRLF +    call Font_CRLF 
-   RET+    ret
  
 fincx_noedgex: fincx_noedgex:
-   LD (FONT_X),            ; Establecemos el valor de X +    ld (FONT_X),            ; Establecemos el valor de X 
-   RET +    ret
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 1029: Línea 1017:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Font_LF: Font_LF:
-   LD A, (FONT_Y)            ; Cogemos coordenada Y +    ld a, (FONT_Y)            ; Cogemos coordenada Y 
-   CP FONT_SCRHEIGHT-1       ; Estamos en la parte inferior +    cp FONT_SCRHEIGHT-1       ; Estamos en la parte inferior 
-   JR NC, fontlf_noedge      ; de pantalla? -> No avanzar +    jr nc, fontlf_noedge      ; de pantalla? -> No avanzar 
-   INC A                     ; No estamos, avanzar +    inc a                     ; No estamos, avanzar 
-   LD (FONT_Y), A+    ld (FONT_Y), a
  
 fontlf_noedge: fontlf_noedge:
-   RET +    ret
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 1044: Línea 1031:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Font_CR: Font_CR:
-   XOR A +    xor a 
-   LD (FONT_X), +    ld (FONT_X), a 
-   RET +    ret
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 1054: Línea 1040:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Font_CRLF: Font_CRLF:
-   CALL Font_LF +    call Font_LF 
-   CALL Font_CR +    call Font_CR 
-   RET +    ret
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 1064: Línea 1049:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Font_Tab: Font_Tab:
-   PUSH BC +    push bc 
-   PUSH DE +    push de 
-   PUSH HL +    push hl 
-   LD HL, font_tab_string +    ld hl, font_tab_string 
-   CALL PrintString_8x8      ; Imprimimos 3 espacios +    call PrintString_8x8      ; Imprimimos 3 espacios 
-   POP HL +    pop hl 
-   POP DE +    pop de 
-   POP BC +    pop bc 
-   RET+    ret
  
 font_tab_string  DB  "   ", 0 font_tab_string  DB  "   ", 0
- 
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 1083: Línea 1067:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Font_Dec_X: Font_Dec_X:
-   LD A, (FONT_X)            ; Cargamos la coordenada X +    ld a, (FONT_X)            ; Cargamos la coordenada X 
-   OR A +    or a 
-   RET Z                     ; Es cero? no se hace nada (salir) +    ret z                     ; Es cero? no se hace nada (salir) 
-   DEC A                     ; No es cero? Decrementar +    dec a                     ; No es cero? Decrementar 
-   LD (FONT_X), A +    ld (FONT_X), a 
-   RET                       ; Salir +    ret                       ; Salir
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 1097: Línea 1080:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Font_Backspace: Font_Backspace:
-   CALL Font_Dec_X +    call Font_Dec_X 
-   LD A, ' '                 ; Imprimir caracter espacio +    ld a, ' '                 ; Imprimir caracter espacio 
-   PUSH BC +    push bc 
-   PUSH DE +    push de 
-   PUSH HL +    push hl 
-   CALL PrintChar_8x8        ; Sobreescribir caracter +    call PrintChar_8x8        ; Sobreescribir caracter 
-   POP HL +    pop hl 
-   POP DE +    pop de 
-   POP BC +    pop bc 
-   RET                       ; Salir+    ret                       ; Salir
 </code> </code>
  
Línea 1112: Línea 1095:
  
 <code z80> <code z80>
-  LD HL, micharset +    ld hl, micharset 
-  CALL Font_Set_Charset+    call Font_Set_Charset
  
-  LD A, 1+(7*8) +    ld a, 1+(7*8) 
-  Call Font_Set_Attrib+    Call Font_Set_Attrib
  
-  LD A, FONT_NORMAL +    ld a, FONT_NORMAL 
-  CALL Font_Set_Style +    call Font_Set_Style 
-  (...)+    (...)
 </code> </code>
  
-\\  + Nótese que hemos creado funciones del tipo ''Font_Set_X'' o ''Font_Set_Y'' que simplemente modifican valores en nuestras variables por temas ilustrativos, ya que lo normal sería en nuestro ejemplo escribir directamente en las variables en lugar de hacer un ''call''
-\\ + 
 + 
 +\\ 
 +\\
 ==== Impresión de caracteres con estilos ==== ==== Impresión de caracteres con estilos ====
  
- Aunque ya hemos visto una rutina PrintChar_8x8 para impresión de caracteres, vamos a implementar a continuación una nueva versión de la misma con la posibilidad de utilizar diferente estilos de fuente a partir de la fuente original.+ Aunque ya hemos visto una rutina ''PrintChar_8x8'' para impresión de caracteres, vamos a implementar a continuación una nueva versión de la misma con la posibilidad de utilizar diferente estilos de fuente a partir de la fuente original.
  
  Mediante un único juego de caracteres podemos simular estilos de texto a través de código, manipulando "al vuelo" los datos del charset antes de imprimirlos. Esto nos evita la necesidad de tener múltiples charsets de texto para distintos estilos con la consiguiente ocupación de espacio en memoria.  Mediante un único juego de caracteres podemos simular estilos de texto a través de código, manipulando "al vuelo" los datos del charset antes de imprimirlos. Esto nos evita la necesidad de tener múltiples charsets de texto para distintos estilos con la consiguiente ocupación de espacio en memoria.
Línea 1133: Línea 1119:
  Los estilos básicos que podemos conseguir al vuelo son **normal**, **negrita**, **cursiva** y **subrayado**.  Los estilos básicos que podemos conseguir al vuelo son **normal**, **negrita**, **cursiva** y **subrayado**.
  
- Las rutinas de impresión para los 4 estilos esencialmente iguales salvo por el bucle de impresión, por lo que vamos a utilizar una variable global llamada **FONT_STYLE** para indicar el estilo actual en uso, y modificaremos la rutina PrintChar_8x8 para que haga uso del valor del estilo y modifique el bucle de impresión en consecuencia.+ Las rutinas de impresión para los 4 estilos esencialmente iguales salvo por el bucle de impresión, por lo que vamos a utilizar una variable global llamada ''FONT_STYLE'' para indicar el estilo actual en uso, y modificaremos la rutina ''PrintChar_8x8'' para que haga uso del valor del estilo y modifique el bucle de impresión en consecuencia.
  
 <code> <code>
 ;--- Variables de fuente -------------------- ;--- Variables de fuente --------------------
-FONT_CHARSET     DW    $3D00-256+FONT_CHARSET     DW    $3d00-256
 FONT_ATTRIB      DB    56       ; Negro sobre gris FONT_ATTRIB      DB    56       ; Negro sobre gris
 FONT_STYLE       DB    0 FONT_STYLE       DB    0
Línea 1164: Línea 1150:
  
  El esqueleto de la función PrintChar_8x8, a falta de introducir los bucles de impresión, ya lo conocemos:  El esqueleto de la función PrintChar_8x8, a falta de introducir los bucles de impresión, ya lo conocemos:
-    +
 <code z80> <code z80>
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 1173: Línea 1159:
 PrintChar_8x8: PrintChar_8x8:
  
-   LD BC, (FONT_X)      ; B = Y,  C = X +    ld bc, (FONT_X)      ; B = Y,  C = X 
-   EX AFAF          ; Nos guardamos el caracter en A'+    ex afaf          ; Nos guardamos el caracter en A'
  
-   ;;; 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 E             ; DE contiene ahora la direccion destino.+    ld e             ; DE contiene ahora la direccion destino.
  
-   ;;; Calcular posicion origen (array sprites) en HL como: +    ;;; Calcular posicion origen (array sprites) en HL como: 
-   ;;;     direccion = base_sprites + (NUM_SPRITE*8) +    ;;;     direccion = base_sprites + (NUM_SPRITE*8) 
-   EX AFAF          ; Recuperamos el caracter a dibujar de A' +    ex afaf          ; Recuperamos el caracter a dibujar de A' 
-   LD BC, (FONT_CHARSET) +    ld bc, (FONT_CHARSET) 
-   LD H, 0 +    ld h, 0 
-   LD LA +    ld la 
-   ADD HLHL +    add hlhl 
-   ADD HLHL +    add hlhl 
-   ADD HLHL +    add hlhl 
-   ADD HLBC         ; HL = BC + HL = FONT_CHARSET + (A * 8)+    add hlbc         ; HL = BC + HL = FONT_CHARSET + (A * 8)
  
-   EX DEHL          ; Intercambiamos DE y HL (DE=origen, HL=destino)+    ex dehl          ; Intercambiamos DE y HL (DE=origen, HL=destino)
  
-   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
-   ;;; INSERTAR AQUI BUCLES DE IMPRESION SEGUN ESTILO +    ;;; INSERTAR AQUI BUCLES DE IMPRESION SEGUN ESTILO 
-   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
-   ;;; (...) +    ;;; (...)
-   +
-   ;;; Impresion del caracter finalizada  +
-    +
-   ;;; Impresion de atributos +
-   LD A, H            ; Recuperamos el valor inicial de HL +
-   SUB 8              ; Restando los 8 scanlines avanzados+
  
-   ;;; Calcular posicion destino en area de atributos en DE. +    ;;; Impresion del caracter finalizada
-                      ; A = H +
-   RRCA               ; Codigo de Get_Attr_Offset_From_Image +
-   RRCA +
-   RRCA +
-   AND 3 +
-   OR $58 +
-   LD D, A +
-   LD E, L+
  
-   ;;; Escribir el atributo en memoria +    ;;; Impresion de atributos 
-   LD A, (FONT_ATTRIB) +    ld a, h            ; Recuperamos el valor inicial de HL 
-   LD (DE),         ; Escribimos el atributo en memoria +    sub 8              ; Restando los 8 scanlines avanzados 
-   RET+ 
 +    ;;; Calcular posicion destino en area de atributos en DE. 
 +                        ; A = H 
 +    rrca               ; Codigo de Get_Attr_Offset_From_Image 
 +    rrca 
 +    rrca 
 +    and 3 
 +    or $58 
 +    ld d, a 
 +    ld e, l 
 + 
 +    ;;; Escribir el atributo en memoria 
 +    ld a, (FONT_ATTRIB) 
 +    ld (de),         ; Escribimos el atributo en memoria 
 +    ret
 </code> </code>
  
  La manera de obtener los diferentes estilos es la siguiente:  La manera de obtener los diferentes estilos es la siguiente:
  
-\\  +\\ 
-\\ +\\
 **Estilo normal**: **Estilo normal**:
 No se realiza ningún tipo de modificación sobre los datos del carácter: se imprimen tal cual se obtienen del spriteset en un bucle de 8 iteraciones: No se realiza ningún tipo de modificación sobre los datos del carácter: se imprimen tal cual se obtienen del spriteset en un bucle de 8 iteraciones:
  
 <code z80> <code z80>
-   ;;;;;; Estilo NORMAL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +    ;;;;;; Estilo NORMAL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
-   LD B, 8                      ; 8 scanlines a dibujar+    ld b, 8                      ; 8 scanlines a dibujar
 drawchar_loop_normal: drawchar_loop_normal:
-   LD A, (DE)                   ; Tomamos el dato del caracter +    ld a, (de)                   ; Tomamos el dato del caracter 
-   LD (HL),                   ; Establecemos el valor en videomemoria +    ld (hl),                   ; Establecemos el valor en videomemoria 
-   INC DE +    inc de 
-   INC H +    inc h 
-   DJNZ drawchar_loop_normal +    djnz drawchar_loop_normal 
-   JR pchar8_printattr          ; Impresion de atributos+    jr pchar8_printattr          ; Impresion de atributos
 </code> </code>
  
-\\  +\\ 
-\\ +\\
 **Estilo negrita (bold)**: **Estilo negrita (bold)**:
 Para simular el efecto de la negrita necesitamos aumentar el grosor del carácter. Para eso, leemos cada scanline y realizamos un OR de dicho scanline con una copia del mismo desplazada hacia la derecha o hacia la izquierda. Para simular el efecto de la negrita necesitamos aumentar el grosor del carácter. Para eso, leemos cada scanline y realizamos un OR de dicho scanline con una copia del mismo desplazada hacia la derecha o hacia la izquierda.
  
 <code z80> <code z80>
-   ;;;;;; Estilo NEGRITA ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +    ;;;;;; Estilo NEGRITA ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
-   LD B, 8                      ; 8 scanlines a dibujar+    ld b, 8                      ; 8 scanlines a dibujar
 drawchar_loop_bold: drawchar_loop_bold:
-   LD A, (DE)                   ; Tomamos el dato del caracter +    ld a, (de)                   ; Tomamos el dato del caracter 
-   LD C                     ; Creamos copia de A +    ld c                     ; Creamos copia de A 
-   RRCA                         ; Desplazamos A +    rrca                         ; Desplazamos A 
-   OR C                         ; Y agregamos C +    or c                         ; Y agregamos C 
-   LD (HL),                   ; Establecemos el valor en videomemoria +    ld (hl),                   ; Establecemos el valor en videomemoria 
-   INC DE +    inc de 
-   INC H +    inc h 
-   DJNZ drawchar_loop_bold +    djnz drawchar_loop_bold 
-   JR pchar8_printattr          ; Impresion de atributos+    jr pchar8_printattr          ; Impresion de atributos
 </code> </code>
  
-\\  +\\ 
-\\ +\\
 **Estilo Subrayado (underscore)**: **Estilo Subrayado (underscore)**:
 Basta con dibujar los 7 primeros scanlines del carácter correctamente, y trazar como octavo scanline un valor 255 (8 píxeles a 1, una línea horizontal). De esta forma el último scanline se convierte en el subrayado. Basta con dibujar los 7 primeros scanlines del carácter correctamente, y trazar como octavo scanline un valor 255 (8 píxeles a 1, una línea horizontal). De esta forma el último scanline se convierte en el subrayado.
  
 <code z80> <code z80>
-   ;;;;;; Estilo SUBRAYADO ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +    ;;;;;; Estilo SUBRAYADO ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
-   LD B, 7                      ; 7 scanlines a dibujar normales+    ld b, 7                      ; 7 scanlines a dibujar normales
 drawchar_loop_undersc: drawchar_loop_undersc:
-   LD A, (DE)                   ; Tomamos el dato del caracter +    ld a, (de)                   ; Tomamos el dato del caracter 
-   LD (HL),                   ; Establecemos el valor en videomemoria +    ld (hl),                   ; Establecemos el valor en videomemoria 
-   INC DE +    inc de 
-   INC H +    inc h 
-   DJNZ drawchar_loop_undersc+    djnz drawchar_loop_undersc
  
-   ;;; El octavo scanline, una linea de subrayado +    ;;; El octavo scanline, una linea de subrayado 
-   LD A, 255                    ; Ultima linea = subrayado +    ld a, 255                    ; Ultima linea = subrayado 
-   LD (HL), A +    ld (hl), a 
-   INC H                        ; Necesario para el SUB A, 8 +    inc h                        ; Necesario para el sub a, 8 
-   JR pchar8_printattr          ; Impresion de atributos+    jr pchar8_printattr          ; Impresion de atributos
 </code> </code>
  
-\\  +\\ 
-\\ +\\
 **Estilo Cursiva (italic)**: **Estilo Cursiva (italic)**:
 Finalmente, el estilo de letra cursiva implica imprimir los 3 primeros scanlines desplazados hacia la derecha, los 2 centrales sin modificación y los 3 últimos desplazados hacia la izquierda: Finalmente, el estilo de letra cursiva implica imprimir los 3 primeros scanlines desplazados hacia la derecha, los 2 centrales sin modificación y los 3 últimos desplazados hacia la izquierda:
  
 <code z80> <code z80>
-   ;;;;;; Estilo ITALICA ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +    ;;;;;; Estilo ITALICA ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
-   ;;; 3 primeros scanlines, a la derecha, +    ;;; 3 primeros scanlines, a la derecha, 
-   LD B, 3+    ld b, 3
 drawchar_loop_italic1: drawchar_loop_italic1:
-   LD A, (DE)         ; Tomamos el dato del caracter +    ld a, (de)         ; Tomamos el dato del caracter 
-   SRA A              ; Desplazamos A a la derecha +    sra a              ; Desplazamos A a la derecha 
-   LD (HL),         ; Establecemos el valor en videomemoria +    ld (hl),         ; Establecemos el valor en videomemoria 
-   INC DE +    inc de 
-   INC H +    inc h 
-   DJNZ drawchar_loop_italic1+    djnz drawchar_loop_italic1
  
-   ;;; 2 siguientes scanlines, sin tocar +    ;;; 2 siguientes scanlines, sin tocar 
-   LD B, 2+    ld b, 2
  
 drawchar_loop_italic2: drawchar_loop_italic2:
-   LD A, (DE)         ; Tomamos el dato del caracter +    ld a, (de)         ; Tomamos el dato del caracter 
-   LD (HL),         ; Establecemos el valor en videomemoria +    ld (hl),         ; Establecemos el valor en videomemoria 
-   INC DE +    inc de 
-   INC H +    inc h 
-   DJNZ drawchar_loop_italic2+    djnz drawchar_loop_italic2
  
-   LD B, 3+    ld b, 3
 drawchar_loop_italic3: drawchar_loop_italic3:
-   ;;; 3 ultimos scanlines, a la izquierda, +    ;;; 3 ultimos scanlines, a la izquierda, 
-   LD A, (DE)         ; Tomamos el dato del caracter +    ld a, (de)         ; Tomamos el dato del caracter 
-   SLA A              ; Desplazamos A +    sla a              ; Desplazamos A 
-   LD (HL),         ; Establecemos el valor en videomemoria +    ld (hl),         ; Establecemos el valor en videomemoria 
-   INC DE +    inc de 
-   INC H +    inc h 
-   DJNZ drawchar_loop_italic3 +    djnz drawchar_loop_italic3 
-   JR pchar8_printattr+    jr pchar8_printattr
 </code> </code>
  
Línea 1349: Línea 1335:
 PrintChar_8x8: PrintChar_8x8:
  
-   LD BC, (FONT_X)      ; B = Y,  C = X +    ld bc, (FONT_X)      ; B = Y,  C = X 
-   EX AFAF          ; Nos guardamos el caracter en A'+    ex afaf          ; Nos guardamos el caracter en A'
  
-   ;;; 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 E             ; DE contiene ahora la direccion destino.+    ld e             ; DE contiene ahora la direccion destino.
  
-   ;;; Calcular posicion origen (array sprites) en HL como: +    ;;; Calcular posicion origen (array sprites) en HL como: 
-   ;;;     direccion = base_sprites + (NUM_SPRITE*8) +    ;;;     direccion = base_sprites + (NUM_SPRITE*8) 
-   EX AFAF          ; Recuperamos el caracter a dibujar de A' +    ex afaf          ; Recuperamos el caracter a dibujar de A' 
-   LD BC, (FONT_CHARSET) +    ld bc, (FONT_CHARSET) 
-   LD H, 0 +    ld h, 0 
-   LD LA +    ld la 
-   ADD HLHL +    add hlhl 
-   ADD HLHL +    add hlhl 
-   ADD HLHL +    add hlhl 
-   ADD HLBC         ; HL = BC + HL = FONT_CHARSET + (A * 8)+    add hlbc         ; HL = BC + HL = FONT_CHARSET + (A * 8)
  
-   EX DEHL          ; Intercambiamos DE y HL (DE=origen, HL=destino)+    ex dehl          ; Intercambiamos DE y HL (DE=origen, HL=destino)
  
-   ;;; NUEVO: Verificacion del estilo actual +    ;;; NUEVO: Verificacion del estilo actual 
-   LD A, (FONT_STYLE)           ; Obtenemos el estilo actual +    ld a, (FONT_STYLE)           ; Obtenemos el estilo actual 
-   OR A +    or a 
-   JR NZ, pchar8_estilos_on     ; Si es != cero, saltar+    jr nz, pchar8_estilos_on     ; Si es != cero, saltar
  
-   ;;;;;; Estilo NORMAL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +    ;;;;;; Estilo NORMAL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
-   LD B, 8                      ; 8 scanlines a dibujar+    ld b, 8                      ; 8 scanlines a dibujar
 drawchar_loop_normal: drawchar_loop_normal:
-   LD A, (DE)                   ; Tomamos el dato del caracter +    ld a, (de)                   ; Tomamos el dato del caracter 
-   LD (HL),                   ; Establecemos el valor en videomemoria +    ld (hl),                   ; Establecemos el valor en videomemoria 
-   INC DE +    inc de 
-   INC H +    inc h 
-   DJNZ drawchar_loop_normal +    djnz drawchar_loop_normal 
-   JR pchar8_printattr          ; Imprimir atributos+    jr pchar8_printattr          ; Imprimir atributos
  
 pchar8_estilos_on: pchar8_estilos_on:
-   CP FONT_BOLD                 ; ¿Es estilo NEGRITA? +    cp FONT_BOLD                 ; ¿Es estilo NEGRITA? 
-   JR NZ, pchar8_nobold         ; No, saltar+    jr nz, pchar8_nobold         ; No, saltar
  
-   ;;;;;; Estilo NEGRITA ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +    ;;;;;; Estilo NEGRITA ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
-   LD B, 8            ; 8 scanlines a dibujar+    ld b, 8            ; 8 scanlines a dibujar
 drawchar_loop_bold: drawchar_loop_bold:
-   LD A, (DE)         ; Tomamos el dato del caracter +    ld a, (de)         ; Tomamos el dato del caracter 
-   LD C           ; Creamos copia de A +    ld c           ; Creamos copia de A 
-   RRCA               ; Desplazamos A +    rrca               ; Desplazamos A 
-   OR C               ; Y agregamos C +    or c               ; Y agregamos C 
-   LD (HL),         ; Establecemos el valor en videomemoria +    ld (hl),         ; Establecemos el valor en videomemoria 
-   INC DE             ; Incrementamos puntero en caracter +    inc de             ; Incrementamos puntero en caracter 
-   INC H              ; Incrementamos puntero en pantalla (scanline+=1) +    inc h              ; Incrementamos puntero en pantalla (scanline+=1) 
-   DJNZ drawchar_loop_bold +    djnz drawchar_loop_bold 
-   JR pchar8_printattr+    jr pchar8_printattr
  
 pchar8_nobold: pchar8_nobold:
-   CP FONT_UNDERSC              ; ¿Es estilo SUBRAYADO? +    cp FONT_UNDERSC              ; ¿Es estilo SUBRAYADO? 
-   JR NZ, pchar8_noundersc      ; No, saltar+    jr nz, pchar8_noundersc      ; No, saltar
  
-   ;;;;;; Estilo SUBRAYADO ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +    ;;;;;; Estilo SUBRAYADO ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
-   LD B, 7            ; 7 scanlines a dibujar normales+    ld b, 7            ; 7 scanlines a dibujar normales
 drawchar_loop_undersc: drawchar_loop_undersc:
-   LD A, (DE)         ; Tomamos el dato del caracter +    ld a, (de)         ; Tomamos el dato del caracter 
-   LD (HL),         ; Establecemos el valor en videomemoria +    ld (hl),         ; Establecemos el valor en videomemoria 
-   INC DE +    inc de 
-   INC H +    inc h 
-   DJNZ drawchar_loop_undersc+    djnz drawchar_loop_undersc
  
-   ;;; El octavo scanline, una linea de subrayado +    ;;; El octavo scanline, una linea de subrayado 
-   LD A, 255          ; Ultima linea = subrayado +    ld a, 255          ; Ultima linea = subrayado 
-   LD (HL), A +    ld (hl), a 
-   INC H              ; Necesario para el SUB A, 8 +    inc h              ; Necesario para el sub a, 8 
-   JR pchar8_printattr+    jr pchar8_printattr
  
 pchar8_noundersc: pchar8_noundersc:
-   CP FONT_ITALIC               ; ¿Es estilo ITALICA? +    cp FONT_ITALIC               ; ¿Es estilo ITALICA? 
-   JR NZ, pchar8_UNKNOWN        ; No, saltar+    jr nz, pchar8_UNKNOWN        ; No, saltar
  
-   ;;;;;; Estilo ITALICA ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +    ;;;;;; Estilo ITALICA ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
-   ;;; 3 primeros scanlines, a la derecha, +    ;;; 3 primeros scanlines, a la derecha, 
-   LD B, 3+    ld b, 3
 drawchar_loop_italic1: drawchar_loop_italic1:
-   LD A, (DE)         ; Tomamos el dato del caracter +    ld a, (de)         ; Tomamos el dato del caracter 
-   SRA A              ; Desplazamos A a la derecha +    sra a              ; Desplazamos A a la derecha 
-   LD (HL),         ; Establecemos el valor en videomemoria +    ld (hl),         ; Establecemos el valor en videomemoria 
-   INC DE +    inc de 
-   INC H +    inc h 
-   DJNZ drawchar_loop_italic1+    djnz drawchar_loop_italic1
  
-   ;;; 2 siguientes scanlines, sin tocar +    ;;; 2 siguientes scanlines, sin tocar 
-   LD B, 2+    ld b, 2
  
 drawchar_loop_italic2: drawchar_loop_italic2:
-   LD A, (DE)         ; Tomamos el dato del caracter +    ld a, (de)         ; Tomamos el dato del caracter 
-   LD (HL),         ; Establecemos el valor en videomemoria +    ld (hl),         ; Establecemos el valor en videomemoria 
-   INC DE +    inc de 
-   INC H +    inc h 
-   DJNZ drawchar_loop_italic2+    djnz drawchar_loop_italic2
  
-   LD B, 3+    ld b, 3
 drawchar_loop_italic3: drawchar_loop_italic3:
-   ;;; 3 ultimos scanlines, a la izquierda, +    ;;; 3 ultimos scanlines, a la izquierda, 
-   LD A, (DE)         ; Tomamos el dato del caracter +    ld a, (de)         ; Tomamos el dato del caracter 
-   SLA A              ; Desplazamos A +    sla a              ; Desplazamos A 
-   LD (HL),         ; Establecemos el valor en videomemoria +    ld (hl),         ; Establecemos el valor en videomemoria 
-   INC DE +    inc de 
-   INC H +    inc h 
-   DJNZ drawchar_loop_italic3 +    djnz drawchar_loop_italic3 
-   JR pchar8_printattr +    jr pchar8_printattr
  
 pchar8_UNKNOWN:                 ; Estilo desconocido... pchar8_UNKNOWN:                 ; Estilo desconocido...
-   LD B, 8                      ; Lo imprimimos con el normal +    ld b, 8                      ; Lo imprimimos con el normal 
-   JR drawchar_loop_normal      ; (estilo por defecto)+    jr drawchar_loop_normal      ; (estilo por defecto)
  
-   ;;; Impresion de los atributos+    ;;; Impresion de los atributos
 pchar8_printattr: pchar8_printattr:
  
-   LD A           ; Recuperamos el valor inicial de HL +    ld a           ; Recuperamos el valor inicial de HL 
-   SUB 8              ; Restando los 8 scanlines avanzados+    sub 8              ; Restando los 8 scanlines avanzados
  
-   ;;; Calcular posicion destino en area de atributos en DE. +    ;;; Calcular posicion destino en area de atributos en DE. 
-                      ; A = H +                        ; A = H 
-   RRCA               ; Codigo de Get_Attr_Offset_From_Image +    rrca               ; Codigo de Get_Attr_Offset_From_Image 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   AND +    and 
-   OR $58 +    or $58 
-   LD DA +    ld da 
-   LD EL+    ld el
  
-   ;;; Escribir el atributo en memoria +    ;;; Escribir el atributo en memoria 
-   LD A, (FONT_ATTRIB) +    ld a, (FONT_ATTRIB) 
-   LD (DE),         ; Escribimos el atributo en memoria +    ld (de),         ; Escribimos el atributo en memoria 
-   RET+    ret
 </code> </code>
  
- Si definimos esta función PrintChar_8x8 en nuestro programa, la función PrintString_8x8 hará uso de ella y podremos imprimir cadenas en diferentes estilos, como en el siguiente ejemplo:+ Si definimos esta función ''PrintChar_8x8'' en nuestro programa, la función ''PrintString_8x8'' hará uso de ella y podremos imprimir cadenas en diferentes estilos, como en el siguiente ejemplo:
  
 <code z80> <code z80>
-  ; Ejemplo de estilos de fuente +; Ejemplo de estilos de fuente 
-  ORG 32768+    ORG 35000
  
-  LD HL, $3D00-256        ; Saltamos los 32 caracteres iniciales +    ld hl, $3d00-256        ; Saltamos los 32 caracteres iniciales 
-  LD (FONT_CHARSET), HL +    ld (FONT_CHARSET), hl 
-  LD A, 1+(7*8) +    ld a, 1+(7*8) 
-  LD (FONT_ATTRIB), A+    ld (FONT_ATTRIB), a
  
-  ;;; Probamos los diferentes estilos: NORMAL +    ;;; Probamos los diferentes estilos: NORMAL 
-  LD A, FONT_NORMAL +    ld a, FONT_NORMAL 
-  LD (FONT_STYLE), A +    ld (FONT_STYLE), a 
-  LD HL, cadena1 +    ld hl, cadena1 
-  LD A, 4 +    ld a, 4 
-  LD (FONT_Y), A +    ld (FONT_Y), a 
-  XOR A +    xor a 
-  LD (FONT_X), A +    ld (FONT_X), a 
-  CALL PrintString_8x8+    call PrintString_8x8
  
-  ;;; Probamos los diferentes estilos: NEGRITA +    ;;; Probamos los diferentes estilos: NEGRITA 
-  LD A, FONT_BOLD +    ld a, FONT_BOLD 
-  LD (FONT_STYLE), A +    ld (FONT_STYLE), a 
-  LD HL, cadena2 +    ld hl, cadena2 
-  LD A, 6 +    ld a, 6 
-  LD (FONT_Y), A +    ld (FONT_Y), a 
-  XOR A +    xor a 
-  LD (FONT_X), A +    ld (FONT_X), a 
-  CALL PrintString_8x8+    call PrintString_8x8
  
-  ;;; Probamos los diferentes estilos: CURSIVA +    ;;; Probamos los diferentes estilos: CURSIVA 
-  LD A, FONT_ITALIC +    ld a, FONT_ITALIC 
-  LD (FONT_STYLE), A +    ld (FONT_STYLE), a 
-  LD HL, cadena3 +    ld hl, cadena3 
-  LD A, 8 +    ld a, 8 
-  LD (FONT_Y), A +    ld (FONT_Y), a 
-  XOR A +    xor a 
-  LD (FONT_X), A +    ld (FONT_X), a 
-  CALL PrintString_8x8+    call PrintString_8x8
  
-  ;;; Probamos los diferentes estilos: SUBRAYADO +    ;;; Probamos los diferentes estilos: SUBRAYADO 
-  LD A, FONT_UNDERSC +    ld a, FONT_UNDERSC 
-  LD (FONT_STYLE), A +    ld (FONT_STYLE), a 
-  LD HL, cadena4 +    ld hl, cadena4 
-  LD A, 10 +    ld a, 10 
-  LD (FONT_Y), A +    ld (FONT_Y), a 
-  XOR A +    xor a 
-  LD (FONT_X), A +    ld (FONT_X), a 
-  CALL PrintString_8x8+    call PrintString_8x8
  
 loop: loop:
-  JR loop+    jr loop
  
-  RET+    ret
  
 cadena1 DB "IMPRESION CON ESTILO NORMAL.", 0 cadena1 DB "IMPRESION CON ESTILO NORMAL.", 0
Línea 1552: Línea 1537:
 cadena3 DB "IMPRESION CON ESTILO CURSIVA.", 0 cadena3 DB "IMPRESION CON ESTILO CURSIVA.", 0
 cadena4 DB "IMPRESION CON ESTILO SUBRAYADO.", 0 cadena4 DB "IMPRESION CON ESTILO SUBRAYADO.", 0
- 
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
-FONT_CHARSET     DW    $3D00-256+FONT_CHARSET     DW    $3d00-256
 FONT_ATTRIB      DB    56       ; Negro sobre gris FONT_ATTRIB      DB    56       ; Negro sobre gris
 FONT_STYLE       DB    0 FONT_STYLE       DB    0
Línea 1566: Línea 1550:
 FONT_SCRWIDTH    EQU   32 FONT_SCRWIDTH    EQU   32
 FONT_SCRHEIGHT   EQU   24 FONT_SCRHEIGHT   EQU   24
 +
 +    END 35000
 </code> </code>
  
  El anterior ejemplo (que usa el juego de caracteres estándar de la ROM) produce el siguiente resultado en pantalla:  El anterior ejemplo (que usa el juego de caracteres estándar de la ROM) produce el siguiente resultado en pantalla:
  
-\\  +\\ 
-{{ :cursos:ensamblador:gfx4_estilos.png | Estilos de texto con PrintChar_8x8 }} +{{ :cursos:ensamblador:gfx4_estilos.png?640 | Estilos de texto con PrintChar_8x8 }} 
-\\ +\\
  
- La rutina de impresión PrintChar_8x8 es ahora ligeramente más lenta que la original, pero a cambio nos permite diferentes estilos de texto. Para la impresión de texto con estilo normal, sólo le hemos añadido el siguiente código adicional a ejecutar:+ La rutina de impresión ''PrintChar_8x8'' es ahora ligeramente más lenta que la original, pero a cambio nos permite diferentes estilos de texto. Para la impresión de texto con estilo normal, sólo le hemos añadido el siguiente código adicional a ejecutar:
  
 <code z80> <code z80>
-   LD A, (FONT_STYLE) +    ld a, (FONT_STYLE) 
-   OR A +    or a 
-   JR NZ, pchar8_estilos_on      ; Aqui no se produce salto+    jr nz, pchar8_estilos_on      ; Aqui no se produce salto
  
-   ++    +
  
-   JR pchar8_printattr           ; Este salto si se produce +    jr pchar8_printattr           ; Este salto si se produce 
-</code>    +</code> 
-    + 
- Son 13 (LD) + 4 (OR) + 7 (JR NZ sin salto) + 12 (JR) = 36 t-estados adicionales por carácter en estilo normal a cambio de la posibilidad de disponer de 4 estilos de texto diferentes para cualquier charset, incluído el de la ROM.+ Son 13 (''LD'') + 4 (''OR'') + 7 (''jr NZ sin salto'') + 12 (''jr'') = 36 t-estados adicionales por carácter en estilo normal a cambio de la posibilidad de disponer de 4 estilos de texto diferentes para cualquier charset, incluído el de la ROM.
  
  En la rutina se han utilizado operaciones de transferencia LD para imprimir los caracteres, lo que implica que no se respeta el fondo sobre el que se imprime, y se ponen a cero en pantalla todos los píxeles a cero en el charset. Este suele ser el sistema de impresión habitual puesto que el texto, para hacerlo legible, suele imprimirse sobre áreas en blanco de la pantalla, y un caracter impreso sobre otro debe sobreescribir totalmente al primero.  En la rutina se han utilizado operaciones de transferencia LD para imprimir los caracteres, lo que implica que no se respeta el fondo sobre el que se imprime, y se ponen a cero en pantalla todos los píxeles a cero en el charset. Este suele ser el sistema de impresión habitual puesto que el texto, para hacerlo legible, suele imprimirse sobre áreas en blanco de la pantalla, y un caracter impreso sobre otro debe sobreescribir totalmente al primero.
  
- No obstante, la rutina PrintChar_8x8 puede ser modificada por el lector, para utilizar operaciones OR en la transferencia a pantalla y por tanto respetar el contenido de pantalla al imprimir el carácter.+ No obstante, la rutina ''PrintChar_8x8'' puede ser modificada por el lector, para utilizar operaciones ''OR'' en la transferencia a pantalla y por tanto respetar el contenido de pantalla al imprimir el carácter.
  
  
-\\ +\\
 ==== Impresión de cadenas con códigos de control ==== ==== Impresión de cadenas con códigos de control ====
  
  Llegados a este punto, tenemos:  Llegados a este punto, tenemos:
  
-\\ +\\
   * Un sistema de variables que controlan los datos sobre la impresión: posición, estilo, color, fuente de texto, etc.   * Un sistema de variables que controlan los datos sobre la impresión: posición, estilo, color, fuente de texto, etc.
   * Un set de funciones que permite modificar estas variables fácilmente.   * Un set de funciones que permite modificar estas variables fácilmente.
   * Una función de impresión de caracteres 8x8 que utiliza estas variables para imprimir el carácter indicado en la posición adecuada, con el estilo seleccionado y con el color y fondo elegidos.   * Una función de impresión de caracteres 8x8 que utiliza estas variables para imprimir el carácter indicado en la posición adecuada, con el estilo seleccionado y con el color y fondo elegidos.
-\\ +\\
  
- El siguiente paso en la escala de la gestión del texto sería la **impresión de cadenas con formato** para que aproveche nuestras nuevas funciones extendidas. Para esto modificaremos la rutina PrinString_8x8 vista al principio del capítulo de forma que haga uso no sólo de FONT_X y FONT_Y sino también de funciones adicionales que especificaremos en la cadena mediante códigos de control o //tokens//.+ El siguiente paso en la escala de la gestión del texto sería la **impresión de cadenas con formato** para que aproveche nuestras nuevas funciones extendidas. Para esto modificaremos la rutina ''PrinString_8x8'' vista al principio del capítulo de forma que haga uso no sólo de ''FONT_X'' ''FONT_Y'' sino también de funciones adicionales que especificaremos en la cadena mediante códigos de control o //tokens//.
  
  Los códigos de control que vamos a definir y utilizar serán los siguientes:  Los códigos de control que vamos a definir y utilizar serán los siguientes:
  
-\\ +\\
 |< 70% >| |< 70% >|
-^ Código de control ^ Significado ^ +^ Código de control ^ Significado ^
 | 0 | Indica Fin de cadena | | 0 | Indica Fin de cadena |
 | 1 | Cambiar estilo (seguido del byte con el estilo) | | 1 | Cambiar estilo (seguido del byte con el estilo) |
Línea 1626: Línea 1612:
 | 14 | Backspace: borrar el caracter de (x-1 si x>0)\\ imprimiendo un espacio y decrementando x. | | 14 | Backspace: borrar el caracter de (x-1 si x>0)\\ imprimiendo un espacio y decrementando x. |
 | 15 | Tabulador (impresion de 3 espacios por llamada a PrintString) | | 15 | Tabulador (impresion de 3 espacios por llamada a PrintString) |
-\\ +\\
  
  Así pues, declaramos las siguientes constantes predefinidas:  Así pues, declaramos las siguientes constantes predefinidas:
Línea 1660: Línea 1646:
  La rutina de impresión de cadenas deberá interpretar cada byte de la misma determinando:  La rutina de impresión de cadenas deberá interpretar cada byte de la misma determinando:
  
-  * Si es un código ASCII >= 32 -> Imprimir el caracter en FONT_X, FONT_Y (y variar las coordenadas). +  * Si es un código ASCII >= 32 -> Imprimir el caracter en ''FONT_X''''FONT_Y'' (y variar las coordenadas). 
-  * Si es un código de control 0 (EOS) -> Fin de la rutina+  * Si es un código de control 0 (''EOS'') -> Fin de la rutina. Nótese que, al contrario que en caso de la rutina PrintString que nos fabricamos para la ROM en nuestra librería **utils.asm**, en esta rutina podemos leer los parámetros de FLASH/BRIGHT e interpretarlos nosotros, por lo que un 0 que vaya detrás de un código de control, no se contabilizará como END_OF_STRING.
   * Si es un código entre el 1 y el 9 -> Recoger parámetro en cadena (siguiente byte) y llamar a la función apropiada.   * Si es un código entre el 1 y el 9 -> Recoger parámetro en cadena (siguiente byte) y llamar a la función apropiada.
   * Si es un código entre el 10 y el 31 -> Llamar a la función apropiada (no hay parámetro).   * Si es un código entre el 10 y el 31 -> Llamar a la función apropiada (no hay parámetro).
Línea 1667: Línea 1653:
  Esto nos permitirá trabajar con cadenas de texto con múltiples formatos sin tener que realizar el posicionamiento, cambio de color, de papel, gestión de los retornos de carro, etc. en nuestro código, con un gran ahorro en código de manipulación gracias a nuestra nueva rutina genérica de impresión.  Esto nos permitirá trabajar con cadenas de texto con múltiples formatos sin tener que realizar el posicionamiento, cambio de color, de papel, gestión de los retornos de carro, etc. en nuestro código, con un gran ahorro en código de manipulación gracias a nuestra nueva rutina genérica de impresión.
  
- Llamaremos a esta rutina de impresión con formato //PrintString_8x8_Format//, y tendrá el siguiente pseudocódigo:+ Llamaremos a esta rutina de impresión con formato ''PrintString_8x8_Format'', y tendrá el siguiente pseudocódigo:
  
 <code> <code>
Línea 1673: Línea 1659:
  
 PrintString_8x8_Format: PrintString_8x8_Format:
-   ;;; +   ;;;
 bucle: bucle:
       ;;; Coger caracter apuntado por HL.       ;;; Coger caracter apuntado por HL.
Línea 1681: Línea 1667:
           ;;; Avanzar el puntero FONT_X           ;;; Avanzar el puntero FONT_X
       ;;; Si HL es menor que 31 :       ;;; Si HL es menor que 31 :
-          ;;; Si es CERO, salir de la rutina con RET Z.+          ;;; Si es CERO, salir de la rutina con ret z.
           ;;; Si es FONT_SET_SETSTYLE (1):           ;;; Si es FONT_SET_SETSTYLE (1):
               ;;; Coger el siguiente byte de la cadena (el estilo)               ;;; Coger el siguiente byte de la cadena (el estilo)
Línea 1698: Línea 1684:
           ;;; Si es FONT_TAB (15):           ;;; Si es FONT_TAB (15):
               ;;; Llamar a funcion Font_Tab               ;;; Llamar a funcion Font_Tab
-      ;;; Saltar a bucle (se saldrá con el RET Z)+      ;;; Saltar a bucle (se saldrá con el ret z)
 </code> </code>
  
Línea 1707: Línea 1693:
 <code z80> <code z80>
 ;------------------------------------------------------------- ;-------------------------------------------------------------
-; Tabla con las direcciones de las 16 rutinas de cambio. +; Tabla con las direcciones de las 16 rutinas de cambio.
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 FONT_CALL_JUMP_TABLE: FONT_CALL_JUMP_TABLE:
-  DW 0000, Font_Set_Style, Font_Set_X, Font_Set_Y, Font_Set_Ink +    DW 0000, Font_Set_Style, Font_Set_X, Font_Set_Y, Font_Set_Ink 
-  DW Font_Set_Paper, Font_Set_Attrib, Font_Set_Bright +    DW Font_Set_Paper, Font_Set_Attrib, Font_Set_Bright 
-  DW Font_Set_Flash, 0000, Font_LF, Font_CRLF, Font_Blank +    DW Font_Set_Flash, 0000, Font_LF, Font_CRLF, Font_Blank 
-  DW Font_CR, Font_Backspace, Font_Tab, Font_Inc_X+    DW Font_CR, Font_Backspace, Font_Tab, Font_Inc_X
 </code> </code>
  
Línea 1721: Línea 1707:
 PrintString_8x8_Format: PrintString_8x8_Format:
 bucle: bucle:
-      ;;; Coger caracter apuntador por HL.+      ;;; Coger caracter apuntado por HL.
       ;;; Incrementar HL       ;;; Incrementar HL
       ;;; Si HL es mayor que 32 :       ;;; Si HL es mayor que 32 :
Línea 1727: Línea 1713:
           ;;; Avanzar el puntero FONT_X           ;;; Avanzar el puntero FONT_X
       ;;; Si HL es menor que 31 :       ;;; Si HL es menor que 31 :
-          ;;; Si es CERO, salir de la rutina con RET Z.+          ;;; Si es CERO, salir de la rutina con ret z.
           ;;; Calculamos DIR_SALTO = TABLA_SALTOS [ COD_CONTROL ]           ;;; Calculamos DIR_SALTO = TABLA_SALTOS [ COD_CONTROL ]
-          ;;; Como la tabla es de 2 bytes -> DIR_SALTO = TABLA_SALTOS + COD_CONTROL*2 +          ;;; Como la tabla es de 2 bytes -> DIR_SALTO = TABLA_SALTOS + COD_CONTROL*2
           ;;; Si es menor que 10, requiere recoger parametro           ;;; Si es menor que 10, requiere recoger parametro
               ;;; Recoger parametro               ;;; Recoger parametro
           ;;; Si es mayor que 10, no requiere recoger parametro           ;;; Si es mayor que 10, no requiere recoger parametro
           ;;; Saltar a la dirección DIR_SALTO           ;;; Saltar a la dirección DIR_SALTO
-      ;;; Saltar a bucle (se saldrá con el RET Z)+      ;;; Saltar a bucle (se saldrá con el ret z)
 </code> </code>
  
Línea 1741: Línea 1727:
 <code z80> <code z80>
 ;------------------------------------------------------------- ;-------------------------------------------------------------
-; Tabla con las direcciones de las 16 rutinas de cambio. +; Tabla con las direcciones de las 16 rutinas de cambio.
 ; Notese que la 9 queda libre para una posible ampliacion. ; Notese que la 9 queda libre para una posible ampliacion.
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 FONT_CALL_JUMP_TABLE: FONT_CALL_JUMP_TABLE:
-  DW 0000, Font_Set_Style, Font_Set_X, Font_Set_Y, Font_Set_Ink +    DW 0000, Font_Set_Style, Font_Set_X, Font_Set_Y, Font_Set_Ink 
-  DW Font_Set_Paper, Font_Set_Attrib, Font_Set_Bright +    DW Font_Set_Paper, Font_Set_Attrib, Font_Set_Bright 
-  DW Font_Set_Flash, 0000, Font_LF, Font_CRLF, Font_Blank +    DW Font_Set_Flash, 0000, Font_LF, Font_CRLF, Font_Blank 
-  DW Font_CR, Font_Backspace, Font_Tab, Font_Inc_X+    DW Font_CR, Font_Backspace, Font_Tab, Font_Inc_X
  
  
Línea 1766: Línea 1752:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 PrintString_8x8_Format: PrintString_8x8_Format:
-    + 
-   ;;; Bucle de impresion de caracter+    ;;; Bucle de impresion de caracter
 pstring8_loop: pstring8_loop:
-   LD A, (HL)                ; Leemos un caracter de la cadena +    ld a, (hl)                ; Leemos un caracter de la cadena 
-   INC HL                    ; Apuntamos al siguiente caracter+    inc hl                    ; Apuntamos al siguiente caracter
  
-   CP 32                     ; Es menor que 32? +    cp 32                     ; Es menor que 32? 
-   JP C,  pstring8_ccontrol  ; Si, es un codigo de control, saltar+    jp c,  pstring8_ccontrol  ; Si, es un codigo de control, saltar
  
-   PUSH HL                   ; Salvaguardamos HL +    push hl                   ; Salvaguardamos HL 
-   CALL PrintChar_8x8        ; Imprimimos el caracter +    call PrintChar_8x8        ; Imprimimos el caracter 
-   POP HL                    ; Recuperamos HL+    pop hl                    ; Recuperamos HL
  
-   ;;; Avanzamos el cursor usando Font_Blank, que incrementa X +    ;;; Avanzamos el cursor usando Font_Blank, que incrementa X 
-   ;;; y actualiza X e Y si se llega al borde de la pantalla +    ;;; y actualiza X e Y si se llega al borde de la pantalla 
-   CALL Font_Inc_X           ; Avanzar coordenada X +    call Font_Inc_X           ; Avanzar coordenada X 
-   JR pstring8_loop          ; Continuar impresion hasta CHAR=0+    jr pstring8_loop          ; Continuar impresion hasta CHAR=0
  
 pstring8_ccontrol: pstring8_ccontrol:
-   OR A                      ; A es cero?  +    or a                      ; A es cero? 
-   RET Z                     ; Si es 0 (fin de cadena) volver +    ret z                     ; Si es 0 (fin de cadena) volver
-    +
-   ;;; Si estamos aqui es porque es un codigo de control distinto > 0 +
-   ;;; Ahora debemos calcular la direccion de la rutina que lo atendera.+
  
-   ;;; Calculamos la direccion destino a la que saltar usando +    ;;; Si estamos aqui es porque es un codigo de control distinto > 0 
-   ;;; la tabla de saltos y el codigo de control como indice +    ;;; Ahora debemos calcular la direccion de la rutina que lo atendera.
-   EX DE, HL +
-   LD HL, FONT_CALL_JUMP_TABLE +
-   RLCA                      A = A * 2 = codigo de control * 2 +
-   LD C, A +
-   LD B, 0                   BC = A*2 +
-   ADD HL, BC                HL = DIR FONT_CALL_JUMP_TABLE+(CodControl*2) +
-   LD C, (HL)                ; Leemos la parte baja de la direccion en C... +
-   INC HL                    ; ... para no corromper HL y poder leer ... +
-   LD H, (HL)                ; ... la parte alta sobre H ... +
-   LD L, C                   ; No hemos usado A porque se usa en el CP+
  
-   ;;; Si CCONTROL>0 y CCONTROL<10 -> recoger parametro y saltar rutina +    ;;; Calculamos la direccion destino la que saltar usando 
-   ;;; Si CCONTROL>CCONTROL<32 -> saltar rutina sin recogida +    ;;; la tabla de saltos el codigo de control como indice 
-   CP 18                     Comprobamos si (CCONTROL-1)*2 < 18 +    ex de, hl 
-   JP NCpstring8_noparam   Es decirsi CCONTROL > 9no hay param+    ld hl, FONT_CALL_JUMP_TABLE 
 +    rlca                      ; A = A * 2 = codigo de control * 2 
 +    ld c, 
 +    ld b, 0                   BC = A*2 
 +    add hl, bc                ; HL = DIR FONT_CALL_JUMP_TABLE+(CodControl*2) 
 +    ld c(hl)                Leemos la parte baja de la direccion en C... 
 +    inc hl                    ; ... para no corromper HL y poder leer ... 
 +    ld h(hl)                ; ... la parte alta sobre H ... 
 +    ld lc                   ; No hemos usado A porque se usa en el CP
  
-   ;;; Si CCONTROL < 10 -> recoger parametro: +    ;;; Si CCONTROL>0 y CCONTROL<10 -> recoger parametro y saltar a rutina 
-   LD A, (DE               ; Cogemos el parametro en cuestion de la cadena +    ;;; Si CCONTROL>9 y CCONTROL<32 -> saltar a rutina sin recogida 
-   INC DE                    Apuntamos al siguiente caracter+    cp 18                     ; Comprobamos si (CCONTROL-1)*2 < 18 
 +    jp nc, pstring8_noparam   ; Es decir, si CCONTROL > 9, no hay param
  
-   ;;; Realizamos el salto a la rutina con o sin parametro recogido+    ;;; Si CCONTROL < 10 -> recoger parametro: 
 +    ld a, (de)                ; Cogemos el parametro en cuestion de la cadena 
 +    inc de                    ; Apuntamos al siguiente caracter 
 + 
 +    ;;; Realizamos el salto a la rutina con o sin parametro recogido
 pstring8_noparam: pstring8_noparam:
-   LD BC, pstring8_retaddr   ; Ponemos en BC la dir de retorno +    ld bc, pstring8_retaddr   ; Ponemos en BC la dir de retorno 
-   PUSH BC                   ; Hacemos un push de la dir de retorno +    push bc                   ; Hacemos un push de la dir de retorno 
-   JP (HL)                   ; Saltamos a la rutina seleccionada+    jp (hl)                   ; Saltamos a la rutina seleccionada
  
-   ;;; Este es el punto al que volvemos tras la rutina+    ;;; Este es el punto al que volvemos tras la rutina
 pstring8_retaddr: pstring8_retaddr:
-   EX DEHL                 ; Recuperamos en HL el puntero a cadena +    ex dehl                 ; Recuperamos en HL el puntero a cadena 
-   JR pstring8_loop          ; Continuamos en el bucle +    jr pstring8_loop          ; Continuamos en el bucle 
-   +
 </code> </code>
  
  
- El esqueleto de la rutina y la parte de impresión ya la conocemos, porque es idéntica a PrintString_8x8. El principal añadido es la interpretación de los códigos de control, donde la parte más interesante es la construcción y uso de la tabla de saltos: + El esqueleto de la rutina y la parte de impresión ya la conocemos, porque es idéntica a ''PrintString_8x8''. El principal añadido es la interpretación de los códigos de control, donde la parte más interesante es la construcción y uso de la tabla de saltos:
  
- Una vez ubicadas todas las diferentes direcciones de las rutinas en FONT_CALL_JUMP_TABLE, podemos utilizar el valor del registro A para direccionar la tabla. Para ello debemos multiplicar A por 2 ya que cada dirección consta de 2 bytes. Cargando A*2 en BC podemos calcular la dirección destino en la tabla como HL+BC (BASE+DESPLAZAMIENTO = BASE+COD_CONTROL*2). Leyendo el valor apuntado por HL obtenemos la dirección de la tabla, es decir, la dirección de la rutina que puede interpretar el código de control que hemos recibido.+ Una vez ubicadas todas las diferentes direcciones de las rutinas en ''FONT_CALL_JUMP_TABLE'', podemos utilizar el valor del registro A para direccionar la tabla. Para ello debemos multiplicar A por 2 ya que cada dirección consta de 2 bytes. Cargando A*2 en BC podemos calcular la dirección destino en la tabla como HL+BC (BASE+DESPLAZAMIENTO = BASE+COD_CONTROL*2). Leyendo el valor apuntado por HL obtenemos la dirección de la tabla, es decir, la dirección de la rutina que puede interpretar el código de control que hemos recibido.
  
 <code z80> <code z80>
-   ;;; Calculamos la direccion destino a la que saltar usando +    ;;; Calculamos la direccion destino a la que saltar usando 
-   ;;; la tabla de saltos y el codigo de control como indice +    ;;; la tabla de saltos y el codigo de control como indice 
-   EX DEHL +    ex dehl 
-   LD HL, FONT_CALL_JUMP_TABLE +    ld hl, FONT_CALL_JUMP_TABLE 
-   RLCA                      ; A = A * 2 = codigo de control * 2 +    rlca                      ; A = A * 2 = codigo de control * 2 
-   LD CA +    ld ca 
-   LD B, 0                   ; BC = A*2 +    ld b, 0                   ; BC = A*2 
-   ADD HLBC                ; HL = DIR FONT_CALL_JUMP_TABLE+(CodControl*2) +    add hlbc                ; HL = DIR FONT_CALL_JUMP_TABLE+(CodControl*2) 
-   LD C, (HL)                ; Leemos la parte baja de la direccion en C... +    ld c, (hl)                ; Leemos la parte baja de la direccion en C... 
-   INC HL                    ; ... para no corromper HL y poder leer ... +    inc hl                    ; ... para no corromper HL y poder leer ... 
-   LD H, (HL)                ; ... la parte alta sobre H ... +    ld h, (hl)                ; ... la parte alta sobre H ... 
-   LD L                  ; No hemos usado A porque se usa en el CP +    ld l                  ; No hemos usado A porque se usa en el CP 
-                             ; HL = FONT_CALL_JUMP_TABLE+(CodControl*2) +                              ; HL = FONT_CALL_JUMP_TABLE+(CodControl*2)
-                              +
-   ; (...)                   ; Codigo de recogida de parametro si procede +
-    +
-   LD BC, pstring8_retaddr   ; Ponemos en BC la dir de retorno +
-   PUSH BC                   ; Hacemos un push de la dir de retorno +
-   JP (HL)                   ; Saltamos a la rutina seleccionada+
  
-   ;;; Este es el punto al que volvemos tras la rutina+    ; (...)                   ; Codigo de recogida de parametro si procede 
 + 
 +    ld bc, pstring8_retaddr   ; Ponemos en BC la dir de retorno 
 +    push bc                   ; Hacemos un push de la dir de retorno 
 +    jp (hl)                   ; Saltamos a la rutina seleccionada 
 + 
 +    ;;; Este es el punto al que volvemos tras la rutina
 pstring8_retaddr: pstring8_retaddr:
 </code> </code>
  
- Con el anterior cálculo, por ejemplo, si recibimos un código de control 6, se pondrá en HL la dirección de memoria contenida en FONT_CALL_JUMP_TABLE+(6*2), que es el valor //Font_Set_Attrib//, que el ensamblador sustituirá en la tabla durante el proceso de ensamblado por la dirección de memoria de dicha rutina.+ Con el anterior cálculo, por ejemplo, si recibimos un código de control 6, se pondrá en HL la dirección de memoria contenida en FONT_CALL_JUMP_TABLE+(6*2), que es el valor ''Font_Set_Attrib'', que el ensamblador sustituirá en la tabla durante el proceso de ensamblado por la dirección de memoria de dicha rutina.
  
- Nótese cómo después de calcular el valor de salto correcto para HL tenemos que simular un "CALL HL", que no forma parte del juego de instrucciones del Spectrum. ¿Cómo realizamos esto? Utilizando la pila y la instrucción JP. Recordemos que un CALL es un salto a subrutina, lo cual implica introducir en la pila la dirección de retorno y salta a la dirección de la rutina. Cuando la rutina realice el RET, se extrae de la pila la dirección de retorno para continuar el flujo del programa.+ Nótese cómo después de calcular el valor de salto correcto para HL tenemos que simular un ''call HL'' que no forma parte del juego de instrucciones del Spectrum. ¿Cómo realizamos esto? Utilizando la pila y la instrucción jp. Recordemos que un call es un salto a subrutina, lo cual implica introducir en la pila la dirección de retorno y salta a la dirección de la rutina. Cuando la rutina realice el ret, se extrae de la pila la dirección de retorno para continuar el flujo del programa.
  
- En el código anterior introducimos en el registro BC la dirección de la etiqueta **pstring8_retaddr**, que es la posición exacta de memoria después del salto. Una vez introducida en la pila la dirección de retorno, saltamos con el salto incondicional **JP (HL)** a la rutina especificada por el código de control. La subrutina efectuará la tarea correspondiente y volverá con un RET, provocando que la rutina de impresión de cadenas continúe en pstring8_retaddr, que es la dirección que el RET extraerá de la pila para volver.+ En el código anterior introducimos en el registro BC la dirección de la etiqueta ''pstring8_retaddr'', que es la posición exacta de memoria después del salto. Una vez introducida en la pila la dirección de retorno, saltamos con el salto incondicional ''jp (hl)'' a la rutina especificada por el código de control. La subrutina efectuará la tarea correspondiente y volverá con un ret, provocando que la rutina de impresión de cadenas continúe en ''pstring8_retaddr'', que es la dirección que el ''RET'' extraerá de la pila para volver.
  
  Hemos hecho distinción de 2 tipos de subrutinas de control, ya que las 9 primeras requieren recoger un parámetro de la cadena (apuntado por HL) y las restantes no. El cálculo de la dirección de salto es igual en todos los casos pero para las 9 primeras es necesario obtener el dato adicional al código de control en el registro A antes de saltar. El registro A es el parámetro común en todas las subrutinas de gestión de códigos de control que requieren parámetros, algo necesario para poder usar las rutinas vía tabla de saltos.  Hemos hecho distinción de 2 tipos de subrutinas de control, ya que las 9 primeras requieren recoger un parámetro de la cadena (apuntado por HL) y las restantes no. El cálculo de la dirección de salto es igual en todos los casos pero para las 9 primeras es necesario obtener el dato adicional al código de control en el registro A antes de saltar. El registro A es el parámetro común en todas las subrutinas de gestión de códigos de control que requieren parámetros, algo necesario para poder usar las rutinas vía tabla de saltos.
Línea 1867: Línea 1853:
  
 <code z80> <code z80>
-   ;;; Si CCONTROL>0 y CCONTROL<10 -> recoger parametro y saltar a rutina +    ;;; Si CCONTROL>0 y CCONTROL<10 -> recoger parametro y saltar a rutina 
-   ;;; Si CCONTROL>9 y CCONTROL<32 -> saltar a rutina sin recogida +    ;;; Si CCONTROL>9 y CCONTROL<32 -> saltar a rutina sin recogida 
-   CP 18                     ; Comprobamos si (CCONTROL-1)*2 < 18 +    cp 18                     ; Comprobamos si (CCONTROL-1)*2 < 18 
-   JP NC, pstring8_noparam   ; Es decir, si CCONTROL > 9, no hay param+    jp nc, pstring8_noparam   ; Es decir, si CCONTROL > 9, no hay param
 </code> </code>
  
  En lugar de volver a dividir el código de control entre 2 (recordemos que se multiplicó por 2 para el cálculo de la dirección de salto) y comprobar si es > 9, podemos comprobar directamente si es > 9*2 = 18.  En lugar de volver a dividir el código de control entre 2 (recordemos que se multiplicó por 2 para el cálculo de la dirección de salto) y comprobar si es > 9, podemos comprobar directamente si es > 9*2 = 18.
  
- Tras interpretar el código de control, bastará con volver a saltar al principio de la rutina para continuar con el siguiente carácter. Todo el proceso se repetirá hasta recibir en A un código de control 0 (FONT_EOS, de FONT_END_OF_STRING).+ Tras interpretar el código de control, bastará con volver a saltar al principio de la rutina para continuar con el siguiente carácter. Todo el proceso se repetirá hasta recibir en A un código de control 0 (''FONT_EOS'', de FONT_END_OF_STRING).
  
  Una vez explicada la rutina, veamos un ejemplo de cómo podríamos utilizarla en nuestros programas:  Una vez explicada la rutina, veamos un ejemplo de cómo podríamos utilizarla en nuestros programas:
  
 <code z80> <code z80>
-  ; Ejemplo de gestion de texto +; Ejemplo de gestion de texto 
-  ORG 32768+    ORG 35000
  
-  LD HL, $3D00-256        ; Saltamos los 32 caracteres iniciales +    ld hl, $3d00-256        ; Saltamos los 32 caracteres iniciales 
-  CALL Font_Set_Charset+    call Font_Set_Charset
  
-  LD A, 1+(7*8) +    ld a, 1+(7*8) 
-  Call Font_Set_Attrib+    Call Font_Set_Attrib
  
-  ;;; Probamos los diferentes estilos: NORMAL +    ;;; Probamos los diferentes estilos: NORMAL 
-  LD A, FONT_NORMAL +    ld a, FONT_NORMAL 
-  CALL Font_Set_Style +    call Font_Set_Style 
-  LD HL, cadena1 +    ld hl, cadena1 
-  LD B, 4 +    ld b, 4 
-  LD C, 0 +    ld c, 0 
-  CALL Font_Set_XY +    call Font_Set_XY 
-  CALL PrintString_8x8_Format+    call PrintString_8x8_Format
  
-loop:  +loop: 
-  JR loop+    jr loop
  
 cadena1 DB "SALTO DE", FONT_LF, "LINEA ", FONT_SET_X, 4, FONT_SET_Y, 9 cadena1 DB "SALTO DE", FONT_LF, "LINEA ", FONT_SET_X, 4, FONT_SET_Y, 9
Línea 1910: Línea 1896:
         DB FONT_SET_INK, 6, "TABULADOR + INK 6 PAPER 0"         DB FONT_SET_INK, 6, "TABULADOR + INK 6 PAPER 0"
         DB FONT_EOS         DB FONT_EOS
 +
 +    END 35000
 </code> </code>
  
  La salida en pantalla del anterior ejemplo (añadiendo las funciones correspondientes al código):  La salida en pantalla del anterior ejemplo (añadiendo las funciones correspondientes al código):
  
-\\  +\\ 
-{{ :cursos:ensamblador:gfx4_texto.png | Impresion de cadenas con codigos de control }} +{{ :cursos:ensamblador:gfx4_texto.png?640 | Impresion de cadenas con codigos de control }} 
-\\ +\\
  
  Es importante destacar que podríamos ampliar la rutina de impresión con más códigos de control y funciones. Con la configuración que hemos visto, el código de control 9 queda libre para la introducción de una función adicional que requiera parámetro, y del 17 al 31 podemos añadir más funciones de formato que no requieran parámetros (por ejemplo, combinaciones de color y estilos concretos en una función que cambie BRIGHT, FLASH, INK, PAPER y STYLE, o incluso cambios entre diferentes charsets).  Es importante destacar que podríamos ampliar la rutina de impresión con más códigos de control y funciones. Con la configuración que hemos visto, el código de control 9 queda libre para la introducción de una función adicional que requiera parámetro, y del 17 al 31 podemos añadir más funciones de formato que no requieran parámetros (por ejemplo, combinaciones de color y estilos concretos en una función que cambie BRIGHT, FLASH, INK, PAPER y STYLE, o incluso cambios entre diferentes charsets).
  
- Si necesitaremos más "espacio" para rutinas con parámetro, podríamos "reubicar" los códigos de control por encima del 10 (cambiando los EQUs) y modificando el CP de la rutina que determinar si el control-code tiene parámetro o no. + Si necesitaremos más "espacio" para rutinas con parámetro, podríamos "reubicar" los códigos de control por encima del 10 (cambiando los EQUs) y modificando el CP de la rutina que determinar si el control-code tiene parámetro o no.
  
  Recomendamos al lector que utilice siempre en sus cadenas los códigos de control mediante las constantes EQU en lugar de utilizar los códigos numéricos en sí mismos. Esto permite reubicar los valores numéricos (los EQUs) sin modificar las cadenas. Recordemos que el ensamblador sustituirá las constantes por sus valores numéricos durante el proceso de ensamblado, por lo que la ocupación en las cadenas definitivas no será "mayor" al usar las constantes. El único código de control que no debe reubicarse nunca es FONT_EOS (0).  Recomendamos al lector que utilice siempre en sus cadenas los códigos de control mediante las constantes EQU en lugar de utilizar los códigos numéricos en sí mismos. Esto permite reubicar los valores numéricos (los EQUs) sin modificar las cadenas. Recordemos que el ensamblador sustituirá las constantes por sus valores numéricos durante el proceso de ensamblado, por lo que la ocupación en las cadenas definitivas no será "mayor" al usar las constantes. El único código de control que no debe reubicarse nunca es FONT_EOS (0).
  
- Finalmente, creemos importante indicar al lector que para marcar claramente la dirección de salto del código de control 9 (que no está en uso) se ha usado la cadena "0000", pero probablemente sería más seguro el colocar la dirección de una rutina como FONT_TAB o FONT_CRLF. De esta forma, ante un error del programador al escribir una cadena y utilizar el inexistente código 9 en ella, evitaremos que se produzca un reset (JP $0000) que nos cueste gran cantidad de horas de encontrar / depurar.+ Finalmente, creemos importante indicar al lector que para marcar claramente la dirección de salto del código de control 9 (que no está en uso) se ha usado la cadena "0000", pero probablemente sería más seguro el colocar la dirección de una rutina como FONT_TAB o FONT_CRLF. De esta forma, ante un error del programador al escribir una cadena y utilizar el inexistente código 9 en ella, evitaremos que se produzca un reset (''jp $0000'') que nos cueste gran cantidad de horas de encontrar / depurar.
  
  En cuanto a las diferencias en tiempo de ejecución de PrintString_8x8_Format vs PrintString_8x8, cabe destacar que el coste adicional de la rutina para la impresión del texto normal (ASCIIs < 32) se reduce a las siguientes 2 instrucciones adicionales:  En cuanto a las diferencias en tiempo de ejecución de PrintString_8x8_Format vs PrintString_8x8, cabe destacar que el coste adicional de la rutina para la impresión del texto normal (ASCIIs < 32) se reduce a las siguientes 2 instrucciones adicionales:
  
 <code z80> <code z80>
-   CP 32                     ; Es menor que 32? +    cp 32                     ; Es menor que 32? 
-   JP C,  pstring8_ccontrol  ; Si, es un codigo de control, saltar+    jp c,  pstring8_ccontrol  ; Si, es un codigo de control, saltar
 </code> </code>
  
- Aparte de eso, se ha sustituído el código de avance de la coordenada X por el de las rutinas genéricas vistas anteriormente, lo que añade un //CALL Font_Inc_X// (y su RET) adicional. Así pues, el coste en tiempo de ejecución no difiere apenas de la función sin códigos de control.+ Aparte de eso, se ha sustituído el código de avance de la coordenada X por el de las rutinas genéricas vistas anteriormente, lo que añade un ''call Font_Inc_X'' (y su ''RET'') adicional. Así pues, el coste en tiempo de ejecución no difiere apenas de la función sin códigos de control.
  
- En el caso del código de fin de cadena (EOS = 0), ya no se sale de la rutina con un RET Z sino que se pasa por el **CP 32** y se realiza el salto a //pstring8_ccontrol//.+ En el caso del código de fin de cadena (EOS = 0), ya no se sale de la rutina con un ret z sino que se pasa por el ''cp 32'' y se realiza el salto a ''pstring8_ccontrol''.
  
- Sí que hay un coste real en la ocupación de memoria, puesto que todas las funciones auxiliares de control que hemos definido seguramente pueden no resultarnos útiles en la programación de un juego donde no se utilice apenas texto. Ese código adicional sumado a la gestión de códigos de control de la rutina y a la tabla de saltos puede ser espacio utilizable por nosotros si empleados la rutina sin formato //PrintString_8x8//.+ Sí que hay un coste real en la ocupación de memoria, puesto que todas las funciones auxiliares de control que hemos definido seguramente pueden no resultarnos útiles en la programación de un juego donde no se utilice apenas texto. Ese código adicional sumado a la gestión de códigos de control de la rutina y a la tabla de saltos puede ser espacio utilizable por nosotros si empleados la rutina sin formato ''PrintString_8x8''.
  
  Donde no hay duda de la gran utilidad de las anteriores rutinas es en cualquier juego basado en texto, donde nos evitamos realizar el formato de los textos en base a programación y llamadas continuadas a las funciones de formato, posicionamiento, etc. Bastará con definir las cadenas en nuestro programa con el formato adecuado. El ahorro en líneas de código será muy considerable.  Donde no hay duda de la gran utilidad de las anteriores rutinas es en cualquier juego basado en texto, donde nos evitamos realizar el formato de los textos en base a programación y llamadas continuadas a las funciones de formato, posicionamiento, etc. Bastará con definir las cadenas en nuestro programa con el formato adecuado. El ahorro en líneas de código será muy considerable.
  
-\\ +\\
 ==== Impresión avanzada: datos variables ==== ==== Impresión avanzada: datos variables ====
  
- Nuestro siguiente objetivo es extender PringString_8x8_Format para permitir la utilización de códigos de control que representen valores de variables. El objetivo es simular la funcionalidad de la función printf() del lenguaje C, el cual permite impresiones de cadena como la siguiente:+ Nuestro siguiente objetivo es extender ''PringString_8x8_Format'' para permitir la utilización de códigos de control que representen valores de variables. El objetivo es simular la funcionalidad de la función printf() del lenguaje C, el cual permite impresiones de cadena como la siguiente:
  
 <code c> <code c>
Línea 1950: Línea 1938:
 </code> </code>
  
- Para eso vamos a crear una nueva rutina **PrintString_8x8_Format_Args** que además de los códigos de control de formato, comprenda códigos para la impresión de variables de cadena o numéricas en representación decimal, hexadecimal o binaria.+ Para eso vamos a crear una nueva rutina ''PrintString_8x8_Format_Args'' que además de los códigos de control de formato, comprenda códigos para la impresión de variables de cadena o numéricas en representación decimal, hexadecimal o binaria.
  
  Los nuevos códigos de control imitarán el formato de C (símbolo de % seguido de un identificador del tipo de variable) y podrán estar así integrados dentro del propio texto:  Los nuevos códigos de control imitarán el formato de C (símbolo de % seguido de un identificador del tipo de variable) y podrán estar así integrados dentro del propio texto:
  
-\\ +\\
 |< 70% >| |< 70% >|
 ^ Código de control ^ Significado ^ ^ Código de control ^ Significado ^
Línea 1966: Línea 1954:
 | %s | Imprimir argumento de tipo cadena (acabada en 0 / EOS) | | %s | Imprimir argumento de tipo cadena (acabada en 0 / EOS) |
 | <nowiki>%%</nowiki> | Símbolo del porcentaje (%) | | <nowiki>%%</nowiki> | Símbolo del porcentaje (%) |
-\\ +\\
  
  De esta forma podremos definir cadenas como:  De esta forma podremos definir cadenas como:
Línea 1980: Línea 1968:
  Nótese que podríamos haber empleado el sistema de códigos de formato con los ASCIIs libres entre el 17 y el 31. El lector puede adaptar fácilmente la rutina a ese sistema si así lo deseara.  Nótese que podríamos haber empleado el sistema de códigos de formato con los ASCIIs libres entre el 17 y el 31. El lector puede adaptar fácilmente la rutina a ese sistema si así lo deseara.
  
- Volvamos a PrintString_8x8_Format_Args: Nuestra nueva rutina deberá recibir ahora un parámetro adicional: además de la cadena a imprimir en HL, deberemos apuntar el registro IX a un array con los datos a sustuitir, o apuntando a una única variable de memoria si sólo hay un parámetro.+ Volvamos a ''PrintString_8x8_Format_Args'': Nuestra nueva rutina deberá recibir ahora un parámetro adicional: además de la cadena a imprimir en HL, deberemos apuntar el registro IX a un array con los datos a sustuitir, o apuntando a una única variable de memoria si sólo hay un parámetro.
  
- La rutina es similar a PrintString_8x8_Format, pero añadiendo lo siguiente:+ La rutina es similar a ''PrintString_8x8_Format'', pero añadiendo lo siguiente:
  
 <code> <code>
Línea 1988: Línea 1976:
  
 PrintString_8x8_Format_Args: PrintString_8x8_Format_Args:
-   ;;; +   ;;;
 bucle: bucle:
       ;;; Coger caracter apuntado por HL.       ;;; Coger caracter apuntado por HL.
Línea 1999: Línea 1987:
           ;;; Avanzar el puntero FONT_X           ;;; Avanzar el puntero FONT_X
       ;;; Si HL es menor que 31 :       ;;; Si HL es menor que 31 :
-          ;;; Si es CERO, salir de la rutina con RET Z.+          ;;; Si es CERO, salir de la rutina con ret z.
           (...)           (...)
  
Línea 2018: Línea 2006:
 <code z80> <code z80>
 loop: loop:
-   LD A, (HL)                ; Leemos un caracter de la cadena+    ld a, (hl)                ; Leemos un caracter de la cadena
  
-   ;;; (...)+    ;;; (...)
  
-   CP '%'                    ; Es un caracter %? +    cp '%'                    ; Es un caracter %? 
-   JR NZ, pstring8_novar     ; Comprobamos si es variable +    jr nz, pstring8_novar     ; Comprobamos si es variable 
-   LD A, (HL)                ; Cogemos en A el siguiente char +    ld a, (hl)                ; Cogemos en A el siguiente char 
-   INC HL +    inc hl 
-   CP '%'                    ; Es otro caracter %? (leido %%?) +    cp '%'                    ; Es otro caracter %? (leido %%?) 
-   JR NZ, pstring8v_var      ; No, es una variable -> Saltar +    jr nz, pstring8v_var      ; No, es una variable -> Saltar 
-                             ; Si, era %, seguir para imprimirlo+                              ; Si, era %, seguir para imprimirlo
 (...) (...)
-   ;;; Aqui se gestionan los codigos de control con % (tipo = A)+    ;;; Aqui se gestionan los codigos de control con % (tipo = A)
 pstring8v_var: pstring8v_var:
-   ;;; comprobamos los tipos y saltamos a sus rutinas de gestion +    ;;; comprobamos los tipos y saltamos a sus rutinas de gestion 
-   CP 'd' +    cp 'd' 
-   JR Z, pstring8v_int8 +    jr z, pstring8v_int8 
-   CP 't' +    cp 't' 
-   JR Z, pstring8v_int8_2d +    jr z, pstring8v_int8_2d 
-   CP 'D' +    cp 'D' 
-   JR Z, pstring8v_int16 +    jr z, pstring8v_int16 
-   CP 's' +    cp 's' 
-   JR Z, pstring8v_string +    jr z, pstring8v_string 
-   CP 'x' +    cp 'x' 
-   JR Z, pstring8v_hex8 +    jr z, pstring8v_hex8 
-   CP 'X' +    cp 'X' 
-   JR Z, pstring8v_hex16 +    jr z, pstring8v_hex16 
-   CP 'b' +    cp 'b' 
-   JP Z, pstring8v_bin8 +    jp z, pstring8v_bin8 
-   CP 'B' +    cp 'B' 
-   JP Z, pstring8v_bin16 +    jp z, pstring8v_bin16 
-   JP pstring8_novar         ; Otro: imprimir caracter tal cual+    jp pstring8_novar         ; Otro: imprimir caracter tal cual
 </code> </code>
  
Línea 2055: Línea 2043:
  
 <code z80> <code z80>
-   CP 'd' +    cp 'd' 
-   JR Z, pstring8v_int8+    jr z, pstring8v_int8
  
 (...) (...)
 pstring8v_int8: pstring8v_int8:
-   PUSH HL +    push hl 
-   LD L, (IX+0) +    ld l, (ix+0) 
-   INC IX +    inc ix 
-   CALL Int2String_8 +    call Int2String_8 
-   LD HL, conv2string +    ld hl, conv2string 
-   CALL INC_HL_Remove_Leading_Zeros +    call INC_HL_Remove_Leading_Zeros 
-   CALL PrintString_8x8 +    call PrintString_8x8 
-   POP HL +    pop hl 
-   JP pstring8v_loop         ; Volvemos al bucle principal+    jp pstring8v_loop         ; Volvemos al bucle principal
 </code> </code>
- +
  El código completo de la rutina es el siguiente:  El código completo de la rutina es el siguiente:
  
Línea 2091: Línea 2079:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 PrintString_8x8_Format_Args: PrintString_8x8_Format_Args:
-    + 
-   ;;; Bucle de impresion de caracter+    ;;; Bucle de impresion de caracter
 pstring8v_loop: pstring8v_loop:
-   LD A, (HL)                ; Leemos un caracter de la cadena +    ld a, (hl)                ; Leemos un caracter de la cadena 
-   INC HL                    ; Apuntamos al siguiente caracter+    inc hl                    ; Apuntamos al siguiente caracter
  
-   CP 32                     ; Es menor que 32? +    cp 32                     ; Es menor que 32? 
-   JP C, pstring8v_ccontrol  ; Si, es un codigo de control, saltar+    jp c, pstring8v_ccontrol  ; Si, es un codigo de control, saltar
  
-   CP '%'                    ; Es un caracter %? +    cp '%'                    ; Es un caracter %? 
-   JR NZ, pstring8_novar     ; Comprobamos si es variable +    jr nz, pstring8_novar     ; Comprobamos si es variable 
-   LD A, (HL)                ; Cogemos en A el siguiente char +    ld a, (hl)                ; Cogemos en A el siguiente char 
-   INC HL +    inc hl 
-   CP '%'                    ; Es otro caracter %? (leido %%?) +    cp '%'                    ; Es otro caracter %? (leido %%?) 
-   JR NZ, pstring8v_var      ; No, es una variable -> Saltar +    jr nz, pstring8v_var      ; No, es una variable -> Saltar 
-                             ; Si, era %, seguir para imprimirlo+                              ; Si, era %, seguir para imprimirlo
 pstring8_novar: pstring8_novar:
-   PUSH HL                   ; Salvaguardamos HL +    push hl                   ; Salvaguardamos HL 
-   CALL PrintChar_8x8        ; Imprimimos el caracter +    call PrintChar_8x8        ; Imprimimos el caracter 
-   POP HL                    ; Recuperamos HL+    pop hl                    ; Recuperamos HL
  
-   ;;; Avanzamos el cursor usando Font_Blank, que incrementa X +    ;;; Avanzamos el cursor usando Font_Blank, que incrementa X 
-   ;;; y actualiza X e Y si se llega al borde de la pantalla +    ;;; y actualiza X e Y si se llega al borde de la pantalla 
-   CALL Font_Inc_X           ; Avanzar coordenada X +    call Font_Inc_X           ; Avanzar coordenada X 
-   JR pstring8v_loop         ; Continuar impresion hasta CHAR=0+    jr pstring8v_loop         ; Continuar impresion hasta CHAR=0
  
 pstring8v_ccontrol: pstring8v_ccontrol:
-   OR A                      ; A es cero?  +    or a                      ; A es cero? 
-   RET Z                     ; Si es 0 (fin de cadena) volver +    ret z                     ; Si es 0 (fin de cadena) volver 
-    + 
-   ;;; Si estamos aqui es porque es un codigo de control distinto > 0 +    ;;; Si estamos aqui es porque es un codigo de control distinto > 0 
-   ;;; Ahora debemos calcular la direccion de la rutina que lo atendera.+    ;;; Ahora debemos calcular la direccion de la rutina que lo atendera.
  
-   ;;; Calculamos la direccion destino a la que saltar usando +    ;;; Calculamos la direccion destino a la que saltar usando 
-   ;;; la tabla de saltos y el codigo de control como indice +    ;;; la tabla de saltos y el codigo de control como indice 
-   EX DEHL +    ex dehl 
-   LD HL, FONT_CALL_JUMP_TABLE +    ld hl, FONT_CALL_JUMP_TABLE 
-   DEC A                     ; Decrementamos A (puntero en tabla) +    dec a                     ; Decrementamos A (puntero en tabla) 
-   RLCA                      ; A = A * 2 = codigo de control * 2 +    rlca                      ; A = A * 2 = codigo de control * 2 
-   LD CA +    ld ca 
-   LD B, 0                   ; BC = A*2 +    ld b, 0                   ; BC = A*2 
-   ADD HLBC                ; HL = DIR FONT_CALL_JUMP_TABLE+(CodControl*2) +    add hlbc                ; HL = DIR FONT_CALL_JUMP_TABLE+(CodControl*2) 
-   LD C, (HL                +    ld c, (hl
-   INC HL +    inc hl 
-   LD H, (HL+    ld h, (hl
-   LD L                  ; Leemos la direccion de la tabla en HL+    ld l                  ; Leemos la direccion de la tabla en HL
  
-   ;;; Si CCONTROL>0 y CCONTROL<10 -> recoger parametro y saltar a rutina +    ;;; Si CCONTROL>0 y CCONTROL<10 -> recoger parametro y saltar a rutina 
-   ;;; Si CCONTROL>9 y CCONTROL<32 -> saltar a rutina sin recogida +    ;;; Si CCONTROL>9 y CCONTROL<32 -> saltar a rutina sin recogida 
-   CP 18                     ; Comprobamos si (CCONTROL-1)*2 < 18 +    cp 18                     ; Comprobamos si (CCONTROL-1)*2 < 18 
-   JP NC, pstring8v_noparam  ; Es decir, si CCONTROL > 9, no hay param+    jp nc, pstring8v_noparam  ; Es decir, si CCONTROL > 9, no hay param
  
-   ;;; Si CCONTROL < 10 -> recoger parametro: +    ;;; Si CCONTROL < 10 -> recoger parametro: 
-   LD A, (DE)                ; Cogemos el parametro en cuestion de la cadena +    ld a, (de)                ; Cogemos el parametro en cuestion de la cadena 
-   INC DE                    ; Apuntamos al siguiente caracter+    inc de                    ; Apuntamos al siguiente caracter
  
-   ;;; Realizamos el salto a la rutina con o sin parametro recogido+    ;;; Realizamos el salto a la rutina con o sin parametro recogido
 pstring8v_noparam: pstring8v_noparam:
-   LD BC, pstring8v_retaddr  ; Ponemos en BC la dir de retorno +    ld bc, pstring8v_retaddr  ; Ponemos en BC la dir de retorno 
-   PUSH BC                   ; Hacemos un push de la dir de retorno +    push bc                   ; Hacemos un push de la dir de retorno 
-   JP (HL)                   ; Saltamos a la rutina seleccionada+    jp (hl)                   ; Saltamos a la rutina seleccionada
  
-   ;;; Este es el punto al que volvemos tras la rutina+    ;;; Este es el punto al que volvemos tras la rutina
 pstring8v_retaddr: pstring8v_retaddr:
-   EX DEHL                 ; Recuperamos en HL el puntero a cadena +    ex dehl                 ; Recuperamos en HL el puntero a cadena 
-   JR pstring8v_loop         ; Continuamos en el bucle+    jr pstring8v_loop         ; Continuamos en el bucle
  
-   ;;; Aqui se gestionan los codigos de control con % (tipo = A)+    ;;; Aqui se gestionan los codigos de control con % (tipo = A)
 pstring8v_var: pstring8v_var:
-   ;;; comprobamos los tipos y saltamos a sus rutinas de gestion +    ;;; comprobamos los tipos y saltamos a sus rutinas de gestion 
-   CP 'd' +    cp 'd' 
-   JR Z, pstring8v_int8 +    jr z, pstring8v_int8 
-   CP 't' +    cp 't' 
-   JR Z, pstring8v_int8_2d +    jr z, pstring8v_int8_2d 
-   CP 'D' +    cp 'D' 
-   JR Z, pstring8v_int16 +    jr z, pstring8v_int16 
-   CP 's' +    cp 's' 
-   JR Z, pstring8v_string +    jr z, pstring8v_string 
-   CP 'x' +    cp 'x' 
-   JR Z, pstring8v_hex8 +    jr z, pstring8v_hex8 
-   CP 'X' +    cp 'X' 
-   JP Z, pstring8v_hex16 +    jp z, pstring8v_hex16 
-   CP 'b' +    cp 'b' 
-   JP Z, pstring8v_bin8 +    jp z, pstring8v_bin8 
-   CP 'B' +    cp 'B' 
-   JP Z, pstring8v_bin16 +    jp z, pstring8v_bin16 
-   JP pstring8_novar         ; Otro: imprimir caracter tal cual+    jp pstring8_novar         ; Otro: imprimir caracter tal cual
  
 ;---------------------------------------------------------- ;----------------------------------------------------------
 pstring8v_int8: pstring8v_int8:
-   PUSH HL +    push hl 
-   LD L, (IX+0) +    ld l, (ix+0) 
-   INC IX +    inc ix 
-   CALL Int2String_8 +    call Int2String_8 
-   LD HL, conv2string +    ld hl, conv2string 
-   CALL INC_HL_Remove_Leading_Zeros +    call INC_HL_Remove_Leading_Zeros 
-   CALL PrintString_8x8 +    call PrintString_8x8 
-   POP HL +    pop hl 
-   JP pstring8v_loop         ; Volvemos al bucle principal+    jp pstring8v_loop         ; Volvemos al bucle principal
  
 ;---------------------------------------------------------- ;----------------------------------------------------------
 pstring8v_int8_2d: pstring8v_int8_2d:
-   PUSH HL +    push hl 
-   LD A, (IX+0) +    ld a, (ix+0) 
-   INC IX +    inc ix 
-   CALL Int2String_8_2Digits  +    call Int2String_8_2Digits 
-   LD A                  ; Resultado conversion en DE +    ld a                  ; Resultado conversion en DE 
-   PUSH DE +    push de 
-   CALL PrintChar_8x8        ; Imprimir parte alta (decenas) +    call PrintChar_8x8        ; Imprimir parte alta (decenas) 
-   CALL Font_Inc_X +    call Font_Inc_X 
-   POP DE +    pop de 
-   LD AE +    ld ae 
-   CALL PrintChar_8x8        ; Imprimir parte alta (decenas) +    call PrintChar_8x8        ; Imprimir parte alta (decenas) 
-   CALL Font_Inc_X +    call Font_Inc_X 
-   POP HL +    pop hl 
-   JP pstring8v_loop         ; Volvemos al bucle principal+    jp pstring8v_loop         ; Volvemos al bucle principal
  
 ;---------------------------------------------------------- ;----------------------------------------------------------
 pstring8v_int16: pstring8v_int16:
-   PUSH HL +    push hl 
-   LD L, (IX+0) +    ld l, (ix+0) 
-   INC IX +    inc ix 
-   LD H, (IX+0) +    ld h, (ix+0) 
-   INC IX +    inc ix 
-   CALL Int2String_16 +    call Int2String_16 
-   LD HL, conv2string +    ld hl, conv2string 
-   CALL INC_HL_Remove_Leading_Zeros +    call INC_HL_Remove_Leading_Zeros 
-   CALL PrintString_8x8 +    call PrintString_8x8 
-   POP HL +    pop hl 
-   JP pstring8v_loop         ; Volvemos al bucle principal+    jp pstring8v_loop         ; Volvemos al bucle principal
  
 ;---------------------------------------------------------- ;----------------------------------------------------------
 pstring8v_string: pstring8v_string:
-   PUSH HL  +    push hl 
-   PUSH IX                   ; HL = IX +    push ix                   ; HL = IX 
-   POP HL +    pop hl 
-   call PrintString_8x8      ; Imprimimos cadena +    call PrintString_8x8      ; Imprimimos cadena 
-   POP HL +    pop hl 
-pstring8v_strloop:           ; Incrementamos IX hasta el fin +pstring8v_strloop:            ; Incrementamos IX hasta el fin 
-   LD A, (IX+0)              ; de la cadena, recorriendola +    ld a, (ix+0)              ; de la cadena, recorriendola 
-   INC IX                    ; hasta (IX) = 0 +    inc ix                    ; hasta (IX) = 0 
-   OR A +    or a 
-   JR NZ, pstring8v_strloop+    jr nz, pstring8v_strloop
  
-   ; De esta forma IX ya apunta al siguiente argumento +    ; De esta forma IX ya apunta al siguiente argumento 
-   JP pstring8v_loop         ; Volvemos al bucle principal+    jp pstring8v_loop         ; Volvemos al bucle principal
  
 ;---------------------------------------------------------- ;----------------------------------------------------------
 pstring8v_hex8: pstring8v_hex8:
-   PUSH HL +    push hl 
-   LD L, (IX+0) +    ld l, (ix+0) 
-   INC IX +    inc ix 
-   LD L, 40 +    ld l, 40 
-   CALL Hex2String_8 +    call Hex2String_8 
-   LD HL, conv2string +    ld hl, conv2string 
-   CALL PrintString_8x8 +    call PrintString_8x8 
-   POP HL +    pop hl 
-   JP pstring8v_loop         ; Volvemos al bucle principal+    jp pstring8v_loop         ; Volvemos al bucle principal
  
 ;---------------------------------------------------------- ;----------------------------------------------------------
 pstring8v_hex16: pstring8v_hex16:
-   PUSH HL +    push hl 
-   LD L, (IX+0) +    ld l, (ix+0) 
-   INC IX +    inc ix 
-   LD H, (IX+0) +    ld h, (ix+0) 
-   INC IX +    inc ix 
-   CALL Hex2String_16 +    call Hex2String_16 
-   LD HL, conv2string +    ld hl, conv2string 
-   CALL PrintString_8x8 +    call PrintString_8x8 
-   POP HL +    pop hl 
-   JP pstring8v_loop         ; Volvemos al bucle principal+    jp pstring8v_loop         ; Volvemos al bucle principal
  
 ;---------------------------------------------------------- ;----------------------------------------------------------
 pstring8v_bin8: pstring8v_bin8:
-   PUSH HL +    push hl 
-   LD L, (IX+0) +    ld l, (ix+0) 
-   INC IX +    inc ix 
-   CALL Bin2String_8 +    call Bin2String_8 
-   LD HL, conv2string +    ld hl, conv2string 
-   CALL PrintString_8x8 +    call PrintString_8x8 
-   POP HL +    pop hl 
-   JP pstring8v_loop         ; Volvemos al bucle principal+    jp pstring8v_loop         ; Volvemos al bucle principal
  
 ;---------------------------------------------------------- ;----------------------------------------------------------
 pstring8v_bin16: pstring8v_bin16:
-   PUSH HL +    push hl 
-   LD L, (IX+0) +    ld l, (ix+0) 
-   INC IX +    inc ix 
-   LD H, (IX+0) +    ld h, (ix+0) 
-   INC IX +    inc ix 
-   CALL Bin2String_16 +    call Bin2String_16 
-   LD HL, conv2string +    ld hl, conv2string 
-   CALL PrintString_8x8 +    call PrintString_8x8 
-   POP HL +    pop hl 
-   JP pstring8v_loop         ; Volvemos al bucle principal+    jp pstring8v_loop         ; Volvemos al bucle principal
 </code> </code>
  
Línea 2292: Línea 2280:
  
 <code z80> <code z80>
-  ; Ejemplo de impresion de texto con argumentos +; Ejemplo de impresion de texto con argumentos 
-  ORG 32768+    ORG 35000
  
-  LD HL, $3D00-256 +    ld hl, $3d00-256 
-  CALL Font_Set_Charset+    call Font_Set_Charset
  
-  LD BC, $0400 +    ld bc, $0400 
-  CALL Font_Set_XY+    call Font_Set_XY
  
-  LD HL, cadena1 +    ld hl, cadena1 
-  LD IX, args1 +    ld ix, args1 
-  CALL PrintString_8x8_Format_Args+    call PrintString_8x8_Format_Args
  
-  LD HL, cadena2 +    ld hl, cadena2 
-  LD IX, args2 +    ld ix, args2 
-  CALL PrintString_8x8_Format_Args+    call PrintString_8x8_Format_Args
  
-loop:  +loop: 
-  JR loop +    jr loop 
-  RET+    ret
  
-cadena1 DB "VALOR 8 bits: 40", FONT_CRLF, FONT_CRLF+cadena1 DB "Valor 8 bits: 40", FONT_CRLF, FONT_CRLF
         DB "Decimal: %d" , FONT_CRLF         DB "Decimal: %d" , FONT_CRLF
         DB "Hexadecimal: $%x" , FONT_CRLF         DB "Hexadecimal: $%x" , FONT_CRLF
         DB "Binario: %%%b" , FONT_CRLF         DB "Binario: %%%b" , FONT_CRLF
         DB FONT_CRLF, FONT_CRLF         DB FONT_CRLF, FONT_CRLF
-        DB "VALOR 16 bits: 1205", FONT_CRLF, FONT_CRLF+        DB "Valor 16 bits: 1205", FONT_CRLF, FONT_CRLF
         DB "Decimal: %D" , FONT_CRLF         DB "Decimal: %D" , FONT_CRLF
         DB "Hexadecimal: $%X" , FONT_CRLF         DB "Hexadecimal: $%X" , FONT_CRLF
Línea 2332: Línea 2320:
  
 args2   DB 2, "cad 1", FONT_EOS, "cad 2", FONT_EOS args2   DB 2, "cad 1", FONT_EOS, "cad 2", FONT_EOS
 +
 +    END 35000
 </code> </code>
  
-\\  +\\ 
-{{ :cursos:ensamblador:gfx4_params.png | Procesado de parametros}} +{{ :cursos:ensamblador:gfx4_params.png?640 | Procesado de parametros}} 
-\\ +\\
  
  No es necesario que el vector de parámetros contenga más de un elemento. Podemos utilizar la rutina directamente con una variable de datos para imprimir su valor:  No es necesario que el vector de parámetros contenga más de un elemento. Podemos utilizar la rutina directamente con una variable de datos para imprimir su valor:
  
 <code z80> <code z80>
-  LD HL, cadvidas                     ; Cadena +    ld hl, cadvidas                     ; Cadena 
-  LD IX, vidas                        ; Variable de argumentos +    ld ix, vidas                        ; Variable de argumentos 
-  CALL PrintString_8x8_Format_Args    ; Imprimir+    call PrintString_8x8_Format_Args    ; Imprimir
  
   (...)   (...)
-  +
 cadvidas DB "Tienes %d vidas", FONT_EOS cadvidas DB "Tienes %d vidas", FONT_EOS
 vidas    DB 10 vidas    DB 10
 </code> </code>
  
- Sí que hay que ser especialmente cuidadoso a la hora de definir los parámetros en la variable que apuntamos con IX: es importante que cada parámetro tenga su tamaño adecuado (DB, DW), y que no le falten los End Of String (0) a las cadenas. + Sí que hay que ser especialmente cuidadoso a la hora de definir los parámetros en la variable que apuntamos con IX: es importante que cada parámetro tenga su tamaño adecuado (''DB''''DW''), y que no le falten los End Of String (0) a las cadenas.
  
  Nótese que los parámetros que se imprimen pueden ser modificados por el programa, por lo que esta rutina es muy útil en juegos o programas que trabajen con muchos datos a mostrar.  Nótese que los parámetros que se imprimen pueden ser modificados por el programa, por lo que esta rutina es muy útil en juegos o programas que trabajen con muchos datos a mostrar.
  
-\\  +\\ 
-\\ +\\
 **Añadiendo más códigos de control** **Añadiendo más códigos de control**
-\\  +\\ 
-\\ +\\
  
  El sistema que acabamos de ver permite su ampliación con nuevos tipos de datos o métodos de impresión específicos. Supongamos por ejemplo que queremos añadir 2 nuevos tipos de impresión de valores enteros, uno en el que se añadan los ceros al inicio de las cadenas resultantes de la conversión, y otro que permita la impresión justificada.  El sistema que acabamos de ver permite su ampliación con nuevos tipos de datos o métodos de impresión específicos. Supongamos por ejemplo que queremos añadir 2 nuevos tipos de impresión de valores enteros, uno en el que se añadan los ceros al inicio de las cadenas resultantes de la conversión, y otro que permita la impresión justificada.
Línea 2365: Línea 2355:
  Para ello creamos los nuevos "códigos de control":  Para ello creamos los nuevos "códigos de control":
  
-\\ +\\
 |< 80% >| |< 80% >|
 ^ Código de control ^ Significado ^ ^ Código de control ^ Significado ^
Línea 2372: Línea 2362:
 | %j | Imprimir argumento número entero de 8 bits en formato decimal //justificado a derecha// (3 caracteres) | | %j | Imprimir argumento número entero de 8 bits en formato decimal //justificado a derecha// (3 caracteres) |
 | %J | Imprimir argumento número entero de 16 bits en formato decimal //justificado a derecha// (5 caracteres) | | %J | Imprimir argumento número entero de 16 bits en formato decimal //justificado a derecha// (5 caracteres) |
-\\ +\\
  
- A continuación realizamos las modificaciones adecuadas a la rutina PrintString_8x8_Format_Args:+ A continuación realizamos las modificaciones adecuadas a la rutina ''PrintString_8x8_Format_Args'':
  
 <code z80> <code z80>
 PrintString_8x8_Format_Args: PrintString_8x8_Format_Args:
  
-   ;;; (...)+    ;;; (...)
  
-   ;;; Aqui se gestionan los codigos de control con % (tipo = A)+    ;;; Aqui se gestionan los codigos de control con % (tipo = A)
 pstring8v_var: pstring8v_var:
-   ;;; comprobamos los tipos y saltamos a sus rutinas de gestion +    ;;; comprobamos los tipos y saltamos a sus rutinas de gestion 
-   (...) +    (...) 
-   CP 'z' +    cp 'z' 
-   JR Z, pstring8v_int8_zeros +    jr z, pstring8v_int8_zeros 
-   CP 'Z' +    cp 'Z' 
-   JR Z, pstring8v_int16_zeros +    jr z, pstring8v_int16_zeros 
-   CP 'j' +    cp 'j' 
-   JR Z, pstring8v_int8_justify +    jr z, pstring8v_int8_justify 
-   CP 'J' +    cp 'J' 
-   JR Z, pstring8v_int16_justify +    jr z, pstring8v_int16_justify 
-   JP pstring8_novar+    jp pstring8_novar
  
 ;---------------------------------------------------------- ;----------------------------------------------------------
 pstring8v_int8_zeros: pstring8v_int8_zeros:
-   PUSH HL +    push hl 
-   LD L, (IX+0) +    ld l, (ix+0) 
-   INC IX +    inc ix 
-   CALL Int2String_8 +    call Int2String_8 
-   LD HL, conv2string +    ld hl, conv2string 
-   CALL PrintString_8x8      ; No llamamos a Remove_Leading_Zeros +    call PrintString_8x8      ; No llamamos a Remove_Leading_Zeros 
-   POP HL +    pop hl 
-   JP pstring8v_loop         ; Volvemos al bucle principal+    jp pstring8v_loop         ; Volvemos al bucle principal
  
 ;---------------------------------------------------------- ;----------------------------------------------------------
 pstring8v_int16_zeros: pstring8v_int16_zeros:
-   PUSH HL +    push hl 
-   LD L, (IX+0) +    ld l, (ix+0) 
-   INC IX +    inc ix 
-   LD H, (IX+0) +    ld h, (ix+0) 
-   INC IX +    inc ix 
-   CALL Int2String_16 +    call Int2String_16 
-   LD HL, conv2string +    ld hl, conv2string 
-   CALL PrintString_8x8      ; No llamamos a Remove_Leading_Zeros +    call PrintString_8x8      ; No llamamos a Remove_Leading_Zeros 
-   POP HL  +    pop hl 
-   JP pstring8v_loop         ; Volvemos al bucle principal+    jp pstring8v_loop         ; Volvemos al bucle principal
  
 ;---------------------------------------------------------- ;----------------------------------------------------------
 pstring8v_int8_justify: pstring8v_int8_justify:
-   PUSH HL +    push hl 
-   LD L, (IX+0) +    ld l, (ix+0) 
-   INC IX +    inc ix 
-   CALL Int2String_8 +    call Int2String_8 
-   LD HL, conv2string        ; Llamamos a funcion Justify +    ld hl, conv2string        ; Llamamos a funcion Justify 
-   CALL INC_HL_Justify_Leading_Zeros +    call INC_HL_Justify_Leading_Zeros 
-   CALL PrintString_8x8 +    call PrintString_8x8 
-   POP HL +    pop hl 
-   JP pstring8v_loop         ; Volvemos al bucle principal+    jp pstring8v_loop         ; Volvemos al bucle principal
  
 ;---------------------------------------------------------- ;----------------------------------------------------------
 pstring8v_int16_justify: pstring8v_int16_justify:
-   PUSH HL +    push hl 
-   LD L, (IX+0) +    ld l, (ix+0) 
-   INC IX +    inc ix 
-   LD H, (IX+0) +    ld h, (ix+0) 
-   INC IX +    inc ix 
-   CALL Int2String_16 +    call Int2String_16 
-   LD HL, conv2string        ; Llamamos a funcion Justify +    ld hl, conv2string        ; Llamamos a funcion Justify 
-   CALL INC_HL_Justify_Leading_Zeros +    call INC_HL_Justify_Leading_Zeros 
-   CALL PrintString_8x8 +    call PrintString_8x8 
-   POP HL +    pop hl 
-   JP pstring8v_loop         ; Volvemos al bucle principal +    jp pstring8v_loop         ; Volvemos al bucle principal 
-    + 
-   (...)+    (...)
 </code> </code>
  
- Lo normal a la hora de utilizar PrintString_Format_Args en nuestro programa es que eliminemos todos aquellos códigos de control (y sus rutinas de chequeo y de gestión) de los cuales no vayamos a hacer uso, con el consiguiente ahorro de memoria (desaparecen las instrucciones de las subrutinas de gestión).+ Lo normal a la hora de utilizar ''PrintString_Format_Args'' en nuestro programa es que eliminemos todos aquellos códigos de control (y sus rutinas de chequeo y de gestión) de los cuales no vayamos a hacer uso, con el consiguiente ahorro de memoria (desaparecen las instrucciones de las subrutinas de gestión).
  
-\\  +\\ 
-\\ +\\
 ==== Lectura de texto desde teclado ==== ==== Lectura de texto desde teclado ====
  
Línea 2460: Línea 2450:
  En el artículo dedicado al teclado estudiamos rutinas de lectura del mismo que nos proporcionaban scancodes de las teclas pulsadas. También vimos rutinas de obtención del código ASCII correspondiente a un scancode dado.  En el artículo dedicado al teclado estudiamos rutinas de lectura del mismo que nos proporcionaban scancodes de las teclas pulsadas. También vimos rutinas de obtención del código ASCII correspondiente a un scancode dado.
  
- En este caso necesitaremos una rutina más "avanzada"que permita detectar el uso de CAPS SHIFT y DELETE (CAPS SHIFT + '0') y que distinga por tanto entre mayúsculas y minúsculas. Para eso, utilizaremos la rutina de escaneo de teclado y conversión a ASCII de la ROM del Spectrum (**KEY_SCAN**), ubicada en la dirección de memoria $028E de la ROM.+ En este caso vamos a utilizar para nuestro ejemplo la rutina de la ROM ''KEY_SCAN''la cual permite detectar el uso de CAPS SHIFT y DELETE (CAPS SHIFT + '0') y que distingue por tanto entre mayúsculas y minúsculas. ''KEY_SCAN'' está ubicada en la dirección de memoria $028e de la ROM.
  
- Al realizar un CALL a KEY_SCAN se produce una lectura de todas las filas del teclado seguida de una decodificación del resultado de la lectura. La rutina de la ROM coloca entonces en la variable del sistema **LAST_K** (dirección 23560) el código ASCII de la tecla pulsada. KEY_SCAN también decodifica las teclas extendidas y LAST_K nos puede servir también para detectar ENTER (código ASCII 13) o DELETE (código ASCII 12).+ Al realizar un call ''KEY_SCAN'' se produce una lectura de todas las filas del teclado seguida de una decodificación del resultado de la lectura. La rutina de la ROM coloca entonces en la variable del sistema ''LAST_K'' (dirección 23560) el código ASCII de la tecla pulsada. ''KEY_SCAN'' también decodifica las teclas extendidas y ''LAST_K'' nos puede servir también para detectar ENTER (código ASCII 13) o DELETE (código ASCII 12).
  
  El desarrollo de la rutina será el siguiente:  El desarrollo de la rutina será el siguiente:
Línea 2491: Línea 2481:
 </code> </code>
  
- Veamos el código de la rutina **InputString_8x8**:+ Veamos el código de la rutina ''InputString_8x8'':
  
 <code z80> <code z80>
 LAST_K      EQU    23560 LAST_K      EQU    23560
-KEY_SCAN    EQU    $028E+KEY_SCAN    EQU    $028e
  
 ;------------------------------------------------------------- ;-------------------------------------------------------------
Línea 2503: Línea 2493:
 ; Entrada: ; Entrada:
 ; Registro HL = Puntero a la cadena de texto a obtener. ; Registro HL = Puntero a la cadena de texto a obtener.
-; Registro A = Longitud maxima de la cadena a obtener  +; Registro A = Longitud maxima de la cadena a obtener 
-; Usa: +; Usa:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 InputString_8x8: InputString_8x8:
  
-   PUSH HL                   ; Guardamos el puntero a la cadena  +    push hl                   ; Guardamos el puntero a la cadena 
-   PUSH DE +    push de 
-   PUSH BC                   ; Modificados por KEY_SCAN +    push bc                   ; Modificados por KEY_SCAN
- +
-   LD (inputs_counter), A    ; Contador de caracteres a usar +
-   LD (inputs_limit), A      ; Guardamos la longitud maxima+
  
 +    ld (inputs_counter), a    ; Contador de caracteres a usar
 +    ld (inputs_limit), a      ; Guardamos la longitud maxima
  
 inputs_start: inputs_start:
-   LD A, '_'                 ; Imprimir nuevo cursor +    ld a, '_'                 ; Imprimir nuevo cursor 
-   CALL Font_SafePrintChar_8x8+    call Font_SafePrintChar_8x8
  
-   XOR A +    xor a 
-   LD (LAST_K),            ; Limpiar ultima tecla pulsada+    ld (LAST_K),            ; Limpiar ultima tecla pulsada
  
 inputs_loop: inputs_loop:
-   PUSH HL                   ; KEY_SCAN modifica HL -> preservar +    push hl                   ; KEY_SCAN modifica HL -> preservar 
-   CALL KEY_SCAN             ; Escanear el teclado +    call KEY_SCAN             ; Escanear el teclado 
-   POP HL +    pop hl 
-   LD A, (LAST_K)            ; Obtener el valor decodificado+    ld a, (LAST_K)            ; Obtener el valor decodificado 
 + 
 +    cp 13 
 +    jr z, inputs_end          ; Es enter? -> fin de rutina
  
-   CP 13 +    cp 12 
-   JR Zinputs_end          ; Es enter? -> fin de rutina+    jr zinputs_delete       ; Es DELETE? -> borrar caracter
  
-   CP 12 +    cp 32 
-   JR Zinputs_delete       ; Es DELETE? -> borrar caracter+    jr cinputs_loop         ; Es < 32? -> repetir bucle escaneo
  
-   CP 32 +    ;;; Si estamos aqui, ASCII >= 32 -> Es caracter valido -> Guardiar 
-   JR C, inputs_loop         ; Es < 32? -> repetir bucle escaneo +    ex afaf               ; Nos guardamos el valor ASCII en A'
-    +
-   ;;; Si estamos aqui, ASCII >= 32 -> Es caracter valido -> Guardiar +
-   EX AFAF               ; Nos guardamos el valor ASCII en A'+
  
-   ;;; Comprobacion de longitud maxima de cadena +    ;;; Comprobacion de longitud maxima de cadena 
-   LD A, (inputs_counter)    ; A = caracteres disponibles +    ld a, (inputs_counter)    ; A = caracteres disponibles 
-   OR A                      ; Comprobar si es 0 +    or a                      ; Comprobar si es 0 
-   JR Z, inputs_loop         ; Si es cero, no insertar caracter +    jr z, inputs_loop         ; Si es cero, no insertar caracter 
-   DEC A +    dec a 
-   LD (inputs_counter),    ; Decrementar espacio disponible+    ld (inputs_counter),    ; Decrementar espacio disponible
  
-   EX AFAF               ; Recuperamos ASCII de A' +    ex afaf               ; Recuperamos ASCII de A' 
-   LD (HL),                ; Guardamos el caracter leido +    ld (hl),                ; Guardamos el caracter leido 
-   INC HL                    ; Avanzamos al siguiente caracter e imprimir +    inc hl                    ; Avanzamos al siguiente caracter e imprimir 
-   CALL Font_SafePrintChar_8x8 +    call Font_SafePrintChar_8x8 
-   CALL Font_Inc_X +    call Font_Inc_X 
-   JR inputs_start           ; Repetir continuamente hasta ENTER+    jr inputs_start           ; Repetir continuamente hasta ENTER
  
 ;;; Codigo a ejecutar cuando se pulsa enter ;;; Codigo a ejecutar cuando se pulsa enter
 inputs_end:                  ; ENTER pulsado -> Fin de la rutina inputs_end:                  ; ENTER pulsado -> Fin de la rutina
  
-   LD A, ' '                 ; Borramos de la pantalla el cursor +    ld a, ' '                 ; Borramos de la pantalla el cursor 
-   CALL Font_SafePrintChar_8x8 +    call Font_SafePrintChar_8x8 
-   XOR A +    xor a 
-   LD (HL),                ; Almacenamos un FIN DE CADENA en HL +    ld (hl),                ; Almacenamos un FIN DE CADENA en HL 
-   POP BC +    pop bc 
-   POP DE                    ; Recuperamos valores de registros +    pop de                    ; Recuperamos valores de registros 
-   POP HL                    ; Recuperamos el inicio de la cadena +    pop hl                    ; Recuperamos el inicio de la cadena 
-   RET+    ret
  
 ;;; Codigo a ejecutar cuando se pulsa DELETE ;;; Codigo a ejecutar cuando se pulsa DELETE
 inputs_delete:               ; DELETE pulsado -> Borrar caracter inputs_delete:               ; DELETE pulsado -> Borrar caracter
-   LD A, (inputs_limit)       +    ld a, (inputs_limit) 
-   LD BA +    ld ba 
-   LD , (inputs_counter) +    LD , (inputs_counter) 
-   CP B                      ; Si char_disponibles-limite == 0 ... +    cp b                      ; Si char_disponibles-limite == 0 ... 
-   JR Z, inputs_loop         ; ... no podemos borrar (inicio de cadena)+    jr z, inputs_loop         ; ... no podemos borrar (inicio de cadena)
  
-   INC A                     ; Si no, si que podemos borrar: +    inc a                     ; Si no, si que podemos borrar: 
-   LD (inputs_counter),    ; incrementar espacio disponible+    ld (inputs_counter),    ; incrementar espacio disponible
  
-   DEC HL                    ; Decrementar puntero en la cadena +    dec hl                    ; Decrementar puntero en la cadena 
-   LD A, ' '                 ; Borrar cursor y caracter anterior +    ld a, ' '                 ; Borrar cursor y caracter anterior 
-   CALL Font_SafePrintChar_8x8 +    call Font_SafePrintChar_8x8 
-   CALL Font_Dec_X +    call Font_Dec_X 
-   JR inputs_start           ; Bucle principal+    jr inputs_start           ; Bucle principal
  
 inputs_counter   DB  0 inputs_counter   DB  0
Línea 2588: Línea 2577:
 </code> </code>
  
- InputString_8x8 utiliza una nueva subrutina llamada **Font_SafePrintChar8x8** que no es más que una encapsulación del PrintChar_8x8 original en la que se preservan y restauran los valores de los registros que modifica internamente PrintChar:+'' InputString_8x8'' utiliza una nueva subrutina llamada ''Font_SafePrintChar8x8'' que no es más que una encapsulación del ''PrintChar_8x8'' original en la que se preservan y restauran los valores de los registros que modifica internamente ''PrintChar'':
  
 <code z80> <code z80>
Línea 2595: Línea 2584:
 ;------------------------------------------------------------- ;-------------------------------------------------------------
 Font_SafePrintChar_8x8 Font_SafePrintChar_8x8
-   PUSH BC +    push bc 
-   PUSH DE +    push de 
-   PUSH HL                   ; Preservar registros +    push hl                   ; Preservar registros 
-   CALL PrintChar_8x8        ; Imprimir caracter +    call PrintChar_8x8        ; Imprimir caracter 
-   POP HL                    ; Recuperar registros +    pop hl                    ; Recuperar registros 
-   POP DE +    pop de 
-   POP BC +    pop bc 
-   RET+    ret
 </code> </code>
  
  Veamos un ejemplo de uso de nuestra nueva función de INPUT:  Veamos un ejemplo de uso de nuestra nueva función de INPUT:
- +
 <code z80> <code z80>
- ; Ejemplo de input de texto +; Ejemplo de input de texto 
-  ORG 32768+    ORG 35000
  
-  LD HL, $3D00-256 +    ld hl, $3d00-256 
-  CALL Font_Set_Charset+    call Font_Set_Charset
  
-  ;;; Imprimir cadena "Introduce un texto:" +    ;;; Imprimir cadena "Introduce un texto:" 
-  LD HL, cadena1 +    ld hl, cadena1 
-  LD B, 4 +    ld b, 4 
-  LD C, 0 +    ld c, 0 
-  CALL Font_Set_XY +    call Font_Set_XY 
-  CALL PrintString_8x8_Format+    call PrintString_8x8_Format
  
-  ;;; Obtener el input del usuario +    ;;; Obtener el input del usuario 
-  LD HL, input1 +    ld hl, input1 
-  LD A, 20 +    ld a, 20 
-  CALL InputString_8x8+    call InputString_8x8
  
-  ;;; Imprimir "Tu cadena es:" + la cadena resultante +    ;;; Imprimir "Tu cadena es:" + la cadena resultante 
-  LD HL, cadena2 +    ld hl, cadena2 
-  CALL PrintString_8x8_Format+    call PrintString_8x8_Format
  
-  LD HL, input1 +    ld hl, input1 
-  CALL PrintString_8x8_Format+    call PrintString_8x8_Format 
 + 
 +    ret
  
-  RET 
-   
 cadena1 DB "Introduce un texto:", FONT_CRLF, FONT_CRLF cadena1 DB "Introduce un texto:", FONT_CRLF, FONT_CRLF
         DB FONT_SET_INK, 2, FONT_SET_STYLE, FONT_BOLD         DB FONT_SET_INK, 2, FONT_SET_STYLE, FONT_BOLD
Línea 2645: Línea 2634:
 input1  DS 35 input1  DS 35
         DB 0         DB 0
 +
 +    END 35000
 </code> </code>
  
-\\  +\\ 
-{{ :cursos:ensamblador:gfx4_input.png | Input de texto }} +{{ :cursos:ensamblador:gfx4_input.png?640 | Input de texto }} 
-\\ +\\
  
  Si nuestro programa o juego va a requerir un posibilidades de introducción o edición de textos avanzadas, sería aconsejable ampliar la rutina anterior con nuevas opciones o mejoras como las siguientes:  Si nuestro programa o juego va a requerir un posibilidades de introducción o edición de textos avanzadas, sería aconsejable ampliar la rutina anterior con nuevas opciones o mejoras como las siguientes:
  
-\\  +\\ 
-  * Permitir edición multilínea. La rutina actual no permite trabajar (al menos en cuanto al borrado) con entrada de texto de múltiples líneas. Se podría editar la rutina para permitir editar más de una línea de texto, realizando una versión especial de Font_Dec_X que decremente el valor de FONT_Y y ponga FONT_X=0 cuando tratemos de borrar desde el margen izquierdo de la pantalla. +  * Permitir edición multilínea. La rutina actual no permite trabajar (al menos en cuanto al borrado) con entrada de texto de múltiples líneas. Se podría editar la rutina para permitir editar más de una línea de texto, realizando una versión especial de ''Font_Dec_X'' que decremente el valor de ''FONT_Y'' y ponga ''FONT_X=0'' cuando tratemos de borrar desde el margen izquierdo de la pantalla. 
-  * Habilitar el uso de las teclas de cursor para moverse entre los caracteres de la cadena y así permitir edición avanzada. La rutina debería basarse entonces en un FONT_X y FONT_Y propios y ya no se podría utilizar FONT_BACKSPACE para el borrado. Además, al insertar un carácter en el interior de la cadena habría que mover todos los caracteres en memoria una posición a la derecha y redibujar la cadena completa en pantalla. El cursor podría simularse entonces con FLASH o subrayando la letra actual (por lo que no serviría para editar texto subrayado).+  * Habilitar el uso de las teclas de cursor para moverse entre los caracteres de la cadena y así permitir edición avanzada. La rutina debería basarse entonces en un ''FONT_X'' ''FONT_Y'' propios y ya no se podría utilizar ''FONT_BACKSPACE'' para el borrado. Además, al insertar un carácter en el interior de la cadena habría que mover todos los caracteres en memoria una posición a la derecha y redibujar la cadena completa en pantalla. El cursor podría simularse entonces con FLASH o subrayando la letra actual (por lo que no serviría para editar texto subrayado).
   * Permitir llamar a la función con una cadena ya en la zona apuntada por HL. En conjunción con la mejora anterior permitiría editar texto anteriormente introducido.   * Permitir llamar a la función con una cadena ya en la zona apuntada por HL. En conjunción con la mejora anterior permitiría editar texto anteriormente introducido.
-\\ +\\
  
  En cualquier caso, la rutina que acabamos de ver es más que suficiente para recoger cadenas simples en nuestro programa.  En cualquier caso, la rutina que acabamos de ver es más que suficiente para recoger cadenas simples en nuestro programa.
  
-\\  +\\ 
-\\ +\\
 ===== Fuentes de 4x8 píxeles (64 caracteres en pantalla) ===== ===== Fuentes de 4x8 píxeles (64 caracteres en pantalla) =====
  
Línea 2673: Línea 2664:
  La fuente de texto en 4x8 tiene el siguiente aspecto:  La fuente de texto en 4x8 tiene el siguiente aspecto:
  
-\\ +\\
 {{ :cursos:ensamblador:gfx4_charset4x8_1.png | Charset 4x8 }} {{ :cursos:ensamblador:gfx4_charset4x8_1.png | Charset 4x8 }}
-\\ +\\
  
- Para utilizar este set de caracteres sólo tendremos que realizar una nueva rutina de impresión llamada **PrintChar_4x8** y modificar la variable que define la anchura de la pantalla, FONT_SWIDTH (que pasa de valer 32 a 64).+ Para utilizar este set de caracteres sólo tendremos que realizar una nueva rutina de impresión llamada ''PrintChar_4x8'' y modificar la variable que define la anchura de la pantalla, ''FONT_SWIDTH'' (que pasa de valer 32 a 64).
  
  La definición de la fuente de 4x8 píxeles en formato DB es la siguiente:  La definición de la fuente de 4x8 píxeles en formato DB es la siguiente:
Línea 2684: Línea 2675:
 ; half width 4x8 font - 384 bytes ; half width 4x8 font - 384 bytes
 charset_4x8: charset_4x8:
-   DB $00,$02,$02,$02,$02,$00,$02,$00,$00,$52,$57,$02,$02,$07,$02,$00 +    DB $00,$02,$02,$02,$02,$00,$02,$00,$00,$52,$57,$02,$02,$07,$02,$00 
-   DB $00,$25,$71,$62,$32,$74,$25,$00,$00,$22,$42,$30,$50,$50,$30,$00 +    DB $00,$25,$71,$62,$32,$74,$25,$00,$00,$22,$42,$30,$50,$50,$30,$00 
-   DB $00,$14,$22,$41,$41,$41,$22,$14,$00,$20,$70,$22,$57,$02,$00,$00 +    DB $00,$14,$22,$41,$41,$41,$22,$14,$00,$20,$70,$22,$57,$02,$00,$00 
-   DB $00,$00,$00,$00,$07,$00,$20,$20,$00,$01,$01,$02,$02,$04,$14,$00 +    DB $00,$00,$00,$00,$07,$00,$20,$20,$00,$01,$01,$02,$02,$04,$14,$00 
-   DB $00,$22,$56,$52,$52,$52,$27,$00,$00,$27,$51,$12,$21,$45,$72,$00 +    DB $00,$22,$56,$52,$52,$52,$27,$00,$00,$27,$51,$12,$21,$45,$72,$00 
-   DB $00,$57,$54,$56,$71,$15,$12,$00,$00,$17,$21,$61,$52,$52,$22,$00 +    DB $00,$57,$54,$56,$71,$15,$12,$00,$00,$17,$21,$61,$52,$52,$22,$00 
-   DB $00,$22,$55,$25,$53,$52,$24,$00,$00,$00,$00,$22,$00,$00,$22,$02 +    DB $00,$22,$55,$25,$53,$52,$24,$00,$00,$00,$00,$22,$00,$00,$22,$02 
-   DB $00,$00,$10,$27,$40,$27,$10,$00,$00,$02,$45,$21,$12,$20,$42,$00 +    DB $00,$00,$10,$27,$40,$27,$10,$00,$00,$02,$45,$21,$12,$20,$42,$00 
-   DB $00,$23,$55,$75,$77,$45,$35,$00,$00,$63,$54,$64,$54,$54,$63,$00 +    DB $00,$23,$55,$75,$77,$45,$35,$00,$00,$63,$54,$64,$54,$54,$63,$00 
-   DB $00,$67,$54,$56,$54,$54,$67,$00,$00,$73,$44,$64,$45,$45,$43,$00 +    DB $00,$67,$54,$56,$54,$54,$67,$00,$00,$73,$44,$64,$45,$45,$43,$00 
-   DB $00,$57,$52,$72,$52,$52,$57,$00,$00,$35,$15,$16,$55,$55,$25,$00 +    DB $00,$57,$52,$72,$52,$52,$57,$00,$00,$35,$15,$16,$55,$55,$25,$00 
-   DB $00,$45,$47,$45,$45,$45,$75,$00,$00,$62,$55,$55,$55,$55,$52,$00 +    DB $00,$45,$47,$45,$45,$45,$75,$00,$00,$62,$55,$55,$55,$55,$52,$00 
-   DB $00,$62,$55,$55,$65,$45,$43,$00,$00,$63,$54,$52,$61,$55,$52,$00 +    DB $00,$62,$55,$55,$65,$45,$43,$00,$00,$63,$54,$52,$61,$55,$52,$00 
-   DB $00,$75,$25,$25,$25,$25,$22,$00,$00,$55,$55,$55,$55,$27,$25,$00 +    DB $00,$75,$25,$25,$25,$25,$22,$00,$00,$55,$55,$55,$55,$27,$25,$00 
-   DB $00,$55,$55,$25,$22,$52,$52,$00,$00,$73,$12,$22,$22,$42,$72,$03 +    DB $00,$55,$55,$25,$22,$52,$52,$00,$00,$73,$12,$22,$22,$42,$72,$03 
-   DB $00,$46,$42,$22,$22,$12,$12,$06,$00,$20,$50,$00,$00,$00,$00,$0F +    DB $00,$46,$42,$22,$22,$12,$12,$06,$00,$20,$50,$00,$00,$00,$00,$0f 
-   DB $00,$20,$10,$03,$05,$05,$03,$00,$00,$40,$40,$63,$54,$54,$63,$00 +    DB $00,$20,$10,$03,$05,$05,$03,$00,$00,$40,$40,$63,$54,$54,$63,$00 
-   DB $00,$10,$10,$32,$55,$56,$33,$00,$00,$10,$20,$73,$25,$25,$43,$06 +    DB $00,$10,$10,$32,$55,$56,$33,$00,$00,$10,$20,$73,$25,$25,$43,$06 
-   DB $00,$42,$40,$66,$52,$52,$57,$00,$00,$14,$04,$35,$16,$15,$55,$20 +    DB $00,$42,$40,$66,$52,$52,$57,$00,$00,$14,$04,$35,$16,$15,$55,$20 
-   DB $00,$60,$20,$25,$27,$25,$75,$00,$00,$00,$00,$62,$55,$55,$52,$00 +    DB $00,$60,$20,$25,$27,$25,$75,$00,$00,$00,$00,$62,$55,$55,$52,$00 
-   DB $00,$00,$00,$63,$55,$55,$63,$41,$00,$00,$00,$53,$66,$43,$46,$00 +    DB $00,$00,$00,$63,$55,$55,$63,$41,$00,$00,$00,$53,$66,$43,$46,$00 
-   DB $00,$00,$20,$75,$25,$25,$12,$00,$00,$00,$00,$55,$55,$27,$25,$00 +    DB $00,$00,$20,$75,$25,$25,$12,$00,$00,$00,$00,$55,$55,$27,$25,$00 
-   DB $00,$00,$00,$55,$25,$25,$53,$06,$00,$01,$02,$72,$34,$62,$72,$01 +    DB $00,$00,$00,$55,$25,$25,$53,$06,$00,$01,$02,$72,$34,$62,$72,$01 
-   DB $00,$24,$22,$22,$21,$22,$22,$04,$00,$56,$A9,$06,$04,$06,$09,$06+    DB $00,$24,$22,$22,$21,$22,$22,$04,$00,$56,$a9,$06,$04,$06,$09,$06
 </code> </code>
  
Línea 2726: Línea 2717:
  Como en cada byte de la fuente se definen 2 caracteres (izquierdo y derecho) y a su vez a la hora imprimir en pantalla hay 2 posibilidades de impresión en el mismo bloque (parte izquierda y parte derecha del bloque), necesitamos 4 rutinas que cubran esas cuatro posibilidades.  Como en cada byte de la fuente se definen 2 caracteres (izquierdo y derecho) y a su vez a la hora imprimir en pantalla hay 2 posibilidades de impresión en el mismo bloque (parte izquierda y parte derecha del bloque), necesitamos 4 rutinas que cubran esas cuatro posibilidades.
  
-\\ +\\
   * Imprimir un carácter de la "parte izquierda" (nibble alto en datos de caracter) de la fuente en la "parte izquierda" de un bloque de pantalla (nibble alto del byte en videoram).   * Imprimir un carácter de la "parte izquierda" (nibble alto en datos de caracter) de la fuente en la "parte izquierda" de un bloque de pantalla (nibble alto del byte en videoram).
   * Imprimir un carácter de la "parte derecha" (nibble bajo en datos de caracter) de la fuente en la "parte izquierda" de un bloque de pantalla (nibble alto del byte en videoram).   * Imprimir un carácter de la "parte derecha" (nibble bajo en datos de caracter) de la fuente en la "parte izquierda" de un bloque de pantalla (nibble alto del byte en videoram).
   * Imprimir un carácter de la "parte izquierda" (nibble alto en datos de caracter) de la fuente en la "parte derecha" de un bloque de pantalla (nibble bajo del byte en videoram).   * Imprimir un carácter de la "parte izquierda" (nibble alto en datos de caracter) de la fuente en la "parte derecha" de un bloque de pantalla (nibble bajo del byte en videoram).
   * Imprimir un carácter de la "parte derecha" (nibble bajo en datos de caracter) de la fuente en la "parte derecha" de un bloque de pantalla (nibble bajo del byte en videoram).   * Imprimir un carácter de la "parte derecha" (nibble bajo en datos de caracter) de la fuente en la "parte derecha" de un bloque de pantalla (nibble bajo del byte en videoram).
-\\ +\\
  
  Al trazar el carácter en pantalla tenemos que hacerlo con OR para respetar otro posible carácter de 4x8 que pueda haber en el mismo bloque, ya lo estemos imprimiendo en la parte izquierda de un bloque (respetar el nibble de la derecha) o en la derecha (respetar el nibble de la izquierda).  Al trazar el carácter en pantalla tenemos que hacerlo con OR para respetar otro posible carácter de 4x8 que pueda haber en el mismo bloque, ya lo estemos imprimiendo en la parte izquierda de un bloque (respetar el nibble de la derecha) o en la derecha (respetar el nibble de la izquierda).
  
- Veamos la rutina de impresión **PrintChar_4x8** seguida de la 4 subrutinas de volcado de carácter que son llamados una vez calculados HL y DE como origen y destino.+ Veamos la rutina de impresión ''PrintChar_4x8'' seguida de la 4 subrutinas de volcado de carácter que son llamados una vez calculados HL y DE como origen y destino.
  
 <code z80> <code z80>
Línea 2752: Línea 2743:
 PrintChar_4x8: PrintChar_4x8:
  
-   RRA                       ; Dividimos A por 2 (resto en CF) +    rra                       ; Dividimos A por 2 (resto en CF) 
-   PUSH AF                   ; Guardamos caracter y CF en A'+    push af                   ; Guardamos caracter y CF en A'
  
-   ;;; Calcular posicion origen (array fuente) en HL como: +    ;;; Calcular posicion origen (array fuente) en HL como: 
-   ;;;     direccion = base_charset + ((CARACTER/2)*8) +    ;;;     direccion = base_charset + ((CARACTER/2)*8) 
-   LD BC, (FONT_CHARSET) +    ld bc, (FONT_CHARSET) 
-   LD H, 0 +    ld h, 0 
-   LD LA +    ld la 
-   ADD HLHL +    add hlhl 
-   ADD HLHL +    add hlhl 
-   ADD HLHL +    add hlhl 
-   ADD HLBC                ; HL = Direccion origen de A en fuente+    add hlbc                ; HL = Direccion origen de A en fuente
  
-   ;;; Calculamos las coordenadas destino de pantalla en DE: +    ;;; Calculamos las coordenadas destino de pantalla en DE: 
-   LD BC, (FONT_X)           ; B = Y,  C = X +    ld bc, (FONT_X)           ; B = Y,  C = X 
-   RR C +    rr c 
-   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 E                 ; DE contiene ahora la direccion destino.+    ld e                 ; DE contiene ahora la direccion destino.
  
-   ;;; Calculamos posición en pantalla. Tenemos que dividirla por 2 porque +    ;;; Calculamos posición en pantalla. Tenemos que dividirla por 2 porque 
-   ;;; en cada columna de pantalla caben 2 caracteres. Usaremos el resto  +    ;;; en cada columna de pantalla caben 2 caracteres. Usaremos el resto 
-   ;;; (Carry) para saber si va en la izq (CF=0) o der (CF=1) del caracter. +    ;;; (Carry) para saber si va en la izq (CF=0) o der (CF=1) del caracter. 
-   LD A, (FONT_X)            ; Volvemos a leer coordenada X +    ld a, (FONT_X)            ; Volvemos a leer coordenada X 
-   RRA                       ; Dividimos por 2 (posicion X en pantalla) +    rra                       ; Dividimos por 2 (posicion X en pantalla) 
-                             ; Ademas el carry tiene el resto (par/impar) +                                ; Ademas el carry tiene el resto (par/impar) 
-   JR NC, pchar4_x_odd       ; Saltar si es columna impar (por el CF)+    jr nc, pchar4_x_odd       ; Saltar si es columna impar (por el CF)
  
-   ;;; Ahora tenemos que imprimir el caracter en pantalla. Hemos saltado +    ;;; Ahora tenemos que imprimir el caracter en pantalla. Hemos saltado 
-   ;;; a pchar4_x_even o pchar4_x_odd segun si la posicion en pantalla es +    ;;; a pchar4_x_even o pchar4_x_odd segun si la posicion en pantalla es 
-   ;;; par o impar, pero cada una de estas 2 opciones nos da la posibilidad +    ;;; par o impar, pero cada una de estas 2 opciones nos da la posibilidad 
-   ;;; de usar una rutina u otra segun si el caracter ASCII es par o impar +    ;;; de usar una rutina u otra segun si el caracter ASCII es par o impar 
-   ;;; ya que tenemos que cogerlo de la fuente de una forma u otra +    ;;; ya que tenemos que cogerlo de la fuente de una forma u otra 
-   ;;; Posicion de columna en pantalla par:+    ;;; Posicion de columna en pantalla par:
 pchar4_x_even  : pchar4_x_even  :
-   POP AF                    ; Restaura A=char y CF=si es char par/impar +    pop af                    ; Restaura A=char y CF=si es char par/impar 
-   JR C, pchar4_l_on_l +    jr c, pchar4_l_on_l 
-   JR pchar4_r_on_l+    jr pchar4_r_on_l
  
 pchar4_x_odd: pchar4_x_odd:
-   POP AF                    ; Restaura A=char y CF=si es char par/impar +    pop af                    ; Restaura A=char y CF=si es char par/impar 
-   JR NC, pchar4_r_on_r +    jr nc, pchar4_r_on_r 
-   JR pchar4_l_on_r+    jr pchar4_l_on_r
  
 pchar4_continue: pchar4_continue:
Línea 2809: Línea 2800:
 pchar4_printattr: pchar4_printattr:
  
-   LD A           ; Recuperamos el valor inicial de DE +    ld a           ; Recuperamos el valor inicial de DE 
-   SUB 8              ; Restando los 8 scanlines avanzados+    sub 8              ; Restando los 8 scanlines avanzados
  
-   ;;; Calcular posicion destino en area de atributos en HL. +    ;;; Calcular posicion destino en area de atributos en HL. 
-   RRCA               ; A ya es = D, listo para rotar +    rrca               ; A ya es = D, listo para rotar 
-   RRCA               ; Codigo de Get_Attr_Offset_From_Image +    rrca               ; Codigo de Get_Attr_Offset_From_Image 
-   RRCA +    rrca 
-   AND +    and 
-   OR $58 +    or $58 
-   LD HA +    ld ha 
-   LD L+    ld le
- +
-   ;;; Escribir el atributo en memoria +
-   LD A, (FONT_ATTRIB) +
-   LD (HL), A         ; Escribimos el atributo en memoria +
-   RET+
  
 +    ;;; Escribir el atributo en memoria
 +    ld a, (FONT_ATTRIB)
 +    ld (hl), a         ; Escribimos el atributo en memoria
 +    ret
  
 ;;;------------------------------------------------------------------ ;;;------------------------------------------------------------------
Línea 2835: Línea 2825:
 ;;;---------------------------------------------------- ;;;----------------------------------------------------
 pchar4_l_on_l: pchar4_l_on_l:
-   LD B, 8                   ; 8 scanlines / iteraciones +    ld b, 8                   ; 8 scanlines / iteraciones 
-pchar4_ll_lp:  +pchar4_ll_lp: 
-   LD A, (DE)                ; Leer byte de la pantalla +    ld a, (de)                ; Leer byte de la pantalla 
-   AND %11110000 +    and %11110000 
-   LD C                  ; Nos lo guardamos en C +    ld c                  ; Nos lo guardamos en C 
-   LD A, (HL)                ; Cogemos el byte de la fuente +    ld a, (hl)                ; Cogemos el byte de la fuente 
-   AND %00001111 +    and %00001111 
-   OR C                      ; Lo combinamos con el fondo +    or c                      ; Lo combinamos con el fondo 
-   LD (DE),                ; Y lo escribimos en pantalla +    ld (de),                ; Y lo escribimos en pantalla 
-   INC D                     ; Siguiente scanline +    inc d                     ; Siguiente scanline 
-   INC HL                    ; Siguiente dato del "sprite" +    inc hl                    ; Siguiente dato del "sprite" 
-   DJNZ pchar4_ll_lp +    djnz pchar4_ll_lp 
-   JR pchar4_continue        ; Volver tras impresion +    jr pchar4_continue        ; Volver tras impresion
  
 ;;;---------------------------------------------------- ;;;----------------------------------------------------
-pchar4_r_on_r:  +pchar4_r_on_r: 
-   LD B, 8                   ; 8 scanlines / iteraciones+    ld b, 8                   ; 8 scanlines / iteraciones
 pchar4_rr_lp: pchar4_rr_lp:
-   LD A, (DE)                ; Leer byte de la pantalla +    ld a, (de)                ; Leer byte de la pantalla 
-   AND %00001111 +    and %00001111 
-   LD C                  ; Nos lo guardamos en C +    ld c                  ; Nos lo guardamos en C 
-   LD A, (HL)                ; Cogemos el byte de la fuente +    ld a, (hl)                ; Cogemos el byte de la fuente 
-   AND %11110000 +    and %11110000 
-   OR C                      ; Lo combinamos con el fondo +    or c                      ; Lo combinamos con el fondo 
-   LD (DE),                ; Y lo escribimos en pantalla +    ld (de),                ; Y lo escribimos en pantalla 
-   INC D                     ; Siguiente scanline +    inc d                     ; Siguiente scanline 
-   INC HL                    ; Siguiente dato del "sprite" +    inc hl                    ; Siguiente dato del "sprite" 
-   DJNZ pchar4_rr_lp +    djnz pchar4_rr_lp 
-   JR pchar4_continue        ; Volver tras impresion +    jr pchar4_continue        ; Volver tras impresion
  
 ;;;---------------------------------------------------- ;;;----------------------------------------------------
-pchar4_l_on_r:  +pchar4_l_on_r: 
-   LD B, 8                   ; 8 scanlines / iteraciones +    ld b, 8                   ; 8 scanlines / iteraciones 
-pchar4_lr_lp:  +pchar4_lr_lp: 
-   LD A, (DE)                ; Leer byte de la pantalla +    ld a, (de)                ; Leer byte de la pantalla 
-   AND %00001111 +    and %00001111 
-   LD C                  ; Nos lo guardamos en C +    ld c                  ; Nos lo guardamos en C 
-   LD A, (HL)                ; Cogemos el byte de la fuente +    ld a, (hl)                ; Cogemos el byte de la fuente 
-   RRCA                      ; Lo desplazamos 4 veces >> dejando +    rrca                      ; Lo desplazamos 4 veces >> dejando 
-   RRCA                      ; lo bits 4 al 7 vacios +    rrca                      ; lo bits 4 al 7 vacios 
-   RRCA +    rrca 
-   RRCA +    rrca 
-   AND %11110000 +    and %11110000 
-   OR C                      ; Lo combinamos con el fondo +    or c                      ; Lo combinamos con el fondo 
-   LD (DE),                ; Y lo escribimos en pantalla +    ld (de),                ; Y lo escribimos en pantalla 
-   INC D                     ; Siguiente scanline +    inc d                     ; Siguiente scanline 
-   INC HL                    ; Siguiente dato del "sprite" +    inc hl                    ; Siguiente dato del "sprite" 
-   DJNZ pchar4_lr_lp +    djnz pchar4_lr_lp 
-   JR pchar4_continue        ; Volver tras impresion +    jr pchar4_continue        ; Volver tras impresion
  
 ;;;---------------------------------------------------- ;;;----------------------------------------------------
-pchar4_r_on_l:  +pchar4_r_on_l: 
-   LD B, 8                   ; 8 scanlines / iteraciones +    ld b, 8                   ; 8 scanlines / iteraciones 
-pchar4_rl_lp:  +pchar4_rl_lp: 
-   LD A, (DE)                ; Leer byte de la pantalla +    ld a, (de)                ; Leer byte de la pantalla 
-   AND %11110000 +    and %11110000 
-   LD C                  ; Nos lo guardamos en C +    ld c                  ; Nos lo guardamos en C 
-   LD A, (HL)                ; Cogemos el byte de la fuente +    ld a, (hl)                ; Cogemos el byte de la fuente 
-   RLCA                      ; Lo desplazamos 4 veces << dejando +    rlca                      ; Lo desplazamos 4 veces << dejando 
-   RLCA                      ; los bits 0 al 3 vacios +    rlca                      ; los bits 0 al 3 vacios 
-   RLCA +    rlca 
-   RLCA +    rlca 
-   AND %00001111 +    and %00001111 
-   OR C                      ; Lo combinamos con el fondo +    or c                      ; Lo combinamos con el fondo 
-   LD (DE),                ; Y lo escribimos en pantalla +    ld (de),                ; Y lo escribimos en pantalla 
-   INC D                     ; Siguiente scanline +    inc d                     ; Siguiente scanline 
-   INC HL                    ; Siguiente dato del "sprite" +    inc hl                    ; Siguiente dato del "sprite" 
-   DJNZ pchar4_rl_lp +    djnz pchar4_rl_lp 
-   JR pchar4_continue        ; Volver tras impresion+    jr pchar4_continue        ; Volver tras impresion
 </code> </code>
  
Línea 2915: Línea 2902:
 </code> </code>
  
- También habría que crear un PrintString_4x8 idéntico a PrintString_8x8 pero que llame a PrintChar_4x8, y modificar aquellas rutinas que hagan referencia a una de estas 2 funciones, como por ejemplo:+ También habría que crear un ''PrintString_4x8'' idéntico a ''PrintString_8x8'' pero que llame a ''PrintChar_4x8'', y modificar aquellas rutinas que hagan referencia a una de estas 2 funciones, como por ejemplo:
  
 <code z80> <code z80>
 Font_Backspace: Font_Backspace:
-   CALL Font_Dec_X +    call Font_Dec_X 
-   LD A, ' '                 ; Imprimir caracter espacio +    ld a, ' '                 ; Imprimir caracter espacio 
-   PUSH BC +    push bc 
-   PUSH DE +    push de 
-   PUSH HL +    push hl 
-   CALL PrintChar_4x8        ; Sobreescribir caracter +    call PrintChar_4x8        ; Sobreescribir caracter 
-   POP HL +    pop hl 
-   POP DE +    pop de 
-   POP BC +    pop bc 
-   RET                       ; Salir+    ret                       ; Salir
 </code> </code>
  
Línea 2934: Línea 2921:
  
 <code z80> <code z80>
-  ; Ejemplo de fuente de 4x8 pixeles (64 caracteres por linea) +; Ejemplo de fuente de 4x8 pixeles (64 caracteres por linea) 
-  ORG 32768+    ORG 35000
  
-  LD HL, charset_4x8-128                    ; Inicio charset - (256/2) +    ld hl, charset_4x8-128                    ; Inicio charset - (256/2) 
-  CALL Font_Set_Charset+    call Font_Set_Charset
  
-  LD HL, cadena1 +    ld hl, cadena1 
-  LD BC, $0400                              ; X=00, Y=04 +    ld bc, $0400                              ; X=00, Y=04 
-  CALL Font_Set_XY +    call Font_Set_XY 
-  CALL PrintString_4x8_Format+    call PrintString_4x8_Format
  
-loop:  +loop: 
-  JR loop +    jr loop 
-  RET+    ret
  
 cadena1 DB "Fuente de 4x8", FONT_CRLF, FONT_CRLF cadena1 DB "Fuente de 4x8", FONT_CRLF, FONT_CRLF
Línea 2960: Línea 2947:
         DB "modificarlos solo antes o tras un espacio."         DB "modificarlos solo antes o tras un espacio."
         DB FONT_EOS         DB FONT_EOS
 +
 +    END 35000
 </code> </code>
  
-\\  +\\ 
-{{ :cursos:ensamblador:gfx4_64col.png | Ejemplo de impresión a 64 columnas }} +{{ :cursos:ensamblador:gfx4_64col.png?640 | Ejemplo de impresión a 64 columnas }} 
-\\ +\\
  
- Nótese cómo hemos inicializado FONT_CHARSET a la dirección de la fuente menos 128 en lugar de restarle 256. Esto se debe a que la fuente tiene 2 caracteres definidos en cada byte y vamos a dividir el ASCII entre 2 en nuestra rutina, por lo que el carácter en que empieza nuestra fuente, el 32, está en charset4x8 - (256/2) = charset4x8 - 128.+ Nótese cómo hemos inicializado ''FONT_CHARSET'' a la dirección de la fuente menos 128 en lugar de restarle 256. Esto se debe a que la fuente tiene 2 caracteres definidos en cada byte y vamos a dividir el ASCII entre 2 en nuestra rutina, por lo que el carácter en que empieza nuestra fuente, el 32, está en charset4x8 - (256/2) = charset4x8 - 128.
  
  Otro detalle importante es el tema de los atributos: como cada bloque de pantalla contiene 2 caracteres, no podemos establecer atributos diferentes para 2 caracteres del mismo byte. Por esto, hay que ser cauto a la hora de establecer atributos. La solución más sencilla es cambiar las tintas en posiciones donde haya espacios, ya que en ese caso el cambio será efectivo en la letra deseada si ésta es la primera del byte, o en el espacio seguido de la letra deseada si está en la parte derecha. Los cambios de PAPER, BRIGHT o FLASH supondrán problemas si no se realizan siempre en posiciones de pantalla pares.  Otro detalle importante es el tema de los atributos: como cada bloque de pantalla contiene 2 caracteres, no podemos establecer atributos diferentes para 2 caracteres del mismo byte. Por esto, hay que ser cauto a la hora de establecer atributos. La solución más sencilla es cambiar las tintas en posiciones donde haya espacios, ya que en ese caso el cambio será efectivo en la letra deseada si ésta es la primera del byte, o en el espacio seguido de la letra deseada si está en la parte derecha. Los cambios de PAPER, BRIGHT o FLASH supondrán problemas si no se realizan siempre en posiciones de pantalla pares.
Línea 2990: Línea 2979:
   * {{cursos:ensamblador:gfx4_64col.tap|Tap del ejemplo anterior}}   * {{cursos:ensamblador:gfx4_64col.tap|Tap del ejemplo anterior}}
  
-\\ +\\
 ===== Enlaces ===== ===== Enlaces =====
  
Línea 3000: Línea 2989:
   * [[http://www.worldofspectrum.org/faq/reference/z80reference.htm|Z80 Reference de WOS]].   * [[http://www.worldofspectrum.org/faq/reference/z80reference.htm|Z80 Reference de WOS]].
   * [[http://www.speccy.org/trastero/cosas/Fichas/fichas.htm|Microfichas de CM de MicroHobby]].   * [[http://www.speccy.org/trastero/cosas/Fichas/fichas.htm|Microfichas de CM de MicroHobby]].
-  * [[http://www.arrakis.es/~ninsesabe/pasmo/|PASMO]]. 
  
 +\\ 
 +**[ [[.:indice|⬉]] | [[.:gfx3_sprites_lowres|⬅]] | [[.:gfx5_mapeados|➡]] ]**
  • cursos/ensamblador/gfx4_fuentes.txt
  • Última modificación: 19-01-2024 08:23
  • por sromero