Instrucciones de incremento/decremento

Las instrucciones de incremento/decremento son:

  • “inx”.
  • “dex”.
  • “iny”.
  • “dey”.
  • “inc”.
  • “dec”.

Las dos primeras valen incrementar/decrementar el índice X. Las dos segundas valen para incrementar/decrementar el índice Y. Y las dos últimas valen para incrementar/decrementar una posición de memoria.

Incrementar/decrementar el índice X o el índice Y es útil para recorrer zonas de memoria (modos de direccionamiento indexados) y para implementar contadores. Incrementar/decrementar posiciones de memoria también es útil para recorrer zonas de memoria (modos de direccionamiento indirectos) y para implementar contadores. Y una aplicación obvia de los contadores son los bucles.

Las instrucciones “inx”, “dex”, “iny” y “dey” utilizan el modo de direccionamiento implícito y afectan a los flags S – sign y Z – zero.

Las instrucciones “inc” y “dec” soportan los modos de direccionamiento absoluto, página cero, absoluto indexado y página cero indexado, y también modifican los flags S – sign y Z – zero.

Los detalles de estas instrucciones se pueden consultar en http://www.6502.org/tutorials/6502opcodes.html#INX, http://www.6502.org/tutorials/6502opcodes.html#INC y http://www.6502.org/tutorials/6502opcodes.html#DEC.

Instrucciones de salto condicional

Las instrucciones de salto condicional o “branches” (bifurcaciones) son:

  • “beq” = branch on equal
  • “bne” = branch on not equal
  • “bcs” = branch on carry set
  • “bcc” = branch on carry clear
  • “bpl” = branch on plus
  • “bmi” = branch on minus
  • “bvs” = branch on overflow set
  • “bvc” = branch on overflow clear

Estas instrucciones dan un salto a la dirección indicada (en realidad usan el modo de direccionamiento relativo, es decir, un offset) cuando se cumple una determinada condición, y continúan con la siguiente instrucción cuando no se cumple. Las condiciones se basan en los flags del registro de estado.

“beq” y “bne”

Estas instrucciones se basan en el flag Z – zero. “beq” salta cuando Z está activado y “bne” cuando no lo está.

El hecho de hablar de “equal” o “not equal” viene de que, como veremos más adelante, el microprocesador realiza las comparaciones mediante una resta, lo que significa que la igualdad equivale a una resta cero, y la no igualdad a una resta distinta de cero.

“bcs” y “bcc”

Estas instrucciones se basan en el flag C – carry. “bcs” salta cuando C está activado y “bcc” cuando no lo está.

Como ya vimos, el flag C se activa cuando una suma (“adc”) desborda la capacidad del acumulador (“me llevo una”). Y se desactiva cuando una resta (“sbc”) es negativa (“tomo una”).

“bvs” y “bvc”

Estas instrucciones se basan en el flag V – overflow. “bvs” salta cuando V está activado y “bvc” cuando no lo está.

El flag V se activa cuando al sumar dos números positivos el resultado es negativo. Si se está trabajando con números con signo hay que detectar esta situación para evitar interpretar el resultado de forma incorrecta.

“bmi” y “bpl”

Estas instrucciones se basan en el flag N – negative o, lo que es lo mismo, el flag S – sign. “bmi” salta cuando N está activado y “bpl” cuando no lo está.

El flag N se activa cuando, como resultado de una instrucción, el bit de signo (bit 7) está activado.

Modos de direccionamiento y flags

Todas estas instrucciones utilizan el modo de direccionamiento relativo. Es decir, aunque el programador utilice como destino una dirección de memoria o una etiqueta (ej. “beq iguales”), el ensamblador al ensamblar sustituye esa dirección o etiqueta por un offset, es decir, un salto hacia delante o hacia atrás de N posiciones de memoria. Ese N puede oscilar entre 127 y -128 posiciones de memoria, y se cuenta respecto del primer byte de la instrucción siguiente.

Respecto a los flags, estas instrucciones no los modifican. Al contrario, y como ya se ha visto, utilizan los flags como información de entrada para determinar su funcionamiento (salto o no salto).

Los detalles de estas instrucciones pueden consultarse aquí: http://www.6502.org/tutorials/6502opcodes.html#BRA.

Otras instrucciones relacionadas

Las instrucciones de salto condicional se suelen usar conjuntamente con instrucciones de incremento/decremento y comparaciones, que se verán en las entradas que siguen. Se usan conjuntamente para implementar bucles.


