cursos:ensamblador:lenguaje_2

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_2 [07-08-2007 06:57] sromerocursos:ensamblador:lenguaje_2 [26-03-2024 08:13] (actual) – [Desplazamiento de bits] sromero
Línea 2: Línea 2:
  
  
 +====== Desplazamientos de memoria, manipulación de bits y operaciones lógicas ======
  
-===== DESPLAZAMIENTO DE MEMORIA, MANIPULACIÓN DE BITS Y OPERACIONES LÓGICAS ===== +En el anterior capítulo comenzamos nuestra andadura en el lenguaje ensamblador del Z80 por medio de las instrucciones de carga (**LD**), operaciones aritméticas (''ADD''''ADC''''SUB''''SBC''''INC''''DEC'') y de intercambio (''EXX'' ''EX''). Mientras se introducían las diferentes instrucciones, mostramos la manera de emplear los registros y cómo los resultados podían afectar a los flags del registro F, mediante las "tablas de afectación de flags".
- +
-En nuestra anterior entrega comenzamos nuestra andadura en el lenguaje ensamblador del Z80 por medio de las instrucciones de carga (**LD**), operaciones aritméticas (**ADD, ADC, SUB, SBC, INC, DEC**) y de intercambio (**EXX** **EX**). Mientras se introducían las diferentes instrucciones, os mostramos la manera de emplear los registros y cómo los resultados podían afectar a los flags del registro F, mediante las "tablas de afectación de flags".+
  
 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, bastando ahora con una simple descripción de cada una de ellas. Las tablas de afectación de flags y comentarios sobre los operandos permitidos (o prohibidos) para cada una de ellas completarán la formación necesaria. 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, bastando ahora con una simple descripción de cada una de ellas. Las tablas de afectación de flags y comentarios sobre los operandos permitidos (o prohibidos) para cada una de ellas completarán la formación necesaria.
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** **RES**), las operaciones lógicas (**AND, OR** **XOR**) y las operaciones de desplazamiento de bits (**RR, RL, RLC, RRC, SLA, SRA** y **SRL**).+En esta entrega trataremos las operaciones con bits (''NEG''''CPL''''BIT''''SET'' ''RES''), las operaciones lógicas (''AND''''OR'' ''XOR'') y las operaciones de desplazamiento de bits (''RR''''RL''''RLC''''RRC''''SLA''''SRA'', ''SRL'' y ''SLI'').
  
-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), y veremos 4 instrucciones muy sencillas: **SCF, CCF, NOP** **DAA**.+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), y veremos 4 instrucciones muy sencillas: ''SCF''''CCF''''NOP'' ''DAA''.
  
 +\\ 
 +===== Instrucciones de desplazamiento de memoria =====
  
-===== INSTRUCCIONES DE DESPLAZAMIENTO DE MEMORIA =====+ Ya conocemos la existencia de las instrucciones de carga (''LD''), que nos permitían mover valores entre 2 registros o entre la memoria y los 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: ''LDI'' ''LDD'', no admiten parámetros. Lo que hacen estas instrucciones es:
-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: **LDI** **LDD**, no admiten parámetros. Lo que hacen estas instrucciones es:+
  
 **LDI (Load And Increment):** **LDI (Load And Increment):**
Línea 41: Línea 39:
  
 <code> <code>
