cursos:ensamblador:lenguaje_1

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_1 [19-01-2024 06:27] sromerocursos:ensamblador:lenguaje_1 [22-01-2024 07:51] (actual) – [Instrucciones LD (instrucciones de carga)] sromero
Línea 23: Línea 23:
 Además, podemos agrupar algunos de estos registros en pares de 16 bits para determinadas operaciones: Además, podemos agrupar algunos de estos registros en pares de 16 bits para determinadas operaciones:
 \\  \\ 
-    * **AF**: Formado por el registro A como byte más significativo (Byte alto) y por F como byte menos significativo (Byte bajo). Si A vale $ff y F vale $00, AF valdrá automáticamente "$ff00".+    * **AF**: Formado por el registro A como byte más significativo (Byte alto) y por F como byte menos significativo (Byte bajo). Si ''A'' vale **$ff** ''F'' vale **$00**''AF'' valdrá automáticamente **$ff00**.
     * **BC**: Agrupación de los registros B y C que se puede utilizar en bucles y para acceder a puertos. También se utiliza como "repetidor" o "contador" en las operaciones de acceso a memoria (''LDIR'', ''LDDR'', etc.).     * **BC**: Agrupación de los registros B y C que se puede utilizar en bucles y para acceder a puertos. También se utiliza como "repetidor" o "contador" en las operaciones de acceso a memoria (''LDIR'', ''LDDR'', etc.).
     * **DE, HL**: Registros de 16 bits formados por D y E por un lado y H y L por otro. Utilizaremos generalmente estos registros para leer y escribir en memoria en una operación única, así como para las operaciones de acceso a memoria como ''LDIR'', ''LDDR'', etc.     * **DE, HL**: Registros de 16 bits formados por D y E por un lado y H y L por otro. Utilizaremos generalmente estos registros para leer y escribir en memoria en una operación única, así como para las operaciones de acceso a memoria como ''LDIR'', ''LDDR'', etc.
Línea 31: Línea 31:
     * **IX, IY**: Dos registros de 16 bits pensados para acceder a memoria de forma indexada. Gracias a estos registros podemos realizar operaciones como: ''LD (IX+desplazamiento), VALOR''. Este tipo de registros se suele utilizar pues para hacer de índices dentro de tablas o vectores. El desplazamiento es un valor numérico de 8 bits en complemento a 2, lo que nos permite un rango desde -128 a +127 (puede ser negativo para acceder a posiciones de memoria anteriores a IX).     * **IX, IY**: Dos registros de 16 bits pensados para acceder a memoria de forma indexada. Gracias a estos registros podemos realizar operaciones como: ''LD (IX+desplazamiento), VALOR''. Este tipo de registros se suele utilizar pues para hacer de índices dentro de tablas o vectores. El desplazamiento es un valor numérico de 8 bits en complemento a 2, lo que nos permite un rango desde -128 a +127 (puede ser negativo para acceder a posiciones de memoria anteriores a IX).
     * **SP**: Puntero de pila, como veremos en su momento apunta a la posición actual de la "cabeza" de la pila.     * **SP**: Puntero de pila, como veremos en su momento apunta a la posición actual de la "cabeza" de la pila.
-    * **PC**: Program Counter o Contador de Programa. Como ya vimos en la anterior entrega, contiene la dirección de la instrucción actual a ejecutar. No modificaremos PC directamente moviendo valores a este registro, sino que lo haremos mediante instrucciones de salto (''jp'', ''jr'', ''call''...).+    * **PC**: Program Counter o Contador de Programa. Como ya vimos en la anterior entrega, contiene la dirección de la instrucción actual a ejecutar. No modificaremos PC directamente moviendo valores a este registro, sino que lo haremos mediante instrucciones de salto (''JP'', ''JR'', ''CALL''...).
 \\  \\ 
  Por último, tenemos disponible un banco alternativo de registros, conocidos como **Shadow Registers** o //Registros Alternativos//, que se llaman igual que sus equivalentes principales pero con una comilla simple detrás: A', F', B', C', D'. E', H' y L'.  Por último, tenemos disponible un banco alternativo de registros, conocidos como **Shadow Registers** o //Registros Alternativos//, que se llaman igual que sus equivalentes principales pero con una comilla simple detrás: A', F', B', C', D'. E', H' y L'.
  
-En cualquier momento podemos intercambiar el valor de los registros A, B, C, D, E, F, H y L con el valor de los registros A', B', C', D', E', F', H' y L' mediante las instrucciones de ensamblador ''ex afaf<nowiki>'</nowiki>'' y ''EXX''. La utilidad de estos Shadow Registers es almacenar valores temporales y proporcionarnos más registros para operar: podremos intercambiar el valor de los registros actuales con los temporales, realizar operaciones con los registros sin perder los valores originales (que al hacer el intercambio se quedarán en los registros Shadow), y después recuperar los valores originales volviendo a ejecutar un intercambio.+En cualquier momento podemos intercambiar el valor de los registros A, B, C, D, E, F, H y L con el valor de los registros A', B', C', D', E', F', H' y L' mediante las instrucciones de ensamblador ''EX AFAF<nowiki>'</nowiki>'' y ''exx''. La utilidad de estos Shadow Registers es almacenar valores temporales y proporcionarnos más registros para operar: podremos intercambiar el valor de los registros actuales con los temporales, realizar operaciones con los registros sin perder los valores originales (que al hacer el intercambio se quedarán en los registros Shadow), y después recuperar los valores originales volviendo a ejecutar un intercambio.
  
 Ya conocemos los registros disponibles, veamos ahora ejemplos de operaciones típicas que podemos realizar con ellos: Ya conocemos los registros disponibles, veamos ahora ejemplos de operaciones típicas que podemos realizar con ellos:
