cursos:ensamblador:lenguaje_3

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:lenguaje_3 [19-01-2024 06:48] sromerocursos:ensamblador:lenguaje_3 [19-01-2024 07:14] (actual) sromero
Línea 4: Línea 4:
 ===== Instrucciones condicionales, saltos y bucles ===== ===== Instrucciones condicionales, saltos y bucles =====
  
- Una vez hemos visto la mayoría de instrucciones aritméticas y lógicas, es el momento de utilizarlas como condicionales para realizar cambios en el flujo lineal de nuestro programa. En esta entrega aprenderemos a usar etiquetas y saltos mediante instrucciones condicionales (''CP'', ''jr + condición'', ''jp + condición'', etc.), lo que nos permitirá implementar en ensamblador las típicas instrucciones IF/THEN/ELSE, los GOTO de BASIC y por tanto realizar bucles.+ Una vez hemos visto la mayoría de instrucciones aritméticas y lógicas, es el momento de utilizarlas como condicionales para realizar cambios en el flujo lineal de nuestro programa. En esta entrega aprenderemos a usar etiquetas y saltos mediante instrucciones condicionales (''CP'', ''JR + condición'', ''JP + condición'', etc.), lo que nos permitirá implementar en ensamblador las típicas instrucciones IF/THEN/ELSE, los GOTO de BASIC y por tanto realizar bucles.
  
  
Línea 590: Línea 590:
 ===== Saltos condicionales con los flags ===== ===== Saltos condicionales con los flags =====
  
- Ya hemos visto la forma de realizar saltos incondicionales. A continuación veremos cómo realizar los saltos (ya sean absolutos con ''jp'' o relativos con ''jr'') //de acuerdo a unas determinadas condiciones//.+ Ya hemos visto la forma de realizar saltos incondicionales. A continuación veremos cómo realizar los saltos (ya sean absolutos con ''JP'' o relativos con ''JR'') //de acuerdo a unas determinadas condiciones//.
  
  Las instrucciones condicionales disponibles trabajan con el estado de los flags del registro F, y son:  Las instrucciones condicionales disponibles trabajan con el estado de los flags del registro F, y son:
Línea 653: Línea 653:
     nop     nop
  
-    dec a          ; Decrementamos A. +    dec a               ; Decrementamos A. 
-                   ; Cuando A sea cero, Z se pondrá a 1+                        ; Cuando A sea cero, Z se pondrá a 1
  
-    jr nz, bucle   ; Mientras Z=0, repetir el bucle+    jr nz, bucle        ; Mientras Z=0, repetir el bucle 
 + 
 +    ld a, 200           ; Aquí llegaremos cuando Z sea 1 (A valga 0)
  
-    ld a, 200      ; Aquí llegaremos cuando Z sea 1 (A valga 0) 
     ; resto del programa     ; resto del programa
 </code> </code>
Línea 679: Línea 680:
  
 <code z80> <code z80>
-    sub b              ; A = A-B +    sub b                ; A = A-B 
-    jr z, iguales      ; Si Z=1 saltar a iguales +    jr z, iguales        ; Si Z=1 saltar a iguales 
-    jr nz, distintos   ; Si Z=0 saltar a distintos+    jr nz, distintos     ; Si Z=0 saltar a distintos
  
 iguales: iguales:
Línea 689: Línea 690:
 distintos: distintos:
     ;;; (código)     ;;; (código)
-    ;jr seguir         ; Este salto no es necesario, +    ;jr seguir           ; Este salto no es necesario, 
-                       ; ya continuamos en "seguir"+                         ; ya continuamos en "seguir"
  
 seguir: seguir:
Línea 698: Línea 699:
  
 <code z80> <code z80>
-    sub b              ; A = A-B +    sub b                ; A = A-B 
-    jr nz, distintos   ; Si Z=0 saltar a distintos +    jr nz, distintos     ; Si Z=0 saltar a distintos 
-                       ; Si Z=1, seguimos aqui, ya en "iguales"+                         ; Si Z=1, seguimos aqui, ya en "iguales"
  
 iguales: iguales:
