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:lenguaje_2 [24-10-2010 07:59] – sromero | cursos:ensamblador:lenguaje_2 [26-03-2024 08:13] (actual) – [Desplazamiento de bits] sromero | ||
---|---|---|---|
Línea 2: | Línea 2: | ||
- | \\ | + | ====== Desplazamientos |
- | ===== Desplazamiento | + | |
- | En nuestra | + | En el anterior |
Toda la teoría explicada en el anterior capítulo del curso nos permitirá avanzar ahora mucho más rápido, ya que con todos los conceptos asimilados podemos ir realizando una rápida introducción a nuevas instrucciones, | Toda la teoría explicada en el anterior capítulo del curso nos permitirá avanzar ahora mucho más rápido, ya que con todos los conceptos asimilados podemos ir realizando una rápida introducción a nuevas instrucciones, | ||
Línea 11: | Línea 10: | ||
Para poder continuar con éste y posteriores capítulos del curso será imprescindible haber comprendido y asimilado todos los conocimientos de las entregas anteriores, de modo que si no es así, recomendamos al lector que relea las entregas 1, 2 y 3, y que se asegure de comprender todos los conceptos explicados. | Para poder continuar con éste y posteriores capítulos del curso será imprescindible haber comprendido y asimilado todos los conocimientos de las entregas anteriores, de modo que si no es así, recomendamos al lector que relea las entregas 1, 2 y 3, y que se asegure de comprender todos los conceptos explicados. | ||
- | En esta entrega trataremos las operaciones con bits (**NEG, CPL, BIT, SET** y **RES**), las operaciones lógicas (**AND, OR** y **XOR**) y las operaciones de desplazamiento de bits (**RR, RL, RLC, RRC, SLA, SRA** y **SRL**). | + | En esta entrega trataremos las operaciones con bits ('' |
- | No obstante, antes de pasar a hablar de las operaciones con bits finalizaremos con la descripción de las instrucciones de carga (en este caso las repetitivas), | + | No obstante, antes de pasar a hablar de las operaciones con bits finalizaremos con la descripción de las instrucciones de carga (en este caso las repetitivas), |
\\ | \\ | ||
===== Instrucciones de desplazamiento de memoria ===== | ===== Instrucciones de desplazamiento de memoria ===== | ||
+ | Ya conocemos la existencia de las instrucciones de carga ('' | ||
- | En la entrega anterior conocimos la existencia de las instrucciones de carga (LD), que nos permitían mover valores entre registros. Lo que vamos a ver a continuación es cómo podemos copiar un byte de una posición de memoria a otra, con una sóla instrucción. | + | Las 2 instrucciones que vamos a describir: |
- | + | ||
- | Las 2 instrucciones que vamos a describir: | + | |
**LDI (Load And Increment): | **LDI (Load And Increment): | ||
Línea 41: | Línea 39: | ||
< | < | ||
- | LDI: Copiar | + | ldi: Copiar |
DE=DE+1 | DE=DE+1 | ||
HL=HL+1 | HL=HL+1 | ||
BC=BC-1 | BC=BC-1 | ||
- | LDD: Copiar | + | ldd: Copiar |
DE=DE-1 | DE=DE-1 | ||
HL=HL-1 | HL=HL-1 | ||
Línea 55: | Línea 53: | ||
<code z80> | <code z80> | ||
- | LD HL, 16384 | + | ld hl, 16384 |
- | LD DE, 40000 | + | ld de, 40000 |
- | LDI | + | ldi |
</ | </ | ||
- | Qué tiene de especial LDI con respecto a realizar la copia a mano con operaciones LD? Pues que al incrementar HL y DE, lo que hace es apuntar a los siguientes elementos en memoria (HL=16385 y DE=40001), con lo cual nos facilita la posibilidad de copiar múltiples datos (no sólo 1), con varios LDI. Lo mismo ocurre con LDD, que al decrementar DE y HL los hace apuntar a los bytes anteriores de origen y destino. | + | ¿Qué |
- | Pero para facilitarnos | + | Pero para facilitarnos aún más la tarea de copia (y no tener que realizar bucles manualmente), |
< | < | ||
- | LDIR = Repetir | + | ldir = Repetir |
= Repetir: | = Repetir: | ||
- | Copiar | + | Copiar |
DE=DE+1 | DE=DE+1 | ||
HL=HL+1 | HL=HL+1 | ||
Línea 73: | Línea 71: | ||
Hasta que BC = 0 | Hasta que BC = 0 | ||
- | LDDR = Repetir | + | lddr = Repetir |
= Repetir: | = Repetir: | ||
- | Copiar | + | Copiar |
DE=DE-1 | DE=DE-1 | ||
HL=HL-1 | HL=HL-1 | ||
Línea 85: | Línea 83: | ||
<code z80> | <code z80> | ||
- | LD HL, 16384 | + | ld hl, 16384 |
- | LD DE, 50000 | + | ld de, 50000 |
- | LD BC, 6912 | + | ld bc, 6912 |
- | LDIR | + | ldir |
</ | </ | ||
- | Con el anterior programa, copiamos los 6912 bytes que hay a partir de la dirección de memoria 16384 (la pantalla) y los copiamos | + | Con el anterior programa, copiamos los 6912 bytes que hay a partir de la dirección de memoria 16384 (la pantalla) y los almacenamos |
Para demostrar esto, ensamblemos y ejecutemos el siguiente ejemplo: | Para demostrar esto, ensamblemos y ejecutemos el siguiente ejemplo: | ||
<code z80> | <code z80> | ||
- | ; Ejemplo de LDIR donde copiamos 6144 bytes de la ROM | + | ; Ejemplo de ldir donde copiamos 6144 bytes de la ROM |
; a la videomemoria. Digamos que " | ; a la videomemoria. Digamos que " | ||
- | ORG 40000 | + | |
- | LD HL, 0 ; Origen: la ROM | + | ld hl, 0 ; Origen: la ROM |
- | LD DE, 16384 ; Destino: la VideoRAM | + | ld de, 16384 ; Destino: la VideoRAM |
- | LD BC, 6144 ; toda la pantalla | + | ld bc, 6144 ; toda la pantalla |
- | | + | |
- | RET | + | ret |
</ | </ | ||
- | {{ cursos: | + | Este ejemplo copia el contenido de los primeros 6144 bytes de memoria (el inicio de la ROM) sobre la videomemoria, |
+ | |||
+ | \\ | ||
+ | {{ cursos: | ||
+ | \\ | ||
- | Os animo a que probéis | + | Al probar |
<code basic> | <code basic> | ||
- | 5 REM Ejecutar " | + | 10 REM Copiamos la ROM en la VideoRAM |
- | 10 REM Copiamos la ROM en la VideoRAM | + | 20 For i=0 TO 6144 : POKE (16384+I), (PEEK I) : NEXT I |
- | | + | 30 PAUSE 0 |
- | 30 PAUSE 0 | + | RUN |
</ | </ | ||
- | Concluimos | + | Concluímos |
< | < | ||
Línea 125: | Línea 127: | ||
| | ||
| | ||
- | LDI |- - 0 * 0 -| | + | ldi |- - 0 * 0 -| |
- | LDD |- - 0 * 0 -| | + | ldd |- - 0 * 0 -| |
- | LDDR |- - 0 0 0 -| | + | lddr |- - 0 0 0 -| |
- | LDIR |- - 0 0 0 -| | + | ldir |- - 0 0 0 -| |
</ | </ | ||
Línea 143: | Línea 145: | ||
</ | </ | ||
- | Una duda que puede asaltarle al lector es: "si tenemos LDIR para copiar bloques, ¿para qué nos puede servir LDDR? ¿No es una instrucción redundante, que podemos no necesitar nunca gracias a LDIR? Pues como bien nos apunta Miguel (devil_net) | + | Una duda que puede asaltarle al lector es: "si tenemos |
- | Supongamos que tenemos que realizar una copia de 1000 bytes desde 25000 hasta 25100. | + | Supongamos que tenemos que realizar una copia de 1000 bytes desde 25000 hasta 25100. |
<code z80> | <code z80> | ||
- | LD HL, 25000 | + | ld hl, 25000 |
- | LD DE, 25100 | + | ld de, 25100 |
- | LD BC, 1000 | + | ld bc, 1000 |
- | LDIR | + | ldir |
</ | </ | ||
- | Este código no funcionará como esperamos. Ambas zonas se superponen, con lo cual si lo ejecutamos, ocurrirá lo siguiente: | + | Este código no funcionará como esperamos: ambas zonas se superponen, con lo cual si lo ejecutamos, ocurrirá lo siguiente: |
* El byte en [25000] se copiará a [25100]. | * El byte en [25000] se copiará a [25100]. | ||
Línea 160: | Línea 162: | ||
* etc... | * etc... | ||
- | ¿Qué ocurrirá cuando LDIR llegue al byte número 25100 y lo intente copiar a 25200? Sencillamente, | + | ¿Qué ocurrirá cuando |
- | Para ello, lo correcto sería utilizar el siguiente código: | + | Para ello, lo correcto sería utilizar el siguiente código |
<code z80> | <code z80> | ||
- | LD HL, 25999 | + | ld hl, 25999 |
- | LD DE, 25099 | + | ld de, 25099 |
- | LD BC, 1000 | + | ld bc, 1000 |
- | LDDR | + | lddr |
</ | </ | ||
- | Es decir, apuntamos HL y DE al final de los 2 bloques de copia, y copiamos los bloques desde abajo, decrementando. De este modo nunca " | + | Es decir, apuntamos HL y DE al final de los 2 bloques de copia, y copiamos los bloques desde abajo, decrementando. De este modo nunca sobreescribimos con un dato ninguna |
En este ejemplo: | En este ejemplo: | ||
Línea 187: | Línea 189: | ||
===== Un ejemplo de rutina con LDIR ===== | ===== Un ejemplo de rutina con LDIR ===== | ||
- | Vamos a ver un ejemplo de rutina en ensamblador que utiliza LDIR con un propósito concreto: vamos a cargar una pantalla de carga (por ejemplo, para nuestros juegos) de forma que no aparezca poco a poco como lo haría con LOAD "" | + | Vamos a ver un ejemplo de rutina en ensamblador que utiliza |
Para eso lo que haremos será lo siguiente: | Para eso lo que haremos será lo siguiente: | ||
Línea 194: | Línea 196: | ||
<code z80> | <code z80> | ||
- | ORG 40000 | + | |
- | LD HL, 50000 ; Origen: 50000 | + | |
- | LD DE, 16384 ; Destino: la VideoRAM | + | ld hl, 50000 ; Origen: 50000 |
- | LD BC, 6912 ; toda la pantalla | + | ld de, 16384 ; Destino: la VideoRAM |
- | LDIR ; copiar | + | ld bc, 6912 ; toda la pantalla |
- | RET | + | |
+ | ret | ||
</ | </ | ||
- | La ensamblamos con pasmo a formato binario (pasmo carga.asm carga.bin) y obtenemos el siguiente código máquina (que podremos ver con hexedit, hexdump o cualquier otro editor/ | + | La ensamblamos con pasmo a formato binario ('' |
< | < | ||
- | 33, 80, 195, 017, 0, 64, 1, 0, 27, 237, 176, 201 | + | 33, 80, 195, 17, 0, 64, 1, 0, 27, 237, 176, 201 |
</ | </ | ||
Línea 214: | Línea 217: | ||
20 CLEAR 39999 | 20 CLEAR 39999 | ||
30 DATA 33, 80, 195, 017, 0, 64, 1, 0, 27, 237, 176, 201 | 30 DATA 33, 80, 195, 017, 0, 64, 1, 0, 27, 237, 176, 201 | ||
- | 40 FOR I=0 TO 11 : READ OPCODE : POKE 40000+I, OPCODE : NEXT I | + | 40 For i=0 TO 11 : READ OPCODE : POKE 40000+I, OPCODE : NEXT I |
50 LOAD "" | 50 LOAD "" | ||
60 RANDOMIZE USR 40000 | 60 RANDOMIZE USR 40000 | ||
Línea 220: | Línea 223: | ||
</ | </ | ||
- | Grabamos este cargador en cinta (o tap/tzx), y a continuación, | + | Grabamos este cargador en cinta (o tap/tzx), y a continuación, |
Ejecutamos el programa resultante en emulador o Spectrum, y veremos cómo la carga de la pantalla no puede verse en el monitor. Cuando está termina su carga, la rutina ensamblador se ejecuta y se vuelca, de golpe, a la videoram (estad atentos a la carga, porque el volcado es muy rápido). | Ejecutamos el programa resultante en emulador o Spectrum, y veremos cómo la carga de la pantalla no puede verse en el monitor. Cuando está termina su carga, la rutina ensamblador se ejecuta y se vuelca, de golpe, a la videoram (estad atentos a la carga, porque el volcado es muy rápido). | ||
- | {{ cursos: | + | \\ |
+ | {{ cursos: | ||
\\ | \\ | ||
Línea 233: | Línea 237: | ||
Antes de comenzar con las instrucciones de manipulación de registros y datos a nivel de bits vamos a ver una serie de instrucciones difíciles de encuadrar en futuros apartados y que pueden sernos de utilidad en nuestros programas: | Antes de comenzar con las instrucciones de manipulación de registros y datos a nivel de bits vamos a ver una serie de instrucciones difíciles de encuadrar en futuros apartados y que pueden sernos de utilidad en nuestros programas: | ||
- | * **SCF: Set Carry Flag** : Esta instrucción (que no admite parámetros) pone a 1 el Carry Flag del registro F. Puede sernos útil en determinadas operaciones aritméticas. | + | * '' |
+ | |||
+ | * '' | ||
- | * **CCF: Complement Carry Flag** : Esta instrucción (que tampoco admite parámetros) invierte el estado del bit de Carry Flag: si está a 1 lo pone a 0, y viceversa. Puede servirnos para poner a 0 el carry flag mediante la combinación de SCF + CCF. | + | * '' |
- | + | ||
- | * **NOP: | + | |
- | * **DAA: Decimal Adjust Accumulator** : Esta instrucción permite realizar ajustes en los resultados de operaciones con números BCD (tras operaciones aritméticas). ¿Qué son los números en formato BCD? Es una manera de representar números en los registros (o memoria) de forma que de los 8 bits de un byte se utilizan los 4 bits del 0 al 3 para representar un número del 0 al 9 (4 bits = desde 0000 hasta 1111), y los 4 bits del bit 4 al 7 para representar otro número del 0 al 9. A los 2 números BCD juntos se les llama "Byte BCD" o " | + | * '' |
Todas estas instrucciones afectan a los flags de la siguiente manera: | Todas estas instrucciones afectan a los flags de la siguiente manera: | ||
Línea 247: | Línea 251: | ||
| | ||
| | ||
- | SCF |- - 0 - 0 1| | + | scf |- - 0 - 0 1| |
- | CCF |- - ? - 0 *| | + | ccf |- - ? - 0 *| |
- | NOP |- - - - - -| | + | nop |- - - - - -| |
- | DAA |* * * P - *| | + | daa |* * * P - *| |
</ | </ | ||
- | |||
\\ | \\ | ||
Línea 262: | Línea 265: | ||
==== CPL y NEG ==== | ==== CPL y NEG ==== | ||
- | **CPL** es una instrucción que se usa para obtener el inverso del registro A. No admite parámetros (el operando destino es el registro A) y cuando la ejecutamos, se invierte el estado de cada uno de los bits de A, de forma que los unos pasan a valer cero, y los ceros, uno. | + | '' |
<code z80> | <code z80> | ||
- | LD A, %10000001 | + | ld a, %10000001 |
- | CPL ; A = %01111110 | + | cpl ; A = %01111110 |
</ | </ | ||
- | La tabla de afectación de flags de CPL es: | + | La tabla de afectación de flags de '' |
< | < | ||
Línea 275: | Línea 278: | ||
| | ||
| | ||
- | CPL |- - 1 - 1 -| | + | cpl |- - 1 - 1 -| |
</ | </ | ||
Es decir, se deja a uno el flag de Resta (N) y el de HalfCarry (H). El resto de flags no se ven afectados. | Es decir, se deja a uno el flag de Resta (N) y el de HalfCarry (H). El resto de flags no se ven afectados. | ||
- | | + | |
Por ejemplo: | Por ejemplo: | ||
<code z80> | <code z80> | ||
- | LD A, 1 | + | ld a, 1 ; A = +1 |
- | NEG ; A = -1 = %11111111 | + | neg |
</ | </ | ||
- | La tabla de afectación de flags de NEG es: | + | La tabla de afectación de flags de '' |
< | < | ||
Línea 295: | Línea 298: | ||
| | ||
| | ||
- | NEG |* * * V 1 *| | + | neg |* * * V 1 *| |
</ | </ | ||
Línea 304: | Línea 307: | ||
Las siguientes instrucciones que vamos a ver nos permitirán el manejo de cualquiera de los bits de un registro o posición de memoria: activar un bit (ponerlo a uno), desactivar un bit (ponerlo a cero), o comprobar su valor (averiguar si es cero o uno) afectando a los flags. | Las siguientes instrucciones que vamos a ver nos permitirán el manejo de cualquiera de los bits de un registro o posición de memoria: activar un bit (ponerlo a uno), desactivar un bit (ponerlo a cero), o comprobar su valor (averiguar si es cero o uno) afectando a los flags. | ||
- | Comencemos con "**SET**". Esta instrucción activa (pone a valor 1) uno de los bits de un registro o dirección de memoria. El formato de la instrucción es: | + | Comencemos con '' |
<code z80> | <code z80> | ||
- | | + | set bit, DESTINO |
</ | </ | ||
- | donde Bit es un número entre 0 (el bit menos significativo o bit 0) y 7 (el de más valor o más significativo), | + | donde Bit es un número entre 0 (el bit menos significativo o bit 0) y 7 (el de más valor o más significativo), |
<code z80> | <code z80> | ||
- | | + | set 5, a ; Activar el bit 5 del registro A |
- | SET 0, H ; Activar el bit 0 del registro H | + | set 0, h ; Activar el bit 0 del registro H |
- | SET 7, [HL] ; Activar el bit 7 del dato contenido en | + | set 7, (hl) ; Activar el bit 7 del dato contenido en |
- | ; la dirección de memoria apuntada por HL | + | |
- | SET 1, [IX+10] ; Activar el bit 1 del dato en [IX+10] | + | set 1, (ix+10) ; Activar el bit 1 del dato en (IX+10) |
</ | </ | ||
- | La instrucción | + | La instrucción |
<code z80> | <code z80> | ||
- | | + | res bit, DESTINO |
- | | + | res 0, h ; Desactivar el bit 0 del registro H |
- | RES 7, [HL] ; Desactivar el bit 7 del dato contenido en | + | res 7, (hl) ; Desactivar el bit 7 del dato contenido en |
- | ; la dirección de memoria apuntada por HL | + | |
- | RES 1, [IX-5] ; Desactivar el bit 0 del dato en [IX-5] | + | res 1, (ix-5) ; Desactivar el bit 0 del dato en (IX-5) |
</ | </ | ||
- | SET y RES no afectan a los flags, como podemos ver en su tabla de afectación de indicadores: | + | '' |
< | < | ||
Línea 337: | Línea 340: | ||
Instrucción | Instrucción | ||
| | ||
- | | + | |
- | | + | |
</ | </ | ||
- | La última instrucción de manipulación de bits individuales que veremos en este apartado es **BIT**. Esta instrucción modifica el flag de cero (Z) y deja su valor en 0 ó 1 dependiendo del valor del bit que estamos probando. Si estamos probando, por ejemplo, el bit 5 del registro A, ocurrirá lo siguiente: | + | La última instrucción de manipulación de bits individuales que veremos en este apartado es '' |
* Si el bit 5 del registro A es cero: el Flag Z se pone a 1. | * Si el bit 5 del registro A es cero: el Flag Z se pone a 1. | ||
Línea 351: | Línea 354: | ||
<code z80> | <code z80> | ||
- | | + | bit bit, DESTINO |
</ | </ | ||
- | El destino puede ser el mismo que en SET y RES: un registro, posición de memoria apuntado por HL o posición de memoria apuntada por un registro índice más un desplazamiento. | + | El destino puede ser el mismo que en '' |
Por ejemplo: | Por ejemplo: | ||
<code z80> | <code z80> | ||
- | LD A, 8 | + | ld a, 8 ; A = %00001000 |
- | BIT 7, A | + | bit 7, a ; El flag Z vale 1 |
- | | + | |
- | BIT 3, A | + | bit 3, a ; El flag Z vale 0 |
- | | + | |
- | | + | |
</ | </ | ||
- | El lector se preguntará ... ¿cuál es la utilidad de BIT? Bien, el hecho de que BIT modifique el Zero Flag de acuerdo al bit que queremos comprobar nos permitirá utilizar instrucciones condicionales para realizar muchas tareas. Por ejemplo, podemos comprobar el bit 0 de un registro (algo que nos permitiría saber si es par o impar) y en caso de que se active el flag de Zero (Si z=1, el bit 0 vale 0, luego es par), realizar un salto a una determinada línea de programa. | + | El lector se preguntará ... ¿cuál es la utilidad de '' |
Por ejemplo: | Por ejemplo: | ||
<code z80> | <code z80> | ||
- | | + | bit 0, a ; Que valor tiene el bit 0? |
- | | + | |
- | JP Z es_par | + | jp z, es_par |
- | | + | |
- | | + | |
</ | </ | ||
Línea 385: | Línea 388: | ||
Instrucción | Instrucción | ||
| | ||
- | | + | |
</ | </ | ||
Línea 396: | Línea 399: | ||
==== RLC, RRC, RL y RC ==== | ==== RLC, RRC, RL y RC ==== | ||
- | Para realizar esta tarea tenemos disponibles 2 instrucciones básicas: | + | Para realizar esta tarea tenemos disponibles 2 instrucciones básicas: |
+ | |||
+ | Pueden trabajar con registros de 8 bits, la memoria apuntada por HL, y la memoria apuntada por (IX+d). | ||
+ | |||
+ | <code z80> | ||
+ | rlc r | ||
+ | rlc (hl) | ||
+ | rlc (ix+d) | ||
+ | rlc (iy+d) | ||
+ | |||
+ | rrc r | ||
+ | rrc (hl) | ||
+ | rrc (ix+d) | ||
+ | rrc (iy+d) | ||
+ | |||
+ | rl r | ||
+ | rl (hl) | ||
+ | rl (ix+d) | ||
+ | rl (iy+d) | ||
+ | |||
+ | rr r | ||
+ | rr (hl) | ||
+ | rr (ix+d) | ||
+ | rr (iy+d) | ||
+ | </ | ||
+ | |||
+ | Este es el efecto que tienen en los datos destino: | ||
< | < | ||
- | Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 | + | Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 |
- | | + | ----------------- -> RLC -> |
- | | + | Valor a b c d e f g h b c d e f g h a |
- | Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 | + | Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 |
- | | + | ----------------- -> RRC -> |
- | | + | Valor a b c d e f g h h a b c d e f g |
</ | </ | ||
- | Así, RLC de 00000001 daría como resultado 00000010. Como la rotación es circular, todos los bits se mueven una posición a la izquierda y el bit 7 se copia en el bit 0. Asímismo, RRC de 00000001 daría como resultado 10000000, ya que el bit 0 al rotarse a la derecha (como todos los demás bits) se copia donde estaba el bit 7. Cabe destacar que el Carry Flag se vé afectado, ya que el bit 7 en RLC y el 0 en RRC también se copiará allí. | + | Así, '' |
+ | \\ | ||
{{ cursos: | {{ cursos: | ||
+ | \\ | ||
Por ejemplo, supongamos el valor 10000001 almacenado en el registro B: | Por ejemplo, supongamos el valor 10000001 almacenado en el registro B: | ||
Línea 418: | Línea 449: | ||
<code z80> | <code z80> | ||
- | LD B, %10000001 | + | ld b, %10000001 |
- | RLC B ; B = 00000011 | + | rlc b ; B = 00000011 |
- | LD B, %10000001 | + | ld b, %10000001 |
- | RRC B ; B = 11000000 | + | rrc b ; B = 11000000 |
</ | </ | ||
- | No sólo podemos rotar registros: en general el destino de la rotación podrá ser un registro, el contenido de la dirección de memoria apuntada por [HL], o bien el contenido de la memoria apuntada por un registro índice más desplazamiento ([IX+N] o [IY+N]). Más adelante veremos la tabla de afectación de flags de esta y otras instrucciones que veremos a continuación. | + | No sólo podemos rotar registros: en general el destino de la rotación podrá ser un registro, el contenido de la dirección de memoria apuntada por (HL), o bien el contenido de la memoria apuntada por un registro índice más desplazamiento ((IX+N) o [IY+N]). Más adelante veremos la tabla de afectación de flags de esta y otras instrucciones que veremos a continuación. |
- | Además de RLC y RRC (rotación circular), tenemos disponibles 2 instrucciones más que nos permiten apoyarnos en el Carry Flag del registro F como si fuera un bit más de nuestro registro, comportándose como el noveno bit (de más valor) del registro: hablamos de las instrucciones | + | Además de '' |
< | < | ||
Línea 440: | Línea 471: | ||
</ | </ | ||
- | El CarryFlag hace de bit extra: por un lado se copia al Bit 0 o al Bit 7 según estemos rotando a izquierda o a derecha, y por otra parte recibe el valor del bit 7 del bit 0 (respectivamente para RL y RR). | + | El CarryFlag hace de bit extra: por un lado se copia al Bit 0 o al Bit 7 según estemos rotando a izquierda o a derecha, y por otra parte recibe el valor del bit 7 del bit 0 (respectivamente para '' |
- | {{ cursos: | + | \\ |
+ | {{ cursos: | ||
+ | \\ | ||
Por ejemplo, supongamos el valor 10000001 almacenado en el registro B y que el carry flag estuviera a uno: | Por ejemplo, supongamos el valor 10000001 almacenado en el registro B y que el carry flag estuviera a uno: | ||
Línea 449: | Línea 482: | ||
<code z80> | <code z80> | ||
- | | + | scf |
- | LD B, %00000010 | + | ld b, %00000010 |
- | RL B ; B = 00000101 y C=0 (del bit 7) | + | rl b ; B = 00000101 y C=0 (del bit 7) |
- | | + | scf |
- | LD B, %01000001 | + | ld b, %01000001 |
- | RR B ; B = 10100000 y C=1 (del bit 0) | + | rr b ; B = 10100000 y C=1 (del bit 0) |
</ | </ | ||
- | Así pues, RLC y RRC son circulares y no utilizan el Carry Flag, mientras que RR y RL sí que lo utilizan, como un bit extra. Utilizando RR/RL 9 veces o bien RLC/RRC 8 veces sobre un mismo registro obtenemos el valor original antes de comenzar a rotar. | + | Así pues, '' |
+ | |||
+ | Utilizando | ||
Veamos la tabla de afectación de flags de estas nuevas instrucciones: | Veamos la tabla de afectación de flags de estas nuevas instrucciones: | ||
Línea 466: | Línea 501: | ||
Instrucción | Instrucción | ||
| | ||
- | | + | |
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
Línea 475: | Línea 510: | ||
\\ | \\ | ||
- | ==== RLA, RRA, RLCA y RRCA ==== | + | ==== RLCA, RRCA, RLA y RRA ==== |
+ | |||
+ | Aunque pueda parecer sorprendente (ya que podemos utilizar las 4 operaciones anteriores con el registro A como operando), | ||
+ | existen 4 instrucciones más dedicadas exclusivamente a trabajar con " | ||
+ | |||
+ | <code z80> | ||
+ | rlca | ||
+ | rrca | ||
+ | rla | ||
+ | rra | ||
+ | </ | ||
- | Aunque pueda parecer sorprendente (ya que podemos utilizar las 4 operaciones anteriores con el registro A como operando), existen 4 instrucciones más dedicadas exclusivamente a trabajar con " | + | diferencia entre estas 4 instrucciones y su versión con un espacio en medio ('' |
< | < | ||
Línea 483: | Línea 528: | ||
Instrucción | Instrucción | ||
| | ||
- | | + | |
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
Línea 494: | Línea 539: | ||
==== RLD y RRD ==== | ==== RLD y RRD ==== | ||
- | Y para acabar con las instrucciones de rotación, tenemos | + | Y para acabar con las instrucciones de rotación, tenemos |
- | Concretamente, | + | <code z80> |
+ | rld | ||
+ | rrd | ||
+ | </ | ||
- | * Leer el dato contenido | + | Ambas instrucciones trabajan con los 2 '' |
- | * Coger los 4 bits más significativos | + | |
- | * Rotar A hacia la izquierda 4 veces (copiando | + | Es decir: |
- | * Copiar | + | |
+ | * Los 8 bits de A los agrupamos en 2 nibles: '' | ||
+ | |||
+ | * Los 8 bits del contenido de (HL) los agrupamos en 2 nibles: '' | ||
+ | |||
+ | * Si colocamos A como el byte alto y (HL) como el byte bajo, nos quedan | ||
+ | |||
+ | * Las instrucciones '' | ||
+ | |||
+ | * La parte alta de A (los bits 7 a 4) se quedan inalterados. No forman parte del " | ||
+ | |||
+ | Por tanto: | ||
+ | |||
+ | * '' | ||
+ | |||
+ | * '' | ||
+ | |||
+ | \\ | ||
+ | Gráficamente, | ||
+ | |||
+ | \\ | ||
+ | {{ : | ||
+ | \\ | ||
+ | |||
+ | Y '' | ||
+ | |||
+ | \\ | ||
+ | {{ : | ||
+ | \\ | ||
- | Resumiendo, supongamos | + | Veamos ejemplos con " |
< | < | ||
- | | ||
- | | ||
- | a b c d e f g h | ||
- | [HL]: | + | < |
- | | + | |
- | s t u v w x y z | + | -------------------- |
+ | - - - - a b c d e f g h i j k l | ||
</ | </ | ||
- | **Resultado de RRD:** | + | Operaciones realizadas (en pseudocódigo): |
< | < | ||
- | Registro A: Bit 7 6 5 4 3 2 1 0 | + | rld: (HL) = ((HL)<< |
- | -------------------- | + | A = ((HL)>> |
- | e f g h s t u v | + | rrd: (HL) = ( A<<4 ) | ((HL)>> |
+ | A = ((HL)<< | ||
</ | </ | ||
- | **Resultado de RLD:** | + | |
+ | Resultado de **rld**: | ||
< | < | ||
- | Registro | + | A |
- | | + | ---- |
- | s t u v e f g h | + | A2-A1 M2-Ml => rld => A2-M2 M1-A1 |
+ | |||
+ | < | ||
+ | A: Bit 7 6 5 4 3 2 1 0 | ||
+ | | ||
+ | - - - - e f g h i j k l a b c d | ||
</ | </ | ||
- | En pseudocódigo C: | + | Resultado de **rrd**: |
< | < | ||
- | | + | |
- | RLD: A = ( [HL]<< | + | |
+ | A2-A1 M2-Ml => rrd => A2-M1 Al-M2 | ||
+ | |||
+ | <--A2--><--A1--> | ||
+ | | ||
+ | -------------------- | ||
+ | - - - - i j k l a b c d e f g h | ||
</ | </ | ||
Línea 543: | Línea 631: | ||
Instrucción | Instrucción | ||
| | ||
- | | + | |
- | | + | |
</ | </ | ||
Línea 552: | Línea 640: | ||
===== Desplazamiento de bits ===== | ===== Desplazamiento de bits ===== | ||
- | El siguiente set de instrucciones que veremos nos permitirá DESPLAZAR (SHIFT) los bits de un dato de 8 bits (por ejemplo, almacenado en un registro o en memoria) hacia la izquierda o hacia la derecha. Desplazar es parecido a rotar, sólo que el desplazamiento no es circular; es decir, los bits que salen por un lado no entran por otro, sino que entran ceros: | + | El siguiente set de instrucciones que veremos nos permitirá DESPLAZAR (SHIFT) los bits de un dato de 8 bits (por ejemplo, almacenado en un registro o en memoria) hacia la izquierda o hacia la derecha. Desplazar es parecido a rotar, sólo que el desplazamiento no es circular; es decir, los bits que salen por un lado no entran por otro, sino que entran ceros en el caso de desplazar a la izquierda, o copias del bit 7 en el caso de desplazar a la derecha: |
< | < | ||
- | | + | SLA: |
- | | + | 00010001 |
+ | | ||
entra como 0. El bit 7 se copia al Carry) | entra como 0. El bit 7 se copia al Carry) | ||
- | 10000001 ROTADO | + | SRA: |
- | (el 0 del bit 7 del resultado entra nuevo, el 1 del | + | |
- | bit 0 origen se pierde) | + | (el 0 del bit 7 del resultado entra nuevo, el 1 del |
+ | bit 0 origen se pierde, el cuarto se desplaza) | ||
</ | </ | ||
- | Las instrucciones de desplazamiento a izquierda y derecha en Z80 se llaman | + | Las instrucciones de desplazamiento a izquierda y derecha en Z80 se llaman |
<code z80> | <code z80> | ||
- | SRA operando | + | sla r |
- | SLA operando | + | sla (hl) |
+ | sla (ix+d) | ||
+ | sla (iy+d) | ||
+ | |||
+ | sra r | ||
+ | sra (hl) | ||
+ | sra (ix+d) | ||
+ | sra (iy+d) | ||
</ | </ | ||
- | Donde operando puede ser el mismo tipo de operando | + | De nuevo, el operando |
+ | ) o (IX/IY+s). Lo que realizan estas operaciones sobre el dato operando es: | ||
< | < | ||
Línea 597: | Línea 695: | ||
* En la izquierda (bit 7) se mantiene su valor anterior. | * En la izquierda (bit 7) se mantiene su valor anterior. | ||
- | Nótese pues que SLA y SRA nos permiten trabajar también con números negativos. En el caso de SLA se utiliza el carry flag para almacenar el estado del bit 7 tras la rotación (con lo cual podemos conservar el signo si sabemos dónde buscarlo). En el caso de SRA, porque el bit 7 además de desplazarse hacia la derecha se mantiene en su posición (manteniendo el signo). | + | Nótese pues que '' |
- | El hecho de desplazar un número binario una posición a izquierda o derecha tiene una curiosa propiedad: | + | El hecho de desplazar un número binario una posición a izquierda o derecha tiene una curiosa propiedad: |
- | Pensemos un poco en nuestro sistema decimal: si tenemos un determinado número y desplazamos todos los dígitos una posición a la izquierda y añadimos un cero, lo que está sucediendo es que multiplicamos el valor del número por la base: | + | Pensemos un poco en nuestro sistema decimal: si tenemos un determinado número y desplazamos todos los dígitos una posición a la izquierda y añadimos un cero, lo que está sucediendo es que multiplicamos el valor del número por la base (10): |
< | < | ||
- | 1 5 -> Desplazar y cero -> 1 5 0 | + | 1 5 -> Desplazar y añadir |
- | | + | (equivale a multiplicar por la base, es decir, por 10) |
</ | </ | ||
Línea 611: | Línea 709: | ||
< | < | ||
- | 1 5 2 -> Desplazar y cero -> 0 1 5 | + | 1 5 2 -> Desplazar y añadir |
- | | + | (equivale a dividir por la base, es decir, por 10). |
</ | </ | ||
- | En binario ocurre lo mismo: al desplazar un byte a la izquierda estamos multiplicando por 2, y al hacerlo a la derecha estamos dividiendo por 2 (siempre divisiones enteras). Veamos unos ejemplos: | + | En binario ocurre lo mismo: al desplazar un byte a la izquierda estamos multiplicando por 2 (por la base), y al hacerlo a la derecha estamos dividiendo por 2 (siempre divisiones enteras). Veamos unos ejemplos: |
< | < | ||
Línea 632: | Línea 730: | ||
Cada vez que realizamos un desplazamiento estamos multiplicando o dividiendo el resultado por dos, de forma que: | Cada vez que realizamos un desplazamiento estamos multiplicando o dividiendo el resultado por dos, de forma que: | ||
- | + | |< 60% >| | |
- | ^ Dirección Desplaz. ^ Núm. desplazamientos ^ Operación (con **SLA**) ^ | + | ^ Dirección Desplaz. ^ Núm. desplazamientos ^ Operación (con **SLA**) ^ |
| Izquierda (<<) | 1 | N = N*2 | | | Izquierda (<<) | 1 | N = N*2 | | ||
| Izquierda (<<) | 2 | N = (N*2)*2 = N*4 | | | Izquierda (<<) | 2 | N = (N*2)*2 = N*4 | | ||
Línea 642: | Línea 740: | ||
| Izquierda (<<) | 7 | N = (...) N*128 | | | Izquierda (<<) | 7 | N = (...) N*128 | | ||
- | + | |< 60% >| | |
- | ^ Dirección Desplaz. ^ Núm. desplazamientos ^ Operación (con **SRA**) ^ | + | ^ Dirección Desplaz. ^ Núm. desplazamientos ^ Operación (con **SRA**) ^ |
| Derecha (>>) | 1 | N = N/2 | | | Derecha (>>) | 1 | N = N/2 | | ||
| Derecha (>>) | 2 | N = (N/2)/2 = N/4 | | | Derecha (>>) | 2 | N = (N/2)/2 = N/4 | | ||
Línea 652: | Línea 750: | ||
| Derecha (>>) | 7 | N = (...) N/128 | | | Derecha (>>) | 7 | N = (...) N/128 | | ||
- | Así, desplazar una vez a la izquierda equivale a multiplicar por 2, 2 veces, por 4, 3 veces, por 8, etc. En resumen, desplazar un registro N veces a la izquierda equivale a multiplicarlo por 2 elevado a N. Lo mismo ocurre con la multiplicación. | + | Así, desplazar una vez a la izquierda equivale a multiplicar por 2. Desplazar |
De este modo, acabamos de descubrir una manera muy sencilla y efectiva (y rápida, muy rápida para el microprocesador) de efectuar multiplicaciones y divisiones por 2, 4, 8, 16, 32, 64 y 128. | De este modo, acabamos de descubrir una manera muy sencilla y efectiva (y rápida, muy rápida para el microprocesador) de efectuar multiplicaciones y divisiones por 2, 4, 8, 16, 32, 64 y 128. | ||
- | Existe una pequeña variante de SRA llamada | + | Existe una pequeña variante de '' |
+ | |||
+ | <code z80> | ||
+ | srl r | ||
+ | srl (hl) | ||
+ | srl (ix+d) | ||
+ | srl (iy+d) | ||
+ | </ | ||
+ | |||
+ | La diferencia es que '' | ||
< | < | ||
Línea 670: | Línea 777: | ||
* Por la izquierda entra un cero. | * Por la izquierda entra un cero. | ||
+ | \\ | ||
{{ cursos: | {{ cursos: | ||
+ | \\ | ||
+ | No existe una instrucción oficial '' | ||
- | Nuestra | + | |
+ | |||
+ | <code z80> | ||
+ | sli r | ||
+ | sli (hl) | ||
+ | sli (ix+d) | ||
+ | sli (iy+d) | ||
+ | </ | ||
+ | |||
+ | Esta instrucción funciona como '' | ||
+ | |||
+ | < | ||
+ | Bit 7 6 5 4 3 2 1 0 C 7 6 5 4 3 2 1 0 | ||
+ | ----------------- -> SLI -> ------------------------ | ||
+ | a b c d e f g h a b c d e f g h 1 | ||
+ | </ | ||
+ | |||
+ | | ||
< | < | ||
Flags | Flags | ||
Instrucción | Instrucción | ||
- | | + | ------------------------------------------------------------------------------ |
- | SLA s |* * 0 P 0 *| Shift Left Arithmetic (s=s*2) | + | SLA s |* * 0 P 0 *| Shift Left Arithmetic (s = s<< |
- | SRA s |* * 0 P 0 *| Shift Right Arithmetic (s=s/2) | + | SRA s |* * 0 P 0 *| Shift Right Arithmetic (s = s/2) |
- | SRL s |* * 0 P 0 *| Shift Right Logical (s=s>> | + | SRL s |* * 0 P 0 *| Shift Right Logical (s = s>>1) |
+ | SLI s |* * 0 P 0 *| Shift Left and Increment s = (s<< | ||
</ | </ | ||
- | Cabe destacar que gracias | + | \\ |
+ | ===== Operaciones de desplazamiento de 16 bits ===== | ||
+ | |||
+ | | ||
< | < | ||
Línea 706: | Línea 837: | ||
<code z80> | <code z80> | ||
- | SLA E | + | sla e |
- | | + | rl d |
</ | </ | ||
- | Lo que hacemos con "SLA E" | + | Lo que hacemos con '' |
Línea 723: | Línea 854: | ||
</ | </ | ||
- | Primero con SLA E rotamos la parte baja, metiendo el bit " | + | Primero con '' |
< | < | ||
- | SLA E: | + | sla e: |
| | ||
Línea 736: | Línea 867: | ||
</ | </ | ||
- | Ahora con RL D rotamos D introduciendo el bit " | + | Ahora con '' |
< | < | ||
- | RL D: | + | rl d: |
| | ||
Línea 753: | Línea 884: | ||
<code z80> | <code z80> | ||
- | SRA D | + | srl d |
- | RR E | + | rr e |
</ | </ | ||
Línea 762: | Línea 893: | ||
<code z80> | <code z80> | ||
- | LD IX, 16384 | + | ld ix, 16384 |
- | SLA (IX) | + | sla (ix) |
- | RL (IX+01H) | + | rl (ix+$01) |
</ | </ | ||
Línea 770: | Línea 901: | ||
\\ | \\ | ||
- | ===== Operaciones logicas: AND, OR y XOR ===== | + | ===== Resumen de instrucciones de rotación |
- | Para acabar con el artículo | + | ^ Instrucción ^ Acción ^ Resultado (X=valor del CF) ^ Flags afectados ^ |
+ | | **RLC ** | **Rotate Left Circular**\\ Rota el registro o dato en un bit a la izquierda.\\ El CF no entra en el registro.\\ El CF se ve afectado: CF = copia del bit 7 | < | ||
+ | ---------------------- | ||
+ | | ||
+ | ------------- | ||
+ | |* * 0 P 0 *|</ | ||
+ | | **RRC** | **Rotate Right Circular**\\ Rota el registro o dato en un bit a la derecha.\\ No interviene el CF. \\ El CF se ve afectado: CF = copia del bit 0 | < | ||
+ | ---------------------- | ||
+ | | ||
+ | ------------- | ||
+ | |* * 0 P 0 *|</ | ||
+ | | **RL** | **Rotate Left**\\ Rota el registro o dato en un bit a la izquierda.\\ El CF es un bit más (el 8) del registro.\\ Inserta el CF en el bit 0.\\ El CF se ve afectado: CF = valor del bit 7 | < | ||
+ | ---------------------- | ||
+ | | ||
+ | ------------- | ||
+ | |* * 0 P 0 *|</ | ||
+ | | **RR** | **Rotate Right**\\ Rota el registro o dato en un bit a la derecha.\\ El CF es un bit más (el 8) del registro.\\ Inserta el CF en el bit 0. \\ El CF se ve afectado: CF = valor del bit 0 | < | ||
+ | ---------------------- | ||
+ | | ||
+ | ------------- | ||
+ | |* * 0 P 0 *|</ | ||
+ | | **RLCA** | **Rotate Left Circular Accumulator**\\ Rota el registro A en un bit a la izquierda.\\ Igual que **RLC** pero con diferente afectación | ||
+ | ---------------------- | ||
+ | | ||
+ | ------------- | ||
+ | |- - 0 - 0 *|</ | ||
+ | | **RRCA** | **Rotate Right Circular Accumulator**\\ Rota el registro A en un bit a la derecha.\\ Igual que **RRC** pero con diferente afectación de Flags.\\ No interviene el CF. \\ El CF se ve afectado: CF = copia del bit 0 | < | ||
+ | ---------------------- | ||
+ | | ||
+ | ------------- | ||
+ | |- - 0 - 0 *|</ | ||
+ | | **RLA** | **Rotate Left Accumulator**\\ Rota el registro Aen un bit a la izquierda.\\ Igual que **RL** pero con diferente afectación | ||
+ | ---------------------- | ||
+ | | ||
+ | ------------- | ||
+ | |- - 0 - 0 *|</ | ||
+ | | **RRA** | **Rotate Right Accumulator**\\ Rota el registro A en un bit a la derecha.\\ Igual que **RR** pero con diferente afectación de Flags.\\ El CF es un bit más (el 8) del registro.\\ Inserta el CF en el bit 0. \\ El CF se ve afectado: CF = valor del bit 0 | < | ||
+ | ---------------------- | ||
+ | | ||
+ | ------------- | ||
+ | |- - 0 - 0 *|</ | ||
+ | | **SLA** | **Shift Left Arithmetic**\\ Desplaza el registro o dato en un bit a la izquierda.\\ Introduce un 0 por la derecha (bit 0).\\ Equivalente a multiplicar por 2.\\ El bit saliente (bit 7) se copia al CF. | < | ||
+ | ---------------------- | ||
+ | | ||
+ | ------------- | ||
+ | |* * 0 P 0 *|</ | ||
+ | | **SRA** | **Shift Right Arithmetic**\\ Desplaza el registro o dato en un bit a la derecha.\\ Deja el bit 7 sin tocar (se queda como copia del bit 6).\\ Equivalente a dividir por 2 (con signo).\\ El bit saliente (bit 0) se copia al CF. | < | ||
+ | ---------------------- | ||
+ | | ||
+ | ------------- | ||
+ | |* * 0 P 0 *|</ | ||
+ | | **SRL** | **Shift Right Logical**\\ Desplaza el registro o dato en un bit a la derecha.\\ Introduce un 0 por la izquierda (bit 7).\\ Equivalente a dividir por 2 en números positivos.\\ El bit saliente (bit 0) se copia al CF. | < | ||
+ | ---------------------- | ||
+ | | ||
+ | ------------- | ||
+ | |* * 0 P 0 *|</ | ||
+ | | **SLI** | **Shift Left And Increment**\\ Desplaza el registro o dato en un bit a la izquierda.\\ Introduce un 1 por la derecha (bit 0).\\ El bit saliente (bit 7) se copia al CF. | < | ||
+ | ---------------------- | ||
+ | | ||
+ | ------------- | ||
+ | |* * 0 P 0 *|</ | ||
+ | \\ | ||
+ | Por último, tenemos **rld** y **rrd** sin operando), que realizan una rotación de 4 bits entre A y el contenido de la memoria apuntada por HL. Esta operación existe para permitir realizar una rotación de 4 bits a derecha o izquierda del número de 12 bits cuyos bits más significativos (bits 8-11) son los 4 bits menos significativos de A, y sus 8 bits más bajos están contenidos en (HL). | ||
+ | |||
+ | \\ | ||
+ | * **rld** deja en el nibble alto de A los 4 bits más altos de (HL) y el nibble bajo los 4 bits más bajos de A. Es decir, rota 4 bits a la izquierda los 3 nibbles formados por '' | ||
+ | |||
+ | \\ | ||
+ | * **rrd** deja en el nibble alto de A los 4 bits más bajos de A y en el nibble bajo los 4 bits más altos de (HL). Es decir, rota 4 bits a la derecha los 3 nibbles formados por '' | ||
+ | |||
+ | \\ | ||
+ | < | ||
+ | < | ||
+ | | ||
+ | -------------------- | ||
+ | - - - - a b c d e f g h i j k l | ||
+ | </ | ||
+ | |||
+ | Resultado **rld**: | ||
+ | |||
+ | < | ||
+ | A | ||
+ | ---- | ||
+ | A2-A1 M2-Ml => rld => A2-M2 M1-A1 | ||
+ | |||
+ | < | ||
+ | | ||
+ | -------------------- | ||
+ | - - - - e f g h i j k l a b c d | ||
+ | </ | ||
+ | |||
+ | Resultado **rrd**: | ||
+ | |||
+ | < | ||
+ | A | ||
+ | ---- | ||
+ | A2-A1 M2-Ml => rrd => A2-M1 Al-M2 | ||
+ | |||
+ | < | ||
+ | | ||
+ | -------------------- | ||
+ | - - - - i j k l a b c d e f g h | ||
+ | </ | ||
+ | |||
+ | Con la siguiente afectación de flags: | ||
+ | |||
+ | < | ||
+ | Flags | ||
+ | Instrucción | ||
+ | | ||
+ | rld |* * 0 P 0 -| | ||
+ | rrd |* * 0 P 0 -| | ||
+ | </ | ||
+ | |||
+ | A continuación podemos ver un resumen gráfico de las diferentes instrucciones de desplazamiento obtenido del libro "// | ||
+ | |||
+ | \\ | ||
+ | {{ : | ||
+ | \\ | ||
+ | |||
+ | \\ | ||
+ | ===== Operaciones logicas: AND, OR y XOR ===== | ||
+ | |||
+ | Para acabar con el artículo de hoy vamos a ver 3 operaciones a nivel de bits: '' | ||
+ | |||
+ | |< 40% 33% 33% 33% >| | ||
^ Bit 1 ^ Bit 2 ^ Resultado AND ^ | ^ Bit 1 ^ Bit 2 ^ Resultado AND ^ | ||
| 1 | 1 | 1 | | | 1 | 1 | 1 | | ||
Línea 781: | Línea 1037: | ||
| 0 | 0 | 0 | | | 0 | 0 | 0 | | ||
+ | |< 40% 33% 33% 33% >| | ||
^ Bit 1 ^ Bit 2 ^ Resultado OR ^ | ^ Bit 1 ^ Bit 2 ^ Resultado OR ^ | ||
| 1 | 1 | 1 | | | 1 | 1 | 1 | | ||
Línea 788: | Línea 1044: | ||
| 0 | 0 | 0 | | | 0 | 0 | 0 | | ||
+ | |< 40% 33% 33% 33% >| | ||
^ Bit 1 ^ Bit 2 ^ Resultado XOR ^ | ^ Bit 1 ^ Bit 2 ^ Resultado XOR ^ | ||
| 1 | 1 | 0 | | | 1 | 1 | 0 | | ||
Línea 797: | Línea 1053: | ||
Podría decirse que: | Podría decirse que: | ||
- | | + | * **AND es la multiplicación lógica**: si cualquiera de los 2 bits es cero, el resultado es cero (0*0=0, 0*1=0, 1*0=0); dicho resultado sólo será uno cuando ambos bits sean 1 (1*1=1). |
- | * **OR es la suma lógica**: si alguno de los bits es uno, el resultado es uno (1+1=1, 0+1=1, 1+0=1). Sólo obtendremos un 0 al hacer un OR entre 2 bits cuando ambos son cero. | + | |
- | * **XOR es una operación de "O EXCLUSIVO" | + | * **OR es la suma lógica**: si alguno de los bits es uno, el resultado es uno (1+1=1, 0+1=1, 1+0=1). Sólo obtendremos un 0 al hacer un OR entre 2 bits cuando ambos son cero. |
+ | |||
+ | * **XOR es una operación de "O EXCLUSIVO" | ||
Ejemplos: | Ejemplos: | ||
< | < | ||
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
- | A la hora de realizar estas operaciones lógicas en nuestro Z80 disponemos de 3 instrucciones cuyos nombres son, como podéis imaginar, AND, OR y XOR. Las tres tienen el mismo formato: | + | A la hora de realizar estas operaciones lógicas en nuestro Z80 disponemos de 3 instrucciones cuyos nombres son, como podéis imaginar, |
<code z80> | <code z80> | ||
- | | + | and ORIGEN |
- | OR | + | or ORIGEN |
- | XOR ORIGEN | + | xor ORIGEN |
</ | </ | ||
- | Donde ORIGEN puede ser cualquier registro de 8 bits, valor inmediato de 8 bits, contenido de la memoria apuntada por [HL], o contenido de la memoria apuntada por un registro índice más un desplazamiento. El formato de la instrucción no requiere 2 operandos, ya que el registro destino sólo puede ser A. | + | Donde '' |
- | La operación CPL, que vimos al principio de esta entrega, también se considera una operación lógica, equivalente a NOT (0->1 y 1->0). | + | La operación |
- | Pero continuemos con AND, OR y XOR. Veamos algunos ejemplos de instrucciones válidas: | + | Pero continuemos con '' |
<code z80> | <code z80> | ||
- | AND B | + | and b |
- | OR C | + | or c |
- | OR [HL] | + | or (hl) |
- | XOR [IX+10] | + | xor (ix+10) |
- | AND 45 | + | and 45 |
</ | </ | ||
Línea 836: | Línea 1094: | ||
< | < | ||
- | AND ORIGEN -> A = A & ORIGEN | + | AND ORIGEN -> A = A & ORIGEN |
- | | + | OR ORIGEN -> A = A | ORIGEN |
- | XOR ORIGEN -> A = A ^ ORIGEN | + | XOR ORIGEN -> A = A ^ ORIGEN |
- | (Donde & = AND, | = OR y ^ = XOR) | + | (Donde & = AND, | = OR y ^ = XOR) |
</ | </ | ||
- | Recordemos que AND, OR y XOR son operaciones de un sólo bit, de modo que al trabajar con registros (o memoria, o valores inmediatos), | + | Recordemos que '' |
< | < | ||
Línea 854: | Línea 1112: | ||
| | ||
B7 B6 B5 B4 B3 B2 B1 B0 | B7 B6 B5 B4 B3 B2 B1 B0 | ||
- | + | ||
| | ||
- | A7 = A7 AND B7 | + | A7 = A7 and b7 |
- | A6 = A6 AND B6 | + | A6 = A6 and b6 |
- | A5 = A5 AND B5 | + | A5 = A5 and b5 |
- | A4 = A4 AND B4 | + | A4 = A4 and b4 |
- | A3 = A3 AND B3 | + | A3 = A3 and b3 |
- | A2 = A2 AND B2 | + | A2 = A2 and b2 |
- | A1 = A1 AND B1 | + | A1 = A1 and b1 |
- | A0 = A0 AND B0 | + | A0 = A0 and b0 |
</ | </ | ||
- | Es decir, se hace una operación AND entre el bit 7 de A y el bit 7 de B, y se almacena el resultado en el bit 7 de A, y lo mismo para los bits restantes. | + | Es decir, se hace una operación AND entre el bit 7 de A y el bit 7 de B, y se almacena el resultado en el bit 7 de A, y lo mismo para los bits restantes. |
- | ¿Para qué pueden servirnos estas 3 operaciones lógicas? | + | ¿Para qué pueden servirnos estas 3 operaciones lógicas? |
Podríamos hacer: | Podríamos hacer: | ||
<code z80> | <code z80> | ||
- | | + | res 7, a |
- | RES 6, A | + | res 6, a |
- | RES 5, A | + | res 5, a |
- | RES 4, A | + | res 4, a |
</ | </ | ||
- | Pero sería mucho más sencillo: | + | |
+ | Pero sería mucho más sencillo | ||
<code z80> | <code z80> | ||
- | | + | and %00001111 |
</ | </ | ||
Línea 888: | Línea 1147: | ||
< | < | ||
- | A = A AND 00001111b | + | A = A and 00001111b |
</ | </ | ||
Línea 900: | Línea 1159: | ||
</ | </ | ||
- | Como AND es la operación lógica de la multiplicación, | + | Como AND es la operación lógica de la multiplicación, |
- | De la misma forma, por ejemplo, OR nos permite fusionar 2 cuartetos de bits: | + | De la misma forma, por ejemplo, |
< | < | ||
Línea 911: | Línea 1170: | ||
</ | </ | ||
- | La afectación de flags de las 3 instrucciones es idéntica: | + | Por lo tanto, '' |
+ | |||
+ | < | ||
+ | res 0, a => and %11111110 | ||
+ | res 1, a => and %11111101 | ||
+ | res 2, a => and %11111011 | ||
+ | (...) | ||
+ | res 7, a => and $01111111 | ||
+ | |||
+ | set 0, a => or %00000001 | ||
+ | set 1, a => or %00000010 | ||
+ | set 2, a => or %00000100 | ||
+ | (...) | ||
+ | set 7, a => or %10000000 | ||
+ | </ | ||
+ | |||
+ | La desventaja de usar '' | ||
+ | |||
+ | |||
+ | La afectación de flags de las 3 instrucciones | ||
< | < | ||
Línea 917: | Línea 1195: | ||
Instrucción | Instrucción | ||
| | ||
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
- | Una curiosidad: XOR A es equivalente a "LD A, 0". Dejamos como ejercicio al lector comprobar por qué mediante algún ejemplo práctico. | + | Finalmente, la instrucción '' |
- | \\ | + | < |
- | ===== En la proxima entrega ===== | + | bit 0, a |
+ | bit 1, a | ||
+ | bit 2, a | ||
+ | (...) | ||
+ | bit 7, a | ||
+ | </ | ||
+ | |||
+ | Por ejemplo, si queremos saber si el bit 7 de A está a 1 o a 0, podemos hacer un '' | ||
+ | |||
+ | <code z80> | ||
+ | and %10000000 | ||
+ | jp nz, bit_7_activo | ||
+ | </ | ||
+ | |||
+ | Hay otros usos imaginativos de las operaciones lógicas: | ||
+ | |||
+ | Para empezar, '' | ||
+ | |||
+ | < | ||
+ | xor a | ||
+ | </ | ||
- | Veremos conceptos e instrucciones muy importantes en cualquier programa en ensamblador: | + | Por otra parte, |
Línea 952: | Línea 1250: | ||
* [[http:// | * [[http:// | ||
* [[http:// | * [[http:// | ||
- | | + | |
+ | \\ | ||
+ | **[ [[.:indice|⬉]] | [[.: | ||