Línea 47: Línea 47:
  
 <code z80> <code z80>
-    ld c, $00       ; C vale 0 +    ld c, $00            ; C vale 0 
-    ld b, $01       ; B vale 1 +    ld b, $01            ; B vale 1 
-                    ; con esto, BC = $0100 +                         ; con esto, BC = $0100 
-    ld a, b         ; A ahora vale 1 +    ld a, b              ; A ahora vale 1 
-    ld hl, $1234    ; HL vale $1234 o 4660d +    ld hl, $1234         ; HL vale $1234 o 4660d 
-    ld a, (hl)      ; A contiene el valor de (4660) +    ld a, (hl)           ; A contiene el valor de (4660) 
-    ld a, (16384)   ; A contiene el valor de (16384) +    ld a, (16384)        ; A contiene el valor de (16384) 
-    ld (16385), a   ; Escribimos en (16385) el valor de A +    ld (16385), a        ; Escribimos en (16385) el valor de A 
-    add a, b        ; Suma: A = A + B +    add a, b             ; Suma: A = A + B 
-    inc b           ; Incrementamos B (B = 1+1 =2) +    inc b                ; Incrementamos B (B = 1+1 =2) 
-                    ; Ahora BC vale $0200 +                         ; Ahora BC vale $0200 
-    inc  bc         ; Incrementamos BC +    inc  bc              ; Incrementamos BC 
-                    ; (BC = $0200+1 = $0201)+                         ; (BC = $0200+1 = $0201)
 </code> </code>
  
Línea 73: Línea 73:
     ld sp, bc     ld sp, bc
     ex de, hl     ex de, hl
-    EX BCDE+    ex bcde
     add hl, bc     add hl, bc
     add de, bc     add de, bc
Línea 84: Línea 84:
                   ; sólo se puede cargar un valor inmediato NN                   ; sólo se puede cargar un valor inmediato NN
  
-   EX BCDE      ; NO: Existe ex de, hl, pero no EX BC, DE+   ex bcde      ; NO: Existe ex de, hl, pero no EX BC, DE
  
    add de, bc     ; NO: Sólo se puede usar HL como operando destino    add de, bc     ; NO: Sólo se puede usar HL como operando destino
Línea 154: Línea 154:
             ld b, 100             ld b, 100
     bucle:     bucle:
-            (...)        ; código+            (...)              ; código
  
-            dec b        ; Decrementamos B (B=B-1)+            dec b              ; Decrementamos B (B=B-1)
             jr nz, bucle             jr nz, bucle
 +            
             ; Si el resultado de la operación anterior no es cero (NZ = Non Zero),             ; Si el resultado de la operación anterior no es cero (NZ = Non Zero),
             ; saltar a la etiqueta bucle y continuar. dec b hará que el flag Z             ; saltar a la etiqueta bucle y continuar. dec b hará que el flag Z
Línea 177: Línea 178:
  
 <code z80> <code z80>
-     ld a, c              ; A = C+     ld a, c                   ; A = C
      ; Tenemos que hacer esto porque no existe      ; Tenemos que hacer esto porque no existe
      ; una instruccion sub b, c . Sólo se puede      ; una instruccion sub b, c . Sólo se puede
      ; restar un registro al registro A.      ; restar un registro al registro A.
  
-     sub b                ; A = A-B +     sub b                     ; A = A-B 
-     jp z, Es_Igual       ; Si A=B la resta es cero y Z=1 +     jp z, Es_Igual            ; Si A=B la resta es cero y Z=1 
-     jp nz, No_Es_Igual   ; Si A<>B la resta no es cero y Z=0+     jp nz, No_Es_Igual        ; Si A<>B la resta no es cero y Z=0
      (...)      (...)
  
Línea 224: Línea 225:
  
 <code z80> <code z80>
-LD DESTINO, ORIGEN+ld DESTINO, ORIGEN
 </code> </code>
  
Línea 232: Línea 233:
  
 <code z80> <code z80>
-ld a, 10         ; A = 10 +ld a, 10              ; A = 10 
-ld b, 200        ; B = 200 +ld b, 200             ; B = 200 
-ld bc, 12345     ; BC = 12345+ld bc, 12345          ; BC = 12345
 </code> </code>
  
Línea 240: Línea 241:
  
 <code z80> <code z80>
-ld a, b          ; A = B +ld a, b               ; A = B 
-ld bc, de        ; BC = DE+ld bc, de             ; BC = DE
 </code> </code>
  
Línea 247: Línea 248:
  
 <code z80> <code z80>
