Usted está aquí: Inicio Ver Cursos Archivados Arquitectura de Ordenadores ejercicios-1 Ejercicios de Arquitectura de Ordenadores (2)

Ejercicios de Arquitectura de Ordenadores (2)

Acciones de Documento
  • Vista de contenidos
  • Marcadores (bookmarks)
  • Exportación de LTI
Autor: Abelardo Pardo

 

1. Rutina sospechosa

Un programador ha escrito la siguiente rutina:

rutina:
push %ebp
mov %esp, %ebp
add $13, 4(%ebp)
pop %ebp
ret

El programa principal que invoca a esta rutina contiene las siguientes instrucciones tal y como las muestra el compilador en su informe sobre el restulado del ensamblaje:

1                            .data
2 0000 50726F67 msg: .asciz "Programa terminado.\n"
2 72616D61
2 20746572
2 6D696E61
2 646F2E0A
3
4 .text
5 .globl main
6 main:
7 0000 50 push %eax
8 0001 51 push %ecx
9 0002 52 push %edx
10
11 0003 E8FCFFFF call rutina # Llamada a rutina dada
11 FF
12
13 0008 68000000 push $msg
13 00
14 000d E8FCFFFF call printf
14 FF
15 0012 83C404 add $4, %esp
16
17 0015 5A pop %edx
18 0016 59 pop %ecx
19 0017 58 pop %eax
20 0018 C3 ret

Explicar detalladamente cuál es el resultado de la ejecución de este programa. ¿Ejecuta correctamente? ¿Qué escribe por pantalla? ¿Por qué?

2. Manipulación de la dirección de retorno de una subrutina

El programa retorno.s, listado a continuación, contiene tres bloques de código, cada uno de los cuales simplemente escribe un mensaje por pantalla y llama a la subrutina manipular.

.data
texto: .asciz "Ejecutando bloque %c\n"

.text
.global main

main:
push %eax # salvar registros
push %ecx
push %edx

bloqueA: # bloque A
push $'A'
push $texto
call printf
add $8, %esp

call manipular
bloqueB: # bloque B
push $'B'
push $texto
call printf
add $8, %esp

call manipular
bloqueC: # bloque C
push $'C'
push $texto
call printf
add $8, %esp

call manipular
done:
pop %edx # restaurar registros
pop %ecx
pop %eax
ret

# Rutina para manipular la direccion de retorno

manipular:
# rutina vacia: no hace nada
# añade el codigo necesario aqui
ret

Tal como está codificado, la rutina manipular no hace nada, de modo que el programa simplemente escribe los tres mensajes en orden y termina, tal como se muestra:

shell$ ./retorno
Ejecutando bloque A
Ejecutando bloque B
Ejecutando bloque C
shell$

Se pide: Añadir el código necesario en la rutina manipular para que los mensajes se impriman en orden A-C-B y el programa no termine nunca:

shell$ ./retorno
Ejecutando bloque A
Ejecutando bloque C
Ejecutando bloque B
Ejecutando bloque A
Ejecutando bloque C
Ejecutando bloque B
Ejecutando bloque A
Ejecutando bloque C
Ejecutando bloque B
...

Es decir, primero debe ejecutarse el bloque A, después el bloque C, después el bloque B y después vuelve al bloque A, repitiéndose indefinidamente el proceso.

Recuerda que la dirección de retorno se almacena en la cima de la pila justo antes de llamar a la subrutina. La rutina manipular debera comprobar la direccion de retorno y cambiarla por el valor adecuado dependiendo del punto desde donde se haya llamado. Puedes utilizar las etiquetas definidas tanto para identificar el punto al que debía volver como para especificar la nueva dirección.

Importante:No debe modificarse el código del programa principal, sólo la rutina. Si no, la solución no será válida.

3. Cálculo del factorial

Recordemos que el factorial de un número n se calcula como:

n! = n * (n-1) * ... * 1.

Esta fórmula se puede reescribir de la siguiente forma:

n! = n * (n-1)!

La fórmula anterior nos sugiere una posible manera de implementar un método en Java que calcule el factorial de un número de forma recursiva, es decir, invocándose a si mismo:

1: public int factorial(int a) {
2: int resultado;

3: resultado = 1;

4: if (a < 0) {
5: resultado = 0;
}
6: else if (a > 1) {
7: resultado = a *
8: factorial(a - 1);
9: }

9: return resultado;
10:}

Nótese que cuando el método recibe un número negativo el resultado que se devuelve es cero.

Se pide:Crear un fichero factorial.s con una subrutina que codifique el método en Java para calcular el factorial mostrado anteriormente. La subrutina recibe sus parámetros a través de la pila y devuelve el resultado (un entero) en la posición 12(%ebp). Se debe de reservar espacio para la variable local resultado.

El código del fichero main.s que invoca a la rutina factorial es el siguiente:

.data
msg: .string "Factorial(%d) = %d\n"

.text
.globl main

main:
push %ebp
mov %esp, %ebp

push %eax
push %ebx
push %ecx
push %edx

mov $0, %ebx
loop: cmp $10, %ebx
jz done

sub $4, %esp
push %ebx
call factorial
add $4, %esp

push %ebx
push $msg
call printf
add $12, %esp

inc %ebx
jmp loop

done: pop %edx
pop %ecx
pop %ebx
pop %eax

mov %ebp, %esp
pop %ebp
ret


Compilar los dos ficheros y obtener el ejecutable, que ha de producir la siguiente salida:

shell$ factorial
Factorial(0) = 1
Factorial(1) = 1
Factorial(2) = 2
Factorial(3) = 6
Factorial(4) = 24
Factorial(5) = 120
Factorial(6) = 720
Factorial(7) = 5040
Factorial(8) = 40320
Factorial(9) = 362880
shell$

Nótese que el programa consta de dos ficheros, main.s y factorial.s. No se permite realizar modificaciones en main.s. Únicamente se entrega el fichero factorial.s.

4. Imprimir los argumentos de un programa

Cuando se ejecuta un programa en ensamblador, en realidad el código es una subrutina interna de un programa mucho más grande. El nombre de la subrutina es main, por eso se ha de poner esta etiqueta como punto de entrada de un programa.

La subrutina main tiene la posibilidad de recibir parámetros directamente del usuario por medio de la línea de comandos. Supóngase que el fichero printargs.s se compila para obtener el ejecutable printargs. Para ejecutar el programa simplemente se debe teclear su nombre en el intérprete de comandos de unix. Si en lugar de poner el nombre sólo, se añade a continuación una secuencia de strings separados por espacios, estos strings se pueden acceder desde la rutina main a través de su bloque de activación. Por ejemplo, al introducir el comando:

$ printargs argumento1 argumento2 argumento3

los strings argumento1, argumento2 y argumento3 se almacenan en el bloque de activación de la rutina como parámetros, con lo que el programa puede acceder a ellos.

La forma en que se almacenan estos strings es un array. En el ejemplo anterior, el primer componente del array es la dirección del string que contiene el nombre del programa, el segundo es la dirección del string argumento1 y así sucesivamente. La rutina main recibe como primer parámetro (en 8(%ebp)) un entero que indica el número de strings recibidos y como segundo parámetro (en 12(%ebp) la dirección base del array en el que están almacenadas las direcciones de los strings pasados como argumentos. Este array tiene la particularidad adicional de que en su última posición se ha almacenado una dirección de memoria con valor cero.

La siguiente figura ilustra cómo recibe los parámetros la rutina main y cómo se almacenan en la pila.

Escribir el programa printargs.s que atraviese el array de argumentos utilizando el modo de direccionamiento base + índice escalado e imprima todos los argumentos recibidos uno por línea. El programa no debe imprimir ningún otro dato por pantalla.

Verificar que el programa funciona como se espera con los siguientes cuatro ejemplos:

prompt$ printargs arg1 arg2 arg3
printargs
arg1
arg2
arg3

prompt$ printargs
printargs
prompt$ printargs arg1
printargs
arg1
prompt$ printargs as%d%d%d%d%d%d%s%s%s%s%s%s
printargs
as%d%d%d%d%d%d%s%s%s%s%s%s
prompt$
Reutilizar Curso
Descargar este curso