La respuesta es sí y no a la vez. La instrucción existe, pero no se llama LDPC, sino JMP (del inglés JuMP, salto). Y es que el cargar el PC con un valor trae como consecuencia inmediata que la ejecución pasa (salta) a la dirección que hemos cargado. Sirva este ejemplo de bucle infinito para ver de lo que hablamos:
0x100 CC 00 00 ldd #0
0x103 C3 00 01 bucleinf: addd #1
0x106 7E 01 03 jmp bucleinf
El fragmento de código carga primero el registro D con 0 y, a continuación, se pone frenéticamente a incrementarlo en una unidad ad infinitum. Cuando D llega a 0xFFFF, pasa a 0x0000, se activa el bit de acarreo y vuelta a empezar.
Observad que, aunque lo que se carga en el PC es el valor de la
etiqueta bucleinf y no el valor contenido en la dirección
de memoria apuntada por ella, no se pone el símbolo
# delante de ella. Comparad con las instrucciones:
...
0x200 8E 01 03 ldx #bucleinf ; carga X con 0x103
0x203 9E 01 03 ldx bucleinf ; carga X con lo que hay en 0x103;
; mirando arriba el código
; ensamblado, X se carga con 0xC300
...
No es, sin embargo, esta la instrucción de salto que más se usa. De su familia es la instrucción BRA (del inglés BRAnch, desvío o ramal). El bucle infinito anterior, desde el punto de vista del ensamblador es equivalente al que se muestra a continuación, pero prestad atención al código máquina:
0x100 CC 00 00 ldd #0
0x103 C3 00 01 bucleinf: addd #1
0x106 20 FB bra bucleinf
0x108 ...
El código de instrucción de BRA es 0x20, pero ¿De dónde viene el misterioso 0xFB que aparece a continuación? La respuesta es que se trata de un salto relativo. Dicha instrucción, en código máquina suma el siguiente byte, considerado con signo, al contenido del PC.
0xFB en complemento a dos es -(1+~0xFB) = -(1+0x04) = -5. Luego sumamos al PC -5 para encontrar el destino del salto: 0x108-5 = 0x103. Recordad que, cuando se ejecute la instrucción BRA, el PC ya está apuntando a la siguiente instrucción. Ni que decir tiene que el cálculo de estos desplazamientos los hace el ensamblador por nosotros.
El utilizar BRA en lugar de JMP tiene tres ventajas y un inconveniente:
Para solucionar el inconveniente último, existe una versión que permite desplazamientos de 16 bits: LBRA (Long BRAnch), aunque es más lenta que JMP y ocupa lo mismo.
| N | Z | V | C | |
| Flag activo | BMI | BEQ | BVS | BCS |
| Flag inactivo | BPL | BNE | BVC | BCC |
El significado de las dos últimas columnas es directo: Branch if C Set, Branch if C Clear, ... En el caso del flag Z quedará claro más abajo por qué BEQ significa Branch if EQual y BNE significa Branch if Not Equal. El bit del signo N se activa cuando el resultado de una operación que le afecta es negativo (Branch if MInus) y se desactiva cuando dicho resultado es positivo o cero (Brach if PLus).
Cuando se usan estas instrucciones hay que tener muy presentes cuáles de las instrucciones precedentes han afectado a los flags. Es por ello que en los resúmenes de al final de cada sesión se pone los flags afectados por las instrucciones vistas.
Usemos nuestro nuevo conocimiento para simplificar un poco aquel galimatías de la sesión anterior en el que teníamos que sumar siete al registro B si este era mayor o igual que 10. Recordemos el código original:
std temp
clra
addb #246
adca #0 ; en A hay un 1 si la primera cifra es mayor o igual que 10
ldb #'A-'9-1
mul
addb temp+1
Con lo que hemos visto, lo podemos simplificar bastante:
addb #246
bcc no_sumes
addb #'A-'9-1
no_sumes: subb #246
El nuevo código es más rápido, ocupa menos y no necesita hacer uso de una variable temporal ni mancha el registro A. Pues aún lo tenemos que mejorar un poco más en lo que queda de sesión.
El ensamblador logra acercarse al significado de los saltos condicionales
mediante un sencillo truco. Supongamos que queremos comparar dos
números almacenados en las posiciones de memoria valor1
y valor2. Lo que tenemos que
hacer es hallar la diferencia entre A y B. El ensamblador tiene una
serie de instrucciones que, fijándose en los flags que ha
dejado dicha resta, puede efectuar uno u otro tipo de salto condicional.
El primer ejemplo ya lo hemos visto. Supongamos que queremos saltar
a la etiqueta destino si A es igual a B. Al hacer A-B,
el resultado nos da cero, por lo que el flag Z se activa. Si miramos
a la tabla de saltos de arriba, debemos codificar lo anterior así:
lda valor1
suba valor2
beq destino ; BEQ corresponde con saltar cuando Z=1
Esta es la razón por la que la instrucción que salta cuando Z vale 1 no se llama BZS (Branch if Z Set), sino BEQ (Branch if EQual): porque están preparadas para ejecutarse después de haber hecho una resta.
Como todos ya sabemos, un número puede ser mayor o menor que otro dependiendo de si los interpretamos como números con o sin signo. Así, en aritmética de 8 bits, 0xFF corresponde con 255, si se considera sin signo, o a -1, si se considera con signo. Si tomamos que A contiene 0x07 y B contiene 0xFF, A<B si consideramos los números sin signo o A>B, si los consideramos con signo. Por esta razón, existen para todas las posibilidades de salto dos versiones, con signo y sin signo y son las que se muestran a continuación:
| Condición | Sin signo | Con signo |
| Mayor que | BHI (higher) | BGT (greater than) |
| Mayor o igual que | BHS (higher or same) | BGE (greater or equal) |
| Igual que | BEQ (equal) | |
| Diferente de | BNE (not equal) | |
| Menor o igual que | BLS (lower or same) | BLE (less or equal) |
| Menor que | BLO (lower) | BLT (less than) |
El ejemplo anterior en que sumamos 7 al registro B si vale 10 o más, tiene mucho más significado si se programa así:
subb #10 ; vamos a comparar con 10
blo no_sumes ; saltar si es menor
addb #'A-'9-1 ; sumarle 7
no_sumes: addb #10 ; sumar 10 para compensar los 10 que le quitamos
El acabado profesional de este fragmento de código nos lo da el usar la instrucción CMP. Esta instrucción realiza la resta al igual que hace SUB, activa los flags correspondientes como hace SUB, pero a diferencia de SUB, descarta el resultado y no modifica el registro, por lo que el código anterior, si se usa CMP, quedaría:
cmpb #10 ; vamos a comparar con 10
blo no_sumes ; saltar si es menor
addb #'A-'9-1 ; sumarle 7
no_sumes: ... ; continuar con lo que siga
Después de la visita al cirujano plástico, nuestro programa que imprime un número en hexa por la pantalla nos ha quedado bastante rejuvenecido:
.area PROG (ABS)
; definimos una constante
fin .equ 0xFF01
pantalla .equ 0xFF00
.org 0x100
.globl programa
programa:
lda #28 ; pongamos este nUmero como prueba
; imprimamos 0x
ldb #'0
stb pantalla
ldb #'x
stb pantalla
; primero imprimamos la primera cifra hexadecimal
tfr a,b
lsrb
lsrb
lsrb
lsrb ; en B estA la primera cifra, de 0 a 15
cmpb #10
blo no1
addb #'A-'9-1
no1: addb #'0
stb pantalla
; ahora imprimimos la segunda cifra hexadecimal
tfr a,b
lslb
lslb
lslb
lslb
lsrb
lsrb
lsrb
lsrb ; en B estA la segunda cifra, de 0 a 15
cmpb #10
blo no2
addb #'A-'9-1
no2: addb #'0
stb pantalla
; imprimamos un salto de lInea al final
ldb #'\n
stb pantalla
; el programa acaba
clra
sta fin
.org 0xFFFE ; vector de RESET
.word programa
.area PROG (ABS)
; definimos una constante
fin .equ 0xFF01
pantalla .equ 0xFF00
.org 0x100
.globl programa
programa:
lda #137 ; un nUmero como cualquier otro, para probar
; primera cifra
ldb #'0
cmpa #100
blo Menor100
suba #100
incb
cmpa #100
blo Menor200
incb
suba #100
Menor100:
Menor200:
stb pantalla
; segunda cifra. En A quedan las dos Ultimas cifras
clrb
cmpa #80
blo Menor80
incb
suba #80
Menor80:lslb
cmpa #40
blo Menor40
incb
suba #40
Menor40:lslb
cmpa #20
blo Menor20
incb
suba #20
Menor20:lslb
cmpa #10
blo Menor10
incb
suba #10
Menor10:addb #'0
stb pantalla
adda #'0
sta pantalla
; imprimimos un salto de lInea
ldb #'\n
stb pantalla
; el programa acaba
clra
sta fin
.org 0xFFFE ; vector de RESET
.word programa
Expliquémoslo. El algoritmo consiste en ir desgajando al número,
reduciéndolo, mientras se van imprimiendo sus cifras:
Está claro que si queremos que el registro B contenga la segunda cifra, esa cifra será un número del 0 al 9 y ocupará por tanto, 4 bits: del 00002 al 10012. ¿Qué números tendrán el bit más significativo de B activo? Los 80s y los 90s. Luego el número con el que debo comparar primero es 80. Al igual que arriba, si el número es mayor o igual que 80, activamos el cuarto bit de B (10002) y le quitamos 80 al número. Si no, no hacemos nada. El número que queda es ahora menor que 80. ¿Qué números menores que 80 tienen el tercer bit de la segunda cifra activado? Los 40s, los 50s, los 60s y los 70s, es decir, los mayores o iguales que 40. El siguiente número para comparar es 40, y así se va prosiguiendo comparando con 20 y con 10.
Como ya os habréis dado cuenta, los bits de B no se activan directamente, sino que se van haciendo desplazamientos a la izquierda e incrementos con el mismo resultado final.
; segunda cifra. En A quedan las dos Ultimas cifras
clrb
cmpa #80
blo Menor80
incb
suba #80
Menor80:lslb
cmpa #40
blo Menor40
incb
suba #40
Menor40:lslb
cmpa #20
blo Menor20
incb
suba #20
Menor20:lslb
cmpa #10
blo Menor10
incb
suba #10
Menor10:addb #'0
En este fragmento de código vemos claramente un patrón que se repite. Es un muy buen candidato para construir un bucle. Para ello, seguimos los siguientes pasos
lslb
cmpa X
blo etiqueta
incb
suba X
etiqueta:
tfr d,x ; guardamos D en X temporalmente
lda #80 ; valor inicial: variable=80
sta variable ; variable estarA definida al principio del programa
tfr x,d ; recuperamos D
bucle: lslb
cmpa variable
blo Menor
incb
suba variable
Menor: lsr variable ; variaciOn de la variable entre iteraciones
tfr d,x ; condiciOn de permanencia en el bucle
lda variable
cmpa #10
tfr x,d
bhs bucle
.area PROG (ABS)
; definimos una constante
fin .equ 0xFF01
pantalla .equ 0xFF00
.org 0x100
.globl programa
variable: .byte 0
programa:
lda #137
; primera cifra
ldb #'0
cmpa #100
blo Menor100
suba #100
incb
cmpa #100
blo Menor200
incb
suba #100
Menor100:
Menor200:
stb pantalla
; segunda cifra. En A quedan las dos Ultimas cifras
ldb #80
stb variable
clrb
bucle: lslb
cmpa variable
blo Menor
incb
suba variable
Menor: tfr d,x
lda variable
lsra
sta variable
cmpa #10
tfr x,d
bhs bucle
addb #'0
stb pantalla
adda #'0
sta pantalla
; imprimimos un salto de lInea
ldb #'\n
stb pantalla
; el programa acaba
clra
sta fin
.org 0xFFFE ; vector de RESET
.word programa
A esta técnica se le denomina enrollamiento de bucles. A veces, se hace el proceso inverso: cuando se quiere interpretar un código que no se entiende se prueba a ver si desenrollando el bucle se ve la luz
= si el ordenador acertó,
< si el número que piensa es menor que el
del ordenador y >, si es mayormayor y menormayor=1000 y
menor=99mayor y menor.
En nuestro caso, 549mayor para que valgan: mayor=549 y
menor=99menor para que valgan: mayor=1000 y
menor=549Ejemplo de una ejecución:
549 < 324 > 436 < 380 < 352 < 338 < 331 > 334 < 332 > 333 =
NOPJMP xBxx yLBCC, LBCS, LBNE, LBEQ, LBVC, LBVS, LBPL, LBMI,
LBHI, LBHS, LBLS, LBLO,
LBGT, LBGE, LBLE, LBLT, LBRA, LBRNCMPxTSTxTSTlscdrmmancatecho $?