-ld (12345), a    ; Memoria[12345] = valor en A +ld (12345), a         ; Memoria[12345] = valor en A 
-ld (hl), 10      ; Memoria[valor de HL] = 10+ld (hl), 10           ; Memoria[valor de HL] = 10
 </code> </code>
  
Línea 254: Línea 255:
  
 <code z80> <code z80>
-ld a, (12345)    ; A = valor en Memoria[12345] +ld a, (12345)         ; A = valor en Memoria[12345] 
-ld b, (hl)       ; B = valor en Memoria[valor de HL]+ld b, (hl)            ; B = valor en Memoria[valor de HL]
 </code> </code>
  
- Nótese cómo el operador () nos permite acceder a memoria. En nuestros ejemplos, ''ld a, (12345)'' no significa meter en A el valor 12345 (cosa imposible al ser un registro de 16 bits) sino almacenar en el registro A el valor que hay almacenado en la celdilla número 12345 de la memoria del Spectrum.+ Nótese cómo **el operador ''()'' nos permite acceder a la memoria del Spectrum**. En nuestros ejemplos, ''ld a, (12345)'' no significa meter en A el valor 12345 (cosa imposible al ser un registro de 16 bits) sino almacenar en el registro A el valor que hay almacenado en la celdilla número 12345 de la memoria del Spectrum. 
 + 
 + Este operador indica que se hace referencia a una posición de memoria referenciada por el valor que hay dentro de los paréntesis. Dicho valor referencia a una celdilla de memoria de 8 bits. 
 + 
 + Es decir, si escribiéramos en BASIC del Spectrum (con ''PEEK'' y ''POKE'') las instrucciones de carga de 8 bits que referencian a la memoria, veríamos lo siguiente: 
 + 
 +<code z80> 
 +ld a, (16384)     =>    LET A = PEEK 16384 
 +ld (16384), a     =>    POKE 16384, a 
 + 
 +ld hl, 16384      =>    HL = 16384 
 +ld a, (hl)        =>    LET A = PEEK HL  =>    LET A = PEEK 16384 
 +ld (hl), a        =>    POKE HL, a       =>    POKE (16384), a 
 +</code> 
 + 
 +En el segundo ejemplo hemos utilizado ''ld hl, 16384'', que significa "carga en HL el valor 16384". Como no hay paréntesis en la instrucción, no estamos haciendo una referencia a memoria sino al valor inmediato 16384, el cual metemos en HL. Después, al utilizar los paréntesis en ''ld a, (hl)'', sí que hacemos una referencia a memoria, con la dirección absoluta contenida en HL. 
 + 
 +No sólo podemos leer de o escribir en una dirección de memoria valores de 8 bits, también podemos leer y escribir valores de 16 bits. Evidentemente, como la memoria es un conjunto de "celdillas de 8 bits", para leer o escribir valores de 16 bits lo haremos en 2 celdillas: la celdilla apuntada por la dirección, y la siguiente. 
 + 
 +De nuevo, viéndolo "en instrucciones BASIC", podemos ver la diferencia entre asignar un valor de 16 bits inmediato, o referenciar a una posición de memoria para leer 16 bits: 
 + 
 +<code z80> 
 +ld hl, 16384      =>    HL = 16384 
 + 
 +ld hl, (16384)    =>    HL = (PEEK 16384) + 256*(PEEK 16385) 
 +                           => L = PEEK 16384 
 +                              H = PEEK 16385 
 +</code> 
 + 
 +En ''ld hl, (16384)'', metemos en HL el dato de 16 bits en 16384 y 16385, un valor de 8 bits para cada uno de los 2 registrow de 8 bits de HL (concretamente, H será el valor contenido en 16385 y L el valor en 16384, posteriormente veremos por qué se leen en orden inverso). 
 + 
 +De la misma forma, si hablamos de escribir en memoria un valor de 16 bits: 
 + 
 +<code z80> 
 +ld (16384), hl    =>    POKE 16384, L 
 +                        POKE 16385, H 
 +</code>
  
 En un microprocesador con un juego de instrucciones ortogonal, se podría usar cualquier origen y cualquier destino sin distinción. En el caso del Z80 no es así. El listado completo de operaciones válidas con LD es el siguiente: En un microprocesador con un juego de instrucciones ortogonal, se podría usar cualquier origen y cualquier destino sin distinción. En el caso del Z80 no es así. El listado completo de operaciones válidas con LD es el siguiente:
Línea 279: Línea 316:
 ld r, N ld r, N
 ld rr, NN ld rr, NN
-LD ri, NN+ld ri, NN
  
 ; Copia de un registro a otro ; Copia de un registro a otro
Línea 293: Línea 330:
 ld a, (NN) ld a, (NN)
 ld rr, (NN) ld rr, (NN)
-LD ri, (NN)+ld ri, (NN)
 ld (NN), rr ld (NN), rr
 ld (NN), ri ld (NN), ri
  
 ; Acceso indexado a memoria ; Acceso indexado a memoria
-LD (ri+N), r+ld (ri+N), r
 ld r, (ri+N) ld r, (ri+N)
-LD (ri+N), N+ld (ri+N), N
 </code> </code>
  