-LDI:    Copiar [HLen [DE]+ldi:    Copiar (HLen [DE]
         DE=DE+1         DE=DE+1
         HL=HL+1         HL=HL+1
         BC=BC-1         BC=BC-1
  
-LDD:    Copiar [HLen [DE]+ldd:    Copiar (HLen [DE]
         DE=DE-1         DE=DE-1
         HL=HL-1         HL=HL-1
Línea 54: Línea 52:
 Estas instrucciones lo que nos permiten es copiar datos de una zona de la memoria a otra. Por ejemplo, supongamos que queremos copiar el byte contenido en 16384 a la posición de memoria 40000: Estas instrucciones lo que nos permiten es copiar datos de una zona de la memoria a otra. Por ejemplo, supongamos que queremos copiar el byte contenido en 16384 a la posición de memoria 40000:
  
-<code asm+<code z80
- LD HL, 16384 +ld hl, 16384 
- LD DE, 40000 +ld de, 40000 
- LDI+ldi
 </code> </code>
  
-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é 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.
  
-Pero para facilitarnos más aún la tarea de copia (y no tener que realizar bucles manualmente), el Z80 nos proporciona las instrucciones **LDIR** **LDDR**, que funcionan igual que LDI y LDD pero copiando tantos bytes como valga el registro BC. Es decir:+Pero para facilitarnos aún más la tarea de copia (y no tener que realizar bucles manualmente), el Z80 nos proporciona las instrucciones ''LDIR'' ''LDDR'', que funcionan igual que ''LDI'' ''LDD'' pero copiando tantos bytes como valor contenga el registro BC. Es decir:
  
 <code> <code>
-LDIR = Repetir LDI hasta que BC valga 0+ldir = Repetir ldi hasta que BC valga 0
      = Repetir:      = Repetir:
-          Copiar [HLen [DE]+          Copiar (HLen [DE]
           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 LDD hasta que BC valga 0+lddr = Repetir ldd hasta que BC valga 0
      = Repetir:      = Repetir:
-          Copiar [HLen [DE]+          Copiar (HLen [DE]
           DE=DE-1           DE=DE-1
           HL=HL-1           HL=HL-1
Línea 84: Línea 82:
 Estas instrucciones son enormemente útiles porque nos permiten copiar bloques de datos desde una zona de la memoria a otra. Por ejemplo, podemos hacernos una copia del estado de la pantalla en una zona de memoria mediante: Estas instrucciones son enormemente útiles porque nos permiten copiar bloques de datos desde una zona de la memoria a otra. Por ejemplo, podemos hacernos una copia del estado de la pantalla en una zona de memoria mediante:
  
-<code asm+<code z80
-  LD HL, 16384 +ld hl, 16384 
-  LD DE, 50000 +ld de, 50000 
-  LD BC, 6912 +ld bc, 6912 
-  LDIR+ldir
 </code> </code>
  
-Con el anterior programa, copiamos los 6912 bytes que hay a partir de la dirección de memoria 16384 (la pantalla) y los copiamos a partir de la dirección 50000. De este modo, desde 50000 a 56912 tendremos una copia del estado de la pantalla (podría servir, por ejemplo, para modificar cosas en esta "pantalla virtual" y después copiarla de nuevo a la videoram, tomando HL=50000 y DE=16384).+Con el anterior programa, copiamos los 6912 bytes que hay a partir de la dirección de memoria 16384 (la pantalla) y los almacenamos a partir de la dirección 50000. De este modo, desde 50000 a 56912 tendremos una copia del estado de la pantalla (podría servir, por ejemplo, para modificar cosas en esta "pantalla virtual" y después copiarla de nuevo a la videoram, tomando HL=50000 y DE=16384).
  
 Para demostrar esto, ensamblemos y ejecutemos el siguiente ejemplo: Para demostrar esto, ensamblemos y ejecutemos el siguiente ejemplo:
  
-<code asm+<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 "veremos la ROM" :)  ; a la videomemoria. Digamos que "veremos la ROM" :)
- ORG 40000+    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 
-  LDIR             ; copiar+    ldir             ; copiar
  
- RET+    ret
 </code> </code>
  
-{{ cursos:ensamblador:la_rom.gif |Aspecto de la rom al copiarla a la VRAM}}+ Este ejemplo copia el contenido de los primeros 6144 bytes de memoria (el inicio de la ROM) sobre la videomemoria, haciendo aparecer píxeles que se corresponden con los valores que hay en la rom (las instrucciones de arranque y el intérprete BASIC del Spectrum): 
 + 
 +\\  
 +{{ cursos:ensamblador:la_rom.gif?634 |Aspecto de la rom al copiarla a la VRAM}} 
 +\\ 
  
-Os animo a que probéis el equivalente BASIC del ejemplo anterior y verifiquéis las diferencias de velocidad existentes:+ Al probar el equivalente BASIC del ejemplo anterior se puede comprobar la diferencia de velocidad existente:
  
 <code basic> <code basic>
-  5 REM Ejecutar "bas2tap -a10 copiarom.bas copiarom.tap" +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 
- 20 FOR I=0 TO 6144 : POKE (16384+I), (PEEK I) : NEXT I +30 PAUSE 0 
- 30 PAUSE 0+RUN
 </code> </code>
  
-Concluimos pues que en todas estas instrucciones de copia de memoria o transferencia, HL es el origen, DE el destino y BC el número de bytes a transferir. Con LDI y LDD sólo copiaremos 1 byte (independientemente del valor de BC, aunque lo decrementará), y con LDIR LDDR copiaremos tantos bytes como valga BC, decrementando BC hasta que su valor llega a cero. Los flags quedarán afectados, especialmente con LDI y LDD para indicarnos mediante el registro P/V si BC ha llegado a cero.+Concluímos pues que en todas estas instrucciones de copia de memoria o transferencia, HL es el origen, DE el destino y BC el número de bytes a transferir. Con ''LDI'' ''LDD'' sólo copiaremos 1 byte (independientemente del valor de BC, aunque lo decrementará), y con ldir lddr copiaremos tantos bytes como valga BC, decrementando BC hasta que su valor llega a cero. Los flags quedarán afectados, especialmente con ''LDI'' ''LDD'' para indicarnos mediante el registro P/V si BC ha llegado a cero.
  
 <code> <code>
Línea 125: Línea 127:
    Instrucción       |S Z H P N C|    Instrucción       |S Z H P N C|
  ----------------------------------  ----------------------------------
-   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 -|
 </code> </code>
  
Línea 143: Línea 145:
 </code> </code>
  
-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) LDDR es especialmente útil cuando hay que hacer copias de bloques de datos que se superponen.+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''La respuesta es que ''LDDR'' es especialmente útil cuando hay que hacer copias de bloques de datos que se superponen.
  
-Supongamos que tenemos que realizar una copia de 1000 bytes desde 25000 hasta 25100. Supongamos que preparamos el siguiente código:+Supongamos que tenemos que realizar una copia de 1000 bytes desde 25000 hasta 25100. Preparamos para ello el siguiente código:
  
-<code asm+<code z80
- LD HL, 25000 +ld hl, 25000 
- LD DE, 25100 +ld de, 25100 
- LD BC, 1000 +ld bc, 1000 
- LDIR+ldir
 </code> </code>
  
-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, que hemos perdido el contenido REAL del byte número 25100, porque fue machacado al principio de la ejecución del LDIR por el byte contenido en [25000]. No estamos moviendo el bloque correctamente, porque las zonas se superponen y cuando llegamos a la zona destino, estamos copiando bytes que movimos desde el origen.+¿Qué ocurrirá cuando ''LDIR'' llegue al byte número 25100 y lo intente copiar a 25200? Sencillamente, que hemos perdido el contenido REAL del byte número 25100, porque fue machacado al principio de la ejecución del ldir por el byte contenido en [25000]. No estamos moviendo el bloque correctamente, porque las zonas se superponen y cuando llegamos a la zona destino, estamos copiando bytes que movimos desde el origen.
  
-Para ello, lo correcto sería utilizar el siguiente código:+Para ello, lo correcto sería utilizar el siguiente código de "copia hacia atrás":
  
-<code asm+<code z80
- LD HL26000 +ld hl25999 
- LD DE26100 +ld de25099 
- LD BC, 1000 +ld bc, 1000 
- LDDR+lddr
 </code> </code>
  
-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 "machacamos" una posición de memoria que vayamos a copiar posteriormente con un dato.+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 posición de memoria que vayamos a copiar posteriormente.
  
 En este ejemplo: En este ejemplo:
Línea 184: Línea 186:
 Que es, efectivamente, lo que queríamos hacer, pero sin perder datos en la copia: copiar 1000 bytes desde 25000 a 25100 (sólo que realizamos la copia de abajo a arriba). Que es, efectivamente, lo que queríamos hacer, pero sin perder datos en la copia: copiar 1000 bytes desde 25000 a 25100 (sólo que realizamos la copia de abajo a arriba).
  
 +\\ 
 +===== 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 "" SCREEN$'', sino que aparezca de golpe.
- +
- +
-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 "" SCREEN$, sino que aparezca de golpe.+
  
 Para eso lo que haremos será lo siguiente: Para eso lo que haremos será lo siguiente:
Línea 194: Línea 195:
 Crearemos una rutina en ensamblador que copiará 6912 bytes desde la dirección 50000 hasta la posición 16384 (la videoram). La rutina ya la hemos visto: Crearemos una rutina en ensamblador que copiará 6912 bytes desde la dirección 50000 hasta la posición 16384 (la videoram). La rutina ya la hemos visto:
  
-<code asm+<code z80
- ORG 40000 +    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+    ldir             ; copiar 
 +    ret
 </code> </code>
  
-La ensamblamos con pasmo y obtenemos el siguiente código máquina:+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/visor hexadecimal):
  
 <code> <code>
- 33, 80, 195, 017, 0, 64, 1, 0, 27, 237, 176, 201+33, 80, 195, 17, 0, 64, 1, 0, 27, 237, 176, 201
 </code> </code>
  
Línea 215: 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 "" CODE 50000, 6912 50 LOAD "" CODE 50000, 6912
 60 RANDOMIZE USR 40000 60 RANDOMIZE USR 40000
Línea 221: Línea 223:
 </code> </code>
  
-Grabamos este cargador en cinta (o tap/tzx), y a continuación, tras el cargador, grabamos una pantalla de carga.+ Grabamos este cargador en cinta (o tap/tzx), y a continuación, tras el cargador, grabamos una pantalla de carga, que es cargada desde cinta en la dirección de memoria 50000 con la sentencia BASIC ''LOAD "" CODE''.
  
 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:ensamblador:carga.gif |La pantalla de carga de ZXColumns, volcada a videoram}}+\\  
 +{{ cursos:ensamblador:carga.gif?636 |La pantalla de carga de ZXColumns, volcada a videoram}}
 \\  \\ 
  
  
- +\\  
-===== ALGUNAS INSTRUCCIONES ESPECIALES ===== +===== Algunas instrucciones especiales =====
  
 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.+    * ''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. +    * ''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'', aunque esta misma operación se puede realizar con un simple ''and a'' u ''or a''.
-     +
-    * **NOP: No OPeration** :  Esta instrucción especial del microprocesador ocupa un byte en el código (opcode 00h) y no efectúa ninguna operación ni afecta a ningún flag. Eso sí, se toma 4 t-states (t-estados, o ciclos del procesador) para ejecutarse. ¿Para qué puede servir una instrucción que no realiza ninguna acción y que requiere tiempo del procesador (aunque sea muy poco) para ejecutarse? Muy sencillo: para múltiples cosas. Por un lado, podemos utilizarla en bucles de retardos (varios NOPs ejecutados en un bucle que se repita varias veces) para poner retardos en nuestros programas o juegos. Por otro, como ocupa un byte en memoria (en el código) y no realiza ninguna operación, podemos utilizarla para rellenar zonas de nuestro código, y así alinear código posterior en una determinada dirección que nos interese.+
  
-    * **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 "números en formato BCD". Un número BCD puede estar formado por varios bytes BCD, siendo cada byte 2 cifras del mismo. Así, para representar un número de 10 cifras en BCD sólo es necesario utilizar 5 bytes. Además, podemos utilizar un byte extra que indique la posición de la "coma decimal" para así poder trabajar con números decimales en ensamblador. Si queremos realizar operaciones entre este tipo de números deberemos programarnos nosotros mismos las rutinas para realizarlas. \\ \\ A lo largo del curso no utilizaremos números en BCD y por lo tanto es muy probable que no lleguemos a utilizar DAA, pero conviene saber que el Z80 nos brinda la oportunidad de utilizar números más grandes de 16 bits, operando con números en BCD. Para realizar juegos normalmente no necesitaremos de estas instrucciones.+    * ''nop'': **No OPeration** :  Esta instrucción especial del microprocesador ocupa un byte en el código (opcode $00) y no efectúa ninguna operación ni afecta a ningún flag. En cambio, se toma 4 t-states (t-estados, o ciclos del procesador) para ejecutarse, debido al ciclo de fetch/decode/execute del procesador. ¿Para qué puede servir una instrucción que no realiza ninguna acción y que requiere tiempo del procesador (aunque sea muy poco) para ejecutarse? Por un lado, podemos utilizarla en bucles de retardos (varios ''NOP''s ejecutados en un bucle que se repita varias veces) para poner retardos en nuestros programas o juegos. Por otro, como ocupa un byte en memoria (en el código) y no realiza ninguna operación, podemos utilizarla para rellenar zonas de nuestro código, y así alinear código posterior en una determinada dirección que nos interese. 
 + 
 +    * ''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 "números en formato BCD". Un número BCD puede estar formado por varios bytes BCD, siendo cada byte 2 cifras del mismo. Así, para representar un número de 10 cifras en BCD sólo es necesario utilizar 5 bytes. Además, podemos utilizar un byte extra que indique la posición de la "coma decimal" para así poder trabajar con números decimales en ensamblador. Si queremos realizar operaciones entre este tipo de números deberemos programarnos nosotros mismos las rutinas para realizarlas. \\ \\ A lo largo del curso no utilizaremos números en BCD y por lo tanto es muy probable que no lleguemos a utilizar daa, pero conviene saber que el Z80 nos brinda la oportunidad de utilizar números más grandes de 16 bits, operando con números en BCD. Para realizar juegos normalmente no necesitaremos de estas instrucciones.
  
 Todas estas instrucciones afectan a los flags de la siguiente manera: Todas estas instrucciones afectan a los flags de la siguiente manera:
Línea 249: Línea 251:
    Instrucción       |S Z H P N C|    Instrucción       |S Z H P N C|
  ----------------------------------  ----------------------------------
-   SCF               |- - 0 - 0 1| +   scf               |- - 0 - 0 1| 
-   CCF               |- - ? - 0 *| +   ccf               |- - ? - 0 *| 
-   NOP               |- - - - - -| +   nop               |- - - - - -| 
-   DAA               |* * * P - *|+   daa               |* * * P - *|
 </code> </code>
  
- +\\  
- +===== Operaciones con bits =====
-===== OPERACIONES CON BITS =====+
  
 El conjunto de instrucciones que vamos a ver hoy está pensado para trabajar con los bits individuales de un registro: invertir los bits de un registro, obtener el complemento a dos de un registro y poner a 0 o a 1, o comprobar, un determinado bit de un registro. El conjunto de instrucciones que vamos a ver hoy está pensado para trabajar con los bits individuales de un registro: invertir los bits de un registro, obtener el complemento a dos de un registro y poner a 0 o a 1, o comprobar, un determinado bit de un registro.
  
-==== CPL NEG ====+\\  
 +==== CPL 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.+''CPL'' es una instrucción que se usa para obtener el inverso en bits 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 asm+<code z80
- LD A, %10000001 +ld a, %10000001 
- CPL                 ; A = %01111110+cpl                 ; A = %01111110
 </code> </code>
  
-La tabla de afectación de flags de CPL es:+La tabla de afectación de flags de ''CPL'' es:
  
 <code> <code>
Línea 276: Línea 278:
    Instrucción       |S Z H P N C|    Instrucción       |S Z H P N C|
  ----------------------------------  ----------------------------------
-   CPL               |- - 1 - 1 -|+   cpl               |- - 1 - 1 -|
 </code> </code>
  
 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.
  
- Existe una instrucción similar a CPL, pero que además de realizar la inversión de unos y ceros suma 00000001 al resultado de la inversión del registro A. Esta instrucción es **NEG**. El resultado es que en A obtenemos el valor negativo del número en complemento a dos almacenado en este registro (A = -A).+ Existe una instrucción similar a ''CPL'', pero que además de realizar la inversión de unos y ceros suma 00000001 al resultado de la inversión del registro A. Esta instrucción es ''NEG''. El resultado es que en A obtenemos el valor negativo del número en complemento a dos almacenado en este registro (A = -A).
  
 Por ejemplo: Por ejemplo:
  
-<code asm+<code z80
- LD A, 1     ; A = +1 +ld a, 1        ; A = +1 
- NEG         ; A = -1 = %11111111+neg            ; A = -1 = %11111111
 </code> </code>
  
-La tabla de afectación de flags de NEG es:+La tabla de afectación de flags de ''NEG'' es:
  
 <code> <code>
Línea 296: Línea 298:
    Instrucción       |S Z H P N C|    Instrucción       |S Z H P N C|
  ----------------------------------  ----------------------------------
-   NEG               |* * * V 1 *|+   neg               |* * * V 1 *|
 </code> </code>
  
  
-==== SET, RES BIT ====+\\  
 +==== SET, RES BIT ====
  
 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 ''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:
  
-<code asm+<code z80
- SET bit, DESTINO+set bit, DESTINO
 </code> </code>
  
-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), y destino puede ser cualquier registro de 8 bits (A, B, C, D, E, H y L), una dirección de memoria apuntada por HL (es decir, el destino puede ser [HL]), o una dirección de memoria indexada por [IX+N[IY+N]. Con esto, las siguientes instrucciones serían válidas:+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), y destino puede ser cualquier registro de 8 bits (A, B, C, D, E, H y L), una dirección de memoria apuntada por HL (es decir, el destino puede ser (HL)), o una dirección de memoria indexada por (IX+N(IY+N). Con esto, las siguientes instrucciones serían válidas:
  
-<code asm+<code z80
- SET 5,         ; Activar el bit 5 del registro A +set 5,         ; Activar el bit 5 del registro A 
- SET 0,         ; Activar el bit 0 del registro H +set 0,         ; 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 +                 ; 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)
 </code> </code>
  
-La instrucción contraria a SET es **RES** (de reset), que pone a cero uno de los bits del destino especificado. Su formato es igual que el de SET, como podemos ver en los siguientes ejemplos:+La instrucción opuesta ''SET'' es ''RES'' (de reset), que pone a cero el bit indicado del destino especificado. Su formato es igual que el de SET, como podemos ver en los siguientes ejemplos:
  
-<code asm+<code z80
- RES bit, DESTINO+res bit, DESTINO
  
- RES 0,         ; Desactivar el bit 0 del registro H +res 0,         ; 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 +                 ; 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)
 </code> </code>
  