Línea 707: Línea 708:
  
 distintos: distintos:
-    ;;; (código)       ; No es neces+    ;;; (código)         ; No es necesario el salto
  
 seguir: seguir:
Línea 774: Línea 775:
  
 <code z80> <code z80>
-CP origen+cp origen
 </code> </code>
  
Línea 784: Línea 785:
  
 <code z80> <code z80>
-    sub b                  ; A = A-B +    sub b                    ; A = A-B 
-    jr z, iguales          ; Si Z=1 saltar a iguales +    jr z, iguales            ; Si Z=1 saltar a iguales 
-    jr nz, distintos       ; Si Z=0 saltar a distintos+    jr nz, distintos         ; Si Z=0 saltar a distintos
 </code> </code>
  
Línea 792: Línea 793:
  
 <code z80> <code z80>
-    cp b                   ; Flags = estado(A-B) +    cp b                     ; Flags = estado(A-B) 
-    jr z, iguales          ; Si Z=1 saltar a iguales +    jr z, iguales            ; Si Z=1 saltar a iguales 
-    jr nz, distintos       ; Si Z=0 saltar a distintos+    jr nz, distintos         ; Si Z=0 saltar a distintos
 </code> </code>
  
Línea 831: Línea 832:
     ld c, 6     ld c, 6
  
-    cp b                  ; IF A==B +    cp b                    ; IF A==B 
-    jr z, A_Igual_a_B     ; THEN goto A_Igual_a_B +    jr z, A_Igual_a_B       ; THEN goto A_Igual_a_B 
-    cp c                  ; IF A==C +    cp c                    ; IF A==C 
-    jr z, A_Igual_a_C     ; THEN goto A_Igual_a_C +    jr z, A_Igual_a_C       ; THEN goto A_Igual_a_C 
-    jp Fin                ; si no, salimos+    jp Fin                  ; si no, salimos
  
 A_Igual_a_B: A_Igual_a_B:
Línea 854: Línea 855:
    Instrucción       |S Z H P N C|    Instrucción       |S Z H P N C|
  ----------------------------------  ----------------------------------
- |CP s               |* * * V 1 *|+ |cp s               |* * * V 1 *|
 </code> </code>
  
Línea 874: Línea 875:
     cp e     cp e
     jr nz, no_iguales     jr nz, no_iguales
 +
 iguales: iguales:
     ;;; (...)     ;;; (...)
Línea 887: Línea 889:
     ;;; VALOR_NUMERICO puede ser cualquier valor de 0 a 65535     ;;; VALOR_NUMERICO puede ser cualquier valor de 0 a 65535
     ld a, h     ld a, h
-    CP VALOR_NUMERICO / 256         ; Parte alta (VALOR/256)+    cp VALOR_NUMERICO / 256         ; Parte alta (VALOR/256)
     jr nz, no_iguales     jr nz, no_iguales
     ld a, l     ld a, l
-    CP VALOR_NUMERICO % 256         ; Parte baja (Resto de VALOR/256)+    cp VALOR_NUMERICO % 256         ; Parte baja (Resto de VALOR/256)
     jr nz, no_iguales     jr nz, no_iguales
 iguales: iguales:
Línea 902: Línea 904:
 ===== Consideraciones de las condiciones ===== ===== Consideraciones de las condiciones =====
  
- A la hora de utilizar instrucciones condicionales hay que tener en cuenta que no todas las instrucciones afectan a los flags. Por ejemplo, la instrucción ''dec bc'' no pondrá el flag Z a uno cuando BC sea cero. Si intentamos montar un bucle mediante ''dec bc'' + ''JR NZ'', nunca saldremos del mismo, ya que ''dec bc'' no afecta al flag de zero.+ A la hora de utilizar instrucciones condicionales hay que tener en cuenta que no todas las instrucciones afectan a los flags. Por ejemplo, la instrucción ''dec bc'' no pondrá el flag Z a uno cuando BC sea cero. Si intentamos montar un bucle mediante ''dec bc'' + ''jr nz'', nunca saldremos del mismo, ya que ''dec bc'' no afecta al flag de zero.
  
 <code z80> <code z80>