Línea 324: Línea 361:
 ; Carga de valores en registros ; Carga de valores en registros
 ; registro_destino = valor ; registro_destino = valor
-ld a, 100          ; ld r, N +ld a, 100               ; ld r, N 
-ld bc, 12345       ; ld rr, NN+ld bc, 12345            ; ld rr, NN
  
 ; Copia de registros en registros ; Copia de registros en registros
 ; registro_destino = registro_origen ; registro_destino = registro_origen
-ld b, c            ; ld r, r +ld b, c                 ; ld r, r 
-ld a, b            ; ld r, r +ld a, b                 ; ld r, r 
-ld bc, de          ; ld rr, rr+ld bc, de               ; ld rr, rr
  
 ; Acceso a memoria ; Acceso a memoria
 ; (Posicion_memoria) = VALOR o bien ; (Posicion_memoria) = VALOR o bien
 ;  Registro = VALOR en (Posicion de memoria) ;  Registro = VALOR en (Posicion de memoria)
-ld a, (hl)         ; ld r, (rr) +ld a, (hl)              ; ld r, (rr) 
-ld (bL), b         ; ld (rr), r +ld (bc), a              ; ld (rr), r 
-ld (12345), a      ; ld (NN), a +ld (12345), a           ; ld (NN), a 
-ld a, (hl)         ; ld r, (rr) +ld a, (hl)              ; ld r, (rr) 
-ld (de), a         ; ld (rr), r +ld (de), a              ; ld (rr), r 
-ld (bc), 1234h     ; ld (bc), NN +ld (bc), 1234h          ; ld (bc), NN 
-ld (12345), de     ; ld (NN), rr +ld (12345), de          ; ld (NN), rr 
-ld ix, (12345)     ; LD ri, (NN) +ld ix, (12345)          ; LD ri, (NN) 
-ld (34567), iy     ; ld (NN), ri+ld (34567), iy          ; ld (NN), ri
  
 ; Acceso indexado a memoria ; Acceso indexado a memoria
 ; (Posicion_memoria) = VALOR o VALOR = (Posicion_memoria) ; (Posicion_memoria) = VALOR o VALOR = (Posicion_memoria)
 ; Donde la posicion es IX+N o IY+N: ; Donde la posicion es IX+N o IY+N:
-ld (ix+10), a      ; LD (ri+N), r +ld (ix+10), a           ; LD (ri+N), r 
-ld a, (iy+100)     ; ld r, (ri+N) +ld a, (iy+100)          ; ld r, (ri+N) 
-ld (ix-30), 100    ; LD (ri+N), N+ld (ix-30), 100         ; LD (ri+N), N
 </code> </code>
  
Línea 361: Línea 398:
                          Flags                          Flags
    Instrucción       |S Z H P N C|    Instrucción       |S Z H P N C|
- ----------------------------------+ ---------------------------------
    ld r, r           |- - - - - -|    ld r, r           |- - - - - -|
    ld r, N           |- - - - - -|    ld r, N           |- - - - - -|
Línea 367: Línea 404:
    ld (rr), n        |- - - - - -|    ld (rr), n        |- - - - - -|
    ld (rr), n        |- - - - - -|    ld (rr), n        |- - - - - -|
-   LD ri, (NN)       |- - - - - -|+   ld ri, (NN)       |- - - - - -|
    ld (NN), ri       |- - - - - -|    ld (NN), ri       |- - - - - -|
-   LD (ri+d), N      |- - - - - -| +   ld (ri+d), N      |- - - - - -| 
-   LD (ri+d), r      |- - - - - -|+   ld (ri+d), r      |- - - - - -|
    ld r, (ri+d)      |- - - - - -|    ld r, (ri+d)      |- - - - - -|
    ld a, i           |* * 0 * 1 0|    ld a, i           |* * 0 * 1 0|
Línea 379: Línea 416:
 Esto quiere decir, y es muy importante, que una operación como ''ld a, 0'', por ejemplo, no activará el flag de Zero del registro F. Esto quiere decir, y es muy importante, que una operación como ''ld a, 0'', por ejemplo, no activará el flag de Zero del registro F.
  