-SET y RES no afectan a los flags, como podemos ver en su tabla de afectación de indicadores:+''SET'' ''RES'' no afectan a los flags, como podemos ver en su tabla de afectación de indicadores:
  
 <code> <code>
Línea 337: Línea 340:
   Instrucción       |S Z H P N C|   Instrucción       |S Z H P N C|
  ----------------------------------  ----------------------------------
-  SET b, s          |- - - - - -| +  set b, s          |- - - - - -| 
-  RES b, s          |- - - - - -|+  res b, s          |- - - - - -|
 </code> </code>
  
-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 ''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:
  
     * 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 350: Línea 353:
 Su formato es: Su formato es:
  
-<code asm+<code z80
- BIT bit, DESTINO+bit bit, DESTINO
 </code> </code>
  
-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 ''SET'' ''RES'': un registro, posición de memoria apuntado por HL o posición de memoria apuntada por un registro índice más un desplazamiento.
  
 Por ejemplo: Por ejemplo:
  
-<code asm+<code z80
- LD A, 8       ; A = %00001000 +ld a, 8              ; A = %00001000 
- BIT 7, A      ; El flag Z vale 1 +bit 7, a             ; El flag Z vale 1 
-               ; porque el bit 7 es 0 +                     ; porque el bit 7 es 0 
- BIT 3, A      ; El flag Z vale 0 +bit 3, a             ; El flag Z vale 0 
-               ; porque el bit 3 no es 0 +                     ; porque el bit 3 no es 0 
-               ; (es 1).+                     ; (es 1).
 </code> </code>
  
-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 ''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.
  
 Por ejemplo: Por ejemplo:
  
-<code asm+<code z80
- BIT 0, A      ; Que valor tiene el bit 0? +bit 0, a             ; Que valor tiene el bit 0? 
-               ; Ahora Z = bit 0 de A. +                     ; Ahora Z = neg del bit 0 de A. 
- JP Z es_par   ; Saltar si esta Z activado +jp z, es_par         ; Saltar si esta Z activado 
-               ; (si Z=1 -> salta a es_par) +                     ; (si Z=1 -> salta a es_par) 
-               ; ya que si Z=1, es porque e+                     ; ya que si Z=1, es porque el bit era 0
 </code> </code>
  
Línea 385: Línea 388:
   Instrucción       |S Z H P N C|   Instrucción       |S Z H P N C|
  ----------------------------------  ----------------------------------
-  BIT b, s          |? * 1 ? 0 -|+  bit b, s          |? * 1 ? 0 -|
 </code> </code>
  
-===== ROTACION DE BITS ===== +\\  
 +===== Rotacion de bits =====
  
 El siguiente set de instrucciones que veremos nos permitirá ROTAR (ROTATE) los bits de un dato de 8 bits (por ejemplo, almacenado en un registro o en memoria) hacia la izquierda o hacia la derecha. El siguiente set de instrucciones que veremos nos permitirá ROTAR (ROTATE) los bits de un dato de 8 bits (por ejemplo, almacenado en un registro o en memoria) hacia la izquierda o hacia la derecha.
  
 +\\ 
 ==== RLC, RRC, RL y RC ==== ==== RLC, RRC, RL y RC ====
  
-Para realizar esta tarea tenemos disponibles 2 instrucciones básicas: **RLC** **RRC**. La primera de ellas, RLC, rota el registro o dato en un bit a la izquierda (RLC = Rotate Left Circular), y la segunda lo hace a la derecha.+Para realizar esta tarea tenemos disponibles 2 instrucciones básicas: ''RLC'' ''RRC''. La primera de ellas, ''RLC'', rota el registro o dato en un bit a la izquierda (RLC = Rotate Left Circular), y la segunda lo hace a la derecha. 
 + 
 +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) 
 +</code> 
 + 
 +Este es el efecto que tienen en los datos destino:
  
 <code> <code>