Programa de ejemplo: Prog17

Instrucciones para operaciones lógicas

Las instrucciones para hacer operaciones lógicas son:

  • “and”.
  • “ora”.
  • “eor”.

La instrucción “and” realiza el AND bit a bit del acumulador y de un valor u operando que vendrá suministrado por el modo de direccionamiento utilizado. El resultado de ese AND se guarda en el acumulador.

La instrucción “ora” realiza el OR bit a bit del acumulador y de un valor u operando que, nuevamente, vendrá suministrado por el modo de direccionamiento utilizado. El resultado de ese OR se guarda en el acumulador.

Por último, la instrucción “eor” realiza el XOR u OR exclusivo bit a bit del acumulador y de un valor u operando que, una vez más, vendrá suministrado por el modo de direccionamiento utilizado. El resultado de ese XOR se guarda en el acumulador.

Las tablas de verdad de las operaciones AND, OR y XOR se pueden consultar aquí:

Tablas verdad

En la práctica, estas instrucciones se utilizan mucho para:

  • “and”: Poner a cero determinados bits seleccionados.
  • “ora”: Poner a uno determinados bits seleccionados.
  • “eor”: Cambiar de cero a uno o al revés determinados bits seleccionados.

Para verlo, supongamos que en las tablas anteriores la columna A es el acumulador y la columna B es un valor que llamaremos “máscara” y que vendrá suministrado de forma directa (modo de direccionamiento inmediato) o como el contenido de una posición de memoria (modo de direccionamiento absoluto u otros). Para ser más exactos, un bit del acumulador y un bit de la máscara, pero el proceso es el mismo para cada uno de los 8 bits del acumulador/máscara.

Pues bien, en el caso de AND, si el bit de la máscara vale 0, el bit resultante es 0. Y si el bit de la máscara es 1, el bit resultante es el bit original de A. Total, los bits 0 de la “máscara” ponen a 0 los bits correspondientes en el acumulador.

Análogamente, en el caso de OR, si el bit de la máscara vale 1, el bit resultante es 1. Y si el bit de la máscara es 0, el bit resultante es el bit original de A. Total, los bits 1 de la “máscara” ponen a 1 los bits correspondientes en el acumulador.

Por último, en el caso de XOR, si el bit de la máscara vale 1, el bit resultante cambia de valor. Y si el bit de la máscara es 0, el bit resultante se queda como estaba. Total, los bits 1 de la “máscara” cambian los bits correspondientes en el acumulador.

De hecho, en lógica se suele definir también la operación NOT, o complemento, que cambia los 0’s por 1’s y los 1’s por 0’s. Sin embargo, en el 6510 no es necesaria una instrucción “not”, ya que, por lo visto en el párrafo anterior, un “eor %11111111” consigue el mismo efecto (cambia los 8 bits).

Estas instrucciones suelen utilizarse con máscaras expresadas en binario (ej. %10010110), porque de este modo salta a la vista qué bits son los que se están poniendo a cero (los 0’s de un AND), o a uno (los 1’s de un OR), o cambiando (los 1’s de un XOR). Y se usan mucho cuando se programa vídeo (VIC), sonido (SID), o entrada/salida (CIAs), porque estos chips disponen de muchos registros (para el programador posiciones de memoria, al fin y al cabo) que atribuyen significados o funciones específicas a determinados bits.

Todas estas instrucciones soportan los modos de direccionamiento inmediato, absoluto, página cero, absoluto indexado, página cero indexado, indirecto – indexado e indexado – indirecto. Y todas afectan a los flags S – sign y Z – zero.

Sus detalles se pueden consultar aquí: http://www.6502.org/tutorials/6502opcodes.html#AND, http://www.6502.org/tutorials/6502opcodes.html#ORA y http://www.6502.org/tutorials/6502opcodes.html#EOR.


Programa de ejemplo: Prog16

Instrucciones para operaciones aritméticas (multiplicación y división)

El 6510 no tiene instrucciones específicas para multiplicar o dividir. Estas operaciones tienen que implementarse mediante rutinas.

En el caso de querer multiplicar por dos esto puede hacerse con la instrucción de desplazamiento de bits “asl”. Esta instrucción mueve los bits del acumulador (o de una posición de memoria) hacia la izquierda e introduce un bit 0 por la derecha. Por tanto, es el equivalente en el mundo binario (multiplicar por dos) a poner un cero por la derecha en el mundo decimal (multiplicar por 10).
Repitiendo la operación varias veces se puede multiplicar por cuatro, por ocho, etc., siempre con cuidado de no desbordar la capacidad del acumulador (8 bits).