-Al respecto de escritura y lectura de valores de 16 bits utilizando instrucciones que trabajan con 8 bits, queremos recordar en este punto que el Z80 es una CPU LITTLE-ENDIAN por lo que los valores de 16 bits aparecerán en memoria "invertidos", es decir, primero el byte menos significativo y en la celdilla siguiente el byte más significativo.+\\  
 +===== Tamaños y ciclos ===== 
 + 
 +Hay otros dos datos que, como la afectación de flags, son muy importantes sobre las diferentes instrucciones que iremos viendo. 
 + 
 +Uno es el tamaño en bytes de cada instrucción, que viene determinado por los opcodes que ocupa. Así, un ''ld a, b'' ocupa un sólo byte (**$78**), ''ld a, $ff'' ocupa 2 bytes (al opcode **$3E** le sigue el operando **$ff**) y ''ld bc, $1234'' ocupa 3 bytes (al opcode **01** le siguen los 2 bytes de $1234). 
 + 
 +Del mismo modo, tenemos el tiempo que tarda en ejecutarse cada una de ellas, lo que se conoce como el número de **ciclos** (o **t-estados** / **t-states**). Este es el tiempo que tarda el procesador Z80 en leer de memoria, decodificar y ejecutar cada instrucción. Cada lectura de byte de memoria requiere en general de 3 t-estados extra, así que no lo es lo mismo una instrucción sencilla de un sólo byte de opcode como ''ld a, b'' que una con 3 bytes como ''ld bc, $1234''
 + 
 +En nuestro ejemplo anterior, el ''ld a, b'' de un sólo byte se ejecuta en 4 ciclos de reloj (3 para leer de memoria el opcode y 1 para ejecutarlo), el ''ld a, $ff'' son 7 ciclos de reloj o t-estados (los 3 de leer de memoria el primer byte, 3 de leer el segundo byte, y uno más para la ejecución) y ''ld bc, $1234'' que ocupa 3 bytes requiere 3+3+3+1 = 10 ciclos de reloj. 
 + 
 +En estos capítulos iniciales del curso no nos deben de preocupar tanto lo que ocupan las instrucciones y cuánto tardan en ejecutarse como el saber qué instrucciones existen, la manera en que operan y cómo se utilizan. No obstante, sí que necesitaremos más adelante tener un buen conocimiento de tamaño y tiempo de ejecución de cada instrucción para desarrollar programas. 
 + 
 +En el último capítulo dedicado a las diferentes instrucciones veremos una tabla donde se detallan todos los tamaños y tiempos de las diferentes instrucciones. 
 + 
 +Un apunte sobre ''ld bc, $1234'': Al respecto de escritura y lectura de valores de 16 bits utilizando instrucciones que trabajan con 8 bits, queremos recordar en este punto que el Z80 es una CPU LITTLE-ENDIAN por lo que los valores de 16 bits aparecerán en memoria "invertidos", es decir, primero el byte menos significativo y en la celdilla siguiente el byte más significativo. Es decir, que el opcode correspondiente a ''ld bc, $1234'' en memoria no es "**$01 $12 $34**" sino "**$01 $34 $12**";
  
 \\  \\ 
Línea 389: Línea 441:
  
 <code z80> <code z80>
-ld a, 0      ; A = 0 +ld a, 0                ; A = 0 
-inc a        ; A = A+1 = 1 +inc a                  ; A = A+1 = 1 
-ld b, a      ; B = A = 1 +ld b, a                ; B = A = 1 
-inc b        ; B = B+1 = 2 +inc b                  ; B = B+1 = 2 
-inc b        ; B = B+1 = 3+inc b                  ; B = B+1 = 3
 ld  bc, 0 ld  bc, 0
-inc bc       ; BC = 0001h +inc bc                 ; BC = $0001 
-inc b        ; BC = 0101h (ya que B=B+1 y es la parte alta) +inc b                  ; BC = $0101 (ya que B=B+1 y es la parte alta) 
-dec a        ; A = A-1 = 0+dec a                  ; A = A-1 = 0
 </code> </code>
  
Línea 430: Línea 482:
  
 <code z80> <code z80>
-inc a          ; A = A+1 +inc a               ; A = A+1 
-dec b          ; B = B-1 +dec b               ; B = B-1 
-inc de         ; DE = DE+1 +inc de              ; DE = DE+1 
-dec ix         ; IX = IX-1 +dec ix              ; IX = IX-1 
-inc (hl)       ; (HL) = (HL)+1 +inc (hl)            ; (HL) = (HL)+1 
-inc (ix-5)     ; (IX-5) = (IX-5)+1 +inc (ix-5)          ; (IX-5) = (IX-5)+1 
-dec (iy+100)   ; (IY+100) = (IY+100)+1+dec (iy+100)        ; (IY+100) = (IY+100)+1
 </code> </code>
  
Línea 456: Línea 508:
  ----------------------------------  ----------------------------------
    inc r             |* * * V 0 -|    inc r             |* * * V 0 -|