Línea 931: Línea 933:
 </code> </code>
  
- Más detalles sobre los saltos condicionales: esta vez respecto al signo. Las condiciones P y M (''jp P'', ''jp M'') nos permitirán realizar saltos según el estado del bit de signo. Resultará especialmente útil después de operaciones aritméticas.+ Más detalles sobre los saltos condicionales: esta vez respecto al signo. Las condiciones P y M (''jp p'', ''jp m'') nos permitirán realizar saltos según el estado del bit de signo. Resultará especialmente útil después de operaciones aritméticas.
  
  Los saltos por Paridad/Overflow (jp po, jp PE) permitirán realizar saltos en función de la paridad cuando la última operación realizada modifique ese bit de F según la paridad del resultado. La misma condición nos servirá para desbordamientos si la última operación que afecta a flags realizada modifica este bit con respecto a dicha condición.  Los saltos por Paridad/Overflow (jp po, jp PE) permitirán realizar saltos en función de la paridad cuando la última operación realizada modifique ese bit de F según la paridad del resultado. La misma condición nos servirá para desbordamientos si la última operación que afecta a flags realizada modifica este bit con respecto a dicha condición.
  
- ¿Qué quiere decir esto? Que si, por ejemplo, realizamos una suma o resta, ''jp PO'' y ''jp PE'' responderán en función de si ha habido un desbordamiento o no y no en función de la paridad, porque las sumas y restas actualizan dicho flag según los desbordamientos, no según la paridad.+ ¿Qué quiere decir esto? Que si, por ejemplo, realizamos una suma o resta, ''jp po'' y ''jp pe'' responderán en función de si ha habido un desbordamiento o no y no en función de la paridad, porque las sumas y restas actualizan dicho flag según los desbordamientos, no según la paridad.
  
 \\  \\ 
Línea 955: Línea 957:
     ld a, 'y'     ld a, 'y'
     jr continuar         ; Podemos evitarnos este salto     jr continuar         ; Podemos evitarnos este salto
 +
 es_cero: es_cero:
     ld a, 'x'     ld a, 'x'
 +
 continuar: continuar:
     ;;; Aqui A vale 'x' o 'y' segun el valor de 0     ;;; Aqui A vale 'x' o 'y' segun el valor de 0
Línea 968: Línea 972:
     jr z, es_cero     jr z, es_cero
     ld a, 'y'     ld a, 'y'
 +
 es_cero: es_cero:
     ;;; Aqui A vale 'x' o 'y' segun el valor de 0     ;;; Aqui A vale 'x' o 'y' segun el valor de 0
Línea 1077: Línea 1082:
  No obstante, nos obliga a copiar B en A para poder hacer el ''CP''.  No obstante, nos obliga a copiar B en A para poder hacer el ''CP''.
  
- Gracias a los flags del Z80 hay una forma mucho más eficiente de repetir N veces una porción de código. Esta forma es, en lugar de contar desde 1 hasta N, hacerlo desde N hasta 0, usando ''DEC''/''jr'' o ''DJNZ''. La cuenta atrás hasta 0 activará el ZF (Flag de Zero) sin necesidad de usar CP:+ Gracias a los flags del Z80 hay una forma mucho más eficiente de repetir N veces una porción de código. Esta forma es, en lugar de contar desde 1 hasta N, hacerlo desde N hasta 0, usando ''DEC''/''JR'' o ''DJNZ''. La cuenta atrás hasta 0 activará el ZF (Flag de Zero) sin necesidad de usar CP:
  
- Usando ''DEC''/''jr'' o ''DEC''/''jp'':+ Usando ''DEC''/''JR'' o ''DEC''/''JP'':
  
 <code z80> <code z80>
Línea 1126: Línea 1131:
 Hemos visto cómo podemos ejecutar código un número determinado de veces en base a contar desde un determinado valor hasta 0, utilizando los flags para saber cuándo debe de finalizar el bucle y continuar la ejecución del programa. Hemos visto cómo podemos ejecutar código un número determinado de veces en base a contar desde un determinado valor hasta 0, utilizando los flags para saber cuándo debe de finalizar el bucle y continuar la ejecución del programa.
  
