Hace pocos dias empece a leer Assembly_Language_For_Intel_based_computers (solo voy por el capitulo 7). Ya tenia una pequeña base sobre las instrucciones en ensamblador gracias a que he empezado tambien a seguir el curso de ingenieria inversa de Ricardo Narvaja, asi que he ido haciendo algun ejercicio basandome en lo que habia estudiado del libro y en lo que habia visto a traves del debuger. No he tenido ningun problema ni para escribir el codigo ni para depurar los programas, pero antes de seguir avanzando, me gustaria que si podeis, los que mas tiempo llevais utilizando ensamblador, me dieseis algun consejo sobre estilo o alguna otra cosa que se pueda mejorar para obtener claridad. Se por C que es muy importante mantener el codigo ordenado y claro, pero no se si lo que estoy aplicando hasta ahora sirve solo a pequeña escala y luego al meterme en camisas de once varas acabare liado...
Bueno, aqui dejo el codigo y ¡que empieze el chaparron!
Un ultimo aviso, no he llegado a las interrupciones ni conozco la parte del API de Windows que controla la consola, asi que la entrada/salida la hago con funciones de C.
Ah, y el ensamblador que utilizo es MASM32, no se si hay diferencias con respecto al resto... solo llevo 5 dias con esto...
Código
; PROGRAMA: SWITCH ; Ejecuta un menu, recoge la opcion y despliega un mensaje segun la opcion .386 ; compatible con 80386 y posteriores .model flat, stdcall ; modo 32 bits protegido y convencion de llamadas .stack 4096 ; Tamaño de la pila... ;============================================================= ;stdio.h include c:\masm32\include\msvcrt.inc includelib c:\masm32\lib\msvcrt.lib ;ExitProcess include c:\masm32\include\kernel32.inc includelib c:\masm32\lib\kernel32.lib ;============================================================= .data opciones db "Escoge una opcion:",13,10,"1.",13,10,"2.",13,10,"0.Salir",13,10,">",0 formatoi db "%ld",0 cad1 db 13,10,"Opcion 1 escogida.",13,10,13,10,0 cad2 db 13,10,"Opcion 2 escogida.",13,10,13,10,0 cadsalir db 13,10,"Has escogido salir.",13,10,13,10,0 endl db 13,10,0 .data? ; Las dos componentes son el valor del caso y la direccion ; y de la funcion/codigo que ejecuta el codigo asociado SwitchTonto dd 4 dup(?) ; dos elementos tipo switch case: [caso,funcion] opcion dd ? .code main PROC mov ebx , OFFSET SwitchTonto ; cargamos la direccion de los pares valor-funcion xor eax , eax ; dejamos eax a cero mov eax , 1 ; pasamos a eax el primer caso del switch mov [ebx] , eax ; lo guardamos en el primer par mov [ebx + 4] , op1 ; y copiamos la direccion de la funcion que maneja el caso mov eax , 2 ; lo mismo con el segundo caso del switch mov [ebx + 8] , eax mov [ebx + 12] , op2 ;do{ MENU_INI: sub esp , 4 ;hacemos hueco en la pila para el valor de retorno call menu ; desplegamos la informacion y en la pila vuelve la opcion pop eax ; recogemos la opcion elegida mov esi , 2 ; esi sera el subindice para recorrer el array del switch SWITCH_INI: ; principio de la eleccion dec esi ; pasamos al siguiente indice cmp esi , 0FFFFFFFFh ; si es -1 jz SWITCH_FIN ; hemos terminado de leer el array y salimos del switch cmp eax , [ebx + 8 * esi] ; comparamos el valor devuelto por menu con el del caso jnz SWITCH_INI ; si no son iguales, vamos a comprobar el siguiente caso ;en este punto eax ya no nos sirve, ya que hemos encontrado la opcion elegida mov eax , 8 ; calculamos el desplazamiento dentro del par valor-funcion mul esi ; en el que se encuentra el offset de la direccion de la funcion: add eax , 4 ; 8 * indice + 4 call DWORD PTR [ebx + eax] ; y llamamos a la funcion que maneja el caso SWITCH_FIN: test eax , eax ; si la opcion elegida no es cero (salir) jnz MENU_INI ; volvemos a llamar al menu MENU_FIN: ;}while(opcion != 0); call salir ; desplegamos el mensaje de salida call crt_getchar ; esperamos a pulsar intro push 0 ; y terminamos el programa call ExitProcess ret main ENDP ; long menu(void) menu PROC pushad ; guardamos los registros para devolverlos como estaban ; do{}while(opcion<0 || opcion>2) PEDIR_OPCION_INI: push OFFSET endl ; imprimimos una nueva linea para separar el menu de lo anterior call crt_printf add esp , 4 ; "sacamos" el paramtro de printf de la pila push OFFSET opciones ; mostramos las opciones call crt_printf add esp , 4 ; y dejamos la pila apuntando a los registros pusheados push OFFSET opcion ; ponemos la direccion de la variable opcion push OFFSET formatoi ; la cadena de formato para scanf call crt_scanf ; llamamos a scanf add esp , 8 ; y desplazamos la pila tantas posiciones como bytes tenian los parametros call limpiarbuf ; sacamos todos los datos que quedan en la entrada mov eax , opcion ; pasamos a eax el valor introducido cmp eax , 0 ; si el valor es menor que cero jl PEDIR_OPCION_INI cmp eax , 2 ; o mayor que 2 jg PEDIR_OPCION_INI ; volvemos a pedir el dato PEDIR_OPCION_FIN: mov edi , esp ; copiamos en edi el puntero de pila mov [edi + 36] , eax ; y guardamos la opcion elegida despues de los 8 registros y la direccion de retorno popad ; dejamos los registros como estaban al entrar en la funcion ret ; y regresamos menu ENDP limpiarbuf PROC pushad ; guardamos los registros generales ;while(getchar() != '\n') LIMPIEZA_INI: call crt_getchar cmp eax , 10 jnz LIMPIEZA_INI LIMPIEZA_FIN: popad ; y dejamos los registros como antes de la llamada ret ; volvemos limpiarbuf ENDP ; imprime el mensaje de que se ha elegido la opcion 1 op1 PROC pushad push OFFSET cad1 call crt_printf add esp , 4 popad ret op1 ENDP ; imprime el mensaje de que se ha elegido la opcion 2 op2 PROC pushad push OFFSET cad2 call crt_printf add esp , 4 popad ret op2 ENDP ; imprime el mensaje de que se ha elegido salir salir PROC pushad push OFFSET cadsalir call crt_printf add esp , 4 popad ret salir ENDP END main
¡Saludos!