En el anterior capitulo aprendimos como el 6502 realiza operaciones aritméticas gracias a la
ALU, se dijo además que este subsistema era también el encargado de tratar todo lo que tuviera que ver con las decisiones lógicas y es precisamente en este apartado donde profundizaremos a lo largo de este capitulo.
Todo programa es un conjunto de instrucciones ordenadas con el único fin de solucionar un problema. Todo problema puede y debe ser estructurado en una serie de pasos lógicos a través de varias ramificaciones que nos irán resolviendo partes del problema que se interconectan hasta darnos finalmente su solución. A estos pasos los llamaremos su algoritmo. Un programa complejo estará compuesto de varios problemas a resolver y por tanto contendrá sus pertinentes algoritmos que a su vez se estructuran en un algoritmo general que los enlaza a todos.
A la hora de elaborar un programa complejo es recomendable (aunque no absolutamente necesario) hacer antes un esquema en el que se detalle de forma lógica su algoritmo, en el que se reflejen todas las posibles opciones (ramificaciones). Este tipo de esquemas se conocen en la jerga informática como
diagramas de flujo u organigramas. Para esta tarea también podemos echar mano de
pseudocódigo, otra forma de definir nuestro programa mediante instrucciones bien claras de entender por nosotros (una especie de lenguaje de programación universal). Lo verdaderamente importante aqui es comprender a la perfección lo que se quiere hacer antes de teclear ni una sóla linea de código.
En el Basic v2 del c64 el apartado lógico de un programa se lleva a cabo mediante las instrucciones IF ... THEN, ON ... GOTO, ON ... GOSUB, GOSUB y GOTO, que son instrucciones capaces de alterar el flujo normal de un programa según la comprobación lógica de una condición.
Como cabria esperar existe un juego de intrucciones código máquina que realizan idénticas labores aunque, como descubriréis, no se es tan directo como en Basic.
Esto no quiere decir que sea excesivamente más complicado programar decisiones lógicas en CM, simplemente requiere que comprendáis perfectamente como funciona el micro por dentro y esto implica que comprendáis que son las banderas ("flags") del procesador ya que el testeo de condiciones está ligado a sus banderas. En capitulos anteriores ya se las ha ido haciendo referencia pero ahora las veremos con todo el detenimiento que merecen.
Las banderas del procesador
En el 6502 hay siete banderas, todas ellas ocupan un bit y se hallan agrupadas dentro del registro de 8 bits "P" (palabra de estado del procesador). El bit 5 se dejó reservado para futuras implementaciones del chip y no tiene uso en el 6502.
Cada una de estas banderas cambiará según se vayan ejecutando instrucciones. Hay instrucciones que las cambian directamente y otras indirectamente (afectando a una o más banderas) como resultado de una operación.
El significado de cada bit es el siguiente:
Bandera N, Negative (Negativo)
También llamada bandera de signo, tendrá su bit puesto a uno si el resultado de la última operación (incluida una operación de carga) fue un número negativo (en representación de complemento a dos). Tiene una correspondencia directa con el bit 7 del registro acumulador por lo que una buena forma de comprobar el estado de este bit en el acumulador seria comprobando esta bandera. Fijaos a su vez que la bandera N ocupa muy convenientemente el bit 7 dentro del registro P (su bit más a la izquierda).
Bandera V, oVerflow (Rebasamiento)
La bandera de rebasamiento nos indica si hubo un acarreo (en la última operación efectuada) desde el bit 6 al 7, algo que deberemos tener en cuenta solamente si estamos sumando o restando números que sabemos que son expresados en complemento a dos. Si hubo dicho rebasamiento el bit V será igual a uno, reseteándose a cero en caso contrario.
Bandera B, Break (Interrupción por software)
Esta bandera se pondrá a uno cuando se ejecute una instrucción BRK, la cual fuerza a generar una interrupción que detiene el programa en curso. Se usa esta bandera para diferenciarse de los demás tipos de interrupciones y que los programas puedan saber que se interrumpió manualmente (por software).
Bandera D, Decimal
El 6502 puede operar aritméticamente de dos formas distintas: en Modo Binario (por defecto) y en Modo Decimal. Para fijar el modo de operación activo se usa esta bandera. En el modo decimal los números son entendidos como codificados en binario-decimal (
BCD, de "Binary Coded Decimal"). En este modo, el número diez por ejemplo se expresa como "0001 0000" (uno-cero), en lugar de su representación binaria normal "0000 1010" (diez). Este modo se pensó para manejar eficientemente programas de tipo financiero que trabajen con cifras astronómicas, ya que en estos casos la precisión es más importante que la velocidad de cálculo.
Bandera I, Interrupt Disable (Desactivador de Interrupciones)
Este bit indica si las interrupciones están o no habilitadas (a excepción de las
NMI ó no emmascarables). Si I=1 no se producirán interrupciones normales, y si I=0 éstas estarán permitidas (valor por defecto en el momento del encendido).
Bandera Z, Zero (Cero)
Esta bandera refleja si el resultado de la última operación fue cero (incluida una operación de carga como LDA #0), activándose si fuera el caso (Z=1). Si el resultado hubiese sido distinto de cero, la bandera permaneceria intacta. Es una bandera muy usada en bifurcaciones lógicas. Cuando un número es igual a otro, al restarlos el resultado es cero. Con esta sencilla premisa es posible comparar números entre si y saltar a un punto u otro del programa mediante instrucciones de bifurcación condicional como
BEQ y
BNE. Éstas testean el estado de Z y actúan en consecuencia. Sin embargo, para comparar dos números no es estrictamente necesario realizar su resta. La instrucción CMP (que compara el acumulador con un byte en memoria), y las respectivas para los registros X e Y: CPX, CPY, realizarán esta resta internamente (sin almacenar el resultado) y actualizarán el flag cero de acuerdo a ésta.
Bandera C, Carry (Acarreo)
Esta bandera refleja si en la anterior operación se produjo un acarreo. Esta bandera, como algunas de las otras también encenderse o apagarse a voluntad. Las instrucciones
SEC y
CLC se ocupan de ello respectivamente. Ya se vió en el capitulo anterior en que consistia el acarreo por lo que no redundaré más explicando el concepto. Las instrucciones de salto condicional que tienen que ver con esta bandera son BCS y BCC, que saltarán si el acarreo está fijado o borrado respectivamente. Como podemos fijar la bandera a nuestro antojo es usual para algunos programadores usen esta bandera para indicar posibles errores al llamador de una subrutina a modo de código de terminación.
A continuación, vistas las banderas, conoceremos las instrucciones que permiten programar el apartado de las decisiones lógicas en nuestros programas (separados en subcategorias):
* Instrucciones que permiten realizar tests lógicos.
* Instrucciones que modifican las banderas de Control
* Instrucciones que modifican las demás banderas
* Instrucciones de bifurcación condicional.
Instrucciones que permiten realizar tests lógicos
Aqui vereis agrupadas las instrucciones que comparan dos números como
CMP, y las que permiten testear bits específicos como
AND y
BIT.
Al emplear estas instrucciones el resultado quedará reflejado en una (o varias) de las banderas. Este resultado permitirá más tarde bifurcar a un punto u otro del programa gracias a las instrucciones de bifurcación condicional tales como
BEQ ó
BNE, como veremos en su correspondiente apartado más abajo.
CMP |
|
Compara memoria con el acumulador
|
(CoMPare Memory with accumulator) |
* Añadir 1 si cruza límite de página.
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Inmediato |
cmp
# Oper |
$c9 |
2 |
2 |
Página Cero |
cmp
Oper |
$c5 |
2 |
3 |
Página Cero, x |
cmp
Oper, x |
$d5 |
2 |
4 |
Absoluto |
cmp
Oper |
$cd |
3 |
4 |
Absoluto, x |
cmp
Oper, x |
$dd |
3 |
4* |
Absoluto, y |
cmp
Oper, y |
$d9 |
3 |
4* |
(Indirecto, x) |
cmp
(Oper, x) |
$c1 |
2 |
6 |
(Indirecto), y |
cmp
(Oper),y |
$d1 |
2 |
5* |
Compara una posición de la memoria (el siguiente byte de la instrucción, en direccionamiento Inmediato)
con el acumulador, modificando las banderas del registro de estado pertinentes (ver esquema con banderas
afectadas, en la tabla superior).
Para la comparación, internamente se resta el registro acumulador (a) menos el operando (a-M), ignorando el resultado
(el acumulador y la memoria permanecen intactos) pero modificando las banderas que hayan sido afectadas. Luego se pueden usar instrucciones de salto
condicional según éstas para bifurcar a diferentes puntos del programa.
Ejemplo de uso:
|
lda
#$25 |
// a = $25 |
|
cmp
valor |
// modifica Z y C según resultado
"resta" |
|
beq
igual |
// salta a iguales (si Z = 1) |
|
bcs
mayor |
// salta a mayor (si C = 1) |
|
bcc
menor |
// salta a menor (si C = 0) |
|
... |
|
CPX |
|
Compara memoria con el registro de índice x
|
(ComPare memory and index X) |
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Inmediato |
cpx
# Oper |
$e0 |
2 |
2 |
Página Cero |
cpx
Oper |
$e4 |
2 |
3 |
Absoluto |
cpx
Oper |
$ec |
3 |
4 |
Compara una posición de la memoria (el siguiente byte de la instrucción, en direccionamiento Inmediato)
con el registro de índice x, modificando las banderas del registro de estado pertinentes (ver esquema con banderas
afectadas, en la tabla superior).
CPY |
|
Compara memoria con el registro de índice y
|
(ComPare memory and index Y) |
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Inmediato |
cpy
# Oper |
$c0 |
2 |
2 |
Página Cero |
cpy
Oper |
$c4 |
2 |
3 |
Absoluto |
cpy
Oper |
$cc |
3 |
4 |
Compara una posición de la memoria (el siguiente byte de la instrucción, en direccionamiento Inmediato)
con el registro de índice y, modificando las banderas del registro de estado pertinentes (ver esquema con banderas
afectadas, en la tabla superior).
AND |
|
"Y" lógico con el acumulador
|
("AND" memory with accumulator) |
* Añadir 1 si cruza límite de página.
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Inmediato |
and
# Oper |
$29 |
2 |
2 |
Página Cero |
and
Oper |
$25 |
2 |
3 |
Página Cero, x |
and
Oper, x |
$35 |
2 |
4 |
Absoluto |
and
Oper |
$2d |
3 |
4 |
Absoluto, x |
and
Oper, x |
$3d |
3 |
4* |
Absoluto, y |
and
Oper, y |
$39 |
3 |
4* |
(Indirecto, x) |
and
(Oper, x) |
$21 |
2 |
6 |
(Indirecto), y |
and
(Oper),y |
$31 |
2 |
5 |
Realiza un Y lógico entre el acumulador y el Operando de la memoria. El resultado de la
operación "Y lógico" será 1 sólo cuando ambos patrones de bit sean igual a 1, en caso contrario
el resultado será siempre 0.
Un uso muy común para and, es usarlo para
desactivar determinados bits colocando un cero en el lugar que queramos
desactivar, y poniendo unos en los lugares que queramos dejar inalterados, a
través de una máscara de bits predeterminada. Para activar bits usariamos
eor, y hariamos lo contrario: pondriamos en
la máscara un 1 en los lugares que quisieramos tener el bit activado y ceros en
los que queramos dejar inalterados.
Por Ejemplo, si queremos desactivar el bit 5 (y sólo ese bit) de la variable
"valor":
|
lda
valor |
// carga un valor en a |
|
and #%11011111 |
// máscara: cero en el bit 5, el
resto unos |
|
sta
valor |
// actualiza valor (con el bit 5
apagado) |
En el ejemplo, pongamos que valor = %11110010
(tendria el bit5 = 1)
Al ejecutar and, y guardarlo en
valor, éste valdria
%11010010. Sólo habria cambiado el valor del bit de la posición en la
que habiamos puesto un 0 (en la quinta posición).
BIT |
|
Comprueba bits en memoria con el acumulador
|
(Test bits in memory with accumulator) |
Operación: A & M, M7 => N, M6 => V |
|
N |
Z |
C |
I |
D |
V |
M7 |
x |
- |
- |
- |
M6 |
|
|
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Página Cero |
bit
Oper |
$24 |
2 |
3 |
Absoluto |
bit
Oper |
$2c |
3 |
4 |
Esta instrucción es una versión lógica de las operaciones de comparación, en las que el contenido
del acumulador no se modifica, pero las banderas se ajustan al resultado obtenido.
Lo que hace es que el bit 6 (V, overflow) y el bit 7 (N, negative) del acumulador se transfieren al registro de estado.
Si el resultado de A & M es cero, entonces Z=1, en cualquier otro caso Z=0
Se trata de una forma de testear bits importantes del acumulador sin afectar su contenido,
como si sucederia si lo hicieramos con and, por ejemplo.
Instrucciones que modifican las banderas de Control
En este grupo se incluyen todas aquellas instrucciones que permiten modificar directamente las banderas, pero no todas las instrucciones que las afectan (¡casi todo el juego de instrucciones!). Éstas son las que afectan a las cuatro banderas que tienen que ver con decisiones de tipo lógico, oséase las banderas
Zero, Carry, Negative y Overflow.
Sin embargo, instrucciones que afectan directamente a banderas de control, sólo podemos modificar la bandera de acarreo, (
SEC, CLC) y borrar la bandera de rebasamiento con
CLV. El resto de banderas se modifican operando con las instrucciones de carga y/o aritméticas pertinentes que indirectamente ajusten la bandera adecuada. Recordad que las instrucciones de almacenamiento del 6502 no modifican las banderas.
Tambien habria que incluir aqui las instrucciones
PHP y
PLP, y es que ellas solas guardan/recuperan en la pila el registro
P (¡con todas las banderas!), pero sólo las mencionaré por aqui de pasada ya que se verán con más detalle cuando se hable de la pila.
SEC |
|
Activa Bandera de Acarreo
|
(SEt Carry Flag) |
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Implícito |
sec |
$38 |
1 |
2 |
Pone la bandera de acarreo (C) = 1.
CLC |
|
Borra Bandera de Acarreo
|
(CLear Carry flag) |
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Implícito |
clc |
$18 |
1 |
2 |
Pone la bandera de acarreo (C) = 0.
CLV |
|
Borra la bandera de rebasamiento
|
(Clear OVerflow flag) |
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Implícito |
clv |
$b8 |
1 |
2 |
Borra la bandera de rebasamiento, V = 0.
Cabe destacar que no existe la instrucción equivalente para poner la bandera a uno.
Instrucciones que modifican las demás banderas
En este apartado veremos esas otras instrucciones que aunque no tengan que ver con banderas de control lógico si que permiten modificar el estado de esas otras banderas de estado.
BRK |
|
Forzar Break (interrupción software)
|
(Forced Break) |
Operación: Fuerza interrupción software, transf. a
pila PC+2 y P |
|
|
|
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Implícito |
brk |
$00 |
1 |
7 |
Esta instrucción fuerza una interrupción por software. El Sistema toma control y ejecuta una rutina
de servicio de interrupción, que devuelve el sistema al BASIC. Sin embargo, el usuario puede cambiar
el salto a esta rutina de interrupción.
Es una instrucción que resulta muy útil para crear "breakpoints" (puntos de interrupción del flujo del programa) a lo largo de los programas
para facilitar así la depuración de posibles errores, ya que detendremos el flujo de ejecución en aquellos
puntos que deseemos comprobar.
CLD |
|
Desactiva modo Decimal
|
(CLear Decimal mode) |
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Implícito |
cld |
$d8 |
1 |
2 |
Desactiva el modo decimal, D = 0.
CLI |
|
Permite interrupciones.
|
(Clear Interrupt disable bit) |
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Implícito |
cli |
$58 |
1 |
2 |
Desactiva la bandera de desactivación de interrupciones, I = 0,
(con lo cual las permite).
SED |
|
Activa modo Decimal
|
(SEt Decimal mode) |
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Implícito |
sed |
$f8 |
1 |
2 |
Coloca el procesador en modo decimal (o BCD).
SEI |
|
Deshabilita interrupciones.
|
(Set Interrupt disable status) |
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Implícito |
sei |
$78 |
1 |
2 |
Activa la bandera de desactivación de interrupciones, I =
1,
(con lo cual las deshabilita).
Instrucciones de Bifurcación Condicional
Como hay cuatro banderas de control lógico (Z,C,N,V) tenemos derivadas de éstas 8 instrucciones de salto condicional:
BEQ |
|
Saltar si el resultado es cero
|
(Branch on result zero) |
Operación: Salta si Z = 1 |
|
|
|
* Añadir 1 si salto ocurre dentro de la misma página.
* Añadir 2 si salto ocurre en una página diferente.
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Relativo |
beq
Oper |
$f0 |
2 |
2* |
Si la bandera de cero = 1, salta un número de bytes indicado por el siguiente byte de la instrucción, en formato
byte con signo, pudiendo avanzar (127 bytes) o retroceder (128 bytes).
BNE |
|
Saltar si el resultado no es cero
|
(Branch on result not zero) |
Operación: Salta si Z = 0 |
|
|
|
* Añadir 1 si salto ocurre dentro de la misma página.
* Añadir 2 si salto ocurre en una página diferente.
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Relativo |
bne
Oper |
$d0 |
2 |
2* |
Si la bandera de cero = 0, salta un número de bytes indicado por el siguiente byte de la instrucción, en formato
byte con signo, pudiendo avanzar (127 bytes) o retroceder (128 bytes).
BCS |
|
Saltar si acarreo está a uno.
|
(Branch on Carry Set) |
Operación: Salta si C = 1 |
|
|
|
* Añadir 1 si salto ocurre dentro de la misma página.
* Añadir 2 si salto ocurre en una página diferente.
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Relativo |
bcs
Oper |
$b0 |
2 |
2* |
Si la bandera de acarreo = 1, salta un número de bytes indicado por el siguiente byte de la instrucción, en formato
byte con signo, pudiendo avanzar (127 bytes) o retroceder (128 bytes).
BCC |
|
Saltar si acarreo está a cero.
|
(Branch on Carry Clear) |
Operación: Salta si C = 0 |
|
|
|
* Añadir 1 si salto ocurre dentro de la misma página.
* Añadir 2 si salto ocurre en una página diferente.
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Relativo |
bcc
Oper |
$90 |
2 |
2* |
Si la bandera de acarreo = 0, salta un número de bytes indicado por el siguiente byte de la instrucción, en formato
byte con signo, pudiendo avanzar (127 bytes) o retroceder (128 bytes).
BPL |
|
Saltar si resultado Positivo
|
(Branch on result PLus) |
Operación: Salta si N = 0 |
|
|
|
* Añadir 1 si salto ocurre dentro de la misma página.
* Añadir 2 si salto ocurre en una página diferente.
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Relativo |
bpl
Oper |
$10 |
2 |
2* |
Si la bandera de negativo (N) = 0, salta un número de bytes indicado por el siguiente byte de la instrucción, en formato
byte con signo, pudiendo avanzar (127 bytes) o retroceder (128 bytes).
BMI |
|
Saltar si resultado Negativo
|
(Branch on result MInus) |
Operación: Salta si N = 1 |
|
|
|
* Añadir 1 si salto ocurre dentro de la misma página.
* Añadir 2 si salto ocurre en una página diferente.
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Relativo |
bmi
Oper |
$30 |
2 |
2* |
Si la bandera de negativo (N) = 1, salta un número de bytes indicado por el siguiente byte de la instrucción, en formato
byte con signo, pudiendo avanzar (127 bytes) o retroceder (128 bytes).
BVS |
|
Saltar si Rebasamiento está a 1
|
(Branch on oVerflow Set) |
Operación: Salta si V = 1 |
|
|
|
* Añadir 1 si salto ocurre dentro de la misma página.
* Añadir 2 si salto ocurre en una página diferente.
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Relativo |
bvs
Oper |
$70 |
2 |
2* |
Si la bandera de rebasamiento (V,
overflow) = 1, salta un número de bytes indicado por el siguiente byte de la instrucción, en formato
byte con signo, pudiendo avanzar (127 bytes) o retroceder (128 bytes).
BVC |
|
Saltar si Rebasamiento está a 0
|
(Branch on oVerflow Clear) |
Operación: Salta si V = 0 |
|
|
|
* Añadir 1 si salto ocurre dentro de la misma página.
* Añadir 2 si salto ocurre en una página diferente.
Modo de direccionamiento |
Sintaxis |
Cód. Oper.
|
Nº. Bytes |
Nº. Ciclos |
Relativo |
bvc
Oper |
$50 |
2 |
2* |
Si la bandera de rebasamiento (V,
overflow) = 0, salta un número de bytes indicado por el siguiente byte de la instrucción, en formato
byte con signo, pudiendo avanzar (127 bytes) o retroceder (128 bytes).
No hay comentarios:
Publicar un comentario