cursos:ensamblador:asmz88dk

Diferencias

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

Enlace a la vista de comparación

Ambos lados, revisión anterior Revisión previa
Próxima revisión
Revisión previa
cursos:ensamblador:asmz88dk [10-01-2024 08:51] – [Integración de ASM en Z88DK] sromerocursos:ensamblador:asmz88dk [21-01-2024 16:52] (actual) – [Enlaces] sromero
Línea 1: Línea 1:
 ====== Integración de ASM en Z88DK ====== ====== Integración de ASM en Z88DK ======
  
-En este capítulo vamos a ver cómo podemos usar ASM dentro del compilador de C más utilizado en Spectrum: el Z88DK.+En este capítulo vamos a ver cómo podemos usar ASM dentro del compilador de C más utilizado en Spectrum: el **Z88DK** (//Z88 Development Kit//).
  
 La idea de hacer programas mixtos en C con pequeñas partes en ASM es la de escribir en ensamblador las partes más críticas o importantes del mismo para acelerar su ejecución. La idea de hacer programas mixtos en C con pequeñas partes en ASM es la de escribir en ensamblador las partes más críticas o importantes del mismo para acelerar su ejecución.
Línea 15: Línea 15:
 ===== Embeber ASM dentro de código C ===== ===== Embeber ASM dentro de código C =====
  
-Z88DK permite utilizar ASM de diferentes formas. En nuestro caso vamos a ver cómo se "embebeC en ASM con las siguientes directivas:+Z88DK permite utilizar ASM de diferentes formas. En nuestro caso vamos a ver cómo se embebe C en ASM con las directivas ''#asm'' y ''#endasm'':
  
 <code c> <code c>
Línea 39: Línea 39:
 { {
   #asm   #asm
-    XOR A +    xor a 
-    LD HL, 16384          ; HL = Inicio de la videoram +    ld hl, 16384          ; HL = Inicio de la videoram 
-    LD (HL),            ; Escribimos el patron A en (HL) +    ld (hl),            ; Escribimos el patron A en (HL) 
-    LD DE, 16385          ; Apuntamos DE a 16385 +    ld de, 16385          ; Apuntamos DE a 16385 
-    LD BC, 192*32-1       ; Copiaremos 192*32-1 veces (HL) en (DE) +    ld bc, 192*32-1       ; Copiaremos 192*32-1 veces (HL) en (DE) 
-    LDIR                  +    ldir                  
   #endasm   #endasm
 } }
 </code> </code>
  
- No es necesario hacer PUSH y POP de los registros para preservar sus valores porque Z88DK lo hace automáticamente por nosotros.+ No es necesario hacer ''PUSH'' ''POP'' de los registros para preservar sus valores porque Z88DK lo hace automáticamente por nosotros.
  
  Podremos llamar a esta función desde nuestro código en C como a cualquier otra función:  Podremos llamar a esta función desde nuestro código en C como a cualquier otra función:
Línea 60: Línea 60:
  
 \\  \\ 
-===== Cómo leer parámetros de funciones desde ASM =====+===== Cómo leer parámetros de llamada desde ASM =====
  
  En C (y en otros lenguajes de programación) los parámetros se insertan normalmente en la pila en el orden en que aparecen en el código del programa. La subrutina debe utilizar el registro SP (sin modificarlo, y sin desapilar los valores) para acceder a los valores apilados en orden inverso, leyendo sus valores de la memoria. Estos valores son siempre de 16 bits aunque las variables pasadas sean de 8 bits (en este caso ignoraremos el byte que no contiene datos, el segundo).  En C (y en otros lenguajes de programación) los parámetros se insertan normalmente en la pila en el orden en que aparecen en el código del programa. La subrutina debe utilizar el registro SP (sin modificarlo, y sin desapilar los valores) para acceder a los valores apilados en orden inverso, leyendo sus valores de la memoria. Estos valores son siempre de 16 bits aunque las variables pasadas sean de 8 bits (en este caso ignoraremos el byte que no contiene datos, el segundo).
Línea 66: Línea 66:
  Veamos unos ejemplos:  Veamos unos ejemplos:
  
-<code>+<code c>
 //----------------------------------------------------------------- //-----------------------------------------------------------------
 // Sea parte de nuestro programa en C: // Sea parte de nuestro programa en C:
Línea 83: Línea 83:
  
 #asm #asm
-    LD HL,2 +    ld hl, 2 
-    ADD HL,SP           ; Ahora SP apunta al ultimo parametro metido+    add hlsp          ; Ahora SP apunta al ultimo parametro metido
                         ; en la pila por el compilador (valor de Y)                         ; en la pila por el compilador (valor de Y)
  
-    LD C, (HL+    ld c, (hl
-    INC HL +    inc hl 
-    LD B, (HL+    ld b, (hl
-    INC HL              ; Ahora BC = y+    inc hl              ; Ahora BC = y
  
-    LD E, (HL+    ld e, (hl
-    INC HL +    inc hl 
-    LD D, (HL+    ld d, (hl
-    INC HL              ; Ahora, DE = x+    inc hl              ; Ahora, DE = x
  
     ;;; (ahora hacemos lo que queramos en asm)     ;;; (ahora hacemos lo que queramos en asm)
Línea 103: Línea 103:
 </code> </code>
  
- Como hemos comentado en un apartado anterior, no tenemos que preocuparnos por hacer PUSH y POP de los registros para preservar su valor dado que Z88DK lo hace automáticamente antes y después de cada #asm y #endasm.+ Como hemos comentado en un apartado anterior, no tenemos que preocuparnos por hacer ''PUSH'' ''POP'' de los registros para preservar su valor dado que Z88DK lo hace automáticamente antes y después de cada ''#asm'' ''#endasm''.
  
- El problema es que conforme crece el número de parámetros apilados, es posible que tengamos que hacer malabarismos para almacenarlos, dado que no podemos usar HL (es nuestro puntero a la pila en las lecturas). Veamos el siguiente ejemplo con 3 parámetros, donde tenemos que usar PUSH para guardar el valor de DE y EX DEHL para acabar asociando el valor final a HL:+ El problema es que conforme crece el número de parámetros apilados, es posible que tengamos que hacer malabarismos para almacenarlos, dado que no podemos usar HL (es nuestro puntero a la pila en las lecturas). Veamos el siguiente ejemplo con 3 parámetros, donde tenemos que usar ''PUSH'' para guardar el valor de DE y ''ex dehl'' para acabar asociando el valor final a HL:
  
-<code>+<code c>
 //----------------------------------------------------------------- //-----------------------------------------------------------------
 int Funcion(int x, int y, int z) int Funcion(int x, int y, int z)
Línea 113: Línea 113:
  
 #asm #asm
-    LD HL,2 +    ld hl, 2 
-    ADD HL,SP           ; Ahora SP apunta al ultimo parametro metido+    add hlsp          ; Ahora SP apunta al ultimo parametro metido
                         ; en la pila por el compilador (z)                         ; en la pila por el compilador (z)
-    LD C, (HL+    ld c, (hl
-    INC HL +    inc hl 
-    LD B, (HL+    ld b, (hl
-    INC HL              ; Ahora BC = z+    inc hl              ; Ahora BC = z
  
-    LD E, (HL+    ld e, (hl
-    INC HL +    inc hl 
-    LD D, (HL+    ld d, (hl
-    INC HL              ; Ahora, DE = y+    inc hl              ; Ahora, DE = y
  
-    PUSH DE             ; Guardamos DE+    push de             ; Guardamos DE
  
-    LD E, (HL+    ld e, (hl
-    INC HL +    inc hl 
-    LD D, (HL+    ld d, (hl
-    INC HL              ; Usamos DE para leer el valor de x+    inc hl              ; Usamos DE para leer el valor de x
  
-    EX DEHL           ; Ahora cambiamos x a HL +    ex dehl           ; Ahora cambiamos x a HL 
-    POP DE              ; Y recuperamos el valor de y en DE+    pop de              ; Y recuperamos el valor de y en DE
  
     ;;; (ahora hacemos lo que queramos en asm)     ;;; (ahora hacemos lo que queramos en asm)
Línea 144: Línea 144:
  La manera de leer bytes (variables de tipo char) pulsados en C es de la misma forma que leemos una palabra de 16 bits, pero ignorando la parte alta. En realidad, como la pila es de 16 bits, el compilador convierte el dato de 8 bits en uno de 16 (rellenando con ceros) y mete en la pila este valor:  La manera de leer bytes (variables de tipo char) pulsados en C es de la misma forma que leemos una palabra de 16 bits, pero ignorando la parte alta. En realidad, como la pila es de 16 bits, el compilador convierte el dato de 8 bits en uno de 16 (rellenando con ceros) y mete en la pila este valor:
  
-<code>+<code c>
 //----------------------------------------------------------------- //-----------------------------------------------------------------
 int Funcion(char x, char y) int Funcion(char x, char y)
Línea 150: Línea 150:
  
 #asm #asm
-    LD HL,2 +    ld hl, 2 
-    ADD HL,SP           ; Ahora SP apunta al ultimo parametro metido+    add hlsp          ; Ahora SP apunta al ultimo parametro metido
                         ; en la pila por el compilador (y)                         ; en la pila por el compilador (y)
  
-    LD A, (HL)          ; Aquí tenemos nuestro dato de 8 bits (y) +    ld a, (hl)          ; Aquí tenemos nuestro dato de 8 bits (y) 
-    LD BA +    ld ba 
-    INC HL +    inc hl 
-    INC HL              ; La parte alta del byte no nos interesa+    inc hl              ; La parte alta del byte no nos interesa
  
-    LD A, (HL)          ; Aquí tenemos nuestro dato de 8 bits (x) +    ld a, (hl)          ; Aquí tenemos nuestro dato de 8 bits (x) 
-    LD CA+    ld ca
     ; Si hubiera más parámetros, necesitaríamos seguir haciendo dos     ; Si hubiera más parámetros, necesitaríamos seguir haciendo dos
-    ; "INC HL" para acceder a ellos. Como es el último, no es necesario.+    ; "inc hl" para acceder a ellos. Como es el último, no es necesario.
          
     ;;; (ahora hacemos lo que queramos en asm)     ;;; (ahora hacemos lo que queramos en asm)
Línea 169: Línea 169:
 </code> </code>
  
-Por ejemplo, veamos nuestra rutina anterior de ClearScreen() permitiendo pasar un parámetro para indicar el carácter a utilizar para el rellenado de la pantalla:+Por ejemplo, veamos nuestra rutina anterior de ''ClearScreen()'' permitiendo pasar un parámetro para indicar el carácter a utilizar para el rellenado de la pantalla:
  
 <code c> <code c>
Línea 175: Línea 175:
 { {
   #asm   #asm
-    LD HL,2 +    ld hl, 2 
-    ADD HL,SP             ; Ahora SP apunta al ultimo parametro metido+    add hlsp            ; Ahora SP apunta al ultimo parametro metido
                           ; en la pila por el compilador (value)                           ; en la pila por el compilador (value)
-    LD A, (HL)            ; Aquí tenemos nuestro dato de 8 bits (value)+    ld a, (hl)            ; Aquí tenemos nuestro dato de 8 bits (value)
          
-    LD HL, 16384          ; HL = Inicio de la videoram +    ld hl, 16384          ; HL = Inicio de la videoram 
-    LD (HL),            ; Escribimos el patron A en (HL) +    ld (hl),            ; Escribimos el patron A en (HL) 
-    LD DE, 16385          ; Apuntamos DE a 16385 +    ld de, 16385          ; Apuntamos DE a 16385 
-    LD BC, 192*32-1       ; Copiaremos 192*32-1 veces (HL) en (DE) +    ld bc, 192*32-1       ; Copiaremos 192*32-1 veces (HL) en (DE) 
-    LDIR                  +    ldir                  
   #endasm   #endasm
 } }
Línea 191: Línea 191:
  En ocasiones, es posible que incluso tengamos que utilizar variables auxiliares de memoria para guardar datos:  En ocasiones, es posible que incluso tengamos que utilizar variables auxiliares de memoria para guardar datos:
  
-<code>+<code c>
 //----------------------------------------------------------------- //-----------------------------------------------------------------
 int Funcion(int x, int y, char z) int Funcion(int x, int y, char z)
Línea 197: Línea 197:
  
 #asm #asm
-    LD HL,2 +    ld hl, 2 
-    ADD HL,SP           ; Ahora SP apunta al ultimo parametro metido+    add hlsp          ; Ahora SP apunta al ultimo parametro metido
                         ; en la pila por el compilador (z)                         ; en la pila por el compilador (z)
  
-    LD C, (HL+    ld c, (hl
-    INC HL +    inc hl 
-    LD B, (HL+    ld b, (hl
-    INC HL              ; Ahora BC = y +    inc hl              ; Ahora BC = y 
-    LD (valor_y), BC    ; nos lo guardamos, BC libre de nuevo+    ld (valor_y), bc    ; nos lo guardamos, bc libre de nuevo
  
-    LD C, (HL+    ld c, (hl
-    INC HL +    inc hl 
-    LD B, (HL+    ld b, (hl
-    INC HL +    inc hl 
-    LD (valor_x), BC    ; Nos lo guardamos, BC libre de nuevo+    ld (valor_x), bc    ; Nos lo guardamos, bc libre de nuevo
  
-    LD A, (HL+    ld a, (hl
-    LD (valor_z),     ; Nos guardamos el byte +    ld (valor_z),     ; Nos guardamos el byte 
-    INC HL +    inc hl 
-    INC HL              ; La parte alta del byte no nos interesa+    inc hl              ; La parte alta del byte no nos interesa
  
     ;;; (ahora hacemos lo que queramos en asm)     ;;; (ahora hacemos lo que queramos en asm)
  
-    RET+    ret
  
 valor_x   DW  0 valor_x   DW  0
Línea 234: Línea 234:
 ===== Devolver parámetros desde funciones escritas en ASM ===== ===== Devolver parámetros desde funciones escritas en ASM =====
  
- Por contra, para devolver valores no se utiliza la pila dado que no podemos tocarla, ya que el RET volvería al valor pulsado y no a la dirección de retorno. Lo que se hace en Z88DK es utilizar un determinado registro. En el caso de Z88DK, se utiliza el registro **HL**. Si la función es de tipo INT o CHAR en cuanto a devolución, el valor que dejemos en HL al finalizar la función será el que se asignará en una llamada de este tipo:+ Por contra, para devolver valores no se utiliza la pila dado que no podemos tocarla, ya que el ''RET'' trataría de volver al valor pulsado y no a la dirección de retorno. Lo que se hace en Z88DK es utilizar un determinado registro. En el caso de Z88DK, se utiliza el registro **HL**. Si la función es de tipo INT o CHAR en cuanto a devolución, el valor que dejemos en HL al finalizar la función será el que se asignará en una llamada de este tipo:
  
 <code c> <code c>
Línea 243: Línea 243:
 \\  \\ 
 ===== Ejemplos de Integracion de ASM en Z88DK ===== ===== Ejemplos de Integracion de ASM en Z88DK =====
 +
 +\\ 
 +==== Ejemplos varios ====
  
  Para aprovechar esta introducción de "uso de ASM en Z88DK", veamos el código de algunos ejemplos de funciones en C que usen ASM internamente y que muestren, entre otras cosas, la lectura de parámetros de la pila, el acceso a variables del código C, el uso de etiquetas, o la devolución de valores.  Para aprovechar esta introducción de "uso de ASM en Z88DK", veamos el código de algunos ejemplos de funciones en C que usen ASM internamente y que muestren, entre otras cosas, la lectura de parámetros de la pila, el acceso a variables del código C, el uso de etiquetas, o la devolución de valores.
  
-<code> + Como ejemplo de función sin parámetros, y que tampoco devuelve ningún valor, además del ''ClearScreen()'' que hemos visto al principio de este capítulopodemos examinar la siguiente función de "Fundido de pantalla a negro", la cual hace uso de etiquetas de Z88DKque van precedidas de un carácter punto en lugar del sufijo de los dos puntos que usamos normalmente en programas ensambladores: 
-// +  
-// Devuelve la direccion de memoria del atributo de un caracter +<code c>
-// de pantalla, de coordenadas (x,y). Usando la dirección que +
-// devuelve esta función (en HL, devuelto en la llamada), podemos +
-// leer o cambiar los atributos de dicho carácter. +
-// +
-// Llamada:   valor =  Get_LOWRES_Attrib_Address( 13 ); +
-// +
-int Get_LOWRES_Attrib_Address(char x, char y) +
-+
-#asm +
- +
-    LD HL+
-    ADD HL, SP                 ; Leemos x e y de la pila +
-    LD  D, (HL)  ; d = y +
-    INC HL                     ; Primero "y" y luego "x"+
-    INC HL                     ; Como son "char", ignoramos parte alta. +
-    LD  E, (HL)  ; e = x +
- +
-    LD H, 0 +
-    LD L, D +
-    ADD HL, HL                 ; HL = HL*2 +
-    ADD HL, HL                 ; HL = HL*4 +
-    ADD HL, HL                 ; HL = HL*8 +
-    ADD HL, HL                 ; HL = HL*16 +
-    ADD HL, HL                 ; HL = HL*32 +
-    LD D, 0 +
-    ADD HL, DE                 ; Ahora HL = (32*y)+x +
-    LD BC, 16384+6144          ; Ahora BC = offset attrib (0,0) +
-    ADD HL, BC                 ; Sumamos y devolvemos en HL +
- +
-#endasm +
-+
- +
-// +
-// Set Border +
-// Ejemplo de modificación del bordemuestra cómo leer variables +
-// globales de en ASM, añadiendo "_" delante. +
-// +
-unsigned char bordeactual; +
- +
-void BORDER( unsigned char value ) +
-+
-#asm +
-    LD HL, 2 +
-    ADD HL, SP +
-    LD A, (HL) +
-    LD C, 254 +
-    OUT (C), A +
-    LD (_bordeactual),+
- +
-    RLCA                   ; Adaptamos el borde para guardarlo +
-    RLCA                   ; en la variable del sistema BORDCR +
-    RLCA                   ; Color borde -> a zona de PAPER +
-    LD HL, 23624           ; lo almacenamos en BORDCR para que +
-    LD (HL), A             ; lo usen las rutinas de la ROM. +
-#endasm +
-} +
- +
 // //
 // Realización de un fundido de la pantalla hacia negro // Realización de un fundido de la pantalla hacia negro
Línea 313: Línea 258:
 // ellos y con un punto "." delante. // ellos y con un punto "." delante.
 // //
-void FadeScreen( void )+void FadeScreen(void)
 { {
  
 #asm #asm
-    LD B, 9                      ; Repetiremos el bucle 9 veces+    ld b, 9                      ; Repetiremos el bucle 9 veces
  
 .fadescreen_loop1 .fadescreen_loop1
-    LD HL, 16384+6144            ; Apuntamos HL a la zona de atributos +    ld hl, 16384+6144            ; Apuntamos HL a la zona de atributos 
-    LD DE, 768                   ; Iteraciones bucle+    ld de, 768                   ; Iteraciones bucle
  
-    HALT +    halt 
-    HALT                         ; Ralentizamos el efecto+    halt                         ; Ralentizamos el efecto
  
 .fadescreen_loop2 .fadescreen_loop2
-    LD A, (HL)                   ; Cogemos el atributo +    ld a, (hl)                   ; Cogemos el atributo 
-    AND 127                      ; Eliminamos el bit de flash +    and 127                      ; Eliminamos el bit de flash 
-    LD CA+    ld ca
  
-    AND 7                        ; Extraemos la tinta (AND 00000111b) +    and 7                        ; Extraemos la tinta (and 00000111b) 
-    JR Z, fadescreen_ink_zero    ; Si la tinta ya es cero, no hacemos nada+    jr z, fadescreen_ink_zero    ; Si la tinta ya es cero, no hacemos nada
  
-    DEC A                        ; Si no es cero, decrementamos su valor+    dec a                        ; Si no es cero, decrementamos su valor
  
 .fadescreen_ink_zero .fadescreen_ink_zero
  
-    EX AFAF                    ; Nos hacemos una copia de la tinta en A +    ex afaf                    ; Nos hacemos una copia de la tinta en A 
-    LD A                     ; Recuperamos el atributo +    ld a                     ; Recuperamos el atributo 
-    SRA A +    sra a 
-    SRA A                        ; Pasamos los bits de paper a 0-2 +    sra a                        ; Pasamos los bits de paper a 0-2 
-    SRA A                        ; con 3 instrucciones de desplazamiento >>+    sra a                        ; con 3 instrucciones de desplazamiento >>
  
-    AND 7                        ; Eliminamos el resto de bits +    and 7                        ; Eliminamos el resto de bits 
-    JR Z, fadescreen_paper_zero  ; Si ya es cero, no lo decrementamos+    jr z, fadescreen_paper_zero  ; Si ya es cero, no lo decrementamos
  
-    DEC A                        ; Lo decrementamos+    dec a                        ; Lo decrementamos
  
 .fadescreen_paper_zero .fadescreen_paper_zero
-    SLA A +    sla a 
-    SLA A                        ; Volvemos a color paper en bits 3-5 +    sla a                        ; Volvemos a color paper en bits 3-5 
-    SLA A                        ; Con 3 instrucciones de desplazamiento <<+    sla a                        ; Con 3 instrucciones de desplazamiento <<
  
-    LD C                     ; Guardamos el papel decrementado en A +    ld c                     ; Guardamos el papel decrementado en A 
-    EX AFAF                    ; Recuperamos A +    ex afaf                    ; Recuperamos A 
-    OR C                         ; A = A OR C   PAPEL OR TINTA+    or c                         ; A = A or c   PAPEL OR TINTA
  
-    LD (HL),                   ; Almacenamos el atributo modificado +    ld (hl),                   ; Almacenamos el atributo modificado 
-    INC HL                       ; Avanzamos puntero de memoria+    inc hl                       ; Avanzamos puntero de memoria
  
-    DEC DE +    dec de 
-    LD AD +    ld ad 
-    OR E +    or e 
-    JP NZ, fadescreen_loop2      ; Hasta que DE == 0+    jp nz, fadescreen_loop2      ; Hasta que DE == 0
  
-    DJNZ fadescreen_loop1        ; Repeticion 9 veces+    djnz fadescreen_loop1        ; Repeticion 9 veces
  
 #endasm #endasm
Línea 372: Línea 317:
 </code> </code>
  
- Un detalle a tener en cuenta, en Z88DK se soporta "EX AFAF", mientras que pasmo requiere poner la comilla del shadow-register: "EX AFAF'".+ Un detalle a tener en cuenta, para demostrar esas pequeñas diferencias entre **z80asm** y **pasmo**es que z80asm utiliza el nmemónico ''ex afaf'' mientras que pasmo requiere poner la comilla del shadow-register: ''ex afaf<nowiki>'</nowiki>''.
  
 \\  \\ 
Línea 379: Línea 324:
  
  En la anterior captura podéis ver el aspecto de uno de los pasos del fundido.  En la anterior captura podéis ver el aspecto de uno de los pasos del fundido.
 +
 + A continuación vamos a ver una rutina que calcula la dirección de un atributo de pantalla dadas las coordenadas X,Y en baja resolución (0-31, 0-23) de un "bloque" 8x8 de la pantalla. La función recibe dos parámetros de tipo ''char'' (**x** e **y**). Para recoger los valores de estos parámetros con el objetivo de operar con ellos, ignora la parte alta de cada uno de los 2 parámetros en la pila.
 + 
 + El cálculo resultante se almacena en HL para que este valor sea devuelto a C, de forma que se asigne como resultado de la llamada.
 + 
 +<code c>
 +//
 +// Devuelve la direccion de memoria del atributo de un caracter
 +// de pantalla, de coordenadas (x,y). Usando la dirección que
 +// devuelve esta función (en HL, devuelto en la llamada), podemos
 +// leer o cambiar los atributos de dicho carácter.
 +//
 +// Llamada:   valor = Get_LOWRES_Attrib_Address(1, 3);
 +//
 +int Get_LOWRES_Attrib_Address(char x, char y)
 +{
 +#asm
 +
 +    ld hl, 2
 +    add hl, sp                 ; Leemos x e y de la pila
 +    ld  d, (hl)  ; d = y
 +    inc hl                     ; Primero "y" y luego "x".
 +    inc hl                     ; Como son "char", ignoramos parte alta.
 +    ld  e, (hl)  ; e = x
 +
 +    ld h, 0
 +    ld l, d
 +    add hl, hl                 ; HL = HL*2
 +    add hl, hl                 ; HL = HL*4
 +    add hl, hl                 ; HL = HL*8
 +    add hl, hl                 ; HL = HL*16
 +    add hl, hl                 ; HL = HL*32
 +    ld d, 0
 +    add hl, de                 ; Ahora HL = (32*y)+x
 +    ld bc, 16384+6144          ; Ahora BC = offset attrib (0,0)
 +    add hl, bc                 ; Sumamos y devolvemos en HL
 +#endasm
 +}
 +</code>
 +
 +El siguiente ejemplo muestra cómo acceder a variables globales del programa en C desde nuestro código en ASM, utilizando un "subrayado" ("_") delante del nombre de la variable que queremos acceder
 +
 +<code c>
 +//
 +// Set Border
 +// Ejemplo de modificación del borde, muestra cómo leer variables
 +// globales de C en ASM, añadiendo "_" delante.
 +//
 +unsigned char bordeactual;
 +
 +void BORDER(unsigned char value)
 +{
 +#asm
 +    ld hl, 2
 +    add hl, sp
 +    ld a, (hl)             ; A = parametro de la pila (value)
 +    
 +    ld c, $FE
 +    out (c), a
 +    ld (_bordeactual), a   ; Guardamos el valor del borde en la variable
 +#endasm
 +}
 +</code>
 +
  
 \\  \\ 
 +==== Rutina descompresora de RLE en C+ASM ====
 +
 + A continuación se muestra la función de descompresión RLE escrita en ASM embebido en una función de C. El ejemplo demuestra de nuevo la lectura de parámetros (en este caso, direcciones de memoria origen, destino, y un tamaño en 16 bits), así como las etiquetas que usa Z88DK, precedidas por punto.
 +
 +<code c>
 +int RLE_decompress_ASM(unsigned char *, unsigned char *, int);
 + 
 +//---------------------------------------------------------------------------
 +// RLE_decompress_ASM( src, dst, longitud );
 +//---------------------------------------------------------------------------
 +int RLE_decompress_ASM( unsigned char *src, unsigned char *dst, int length )
 +{
 + 
 +#asm
 +    ld hl,2
 +    add hl,sp
 + 
 +    ld c, (hl)
 +    inc hl
 +    ld b, (hl)
 +    inc hl                              // BC = lenght
 + 
 +    ld e, (hl)
 +    inc hl
 +    ld d, (hl)
 +    inc hl                              // de = dst
 +    push de
 + 
 +    ld e, (hl)
 +    inc hl
 +    ld d, (hl)
 +    inc hl                              // de = src
 + 
 +    ex de, hl
 +    pop de                              // now de = dst and hl = src
 + 
 +    // After this:  HL = source, DE = destination, BC = lenght of RLE data
 + 
 +.RLE_dec_loop
 +    ld a,(hl)                          // leemos un byte
 + 
 +    cp 192
 +    jp nc, RLE_dec_compressed          // si byte > 192 = está comprimido
 +    ld (de), a                         // si no está comprimido, escribirlo
 +    inc de
 +    inc hl
 +    dec bc
 + 
 +.RLE_dec_loop2
 +    ld a,b
 +    or c
 +    jr nz, RLE_dec_loop
 +    ret                                 // miramos si hemos acabado
 + 
 +.RLE_dec_compressed                    // bucle para descompresión
 +    push bc
 +    and 63                              // cogemos el numero de repeticiones
 +    ld b, a                             // lo salvamos en B
 +    inc hl                              // y leemos otro byte (dato a repetir)
 +    ld a, (hl)
 + 
 +.RLE_dec_loop3
 +    ld (de),                          // bucle de escritura del dato B veces
 +    inc de
 +    djnz RLE_dec_loop3
 +    inc hl
 +    pop bc                              // recuperamos BC
 +    dec bc                              // Este dec bc puede hacer BC=0 si los datos
 +                                        // RLE no correctos. Cuidado (mem-smashing).
 +    dec bc
 +    jr RLE_dec_loop2
 +    ret
 + 
 +#endasm
 +}
 +</code>
 +
 ===== Paginación de memoria desde Z88DK (C)  ===== ===== Paginación de memoria desde Z88DK (C)  =====
  
Línea 388: Línea 474:
 //--- SetRAMBank ------------------------------------------------------ //--- SetRAMBank ------------------------------------------------------
 // //
-// Se mapea el banco (0-7) indicado sobre $C000.+// Se mapea el banco (0-7) indicado sobre $c000.
 // //
 // Ojo: en esta función no se deshabilitan las interrupciones y además,  // Ojo: en esta función no se deshabilitan las interrupciones y además, 
Línea 402: Línea 488:
  
     ld b, a     ld b, a
-    ld a, ($5B5C)+    ld a, ($5b5c)
     and f8h     and f8h
     or b     or b
-    ld bc, $7FFD +    ld bc, $7ffd 
-    ld ($5B5C), a+    ld ($5b5c), a
     out (c), a        ; Realizamos cambio de banco     out (c), a        ; Realizamos cambio de banco
    #endasm    #endasm
Línea 412: Línea 498:
 </code> </code>
  
-Con el anterior código podemos mapear uno de los bancos de memoria de 16KB sobre la página que va desde $C000 a $FFFF, pero debido al uso de memoria, variables y estructuras internas que hace Z88DK, debemos seguir una serie de consideraciones.+Con el anterior código podemos mapear uno de los bancos de memoria de 16KB sobre la página que va desde $c000 a $ffff, pero debido al uso de memoria, variables y estructuras internas que hace Z88DK, debemos seguir una serie de consideraciones
 + 
 +  * Todo el código en ejecución debe estar por debajo de $c000, para lo cual es recomendable definir los gráficos al final del "binario".
  
-  * Todo el código en ejecución debe estar por debajo de $C000, para lo cual es recomendable definir los gráficos al final del "binario". 
   * Es importantísimo colocar la pila en la memoria baja, mediante la siguiente instrucción (o similar, según la dirección en que queremos colocarla) al principio de nuestro programa:   * Es importantísimo colocar la pila en la memoria baja, mediante la siguiente instrucción (o similar, según la dirección en que queremos colocarla) al principio de nuestro programa:
  
 \\  \\ 
-<code>+<code c>
 /* Allocate space for the stack */ /* Allocate space for the stack */
 #pragma output STACKPTR=24500 #pragma output STACKPTR=24500
Línea 424: Línea 511:
 \\  \\ 
  
- La regla general es asegurarse de que no haya nada importante (para la ejecución de nuestro programa) en el bloque $C000 a $FFFF cuando se haga el cambio: ni la pila, ni código al que debamos acceder. Tan sólo datos que puedan ser intercambiandos de un banco a otro sin riesgo para la ejecución del mismo (por ejemplo, los datos de un nivel de juego en el que ya no estamos).+ La regla general es asegurarse de que no haya nada importante (para la ejecución de nuestro programa) en el bloque $c000 a $ffff cuando se haga el cambio: ni la pila, ni código al que debamos acceder. Tan sólo datos que puedan ser intercambiandos de un banco a otro sin riesgo para la ejecución del mismo (por ejemplo, los datos de un nivel de juego en el que ya no estamos). 
 + 
 + 
 +\\  
 +===== Enlaces ===== 
 + 
 +   * [[https://github.com/z88dk/z88dk/wiki/Tool---z80asm|Documentación de z88dk-z80asm]]
  
  
 \\  \\ 
-**[ [[.:indice|⬉]] | [[.:avanzadas2|⬅]] ]**+**[ [[.:indice|⬉]] | [[.:avanzadas2|⬅]] | [[.:anexo1|➡]] ]**
  • cursos/ensamblador/asmz88dk.1704876696.txt.gz
  • Última modificación: 10-01-2024 08:51
  • por sromero