Diferencias
Muestra las diferencias entre dos versiones de la página.
Ambos lados, revisión anterior Revisión previa Próxima revisión | Revisión previa | ||
cursos:ensamblador:habituales [24-01-2024 11:54] – sromero | cursos:ensamblador:habituales [02-02-2024 18:39] (actual) – [Comparaciones de 8 bits] sromero | ||
---|---|---|---|
Línea 1: | Línea 1: | ||
- | |||
====== Operaciones habituales ====== | ====== Operaciones habituales ====== | ||
Línea 6: | Línea 5: | ||
Por ejemplo, las comparaciones de 8 y 16 bits son básicas y tendremos que usarlas decenas (sino cientos) de veces a lo largo de nuestro programa para implementar la lógica no sólo de bucles sino también de condiciones como "// | Por ejemplo, las comparaciones de 8 y 16 bits son básicas y tendremos que usarlas decenas (sino cientos) de veces a lo largo de nuestro programa para implementar la lógica no sólo de bucles sino también de condiciones como "// | ||
- | También veremos construcciones u optimizaciones habituales en ensamblador, | + | También veremos construcciones u optimizaciones habituales en ensamblador, |
Línea 12: | Línea 11: | ||
===== Comparaciones de 8 bits ===== | ===== Comparaciones de 8 bits ===== | ||
- | Las comparaciones de valores 8 bits son situaciones extremadamente habituales en nuestros programas. En múltiples ocasiones tendremos que verificar si el valor de un determinado registro es igual, distingo, menor, mayor, menor igual o mayor igual que el valor de otro registro o que un valor inmediato. | + | Las comparaciones de valores |
Como ya vimos en el capítulo dedicado a las instrucciones básicas, las comparaciones se basan en **restas** ('' | Como ya vimos en el capítulo dedicado a las instrucciones básicas, las comparaciones se basan en **restas** ('' | ||
Línea 37: | Línea 36: | ||
\\ | \\ | ||
- | Así, tras un '' | + | Así, tras un '' |
\\ | \\ | ||
- | * **A == N** (A igual n): Si se activa **ZF** (flag de Zero), es porque el resultado de la resta es 0, es decir, A < n son iguales. | + | * **A == N** (A igual n): Si se activa **ZF** (flag de Zero), es porque el resultado de la resta es 0, es decir, A y N son iguales. |
* **A != n** (A distinto de n): Si no se activa **ZF** (flag de Zero), es porque el resultado de la resta no es 0, es decir A y n son diferentes. | * **A != n** (A distinto de n): Si no se activa **ZF** (flag de Zero), es porque el resultado de la resta no es 0, es decir A y n son diferentes. | ||
- | * **A < n** (A menor que n): Si no era igual, y se activa **CF** (flag de Carry), es porque el resultado de la resta es negativo, es decir, A es menor que B. | + | * **A < n** (A menor que n): Si no era igual, y se activa **CF** (flag de Carry), es porque el resultado de la resta es negativo, es decir, A es menor que B. No es necesario comprobar primero el valor del ZF, si CF está activo, entonces A es menor que N, siempre. |
* **A > n** (mayor que): Si no era igual, y no se activa **CF** (flag de Carry), es porque el resultado de la resta es positivo, es decir, A es mayor que B. | * **A > n** (mayor que): Si no era igual, y no se activa **CF** (flag de Carry), es porque el resultado de la resta es positivo, es decir, A es mayor que B. | ||
Línea 51: | Línea 50: | ||
\\ | \\ | ||
- | La igualdad requiere sólo verificar el flag de Zero, mientras | + | La igualdad requiere sólo verificar el flag de Zero, la comparación " |
< | < | ||
Línea 58: | Línea 57: | ||
= | = | ||
!= => Z=0 | != => Z=0 | ||
- | < | + | < |
> | > | ||
<= => Z=1, C=1 | <= => Z=1, C=1 | ||
Línea 75: | Línea 74: | ||
</ | </ | ||
- | Veamos ejemplos de código, comparando A con 50 (podemos usar para los saltos tanto '' | + | Veamos ejemplos de código, comparando A con 50 (podemos usar para los saltos tanto '' |
- | Para comparar si **A == 50**, simplemente comprobamos ZF: | + | Para comparar si **A == 50** o si **A != 50**, simplemente comprobamos ZF: |
<code z80> | <code z80> | ||
Línea 90: | Línea 89: | ||
; igual a 50 | ; igual a 50 | ||
; (... codigo para caso a == 50 ...) | ; (... codigo para caso a == 50 ...) | ||
- | |||
- | fin_comparacion: | ||
- | </ | ||
- | |||
- | Para comparar si **A != 50**, de nuevo comprobamos ZF: | ||
- | |||
- | <code z80> | ||
- | cp 50 | ||
- | jr z, distinto_de_50 | ||
- | |||
- | ; igual a 50 | ||
- | ; (... codigo para caso a == 50 ...) | ||
- | jr fin_comparacion | ||
- | |||
- | distinto_a_50: | ||
- | ; (... codigo para caso a != 50 ...) | ||
fin_comparacion: | fin_comparacion: | ||
Línea 145: | Línea 128: | ||
<code z80> | <code z80> | ||
cp 50 | cp 50 | ||
- | jp nc, menor_o_igual_que_50 | + | jp c, menor_que_50 |
- | jp nz, menor_o_igual_que_50 | + | jp nz, mayor_que_50 |
; Ahora o es ' | ; Ahora o es ' | ||
- | ; mayor o igual que 50 | + | ; igual que 50 |
+ | ; (... codigo para caso a == 50 ...) | ||
+ | |||
+ | jr fin_comparacion | ||
+ | |||
+ | mayor_que_50: | ||
; (... codigo para caso a > 50 ...) | ; (... codigo para caso a > 50 ...) | ||
jr fin_comparacion | jr fin_comparacion | ||
- | menor_o_igual_que_50: | + | menor_que_50: |
- | ; (... codigo para caso a <= 50 ...) | + | ; (... codigo para caso a < 50 ...) |
fin_comparacion: | fin_comparacion: | ||
</ | </ | ||
- | Otra opción | + | Como se puede ver en el código, tenemos que añadir un '' |
<code z80> | <code z80> | ||
- | ; comparamos con valor +1 y entonces sí que podemos hacer NC | + | ; comparamos con valor +1 y entonces sí que podemos hacer JP NC |
; y comprobar " | ; y comprobar " | ||
; saltaría con >=51 es decir, con >50 | ; saltaría con >=51 es decir, con >50 | ||
- | cp 51 | + | cp 50+1 |
- | jp nc, mayor_O_IGUAL_que_51 = mayor_que_50 | + | jp nc, mayor_que_50 |
- | ; aqui A < 50 | + | ; aqui A <= 50 |
jr fin_comparacion | jr fin_comparacion | ||
mayor_que_50: | mayor_que_50: | ||
- | | + | |
+ | |||
+ | fin_comparacion: | ||
+ | </ | ||
+ | |||
+ | <code z80> | ||
+ | ; comparamos con valor -1 y entonces sí que podemos hacer JP C | ||
+ | ; y comprobar " | ||
+ | ; saltaría con <=49 es decir, con <50 | ||
+ | cp 50-1 | ||
+ | jp c, menor_igual_que_49 | ||
+ | |||
+ | ; aqui A > 50 | ||
+ | jr fin_comparacion | ||
+ | |||
+ | menor_igual_que_49: | ||
+ | ; aqui A <= 49 y por tanto A < 50 | ||
fin_comparacion: | fin_comparacion: | ||
Línea 290: | Línea 294: | ||
* Si **HL > DE** => Z=0 y C=0. | * Si **HL > DE** => Z=0 y C=0. | ||
- | De la misma forma, podemos realizar rutinas específicas para compara | + | De la misma forma, podemos realizar rutinas específicas para comparar |
<code z80> | <code z80> | ||
Línea 352: | Línea 356: | ||
\\ | \\ | ||
**Resetear el Carry Flag** | **Resetear el Carry Flag** | ||
+ | |||
+ | No existe una instrucción para poner el Carry Flag a 0, aunque sí una para ponerlo a 1, y una para complementarlo. Esto permite ponerlo a 0 usando 2 instrucciones (2 bytes) y 8 t-estados. Sin embargo, es más fácil hacerlo usando cualquier operación lógica que lo resetee, como '' | ||
<code z80> | <code z80> | ||
Línea 362: | Línea 368: | ||
</ | </ | ||
- | Utilizando instrucciones lógicas, de resta o de comparación, | + | Utilizando instrucciones lógicas, de resta o de comparación, así como instrucciones específicas del Z80, podemos usar las siguientes construcciones: |
<code z80> | <code z80> | ||
- | or a ; ZF=1 si A es 0, y además limpia el CF | + | |
- | and a | + | |
- | xor a | + | and a |
- | cp a ; Siempre establece | + | xor a |
- | sub a | + | cp a ; Hace ZF=1 y pone CF=0 |
+ | sub a | ||
+ | or 1 ; Modifica A, pone CF=0 y altera ZF y S como corresponda. | ||
+ | or %10000000 | ||
+ | |||
+ | xor a ; Pone el flag P/O=1 | ||
+ | sub a ; Pone el flag P/O=0 | ||
</ | </ | ||
Línea 375: | Línea 387: | ||
<code z80> | <code z80> | ||
- | sub 0 | + | |
- | | + | add a, 0 |
- | | + | cp 0 |
</ | </ | ||
+ | |||
+ | \\ | ||
+ | **Complementar A** | ||
+ | |||
+ | <code z80> | ||
+ | ; No óptimo: | ||
+ | xor %11111111 | ||
+ | |||
+ | ; Óptimo: | ||
+ | cpl ; 1 byte, 4 t-estados | ||
+ | </ | ||
+ | |||
\\ | \\ | ||
Línea 414: | Línea 438: | ||
; Si CF era 0 hace A=0 | ; Si CF era 0 hace A=0 | ||
</ | </ | ||
+ | |||
\\ | \\ | ||
- | **Realizar un '' | + | **Asignar valores directos a memoria** |
<code z80> | <code z80> | ||
- | | + | |
- | sub Parte_baja_Registro | + | ld a, $40 |
- | ld Parte_baja_Registro, a | + | |
- | | + | |
- | | + | |
- | ld Parte_alta_Registro, a | + | ld (hl), $40 ; 2 bytes y 10 t-estados |
</ | </ | ||
- | Por ejemplo, para simular '' | + | \\ |
+ | **Asignar dos valores a la parte alta y baja de un registro** | ||
<code z80> | <code z80> | ||
+ | ; No óptimo: | ||
+ | ld b, $20 | ||
+ | ld c, $30 ; 4 bytes, 14 t-estados | ||
+ | |||
+ | ; Opción óptima 1: | ||
+ | ld bc, $2030 ; 3 bytes, 10 t-estados | ||
+ | |||
+ | ; Opción óptima 2: | ||
+ | ld bc, | ||
+ | |||
+ | ; 3 bytes, 10 t-estados | ||
+ | </ | ||
+ | |||
+ | \\ | ||
+ | **Dividir con '' | ||
+ | |||
+ | Para dividir A por potencias de dos, como veremos en el capítulo de operaciones aritméticas, | ||
+ | |||
+ | En su lugar podemos usar '' | ||
+ | |||
+ | <code z80> | ||
+ | ; No óptimo: | ||
+ | srl a | ||
+ | srl a | ||
+ | srl a ; 6 bytes y 24 t-estados | ||
+ | |||
+ | ; Óptimo: | ||
+ | rrca | ||
+ | rrca | ||
+ | rrca | ||
+ | and %00011111 | ||
+ | ; insertados por los 3 rrca. | ||
+ | ; 4 bytes y 15 t-estados | ||
+ | </ | ||
+ | |||
+ | \\ | ||
+ | **Establecer varias variables consecutivas a un valor** | ||
+ | |||
+ | Supongamos que tenemos varias variables consecutivas en memoria y queremos asignarles un mismo valor a todas ellas, por ejemplo 0. Para eso, lo mejor establecer la primera de las variables y usar HL, DE y '' | ||
+ | |||
+ | <code z80> | ||
+ | variable1 | ||
+ | variable2 | ||
+ | variable3 | ||
+ | variable4 | ||
+ | variable5 | ||
+ | |||
+ | ; No óptimo: | ||
xor a | xor a | ||
- | | + | |
- | ld l, a | + | ld (variable2), a |
- | | + | |
- | | + | ld (variable4), a |
- | ld h, a | + | |
+ | |||
+ | ; Óptimo: | ||
+ | ld hl, variable1 | ||
+ | ld de, variable1+1 | ||
+ | xor a | ||
+ | ld (hl), a ; Establecemos el valor del primero | ||
+ | ld bc, 4 ; Numero de elementos a asignar - 1 | ||
+ | ldir ; Hacemos el resto copiando BC veces | ||
+ | ; el valor del primero, en adelante. | ||
</ | </ | ||
+ | |||
+ | Con este código, ahorramos 3 bytes por cada '' | ||
+ | |||
+ | \\ | ||
+ | **Incrementar el valor de una variable y dejarla en A** | ||
+ | |||
+ | <code z80> | ||
+ | ; No óptimo | ||
+ | ld a, (variable) | ||
+ | inc a | ||
+ | ld (variable), a | ||
+ | |||
+ | ; Óptimo: | ||
+ | ld hl, variable | ||
+ | inc (hl) | ||
+ | ld a, (hl) ; Ahorramos 2 bytes y 2 t-estados | ||
+ | </ | ||
+ | |||
+ | \\ | ||
+ | **Copiar (HL) => (DE) sin modificar BC** | ||
+ | |||
+ | Como ya sabemos, **LDI** copia el valor de la memoria apuntado por HL en la posición de memoria apuntada por DE, y decrementa BC. | ||
+ | |||
+ | Cuando no queremos que BC sea modificado, tendemos a hacer lo siguiente: | ||
+ | |||
+ | <code z80> | ||
+ | ; No óptimo | ||
+ | ld a, (hl) | ||
+ | ld (de), A | ||
+ | inc hl | ||
+ | inc de | ||
+ | </ | ||
+ | |||
+ | Sin embargo, como '' | ||
+ | |||
+ | <code z80> | ||
+ | ; Óptimo: | ||
+ | ldi ; (HL) => (DE) y se decrementa BC | ||
+ | inc bc ; recuperamos el BC decrementado | ||
+ | </ | ||
+ | |||
+ | |||
+ | \\ | ||
+ | **Testear el estado del bit 0 o el 7 de A para un salto** | ||
+ | |||
+ | Si queremos comprobar el estado del bit 0 o el bit 7 de A para un salto, lo normal es hacer lo siguiente: | ||
+ | |||
+ | <code z80> | ||
+ | ; Con bit 0: | ||
+ | bit 0, a ; 2 bytes, 8 t-estados | ||
+ | jr z, destino | ||
+ | |||
+ | ; Con bit 7: | ||
+ | bit 7, a ; 2 bytes, 8 t-estados | ||
+ | call z, destino | ||
+ | </ | ||
+ | |||
+ | En su lugar podemos utilizar operaciones de desplazamiento de dichos bits hacia el Carry Flag (1 byte y 4 t-estados) y saltar según el valor de C: | ||
+ | |||
+ | <code z80> | ||
+ | ; Con bit 0: | ||
+ | rra ; 1 byte, 4 t-estados | ||
+ | jr c, destino | ||
+ | |||
+ | ; Con bit 7: | ||
+ | rla ; 1 byte, 4 t-estados | ||
+ | call c, destino | ||
+ | </ | ||
+ | |||
\\ | \\ |