Buenas, no te he podido responder antes porque aún estoy fuera...
Para tu duda, simplemente creas las variables tal cual dentro de tu función: "int numero;", "char letra;", "char* cadena;", "long pepito;", etc... y dentro del código asm, cuando necesites acceder a una de ellas simplemente debes obtener la dirección de la variable y luego utilizando uno de los modos de direccionamiento soportados por el procesador, lees o escribes en esa variable.
A modo de ejemplo: "sumar dos números en ensamblador":
Código:
#include <stdio.h>
void main (void){
int a, b, resultado;
printf("Introduce numero a: \n");
scanf("%d", &a);
printf("Introduce numero b: ", &b);
scanf("%d", &b);
__asm{
lea esi, a //esi=dirección de variable a
lea edi, b //edi=dirección de variable b
mov eax, [esi] //eax=contenido de variable a
mov ebx, [edi] //ebx=contenido de variable b
add eax, ebx //eax=eax+ebx
lea edx, resultado //edx=dirección de variable resultado
mov [edx], eax //resultado=eax=suma de a + b
}
printf("Resulado: %d\n", resultado);
}
Por supuesto hay múltiples formas de hacerlo... y mucho más si el procesador es de arquitectura CISC.
En cuanto a un ejemplo de imprimir por pantalla una cadena de texto, pues debes tener en cuenta un pequeño detalle...
El programa que logres compilar, cuando sea ejecutado por el sistema operativo, lo hará en modo usuario, por lo que el acceso a los periféricos directamente ("directamente" quiere decir acceder a los registros o mapa de memoria del periférico), desde ese programa desembocarán en una excepción impidiendo por tanto dicho acceso.
Por tanto, ¿Qué mecanismos hay para acceder en un sistema operativo con protección a los periféricos? La respuesta es mediante llamadas al sistema.
En el caso de DOS se disponía de interrupciones software (instrucciones INT xx), las cuales identificaban mediante el número "xx", la SSI de ésta desviando el flujo de ejecución hacia la primera instrucción apuntada por el vector de interrupción asociado. En Windows (superiores a NT4.0), se dispone de la API WIN32. (No voy a entrar ahora en más detalles de por qué a los cambios ni lo que ocurre minuciosamente).
Puesto que en tu caso se trata de una aplicación DOS emulada bajo un Windows con arquitectura NT, puedes usar tanto una como otra. Es decir, puedes usar funciones de la API (echa un vistazo a la MSDN sobre el uso de la consola: http://msdn.microsoft.com/en-us/library/ms682055(VS.85).aspx), o bien, como ya has planteado, usar directamente las funciones del DOS, (mediante la interrupción INT 21, con la función ah=09). De un modo u otro, sería recomendable que echaras un vistazo a todas las funciones del DOS si vas a seguir desarrollando aplicaciones DOS (hay para el manejo del ratón, del teclado, de sonido, de acceso al disco, a ficheros, etc...)... en la época del MS-DOS, conocer todas estas funciones significaba ser "un gurú" de la programación jejeje.
La cuestión radica en ver cómo realizar las llamadas a esas funciones.
Todo radica en ver cómo funciona el tema de las llamadas en ensamblador. Como ya sabes, ésto se lleva a cabo mediante la instrucción CALL $dirección_funcion, pero la mayoría de las veces las funciones o procedimientos a los que se llaman reciben parámetros (y en el caso de las funciones, devuelven un resultado).
¿Cómo paso los parámetros? La respuesta es: Mediante la pila.
Existen estándares que implementan las funciones llamadas y que el proceso que llama a estas funciones debe cumplir si se quiere que el código funcione... existen por ejemplo los estándares cdecl, stdcall, syscall, fastcall, etc... (y que no voy a explicar ahora...) y que cada API, cada función, o cada cual adapta según sus conveniencias. En el caso de la API WIN32, el estándar segudio es el stdcall... (sería recomendable que echaras un vistazo a este tema antes de seguir, y si no entiendes algo, pregunta).
Así, los parámetros se pasan mediante instrucciones PUSH (de derecha a izquierda si es stdcall), y cuando ya estén todos, entonces se llama a la función con un CALL.
Esta función llamada, siguiendo el estandar stdcall (también ocurre con algunos otros) lo primero que hará será hacer un PUSH EBP; MOV EBP, ESP; que prepara la pila para su uso interno, y para poder acceder a los parámetros... por ejemplo mediante un MOV EAX, [EBP+8]... (no quiero ser pesado, ni extenderme mucho más... pero si quieres puedes leer los números de la ezine de aquí como punto de partida en este tema).
No obstante, gracias a los fabricantes de compiladores, que escribieron código engorroso por nosotros, implementaron funciones de manejo mucho más fácil que toda la API, de forma que el programador no tuviera que interactuar directamente con ella. Así por ejemplo en C, tienes la función "fopen" para abrir un fichero, definido como tipo FILE*, y que utiliza las llamadas de más bajo nivel "open" y "fdopen" para el manejo de descriptores de fichero, las cuales ya interactúan con el sistema operativo... en el caso de Windows, llamando a "CreateFile".
En tal caso, si optas por no interactuar con la engorrosa API, puedes hacer en tu código ensamblador un CALL directamente a dicha función (sabiendo que en C, el estandar seguido es cdecl). Para el caso de "imprimir una cadena de texto", puedes hacer uso de la función "printf" que ya implementa C, y según sea el sistema operativo, dicha función llamará a las funciones del sistema que sean necesarias para llevar a cabo la tarea (siendo ya el SO quien accede al hardware).
Así a modo de ejemplo, para "imprimir una cadena de texto" en ensamblador, usando C, puedes hacer lo siguiente:
Código:
#include <stdio.h>
void main(){
const(char*) cadena = "Hola DOS\n";
__asm{
push dword ptr cadena
call printf
add esp,8
}
}
Después de lo que llevas de lectura de este post, te remito al post: http://foro.hackhispano.com/showthread.php?t=34665#17, que ya hablamos y en donde te explico brevemente el flujo de ejecución de un proceso que accede al hardware, por si te entra la duda de "interactuar" a más bajo nivel.
Un saludo.
Marcadores