Contrariamente, también es posible dividir por dos, cuatro, ocho, etc., con la instrucción “lsr”, que desplaza los bits del acumulador (o de una posición de memoria) hacia la derecha. Nuevamente, es el equivalente en el mundo binario (dividir por dos) a quitar dígitos por la derecha en el mundo decimal (dividir por 10). Lógicamente estamos hablando de divisiones enteras, sin decimales.

En todo caso, “asl” y “lsr” se consideran instrucciones de desplazamiento de bits, no instrucciones aritméticas, aunque sirvan para hacer algunas multiplicaciones y divisiones simples. Se consideran de la misma familia que “rol” y “ror”, y las veremos en su entrada específica.


Programa de ejemplo: Prog15

Instrucciones para operaciones aritméticas (resta)

“sbc” es la instrucción que resta un número (sustraendo) del acumulador (minuendo). La resta la deja en el acumulador.

Igual que en la suma interviene el flag C – carry, en la resta también interviene. Pero de una forma bastante más compleja de entender y de explicar. Vamos a intentarlo.

Ya comentamos en su momento que el microprocesador implementa la resta como una suma, concretamente sumando al acumulador/minuendo el complemento a dos del sustraendo. En principio, esto es un detalle de funcionamiento interno del microprocesador que debería ser “transparente” al programador, pero para entender mejor lo que ocurre y cómo programar, es mejor restar igual que lo hace el microprocesador.

Minuendo mayor que sustraendo:

Como ejemplo, tomemos minuendo = $ff (255) y sustraendo = $f0 (240). El resultado de esta resta debería ser $0f (15). Veamos si es así:

  • $ff = %11111111
  • $f0 = %11110000
  • Complemento de $f0 = %00001111
  • Complemento a dos de $f0 = %00010000
  • Resta $ff – $f0 = Suma $ff + complemento a dos de $f0 = %11111111 + %00010000 = %100001111 = %00001111 + flag C activado = $0f + flag C activado

El resultado es el que cabía esperar: $0f o 15. Y llama la atención el hecho de que el flag C quede activado porque al sumar el complemento a dos se necesitan 9 bits, y sólo se dispone de 8.

Minuendo menor que sustraendo:

Tomemos ahora el caso contrario, minuendo = $f0 (240) y sustraendo = $ff (255). El resultado debería ser -15, es decir, $f1. Veamos si es así:

  • $f0 = %11110000
  • $ff = %11111111
  • Complemento de $ff = %00000000
  • Complemento a dos de $ff = %00000001
  • Resta $f0 – $ff = Suma $f0 + complemento a dos de $ff = %11110000+ %00000001 = %11110001 = $f1 + flag C desactivado

Nuevamente, el resultado es el que cabría esperar: $f1 (que hay que interpretar como -15). Y ahora llama la atención que, al contrario que en el caso anterior, el flag C quede desactivado.

Conclusiones sobre la resta mono-byte:

Cuando el minuendo es mayor que el sustraendo, es decir, cuando la resta es positiva, el flag C se activa. Por el contrario, cuando el minuendo es menor que el sustraendo, es decir, cuando la resta es negativa, el flag C se desactiva.

De hecho, con la resta se hace al revés que con la suma (que primero se debe borrar el acarreo con “clc” y luego se suma con “adc”): primero se debe activar el acarreo con “sec” y luego se resta con “sbc”. Si la resta es positiva, el acarreo seguirá activado después de la resta; si es negativa se habrá desactivado.

Conclusiones sobre la resta multi-byte:

Recordemos que se podía hacer suma multi-byte, y que la instrucción “adc” sumaba acumulador, operando y acarreo precisamente para extender la suma de un byte al siguiente (el “me llevo una” de la suma).

Análogamente, se puede hacer resta multi-byte. Y por lo que se ha visto en el apartado anterior (C activado = resta positiva; C desactivado = resta negativa) el “tomo una” de la resta, es decir, el tomar prestada una unidad del siguiente byte porque si no la resta del byte actual saldría negativa, hay que hacerlo, precisamente, cuando el acarreo está desactivado.

Es por esto que la instrucción “sbc” lo que hace es acumulador – operando – complemento de C. Y esta resta se vuelve a meter en el acumulador.

En fin, complejo de explicar, pero lo hemos intentado…

Modos de direccionamiento y flags:

