Instrucciones de salto incondicional

La única instrucción de salto incondicional es:

  • “jmp”.

“jmp” produce un salto en el contador de programa a la dirección indicada, cuando se utiliza el modo de direccionamiento absoluto (ej. “jmp $c000”). Y produce un salto a la dirección contenida en la dirección indicada (puntero o vector), cuando se utiliza el modo de direccionamiento indirecto (ej. “jmp ($c000)).

En este último caso, puesto que una posición de memoria sólo puede contener un único byte, y una dirección del C64 requiere dos bytes, se aplica la regla de que el LSB de la dirección de destino está en la posición indicada en la instrucción, y el MSB de la dirección de destino está en la posición siguiente (ej. $c000 => LSB destino, $c001 => MSB destino). Esto es así porque el 6510 sigue la arquitectura “Little endian”.

Jmp

“jmp” no soporta más modos de direccionamiento. Sólo el absoluto y el indirecto. Y no modifica ningún flag del registro de estado.

Sus detalles pueden verse aquí: http://www.6502.org/tutorials/6502opcodes.html#JMP.

Instrucciones de rotación de bits

Las instrucciones de rotación de bits son:

  • “rol”.
  • “ror”.

Estas instrucciones son básicamente equivalentes a las ya vistas “asl” y “lsr”, con la única diferencia de que el bit que sale del acarreo se vuelve a introducir en el bit 0, en el caso de “rol”, y en el bit 7, en el caso de “ror”.

Rot bits

Estas instrucciones soportan los mismos modos de direccionamiento que “asl” y “lsr”, es decir, los modos: acumulador, absoluto, página cero, absoluto indexado y página cero indexado. Y afectan a los mismos flags: S – sign, Z – zero y C – carry.

Sus detalles se pueden consultar aquí: http://www.6502.org/tutorials/6502opcodes.html#ROL.


Programa de ejemplo: Prog19

Instrucciones de desplazamiento de bits

Las instrucciones de desplazamiento de bits ya se presentaron al hablar de multiplicaciones y divisiones:

  • “asl”.
  • “lsr”.

La instrucción “asl” mueve los bits del acumulador (o de una posición de memoria) un bit a la izquierda, metiendo el bit más significativo (bit 7) en el flag C – carry, y un bit a 0 en el bit menos significativo (bit 0). Esta operación equivale a multiplicar por dos.

Contrariamente, la instrucción “lsr” mueve los bits del acumulador (o de una posición de memoria) un bit a la derecha, metiendo un bit a 0 en el bit más significativo (bit 7), y el bit menos significativo (bit 0) en el flag C – carry. Esta operación equivale a dividir por dos.

Desp bits

Estas instrucciones son útiles, con carácter general, para manipular bits. Por ejemplo, moviendo cuatro bits a la derecha, se puede pasar de un byte a su nibble más significativo.

Estas instrucciones soportan los modos de direccionamiento acumulador, absoluto, página cero, absoluto indexado y página cero indexado. Y afectan a los flags S – sign, Z – zero y C – carry.

Sus detalles se pueden consultar en http://www.6502.org/tutorials/6502opcodes.html#ASL y http://www.6502.org/tutorials/6502opcodes.html#LSR.


Programa de ejemplo: Prog18

Bucles

Juntando contadores, comparaciones y saltos condicionales es muy fácil hacer bucles, que son construcciones muy típicas en todo tipo de programación consistentes en repetir la ejecución del mismo segmento de programa N veces, hasta que se cumple una condición de fin.

En ensamblador del 6510 hay muchas formas de hacer bucles, pero la forma más típica consiste en utilizar el registro X o el registro Y como contador (creciente o decreciente), comparar X o Y contra un valor de fin y, mientras no se alcance ese valor de fin, incrementar / decrementar el contador y repetir la ejecución.

También es posible hacer bucles con contadores basados en posiciones de memoria (instrucciones “inc” y “dec”). Una ventaja de hacer esto es que, mediante el uso de varios bytes, se pueden superar fácilmente las 256 iteraciones, que es el tope cuando se utiliza un único registro índice (8 bits).


Ejemplo de bucle basado en el registro X: Prog08
Ejemplo de bucle basado en posiciones de memoria: Prog10

Instrucciones de comparación

Las instrucciones de comparación son:

  • “cmp”.
  • “cpx”.
  • “cpy”.

La primera compara el acumulador con el operando. La segunda compara el registro X con el operando. Y la última compara el registro Y con el operando.

En los tres casos, la comparación se articula como la resta registro – operando, y los flags del registro de estado se activan o desactivan en función de esa resta. Por ello, si la comparación/resta arroja la igualdad, se activa el flag Z – zero; en caso contrario, se desactiva el flag Z – zero. Igualmente, si la comparación/resta arroja que el registro es mayor que el operando (resta positiva), se activa el flag C – carry; en caso contrario (resta negativa), se desactiva el flag C – carry.

“cmp” soporta los modos de direccionamiento inmediato, absoluto, página cero, absoluto indexado, página cero indexado, indirecto – indexado e indexado – indirecto. Por su parte, “cpx” y “cpy” sólo soportan los modos de direccionamiento inmediato, absoluto y página cero.

Todas estas instrucciones modifican los flags S – sign, Z – zero y C – carry. Sus detalles se pueden consultar aquí: http://www.6502.org/tutorials/6502opcodes.html#CMP.

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