-   INC [HL]          |* * * V 0 -| +   inc (hl)          |* * * V 0 -| 
-   INC [ri+N       |* * * V 0 -|+   inc (ri+N       |* * * V 0 -|
    inc rr            |- - - - - -|    inc rr            |- - - - - -|
    dec r             |* * * V 1 -|    dec r             |* * * V 1 -|
Línea 494: Línea 546:
  
 <code z80> <code z80>
-ADD DESTINO, ORIGEN+add DESTINO, ORIGEN
 </code> </code>
  
Línea 500: Línea 552:
  
 <code z80> <code z80>
-ADD A, s +add a, s 
-ADD HL, ss +add hl, ss 
-ADD ri, rr+add ri, rr
 </code> </code>
  
Línea 522: Línea 574:
  
 <code z80> <code z80>
-ADD A, s +add a, s 
-add a, b        ; A = A + B +add a, b             ; A = A + B 
-add a, 100      ; A = A + 100 +add a, 100           ; A = A + 100 
-ADD A[HL]     ; A = A + [HL] +add a(hl)          ; A = A + (HL) 
-ADD A[IX+10]  ; A = A + [IX+10]+add a(ix+10)       ; A = A + (IX+10)
  
-ADD HL, ss +add hl, ss 
-add hl, bc      ; HL = HL + BC +add hl, bc           ; HL = HL + BC 
-add hl, sp      ; HL = HL + SP+add hl, sp           ; HL = HL + SP
  
-ADD ri, rr +addri, rr 
-add ix, bc      ; IX = IX + BC +add ix, bc           ; IX = IX + BC 
-add iy, de      ; IY = IY + DE +add iy, de           ; IY = IY + DE 
-add iy, ix      ; IY = IY + IX +add iy, ix           ; IY = IY + IX 
-add ix, iy      ; IX = IX + IY+add ix, iy           ; IX = IX + IY
 </code> </code>
  
Línea 542: Línea 594:
  
 <code z80> <code z80>
-add b, c      ; Sólo A puede ser destino +add b, c             ; Sólo A puede ser destino 
-add bc, de    ; Sólo puede ser destino HL +add bc, de           ; Sólo puede ser destino HL 
-add ix, ix    ; No podemos sumar un registro índice a él mismo+add ix, ix           ; No podemos sumar un registro índice a él mismo
 </code> </code>
  
 La afectación de los flags ante las operaciones de sumas es la siguiente: La afectación de los flags ante las operaciones de sumas es la siguiente:
  
-   * Para ''ADD A, s'', el registro N (Substraction) se pone a 0 (lógicamente, ya que sólo se pone a uno cuando se ha realizado una resta). El registro P/V se comporta como un registro de Overflow e indica si ha habido overflow (desbordamiento) en la operación. El resto de flags (Sign, Zero, Half-Carry y Carry) se verán afectados de acuerdo al resultado de la operación de suma.+   * Para ''add a, s'', el registro N (Substraction) se pone a 0 (lógicamente, ya que sólo se pone a uno cuando se ha realizado una resta). El registro P/V se comporta como un registro de Overflow e indica si ha habido overflow (desbordamiento) en la operación. El resto de flags (Sign, Zero, Half-Carry y Carry) se verán afectados de acuerdo al resultado de la operación de suma.
  
-   * Para ''ADD HL, ss'' y ''ADD ri, rr'', se pone a 0 el flag N, y sólo se verá afectado el flag de acarreo (C) de acuerdo al resultado de la operación.+   * Para ''add hl, ss'' y ''add ri, rr'', se pone a 0 el flag N, y sólo se verá afectado el flag de acarreo (C) de acuerdo al resultado de la operación.
  
 O, en forma de tabla de afectación: O, en forma de tabla de afectación:
Línea 559: Línea 611:
    Instrucción       |S Z H P N C|    Instrucción       |S Z H P N C|
  ----------------------------------  ----------------------------------
- ADD A, s            |* * * V 0 *| + add a, s            |* * * V 0 *| 
- ADD HL, ss          |- - ? - 0 *| + add hl, ss          |- - ? - 0 *| 
- ADD ri, rr          |- - ? - 0 *|+ add ri, rr          |- - ? - 0 *|
 </code> </code>
  
Línea 600: Línea 652:
 ==== Resta: SUB (Substract) ==== ==== Resta: SUB (Substract) ====
  
-En el caso de las restas, sólo es posible realizar (de nuevo gracias a la no ortogonalidad del J.I. del Z80) la operación "A=A-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 ''SUB'' no requiere 2 operandos, ya que el registro destino sólo puede ser A:+En el caso de las restas, sólo es posible realizar (de nuevo gracias a la no ortogonalidad del J.I. del Z80) la operación "A=A-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 ''SUB'' no requiere 2 operandos, ya que el registro destino sólo puede ser A:
  
 <code z80> <code z80>
-SUB ORIGEN+sub ORIGEN
 </code> </code>
  
Línea 609: Línea 661:
  
 <code z80> <code z80>
-sub r        ; A = A - r +sub r                ; A = A - r 
-SUB        ; A = A - N +sub                ; A = A - N 
-SUB [HL]     ; A = A - [HL] +sub (hl)             ; A = A - (HL) 
-SUB [rr+d]   ; A = A - [rr+d]+sub (rr+d)           ; A = A - (rr+d)
 </code> </code>
  
Línea 620: Línea 672:
 sub b           ; A = A - B sub b           ; A = A - B
 sub 100         ; A = A - 100 sub 100         ; A = A - 100
-SUB [HL]        ; A = A - [HL] +sub (hl)        ; A = A - (HL) 
-SUB [IX+10    ; A = A - [IX+10]+sub (ix+10    ; A = A - (IX+10)
 </code> </code>
  
Línea 642: Línea 694:
  
 <code> <code>
-"ADC A, s"    equivale a    "A = A + s + CarryFlag" +"adc a, s"    equivale a    "A = A + s + CarryFlag" 
-"ADC HL, ss"  equivale a    "HL = HL + ss + CarryFlag"+"adc hl, ss"  equivale a    "HL = HL + ss + CarryFlag"
 </code> </code>
  
Línea 654: Línea 706:
   Instrucción       |S Z H P N C|   Instrucción       |S Z H P N C|
  ----------------------------------  ----------------------------------
- ADC A,s            |* * * V 0 *| + adc a,s            |* * * V 0 *| 
- ADC HL,ss          |* * ? V 0 *|+ adc hl,ss          |* * ? V 0 *|
 </code> </code>
  
Línea 796: Línea 848:
 Como ya se ha explicado, disponemos de un banco de registros alternativos (los Shadow Registers), y podemos conmutar los valores entre los registros estándar y los alternativos mediante unas determinadas instrucciones del Z80. Como ya se ha explicado, disponemos de un banco de registros alternativos (los Shadow Registers), y podemos conmutar los valores entre los registros estándar y los alternativos mediante unas determinadas instrucciones del Z80.
  
-El Z80 nos proporciona una serie de registros de propósito general (así como un registro de flags), de nombres A, B, C, D, E, F, H y L. El micro dispone también de unos registros extra (set alternativo conocido como Shadow Registers) de nombre A', B', C', D', E', F', H' y L', que aprovecharemos en cualquier momento de nuestro programa. No obstante, no podremos hacer uso directo de estos registros en instrucciones en ensamblador. No es posible, por ejemplo, ninguna de las siguientes instrucciones:+El Z80 nos proporciona una serie de registros de propósito general (así como un registro de flags), de nombres A, B, C, D, E, F, H y L. El micro dispone también de unos registros extra (set alternativo conocido como Shadow Registers) de nombre A', B', C', D', E', F', H' y L', que aprovecharemos en cualquier momento de nuestro programa. No obstante, no podremos hacer uso directo de estos registros en instrucciones en ensamblador. **No es posible**, por ejemplo, usar ninguna de las siguientes instrucciones (porque no existen):
  
 <code> <code>
-LD B', $10+ld b', $10
 inc a' inc a'
-LD HL', $1234 +ld hl', $1234 
-LD A', ($1234)+ld a', ($1234)
 </code> </code>
  
Línea 833: Línea 885:
 |< 60% >| |< 60% >|
 ^ Registro ^ Valor ^ Registro ^ Valor ^ ^ Registro ^ Valor ^ Registro ^ Valor ^
-| A | 01h | A' | 00h +| A | $01 | A' | $00 
-| F | 10h | F' | 00h |+| F | $10 | F' | $00 |
  
 a: a:
Línea 840: Línea 892:
 |< 60% >| |< 60% >|
 ^ Registro ^ Valor ^ Registro ^ Valor ^ ^ Registro ^ Valor ^ Registro ^ Valor ^
-| A | 00h | A' | 01h +| A | $00 | A' | $01 
-| F | 00h | F' | 10h |+| F | $00 | F' | $10 |
  
 Realizando de nuevo un ''ex af, af<nowiki>'</nowiki>'' volveríamos a los valores originales en ambos registros. Realizando de nuevo un ''ex af, af<nowiki>'</nowiki>'' volveríamos a los valores originales en ambos registros.
Línea 868: Línea 920:
     ld ($1236), a     ld ($1236), a
  
-    ; Recuperamos los valores de los registros+    ; Recuperamos los registros:
     ex af, af'                       ; Intercambiamos AF con AF'     ex af, af'                       ; Intercambiamos AF con AF'
     exx     exx
Línea 894: Línea 946:
 </code> </code>
  
-Además de ''EXX'' y ''ex afaf<nowiki>'</nowiki>'' tenemos disponibles 3 instrucciones de intercambio más que no trabajan con los registros alternativos, sino entre la memoria y registros, y la pila (o memoria en general) y los registros HL, IX e IY.+Además de ''EXX'' y ''EX AFAF<nowiki>'</nowiki>'' tenemos disponibles 3 instrucciones de intercambio más que no trabajan con los registros alternativos, sino entre la memoria y registros, y la pila (o memoria en general) y los registros HL, IX e IY.
  
 |< 50% >| |< 50% >|
Línea 901: Línea 953:
 | ex (sp), hl | Intercambiar el valor de HL con el valor de 16 bits\\ de la posición de memoria apuntada por el registro SP\\ (por ejemplo, para intercambiar el valor de HL con el\\ del último registro que hayamos introducido en la pila). | | ex (sp), hl | Intercambiar el valor de HL con el valor de 16 bits\\ de la posición de memoria apuntada por el registro SP\\ (por ejemplo, para intercambiar el valor de HL con el\\ del último registro que hayamos introducido en la pila). |
 | ex (sp), ix | Igual que el anterior, pero con IX. | | ex (sp), ix | Igual que el anterior, pero con IX. |
-EX (SP), IY | Igual que el anterior, pero con IY. |+ex (sp), iy | Igual que el anterior, pero con IY. |
  
 La primera de estas instrucciones nos resultará muy útil en nuestros programas en ensamblador, ya que nos permite intercambiar los valores de los registros DE y HL. Las 3 instrucciones restantes permiten intercambiar el valor apuntado por SP (en memoria) por el valor de los registros HL, IX o IY. La primera de estas instrucciones nos resultará muy útil en nuestros programas en ensamblador, ya que nos permite intercambiar los valores de los registros DE y HL. Las 3 instrucciones restantes permiten intercambiar el valor apuntado por SP (en memoria) por el valor de los registros HL, IX o IY.
  
-Como ya hemos comentado cuando hablamos del carácter Low-Endian de nuestra CPU, al escribir en memoria (también en la pila) primero se escribe el Byte Bajo y luego el Byte Alto. Posteriormente lo leeremos de la misma forma, de tal modo que si los bytes apuntados en la pila (en memoria) son "$ff $00", al hacer el EX (SP), hl, el registro HL valdrá "$00ff".+Como ya hemos comentado cuando hablamos del carácter Low-Endian de nuestra CPU, al escribir en memoria (también en la pila) primero se escribe el Byte Bajo y luego el Byte Alto. Posteriormente lo leeremos de la misma forma, de tal modo que si los bytes apuntados en la pila (en memoria) son **$ff $00**, al hacer el ''ex (sp), hl'', el registro HL valdrá **$00ff**.
  
 Nótese que aprovechando la pila (como veremos en su momento) también podemos intercambiar los valores de los registros mediante: Nótese que aprovechando la pila (como veremos en su momento) también podemos intercambiar los valores de los registros mediante:
Línea 952: Línea 1004:
     push bc              ; Necesitamos salvaguardar BC     push bc              ; Necesitamos salvaguardar BC
     ld b, 9              ; (porque vamos a usarlo para algo)     ld b, 9              ; (porque vamos a usarlo para algo)
 +
     ... hacer algo con BC ...     ... hacer algo con BC ...
 +    
     pop bc               ; recuperar el valor de BC     pop bc               ; recuperar el valor de BC
 </code> </code>
Línea 962: Línea 1016:
     ld (1000), bc        ; Necesitamos salvaguardar BC     ld (1000), bc        ; Necesitamos salvaguardar BC
     ld b,9               ; (porque vamos a usarlo para algo)     ld b,9               ; (porque vamos a usarlo para algo)
 +    
     .....     .....
 +    
     ld (1002), bc        ; guardamos el resultado     ld (1002), bc        ; guardamos el resultado
     ld bc, (1000)        ; restauramos el valor de BC     ld bc, (1000)        ; restauramos el valor de BC
Línea 973: Línea 1029:
  
 <code z80> <code z80>
-    LD (save_bc+1), BC    ; Escribimos BC en la parte NN NN del+    ld (save_bc+1), bc    ; Escribimos BC en la parte NN NN del
                           ; opcode "ld bc, NN NN" en memoria                           ; opcode "ld bc, NN NN" en memoria
  
Línea 986: Línea 1042:
 </code> </code>
  
-El ejemplo anterior es muy interesante. Cuando hacemos el ''LD (save_bc+1), BC'', estamos sobreescribiendo nuestro propio programa. Concretamente, lo que hacemos es CAMBIAR el opcode que estaba ensamblado (''ld bc, $0000'', que en memoria sería "**$01 $00 $00**") por **$01 XX XX**.+El ejemplo anterior es muy interesante. Cuando hacemos el ''ld (save_bc+1), bc'', estamos sobreescribiendo nuestro propio programa. Concretamente, lo que hacemos es CAMBIAR el opcode que estaba ensamblado (''ld bc, $0000'', que en memoria sería "**$01 $00 $00**") por **$01 XX XX**.
  
-En este caso ''save_bc'' apuntaría al $01, y con ''save_bc+1'' lo que hacemos es escribir después del opcode de ''ld bc,'', en la parte del opcode que contiene el valor a cargar en el registro. Cuando se ejecuta el ''LD (save_bc+1), BC'', estamos escribiendo el valor de BC en ese momento encima de "XX XX" de forma que cuando la ejecución del programa continúe y lleguemos a ese punto, el comando ''ld bc, XX'' se ejecutará y recuperará en BC el valor que tenía BC cuando se almacenó en esa posición de memoria.+En este caso ''save_bc'' apuntaría al $01, y con ''save_bc+1'' lo que hacemos es escribir después del opcode de ''ld bc,'', en la parte del opcode que contiene el valor a cargar en el registro. Cuando se ejecuta el ''ld (save_bc+1), bc'', estamos escribiendo el valor de BC en ese momento encima de "XX XX" de forma que cuando la ejecución del programa continúe y lleguemos a ese punto, el comando ''ld bc, XX'' se ejecutará y recuperará en BC el valor que tenía BC cuando se almacenó en esa posición de memoria.
  
-Con esto, estamos preservando el valor del registro a cambio de una escritura en memoria (''ld (nn), bc'' = 20 ciclos de reloj) y de su posterior asignación (10 ciclos), un total de 30 ciclos de reloj. La alternativa de PUSH POP utilizaría 21 ciclos en total (11 el ''PUSH'' y 10 el ''POP'').+Con esto, estamos preservando el valor del registro a cambio de una escritura en memoria (''ld (nn), bc'' = 20 ciclos de reloj) y de su posterior asignación (10 ciclos), un total de 30 ciclos de reloj. La alternativa con PUSH/POP utilizaría 21 ciclos en total (11 el ''PUSH'' y 10 el ''POP'').
  
 \\  \\ 
  • cursos/ensamblador/lenguaje_1.1705645661.txt.gz
  • Última modificación: 19-01-2024 06:27
  • por sromero