- 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 ->     ----------------- +        ----------------- -> RLC ->     ----------------- 
- Valor    a b c d e f g h                 b c d e f g h a+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 ->     ----------------- +        ----------------- -> RRC ->     ----------------- 
- Valor    a b c d e f g h                 h a b c d e f g+Valor    a b c d e f g h                 h a b c d e f g
 </code> </code>
  
-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í, ''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í.
  
 +\\ 
 {{ cursos:ensamblador:rlc_rrc.gif |RLC y RRC}} {{ cursos:ensamblador:rlc_rrc.gif |RLC y RRC}}
 +\\ 
  
 Por ejemplo, supongamos el valor 10000001 almacenado en el registro B: Por ejemplo, supongamos el valor 10000001 almacenado en el registro B:
Línea 416: Línea 448:
 El resultado las 2 operaciones descritas sería: El resultado las 2 operaciones descritas sería:
  
-<code asm+<code z80
- LD B, %10000001   ; B = 10000001 +ld b, %10000001   ; B = 10000001 
- RLC B             ; B = 00000011+rlc b             ; B = 00000011
  
- LD B, %10000001   ; B = 10000001 +ld b, %10000001   ; B = 10000001 
- RRC B             ; B = 00000011+rrc b             ; B = 11000000
 </code> </code>
  