La instrucción “sbc” soporta los mismos modos de direccionamiento que “adc”: inmediato, absoluto, página cero, absoluto indexado, página cero indexado, indirecto – indexado, e indexado – indirecto. Y afecta a los mismos flags: S – sign, V – overflow, Z – zero y C – carry.

Los detalles de esta instrucción se pueden consultar en http://www.6502.org/tutorials/6502opcodes.html#SBC.


Programa de ejemplo: Prog14

Instrucciones para operaciones aritméticas (suma)

Las instrucciones para hacer operaciones aritméticas son:

  • “adc”.
  • “sbc”.

La primera instrucción vale para sumar y la segunda para restar.

“adc” es la instrucción que permite sumar un número al acumulador, dejando la suma nuevamente en el acumulador. Y un detalle muy importante es la “c” del final; la instrucción no se llama “add”, se llama “adc”. Esto es así porque en realidad lo que se suma son tres cosas: el acumulador, el operando, y el flag C – carry.

Esto es fuente de muchísimos errores, porque uno hace un programa para sumar el acumulador (supongamos que tiene el valor 10) y otro valor (supongamos que otro 10), y a veces obtiene 20 y otras veces 21, y no se explica por qué. La explicación es el flag C – carry, que a veces vale 0 y a veces vale 1, en función de la situación previa del registro de estado. Para evitar esto, siempre hay que preceder toda (o casi toda) instrucción “adc” con una instrucción “clc”, que lo que hace es borrar el acarreo.

Pero el hecho de sumar el flag C tiene su razón de ser. No está ahí para molestar. Al sumar dos bytes (el acumulador y otro) es posible que la suma exceda la capacidad del acumulador (ej. $ff + $ff = $1fe) y esto se señaliza poniendo el flag C a uno. En cierto modo el flag C nos está señalizando ese “1” extra que tenemos por la izquierda. Pero es que, además, si a uno no le llega con un byte para la suma, lo lógico es que use dos bytes. Y aquí es donde entra el acarreo: en la suma multi-byte.

Para sumar dos sumandos de dos bytes, lo que hay que hacer es:

  • Borrar el acarreo que pudiera venir heredado de antes (“clc”), para que así no moleste.
  • Sumar los bytes menos significativos de ambos sumandos. Uno tendrá que estar en el acumulador, y el otro estará en memoria según el modo de direccionamiento que se utilice. También se sumará el acarreo (“adc”), pero ya hemos dicho que lo habremos puesto a cero. El resultado se llevará al byte menos significativo de la suma.
  • Sumar los bytes más significativos de ambos sumandos. Nuevamente, uno tendrá que estar en el acumulador. Y también se sumará el acarreo derivado de la suma de los bytes menos significativos (“adc”). El resultado se llevará al byte más significativo de la suma.

Obsérvese que entre la suma de los bytes menos significativos y la suma de los bytes más significativos no se borra el acarreo (“clc”). Esto es deliberado; interesa tener en cuenta ese acarreo para que la suma sea correcta.

El procedimiento anterior se puede generalizar fácilmente a N bytes. Y ahora se le ve la razón de ser al acarreo: no sólo señalizar un desbordamiento de capacidad; también facilitar la suma multi-byte.

La instrucción “adc” soporta muchos modos de direccionamiento: inmediato, absoluto, página cero, absoluto indexado, página cero indexado, indirecto – indexado e indexado – indirecto. Y afecta a los flags: S – sign, V – overflow, Z – zero y C – carry.

Además, debe tenerse en cuenta que las reglas de la suma no son las mismas si se está usando codificación binaria (lo más habitual) o si se está usando codificación BCD (menos habitual). En el segundo caso habrá que informar al microprocesador mediante la instrucción “sed”.

Los detalles de esta instrucción se pueden consultar en http://www.6502.org/tutorials/6502opcodes.html#ADC.


Programa de ejemplo: Prog13

Instrucciones de transferencia entre registros

Las instrucciones de transferencia entre registros son:

  • “tax”.
  • “txa”.
  • “tay”.
  • “tya”.

Las dos primeras mueven el contenido del acumulador al registro X y viceversa. Las dos segundas mueven el contenido del acumulador al registro Y y viceversa.

Todas ellas utilizan el modo de direccionamiento implícito (porque los datos sobre los que se operan están implícitos) y afectan a los flags S – sign y Z – zero.

Los detalles de estas instrucciones se pueden consultar aquí: http://www.6502.org/tutorials/6502opcodes.html#TAX.


Programa de ejemplo: Prog12