-En ciertas ocasiones concretas los desarrolladores evitan utilizar bucles y lo que hacen es **repetir** el mismo bloque de instrucciones N veces, para evitar el salto (''DJNZ'', o ''CP''/''DEC'' + ''jr'' o ''jp'') y su coste en t-estados.+En ciertas ocasiones concretas los desarrolladores evitan utilizar bucles y lo que hacen es **repetir** el mismo bloque de instrucciones N veces, para evitar el salto (''DJNZ'', o ''CP''/''DEC'' + ''JR'' o ''JP'') y su coste en t-estados.
  
 Este proceso se llama **desenrollar el bucle** y consiste en, básicamente, no usar un bucle, sino sustituirlo por N repeticiones del código en el fichero de texto que después ensamblaremos. Para hacer algo así evidentemente necesitamos conocer el número de repeticiones del código. Este proceso se llama **desenrollar el bucle** y consiste en, básicamente, no usar un bucle, sino sustituirlo por N repeticiones del código en el fichero de texto que después ensamblaremos. Para hacer algo así evidentemente necesitamos conocer el número de repeticiones del código.
Línea 1219: Línea 1224:
    * La instrucción para cargar B con un valor ya no es necesaria (-2 bytes y 7 ciclos de reloj menos de ejecución del programa).    * La instrucción para cargar B con un valor ya no es necesaria (-2 bytes y 7 ciclos de reloj menos de ejecución del programa).
  
-   * El ''DJNZ'' tampoco es necesario ya (-2 bytes y 13 ciclos de reloj menos en cada iteración del bucle y 7 menos al acabar el bucle, es decir, ¡98 t-estados menos!).+   * El ''djnz'' tampoco es necesario ya (-2 bytes y 13 ciclos de reloj menos en cada iteración del bucle y 7 menos al acabar el bucle, es decir, ¡98 t-estados menos!).
  
    * El último ''inc hl'' ya no es necesario, mientras que en el caso del bucle se habría ejecutado (-1 byte y 6 ciclos de reloj menos).    * El último ''inc hl'' ya no es necesario, mientras que en el caso del bucle se habría ejecutado (-1 byte y 6 ciclos de reloj menos).
Línea 1298: Línea 1303:
  
 <code> <code>
-cpi =     CP [HL]+cpi =     cp (hl)
           inc hl           inc hl
           dec bc           dec bc
Línea 1309: Línea 1314:
  
 <code> <code>
-cpd =     CP [HL]+cpd =     cp (hl)
           dec hl           dec hl
           dec bc           dec bc
 </code> </code>
  
- Y el pequeño matiz: así como CP [HL] afecta al indicador C de Carry, //cpi y cpd//, aunque realizan esa operación intermedia, //no lo afectan//.+ Y el pequeño matiz: así como ''cp (hl)'' afecta al indicador C de Carry, //cpi y cpd//, aunque realizan esa operación intermedia, //no lo afectan//.
  
  Las instrucciones ''CPIR'' y ''CPDR'' son equivalentes a ''CPI'' y ''CPD'', pero ejecutándose múltiples veces: hasta que BC sea cero o bien se encuentre en la posición de memoria apuntada por HL un valor numérico igual al que contiene el registro A. Literalmente, //es una instrucción de búsqueda//: buscamos hacia adelante (''CPIR'') o hacia atrás (''CPDR''), desde una posición de memoria inicial (HL), un valor (A), entre dicha posición inicial (HL) y una posición final (HL+BC o HL-BC para ''CPIR'' y ''CPDR'').  Las instrucciones ''CPIR'' y ''CPDR'' son equivalentes a ''CPI'' y ''CPD'', pero ejecutándose múltiples veces: hasta que BC sea cero o bien se encuentre en la posición de memoria apuntada por HL un valor numérico igual al que contiene el registro A. Literalmente, //es una instrucción de búsqueda//: buscamos hacia adelante (''CPIR'') o hacia atrás (''CPDR''), desde una posición de memoria inicial (HL), un valor (A), entre dicha posición inicial (HL) y una posición final (HL+BC o HL-BC para ''CPIR'' y ''CPDR'').