-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+No [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+No [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 **RL** **RC**:+Además de ''RLC'' ''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 ''RL'' ''RC'':
  
 <code> <code>
Línea 439: Línea 471:
 </code> </code>
  
-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 ''RL'' ''RR'').
  
-{{ cursos:ensamblador:rl_rr.gif |RL y RR}}+\\  
 +{{ cursos:ensamblador:rl_rr.gif | RL y RR }} 
 +\\ 
  
 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 447: Línea 481:
 El resultado las 2 operaciones descritas sería: El resultado las 2 operaciones descritas sería:
  
-<code asm+<code z80
- SCF              ; Set Carry Flag (hace C=1) +scf                  ; Set Carry Flag (hace C=1) 
- LD B, %00000010  ; B = 00000010 +ld b, %00000010      ; B = 00000010 
- RL B             ; B = 00000101 y C=0 (del bit 7)+rl b                 ; B = 00000101 y C=0 (del bit 7)
  
- SCF              ; Set Carry Flag (hace C=1) +scf                  ; Set Carry Flag (hace C=1) 
- LD B, %01000001  ; B = 01000000 +ld b, %01000001      ; B = 01000000 
- RR B             ; B = 10100000 y C=1 (del bit 0)+rr b                 ; B = 10100000 y C=1 (del bit 0)
 </code> </code>
  
-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, ''RLC'' ''RRC'' son circulares y no utilizan el Carry Flag, mientras que ''RR'' ''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.
  
 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 465: Línea 501:
   Instrucción       |S Z H P N C|         Significado   Instrucción       |S Z H P N C|         Significado
  -----------------------------------------------------------------  -----------------------------------------------------------------
-  RLC s             |* * 0 P 0 *|         Rotate Left Circular +  rlc s             |* * 0 P 0 *|         Rotate Left Circular 
-  RRC s             |* * 0 P 0 *|         Rotate Right Circular +  rrc s             |* * 0 P 0 *|         Rotate Right Circular 
-  RL s              |* * 0 P 0 *|         Rotate Left (con Carry) +  rl s              |* * 0 P 0 *|         Rotate Left (con Carry) 
-  RR s              |* * 0 P 0 *|         Rotate Right (con Carry)+  rr s              |* * 0 P 0 *|         Rotate Right (con Carry)
 </code> </code>
  
 El destino "s" puede ser cualquier registro de 8 bits, memoria apuntada por HL o registros índice con desplazamiento. Como veis hay muchos flags afectados, y en esta ocasión el flag P/V ya no nos sirve para indicar desbordamientos sino que su estado nos da la PARIDAD del resultado de la operación de rotación. Con el flag P a uno, tenemos paridad par (even), es decir, el número de bits a uno en el resultado es par. Si está a cero significa que el número de bits a uno en el resultado es impar. El destino "s" puede ser cualquier registro de 8 bits, memoria apuntada por HL o registros índice con desplazamiento. Como veis hay muchos flags afectados, y en esta ocasión el flag P/V ya no nos sirve para indicar desbordamientos sino que su estado nos da la PARIDAD del resultado de la operación de rotación. Con el flag P a uno, tenemos paridad par (even), es decir, el número de bits a uno en el resultado es par. Si está a cero significa que el número de bits a uno en el resultado es impar.
  
-==== RLA, RRA, RLCA RRCA ====+\\  
 +==== RLCARRCA, 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 "A": hablamos de ''RLCA'', ''RRCA'', ''RLA'' y ''RRA''. La  
 + 
 +<code z80> 
 +rlca 
 +rrca 
 +rla 
 +rra 
 +</code>
  
-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 "A": hablamos de **RLA, RRA, RLCA** y **RRCA**. La diferencia entre estas 4 instrucciones y su versión con un espacio en medio (RL ARR ARLC A RRC A) radica simplemente en que las nuevas 4 instrucciones (sin espacio) alteran los flags de una forma diferente:+diferencia entre estas 4 instrucciones y su versión con un espacio en medio (''rlc a''''rrc a''''rl a'' ''rr a'') radica simplemente en que las nuevas 4 instrucciones trabajan con A y alteran los flags de una forma diferente:
  
 <code> <code>
Línea 481: Línea 528:
   Instrucción       |S Z H P N C|       Significado   Instrucción       |S Z H P N C|       Significado
  -----------------------------------------------------------------  -----------------------------------------------------------------
-  RLA               |- - 0 - 0 *|       Rotate Left Accumulator +  rla               |- - 0 - 0 *|       Rotate Left Accumulator 
-  RRA               |- - 0 - 0 *|       Rotate Right Accumulator +  rra               |- - 0 - 0 *|       Rotate Right Accumulator 
-  RLCA              |- - 0 - 0 *|       Rotate Left Circular Acc. +  rlca              |- - 0 - 0 *|       Rotate Left Circular Acc. 
-  RRCA              |- - 0 - 0 *|       Rotate Right Circular Acc.+  rrca              |- - 0 - 0 *|       Rotate Right Circular Acc.
 </code> </code>
  
 Como veis, están pensadas para alterar MENOS flags que sus homónimas de propósito general (algo que nos puede interesar en alguna ocasión). Como veis, están pensadas para alterar MENOS flags que sus homónimas de propósito general (algo que nos puede interesar en alguna ocasión).
  
 +\\ 
 +==== RLD y RRD ====
  
 +Y para acabar con las instrucciones de rotación, tenemos ''RLD'' y ''RRD'', que realiza una rotación de 4 bits entre A y el contenido de la memoria apuntada por HL. Son instrucciones sin operando:
  
-==== RLD y RRD ====+<code z80> 
 +rld 
 +rrd 
 +</code>
  
-Y para acabar con las instrucciones de rotación, tenemos **RLD** y **RRD**, que realiza una rotación entre A y el contenido de la memoria apuntada por HL.+Ambas instrucciones trabajan con los 2 ''nibbles'' (bloques de 4 bits) de A (''A2-A1''y el contenido de la dirección de memoria apuntada por HL (''M2-M1''), formando con ellos un "registro" de 16 bits, de los cuales se trabaja sólo con 12 bits (el pack ''A1-M2-M1'').
  
-Concretamente, RRD lo que hace es:+Es decir:
  
-    Leer el dato contenido en la dirección de memoria apuntada por HL+   Los 8 bits de A los agrupamos en 2 nibles: ''A2-A1'' siendo A2 el nibble alto (bits 7-4) y A1 el nibble bajo (bits 3-0)
-    Coger los 4 bits más significativos (bit 4-7) de ese valor+ 
-    Rotar hacia la izquierda 4 veces (copiando los bits 0-3 en las posiciones 4-7)+   Los 8 bits del contenido de (HL) los agrupamos en 2 nibles: ''M2-M1'' siendo M2 el nibble alto (bits 7-4) y M1 el nibble bajo (bits 3-0). 
-    Copiar los 4 bits extraídos de la memoria en los bits menos significativos de A.+ 
 +   Si colocamos como el byte alto y (HL) como el byte bajo, nos quedan los 4 nibbles ''A2-A1-M2-M1''
 + 
 +   Las instrucciones ''RRD'' y ''RLD'' trabajando con los 3 nibbles bajos ''A1-M2-M1'', realizando rotaciones de 4 bits (BCD) a izquierda o derecha. 
 + 
 +   * La parte alta de A (los bits 7 a 4) se quedan inalterados. No forman parte del "número BCD de 12 bits" por lo que no se ven afectados por la rotación. 
 + 
 +Por tanto: 
 + 
 +   * ''A1-M2-M1'' => ''RLD'' => ''M2-M1-A1'' 
 + 
 +   * ''A1-M2-M1'' => ''RRD'' => ''M1-A1-M2'' 
 + 
 +\\  
 +Gráficamente, ''RLD'' lo que hace es: 
 + 
 +\\  
 +{{ :cursos:ensamblador:rld-nibbles.png |rld}} 
 +\\  
 + 
 +Y ''RRD'': 
 + 
 +\\  
 +{{ :cursos:ensamblador:rrd-nibbles.png |rrd}} 
 +\\ 
  
-Resumiendo, supongamos los siguientes valores de A y [HL]:+Veamos ejemplos con "bits". Supongamos los siguientes valores de A y (HL):
  
 <code> <code>
- Registro A:   Bit 7 6 5 4 3 2 1 0 
-               -------------------- 
-                   a b c d e f g h 
  
- [HL]        Bit 7 6 5 4 3 2 1 0 +         <--A2--><--A1-->                 <--M2--><--M1-->  
-               -------------------- + A:   Bit 7 6 5 4 3 2 1 0      (HL)  Bit 7 6 5 4 3 2 1 0     
-                   s t u v w x y z+      --------------------             -------------------- 
 +          - - - - a b c d                  e f g h i j k l 
 </code> </code>
  
-**Resultado de RRD:**+Operaciones realizadas (en pseudocódigo):
  
 <code> <code>
- Registro A  Bit 7 6 5 3 2 1 0 + rld (HL) = ((HL)<<4) | (A & $0f) 
-               -------------------- +          A = ((HL)>>4) | (A & $f0) 
-                   e f g h s t u v+ rrd:  (HL) = ( A<<4 )  | ((HL)>>4) 
 +          A = ((HL)<<4) | (A & $f0)
 </code> </code>
-**Resultado de RLD:**+ 
 +Resultado de **rld**:
  
 <code> <code>
- Registro A:   Bit 7 6 5 4 3 2 1 0 +    A     (HL)               (HL) 
-               -------------------- +  ----   -----           ----- ----- 
-                   s t u v e f g h+  A2-A1  M2-Ml => rld => A2-M2 M1-A1 
 + 
 +          <--A2--><--A1-->                 <--M2--><--M1-->  
 + A:   Bit 7 6 5 4 3 2 1 0    (HL):     Bit 7 6 5 4 3 2 1 0     
 +      --------------------             -------------------- 
 +          - - - - e f g h                  i j k l a b c d
 </code> </code>
  
-En pseudocódigo C:+Resultado de **rrd**:
  
 <code> <code>
- RRD:  A<<   ([HL]>>4+        (HL            A   (HL) 
- RLD:  ( [HL]<<) | (A & 0x0F)+  ----   -----           ----- ----- 
 +  A2-A1  M2-Ml => rrd => A2-M1 Al-M2 
 + 
 +         <--A2--><--A1-->                 <--M2--><--M1-->  
 + A:   Bit 7 6 5 3 2 1 0    (HL):     Bit 7 6 5 4 3 2 1 0     
 +      --------------------             -------------------- 
 +          - - - - i j k l                  a b c d e f g h
 </code> </code>
  
Línea 542: Línea 631:
   Instrucción       |S Z H P N C|         Significado   Instrucción       |S Z H P N C|         Significado
  -----------------------------------------------------------------  -----------------------------------------------------------------
-  RLD               |* * 0 P 0 -|         Rotate Left 4 bits +  rld               |* * 0 P 0 -|         Rotate Left 4 bits 
-  RRD               |* * 0 P 0 -|         Rotate Right 4 bits+  rrd               |* * 0 P 0 -|         Rotate Right 4 bits
 </code> </code>
  
 Aunque ahora todo este conjunto de instrucciones pueda parecernos carente de utilidad, lo que hace el microprocesador Z80 es proveernos de toda una serie de pequeñas herramientas (como estas de manipulación, chequeo y rotación de bits) para que con ellas podamos resolver cualquier problema, mediante la combinación de las mismas. Os aseguramos que en más de una rutina tendréis que usar instrucciones de rotación o desplazamiento. Aunque ahora todo este conjunto de instrucciones pueda parecernos carente de utilidad, lo que hace el microprocesador Z80 es proveernos de toda una serie de pequeñas herramientas (como estas de manipulación, chequeo y rotación de bits) para que con ellas podamos resolver cualquier problema, mediante la combinación de las mismas. Os aseguramos que en más de una rutina tendréis que usar instrucciones de rotación o desplazamiento.
  
 +\\ 
 +===== 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 en el caso de desplazar a la izquierda, o copias del bit 7 en el caso de desplazar a la derecha:
- +
-===== 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:+
  
 <code> <code>
- 00010001 ROTADO A LA IZQUIERDA es 00100010 + SLA: 
- (movemos todos los bits hacia la izquierda y el bit 0 + 00010001 DESPLAZADO A LA IZQUIERDA es 00100010 
 + (movemos todos los bits hacia la izquierda y el bit 0
   entra como 0. El bit 7 se copia al Carry)   entra como 0. El bit 7 se copia al Carry)
  
- 10000001 ROTADO A LA DERECHA es 01000000 + SRA: 
- (el 0 del bit 7 del resultado entra nuevo, el 1 del  + 00001001 DESPLAZADO A LA DERECHA es 00000100 
-  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)
 </code> </code>
  
-Las instrucciones de desplazamiento a izquierda y derecha en Z80 se llaman **SLA (Shift Left Arithmetic)** **SRA (Shift Right Arithmetic)**, y su formato es:+Las instrucciones de desplazamiento a izquierda y derecha en Z80 se llaman ''SLA'' (Shift Left Arithmetic) y ''SRA'' (Shift Right Arithmetic), y su formato es: 
 + 
 +<code z80> 
 +sla r 
 +sla (hl) 
 +sla (ix+d) 
 +sla (iy+d)
  
-<code asm> +sra r 
- SRA operando +sra (hl) 
- SLA operando+sra (ix+d) 
 +sra (iy+d)
 </code> </code>
  
-Donde operando puede ser el mismo tipo de operando que en las instrucciones de rotación: un registro de 8 bits, [HL[IX/IY+N]. Lo que realizan estas operaciones sobre el dato operando es:+De nuevo, el operando de estas instrucciones puede ser el mismo tipo que en las instrucciones de rotación: un registro de 8 bits, (HL 
 +(IX/IY+s). Lo que realizan estas operaciones sobre el dato operando es:
  
 <code> <code>
Línea 598: 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 ''SLA'' ''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).
  
-El hecho de desplazar un número binario una posición a izquierda o derecha tiene una curiosa propiedad: //el número resultante se multiplica divide por 2//.+El hecho de desplazar un número binario una posición a izquierda o derecha tiene una curiosa propiedad: **el número resultante es el original multiplicado dividido por 2**.
  
-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):
  
 <code> <code>
- 1 5  -> Desplazar y cero  ->  1 5 0 +1 5  -> Desplazar y añadir cero  ->  1 5 0 
- (equivale a multiplicar por la base, es decir, por 10)+(equivale a multiplicar por la base, es decir, por 10)
 </code> </code>
  
Línea 612: Línea 709:
  
 <code> <code>
- 1 5 2 -> Desplazar y cero -> 0 1 5 +1 5 2 -> Desplazar y añadir cero -> 0 1 5 
- (equivale a dividir por la base, es decir, por 10).+(equivale a dividir por la base, es decir, por 10).
 </code> </code>
  
-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:
  
 <code> <code>
Línea 633: 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 643: 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 653: Línea 750:
 | Derecha (>>) | 7 | N = (...) N/128 | | Derecha (>>) | 7 | N = (...) N/128 |
  
-Así, desplazar una vez a la izquierda equivale a multiplicar por 22 veces, por 43 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 2 veces, por 4. Desplazar 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 el desplazamiento a derecha y la división.
  
 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 **SRL** que realiza la misma acción que SRA pero que, a diferencia de esta, lo que hace es //introducir un cero a la izquierda// (en lugar de copiar el bit de signo). La diferencia es que SRA es un desplazamiento aritmético (tiene en cuenta el signo) y //SRL es un desplazamiento lógico// (simplemente desplaza los bits):+Existe una pequeña variante de ''SRA'' llamada ''SRL'' que realiza la misma acción que ''SRA'' pero que, a diferencia de esta, lo que hace es //introducir un cero a la izquierda// (en lugar de copiar el bit de signo). 
 + 
 +<code z80> 
 +srl r 
 +srl (hl) 
 +srl (ix+d) 
 +srl (iy+d) 
 +</code> 
 + 
 +La diferencia es que ''SRA'' es un desplazamiento aritmético (tiene en cuenta el signo) y ''SRL'' //es un desplazamiento lógico// (simplemente desplaza los bits):
  
 <code> <code>
Línea 671: Línea 777:
     * Por la izquierda entra un cero.     * Por la izquierda entra un cero.
  
 +\\ 
 {{ cursos:ensamblador:sla_sra_srl.gif |SLA, SRA y SLR}} {{ cursos:ensamblador:sla_sra_srl.gif |SLA, SRA y SLR}}
 +\\ 
  
 + No existe una instrucción oficial ''SLL'' ("Shift Left Logical") porque literalmente sería lo mismo que ''SLA'', aunque existen como instrucciones no documentadas, desde **$CB $30** hasta **$CB $37** (actuando respectivamente sobre los registros B, C, D, E, H, L, (HL) y A), así como variantes con (IX/IY+d).
  
-Nuestra ya conocida tabla:+ Finalmente, se da la curiosidad de que existe una serie de opcodes que no están documentados en el manual de Z80 y que dan lugar a una operación de desplazamiento nueva llamada ''SLI'' ("Shift Left And Increment") o ''SL1'' (según el ensamblador que utilicemos): 
 + 
 +<code z80> 
 +sli r 
 +sli (hl) 
 +sli (ix+d) 
 +sli (iy+d) 
 +</code> 
 + 
 + Esta instrucción funciona como ''SLA'' (es decir, desplaza el registro o dato un byte a la izquierda) sólo que se inserta un '1' en vez de un '0' como bit 0. 
 + 
 +<code> 
 + 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 
 +</code> 
 + 
 + Veamos nuestra ya conocida tabla de afectación de flags:
  
 <code> <code>
                         Flags                         Flags
   Instrucción       |S Z H P N C|        Significado   Instrucción       |S Z H P N C|        Significado
- ----------------------------------------------------------------- + ------------------------------------------------------------------------------ 
-  SLA s             |* * 0 P 0 *|        Shift Left Arithmetic (s=s*2) +  SLA s             |* * 0 P 0 *|        Shift Left Arithmetic (s = s<<= s*2) 
-  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>>1)+  SRL s             |* * 0 P 0 *|        Shift Right Logical (s = s>>1) 
 +  SLI s             |* * 0 P 0 *|        Shift Left and Increment s = (s<<1)+1 
 </code> </code>
  
-Cabe destacar que gracias al Carry flag podremos realizar operaciones que desborden los 8 bits de que dispone un registro. Por ejemplo, supongamos que queremos realizar una multiplicación por 152 por 2. El resultado del desplazamiento sería:+\\  
 +===== Operaciones de desplazamiento de 16 bits ===== 
 + 
 + Gracias al Carry Flag podremos realizar operaciones de desplazamiento que desborden los 8 bits de que dispone un registro. Por ejemplo, supongamos que queremos realizar una multiplicación por 152 por 2. El resultado del desplazamiento sería:
  
 <code> <code>
Línea 706: Línea 836:
 Además, gracias a la combinación de instrucciones de rotación y desplazamiento podemos realizar operaciones con registros de 16 bits. Por ejemplo, supongamos que queremos multiplicar por 2 el valor positivo que tenemos en el registro DE: Además, gracias a la combinación de instrucciones de rotación y desplazamiento podemos realizar operaciones con registros de 16 bits. Por ejemplo, supongamos que queremos multiplicar por 2 el valor positivo que tenemos en el registro DE:
  
-<code asm+<code z80
-  SLA E +sla e 
-  RL  D+rl  d
 </code> </code>
  
-Lo que hacemos con "SLA E" es desplazar el byte más bajo del registros 16 bits DE hacia la izquierda, dejando el bit 7 de "E" en el Carry Flag, y después realizar una rotación de "D" hacia la izquierda introduciendo el carry flag de la operación anterior en el bit 0 de "D".+Lo que hacemos con ''sla e'' es desplazar el byte más bajo del registro de 16 bits DE hacia la izquierda, dejando el bit 7 de "E" en el Carry Flag, y después realizar una rotación de "D" hacia la izquierda introduciendo el carry flag de la operación anterior en el bit 0 de "D".
  
  
Línea 724: Línea 854:
 </code> </code>
  
-Primero con SLA E rotamos la parte baja, metiendo el bit "i" en el Carry Flag:+Primero con ''sla e'' rotamos la parte baja, metiendo el bit "i" en el Carry Flag:
  
 <code> <code>
- SLA E:+ sla e:
  
                                      E                                      E
Línea 737: Línea 867:
 </code> </code>
  
-Ahora con RL D rotamos D introduciendo el bit "i" en su bit 0:+Ahora con ''rl d'' rotamos D introduciendo el bit "i" en su bit 0:
  
 <code> <code>
- RL D:+ rl d:
  
                                      E                                      E
Línea 753: Línea 883:
 De igual forma, podemos realizar rotaciones de 16 bits a la derecha, haciendo el proceso inverso y comenzando primero con el byte alto: De igual forma, podemos realizar rotaciones de 16 bits a la derecha, haciendo el proceso inverso y comenzando primero con el byte alto:
  
-<code asm+<code z80
- SRA D +srl d 
- RR E+rr e
 </code> </code>
  
Línea 762: Línea 892:
 Y, para finalizar, veamos cómo el operando destino de 16 bits puede ser un par de bytes de memoria, como en el siguiente código de ejemplo: Y, para finalizar, veamos cómo el operando destino de 16 bits puede ser un par de bytes de memoria, como en el siguiente código de ejemplo:
  
-<code asm+<code z80
- LD IX, 16384 +ld ix, 16384 
- SLA (IX+sla (ix
- RL (IX+01H)+rl (ix+$01)
 </code> </code>
  
 Recordad para este ejemplo que en memoria se almacena primero el byte menos significativo de la palabra de 16 bits, y en la siguiente posición de memoria el más significativo. Recordad para este ejemplo que en memoria se almacena primero el byte menos significativo de la palabra de 16 bits, y en la siguiente posición de memoria el más significativo.
  
-===== OPERACIONES LOGICAS: AND, OR Y XOR =====+\\  
 +===== Resumen de instrucciones de rotación y desplazamiento =====
  
 +^ 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 | <code> C    7 6 5 4 3 2 1 0        C    7 6 5 4 3 2 1 0
 +----------------------      ----------------------
 +    a b c d e f g h   =>      b c d e f g h a</code> | <code>|S Z H P N C|
 +-------------
 +|* * 0 P 0 *|</code> |
 +| **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 | <code> C    7 6 5 4 3 2 1 0        C    7 6 5 4 3 2 1 0
 +----------------------      ----------------------
 +    a b c d e f g h   =>      h a b c d e f g</code> | <code>|S Z H P N C|
 +-------------
 +|* * 0 P 0 *|</code> |
 +| **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 | <code> C    7 6 5 4 3 2 1 0        C    7 6 5 4 3 2 1 0
 +----------------------      ----------------------
 +    a b c d e f g h   =>      b c d e f g h X</code> | <code>|S Z H P N C|
 +-------------
 +|* * 0 P 0 *|</code> |
 +| **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 | <code> C    7 6 5 4 3 2 1 0        C    7 6 5 4 3 2 1 0
 +----------------------      ----------------------
 +    a b c d e f g h   =>      X a b c d e f g </code> | <code>|S Z H P N C|
 +-------------
 +|* * 0 P 0 *|</code> |
 +| **RLCA** | **Rotate Left Circular Accumulator**\\ Rota el registro A en un bit a la izquierda.\\ Igual que **RLC** pero con diferente afectación de Flags.\\ El CF no entra en el registro.\\ El CF se ve afectado: CF = copia del bit 7 | <code> C    7 6 5 4 3 2 1 0        C    7 6 5 4 3 2 1 0
 +----------------------      ----------------------
 +    a b c d e f g h   =>      b c d e f g h a</code> | <code>|S Z H P N C|
 +-------------
 +|- - 0 - 0 *|</code> |
 +| **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 | <code> C    7 6 5 4 3 2 1 0        C    7 6 5 4 3 2 1 0
 +----------------------      ----------------------
 +    a b c d e f g h   =>      h a b c d e f g</code> | <code>|S Z H P N C|
 +-------------
 +|- - 0 - 0 *|</code> |
 +| **RLA** | **Rotate Left Accumulator**\\ Rota el registro Aen un bit a la izquierda.\\ Igual que **RL** 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 7 | <code> C    7 6 5 4 3 2 1 0        C    7 6 5 4 3 2 1 0
 +----------------------      ----------------------
 +    a b c d e f g h   =>      b c d e f g h X</code> | <code>|S Z H P N C|
 +-------------
 +|- - 0 - 0 *|</code> |
 +| **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 | <code> C    7 6 5 4 3 2 1 0        C    7 6 5 4 3 2 1 0
 +----------------------      ----------------------
 +    a b c d e f g h   =>      X a b c d e f g </code> | <code>|S Z H P N C|
 +-------------
 +|- - 0 - 0 *|</code> |
 +| **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. | <code> C    7 6 5 4 3 2 1 0        C    7 6 5 4 3 2 1 0
 +----------------------      ----------------------
 +    a b c d e f g h   =>      b c d e f g h 0</code> | <code>|S Z H P N C|
 +-------------
 +|* * 0 P 0 *|</code> |
 +| **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. | <code> C    7 6 5 4 3 2 1 0        C    7 6 5 4 3 2 1 0
 +----------------------      ----------------------
 +    a b c d e f g h   =>      a a b c d e f g</code> | <code>|S Z H P N C|
 +-------------
 +|* * 0 P 0 *|</code> |
 +| **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. | <code> C    7 6 5 4 3 2 1 0        C    7 6 5 4 3 2 1 0
 +----------------------      ----------------------
 +    a b c d e f g h   =>      0 a b c d e f g</code> | <code>|S Z H P N C|
 +-------------
 +|* * 0 P 0 *|</code> |
 +| **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. | <code> C    7 6 5 4 3 2 1 0        C    7 6 5 4 3 2 1 0
 +----------------------      ----------------------
 +    a b c d e f g h   =>      b c d e f g h 1</code> | <code>|S Z H P N C|
 +-------------
 +|* * 0 P 0 *|</code> |
  
-Para acabar con el artículo de hoy vamos a ver 3 operaciones a nivel de bits: AND, OR y XOR. Estas 3 operaciones lógicas se realizan entre 2 bits, dando un tercer bit como resultado: 
  
-  Bit  Bit   Resultado +\\  
-      2       AND +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). 
----------------------- + 
-   1    1        1 +\\  
-      0        0 +  * **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 ''A-parte-baja + (HL)-parte-alta + (HL)-parte-baja'' sin modificar ''A-parte-alta''
-      1        + 
-             0+\\  
 +  * **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-parte-baja + (HL)-parte-alta + (HL)-parte-baja'' sin modificar ''A-parte-alta''
 + 
 +\\  
 +<code> 
 +         <--A2--><--A1-->                     <--M2--><--M1-->  
 + A:   Bit 7 6 5 4 3 2    (HL):         Bit 7 6 5 4 3 1 0     
 +      --------------------                 -------------------- 
 +          - - - - a b c d                      e f g h i j k l 
 +</code> 
 + 
 +Resultado **rld**: 
 + 
 +<code> 
 +    A     (HL)               (HL) 
 +  ----   -----           ----- ----- 
 +  A2-A1  M2-Ml => rld => A2-M2 M1-A1 
 + 
 +          <--A2--><--A1-->                 <--M2--><--M1-->  
 + A:   Bit 7 6 5 4 3 2    (HL):     Bit 7 6 5 4 3 2 1     
 +      --------------------             -------------------- 
 +          - - - - e f g h                  i j k l a b c d 
 +</code> 
 + 
 +Resultado **rrd**: 
 + 
 +<code> 
 +    A     (HL)               (HL) 
 +  ----   -----           ----- ----- 
 +  A2-A1  M2-Ml => rrd => A2-M1 Al-M2 
 + 
 +         <--A2--><--A1-->                 <--M2--><--M1-->  
 + A:   Bit 7 6 5 4 3 2 1 0    (HL):     Bit 7 6 5 4 3 2 1 0     
 +      --------------------             -------------------- 
 +          - - - - i j k l                  a b c d e f g h 
 +</code> 
 + 
 +Con la siguiente afectación de flags: 
 + 
 +<code> 
 +                        Flags 
 +  Instrucción       |S Z H P N C|         Significado 
 + ----------------------------------------------------------------- 
 +  rld               |* * -|         Rotate Left 4 bits 
 +  rrd               |* * P 0 -|         Rotate Right 4 bits 
 +</code> 
 + 
 +A continuación podemos ver un resumen gráfico de las diferentes instrucciones de desplazamiento obtenido del libro "//Inside your Spectrum//", de //Jeff Naylor// y //Diane Rogers//, al cual se le ha añadido la instrucción ''SLI'' (también conocida como ''SL1'' la cual no está contemplada en el libro): 
 + 
 +\\  
 +{{ :cursos:ensamblador:resumen-instrucciones-rotacion-y-desplazamiento.jpg }} 
 +\\  
 + 
 +\\  
 +===== Operaciones logicas: AND, OR y XOR =====
  
 +Para acabar con el artículo de hoy vamos a ver 3 operaciones a nivel de bits: ''AND'', ''OR'' y ''XOR''. Estas 3 operaciones lógicas se realizan entre 2 bits, dando un tercer bit como resultado:
  
-  Bit  Bit   Resultado +|< 40% 33% 33% 33% >| 
-            OR +Bit 1 ^ Bit 2 ^ Resultado AND ^ 
----------------------- +| 
-             +| 0 | 
-             1 +| 0 | 
-             +|
-             0+
  
 +|< 40% 33% 33% 33% >|
 +^ Bit 1 ^ Bit 2 ^ Resultado OR ^
 +| 1 | 1 | 1 |
 +| 1 | 0 | 1 |
 +| 0 | 1 | 1 |
 +| 0 | 0 | 0 |
  
-  Bit  Bit   Resultado +|< 40% 33% 33% 33% >| 
-      2       XOR +Bit 1 ^ Bit 2 ^ Resultado XOR ^ 
----------------------- +| 
-             +| 
-             +| 
-             +|
-             0+
  
 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). +   * **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" (exclusive OR) donde el resultado es cero cuando los 2 bits operandos son iguales, y uno cuando los 2 bits operandos son diferentes.+   * **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"** (exclusive OR) donde el resultado es cero cuando los 2 bits operandos son iguales, y uno cuando los 2 bits operandos son diferentes.
  
 Ejemplos: Ejemplos:
  
- 10010101 AND 0000111  = 00000101+<code> 
 + 10010101 and 0000111  = 00000101
  
- 00000101 OR  1100000  = 11000101+ 00000101 or  1100000  = 11000101
  
- 11000011 XOR 10011001 = 01011010+ 11000011 xor 10011001 = 01011010 
 +</code>
  
-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, ''AND''''OR'' ''XOR''. Las tres tienen el mismo formato:
  
- AND ORIGEN +<code z80> 
- OR  ORIGEN +and ORIGEN 
- XOR ORIGEN+or ORIGEN 
 +xor ORIGEN 
 +</code>
  
-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 ''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.
  
-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 ''CPL'', que vimos al principio de este capítulo, también se considera una operación lógica, equivalente a NOT (0->1 y 1->0).
  
-Pero continuemos con AND, OR y XOR. Veamos algunos ejemplos de instrucciones válidas:+Pero continuemos con ''AND''''OR'' ''XOR''. Veamos algunos ejemplos de instrucciones válidas:
  
- AND B +<code z80> 
- OR C +and b 
- OR [HL] +or c 
- XOR [IX+10] +or (hl) 
- AND 45+xor (ix+10) 
 +and 45 
 +</code>
  
 La operación realizada por estas instrucciones sería: La operación realizada por estas instrucciones sería:
  
- AND ORIGEN -> A = A & ORIGEN +<code> 
- OR  ORIGEN -> A = A | ORIGEN +AND ORIGEN -> A = A & ORIGEN 
- XOR ORIGEN -> A = A ^ ORIGEN+OR  ORIGEN -> A = A | ORIGEN 
 +XOR ORIGEN -> A = A ^ ORIGEN
  
- ( Donde & = AND, | = OR y ^ = XOR )+(Donde & = AND, | = OR y ^ = XOR) 
 +</code>
  
-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), en realidad estamos realizando 8 operaciones AND, OR o XOR. Por ejemplo, al hacer un AND entre los registros A y B con "AND B" (A=A&B), realizamos las siguientes operaciones: 
  
 +Recordemos que ''AND'', ''OR'' y ''XOR'' son operaciones lógicas de un sólo bit, de modo que al trabajar con registros (o memoria, o valores inmediatos), en realidad estamos realizando 8 operaciones AND, OR o XOR, entre los diferentes bits de los operandos. Por ejemplo, al hacer un AND entre los registros A y B con ''and b'' (A=A&b), realizamos las siguientes operaciones:
 +
 +<code>
  Registro A:   Bit  7  6  5  4  3  2  1  0  Registro A:   Bit  7  6  5  4  3  2  1  0
                ----------------------------                ----------------------------
Línea 851: Línea 1113:
                    B7 B6 B5 B4 B3 B2 B1 B0                    B7 B6 B5 B4 B3 B2 B1 B0
  
-Resultado:+ Resultado:
  
- 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 
 +</code>
  
-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? Os aseguro que a lo largo de vuestros programas tendréis que usarlas, y mucho, porque son operaciones muy importantes a la hora de manipular registros. Por ejemplo, supongamos que queremos eliminar los 4 bits más altos de un registro, dejándolos a cero, y dejar sin alterar el estado de los 4 bits menos significativos.+¿Para qué pueden servirnos estas 3 operaciones lógicas? Tenga el lector por seguro que a lo largo de nuestros programas tendremos que usarlas, y mucho, porque son operaciones muy importantes a la hora de manipular registros. Por ejemplo, supongamos que queremos eliminar los 4 bits más altos de un registro, dejándolos a cero, y dejar sin alterar el estado de los 4 bits menos significativos.
  
 Podríamos hacer: Podríamos hacer:
  
- RES 7, A +<code z80> 
- RES 6, A +res 7, a 
- RES 5, A +res 6, a 
- RES 4, A+res 5, a 
 +res 4, 
 +</code>
  
-Pero sería mucho más sencillo:+Pero sería mucho más sencillo y mucho más rápido (una sóla instrucción de 2 bytes y 7 ciclos de reloj, en vez de 4 que suman 8 bytes y 32 ciclos):
  
- AND %00001111+<code z80> 
 +and %00001111 
 +</code>
  
 O sea, realizar la operación: O sea, realizar la operación:
  
- A = A AND 00001111b+<code> 
 +A = A and 00001111b 
 +</code>
  
 Veamos un ejemplo del porqué: Veamos un ejemplo del porqué:
  
 +<code>
  Sea A = 10101011  Sea A = 10101011
  valor = 00001111  valor = 00001111
        ------------ <-- Operación AND        ------------ <-- Operación AND
          00001011          00001011
 +</code>
  
-Como AND es la operación lógica de la multiplicación, al hacer un AND de A con 00001111, todos aquellos bits que son cero en 00001111 quedarán a cero en el resultado, y todos aquellos bits que son uno en 00001111 no modificarán el estado de los bits de A.+ Como AND es la operación lógica de la multiplicación, al hacer un ''AND'' de A con 00001111, todos aquellos bits que son cero en 00001111 quedarán a cero en el resultado, y todos aquellos bits que son uno en 00001111 no modificarán el estado de los bits de A.
  
-De la misma forma, por ejemplo, OR nos permite fusionar 2 cuartetos de bits:+De la misma forma, por ejemplo, ''OR'' nos permite fusionar 2 cuartetos de bits:
  
 +<code>
  Sea A = 10100000  Sea A = 10100000
  Sea B = 00001111  Sea B = 00001111
        ------------ <-- Operación OR        ------------ <-- Operación OR
          10101111          10101111
 +</code>
  
-La afectación de flags de las 3 instrucciones es idéntica:+Por lo tanto, ''AND'' permite simular ''RES'' y ''OR'' permite simular ''SET'' sobre A, pero más rápido, y pudiendo alterar más de un bit en una única operación (cosa que ''SET''/''RES'' no permiten, habría que hacerlo con varias):
  
 +<code>
 +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
 +</code>
 +
 + La desventaja de usar ''AND'' y ''OR'' como ''RES'' y ''SET'', es que sólo podremos hacerlos sobre el registro A, y no sobre cualquier otro registro de 8 bits como permiten ''RES'' y ''SET'', y que alteran el valor de A durante el proceso.
 +
 +
 +La afectación de flags de las 3 instrucciones lógicas es idéntica:
 +
 +<code>
                         Flags                         Flags
   Instrucción       |S Z H P N C|   Instrucción       |S Z H P N C|
  ----------------------------------  ----------------------------------
-  AND s             |* * * P 0 0| +  and s             |* * * P 0 0| 
-  OR s              |* * * P 0 0| +  or s              |* * * P 0 0| 
-  XOR s             |* * * P 0 0|+  xor s             |* * * P 0 0| 
 +</code>
  
-Una curiosidadXOR 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 ''BIT'' sobre A también se puede simular con ''AND'':
  
-EN LA PROXIMA ENTREGA+<code> 
 +bit 0, a  =>  and %00000001 
 +bit 1, a  =>  and %00000010 
 +bit 2, a  =>  and %00000100 
 +(...) 
 +bit 7, a  =>  and $10000000 
 +</code>
  
-Veremos conceptos e instrucciones muy importantes en cualquier programa en ensamblador: qué es cómo se usa la PILA (stackdel Spectrum, y cómo realizar cambios en el flujo del programa con sentencias condicionales equivalentes al IF/THEN/ELSE.+Por ejemplo, si queremos saber si el bit 7 de A está a 1 o a 0, podemos hacer un ''and %10000000''. Este AND dejará a 0 todos los bits menos el 7, que lo dejará sin modificar, con el valor que tuviera antes de la operación AND. Por lo tanto, se quedará a 0 o a 1. Si era 0, el resultando del AND de todo el byte será igual a zero y se activará el Zero Flag, mientras que si estaba a 1, el byte valdrá 128 (bit 7 activopor lo que el Zero Flag se quedará inactivoHemos simulado así un ''BIT'' pero alterando A:
  
-FICHEROS+<code z80> 
 +and %10000000 
 +jp nz, bit_7_activo      ; Saltar si no esta Z activado => bit 7 es 0 
 +</code>
  
-    * Ejemplos de suma y resta en BCD + Hay otros usos imaginativos de las operaciones lógicas:
-    * Ejemplo de programa en BASIC que copia la ROM en la VRAM +
-    * Fichero tap del ejemplo copiarom.bas +
-    * Rutina de volcado de pantalla en ASM +
-    * Rutina de volcado de pantalla en ASM (binario) +
-    * Cargador BASIC de la rutina anterior +
-    * Ejemplo ejecutable de carga de pantalla con volcado +
-    * Ejemplo de programa en ASM +
-    * Fichero tap del ejemplo anterior+
  
-LINKS+ Para empezar, ''xor a'' es equivalente a hacer ''ld a, 0'', ya que la operación XOR de un número consigo mismo daría 0 en todos sus bits porque XOR sólo deja a uno los bits diferentes (y A xor a tiene todos los bits iguales, por lo que todos quedan a 0). Es muy habitual ver ''xor a'' en programas en vez de ''ld a, 0'' porque ocupa un sólo byte en el programa en vez de dos, y además se ejecuta más rápido.
  
-    * Juego de caracteres +<code> 
-    * Web del Z80 +xor a    =>   ld a, 0 
-    * Z80 Reference de WOS +</code> 
-    * Z80 Reference de TI86 + 
-    * FAQ de Icarus Productions + Por otra parte, se suele utilizar ''or a'' para limpiar el Carry Flag y ponerlo a 0. 
-    * Microfichas de CM de MicroHobby + 
-    * Tablas de ensamblado y t-estados (pulsar en z80.txt, z80_reference.txt, z80time.txt) + 
-    * Z80 Reference de WOS +\\  
-    * Curso de ensamblador de z80.info +===== Ficheros ===== 
-    Pasmo+ 
 +    {{cursos:ensamblador:04_copiarom.bas|Ejemplo de programa en BASIC que copia la ROM en la VRAM}} 
 +    * {{cursos:ensamblador:04_copiarom.tap|Fichero tap del ejemplo copiarom.bas}} 
 +    * {{cursos:ensamblador:04_carga.zip|Rutina de volcado de pantalla en ASM (asm+bin+bas)}} 
 +    * {{cursos:ensamblador:04_ejemplo.asm|Ejemplo ASM de carga de pantalla con volcado}} 
 +    * {{cursos:ensamblador:04_ejemplo.tap|Fichero tap del ejemplo anterior}} 
 +    * {{cursos:ensamblador:04_sumaresta.asm|Ejemplos de suma y resta en BCD}} 
 + 
 +\\  
 +===== Enlaces ===== 
 + 
 +    * [[http://www.worldofspectrum.org/ZXSpectrum128+3Manual/chapter8pt28.html|Juego de caracteres]] 
 +    * [[http://www.z80.info/|Web del Z80]] 
 +    * [[http://www.worldofspectrum.org/faq/reference/z80reference.htm|Z80 Reference de WOS]] 
 +    * [[http://ti86.acz.org/z80_ref.htm|Z80 Reference de TI86]] 
 +    * [[http://icarus.ticalc.org/articles/z80_faq.html|FAQ de Icarus Productions]] 
 +    * [[http://www.speccy.org/trastero/cosas/Fichas/fichas.htm|Microfichas de CM de MicroHobby]] 
 +    * [[http://www.ticalc.org/pub/text/z80/|Tablas de ensamblado y t-estados (pulsar en z80.txt, z80_reference.txt, z80time.txt)]] 
 +    * [[http://www.worldofspectrum.org/faq/reference/Z80reference.htm|Z80 Reference de WOS]] 
 +    * [[http://www.z80.info/lesson1.htm|Curso de ensamblador de z80.info]] 
 + 
 +\\  
 +**[ [[.:indice|⬉]] | [[.:lenguaje_1|⬅]] | [[.:lenguaje_3|➡]] ]**
  
  • cursos/ensamblador/lenguaje_2.1186469851.txt.gz
  • Última modificación: 07-08-2007 06:57
  • por sromero