1. Registros.
El lenguaje ensamblador no tiene "nombres de variables" como pueda tener C++ o Delphi. En vez de eso, cada CPU tiene algunos registros. Los registros más interesantes son de propósito general, porque los puedes utilizar como "variables". EL MN103S tiene 4 registros de direcciones (A0..A3) y 4 registros de datos (D0..D3). Los registros de dirección los usarás normalmente para guardar punteros y los registros de datos los usarás normalmente para guardar valores. Por ejemplo. Digamos que en Delphi tenemos una variable llamada "calcresult". Si quieres guardar el valor "3" en esta variable, y luego sumarle 4, deberías hacer lo siguiente:
calcresult:=3;
calcresult:=calcresult+4;
La variable ahora contiene el valor "7". Para hacer lo mismo en ensamblador, usaremos un registro de datos. Vamos a usar D0:
mov 3, D0 <= Esta instrucción guarda el valor "3" en el registro de datos (como si fuera una "variable").
add 4, D0 <= D0 ahora contiene "7"
Así que como puedes ver, puedes pensar en los registros de datos como "variables" para guardar valores y realizar cualquier tipo de cálculo en ellos. Ahora, puedes guardar datos en registros de direcciones también, pero la diferencia es que normalmente usarás los registros de direcciones como punteros. Digamos que tienes un puntero a una tabla y quieres guardar el resultado de nuestro cálculo anterior en el primer byte de la tabla. Lo podemos hacer así:
mov 0x50000, A0 (O si estás programando y tienes una etiqueta para la tabla, puedes usar mov #tablename, A3 por ejemplo).
movbu D0, (A0)
Bien, lo que hace esto es mover el puntero a nuestra tabla a un registro de datos, y entonces mover la variable D0 (que contiene "7"), a la dirección de memoria 0x50000. Esto se llama "Direccionamiento indirecto". Para clarificarlo:
mov 0x1000, A0 <= Esto guarda en A0 el valor 0x1000
move 0x1000, (A0) <= Esto es "direccionamiento indirecto" y mueve el valor a la dirección de memoria que está en A0 (en este caso contiene 0x1000, así que estas dos instrucciones moverían el valor "7" a la dirección de memoria 0x1000.
Ok, digamos que queremos introducir otro valor en nuestra tabla y que esta tenga 5 entradas, cada una de un byte de longitud. ¿Cómo movemos un byte a la siguiente entrada de la tabla? Simple. Puedes hacerlo de dos maneras: simplemente añadiendo 1 byte sobre A0 (dirección de memoria 1X001):
add 1, A0
movbu D0, (A0)
Lo que conseguimos es mover el contenido de la variable D0 a la dirección de memoria 0x1001. También puedes usar lo siguiente:
mov 1,D1
movbu D0,(D1,A0)
Lo que hacemos es mover el contenido de D0 a la dirección de memoria que se calcula mediante añadir el contenido de D1 a D0. D1=1 en este ejemplo y asumimos que A0 sigue conteniendo 0x1000. Esto moverá el contenido de D0 a la dirección de memoria 0x1001, que es la segunda entrada de la tabla de nuestro ejemplo.
Te habrás dado cuenta de que uso "movbu" en vez de "mov". Esta extensión "bu" significa "byte, unsigned", así que estamos moviendo un BYTE y no una palabra o palabra larga por ejemplo. Puedes usar MOVH por ejemplo (esto mueve una "sign extended half word), o solamente MOV, que mueve una palabra. Mira el manual de instrucciones para ver los diferentes tipos de "MOV".
2. Flujo de programa.
Para llamar a una subrutina usamos la instrucción "call". Por ejemplo:
call sub_1
Esta instrucción salta a la rutina "sub_1". Empieza a ejecutarla hasta que se encuentra una instrucción "ret" o "retf", las cuales dicen a la CPU que ha llegado al fin de la subrutina y que continue ejecutando a continuación de la instrucción que llamó a la subrutina. También puedes "saltar" a una subrutina mediante la instrucción "jmp". Por ejemplo:
jmp sub_1
Esto hace lo mismo que una instrucción "call". Salta a la rutina y la ejecuta. PERO con esta instrucción la rutina no va a retornar al punto de partida, sino que sigur a continuación de su finalización.
3. Estructuras IF-THEN.
En Delphi puedes programar que SI calcresult= 4 ENTONCES sub_1. En ensamblador, esto se hace mediante "comparación" de un valor con otro y "bifurcar" hacia una subrutina basándose el resultado de la comparación. Por ejemplo:
cmp 4, D0
beq sub_1
Esto compara si D0 y "4" son iguales para lanzar la subrutina sub_1. Hay muchas condiciones de comparación, por ejemplo puedes usar "menor que":
cmp 4, D0
blt sub_1
También puedes usar "no igual a" (bne), "mayor que"... mira el manual de instrucciones.
4. Operaciones aritméticas.
Puedes sumar, restar, multiplicar, dividir, AND, OR, XOR... etc los registros de datos. Mira el manual de instrucciones:
XOR 0xF,D0
Esto hace un XOR al valor de D0 y guarda el resultado en D0.
Una cosa más: estudia el manual de instrucciones, observa el código fuente y serás capaz de deducirlo casi todo. Por ejemplo:
movbu (0xF1B5),D0
Te habrás dado cuenta de que los paréntesis indican un direccionamiento indirecto. Lo que hace eso es mover el valor del byte que está en la dirección de memoria 0xF1B5 a D0.
Una cosa más para terminar: Puedes fijar/borrar bits en la memoria mediante "bset" o "bclr" y probarlos mediante "btst" y entonces bifurcar con un "beq" por ejemplo. Si tienes alguna pregunta, o no entiendes algo, pregunta aqui (en este post de EOL) y entre todos intentaremos comprender el ensamblador del controlador del lector de DVD de la XBOX360.
Una cosa más (by scener), en este documento se hace mucho hincapié en el manual del microcontrolador MN103S. Creo que sería de ayuda buscarlo por internet para complementar un poco todo esto y llevar a buen puerto este aprendizaje que tan util puede sernos, tanto para XBOX360 como para el futuro.
Y aquí el link al manual del microcontrolador.
Manual