Diferencias
Muestra las diferencias entre dos versiones de la página.
Ambos lados, revisión anterior Revisión previa Próxima revisión | Revisión previa | ||
cursos:ensamblador:lenguaje_1 [13-01-2024 10:14] – sromero | cursos:ensamblador:lenguaje_1 [22-01-2024 07:51] (actual) – [Instrucciones LD (instrucciones de carga)] sromero | ||
---|---|---|---|
Línea 3: | Línea 3: | ||
====== Arquitectura del Z80 e Instrucciones básicas ====== | ====== Arquitectura del Z80 e Instrucciones básicas ====== | ||
- | \\ | + | Empezaremos nuestro periplo por el ensamblador del Z80 viendo |
- | + | ||
- | En este capítulo explicaremos la sintaxis utilizada en los programas en ensamblador. Para ello comenzaremos con una definición general de la sintaxis para el ensamblador | + | |
- | + | ||
- | Posteriormente veremos | + | |
- | + | ||
- | Esta entrega del curso es delicada y complicada: por un lado, tenemos que explicar las normas y sintaxis del ensamblador cruzado Pasmo antes de que conozcamos la sintaxis del lenguaje ensamblador en sí, y por el otro, no podremos utilizar Pasmo hasta que conozcamos la sintaxis del lenguaje. | + | |
- | + | ||
- | Además, el lenguaje ensamblador tiene disponibles muchas instrucciones diferentes, y nos resultaría imposible explicarlas todas en un mismo capítulo, lo que nos fuerza a explicar las instrucciones del microprocesador en varias entregas. Esto implica que hablaremos de Pasmo comentando reglas, opciones de instrucciones y directivas que todavía no conocemos. | + | |
- | + | ||
- | Es por esto que recomendamos al lector que, tras releer anteriores capítulos de este libro, se tome esta entrega de una manera especial, leyéndola 2 veces. La " | + | |
- | + | ||
- | \\ | + | |
- | ===== Sintaxis del lenguaje ASM en Pasmo ===== | + | |
- | + | ||
- | En anteriores capítulos ya hablamos de Pasmo, el ensamblador cruzado que recomendamos para el desarrollo de programas para Spectrum. Este ensamblador traduce nuestros ficheros de texto .asm con el código fuente de programa (en lenguaje ensamblador) a ficheros .bin (o .tap/.tzx) que contendrán el código máquina directamente ejecutable por el Spectrum. | + | |
- | + | ||
- | Supondremos para el resto del capítulo que ya tenéis instalado Pasmo (ya sea la versión Windows o la de UNIX/Linux) en vuestro sistema y que sabéis utilizarlo de forma básica (bastará con saber realizar un simple ensamblado de programa, como ya vimos en el primer capítulo), y que podéis ejecutarlo dentro del directorio de trabajo que habéis elegido. | + | |
- | + | ||
- | El ciclo de desarrollo con Pasmo será el siguiente: | + | |
- | + | ||
- | * Con un editor de texto, tecleamos nuestro programa en un fichero .ASM con la sintaxis que veremos a continuación. | + | |
- | * Salimos del editor de texto y ensamblamos el programa: | + | |
- | * Si queremos generar un fichero .bin de código objeto cuyo contenido POKEar en memoria (o cargar con '' | + | |
- | * Si queremos generar un fichero .tap directamente ejecutable (de forma que sea pasmo quien añada el cargador BASIC), lo ensamblamos con '' | + | |
- | + | ||
- | Todo esto se mostró bastante detalladamente en su momento en el primer capítulo del curso. | + | |
- | + | ||
- | Con esto, ya sabemos ensamblar programas creados adecuadamente, | + | |
- | + | ||
- | Es sencillo: escribiremos nuestro programa en un fichero de texto con extensión .asm. En este fichero de texto se ignorarán las líneas en blanco y los comentarios, | + | |
- | + | ||
- | Lo que vamos a ver a continuación son las normas que debe cumplir un programa para poder ser ensamblado en Pasmo. Es necesario explicar estas reglas para que el lector pueda consultarlas en el futuro, cuando esté realizando sus propios programas. No te preocupes si no entiendes alguna de las reglas, cuando llegues al momento de implementar tus primeras rutinas, las siguientes normas te serán muy útiles:\\ | + | |
- | + | ||
- | * **Normas para las instrucciones: | + | |
- | * Pondremos una sóla instrucción de ensamblador por línea. | + | |
- | * Como existen diferencias entre los "fin de línea" | + | |
- | * Dado que cada editar renderiza los tabuladores con diferentes tamaños, recomendamos utilizar espacios para indentar. Para eso, utiliza la indentación de " | + | |
- | * Además de una instrucción, | + | |
- | * Las instrucciones y los registros pueden estar tanto en mayúsculas como en minúsculas. Es igual de válido '' | + | |
- | * De la misma forma, hay quien pone espacios tras las comas y quien no. Es igual de válido '' | + | |
- | \\ | + | |
- | * **Normas para los valores numéricos: | + | |
- | * Todos los valores numéricos se considerarán, | + | |
- | * Para introducir valores números en hexadecimal los precederemos del carácter " | + | |
- | * Podremos también especificar la base del literal poniendoles como prefijo las cadena &H ó 0x (para hexadecimal) o &O (para octal). | + | |
- | * Podemos especificar también los números mediante sufijos: Usando una " | + | |
- | \\ | + | |
- | * **Normas para cadenas de texto: ** | + | |
- | * Podemos separar las cadenas de texto mediante comillas simples o dobles. | + | |
- | * El texto encerrado entre comillas simples no recibe ninguna interpretación, | + | |
- | * El texto encerrado entre comillas dobles permite introducir caracteres especiales al estilo de C/C++ como \n, \r o \t (nueva línea, retorno de carro, tabulador...). | + | |
- | * El texto encerrado entre comillas dobles también admite \xNN para introducir el carácter correspondiente a un número hexadecimal NN. | + | |
- | * Una cadena de texto de longitud 1 (un carácter) puede usarse como una constante (valor ASCII del carácter) en expresiones como, por ejemplo, ' | + | |
- | \\ | + | |
- | * **Normas para los nombres de ficheros: | + | |
- | * Si vemos que nuestro programa se hace muy largo y por lo tanto incómodo para editarlo, podemos partir el fichero en varios ficheros e incluirlos mediante directivas INCLUDE (para incluir ficheros ASM) o INCBIN (para incluir código máquina ya compilado). Al especificar nombres de ficheros, deberán estar entre dobles comillas o simples comillas. | + | |
- | \\ | + | |
- | * **Normas para los identificadores: | + | |
- | * Los identificadores son los nombres usados para etiquetas y también los símbolos definidos mediante EQU y DEFL. | + | |
- | * Podemos utilizar cualquier cadena de texto, excepto los nombres de las palabras reservadas de ensamblador. | + | |
- | \\ | + | |
- | * **Normas para las etiquetas: | + | |
- | * Una etiqueta es un identificador de texto que ponemos poner al principio de cualquier línea de nuestro programa, por ejemplo: " | + | |
- | * Podemos añadir el tradicional sufijo ":" | + | |
- | * Para un programa ensamblador, | + | |
- | * Debido a esto, no podemos usar 2 etiquetas iguales en el programa. Es decir, dentro de 2 rutinas diferentes donde hacemos algún bucle, no podemos usar una etiqueta de nombre por ejemplo " | + | |
- | \\ | + | |
- | * **Directivas: | + | |
- | * Tenemos a nuestra disposición una serie de directivas para facilitarnos la programación, | + | |
- | * Una de las directivas más importantes es '' | + | |
- | * La directiva '' | + | |
- | * Iremos viendo el significado de las directivas conforme las vayamos usando, pero es aconsejable [[http:// | + | |
- | \\ | + | |
- | * **Operadores** | + | |
- | * Podemos utilizar los operadores típicos +, -, *. /, así como otros operadores de desplazamiento de bits como >> y <<. | + | |
- | * Tenemos disponibles operadores de comparación como EQ, NE, LT, LE, GT, GE o los clásicos =, !=, <, >, <=, >=. | + | |
- | * Existen también operadores lógicos como AND, OR, NOT, o sus variantes &, |, !. | + | |
- | * Los operadores sólo tienen aplicación en tiempo de ensamblado, es decir, no podemos multiplicar o dividir en tiempo real en nuestro programa usando * o /. Estos operadores están pensados para que podamos poner expresiones como 32*10+12, en lugar del valor numérico del resultado, por ejemplo. | + | |
- | * A la hora de utilizar el paréntesis para poner un cálculo inmediato en una expresión, como por ejemplo ((32*10)+12), | + | |
- | + | ||
- | \\ | + | |
- | ===== Aspecto de un programa en ensamblador ===== | + | |
- | + | ||
- | Veamos un ejemplo de programa en ensamblador que muestra el uso de algunas de estas normas, para que las podamos entender fácilmente mediante los comentarios incluidos: | + | |
- | + | ||
- | <code z80> | + | |
- | ; Programa de ejemplo para mostrar el aspecto de | + | |
- | ; un programa típico en ensamblador para Pasmo. | + | |
- | ; Copia una serie de bytes a la videomemoria con | + | |
- | ; instrucciones simples (sin optimizar). | + | |
- | ORG 40000 | + | |
- | + | ||
- | ; Aqui empieza nuestro programa que copia los | + | |
- | ; 7 bytes desde la etiqueta " | + | |
- | ; videomemoria ([16384] en adelante). | + | |
- | + | ||
- | LD HL, destino | + | |
- | LD DE, datos ; DE = origen de los datos | + | |
- | LD B, 6 ; numero de datos a copiar | + | |
- | + | ||
- | bucle: | + | |
- | + | ||
- | LD A, (DE) ; Leemos un dato de [DE] | + | |
- | ADD A, valor ; Le sumamos 1 al dato leído | + | |
- | LD (HL), A ; Lo grabamos en el destino [HL] | + | |
- | INC DE ; Apuntamos al siguiente dato | + | |
- | INC HL ; Apuntamos al siguiente destino | + | |
- | + | ||
- | DJNZ bucle ; Equivale a: | + | |
- | ; B = B-1 | + | |
- | ; if (B>0) goto Bucle | + | |
- | RET | + | |
- | + | ||
- | valor | + | |
- | destino | + | |
- | + | ||
- | datos DEFB 127, %10101010, 0, 128, $FE, %10000000, 00h | + | |
- | + | ||
- | END | + | |
- | </ | + | |
- | + | ||
- | Algunos detalles a tener en cuenta: | + | |
- | + | ||
- | * Se utiliza normalmente una instrucción por línea. Se puede poner más de una separándolas normalmente por ':' | + | |
- | + | ||
- | * Los comentarios pueden ir en sus propias líneas, o la derecha de las propias instrucciones. | + | |
- | + | ||
- | * Podemos definir " | + | |
- | + | ||
- | * Podemos poner etiquetas (como '' | + | |
- | + | ||
- | * Podemos ubicar en cualquier posición de memoria (antes o después de las rutinas) datos (que no instrucciones) para utilizarlas como hacemos en el ejemplo anterior con la directiva '' | + | |
- | + | ||
- | * Los datos definidos con '' | + | |
- | + | ||
- | Podéis ensamblar el ejemplo anterior con pasmo mediante: | + | |
- | + | ||
- | < | + | |
- | pasmo --tapbas ejemplo.asm ejemplo.tap | + | |
- | </ | + | |
- | + | ||
- | Una vez cargado y ejecutado el TAP en el emulador de Spectrum, podréis ejecutar el código máquina en BASIC con un '' | + | |
- | + | ||
- | \\ | + | |
- | {{ cursos: | + | |
- | \\ | + | |
- | Los píxeles que aparecen | + | El lenguaje ensamblador tiene disponibles muchas instrucciones diferentes, y resultaría imposible explicarlas todas en un mismo capítulo, lo que nos fuerza a explicar |
- | Si cambiáis el '' | + | Comencemos con los conceptos más importantes y las instrucciones más básicas, como las instrucciones |
\\ | \\ | ||
===== Los registros ===== | ===== Los registros ===== | ||
- | Como ya vimos en la anterior entrega, todo el " | + | Como ya vimos en el capítulo dedicado a la Arquitectura del Spectrum y del Z80, todo el " |
El Z80 tiene una serie de registros de 8 bits con nombres específicos: | El Z80 tiene una serie de registros de 8 bits con nombres específicos: | ||
Línea 169: | 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 | + | * **AF**: Formado por el registro A como byte más significativo (Byte alto) y por F como byte menos significativo (Byte bajo). Si '' |
* **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 " | * **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 " | ||
* **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 '' | * **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 '' | ||
Línea 181: | Línea 35: | ||
Por último, tenemos disponible un banco alternativo de registros, conocidos como **Shadow Registers** o //Registros Alternativos//, | Por último, tenemos disponible un banco alternativo de registros, conocidos como **Shadow Registers** o //Registros Alternativos//, | ||
- | 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 '' | + | 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 '' |
Ya conocemos los registros disponibles, | Ya conocemos los registros disponibles, | ||
Línea 193: | Línea 47: | ||
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
- | ; con esto, BC = $0100 | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | ; Ahora BC vale $0200 | + | |
- | | + | |
- | ; (BC = $0200+1 = $0201) | + | |
</ | </ | ||
- | | + | |
Cabe destacar un gran inconveniente del juego de instrucciones del Z80, y es que no es // | Cabe destacar un gran inconveniente del juego de instrucciones del Z80, y es que no es // | ||
Línea 215: | Línea 69: | ||
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
Línea 227: | Línea 81: | ||
<code z80> | <code z80> | ||
- | LD SP, BC ; NO: No se puede cargar el valor un registro en SP, | + | ld sp, bc ; NO: No se puede cargar el valor un registro en SP, |
; sólo se puede cargar un valor inmediato NN | ; sólo se puede cargar un valor inmediato NN | ||
- | EX BC, DE ; NO: Existe | + | ex bc, de ; NO: Existe |
- | 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 |
; en las sumas de 16 bytes con registros de propósito | ; en las sumas de 16 bytes con registros de propósito | ||
; general. Una alternativa sería: | ; general. Una alternativa sería: | ||
; | ; | ||
- | ; LD HL, 0 ; HL = 0 | + | ; ld hl, 0 ; HL = 0 |
- | ; ADD HL, BC ; HL = HL + BC | + | ; add hl, bc ; HL = HL + BC |
- | ; EX DE, HL ; Intercambiamos el valor de HL y DE | + | ; ex de, hl ; Intercambiamos el valor de HL y DE |
- | LD BC, DE ; NO:, pero se pueden tomar alternativas, | + | ld bc, de ; NO:, pero se pueden tomar alternativas, |
; | ; | ||
- | ; PUSH DE | + | ; push de |
- | ; POP BC | + | ; pop bc |
: | : | ||
; o también: | ; o también: | ||
; | ; | ||
- | ; LD B, D | + | ; ld b, d |
- | ; LD C, E | + | ; ld c, e |
- | LD DE, HL ; NO: mismo caso anterior. | + | ld de, hl ; NO: mismo caso anterior. |
- | LD SP, BC ; NO: no existe como instrucción. | + | ld sp, bc ; NO: no existe como instrucción. |
</ | </ | ||
Línea 270: | Línea 124: | ||
Debido a esto, no debemos modificar el registro IY, a menos que cumplamos estas 2 condiciones: | Debido a esto, no debemos modificar el registro IY, a menos que cumplamos estas 2 condiciones: | ||
- | 1.- Cambiamos el modo de funcionamiento del Spectrum de IM1 (su modo por defecto) a IM2 (un modo " | + | |
- | 2.- No llamamos ni utilizamos ninguna rutina de la ROM para ninguna tarea. | + | - No llamamos ni utilizamos ninguna rutina de la ROM para ninguna tarea. |
Ambas cosas son el procedimiento habitual en un juego o programa, por lo que podremos utilizar el registro IY en nuestros programas, pero no inicialmente. A lo largo del curso no lo usaremos ya que vamos a utilizar rutinas de la ROM de apoyo para la mayoría de nuestros ejemplos. | Ambas cosas son el procedimiento habitual en un juego o programa, por lo que podremos utilizar el registro IY en nuestros programas, pero no inicialmente. A lo largo del curso no lo usaremos ya que vamos a utilizar rutinas de la ROM de apoyo para la mayoría de nuestros ejemplos. | ||
Línea 298: | Línea 152: | ||
<code z80> | <code z80> | ||
; Repetir algo 100 veces | ; Repetir algo 100 veces | ||
- | | + | |
bucle: | bucle: | ||
- | (...) ; código | + | (...) ; código |
- | | + | |
- | | + | |
+ | | ||
; 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. | + | ; saltar a la etiqueta bucle y continuar. |
- | ; se ponga a 1 cuando B llegue a cero, lo que afectará al JR NZ. | + | ; se ponga a 1 cuando B llegue a cero, lo que afectará al jr NZ. |
; Como resultado, este trozo de código (...) se ejecutará 100 veces. | ; Como resultado, este trozo de código (...) se ejecutará 100 veces. | ||
</ | </ | ||
- | Como veremos en su momento, existe una instrucción equivalente a '' | + | Como veremos en su momento, existe una instrucción equivalente a '' |
Además de para bucles, también podemos utilizarlo para comparaciones. Supongamos que queremos hacer en ensamblador una comparación de igualdad, algo como: | Además de para bucles, también podemos utilizarlo para comparaciones. Supongamos que queremos hacer en ensamblador una comparación de igualdad, algo como: | ||
Línea 323: | Línea 178: | ||
<code z80> | <code z80> | ||
- | LD A, C | + | ld a, c ; A = C |
; Tenemos que hacer esto porque no existe | ; Tenemos que hacer esto porque no existe | ||
- | ; una instruccion | + | ; una instruccion |
; restar un registro al registro A. | ; restar un registro al registro A. | ||
- | SUB B | + | sub b ; A = A-B |
- | JP Z, Es_Igual | + | jp z, Es_Igual |
- | JP NZ, No_Es_Igual | + | jp nz, No_Es_Igual |
(...) | (...) | ||
Línea 370: | Línea 225: | ||
<code z80> | <code z80> | ||
- | LD DESTINO, ORIGEN | + | ld DESTINO, ORIGEN |
</ | </ | ||
Línea 378: | Línea 233: | ||
<code z80> | <code z80> | ||
- | LD A, 10 | + | ld a, 10 ; A = 10 |
- | LD B, 200 ; B = 200 | + | ld b, 200 |
- | LD BC, 12345 | + | ld bc, 12345 ; BC = 12345 |
</ | </ | ||
Línea 386: | Línea 241: | ||
<code z80> | <code z80> | ||
- | LD A, B | + | ld a, b ; A = B |
- | LD BC, DE | + | ld bc, de ; BC = DE |
</ | </ | ||
Línea 393: | Línea 248: | ||
<code z80> | <code z80> | ||
- | LD (12345), | + | ld (12345), |
- | LD (HL), 10 ; Memoria[valor de HL] = 10 | + | ld (hl), 10 |
</ | </ | ||
Línea 400: | Línea 255: | ||
<code z80> | <code z80> | ||
- | LD A, (12345) | + | ld a, (12345) |
- | LD B, (HL) | + | ld b, (hl) ; B = valor en Memoria[valor de HL] |
</ | </ | ||
- | | + | |
+ | |||
+ | 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 '' | ||
+ | |||
+ | <code z80> | ||
+ | ld a, (16384) | ||
+ | ld (16384), a | ||
+ | |||
+ | ld hl, 16384 => HL = 16384 | ||
+ | ld a, (hl) => LET A = PEEK HL => LET A = PEEK 16384 | ||
+ | ld (hl), a => POKE HL, a | ||
+ | </ | ||
+ | |||
+ | En el segundo ejemplo hemos utilizado '' | ||
+ | |||
+ | 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, | ||
+ | |||
+ | De nuevo, viéndolo "en instrucciones BASIC", | ||
+ | |||
+ | <code z80> | ||
+ | ld hl, 16384 => HL = 16384 | ||
+ | |||
+ | ld hl, (16384) | ||
+ | => L = PEEK 16384 | ||
+ | H = PEEK 16385 | ||
+ | </ | ||
+ | |||
+ | En '' | ||
+ | |||
+ | 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 | ||
+ | </ | ||
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 423: | Línea 314: | ||
<code z80> | <code z80> | ||
; Carga de valores en registros | ; Carga de valores en registros | ||
- | 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 | ||
- | LD r, r | + | ld r, r |
- | LD rr, rr | + | ld rr, rr |
; Acceso a memoria | ; Acceso a memoria | ||
- | LD r, (HL) | + | ld r, (hl) |
- | LD (NN), A | + | ld (NN), a |
- | LD (HL), N | + | ld (hl), N |
- | LD A, (rr) ; (excepto rr=SP) | + | ld a, (rr) ; (excepto rr=SP) |
- | LD (rr), A ; (excepto rr=SP) | + | ld (rr), a ; (excepto rr=SP) |
- | 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 |
</ | </ | ||
Línea 453: | Línea 344: | ||
<code z80> | <code z80> | ||
; Manipulación del puntero de pila (SP) | ; Manipulación del puntero de pila (SP) | ||
- | LD SP, ri | + | ld sp, ri |
- | LD SP, HL | + | ld sp, hl |
; Para manipular el registro I | ; Para manipular el registro I | ||
- | LD A, I | + | ld a, i |
- | LD I, A | + | ld i, a |
; Para manipular el registro R | ; Para manipular el registro R | ||
- | LD A, R | + | ld a, r |
- | LD R, A | + | ld r, a |
</ | </ | ||
Línea 470: | 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 BC, 12345 | + | 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 b, c ; ld r, r |
- | LD A, B | + | ld a, b ; ld r, r |
- | LD BC, DE | + | 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 a, (hl) ; ld r, (rr) |
- | LD (BL), B ; LD (rr), r | + | ld (bc), a |
- | LD (12345), | + | ld (12345), |
- | LD A, (HL) | + | ld a, (hl) ; ld r, (rr) |
- | LD (DE), A ; LD (rr), r | + | ld (de), a |
- | LD (BC), 1234h | + | ld (bc), 1234h ; ld (bc), NN |
- | LD (12345), | + | ld (12345), |
- | LD IX, (12345) | + | ld ix, (12345) |
- | LD (34567), | + | ld (34567), |
; 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 (ix+10), a ; LD (ri+N), r |
- | LD A, (IY+100) | + | ld a, (iy+100) ; ld r, (ri+N) |
- | LD (IX-30), 100 ; LD (ri+N), N | + | ld (ix-30), 100 |
</ | </ | ||
| | ||
- | Un detalle muy importante respecto a las instrucciones de carga: **en el caso de las operaciones LD, el registro F no ve afectado ninguno de sus indicadores o flags en relación al resultado de la ejecución de las mismas** (salvo en el caso de '' | + | Un detalle muy importante respecto a las instrucciones de carga: **en el caso de las operaciones LD, el registro F no ve afectado ninguno de sus indicadores o flags en relación al resultado de la ejecución de las mismas** (salvo en el caso de '' |
< | < | ||
Flags | Flags | ||
| | ||
- | ---------------------------------- | + | |
- | LD r, r |- - - - - -| | + | ld r, r |- - - - - -| |
- | LD r, N |- - - - - -| | + | ld r, N |- - - - - -| |
- | LD rr, rr |- - - - - -| | + | ld rr, rr |- - - - - -| |
- | 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| |
- | LD A, R |* * 0 * 1 0| | + | ld a, r |* * 0 * 1 0| |
</ | </ | ||
\\ | \\ | ||
- | Esto quiere decir, y es muy importante, que una operación como '' | + | Esto quiere decir, y es muy importante, que una operación como '' |
- | 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 " | + | \\ |
+ | ===== 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, | ||
+ | |||
+ | 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 '' | ||
+ | |||
+ | En nuestro ejemplo anterior, el '' | ||
+ | |||
+ | 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 '' | ||
\\ | \\ | ||
Línea 535: | Línea 441: | ||
<code z80> | <code z80> | ||
- | LD A, 0 ; A = 0 | + | ld a, 0 ; A = 0 |
- | INC A | + | inc a |
- | LD B, A | + | ld b, a |
- | INC B | + | inc b |
- | INC B | + | inc b |
- | LD BC, 0 | + | ld bc, 0 |
- | INC BC ; BC = 0001h | + | inc bc ; BC = $0001 |
- | INC B | + | inc b |
- | DEC A | + | dec a |
</ | </ | ||
Línea 549: | Línea 455: | ||
<code z80> | <code z80> | ||
- | INC r | + | inc r |
- | DEC r | + | dec r |
- | INC rr | + | inc rr |
- | DEC rr | + | dec rr |
</ | </ | ||
Línea 558: | Línea 464: | ||
<code z80> | <code z80> | ||
- | INC (HL) | + | inc (hl) |
- | DEC (HL) | + | dec (hl) |
</ | </ | ||
Línea 565: | Línea 471: | ||
<code z80> | <code z80> | ||
- | INC (IX+N) | + | inc (ix+n) |
- | DEC (IX+N) | + | dec (ix+n) |
- | INC (IY+N) | + | inc (iy+n) |
- | DEC (IY+N) | + | dec (iy+n) |
</ | </ | ||
Línea 576: | Línea 482: | ||
<code z80> | <code z80> | ||
- | INC A | + | inc a ; A = A+1 |
- | DEC B | + | dec b ; B = B-1 |
- | INC DE ; DE = DE+1 | + | inc de |
- | DEC IX ; IX = IX-1 | + | dec ix |
- | INC (HL) | + | inc (hl) ; (HL) = (HL)+1 |
- | INC (IX-5) | + | inc (ix-5) ; (IX-5) = (IX-5)+1 |
- | DEC (IY+100) | + | dec (iy+100) ; (IY+100) = (IY+100)+1 |
</ | </ | ||
Unos apuntes sobre la afectación de los flags ante el uso de INC y DEC: | Unos apuntes sobre la afectación de los flags ante el uso de INC y DEC: | ||
- | * Si un registro de 8 bits vale 255 ($FF) y lo incrementamos, | + | * Si un registro de 8 bits vale 255 ($ff) y lo incrementamos, |
- | * Si un registro de 16 bits vale 65535 ($FFFF) y lo incrementamos, | + | * Si un registro de 16 bits vale 65535 ($ffff) y lo incrementamos, |
- | * Si un registro de 8 bits vale 0 y lo decrementamos, | + | * Si un registro de 8 bits vale 0 y lo decrementamos, |
- | * Si un registro de 16 bits vale 0 ($0) y lo decrementamos, | + | * Si un registro de 16 bits vale 0 ($0) y lo decrementamos, |
* En estos desbordamientos no se tomará en cuenta para nada el bit de Carry (acarreo) de los flags (registro F), ni tampoco lo afectarán tras ejecutarse. | * En estos desbordamientos no se tomará en cuenta para nada el bit de Carry (acarreo) de los flags (registro F), ni tampoco lo afectarán tras ejecutarse. | ||
- | * Las operaciones INC y DEC sobre registros de 16 bits (BC, DE, HL, IX, IY, SP) no afectan a los flags. Esto implica que no podemos usar como condición de flag zero para un salto el resultado de instrucciones como "DEC BC", por ejemplo. | + | * Las operaciones INC y DEC sobre registros de 16 bits (BC, DE, HL, IX, IY, SP) no afectan a los flags. Esto implica que no podemos usar como condición de flag zero para un salto el resultado de instrucciones como "dec bc", por ejemplo. |
* Las operaciones INC y DEC sobre registros de 8 bits y sobre la memoria no afectan al flag de acarreo, pero sí que pueden afectar al flag de Zero (Z), al de Paridad/ | * Las operaciones INC y DEC sobre registros de 8 bits y sobre la memoria no afectan al flag de acarreo, pero sí que pueden afectar al flag de Zero (Z), al de Paridad/ | ||
Línea 601: | Línea 507: | ||
| | ||
| | ||
- | 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 -| |
- | DEC rr |- - - - - -| | + | dec rr |- - - - - -| |
</ | </ | ||
Línea 640: | Línea 546: | ||
<code z80> | <code z80> | ||
- | ADD DESTINO, ORIGEN | + | add DESTINO, ORIGEN |
</ | </ | ||
Línea 646: | 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 |
</ | </ | ||
Línea 668: | Línea 574: | ||
<code z80> | <code z80> | ||
- | ; ADD A, s | + | ; add a, s |
- | ADD A, B | + | add a, b ; A = A + B |
- | ADD A, 100 ; A = A + 100 | + | add a, 100 |
- | ADD A, [HL] ; A = A + [HL] | + | add a, (hl) |
- | ADD A, [IX+10] | + | add a, (ix+10) ; A = A + (IX+10) |
- | ; ADD HL, ss | + | ; add hl, ss |
- | ADD HL, BC | + | add hl, bc ; HL = HL + BC |
- | ADD HL, SP | + | add hl, sp ; HL = HL + SP |
- | ; ADD ri, rr | + | ; addri, rr |
- | ADD IX, BC | + | add ix, bc ; IX = IX + BC |
- | ADD IY, DE | + | add iy, de ; IY = IY + DE |
- | ADD IY, IX | + | add iy, ix ; IY = IY + IX |
- | ADD IX, IY | + | add ix, iy ; IX = IX + IY |
</ | </ | ||
Línea 688: | Línea 594: | ||
<code z80> | <code z80> | ||
- | ADD B, C | + | add b, c ; Sólo A puede ser destino |
- | ADD BC, DE | + | add bc, de ; Sólo puede ser destino HL |
- | ADD IX, IX | + | add ix, ix ; No podemos sumar un registro índice a él mismo |
</ | </ | ||
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 '' | + | * Para '' |
- | * Para '' | + | * Para '' |
O, en forma de tabla de afectación: | O, en forma de tabla de afectación: | ||
Línea 705: | Línea 611: | ||
| | ||
| | ||
- | 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 *| |
</ | </ | ||
Línea 736: | Línea 642: | ||
<code z80> | <code z80> | ||
- | LD A, %10000000 | + | ld a, %10000000 |
- | LD B, %10000000 | + | ld b, %10000000 |
- | ADD A, B | + | add a, b |
</ | </ | ||
Línea 746: | 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 " | + | 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 " |
<code z80> | <code z80> | ||
- | SUB ORIGEN | + | sub ORIGEN |
</ | </ | ||
Línea 755: | Línea 661: | ||
<code z80> | <code z80> | ||
- | SUB r ; A = A - r | + | sub r ; A = A - r |
- | SUB N ; A = A - N | + | sub N ; 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) |
</ | </ | ||
Línea 764: | Línea 670: | ||
<code z80> | <code z80> | ||
- | 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) |
</ | </ | ||
Línea 788: | Línea 694: | ||
< | < | ||
- | "ADC A, s" | + | "adc a, s" |
- | "ADC HL, ss" | + | "adc hl, ss" |
</ | </ | ||
Línea 800: | Línea 706: | ||
Instrucción | Instrucción | ||
| | ||
- | ADC A,s |* * * V 0 *| | + | adc a,s |* * * V 0 *| |
- | ADC HL,ss |* * ? V 0 *| | + | adc hl,ss |* * ? V 0 *| |
</ | </ | ||
Línea 813: | Línea 719: | ||
< | < | ||
- | "SBC A, s" | + | "sbc a, s" |
- | "SBC HL, ss" | + | "sbc hl, ss" |
</ | </ | ||
Línea 823: | Línea 729: | ||
Instrucción | Instrucción | ||
| | ||
- | SBC A,s |* * * V 1 *| | + | sbc a,s |* * * V 1 *| |
- | SBC HL,ss |* * ? V 1 *| | + | sbc hl,ss |* * ? V 1 *| |
</ | </ | ||
Línea 942: | 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. |
< | < | ||
- | LD B', $10 | + | ld b', $10 |
- | INC A' | + | inc a' |
- | LD HL', $1234 | + | ld hl', $1234 |
- | LD A', ($1234) | + | ld a', ($1234) |
</ | </ | ||
Línea 955: | Línea 861: | ||
|< 60% >| | |< 60% >| | ||
^ Registro ^ Valor ^ Registro ^ Valor ^ | ^ Registro ^ Valor ^ Registro ^ Valor ^ | ||
- | | B | $A0 | B' | $00 | | + | | B | $a0 | B' | $00 | |
| C | $55 | C' | $00 | | | C | $55 | C' | $00 | | ||
| D | $01 | D' | $00 | | | D | $01 | D' | $00 | | ||
- | | E | $FF | E' | $00 | | + | | E | $ff | E' | $00 | |
| H | $00 | H' | $00 | | | H | $00 | H' | $00 | | ||
| L | $31 | L' | $00 | | | L | $31 | L' | $00 | | ||
Línea 966: | Línea 872: | ||
|< 60% >| | |< 60% >| | ||
^ Registro ^ Valor ^ Registro ^ Valor ^ | ^ Registro ^ Valor ^ Registro ^ Valor ^ | ||
- | | B | $00 | B' | $A0 | | + | | B | $00 | B' | $a0 | |
| C | $00 | C' | $55 | | | C | $00 | C' | $55 | | ||
| D | $00 | D' | $01 | | | D | $00 | D' | $01 | | ||
- | | E | $00 | E' | $FF | | + | | E | $00 | E' | $ff | |
| H | $00 | H' | $00 | | | H | $00 | H' | $00 | | ||
| L | $00 | L' | $31 | | | L | $00 | L' | $31 | | ||
Línea 975: | Línea 881: | ||
Si realizamos de nuevo '' | Si realizamos de nuevo '' | ||
- | Aparte de la instrucción '' | + | Aparte de la instrucción '' |
|< 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 986: | 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 '' | + | Realizando de nuevo un '' |
De esta forma podemos disponer de un set de registros extra Acumulador/ | De esta forma podemos disponer de un set de registros extra Acumulador/ | ||
Línea 999: | Línea 905: | ||
; Cambiamos de banco de registros: | ; Cambiamos de banco de registros: | ||
- | | + | |
- | | + | |
; Hacemos nuestras operaciones | ; Hacemos nuestras operaciones | ||
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
; (...etc...) | ; (...etc...) | ||
; (...aquí más operaciones...) | ; (...aquí más operaciones...) | ||
; Grabamos el resultado en memoria | ; Grabamos el resultado en memoria | ||
- | | + | |
- | ; Recuperamos | + | ; Recuperamos los registros: |
- | | + | |
- | | + | |
; Volvemos al lugar de llamada de la rutina | ; Volvemos al lugar de llamada de la rutina | ||
- | | + | |
</ | </ | ||
Línea 1025: | Línea 931: | ||
<code z80> | <code z80> | ||
- | | + | |
; ... Realizamos una serie de operaciones complejas ... | ; ... Realizamos una serie de operaciones complejas ... | ||
; Guardamos en la pila el valor de HL | ; Guardamos en la pila el valor de HL | ||
- | | + | |
; Recuperamos el juego de registros original | ; Recuperamos el juego de registros original | ||
- | | + | |
; Obtenemos de la pila el valor calculado | ; Obtenemos de la pila el valor calculado | ||
- | | + | |
- | | + | |
</ | </ | ||
Línea 1044: | Línea 950: | ||
|< 50% >| | |< 50% >| | ||
^ Instrucción ^ Resultado ^ | ^ Instrucción ^ Resultado ^ | ||
- | | EX DE, HL | Intercambiar los valores de DE y HL. | | + | | ex de, hl | Intercambiar los valores de DE y HL. | |
- | | 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, | La primera de estas instrucciones nos resultará muy útil en nuestros programas en ensamblador, | ||
- | 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á | + | 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 '' |
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: | ||
<code z80> | <code z80> | ||
- | PUSH BC | + | push bc |
- | PUSH DE | + | push de |
- | POP BC | + | pop bc |
- | POP DE | + | pop de |
</ | </ | ||
Línea 1065: | Línea 971: | ||
<code z80> | <code z80> | ||
- | PUSH HL | + | push hl |
- | LD L, C | + | ld l, c |
- | LD H, B | + | ld h, b |
- | POP BC | + | pop bc |
</ | </ | ||
Línea 1078: | Línea 984: | ||
Como hemos visto, con los Shadow Registers tenemos un set de registros adicional donde hacer cálculos, algo que parece en principio maravilloso si necesitamos más registros para realizar operaciones y no queremos acceder ni a memoria ni a la pila para almacenar datos o valores intermedios. | Como hemos visto, con los Shadow Registers tenemos un set de registros adicional donde hacer cálculos, algo que parece en principio maravilloso si necesitamos más registros para realizar operaciones y no queremos acceder ni a memoria ni a la pila para almacenar datos o valores intermedios. | ||
- | Existen algunas restricciones para el uso de los Shadow Registers (pero que como veremos, no nos afectan en el Spectrum): Si la ROM de nuestro sistema, en su rutina de gestión de interrupciones (ISR) utiliza los Shadow Registers, necesitaremos deshabilitar las interrupciones con **DI** (Disable Interrupts) antes de usar "EXX" y volver a habilitarlas con **EI** (Enable Interrupts" | + | Existen algunas restricciones para el uso de los Shadow Registers (pero que como veremos, no nos afectan en el Spectrum): Si la ROM de nuestro sistema, en su rutina de gestión de interrupciones (ISR) utiliza los Shadow Registers, necesitaremos deshabilitar las interrupciones con **di** (Disable Interrupts) antes de usar "exx" y volver a habilitarlas con **ei** (Enable Interrupts" |
Pero los Shadow Registers tienen una desventaja muy grande y que ya hemos visto, y es que no podemos utilizarlos directamente (no existen instrucciones para operar con ellos) y tampoco podemos usarlos a la vez que los registros normales. | Pero los Shadow Registers tienen una desventaja muy grande y que ya hemos visto, y es que no podemos utilizarlos directamente (no existen instrucciones para operar con ellos) y tampoco podemos usarlos a la vez que los registros normales. | ||
Línea 1084: | Línea 990: | ||
Lo único que podemos hacer es intercambiar los registros actuales con los alternativos y viceversa, con lo cual nuestra posibilidad de operar entre los registros normales y los Shadow es muy limitada. Sí, tenemos un set de registros extra para hacer operaciones, | Lo único que podemos hacer es intercambiar los registros actuales con los alternativos y viceversa, con lo cual nuestra posibilidad de operar entre los registros normales y los Shadow es muy limitada. Sí, tenemos un set de registros extra para hacer operaciones, | ||
- | Debemos almacenar estos valores en la pila, la memoria, o el registro AF (si ejecutamos '' | + | Debemos almacenar estos valores en la pila, la memoria, o el registro AF (si ejecutamos '' |
Es decir, que usar los Shadow Registers implica utilizar la memoria o la pila, de modo que si ya tenemos que hacer esto... ¿por qué no usar la memoria o la pila directamente para salvaguardar datos de nuestros registros normales cuando lo necesitemos? | Es decir, que usar los Shadow Registers implica utilizar la memoria o la pila, de modo que si ya tenemos que hacer esto... ¿por qué no usar la memoria o la pila directamente para salvaguardar datos de nuestros registros normales cuando lo necesitemos? | ||
Línea 1090: | Línea 996: | ||
Salvo excepciones, | Salvo excepciones, | ||
- | Veamos los 3 ejemplos (sin usar EXX): | + | Veamos los 3 ejemplos (sin usar exx): |
**Opción 1.- Usar la pila:** | **Opción 1.- Usar la pila:** | ||
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
- | | + | |
... hacer algo con BC ... | ... hacer algo con BC ... | ||
- | | + | |
+ | pop bc ; recuperar el valor de BC | ||
</ | </ | ||
Línea 1105: | Línea 1013: | ||
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
- | | + | |
+ | | ||
..... | ..... | ||
- | | + | |
- | | + | ld (1002), |
+ | | ||
.... | .... | ||
</ | </ | ||
Línea 1119: | Línea 1029: | ||
<code z80> | <code z80> | ||
- | | + | |
- | ; opcode "LD BC, NN NN" en memoria | + | ; opcode "ld bc, NN NN" en memoria |
... hacer cosas con BC, perdiendo su valor ... | ... hacer cosas con BC, perdiendo su valor ... | ||
save_bc: | save_bc: | ||
- | | + | |
; por el valor de BC, así que cuando | ; por el valor de BC, así que cuando | ||
- | ; el z80 llegue aquí no es ya LD BC, 0 | + | ; el z80 llegue aquí no es ya ld bc, 0 |
- | ; sino LD BC, valor_que_tenia_BC | + | ; sino ld bc, valor_que_tenia_BC |
; así que recuperaremos BC aquí. | ; así que recuperaremos BC aquí. | ||
</ | </ | ||
- | El ejemplo anterior es muy interesante. Cuando hacemos el '' | + | El ejemplo anterior es muy interesante. Cuando hacemos el '' |
- | En este caso '' | + | En este caso '' |
- | Con esto, estamos preservando el valor del registro a cambio de una escritura en memoria ('' | + | Con esto, estamos preservando el valor del registro a cambio de una escritura en memoria ('' |
\\ | \\ | ||
Línea 1150: | Línea 1060: | ||
===== Ficheros ===== | ===== Ficheros ===== | ||
- | * {{cursos: | ||
- | * {{cursos: | ||
* {{cursos: | * {{cursos: | ||
* {{cursos: | * {{cursos: |