Línea 1392: Línea 1397:
     ORG 50000     ORG 50000
  
-    ld hl, texto     ; Inicio de la busqueda +    ld hl, texto        ; Inicio de la busqueda 
-    ld a, 'X'        ; Carácter (byte) a buscar +    ld a, 'X'           ; Carácter (byte) a buscar 
-    ld bc, 100       ; Número de bytes donde buscar +    ld bc, 100          ; Número de bytes donde buscar 
-    cpir             ; Realizamos la búsqueda+    cpir                ; Realizamos la búsqueda
  
-    jp nz, No_Hay    ; Si no encontramos el caracter buscado +    jp nz, No_Hay       ; Si no encontramos el caracter buscado 
-                     ; el flag de Z estará a cero.+                        ; el flag de Z estará a cero.
  
-                     ; Si seguimos por aquí es que se encontró +                        ; Si seguimos por aquí es que se encontró 
-    dec hl           ; Decrementamos HL para apuntar al byte +    dec hl              ; Decrementamos HL para apuntar al byte 
-                     ; encontrado en memoria.+                        ; encontrado en memoria.
  
     ld bc, texto     ld bc, texto
     scf     scf
-    ccf              ; Ponemos el carry flag a 0 (scf+ccf) +    ccf                 ; Ponemos el carry flag a 0 (scf+ccf) 
-    sbc hl, bc       ; HL = HL - BC +    sbc hl, bc          ; HL = HL - BC 
-                     ;    = (posicion encontrada) - (inicio cadena) +                        ;    = (posicion encontrada) - (inicio cadena) 
-                     ;    = posición de 'X' dentro de la cadena.+                        ;    = posición de 'X' dentro de la cadena.
  
     ld b, h     ld b, h
-    ld c, l          ; BC = HL+    ld c, l             ; BC = HL
  
-    ret              ; Volvemos a basic con el resultado en BC+    ret                 ; Volvemos a basic con el resultado en BC
  
 No_Hay: No_Hay:
Línea 1438: Línea 1443:
    * Hacemos HL = posición de memoria donde empieza la cadena.    * Hacemos HL = posición de memoria donde empieza la cadena.
    * Hacemos A = 'X'.    * Hacemos A = 'X'.
-   * Ejecutamos un ''CPIR''+   * Ejecutamos un ''cpir''
    * En HL obtendremos la posición absoluta + 1 donde se encuentra el carácter 'X' encontrado (o FFFFh si no se encuentra). Exactamente 50041.    * En HL obtendremos la posición absoluta + 1 donde se encuentra el carácter 'X' encontrado (o FFFFh si no se encuentra). Exactamente 50041.
    * Decrementamos HL para que apunte a la 'X' (50040).    * Decrementamos HL para que apunte a la 'X' (50040).
Línea 1444: Línea 1449:
    * Volvemos al BASIC con el resultado en BC. El ''PRINT USR 50000'' imprimirá dicho valor de retorno.    * Volvemos al BASIC con el resultado en BC. El ''PRINT USR 50000'' imprimirá dicho valor de retorno.
  
- Nótese que el bloque desde ''SCF'' hasta ''ld c, l'' tiene como objetivo ser el equivalente a ''HL = HL - BC'', y se tiene que hacer de esta forma porque no existe ''sub hl, bc'' ni ''ld bc, hl'':+ Nótese que el bloque desde ''scf'' hasta ''ld c, l'' tiene como objetivo ser el equivalente a ''HL = HL - BC'', y se tiene que hacer de esta forma porque no existe ''sub hl, bc'' ni ''ld bc, hl'':
  
 <code> <code>
  • cursos/ensamblador/lenguaje_3.1705646918.txt.gz
  • Última modificación: 19-01-2024 06:48
  • por sromero