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:arquitectura [06-01-2024 14:55] – [Enlaces] sromero | cursos:ensamblador:arquitectura [20-01-2024 20:11] (actual) – [Partes del mapa de Memoria del Spectrum] sromero | ||
---|---|---|---|
Línea 16: | Línea 16: | ||
\\ | \\ | ||
- | En un vistazo general, podemos ver que el microprocesador Z80 se conecta mediante los puertos de entrada/ | + | En un vistazo general, podemos ver que el microprocesador Z80 se conecta mediante los puertos de entrada/ |
Al mismo tiempo, los Buses de Datos y de Direcciones conectan al microprocesador con la memoria. Esta conexión es la que permite que el Z80 pueda leer y escribir en cualquier posición de la RAM, y leer datos de la ROM (que, juntas, conforman la totalidad de la memoria disponible). | Al mismo tiempo, los Buses de Datos y de Direcciones conectan al microprocesador con la memoria. Esta conexión es la que permite que el Z80 pueda leer y escribir en cualquier posición de la RAM, y leer datos de la ROM (que, juntas, conforman la totalidad de la memoria disponible). | ||
Línea 47: | Línea 47: | ||
| | ||
- | | + | |
\\ | \\ | ||
Línea 56: | Línea 56: | ||
<code z80> | <code z80> | ||
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
Línea 71: | Línea 71: | ||
Cuando tratemos las diferentes instrucciones del Z80 veremos en más detalle los registros, su tamaño, cómo se agrupan, y de qué forma podemos usarlos para operar entre ellos y realizar nuestras rutinas o programas. | Cuando tratemos las diferentes instrucciones del Z80 veremos en más detalle los registros, su tamaño, cómo se agrupan, y de qué forma podemos usarlos para operar entre ellos y realizar nuestras rutinas o programas. | ||
- | Finalmente, existe un registro especial del procesador llamado PC (Program Counter, o Contador de Programa). Este registro es de 16 bits (puede contener un valor entre 0 y 65535), y su utilidad es la de apuntar a la dirección de memoria de la siguiente instrucción a ejecutar. Así, cuando arrancamos nuestro Spectrum, el registro PC vale $0000, con lo que lo primero que se ejecuta en el Spectrum es el código que hay en $0000. Una vez leído y ejecutado ese primer código de instrucción, | + | Finalmente, existe un registro especial del procesador llamado |
Los programas se ejecutan linealmente mediante un ciclo basado en: Leer instrucción en la dirección de memoria apuntada por PC, incrementar registro PC, ejecutar instrucción. Posteriormente veremos más acerca de PC. | Los programas se ejecutan linealmente mediante un ciclo basado en: Leer instrucción en la dirección de memoria apuntada por PC, incrementar registro PC, ejecutar instrucción. Posteriormente veremos más acerca de PC. | ||
- | Ya hemos visto qué son los registros del microprocesador. Ahora bien, en el ejemplo anterior, ¿cómo sabe el microprocesador qué tiene que hacer cuando se encuentra un comando | + | Ya hemos visto qué son los registros del microprocesador. Ahora bien, en el ejemplo anterior, ¿cómo sabe el microprocesador qué tiene que hacer cuando se encuentra un comando |
- | Por ejemplo, cuando el microprocesador está ejecutando nuestro anterior programa y lee los valores numéricos correspondientes a "LD A, 10", el Z80 utiliza el microcódigo encargado de mover el valor 10 al registro A. Este microcódigo no es más que una secuencia de señales hardware y cambios de estados electrónicos cuyo resultado será, exactamente, | + | Por ejemplo, cuando el microprocesador está ejecutando nuestro anterior programa y lee los valores numéricos correspondientes a '' |
Este microcódigo está dentro del microprocesador porque sus diseñadores implementaron todas y cada una de las operaciones que puede hacer el Z80. Cuando pedimos meter un valor en un registro, leer el valor de un registro, sumar un registro con otro, escribir el valor de un registro en una dirección de memoria, saltar a otra parte del programa, etc, para cada una de esas situaciones, | Este microcódigo está dentro del microprocesador porque sus diseñadores implementaron todas y cada una de las operaciones que puede hacer el Z80. Cuando pedimos meter un valor en un registro, leer el valor de un registro, sumar un registro con otro, escribir el valor de un registro en una dirección de memoria, saltar a otra parte del programa, etc, para cada una de esas situaciones, | ||
Línea 96: | Línea 96: | ||
Pero nuestro procesador, como hemos dicho, necesita conectarse con elementos del exterior, entre ellos (y casi exclusivamente en el caso del Spectrum) tenemos la memoria, el teclado, el altavoz y la unidad de cinta. | Pero nuestro procesador, como hemos dicho, necesita conectarse con elementos del exterior, entre ellos (y casi exclusivamente en el caso del Spectrum) tenemos la memoria, el teclado, el altavoz y la unidad de cinta. | ||
- | Visto de un modo muy simple, el Z80 puede acceder a través de una serie de patillas a múltiples elementos externos. Esos elementos se conectan a la CPU Z80 a través de las patillas de "Buses de datos y direcciones" | + | Visto de un modo muy simple, el Z80 puede acceder a través de una serie de patillas a múltiples elementos externos. Esos elementos se conectan a la CPU Z80 a través de las patillas de Buses de datos y direcciones (que podemos leer en los puertos de Entrada/ |
| | ||
Línea 114: | Línea 114: | ||
</ | </ | ||
- | Este ejemplo lee (en un bucle infinito) una de las diferentes filas del teclado, mediante la lectura del puerto 63486 (F7FEh) con la instrucción de lectura de puertos de BASIC "IN". El estado de las 5 teclas que van desde el " | + | Este ejemplo lee (en un bucle infinito) una de las diferentes filas del teclado, mediante la lectura del puerto 63486 (F7FEh) con la instrucción de lectura de puertos de BASIC '' |
Si no hay ninguna tecla pulsada, el estado de estos 5 bits será un " | Si no hay ninguna tecla pulsada, el estado de estos 5 bits será un " | ||
Línea 123: | Línea 123: | ||
\\ | \\ | ||
- | {{ cursos: | + | {{ cursos: |
\\ | \\ | ||
Línea 150: | Línea 150: | ||
A la hora de leer estos puertos, el bit menos significativo (D0) siempre hace referencia a la tecla más alejada del centro del teclado (" | A la hora de leer estos puertos, el bit menos significativo (D0) siempre hace referencia a la tecla más alejada del centro del teclado (" | ||
- | En ensamblador también hay disponibles 2 instrucciones para leer el contenido de un puerto de Entrada/ | + | En ensamblador también hay disponibles 2 instrucciones para leer el contenido de un puerto de Entrada/ |
<code z80> | <code z80> | ||
; Cargamos en BC el valor del puerto a leer | ; Cargamos en BC el valor del puerto a leer | ||
- | LD BC, F7FEh | + | ld bc, f7FEh |
; Leemos en el registro A el valor del puerto | ; Leemos en el registro A el valor del puerto | ||
- | IN A, (C) | + | in a, (c) |
</ | </ | ||
Línea 168: | Línea 168: | ||
\\ | \\ | ||
- | El lector debería extraer una conclusión adicional del ejemplo del teclado: la gran diferencia de proceso que hay entre programar en ensamblador y programar en BASIC. Supongamos que nos interesa leer el estado del teclado para saber si unas determinadas teclas están pulsadas o no. Para esto, en ensamblador (aunque esto también podemos hacerlo en BASIC) leemos directamente el estado del teclado con un par de simples instrucciones (LD + IN). | + | El lector debería extraer una conclusión adicional del ejemplo del teclado: la gran diferencia de proceso que hay entre programar en ensamblador y programar en BASIC. Supongamos que nos interesa leer el estado del teclado para saber si unas determinadas teclas están pulsadas o no. Para esto, en ensamblador (aunque esto también podemos hacerlo en BASIC) leemos directamente el estado del teclado con un par de simples instrucciones ('' |
- | En BASIC, por contra, al usar INKEY$ estamos esperando la ejecución de un código que, además de leer TODAS las filas del teclado (no sólo aquellas de las teclas que nos interesen), realiza una conversión de todos los bits pulsados o no pulsados mediante una tabla para al final proporcionarnos el código ASCII de la tecla pulsada. | + | En BASIC, por contra, al usar '' |
Lo que son varias instrucciones simples en ASM, en BASIC se realiza mediante cientos de instrucciones en ensamblador que nos acaban dando la última tecla pulsada. Es por eso que el intérprete BASIC es tan lento: cada operación BASIC son decenas, cientos o miles de instrucciones en ensamblador que ni vemos ni controlamos. Programando directamente en ASM, el microprocesador hará EXCLUSIVAMENTE lo que nosotros le digamos que haga. He aquí la " | Lo que son varias instrucciones simples en ASM, en BASIC se realiza mediante cientos de instrucciones en ensamblador que nos acaban dando la última tecla pulsada. Es por eso que el intérprete BASIC es tan lento: cada operación BASIC son decenas, cientos o miles de instrucciones en ensamblador que ni vemos ni controlamos. Programando directamente en ASM, el microprocesador hará EXCLUSIVAMENTE lo que nosotros le digamos que haga. He aquí la " | ||
Línea 191: | Línea 191: | ||
Cada uno de estos cajones (más técnicamente, | Cada uno de estos cajones (más técnicamente, | ||
- | El microprocesador Z80 puede acceder a cualquier posición de memoria tanto para leer como para escribir. Internamente, | + | El microprocesador Z80 puede acceder a cualquier posición de memoria tanto para leer como para escribir. Internamente, |
(Nota: el operador () (paréntesis abierto y cerrado) en ensamblador significa acceso a memoria). | (Nota: el operador () (paréntesis abierto y cerrado) en ensamblador significa acceso a memoria). | ||
Línea 197: | Línea 197: | ||
\\ | \\ | ||
\\ | \\ | ||
- | **Ejecución de: LD A, ($1234)** | + | **Ejecución de: ld a, ($1234)** |
\\ | \\ | ||
Línea 207: | Línea 207: | ||
* La memoria recibe la señal de "deseo leer un dato" por esta señal de control, y mira el bus de direcciones que le conecta con el Spectrum. Mirando el estado de las 16 líneas encuentra el " | * La memoria recibe la señal de "deseo leer un dato" por esta señal de control, y mira el bus de direcciones que le conecta con el Spectrum. Mirando el estado de las 16 líneas encuentra el " | ||
- | * La memoria lee el valor de la celdilla de memoria $1234 (supongamos que contiene, por ejemplo $0F, que es 00001111 en binario) y cambia las 8 conexiones del Bus de Datos para que contengan 00001111 (4 " | + | * La memoria lee el valor de la celdilla de memoria $1234 (supongamos que contiene, por ejemplo $0f, que es 00001111 en binario) y cambia las 8 conexiones del Bus de Datos para que contengan 00001111 (4 " |
* El Z80 consulta el bus de datos y ve el estado de las líneas, con lo que lee el " | * El Z80 consulta el bus de datos y ve el estado de las líneas, con lo que lee el " | ||
- | * El Z80 coloca en el registro A el valor $0F. | + | * El Z80 coloca en el registro A el valor $0f. |
\\ | \\ | ||
Línea 218: | Línea 218: | ||
\\ | \\ | ||
\\ | \\ | ||
- | **Ejecución de: LD ($1234), | + | **Ejecución de: ld ($1234), |
\\ | \\ | ||
\\ | \\ | ||
- | * Supongamos que A contiene el valor 15 ($0F): el Z80 coloca las líneas del bus de datos a los valores 00001111 (donde cada 0 es 0 Voltios y cada 1 son 5 Voltios). | + | * Supongamos que A contiene el valor 15 ($0f): el Z80 coloca las líneas del bus de datos a los valores 00001111 (donde cada 0 es 0 Voltios y cada 1 son 5 Voltios). |
* El Z80 coloca las líneas del bus de direcciones a 00010010 00110100 ($1234). | * El Z80 coloca las líneas del bus de direcciones a 00010010 00110100 ($1234). | ||
Línea 230: | Línea 230: | ||
* La memoria recibe la señal de "deseo escribir un dato", y mira el bus de direcciones que le conecta con el Spectrum. Obteniendo el estado de las 16 líneas encuentra el " | * La memoria recibe la señal de "deseo escribir un dato", y mira el bus de direcciones que le conecta con el Spectrum. Obteniendo el estado de las 16 líneas encuentra el " | ||
- | * La memoria lee el valor del bus de datos para saber qué dato tiene que escribir y obtiene el valor $0F. | + | * La memoria lee el valor del bus de datos para saber qué dato tiene que escribir y obtiene el valor $0f. |
- | * La memoria escribe en su celdilla número $1234 el valor $0F. | + | * La memoria escribe en su celdilla número $1234 el valor $0f. |
\\ | \\ | ||
- | Estas son las 2 operaciones básicas que el Z80 puede realizar con la memoria: leer una posición de memoria y escribir en una posición de memoria. Nosotros no tenemos que preocuparnos de ninguna de las señales hardware necesarias para realizar lecturas y escrituras, de eso se encarga el microprocesador. Para nosotros, a nivel de ensamblador, | + | Estas son las 2 operaciones básicas que el Z80 puede realizar con la memoria: leer una posición de memoria y escribir en una posición de memoria. Nosotros no tenemos que preocuparnos de ninguna de las señales hardware necesarias para realizar lecturas y escrituras, de eso se encarga el microprocesador. Para nosotros, a nivel de ensamblador, |
- | Las celdillas de memoria desde la nº 0 a la 16383 están ocupadas por un chip que es la ROM del Spectrum. Este chip es de sólo lectura (ROM = Read Only Memory), lo cual quiere decir que si intentamos escribir en las celdillas desde la 0 a la 16383 no conseguiremos cambiar el valor almacenado en ellas. ¿Por qué no se puede escribir aquí? Porque es la ROM del Spectrum, es un chip que contiene el " | + | Las celdillas de memoria desde la nº 0 a la 16383 están ocupadas por un chip que es la '' |
\\ | \\ | ||
Línea 288: | Línea 288: | ||
\\ | \\ | ||
- | **Bloque desde 0 ($0000) a 16383 ($3FFF)**: | + | **Bloque desde 0 ($0000) a 16383 ($3fff)**: |
Memoria ROM, de solo lectura. En total son 16384 bytes (16KBytes). | Memoria ROM, de solo lectura. En total son 16384 bytes (16KBytes). | ||
- | **Bloque desde 16384 ($4000) a 22527 ($57FF)**: | + | **Bloque desde 16384 ($4000) a 22527 ($57ff)**: |
" | " | ||
Un byte por línea de 8 pixels definiendo cuál está encendido o no. En total son 6144 bytes (6KB) | Un byte por línea de 8 pixels definiendo cuál está encendido o no. En total son 6144 bytes (6KB) | ||
- | **Bloque desde 22528 ($5800) a 23295 ($5AFF)**: | + | **Bloque desde 22528 ($5800) a 23295 ($5aff)**: |
" | " | ||
Un byte por cuadro de 8x8 pixels. | Un byte por cuadro de 8x8 pixels. | ||
Línea 301: | Línea 301: | ||
En total son 768 bytes. | En total son 768 bytes. | ||
- | **Bloque desde 23296 ($5B00) a 23551 ($5BFF)**: | + | **Bloque desde 23296 ($5b00) a 23551 ($5bff)**: |
Buffer de impresora. Son 256 bytes que almacenan temporalmente lo que se imprimirá. | Buffer de impresora. Son 256 bytes que almacenan temporalmente lo que se imprimirá. | ||
Si no tenemos impresora (o nuestro programa no la empleará) podemos aprovechar estos | Si no tenemos impresora (o nuestro programa no la empleará) podemos aprovechar estos | ||
256 bytes para almacenar una pequeña rutina en código máquina o la pila del programa. | 256 bytes para almacenar una pequeña rutina en código máquina o la pila del programa. | ||
- | **Bloque desde 23552 ($5C00) a 23733 ($5CB5)**: | + | **Bloque desde 23552 ($5c00) a 23733 ($5cb5)**: |
Variables del sistema. Contienen varios elementos de información que indican al ordenador | Variables del sistema. Contienen varios elementos de información que indican al ordenador | ||
en qué estado se halla y son el objetivo de este documento. Algunas de ellas establecen | en qué estado se halla y son el objetivo de este documento. Algunas de ellas establecen | ||
los límites de las próximas divisiones que veremos en este mismo mapa de memoria | los límites de las próximas divisiones que veremos en este mismo mapa de memoria | ||
- | **Bloque desde 23734 ($5CB6) a 24576 ($6000)**: | + | **Bloque desde 23734 ($5cb6) a 24576 ($6000)**: |
A partir de aquí, hasta aproximadamente 24576, tenemos ciertas variables del sistema y | A partir de aquí, hasta aproximadamente 24576, tenemos ciertas variables del sistema y | ||
porciones de memoria usadas por BASIC como: | porciones de memoria usadas por BASIC como: | ||
Línea 331: | Línea 331: | ||
En general, podemos utilizar la memoria desde aquí en adelante para nuestros programas. | En general, podemos utilizar la memoria desde aquí en adelante para nuestros programas. | ||
- | En un modelo 16K, toda la RAM libre y disponible es aquella que queda entre $6000 (24574) y $7FFF (32767), es decir, apenas 8KB de memoria RAM para código, gráficos y sonidos de los programas, motivo por el cual los juegos de 16KB no podían ser demasiado complejos. | + | En un modelo 16K, toda la RAM libre y disponible es aquella que queda entre $6000 (24574) y $7fff (32767), es decir, apenas 8KB de memoria RAM para código, gráficos y sonidos de los programas, motivo por el cual los juegos de 16KB no podían ser demasiado complejos. |
- | En un modelo 48K, encontraremos después de $6000 mucha más RAM. Aparte de los 8KB entre $6000 y $7FFF, tenemos otros 2 bloques de 16KB (un total de 32KB más) de $8000 (32768) a $BFFF (49151) y de $C000 (49152) a $FFFF (65535). | + | En un modelo 48K, encontraremos después de $6000 mucha más RAM. Aparte de los 8KB entre $6000 y $7fff, tenemos otros 2 bloques de 16KB (un total de 32KB más) de $8000 (32768) a $bfff (49151) y de $c000 (49152) a $ffff (65535). |
+ | |||
+ | La siguiente figura, extraída del libro "// | ||
+ | |||
+ | \\ | ||
+ | {{ : | ||
+ | \\ | ||
\\ | \\ | ||
**¿Dónde están los UDG?** | **¿Dónde están los UDG?** | ||
- | El valor de la dirección de memoria **23675** (**$5C7B** - variable del sistema "**UDG**") contiene la dirección de memoria donde están los gráficos definidos por el usuario. Por defecto en un Spectrum 48K, esta variable apunta a la dirección de memoria 65368, lo que deja 65535-65368 = 168 bytes para UDGs, que son (168/8) un total de 21 UDGs. | + | El valor de la dirección de memoria **23675** (**$5c7b** - variable del sistema "'' |
- | Para establecer nuestros propios UDGs, podemos pues POKEar datos gráficos en esas direcciones, | + | Para establecer nuestros propios UDGs, podemos pues POKEar datos gráficos en esas direcciones, |
Línea 355: | Línea 361: | ||
Una vez hemos visto todas las partes funcionales, | Una vez hemos visto todas las partes funcionales, | ||
- | Como ya hemos visto, los diferentes dispositivos externos (teclado, altavoz, cassette, elementos conectados al puerto de expansión, joysticks) se comunican con la CPU por medio de puertos de Entrada/ | + | Como ya hemos visto, los diferentes dispositivos externos (teclado, altavoz, cassette, elementos conectados al puerto de expansión, joysticks) se comunican con la CPU por medio de puertos de Entrada/ |
En realidad el Z80, visto de una forma simplificada, | En realidad el Z80, visto de una forma simplificada, | ||
Línea 387: | Línea 393: | ||
* Encendido de ordenador: | * Encendido de ordenador: | ||
o PC (el Contador de Programa) se pone a 0. | o PC (el Contador de Programa) se pone a 0. | ||
- | o SP se pone a $FFFF. | + | o SP se pone a $ffff. |
* Mientras No Se Apague el Ordenador: | * Mientras No Se Apague el Ordenador: | ||
Leer de la memoria la siguiente instrucción, | Leer de la memoria la siguiente instrucción, | ||
Línea 403: | Línea 409: | ||
o Ejecutar la instrucción | o Ejecutar la instrucción | ||
Fin Mientras | Fin Mientras | ||
- | | ||
</ | </ | ||
- | En realidad, según el texto "The Undocumented Z80 Documented", | + | En realidad, según el texto "The Undocumented Z80 Documented", |
- | tras un reset los registros quedan con los siguientes valores: | + | |
< | < | ||
| | ||
- | AF, SP = $FFFF | + | AF, SP = $ffff |
| | ||
| | ||
Línea 430: | Línea 434: | ||
==== Opcodes y código máquina ==== | ==== Opcodes y código máquina ==== | ||
- | Nuestro microprocesador Z80 no entiende los comandos en ensamblador que hemos estado viendo en estos 2 primeros capítulos del curso de código máquina; el Z80 sólo entiende números binarios, números de 8 bits de 0 a 255 (o de $00 a $FF en hexadecimal). | + | Nuestro microprocesador Z80 no entiende los comandos en ensamblador que hemos estado viendo en estos 2 primeros capítulos del curso de código máquina; el Z80 sólo entiende números binarios, números de 8 bits de 0 a 255 (o de $00 a $ff en hexadecimal). |
De entre los registros del microprocesador hay uno llamado PC (Program Counter o Contador de Programa), que como ya hemos visto, es el " | De entre los registros del microprocesador hay uno llamado PC (Program Counter o Contador de Programa), que como ya hemos visto, es el " | ||
Línea 437: | Línea 441: | ||
<code z80> | <code z80> | ||
- | LD A, 0 | + | ld a, 0 |
- | INC A | + | inc a |
- | LD B, $FFh | + | ld b, $ffh |
- | INC B | + | inc b |
- | LD DE, $AABB | + | ld de, $aabb |
- | RET | + | ret |
</ | </ | ||
Línea 470: | Línea 474: | ||
|< 50% 33% 33% 33% >| | |< 50% 33% 33% 33% >| | ||
^ Dirección ^ Valor hexadecimal ^ Significado ^ | ^ Dirección ^ Valor hexadecimal ^ Significado ^ | ||
- | | 40000 | 3e | LD A, | | + | | 40000 | 3e | ld a, | |
| 40001 | 00 | 00h | | | 40001 | 00 | 00h | | ||
- | | 40002 | 3c | INC A | | + | | 40002 | 3c | inc a | |
- | | 40003 | 06 | LD B, | | + | | 40003 | 06 | ld b, | |
| 40004 | ff | FFh | | | 40004 | ff | FFh | | ||
- | | 40005 | 04 | INC B | | + | | 40005 | 04 | inc b | |
- | | 40006 | 11 | LD DE, | | + | | 40006 | 11 | ld de, | |
| 40007 | bb | BBh | | | 40007 | bb | BBh | | ||
| 40008 | aa | AAh | | | 40008 | aa | AAh | | ||
- | | 40009 | c9 | RET | | + | | 40009 | c9 | ret | |
- | A la hora de ejecutar el programa, nuestro RANDOMIZE USR 40000 lo que hace en realidad es cambiar el valor del registro " | + | A la hora de ejecutar el programa, nuestro |
< | < | ||
* Leer el byte contenido en la dirección de memoria " | * Leer el byte contenido en la dirección de memoria " | ||
* Incrementar PC (PC=PC+1). | * Incrementar PC (PC=PC+1). | ||
- | * El byte es " | + | * El byte es " |
- | tiene que meter en A un valor numérico. | + | |
- | * El valor extra para "LD A," está a continuación en memoria, | + | |
+ | * El valor extra para "ld a," está a continuación en memoria, | ||
así que se lee la memoria de nuevo: | así que se lee la memoria de nuevo: | ||
o operando = [PC] = $00 | o operando = [PC] = $00 | ||
o Incrementar PC (PC=PC+1) | o Incrementar PC (PC=PC+1) | ||
* Ya se tiene el " | * Ya se tiene el " | ||
- | ejecuta: "LD A, 00". (se ejecuta el microcódigo correspondiente | + | ejecuta: "ld a, 00". (se ejecuta el microcódigo correspondiente |
dentro de la CPU). | dentro de la CPU). | ||
</ | </ | ||
- | Esto que hemos visto es el proceso de " | + | Esto que hemos visto es el proceso de " |
< | < | ||
Línea 503: | Línea 508: | ||
* Incrementar PC (PC=PC+1). | * Incrementar PC (PC=PC+1). | ||
* El byte es " | * El byte es " | ||
- | * No hacen falta operandos extra, | + | * No hacen falta operandos extra, |
- | * Ya se tiene el " | + | * Ya se tiene el " |
</ | </ | ||
- | Y este ciclo se vuelve a repetir, una y otra vez, hasta que llegamos al RET: | + | Y este ciclo se vuelve a repetir, una y otra vez, hasta que llegamos al ret: |
< | < | ||
* Leer el byte contenido en la dirección de memoria " | * Leer el byte contenido en la dirección de memoria " | ||
* Incrementar PC (PC=PC+1). | * Incrementar PC (PC=PC+1). | ||
- | * El byte es " | + | * El byte es " |
- | * No hacen falta operandos extra, | + | * No hacen falta operandos extra, |
- | * Ya se tiene el " | + | * Ya se tiene el " |
</ | </ | ||
Línea 521: | Línea 526: | ||
* Como véis, el microprocesador no entiende el lenguaje ensamblador, | * Como véis, el microprocesador no entiende el lenguaje ensamblador, | ||
- | * La primera parte leída de la instrucción es el OPCODE (código de operación), | + | * La primera parte leída de la instrucción es el OPCODE (código de operación), |
- | * Para el Spectrum, no hay diferencia entre instrucciones y datos. Un "$3C" | + | * Para el Spectrum, no hay diferencia entre instrucciones y datos. Un **$3c** puede ser un '' |
* Al no existir diferencia entre instrucciones y datos, si cambiamos PC de forma que apunte a una zona de la memoria donde hay datos y no código, el Z80 tratará de interpretar los números que va leyendo como si fueran opcodes (con resultados imprecedibles, | * Al no existir diferencia entre instrucciones y datos, si cambiamos PC de forma que apunte a una zona de la memoria donde hay datos y no código, el Z80 tratará de interpretar los números que va leyendo como si fueran opcodes (con resultados imprecedibles, | ||
- | * Por último, existen una serie de opcodes compuestos (dejando de lado los operandos) que ocupan más de 1 byte. Esos opcodes suelen comenzar por CB, ED o FD, de forma que, por ejemplo el opcode | + | * Por último, existen una serie de opcodes compuestos (dejando de lado los operandos) que ocupan más de 1 byte. Esos opcodes suelen comenzar por CB, ED o FD, de forma que, por ejemplo el opcode |
- | * Cuando un operando es de 16 bits (2 bytes), primero encontramos el byte bajo y luego el byte alto. Así, nuestro | + | * Cuando un operando es de 16 bits (2 bytes), primero encontramos el byte bajo y luego el byte alto. Así, nuestro |
\\ | \\ | ||
Línea 541: | Línea 546: | ||
<code z80> | <code z80> | ||
- | ORG 50000 | + | |
- | LD HL, $4000 ; 21 00 40 | + | ld hl, $4000 ; 21 00 40 |
- | RET ; C9 | + | |
</ | </ | ||
- | Teniendo en cuenta que el opcode de "**LD HL, NNNN**" | + | Teniendo en cuenta que el opcode de '' |
|< 40% 50% 50% >| | |< 40% 50% 50% >| | ||
Línea 554: | Línea 559: | ||
| 50001 | $00 | | | 50001 | $00 | | ||
| 50002 | $40 | | | 50002 | $40 | | ||
- | | 50003 | $C9 | | + | | 50003 | $c9 | |
- | Si ahora saltamos a la dirección 50000 para ejecutar el programa, lo primero que encuentra el procesador es $21, que indica al procesador que la instrucción es un "LD HL" | + | Si ahora saltamos a la dirección 50000 para ejecutar el programa, lo primero que encuentra el procesador es $21, que indica al procesador que la instrucción es un '' |
Esto no sólo ocurre con las instrucciones ensambladas, | Esto no sólo ocurre con las instrucciones ensambladas, | ||
<code z80> | <code z80> | ||
- | LD BC, $1234 | + | ld bc, $1234 |
- | LD ($4000), | + | |
</ | </ | ||
- | Si revisamos los valores almacenados en memoria, encontraremos el valor $34 en $4000 y el valor $12 en $4001. | + | Si revisamos los valores almacenados en memoria, encontraremos el valor **$34** en **$4000** y el valor **$12** en **$4001**. |
Y si después hacemos: | Y si después hacemos: | ||
<code z80> | <code z80> | ||
- | LD DE, ($4000) | + | ld de, ($4000) |
</ | </ | ||
El valor en DE será $1234, el valor correcto tal y como lo escribimos en nuestro programa, y no el valor al revés. De igual forma que el dato escribió como " | El valor en DE será $1234, el valor correcto tal y como lo escribimos en nuestro programa, y no el valor al revés. De igual forma que el dato escribió como " | ||
- | Donde sí debemos tener nosotros en cuenta este orden es a la hora de recuperar valores de 16 bits de memoria con las operaciones de 8 bits del tipo "**LD A, (HL)**". Si por ejemplo necesitamos acceder sólo a la parte baja de un dato de 16 bits, debemos saber que el primero de los 2 bytes que encontraremos será la parte baja y no la parte alta del valor. | + | Donde sí debemos tener nosotros en cuenta este orden es a la hora de recuperar valores de 16 bits de memoria con las operaciones de 8 bits del tipo '' |
En los procesadores del tipo // | En los procesadores del tipo // | ||
Línea 593: | Línea 598: | ||
===== Tiempos de ejecución ===== | ===== Tiempos de ejecución ===== | ||
- | Cada instrucción necesita un tiempo diferente para ejecutarse. No es lo mismo un simple | + | Cada instrucción necesita un tiempo diferente para ejecutarse. No es lo mismo un simple |
Los tiempos de ejecución de cada instrucción son, pues, diferentes, y para conocerlos tendremos que consultar cualquier tabla de tiempos, medidos en t-states o t-estados. El t-estado o "ciclo del procesador", | Los tiempos de ejecución de cada instrucción son, pues, diferentes, y para conocerlos tendremos que consultar cualquier tabla de tiempos, medidos en t-states o t-estados. El t-estado o "ciclo del procesador", | ||
+ | |||
+ | Saber los tiempos de ejecución y la cantidad de bytes que ocupa cada instrucción es vital para escribir rutinas óptimas, y a veces los programadores recurren a trucos para escribir rutinas que ocupen menos espacio, o que se ejecuten en menos ciclos de reloj, o ambas cosas, según las necesidades de cada programa. | ||
+ | |||
+ | En sistemas con un procesador tan limitado y con tan poca memoria como el Spectrum cada byte y cada t-estado cuenta. Por ejemplo, la siguiente instrucción: | ||
+ | |||
+ | <code z80> | ||
+ | ld a, 0 ; A = 0 | ||
+ | </ | ||
+ | |||
+ | Pone en A el valor 0 y ocupa 2 bytes (**$3e $00**) y tarda 7 ciclos de reloj en ejecutarse. | ||
+ | |||
+ | Sin embargo, la siguiente operación: | ||
+ | |||
+ | <code z80> | ||
+ | xor a ; A = A xor a = 0 | ||
+ | </ | ||
+ | |||
+ | Realiza la misma acción (hacer un '' | ||
+ | |||
+ | Utilizando este tipo de trucos a lo largo del programa, en programas grandes acabamos ahorrando mucho espacio y consiguiendo que el programa sea más rápido. | ||
\\ | \\ | ||
===== El software de Spectrum ===== | ===== El software de Spectrum ===== | ||
- | A estas alturas ya debemos tener claro cómo funciona el Spectrum, con su microprocesador Z80 continuamente ejecutando el código apuntado por "PC", incrementando este y de nuevo repitiendo el ciclo. | + | A estas alturas ya debemos tener claro cómo funciona el Spectrum, con su microprocesador Z80 continuamente ejecutando el código apuntado por **PC**, incrementando este y de nuevo repitiendo el ciclo. |
Cuando encendemos nuestro Spectrum, PC vale 0000h y se ejecuta la ROM que, como ya hemos comentado, no es más que un programa hecho por los ingenieros que desarrollaron el Spectrum. | Cuando encendemos nuestro Spectrum, PC vale 0000h y se ejecuta la ROM que, como ya hemos comentado, no es más que un programa hecho por los ingenieros que desarrollaron el Spectrum. | ||
Línea 609: | Línea 634: | ||
\\ | \\ | ||
- | - Desde cinta: Nuestro LOAD "" | + | - Desde cinta: Nuestro |
- Desde disco: exactamente igual que en el caso de la cinta, pero el medio de almacenamiento es un disco de 3" o de 3.5" | - Desde disco: exactamente igual que en el caso de la cinta, pero el medio de almacenamiento es un disco de 3" o de 3.5" | ||
- Ficheros TAP y TZX: son ficheros de ordenador que almacenan los datos exactamente igual que si fuera una cinta real: almacenan opcodes, datos y operandos, que luego serán cargados en memoria.\\ | - Ficheros TAP y TZX: son ficheros de ordenador que almacenan los datos exactamente igual que si fuera una cinta real: almacenan opcodes, datos y operandos, que luego serán cargados en memoria.\\ |